前言:本文章针对stm32系统时钟、时钟配置、systick定时器、库函数HAL_Delay函数进行了讲解。比较重要得内容是systick中断作用,以后再实时操作系统中会常常用到。另外HAl_Delay延时函数使用得实时有个局限性,因为它是通过中断方式实现得,而systick中断优先级最低,在中断中运行HAL_Delay会导致死锁的现象,很多时候我们需要自己重写一个延时函数。
目录
一、系统时钟概述
1、时钟系统的概念及意义
2、常见振荡器介绍
3、时钟树分析
二、STM32时钟配置实例
1、硬件原理图分析
2、cubmx配置
3、启动函数分析
4、操作实现
三、Systick定时器讲解
1、简介
2、定时器工作原理分析
3、寄存器
4、定时器使用实例
四、HAL_Delay函数的实现
1、HAL_Delay()分析
2、HAL_Delay() 的局限
概念:
时钟系统是由振荡器(信号源)、定时唤醒器、分频器等组成的电路。
常用的信号源有晶体振荡器和RC振荡器。
意义:
时钟是嵌入式系统的脉搏,处理器内核在时钟驱动下完成指令执行,状态变换等动作,外设部件在时钟的驱动下完成各种工作,比如串口数据的发送、A/D转换、定时器计数等等。因此时钟对于计算机系统是至关重要的,通常时钟系统出现问题也是致命的,比如振荡器不起振、振荡不稳、停振等。
概念:
振荡器是用来产生重复电子讯号的电子元件。其构成的电路叫振荡电路,能将直流电转换为具有一定频率交流信号输出的电子电路或装置。
分类:
振荡器主要分为RC,LC振荡器和晶体振荡器。RC振荡器是采用RC网络作为选频移相网络的振荡器。LC振荡器是采用LC振荡回路作为移相和选频网络的正反馈振荡器。晶体振荡器的振荡频率受石英晶体控制。
(1)RC振荡器
RC振荡器是又电阻电容构成的振荡电路,能将直流电转换为具有一定频率交流信号输出的电子电路或装置。
优点实现的成本比较低,毕竟就是一个电阻电容 。
缺点是由于电阻电容的精度问题所以RC振荡器的震荡频率会有误差,同时受到温度、湿度的影响
(2)晶体振荡器
石英晶体振荡器是高精度和高稳定度的振荡器,被广泛应用于彩电、计算机、遥控器等各类振荡电路中,以及通信系统中用于频率发生器、为数据处理设备产生时钟信号和为特定系统提供基准信号
优点是相对来说震荡频率一般都比较稳定,同时精度也较高
缺点就是价格要稍微高点了,还有用晶体振荡器一般还需要接两个15-33pF起振电容
STM32 中主要有四个时钟源
(2)复位和时钟控制 (RCC)
RCC TypeDef结构体介绍
根据中文参考手册,RCC的存储器映射是0x3800(U代表无符号) ,通过这个机构体,我们可以更方便的配置RCC寄存器。
实验要求: 配置STM32F407的时钟,并对比STM32时钟配置前后LED外设闪烁的快慢
我们用到led和uart串口,并使能rcc。
void SystemInit(void)
System_stm32f4xx.c中定义,在系统启动之后,程序会先执行 HAL 库定义的 SystemInit 函数,进行系统一些初始化配置,复位 RCC 时钟配置为默认复位值(默认开启 HSI)
void SystemClock_Config(void)
在main.c中定义,实现系统时钟的具体配置,配置PLL, 配置AHB和APB1,APB2的时钟
STM32F4 的外设在使用之前,必须对时钟进行使能,如果没有使能时钟,那么外设是无法正常工作的。
在 STM32F4 的 HAL 库中,外设时钟使能操作都是在 RCC 相关固件库文件头文件 stm32f4xx_hal_rcc.h 定义的。大家打开 stm32f4xx_hal_rcc.h 头文件可以看到文件中除了少数几 个函数声明之外大部分都是宏定义标识符。外设时钟使能在 HAL 库中都是通过宏定义标识符 来实现的
自写一个MyDelay的函数,目的观察配置时钟前后led闪烁频率得变化。
void My_Delay(uint32_t time){
uint32_t i,j;
for(i = 0; i < time; i++){
for(j=0 ; j<5000;j++);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
*
* @retval None
*/
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_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9|GPIO_PIN_10,GPIO_PIN_RESET); //亮
My_Delay(500);
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9|GPIO_PIN_10,GPIO_PIN_SET); //灭
My_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
工作原理
滴答定时器是一个24位定时器,也就是最多能计数2^24。在使用的时候,我们一般给计数器送一个初始的计数值,计数器向下计数,每来一个时钟信号,计数初值就减一,计数值减到0的时候,就会发出一次中断。然后重新从计数初值再减一计数,循环不断。
默认使能systick
Main中已经实现对SysTick定时器的初始化void SystemClock_Config(void)
在core_cm4.h定义了SysTick_Config(uint32_t ticks)
作用1:可以用户自己写
作用2:如果带操作系统,就需要用到这个函数。
可以重写函数测试打印
实验效果:定时打印
利用SysTick实现精准的延时
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/*增加一个频率以保证最小的等待 */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
while((HAL_GetTick() - tickstart) < wait)
{
}
}
HAL库的延时函数有一个局限性,在中断服务函数中使用HAL_Delay会引起混乱,因为它是通过中断方式实现,而 Systick 的中断一般操作系统优先级是最低的,所以在中断中运行 HAL_Delay会导致死锁的现象。