ARM---IMX6ULL 的 PWM 模块介绍及其程序的编写

目录

[一. PWM](#一. PWM)

[1.1 PWM 简介](#1.1 PWM 简介)

[1.2 PWM 关键参数](#1.2 PWM 关键参数)

[1.2.1 频率](#1.2.1 频率)

[1.2.2 占空比](#1.2.2 占空比)

[1.3 PWM 用途](#1.3 PWM 用途)

[二. IMX6ULL 的 PWM 模块](#二. IMX6ULL 的 PWM 模块)

[2.1 PWM 模块](#2.1 PWM 模块)

[2.1.1 与 LCD 相关](#2.1.1 与 LCD 相关)

[2.1.2 PWM 模块](#2.1.2 PWM 模块)

[2.2 PWM 原理](#2.2 PWM 原理)

[2.3 与 PWM 配置相关的寄存器](#2.3 与 PWM 配置相关的寄存器)

[2.3.1 PWMx_PWMCR寄存器](#2.3.1 PWMx_PWMCR寄存器)

[2.3.2 PWMx_PWMIR 寄存器](#2.3.2 PWMx_PWMIR 寄存器)

[2.3.3 PWMx_PWMSR寄存器](#2.3.3 PWMx_PWMSR寄存器)

[2.3.4 PWMx_PWMPR 寄存器](#2.3.4 PWMx_PWMPR 寄存器)

[2.3.5 PWMx_PWMSAR 寄存器](#2.3.5 PWMx_PWMSAR 寄存器)

[三. PWM 模块示例代码](#三. PWM 模块示例代码)

[3.1 初始化函数](#3.1 初始化函数)

[3.2 中断函数](#3.2 中断函数)


一. PWM

1.1 PWM 简介

PWM 全称是 Pulse Wid th Modulation ,也就是脉冲宽度调制, PWM 信号如图所示**:**

1.2 PWM 关键参数

1.2.1 频率

频率就是开关速度,把一次开关算作一个周期,那么频率就是 1 秒内进行了多少次开关

周期:T = 1 / f

1.2.2 占空比

占空比就是一个周期内高电平时间和低电平时间的比例,一个周期内高电平时间越长占空比就越大,反之占空比就越小。

占空比用百分之表示, 如果一个周期内全是低电平那么占空比就是 0%,如果一个周期内全是高电平那么占空比就是 1 00%

1.3 PWM 用途

给 LCD 的背光引脚输入一个 PWM 信号,这样就可以通过调整占空比的方式来调整 LCD 背光亮度了。提高占空比就会提高背光亮度,降低占空比就会降低背光亮度。重点就在于 PWM 信号的产生和占空比的控制。

IMX6ULL 提供了 PWM 外设,因此可以配置 PWM 外设来产生 PWM 信号

二. IMX6ULL 的 PWM 模块

2.1 PWM 模块

2.1.1 与 LCD 相关

原理图:

可以看到 LCDPWM 引脚连接在了GPIO1_IO8 这个引脚上

IMX6ULL 开发板的 LCD 背光控制引脚连接在 IMX6ULL 的 GPIO1_IO8 上,GPIO1_IO8 可以复用 为PWM1_OUT

2.1.2 PWM 模块

IMX6ULL 的 PWM 模块如下图所示:

图中的各部分功能如下:

、此部分是一个选择器,用于选择 PWM 信号的时钟源,一共有三种时钟源:ipg_clk、i pg_clk_highfreqipg_clk_32k

、这是一个 12 位的分频器,可以对中选择的时钟源进行分频。

、这是 PWM 的 16 位计数器寄存器 ,保存着 PWM 的计数值

、这是 PWM 的 16 位周期寄存器 ,此寄存器用来控制 PWM 的频率

、这是 PWM 的 16 位采样寄存器 ,此寄存器用来控制 PWM 的占空比

、此部分是 PWM 的中断信号,PWM 是提供中断功能的,如果使能了相应的中断的话就会产生中断。

、此部分是 PWM 对应的输出 IO,产生的 PWM 信号就会从对应的 IO 中输出

2.2 PWM 原理

可以通过配置相应的寄存器来设置 PWM 信号的频率和占空比 ,PWM 的 16 位计数器是个向上计数器,此计数器会从 0X0000 开始计数,直到计数值等于寄存器 PWMx_PWMPR(x=1~8) + 1 ,然后计数器就会重新从 0X0000 开始计数,如此往复。所以寄存器 PWMx_PWMPR 可以设置 PWM 的频率

在一个周期内,PWM 从 0X0000 开始计数的时候,PWM 引脚先输出高电平(默认情况下, 可以通过配置输出低电平) 。采样 FIFO 中保存的采样值会在每个时钟和计数器值进行比较,当采样值和计数器相等的话 PWM 引脚就会改为输出低电平(默认情况下,同样可以通过配置输出高电平 )。计数器会持续计数,直到和周期寄存器 PWMx_PWMPR(x=1~8) + 1 的值相等,这样一个周期就完成了。

所以,采样 FIFO 控制着占空比,而采样 FIFO 里面的值来源于采样寄存器 PWMx_PWMSAR因此相当于 PWMx_PWMSAR 控制着占空比。至此,PWM 信号的频率和占 空比设置就知道该如何去做了。

在开启 PWM 之前最好设置好 PWMx_PWMPRPWMx_PWMSAR 这两个寄存器,也就是设置好 PWM 的频率和占空比。 当向 PWMx_PWMSAR 寄存器写入采样值的时候,如果 FIFO 没满的话其值会被存储到 FIFO 中。如果 FIFO 满的时候写入采样值就会导致寄存器 PWMx_PWMSR 的位 FWE(bit6) 置 1,表示 FIFO 写错误,FIFO 里面的值也并不会改变。FIFO 可以在任何时候写入,但是只有在 PWM 使能的情况下读取。寄存器 PWMx_SR 的位 FIFOAV(bit2:0)记录着当前 FIFO 中有多少个数据。从采样寄存器 PWMx_PWMSAR 读取一次数据,FIFO 里面的数据就会减一,每产生一个周期的 PWM 信号,FIFO 里面的数据就会减一,相当于被用掉了。PWM 有个 FIFO 空中断,当 FIFO 为空的时候就会触发此中断,可以在此中断处理函数中向 FIFO 写入数据

**其基本原理是:**PWM外设有一个16位自增计数器,当外设以设定的某个频率工作时,能够到达某个给定计数值时控制外部某个引脚的电平发生变化。这个计数值就是PWM中的周期值。例如

PWM1控制GPIO1_IO08引脚,在启动PWM时,该引脚为高电平,当到达计数值的时候引脚再次恢复为高电平。

那么什么时候引脚会变成低电平呢? PWM内部还有一个比较寄存器,当计数的值与比较寄存器的值相等时,引脚电平就来一次翻转。举个例子:假设设置计数值为1000,比较寄存器的值是500,计数器从0开始计数,一开始计数时引脚电平是高,当计数到500时,引脚自动变为低,继续计数到1000时,引脚再次恢复为高。这样就产生了一个占空比为50%的PWM信号。而1000和PWM的实际工作频率则决定了PWM的周期。

需要注意的是,上述原理中的比较寄存器在IMX6U的PWM外设中被设计为一个16X4FIFO。 16指的是比较值的位数, 4指的是FIFO的个数。换句话说就是这个比较值其实是被放到一个长度为4的队列中去的。每次比较产生电平翻转后, FIFO里的数据就会少一个,当FIFO小于某个我们设定的数时就会产生中断。很明显,随着这个FIFO里的值发生变化, PWM的占空比也会发生变化

2.3 与 PWM 配置相关的寄存器

2.3.1 PWMx_PWMCR寄存器

PWM控制寄存器

|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| FWM | FIFO 水位线,用来设置 FIFO 空余位置为多少的时候表示 FIFO 为空。 设置为 0 的时候表示 FIFO 空余位置大于等于 1 的时候 FIFO 为空; 设置为 1 的时候表示 FIFO 空 余位置大于等于 2 的时候 FIFO 为空; 设置为 2 的时候表示 FIFO 空余位置大于等于 3 的时候 F IFO 为空; 设置为 3 的时候表示 FIFO 空余位置大于等于 4 的时候 FIFO 为空 |
| STOPEN | 此位用来设置停止模式下 PWM 是否工作 为 0 的话表示在停止模式下PWM 继续工作, 为 1 的话表示停止模式下关闭 PWM |
| DOZEN | 此位用来设置休眠模式下 PWM 是否工作 为 0 的话表示在休眠模式下PWM 继续工作, 为 1 的话表示休眠模式下关闭 PWM; |
| WAITEN | 此位用来设置等待模式下 PWM 是否工作, 为 0 的话表示在等待模式下PWM 继续工作, 为 1 的话表示等待模式下关闭 PWM; |
| DBGEN | 此位用来设置调试模式下 PWM 是否工作, 为 0 的话表示在调试模式下PWM 继续工作, 为 1 的话表示调试模式下关闭 PWM |
| BCTR | 字节交换控制位,用来控制 16 位的数据进入 FIFO 的字节顺序。 为 0 的时候不进行字节交换, 为 1 的时候进行字节交换 |
| HCTR | 半字交换控制位,用来决定从 32 位 IP 总线接口传输来的哪个半字数据写入采样寄存器的 低 16 位中 |
| POUTC | PWM 输出控制控制位,用来设置 PWM 输出模式, 为 0 的时候表示PWM 先输出高电平,当计数器值和采样值相等的话就输出低电平。 为 1 的时候相反, 当为 2 或者 3 的时候 PWM 信号不输出。 设置为 0,也就是一开始输出高电平,当计数器值和采样值相等的话就改为低电平,这样采样值越大高电平时间就越长,占空比就越大 |
| CLKSRC | PWM 时钟源选择 为 0 的话关闭 为 1 的话选择 ipg_clk 为时钟源 为 2 的话选择 ipg_clk_highfreq 为时钟源 为 3 的话选择 ipg_clk_32k 为时钟源。 设置为 1,也就是选择ipg_clk 为 PWM 的时钟源,因此 PWM 时钟源频率为 66MHz |
| PRESCALER | 分频值,可设置为 0~4095,对应着 1~4096 分频。 设置为65,就是66分频,PWM实际工作频率为1MHz |
| SWR | 软件复位 向此位写 1 就复位 PWM,此位是自清零的,当复位完成以后此位会自动清零 |
| REPEAT | 重复采样设置,此位用来设置 FIFO 中的每个数据能用几次。 00:一次 01:两次 10:四次 11:八次 设置为 3,即 FIFO 中的每个数据用8次 |
| EN | PWM 使能位, 为 1 的时候使能 PWM, 为 0 的时候关闭 PWM |

2.3.2 PWMx_PWMIR 寄存器

中断控制寄存器

|---------|----------------------------------------------------------------------|
| CIE | 比较中断使能位 为 1 的时候使能比较中断 为 0 的时候关闭比较中断 |
| RIE | 翻转中断使能位 当计数器值等于采样值并回滚到 0X0000 的时候就会产生此中断 为 1 的时候使能翻转中断 为 0 的时候关闭翻转中断 |
| FIE | FIFO 空中断 为 1 的时候使能 为 0 的时候关闭 需要使能这个中断 |

2.3.3 PWMx_PWMSR寄存器

状态及标志寄存器

|------------|-------------------------------------------------------------|
| FWE | FIFO 写错误事件,为 1 的时候表示发生了 FIFO 写错误 |
| CMP | FIFO 比较事件发标志位,为 1 的时候表示发生 FIFO 比较事件 |
| ROV | 翻转事件标志位,为 1 的话表示翻转事件发生 |
| FE | FIFO 到达水位线标志, 为 1 的时候表示 FIFO 已到达水位线。 要在中断中关注这个标志,并及时清零; |
| FIFOAV | 此位记录 FIFO 中的有效数据个数,有效值为 0~4,分别表示 FIFO 中有0~4 个有效数 据。 |

2.3.4 PWMx_PWMPR 寄存器

**PWM 周期寄存器:**这个是 PWM 周期寄存器,可以通过此寄存器来 设置 PWM 的频率

从图可以看出,寄存器 PWMx_PWMPR 只有低 16 位有效,当 PWM 计数器的值等 于 PERIOD+1 的时候就会从 0X0000 重新开始计数,开启另一个周期。PWM 的频率计算公式 如下:

其中 PCLK 是最终进入 PWM 的时钟频率,假如 PCLK 的频率为 1MHz,现在要产生一个频率为 1KHz 的 PWM 信号,那么就可以设置PERIOD = 1000000 / 1000 -- 2 = 998

2.3.5 PWMx_PWMSAR 寄存器

**采用寄存器:**采样寄存器用于设置占空比

此寄存器也是只有低 16 位有效,为采样值。通过这个采样值即可调整占空比,当计数器的值小于 SAMPLE 的时候输出高电平(或低电平)。当计数器值大于等于 SAMPLE,小于寄存器 PWM1_PWMPR 的 PERIO 的时候输出低电平(或高电平)

同样在上面的例子中,假如要设置 PWM 信号的占空比为 50%,那么就可以将 SAMPLE 设置为(PERIOD + 2) / 2 = 1000 / 2 =500

三. PWM 模块示例代码

3.1 初始化函数

cpp 复制代码
//PWM 频率设置,周期设置
void pwm_set_fre(unsigned int t)
{
    //PWM1->PWMPR = 0;
    //设置 PWM 周期为 1KHz
    //PWM1->PWMPR = 998;
    PWM1->PWMPR = 1000000 / t - 2;
}

//设置 PWM 占空比
//value范围:0 - 1
void pwm_set_duty(float value)
{
    unsigned short temp = PWM1->PWMPR + 2;
    int i = 0;
    for(i = 0; i < 4; i++)
    {
        PWM1->PWMSAR = value * temp;
    }
}


//PWM 初始化
void pwm_init(void)
{

    //设置引脚复用
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT, 1);
    //设置引脚电气属性
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT, 0xF0B0);

    //复位 PWM
    PWM1->PWMCR |= (1 << 3); 
    //等待复位完成
    while( (PWM1->PWMCR & (1 << 3)) != 0 );

    PWM1->PWMCR |= (1 << 26);
    //设置 PWM 起始极性为高电平
    PWM1->PWMCR &= ~(3 << 18);
    //时钟选择:选择 ipg_clock = 66MHz
    PWM1->PWMCR |= (1 << 16);
    //设置分频:设置66分频,PWM实际工作频率:1MHz
    PWM1->PWMCR |= (65 << 4);
    //设置FIFO中一个数据可以使用几次:一个数据可以用8次
    PWM1->PWMCR |= (0x3 << 1);
    
    //使能 PWM 中断
    PWM1->PWMIR |= (1 << 0);

    //设置频率
    pwm_set_fre(1000);

    //设置占空比
    pwm_set_duty(pwm_duty);
    

    //注册中断处理函数
    system_interrupt_register(PWM1_IRQn, pwm_irq_handler);
    //设置中断优先级
    GIC_SetPriority(PWM1_IRQn, 0);
    //使能GIC
    GIC_EnableIRQ(PWM1_IRQn);

    pwm_start();
}

3.2 中断函数

cpp 复制代码
//lcd背光pwm占空比
float pwm_duty = 0.3;



//PWM 中断处理函数
void pwm_irq_handler(void)
{
    if( (PWM1->PWMSR & (1 << 3)) != 0 )
    {
        pwm_set_duty(pwm_duty);
        //清除标志位:写一清零
        PWM1->PWMSR = (1 << 3);
    }
}
相关推荐
凉、介12 小时前
Armv8 原子性:体系结构保证与编程语义
linux·学习·嵌入式·arm·原子操作·atomic
byte轻骑兵13 小时前
如何定制BlueZ编译:开启/关闭BLE/音频模块的编译参数配置
linux·arm开发·蓝牙·bluez·电脑蓝牙
亿道电子Emdoor1 天前
【Arm】MSP和PSP的区别简介
c语言·arm开发·单片机·mcu
Lee_jerome2 天前
《C/C++编译全家桶:g++四阶段、.a与.so区别、CMake构建、ARM交叉编译,这篇全讲透了》
c语言·开发语言·arm开发·c++·编译·cmake·cmakelists
Dlrb12113 天前
ARM---IMX6ULL的LCD模块简介及LCD驱动程序的编写
arm开发·lcd·arm·imx6ull·显存·像素
深信达沙箱3 天前
漏洞修不完、合规过不了?先御OS如何让嵌入式设备‘出厂即合规
arm开发·操作系统·嵌入式操作系统
木木_王3 天前
嵌入式学习 | STM32 裸板驱动开发(Day05)超详细复习笔记外部中断 EXTI、NVIC 优先级、串口中断实战
arm开发·stm32·学习
Hi202402173 天前
Cortex-A53 + GIC700 + Boot BRAM Vivado 全流程复现
arm·fpga·vivado
振南的单片机世界3 天前
3.5T静默分帧,CRC16校验:Modbus-RTU帧结构解析
arm开发·stm32·单片机·嵌入式硬件