📄 lcd_drv.c
字号:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/fcntl.h>
#include <asm/unistd.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "ep7312_sys.h"
#include "lcd_struct.h"
// ioctl 命令ID
#define LCD_Clear 0
#define LCD_Draw_Pixel 1
#define LCD_Draw_VLine 2
#define LCD_Draw_HLine 3
#define LCD_Draw_Rectangle 4
#define LCD_Write_EN 10
#define LCD_Write_CN 11
#define LCD_Save_SCR 12
#define LCD_Load_SCR 13
#define LCD_Release_SCR 14
// 定义LCD的主设备号
#define DEV_MAJOR 60
// 定义LCD显示模式参数
#define SCR_X 320 /* LCD屏幕宽度 */
#define SCR_Y 240 /* LCD屏幕高度 */
#define BPP 4 /* LCD显示模式 */
// 半字节复制标志
#define LowS 0 /* 取字节低四位标志 */
#define HighS 1 /* 取字节高四位标志 */
// LCD显示缓存的基址
static unsigned char * __lcd_base;
/* 设置LCD寄存器 */
static void setup_lcd(void)
{
// 禁用LCD设备
SYSCON1 &= ~0x00001000;
// 设置LCD控制寄存器
// 显示缓存容量[0:12] : 320 * 240 * 12 / 128 = 0x1c1f
// 线长度[13:18] : 320 / 16 - 1 = 0x13
// 像素预定标[19:24] : 0x01
// AC预定标[25:29] : 0x13
// 灰度使能[30] : = 1, 使用灰度级显示
// 灰度级模式[31] : = 1, 4 bpp模式(16灰度级)
LCDCON = 0xe60f7c1f;
// 设置调色板颜色寄存器的低位和高位有效字
PALLSW = 0x76543210; /* 低位有效字 */
PALMSW = 0xfedcba98; /* 高位有效字 */
// 设置LCD显示缓冲区在系统存储区域的起始位置
FBADDR = 0xc;
// 启用LCD设备
SYSCON1 |= 0x00001000;
return;
}
// 限制横坐标范围
// 使横坐标在0至SCR_X之间
static int xFormat(int x)
{
int fx;
fx = x;
fx = (fx < 0) ? 0 : fx;
fx = (fx >= SCR_X) ? (SCR_X - 1) : fx;
return fx;
}
// 限制纵坐标范围
// 使纵坐标在0至SCR_Y之间
static int yFormat(int y)
{
int fy;
fy = y;
fy = (fy < 0) ? 0 : fy;
fy = (fy >= SCR_Y) ? (SCR_Y - 1) : fy;
return fy;
}
// 释放保存屏幕缓冲区
static void lcd_kernel_release_screen(struct save_struct * buffer)
{
if ((*buffer).save_buf != NULL)
{
// 释放缓冲区
kfree((*buffer).save_buf);
// 恢复缓冲区初始化设置
(*buffer).save_buf = NULL;
(*buffer).save_w = (*buffer).save_h = 0;
}
}
// 保存屏幕部分区域的内容
static void lcd_kernel_save_screen(int x1, int y1, int x2, int y2, struct save_struct * buffer)
{
unsigned char * fb_lptr; /* 每行第一个点的显存地址 */
unsigned char bufferByte; /* 临时缓存字节 */
long buffer_size; /* 保存屏幕缓冲区的字节数 */
int factorCount; /* 每行需要存取的半字节的个数 */
int sb_sign, fb_sign; /* 半字节复制标志, = LowS: 复制低四位, = HighS: 复制高四位*/
int p_sb; /* 保存屏幕缓冲区指针偏移量 */
int p_fb; /* 显示缓冲区指针偏移量 */
int currLine; /* 当前行的纵坐标 */
int tmp, i;
// 修正保存范围坐标, 使其在屏幕的显示范围之内
x1 = xFormat(x1);
x2 = xFormat(x2);
y1 = yFormat(y1);
y2 = yFormat(y2);
// 修正保存范围的横坐标, 使x2值永远大于x1的值
if (x1 > x2)
{
tmp = x1;
x1 = x2;
x2 = tmp;
}
// 修正保存范围的纵坐标, 使y2值永远大于y1的值
if (y1 > y2)
{
tmp = y1;
y1 = y2;
y2 = tmp;
}
// 计算新保存的屏幕的宽度和高度
(*buffer).save_w = x2 - x1 + 1;
(*buffer).save_h = y2 - y1 + 1;
// 计算新缓冲区大小
buffer_size = (*buffer).save_w * (*buffer).save_h * 3 * BPP / 8 + 1;
// 申请新的屏幕缓冲区
(*buffer).save_buf = kmalloc(buffer_size, GFP_KERNEL);
// 初始化保存屏幕半字节复制标志
sb_sign = LowS;
// 计算每行中半字节的个数
factorCount = (*buffer).save_w * 3;
// 初始化屏幕缓冲区指针偏移量
p_sb = 0;
// 复制显存的内容到保存屏幕缓冲区中
for (currLine = y1; currLine <= y2; currLine++)
{
// 计算当前行第一个点的显存地址
fb_lptr = __lcd_base + (x1 + currLine * SCR_X) * 3 * BPP / 8;
// 初始化显存半字节复制标志
fb_sign = (x1 & 0x1);
// 初始化显示缓冲区指针偏移量
p_fb = 0;
// 复制显存当前行的内容
for (i = 0; i < factorCount; i++)
{
// 临时缓冲字节清零
bufferByte &= 0x00;
// 根据显存半字节复制标志, 将显存中当前字节的内容的低四位(或高四位)
// 复制到临时缓冲字节的低四位中
if (fb_sign == LowS)
bufferByte = (fb_lptr[p_fb] & 0x0f);
else
bufferByte = (fb_lptr[p_fb] & 0xf0) >> 4;
// 根据保存屏幕缓冲区半字节复制标志,
// 对保存屏幕缓冲区当前字节中相应的四位清零;
// 如果是要复制高四位的内容,
// 还要将临时缓冲字节的低四位内容移到高四位中
if (sb_sign == HighS)
{
(*buffer).save_buf[p_sb] &= 0x0f;
bufferByte = bufferByte << 4;
}
else
(*buffer).save_buf[p_sb] &= 0xf0;
// 用临时缓冲字节更新保存屏幕缓冲区的当前字节
(*buffer).save_buf[p_sb] |= bufferByte;
// 根据半字节复制标志, 设置相应的指针偏移量
if (fb_sign == HighS)
p_fb++;
if (sb_sign == HighS)
p_sb++;
// 更新半字节复制标志
sb_sign ^= 0x1;
fb_sign ^= 0x1;
}
}
}
// 加载屏幕部分区域的内容
static void lcd_kernel_load_screen(int x1, int y1, struct save_struct buffer)
{
unsigned char * fb_lptr; /* 每行第一个点的显存地址 */
unsigned char bufferByte; /* 临时缓存字节 */
int factorCount; /* 每行需要存取的半字节的个数 */
int sb_sign, fb_sign; /* 半字节复制标志, = LowS: 复制低四位, = HighS: 复制高四位*/
int p_sb; /* 保存屏幕缓冲区指针偏移量 */
int p_fb; /* 显示缓冲区指针偏移量 */
int x2, y2; /* 加载区域右下角的坐标 */
int currLine; /* 当前行的纵坐标 */
int i;
// 修正加载范围坐标, 使其在屏幕的显示范围之内
x1 = xFormat(x1);
y1 = xFormat(y1);
// 计算加载范围右下角的坐标
x2 = x1 + buffer.save_w - 1;
y2 = y1 + buffer.save_h - 1;
// 如果加载范围超出屏幕的显示范围
// 则不作任何操作返回
if (x2 >= SCR_X || y2 >= SCR_Y)
{
printk(KERN_INFO "Load position out of screen boundary!\n");
return;
}
// 如果尚未执行保存屏幕的操作,
// 则不作任何操作返回
if (buffer.save_buf == NULL)
{
printk(KERN_INFO "No Saved Screen!\n");
return;
}
// 初始化保存屏幕半字节复制标志
sb_sign = LowS;
// 计算每行中半字节的个数
factorCount = buffer.save_w * 3;
// 初始化屏幕缓冲区指针偏移量
p_sb = 0;
// 加载保存屏幕缓冲区的内容到显存中
for (currLine = y1; currLine <= y2; currLine++)
{
// 计算当前行第一个点的显存地址
fb_lptr = __lcd_base + (x1 + currLine * SCR_X) * 3 * BPP / 8;
// 初始化显存半字节复制标志
fb_sign = (x1 & 0x1);
// 初始化显示缓冲区指针偏移量
p_fb = 0;
// 加载当行的内容到显存中
for (i = 0; i < factorCount; i++)
{
// 临时缓冲字节清零
bufferByte &= 0x00;
// 根据保存屏幕半字节复制标志, 将保存屏幕缓冲区中
// 当前字节的内容的低四位(或高四位)
// 复制到临时缓冲字节的低四位中
if (sb_sign == LowS)
bufferByte = (buffer.save_buf[p_sb] & 0x0f);
else
bufferByte = (buffer.save_buf[p_sb] & 0xf0) >> 4;
// 根据显存半字节复制标志,
// 对显存当前字节中相应的四位清零;
// 如果是要复制高四位的内容,
// 还要将临时缓冲字节的低四位内容移到高四位中
if (fb_sign == HighS)
{
fb_lptr[p_fb] &= 0x0f;
bufferByte = bufferByte << 4;
}
else
fb_lptr[p_fb] &= 0xf0;
// 用临时缓冲字节更新显存内容
fb_lptr[p_fb] |= bufferByte;
// 根据半字节复制标志, 设置相应的指针偏移量
if (fb_sign == HighS)
p_fb++;
if (sb_sign == HighS)
p_sb++;
// 更新半字节复制标志
sb_sign ^= 0x1;
fb_sign ^= 0x1;
}
}
}
// 绘制一个像素点
static void lcd_kernel_pixel(int x, int y, COLOR color)
{
unsigned char* fb_ptr; /* 更新像素点对应的显存地址 */
COLOR pure_color = 0x0000; /* 更新颜色信息 */
// 如果坐标越界则不做任何操作, 直接返回
if ( x < 0 || x >= SCR_X|| y < 0 || y >= SCR_Y)
{
return;
}
// 计算当前点对应的显存地址
fb_ptr = __lcd_base + (x + y * SCR_X) * 3 * BPP / 8;
// 更新像素点的颜色信息
// 处理x坐标为奇数的情况
if (x & 0x1)
{
pure_color = (color & 0x000f) << 4; /* 设置新的红色信息 */
*fb_ptr &= 0x0f; /* 保留当前字节中原有的其它像素点的直 */
*fb_ptr |= pure_color; /* 更新红色信息 */
pure_color = (color & 0x0ff0) >> 4; /* 设置新的绿色和蓝色信息 */
*(fb_ptr + 1) = 0xff & pure_color; /* 更新绿色和蓝色信息 */
}
// 处理x坐标为偶数的情况
else
{
pure_color = color & 0x00ff; /* 设置新的红色和绿色信息 */
*fb_ptr = 0xff & pure_color; /* 更新红色和绿色信息*/
pure_color = ( color & 0x0f00 ) >> 8; /* 设置新的蓝色信息 */
*(fb_ptr + 1) &= 0xf0; /* 保留当前字节中原有的其它像素点的直 */
*(fb_ptr + 1) |= pure_color; /* 更新蓝色信息*/
}
return;
}
// 绘制一条垂直直线
static void lcd_kernel_vline(int x, int y1, int y2, COLOR color)
{
int tmp;
int i = 0;
// 如果起始点的y坐标大于终止点的y坐标, 则交换起始点和终止点
if (y1 > y2)
{
tmp = y1;
y1 = y2;
y2 = tmp;
}
// 计算两点的垂直距离
tmp = y2 - y1;
// 用点组成垂直的直线
for (i = 0; i <= tmp; i++ )
{
lcd_kernel_pixel(x, y1 + i,color);
}
return;
}
// 绘制一条水平直线
static void lcd_kernel_hline(int x1, int x2, int y, COLOR color)
{
int tmp;
int i = 0;
// 如果起始点的x坐标大于终止点的x坐标, 则交换起始点和终止点
if (x1 > x2)
{
tmp = x1;
x1 = x2;
x2 = tmp;
}
// 计算两点的水平距离
tmp = x2 - x1;
// 用点组成水平的直线
for (i = 0; i <= tmp; i++ )
{
lcd_kernel_pixel(x1 + i, y, color);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -