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

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

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

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

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

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

目录

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

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

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

源码分析

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

时钟源的选择

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

系统时钟配置: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.3V

  crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE);	//启用 PLL,并等待 PLL 稳定。
  /* wait till hext is ready */
  while(crm_hext_stable_wait() == ERROR){}

  /* config pll clock resource
  common 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 other
  frequency 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_2
		PLL时钟 = (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为80hz
  fac_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;					//选择外部中断线4
  exint_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_4
    at32_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)
  {
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值