51单片机 ds1302通信问题 求助
本人最近在看江协科技的视频学习51单片机,但在使用ds1302实时时钟时遇到了问题:开发板:普中c51开发板
芯片型号:STC89C52RC
问题描述:在使用ds1302和mcu进行通信时,在读取数据之前io口不置0,则通过串口通信接收到的数据总会间隔一个出现一次0x7F(既通信IO口接收到的一直为1),代码如下:
main.c文件:
#include <REGX52.h>
#include "ds1302.h"
#include "Delay.h"
#include "serial.h"
void main()
{
unsigned char ds1302_timer[] = {25, 9, 2, 2, 24, 51, 49};
unsigned char i = 0;
unsigned char timer = 0x00;
uart_init();
DS1302_Write(0x8E, 0x00); // 关闭写保护
for(i = 0; i < 7; i++)
DS1302_Write(time_address, ds1302_timer%10+ds1302_timer/10*16);
while(1)
{
for(i = 0; i < 7; i++)
{
timer = DS1302_Read(time_address);
uart_transmit(timer);
}
Delay(100);
}
}
ds1302.c文件:
#include <REGX52.h>
#include "Delay.h"
#include "ds1302.h"
sbit SCLK = P3^6;
sbit CE = P3^5;
sbit IO = P3^4;
const unsigned char time_address[] = {0x8C, 0x88, 0x86, 0x8A, 0x84, 0x82, 0x80};
void DS1302_Write_Byte(unsigned char command)
{
unsigned char i = 0;
for(i = 0; i < 8; i++)
{
IO = command & 0x01;
Delay_Microsecond(2);
SCLK = 0;
Delay_Microsecond(2);
SCLK = 1;
command >>= 1;
}
}
void DS1302_Write(unsigned char address, unsigned char control)
{
CE = 0;
Delay_Microsecond(2);
SCLK = 0;
Delay_Microsecond(2);
CE = 1;
Delay_Microsecond(2);
DS1302_Write_Byte(address);
DS1302_Write_Byte(control);
CE = 0;
}
unsigned char DS1302_Read_Byte()
{
unsigned char i = 0;
unsigned char time = 0x00;
//IO = 0;这里不置0则遇到间隔一次就出现io读取到的一直为1的问题
for(i = 0; i < 8; i++)
{
SCLK = 1;
Delay_Microsecond(2);
SCLK = 0;
Delay_Microsecond(2);
if(IO == 1)
time |= (0x01 << i);
}
return time;
}
unsigned char DS1302_Read(unsigned char address)
{
unsigned char time = 0x00;
address |= 0x01;
CE = 0;
Delay_Microsecond(2);
SCLK = 0;
Delay_Microsecond(2);
CE = 1;
Delay_Microsecond(2);
DS1302_Write_Byte(address);
time = DS1302_Read_Byte();
CE = 0;
return time;
}
serial.c文件:
#include <regx52.h>
#include "serial.h"
// 波特率9600
void uart_init()
{
PCON &= 0x0F;
SCON = 0x50;
TMOD &= 0x0F;
TMOD |= 0x20;
TL1 = 0xFD;
TH1 = 0xFD;
EA = 0;
TI = 0;
RI = 0;
TR1 = 1;
}
void uart_transmit(unsigned char byte)
{
SBUF = byte;
while(!TI);
TI = 0;
}
尊敬的用户:
您好!感谢您对国芯人工智能的关注与支持。针对您在使用51单片机(STC89C52RC)与DS1302通信过程中遇到的问题,我们已认真阅读您的描述,并结合相关技术原理进行分析,现将问题原因及解决建议整理如下:
一、问题现象分析
您提到在读取DS1302数据之前未将IO口置为低电平,导致串口接收到的数据中出现间隔性0x7F(即二进制1111111),且通信IO口始终为高电平。此现象表明DS1302与MCU之间的通信逻辑存在异常。
二、可能原因分析
1. 时序不匹配
DS1302采用SPI协议进行通信,其时序要求较为严格。若未在通信前正确配置控制引脚(CE、SCLK、I/O),可能导致数据传输失败或读取错误。
2. IO口未初始化为输出模式
DS1302的I/O口在通信过程中需作为双向口使用,但若未正确设置方向寄存器(如P2M1/P2M0),可能导致数据无法正常传输。
3. 未执行写入命令操作
DS1302在读取数据前需先发送读取命令(如0x81),若未正确发送该指令,可能导致读取结果异常。
4. 电源或时钟不稳定
DS1302依赖外部32.768kHz晶振和VCC供电,若供电不稳定或晶振未起振,可能导致通信中断或数据错误。
三、解决方案建议
1. 确保IO口初始化正确
在代码中明确设置DS1302的控制引脚(CE、SCLK、I/O)为输出模式,并在通信前将CE拉低以使能芯片。
2. 遵循DS1302通信时序
在读取数据前,应按照以下步骤操作:
将CE拉低;
发送读取命令(如0x81);
通过SCLK脉冲读取数据;
最后将CE拉高以结束通信。
3. 检查DS1302硬件连接
确认DS1302的VCC、GND、SCLK、I/O、CE引脚与MCU连接正确,且晶振电路工作正常。
4. 调试与验证
建议使用示波器或逻辑分析仪观察SCLK、I/O信号波形,确认时序是否符合DS1302规范。同时,可通过串口打印中间变量,验证程序执行流程是否正确。
四、参考代码片段(仅供参考)
```c
void DS1302WriteByte(unsigned char dat) {
unsigned char i;
for(i=0; i> i) & 0x01;
DS1302SCLK = 1;
}
}
unsigned char DS1302ReadByte(void) {
unsigned char i, dat = 0;
for(i=0; i
[本文内容由国芯人工智能辅助生成,仅供参考] 已帮您转发给专业技术,稍后晚点会有技术回复 yao眼的光 发表于 2025-9-3 17:32
已帮您转发给专业技术,稍后晚点会有技术回复
感谢您{:haixiu:} 建议使用内置RTC的Ai MCU,不需要外挂RTC芯片
测试通过的DS1302程序如下:
头文件:
#define DS1302_CLK P30 //定义DS1302时钟IO口
#define DS1302_DAT P31 //定义DS1302数据IO口
#define DS1302_RST P32 //定义DS1302复位IO口
#define Protrct_Enable 0x80 //使能写保护
#define Protect_Disable 0x00 //禁用写保护
#define Write_Protect 0x8e //定义写保护寄存器地址
#define Write_TCS 0x90 //定义写充电寄存器地址
#define Write_Year 0x8c //定义写年寄存器地址
#define Write_Week 0x8a //定义写星期寄存器地址
#define Write_Month 0x88 //定义写月寄存器地址
#define Write_Date 0x86 //定义写日寄存器地址
#define Write_Hour 0x84 //定义写小时寄存器地址
#define Write_Minute 0x82 //定义写分钟寄存器地址
#define Write_Second 0x80 //定义写秒钟寄存器地址
#define Read_Year 0x8d //定义读年寄存器地址
#define Read_Week 0x8b //定义读星期寄存器地址
#define Read_Month 0x89 //定义读月寄存器地址
#define Read_Date 0x87 //定义读日寄存器地址
#define Read_Hour 0x85 //定义读小时寄存器地址
#define Read_Minute 0x83 //定义读分钟寄存器地址
#define Read_Second 0x81 //定义读秒钟寄存器地址
//#define Init_TCS 0xa0 //设置DS1302充电方式为关闭
//#define Init_TCS 0xa5 //设置DS1302充电方式为1个二极管,2K电阻
//#define Init_TCS 0xa6 //设置DS1302充电方式为1个二极管,4K电阻
//#define Init_TCS 0xa7 //设置DS1302充电方式为1个二极管,8K电阻
//#define Init_TCS 0xa9 //设置DS1302充电方式为2个二极管,2K电阻
//#define Init_TCS 0xaa //设置DS1302充电方式为2个二极管,4K电阻
#define Init_TCS 0xab //设置DS1302充电方式为2个二极管,8K电阻
#define Init_Year 0x25 //初始化年
#define Init_Week 0x04 //初始化星期
#define Init_Month 0x09 //初始化月
#define Init_Date 0x04 //初始化日
#define Init_Hour 0x12 //初始化小时
#define Init_Minute 0x00 //初始化分钟
#define Init_Second 0x00 //初始化秒钟子程序:
/*----------------------------地址、数据发送子程序----------------------------*/
void Write_DS1302 (unsigned char addr,unsigned char dat)
{
unsigned char i,temp; //声明移位次数、临时变量
DS1302_RST=0; //拉低DS1302_RST,禁止DS1302数据传输
DS1302_CLK=0; //拉低DS1302_CLK
DS1302_RST=1; //DS1302_RST为高,使能DS1302数据传输
/*----------------------------发送地址----------------------------*/
for (i=8;i>0;i--) //循环移位8次
{
DS1302_CLK=0; //拉低DS1302_CLK
temp=addr; //把地址赋值给临时变量
DS1302_DAT=(bit)(temp&0x01); //每次传输低字节,数据放到数据端口
DS1302_CLK=1; //拉高DS1302_CLK,锁存数据
addr>>=1; //地址右移一位
}
/*----------------------------发送数据----------------------------*/
for(i=8;i>0;i--) //循环移位8次
{
DS1302_CLK=0; //拉低DS1302_CLK
temp=dat; //把数据赋值给临时变量
DS1302_DAT=(bit)(temp&0x01); //只取最低一位
DS1302_CLK=1; //拉高DS1302_CLK,锁存数据
dat>>=1; //右移一位
}
DS1302_CLK=0; //拉低DS1302_CLK
DS1302_RST=0; //拉低DS1302_RST,禁止DS1302数据传输
}
/*----------------------------数据读取子程序----------------------------*/
unsigned char Read_DS1302(unsigned char addr)
{
unsigned char i,temp,dat,dat1;
DS1302_RST=0; //拉低DS1302_RST,禁止DS1302数据传输
DS1302_CLK=0; //拉低DS1302_CLK
DS1302_RST=1; //DS1302_RST为高,使能DS1302数据传输
/*----------------------------发送地址----------------------------*/
for(i=8;i>0;i--) //循环移位8次
{
DS1302_CLK=0; //拉低DS1302_CLK
temp=addr; //把地址赋值给临时变量
DS1302_DAT=(bit)(temp&0x01); //每次传输低位
DS1302_CLK=1; //拉高DS1302_CLK,锁存数据
addr>>=1; //addr右移一位
}
/*----------------------------读取数据----------------------------*/
P3M0=P3M0&0x1d;
for(i=8;i>0;i--) //循环移位8次
{
ACC>>=1; //ACC右移一位
DS1302_CLK=1; //先拉高DS1302_CLK
DS1302_CLK=0; //拉低DS1302_CLK,准备读取数据
ACC_7=DS1302_DAT; //读取数据,ACC_7为存放数据变量的最高位一位
}
P3M0=P3M0|0x02;
DS1302_CLK=0; //拉低DS1302_CLK
DS1302_RST=0; //拉低DS1302_RST,禁止DS1302数据传输
dat=ACC; //把ACC赋值给dat
dat1=dat&0xf0; //数据进制转换, 提取高四位
dat1>>=4; //移动到低四位,作为十位
dat=dat&0x0f; //提取低四位,作为个位
dat=dat+dat1*10; //得到的十进制数
return dat; //返回十进制值
}
/*----------------------------初始化DS1302----------------------------*/
void Init_DS1302(void)
{
Write_DS1302(Write_Protect,Protect_Disable); //禁用写保护
Write_DS1302(Write_TCS,Init_TCS); //初始化充电方式
Write_DS1302(Write_Year,Init_Year); //初始化年
Write_DS1302(Write_Week,Init_Week); //初始化星期
Write_DS1302(Write_Month,Init_Month); //初始化月
Write_DS1302(Write_Date,Init_Date); //初始化日
Write_DS1302(Write_Hour,Init_Hour); //初始化小时
Write_DS1302(Write_Minute,Init_Minute); //初始化分钟
Write_DS1302(Write_Second,Init_Second); //初始化秒钟
Write_DS1302(Write_Protect,Protrct_Enable); //使能写保护
}调用:
Read_DS1302(Read_Hour)
Read_DS1302(Read_Minute)
Read_DS1302(Read_Second)
串口程序参考:
https://www.stcaimcu.com/thread-16321-1-1.html
https://www.stcaimcu.com/thread-4598-1-1.html
页:
[1]