近日调试GD32驱动ds3231的程序,程序从STM 32移植过来的,所以就没想太多。当时STM32有HAL库开发,运行在内部晶振经PLL后的64MHZ频率。具体驱动程序网上一大堆,我这里就不在赘述了。只写一点我的感受。
在STM32上运行良好,延时程序采用单独书写的delay.c。移植过来后由于刚开始时钟方面配置的问题,导致GD32运行在默认8MHZ情况下,这时候DS3231的读写一切正常。直到我将时钟提高到108MHZ后,数据始终读不出来,用示波器观察波形,发现根本就没有所谓IIC 波形。这就很郁闷了。我试着将单片机运行频率降低,结果依然不行。通过研究示波器波形发现,在108MHZ情况下,iic的波形根本就存在,好像软件延时根本就没起作用。于是乎在网上查找相关记录,然并卵。于是从头开始研究IIC,最后还是看到koukou_1538725213在本站写的“模拟IIC的实现”一文,猜测是不是将延时函数写到和DS3231一个文件里,不使用调用头文件的形式会有作用。果不其然,IIC波形立刻就出来了,还可以调速。感谢koukou_1538725213。哪位大侠知道是何原因,请一定指教一下,谢谢啦!
具体部分程序如下:可以看到里面有延时函数的头文件调用。
#include "main.h"
#include "DS3231.h"
#include "gd32f10x.h"
//#include "delay.h"
#include "usart.h"
TIMER_TYPE Timer={0};
//uint8_t mid_flag; 居中标志,暂时没有使用
extern uint16_t sec;
//uint32_t DS3231_Now_Time=0;
const unsigned char rtc_address[7]={0x00,0x01,0x02,0x03,0x04,0x05,0x06};//秒分时日月周年 最低位读写位
/*设置SDA引脚为输入模式*/
void DS3231_SET_SDA_IN(void)
{
gpio_init(DS3231_SDA_GPIO_PORT,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,DS3231_SDA_GPIO_PIN);
}
/*设置SDA引脚为输出模式*/
void DS3231_SET_SDA_OUT(void)
{ gpio_init(DS3231_SDA_GPIO_PORT,GPIO_MODE_OUT_OD,GPIO_OSPEED_50MHZ,DS3231_SDA_GPIO_PIN);
}
/*设置SCL引脚为输入模式*/
void DS3231_SET_SCL_IN(void)
{ gpio_init(DS3231_SCL_GPIO_PORT,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,DS3231_SCL_GPIO_PIN);
}
/*设置SCL引脚为输出模式*/
void DS3231_SET_SCL_OUT(void)
{ gpio_init(DS3231_SCL_GPIO_PORT,GPIO_MODE_OUT_OD,GPIO_OSPEED_50MHZ,DS3231_SCL_GPIO_PIN);
}
//定义一个软件延时函数,用于对IIC 调速
static void I2C_delay(void)
{
volatile int i=8;
while(i--);
}
//使用软件模拟IIC //
//初始化
void IIC_init(void)
{
DS3231_SET_SDA_OUT(); //设置SDA引脚为通用开漏输出模式
DS3231_SET_SCL_OUT(); //设置SCL引脚为通用开漏输出模式
IIC_SDA_H; //SDA置高
IIC_SCL_H; //SCL置高
// Read_RTC();
}
//读取数据
unsigned char SDA_READ(void)
{
return gpio_input_bit_get(DS3231_SDA_GPIO_PORT, DS3231_SDA_GPIO_PIN); /* 读SDA口线状态 */
}
//产生起始信号
void IIC_Start(void)
{
DS3231_SET_SDA_OUT();
IIC_SCL_H;//set SDA pin as output high
I2C_delay();
IIC_SDA_H;//set SCL pin as output high
I2C_delay();
IIC_SDA_L;//set SDA pin as output low
I2C_delay();
IIC_SCL_L;//set SCL pin as output low
I2C_delay();
}
//产生停止信号
void IIC_Stop(void)
{
DS3231_SET_SDA_OUT();
IIC_SCL_L;//set SDA pin as output low
I2C_delay();
IIC_SDA_L;//set SCL pin as output high
I2C_delay();
IIC_SCL_H;//set SDA pin as output high
I2C_delay();
IIC_SDA_H;
I2C_delay();
}
//等待响应信号
//0--接收应答成功 1-接收应答失败
unsigned char IIC_Wait_Ack(void)
{
int ucErrTime = 0;
IIC_SDA_H;//set SCL pin as output high
IIC_SCL_H; //set sda as input with pull up
DS3231_SET_SDA_IN(); //设置SDA引脚为浮空输入模式
while(SDA_READ())
{
ucErrTime++;
if(ucErrTime > 1024)
{
DS3231_SET_SDA_OUT(); //设置SDA引脚为通用开漏输出模式
IIC_Stop();
return 1;
}
}
DS3231_SET_SDA_OUT(); //设置SDA引脚为通用开漏输出模式
IIC_SCL_L; //set SCL pin as output low
return 0;
}
//产生ACK信号
void IIC_Ack(void)
{
IIC_SCL_L;//set SCL pin as output low
I2C_delay();
IIC_SDA_L;//set SDA pin as output low
I2C_delay();
IIC_SCL_H;//set SDA pin as output high
I2C_delay();
IIC_SCL_L; //set SCL pin as output low
I2C_delay();
}
//No ACK
void IIC_NAck(void)
{
IIC_SCL_L; //set SCL pin as output low
I2C_delay();
IIC_SDA_H;//set SDA pin as output high
I2C_delay();
IIC_SCL_H;//set SCL pin as output high
I2C_delay();//延时
IIC_SCL_L;//set SCL pin as output low
I2C_delay();
}
//IIC发送一个字节
void IIC_Send_Byte(unsigned char txd)
{
unsigned char i;
IIC_SCL_L; //set SCL pin as output low
for(i = 0; i < 8; i++)
{
if((txd & 0x80)>>7)
{
IIC_SDA_H;//set SDA pin as output high
}
else
{
IIC_SDA_L;//set SDA pin as output low
}
txd <<= 1;
I2C_delay();
IIC_SCL_H;//set SCL pin as output high
I2C_delay();
IIC_SCL_L; //set SCL pin as output low
I2C_delay();
}
}
//读取一个字节
unsigned char IIC_Read_Byte(unsigned char ack)
{
unsigned char i, res = 0;
IIC_SDA_H;
DS3231_SET_SDA_IN(); //set sda as input with pull up
for(i = 0; i < 8; i++ )
{
IIC_SCL_L;//set SCL pin as output high
// delay_1us();
I2C_delay();
IIC_SCL_H;//set SCL pin as output high
// delay_1us();
I2C_delay();
res <<= 1;
if(SDA_READ())
{
res ++;
}
}
DS3231_SET_SDA_OUT();
if(!ack)
IIC_NAck();
else
IIC_Ack();
return res;
}
//-------------------------------------------------------------------
void IIC_single_byte_write(unsigned char Waddr,unsigned char Data)
{
//指定位置写入一个字节数据函数
IIC_Start();//产生起始信号
IIC_Send_Byte(0xd0);//写入设备地址(写)
IIC_Wait_Ack();//等待设备的应答
IIC_Send_Byte(Waddr);//写入要操作的单元地址。
IIC_Wait_Ack();//等待设备的应答。
IIC_Send_Byte(Data);
IIC_Wait_Ack();
IIC_Stop();//产生停止符号。
}
//-------------------------------------------------------------------
unsigned char IIC_single_byte_read(unsigned char Waddr)
{
//从任意地址读取一个字节数据函数
unsigned char Data;//定义一个缓冲寄存器。
IIC_Start();//产生起始信号
IIC_Send_Byte(0xd0);//写入设备地址(写)
IIC_Wait_Ack();//等待设备的应答
IIC_Send_Byte(Waddr);//写入要操作的单元地址。
IIC_Wait_Ack();//等待设备的应答。
IIC_Stop();//产生停止符号。
IIC_Start();//产生起始信号
IIC_Send_Byte(0xd1);//写入设备地址(写)
IIC_Wait_Ack();//等待设备的应答
Data=IIC_Read_Byte(0);//写入数据。
// i2c_Delay();
IIC_Stop();//产生停止符号。
//-------------------返回读取的数据--------------------
return Data;//返回读取的一个字节数据。
}
/******************************************
名 称:Read_RTC();
功 能:循环读取当前时间;
参 数:i:计数;temp:时间缓存区;p:缓存区指针
rtc_address:时间地址存放缓存区
**********************************************/
void Read_RTC(void)
{
unsigned char i;
unsigned char temp[7]={0};
const unsigned char *p;
p=rtc_address; //地址传递
for(i=0;i<7;i++) //分6次读取 秒分时日月年
{
temp[i]=IIC_single_byte_read(*p);
p++;
}
//Timer.second=temp[0];
sec=temp[0];
Timer.minute=temp[1];
Timer.hour=temp[2];
Timer.week=temp[3];
Timer.day=temp[4];
Timer.month=temp[5]&0X1F;
Timer.year=temp[6];
}
unsigned char BCD2HEX(unsigned char val) //BCD转换为Byte
{
unsigned char temp;
temp=val&0x0f;
val>>=4;
val&=0x0f;
val*=10;
temp+=val;
return temp;
}
unsigned char HEX2BCD(unsigned char val) //B码转换为BCD码
{
unsigned char i,j,k;
i=val/10;
j=val%10;
k=j+(i<<4);
return k;
}
/****************************************************************
函数功能:依据服务器GPRS传来时间信息 设置DS3231时间
输入参数:服务器传来信息
返回参数:无
备 注:无
*****************************************************************/
void Set_RTC(unsigned char *data)
{
unsigned char i;
for(i=0;i<7;i++)
{
data[i+7]=HEX2BCD(data[i+7]);
}
IIC_single_byte_write(0x0e,0X8c);
for(i=0;i<7;i++) //6次写入 秒分时周日月年
{
IIC_single_byte_write(rtc_address[i],data[i+7]);
}
IIC_single_byte_write(0x0e,0x0c);
}
void DS1302_Initial(void)
{
IIC_SDA_H;//释放IIC总线的数据线。
IIC_SCL_H;
IIC_single_byte_write(0x0e,0x0c);
}
/****************************************************************
函数功能:读取DS3231中当前时间 将时间存入TIMER_TYPE Timer结构体中
输入参数:无
返回参数:无
备 注:无 测试代码,稍后可删除
*****************************************************************/
void Clock_Run(void)
{
Read_RTC();
//-----测试代码--------
usart2_send_byte(0x20);
usart2_send_byte(Timer.year);
usart2_send_byte(Timer.month);
usart2_send_byte(Timer.day);
usart2_send_byte(Timer.week);
usart2_send_byte(Timer.hour);
usart2_send_byte(Timer.minute);
usart2_send_byte(sec);
//---------------------
}
/*******************************************
名 称:void DS3231_Set(uint8_t yea,uint8_t mon,uint8_t da,uint8_t we,uint8_t hou,uint8_t min,uint8_t sec)
功 能:设置DS3231的时间,
参 数:yea:年 mon:月 da:日 we:周 hou:时 min:分 se:秒
说 明:数据格式采用16进制,例如22时56分应写成 hou:0x16;min:0x38
**********************************************/
void DS3231_Set(uint8_t yea,uint8_t mon,uint8_t da,uint8_t we,uint8_t hou,uint8_t min,uint8_t se)
{
uint8_t temp=0;
temp=HEX2BCD(yea);
IIC_single_byte_write(DS3231_YEAR,temp);
temp=HEX2BCD(mon);
temp=temp|0x80;
IIC_single_byte_write(DS3231_MONTH,temp);
temp=HEX2BCD(da);
IIC_single_byte_write(DS3231_DAY,temp);
temp=HEX2BCD(we);
IIC_single_byte_write(DS3231_WEEK,temp);
temp=HEX2BCD(hou);
IIC_single_byte_write(DS3231_HOUR,temp);
temp=HEX2BCD(min);
IIC_single_byte_write(DS3231_MINUTE,temp);
temp=HEX2BCD(se);
IIC_single_byte_write(DS3231_SECOND,temp);
}