[其他]

灵动微课堂 (第128讲) | 基于MM32 MCU的OS移植与应用——AMetal ...

[复制链接]
333|2
手机看帖
扫描二维码
随时随地手机跟帖
cr315|  楼主 | 2021-2-3 17:08 | 显示全部楼层 |阅读模式
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);
  }
}
}

使用特权

评论回复
在水一方00| | 2021-2-28 22:42 | 显示全部楼层
短接PA1、PB2引脚,可以看到测量脉冲周期与配置的PWM周期一致。

使用特权

评论回复
yangxiaor520| | 2021-3-2 10:42 | 显示全部楼层
这又是啥子OS?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1190

主题

2989

帖子

0

粉丝