14.【NXP 号令者RT1052】开发——实战-PWM 输出
14.【NXP 号令者RT1052】开发——实战-PWM 输出
上一章,我们介绍了 RT1052 的四定时器 QTMR1 的通道 0,用该通道的中断来控制 DS1 的闪烁,这一章,我们将向大家介绍如何使用 QTMR4 的通道 3 来产生 PWM 输出。在本章中,我们将使用 QTMR4 的通道 3 来产生 PWM 来控制 DS0 的亮度。
14.1 PWM 简介
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。RT1052 的 QTMR 定时器支持交替比较模式(CTRLy[OUTMODE]=100,y 表示通道编号,范围:0~3,下同)生成 PWM,

上图是QTMR 交替比较模式生成 PWM 原理示意图。
实验目标
使用 QTMR4 通道 3 在 IO 口 GPIO2_IO27 (GPIO_B1_11) 输出 PWM 波形,控制 DS0 的亮度。
配置步骤
1. 使能 QTMR4 时钟
c
CLOCK_EnableClock(kCLOCK_Timer4);
实际上
QTMR_Init()内部已调用,无需重复。
2. 初始化 GPIO_B1_11 (GPIO2_IO27)
将 GPIO_B1_11 配置为 QTMR4 通道 3 的输出引脚:
c
//配置 GPIO_B1_11 为 QTIMER4_TIMER3 的输出引脚
IOMUXC_SetPinMux(IOMUXC_GPIO_B1_11_QTIMER4_TIMER3, 0);
IOMUXC_SetPinConfig(IOMUXC_GPIO_B1_11_QTIMER4_TIMER3, 0x10B0);
3. 初始化 QTMR4
c
qtmr_config_t qtimer4pwm_config;
QTMR_GetDefaultConfig(&qtimer4pwm_config); //先设置为默认配置
qtimer4pwm_config.primarySource = kQTMR_ClockDivide_128; //设置第一时钟源
QTMR_Init(TMR4, kQTMR_Channel_3, &qtimer4pwm_config); //初始化 TIM4 通道 3
4. 设置 PWM 功能
使用 QTMR_SetupPwm 配置 PWM:
c
status_t QTMR_SetupPwm(TMR_Type * base, qtmr_channel_selection_t channel, uint32_t pwmFreqHz,uint8_t dutyCyclePercent, bool outputPolarity, uint32_t srcClock_Hz);
参数说明
- base:定时器选择,这里为
TMR4。 - channel:通道选择,这里为
kQTMR_Channel_3。 - pwmFreqHz:PWM 频率(Hz)。
- dutyCyclePercent:占空比百分比。
- outputPolarity:输出极性。
- srcClock_Hz:QTMR 时钟源频率。
示例:
c
//PWM 频率 500K,占空比 50%
QTMR_SetupPwm(TMR4, kQTMR_Channel_3, 500000, 50, false, 234375);
5. 开启 QTMR4 通道 3
c
//通道 3 在第一时钟源的上升沿计数
QTMR_StartTimer(TMR4, kQTMR_Channel_3, kQTMR_PriSrcRiseEdge);
说明
- 完成以上配置后,PWM 已经开始输出。
- 默认频率和占空比固定。
- 通过修改 QTMR4 通道 3 的 CMPLD1 和 CMPLD2 寄存器,可以动态调整 PWM 占空比,从而控制 DS0 的亮度。
14.2 硬件设计
本实验用到的硬件资源有:
- 指示灯 DS0
- QTMR4 通道 3 定时器
本章将通过 QTMR4 的通道 3 来控制 DS0 的亮灭,DS0 是连接到 GPIO1_IO03(P103)上的,这个前面已经有介绍了。而 QTMR4 通道 3 的输出是连接在 GPIO2_IO27(P227)上,

因此,DS0 和 GPIO2_IO27 并没有直接连在一起,所以,需要我们拿一根杜邦线,把 P103和 P227 短接在一起。另外 P103 需要设置成浮空输入才行,以免 IO 输出冲突。

14.3 软件设计
软件设计我们在之前的工程上面增加,首先,打开上次的工程,然后在 HARDWARE 下新建 PWM 文件夹,在 PWM 下新建一个 pwm.c 的文件和 pwm.h 的头文件
pwm.c
#include "pwm.h"
#include "lpuart.h"//QTIMER4配置结构体
qtmr_config_t qtimer4pwm_config;//计算2的乘方
//time:多少个2相乘,time不能大于7
//返回值:2^time,结果不能大于255;
u8 Calcu_2invo(u8 time)
{u8 i=0;u8 value=1;if(time>7)time=7;if(time==0)value=1;else{for(i=0;i<time;i++){value*=2;}}return value;
}//初始化QTIMER4通道3作为PWM输出通道,
//QTIMER4时钟源为IPG_CLK_ROOT=150MHz
//prisrc : 第一时钟源选择
// 0000~0011,通道0~3的输入引脚.
// 0100~0111,通道0~3的输出.可用于级联.
// 1000~1111,IPG_CLK_ROOT时钟的:1,2,4,8,16,32,64,128分频.
//clk: PWM频率
//duty: 占空比,百分比
void QTMR4_CH3_PWM_Init(u8 prisrc,u32 clk, u8 duty)
{u8 fredivi=1;qtmr_primary_count_source_t qtimer_source;qtimer_source=(qtmr_primary_count_source_t)prisrc;//配置GPIO_B1_11为QTIMER4_TIMER3的输出引脚IOMUXC_SetPinMux(IOMUXC_GPIO_B1_11_QTIMER4_TIMER3,0); //配置IO引脚GPIO_AD_B1_11的功能//低转换速度,驱动能力为R0/6,速度为100Mhz,关闭开路功能,使能pull/keepr//选择keeper功能,下拉100K Ohm,关闭HystIOMUXC_SetPinConfig(IOMUXC_GPIO_B1_11_QTIMER4_TIMER3,0x10B0);fredivi=Calcu_2invo(prisrc-8);//初始化QTIMER4QTMR_GetDefaultConfig(&qtimer4pwm_config); //先设置为默认配置,后面在根据实际情况配置qtimer4pwm_config.primarySource=qtimer_source; //设置第一时钟源QTMR_Init(TMR4,kQTMR_Channel_3,&qtimer4pwm_config); //初始化TIM4通道3QTMR_SetupPwm(TMR4,kQTMR_Channel_3,clk,duty,false,CLOCK_GetFreq(kCLOCK_IpgClk)/fredivi); //初始化PWMQTMR_StartTimer(TMR4,kQTMR_Channel_3,kQTMR_PriSrcRiseEdge); //通道3在primary时钟源的上升沿计数
}//设置占空比
//prisrc : 第一时钟源选择
// 0000~0011,通道0~3的输入引脚.
// 0100~0111,通道0~3的输出.可用于级联.
// 1000~1111,IPG_CLK_ROOT时钟的:1,2,4,8,16,32,64,128分频.
//clk: PWM频率
//duty: 占空比,百分比
void QTMER4CH3_PWM_DutySet(u8 prisrc,u32 clk, u8 duty)
{u8 fredivi=1;u32 srcclk,period,hightime,lowtime;fredivi=Calcu_2invo(prisrc-8);srcclk=CLOCK_GetFreq(kCLOCK_IpgClk)/fredivi;period=(srcclk/clk); //一个PWM周期需要的计数值hightime=(period*duty)/100; //一个PWM周期中高电平时间(计数值)lowtime=period-hightime; //一个PWM周期中低电平时间(计数值)TMR4->CHANNEL[kQTMR_Channel_3].CMPLD1=lowtime;TMR4->CHANNEL[kQTMR_Channel_3].CMPLD2=hightime;
}
此 部 分 代 码 有 三 个 函数: Calcu_2invo 、 QTMR4_CH3_PWM_Init 和QTMER4CH3_PWM_DutySet 。函数 Calcu_2invo 是 用 来 计 算 乘 方 的 , 很 简 单 。 函 数QTMR4_CH3_PWM_Init 是初始化 QTMR4 的通道 3 作为 PWM 的,该函数有 3 个参数:prisrc、clk 和 duty。prisrc 用于选择第一时钟源;clk 设置 PWM 频率,duty 设置 PWM 默认占空比,这三个参数决定了 PWM 的频率和占空比。该函数基本上就是参考前面介绍的 4 个步骤来初始化QTMR4 通道 3 的。函数 QTMER4CH3_PWM_DutySet 是用来改变 PWM 占空比的,此函数也有三个参数,参数含义和 QTMR4_CH3_PWM_Init 一样,分别为定时器第一时钟源、PWM 频率和 PWM 占空比,此函数本质上就是根据设置好的 PWM 频率和占空比值来修改寄存器CMPLD1 和 CMPLD2 的值。
pwm.h
#ifndef _PWM_H
#define _PWM_H
#include "sys.h"void QTMR4_CH3_PWM_Init(u8 prisrc,u32 clk, u8 duty);
void QTMER4CH3_PWM_DutySet(u8 prisrc,u32 clk, u8 duty);
#endif
main.c
#include "sys.h"
#include "lpuart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "wdog.h"
#include "rtwdog.h"
#include "gptimer.h"
#include "pitimer.h"
#include "qtimer.h"
#include "pwm.h"int main(void)
{u8 key=0;MPU_Memory_Protection(); //初始化MPURT1052_Clock_Init(); //配置系统时钟DELAY_Init(600); //延时函数初始化LPUART1_Init(115200); //初始化串口1RT1052_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);//优先级分组4
#ifdef LED_DEBUGu8 led0sta=1,led1sta=1; //LED0,LED1的当前状态LED_Init(); //初始化LED
#endif
#ifdef KEY_DEBUGKEY_Init(); //初始化KEY
#endif
#ifdef LPUART_DEBUGu8 len; //接收数据长度u16 times=0; //延时计数器LED0(0); //先点亮红灯
#endif
#ifdef EXTIX_DEBUGEXTIX_Init(); //初始化外部中断
#endif
#ifdef WDOG_DEBUGWDOG1_Init(3,2); //初始化看门狗1,2秒溢出,提前1秒进入中断,方便喂狗LED0(0); //先点亮LED灯delay_ms(300); //延时300ms再初始化看门狗,LED0的变化"可见"
#endif
#ifdef RTWDOG_DEBUGLED_Init(); //初始化LED KEY_Init(); //初始化KEYdelay_ms(100); //延时100ms再初始化看门狗,LED0的变化"可见"MYRTWDOG_Init(1,0,32768,0); //初始化RT看门狗,1秒溢出,非窗口模式LED0(0); //先点亮LED灯
#endif
#ifdef GPT1_DEBUGLED_Init(); //初始化LED KEY_Init(); //初始化按键GPT1_Int_Init(3750-1,10000); //设置GPT1 0.5秒钟产生一次中断
#endif
#ifdef PIT_DEBUGLED_Init(); //初始化LED PIT_CH0_Int_Init(75000000/2); //设置PIT 0.5秒钟产生一次中断
#endif
#ifdef QTMR_DEBUGKEY_Init(); //初始化按键LED_Init(); //初始化LEDQTMR1_CH0_Int_Init(15,46875); //设置QTMR1 0.04秒钟产生一次中断
#endifu8 dir=1; u16 led1pwmval=0; gpio_pin_config_t led_config;LED_Init(); //初始化LED KEY_Init(); //初始化按键//初始化PWM,定时器时钟为:150/64=2.34375Mhz,设置频率为5Khz,50%占空比QTMR4_CH3_PWM_Init(14,5000,0);//设置P103为输入模式,防止干扰PWMled_config.direction=kGPIO_DigitalInput; //输出led_config.interruptMode=kGPIO_NoIntmode; //不使用中断功能led_config.outputLogic=0; //默认高电平,LED灯关闭GPIO_PinInit(GPIO1,3,&led_config); //初始化GPIO1_3while(1){delay_ms(10); if(dir)led1pwmval++;else led1pwmval--; if(led1pwmval>=100)dir=0;if(led1pwmval==0)dir=1; QTMER4CH3_PWM_DutySet(14,5000,led1pwmval);
#ifdef QTMR_DEBUGLED0_Toggle;delay_ms(1000);
#endif
#ifdef PIT_DEBUGLED0_Toggle;delay_ms(1000);
#endif
#ifdef GPT1_DEBUGLED0_Toggle;delay_ms(1000);
#endif
#ifdef RTWDOG_DEBUGkey=KEY_Scan(0);if(key==WKUP_PRES) //如果按键按下,则喂狗.{RTWDOG_Feed();} delay_ms(10);
#endif
#ifdef WDOG_DEBUGLED0(1); //关闭DS0,如不复位,DS0将一直处于关闭状态.delay_ms(100);
#endif
#ifdef EXTIX_DEBUGprintf("Int example driver!\r\n");delay_ms(1000);
#endif
#ifdef KEY_DEBUGkey=KEY_Scan(0); //得到键值if(key){ switch(key){ case WKUP_PRES: //控制LED0,LED1互斥点亮led1sta=!led1sta;led0sta=!led1sta;break;case KEY2_PRES: //控制LED0翻转led0sta=!led0sta;break;case KEY1_PRES: //控制LED1翻转 led1sta=!led1sta;break;case KEY0_PRES: //同时控制LED0,LED1翻转 led0sta=!led0sta;led1sta=!led1sta;break;}LED0(led0sta); //控制LED0状态LED1(led1sta); //控制LED1状态}else delay_ms(10);
#endif // KEY_DEBUG
#ifdef LPUART_DEBUGif(LPUART_RX_STA&0x8000){ len=LPUART_RX_STA&0x3fff;//得到此次接收到的数据长度printf("\r\n发送的消息为:\r\n");LPUART_WriteBlocking(LPUART1,LPUART_RX_BUF,len);//发送接收到的数据printf("\r\n\r\n");//插入换行LPUART_RX_STA=0;}else{times++;if(times%5000==0){printf("\r\nALIENTEK RT1052开发板 串口实验\r\n");}if(times%200==0)printf("请输入数据,以回车键结束\r\n"); if(times%30==0)LED0_Toggle;//闪烁LED,提示系统正在运行.delay_ms(10); }
#endif // LPUART_DEBUG}
}
此部分代码有 1 个 main 函数,main 函数和之前的代码类似,通过 QTMR4_CH3_PWM_Init设置 QTMR4 通道 3 的相关参数,然后设置 P103 为模拟输入,防止 IO 冲突。最后,在死循环里面,我们通过 led0pwmval 的值来控制 DS0 的亮度。led0pwmval 的值从0 变到 100,然后又从 100 变到 0,如此循环,因此 DS0 的亮度也会跟着从亮变到暗,然后又从暗变到亮。至于这里的值我们为什么取 100,是因为 PWM 的占空比为 0%~100%啊!至此,我们的软件设计就完成了。
然后添加头文件,添加 pwm.c到组
编译,下载。


总结
本章通过配置 QTMR4 通道 3 输出 PWM 波形,实现了对 DS0 亮度的控制。实验中先将 GPIO2_IO27 配置为 QTMR4 通道 3 的输出引脚,再初始化定时器并设置 PWM 频率与占空比,最后启动定时器输出 PWM。通过在主循环中动态修改 CMPLD1 和 CMPLD2 寄存器的值,改变占空比,使 DS0 的亮度在 0%~100% 范围内循环变化,从暗到亮再从亮到暗,完成了 PWM 控制 LED 灯亮度的效果。
OK!谢谢大家!
