最终情况-实现串口发送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控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。

时钟线

STM32F103ZET6-RTC实时时钟_IT

RTC 从配置上分两大部分—时钟的配置、定时器的配置

       时钟的配置—可以直接访问

      直接由RCC的BDCR来配置时钟:时钟源的选择

       定时器的配置—不可以直接访问,因为定时器相关的寄存器在备份区域

1、 使能备份区域访问—  PWREN、BKPEN 

a)       开电源控制器以及备份区的时钟

b)      电源PWR_CR的DBP置1

2、 配置分频

3、 设置计数器计数值 -- 给他初始时间

4、 需要开中断,就开不需要就不开

5、 需要设置闹钟,就设置闹钟

配置RTC寄存器

STM32F103ZET6-RTC实时时钟_IT_02

STM32内部的RTC的操作

STM32F103ZET6-RTC实时时钟_寄存器_03

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