LCD背光调节实验
一、LCD背光调节介绍
LCD都有一个背光控制引脚,给这个背光控制引脚输入高电平就会点亮背光,输入低电平就会关闭背光。假如不断的打开和关闭背光,当速度足够快的时候就不会感觉到背光关闭的过程。PWM可以实现这个功能,PWM(Pulse Width Modulation)脉冲宽度调制,其信号如下图
PWM信号有两个关键参数:频率和占空比,频率就是开关速度,把一次开关算作一个周期,那么频率就是1秒内进行了多少次开关;占空比就是一个周期内高电平时间和低电平时间的比例。
给LCD背光引脚输入一个PWM信号,这样就可以通过调整占空比的方式来调整LCD背光亮度了。提高占空比会提高背光亮度,降低占空比会降低背光亮度。IMX6U提供了PWM外设,可以配置PWM外设来产生PWM信号
IMX6U共有8路PWM信号,每个PWM包含一个16位的计数器和一个4*16的数据FIFO,IMX6U的PWM外设结构图如下示
上图中各部分功能如下
- 选择器,用于选择PWM信号的时钟源
- 12位分频器,可对时钟源进行分频
- 16位计数器寄存器,保存PWM的计数值
- 16位周期寄存器,用来控制PWM的频率
- 16位采样寄存器,用来控制PWM的占空比
- PWM的中断信号
- PWM对应的输出IO,产生的PWM信号会从对应的IO中输出
PWM的16位计数器是个向上计数器,此计数器会从0X0000开始计数,直到计数值等于寄存器PWMx_PWMPR+1,然后计数器就会重新从0X0000开始计数,如此往复。所以PWMx_PWMPR可以设置PWM的频率
在一个周期内,PWM从0X0000开始计数的时候,PWM引脚先输出高电平(也可设置为低电平),采样FIFO中保存的采样值会在每个时钟和计数器值进行比较,当两者相等的话PWM引脚就会改为输出低电平。计数器会持续计数,直到和周期寄存器PWMx_PWMPR+1的值相等,这样一个周期就完成了。FIFO里面的采样值由采样寄存器PWMx_PWMSAR设置,所以PWMx_PWMSAR控制着占空比
几个重要的寄存器介绍,以PWM1为例
- PWM1_PWMCR 寄存器:控制寄存器
– FWM:FIFO水位线,用来设置FIFO空余位为多少时表示FIFO为空
– STOPEN:停止模式下PWM是否工作,为0时继续工作,为1时关闭
– DOZEN:休眠模式下PWM是否工作,为0时继续工作,为1时关闭
– WAITEN:等待模式下PWM是否工作,为0时继续工作,为1时关闭
– DEGEN:调试模式下PWM是否工作,为0时继续工作,为1时关闭
– BCTR:字节交换控制位,用来控制16位的数据君如FIFO的字节顺序
– HCRT:半字交换控制位,用来决定哪个半字数据写入采样寄存器的低16位中
– POUTC:PWM输出控制位,为0表示PWM先输出高电平,为1相反
– CLKSRC:PWM时钟源选择
– PRESCALER:分频值,可设置位0 ~ 4095,对应1 ~ 4096分频
– SWR:软件复位,置1就复位PWM,此位是自清零的
– REPEAT:重复采样设置,用来设置FIFO中的每个数据能用几次
– EN:PWM使能位,为1使能,为0关闭
- PWM1_PWMIR 寄存器:中断控制寄存器
– CIE:比较中断使能位,为1使能,为0关闭
– RIE:翻转中断使能位,计数值等于采样值并回滚到0x0000时产生此中断
– FIE:FIFO空中断,为1使能,为0关闭
- PWM1_PWMSR 寄存器
– FWE:FIFO写错误事件,为1时表示发生了FIFO写错误
– CMP:FIFO比较事件发生标志位,为1时表示发生FIFO比较事件
– ROV:翻转事件标志位,为1时表示翻转事件发生
– FE:FIFO空标志位,为1时表示FIFO为空
– FIFOAV:记录FIFO中的有效数据个数
- PWM1_PWMPR 寄存器:周期寄存器,用来设置PWM频率
– PERIOD:当计数器的值等于PERIOD+1时就会从0x0000重新开始计数,开启另一周期
– PWM的频率计算公式:PWMO = PCLK/(PERIOD+2)
- PWM1_PWMSAR 寄存器:采样寄存器,用来设置占空比
– SAMPLE:采样值,当计数器的值小于SAMPLE时输出高电平(或低电平),大于时输出低电平(或高电平)
综上所述,配置GPIO1_IO8引脚为PWM1输出引脚的步骤如下:
- 配置GPIO1_IO8复用功能,将其复用为PWM1_OUT信号线
- 初始化PWM1,配置频率和占空比
- 设置中断
- 使能PWM1
二、硬件介绍
本例程用到的硬件资源:
- LED0
- RGB LCD接口
- KEY0
本实验一开始设置RGB LCD背光亮度PWM信号频率为1KHz,占空比为10%,此时屏幕亮度很低。然后通过KEY0逐步的提升PWM信号的占空比,按10%步进。LED0不断闪烁提示系统正在运行
三、程序编写
- 新建backlight文件夹,在文件夹中创建实时时钟驱动文件bsp_backlight.c和bsp_backlight.h
/* 背光PWM结构体 */
struct backlight_dev_struc
{
unsigned char pwm_duty; /* 占空比 */
};
/* 函数声明 */
void backlight_init(void);
void pwm1_enable(void);
void pwm1_setsample_value(unsigned int value);
void pwm1_setperiod_value(unsigned int value);
void pwm1_setduty(unsigned char duty);
void pwm1_irqhandler(void);
/* 背光设备 */
struct backlight_dev_struc backlight_dev;
/*
* @description : pwm1中断处理函数
* @param : 无
* @return : 无
*/
void pwm1_irqhandler(void)
{
if(PWM1->PWMSR & (1 << 3)) /* FIFO为空中断 */
{
/* 将占空比信息写入到FIFO中,其实就是设置占空比 */
pwm1_setduty(backlight_dev.pwm_duty);
PWM1->PWMSR |= (1 << 3); /* 写1清除中断标志位 */
}
}
/*
* @description : 初始化背光PWM
* @param : 无
* @return : 无
*/
void backlight_init(void)
{
unsigned char i = 0;
/* 1、背光PWM IO初始化 */
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT, 0); /* 复用为PWM1_OUT */
/* 配置PWM IO属性
*bit 16:0 HYS关闭
*bit [15:14]: 10 100K上拉
*bit [13]: 1 pull功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 010 驱动能力为R0/2
*bit [0]: 0 低转换率
*/
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT, 0XB090);
/* 2、初始化PWM1 */
/*
* 初始化寄存器PWMCR
* bit[27:26] : 01 当FIFO中空余位置大于等于2的时候FIFO空标志值位
* bit[25] :
0 停止模式下PWM不工作
* bit[24] : 0 休眠模式下PWM不工作
* bit[23] : 0 等待模式下PWM不工作
* bit[22] : 0 调试模式下PWM不工作
* bit[21] : 0 关闭字节交换
* bit[20] : 0 关闭半字数据交换
* bit[19:18] : 00 PWM输出引脚在计数器重新计数的时候输出高电平
* 在计数器计数值达到比较值以后输出低电平
* bit[17:16] : 01 PWM时钟源选择IPG CLK = 66MHz
* bit[15:4] : 65 分频系数为65+1=66,PWM时钟源 = 66MHZ/66=1MHz
* bit[3] : 0 PWM不复位
* bit[2:1] : 00 FIFO中的sample数据每个只能使用一次。
* bit[0] : 0 先关闭PWM,后面再使能
*/
PWM1->PWMCR = 0; /* 寄存器先清零 */
PWM1->PWMCR |= (1 << 26) | (1 << 16) | (65 << 4);
/* 设置PWM周期为1000,那么PWM频率就是1M/1000 = 1KHz。 */
pwm1_setperiod_value(1000);
/* 设置占空比,默认50%占空比 ,写四次是因为有4个FIFO */
backlight_dev.pwm_duty = 50;
for(i = 0; i < 4; i++)
{
pwm1_setduty(backlight_dev.pwm_duty);
}
/* 使能FIFO空中断,设置寄存器PWMIR寄存器的bit0为1 */
PWM1->PWMIR |= 1 << 0;
system_register_irqhandler(PWM1_IRQn, (system_irq_handler_t)pwm1_irqhandler, NULL); /* 注册中断服务函数 */
GIC_EnableIRQ(PWM1_IRQn); /* 使能GIC中对应的中断 */
PWM1->PWMSR = 0; /* PWM中断状态寄存器清零 */
pwm1_enable(); /* 使能PWM1 */
}
/*
* @description : 使能PWM
* @param : 无
* @return : 无
*/
void pwm1_enable(void)
{
PWM1->PWMCR |= 1 << 0;
}
/*
* @description : 设置Sample寄存器,Sample数据会写入到FIFO中,
* 所谓的Sample寄存器,就相当于比较寄存器,假如PWMCR中的POUTC
* 设置为00的时候。当PWM计数器中的计数值小于Sample的时候
* 就会输出高电平,当PWM计数器值大于Sample的时候输出底电平,
* 因此可以通过设置Sample寄存器来设置占空比
* @param - value : 寄存器值,范围0~0XFFFF
* @return : 无
*/
void pwm1_setsample_value(unsigned int value)
{
PWM1->PWMSAR = (value & 0XFFFF);
}
/*
* @description : 设置PWM周期,就是设置寄存器PWMPR,PWM周期公式如下
* PWM_FRE = PWM_CLK / (PERIOD + 2), 比如当前PWM_CLK=1MHz
* 要产生1KHz的PWM,那么PERIOD = 1000000/1K - 2 = 998
* @param - value : 周期值,范围0~0XFFFF
* @return : 无
*/
void pwm1_setperiod_value(unsigned int value)
{
unsigned int regvalue = 0;
if(value < 2)
regvalue = 2;
else
regvalue = value - 2;
PWM1->PWMPR = (regvalue & 0XFFFF);
}
/*
* @description : 设置PWM占空比
* @param - value : 占空比0~100,对应0%~100%
* @return : 无
*/
void pwm1_setduty(unsigned char duty)
{
unsigned short preiod;
unsigned short sample;
backlight_dev.pwm_duty = duty;
preiod = PWM1->PWMPR + 2;
sample = preiod * backlight_dev.pwm_duty / 100;
pwm1_setsample_value(sample);
}
- 主函数main.c中编写测试程序
/*
* @description : 使能I.MX6U的硬件NEON和FPU
* @param : 无
* @return : 无
*/
void imx6ul_hardfpu_enable(void)
{
uint32_t cpacr;
uint32_t fpexc;
/* 使能NEON和FPU */
cpacr = __get_CPACR();
cpacr = (cpacr & ~(CPACR_ASEDIS_Msk | CPACR_D32DIS_Msk))
| (3UL << CPACR_cp10_Pos) | (3UL << CPACR_cp11_Pos);
__set_CPACR(cpacr);
fpexc = __get_FPEXC();
fpexc |= 0x40000000UL;
__set_FPEXC(fpexc);
}
/*
* @description : main函数
* @param : 无
* @return : 无
*/
int main(void)
{
unsigned char keyvalue = 0;
unsigned char i = 0;
unsigned char state = OFF;
unsigned char duty = 0;
imx6ul_hardfpu_enable(); /* 使能I.MX6U的硬件浮点 */
int_init(); /* 初始化中断(一定要最先调用!) */
imx6u_clkinit(); /* 初始化系统时钟 */
delay_init(); /* 初始化延时 */
clk_enable(); /* 使能所有的时钟 */
led_init(); /* 初始化led */
beep_init(); /* 初始化beep */
uart_init(); /* 初始化串口,波特率115200 */
lcd_init(); /* 初始化LCD */
backlight_init(); /* 初始化背光PWM */
tftlcd_dev.forecolor = LCD_RED;
lcd_show_string(50, 10, 400, 24, 24, (char*)"ZERO-IMX6U BACKLIGHT PWM TEST");
lcd_show_string(50, 40, 200, 16, 16, (char*)"ATOM@ALIENTEK");
lcd_show_string(50, 60, 200, 16, 16, (char*)"2019/3/27");
lcd_show_string(50, 90, 400, 16, 16, (char*)"PWM Duty: %");
tftlcd_dev.forecolor = LCD_BLUE;
/* 设置默认占空比 10% */
duty = 10;
lcd_shownum(50 + 72, 90, duty, 3, 16);
pwm1_setduty(duty);
while(1)
{
keyvalue = key_getvalue();
if(keyvalue == KEY0_VALUE)
{
duty += 10; /* 占空比加10% */
if(duty > 100) /* 如果占空比超过100%,重新从10%开始 */
duty = 10;
lcd_shownum(50 + 72, 90, duty, 3, 16);
pwm1_setduty(duty); /* 设置占空比 */
}
delayms(10);
i++;
if(i == 50)
{
i = 0;
state = !state;
led_switch(LED0,state);
}
}
return 0;
}
四、下载验证
- 修改Makefile文件:修改TARGET为backlight,追加“bsp/backlight”文件夹
- 使用imxdownload软件将bin文件下载到SD卡中
- 烧写成功后,插入SD卡,复位开发板后,LCD屏幕上显示占空比为10%的亮度,此时亮度很低;按下KEY0按键,可以以10%的跨度调节屏幕亮度,直至100%