目录
一、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);
}