掌上实验室V8系列教程(四)定时器PWM输出

1 项目功能

三色RGB LED 通过PWM 调色


2 电路原理

我们可以通过3路PWM波分别调整RGB三个二极管的亮度,即调整RGB的比例,合成不同的颜色。

PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占空比的变化来调节信号、能量等的变化,占空比就是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,例如方波的占空比就是50%。

当PA1输出“0”时,红色LED(R)亮;PA1输出“1”,红色LED灭。也就是说上述电路是低电平有效,上图的PWM波形周期为4ms,占空比为 3/4*100% = 75%。如果PA1输出占空比为75%的PWM波(低电平有效),红色LED的亮度就是最大亮度的75%。对于绿色LED(G)和蓝色LED(B),依次类推。

3 定时器功能

通用定时器 TMR2 包含一个支持向上、向下、双向计数的 16 位计数器、4 个捕获/比较寄存器、
4 组独立的通道。可实现输入捕获、可编程 PWM 输出

TMR2 计数时钟可从内部时钟(CK_INT)、外部时钟(外部时钟模式 A、B)、内部触发输入(ISx)这些时钟源提供。

TMR 的输出部分由比较器和输出控制构成,用于编程输出信号的周期、占空比、极性,如下图所示。

 配置 CxC[2:0]≠2’b00 将通道配置为输出可实现多种输出模式,此时,计数器计数值将与通道寄存器(TMRx_CxDT)值比较,并根据 CxOCTRL[2:0]位配置的输出模式,产生中间信号 CxORAW,再经过输出控制逻辑处理后输送到 IO。输出信号的周期由周期寄存器(TMRx_PR)值配置,占空比则由通道寄存器(TMRx_CxDT)值配置。TMR2有PWM、强制输出模式、输出比较模式、单周期模式和快速输出模式几种。本例中用到的是PWM模式。

PWM 模式:CxOCTRL=3’b110/111 时,开启 PWM 模式,每个通道可独立配置并输出
一路 PWM 信号。此时,输出信号的周期由 TMRx_PR 配置,占空比由 CxDT 值配置,计数
器值与通道寄存器(TMRx_CxDT)值进行比较,根据计数方向输出指定电平信号,关于 PWM
模式 A/B 详见 CxOCTRL[2:0]位描述。当计数模式为双向计数时,可根据 OWCDIR 位指示
计数方向。下图是向上计数下PWM模式A的例子

 PR值是32,所以周期是33(0~32),通道寄存器值CxDT的值是3,所以计数3次之后C1ORAW翻转,引起输出引脚IO翻转。

TMR2最多可以输出4通道PWM,本例中采用了其中的三个通道(ch2,ch3,ch4)。

4 程序设计

4.1 驱动包选择

首先驱动包的选择如下图,本例用到的有crm、gpio、misc和tmr。

4.2 GPIO配置

  PA2、PA3、PA4主功能是GPIO,需要把他们配置成多工功能。

void gpio_pins_init(void)
{
	crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);

	gpio_init_type gpio_init_struct;
	gpio_default_para_init(&gpio_init_struct);

	gpio_init_struct.gpio_pins = GPIO_PINS_1 | GPIO_PINS_2 | GPIO_PINS_3;
	gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
	gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
	gpio_init(GPIOA, &gpio_init_struct);
}

4.3 TMR2配置

定时器2共有四路输出,掌上实验室V8采用了TMR2的三个通道Ch2、Ch3、Ch4输出连接三色LED的RGB引脚。根据电路图可以看出,MCU输出低的时候,对应的LED会亮,所以PWM极性应该设置成低有效。

void tmr2_pwm_init(void)
{
	uint16_t tmr_pr   = 254;
	
	uint16_t ccr2_val = 123;
	uint16_t ccr3_val = 123;
	uint16_t ccr4_val = 123;
	uint16_t prescaler_value = 0;

	crm_periph_clock_enable(CRM_TMR2_PERIPH_CLOCK, TRUE);

	tmr_output_config_type tmr_oc_init_structure;
	prescaler_value = 0;

	/* tmr3 time base configuration */
	tmr_base_init(TMR2, tmr_pr, prescaler_value);
	tmr_cnt_dir_set(TMR2, TMR_COUNT_UP);
	tmr_clock_source_div_set(TMR2, TMR_CLOCK_DIV1);

	tmr_output_default_para_init(&tmr_oc_init_structure);
	tmr_oc_init_structure.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
	tmr_oc_init_structure.oc_idle_state = FALSE;
	tmr_oc_init_structure.oc_polarity = TMR_OUTPUT_ACTIVE_LOW;
	tmr_oc_init_structure.oc_output_state = TRUE;

	tmr_output_channel_config(TMR2, TMR_SELECT_CHANNEL_2, &tmr_oc_init_structure);
	tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_2, ccr2_val);
	tmr_output_channel_buffer_enable(TMR2, TMR_SELECT_CHANNEL_2, TRUE);

	tmr_output_channel_config(TMR2, TMR_SELECT_CHANNEL_3, &tmr_oc_init_structure);
	tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_3, ccr3_val);
	tmr_output_channel_buffer_enable(TMR2, TMR_SELECT_CHANNEL_3, TRUE);

	tmr_output_channel_config(TMR2, TMR_SELECT_CHANNEL_4, &tmr_oc_init_structure);
	tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_4, ccr4_val);
	tmr_output_channel_buffer_enable(TMR2, TMR_SELECT_CHANNEL_4, TRUE);

	tmr_period_buffer_enable(TMR2, TRUE);

	/* tmr enable counter */
	tmr_counter_enable(TMR2, TRUE);
}

这里pr=255,所以周期是255+1=256个时钟周期。ccr2_val,ccr3_val,ccr4_val分别对应RGB的值,可以在0~255之间选择。

PWM周期对于本例不重要,所以没有做特别的计算。如果需要准确设置PWM周期,可以通过下列公式计算:

T = 1/fclk * (prescaler_value+1) * (tmr_pr+1)

fclk的计算可以参考:掌上实验室V8系列教程(五)定时器中断及应用


 

5 完整示例代码

#include "at32f403a_407_conf.h"

void gpio_pins_init(void)
{
	crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);

	gpio_init_type gpio_init_struct;
	gpio_default_para_init(&gpio_init_struct);

	gpio_init_struct.gpio_pins = GPIO_PINS_1 | GPIO_PINS_2 | GPIO_PINS_3;
	gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
	gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
	gpio_init(GPIOA, &gpio_init_struct);
}

void tmr2_pwm_init(void)
{
	uint16_t tmr_pr   = 255;
	
	uint16_t ccr2_val = 255;
	uint16_t ccr3_val = 0;
	uint16_t ccr4_val = 0;
	uint16_t prescaler_value = 0;

	crm_periph_clock_enable(CRM_TMR2_PERIPH_CLOCK, TRUE);

	tmr_output_config_type tmr_oc_init_structure;
	prescaler_value = 0;

	/* tmr3 time base configuration */
	tmr_base_init(TMR2, tmr_pr, prescaler_value);
	tmr_cnt_dir_set(TMR2, TMR_COUNT_UP);
	tmr_clock_source_div_set(TMR2, TMR_CLOCK_DIV1);

	tmr_output_default_para_init(&tmr_oc_init_structure);
	tmr_oc_init_structure.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
	tmr_oc_init_structure.oc_idle_state = FALSE;
	tmr_oc_init_structure.oc_polarity = TMR_OUTPUT_ACTIVE_LOW;
	tmr_oc_init_structure.oc_output_state = TRUE;

	tmr_output_channel_config(TMR2, TMR_SELECT_CHANNEL_2, &tmr_oc_init_structure);
	tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_2, ccr2_val);
	tmr_output_channel_buffer_enable(TMR2, TMR_SELECT_CHANNEL_2, TRUE);

	tmr_output_channel_config(TMR2, TMR_SELECT_CHANNEL_3, &tmr_oc_init_structure);
	tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_3, ccr3_val);
	tmr_output_channel_buffer_enable(TMR2, TMR_SELECT_CHANNEL_3, TRUE);

	tmr_output_channel_config(TMR2, TMR_SELECT_CHANNEL_4, &tmr_oc_init_structure);
	tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_4, ccr4_val);
	tmr_output_channel_buffer_enable(TMR2, TMR_SELECT_CHANNEL_4, TRUE);

	tmr_period_buffer_enable(TMR2, TRUE);

	/* tmr enable counter */
	tmr_counter_enable(TMR2, TRUE);
}

void delay_us(int us)
{
	SysTick->LOAD = us * system_core_clock/1000000; 
	SysTick->VAL = 0;
	SysTick->CTRL = 0x5; //Systick采用系统时钟为时钟源,并启动
	while((SysTick->CTRL & (1<<16))==0);
	SysTick->CTRL = 0;	 //停止Systick
}

void delay_ms(int ms)
{
	for(;ms>0;ms--)
		delay_us(1000);
}

int main(void)
{
	gpio_pins_init();
	tmr2_pwm_init();
	
	for(;;){
		for(int r=0;r<255;r++)
		{
			tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_2, r);
			delay_ms(10);
		}
		for(int g=0;g<255;g++)
		{
			tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_3, g);
			delay_ms(10);
		}
		for(int b=0;b<255;b++)
		{
			tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_4, b);
			delay_ms(10);
		}
		delay_ms(500);
		tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_2, 0);
		tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_3, 0);
		tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_4, 0);
		delay_ms(500);
	}
}

6 参考资源

《掌上实验室V8》 原理图

AT32F403A AT32F407 固件库2.0.2 应用说明

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值