ARM(13) - PWM控制LCD
一、PWM 基础概念
定义:脉冲宽度调制(Pulse Width Modulation,PWM),通过周期性的高低电平信号,利用 “高电平占空比” 模拟输出等效电压 / 功率,实现 “数字信号控制模拟量” 的效果。
核心参数:
- 周期(T):PWM 信号一个完整高低电平循环的时间,对应频率
f = 1/T
(如 T=1ms 时,f=1kHz)。 - 占空比(D):一个周期内高电平时间占比(范围 0%~100%),公式:
D = (高电平时间 / 周期) × 100%
。
典型应用:LED 调光(占空比越高越亮)、直流电机转速控制(占空比越高转速越快)、蜂鸣器音调调节(频率决定音调)、模拟电压输出(如占空比 50% 等效输出 1/2 电源电压)。
LCD引脚
二、IMX6ULL PWM 硬件特性
2.1 模块核心概况
- 通道数量:内置 8 个独立 PWM 通道(PWM1~PWM8),可同时输出多路独立波形。
- 工作模式:支持两种波形模式,满足不同场景需求:
- 边缘对齐模式(默认):计数器 “从 0 计数到最大值” 后清零,高电平在计数前半段(或后半段,取决于配置)。
- 中心对齐模式:计数器 “从 0 计数到最大值,再从最大值减到 0”,高电平对称分布在周期中心(适合电机对称控制)。
- 时钟源可选:提供 4 种时钟源,需通过寄存器配置,覆盖不同频率需求:
时钟源名称 典型频率(默认) 适用场景 IPG_CLK 66MHz 中频率需求(如 1kHz~1MHz) IPG_CLK_HIGH 132MHz 较高频率需求 AXI_CLK 264MHz 高频率需求(如 10MHz 以上) PER_CLK 66MHz 与外设时钟同步需求 - 中断支持:可配置周期中断(每次周期结束触发)、比较中断(计数到比较值时触发)、溢出中断,用于动态更新占空比 / 周期(无需轮询)。
2.2 关键硬件逻辑
PWM 波形生成的核心逻辑由 4 部分组成,需通过寄存器逐层配置:
- 分频器:对选定的时钟源进行分频,得到 “PWM 计数器时钟”(频率
f_cnt
),分频系数可通过寄存器设置(1~128 分频),决定 PWM 精度(分频系数越小,f_cnt
越高,精度越高)。 - 周期寄存器(PWMPR):设定计数器的 “计数上限”(记为
PR
),计数器从 0 计数到PR
为一个完整周期。周期计算公式:T = (PR + 1) / f_cnt
(因计数包含 0~PR 共 PR+1 个时钟周期)。 - 比较寄存器(PWMSAR):设定 “高电平结束时刻”(记为
SAR
),计数器计数到SAR
时,PWM 输出从高电平切换为低电平(边缘对齐模式)。占空比计算公式:D = (SAR + 1) / (PR + 1) × 100%
(需满足SAR ≤ PR
,否则占空比异常)。 - 控制寄存器(PWMCR):PWM 模块的 “总开关”,负责配置时钟源选择、分频系数、工作模式、软复位、PWM 输出使能等核心功能。
脉冲宽度调制器框图
三、核心寄存器解析(结合代码)
代码中核心操作的 PWM 寄存器如下,需结合《I.MX6ULL 参考手册》中 PWM 章节理解:
寄存器名 | 作用(代码中体现) | 关键位含义 |
---|---|---|
PWMCR | PWM 控制器配置寄存器 | bit0:PWM 使能位;bit3:软件复位(SW_RST);bit1~2:对齐模式;bit4~11:预分频系数;bit16:计数器模式;bit26:时钟源选择 |
PWMPR | 周期寄存器(Period Register) | 存储 PWM 周期的计数阈值,计数器从 0 计数到 PWMPR 时完成一个周期 |
PWMSAR | 比较寄存器(Sample Register) | 存储高电平计数阈值,计数器值≤PWMSAR 时输出高电平,否则低电平(占空比 = PWMSAR/PWMPR) |
PWMSR | 状态寄存器(Status Register) | bit3:周期匹配中断标志(代码中用于判断中断触发);中断标志需 “写 1 清除” |
PWMIR | 中断使能寄存器(Interrupt Register) | bit0:使能周期匹配中断(代码中开启 PWM 中断的关键) |
电气特性
设置周期寄存器(周期计数值 = 1000-2,实际周期 = (计数值+2)*时钟周期)
四、代码
1、pwm.c
1 引脚复用配置(PWM 输出引脚初始化)
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT, 0); // 引脚复用:GPIO1_IO08 → PWM1_OUT IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT, 0xB9); // 引脚电气属性配置
- 引脚复用原理:I.MX6ULL 的引脚是 “多功能复用” 的,需通过
IOMUXC_SetPinMux
指定引脚功能(此处为 PWM1 输出),第二个参数 “0” 表示不使用 GPIO 中断。- 电气属性配置:
IOMUXC_SetPinConfig
配置引脚的驱动能力、上下拉 / 浮空状态,0xB9
对应配置:
- 驱动能力:R0(最大驱动电流)
- fast rate:快速
2 PWM 控制器初始化(pwm1_init 函数)
初始化流程:软件复位 → 配置控制器 → 配置周期 / 占空比 → 使能中断 → 使能 PWM
步骤 1:PWM 软件复位
- 软件复位的目的:清除寄存器旧值,确保初始化从 “干净状态” 开始。
步骤 2:配置 PWMCR(核心控制位)
各 bit 配置含义:
1<<26
:选择时钟源为 IPG_CLK(默认时钟,需确保 clock_init 已初始化 IPG 时钟)1<<16
:计数器工作在 “递增模式”(从 0 计数到 PWMPR)65<<4
:预分频系数 = 65(实际分频比 = 65+1=66,因为预分频器是 “+1” 计数)3<<1
:对齐模式为 “左对齐”(高电平从周期开始持续到 PWMSAR,符合常规 PWM 输出)步骤 3:配置周期与初始占空比
- PWM 频率计算:假设 IPG_CLK 频率 = 66MHz(默认),则:
分频后时钟频率 = 66MHz / (65+1) = 1MHz
PWM 周期 = 999 × (1/1MHz) = 999μs ≈ 1ms
PWM 频率 = 1 / 1ms = 1kHzset_ratio(0.5)
:初始占空比 50%,即高电平持续时间 = 999μs × 0.5 ≈ 500μs。步骤 4:PWM 中断配置
// 1. 使能PWM周期匹配中断(PWMSR的bit3触发) PWM1->PWMIR |= (1 << 0); // 2. 注册中断处理函数(绑定PWM1_IRQn中断线与pwm1_irq_handler) system_interrupt_register(PWM1_IRQn, pwm1_irq_handler); // 3. GIC中断控制器配置(使能中断+设置优先级) GIC_EnableIRQ(PWM1_IRQn); // 使能GIC中的PWM1中断 GIC_SetPriority(PWM1_IRQn, 0); // 优先级0(最高,可根据需求调整)
- 中断作用:代码中通过 “周期匹配中断” 确保每次 PWM 周期更新时,重新设置占空比(避免占空比更新不及时)。
3 占空比设置函数(set_ratio)
- 循环 4 次写入的原因:I.MX6ULL 的 PWMSAR 是 “影子寄存器”,需多次写入确保值被正确更新(硬件要求,避免单次写入失败)。
- 边界处理:代码中未显式限制 x 的范围,实际开发中需添加
x = (x>1.0)?1.0:(x<0.0)?0.0:x;
,防止占空比超出 0%~100%。4 PWM 中断处理函数(pwm1_irq_handler)
中断标志清除:I.MX6ULL 的 PWM 中断标志需 “写 1 清除”
#include "pwm.h" // PWM相关的宏定义或函数声明
#include "MCIMX6Y2.h" // i.MX6Y2处理器的寄存器定义(核心头文件)
#include "fsl_iomuxc.h" // 引脚复用配置库(用于配置GPIO为PWM功能)
#include "interrupt.h" // 中断相关函数声明/*** @brief 设置PWM占空比* @param x 占空比(0.0~1.0,对应0%~100%)* @note 占空比 = 比较寄存器值 / 周期寄存器值,通过修改比较寄存器实现占空比调节*/
void set_ratio(float x)
{// 读取PWM周期寄存器(PWMPR)的值,该值决定PWM周期unsigned short t = PWM1->PWMPR; int i = 0;// 多次写入比较寄存器(PWMSAR),确保硬件正确接收(部分芯片需要多次写入生效)for(i = 0;i < 4;i++){// 设置比较寄存器值 = 周期值 * 占空比,决定高电平持续时间PWM1->PWMSAR = t * x; }
}// 全局变量:占空比参数(初始值50%),主循环中会动态修改
float g_f = 0.5;/*** @brief PWM1中断处理函数* @note 当PWM周期结束时触发中断,在中断中更新占空比*/
void pwm1_irq_handler(void)
{// 检查是否是PWM周期结束中断(第3位为周期结束标志位,具体需参考芯片手册)if ((PWM1->PWMSR & (1 << 3)) != 0){// 用全局变量g_f更新占空比set_ratio(g_f);// 清除中断标志位(写入1清除,具体需参考芯片手册)PWM1->PWMSR |= (1 << 3);}
}/*** @brief 初始化PWM1外设* @note 包含引脚复用配置、PWM控制器配置、中断使能等*/
void pwm1_init(void)
{// 1. 配置引脚复用:将GPIO1_IO08复用为PWM1输出功能// 参数1:复用选择(GPIO1_IO08 -> PWM1_OUT);参数2:关闭GPIO功能(0表示禁用GPIO)IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT, 0);// 2. 配置引脚电气属性:上拉/下拉、驱动强度、速率等// 0xB9对应配置:使能上拉、100K欧姆上拉电阻、驱动强度最大IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT, 0xB9);// 3. 配置PWM控制器(PWMCR为控制寄存器)// 先将PWM置于软件复位状态(第3位为复位位)PWM1->PWMCR |= (1 << 3);// 等待复位完成(复位位自动清零)while((PWM1->PWMCR & (1 << 3)) != 0);// 4. 配置PWM核心参数(清除原有配置后重新设置)PWM1->PWMCR = 0; // 先清零// 配置PWMCR关键位:// (1 << 26):使能比较寄存器自动重载(周期结束后更新比较值)// (1 << 16):使能PWM输出(输出使能位)// (65 << 4):预分频系数=65(时钟源分频后作为PWM计数时钟)// (3 << 1):计数模式=递增计数(从0到周期值后复位)PWM1->PWMCR |= (1 << 26) | (1 << 16) | (65 << 4) | (3 << 1);// 清除第18-19位(设置PWM时钟源为IPG_CLK,具体需参考芯片手册)PWM1->PWMCR &= ~(3 << 18);// 5. 配置PWM中断(PWMIR为中断寄存器)// 使能周期结束中断(第0位为周期结束中断使能位)PWM1->PWMIR |= (1 << 0);// 6. 设置PWM周期和初始占空比PWM1->PWMPR = 1000 - 2; // 周期寄存器值=998,决定PWM周期(周期= (998+2)/PWM时钟频率)set_ratio(0.5); // 初始占空比50%// 7. 配置中断控制器(GIC)// 注册PWM1中断处理函数到中断向量表system_interrupt_register(PWM1_IRQn, pwm1_irq_handler);// 使能GIC中PWM1中断通道GIC_EnableIRQ(PWM1_IRQn);// 设置PWM1中断优先级为0(最高优先级)GIC_SetPriority(PWM1_IRQn, 0);// 8. 使能PWM(第0位为PWM使能位)PWM1->PWMCR |= (1 << 0);
}
2、main 函数
int main(void)
{// 1. 初始化外设(时钟、中断、LED、PWM等)clock_init(); // 初始化系统时钟(含IPG_CLK,PWM依赖此时钟)system_interrupt_init(); // 初始化中断控制器(GIC)// ... 其他外设初始化 ...pwm1_init(); // 初始化PWM1// 2. 主循环:周期性调整占空比(0%→50%→100%→0%循环)while (1){g_f += 0.5; // 每次增加0.5(占空比+50%)if(g_f >= 1){g_f = 0; // 超过100%则重置为0%}delay_ms(1000); // 每隔1秒调整一次}
}
现象
- 功能效果:PWM1 输出 1kHz 信号,占空比每 1 秒切换一次(50%→100%→50%→0%...),若接LCD 则会看到 “亮→最亮→亮→灭” 的渐变效果。