art.1 timer介绍
MM32系列MCU有三种定时器,分为基本定时器,通用定时器和高级定时器。
基本定时器TIM14、TIM16和TIM17是均为16位的只能递增计数的定时器,可以定时,有1个外部IO。
通用定时器TIM2是一个32位的可以递增、递减、递增/递减计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有4个外部IO。通用定时器TIM3是一个16位的可以递增、递减、递增/递减数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有4个外部IO。
高级定时器TIM1/8是均为16位的可以递增、递减、递增/递减计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有4对外部IO。
1.1时钟源
定时器时钟TIMxCLK,即内部时钟CK_INT,经APB1/APB2预分频器后分频提供,如果APB1/APB2预分频系数等于1,则频率不变,否则频率乘以2,库函数中APB1预分频的系数是2,即PCLK1=24M,所以定时器时钟TIMxCLK=24*2=48M。
1.2 计数器时钟
定时器时钟经过PSC预分频器之后,即CK_CNT,用来驱动计数器计数。PSC是一个16位的预分频器,可以对定时器时钟TIMxCLK进行1~65536之间的任何一个数进行分频。
具体计算方式为:CK_CNT=TIMxCLK/(PSC+1)
1.3计数器
计数器CNT是一个16/32位的计数器,可以递增、递减、递增/递减计数,最大计数值为65535/(232-1)。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。
1.4自动重装载寄存器
自动重装载寄存器ARR是一个16位/32位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
1.5定时时间的计算
定时器的定时时间等于计数器的中断周期乘以中断的次数。计数器在CK_CNT的驱动下,计一个数的时间则是CK_CLK的倒数,等于:1/(TIMxCLK/(PSC+1)),产生一次中断的时间则等于:1/(CK_CLK*ARR)。如果在中断服务程序里面设置一个变量time,用来记录中断的次数,那么就可以计算出我们需要的定时时间等于:1/CK_CLK*(ARR+1)*time。
AMetal平台提供了定时器初始化函数,可以实现基础定时、PWM输出和输入捕获等功能,用户对照MM32技术手册直接调用相关函数即可实现需要的功能,下面我们一起来熟悉AMetal平台提供的相关函数接口使用方法。
Part.2 定时器初始化
2.1定时器初始化
AMetal平台提供了定时器初始化函数,可以直接调用初始化函数。以定时器1初始化函数为例,函数原型为:
am_timer_handle_t am_mm32l073_tim1_timing_inst_init(void);
该函数在user_config目录下的am_hwconf_mm32l073_tim_timing.c文件中定义,在am_mm32l073_inst_init.h中声明。因此使用定时器初始化函数时,需要包含头文件am_mm32l073_inst_init.h。
初始化定时器1,调用该函数时需要定义一个am_timer_handle_t类型的变量,用于保存获取的定时器1服务句柄,定时器初始化程序为:
am_timer_handle_t tim1_timing_handle;
tim1_timing_handle=am_mm32l073_tim1_timing_inst_init();
2.2定时器PWM初始化
AMetal平台提供了定时器的PWM初始化函数,可以直接调用初始化函数。MM32L073核心板上的LED可通过跳线帽连接到PB1引脚,PB1可复用为TIM3_CH4。因此以定时器3 PWM初始化函数为例,函数原型为:
am_pwm_handle_t am_mm32l073_tim3_pwm_inst_init(void);
该函数在user_config目录下的am_hwconf_mm32l073_tim_pwm.c文件中定义,在am_mm32l073_inst_init.h中声明。因此使用定时器3的PWM初始化函数时,需要包含头文件am_mm32l073_inst_init.h。
初始化定时器3PWM,调用该函数时需要定义一个am_pwm_handle_t类型的变量,用于保存获取的定时器3PWM服务句柄,定时器3PWM初始化程序为:
am_pwm_handle_tpwm_handle;
pwm_handle=am_mm32l073_tim3_pwm_inst_init();
2.3输入捕获初始化
AMetal平台提供了定时器的输入捕获初始化函数,可以直接调用初始化函数。以定时器2的输入捕获初始化函数为例,函数原型为:
am_cap_handle_t am_mm32l073_tim2_cap_inst_init(void);
该函数在user_config目录下的am_hwconf_mm32l073_tim_cap.c文件中定义,在am_mm32l073_inst_init.h中声明。因此使用定时器2的输入捕获初始化函数时,需要包含头文件am_mm32l073_inst_init.h。
初始化定时器2输入捕获,调用该函数时需要定义一个am_cap_handle_t类型的变量,用于保存获取的定时器2输入捕获的服务句柄,定时器2出入捕获初始化程序为:
am_cap_handle_tcap_handle;
cap_handle=am_mm32l073_tim2_cap_inst_init();
Part.3 接口函数
3.1定时器定时接口函数
定时器的定时时间与时钟频率、分频系数、计数器位数有关。为了准确配置定时器的定时时间,需要知道定时器的时钟频率、可配置的分频系数、计数器位数。AMetal平台已提供获取定时器时钟频率、信息的函数,在am_timer.h中以内联函数的形式定义,am_timer.h可在service目录下的am_timer.c包含的头文件中找到。
获取定时器信息,主要获取时钟频率、可配置的分频系数值、定时器位数。以定时器1为例,获取定时器1的时钟频率,函数原型为:
int am_timer_clkin_freq_get(am_timer_handle_thandle,uint32_t*p_freq);
- handle为定时器的服务句柄,即为初始化定时器1获取的句柄
- p_freq为指向时钟频率的数据指针,即传入一个内存地址
基于以上信息,获取时钟频率的程序为:
uint32_tclkin_freq=0;
am_timer_clkin_freq_get(tim1_timing_handle,&clkin_freq);
获取定时器信息的函数原型为:
Const am_timer_info_t *am_timer_info_get(am_timer_handle_thandle);
- handle为定时器的服务句柄,即为初始化定时器1获取的句柄
- 返回值为am_timer_info_t类型的指针,因此需要定义一个am_timer_info_t类型的指针,用于保存返回值
基于以上信息,获取定时器1信息的程序为:
constam_timer_info_t *p_tim1_timing_info;
p_tim1_timing_info=am_timer_info_get(tim1_timing_handle);
获取的信息,可以通过调试或用串口打印看到,进而根据定时器时钟频率和信息配置定时器。以串口打印获取的信息为例,程序如下所示。通过串口信息,可以清楚的知道定时器的相关信息,为后面定时器的配置做了准备。
打印定时器信息:
AM_DBG_INFO("clk_freq=%d\r\n",clkin_freq);
AM_DBG_INFO("width=%d\r\n",p_tim1_timing_info->counter_width);
AM_DBG_INFO("chan=%d\r\n",p_tim1_timing_info->chan_num);
AM_DBG_INFO("prescaler=%d\r\n",p_tim1_timing_info->prescaler);
3.2定时器编写回调函数
当定时时间到,调用回调函数,定时执行回调函数。回调函数与普通函数一样,程序如下所示。
定时器1回调函数:
void tim1_timing_callback(void*p_arg)
{
AM_DBG_INFO("Timingtime!\r\n");
am_gpio_toggle(PIOB_1);
}
编写的定时回调函数需要与定时器关联起来,需要设置回调函数。设置回调函数原型为:
int am_timer_callback_set (
am_timer_handle_t handle,
uint8_t chan,
void (*pfn_callback)(void *),
void *p_arg);
- handle为定时器的服务句柄,即为初始化定时器1获取的句柄
- chan为使用的定时器通道,定时器1只有一个通道
- (*pfn_callback)(void*)为回调函数,即将定义的回调函数名传入
- p_arg为回调函数的参数
基于以上信息,设置回调函数的程序为:
am_timer_callback_set(tim1_timing_handle,0,tim1_timing_callback,NULL);
3.3定时器配置定时时间
通过获取定时器时钟频率、定时器信息,可知时钟频率为48MHz,计数器宽度为16位,最大计数值为65535,分频器最大值为65536。AMetal提供了定时器分频值设置函数、定时计数值设置函数,函数均定义在am_timer.h中。
分频值设置函数在原型为:
int am_timer_prescale_set (
am_timer_handle_t handle,
uint8_t chan,
uint32_t prescale);
- handle为定时器的服务句柄,即为初始化定时器1获取的句柄
- chan为使用的定时器通道
- prescale为定时器时钟频率分频值
定时计数值设置函数原型为:
intam_timer_enable(am_timer_handle_thandle,uint8_tchan,uint32_tcount);
- handle为定时器的服务句柄,即为初始化定时器1获取的句柄
- chan为使用的定时器通道
- count为设置的最大计数值,计数器计数到该数值,调用回调函数,计数值清零重新计数
基于以上信息,设置分频系数为48000,计数值为500,即定时时间为500ms,具体程序为:
am_timer_prescale_set(tim1_timing_handle,0,48000);
am_timer_enable(tim1_timing_handle,0,500);
3.4PWM输出接口函数
先进行PWM参数配置,PWM参数包括频率、占空比、通道。AMetal平台提供了PWM参数配置的函数,可以直接使用。该函数以内联函数的形式定义在am_pwm.h中,am_pwm.h可以在soc目录下am_zlg_tim_pwm.c包含的头文件中找到。其函数原型为:
int am_pwm_config (
am_pwm_handle_t handle,
int chan,
unsigned long duty_ns,
unsigned long period_ns);
- handle为PWM的服务句柄,传入的实参为PWM初始化获取的服务句柄
- chan为使用的PWM通道,通道号从0开始,因此TIM3_CH4的通道号为3
- duty_ns为PWM脉宽时间,单位为ns,以PWM周期500ms为例,50%占空比时的脉宽时间为250ms,传入的是实参为250000000
- period_ns为PWM周期时间,单位ns,以PWM周期500ms为例,传入的实参为500000000
根据以上信息,配置PWM周期为500ms,占空比为50%的程序为:
am_pwm_config(pwm_handle,3,250000000,500000000);
PWM初始化、配置参数、使能后即可从对应I/O引脚输出PWM波形。AMetal平台提高了PWM使能、禁能函数,函数定义am_pwm.h中。PWM使能函数原型为:
int am_pwm_enable(am_pwm_handle_thandle,intchan);
- handle为PWM的服务句柄,传入的实参为PWM初始化获取的服务句柄
- chan为使用的PWM通道,即PWM配置参数时配置的通道
基于以上信息,PWM使能程序为:
am_pwm_enable(pwm_handle,3);
3.5CAP输入捕获接口函数
当捕获事件发生时,调用回调函数,执行用户任务程序。以测量输入脉冲的周期为例,编写的回调函数如下所示。其中形式参数count为计数器的值。
捕获事件回调函数:
void cap_callback(void*p_arg,unsignedintcount)
{
staticuint8_ttimes=0;//标记事件发生次数
IF(times==0)
{//第一次上升沿事件
g_time_ns=count;
times=1;
}
else//第二次上升沿事件
{
if(count>g_time_ns)
{//计数器没有溢出
g_time_ns=count-g_time_ns;
}
else
{//计数器溢出
g_time_ns=count+(0xffffffff-g_time_ns);
}
times=0;
g_cap_flag=1;
}
}
输入捕获可选择捕获通道、捕获事件的类型,可设置捕获事件回调函数。AMetal平台提供了输入捕获配置函数,该函数定义在am_cap.h中,am_cap.h包含在soc目录下的am_zlg_tim_cap.c的头文件。输入捕获配置函数原型为:
int am_cap_config (
am_cap_handle_t handle,
int chan,
unsigned int options,
am_cap_callback_t pfn_callback,
void *p_arg);
- handle为输入捕获的服务句柄,传入实参为初始化输入捕获时获取的句柄cap_handle
- chan为使用的输入捕获通道,通道号从0开始,使用TIM2_CH2,通道为1
- options为输入捕获事件类型,可配置为上升沿、下降沿、双边沿,可取值已在am_cap.h中使用宏定义。以上升沿触发捕获为例,实参为AM_CAP_trigger_RISE
- pfn_callback为输入捕获回调函数,即为回调函数名cap_callback
- p_arg为回调函数参数,无参数即为NULL
宏名
| 功能说明
| AM_CAP_TRIGGER_RISE
| 上升沿触发捕获
| AM_CAP_TRIGGER_FALL
| 下降沿触发捕获
| AM_CAP_TRIGGER_BOTH_EDGES
| 双边沿触发捕获
| 表1 输入捕获事件类型
基于以上信息,输入捕获配置程序为:
am_cap_config(cap_handle,1,AM_CAP_TRIGGER_RISE,cap_callback,NULL);
配置完捕获通道、捕获事件类型、捕获回调函数,使能捕获通道后,当发生事件便会执行回调函数。使能捕获通道函数定义在am_cap.h中,函数原型为:
int am_cap_enable(am_cap_handle_thandle,intchan);
- handle为输入捕获的服务句柄,传入实参为初始化输入捕获时获取的句柄cap_handle
- chan为初始化的输入捕获通道,通道为1
基于以上信息,使能输入捕获的程序为:
am_cap_enable(cap_handle,1);
Part.4 应用实例
4.1定时示例程序
定时500ms,在回调函数中翻转LED,并通过串口打印信息,程序如下所示。
定时器定时范例程序:
#include"ametal.h"
#include"am_board.h"
#include"am_vdebug.h"
#include"am_delay.h"
#include"am_led.h"
#include"am_gpio.h"
#include"mm32l073_pin.h"
#include"am_mm32l073_inst_init.h"
void tim1_timing_callback(void*p_arg)
{
AM_DBG_INFO("Timingtime!\r\n");
am_gpio_toggle(PIOB_1);
}
int am_main(void)
{
am_timer_handle_ttim1_timing_handle;//定时器服务句柄
constam_timer_info_t*p_tim1_timing_info;//定时器信息
uint32_tclkin_freq=0;//定时器时钟频率
AM_DBG_INFO("Startupsuccessful!\r\n");
//初始化LEDGPIO
am_gpio_pin_cfg(PIOB_1,AM_GPIO_OUTPUT_INIT_HIGH);
//初始化定时器
tim1_timing_handle=am_mm32l073_tim1_timing_inst_init();
//获取定时器时钟频率
am_timer_clkin_freq_get(tim1_timing_handle,&clkin_freq);
AM_DBG_INFO("clk_freq=%d\r\n",clkin_freq);
//获取定时器信息
p_tim1_timing_info=am_timer_info_get(tim1_timing_handle);
AM_DBG_INFO("width=%d\r\n",p_tim1_timing_info->counter_width);
AM_DBG_INFO("chan_num=%d\r\n",p_tim1_timing_info->chan_num);
AM_DBG_INFO("prescaler=%d\r\n",p_tim1_timing_info->prescaler);
//设置回调函数
am_timer_callback_set(tim1_timing_handle,0,tim1_timing_callback,NULL);
//设置时钟分频系数
am_timer_prescale_set(tim1_timing_handle,0,48000);
//设置定时中断频率为2Hz
am_timer_enable(tim1_timing_handle,0,500);
while(1)
{
}
}
4.2PWM示例程序
以定时器3的PWM通道4为例,从PB1引脚输出周期为500ms、50%占空比的PWM,控制LED闪烁,程序如下所示。编译下载后,可以看到LED0不断闪烁。
PWM范例程序:
#include"ametal.h"
#include"am_board.h"
#include"am_vdebug.h"
#include"am_delay.h"
#include"am_led.h"
#include"am_mm32l073_inst_init.h"
intam_main(void)
{
am_pwm_handle_tpwm_handle;
AM_DBG_INFO("Startupsuccessful!\r\n");
pwm_handle=am_mm32l073_tim3_pwm_inst_init();
am_pwm_config(pwm_handle,3,250000000,500000000);
am_pwm_enable(pwm_handle,3);
while(1)
{
}
}
4.3输入捕获示例程序
将PA1配置为定时器2的TIM2_CH2输入捕获,测量从PB1引脚输出定时器3的PWM的周期,范例程序如下所示。短接PA1、PB2引脚,可以看到测量脉冲周期与配置的PWM周期一致。
输入捕获范例程序:
#include"ametal.h"
#include"am_board.h"
#include"am_vdebug.h"
#include"am_delay.h"
#include"am_mm32l073_inst_init.h"
uint32_tg_time_ns=0;
uint8_tg_cap_flag=0;
voidcap_callback(void*p_arg,unsignedintcount)
{
static uint8_t times=0;//标记事件发生次数
if(times==0)
{//第一次上升沿事件
g_time_ns=count;
times=1;
}
else
{
//第二次上升沿事件
if(count>g_time_ns){//计数器没有溢出
g_time_ns=count-g_time_ns;
}
else
{//计数器溢出
g_time_ns=count+(0xffffffff-g_time_ns);
}
times=0;
g_cap_flag=1;
}
}
int am_main(void)
{
am_pwm_handle_tpwm_handle;
am_cap_handle_tcap_handle;
AM_DBG_INFO("Startupsuccessful!\r\n");
pwm_handle=am_mm32l073_tim3_pwm_inst_init();
am_pwm_config(pwm_handle,3,5000000,10000000);
am_pwm_enable(pwm_handle,3);
cap_handle=am_mm32l073_tim2_cap_inst_init();
am_cap_config(cap_handle,1,AM_CAP_TRIGGER_RISE,cap_callback,NULL);
am_cap_enable(cap_handle,1);
while(1)
{
if(g_cap_flag)
{
g_cap_flag=0;
//将计数值换算成时间
am_cap_count_to_time(cap_handle,1,0,g_time_ns,&g_time_ns);
AM_DBG_INFO("PWM_period=%dns\r\n",g_time_ns);
}
}
}
|
|