目录
概述
本文主要是使用AT32F403A开发板,基于V2库使用通用和高级定时器输出pwm。
串口工具使用的Atlink-ez自带的串口功能。
工程建立、调试工具配置在前面章节有详细介绍。
硬件
硬件方面使用的是参考官方AT32F437 SURF板子而设计的一个AT32F403A开发板,板子上的芯片是AT32F403AVGT7的型号,开发板上面还板载了一个atlink-ez的仿真器,atlink-ez除了可以在线仿真和下载之外还有一个串口的功能,硬件上是通过跳线帽接到了MCU的串口1,pa9/10上面。
如下图是开发板pcb图,以及硬件资源。(左边上角的就是atlink-ez,用usb线接到pc即可):
如下是实物图:
本章是pwm输出的功能,TMR3使用的通道1(PA6),TMR1使用的通道1(PA8),示波器直接接到对应的排针上即可。
PWM
脉冲宽度调制(PWM),简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。核心在对脉冲的宽度的控制。
AT32F403A的14个定时器中除了TMR6/7这两个基本定时器之外,其他的定时器都支持输出pwm的功能,通用定时器的支持4个通道输出pwm,高级定时器的支持7个通道输出pwm,其中有三组是互补通道。
软件
初始化
本章TMR3使用的通道1(PA6),TMR1使用的通道1(PA8),先要设置gpio为复用推挽输出。
使能定时器的时钟,然后设置预分频、重载值,也就是pwm的周期频率,设置计数方式,设置通道的输出参数,设置占空比,最后使能定时器。
PWM周期
先通过预分频以及系统时钟和apb总线分频算出定时器的工作时钟,这个在上一章有详细介绍。当定时器工作时钟为240M,重载值为240时,那么输出的pwm的周期就是1us,也就是1M的频率;当重载值为480时,那么输出的pwm的周期就是2us,也就是0.5M的频率。
Pwm波形配置
如下是输出通道的参数配置:
typedef struct
{
tmr_output_control_mode_type oc_mode; /*!< output channel mode */
confirm_state oc_idle_state; /*!< output channel idle state */
confirm_state occ_idle_state; /*!< output channel complementary idle state */
tmr_output_polarity_type oc_polarity; /*!< output channel polarity */
tmr_output_polarity_type occ_polarity; /*!< output channel complementary polarity */
confirm_state oc_output_state; /*!< output channel enable */
confirm_state occ_output_state; /*!< output channel complementary enable */
} tmr_output_config_type;
oc_mode:通道输出模式
oc_idle_state:通道空闲状态
occ_idle_state:互补通道空闲状态
oc_polarity:通道输出极性
occ_polarity:互补通道输出极性
oc_output_state:通道输出状态
occ_output_state:互补通道输出状态
实际输出的波形除了跟输出模式和计数方向还跟输出极性有关。OWCDIR是计数方向,0为向上,1为向下,TMRx_C1DT是通道1的数据寄存器值,也就是我们设置的占空比的值,TMRx_CVAL是计数值,C1ORAM是通道1中间信号,当设置为PWM模式A,向上的计数方式的时候,TMRx_CVAL计数值小于TMRx_C1DT通道1的值的时候C1ORAM中间信号是为高电平,大于的时候,C1ORAM为低电平。极性就是输出的有效信号,最终实际输出的波形是中间信号C1ORAM和极性来决定的,当设置极性为高有效的时候,即输出的波形的高电平段就是C1ORAM中间信号的高电平段,低电平段是C1ORAM的低电平段,也就是输出的波形和C1ORAM的波形是一致的;当设置极性为低电平有效的时候,输出的波形的高电平段即为C1ORAM中间信号的低电平段,低电平段是C1ORAM的高电平段。这里是以通道1为例,其他通道也是一样的。
所以设置向上计数方式,PWM模式A,极性为高,那么在输出的波形的一个周期里面就是先高电平,后低电平,高低的电平的持续时间就是通道数据寄存器的值来决定,占空比也就是通道数据寄存器的值除以定时器的重载值。要验证上述的时候,一定要设置占空比不是50%,不然高低电平的时间一样,不好区分。
需要注意的是高级定时器的pwm输出和通用的定时器的有一些不同,首先是高级定时器有互补通道,在对通道初始化的时候tmr_output_channel_config()这个函数的通道参数选择互补的通道是无效的。其次高级定时器的pwm输出,必须使能定时器的输出使能才可以输出pwm,使能这个函数 tmr_output_enable(TMR1, TRUE),通用定时器不需要,还有就是高级定时器有重复计数功能,一般默认都是设置为1,也就是不重复,这个和通用定时器就一样了。
初始化代码
TMR3:
/**
*TMR3 输出pwm初始化函数
*预分频1,重载值480
*时钟1分频
*定时器计数时钟240M,
*pwm周期:(480/240)us=2us,
*频率:1/2us=0.5M
*占空比:120/480=25%
*/
void pwm_tmr3_init(void)
{
gpio_init_type gpio_init_struct;
tmr_output_config_type tmr_oc_init_structure;
crm_periph_clock_enable(CRM_TMR3_PERIPH_CLOCK, TRUE); //开启tmr3时钟
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); //开启gpioa时钟
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_pins = GPIO_PINS_6 ; //PA6
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; //推挽
gpio_init_struct.gpio_pull = GPIO_PULL_NONE; //无上下拉
gpio_init_struct.gpio_mode = GPIO_MODE_MUX; //复用
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(GPIOA, &gpio_init_struct);
tmr_base_init(TMR3, 479, 0); //设置TMR3的预分频为1=0+1;重载值480=479+1;(参数要减去1)
tmr_cnt_dir_set(TMR3, TMR_COUNT_UP); //设置为向上计数方式
tmr_output_default_para_init(&tmr_oc_init_structure);
tmr_oc_init_structure.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A; //PWM模式A
tmr_oc_init_structure.oc_idle_state = FALSE;
tmr_oc_init_structure.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH; //极性,高电平有效
tmr_oc_init_structure.oc_output_state = TRUE; //使能输出
tmr_output_channel_config(TMR3, TMR_SELECT_CHANNEL_1, &tmr_oc_init_structure); //设置TMR3的通道1
tmr_channel_value_set(TMR3, TMR_SELECT_CHANNEL_1, 119); //设置TMR3的通道1的数据寄存器的值(占空比),120=119+1,
tmr_output_channel_buffer_enable(TMR3, TMR_SELECT_CHANNEL_1, TRUE); //设置TMR3的通道1的数据寄存器的值,溢出事件时更新
tmr_period_buffer_enable(TMR3, TRUE); //TMR3 的重载值,溢出事件更新
tmr_counter_enable(TMR3, TRUE); //使能tmr,开始计数
}
TMR1:
/**
*TMR1 输出pwm初始化函数
*预分频1,重载值240
*时钟1分频
*定时器计数时钟240M,
*pwm周期:(240/240)us=1us,
*频率:1/1us=1M
*占空比:60/240=25%
*/
void pwm_tmr1_init(void)
{
gpio_init_type gpio_init_struct;
tmr_output_config_type tmr_oc_init_structure;
crm_periph_clock_enable(CRM_TMR1_PERIPH_CLOCK, TRUE); //开启tmr1时钟
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); //开启gpioa时钟
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_pins = GPIO_PINS_8 ; //PA8
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; //推挽输出
gpio_init_struct.gpio_pull = GPIO_PULL_NONE; //无上下拉
gpio_init_struct.gpio_mode = GPIO_MODE_MUX; //复用
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(GPIOA, &gpio_init_struct);
tmr_base_init(TMR1, 239, 0); //设置TMR1的预分频为1=0+1;重载值240=239+1;参数要减去1
tmr_cnt_dir_set(TMR1, TMR_COUNT_UP); //设置为向上计数方式
tmr_output_default_para_init(&tmr_oc_init_structure);
tmr_oc_init_structure.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A; //PWM模式A
tmr_oc_init_structure.oc_idle_state = FALSE;
tmr_oc_init_structure.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH; //极性,高电平有效
tmr_oc_init_structure.oc_output_state = TRUE; //使能输出
tmr_output_channel_config(TMR1, TMR_SELECT_CHANNEL_1, &tmr_oc_init_structure); //设置TMR1的通道1
tmr_channel_value_set(TMR1, TMR_SELECT_CHANNEL_1, 59); //设置TMR1的通道1的数据寄存器的值(占空比),60=59+1,
tmr_output_channel_buffer_enable(TMR1, TMR_SELECT_CHANNEL_1, TRUE); //设置TMR1的通道1的数据寄存器的值,溢出事件时更新
tmr_period_buffer_enable(TMR1, TRUE); //TMR1 的重载值,溢出事件时更新
tmr_output_enable(TMR1, TRUE); //使能输出,高级定时器特有。
tmr_counter_enable(TMR1, TRUE); //使能tmr,开始计数
}
测试
测试代码:初始化完后,直接跑空循环
测试结果:输出的都和软件设置的一致。
TMR1CH1,PA8的输出:
TMR3CH1,PA6的输出:
最后
有问题的可以加QQ群技术交流,同时相关代码上传到QQ群中。