完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
通过串口1将RTC实时时钟数据发送到电脑串口调试助手上,并可在串口调试助手上输入数据与单片机通信。
main函数 #include "stm32f10x.h" #include "sys.h" #include "delay.h" #include "led.h" #include "key.h" #include "usart.h" #include "rtc.h" int main (void) { u8 bya; //定义变量用来接收设置时间函数的返回值 //打开逻辑电源和后备寄存器的时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); RTC_Config(); //实时时钟初始化 uart_init(115200); //串口初始化,参数中写波特率 USART_RX_STA=0xC000; //初始值设为有回车的状态,即显示一次欢迎词 while(1) { if(USART_RX_STA&0xC000) //如果标志位是0xC000表示收到数据串完成,可以处理。 { if((USART_RX_STA&0x3FFF)==0) //单独的回车键再显示一次欢迎词 { if(RTC_Get()==0) //读出时间值,同时判断返回值是不是0,非0时读取的值是错误的 { printf(" STM32实时时钟测试程序 rn"); printf(" 现在实时时间:%d-%d-%d %d:%d%d:%d%d ",ryear,rmon,rday,rhour,rmin/10,rmin%10,rsec/10,rsec%10);//显示日期时间 if(rweek==0)printf("星期日 rn");//rweek值为0时表示星期日 if(rweek==1)printf("星期一 rn"); if(rweek==2)printf("星期二 rn"); if(rweek==3)printf("星期三 rn"); if(rweek==4)printf("星期四 rn"); if(rweek==5)printf("星期五 rn"); if(rweek==6)printf("星期六 rn"); printf(" 单按回车键更新时间。输入字母C初始化时钟 rn"); printf(" 请输入设置时间,格式20200407120000,按回车键确定! rn"); } else { printf("读取失败!rn"); } } else if((USART_RX_STA&0x3FFF)==1) //判断数据是不是2个 { if(USART_RX_BUF[0]=='c' || USART_RX_BUF[0]=='C') { RTC_First_Config(); //键盘输入c或C,初始化时钟 BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5 printf("初始化成功! rn");//显示初始化成功 } else { printf("指令错误! rn"); //显示指令错误! } } else if((USART_RX_STA&0x3FFF)==14) //判断数据是不是14个 { //将超级终端发过来的数据换算并写入RTC ryear = (USART_RX_BUF[0]-0x30)*1000+(USART_RX_BUF[1]-0x30)*100+(USART_RX_BUF[2]-0x30)*10+USART_RX_BUF[3]-0x30; rmon = (USART_RX_BUF[4]-0x30)*10+USART_RX_BUF[5]-0x30;//串口发来的是字符,减0x30后才能得到十进制0~9的数据 rday = (USART_RX_BUF[6]-0x30)*10+USART_RX_BUF[7]-0x30; rhour = (USART_RX_BUF[8]-0x30)*10+USART_RX_BUF[9]-0x30; rmin = (USART_RX_BUF[10]-0x30)*10+USART_RX_BUF[11]-0x30; rsec = (USART_RX_BUF[12]-0x30)*10+USART_RX_BUF[13]-0x30; bya=RTC_Set(ryear,rmon,rday,rhour,rmin,rsec); //将数据写入RTC计算器的程序 if(bya==0)printf("写入成功! rn");//显示写入成功 else printf("写入失败! rn"); //显示写入失败 } else //如果以上都不是,即是错误的指令。 { printf("指令错误! rn"); //如果不是以上正确的操作,显示指令错误! } USART_RX_STA=0; //将串口数据标志位清0 } } } rtc.c函数 /* //时间读写与设置说明// 1,在mani函数开头放入RTC_Config();就可以使能时钟了。 在RTC_Config();函数中自带判断是不是首次使用RTC 2,使用 RTC_Get();读出时间。读出的数据存放在: 年 ryear (16位) 月 rmon (以下都是8位) 日 rday 时 rhour 分 rmin 秒 rsec 周 rweek 3,使用 RTC_Set(4位年,2位月,2位日,2位时,2位分,2位秒); 写入时间。例如:RTC_Get(2017,08,06,21,34,00); 其他函数都是帮助如上3个函数的,不需要调用。 注意要使用RTC_Get和RTC_Set的返回值,为0时表示读写正确。 */ #include "sys.h" #include "rtc.h" //以下2条全局变量--用于RTC时间的读取 u16 ryear; //4位年 u8 rmon,rday,rhour,rmin,rsec,rweek;//2位月日时分秒周 void RTC_First_Config(void) //首次启用RTC的设置 { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);//启用PWR和BKP的时钟(from APB1) PWR_BackupAccessCmd(ENABLE);//后备域解锁 BKP_DeInit();//备份寄存器模块复位 RCC_LSEConfig(RCC_LSE_ON);//外部32.768KHZ晶振开启 while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//等待稳定 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//RTC时钟源配置成LSE(外部低速晶振32.768KHZ) RCC_RTCCLKCmd(ENABLE);//RTC开启 RTC_WaitForSynchro();//开启后需要等待APB1时钟与RTC时钟同步,才能读写寄存器 RTC_WaitForLastTask();//读写寄存器前,要确定上一个操作已经结束 RTC_SetPrescaler(32767);//设置RTC分频器,使RTC时钟为1Hz,RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) RTC_WaitForLastTask();//等待寄存器写入完成 //当不使用RTC秒中断,可以屏蔽下面2条 // RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中断 // RTC_WaitForLastTask();//等待写入完成 } void RTC_Config(void) //实时时钟初始化 { //在BKP的后备寄存器1中,存了一个特殊字符0xA5A5 //第一次上电或后备电源掉电后,该寄存器数据丢失,表明RTC数据丢失,需要重新配置 if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){//判断寄存数据是否丢失 RTC_First_Config();//重新配置RTC BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5 }else{ //若后备寄存器没有掉电,则无需重新配置RTC //这里我们可以利用RCC_GetFlagStatus()函数查看本次复位类型 if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET){ //这是上电复位 } else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET){ //这是外部RST管脚复位 } RCC_ClearFlag();//清除RCC中复位标志 //虽然RTC模块不需要重新配置,且掉电后依靠后备电池依然运行 //但是每次上电后,还是要使能RTCCLK RCC_RTCCLKCmd(ENABLE);//使能RTCCLK RTC_WaitForSynchro();//等待RTC时钟与APB1时钟同步 //当不使用RTC秒中断,可以屏蔽下面2条 // RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中断 // RTC_WaitForLastTask();//等待操作完成 } #ifdef RTCClockOutput_Enable RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE); BKP_TamperPinCmd(DISABLE); BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock); #endif } void RTC_IRQHandler(void){ //RTC时钟1秒触发中断函数(名称固定不可修改) if (RTC_GetITStatus(RTC_IT_SEC) != RESET){ } RTC_ClearITPendingBit(RTC_IT_SEC); RTC_WaitForLastTask(); } void RTCAlarm_IRQHandler(void){ //闹钟中断处理(启用时必须调高其优先级) if(RTC_GetITStatus(RTC_IT_ALR) != RESET){ } RTC_ClearITPendingBit(RTC_IT_ALR); RTC_WaitForLastTask(); } //判断是否是闰年函数 //月份 1 2 3 4 5 6 7 8 9 10 11 12 //闰年 31 29 31 30 31 30 31 31 30 31 30 31 //非闰年 31 28 31 30 31 30 31 31 30 31 30 31 //输入:年份 //输出:该年份是不是闰年.1,是.0,不是 u8 Is_Leap_Year(u16 year) { if(year%4==0){ //必须能被4整除 if(year%100==0){ if(year%400==0)return 1;//如果以00结尾,还要能被400整除 else return 0; }else return 1; }else return 0; } //设置时钟 //把输入的时钟转换为秒钟 //以1970年1月1日为基准 //1970~2099年为合法年份 //月份数据表 u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表 const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年的月份日期表 //写入时间 u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec) //写入当前时间(1970~2099年有效), { u16 t; u32 seccount=0; if(syear<2000||syear>2099)return 1;//syear范围1970-2099,此处设置范围为2000-2099 for(t=1970;t else seccount+=31536000; //平年的秒钟数 } smon-=1; for(t=0;t if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数 } seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 seccount+=(u32)hour*3600;//小时秒钟数 seccount+=(u32)min*60; //分钟秒钟数 seccount+=sec;//最后的秒钟加上去 RTC_First_Config(); //重新初始化时钟 BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5 RTC_SetCounter(seccount);//把换算好的计数器值写入 RTC_WaitForLastTask(); //等待写入完成 return 0; //返回值:0,成功;其他:错误代码. } //读出时间 u8 RTC_Get(void) //读出当前时间值 //返回值:0,成功;其他:错误代码. { static u16 daycnt=0; u32 timecount=0; u32 temp=0; u16 temp1=0; timecount=RTC_GetCounter(); temp=timecount/86400; //得到天数(秒钟数对应的) if(daycnt!=temp){//超过一天了 daycnt=temp; temp1=1970; //从1970年开始 while(temp>=365){ if(Is_Leap_Year(temp1)){//是闰年 if(temp>=366)temp-=366;//闰年的秒钟数 else {temp1++;break;} } else temp-=365; //平年 temp1++; } ryear=temp1;//得到年份 temp1=0; while(temp>=28){//超过了一个月 if(Is_Leap_Year(ryear)&&temp1==1){//当年是不是闰年/2月份 if(temp>=29)temp-=29;//闰年的秒钟数 else break; }else{ if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年 else break; } temp1++; } rmon=temp1+1;//得到月份 rday=temp+1; //得到日期 } temp=timecount%86400; //得到秒钟数 rhour=temp/3600; //小时 rmin=(temp%3600)/60; //分钟 rsec=(temp%3600)%60; //秒钟 rweek=RTC_Get_Week(ryear,rmon,rday);//获取星期 return 0; } u8 RTC_Get_Week(u16 year,u8 month,u8 day) //按年月日计算星期(只允许1901-2099年)//已由RTC_Get调用 { u16 temp2; u8 yearH,yearL; yearH=year/100; yearL=year%100; // 如果为21世纪,年份数加100 if (yearH>19)yearL+=100; // 所过闰年数只算1900年之后的 temp2=yearL+yearL/4; temp2=temp2%7; temp2=temp2+day+table_week[month-1]; if (yearL%4==0&&month<3)temp2--; return(temp2%7); //返回星期值 } rtc.h文件 #ifndef __RTC_H #define __RTC_H #include "sys.h" //全局变量的声明,在rtc.c文件中定义 //以下2条是使用extern语句声明全局变量 //注意:这里不能给变量赋值 extern u16 ryear; extern u8 rmon,rday,rhour,rmin,rsec,rweek; void RTC_First_Config(void);//首次启用RTC的设置 void RTC_Config(void);//实时时钟初始化 u8 Is_Leap_Year(u16 year);//判断是否是闰年函数 u8 RTC_Get(void);//读出当前时间值 u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//写入当前时间 u8 RTC_Get_Week(u16 year,u8 month,u8 day);//按年月日计算星期 #endif usart.c函数 #include "sys.h" #include "usart.h" // //如果使用ucos,则包括下面的头文件即可. #if SYSTEM_SUPPORT_OS #include "includes.h" //ucos 使用 #endif //加入以下代码,支持printf函数,而不需要选择use MicroLIB #if 1 #pragma import(__use_no_semihosting) //标准库需要的支持函数 struct __FILE { int handle; }; FILE __stdout; //定义_sys_exit()以避免使用半主机模式 void _sys_exit(int x) { x = x; } //重定义fputc函数 int fputc(int ch, FILE *f) { while((USART1->SR&0X40)==0); //循环发送,直到发送完毕 USART1->DR = (u8) ch; return ch; } #endif /*使用microLib的方法*/ /* int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t) ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {} return ch; } int GetKey (void) { while (!(USART1->SR & USART_FLAG_RXNE)); return ((int)(USART1->DR & 0x1FF)); } */ #if EN_USART1_RX //如果使能了接收 //串口1中断服务程序 //注意,读取USARTx->SR能避免莫名其妙的错误 u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. //接收状态 //bit15, 接收完成标志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字节数目 u16 USART_RX_STA=0; //接收状态标记 void uart_init(u32 bound) { //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟 //USART1_TX GPIOA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9 //USART1_RX GPIOA.10初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 //USART 初始化设置 USART_InitStructure.USART_BaudRate = bound;//串口波特率 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口1 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受(接收完成)中断 USART_Cmd(USART1, ENABLE); //使能串口1 } void USART1_IRQHandler(void) //串口1中断服务程序 { u8 Res; #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS. OSIntEnter(); #endif if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾) { Res =USART_ReceiveData(USART1); //读取接收到的数据 if((USART_RX_STA&0x8000)==0)//接收未完成 { if(USART_RX_STA&0x4000)//接收到了0x0d { if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始 else USART_RX_STA|=0x8000; //接收完成了 } else //还没收到0X0D { if(Res==0x0d)USART_RX_STA|=0x4000; else { USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; USART_RX_STA++; //累计接收的数据个数 if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收 } } } } #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS. OSIntExit(); #endif } #endif usart.h文件 #ifndef __USART_H #define __USART_H #include "stdio.h" #include "sys.h" #define USART_REC_LEN 200 //定义最大接收字节数 200 #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收 extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 extern u16 USART_RX_STA; //接收状态标记 void uart_init(u32 bound); #endif 实验效果 注意勾选“发送新行”才有0x0a和0x0d。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1786 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1622 浏览 1 评论
1089 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
730 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1680 浏览 2 评论
1939浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
736浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
571浏览 3评论
597浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
560浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 07:56 , Processed in 1.200022 second(s), Total 78, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号