当前位置: 首页 > news >正文

【雅特力AT32】时钟配置-延时阻塞函数-软件定时器 时钟源与源码分析

个人AT32原创学习笔记,转载需告知作者本人,并注明出处!

目录

总体概述:时钟源的选择与定时器设置

时钟源的选择
定时器与计数器计算

预分频系数
自动重装载值:
定时器周期计算公式
输出pwm设置占空比

源码分析

时钟配置
延时函数
软件定时器

总体概述:时钟源的选择与定时器设置

分析源码前,先来简单了解一下,如果需要详细了解AT32内部时钟机制与配置流程及New_Clock_Configuration的安装与使用,请移步:

【雅特力AT32】 时钟配置及New_Clock_Configuration

无论是时钟配置还是定时器,都离不开一个核心的东西–时钟源,他们都有自己默认的时钟源,也可以用户自定义选择,个人建议结合代码编写需结合时钟树和技术手册,这样更容易快速上手。

时钟源的选择

个人分析所得,如有纰漏,愿闻指正,详细请查找计数手册。
一定要看技术手册和时钟树,如果想具体研究弄明白!!!

系统时钟配置:1. HICK:48mhz(默认8mhz)2. HEXT:4-25mhz;延时函数:	 1. systick_clock_source_config来选择systemclock(HICK);2. 默认 systick_clock,即为经分频后到systick外设的时钟;定时器:	  1. 用户自定义,常见:systemclock/(10000-1)
定时器与计数器计算

玩定时器有很多人卡在这里,我也很久没用了,这里复习总结一下。

预分频系数
预分频系数就是将频率分割,比如分频系数是72,则该时钟的频率会变成72MHZ/72=1MHZ,但是在设置的时候要注意,数值应该是72-1。
假定分频系数是72-1,那么频率变成1MHZ,也就意味着STM32在一秒钟会数1M次,即1us数一次(实际上是上升沿/下降沿)。
自动重装载值:
需要定时1ms,由于1ms=1us*1000,那么预装载值就是1000-1,如此类推;
在预分频系数确定的情况下,定时的时长就由预装载值确定了,到了设定的重装载值,计数器溢出;
定时器周期计算公式
T =(arr+1) * (PSC+1) / Tck 
其中T为单个周期时间,TCK为时钟频率,PSC为时钟预分频系数,arr为自动重装载值
输出pwm设置占空比
通道数据对应设置即可。
如下图:占空比 =(499+1)/(999+1)=	50%;

在这里插入图片描述

源码分析

时钟配置

自行选择时钟源,时钟配置步骤与New_Clock_Configuration下载使用可移步见文章开头介绍的笔者另一篇文章。

/*** @brief  system clock config program* @note   the system clock is configured as follow:*         system clock (sclk)   = (hext * pll_ns)/(pll_ms * pll_fr) / 2*         system clock source   = pll (hext)*         - hext                = HEXT_VALUE		8MHz*         - sclk                = 144000000*         - ahbdiv              = 1*         - ahbclk              = 144000000*         - apb2div             = 1*         - apb2clk             = 144000000*         - apb1div             = 2*         - apb1clk             = 72000000*         - pll_ns              = 72*         - pll_ms              = 1*         - pll_fr              = 1* @param  none* @retval none*/
void system_clock_config(void)
{/* reset crm */crm_reset();											//复位crm时钟配置/* config flash psr register */flash_psr_set(FLASH_WAIT_CYCLE_4);					//配置flash PSR寄存器/* enable pwc periph clock:启用PWC外围时钟  */crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);	//启用 PWC 外设时钟/* ensure system clock to highest, set power ldo output voltage to 1.3v */pwc_ldo_output_voltage_set(PWC_LDO_OUTPUT_1V3);		//置 LDO 输出电压为最高 1.3Vcrm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE);	//启用 PLL,并等待 PLL 稳定。/* wait till hext is ready */while(crm_hext_stable_wait() == ERROR){}/* config pll clock resourcecommon frequency config list: pll source selected  hick or hext(8mhz)_____________________________________________________________________________|        |         |         |         |         |         |        |        || sysclk |   150   |   144   |   120   |   108   |   96    |   72   |   36   ||________|_________|_________|_________|_________|_________|_________________||        |         |         |         |         |         |        |        ||pll_ns  |   75    |   72    |   120   |   108   |   96    |   72   |   72   ||        |         |         |         |         |         |        |        ||pll_ms  |   1     |   1     |   1     |   1     |   1     |   1    |   1    ||        |         |         |         |         |         |        |        ||pll_fr  |   FR_2  |   FR_2  |   FR_4  |   FR_4  |   FR_4  |   FR_4 |   FR_8 ||________|_________|_________|_________|_________|_________|________|________|if pll clock source selects hext with other frequency values, or configure pll to otherfrequency values, please use the at32 new clock  configuration tool for configuration. *//*	配置 PLL 时钟资源:选择 HEXT(8MHz) 作为 PLL 的时钟源;PLL 参数:pll_ns = 72, pll_ms = 1, pll_fr = FR_2PLL时钟 = (hext * pll_ns) / (pll_ms * pll_fr) / 2 = (8MHz * 72) / (1 * 2) / 2 = 144MHz	*/crm_pll_config(CRM_PLL_SOURCE_HEXT, 72, 1, CRM_PLL_FR_2);//启用 PLL,并等待 PLL 稳定。/* enable pll */crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE);/* wait till pll is ready */while(crm_flag_get(CRM_PLL_STABLE_FLAG) != SET){}//配置 AHB 时钟分频为 1,APB2 时钟分频为 1,APB1 时钟分频为 2/* config ahbclk */crm_ahb_div_set(CRM_AHB_DIV_1);		//AHB总线不分频:144HZ/* config apb2clk, the maximum frequency of APB2 clock is 150 MHz  */crm_apb2_div_set(CRM_APB2_DIV_1);		//AHB2不分频/* config apb1clk, the maximum frequency of APB1 clock is 120 MHz  */crm_apb1_div_set(CRM_APB1_DIV_2);			//AHB1由2分频:  72HZ//启用自动步进模式,选择 PLL 作为系统时钟源/* enable auto step mode */crm_auto_step_mode_enable(TRUE);/* select pll as system clock source */crm_sysclk_switch(CRM_SCLK_PLL);/* wait till pll is used as system clock source */while(crm_sysclk_switch_status_get() != CRM_SCLK_PLL){}//等待系统时钟源切换到 PLL//关闭自动步进模式,更新系统核心时钟变量。/* disable auto step mode */crm_auto_step_mode_enable(FALSE);/* update system_core_clock global variable */system_core_clock_update();
}

对应New_Clock_Configuration工具设置,这玩意挺好用,有啥疑问拿代码对照一些挺好,有需要雅特力官网下载即可:

在这里插入图片描述

延时函数

如果不使用下文函数选择,默认为ahb总线上的sys tick为时钟源(如上图);

配置SysTick定时器时钟源(延时函数时钟源选择):systick_clock_source_config()

/*** @brief  config systick clock source* @param  source*         this parameter can be one of the following values:*         - SYSTICK_CLOCK_SOURCE_AHBCLK_DIV8*         - SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV* @retval none*/
void systick_clock_source_config(systick_clock_source_type source)
{if(source == SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV){SysTick->CTRL |= SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV;}else{SysTick->CTRL &= ~(uint32_t)SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV;}
}

典例

/*** @brief  initialize delay function* @param  none* @retval none*/
void delay_init()
{/* configure systick */systick_clock_source_config(SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV);fac_us = system_core_clock / (1000000U);	//system_core_clock为 HEXT时钟(系统核心时钟)8Mhz,则fac_us为80hzfac_ms = fac_us * (1000U);
}/*** @brief  inserts a delay time.* @param  nus: specifies the delay time length, in microsecond.* @retval none*/
void delay_us(uint32_t nus)
{uint32_t temp = 0;SysTick->LOAD = (uint32_t)(nus * fac_us);		//SysTick定时器将被配置为在装载值为80时进行计数,每计数一个单位的时间为1微秒;SysTick->VAL = 0x00;							//VAL寄存器存储的当前计数值清零,确保计时器从0开始计数,实现精确的延时时间。SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;		//启用了SysTick定时器,让其开始计时,以完成延时操作do{temp = SysTick->CTRL;//循环继续的条件为当前定时器计数位为1(temp & 0x01为真),且定时器溢出位为0(!(temp & (1 << 16))为真)}while((temp & 0x01) && !(temp & (1 << 16)));	//判断计数(80)是否溢出(80hz/1us)SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;		//关闭SysTick定时器,CTRL与上使能值的取反值,即清除使能位SysTick->VAL = 0x00;
}/*** @brief  inserts a delay time.* @param  nms: specifies the delay time length, in milliseconds.* @retval none*/
void delay_ms(uint16_t nms)
{uint32_t temp = 0;while(nms){if(nms > STEP_DELAY_MS){SysTick->LOAD = (uint32_t)(STEP_DELAY_MS * fac_ms);nms -= STEP_DELAY_MS;}else{SysTick->LOAD = (uint32_t)(nms * fac_ms);nms = 0;}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;}
}/*** @brief  inserts a delay time.* @param  sec: specifies the delay time, in seconds.* @retval none*/
void delay_sec(uint16_t sec)
{uint16_t index;for(index = 0; index < sec; index++){delay_ms(500);delay_ms(500);}
}
软件定时器

主要展示如何使用软件定时器的配置,典例主要功能如下:
tmr1溢出中断会产生软件触发。
Led2开关表示tmr1溢出中断响应。
Led3和led4切换表示终止第4行中断响应。

/***************************************************************************** @file     main.c* @brief    main program**************************************************************************展示了如何使用软件触发器退出。tmr1溢出中断会产生软件触发。Led2开关表示tmr1溢出中断响应。Led3和led4切换表示终止第4行中断响应。***************************************************************************/#include "at32a423_board.h"
#include "at32a423_clock.h"/** @addtogroup AT32A423_periph_examples* @{*//** @addtogroup 423_EXINT_exint_software_trigger EXINT_exint_software_trigger* @{*/void exint_line4_config(void);		//配置外部中断线4
static void tmr1_config(void);uint8_t i,num,num1 = 0;/*** @brief  exint line4 config. configure pa0 in interrupt mode* @param  None* @retval None*/
void exint_line4_config(void)
{exint_init_type exint_init_struct;crm_periph_clock_enable(CRM_SCFG_PERIPH_CLOCK, TRUE);			//启用外设时钟exint_default_para_init(&exint_init_struct);					//初始化外部中断配置结构exint_init_struct.line_enable = TRUE;							//启用外部中断线exint_init_struct.line_mode = EXINT_LINE_INTERRUPT;			//置外部中断模式为中断模式exint_init_struct.line_select = EXINT_LINE_4;					//选择外部中断线4exint_init_struct.line_polarity = EXINT_TRIGGER_RISING_EDGE;	//设置触发极性为上升沿触发exint_init(&exint_init_struct);		//初始化外部中断exint_flag_clear(EXINT_LINE_4);		//清除外部中断标志nvic_irq_enable(EXINT4_IRQn, 0, 1);	//使能外部中断4的 NVIC 中断
}/*** @brief  tmr1 configuration.* @param  none* @retval none* 配置定时器1,使其以1Hz的频率触发溢出中断,并在溢出时执行相应的中断处理程序*/
static void tmr1_config(void)
{crm_clocks_freq_type crm_clocks_freq_struct = {0};	//定义并初始化系统时钟频率结构体/* get system clock */crm_clocks_freq_get(&crm_clocks_freq_struct);			//获取系统时钟频率crm_periph_clock_enable(CRM_TMR1_PERIPH_CLOCK, TRUE);	//启用定时器1的外设时钟/* (systemclock / (system_core_clock/10000)) / 10000 = 80Hz(1s) *//* 设置定时器1的基本参数,包括计数器的重载值和预分频器的值,以实现80Hz(1秒)的定时功能 */tmr_base_init(TMR1, 10000-1, system_core_clock/10000-1);	//初始化定时器基本参数 ((uint32_t)8000000) /*!< 高速内部时钟的取值(hz) */tmr_cnt_dir_set(TMR1, TMR_COUNT_UP);						//设置定时器计数方向为向上计数tmr_clock_source_div_set(TMR1, TMR_CLOCK_DIV1);			//设置定时器时钟源分频tmr_interrupt_enable(TMR1, TMR_OVF_INT, TRUE);			//使能定时器1的溢出中断nvic_irq_enable(TMR1_OVF_TMR10_IRQn, 0, 0);				//使能定时器1的 NVIC 中断
}/*** @brief  tmr1 interrupt handler* @param  none* @retval none*/
void TMR1_OVF_TMR10_IRQHandler(void)
{//检查定时器溢出中断标志位是否被设置:定时中断if(tmr_interrupt_flag_get(TMR1,TMR_OVF_FLAG) != RESET)	{//生成exint软件中断事件(触发1s定时器中断):EXINT_LINE_4at32_led_toggle(LED2);//	  printf("1s counter: %d\r\n", num1++);exint_software_interrupt_event_generate(EXINT_LINE_4);tmr_flag_clear(TMR1,TMR_OVF_FLAG);}
}/*** @brief  exint4 interrupt handler* @param  none* @retval none*/
void EXINT4_IRQHandler(void)
{//检查外部中断标志位是否被设置if(exint_interrupt_flag_get(EXINT_LINE_4) != RESET){at32_led_toggle(LED3);at32_led_toggle(LED4);
//	if(i++ == 3)	//三秒一次中断
//	{
//		delay_us(5);
//		printf("3s counter: %d\r\n", num++);
//		i = 0;
//	}exint_flag_clear(EXINT_LINE_4);}
}/*** @brief  main function.* @param  none* @retval none*/
int main(void)
{nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);system_clock_config();at32_board_init();/* turn led2/led3/led4 on */at32_led_off(LED2);at32_led_off(LED3);at32_led_off(LED4);exint_line4_config();tmr1_config();tmr_counter_enable(TMR1, TRUE);while(1){}
}

http://www.mrgr.cn/news/28546.html

相关文章:

  • 3D意识(3D Awareness)浅析
  • 【时间之外】IT人求职和创业应知【31】
  • 软件测试必学的16个高频数据库操作及命令
  • 【IEEE/EI会议】第八届先进电子材料、计算机与软件工程国际学术会议(AEMCSE 2025)
  • mongoDB的安装及使用
  • WebRTC视频 01 - 视频采集整体架构
  • SOMEIP_ETS_112: SD_Empty_Option
  • 【C++学习】 IO 流揭秘:高效数据读写的最佳实践
  • 火语言RPA流程组件介绍--浏览器页面操作
  • Qt多元素控件——QTableWidget
  • Go小专栏 第一期
  • PostgreSQL的表压缩
  • 【C++题解】1997. 孤独的素数
  • ChatGPT有三个快捷指令和三个模式,你知道吗?
  • 为了有了ReentrantLock还需要ReentrantReadWriteLock?
  • golang学习笔记25——golang 实现 MD5加密、RSA加密 和 Base64编码
  • c#visionpro开发 方法统计
  • 【蓝牙协议栈】精讲蓝牙PCM和URAT
  • 九、并查集-算法总结
  • Linux进阶 修改文件权限
  • 坚持的力量--完成向CSDN迁移500篇技术文章阶段小记-以此自勉
  • Java应用的日志记录策略:有效监控与调试
  • Flask 第九课 -- 表单处理
  • DepthCrafter:为开放世界视频生成一致的长深度序列
  • AWS 将 OpenSearch 纳入 Linux 基金会旗下
  • Vue3 项目实战甄选硅谷