嵌入式学习硬件I.MX6ULL(十一)framebuffer PWM

目录

一、framebufer

        1.LCD-移植小型图形库 使用framebuffer

二、PWM

        1.介绍

        2.结构

        3.引脚

        4. 相关寄存器

        5. 中断设置及函数

        6、函数


一、framebufer

        1.LCD-移植小型图形库 使用framebuffer

        UART和LCD驱动的实现是我们调试编写其他外设能够使用的利器,可以用来方便地查看程序关键变量或寄存器的值。接下来的实验我们大多使用LCD来显示实验数据,实现在LCD绘制图形、文字、图像等信息其实和之前我们使用的frame_buffer无异,这里我们简单移植一个现有的图形库,方便我们之后的使用。移植之前我们需要编写几个函数,作为图形库的接口。其实就是在指定的位置绘制点、读取某位置的像素值以及绘制一个左上点左边x0,y0右下角坐标x1,y1的矩形。

    graphics.c定义了几个常用的绘制函数,画线、画圆、写字等。我们之后最常用的是在指定位
置绘制字符串的函数:lcd_show_string。例如:

  float f;

        f = get_ori_value_adc1();

        int k = f * 1000;

        int m = k / 1000;

        int n = k % 1000;

        sprintf(buffer,"%d.%dV     ",m,n);

        lcd_show_string(100,100,16*strlen(buffer),32,32,buffer);

#include "framebuffer.h"
#include "font.h" 

/*
 * @description		: 以指定的颜色填充一块矩形
 * @param - x0		: 矩形起始点坐标X轴
 * @param - y0		: 矩形起始点坐标Y轴
 * @param - x1		: 矩形终止点坐标X轴
 * @param - y1		: 矩形终止点坐标Y轴
 * @param - color	: 要填充的颜色
 * @return 			: 读取到的指定点的颜色值
 */
void lcd_fill(unsigned    short x0, unsigned short y0, 
                 unsigned short x1, unsigned short y1, unsigned int color)
{ 
    unsigned short x, y;

	if(x0 < 0) x0 = 0;
	if(y0 < 0) y0 = 0;
	if(x1 >= lcd_dev.width) x1 = lcd_dev.width - 1;
	if(y1 >= lcd_dev.height) y1 = lcd_dev.height - 1;
	
    for(y = y0; y <= y1; y++)
    {
        for(x = x0; x <= x1; x++)
			lcd_drawpoint(x, y, color);
    }
}

/*
 * @description		: 画线函数
 * @param - x1		: 线起始点坐标X轴
 * @param - y1		: 线起始点坐标Y轴
 * @param - x2		: 线终止点坐标X轴
 * @param - y2		: 线终止点坐标Y轴
 * @return 			: 无
 */ 
void lcd_drawline(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2)
{
	unsigned short t; 
	int xerr = 0, yerr = 0, delta_x, delta_y, distance; 
	int incx, incy, uRow, uCol; 
	delta_x = x2 - x1; 					/* 计算坐标增量 */
	delta_y = y2 - y1; 
	uRow = x1; 
	uCol = y1; 
	if(delta_x > 0) 					/* 设置单步方向 */ 
		incx = 1;
	else if(delta_x==0)					/* 垂直线 */
		incx = 0;		
	else 
	{
		incx = -1;
		delta_x = -delta_x;
	} 
	if(delta_y>0)
		incy=1; 
	else if(delta_y == 0)				/* 水平线 */
		incy=0;
	else
	{
		incy = -1;
		delta_y = -delta_y;
	} 
	if( delta_x > delta_y)				/*选取基本增量坐标轴  */
		distance = delta_x; 
	else 
		distance = delta_y; 
	for(t = 0; t <= distance+1; t++ )	/* 画线输出 */
	{  

		lcd_drawpoint(uRow, uCol, lcd_dev.fore_color);/* 画点 */
		xerr += delta_x ; 
		yerr += delta_y ; 
		if(xerr > distance) 
		{ 
			xerr -= distance; 
			uRow += incx; 
		} 
		if(yerr > distance) 
		{ 
			yerr -= distance; 
			uCol += incy; 
		} 
	}  
}   

/*
 * @description	: 画矩形函数
 * @param - x1	: 矩形坐上角坐标X轴
 * @param - y1	: 矩形坐上角坐标Y轴
 * @param - x2	: 矩形右下角坐标X轴
 * @param - y2	: 矩形右下角坐标Y轴
 * @return 		: 无
 */
void lcd_draw_rectangle(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2)
{
	lcd_drawline(x1, y1, x2, y1);
	lcd_drawline(x1, y1, x1, y2);
	lcd_drawline(x1, y2, x2, y2);
	lcd_drawline(x2, y1, x2, y2);
}

/*
 * @description	: 在指定位置画一个指定大小的圆
 * @param - x0	: 圆心坐标X轴
 * @param - y0	: 圆心坐标Y轴
 * @param - y2	: 圆形半径
 * @return 		: 无
 */
void lcd_draw_Circle(unsigned short x0,unsigned short y0,unsigned char r)
{
    int mx = x0, my = y0;
    int x = 0, y = r;

    int d = 1 - r;   
    while(y > x)    	/* y>x即第一象限的第1区八分圆 */
    {
        lcd_drawpoint(x  + mx, y  + my, lcd_dev.fore_color);
        lcd_drawpoint(y  + mx, x  + my, lcd_dev.fore_color);
        lcd_drawpoint(-x + mx, y  + my, lcd_dev.fore_color);
        lcd_drawpoint(-y + mx, x  + my, lcd_dev.fore_color);

        lcd_drawpoint(-x + mx, -y + my, lcd_dev.fore_color);
        lcd_drawpoint(-y + mx, -x + my, lcd_dev.fore_color);
        lcd_drawpoint(x  + mx, -y + my, lcd_dev.fore_color);
        lcd_drawpoint(y  + mx, -x + my, lcd_dev.fore_color);
        if( d < 0)
        {
            d = d + 2 * x + 3;
        }
        else
        {
            d= d + 2 * (x - y) + 5;
            y--;
        }
        x++;
    }
}

/*
 * @description	: 在指定位置显示一个字符
 * @param - x	: 起始坐标X轴
 * @param - y	: 起始坐标Y轴
 * @param - num	: 显示字符
 * @param - size: 字体大小, 可选12/16/24/32
 * @param - mode: 叠加方式(1)还是非叠加方式(0)
 * @return 		: 无
 */
void lcd_showchar(unsigned     short x, unsigned short y,
				      unsigned char num, unsigned char size, 
				      unsigned char mode)
{  							  
    unsigned char  temp, t1, t;
	unsigned short y0 = y;
	unsigned char csize = (size / 8+ ((size % 8) ? 1 : 0)) * (size / 2);	/* 得到字体一个字符对应点阵集所占的字节数	 */	
 	num = num - ' ';  	/*得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库)  */
	for(t = 0; t < csize; t++)
	{   
		if(size == 12) temp = asc2_1206[num][t]; 		/* 调用1206字体 */
		else if(size == 16)temp = asc2_1608[num][t];	/* 调用1608字体 */
		else if(size == 24)temp = asc2_2412[num][t];	/* 调用2412字体 */
		else if(size == 32)temp = asc2_3216[num][t];	/* 调用3216字体 */
		else return;									/* 没有的字库 		*/
		for(t1 = 0; t1 < 8; t1++)
		{			    
			if(temp & 0x80)lcd_drawpoint(x, y, lcd_dev.fore_color);
			else if(mode==0)lcd_drawpoint(x, y, lcd_dev.back_color);
			temp <<= 1;
			y++;
			if(y >= lcd_dev.height) return;			/* 超区域了 */	
			if((y - y0) == size)
			{
				y = y0;
				x++;
				if(x >= lcd_dev.width) return;		/* 超区域了 */
				break;
			}
		}  	 
	}  		    	   	 	  
} 

/*
 * @description	: 计算m的n次方
 * @param - m	: 要计算的值
 * @param - n	: n次方
 * @return 		: m^n次方.
 */
unsigned int lcd_pow(unsigned char m,unsigned char n)
{
	unsigned int result = 1;	 
	while(n--) result *= m;    
	return result;
}

/*
 * @description	: 显示指定的数字,高位为0的话不显示
 * @param - x	: 起始坐标点X轴。
 * @param - y	: 起始坐标点Y轴。
 * @param - num	: 数值(0~999999999)。
 * @param - len : 数字位数。
 * @param - size: 字体大小
 * @return 		: 无
 */
void lcd_shownum(unsigned     short x, 
					 unsigned short y, 
					 unsigned int num, 
					 unsigned char len,
					 unsigned char size)
{         	
	unsigned char  t, temp;
	unsigned char  enshow = 0;						   
	for(t = 0; t < len; t++)
	{
		temp = (num / lcd_pow(10, len - t - 1)) % 10;
		if(enshow == 0 && t < (len - 1))
		{
			if(temp == 0)
			{
				lcd_showchar(x + (size / 2) * t, y, ' ', size, 0);
				continue;
			}else enshow = 1; 	 
		}
	 	lcd_showchar(x + (size / 2) * t, y, temp + '0', size, 0); 
	}
} 

/*
 * @description		: 显示指定的数字,高位为0,还是显示
 * @param - x	 	: 起始坐标点X轴。
 * @param - y	 	: 起始坐标点Y轴。
 * @param - num		: 数值(0~999999999)。
 * @param - len 	: 数字位数。
 * @param - size	: 字体大小
 * @param - mode	: [7]:0,不填充;1,填充0.
 * 					  [6:1]:保留
 *					  [0]:0,非叠加显示;1,叠加显示.
 * @return 	 		: 无
 */
void lcd_showxnum(unsigned     short x, unsigned short y, 
					  unsigned int num, unsigned char len, 
					  unsigned char size, unsigned char mode)
{  
	unsigned char t, temp;
	unsigned char enshow = 0;						   
	for(t = 0; t < len; t++)
	{
		temp = (num / lcd_pow(10, len - t- 1)) % 10;
		if(enshow == 0 && t < (len - 1))
		{
			if(temp == 0)
			{
				if(mode & 0X80) lcd_showchar(x + (size / 2) * t, y, '0', size, mode & 0X01);  
				else  lcd_showchar(x + (size / 2) * t, y , ' ', size, mode & 0X01);  
 				continue;
			}else enshow=1; 
		 	 
		}
	 	lcd_showchar( x + (size / 2) * t, y, temp + '0' , size , mode & 0X01); 
	}
} 

/*
 * @description		: 显示一串字符串
 * @param - x		: 起始坐标点X轴。
 * @param - y		: 起始坐标点Y轴。
 * @param - width 	: 字符串显示区域长度
 * @param - height	: 字符串显示区域高度
 * @param - size	: 字体大小
 * @param - p		: 要显示的字符串首地址
 * @return 			: 无
 */
void lcd_show_string(unsigned short x,unsigned short y,
						  unsigned short width,unsigned short height,
						  unsigned char size,const char *p)
{         
	unsigned char x0 = x;
	width += x;
	height += y;
    while((*p <= '~') &&(*p >= ' '))		/* 判断是不是非法字符! */ 
    {       
        if(x >= width) {x = x0; y += size;}
        if(y >= height) break;				/* 退出 */
        lcd_showchar(x, y, *p , size, 0);
        x += size / 2;
        p++;
    }  
}


二、PWM

        1.介绍

        不管是使用显示器还是手机,其屏幕背光都是可以调节的,通过调节背光就可以控制屏幕的亮度。在户外阳光强烈的时候可以通过调高背光来看清屏幕,在光线比较暗的地方可以调低背光,防止伤眼睛并且省电。 我们使用的开发吧LCD 有一个背光控制引脚,给这个背光控制引脚输入高电平就会点亮背光,输入低电平就会关闭背光。假如我们不断的打开和关闭背光,当速度足够快的时候就不会感觉到背光关闭这个过程了。这个正好可以使用 PWM 来完成,PWM 全称是 Pulse Width Modulation,也就是脉冲宽度调制,本质上来说PWM的目的就是产生一个方波。

        指标:占空比、周期(占空比=高电平/周期)

        2.结构

        其基本原理是,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的占空比也会发生变化,对于本次实验来说LCD的亮度将会发生变化。

        本次实验的目的是产生一个周期为1KHz,占空比可调的PWM信号,用于控制LCD的亮度。按下按键后占空比增加10%,当到达100%再次按下按键后,占空比为0%。

        3.引脚

        

   IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT,0);

    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT,0xb9);

        4. 相关寄存器

    PWM1->PWMCR |= (1 << 3);

    while((PWM1->PWMCR & (1 << 3)) != 0);

    PWM1->PWMCR = 0;

    PWM1->PWMCR |= (1 << 26);

    PWM1->PWMCR &= ~(3 << 18);

    PWM1->PWMCR |= (1 << 16);

    PWM1->PWMCR |= (65 << 4);

    PWM1->PWMCR |= (3 << 1);

   

    PWM1->PWMIR |= (1 << 0);

    PWM1->PWMPR = 1000 - 2;

        5. 中断设置及函数

 system_interrupt_register(pwm1_interrupt_handler,PWM1_IRQn);

 GIC_EnableIRQ(PWM1_IRQn);

float g_f = 0.5;

void pwm1_interrupt_handler(void)

{

    if(PWM1->PWMSR & (1 << 3))

    {

        set_ratio(g_f);

    }

    PWM1->PWMSR |= (1 << 3);

}

        6、函数

        接下来就是代码实现了,在pwm.c中先编写一个用于设置占空比的函数。全局变量duty_ratio就是占空比值0.0~1.0之间。函数的原理就是从PWMPR寄存器中把计数值读出来,然后按照比例计算出比较值,再把比较值写到PWMSAR寄存器中。需要注意的是该寄存器每写入一次相当于FIFO入队一次。

#include"pwm.h"
#include"MCIMX6Y2.h"
#include"fsl_iomuxc.h"
#include "core_ca7.h"
#include"interrupt.h"

void set_ratio(float x)
{
    unsigned int t;
    t = PWM1->PWMPR;
    int i;
    for (i = 0; i < 4;++i)
    {
        PWM1->PWMSAR = t * x;
    }
}

float g_f = 0.5;

void pwm1_interrupt_handler(void)
{
    if(PWM1->PWMSR &(1<<3))
    {
        set_ratio(g_f);
    }
    PWM1->PWMSR |= (1 << 3);
}

void init_pwm1(void)
{
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT, 0);
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT, 0xB9);

    PWM1->PWMCR |= (1 << 3);
    while ((PWM1->PWMCR &= (1 << 3)) != 0 )
        ;
    PWM1->PWMCR = 0;
    PWM1->PWMCR |= (1 << 26) |(1<<16) |(65<<4) |(3<<1);
    PWM1->PWMCR &= ~(3 << 18);

    PWM1->PWMIR |= (1 <<0);
    PWM1->PWMSAR = 500;
    PWM1->PWMPR = 1000-2;
    set_ratio(0.5);
    PWM1->PWMCR |= (1 << 0);
    system_interrupt_register(pwm1_interrupt_handler, PWM1_IRQn);
    GIC_EnableIRQ(PWM1_IRQn);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值