2、初始化与函数/**
******************************************************************************
* @file bsp_i2c_ds3231.c
* @author 兲涳
* @version V1.0
* @date 2020-11-16
* @brief i2c RTC(DS3231)应用函数bsp
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "bsp_i2c_ds3231.h"
/* Private typedef -----------------------------------------------------------*/
_calendar_obj calendar;
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static __IO uint32_t I2CTimeout = I2CT_LONG_TIMEOUT;
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode);
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
* @brief 转换成BCD码
* @param 无
* @retval 无
*/
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 无
* @retval 无
*/
static void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能与 I2C 有关的时钟 */
DS3231_I2C_APBxClock_FUN ( DS3231_I2C_CLK, ENABLE );
DS3231_I2C_GPIO_APBxClock_FUN ( DS3231_I2C_GPIO_CLK, ENABLE );
/* I2C_SCL、I2C_SDA*/
GPIO_InitStructure.GPIO_Pin = DS3231_I2C_SCL_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏复用输出
GPIO_Init(DS3231_I2C_SCL_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DS3231_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏复用输出
GPIO_Init(DS3231_I2C_SDA_PORT, &GPIO_InitStructure);
}
/**
* @brief I2C 工作模式配置
* @param 无
* @retval 无
*/
static void I2C_Mode_Configu(void)
{
I2C_InitTypeDef I2C_InitStructure;
/* 通信速率 */
I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
/* I2C 配置 */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
/* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
/* 地址设置 */
I2C_InitStructure.I2C_OwnAddress1 = I2Cx_OWN_ADDRESS7;
/* 使能应答 */
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
/* I2C的寻址模式 */
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
/* I2C 初始化 */
I2C_Init(DS3231_I2Cx, &I2C_InitStructure);
/* 使能 I2C */
I2C_Cmd(DS3231_I2Cx, ENABLE);
}
/**
* @brief I2C 外设(DS3231)初始化
* @param 无
* @retval 无
*/
void I2C_DS3231_Init(void)
{
I2C_GPIO_Config();
I2C_Mode_Configu();
}
/**
* @brief 写一个字节到I2C DS3231中
* @param
* @ARG data:要写入的字节
* @arg WriteAddr:写地址
* @retval 返回1,表示写入成功.
*/
uint32_t I2C_DS3231_ByteWrite(u8 WriteAddr, u8 data)
{
/* 产生 I2C 起始信号 */
I2C_GenerateSTART(DS3231_I2Cx, ENABLE);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV5 事件并清除标志*/
while (!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 发送 DS3231 设备地址 */
I2C_Send7bitAddress(DS3231_I2Cx, DS3231_ADDRESS, I2C_Direction_Transmitter);
/* 检测 EV6 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
}
/* 发送要写入的 DS3231 内部地址(即 DS3231 内部存储器的地址) */
I2C_SendData(DS3231_I2Cx, WriteAddr);
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV8 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
}
/* 发送一字节要写入的数据 */
I2C_SendData(DS3231_I2Cx, data);
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV8 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
/* 发送停止信号 */
I2C_GenerateSTOP(DS3231_I2Cx, ENABLE);
return 1;
}
/**
* @brief 从DS3231里面读取一个字节数据
* @param
* @arg data:存放从DS3231读取的数据
* @arg ReadAddr:读取数据的DS3231的地址
* @retval data:返回数据.
*/
uint8_t I2C_DS3231_DataRead(u8 ReadAddr)
{
uint8_t data;
I2CTimeout = I2CT_LONG_TIMEOUT;
/* 等待空闲 */
while(I2C_GetFlagStatus(DS3231_I2Cx, I2C_FLAG_BUSY))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
}
/* 产生 I2C 起始信号 */
I2C_GenerateSTART(DS3231_I2Cx, ENABLE);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV5 事件并清除标志*/
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
}
/* 发送 DS3231 设备地址,写 */
I2C_Send7bitAddress(DS3231_I2Cx, DS3231_ADDRESS, I2C_Direction_Transmitter);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV6 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);
}
/* 通过重新设置 PE 位清除 EV6 事件 */
I2C_Cmd(DS3231_I2Cx, ENABLE);
/* 发送要读取的 DS3231 内部地址(即 DS3231 内部存储器的地址) */
I2C_SendData(DS3231_I2Cx, ReadAddr);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV8 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
}
/* 产生第二次 I2C 起始信号 */
I2C_GenerateSTART(DS3231_I2Cx, ENABLE);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV5 事件并清除标志*/
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
}
/* 发送 DS3231 设备地址,读 */
I2C_Send7bitAddress(DS3231_I2Cx, DS3231_ADDRESS, I2C_Direction_Receiver);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV6 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);
}
/* 发送非应答信号 */
I2C_AcknowledgeConfig(DS3231_I2Cx, DISABLE);
/* 发送停止信号 */
I2C_GenerateSTOP(DS3231_I2Cx, ENABLE);
/* 检测 EV7 事件并清除标志 */
I2CTimeout = I2CT_LONG_TIMEOUT;
while(I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
/* 通过 I2C,从设备中读取一个字节的数据 */
data = I2C_ReceiveData(DS3231_I2Cx);
/* 使能应答,方便下一次 I2C 传输 */
I2C_AcknowledgeConfig(DS3231_I2Cx, ENABLE);
return data;
}
/**
* @brief 等待 DS3231 到准备状态
* @param 无
* @retval 无
*/
void I2C_WaitDs3231StandbyState(void)
{
vu16 SR1_Tmp = 0;
do
{
/* 发送起始信号 */
I2C_GenerateSTART(DS3231_I2Cx, ENABLE);
/* 读 I2C1 SR1 寄存器 */
SR1_Tmp = I2C_ReadRegister(DS3231_I2Cx, I2C_Register_SR1);
/* 发送 DS3231 地址 + 方向 */
I2C_Send7bitAddress(DS3231_I2Cx, DS3231_ADDRESS, I2C_Direction_Transmitter);
}while(!(I2C_ReadRegister(DS3231_I2Cx, I2C_Register_SR1) & 0x0002));
/* 清除 AF 位 */
I2C_ClearFlag(DS3231_I2Cx, I2C_FLAG_AF);
/* 发送停止信号 */
I2C_GenerateSTOP(DS3231_I2Cx, ENABLE);
}
/**
* @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 errorCode:错误代码,可以用来定位是哪个环节出错.
* @retval 返回0,表示IIC读取失败.
*/
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
{
/* 使用串口 printf 输出错误信息,方便调试 */
DS3231_ERROR("I2C 等待超时!errorCode = %d",errorCode);
return 0;
}
/**
* @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;
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);
calendar.month=I2C_DS3231_DataRead(0x05);
calendar.month=BCD_DEC(calendar.month);
calendar.date=I2C_DS3231_DataRead(0x04);
calendar.date=BCD_DEC(calendar.date);
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("\r\n=========================设置时间==================");
do
{
printf("\r\n 请输入年份(Please Set Years),范围[2000~2255],输入字符后请加回车:");
scanf("%d",&temp_num);
if(temp_num <2000 || temp_num >65535)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 年份被设置为: %d\n\r", temp_num);
temp_num-=2000;
DEC_BCD(temp_num);
printf("\n\r 年份被设置为11: %d\n\r", temp_num);
tm->year = temp_num;
printf("year = %d %d\n",calendar.year,temp_num);
break;
}
}while(1);
do
{
printf("\r\n 请输入月份(Please Set Months):范围[1~12],输入字符后请加回车:");
scanf("%d",&temp_num);
if(temp_num <1 || temp_num >12)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 月份被设置为: %d\n\r", 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("\r\n 请输入日期(Please Set Months),范围[1~%d],输入字符后请加回车:",day_max);
scanf("%d",&temp_num);
if(temp_num <1 || temp_num >day_max)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 日期被设置为: %d\n\r", temp_num);
DEC_BCD(temp_num);
tm->date = temp_num;
break;
}
}while(1);
GregorianDay( tm );
do
{
printf("\r\n 请输入时钟(Please Set Hours),范围[0~23],输入字符后请加回车:");
scanf("%d",&temp_num);
if( temp_num >23)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 时钟被设置为: %d\n\r", temp_num);
DEC_BCD(temp_num);
tm->hour = temp_num;
break;
}
}while(1);
do
{
printf("\r\n 请输入分钟(Please Set Minutes),范围[0~59],输入字符后请加回车:");
scanf("%d",&temp_num);
if( temp_num >59)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 分钟被设置为: %d\n\r", temp_num);
DEC_BCD(temp_num);
tm->min = temp_num;
break;
}
}while(1);
do
{
printf("\r\n 请输入秒钟(Please Set Seconds),范围[0~59],输入字符后请加回车:");
scanf("%d",&temp_num);
if( temp_num >59)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 秒钟被设置为: %d\n\r", temp_num);
DEC_BCD(temp_num);
tm->sec = temp_num;
break;
}
}while(1);
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
}
/**
* @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->date; /*计算从公元元年元旦到计数日期一共有多少天*/
tm->week=day%7; //算出星期
}
/*********************************************END OF FILE**********************/ |