当前位置: 首页 > news >正文

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_CLK66MHz中频率需求(如 1kHz~1MHz)
    IPG_CLK_HIGH132MHz较高频率需求
    AXI_CLK264MHz高频率需求(如 10MHz 以上)
    PER_CLK66MHz与外设时钟同步需求
  • 中断支持:可配置周期中断(每次周期结束触发)、比较中断(计数到比较值时触发)、溢出中断,用于动态更新占空比 / 周期(无需轮询)。

2.2 关键硬件逻辑

PWM 波形生成的核心逻辑由 4 部分组成,需通过寄存器逐层配置:

  1. 分频器:对选定的时钟源进行分频,得到 “PWM 计数器时钟”(频率 f_cnt),分频系数可通过寄存器设置(1~128 分频),决定 PWM 精度(分频系数越小,f_cnt 越高,精度越高)。
  2. 周期寄存器(PWMPR):设定计数器的 “计数上限”(记为 PR),计数器从 0 计数到 PR 为一个完整周期。周期计算公式:T = (PR + 1) / f_cnt(因计数包含 0~PR 共 PR+1 个时钟周期)。
  3. 比较寄存器(PWMSAR):设定 “高电平结束时刻”(记为 SAR),计数器计数到 SAR 时,PWM 输出从高电平切换为低电平(边缘对齐模式)。占空比计算公式:D = (SAR + 1) / (PR + 1) × 100%(需满足 SAR ≤ PR,否则占空比异常)。
  4. 控制寄存器(PWMCR):PWM 模块的 “总开关”,负责配置时钟源选择、分频系数、工作模式、软复位、PWM 输出使能等核心功能。

脉冲宽度调制器框图

三、核心寄存器解析(结合代码)

代码中核心操作的 PWM 寄存器如下,需结合《I.MX6ULL 参考手册》中 PWM 章节理解:

寄存器名作用(代码中体现)关键位含义
PWMCRPWM 控制器配置寄存器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 = 1kHz
  • set_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 则会看到 “亮→最亮→亮→灭” 的渐变效果。

http://www.dtcms.com/a/389667.html

相关文章:

  • Python基础 3》流程控制语句
  • 牛客算法基础noob44——数组计数维护
  • 并发编程原理与实战(三十)原子操作进阶,原子数组与字段更新器精讲
  • 前端-详解Vue异步更新
  • 基于风格的对抗生成网络
  • 【JavaScript】SSE
  • JAVA算法练习题day15
  • 线性表---双链表概述及应用
  • 作业帮前端面试(准备)
  • 51单片机-使用单总线通信协议驱动DS18B20模块教程
  • 全文单侧引号的替换方式
  • NVIDIA RTX4090 在Ubuntu系统中开启P2P peer access 直连访问
  • 再次深入学习深度学习|花书笔记2
  • 中移物联ML307C模组OPENCPU笔记1
  • 计算机视觉
  • VScode实现uniapp小程序开发(含小程序运行、热重载等)
  • Redis的各种key问题
  • 元宇宙与医疗产业:数字孪生赋能医疗全链路革新
  • 为你的数据选择合适的分布:8个实用的概率分布应用场景和选择指南
  • 掌握Stable Diffusion WebUI:模型选择、扩展管理与部署优化
  • LVGL拼音输入法优化(无bug)
  • 多层感知机:从感知机到深度学习的关键一步
  • PostgreSQL绿色版整合PostGIS插件,以Windows 64位系统为例
  • GEO优化推荐案例:2025年上海源易信息科技的全链路实践
  • 时空预测论文分享:多模态融合 空间索引结构 超图 时演化因果关系
  • 智能手机产量增长4%
  • MySQL高可用MHA实战指南
  • Coze源码分析-资源库-创建工作流-后端源码-核心技术/总结
  • 《棒球团建》国家级运动健将·棒球1号位
  • 基于STM32单片机生理监控心率脉搏TFT彩屏波形曲线加体温测量