文章目录
- 写在前面
- 文件结构,方便扩展 和 代码管理。
- 关于DS3231
-
- DS3231 寄存器设置
- 代码
-
- `softwareIIC.h`
- `software.c`
- `DS3231.h`
- `DS3231.c`
- 总结不易,若对你有帮助,希望点赞收藏是
写在前面
因为毕业设计,需要用到记录时间的功能,对时钟模块一直只闻其名,却从未用过。之前的在使用其他模块的时候,总是一个劲的拿来主义,经常是在别人现成的代码上改改宏定义就直接进行使用,一旦项目需要用到的模块多了起来,每个模块别人写好一个库,在不清楚其基本原理的情况下整个项目就变得臃肿不堪了。(据我所知很多同学本科阶段使用模块都是这样一个模式)。
那么现在在刚回顾和复习完IIC通讯协议下,在这里我将IIC协议实践在DS3231时钟模块上。
文件结构,方便扩展 和 代码管理。
这里建立两个库: softwareIIC.h
和DS3231.h
DS3231.h
即存储DS3231的寄存器定义,时间变量结构体定义,等等。
softwareIIC.h
即存储相关IIC的函数定义,软件 IIC 占用的 IO口定义,等等。
在使用上,只需要在DS3231.c中 #include "softwareIIC.h"
即可。
同理,因为我们将模块和 IIC 协议进行了分离, 在项目中需要加到其他使用IIC协议的模块时,也只需要在对应模块的文件中 调用软件IIC的头文件 即可。
那么在之后其他项目也可以使用这个时候写好的 软件IIC 库, 只需要改改softwareIIC.h 里对于占用端口的宏定义即可, 十分方便代码的移植。
若你是初学者(虽然我也是),希望你能养成这样的一个模式、习惯,这对之后的帮助异常的大。
关于DS3231
DS3231是低成本、高精度的I2C实时时钟(RTC)。该器件包括电池输入端,断开主电源时,仍可保持精准计时。
RTC保存秒、分、时、星期、月和年信息。少于31天的月份,将自动调整月末的日期,包括闰年修正。时钟格式可以时24小时或带AM/PM的12小时格式。提供两个可设置的日历闹钟和1Hz输出。
在这里我用的是在某宝上购买的模块,已经预留出了VCC、GND、SDA、SCL。
DS3231 寄存器设置
第一眼看上去可能有点不明所以,但其实很简单。DS3231采用8421BCD码用来存储时间、日期等数据。(什么是8421BCD码? 你可以简单理解为,一般用四位二进制表示一位数字,比如十进制数字58,对应的8421BCD码即:0101 1000 , 这里1000代表个位数的8 , 0101代表十位数的 5 。)
比如Seconds(秒),寄存器地址是0x00,我们需要读取和设置 秒 ,也只需要对这个寄存器进行读写即可。
秒范围是 00 - 59,故第四位用来存储个位数0 - 9,高三位用来存储十位数 0 - 5 ( 000 - 101)。
其他寄存器同理。
另外关于采用 24小时制 还是 12小时制度,取决于Hours寄存器(0x02)中的第6位。
代码
欧克,关于IIC 借助我上一篇博客相信已经有所掌握,现在我们直接将其使用在和DS3231的通讯和设置中。
softwareIIC.h
#ifndef ___SOFTWARE_IIC
#define ___SOFTWARE_IIC
#include "stm32f10x.h"
#include "./sys/sys.h"
#define IIC_SDA_GPIOx GPIOA
#define IIC_SDA_GPIO_Pin GPIO_Pin_11
#define IIC_SDA_GPIO_RCC RCC_APB2Periph_GPIOA
#define IIC_SDA_RCCPeriphClockcmd RCC_APB2PeriphClockCmd
#define IIC_SCL_GPIOx GPIOA
#define IIC_SCL_GPIO_Pin GPIO_Pin_12
#define IIC_SCL_GPIO_RCC RCC_APB2Periph_GPIOA //上面已经定义过了
#define IIC_SCL_RCCPeriphClockcmd RCC_APB2PeriphClockCmd
#define IIC_SCLSDA_GPIO_RCC RCC_APB2Periph_GPIOA
//使用4线串行接口时使用
#define IIC_SDA PAout(11)
#define IIC_SCL PAout(12)
#define READ_SDA GPIO_ReadInputDataBit(IIC_SDA_GPIOx,IIC_SDA_GPIO_Pin)
void IIC_Init(void);
void IIC_Start(void); //产生起始条件(符)
void IIC_Stop(void); //产生结束条件(符)
uint8_t IIC_Wait_Ask(void);
void IIC_Ack(void);
void IIC_NAck(void);
void IIC_WriteByte(u8 data);
u8 IIC_Read_Byte(unsigned char ack);
#endif // ___SOFTWARE_IIC
software.c
#include "./softwareIIC/softwareIIC.h"
#include "./systick/bsp_systick.h"
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
//开启时钟
IIC_SDA_RCCPeriphClockcmd(IIC_SDA_GPIO_RCC, ENABLE); //or IIC_SCL_GPIOx ,因为 sda scl 使用的是同一个gpiox
//定义pin
GPIO_InitStructer.GPIO_Pin=IIC_SDA_GPIO_Pin | IIC_SCL_GPIO_Pin; //10--SCL 11--SDA //PB10 PB11
//定义频率
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
//定义IO模式
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
//初始化
GPIO_Init(IIC_SDA_GPIOx, &GPIO_InitStructer);
}
static void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
GPIO_InitStructer.GPIO_Pin= IIC_SDA_GPIO_Pin;
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出模式
GPIO_Init(IIC_SDA_GPIOx, &GPIO_InitStructer);
}
static void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
GPIO_InitStructer.GPIO_Pin= IIC_SDA_GPIO_Pin;
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IPU; //上拉输入模式
GPIO_Init(IIC_SDA_GPIOx, &GPIO_InitStructer);
}
void IIC_Start(void) //产生起始条件(符)
{
SDA_OUT();
IIC_SDA=1; //先确保 SDA为高
IIC_SCL=1; //拉高SCL
SysTick_Delay_us(4);
IIC_SDA=0; //产生下降沿
SysTick_Delay_us(4);
IIC_SCL=0; //将时钟线拉低,只有时钟线拉低才运行SDA数据变化有效
SysTick_Delay_us(4);
}
void IIC_Stop(void) //产生结束条件(符)
{
SDA_OUT();
IIC_SCL = 0;
IIC_SDA=0; //先让SDA 为低电平
IIC_SCL=1; //将SCL拉高
SysTick_Delay_us(4); // 延时,保证时长
IIC_SDA=1; //SDA拉高 产生上升沿
SysTick_Delay_us(4); // 结束后释放SDA , SDA变高
}
// uint8_t IIC_Wait_Ask(void)
//{
// SDA_IN();
// IIC_SCL=1;
// SysTick_Delay_us(4);
// IIC_SCL=0;
// SysTick_Delay_us(4);
// return 0;
//}
uint8_t IIC_Wait_Ask(void)
{
uint16_t tempTime = 0;
SDA_IN();
IIC_SDA = 1; //释放数据总线,交由从机控制
SysTick_Delay_us(4);
IIC_SCL = 1;
SysTick_Delay_us(1);
while (GPIO_ReadInputDataBit(IIC_SDA_GPIOx,IIC_SDA_GPIO_Pin)) //读到 0 ,即接收到ACK,循环跳出
{
tempTime++;
if(tempTime > 300)
{
IIC_Stop();
return 1; //超时返回1
}
}
IIC_SCL = 0;
return 0; //接收到 ACK 返回0
}
void IIC_Ack(void)
{
IIC_SCL = 0 ;
SDA_OUT();
IIC_SDA = 0;
SysTick_Delay_us(2);
IIC_SCL = 1;
SysTick_Delay_us(5);
IIC_SCL = 0;
}
//主机不产生应答信号NACK
void IIC_NAck(void)
{
IIC_SCL = 0;
SDA_OUT();
IIC_SDA = 1;
SysTick_Delay_us(2);
IIC_SCL = 1;
SysTick_Delay_us(5);
IIC_SCL = 0;
}
void IIC_WriteByte(u8 data)
{
u8 i;
SDA_OUT();
for(i=0;i<8;i++)
{
IIC_SCL=0;
SysTick_Delay_us(4);
if(data & 0x80)
IIC_SDA=1;
else
IIC_SDA=0;
IIC_SCL=1;
SysTick_Delay_us(4);
IIC_SCL=0;
data<<=1;
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
SysTick_Delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)
receive++;
SysTick_Delay_us(2);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
上面是软件IIC的代码,是对于IIC总线的操作,不具体针对某一个模块,可移植性高。
DS3231.h
#ifndef DS3231_H
#define DS3231_H
#include <stm32f10x.h>
#define DS3231_ADDRESS 0x68 //I2C Slave address
#define DS3231_ADDRESS_Write 0xD0
#define DS3231_ADDRESS_Read 0xD1
#define DS3231_SEC_REG 0x00 // 秒
#define DS3231_MIN_REG 0x01 //
#define DS3231_HOUR_REG 0x02
#define DS3231_WDAY_REG 0x03
#define DS3231_MDAY_REG 0x04
#define DS3231_MONTH_REG 0x05
#define DS3231_YEAR_REG 0x06
#define DS3231_AL1SEC_REG 0x07
#define DS3231_AL1MIN_REG 0x08
#define DS3231_AL1HOUR_REG 0x09
#define DS3231_AL1WDAY_REG 0x0A
#define DS3231_AL2MIN_REG 0x0B
#define DS3231_AL2HOUR_REG 0x0C
#define DS3231_AL2WDAY_REG 0x0D
#define DS3231_CONTROL_REG 0x0E
#define DS3231_STATUS_REG 0x0F
#define DS3231_AGING_OFFSET_REG 0x0F
#define DS3231_TMP_UP_REG 0x11
#define DS3231_TMP_LOW_REG 0x12
#define EverySecond 0x01
#define EveryMinute 0x02
#define EveryHour 0x03
typedef struct DateTImeStruct{
uint8_t second;
uint8_t minute;
uint8_t hour;
uint8_t dayofmonth;
uint8_t month;
uint16_t year;
uint8_t dayOfWeek;
}DateTime;
uint8_t DS3231_setDate(uint8_t year,uint8_t mon,uint8_t day);
uint8_t DS3231_setTime(uint8_t hour , uint8_t min , uint8_t sec);
uint8_t DS3231_getdate(DateTime* ans);
uint8_t DS3231_gettime(DateTime* ans);
#endif
DS3231.c
#include "./DS3231/DS3231.h"
#include "./softwareIIC/softwareIIC.h"
#include "./usart/bsp_usart.h"
void DS3231_Init(void){
IIC_Init();
}
uint8_t IIC_DS3231_ByteWrite(uint8_t WriteAddr , uint8_t date)
{
IIC_Start();
IIC_WriteByte(DS3231_ADDRESS_Write);
if(IIC_Wait_Ask())
return 1;
IIC_WriteByte(WriteAddr);
if(IIC_Wait_Ask())
return 2;
IIC_WriteByte(date);
if(IIC_Wait_Ask())
return 3;
IIC_Stop();
return 0;
}
uint8_t IIC_DS3231_ByteRead(uint8_t ReadAddr,uint8_t* Receive)
{
uint8_t data = 0;
IIC_Start(); //产生起始位
IIC_WriteByte(DS3231_ADDRESS_Write); //发送从机地址(写模式)
if(IIC_Wait_Ask()) //等待响应
return 1;
IIC_WriteByte(ReadAddr); //发送寄存器地址
if(IIC_Wait_Ask()) //等待响应
return 2;
IIC_Start(); //重复起始位
IIC_WriteByte(DS3231_ADDRESS_Read); //发送从机地址(读模式)
if(IIC_Wait_Ask()) //等待响应
return 3;
data = IIC_Read_Byte(0); //读取数据,参数设为0 --- NACK
*Receive = data; //将结果赋值给接收位
IIC_Stop();
return 0;
}
uint8_t DS3231_setDate(uint8_t year,uint8_t mon,uint8_t day)
{
uint8_t temp_H , temp_L;
temp_L = year%10;
temp_H = year/10;
year = (temp_H << 4) + temp_L;
if(IIC_DS3231_ByteWrite(DS3231_YEAR_REG,year)) //set year
{
printf("set year error\r\n");
return 1;
}
temp_L = mon%10;
temp_H = mon/10;
mon = (temp_H << 4) + temp_L;
if(IIC_DS3231_ByteWrite(DS3231_MONTH_REG,mon)) //set mon
{
printf("set month error\r\n");
return 2;
}
temp_L = day%10;
temp_H = day/10;
day = (temp_H << 4) + temp_L;
if(IIC_DS3231_ByteWrite(DS3231_MDAY_REG,day)) //set day
{
printf("set day error\r\n");
return 3;
}
return 0;
}
uint8_t DS3231_setTime(uint8_t hour , uint8_t min , uint8_t sec)
{
uint8_t temp_H , temp_L;
temp_L = hour%10;
temp_H = hour/10;
hour = (temp_H << 4) + temp_L;
if(IIC_DS3231_ByteWrite(DS3231_HOUR_REG,hour)) //set hour
return 1;
temp_L = min%10;
temp_H = min/10;
min = (temp_H << 4) + temp_L;
if(IIC_DS3231_ByteWrite(DS3231_MIN_REG,min)) //SET min
return 2;
temp_L = sec%10;
temp_H = sec/10;
sec = (temp_H << 4) + temp_L;
if(IIC_DS3231_ByteWrite(DS3231_SEC_REG,sec)) //SET sec
return 3;
return 0;
}
static uint8_t bcdToDec(uint8_t byte)
{
uint8_t temp_H , temp_L;
temp_L = byte & 0x0f;
temp_H = (byte & 0xf0) >> 4;
return ( temp_H * 10 )+ temp_L;
}
uint8_t DS3231_gettime(DateTime* ans)
{
uint8_t receive = 0;
if(IIC_DS3231_ByteRead(DS3231_HOUR_REG,&receive))
return 1;
ans->hour = bcdToDec(receive);
if(IIC_DS3231_ByteRead(DS3231_MIN_REG,&receive))
return 2;
ans->minute = bcdToDec(receive);
if(IIC_DS3231_ByteRead(DS3231_SEC_REG,&receive))
return 3;
ans->second = bcdToDec(receive);
return 0;
}
uint8_t DS3231_getdate(DateTime* ans)
{
uint8_t receive = 0;
if(IIC_DS3231_ByteRead(DS3231_YEAR_REG,&receive))
return 1;
ans->year = bcdToDec(receive) + 2000;
if(IIC_DS3231_ByteRead(DS3231_MONTH_REG,&receive))
return 2;
ans->month = bcdToDec(receive);
if(IIC_DS3231_ByteRead(DS3231_MDAY_REG,&receive))
return 3;
ans->dayofmonth = bcdToDec(receive);
return 0;
}