栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

华大单片机 HC32F460 驱动BM8563ESA RTC芯片

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

华大单片机 HC32F460 驱动BM8563ESA RTC芯片

前言

因华大单片机没有单独VBAT管脚,无法使用,如果用单片机自带的RTC模块,系统断电后时间无法准确,需要重新设置,影响用户体验,说以系统加入单独的RTC芯片.

RTC时钟电路

单片机管脚定义

RTC芯片为 [BL(上海贝岭) 的 BM8563ESA 实时时钟RTC

BCD码 百度百科解释 BCD码(Binary-Coded Decimal‎),用4位二进制数来表示1位十进制数中的0~9这10个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。BCD码这种编码形式利用了四个位元来储存一个十进制的数码,使二进制和十进制之间的转换得以快捷的进行。 我的简单解释: 比如说02H地址寄存器中的数据表示秒 Bit7 是无效的 ,Bit0–Bit 6为有效的 . 0x00 至 0x59 ,0x00表示0秒 0x59是16进制数据,但是他表示59秒.程序中需要转换. BM8563ESA驱动程序编写 BM8563ESA 是I2C驱动,标准的I2C驱动可以使用
#include "drvs.h"
//时钟芯片
//	RTC_SCL PB05
//	RTC_SDA	PB04

#define RTC_SCL_COM   PortB,Pin05
#define RTC_SDA_COM   PortB,Pin04

#define RTC_SDA_IN()  RTC_SET_SDA_IN()
#define RTC_SDA_OUT() RTC_SET_SDA_OUT()

//IO操作函数	 
#define RTC_IIC_SCL(v)      v == Disable ? PORT_ResetBits(RTC_SCL_COM): PORT_SetBits(RTC_SCL_COM) //SCL
#define RTC_IIC_SDA(v)      v == Disable ? PORT_ResetBits(RTC_SDA_COM): PORT_SetBits(RTC_SDA_COM) //SDA 
#define RTC_READ_SDA   		PORT_GetBit(RTC_SDA_COM)  //输入SDA 

#define RTC_EE_DEV_ADDR		0xA2		//设备

static u8 ack = 0;

//硬件延时
void bsp_DelayUS(int us)
{
	uint16_t i;

	
	for (i = 0; i < 30*us; i++);
}

void RTC_bsp_InitI2C(void)
{
	PORT_DebugPortSetting(TRST,Disable); //关闭JTDI 调试管脚
	
	PORT_DebugPortSetting(TDO_SWO,Disable); //关闭NJTRST调试管脚
 	stc_port_init_t stcPortInit; 
	MEM_ZERO_STRUCT(stcPortInit);	
	stcPortInit.enPinMode = Pin_Mode_Out;	//输出模式
	stcPortInit.enPinOType = Pin_OType_Od;	//漏极开路
    stcPortInit.enExInt =  Disable;			//外部智力支持Disable
    stcPortInit.enPullUp = Disable;			//内部上拉电阻关闭
	
	    
    PORT_Init(RTC_SCL_COM, &stcPortInit);//	SCL
	PORT_Init(RTC_SDA_COM, &stcPortInit);//	SDA
}

void RTC_SET_SDA_IN(void)
{
//	PORT_DebugPortSetting(TDO_SWO,Disable); //关闭NJTRST调试管脚
	stc_port_init_t stcPortInit;  
    
    MEM_ZERO_STRUCT(stcPortInit);
    
    //设置销模式
    stcPortInit.enPinMode = Pin_Mode_In;//输入模式  
    stcPortInit.enExInt =  Enable;//Enable//Disable
    stcPortInit.enPullUp = Enable;//enPinDrv //内部上拉电阻使能
	PORT_Init(RTC_SDA_COM, &stcPortInit);//	SDA
}

void RTC_SET_SDA_OUT(void)
{
//	PORT_DebugPortSetting(TDO_SWO,Disable); //关闭NJTRST调试管脚
	stc_port_init_t stcPortInit;  
    
    MEM_ZERO_STRUCT(stcPortInit);
    
    //设置销模式
    stcPortInit.enPinMode = Pin_Mode_Out;//输入模式  
    stcPortInit.enPinOType = Pin_OType_Od;	//漏极开路
    stcPortInit.enExInt =  Disable;//Enable//Disable
    stcPortInit.enPullUp = Disable;//enPinDrv //内部上拉电阻使能
	PORT_Init(RTC_SDA_COM, &stcPortInit);//	SDA
}

//I2C驱动部分
//产生IIC起始信号
void RTC_IIC_Start(void)
{
	RTC_SDA_OUT();     //sda线输出
	
	RTC_IIC_SDA(Enable);
	RTC_IIC_SCL(Enable);
	 	  
	bsp_DelayUS(4);
	
 	RTC_IIC_SDA(Disable);
	
	bsp_DelayUS(4);
	
	RTC_IIC_SCL(Disable);
}	
//产生IIC停止信号
void RTC_IIC_Stop(void)
{
	RTC_SDA_OUT();//sda线输出
	
	RTC_IIC_SCL(Disable);
	RTC_IIC_SDA(Disable);
	
 	bsp_DelayUS(4);
	
	RTC_IIC_SCL(Enable);
	bsp_DelayUS(4);
	RTC_IIC_SDA(Enable);
	bsp_DelayUS(4);							   	
}

//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 RTC_IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	RTC_SDA_IN();      //SDA设置为输入  
	RTC_IIC_SDA(Enable);
  bsp_DelayUS(1);
	RTC_IIC_SCL(Enable);
	bsp_DelayUS(1);
	
	while(RTC_READ_SDA)
	{
    ucErrTime++;
		if(ucErrTime>250)
		{
			RTC_IIC_Stop();
			return 1;
		}
	}
	RTC_IIC_SCL(Disable);//时钟输出0 	   
	return 0;  
} 

//产生ACK应答
void RTC_RTC_IIC_Ack(void)
{
	RTC_IIC_SCL(Disable);
	RTC_SDA_OUT();
	RTC_IIC_SDA(Disable);
	bsp_DelayUS(2);
	RTC_IIC_SCL(Enable); 
	bsp_DelayUS(2);
	RTC_IIC_SCL(Disable);
}

//不产生ACK应答		    
void RTC_IIC_NAck(void)
{
	RTC_IIC_SCL(Disable);
	RTC_SDA_OUT();
	RTC_IIC_SDA(Enable);
	bsp_DelayUS(2);
	RTC_IIC_SCL(Enable);
	bsp_DelayUS(2);
	RTC_IIC_SCL(Disable);
}

//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void RTC_IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	RTC_SDA_OUT(); 	    
    RTC_IIC_SCL(Disable);;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {    
		if(txd & 0x80)
		{
			RTC_IIC_SDA(Enable);
		}
		else
		{
			RTC_IIC_SDA(Disable);
		}	  
		bsp_DelayUS(2);   //对TEA5767这三个延时都是必须的
		RTC_IIC_SCL(Enable);
		bsp_DelayUS(2); 
		RTC_IIC_SCL(Disable);
		if(t == 7 )
		{
			RTC_IIC_SDA(Enable);
		}
		txd <<= 1; 
		bsp_DelayUS(2);
    }
	RTC_IIC_SDA(Enable);
	RTC_IIC_SCL(Enable);
	
	if(RTC_READ_SDA==1)
	{
		ack =0;
	}
	else
	{
		ack =1;
	}
	
	RTC_IIC_SCL(Disable);
}

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 RTC_IIC_Read_Byte(void)
{
	unsigned char i,receive=0;
	RTC_SDA_IN();//SDA设置为输入
  for(i=0;i<8;i++ )
	{
		RTC_IIC_SCL(Disable);	
        bsp_DelayUS(2);
		RTC_IIC_SCL(Enable);
        receive<<=1;
    if(RTC_READ_SDA)receive++;   		
		bsp_DelayUS(1); 
		RTC_IIC_SCL(Disable);
  }					 
	
//	if (!ack)
//			IIC_NAck();//发送nACK
//	else
//			IIC_Ack(); //发送ACK   
	return receive;
}

BM8563ESA 驱动部分
u8 GetBM8563(uchar sla,uchar suba,uchar *s,uchar no) 
{
	uchar i; 
	RTC_IIC_Start();
	RTC_IIC_Send_Byte(sla);
	if(ack==0)return(0); 
	RTC_IIC_Send_Byte(suba);
	if(ack==0)return(0); 
	RTC_IIC_Start();
	RTC_IIC_Send_Byte(sla+1);
	if(ack==0)return(0); 
	
	for(i=0;i
		*s = RTC_IIC_Read_Byte();
		RTC_RTC_IIC_Ack();
		s++;
	}
	*s = RTC_IIC_Read_Byte();
	RTC_RTC_IIC_Ack();
	RTC_IIC_Stop();
	return 1;	
}
 
u8 SetBM8563(uchar sla,uchar suba,uchar *s,uchar no)
{
	uchar i;
	RTC_IIC_Start();
	RTC_IIC_Send_Byte(sla);
	if(ack==0)return(0); 
	RTC_IIC_Send_Byte(suba);
	if(ack==0)return(0); 
	for(i =0;i
		RTC_IIC_Send_Byte(*s);
		if(ack==0)return(0);
		s++; 
	}
	RTC_IIC_Stop();
	return 1;
}
BM8563ESA 可用数据提取部分 因为我系统中至需要 年月日 时分秒 位操作宏的定义以及转换函数
#define GET_BIT(value,bit) ((value)&(1<<(bit)))    //读取指定位
#define CPL_BIT(value,bit) ((value)^=(1<<(bit)))   //取反指定位

#define SET0_BIT(value,bit) ((value)&=~(1<<(bit))) //把某个位置0
#define SET1_BIT(value,bit) ((value)|= (1<<(bit))) //把某个位置1
//设置数据的某些位的值



uchar SET_DATA(uchar value,unsigned int bitl,unsigned int bith,uchar data)
{
    uchar v = value;
    if(bitl<=bith)  
    {
        unsigned int bcount = bith-bitl+1;
        unsigned int cbit=0;
        unsigned int cdata=0;
        for(unsigned int i=0;i
            cdata |=(1<
	int h = (data >> 4) & 0x0F;
	int l = data  & 0x0F;
	int s = h*10 +l;
	return s;
}

//把int型转换成BCD码
uchar INT2BCD(int data)
{
	int h = data / 10;
	int l = data % 10;
	int s = h<<4 | l;
	return s;
}

u8 Rtc_data[6];//最终时间数据 //0 年,月,日,时,分,秒
int bm8533_timeFlag =0;
//每500MS获取一次时间
void R_bm8533_TimeData(void)
{
	static uchar trdata[7]; 
	if(GetBM8563(RTC_EE_DEV_ADDR,0x02,trdata,0x07))
	{
		Rtc_data[5] = BCD2INT(SET_DATA(trdata[0],7,7,0));//秒 //把trdata[0] 7到7位设置成0在
		Rtc_data[4] = BCD2INT(SET_DATA(trdata[1],7,7,0));//分
		Rtc_data[3] = BCD2INT(SET_DATA(trdata[2],6,7,0));//时
		Rtc_data[2] = BCD2INT(SET_DATA(trdata[3],6,7,0));//日
		Rtc_data[1] = BCD2INT(SET_DATA(trdata[5],5,7,0));//月
		Rtc_data[0] = BCD2INT(trdata[6]);//年
		bm8533_timeFlag =1; //接收转换完成
	}
}

//RTC读时间驱动
int bm8533_r(int fd,void *buf,int len)
{
	if(bm8533_timeFlag==1)
	{
		bm8533_timeFlag = 0;
		char * data =(char *)buf;
		memcpy(data,Rtc_data,sizeof(Rtc_data));
		return sizeof(Rtc_data);
	}
	return -1;
}

//设置时间我们是一次性从00H寄存器到08H寄存器一共9个数据
uchar cs_wrdata[9];
//RTC写时间驱动
int bm8533_w(int fd,void *buf,int len)
{
	int * data =(int *)buf;	
	for(int i =0;i
		cs_wrdata[i] = INT2BCD(data[i]);//把int行年月日时分秒转换成BCD码,写入到RTC兴平
	}
	SetBM8563(RTC_EE_DEV_ADDR,0x00,cs_wrdata,len);
	return len;
}
//驱动初始化
void RTC_Init_IIC(void)
{
	RTC_bsp_InitI2C(); //初始化驱动
	rxm_reg_r(DEV_RTC,&bm8533_r); 	//注册读时间驱动
	rxm_reg_w(DEV_RTC,&bm8533_w);	//注册写时间驱动
	rxm_addtim(500,&R_bm8533_TimeData);//软件定时器周期调用
}
DRV_INIT(RTC_Init_IIC);	//装载函数并使用

UI层调用/

//计算星期
//iY:年 iM:月 iD:日 返回当前月份
int getWeekdayByYearday(int iY, int iM, int iD) 
{
    int iWeekDay = -1; 
    if (1 == iM || 2 == iM) 
    {   
        iM += 12; 
        iY--;
    }   
    iWeekDay = (iD + 1 + 2 * iM + 3 * (iM + 1) / 5 + iY + iY / 4 - iY / 100 + iY / 400) % 7; 
    return iWeekDay;
}
int WRtcData[9]={0,0,0,13,10,8,2,2,22};
//确定按键
void UI_OK_btn_key(int key)
{
	
	WRtcData[8] = rxw_GetEditValue(ctl_year_val);	//年
	WRtcData[7] = rxw_GetEditValue(ctl_month_val);	//月
	WRtcData[5]	= rxw_GetEditValue(ctl_day_val);	//日
	
	WRtcData[4]	= rxw_GetEditValue(ctl_hour_val);	//时
	WRtcData[3] = rxw_GetEditValue(ctl_minute_val);	//分
	WRtcData[2] = rxw_GetEditValue(ctl_second_val);	//秒 
	
	WRtcData[6] = getWeekdayByYearday(WRtcData[8],WRtcData[7],WRtcData[5]);//星期
	
	rxm_w(DEV_RTC,WRtcData,9);//设置时间 //把WRtcData 数据写入驱动层
}
总结: 驱动层负责数据的转换和时间读写,UI层负载显示需要的时间和设置时间,不需要计算.
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/836532.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号