概述
本文主要是使用AT32F403A开发板,基于V2库的定时器溢出中断。
串口工具使用的Atlink-ez自带的串口功能。
工程建立、调试工具配置在前面章节有详细介绍。
硬件
硬件方面使用的是参考官方AT32F437 SURF板子而设计的一个AT32F403A开发板,板子上的芯片是AT32F403AVGT7的型号,开发板上面还板载了一个atlink-ez的仿真器,atlink-ez除了可以在线仿真和下载之外还有一个串口的功能,硬件上是通过跳线帽接到了MCU的串口1,pa9/10上面。
如下图是开发板pcb图,以及硬件资源。(左边上角的就是atlink-ez,用usb线接到pc即可):
如下是实物图:
本章是定时器中断的使用,是属于内部功能,只要最小核心系统正常,芯片就可以运行。
定时器
AT32F403A总的有14个定时器,种类有基本定时器、通用定时器、高级控制定时器,高级定时器拥有的功能最是丰富。
如下图是各个定时器的功能总表:
虽然不同类型的定时器支持的功能存在差异,但是都有一个基本的功能就是定时中断,也就是我们常说的溢出中断,本文主要是介绍这个功能的使用,后续使用到其他的功能,再对使用的功能进行介绍。
定时器的定时原理都是一样的,根据定时器的计数时钟来确定每计数1是多少时间,然后看重载值是多少就可以知道定时是多大的时间。上图的计数位数就是重载值的最大值,16位的,就是2的16次方,再通过设置分频和预分频就可以设置多种的时间定时。
软件
初始化
首先要使能定时器的时钟,然后设置预分频和重载值,设置计数方式,使能定时器中断,开启溢出中断,使能定时器,最后要编写中断服务函数。设置预分频系数为240,重载值为1000,溢出时间为1ms。
定时时间计算
首先要确定定时器的计数时钟,这个是由APB1/2总线给到定时器的时钟和定时器的分频系数以及预分频系数来决定的。如下图定时器是使用 APB1/2 (APB1和APB2总线)作为时钟,特别地,当 APB1/2 预分频系数是 1 时,定时器的时钟频率等于 APB1/2 的 时钟频率;当 APB 预分频系数不为 1 时,定时器的时钟频率等于 APB1/2 时钟频率的 2 倍。
一般AT32F403A时钟都是设置的系统时钟为240M,AHB是1分频,APB1/2是2分频,由于APB1/2的最高时钟是120M,当系统是240M的时候,APB的预分频至少是2,这个可以通过看时钟配置代码来确定,也就是system_clock_config()这个函数,下面的图片就是这个函数里面的部分代码。
如下图APB1和APB2的预分频都是2,那么定时器的时钟就是APB1/2 * 2 ,而APB1/2的时钟是AHB分频而来,AHB的时钟,下图中是设置的系统时钟的1分频,也就是AHB等于系统时钟。当系统时钟是240M,那么AHB=240M,APB1/2=AHB/2=240M/2=120M,定时器的时钟就是APB1/2*2=120M*2=240M,最后定时器的预分频系数是1时,则定时器的计数时钟就是240M/1=240M了。
系统时钟也可以使用函数获取得到,定义一个结构体作为参数,结构体是库定义好的直接使用即可,通过crm_clocks_freq_get()来获取到各个总线的时钟。
typedef struct
{
uint32_t sclk_freq; /*!< system clock frequency */
uint32_t ahb_freq; /*!< ahb bus clock frequency */
uint32_t apb2_freq; /*!< apb2 bus clock frequency */
uint32_t apb1_freq; /*!< apb1 bus clock frequency */
uint32_t adc_freq; /*!< adc clock frequency */
} crm_clocks_freq_type;
sclk_freq:系统时钟
ahb_freq:AHB时钟
apb1_freq:APB1时钟
apb2_freq:APB2时钟
adc_freq:ADC时钟
获取总线时钟代码:
/*
* 获取系统的时钟,ahp 、abp1、abp2、ADC的时钟
*
*
*/
void get_systemclk(void)
{
crm_clocks_freq_type get_rcc_clock;
crm_clocks_freq_get(&get_rcc_clock);
printf("get_rcc_clock.sclk_freq:%d\r\n",get_rcc_clock.sclk_freq);
printf("get_rcc_clock.ahb_freq:%d\r\n",get_rcc_clock.ahb_freq);
printf("get_rcc_clock.apb1_freq:%d\r\n",get_rcc_clock.apb1_freq);
printf("get_rcc_clock.apb2_freq:%d\r\n",get_rcc_clock.apb2_freq);
printf("get_rcc_clock.adc_freq:%d\r\n",get_rcc_clock.adc_freq);
}
本文是使用的通用定时器3,TMR3是在APB1总线下,系统时钟是240M,AHB预分频是1,APB1的预分频是2,那么TMR3的时钟就是AHB/2*2=240M/2*2=240M,TMR3的预分频系数是240,那么TMR3的计数时钟就是1M,每计数1,时间就是1us,当重载值是1000时,那么就是1000us溢出,溢出后又重新开始计数,以此循环。
初始化代码
中断服务函数
中断服务函数的和外部中断服务函数类似,有单独的也有公用的,如下图,用到那种就需要使用对应的中断服务函数,高级定时器的中断服务函数更多,这里我们是使用的溢出中断,当使用高级定时器1时则使用的是TMR1_OVF_TMR10_IRQHandler的中断服务函数。TMR3_GLOBAL_IRQHandler这种的就是包含TMR3的支持的所有类型的中断。
TMR3中断服务函数:
测试
tmr3中断一次1ms,每中断1000次会置sta1s为1,主循环中当sta1s为1时输出log。当功能正常时每1s会输出log.
测试代码
测试结果
获取到系统以及各个总线的时钟,开启串口助手的时间功能,可以看到log的间隔时间和我们设定时间是一致的,都是1s.
最后
有问题的可以加QQ群技术交流,同时相关代码上传到QQ群中。