HAL库软件IIC读取DS3231

本文介绍了在STM32F1平台中使用DS3231实时时钟芯片的初始化过程,包括设置寄存器、延时函数的选择以及时间设置方法。着重讲解了如何通过软件IIC进行通信,并讨论了在中断中使用延时函数的最佳实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每次上电时都要运行下面两个函数来对DS3231进行初始化:

DS3231_WriteReg(DS3231_CONTROL,0x00);  //将DS3231设置为想要的状态

DS3231_WriteReg(DS3231_STATUS ,0x00);  //将DS3231设置为想要的状态

延时函数采用指令延时而不是systick延时,这样软件IIC和延时函数就可以在中断中运行,如果是systick延时的话那么systick优先级要大于中断,而且就算这样也可能出现一些奇怪的问题,所有如果要在中断中延时且对时间要求没那么严格,个人还是推荐指令延时:

void Delay_Ms(uint16_t ms){
	while(ms--){
		Delay_Us(1000);
	}
}



//
void  Delay_Us(uint16_t us)
{
  __IO uint32_t Delay = us * 64 / 8;//(SystemCoreClock / 8U / 1000000U)
    //见stm32f1xx_hal_rcc.c -- static void RCC_Delay(uint32_t mdelay)
  do
  {
    __NOP();
  }
  while (Delay --);

}

 DS3231驱动的具体C文件和头文件如下,软件IIC实现可参考我的上一篇博客。

DS3231.c:

#include "DS3231.h"
#include "Soft_IIC.h"
#include "Delay.h"
#include "AT24C32.h"
#include "74HC595.h"
#include "iwdg.h"

_calendar_obj calendar; //时钟结构体 

//DS3231时钟中断
//每秒触发一次  	 
void  HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == DS3231_Pin)
	{
		UNUSED(GPIO_Pin);
		
		I2C_DS3231_getTime();   	//更新时间   
	}
	
}



uint8_t DS3231_WriteReg(uint8_t RegAddress, uint8_t Data)  //指定地址写寄存器 (写一个字节)
{
	
	//先发送从机地址(点名从机DS3231_ADDRESS),再发送寄存器地址(点名寄存器),再发送数据()写入从机寄存器
	//HAL_StatusTypeDef hal_state = HAL_I2C_Mem_Write(&hi2c1, DS3231_ADDRESS_write, RegAddress, I2C_MEMADD_SIZE_8BIT, a, 1, 1000);  //1000代表1000ms
	
	/* 1、发送起始信号 */
	iic_start();
	/* 2、发送通讯地址(写操作地址) */
	iic_send_byte(DS3231_ADDRESS_write);
	/* 3、等待应答信号 */
	 if(iic_wait_ack() != 1)  return 1;
	/* 4、发送寄存器地址:0~256 */
	iic_send_byte(RegAddress);
	
	/* 5、等待应答信号 */
	 if(iic_wait_ack() != 1)  return 2;
	/* 6、发送写入数据 */
	iic_send_byte(Data);
	/* 7、等待应答信号 */
	 if(iic_wait_ack() != 1)  return 3;
	/* 8、发送停止信号 */
	iic_stop();
	
  return 0;
	
}  
 
 
  
uint8_t DS3231_ReadReg(uint8_t RegAddress)   //指定地址读寄存器 (读一个字节)
{
	uint8_t Data;

	//HAL_StatusTypeDef hal_state = HAL_I2C_Mem_Read(&hi2c1, DS3231_ADDRESS_read, RegAddress, I2C_MEMADD_SIZE_8BIT, &Data, 1, 1000);
	
    /* 1、发送起始信号 */
    iic_start();
    /* 2、发送通讯地址(写操作地址) */
    iic_send_byte(DS3231_ADDRESS_write);
    /* 3、等待应答信号 */
    if(iic_wait_ack() != 1)  return 1;
	
    /* 4、发送寄存器地址:0~256 */
		iic_send_byte(RegAddress);     //发送地址
	
    /* 5、等待应答信号 */
    if(iic_wait_ack() != 1)  return 2;
    /* 6、发送起始信号 */
    iic_start();
    /* 7、发送通讯地址(读操作地址) */
    iic_send_byte(DS3231_ADDRESS_read );
    /* 8、等待应答信号 */
    if(iic_wait_ack() != 1)  return 3;
    /* 9、接收数据并发送非应答(获取该地址即可) */
    Data = iic_read_byte(0);
    /* 10、发送停止信号 */
    iic_stop();

	
	  return Data;
}
 

/**
  * @brief	BCD(8421)转DEC.//二进制转十进制
  * @param  val:BCD码.
  * @retval i:DEC码.
  */
uint8_t BCD_DEC(uint8_t val)
{
	uint8_t i;
	i= val&0x0f;
	val >>= 4;
	val &= 0x0f;
	val *= 10;
	i += val;    
	return i;
}
// 
/**
  * @brief	DEC转BCD(8421).
  * @param  val:DEC码.
  * @retval k:BCD码.
  */
uint8_t DEC_BCD(uint8_t val)
{
  uint8_t i,j,k;
  i=val/10;
  j=val%10;
  k=j+(i<<4);
  return k;
}
 


/**
  * @brief	时间设置
  * @param   
  *		@arg 	分别输入 年 月 日 星期 时 分 秒
  * @retval 无
  */
//uint8_t century __attribute__((at(0x0801F000)))= 0;//将阴极中毒的数字定位在flash中
void I2C_DS3231_SetTime(uint16_t yea,uint8_t mon,uint8_t da, uint8_t hou,uint8_t min,uint8_t sec)
{
	// uint8_t   yea = 0x23;//23年 0010 0011
//uint8_t	mon = 0x10;//10月 0001 0000
//uint8_t	da = 0x13;//13日 0001 0011
//uint8_t	we = 0x06;//周6 0000 0110
//uint8_t	hou = 0x08; //8时 0000 1000
//uint8_t	min = 0x08;//8分
//uint8_t	sec = 0x08;//8秒
	calendar.year = yea;
	calendar.century = yea/100;
	at24c32_write_one_byte(year_address, calendar.century);   //由于DS3231只能保存年份的后两位,无法保存世纪,
	                                                    //因此设置时间时需要人为设置世纪,然后将其写入EEprom,每次开机再读取世纪
	calendar.month = mon;
	calendar.date = da;
	calendar.hour = hou;
	calendar.min = min;
	calendar.sec = sec;
	calendar.week = GregorianDay(calendar);
	
	while(!((DS3231_ReadReg(DS3231_STATUS)|0x00) == 0x00))  //判断DS3231是否准备好
	{
	}
	//HAL_Delay(5);
	DS3231_WriteReg(DS3231_CONTROL,0x00);
	//HAL_Delay(5);
  DS3231_WriteReg(0x06,DEC_BCD(yea%100));  //年输入格式为0-99
	//HAL_Delay(5);
  DS3231_WriteReg(0x05,DEC_BCD(mon));
	//HAL_Delay(5);
  DS3231_WriteReg(0x04,DEC_BCD(da));
	//HAL_Delay(5);
  DS3231_WriteReg(0x03,DEC_BCD(calendar.week));
	//HAL_Delay(5);
  DS3231_WriteReg(0x02,DEC_BCD(hou));   //24小时制
	//HAL_Delay(5);
  DS3231_WriteReg(0x01,DEC_BCD(min));
	//HAL_Delay(5);
  DS3231_WriteReg(0x00,DEC_BCD(sec));
}	
 
/**
  * @brief	获取时间
  * @param   
  *		@arg pBuffer:存放从DS3231读取的数据的缓冲区指针
  *		@arg ReadAddr:读取数据的DS3231的地址
  *   @arg NumByteToWrite:要从DS3231读取的字节数
  * @retval 返回1,表示读取成功.
  */

int a = 0;
void I2C_DS3231_getTime(void)
{
	
	a = DS3231_ReadReg(DS3231_STATUS);
	while(!((DS3231_ReadReg(DS3231_STATUS)|0x00) == 0x00))  //判断DS3231是否准备好
	{
	}
	calendar.year= calendar.century*100 + BCD_DEC(DS3231_ReadReg(0x06));   //将EEPROM中的世纪和DS3231中的年份相加获得完整年份
	calendar.month= BCD_DEC(DS3231_ReadReg(0x05)); 
	calendar.date= BCD_DEC(DS3231_ReadReg(0x04));  
	calendar.week= BCD_DEC(DS3231_ReadReg(0x03));  
	calendar.hour= BCD_DEC(DS3231_ReadReg(0x02)&0x3f);  //排除12/24小时制变化对hour寄存器值的影响   
	calendar.min= BCD_DEC(DS3231_ReadReg(0x01));
	calendar.sec= BCD_DEC(DS3231_ReadReg(0x00));
}
 
 
 
/**
  * @brief	获取温度
  * @param  无
  * @retval 无
  */
void I2C_DS3231_getTemperature(void)
{
	while(!((DS3231_ReadReg(DS3231_STATUS)|0x00) == 0x00))  //判断DS3231是否准备好
	{
	}
	DS3231_WriteReg(DS3231_CONTROL, 0x20);//主动使能温度转换
	calendar.temperature=DS3231_ReadReg(DS3231_TEMPERATUREH);
}
 

/*计算公历天数得出星期*/
int 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.date; /*计算从公元元年元旦到计数日期一共有多少天*/
 
	tm.week=day%7; //算出星期
	if(tm.week == 0) 
		tm.week = 7;
	return tm.week;
}
 


DS3231.h:头文件

#ifndef __DS3231_H__
#define __DS3231_H__

#include "usart.h"
#include "stm32g0xx_hal.h"




/* DS3231 地址定义 */
#define DS3231_ADDRESS_write 		  0xD0   
#define DS3231_ADDRESS_read 		  0xD1 



/*信息输出*/
#define DS3231_DEBUG_ON         0
 
#define DS3231_INFO(fmt,arg...)           printf("<<-DS3231-INFO->> "fmt"\n",##arg)
#define DS3231_ERROR(fmt,arg...)          printf("<<-DS3231-ERROR->> "fmt"\n",##arg)
#define DS3231_DEBUG(fmt,arg...)          do{\
                                          if(DS3231_DEBUG_ON)\
                                          printf("<<-DS3231-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)
 
/* 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    //年

																			
#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位) 						

typedef struct 
{
	int hour;
	int min;
	int sec;			
	uint16_t year;
	uint8_t century;
	int month;
	int date;
	int week;
	int temperature;
	
}_calendar_obj;	      //定义DS3231中的时间结构体

extern _calendar_obj calendar;	//日历结构体

//void DS3231_Init(void);																																									
uint8_t DS3231_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t DS3231_ReadReg(uint8_t RegAddress);
void I2C_WaitDs3231StandbyState(void);  																				
uint8_t BCD_DEC(uint8_t val);		
uint8_t DEC_BCD(uint8_t val);
 
void I2C_DS3231_SetTime(uint16_t yea,uint8_t mon,uint8_t da,uint8_t hou,uint8_t min,uint8_t sec);
 
void I2C_DS3231_getTime(void);
void I2C_DS3231_getTemperature(void);
int GregorianDay(_calendar_obj tm);

#endif


参考链接:

DS3231 DS Rev8.C (analog.com)

时钟传感器—DS3231-CSDN博客

### 使用STM32 HAL 通过IIC接口读取DS18B20温度传感器数据 #### 准备工作 为了实现这一目标,需准备好如下硬件设备[^1]: - STM32开发板(例如:STM32F4 Discovery) - 温度传感器(如DS18B20) 值得注意的是,虽然提到的代码片段展示了如何向一个设备写入数据[^2],但是针对DS18B20的操作有所不同,因为该传感器通常采用单总线协议而非IIC。然而,如果确实存在一种型号支持IIC,则下面的过程适用于任何基于IIC的温度传感器。 #### 初始化配置 利用STM32CubeMX工具来初始化项目设置,确保启用了I2C外设并选择了相应的GPIO引脚作为SCL和SDA线路。完成这些步骤之后,在生成的代码框架内可以找到`MX_I2C1_Init()`函数用于初始化I2C模块。 #### 编程实例 下面是使用HAL操作I2C的具体编程方法: ```c #include "stm32f4xx_hal.h" // 定义全局变量存储接收到的数据 uint8_t rxData[2]; // 假定每次接收两个字节的数据 void ReadTemperature(void){ uint8_t txBuffer[] = {0}; // 发送命令缓冲区, DS18B20可能不需要额外指令 // 启动转换 (对于某些类型的传感器可能是必需的) // 开始传输 HAL_StatusTypeDef status; status = HAL_I2C_Master_Transmit(&hi2c1, SENSOR_ADDRESS << 1, txBuffer, sizeof(txBuffer), HAL_MAX_DELAY); if(status != HAL_OK){ Error_Handler(); } // 接收来自传感器的数据 status = HAL_I2C_Master_Receive(&hi2c1, SENSOR_ADDRESS << 1, rxData, sizeof(rxData), HAL_MAX_DELAY); if(status != HAL_OK){ Error_Handler(); } // 处理接收到的数据... } ``` 上述代码中假设了`SENSOR_ADDRESS`代表实际使用的I2C地址,并且这里简化处理忽略了启动测量过程以及具体解析温度数值的部分逻辑。对于真正的应用来说,还需要考虑更多细节比如错误检测机制等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值