HAL库之SYSTICK时钟频率探究-F407
最近一直想用系统滴答定时器来做一个us级别的延时,用正点原子和其他的一些函数库都试了一下,最终的结果都不尽人意,要不然就是延时进不来,要不然就是频率错误,自己在示波器的帮助下,搞定了一部分内容,目前能够正常使用,产生1us的延时。
至于直接在函数库里修改系统配置文件,有以下担心,所以一直没有操作。
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);//此配置为HAL库自带配置,延时为1MS;
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000000);//此配置为硬石HAL教程推荐的方法,延时为1US。
这样虽然调用HAL_DELAY() 函数能够正常产生1US的延时,但是毕竟修改了系统内置的配置,系统在初始化和调用一些外设的时候还是会自己用到这个延时函数,导致延时函数变化,可能会产生不可排查的BUG(虽然目前还没发现那个地方调用了此延时函数会发生BUG,总归还是小心点的好)。因此排除此方法。
自己在查看正点原子的教程的时候发现,正点原子的教程里面介绍SYSTICK的时钟为调用外部高速时钟(HSE)的时候时钟为HCLK的1/8,或者调用内置高速时钟(HSI)的时候为FCLK(这里面自己没理解好正点原子的意思,具体原因下面有讲解)
代码如下:
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //配置systick的时钟源fac_us=SYSCLK/8; //不论是否使用OS,fac_us都需要使用fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数 }
以上代码删除了支持OS的部分,这样看起来比较简洁。
再来看配置时钟源的函数
/*** @brief Configures the SysTick clock source.* @param SysTick_CLKSource: specifies the SysTick clock source.* This parameter can be one of the following values:* @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.//当函数选择SysTick_CLKSource_HCLK_Div8的时候,systick时钟=AHPCLOCK(也就是HCLK)/8* @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.//当函数选择SysTick_CLKSource_HCLK的时候,systick时钟=AHBclock(也就是FCLK)* @retval None*/
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{/* Check the parameters */assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));if (SysTick_CLKSource == SysTick_CLKSource_HCLK){SysTick->CTRL |= SysTick_CLKSource_HCLK;}else{SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;}
}
再来看 SysTick_CLKSource_HCLK_Div8的定义
#define SysTick_CLKSource_HCLK_Div8 ((uint32_t)0xFFFFFFFB)
也就是说8分频的时候,将0xFFFFFFFB赋值(进行与运算)到SysTick->CTRL
再来看SysTick_CLKSource_HCLK 的定义
#define SysTick_CLKSource_HCLK ((uint32_t)0x00000004)
也就是将0x00000004赋值(或运算)到)SysTick->CTRL
8分频的结果就是将CTRL的2位段置0,从而选择称为外部时钟源。
不分频 的结果就是将CTRL的2位段置1,从而选择称为内部时钟源。
注:这个内部和外部时钟源不是HSE 和HSI,而是针对系统时钟HCLK而言的。
此图来源于STM32F4XX中文参考手册第107页,正确的标识了两种时钟的区别
下面解释一下这个所谓的内部和外部时钟
系统时钟SYSCLK最大频率为168MHz,它是供STM32中绝大部分部件工作的时钟源。系统时钟可由PLL、HSI或者HSE提供输出,并且它通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用:
①、送给AHB总线、内核、内存和DMA使用的HCLK时钟。
②、分频后送给STM32芯片的系统定时器时钟(Systick=Sysclk/8=9Mhz)
③、直接送给Cortex的自由运行时钟(free running clock)FCLK。【ARMJISHU注:FCLK 为处理器的自由振荡的处理器时钟,用来采样中断和为调试模块计时。在处理器休眠时,通过FCLK 保证可以采样到中断和跟踪休眠事件。 Cortex-M3内核的“自由运行时钟(free running clock)”FCLK。“自由”表现在它不来自系统时钟HCLK,因此在系统时钟停止时FCLK 也继续运行。FCLK和HCLK 互相同步。FCLK 是一个自由振荡的HCLK。FCLK 和HCLK 应该互相平衡,保证进入Cortex-M3 时的延迟相同。】
④、送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给定时器(Timer)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4使用。
⑤、送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给定时器(Timer)1倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器1使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。
注意:STMCUBEMX软件中的时钟图有一个地方容易误导大众,
因此这时候终于搞明白这两个所谓的内部和外部时钟,
当使用HAL库默认的情况时, HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
此时systick采用的时钟为FCLK,频率为168MHZ,因此配置us定时器的LOAD值应该为168*nus,
//延时nus
//nus为要延时的us数.
//注意:nus的值,不要大于2^24/168=99864us
void delay_us(u32 nus)
{ u32 temp; SysTick->LOAD=nus*168; //时间加载 SysTick->VAL=0x00; //清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do{temp=SysTick->CTRL;}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00; //清空计数器
}
当需要修改systick的时钟源为HCLK的1/8时,HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);
此时systick的时钟源为1/8的HCLK,频率为168/8=21MHZ,因此定时器LOAD值应为21*nus
//延时nus
//nus为要延时的us数.
//注意:nus的值,不要大于2^24/21=798915us
void delay_us(u32 nus)
{ u32 temp; SysTick->LOAD=nus*21; //时间加载 SysTick->VAL=0x00; //清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do{temp=SysTick->CTRL;}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00; //清空计数器
}
下面贴一下,在正常HAL库操作生成的代码中,如何编写延时函数,达到延时1us的目的
主函数如下
#include "main.h"
#include "stm32f4xx_hal.h"
#include "adc.h"
#include "i2c.h"
#include "rtc.h"
#include "usart.h"
#include "gpio.h"/* USER CODE BEGIN Includes */
#include "delay.h"
/* USER CODE END Includes *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
//重定义fputc函数
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0);//循环发送,直到发送完毕 USART1->DR = (uint8_t) ch; return ch;
}
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*//* USER CODE END PFP *//* USER CODE BEGIN 0 *//* 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 */delay_init(); //在系统设置玩时钟之后,初始化一下自己写的延时函数/* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_I2C1_Init();MX_USART1_UART_Init();MX_RTC_Init();MX_ADC1_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){delay_us(50000); //延时50ms,也就是一个周期为100ms,z指示灯翻转1次HAL_GPIO_TogglePin(GPIOA, LED2_Pin);//HAL_Delay(1000);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct;RCC_ClkInitTypeDef RCC_ClkInitStruct;RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;/**Configure the main internal regulator output voltage */__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/**Initializes the CPU, AHB and APB busses clocks */RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.LSEState = RCC_LSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 4;RCC_OscInitStruct.PLL.PLLN = 168;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}/**Initializes the CPU, AHB and APB busses clocks */RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}/**Enables the Clock Security System */HAL_RCC_EnableCSS();/**Configure the Systick interrupt time */HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);/**Configure the Systick */HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);/* SysTick_IRQn interrupt configuration */HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @param file: The file name as string.* @param line: The line in file as a number.* @retval None*/
void _Error_Handler(char *file, int line)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */while(1){}/* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t* file, uint32_t line)
{ /* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT *//*** @}*//*** @}*/
延时函数如下:
#include "delay.h"
#include "sys.h"
// static u8 fac_us=0;//us延时倍乘数
static u16 fac_ms=0;//ms延时倍乘数,//初始化延迟函数
//SYSTICK的时钟等于FCLK时钟=168MHZ
//SYSCLK:系统时钟
void delay_init(void)
{fac_us=168; //因为HAL库函数定义的默认值为 168MHZfac_ms=(u16)fac_us*1000;// } //延时nus
//nus为要延时的us数.
//注意:nus的值,不要大于99864us
void delay_us(u32 nus)
{ u32 temp; SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do{temp=SysTick->CTRL;}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00; //清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=2^24/168/1000=99ms
void delay_xms(u16 nms)
{ u32 temp; SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)SysTick->VAL =0x00; //清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do{temp=SysTick->CTRL;}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00; //清空计数器
}
//延时nms
//nms:0~65535
void delay_ms(u16 nms)
{ u8 repeat=nms/99; //这里用99,是考虑到大部分人不会超频使用,如果超频使用,请自己自行修改//比如超频到248M的时候,delay_xms最大只能延时67ms左右了u16 remain=nms%99;while(repeat){delay_xms(99);repeat--;}if(remain)delay_xms(remain);}
以上配置是CUBEMX默认的配置,大家在使用的过程中注意us的使用限制范围就行了0-99864us
假如想使用8分频的HCLK,作为信号源,一下是主程序和延时程序的代码,进攻参考
主程序只需要修改
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);
// HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
延时字程序如下
#include "delay.h"
#include "sys.h"
//
//
// static u8 fac_us=0;//us延时倍乘数
static u16 fac_ms=0;//ms延时倍乘数//初始化延迟函数
//SYSTICK的时钟等于HCLK的1/8时钟=168/8=21MHZ
//SYSCLK:系统时钟
void delay_init(void)
{fac_us=21; fac_ms=(u16)fac_us*1000;} //延时nus
//nus为要延时的us数.
//注意:nus的值,不要大于798915us
void delay_us(u32 nus)
{ u32 temp; SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do{temp=SysTick->CTRL;}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00; //清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对168M条件下,nms<=798ms
void delay_xms(u16 nms)
{ u32 temp; SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)SysTick->VAL =0x00; //清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do{temp=SysTick->CTRL;}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00; //清空计数器
}
//延时nms
//nms:0~65535
void delay_ms(u16 nms)
{ u8 repeat=nms/540; //这里用540,是考虑到某些客户可能超频使用,//比如超频到248M的时候,delay_xms最大只能延时541ms左右了u16 remain=nms%540;while(repeat){delay_xms(540);repeat--;}if(remain)delay_xms(remain);}
现在对比完毕了,两种方式各有优缺点,
1.使用HCLK的1/8作为时钟源的话,在us延时函数里面,延时范围是798915us,远大于用FCLK的99864us。
2.使用FCLK作为时钟源的话,好处是在系统时钟停止时FCLK 也继续运行,此时systick时钟依然运行着。
HAL库之SYSTICK时钟频率探究-F407相关推荐
- 正点原子STM32(基于HAL库)0
目录 开发环境搭建与使用 常用开发工具简介 MDK 安装 仿真器驱动安装 CH340 USB 虚拟串口驱动安装 使用MDK5 编译例程 使用串口下载程序 使用DAP 下载与调试程序 使用DAP 下载程 ...
- 【STM32】HAL库-系统滴答定时器SysTick
SysTick定时器被捆绑在NVIC中,是一个简单的定时器,对于CM3.CM4内核芯片,都有Systick定时器.Systick定时器常用来做延时,或者实时系统的心跳时钟.这样可以节省MCU资源,不用 ...
- [学习笔记]STM32F1 SYSTICK 滴答定时器(寄存器、标准库、HAL库)
目录 0. 博主理解: 1. 实验内容及步骤: 2. 硬件说明 3. 寄存器说明 3.1 SysTick的时钟和使能: 3.2 SysTick重装载数值寄存器: 3.3 SysTick的中断优先级: ...
- 关于HAL库中系统嘀嗒时钟的简单探究
在利用HAL库建立STM32工程时,系统嘀嗒时钟的初始化是在函数HAL_Init()中实现的: HAL_StatusTypeDef HAL_Init(void) {/* Configure Flash ...
- STM32F4 HAL库开发 -- STM32CubeMX
一.STM32CubeMX 简介 STM32CubeMX 是 ST 意法半导体近几年来大力推荐的 STM32 芯片图形化配置工具, 允许用户使用图形化向导生成 C 初始化代码,可以大大减轻开发工作,时 ...
- hal库选择滴答时钟函数_stm32h7“理解hal库框架”
1.按照初始化流程调用的hal库文件 完成初始化工作需要调用到的hal库文件,如下表 序号功能调用hal库文件 1Module Selection(模块选择) Oscillator Values ad ...
- STM32H7时钟树RCC分析--- HAL库配置(二)
上一讲我们说了H7时钟树的一些基本概念,现在的话我们来用HAL库和CubeMx配置一下 再次说明,本文耗时较久,如果您想搞懂RCC初始化流程,请认真阅读,刚开始可能看不懂,但是仔细阅读之后绝对会有收获 ...
- STM32【H7】理论——综述、HAL库简述
文章目录 1. STM32H7芯片简介 1.1 STM32H7与STM32F1.F4系列芯片的区别 1.2 硬件框图 1.3 STM32H7各型号对比 1.4 总线框图和时钟 1.5 AXI总线 1. ...
- STM32F103C8T6基础开发教程(HAL库)—点亮第一颗LED灯
STM32F103C8T6基础开发教程目录 STM32F103C8T6基础开发教程(HAL库)-开发环境配置 STM32F103C8T6基础开发教程(HAL库)-Keil添加注释的快捷键 STM32F ...
最新文章
- pandas 中的函数—— .reset_index()
- 为什么孙悟空能大闹天宫,却打不过路上的妖怪?
- 无需另配定时器在STM32 HAL下实现微秒级延时(兼容FreeRTOS)
- (最短路 Floyd diskstra prim)Frogger --POJ--2253
- android实例教程_Android共享首选项示例教程
- redis srandmember_Redis五大数据类型使用场景
- VScode单步跟踪Nginx(虚拟机中搭建Nginx)源码
- mysql查询最接近的记录
- 2021-05-31驱动总裁万能网卡版
- 关于磁力计和加速度计的融合以及坐标系的对准
- 图片查看器-Python-tkinter
- 浏览器书签有效性验证
- shal()函数绕过和session验证绕过
- GIS开发:Contour(轮廓线)
- 非专业python学多久_非的解释|非的意思|汉典“非”字的基本解释
- TASKCTL4.1不同版本下载
- 松下PLC FP-XHC60T 程序 两个PLC通信控制11个轴 程序稳定已批量生产 注释完整 带威纶通触摸屏程序
- html转图片 workflow,用 Workflow + Day one 给未来的自己做时间履历 | Matrix 精选
- mysql 8 expire_logs_days 废弃 启用binlog_expire_logs_seconds设置binlog自动清除日志时间 阿里云RDS暂时不支持
- PostgreSQL的查询技巧: 零除, GENERATED STORED, COUNT DISTINCT, JOIN和数组LIKE