当前位置:   article > 正文

PWM实验

pwm实验

目录

一、pwm与RGB LCD

二、硬件原理

1、I.MX6U PWM

频率和占空比

2、原理图与数据手册

3、寄存器

PWM1_PWMCR

PWM1_PWMIR​编辑

PWM1_PWMSR​编辑

PWM1_PWMPR​编辑

PWM1_PWMSAR​编辑

 三、代码编写

1、编写bsp_backlight.h

2、编写bsp_backlight.c


一、pwm与RGB LCD

RGB LCD 都有一个背光控制引脚,给这个背光控制引脚输入高电平就会点亮背光,输入低电平就会关闭背光。假如我们不断的打开和关闭背光,当速度足够快时就不会感觉到背光关闭这个过程。这个正好可以使用 PWM 来完成, PWM 全称是 Pulse Width Modulation,也就是脉冲宽度调制

PWM 信号如图

 PWM 信号有两个关键的术语:频率和占空比,频率就是开关速度,把一次开关算作一个周期,那么频率就是 1 秒内进行了多少次开关。占空比就是一个周期内高电平时间和低电平时间的比例,一个周期内高电平时间越长占空比就越大,反之占空比就越小。占空比用百分之表示,如果一个周期内全是低电平那么占空比就是 0%,如果一个周期内全是高电平那么占空比就是100%。

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

二、硬件原理

1、I.MX6U PWM

I.MX6U PWM 结构框图如下

 各部分功能如下:
①、此部分是一个选择器,用于选择 PWM 信号的时钟源,一共有三种时钟源: ipg_clk、
ipg_clk_highfreq 和 ipg_clk_32k。
②、这是一个 12 位的分频器,可以对①中选择的时钟源进行分频。
③、这是 PWM 的 16 位计数器寄存器,保存着 PWM 的计数值。
④、这是 PWM 的 16 位周期寄存器,此寄存器用来控制 PWM 的频率。
⑤、这是 PWM 的 16 位采样寄存器,此寄存器用来控制 PWM 的占空比。
⑥、此部分是 PWM 的中断信号, PWM 是提供中断功能的,如果使能了相应的中断的话就会产生中断。
⑦、此部分是 PWM 对应的输出 IO,产生的 PWM 信号就会从对应的 IO 中输出

频率和占空比

PWM 的 16 位计数器是个向上计数器,此计数器会从 0X0000 开始计数,直到计数值等于寄存器 PWMx_PWMPR(x=1~8)+ 1,然后计数器就会重新从 0X0000 开始计数,如此往复。所以寄存器 PWMx_PWMPR 可以设置 PWM 的频率。
在一个周期内, PWM 从 0X0000 开始计数的时候, PWM 引脚先输出高电平(默认情况下,可以通过配置输出低电平)。采样寄存器PWMx_PWMSAR 中的FIFO 保存的采样值会在每个时钟和计数器值进行比较,当采样值和计数器相等的话 PWM 引脚就会改为输出低电平(默认情况下,同样可以通过配置输出高电平)。计数器会持续计数,直到和周期寄存器 PWMx_PWMPR(x=1~8) + 1 的值相等,这样一个周期就完成了。所以,采样 FIFO 控制着占空比,因此相当于 PWMx_PWMSAR 控制着占空比。至此, PWM 信号的频率和占空比设置就知道该如何去做了

PWM 开启以后会按照默认值运行,并产生 PWM 波形,而这个默认的 PWM 一般并不是需要的波形。因此,在开启 PWM 之前最好设置好 PWMx_PWMPR 和 PWMx_PWMSAR 这两个寄存器,也就是设置好 PWM 的频率和占空比,FIFO保存着采样值,当向PWMSAR寄存器写采样值的时候会写道FIFO里面,内每当读取一次PWMSAR寄存器,FIFO里面的数据都会减一,或者每产生一个PWM信号,FIFO的数据也会减一。直到FIFO为空,那么就无法再产生PWM信号。FIFO为空的时候会产生中断,可以在中断中向FIFO写入采样数据,也就是向PWMSAR写数据

2、原理图与数据手册

LCD 背光控制引脚连接在BLT PWM

BLT PWM在I.MX6U 的 GPIO1_IO8 上,查看数据手册

  GPIO1_IO8 可以复用为 PWM1_OUT。

3、寄存器

使用到的部分寄存器如下

PWM1_PWMCR

FWM(bit27:26): FIFO 水位线,用来设置 FIFO 空余位置为多少的时候表示 FIFO 为空。设置为 0 的时候表示 FIFO 空余位置大于等于 1 的时候 FIFO 为空;设置为 1 的时候表示 FIFO 空余位置大于等于 2 的时候 FIFO 为空;设置为 2 的时候表示 FIFO 空余位置大于等于 3 的时候FIFO 为空;设置为 3 的时候表示 FIFO 空余位置大于等于 4 的时候 FIFO 为空。
STOPEN(bit25): 此位用来设置停止模式下 PWM 是否工作,为 0 的话表示在停止模式下PWM 继续工作,为 1 的话表示停止模式下关闭 PWM。
DOZEN(bit24): 此位用来设置休眠模式下 PWM 是否工作,为 0 的话表示在休眠模式下PWM 继续工作,为 1 的话表示休眠模式下关闭 PWM。
WAITEN(bit23): 此位用来设置等待模式下 PWM 是否工作,为 0 的话表示在等待模式下PWM 继续工作,为 1 的话表示等待模式下关闭 PWM。
DEGEN(bit22): 此位用来设置调试模式下 PWM 是否工作,为 0 的话表示在调试模式下PWM 继续工作,为 1 的话表示调试模式下关闭 PWM。

BCTR(bit21): 字节交换控制位,用来控制 16 位的数据进入 FIFO 的字节顺序。为 0 的时候不进行字节交换,为 1 的时候进行字节交换。
HCRT(bit20): 半字交换控制位,用来决定从 32 位 IP 总线接口传输来的哪个半字数据写入采样寄存器的低 16 位中。
POUTC(bit19:18): PWM 输出控制控制位,用来设置 PWM 输出模式,为 0 的时候表示PWM 先输出高电平,当计数器值和采样值相等的话就输出低电平。为 1 的时候相反,当为 2 或者 3 的时候 PWM 信号不输出。本章我们设置为 0,也就是一开始输出高电平,当计数器值和采样值相等的话就改为低电平,这样采样值越大高电平时间就越长,占空比就越大。
CLKSRC(bit17:16): PWM 时钟源选择,为 0 的话关闭;为 1 的话选择 ipg_clk 为时钟源;为 2 的话选择 ipg_clk_highfreq 为时钟源;为 3 的话选择 ipg_clk_32k 为时钟源。本章我们设置为 1,也就是选择 ipg_clk 为 PWM 的时钟源,因此 PWM 时钟源频率为 66MHz。
PRESCALER(bit15:4): 分频值,可设置为 0~4095,对应着 1~4096 分频。
SWR(bit3): 软件复位,向此位写 1 就复位 PWM,此位是自清零的,当复位完成以后此位会自动清零。
REPEAT(bit2:1): 重复采样设置,此位用来设置 FIFO 中的每个数据能用几次。可设置 0~3,分别表示 FIFO 中的每个数据能用 1~4 次。本章我们设置为 0,即 FIFO 中的每个数据只能用一次。
EN(bit0): PWM 使能位,为 1 的时候使能 PWM,为 0 的时候关闭 PWM

PWM1_PWMIR

 CIE(bit2):比较中断使能位,为 1 的时候使能比较中断,为 0 的时候关闭比较中断。
RIE(bit1):翻转中断使能位,当计数器值等于采样值并回滚到 0X0000 的时候就会产生此中断,为 1 的时候使能翻转中断,为 0 的时候关闭翻转中断。
FIE(bit0): FIFO 空中断,为 1 的时候使能,为 0 的时候关闭

PWM1_PWMSR

 FWE(bit6): FIFO 写错误事件,为 1 的时候表示发生了 FIFO 写错误。
CMP(bit5): FIFO 比较事件发标志位,为 1 的时候表示发生 FIFO 比较事件。
ROV(bit4): 翻转事件标志位,为 1 的话表示翻转事件发生。
FE(bit3): FIFO 空标志位,为 1 的时候表示 FIFO 位空。
FIFOAV(bit2:1): 此位记录 FIFO 中的有效数据个数,有效值为 0~4,分别表示 FIFO 中有0~4 个有效数据。

PWM1_PWMPR

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

PWMO(Hz) = PCLK(Hz) / (PERIOD + 2)

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

PWM1_PWMSAR

 此寄存器也是只有低 16 位有效,为采样值。通过这个采样值即可调整占空比,当计数器的值小于 SAMPLE 的时候输出高电平(或低电平)。当计数器值大于等于 SAMPLE,小于寄存器PWM1_PWMPR 的 PERIO 的时候输出低电平(或高电平)。同样在上面的例子中,假如我们要设
置 PWM 信号的占空比为 50%,那么就可以将 SAMPLE 设置为(PERIOD + 2) / 2 = 1000 / 2=500

 三、代码编写

创建bsp_backlight.c和bsp_backlight.h

1、编写bsp_backlight.h

  1. #ifndef __BSP_BACKLIGHT_H
  2. #define __BSP_BACKLIGHT_H
  3. #include "imx6ul.h"
  4. #include "bsp_int.h"
  5. /* 背光PWM结构体 */
  6. struct backlight_dev_struc
  7. {
  8. unsigned char pwm_duty; /* 占空比 */
  9. };
  10. /* 函数声明 */
  11. void backlight_init(void);
  12. void pwm1_enable(void);
  13. void pwm1_setsample_value(unsigned int value);
  14. void pwm1_setperiod_value(unsigned int value);
  15. void pwm1_setduty(unsigned char duty);
  16. void pwm1_irqhandler(void);
  17. #endif

2、编写bsp_backlight.c

  1. #include "bsp_backlight.h"
  2. /* 背光设备 */
  3. struct backlight_dev_struc backlight_dev;
  4. /*
  5. * @description : pwm1中断处理函数
  6. * @param : 无
  7. * @return : 无
  8. */
  9. void pwm1_irqhandler(void)
  10. {
  11. if(PWM1->PWMSR & (1 << 3)) /* FIFO为空中断 */
  12. {
  13. /* 将占空比信息写入到FIFO中,其实就是设置占空比 */
  14. pwm1_setduty(backlight_dev.pwm_duty);
  15. PWM1->PWMSR |= (1 << 3); /*1清除中断标志位 */
  16. }
  17. }
  18. /*
  19. * @description : 初始化背光PWM
  20. * @param : 无
  21. * @return : 无
  22. */
  23. void backlight_init(void)
  24. {
  25. unsigned char i = 0;
  26. /* 1、背光PWM IO初始化 */
  27. IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT, 0); /* 复用为PWM1_OUT */
  28. /* 配置PWM IO属性
  29. *bit 16:0 HYS关闭
  30. *bit [15:14]: 10 100K上拉
  31. *bit [13]: 1 pull功能
  32. *bit [12]: 1 pull/keeper使能
  33. *bit [11]: 0 关闭开路输出
  34. *bit [7:6]: 10 速度100Mhz
  35. *bit [5:3]: 010 驱动能力为R0/2
  36. *bit [0]: 0 低转换率
  37. */
  38. IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT, 0XB090);
  39. /* 2、初始化PWM1 */
  40. /*
  41. * 初始化寄存器PWMCR
  42. * bit[27:26] : 01 当FIFO中空余位置大于等于2的时候FIFO空标志值位
  43. * bit[25] :
  44. 0 停止模式下PWM不工作
  45. * bit[24] : 0 休眠模式下PWM不工作
  46. * bit[23] : 0 等待模式下PWM不工作
  47. * bit[22] : 0 调试模式下PWM不工作
  48. * bit[21] : 0 关闭字节交换
  49. * bit[20] : 0 关闭半字数据交换
  50. * bit[19:18] : 00 PWM输出引脚在计数器重新计数的时候输出高电平
  51. * 在计数器计数值达到比较值以后输出低电平
  52. * bit[17:16] : 01 PWM时钟源选择IPG CLK = 66MHz
  53. * bit[15:4] : 65 分频系数为65+1=66,PWM时钟源 = 66MHZ/66=1MHz
  54. * bit[3] : 0 PWM不复位
  55. * bit[2:1] : 00 FIFO中的sample数据每个只能使用一次。
  56. * bit[0] : 0 先关闭PWM,后面再使能
  57. */
  58. PWM1->PWMCR = 0; /* 寄存器先清零 */
  59. PWM1->PWMCR |= (1 << 26) | (1 << 16) | (65 << 4);
  60. /* 设置PWM周期为1000,那么PWM频率就是1M/1000 = 1KHz。 */
  61. pwm1_setperiod_value(1000);
  62. /* 设置占空比,默认50%占空比 ,写四次是因为有4个FIFO */
  63. backlight_dev.pwm_duty = 50;
  64. for(i = 0; i < 4; i++)
  65. {
  66. pwm1_setduty(backlight_dev.pwm_duty);
  67. }
  68. /* 使能FIFO空中断,设置寄存器PWMIR寄存器的bit01 */
  69. PWM1->PWMIR |= 1 << 0;
  70. system_register_irqhandler(PWM1_IRQn, (system_irq_handler_t)pwm1_irqhandler, NULL); /* 注册中断服务函数 */
  71. GIC_EnableIRQ(PWM1_IRQn); /* 使能GIC中对应的中断 */
  72. PWM1->PWMSR = 0; /* PWM中断状态寄存器清零 */
  73. pwm1_enable(); /* 使能PWM1 */
  74. }
  75. /*
  76. * @description : 使能PWM
  77. * @param : 无
  78. * @return : 无
  79. */
  80. void pwm1_enable(void)
  81. {
  82. PWM1->PWMCR |= 1 << 0;
  83. }
  84. /*
  85. * @description : 设置Sample寄存器,Sample数据会写入到FIFO中,
  86. * 所谓的Sample寄存器,就相当于比较寄存器,假如PWMCR中的POUTC
  87. * 设置为00的时候。当PWM计数器中的计数值小于Sample的时候
  88. * 就会输出高电平,当PWM计数器值大于Sample的时候输出底电平,
  89. * 因此可以通过设置Sample寄存器来设置占空比
  90. * @param - value : 寄存器值,范围0~0XFFFF
  91. * @return : 无
  92. */
  93. void pwm1_setsample_value(unsigned int value)
  94. {
  95. PWM1->PWMSAR = (value & 0XFFFF);
  96. }
  97. /*
  98. * @description : 设置PWM周期,就是设置寄存器PWMPR,PWM周期公式如下
  99. * PWM_FRE = PWM_CLK / (PERIOD + 2), 比如当前PWM_CLK=1MHz
  100. * 要产生1KHz的PWM,那么PERIOD = 1000000/1K - 2 = 998
  101. * @param - value : 周期值,范围0~0XFFFF
  102. * @return : 无
  103. */
  104. void pwm1_setperiod_value(unsigned int value)
  105. {
  106. unsigned int regvalue = 0;
  107. if(value < 2)
  108. regvalue = 2;
  109. else
  110. regvalue = value - 2;
  111. PWM1->PWMPR = (regvalue & 0XFFFF);
  112. }
  113. /*
  114. * @description : 设置PWM占空比
  115. * @param - value : 占空比0~100,对应0%~100%
  116. * @return : 无
  117. */
  118. void pwm1_setduty(unsigned char duty)
  119. {
  120. unsigned short preiod;
  121. unsigned short sample;
  122. backlight_dev.pwm_duty = duty;
  123. preiod = PWM1->PWMPR + 2;
  124. sample = preiod * backlight_dev.pwm_duty / 100;
  125. pwm1_setsample_value(sample);
  126. }

3、main文件

  1. #include "bsp_clk.h"
  2. #include "bsp_delay.h"
  3. #include "bsp_led.h"
  4. #include "bsp_beep.h"
  5. #include "bsp_key.h"
  6. #include "bsp_int.h"
  7. #include "bsp_uart.h"
  8. #include "bsp_lcd.h"
  9. #include "bsp_lcdapi.h"
  10. #include "bsp_rtc.h"
  11. #include "bsp_icm20608.h"
  12. #include "bsp_spi.h"
  13. #include "stdio.h"
  14. #include "bsp_ft5426.h"
  15. #include "bsp_gt9147.h"
  16. #include "bsp_backlight.h"
  17. /*
  18. * @description : 使能I.MX6U的硬件NEON和FPU
  19. * @param : 无
  20. * @return : 无
  21. */
  22. void imx6ul_hardfpu_enable(void)
  23. {
  24. uint32_t cpacr;
  25. uint32_t fpexc;
  26. /* 使能NEON和FPU */
  27. cpacr = __get_CPACR();
  28. cpacr = (cpacr & ~(CPACR_ASEDIS_Msk | CPACR_D32DIS_Msk))
  29. | (3UL << CPACR_cp10_Pos) | (3UL << CPACR_cp11_Pos);
  30. __set_CPACR(cpacr);
  31. fpexc = __get_FPEXC();
  32. fpexc |= 0x40000000UL;
  33. __set_FPEXC(fpexc);
  34. }
  35. /*
  36. * @description : main函数
  37. * @param : 无
  38. * @return : 无
  39. */
  40. int main(void)
  41. {
  42. unsigned char i = 0,keyvalue=0,duty=0;
  43. unsigned char state = OFF;
  44. imx6ul_hardfpu_enable(); /* 使能I.MX6U的硬件浮点 */
  45. int_init(); /* 初始化中断(一定要最先调用!) */
  46. imx6u_clkinit(); /* 初始化系统时钟 */
  47. delay_init(); /* 初始化延时 */
  48. clk_enable(); /* 使能所有的时钟 */
  49. led_init(); /* 初始化led */
  50. beep_init(); /* 初始化beep */
  51. uart_init(); /* 初始化串口,波特率115200 */
  52. lcd_init(); /* 初始化LCD */
  53. backlight_init();
  54. tftlcd_dev.forecolor = LCD_RED;
  55. lcd_show_string(50, 10, 400, 24, 24, (char*)"ALPHA-IMX6UL BACKLIGHT TEST"); /* 显示字符串 */
  56. lcd_show_string(50, 40, 200, 16, 16, (char*)"MY@4384");
  57. lcd_show_string(50, 60, 200, 16, 16, (char*)"2023/2/23");
  58. /* 设置默认占空比 10% */
  59. if(tftlcd_dev.id == ATKVGA)
  60. duty=100; //VGA只能在满输出时才能亮屏
  61. else
  62. duty = 10;
  63. lcd_shownum(50 + 72, 90, duty, 3, 16);
  64. pwm1_setduty(duty);
  65. while(1)
  66. {
  67. keyvalue = key_getvalue();
  68. if(keyvalue == KEY0_VALUE)
  69. {
  70. duty += 10; /* 占空比加10% */
  71. if(duty > 100) /* 如果占空比超过100%,重新从10%开始 */
  72. duty = 10;
  73. lcd_shownum(50 + 72, 90, duty, 3, 16);
  74. pwm1_setduty(duty); /* 设置占空比 */
  75. }
  76. delayms(10);
  77. i++;
  78. if(i == 50)
  79. {
  80. i = 0;
  81. state = !state;
  82. led_switch(LED0,state);
  83. }
  84. }
  85. return 0;
  86. }

编译烧写到sd,插上开发板之后就可以通过按键进行调节显示屏背光

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号