在使用定时器时,我们会涉及到三个非常重要的概念——分频,计数,重载。这三个概念可以结合生活中使用的时钟来理解。
分频
时钟上不同的指针需要有不同的速度,也就是不同的频率,从而精确的表示时间。
计数
定时器中的计数也是和计数时间成正比的值,频率越高增长速度越快。
重载
重载值就是计数上限,显然计数器不可能无限计数,当定时器中的计数值达到重载值时,计数值就会被清零。
要实现定时或者延时功能,一般有三种方法:软件定时,不可编程硬件定时,可编程硬件定时。
软件定时——让软件循环执行一段程序,程序本身并无执行目的,而是通过执行程序延时固定的时间,也就是以前的程序中经常使用的延时程序。这种方法降低了cpu的利用率。
不可编程硬件定时——如外部芯片555 时基电路,通过外部阻容,达到一定的延时功能,改变阻容大小可以改变延时长度。这种定时器取决于硬件,设定好以后不能通过软件更改。
可编程定时器——这种定时器的定时值可以通过软件确定和修改,使用灵活便捷。
定时器定时时间简单计算方法:定时时间=(设置时钟频率+1)**(分频数+1)/输入频率
Tout = ((arr+1)*(psc+1))/Tclk
Prescaler分频系数psc
Counter Period时钟频率arr
配置HAL库:
PB6对LED输出信号:
设置输入频率72MHZ:
设置TIM1:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim1)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_6);
}
if(htim==&htim2)
{ char data[]="hello windows!\n";
HAL_UART_Transmit(&huart1, (uint8_t *)data, 15, 0xffff); }
}
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM1_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
/* Initialize interrupts */
MX_NVIC_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim1);
HAL_TIM_Base_Start_IT(&htim2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
烧录运行效果:
VID_20221030_113105
STM32F2的实时时钟(RTC)是一个独立的BCD(Binary Coded Decimal)定时器/计时器,提供了一个包括时间与日期的时钟/日历功能,两个闹钟中断,一个定期唤醒中断,并且包括一个自动唤醒单位管理的低功耗模式。实时时钟的缩写是RTC(Real_Time Clock)。RTC 是集成电路,通常称为时钟芯片。
实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
在hal库中生成的代码,每次断电就RTC时间会重置,每次上电都会重新初始化时间
【日期格式】Binary data format 表示十六进制;BCD data format BCD表示码进制
配置RTC:
Activate Clock Source 激活时钟源
Activate calendar激活日历
RTC_OUT设置为[Not RTC_OUT],也就是是否使能 tamper(PC13)引脚上输出校正的秒脉冲时钟
注意,RTC_OUT和Tamper选项共用引脚PC13,因此两个功能只能选择其一。
时钟配置:
时间参数配置:
使用自动配置,初始化时间必须使用BCD data format,原因是库函数存在bug,如果使用Binary data format,月份配置会出错
我们想要验证默认时间是否为 1970年1月1日零分零秒,使用HAL_RTC_GetTime(),HAL_RTC_GetDate()读取时间和日期,并保存到结构体变量中,然后通过串口输出读取的时间和日期。
先调用HAL_RTC_GetTime,再调用HAL_RTC_GetDate,否则不能解锁,即被锁死。
它们的HAL库声明如下:
HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
如果按默认配置运行的话,时间显示如下
这样运行年份和时间显示就是:
因为年份默认只能配置两位,所以最后结果还要在代码里添加对应年份前两位。
显然,cubemx默认将RTC配置为 00/01/01 00:00:00
我们重写fputc函数实现printf函数:
int fputc(int ch,FILE *f){
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
我们要先获取系统当前时间,每秒把日期加一,打印输出日期、时间及星期,需要用到的结构体在stm32f1xx_hal_rtc.h定义:
typedef struct
{
uint8_t WeekDay; /*!< Specifies the RTC Date WeekDay (not necessary for HAL_RTC_SetDate).
This parameter can be a value of @ref RTC_WeekDay_Definitions */
uint8_t Month; /*!< Specifies the RTC Date Month (in BCD format).
This parameter can be a value of @ref RTC_Month_Date_Definitions */
uint8_t Date; /*!< Specifies the RTC Date.
This parameter must be a number between Min_Data = 1 and Max_Data = 31 */
uint8_t Year; /*!< Specifies the RTC Date Year.
This parameter must be a number between Min_Data = 0 and Max_Data = 99 */
} RTC_DateTypeDef;
typedef struct
{
uint8_t Hours; /*!< Specifies the RTC Time Hour.
This parameter must be a number between Min_Data = 0 and Max_Data = 23 */
uint8_t Minutes; /*!< Specifies the RTC Time Minutes.
This parameter must be a number between Min_Data = 0 and Max_Data = 59 */
uint8_t Seconds; /*!< Specifies the RTC Time Seconds.
This parameter must be a number between Min_Data = 0 and Max_Data = 59 */
} RTC_TimeTypeDef;
注意,weekday是0-6之间取值。
本实验在代码上主要编写的是main.c文件,先定义getdata获取日期,gettime获取时间,setdate修改日期:
RTC_DateTypeDef GetData;
RTC_DateTypeDef SetData;
RTC_TimeTypeDef GetTime;
定义函数使得RTC_DateTypeDef自动增加一天:
RTC_DateTypeDef autoAddDate(RTC_DateTypeDef date1 ){
uint8_t mon=date1.Month;
uint8_t day=date1.Date;
uint8_t yea=date1.Year+2000;
uint8_t week=date1.WeekDay;
day++;
int gap=0;
if((yea%400==0)||(yea%4==0&&yea%100!=0))
gap=1;
if(mon==2){
if(gap&&(day>29))
{mon++;day=1;}
else if(day>28)
{mon++;day=1;}
}
else if(((mon<8&&mon%2==1)||(mon>7&&mon%2==0))&&(day>31))
{ mon++;
day=1;
}
else if(day>30){
mon++;
day=1;
}
if(mon>12)
{mon=1;
yea++;}
week=(1+week)%7;
yea=yea-2000;
date1.Month=mon;
date1.Date=day;
date1.Year=yea;
date1.WeekDay=week;
return date1;
}
int main函数编写如下:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_RTC_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
/* Get the RTC current Date */
HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
printf("%02d/%02d/%02d\r\n", 2000+GetData.Year, GetData.Month, GetData.Date);
/* Display date Format : weekday */
switch(GetData.WeekDay){
case 1:printf("星期一\r\n");
break;
case 2:printf("星期二\r\n");
break;
case 3:printf("星期三\r\n");
break;
case 4:printf("星期四\r\n");
break;
case 5:printf("星期五\r\n");
break;
case 6:printf("星期六\r\n");
break;
case 0:printf("星期天\r\n");
break;
default:
break;
}
/* Display time Format : hh:mm:ss */
printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
printf("\r\n");
SetData=autoAddDate(GetData);
HAL_RTC_SetDate(&hrtc, &SetData, RTC_FORMAT_BIN);
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}