最终情况-实现串口发送16进制数修改时间
实验现象:
串口每1s打印一次时间
串口1收到对应格式的数据,可以更新RTC的时间
注意事项:
协议的内容
必须掌握:
(1)不使用空闲中断,也能处理串口数据
(2)理解连包的概念 -- 两帧数据一块过来,可能值触发1次空闲中断
(3)理解遍历的概念
实际应用:
(1)公司的1台设备,有配套的上位机,通过上位机发送时间,让设备更改时间
RTC是
RTC定时器简介
实时时钟是一个独立的定时器。 RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC的设置和时间维持不变。
系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。执行以下操作将使能对后备寄存器和RTC的访问:
设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟。
● 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。
RTC由两个主要部分组成(参见下图)。第一部分(APB1接口)用来和APB1总线相连。此单元还包含一组16位寄存器,可通过APB1总线对其进行读写操作(参见16.4节)。 APB1接口由APB1总线时钟驱动,用来与APB1总线接口。
另一部分(RTC核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是RTC的预分频模块,它可编程产生最长为1秒的RTC时间基准TR_CLK。 RTC的预分频模块包含了一个20位的可编程分频器(RTC预分频器)。如果在RTC_CR寄存器中设置了相应的允许位,则在每个TR_CLK周期中RTC产生一个中断(秒中断)。第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。
时钟线
RTC 从配置上分两大部分—时钟的配置、定时器的配置
时钟的配置—可以直接访问
直接由RCC的BDCR来配置时钟:时钟源的选择
定时器的配置—不可以直接访问,因为定时器相关的寄存器在备份区域
1、 使能备份区域访问— PWREN、BKPEN
a) 开电源控制器以及备份区的时钟
b) 电源PWR_CR的DBP置1
2、 配置分频
3、 设置计数器计数值 -- 给他初始时间
4、 需要开中断,就开不需要就不开
5、 需要设置闹钟,就设置闹钟
配置RTC寄存器
STM32内部的RTC的操作
STM32内部RTC初始化过程
时钟的配置—可以直接访问
直接由RCC的BDCR来配置时钟:时钟源的选择
定时器的配置—不可以直接访问,因为定时器相关的寄存器在备份区域
1、 使能备份区域访问— PWREN、BKPEN
a) 开电源控制器以及备份区的时钟
b) 电源PWR_CR的DBP置1
2、 配置分频
3、 设置计数器计数值
4、 需要开中断,就开不需要就不开
5、 需要设置闹钟,就设置闹钟
利用time接口实现
mktime();//根据结构体转成s数
localtime();//将s数转换成本地时间结构体
rtc.c
#include "rtc.h"
#include "stm32f10x.h"
#include "time.h"
#include "stdlib.h"
#include "string.h"
#include "uart1.h"
struct tm Set_time={0,33,20,9,4-1,2024-1900};
struct tm *Now_time=NULL;
uint32_t Now_time_cnt=0; //当前时间戳的计数值
void RTC_Configuration(void)
{
//1.开时钟 PWR时钟用于控制备份区访问,BKP时钟用于备份寄存器
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
//2.使能备份区访问
PWR_BackupAccessCmd(ENABLE);
//3.初始化备份寄存器函数
BKP_DeInit();
//4.配置LSE(外部低速时钟)作为RTC时钟源RCC_LSEConfig和RCC_RTCCLKConfig函数
RCC_LSEConfig(RCC_LSE_ON);
//5.等待外部晶振启动完成
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{}
//6.LSE为低速外部晶振32.768KHz
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
//7.使能RTC时钟RCC_RTCCLKCmd函数
RCC_RTCCLKCmd(ENABLE);//4 5 6 7 是完成时钟的切换 参考手册16.2 RTC三种时钟来源,我们使用外部低速晶振
//LSE 一般比较准
//8.等待RTC寄存器同步RTC_WaitForSynchro函数/
RTC_WaitForSynchro();
//9.等待上一次对RTC寄存器写入操作完成
RTC_WaitForLastTask();
//10.创建中断结构体
NVIC_InitTypeDef NVIC_InitStructure={0};
//11.配置RTC中断NVIC,设置优先级为1,开启RTC中断
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//12调用xxx函数将参数传入寄存器中
NVIC_Init(&NVIC_InitStructure);
//13.使能RTC 秒中断 RTC_ITConfig函数
RTC_ITConfig(RTC_IT_SEC, ENABLE);
//14.等待上一次对RTC寄存器写入操作完成
RTC_WaitForLastTask();
//15.设置RTC预分频器,使RTC周期为1秒 由于内部会+1所以除32767即可
RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
//16.再等待最后一次对RTC寄存器写入操作完成
RTC_WaitForLastTask();
Time_Adjust();
}
//设置实时时间
void Time_Adjust(void)
{
//1.等待上一次对RTC寄存器写入操作完成
RTC_WaitForLastTask();
//RTC_SetCounter(1712563329);
//2.设置RTC计数器的值,这里设置为mktime(&Set_time)函数转换的时间结构体为秒数
//mktime可以将符合time.h中结构体类型时间转换成秒数
RTC_SetCounter(mktime(&Set_time)); //根据结构体转成s数
//3.等待上一次对RTC寄存器写入操作完成
RTC_WaitForLastTask();
}
uint8_t Second_Flag=0;//秒标志 0代表未到1s 1表示到1s
void RTC_IRQHandler(void)
{
//1.检查RTC秒中断标志位RTC_IT_SEC是否设置
if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
{
//2.清除RTC秒中断标志位
RTC_ClearITPendingBit(RTC_IT_SEC);
//3.Second_Flag秒中断标志位置1
Second_Flag = 1;
//4.再等待上一次对RTC寄存器写入操作完成
RTC_WaitForLastTask();
}
}
void RTC_Handle(void)//不用空闲中断实现
{
uint8_t Length = 0;//应该接收的长度
uint8_t date_cs = 0;//接受检验和
uint8_t i = 0;
//1.至少收够两个字节
if(U1_R_Length>=2)
{
//2.判断有没有收到头
Length = U1_R_Buff[1]+2;//06+2=8
if(U1_R_Length>=Length)//防止前面帧头不对或者收的过多或者过少
{
for(i = 0;i<Length-1;i++)//自己计算校验和 ,不包含自己
{
date_cs+=U1_R_Buff[i];
}
if(U1_R_Buff[0]==0x5A&&date_cs==U1_R_Buff[Length-1])//校验通过
{
//帧头和接收数据长度都是对的
Set_time.tm_year=U1_R_Buff[2]+2000-1900;
Set_time.tm_mon=U1_R_Buff[3]-1;
Set_time.tm_mday=U1_R_Buff[4];
Set_time.tm_hour=U1_R_Buff[5];
Set_time.tm_min=U1_R_Buff[6];
Set_time.tm_sec=U1_R_Buff[7];
Time_Adjust();
//清理接收缓冲区
memset(U1_R_Buff,0,sizeof(U1_R_Buff));
U1_R_Length=0;
}
}
else if(U1_R_Length>=Length)//长度够了,帧头不对 校验不通过
{
//清理接收缓冲区
memset(U1_R_Buff,0,sizeof(U1_R_Buff));
U1_R_Length=0;
}
}
}
rtc.h
#ifndef __RTC_H_
#define __RTC_H_
#include "stm32f10x.h" // Device header
extern uint8_t Second_Flag;
extern struct tm *Now_time;
extern uint32_t Now_time_cnt;
void RTC_Configuration(void);
void Time_Adjust(void);
void RTC_Handle(void);
#endif
main.c
#include "stm32f10x.h"
#include "BEEP.h"
#include "TIME6.h"
#include "dianji.h"
#include "Delay.h"
#include "uart1.h"
#include "stdio.h"
#include "key.h"
#include "exti.h"
#include "string.h"
#include "led.h"
#include "adc.h"
#include "TIME6.h"
#include "dht11.h"
#include "esp8266.h"
#include "ali.h"
#include "dma.h"
#include "pwm.h"
#include "rgb.h"
#include "time.h"
#include "rtc.h"
uint8_t Device_State=0;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
UART1_Config();
RTC_Configuration();
printf("进入主函数\r\n");
while(1)
{
if(Second_Flag==1)
{
Second_Flag=0;
Now_time_cnt=RTC_GetCounter();
printf("%d\r\n",Now_time_cnt);
Now_time=localtime((const time_t *)&Now_time_cnt);
printf("%d-%d-%d %d:%d:%d\r\n",Now_time->tm_year+1900,Now_time->tm_mon+1,Now_time->tm_mday,Now_time->tm_hour,Now_time->tm_min,Now_time->tm_sec);
}
RTC_Handle();
}
}
// 5A 06 18 04 09 13 00 98