一、原理
LCD屏幕上如何显示?
电子枪打到上面显示。
电子枪如何移动?
每过一个CLK移动一次。
颜色如何确定?
由电子枪给的RGB确定。
RGB数据从哪里来?
电子枪从内存的framebuff内存里取数据来打。
电子枪如何得知跳到下一行?
有专门的换行脉冲来操作。
![1a6d04b24b949ff91c427fd94b3765fb.png](https://i-blog.csdnimg.cn/blog_migrate/7c8eb5f5bc2b728f3a4846fd174632e3.jpeg)
看下面这张图片:
先看左右,两侧都有黑框,右侧的黑框前还有换行脉冲的宽度,上下同理,上面也有换行脉冲的宽度。
![85799b2df6031fc8ed6650fdf78e4984.png](https://i-blog.csdnimg.cn/blog_migrate/e772d20b7afb310d6dbf840482946e62.jpeg)
如下图,左边是内存,右边是控制器内部,我们的目的就是要通过控制LCD控制器的寄存器,让他发出合适的时序,实现数据的传输。
![53e3eb4e752464d49b42a7e7ffcdc880.png](https://i-blog.csdnimg.cn/blog_migrate/d7ee3ea9b3552886291ab37b220f9810.jpeg)
二、编程
1.编程框架
编程框架如下:
从下往上编程,先写LCD控制器,控制调用两类LCD,然后写两个LCD的寄存器配置,再然后实现画点,实现好了之后,利用画点写出画线画圆,这里面最主要的是学习不同文件之间的相互调用。
![af96d06438bde365cdd68b41809a6e1a.png](https://i-blog.csdnimg.cn/blog_migrate/001ca9be040eafe3e3898257cc8edb72.jpeg)
因为这个生成的bin文件超过4K,所以必须使用nand flash 启动。这两种启动方式可以翻看以前的文章。
如何理解SDRAM和SRAM?
答:首先他们都是RAM。
SDRAM(同步动态)和SRAM(静态)都是系统的内存。内存的特点就是,掉电之后不能保存信息。所以有了nand flash,这个掉电之后是可以保存的。他们关系就像RAM和ROM,RAM就是随机存取存储器,它就是我们常说的内存。ROM就是只读存储器,它出现的目的就是为了掉电可以保存东西,前期只能读取,后期不断发展,出现了EPROM,EEPROM这些东西,更有利于存储,而后来在这两种技术的发展上又发展出了NAND FLASH闪存,这就是我们现在用的U盘中用到的技术。
一开始它里面的前4K的内容先给SRAM,然后CPU控制内存SRAM将nand flash里面的东西给SDRAM进行运行。
nor flash 启动的时候,SRAM的基地址是0x4000,0000 ,nand flash 启动的时候自身基地址是0x0. nand flash 对于内存控制器来说是不能直接访问的,一般需要经过nand flash控制器。但是nand flash 启动的时候一般不是通过nand flash控制器转移代码,而是如果代码小于4K,nand flash 把代码转移到SRAM中让CPU读取,如果大于4K,就先让4K转移到SRAM,然后再利用4K的代码把剩余的转移到SDRAM中进行读取。
这4k内容里面的指令将会完成以下几个动作:
1.硬件设备初始化
2. 加载U-Boot第二阶段代码到SDRAM空间
3. 设置好栈
4. 跳转到第二阶段stage2代码入口
注意:不能 直接通过nand flash 或者nand flash 控制器读取代码,而可以直接通过nor flash 或者SDRAM,或者SRAM直接读取代码。
nor flash 可读但是不可写,但是nor flash 中又保存着全局变量或者静态变量,这些变量需要写怎么办?这时候就需要进行重定位,把nor falsh 中的变量转移到SDRAM进行写操作。另外,可以直接跑程序,不需要把程序拷贝到内存中执行。当开发板上电以后,从内存映射图可以知道,nor flash会被映射到0x00000000地址(就是nGCS0,这里就不需要片内SRAM来辅助了,所以片内SRAM的起始地址还是0x40000000,不会改变),然后cpu从0x00000000开始执行(也就是在Norfalsh中执行)整个uboot,直到引导内核启动。
综上所述,无论nor启动还是nand 启动都需要进行重定位。nand flash 启动,是需要把超过4k的代码重定位到SDRAM中,而nor flash 启动是需要把nor中不可写的变量重定位到SDRAM中进行写操作。
所以,首先记得把nand启动的相关文件添加到文件中!
下面这个链接讲的非常详细:https://www.cnblogs.com/aaronLinux/p/5540606.html
2.写LCD控制器的代码
(1)首先LCD.h文件,建立一下lcd参数模板
#ifndef _LCD_H
#define _LCD_H
enum {
NORMAL = 0,//枚举,就是换个名字
INVERT = 1,
};
/* NORMAL : 正常极性
* INVERT : 反转极性
*/
typedef struct pins_polarity {
int vclk; /* normal: 在下降沿获取数据 */
int rgb; /* normal: 高电平表示1 */
int hsync; /* normal: 高脉冲 */
int vsync; /* normal: 高脉冲 */
}pins_polarity, *p_pins_polarity;
//指针是为了传参,名字是为了作为模板在其他文件建立子类
//时序
typedef struct time_sequence {
/* 垂直方向 */
int tvp; /* vysnc脉冲宽度 */
int tvb; /* 上边黑框, Vertical Back porch */
int tvf; /* 下边黑框, Vertical Front porch */
/* 水平方向 */
int thp; /* hsync脉冲宽度 */
int thb; /* 左边黑框, Horizontal Back porch */
int thf; /* 右边黑框, Horizontal Front porch */
int vclk;
}time_sequence, *p_time_sequence;
//核心参数结构体模板
typedef struct lcd_params {
/* 引脚极性 */
pins_polarity pins_pol;
/* 时序 */
time_sequence time_seq;
/* 分辨率, bpp */
int xres;
int yres;
int bpp;
/* framebuffer的地址 */
unsigned int fb_base;
}lcd_params, *p_lcd_params;
#endif /* _LCD_H */
(2)lcd.h中的参数模板是为了让lcd_3.5.c和lcd_4.3.c这两个建立类似参数,也为了给lcd_controller.c传递参数,这里面的参数通过指针传递。
在lcd_4.3.c文件中建立框架函数
#define LCD_FB_BASE 0x33c00000
lcd_params lcd_4_3_params = {
.name = "lcd_4.3"
.pins_pol = {
.de = NORMAL, /* normal: 高电平时可以传输数据 */
.pwren = NORMAL, /* normal: 高电平有效 */
.vclk = NORMAL, /* normal: 在下降沿获取数据 */
.rgb = NORMAL, /* normal: 高电平表示1 */
.hsync = INVERT, /* normal: 高脉冲 */
.vsync = INVERT, /* normal: 高脉冲 */
},
.time_seq = {
/* 垂直方向 */
.tvp= 10, /* vysnc脉冲宽度 */
.tvb= 2, /* 上边黑框, Vertical Back porch */
.tvf= 2, /* 下边黑框, Vertical Front porch */
/* 水平方向 */
.thp= 41, /* hsync脉冲宽度 */
.thb= 2, /* 左边黑框, Horizontal Back porch */
.thf= 2, /* 右边黑框, Horizontal Front porch */
.vclk= 9, /* MHz */
},
.xres = 480,
.yres = 272,
.bpp = 16,
.fb_base = LCD_FB_BASE,
};
void lcd_4_3_add(void)
{
register_lcd(&lcd_4_3_params);
}
在lcd_controller.h声明可能用到的函数:
//防止重复包含
#ifndef _LCD_CONTROLLER_H
#define _LCD_CONTROLLER_H
#include "lcd.h"
typedef struct lcd_controller {
//在头文件的结构体里面建立三个函数
//后面是函数参数,前面函数名字
//使用时需要在前面加上"结构体名."
void (*init)(p_lcd_params plcdparams);
void (*enable)(void);
void (*disable)(void);
}lcd_controller, *p_lcd_controller;
#endif /* _LCD_CONTROLLER_H */
s3c2440_lcd_controller.c文件参照lcd_controller.h头文件的模板进行设计
struct lcd_controller s3c2440_lcd_controller = {
.init = s3c2440_lcd_controller_init,
.enalbe = s3c2440_lcd_controller_enable,
.disable = s3c2440_lcd_controller_disable,
};
在s3c2440_lcd_controller.c文件中开始写结构体中对应的三个函数
#define HCLK 100
void jz2440_lcd_pin_init(void)
{
/* 初始化为输出引脚 : 背光引脚 */
GPBCON &= ~0x3;
GPBCON |= 0x01;
/* LCD专用引脚 */
GPCCON = 0xaaaaaaaa;
GPDCON = 0xaaaaaaaa;
/* PWREN */
GPGCON |= (3<<8);
//上述引脚都是在原理图上看出来的
}
/* 根据传入的LCD参数设置LCD控制器 */
void s3c2440_lcd_controller_init(p_lcd_params plcdparams)
{
int pixelplace;
unsigned int addr;
jz2440_lcd_pin_init();
//接下来初始化涉及到的各个寄存器
/* [17:8]: clkval, vclk = HCLK / [(CLKVAL+1) x 2]
* 9 = 100M /[(CLKVAL+1) x 2], clkval = 4.5 = 5
* CLKVAL = 100/vclk/2-1
* [6:5]: 0b11, tft lcd
* [4:1]: bpp mode
* [0] : LCD video output and the logic enable/disable
*/
int clkval = (double)HCLK/plcdparams->time_seq.vclk/2-1+0.5;
int bppmode = plcdparams->bpp == 8 ? 0xb :
plcdparams->bpp == 16 ? 0xc :
0xd; /* 0xd: 24bpp */
LCDCON1 = (clkval<<8) | (3<<5) | (bppmode<<1) ;
/* [31:24] : VBPD = tvb - 1
* [23:14] : LINEVAL = line - 1
* [13:6] : VFPD = tvf - 1
* [5:0] : VSPW = tvp - 1
*/
LCDCON2 = ((plcdparams->time_seq.tvb - 1)<<24) |
((plcdparams->yres - 1)<<14) |
((plcdparams->time_seq.tvf - 1)<<6) |
((plcdparams->time_seq.tvp - 1)<<0);
/* [25:19] : HBPD = thb - 1
* [18:8] : HOZVAL = 列 - 1
* [7:0] : HFPD = thf - 1
*/
LCDCON3 = ((plcdparams->time_seq.thb - 1)<<19) |
((plcdparams->xres - 1)<<8) |
((plcdparams->time_seq.thf - 1)<<0);
/*
* [7:0] : HSPW = thp - 1
*/
LCDCON4 = ((plcdparams->time_seq.thp - 1)<<0);
/* 用来设置引脚极性, 设置16bpp, 设置内存中象素存放的格式
* [12] : BPP24BL
* [11] : FRM565, 1-565
* [10] : INVVCLK, 0 = The video data is fetched at VCLK falling edge
* [9] : HSYNC是否反转
* [8] : VSYNC是否反转
* [7] : INVVD, rgb是否反转
* [6] : INVVDEN
* [5] : INVPWREN
* [4] : INVLEND
* [3] : PWREN, LCD_PWREN output signal enable/disable
* [2] : ENLEND
* [1] : BSWP
* [0] : HWSWP
*/
pixelplace = plcdparams->bpp == 24 ? (0) : |
plcdparams->bpp == 16 ? (1) : |
(1<<1); /* 8bpp */
LCDCON5 = (plcdparams->pins_pol.vclk<<10) |
(plcdparams->pins_pol.rgb<<7) |
(plcdparams->pins_pol.hsync<<9) |
(plcdparams->pins_pol.vsync<<8) |
(plcdparams->pins_pol.de<<6) |
(plcdparams->pins_pol.pwren<<5) |
(1<<11) | pixelplace;
/* framebuffer地址 */
/*
* [29:21] : LCDBANK, A[30:22] of fb
* [20:0] : LCDBASEU, A[21:1] of fb
*/
addr = plcdparams->fb_base & ~(1<<31);
LCDSADDR1 = (addr >> 1);
/*
* [20:0] : LCDBASEL, A[21:1] of end addr
*/
addr = plcdparams->fb_base + plcdparams->xres*plcdparams->yres*plcdparams->bpp/8;
addr >>=1;
addr &= 0x1fffff;
LCDSADDR2 = addr;//
}
void s3c2440_lcd_controller_enalbe(void)
{
/* 背光引脚 : GPB0 */
GPBDAT |= (1<<0);
/* pwren : 给LCD提供AVDD */
LCDCON5 |= (1<<3);
/* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */
LCDCON1 |= (1<<0);
}
void s3c2440_lcd_controller_disable(void)
{
/* 背光引脚 : GPB0 */
GPBDAT &= ~(1<<0);
/* pwren : 给LCD提供AVDD */
LCDCON5 &= ~(1<<3);
/* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */
LCDCON1 &= ~(1<<0);
}
struct lcd_controller s3c2440_lcd_controller = {
.init = s3c2440_lcd_controller_init,
.enalbe = s3c2440_lcd_controller_enalbe,
.disable = s3c2440_lcd_controller_disable,
};
(3)小汇总一下,仍然根据下面这张结构图:
![2aa4ca3b4242c8adb6d8c2afd20fa163.png](https://i-blog.csdnimg.cn/blog_migrate/dd03873e16a3ae1825cc9d439cb11060.jpeg)
lcd.h中建立了参数模板,lcd_4.3.c中建立了对应的子类结构体,进行参数的详细书写;然后lcd_controller.h中建立了参数接收结构体模板,该结构体中有三个函数,初始化,使能,去使能。s3c2440_lcd_controller.c参照模板进行了函数详细书写。lcd.c和lcd_controller.c这两个函数是汇总函数,都是为了调用下面的lcd_4.3.和s3c2440_lcd_controller.c。具体怎么调用呢?听我细细道来:
(1)
先看lcd_controller.c文件,里面有三个函数,第一个register_lcd_controller注册函数,它一定会在s3c2440_lcd_controller.c函数中调用,如下函数所示,
void s3c2440_lcd_contoller_add(void)
{
register_lcd_controller(&s3c2440_lcd_controller);
}
目的就是将其核心结构体的地址通过指针的方式传给注册函数中的数组,来达到保存不同种类结构体地址的目的。下列代码中的第二个函数是选择函数,它可以传回一个数组的编号,同时还能把筛选出来的数组中的某个地址拿出来给一个变量,**该函数不是给向下s3c2440_lcd_controller.c或者向上lcd_4.3.c类用的,它直接是给lcd.c调用的!**第三个初始化函数,就是对第二个选出来的结构体进行初始化!同样也是在lcd.c函数中调用。注意:要区分控制器的结构体的选用和lcd参数的选用!比如说第二个函数的参数是结构体的地址,而第三个初始化函数的的参数是lcd参数的结构体地址,
最后一个函数是s3c2440函数的注册函数的调用,目的不过就是方便上层lcd.c函数可以直接在lcd_controller.c函数中调用进行函数注册,不要跨越太多阶层,其实都可以!
#define LCD_CONTROLLER_NUM 10
static p_lcd_controller p_array_lcd_controller[LCD_CONTROLLER_NUM];//建立数组
static p_lcd_controller g_p_lcd_controller_selected;
int register_lcd_controller(p_lcd_controller plcdcon)
{
int i;
for (i = 0; i < LCD_CONTROLLER_NUM; i++)
{
if (!p_array_lcd_controller[i])
{
p_array_lcd_controller[i] = plcdcon;
return i;
}
}
return -1;
}
int select_lcd_controller(char *name)
{
int i;
for (i = 0; i < LCD_CONTROLLER_NUM; i++)
{
if (p_array_lcd_controller[i] && !strcmp(p_array_lcd_controller[i]->name, name))
{
g_p_lcd_controller_selected = p_array_lcd_controller[i];
return i;
}
}
return -1;
}
/* 向上: 接收不同LCD的参数
* 向下: 使用这些参数设置对应的LCD控制器
*/
int lcd_controller_init(p_lcd_params plcdparams)
{
/* 调用所选择的LCD控制器的初始化函数 */
if (g_p_lcd_controller_selected)
{
g_p_lcd_controller_selected->init(plcdparams);
return 0;
}
return -1;
}
void lcd_contoller_add(void)
{
s3c2440_lcd_contoller_add();
}
(2)
再看看lcd.c文件,如下,同样是三个函数,第一个注册函数,在其子函数 lcd_4_3.c函数中专门有如下
void lcd_4_3_add(void)
{
register_lcd(&lcd_4_3_params);
}
函数进行调用注册,接下来是选择函数,和上面同理,会返回一个数组编号,同时也会把对应的lcd种类的结构体指针保存在一个变量中,方便下面初始化函数调用。注意:lcd.c中的初始化函数与控制器的初始化函数完全不同!这里是所有文件汇集的地方,在这里将对所有的核心函数进行调用,上一个选择函数选出来的结构体参数也是给调用的控制器函数使用!
第一个是lcd_4.3.c中的注册函数调用。
第二个是lcd_controller.c文件中的调用。
第三个是本文件的选择函数的调用。
第四个是lcd_controller.c文件中选择函数的调用。
第五个是lcd_controller.c文件中初始化函数的调用。
#define LCD_NUM 10
static p_lcd_params p_array_lcd[LCD_NUM];
static p_lcd_params g_p_lcd_selected;
int register_lcd(p_lcd_params plcd)
{
int i;
for (i = 0; i < LCD_NUM; i++)
{
if (!p_array_lcd[i])
{
p_array_lcd[i] = plcd;
return i;
}
}
return -1;
}
int select_lcd(char *name)
{
int i;
for (i = 0; i < LCD_NUM; i++)
{
if (p_array_lcd[i] && !strcmp(p_array_lcd[i]->name, name))
{
g_p_lcd_selected = p_array_lcd[i];
return i;
}
}
return -1;
}
int lcd_init(void)
{
/* 注册LCD */
lcd_4_3_add();
/* 注册LCD控制器 */
lcd_contoller_add();
/* 选择某款LCD */
select_lcd("lcd_4.3");
/* 选择某款LCD控制器 */
select_lcd_controller("s3c2440");
/* 使用LCD的参数, 初始化LCD控制器 */
lcd_controller_init(g_p_lcd_selected);
}
(4)接下来这部分就是进行测试了,韦老师的代码很多都没有添加头文件,这个大家写的时候一定要注意一下!然后在lcd_test.c文件中进行测试。
首先在上节的lcd.c文件中添加了这几个函数,第一个函数保证了获得
void get_lcd_params(unsigned int *fb_base, int *xres, int *yres, int *bpp)
{
*fb_base = g_p_lcd_selected->fb_base;
*xres = g_p_lcd_selected->xres;
*yres = g_p_lcd_selected->yres;
*bpp = g_p_lcd_selected->bpp;
}
void lcd_enable(void)
{
lcd_controller_enable();
}
void lcd_disable(void)
{
lcd_controller_disable();
}
void lcd_test(void)
{
unsigned int fb_base;
int xres, yres, bpp;
int x, y;
unsigned short *p;
unsigned int *p2;
/* 初始化LCD */
lcd_init();
/* 使能LCD */
lcd_enable();
/* 获得LCD的参数: fb_base, xres, yres, bpp */
get_lcd_params(&fb_base, &xres, &yres, &bpp);
//注意这里利用指针传递
/* 往framebuffer中写数据 */
if (bpp == 16)
{
/* 让LCD输出整屏的红色 */
/* 565: 0xf800 */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0xf800;
/* green */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0x7e0;
/* blue */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0x1f;
}
else if (bpp == 32)
{
/* 让LCD输出整屏的红色 */
/* 0xRRGGBB */
p2 = (unsigned int *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p2++ = 0xff0000;
/* green */
p2 = (unsigned int *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p2++ = 0x00ff00;
/* blue */
p2 = (unsigned int *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p2++ = 0x0000ff;
}
}
记得修改makefile,把新添加的文件都要放进去
objs += lcd/font.o
objs += lcd/framebuffer.o
objs += lcd/geometry.o
objs += lcd/lcd.o
objs += lcd/lcd_4.3.o
objs += lcd/lcd_controller.o
objs += lcd/lcd_test.o
objs += lcd/s3c2440_lcd_controller.o
(5)最后实现画点画圆函数
画点:
提问:给你一个坐标,你怎样在lcd屏幕上找到位置?
看图:首先要知道基地址fb_base,然后截至目标位置,上面有x行,每行有x*(bpp/8)个字节,所以截至目标位置上一行,总共有yx(bpp/8)个字节,再加上目标位置那一行的前几个位置为x*(bpp/8)个,这样就找到了目标位置了。最后要注意,不同的bpp它的数据类型是不同的,所以利用switch进行选择。这就是fb_put_pixel(int x, int y, unsigned int color)这个函数的设置。同时颜色不同的bpp的数据类型也不同,存储方式不同,所以也要单独设置一下。
![1d5528100a38e0b3cf8883a85b770b81.png](https://i-blog.csdnimg.cn/blog_migrate/530dfe4bb1e31657b11aab15dd7248b2.jpeg)
首先在frambuffer.c文件中添加强制转换函数
#include "lcd.h"//记得添加
/* 实现画点 */
/* 先要获得LCD参数 */
static unsigned int fb_base;
static int xres, yres, bpp;
void fb_get_lcd_params(void)
{
get_lcd_params(&fb_base, &xres, &yres, &bpp);
}
/* rgb: 0x00RRGGBB */
unsigned short convert32bppto16bpp(unsigned int rgb)
{
int r = (rgb >> 16)& 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
/* rgb565 */
r = r >> 3;
g = g >> 2;
b = b >> 3;
return ((r<<11) | (g<<5) | (b));
}
/* color : 32bit, 0x00RRGGBB
*
*/
void fb_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pc; /* 8bpp */
unsigned short *pw; /* 16bpp */
unsigned int *pdw; /* 32bpp */
unsigned int pixel_base = fb_base + (xres * bpp / 8) * y + x * bpp / 8;
switch (bpp)
{
case 8:
pc = (unsigned char *) pixel_base;
*pc = color;
break;
case 16:
pw = (unsigned short *) pixel_base;
*pw = convert32bppto16bpp(color);
break;
case 32:
pdw = (unsigned int *) pixel_base;
*pdw = color;
break;
}
}
添加framebufeer.h文件中声明一下
#ifndef _FRAMEBUFFER_H
#define _FRAMEBUFFER_H
void fb_get_lcd_params(void);
void fb_put_pixel(int x, int y, unsigned int color);
#endif /* _FRAMEBUFFER_H */
有了画点函数,就可以去画圆了,记得把里面的画点函数改成我们自己写的画圆函数。
#include "framebuffer.h"
/*
* http://blog.csdn.net/p1126500468/article/details/50428613
*/
//-------------画圆函数。参数:圆心,半径,颜色----------
// 画1/8圆 然后其他7/8对称画
// ---------------->X
// |(0,0) 0
// | 7 1
// | 6 2
// | 5 3
// (Y)V 4
//
// L = x^2 + y^2 - r^2
void draw_circle(int x, int y, int r, int color)
{
int a, b, num;
a = 0;
b = r;
while(22 * b * b >= r * r) // 1/8圆即可
{
fb_put_pixel(x + a, y - b,color); // 0~1
fb_put_pixel(x - a, y - b,color); // 0~7
fb_put_pixel(x - a, y + b,color); // 4~5
fb_put_pixel(x + a, y + b,color); // 4~3
fb_put_pixel(x + b, y + a,color); // 2~3
fb_put_pixel(x + b, y - a,color); // 2~1
fb_put_pixel(x - b, y - a,color); // 6~7
fb_put_pixel(x - b, y + a,color); // 6~5
a++;
num = (a * a + b * b) - r*r;
if(num > 0)
{
b--;
a--;
}
}
}
//-----------画线。参数:起始坐标,终点坐标,颜色--------
void draw_line(int x1,int y1,int x2,int y2,int color)
{
int dx,dy,e;
dx=x2-x1;
dy=y2-y1;
if(dx>=0)
{
if(dy >= 0) // dy>=0
{
if(dx>=dy) // 1/8 octant
{
e=dy-dx/2;
while(x1<=x2)
{
fb_put_pixel(x1,y1,color);
if(e>0){y1+=1;e-=dx;}
x1+=1;
e+=dy;
}
}
else // 2/8 octant
{
e=dx-dy/2;
while(y1<=y2)
{
fb_put_pixel(x1,y1,color);
if(e>0){x1+=1;e-=dy;}
y1+=1;
e+=dx;
}
}
}
else // dy<0
{
dy=-dy; // dy=abs(dy)
if(dx>=dy) // 8/8 octant
{
e=dy-dx/2;
while(x1<=x2)
{
fb_put_pixel(x1,y1,color);
if(e>0){y1-=1;e-=dx;}
x1+=1;
e+=dy;
}
}
else // 7/8 octant
{
e=dx-dy/2;
while(y1>=y2)
{
fb_put_pixel(x1,y1,color);
if(e>0){x1+=1;e-=dy;}
y1-=1;
e+=dx;
}
}
}
}
else //dx<0
{
dx=-dx; //dx=abs(dx)
if(dy >= 0) // dy>=0
{
if(dx>=dy) // 4/8 octant
{
e=dy-dx/2;
while(x1>=x2)
{
fb_put_pixel(x1,y1,color);
if(e>0){y1+=1;e-=dx;}
x1-=1;
e+=dy;
}
}
else // 3/8 octant
{
e=dx-dy/2;
while(y1<=y2)
{
fb_put_pixel(x1,y1,color);
if(e>0){x1-=1;e-=dy;}
y1+=1;
e+=dx;
}
}
}
else // dy<0
{
dy=-dy; // dy=abs(dy)
if(dx>=dy) // 5/8 octant
{
e=dy-dx/2;
while(x1>=x2)
{
fb_put_pixel(x1,y1,color);
if(e>0){y1-=1;e-=dx;}
x1-=1;
e+=dy;
}
}
else // 6/8 octant
{
e=dx-dy/2;
while(y1>=y2)
{
fb_put_pixel(x1,y1,color);
if(e>0){x1-=1;e-=dy;}
y1-=1;
e+=dx;
}
}
}
}
}
头文件中声明一下:
#ifndef _GEOMETRY_H
#define _GEOMETRY_H
void draw_circle(int x, int y, int r, int color);
void draw_line(int x1,int y1,int x2,int y2,int color);
#endif /* _GEOMETRY_H */
最后在测试函数中测试一下:
首先记得获得以下参数:
/* 获得LCD的参数: fb_base, xres, yres, bpp */
get_lcd_params(&fb_base, &xres, &yres, &bpp);
fb_get_lcd_params();
然后测试:
delay(1000000);
/* 画线 */
draw_line(0, 0, xres - 1, 0, 0xff0000);
draw_line(xres - 1, 0, xres - 1, yres - 1, 0xffff00);
draw_line(0, yres - 1, xres - 1, yres - 1, 0xff00aa);
draw_line(0, 0, 0, yres - 1, 0xff00ef);
draw_line(0, 0, xres - 1, yres - 1, 0xff4500);
draw_line(xres - 1, 0, 0, yres - 1, 0xff0780);
delay(1000000);
/* 画圆 */
draw_circle(xres/2, yres/2, yres/4, 0xff00);