21_I.MX6ULL_PWM背光实验

目录

LCD背光调节简介

相关寄存器

实验源码


LCD背光调节简介

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

21_I.MX6ULL_PWM背光实验_第1张图片

 

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

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

I.MX6U一共有8路PWM信号,每个PWM包含一个16位的计数器和一个4×16的数据 FIFO.I.MX6U的PWM外设结构如图所示:

21_I.MX6ULL_PWM背光实验_第2张图片

 1.此部分是一个选择器,用于选择PWM信号的时钟源,一共有三种时钟源:ipg_clk、ipg_clk_highfreq和ipg_clk_32k

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

3.这是PWM的16位计数器寄存器,保存着PWM的计数值。

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

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

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

7.此部分是PWM对应的输出IO,产生的PWM信号就会从对应的IO中输出, LCD背光控制引脚连接在I.MX6U的GPIO1_IO8上, GPIO1_IO8可以复用为PWM1_OUT

可以通过配置相应的寄存器来设置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开启以后会按照默认值运行,并产生PWM波形,而这个默认的PWM一般并不是我们需要的波形。如果这个PWM波形控制着设备的话就会导致设备因为接收到错误的PWM信号而运行错误,严重情况下可能会损坏设备,甚至人身安全。因此,在开启PWM之前最好设置好PWMx_PWMPR和PWMx_PWMSA 这两个寄存器,也就是设置好 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写入数据。

相关寄存器

寄存器PWM1_PWMCR寄存器

21_I.MX6ULL_PWM背光实验_第3张图片

 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信号不输出。

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次。

EN(bit0):PWM使能位,为1的时候使能PWM,为0的时候关闭PWM。

寄存器PWM1_PWMIR寄存器

这个是PWM的中断控制寄存器

21_I.MX6ULL_PWM背光实验_第4张图片

 CIE(bit2):比较中断使能位,为1的时候使能比较中断,为0的时候关闭比较中断。

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

FIE(bit0):FIFO空中断,为1的时候使能,为0的时候关闭。

寄存器 PWM1_PWMSR

这是状态寄存器

21_I.MX6ULL_PWM背光实验_第5张图片

 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寄存器

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

 寄存器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,小于寄存器PWM_ PWMPR的PERIO的时候输出低电平(或高电平)。同样在上面的例子中,假如我们要设置PWM信号的占空比为50%,那么就可以将SAMPLE设置为(PERIOD+2)/2= 1000/2-500

实验源码

#ifndef _BACKLIGHT_H
#define _BACKLIGHT_H

#include "imx6ul.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);

#endif
#include "bsp_backlight.h"
#include "bsp_int.h"
#include "stdio.h"



/* 背光设备 */
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);
}

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "bsp_lcd.h"
#include "bsp_lcdapi.h"
#include "bsp_rtc.h"
#include "bsp_backlight.h"
#include "stdio.h"

/*
 * @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*)"2023");   
	lcd_show_string(50, 90, 400, 16, 16, (char*)"PWM Duty:   %");  
	tftlcd_dev.forecolor = LCD_BLUE;

	/* 设置默认占空比 10% */
	if(tftlcd_dev.id == ATKVGA)
		duty=100;	//VGA只能在满输出时才能亮屏
	else
		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;
}

你可能感兴趣的:(ARM(IMX6U)裸机,嵌入式硬件,linux,I.MX6ULL,arm开发)