完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、配置IO
/** /** ****************************************************************************** * @file bsp_i2c_ds3231.h * @author 兲涳 * @version V1.0 * @date 2020-11-18 * @brief 模拟i2c RTC(DS3231)应用函数bsp ****************************************************************************** * @attention * ****************************************************************************** */ /****************************************************************************** * @attention * ****************************************************************************** */ /* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __I2C_DS3231_H #define __I2C_DS3231_H /* Includes ------------------------------------------------------------------*/ #ifdef _I2C_DS3231_PUBLIC_ #define PEXT #else #define PEXT extern #endif /* Exported types ------------------------------------------------------------*/ typedef struct { u8 hour; u8 min; u8 sec; u32 year; u8 month; u8 day; u8 week; u16 temperature; }_calendar_obj; /* Exported constants --------------------------------------------------------*/ /* Exported macro ------------------------------------------------------------*/ /**************************I2C参数定义,I2C1或I2C2*****************************/ //#define DS3231_I2C_GPIO_APBxClock_FUN RCC_AHB1PeriphClockCmd //#define DS3231_I2C_GPIO_CLK RCC_APB2Periph_GPIOC #define DS3231_I2C_SCL_PORT GPIOC #define DS3231_I2C_SCL_PIN GPIO_PIN_10 #define DS3231_I2C_SDA_PORT GPIOC #define DS3231_I2C_SDA_PIN GPIO_PIN_11 #define I2C_WR 0 #define I2C_RD 1 #define I2C_SCL_1() HAL_GPIO_WritePin(DS3231_I2C_SCL_PORT, DS3231_I2C_SCL_PIN,GPIO_PIN_SET) #define I2C_SCL_0() HAL_GPIO_WritePin(DS3231_I2C_SCL_PORT, DS3231_I2C_SCL_PIN,GPIO_PIN_RESET) #define I2C_SDA_1() HAL_GPIO_WritePin(DS3231_I2C_SDA_PORT, DS3231_I2C_SDA_PIN,GPIO_PIN_SET) #define I2C_SDA_0() HAL_GPIO_WritePin(DS3231_I2C_SDA_PORT, DS3231_I2C_SDA_PIN,GPIO_PIN_RESET) #define I2C_SDA_READ() HAL_GPIO_ReadPin(DS3231_I2C_SDA_PORT, DS3231_I2C_SDA_PIN) /* STM32 I2C 快速模式 */ #define I2C_Speed 400000 /* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */ #define I2Cx_OWN_ADDRESS7 0X0A /* DS3231 地址定义 */ #define DS3231_ADDRESS 0xD0 /* DS3231寄存器地址 */ #define DS3231_SECOND 0x00 //秒 #define DS3231_MINUTE 0x01 //分 #define DS3231_HOUR 0x02 //时 #define DS3231_WEEK 0x03 //星期 #define DS3231_DAY 0x04 //日 #define DS3231_MONTH 0x05 //月 #define DS3231_YEAR 0x06 //年 0x06 //年 /* 闹铃1 */ #define DS3231_SALARM1ECOND 0x07 //秒 #define DS3231_ALARM1MINUTE 0x08 //分 #define DS3231_ALARM1HOUR 0x09 //时 #define DS3231_ALARM1WEEK 0x0A //星期/日 /* 闹铃2 */ #define DS3231_ALARM2MINUTE 0x0b //分 #define DS3231_ALARM2HOUR 0x0c //时 #define DS3231_ALARM2WEEK 0x0d //星期/日 #define DS3231_CONTROL 0x0e //控制寄存器 #define DS3231_STATUS 0x0f //状态寄存器 #define BSY 2 //忙 #define OSF 7 //振荡器停止标志 #define DS3231_XTAL 0x10 //晶体老化寄存器 #define DS3231_TEMPERATUREH 0x11 //温度寄存器高字节(8位) #define DS3231_TEMPERATUREL 0x12 //温度寄存器低字节(高2位) PEXT _calendar_obj calendar; //日历结构体 /* Exported functions ------------------------------------------------------- */ PEXT void I2C_DS3231_Init(void); PEXT void i2c_Stop(void); PEXT uint32_t I2C_DS3231_ByteWrite(u8 WriteAddr, u8 data); PEXT uint8_t I2C_DS3231_DataRead(u8 ReadAddr); PEXT uint8_t BCD_DEC(u8 val); PEXT uint8_t DEC_BCD(u8 val); PEXT void i2c_SendByte(uint8_t _ucByte); PEXT void I2C_DS3231_SetTime(u8 yea,u8 mon,u8 da,u8 we,u8 hou,u8 min,u8 sec); PEXT void Time_Regulate_Get(_calendar_obj *tm); PEXT void I2C_DS3231_getTime(void); PEXT void I2C_DS3231_getTemperature(void); PEXT void GregorianDay(_calendar_obj * tm); #undef PEXT #endif /*********************************************END OF FILE**********************/ 二、初始化与函数 #define _I2C_DS3231_PUBLIC_ #include "common.h" /** ****************************************************************************** * @file bsp_i2c_ds3231.c * @author 兲涳 * @version V1.0 * @date 2020-11-16 * @brief i2c RTC(DS3231)应用函数bsp ****************************************************************************** * @attention * ****************************************************************************** */ /** * @brief 转换成BCD码 * @param None * @retval 返回BCD码 */ u16 B_BCD(u8 val) { u8 i,j,k; i=val/10; j=val%10; k=j+(i<<4); return k; } /** * @brief I2C I/O配置 * @param None * @retval None */ static void I2C_GPIO_Config(void) { GPIO_InitTypeDef GPIO_Initure; /* 使能与 I2C 有关的时钟 */ __HAL_RCC_GPIOC_CLK_ENABLE(); // 使能GPIOC端口时钟 /* I2C_SCL、I2C_SDA*/ GPIO_Initure.Mode=GPIO_MODE_OUTPUT_OD; //开漏输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 GPIO_Initure.Pin=GPIO_PIN_10|GPIO_PIN_11; //PC3 HAL_GPIO_Init(GPIOC,&GPIO_Initure); HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10|GPIO_PIN_11,GPIO_PIN_SET); //初始化后置1 } /** * @brief I2C 外设(DS3231)初始化 * @param None * @retval None */ void I2C_DS3231_Init(void) { I2C_GPIO_Config(); /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */ i2c_Stop(); } /** * @brief I2C总线位延迟,最快400KHz * @param None * @retval None */ static void i2c_Delay(void) { uint8_t i; /* 下面的时间是通过逻辑分析仪测试得到的。 工作条件:CPU主频72MHz ,MDK编译环境,1级优化 循环次数为10时,SCL频率 = 205KHz 循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us */ for (i = 0; i < 10; i++); } /** * @brief I2C总线启动信号 * @param None * @retval None */ void i2c_Start(void) { /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */ I2C_SDA_1(); I2C_SCL_1(); i2c_Delay(); I2C_SDA_0(); i2c_Delay(); I2C_SCL_0(); i2c_Delay(); } /** * @brief I2C总线停止信号 * @param None * @retval None */ void i2c_Stop(void) { /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */ I2C_SDA_0(); I2C_SCL_1(); i2c_Delay(); I2C_SDA_1(); } /** * @brief MCU向I2C总线设备发送8bit数据 * @param * @arg _ucByte:发送的字节 * @retval None */ void i2c_SendByte(uint8_t _ucByte) { uint8_t i; /* 先发送字节的高位bit7 */ for (i = 0; i < 8; i++) { if (_ucByte & 0x80) { I2C_SDA_1(); } else { I2C_SDA_0(); } i2c_Delay(); I2C_SCL_1(); i2c_Delay(); I2C_SCL_0(); if (i == 7) { I2C_SDA_1(); // 释放总线 } _ucByte <<= 1; /* 左移一个bit */ i2c_Delay(); } } /** * @brief MCU从I2C总线设备读取8bit数据 * @param None * @retval 读到的数据 */ uint8_t i2c_ReadByte(void) { uint8_t i; uint8_t value; /* 读到第1个bit为数据的bit7 */ value = 0; for (i = 0; i < 8; i++) { value <<= 1; I2C_SCL_1(); i2c_Delay(); if (I2C_SDA_READ()) { value++; } I2C_SCL_0(); i2c_Delay(); } return value; } /** * @brief MCU产生一个时钟,并读取器件的ACK应答信号 * @param None * @retval 返回0表示正确应答,1表示无器件响应 */ uint8_t i2c_WaitAck(void) { uint8_t re; I2C_SDA_1(); /* CPU释放SDA总线 */ i2c_Delay(); I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */ i2c_Delay(); if (I2C_SDA_READ()) /* CPU读取SDA口线状态 */ { re = 1; } else { re = 0; } I2C_SCL_0(); i2c_Delay(); return re; } /** * @brief MCU产生一个ACK信号 * @param None * @retval None */ void i2c_Ack(void) { I2C_SDA_0(); /* CPU驱动SDA = 0 */ i2c_Delay(); I2C_SCL_1(); /* CPU产生1个时钟 */ i2c_Delay(); I2C_SCL_0(); i2c_Delay(); I2C_SDA_1(); /* CPU释放SDA总线 */ } /** * @brief MCU产生1个NACK信号 * @param None * @retval None */ void i2c_NAck(void) { I2C_SDA_1(); /* CPU驱动SDA = 1 */ i2c_Delay(); I2C_SCL_1(); /* CPU产生1个时钟 */ i2c_Delay(); I2C_SCL_0(); i2c_Delay(); } /** * @brief 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在 * @param * @arg _Address:设备的I2C总线地址 * @retval 返回值 0 表示正确, 返回1表示未探测到 */ uint8_t i2c_CheckDevice(uint8_t _Address) { uint8_t ucAck; I2C_DS3231_Init(); /* 配置GPIO */ i2c_Start(); /* 发送启动信号 */ /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */ i2c_SendByte(_Address | I2C_WR); ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */ i2c_Stop(); /* 发送停止信号 */ return ucAck; } // /** * @brief 写一个字节到I2C DS3231中 * @param * @arg data:要写入的字节 * @arg WriteAddr:写地址 * @retval 返回1,表示写入成功. */ uint32_t I2C_DS3231_ByteWrite(u8 WriteAddr, u8 data) { /* 第1步:发起I2C总线启动信号 */ i2c_Start(); /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ i2c_SendByte(DS3231_ADDRESS| I2C_WR); /* 此处是写指令 */ /* 第3步:等待ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第4步:发送寄存器地址 */ i2c_SendByte((uint8_t)WriteAddr); /* 第5步:等待ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第6步:开始写入数据 */ i2c_SendByte(data); /* 第7步:等待ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 命令执行成功,发送I2C总线停止信号 */ i2c_Stop(); return 1; cmd_fail: /* 命令执行失败后,发送停止信号 */ /* 发送I2C总线停止信号 */ i2c_Stop(); return 0; } /** * @brief 从DS3231里面读取一个字节数据 * @param * @arg data:存放从DS3231读取的数据 * @arg ReadAddr:读取数据的DS3231的地址 * @retval data:返回数据. */ uint8_t I2C_DS3231_DataRead(u8 ReadAddr) { uint8_t data; /* 第1步:发起I2C总线启动信号 */ i2c_Start(); /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ i2c_SendByte(DS3231_ADDRESS|I2C_WR); /* 此处是写指令 */ /* 第3步:等待ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第4步:发送DS3231寄存器地址 */ i2c_SendByte((uint8_t)ReadAddr); /* 第5步:等待ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第6步:产生第二次 I2C 起始信号 */ i2c_Start(); /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ //i2c_SendByte(DS3231_ADDRESS | I2C_RD); /* 此处是读指令 */ i2c_SendByte(DS3231_ADDRESS | I2C_RD); /* 此处是读指令 */ /* 第8步:发送ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第9步:读取数据 */ data = i2c_ReadByte(); i2c_NAck(); i2c_Stop(); return data; cmd_fail: /* 命令执行失败后,发送停止信号 */ /* 发送I2C总线停止信号 */ i2c_Stop(); return 0; } /** * @brief BCD(8421)转DEC. * @param val:BCD码. * @retval i:DEC码. */ uint8_t BCD_DEC(u8 val) { u8 i; i= val&0x0f; val >>= 4; val &= 0x0f; val *= 10; i += val; return i; } /** * @brief BCD(8421)转DEC. * @param val:DEC码. * @retval k:BCD码. */ uint8_t DEC_BCD(u8 val) { u8 i,j,k; i=val/10; j=val%10; k=j+(i<<4); return k; } /** * @brief 时间设置 * @param * @arg 分别输入 年 月 日 星期 时 分 秒 * @retval 无 */ void I2C_DS3231_SetTime(u8 yea,u8 mon,u8 da,u8 we,u8 hou,u8 min,u8 sec) { u8 temp=0; yea-=2000; temp=DEC_BCD(yea); I2C_DS3231_ByteWrite(0x06,temp); temp=DEC_BCD(mon); I2C_DS3231_ByteWrite(0x05,temp); temp=DEC_BCD(da); I2C_DS3231_ByteWrite(0x04,temp); // temp=DEC_BCD(we); // I2C_DS3231_ByteWrite(0x03,temp); temp=DEC_BCD(hou); I2C_DS3231_ByteWrite(0x02,temp); temp=DEC_BCD(min); I2C_DS3231_ByteWrite(0x01,temp); temp=DEC_BCD(sec); I2C_DS3231_ByteWrite(0x00,temp); } /** * @brief 获取时间 * @param * @arg pBuffer:存放从DS3231读取的数据的缓冲区指针 * @arg ReadAddr:读取数据的DS3231的地址 * @arg NumByteToWrite:要从DS3231读取的字节数 * @retval 返回1,表示读取成功. */ void I2C_DS3231_getTime(void) { calendar.year=I2C_DS3231_DataRead(0x06); calendar.year=BCD_DEC(calendar.year)+2000; calendar.month=I2C_DS3231_DataRead(0x05); calendar.month=BCD_DEC(calendar.month); calendar.day=I2C_DS3231_DataRead(0x04); calendar.day=BCD_DEC(calendar.day); calendar.week=I2C_DS3231_DataRead(0x03); calendar.week=BCD_DEC(calendar.week); calendar.hour=I2C_DS3231_DataRead(0x02); calendar.hour&=0x3f; calendar.hour=BCD_DEC(calendar.hour); calendar.min=I2C_DS3231_DataRead(0x01); calendar.min=BCD_DEC(calendar.min); calendar.sec=I2C_DS3231_DataRead(0x00); calendar.sec=BCD_DEC(calendar.sec); } /** * @brief 保存用户使用串口设置的时间 * @param * @arg tm:用于设置RTC时间的结构体指针 * @retval */ void Time_Regulate_Get(_calendar_obj *tm) { uint32_t temp_num = 0; uint8_t day_max=0 ; printf("rn=========================设置时间=================="); do { printf("rn 请输入年份(Please Set Years),范围[2000~2255],输入字符后请加回车:"); scanf("%d",&temp_num); if(temp_num <2000 || temp_num >2255) { printf("rn 您输入的数字是:%d,不符合要求",temp_num); } else { printf("nr 年份被设置为: %dnr", temp_num); temp_num-=2000; DEC_BCD(temp_num); tm->year = temp_num; break; } }while(1); do { printf("rn 请输入月份(Please Set Months):范围[1~12],输入字符后请加回车:"); scanf("%d",&temp_num); if(temp_num <1 || temp_num >12) { printf("rn 您输入的数字是:%d,不符合要求",temp_num); } else { printf("nr 月份被设置为: %dnr", temp_num); DEC_BCD(temp_num); tm->month = temp_num; break; } }while(1); /*根据月份计算最大日期*/ switch(tm->month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: day_max = 31; break; case 4: case 6: case 9: case 11: day_max = 30; break; case 2: /*计算闰年*/ if(((tm->year+2000)%4==0) && (((tm->year+2000)%100!=0) || ((tm->year+2000)%400==0))) { day_max = 29; } else { day_max = 28; } break; } do { printf("rn 请输入日期(Please Set Months),范围[1~%d],输入字符后请加回车:",day_max); scanf("%d",&temp_num); if(temp_num <1 || temp_num >day_max) { printf("rn 您输入的数字是:%d,不符合要求",temp_num); } else { printf("nr 日期被设置为: %dnr", temp_num); DEC_BCD(temp_num); tm->day = temp_num; break; } }while(1); GregorianDay( tm ); do { printf("rn 请输入时钟(Please Set Hours),范围[0~23],输入字符后请加回车:"); scanf("%d",&temp_num); if( temp_num >23) { printf("rn 您输入的数字是:%d,不符合要求",temp_num); } else { printf("nr 时钟被设置为: %dnr", temp_num); DEC_BCD(temp_num); tm->hour = temp_num; break; } }while(1); do { printf("rn 请输入分钟(Please Set Minutes),范围[0~59],输入字符后请加回车:"); scanf("%d",&temp_num); if( temp_num >59) { printf("rn 您输入的数字是:%d,不符合要求",temp_num); } else { printf("nr 分钟被设置为: %dnr", temp_num); DEC_BCD(temp_num); tm->min = temp_num; break; } }while(1); do { printf("rn 请输入秒钟(Please Set Seconds),范围[0~59],输入字符后请加回车:"); scanf("%d",&temp_num); if( temp_num >59) { printf("rn 您输入的数字是:%d,不符合要求",temp_num); } else { printf("nr 秒钟被设置为: %dnr", temp_num); DEC_BCD(temp_num); tm->sec = temp_num; break; } }while(1); __HAL_UART_ENABLE_IT(&UART1_Handler,UART_IT_RXNE); //开启接收中断 } /** * @brief 获取温度 * @param 无 * @retval 无 */ void I2C_DS3231_getTemperature(void) { I2C_DS3231_ByteWrite(DS3231_CONTROL, 0x20|0x05); calendar.temperature=I2C_DS3231_DataRead(DS3231_TEMPERATUREH); } /*计算公历天数得出星期*/ void GregorianDay(_calendar_obj * tm) { int leapsToDate; int lastYear; int day; int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 }; lastYear=tm->year-1; /*计算从公元元年到计数的前一年之中一共经历了多少个闰年*/ leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; /*如若计数的这一年为闰年,且计数的月份在2月之后,则日数加1,否则不加1*/ if((tm->year%4==0) && ((tm->year%100!=0) || (tm->year%400==0)) && (tm->month>2)) { /* * We are past Feb. 29 in a leap year */ day=1; } else { day=0; } day += lastYear*365 + leapsToDate + MonthOffset[tm->month-1] + tm->day; /*计算从公元元年元旦到计数日期一共有多少天*/ tm->week=day%7; //算出星期 } /*********************************************END OF FILE**********************/ 三、函数调用(我用标准库的代码,如果在hal库有问题自己改下。调用方式是一样的) /** ****************************************************************************** * @file main.c * @author 兲涳 * @version V1.0 * @date 2020-11-16 * @brief ****************************************************************************** * @attention * I2C_2 RTC(DS3231)测试,测试信息通过USART1打印在电脑调试助手上,通过串口设置时间 * !!!串口输入 t 回车 ,进行设置时间。 ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "stm32f10x.h" #include "bsp_usart.h" #include "bsp_i2c_ds3231.h" #include /* Private typedef -----------------------------------------------------------*/ uint8_t i=0; extern _calendar_obj calendar; //日历结构体 /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ #define SOFT_DELAY Delay(0x4FFFFF); /* Private variables ---------------------------------------------------------*/ /* Private functions ---------------------------------------------------------*/ void Delay(__IO u32 nCount); /** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { /* 初始化USART 配置模式为 115200 8-N-1 */ USART_Config(); printf("nDS3231 RTC时钟n"); /* I2C 外设初(DS3231)始化 */ I2C_DS3231_Init(); // I2C_DS3231_SetTime(20,8,24,1,13,43,20); while(1) { SOFT_DELAY; if(i==1) { Time_Regulate_Get( &calendar ); I2C_DS3231_SetTime(calendar.year, calendar.month, calendar.date, calendar.week, calendar.hour, calendar.min, calendar.sec); i=0; } I2C_DS3231_getTime(); //获取时间 I2C_DS3231_getTemperature(); //获取温度 printf("%d年%d月%d日%d时%d分%d秒 星期%d 温度%dn",calendar.year+2000,calendar.month,calendar.date, calendar.hour,calendar.min,calendar.sec,calendar.week,calendar.temperature);//打印到串口屏不能有printf("n");换行!!! } } /** * @brief 延时函数 * @param 无 * @retval 无 */ void Delay(__IO uint32_t nCount) //简单的延时函数 { for(; nCount != 0; nCount--); } |
|
|
|
只有小组成员才能发言,加入小组>>
imx6ull 和 lan8742 工作起来不正常, ping 老是丢包
2481 浏览 0 评论
3341 浏览 9 评论
3022 浏览 16 评论
3514 浏览 1 评论
9119 浏览 16 评论
1243浏览 3评论
636浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
627浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2373浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1936浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-24 14:46 , Processed in 1.462089 second(s), Total 77, Slave 58 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号