STM32F103C8T6 configures RTC to display year, month, day, hour, minute and second (calendar)

1. Introduction to RTC

1. Main block diagram

2. Introduction

The RTC module has a set of continuous counting counters, which can provide clock calendar functions under the corresponding software configuration. Modifying the counter value can reset the current time and date of the system.

RTC
Module and clock configuration system
(RCC_BDCR
register
)
In the backup region, i.e. upon system reset or wake-up from standby mode

back,
RTC
The settings and time remain unchanged.

After system reset, the backup register and
RTC
access is prohibited, this is to prevent access to the fallback area
(BKP)
accidental writing

do. Doing the following will enable access to the backing register and
RTC
Access:


Set the PWREN and BKPEN bits of the register RCC_APB1ENR to enable the power supply and backup interface clock


Set the DBP bit of register PWR_CR to enable access to the backup register and RTC.

● You can choose from the following three types
RTC
clock source:


HSE
clock divided by
128
;

LSE oscillator clock;


LSI
Oscillator clock
(
See details
6.2.8

308/754

Festival
RTC
clock
)
.


2
independent reset types:


APB1
The interface is reset by the system;


RTC
core
(
Prescalers, alarms, counters and dividers
)
Can only be reset by the backup domain
.


3
A dedicated maskable interrupt:


Alarm interrupt, used to generate a software programmable alarm interrupt.


seconds interrupt
, used to generate a programmable periodic interrupt signal
(
up to
1
Second
)
.


Overflow interrupt, indicating that the internal programmable counter overflows and rolls over to
0
status.

2. Main modules

1. rtc file

  1. RTC initialization configuration
  2. RTC_NVIC configuration
  3. Backup area identification–BKP
  4. Set time
  5. Put the time into the Setcounter()—Time_Adjust() function
  6. Get Time
  7. Put the parameters into the time structure
  8. display time
  9. Determine leap year
  10. RTC interrupt service function
  11. printf redirection, rewrite fputc function

2. System files

  1. System file system

  2. Library file

  3. User files

  4. Self-programming

3. Code framework

  1. 4, main function, delay, serial port, rtc
  2. The declaration and definition of the implemented functions in 4 .h files
  3. Write the printf rewrite function into rtc.c
  4. The main function only has initialization function call and time display function. The time display function is in while(1)

4. RTC configuration

If you forget how to configure, check the STM32f103 firmware library reference manual and data sheet examples, there are detailed steps

  1. Enable power supply PWR, backup area BKP
  2. Allow access to backup area
  3. Backup domain reset
  4. Enable external low-speed clock LSE
  5. Wait for LSE to stabilize (the while loop may be stuck here)
  6. Select LSE as RTC clock source
  7. Enable RTC clock
  8. Wait for the last operation to complete
  9. Wait for the register to be synchronized with the APB1 clock. Because the RTC clock is low speed and the inner ring clock is high speed, it must be synchronized (it is likely to get stuck here)
  10. Wait for the last operation to complete
  11. Enable RTC seconds interrupt
  12. Wait for the last operation to complete
  13. Set the pre-allocated register period to 1s
  14. Wait for the last operation to complete

5. Rewrite printf

#include <stdio.h>
#if 1
//Turn off semi-hosting mode
#pragma import(__use_no_semihosting)
//Support functions required by the standard library
struct __FILE{
int handle;
};
 FILE __stdin, __stdout, __stderr;
//FILE __stdin;
//Avoid using semi-hosting mode
void _sys_exit(int x)
{
x=x;
}


/*Rewrite fputc*/
int fputc(int ch, FILE *f)
{
\t
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); //Send ch through the serial port
USART_SendData(USART1,ch);
return ch;
}

6. Time setting Set

The time starts from 00:00:00 on January 1, 1970

1970-The number of days that have passed until now, one day is 60*60*24=86400 seconds

For less than one day, hour*3600 + minute*60 + second = second

second + number of days passed*86400=all seconds

? How many days have passed in that year? Is it 365 or 366? Answer: You need a function to determine whether the year is a leap year.

Use a temporary auto-increment variable to record the year, which will increment each time a year is determined until 2023.

The number of days left in less than one year is calculated according to the month.

? February ? Whether it is 28 or 29, I used the leap year judgment function again. Adding numbers each time was too cumbersome. I used numbers to assemble the number of days in each month. It is convenient to use a for loop. month_table[12]={31,28,31,30,31,30,31,31,30,31,30,31}

The return type is uint32_t. Put this function in RTC_SetCounter (time setting function Set). Please see the code later.

7. Time Get

Get the value placed in the register through RTC_GetCounter() Seconds, divide all seconds/86400 to get all the days. Determine whether it is a leap year from 1970 to 2023. If it is a leap year, days-366 is not just days-365. How to get seconds that are less than a day? Answer: All seconds are 400. The hours in a day are (seconds 400)/3600 = hours, ((seconds 400) 600)/60 = minutes, (seconds 400) 600) = second, and the year will be obtained Put the month and day into the structure time we set

8. Core code

1, rtc.h
#ifndef _RTC_H
#define _RTC_H
#include <stm32f10x.h>
#include <stdio.h>

//time structure
 typedef struct{
     uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;

}cal;
extern cal calendar;
extern int const month_table[12];


void clockinit_RTC(void);
void RTC_NVIC_Config(void);
void rtc_init(void);
uint32_t rtc_timeSet(uint16_t syear,uint8_t smonth,uint8_t sdays,uint8_t shour,uint8_t sminute,uint8_t ssecond);
void Time_Adjust(void);
uint8_t rtc_timeGet(void);
int IS_temp_year(uint16_t year);
void Time_Show(void);
void Time_Display(uint32_t time);
void Time_Display2(void);
#endif
2, rtc.c
#include "rtc.h"
#if 1
//Turn off semi-hosting mode
#pragma import(__use_no_semihosting)
//Support functions required by the standard library
struct __FILE{
int handle;
};
 FILE __stdin, __stdout, __stderr;
//FILE __stdin;
//Avoid using semi-hosting mode
void _sys_exit(int x)
{
x=x;
}


/*Rewrite fputc*/
int fputc(int ch, FILE *f)
{
\t
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); //Send ch through the serial port
USART_SendData(USART1,ch);
return ch;
}

int fgetc(FILE *f){
   char ch;
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET);
   ch=USART_ReceiveData(USART1);
return (int)ch;
}
int ferror(FILE * f){
  return EOF;
}
void _ttywrch(int ch){
   while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
USART_SendData(USART1,ch);
}
//void _sys_exit(int return_code){
// while(1);
//}
#endif

cal calendar;
int const month_table[12] ={31,28,31,30,31,30,31,31,30,31,30,31};
__IO uint8_t TimeDisplay;
//RTC built-in configuration
void rtc_init(){
uint32_t t;
    //Enable rcu_bkpi clock, pwr power supply, bkp backup area
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE);
//Enable backup area and allow access to backup area
PWR_BackupAccessCmd(ENABLE);
\t
    //Backup domain reset
BKP_DeInit();
//Enable external low-speed clock, 32.768k
RCC_LSEConfig(RCC_LSE_ON);
//Wait for the external low-speed clock to stabilize
while((RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) & amp; & amp; (t<10)){
t + + ;
}
//Select LSE as the clock source of RTC
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
//Enable RTC clock
RCC_RTCCLKCmd(ENABLE);
\t\t
//Wait for the last RTC operation to complete
    RTC_WaitForLastTask();
\t\t
//Wait for the register to be synchronized with the APB1 clock. Because the RTC clock is low speed and the inner ring clock is high speed, it must be synchronized.
RTC_WaitForSynchro();
//Wait for the last RTC operation to complete
    RTC_WaitForLastTask();
//Enable RTC second interrupt
RTC_ITConfig(RTC_IT_SEC,ENABLE);
//Wait for the last RTC operation to complete
RTC_WaitForLastTask();
//Set the prescaler register, the time period is 1 second = 32.768k/32768 + 1--------(RTCCLK/RTC_PR)
RTC_SetPrescaler(32768-1);
//Wait for the last RTC operation to complete
RTC_WaitForLastTask();
}

void RTC_NVIC_Config(void){
NVIC_InitTypeDef NVIC_InitStructure;
//Set the priority group to 1, seize 1 bit (0-1), and start from 3 bits (0-7)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
//Set priority channel
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( & amp;NVIC_InitStructure);
}

void clockinit_RTC(){
RTC_NVIC_Config();
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE);
PWR_BackupAccessCmd(ENABLE);
//Identify whether it is the back backup area
  if(BKP_ReadBackupRegister(BKP_DR1)!=0XA5A5){
//Judge whether it is the first time the system is powered on by judging the value of the back register.
printf("RTC has not been configured");
//If yes, perform RTC configuration and set the initial test time
rtc_init();
printf("RTC is configuring");
Time_Adjust();
BKP_WriteBackupRegister(BKP_DR1,0xA5A5);

}else{
//Check if the system is powered off
if(RCC_GetFlagStatus(RCC_FLAG_PORRST)!=RESET){
printf("Power On Resetm occurred...");
}
//See if the reset is caused by the reset pin
else if(RCC_GetFlagStatus(RCC_FLAG_PINRST)!=RESET){
printf("External Reset occured...");
}
printf("No need to configure RTC...");
RTC_WaitForSynchro();
RTC_ITConfig(RTC_IT_SEC,ENABLE);
RTC_WaitForLastTask();
}
RCC_ClearFlag();
Time_Show();
}
//RTC initialization (configuration function, Set parameter, Get time)

//Set time function
void Time_Adjust(void){
   //Wait for the last RTC operation to complete
    RTC_WaitForLastTask();
//Access register with seconds
RTC_SetCounter(rtc_timeSet(2023,9,22,14,30,0));
//Wait for the last RTC operation to complete
    RTC_WaitForLastTask();
}
//Use the set time to get seconds, we use seconds interrupt
uint32_t rtc_timeSet(uint16_t syear,uint8_t smonth,uint8_t sdays,uint8_t shour,uint8_t sminute,uint8_t ssecond){
\t  \t\t
    uint16_t old_year=1970;
//uint32_t all_second=0;
uint16_t days=0;
uint8_t month=0;
uint32_t second=0;
//Calculation for time greater than one year
while(old_year<syear){
if(IS_temp_year(old_year)){
days + = 366;
}else{
days + =365;
}
old_year + + ;
}
//Calculation for less than one year
  for(;month<smonth;month + + ){
if(!IS_temp_year(syear)){
days + = month_table[month];
}else{
if(month==1){
days = days + month_table[month] + 1;
}else{
days + = month_table[month];
}
}
}
days + = sdays;
//Get the number of seconds in the year, month and day, there are 86400 seconds in a day
second = days * 86400;
//Seconds less than one day - hour
second + =hour * 3600;
  //Seconds less than one day - minutes
second + = minute * 60;
//Number of seconds less than one day--seconds
second + = second;
  return second;
\t
}
//Use the obtained seconds to calculate the current date
uint8_t rtc_timeGet(){
  uint32_t getsecond = RTC_GetCounter();
uint16_t gyear = 1970;
uint16_t gdays = 0;
uint8_t gmonth = 0;
uint8_t ghour = 0;
uint8_t gminute = 0;
uint16_t gsecond = 0;
\t
gdays = getsecond / 86400;
while(gdays < 365){
if(IS_temp_year(gyear)){
gdays -= 366;
}else{
gdays -= 365;
}
gyear++;
}
\t
while(gdays >= 28){
if(IS_temp_year(gyear)){
gdays = gdays - month_table[gmonth]-1;
}else{
gdays -= month_table[gmonth];
}
gmonth + + ;
}
calendar.year = gyear;
calendar.month = gmonth;
calendar.day = (uint8_t)gdays;
//Seconds less than one day
gsecond = getsecond % 86400;
ghour = gsecond / 3600;
gminute = (gsecond % 3600) / 60;
gsecond = (gsecond % 3600) `;
  calendar.hour = ghour;
calendar.minute = gminute;
calendar.second = (uint8_t)gsecond;
return 0;

}
//Determine whether the current year is a leap year
int IS_temp_year(uint16_t year){
   if(year%4==0){
if(year 0!=0){
return 1;
}else{
return 0;
}
\t 
}else{
if(year@0==0){
return 1;
}else{
return 0;
}
}
}
void Time_Show(void){
printf("\\
\r");
   while(1){
//Seconds flag
if(TimeDisplay == 1){
//Time_Display(RTC_GetCounter());
Time_Display2();
TimeDisplay = 0;
}
\t 
}

}

void Time_Display(uint32_t time){
    uint32_t hh=0,mm=0,ss=0;
/*When the time reaches 23:59:59, the RTC is reset and the count value is cleared*/
if(RTC_GetCounter() == 0x0001517f){ //(23*3600=82800) + (59*60=3540) + (59)=86399 becomes 0x0001517f in hexadecimal
RTC_SetCounter(0x0);
RTC_WaitForLastTask();
}
hh=time/3600;
mm=(time600)/60;
ss=(time600)`;
}
void RTC_IRQHandler(void){
//Whether seconds interrupt is enabled
   if(RTC_GetITStatus(RTC_IT_SEC)==SET){
/*Clear the second interrupt flag*/
RTC_ClearITPendingBit(RTC_IT_SEC);
/*Set the 1s flag*/
TimeDisplay=1;
RTC_WaitForLastTask();
//\t\t //display time
// Time_Display2();
\t\t 
}
RTC_WaitForLastTask();

}
void Time_Display2(void){
   rtc_timeGet();
   printf("Now time is: %d-%d-%d %d:%d:%d\r\\
", calendar.year, calendar.month, calendar.day, calendar.hour, calendar .minute, calendar.second);
}
3.usart.h
#ifndef _USART_H
#define _USART_H

#include <stm32f10x.h>
#include <stdio.h>

void usart_init(uint32_t bound);
void USART_SendString(uint8_t *ch);
void usart_byte(uint8_t ch);
void usart_buf(uint8_t *buf,uint8_t len);
#endif
4.usart.c
#include <usart.h>

//FILE _stdout;
//_sys_exit(int x)
//{
// x=x;

//}
//int fputc(int ch,FILE *f)
//{
// while((USART1->SR & amp;0x40)==0);
// USART1->DR=(u8) ch;
// return ch;
//}
//int fputc(int ch, FILE *f)
//{
// while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); //Send ch through the serial port
// USART1_SendData(USART1,ch);
// return ch;
//}



void usart_init(uint32_t bound)
{
   GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure;
\t
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO,ENABLE);
   
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, & amp;GPIO_InitStructure);
\t
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;;
GPIO_Init(GPIOA, & amp;GPIO_InitStructure);
\t
USART_InitStructure.USART_BaudRate=bound;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
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, & amp;USART_InitStructure);
USART_Cmd(USART1,ENABLE);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
USART_ClearFlag(USART1,USART_FLAG_TC);
\t 
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init( & amp;NVIC_InitStructure);
\t 
}
void usart_byte(uint8_t ch){
   USART_SendData(USART1,ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
}
//void USART_SendString(uint8_t *ch){
// uint16_t k=0;
// while(*(ch + k) !='\0'){
// k + + ;
// usart_byte(*(ch + k));
// }
//}
void USART_SendString(uint8_t *ch){
    while((*ch)!='\0')
{
usart_byte(*ch + + );
}
}
void usart_buf(uint8_t *buf,uint8_t len){
   uint16_t i;
for(i=0;i<len;i + + ){
usart_byte(buf[i]);
}
}
uint16_t usart_rx_buf[8]={0};
uint8_t USART_RX_CNT=0;
/*
void USART1_IRQHandler(void)
{
  if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)//Receive interrupt (received data must be 0x0d)
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
usart_rx_buf[USART_RX_CNT]=USART_ReceiveData(USART1);//Read the received
USART_RX_CNT + + ;
if((usart_rx_buf[USART_RX_CNT-1]=='\r')||(usart_rx_buf[USART_RX_CNT-1]=='\\
'))
{
USART_SendString((uint8_t *)usart_rx_buf);
//Save the data to the queue once it encounters \r or \\

USART_RX_CNT=0;//Restart receiving data
}
\t\t\t\t    \t\t\t\t
}
}*/

void USART1_IRQHandler(void)
{
uint16_t temp;
  if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)//Receive interrupt (received data must be 0x0d)
{
temp = USART_ReceiveData(USART1);//Read the received
usart_byte((uint8_t)temp);
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);

}
/*
void USART1_IRQHandler(void)
{
  if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)//Receive interrupt (received data must be 0x0d)
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
usart_rx_buf[USART_RX_CNT]=USART_ReceiveData(USART1);//Read the received
USART_RX_CNT + + ;
if(usart_rx_buf[USART_RX_CNT] == '\0' & amp; & amp; (USART_RX_CNT >= 7)){
usart_buf((uint8_t *)usart_rx_buf,8);
//Save the data to the queue once it encounters \r or \\

USART_RX_CNT=0;//Restart receiving data
}
  }
}*///(usart_rx_buf[usart_rx_len-1]=='\r')||(usart_rx_buf[usart_rx_len-1]=='\\
')
5. main.c
#include "led.h"
#include "delay.h"
#include "rtc.h"
#include <stm32f10x.h>
#include "usart.h"
#include <stdio.h>

int main(void){
usart_init(115200);
printf("Serial port configuration.\r\\
");
led_init();
printf("led light configuration\r\\
");
USART_SendString("Hello\r\\
");
clockinit_RTC();
printf("This is a RTC test.\r\\
");

while(1){
     Time_Show();
}
}


9. This experiment is stuck in an infinite loop of waiting for the clock, and no other problems can be verified, so it is temporarily terminated

10. Possible problems

Use default comipler version5 If you choose 5 or 6, an error will be reported.

It is best to turn on C99 Mode√, the reason why an error may be reported

Use ST-Link Debugger

Select SW

Reset and Run

Enable

11. Supplement

Sometimes it is possible to #include “stm32f10x.h”, and the quotation marks will report an error but <> will not.

Dynamic Syntax Checking Cancel√Enable