DS3231时钟模块使用,IIC协议实践。(基于STM32)

   日期:2021-03-22     浏览:102    评论:0    
核心提示:写在前面因为毕业设计,需要用到记录时间的功能,对时钟模块一直只闻其名,却从未用过。之前的在使用其他模块的时候,总是一个劲的拿来主义,经常是在别人现成的代码上改改宏定义就直接进行使用,一旦项目需要用到的模块多了起来,每个模块别人写好一个库,在不清楚其基本原理的情况下整个项目就变得臃肿不堪了。(据我所知很多同学本科阶段使用模块都是这样一个模式)。那么现在在刚回顾和复习完IIC通讯协议下,在这里我将IIC协议实践在DS3231时钟模块上。文件结构,方便扩展 和 代码管理。这里建立两个库: software

文章目录

  • 写在前面
  • 文件结构,方便扩展 和 代码管理。
  • 关于DS3231
    • DS3231 寄存器设置
  • 代码
    • `softwareIIC.h`
    • `software.c`
    • `DS3231.h`
    • `DS3231.c`
  • 总结不易,若对你有帮助,希望点赞收藏是

写在前面

因为毕业设计,需要用到记录时间的功能,对时钟模块一直只闻其名,却从未用过。之前的在使用其他模块的时候,总是一个劲的拿来主义,经常是在别人现成的代码上改改宏定义就直接进行使用,一旦项目需要用到的模块多了起来,每个模块别人写好一个库,在不清楚其基本原理的情况下整个项目就变得臃肿不堪了。(据我所知很多同学本科阶段使用模块都是这样一个模式)。
那么现在在刚回顾和复习完IIC通讯协议下,在这里我将IIC协议实践在DS3231时钟模块上。

文件结构,方便扩展 和 代码管理。

这里建立两个库: softwareIIC.hDS3231.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;
}

总结不易,若对你有帮助,希望点赞收藏是

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
更多>相关资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服