mirror of
https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
synced 2025-12-02 21:01:12 +08:00
发布: 16_使用Framebuffer改造OLED驱动
This commit is contained in:
@@ -53,11 +53,18 @@
|
||||
|
||||
创建内核线程,周期性地把Framebuffer中的数据通过SPI发送给OLED。
|
||||
|
||||
内核线程
|
||||
创建内核线程:
|
||||
|
||||
* 参考文件`include\linux\kthread.h`
|
||||
|
||||
* 参考函数:kernel_thread、kthread_create、 kthread_run
|
||||
* 参考文章:https://blog.csdn.net/qq_37858386/article/details/115573565
|
||||
|
||||
* kthread_create:创建内核线程,线程处于"停止状态",要运行它需要执行`wake_up_process`
|
||||
|
||||
* kthread_run:创建内核线程,并马上让它处于"运行状态"
|
||||
|
||||
* kernel_thread
|
||||
|
||||
|
||||
|
||||
### 2.3 调试
|
||||
|
||||
Binary file not shown.
@@ -41,7 +41,7 @@
|
||||
#define OLED_IOC_INIT 123
|
||||
#define OLED_IOC_SET_POS 124
|
||||
|
||||
//<EFBFBD><EFBFBD>? 表示命令,为1表示数据
|
||||
//为0 表示命令,为1表示数据
|
||||
#define OLED_CMD 0
|
||||
#define OLED_DATA 1
|
||||
|
||||
@@ -70,11 +70,13 @@ static void spi_write_datas(const unsigned char *buf, int len)
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 函数名称<EFBFBD><EFBFBD>?oled_write_cmd
|
||||
* 功能描述<EFBFBD><EFBFBD>?oled向特定地址写入数据或者命<EFBFBD><EFBFBD>? * 输入参数:@uc_data :要写入的数据
|
||||
@uc_cmd:<3A><>?则表示写入数据,<EFBFBD><EFBFBD>?表示写入命令
|
||||
* 函数名称: oled_write_cmd
|
||||
* 功能描述: oled向特定地址写入数据或者命令
|
||||
* 输入参数:@uc_data :要写入的数据
|
||||
@uc_cmd:为1则表示写入数据,为0表示写入命令
|
||||
* 输出参数:无
|
||||
* <EFBFBD><EFBFBD>?<3F><>?值: <20><>? * 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容
|
||||
* 返 回 值: 无
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/03/04 V1.0 芯晓 创建
|
||||
***********************************************************************/
|
||||
@@ -86,17 +88,19 @@ static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd)
|
||||
}
|
||||
else
|
||||
{
|
||||
oled_set_dc_pin(1);//拉高,表示写入数<EFBFBD><EFBFBD>? }
|
||||
oled_set_dc_pin(1);//拉高,表示写入数据
|
||||
}
|
||||
spi_write_datas(&uc_data, 1);//写入
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 函数名称<EFBFBD><EFBFBD>?oled_init
|
||||
* 功能描述<EFBFBD><EFBFBD>?oled_init的初始化,包括SPI控制器得初始<EFBFBD><EFBFBD>? * 输入参数:无
|
||||
* 输出参数<EFBFBD><EFBFBD>?初始化的结果
|
||||
* <EFBFBD><EFBFBD>?<3F><>?值: 成功则返<E58899><E8BF94>?,否则返<E58899><E8BF94>?1
|
||||
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容
|
||||
* 函数名称: oled_init
|
||||
* 功能描述: oled_init的初始化,包括SPI控制器得初始化
|
||||
* 输入参数:无
|
||||
* 输出参数: 初始化的结果
|
||||
* 返 回 值: 成功则返回0,否则返回-1
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/03/15 V1.0 芯晓 创建
|
||||
***********************************************************************/
|
||||
@@ -148,11 +152,13 @@ static int oled_init(void)
|
||||
|
||||
//坐标设置
|
||||
/**********************************************************************
|
||||
* 函数名称<EFBFBD><EFBFBD>?OLED_DIsp_Set_Pos
|
||||
* 功能描述:设置要显示的位<EFBFBD><EFBFBD>? * 输入参数:@ x :要显示的column address
|
||||
* 函数名称: OLED_DIsp_Set_Pos
|
||||
* 功能描述:设置要显示的位置
|
||||
* 输入参数:@ x :要显示的column address
|
||||
@y :要显示的page address
|
||||
* 输出参数<EFBFBD><EFBFBD>?<3F><>? * <20><>?<3F><>?值:
|
||||
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容
|
||||
* 输出参数: 无
|
||||
* 返 回 值:
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/03/15 V1.0 芯晓 创建
|
||||
***********************************************************************/
|
||||
@@ -201,7 +207,8 @@ spidev_write(struct file *filp, const char __user *buf,
|
||||
ker_buf = kmalloc(count, GFP_KERNEL);
|
||||
err = copy_from_user(ker_buf, buf, count);
|
||||
|
||||
oled_set_dc_pin(1);//拉高,表示写入数<EFBFBD><EFBFBD>? spi_write_datas(ker_buf, count);
|
||||
oled_set_dc_pin(1);//拉高,表示写入数据
|
||||
spi_write_datas(ker_buf, count);
|
||||
kfree(ker_buf);
|
||||
return count;
|
||||
}
|
||||
@@ -253,7 +260,7 @@ static int spidev_remove(struct spi_device *spi)
|
||||
{
|
||||
gpiod_put(dc_gpio);
|
||||
|
||||
/* 反注册字符设<EFBFBD><EFBFBD>?*/
|
||||
/* 反注册字符设备 */
|
||||
device_destroy(spidev_class, MKDEV(major, 0));
|
||||
class_destroy(spidev_class);
|
||||
unregister_chrdev(major, "100ask_oled");
|
||||
|
||||
@@ -37,14 +37,74 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#define OLED_IOC_INIT 123
|
||||
#define OLED_IOC_SET_POS 124
|
||||
|
||||
//<EFBFBD><EFBFBD>? 表示命令,为1表示数据
|
||||
//为0 表示命令,为1表示数据
|
||||
#define OLED_CMD 0
|
||||
#define OLED_DATA 1
|
||||
|
||||
static struct fb_info *myfb_info;
|
||||
|
||||
static unsigned int pseudo_palette[16];
|
||||
|
||||
static struct task_struct *oled_thread;
|
||||
|
||||
static unsigned char oled_buf[1024];
|
||||
|
||||
/* from pxafb.c */
|
||||
static inline unsigned int chan_to_field(unsigned int chan,
|
||||
struct fb_bitfield *bf)
|
||||
{
|
||||
chan &= 0xffff;
|
||||
chan >>= 16 - bf->length;
|
||||
return chan << bf->offset;
|
||||
}
|
||||
|
||||
static int mylcd_setcolreg(unsigned regno,
|
||||
unsigned red, unsigned green, unsigned blue,
|
||||
unsigned transp, struct fb_info *info)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
|
||||
regno, red, green, blue); */
|
||||
|
||||
switch (info->fix.visual) {
|
||||
case FB_VISUAL_TRUECOLOR:
|
||||
/* true-colour, use pseudo-palette */
|
||||
|
||||
if (regno < 16) {
|
||||
u32 *pal = info->pseudo_palette;
|
||||
|
||||
val = chan_to_field(red, &info->var.red);
|
||||
val |= chan_to_field(green, &info->var.green);
|
||||
val |= chan_to_field(blue, &info->var.blue);
|
||||
|
||||
pal[regno] = val;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1; /* unknown type */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct fb_ops myfb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_setcolreg = mylcd_setcolreg,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_copyarea = cfb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@@ -70,11 +130,13 @@ static void spi_write_datas(const unsigned char *buf, int len)
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 函数名称<EFBFBD><EFBFBD>?oled_write_cmd
|
||||
* 功能描述<EFBFBD><EFBFBD>?oled向特定地址写入数据或者命<EFBFBD><EFBFBD>? * 输入参数:@uc_data :要写入的数据
|
||||
@uc_cmd:<3A><>?则表示写入数据,<EFBFBD><EFBFBD>?表示写入命令
|
||||
* 函数名称: oled_write_cmd
|
||||
* 功能描述: oled向特定地址写入数据或者命令
|
||||
* 输入参数:@uc_data :要写入的数据
|
||||
@uc_cmd:为1则表示写入数据,为0表示写入命令
|
||||
* 输出参数:无
|
||||
* <EFBFBD><EFBFBD>?<3F><>?值: <20><>? * 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容
|
||||
* 返 回 值: 无
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/03/04 V1.0 芯晓 创建
|
||||
***********************************************************************/
|
||||
@@ -86,17 +148,19 @@ static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd)
|
||||
}
|
||||
else
|
||||
{
|
||||
oled_set_dc_pin(1);//拉高,表示写入数<EFBFBD><EFBFBD>? }
|
||||
oled_set_dc_pin(1);//拉高,表示写入数据
|
||||
}
|
||||
spi_write_datas(&uc_data, 1);//写入
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 函数名称<EFBFBD><EFBFBD>?oled_init
|
||||
* 功能描述<EFBFBD><EFBFBD>?oled_init的初始化,包括SPI控制器得初始<EFBFBD><EFBFBD>? * 输入参数:无
|
||||
* 输出参数<EFBFBD><EFBFBD>?初始化的结果
|
||||
* <EFBFBD><EFBFBD>?<3F><>?值: 成功则返<E58899><E8BF94>?,否则返<E58899><E8BF94>?1
|
||||
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容
|
||||
* 函数名称: oled_init
|
||||
* 功能描述: oled_init的初始化,包括SPI控制器得初始化
|
||||
* 输入参数:无
|
||||
* 输出参数: 初始化的结果
|
||||
* 返 回 值: 成功则返回0,否则返回-1
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/03/15 V1.0 芯晓 创建
|
||||
***********************************************************************/
|
||||
@@ -148,11 +212,13 @@ static int oled_init(void)
|
||||
|
||||
//坐标设置
|
||||
/**********************************************************************
|
||||
* 函数名称<EFBFBD><EFBFBD>?OLED_DIsp_Set_Pos
|
||||
* 功能描述:设置要显示的位<EFBFBD><EFBFBD>? * 输入参数:@ x :要显示的column address
|
||||
* 函数名称: OLED_DIsp_Set_Pos
|
||||
* 功能描述:设置要显示的位置
|
||||
* 输入参数:@ x :要显示的column address
|
||||
@y :要显示的page address
|
||||
* 输出参数<EFBFBD><EFBFBD>?<3F><>? * <20><>?<3F><>?值:
|
||||
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容
|
||||
* 输出参数: 无
|
||||
* 返 回 值:
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/03/15 V1.0 芯晓 创建
|
||||
***********************************************************************/
|
||||
@@ -201,7 +267,8 @@ spidev_write(struct file *filp, const char __user *buf,
|
||||
ker_buf = kmalloc(count, GFP_KERNEL);
|
||||
err = copy_from_user(ker_buf, buf, count);
|
||||
|
||||
oled_set_dc_pin(1);//拉高,表示写入数<EFBFBD><EFBFBD>? spi_write_datas(ker_buf, count);
|
||||
oled_set_dc_pin(1);//拉高,表示写入数据
|
||||
spi_write_datas(ker_buf, count);
|
||||
kfree(ker_buf);
|
||||
return count;
|
||||
}
|
||||
@@ -231,10 +298,77 @@ static const struct of_device_id spidev_dt_ids[] = {
|
||||
};
|
||||
|
||||
|
||||
|
||||
static int oled_thread_func(void *data)
|
||||
{
|
||||
unsigned char *p[8];
|
||||
unsigned char data[8];
|
||||
int i;
|
||||
int j;
|
||||
int line;
|
||||
int bit;
|
||||
unsigned char byte;
|
||||
unsigned char *fb = myfb_info->screen_base;
|
||||
int k;
|
||||
|
||||
while (!kthread_should_stop())
|
||||
{
|
||||
/* 1. 从Framebuffer得到数据 */
|
||||
/* 2. 转换格式 */
|
||||
k = 0;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
for (line = 0; line < 8; line++)
|
||||
p[line] = &fb[i*128 + line * 16];
|
||||
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
for (line = 0; line < 8; line++)
|
||||
{
|
||||
data[line] = *p[line];
|
||||
p[line] += 1;
|
||||
}
|
||||
|
||||
for (bit = 0; bit < 8; bit++)
|
||||
{
|
||||
byte = (((data[0]>>bit) & 1) << 0) |
|
||||
(((data[1]>>bit) & 1) << 1) |
|
||||
(((data[2]>>bit) & 1) << 2) |
|
||||
(((data[3]>>bit) & 1) << 3) |
|
||||
(((data[4]>>bit) & 1) << 4) |
|
||||
(((data[5]>>bit) & 1) << 5) |
|
||||
(((data[6]>>bit) & 1) << 6) |
|
||||
(((data[7]>>bit) & 1) << 7);
|
||||
|
||||
oled_buf[k++] = byte;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 3. 通过SPI发送给OLED */
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
OLED_DIsp_Set_Pos(0, i);
|
||||
oled_set_dc_pin(1);
|
||||
spi_write_datas(&oled_buf[i*128], 128);
|
||||
}
|
||||
|
||||
|
||||
/* 4. 休眠一会 */
|
||||
schedule_timeout_interruptible(HZ);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int spidev_probe(struct spi_device *spi)
|
||||
{
|
||||
dma_addr_t phy_addr;
|
||||
|
||||
/* 1. 记录spi_device */
|
||||
oled = spi;
|
||||
|
||||
@@ -246,14 +380,67 @@ static int spidev_probe(struct spi_device *spi)
|
||||
/* 3. 获得GPIO引脚 */
|
||||
dc_gpio = gpiod_get(&spi->dev, "dc", 0);
|
||||
|
||||
|
||||
|
||||
/* A. 分配fb_info */
|
||||
myfb_info = framebuffer_alloc(0, NULL);
|
||||
|
||||
/* B. 设置fb_info */
|
||||
/* B.1 var : LCD分辨率、颜色格式 */
|
||||
myfb_info->var.xres_virtual = myfb_info->var.xres = 128;
|
||||
myfb_info->var.yres_virtual = myfb_info->var.yres = 64;
|
||||
|
||||
myfb_info->var.bits_per_pixel = 1; /* rgb565 */
|
||||
|
||||
/* B.2 fix */
|
||||
strcpy(myfb_info->fix.id, "100ask_oled");
|
||||
myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * myfb_info->var.bits_per_pixel / 8;
|
||||
|
||||
|
||||
/* fb的虚拟地址 */
|
||||
myfb_info->screen_base = dma_alloc_wc(&spi->dev, myfb_info->fix.smem_len, &phy_addr,
|
||||
GFP_KERNEL);
|
||||
myfb_info->fix.smem_start = phy_addr; /* fb的物理地址 */
|
||||
|
||||
myfb_info->fix.type = FB_TYPE_PACKED_PIXELS;
|
||||
myfb_info->fix.visual = FB_VISUAL_MONO10;
|
||||
|
||||
myfb_info->fix.line_length = myfb_info->var.xres * myfb_info->var.bits_per_pixel / 8;
|
||||
|
||||
/* c. fbops */
|
||||
myfb_info->fbops = &myfb_ops;
|
||||
myfb_info->pseudo_palette = pseudo_palette;
|
||||
|
||||
|
||||
/* C. 注册fb_info */
|
||||
register_framebuffer(myfb_info);
|
||||
|
||||
/* D. 创建内核线程 */
|
||||
dc_pin_init();
|
||||
oled_init();
|
||||
|
||||
oled_thread = kthread_run(oled_thread_func, NULL, "oled_kthead");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spidev_remove(struct spi_device *spi)
|
||||
{
|
||||
kthread_stop(oled_thread);
|
||||
|
||||
/* A. 反注册fb_info */
|
||||
unregister_framebuffer(myfb_info);
|
||||
|
||||
/* B. 释放内存 */
|
||||
dma_free_wc(&spi->dev, myfb_info->fix.smem_len, myfb_info->screen_base,
|
||||
myfb_info->fix.smem_start);
|
||||
|
||||
/* C. 释放fb_info */
|
||||
framebuffer_release(myfb_info);
|
||||
|
||||
gpiod_put(dc_gpio);
|
||||
|
||||
/* 反注册字符设<EFBFBD><EFBFBD>?*/
|
||||
/* 反注册字符设 */
|
||||
device_destroy(spidev_class, MKDEV(major, 0));
|
||||
class_destroy(spidev_class);
|
||||
unregister_chrdev(major, "100ask_oled");
|
||||
|
||||
@@ -566,6 +566,10 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
|
||||
15_编写OLED驱动_上机实验_STM32MP157
|
||||
```
|
||||
|
||||
* 2021.05.07 发布"SPI子系统":
|
||||
```shell
|
||||
16_使用Framebuffer改造OLED驱动
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
75
STM32MP157/doc_pic/11_SPI/16_使用Framebuffer改造OLED驱动.md
Normal file
75
STM32MP157/doc_pic/11_SPI/16_使用Framebuffer改造OLED驱动.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# 使用Framebuffer改造OLED驱动 #
|
||||
|
||||
* 源码:
|
||||
|
||||

|
||||
|
||||
## 1. 思路
|
||||
|
||||

|
||||
|
||||
假设OLED的每个像素使用1位数据表示:
|
||||
|
||||
* Linux Framebuffer中byte0对应OLED上第1行的8个像素
|
||||
* OLED显存中byte0对应OLED上第1列的8个像素
|
||||
|
||||
|
||||
|
||||
为了兼容基于Framebuffer的程序,驱动程序中分配一块Framebuffer,APP直接操作Framebuffer。
|
||||
|
||||
驱动程序周期性地把Framebuffer中的数据搬移到OLED显存上。
|
||||
|
||||
怎么搬移?
|
||||
|
||||
发给OLED线程的byte0、1、2、3、4、5、6、7怎么构造出来?
|
||||
|
||||
* 它们来自Framebuffer的byte0、16、32、48、64、80、96、112
|
||||
* OLED的byte0,由Framebuffer的这8个字节的bit0组合得到
|
||||
* OLED的byte1,由Framebuffer的这8个字节的bit1组合得到
|
||||
* OLED的byte2,由Framebuffer的这8个字节的bit2组合得到
|
||||
* OLED的byte3,由Framebuffer的这8个字节的bit3组合得到
|
||||
* ……
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. 编程
|
||||
|
||||
### 2.1 Framebuffer编程
|
||||
|
||||
分配、设置、注册fb_info结构体。
|
||||
|
||||
* 分配fb_info
|
||||
* 设置fb_info
|
||||
* fb_var
|
||||
* fb_fix
|
||||
* 注册fb_info
|
||||
* 硬件操作
|
||||
|
||||
|
||||
|
||||
### 2.2 数据搬移
|
||||
|
||||
创建内核线程,周期性地把Framebuffer中的数据通过SPI发送给OLED。
|
||||
|
||||
创建内核线程:
|
||||
|
||||
* 参考文件`include\linux\kthread.h`
|
||||
|
||||
* 参考文章:https://blog.csdn.net/qq_37858386/article/details/115573565
|
||||
|
||||
* kthread_create:创建内核线程,线程处于"停止状态",要运行它需要执行`wake_up_process`
|
||||
|
||||
* kthread_run:创建内核线程,并马上让它处于"运行状态"
|
||||
|
||||
* kernel_thread
|
||||
|
||||
|
||||
|
||||
### 2.3 调试
|
||||
|
||||
配置内核,把下列配置项去掉:
|
||||
|
||||

|
||||
|
||||
BIN
STM32MP157/doc_pic/11_SPI/16_使用Framebuffer改造OLED驱动.tif
Normal file
BIN
STM32MP157/doc_pic/11_SPI/16_使用Framebuffer改造OLED驱动.tif
Normal file
Binary file not shown.
BIN
STM32MP157/doc_pic/11_SPI/pic/76_framebuffer_and_oled_gram.png
Normal file
BIN
STM32MP157/doc_pic/11_SPI/pic/76_framebuffer_and_oled_gram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 828 KiB |
BIN
STM32MP157/doc_pic/11_SPI/pic/77_src_oled_framebuffer.png
Normal file
BIN
STM32MP157/doc_pic/11_SPI/pic/77_src_oled_framebuffer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
STM32MP157/doc_pic/11_SPI/pic/78_disable_framebuffer_console.png
Normal file
BIN
STM32MP157/doc_pic/11_SPI/pic/78_disable_framebuffer_console.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -7,7 +7,7 @@
|
||||
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
|
||||
# 请参考各开发板的高级用户使用手册
|
||||
|
||||
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88
|
||||
KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4
|
||||
|
||||
all:
|
||||
make -C $(KERN_DIR) M=`pwd` modules
|
||||
|
||||
@@ -37,14 +37,74 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#define OLED_IOC_INIT 123
|
||||
#define OLED_IOC_SET_POS 124
|
||||
|
||||
//<EFBFBD><EFBFBD>? 表示命令,为1表示数据
|
||||
//为0 表示命令,为1表示数据
|
||||
#define OLED_CMD 0
|
||||
#define OLED_DATA 1
|
||||
|
||||
static struct fb_info *myfb_info;
|
||||
|
||||
static unsigned int pseudo_palette[16];
|
||||
|
||||
static struct task_struct *oled_thread;
|
||||
|
||||
static unsigned char oled_buf[1024];
|
||||
|
||||
/* from pxafb.c */
|
||||
static inline unsigned int chan_to_field(unsigned int chan,
|
||||
struct fb_bitfield *bf)
|
||||
{
|
||||
chan &= 0xffff;
|
||||
chan >>= 16 - bf->length;
|
||||
return chan << bf->offset;
|
||||
}
|
||||
|
||||
static int mylcd_setcolreg(unsigned regno,
|
||||
unsigned red, unsigned green, unsigned blue,
|
||||
unsigned transp, struct fb_info *info)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
|
||||
regno, red, green, blue); */
|
||||
|
||||
switch (info->fix.visual) {
|
||||
case FB_VISUAL_TRUECOLOR:
|
||||
/* true-colour, use pseudo-palette */
|
||||
|
||||
if (regno < 16) {
|
||||
u32 *pal = info->pseudo_palette;
|
||||
|
||||
val = chan_to_field(red, &info->var.red);
|
||||
val |= chan_to_field(green, &info->var.green);
|
||||
val |= chan_to_field(blue, &info->var.blue);
|
||||
|
||||
pal[regno] = val;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1; /* unknown type */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct fb_ops myfb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_setcolreg = mylcd_setcolreg,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_copyarea = cfb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@@ -70,11 +130,13 @@ static void spi_write_datas(const unsigned char *buf, int len)
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 函数名称<EFBFBD><EFBFBD>?oled_write_cmd
|
||||
* 功能描述<EFBFBD><EFBFBD>?oled向特定地址写入数据或者命<EFBFBD><EFBFBD>? * 输入参数:@uc_data :要写入的数据
|
||||
@uc_cmd:<3A><>?则表示写入数据,<EFBFBD><EFBFBD>?表示写入命令
|
||||
* 函数名称: oled_write_cmd
|
||||
* 功能描述: oled向特定地址写入数据或者命令
|
||||
* 输入参数:@uc_data :要写入的数据
|
||||
@uc_cmd:为1则表示写入数据,为0表示写入命令
|
||||
* 输出参数:无
|
||||
* <EFBFBD><EFBFBD>?<3F><>?值: <20><>? * 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容
|
||||
* 返 回 值: 无
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/03/04 V1.0 芯晓 创建
|
||||
***********************************************************************/
|
||||
@@ -86,17 +148,19 @@ static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd)
|
||||
}
|
||||
else
|
||||
{
|
||||
oled_set_dc_pin(1);//拉高,表示写入数<EFBFBD><EFBFBD>? }
|
||||
oled_set_dc_pin(1);//拉高,表示写入数据
|
||||
}
|
||||
spi_write_datas(&uc_data, 1);//写入
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 函数名称<EFBFBD><EFBFBD>?oled_init
|
||||
* 功能描述<EFBFBD><EFBFBD>?oled_init的初始化,包括SPI控制器得初始<EFBFBD><EFBFBD>? * 输入参数:无
|
||||
* 输出参数<EFBFBD><EFBFBD>?初始化的结果
|
||||
* <EFBFBD><EFBFBD>?<3F><>?值: 成功则返<E58899><E8BF94>?,否则返<E58899><E8BF94>?1
|
||||
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容
|
||||
* 函数名称: oled_init
|
||||
* 功能描述: oled_init的初始化,包括SPI控制器得初始化
|
||||
* 输入参数:无
|
||||
* 输出参数: 初始化的结果
|
||||
* 返 回 值: 成功则返回0,否则返回-1
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/03/15 V1.0 芯晓 创建
|
||||
***********************************************************************/
|
||||
@@ -148,11 +212,13 @@ static int oled_init(void)
|
||||
|
||||
//坐标设置
|
||||
/**********************************************************************
|
||||
* 函数名称<EFBFBD><EFBFBD>?OLED_DIsp_Set_Pos
|
||||
* 功能描述:设置要显示的位<EFBFBD><EFBFBD>? * 输入参数:@ x :要显示的column address
|
||||
* 函数名称: OLED_DIsp_Set_Pos
|
||||
* 功能描述:设置要显示的位置
|
||||
* 输入参数:@ x :要显示的column address
|
||||
@y :要显示的page address
|
||||
* 输出参数<EFBFBD><EFBFBD>?<3F><>? * <20><>?<3F><>?值:
|
||||
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容
|
||||
* 输出参数: 无
|
||||
* 返 回 值:
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/03/15 V1.0 芯晓 创建
|
||||
***********************************************************************/
|
||||
@@ -201,7 +267,8 @@ spidev_write(struct file *filp, const char __user *buf,
|
||||
ker_buf = kmalloc(count, GFP_KERNEL);
|
||||
err = copy_from_user(ker_buf, buf, count);
|
||||
|
||||
oled_set_dc_pin(1);//拉高,表示写入数<EFBFBD><EFBFBD>? spi_write_datas(ker_buf, count);
|
||||
oled_set_dc_pin(1);//拉高,表示写入数据
|
||||
spi_write_datas(ker_buf, count);
|
||||
kfree(ker_buf);
|
||||
return count;
|
||||
}
|
||||
@@ -231,10 +298,77 @@ static const struct of_device_id spidev_dt_ids[] = {
|
||||
};
|
||||
|
||||
|
||||
|
||||
static int oled_thread_func(void *data)
|
||||
{
|
||||
unsigned char *p[8];
|
||||
unsigned char data[8];
|
||||
int i;
|
||||
int j;
|
||||
int line;
|
||||
int bit;
|
||||
unsigned char byte;
|
||||
unsigned char *fb = myfb_info->screen_base;
|
||||
int k;
|
||||
|
||||
while (!kthread_should_stop())
|
||||
{
|
||||
/* 1. 从Framebuffer得到数据 */
|
||||
/* 2. 转换格式 */
|
||||
k = 0;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
for (line = 0; line < 8; line++)
|
||||
p[line] = &fb[i*128 + line * 16];
|
||||
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
for (line = 0; line < 8; line++)
|
||||
{
|
||||
data[line] = *p[line];
|
||||
p[line] += 1;
|
||||
}
|
||||
|
||||
for (bit = 0; bit < 8; bit++)
|
||||
{
|
||||
byte = (((data[0]>>bit) & 1) << 0) |
|
||||
(((data[1]>>bit) & 1) << 1) |
|
||||
(((data[2]>>bit) & 1) << 2) |
|
||||
(((data[3]>>bit) & 1) << 3) |
|
||||
(((data[4]>>bit) & 1) << 4) |
|
||||
(((data[5]>>bit) & 1) << 5) |
|
||||
(((data[6]>>bit) & 1) << 6) |
|
||||
(((data[7]>>bit) & 1) << 7);
|
||||
|
||||
oled_buf[k++] = byte;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 3. 通过SPI发送给OLED */
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
OLED_DIsp_Set_Pos(0, i);
|
||||
oled_set_dc_pin(1);
|
||||
spi_write_datas(&oled_buf[i*128], 128);
|
||||
}
|
||||
|
||||
|
||||
/* 4. 休眠一会 */
|
||||
schedule_timeout_interruptible(HZ);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int spidev_probe(struct spi_device *spi)
|
||||
{
|
||||
dma_addr_t phy_addr;
|
||||
|
||||
/* 1. 记录spi_device */
|
||||
oled = spi;
|
||||
|
||||
@@ -246,14 +380,67 @@ static int spidev_probe(struct spi_device *spi)
|
||||
/* 3. 获得GPIO引脚 */
|
||||
dc_gpio = gpiod_get(&spi->dev, "dc", 0);
|
||||
|
||||
|
||||
|
||||
/* A. 分配fb_info */
|
||||
myfb_info = framebuffer_alloc(0, NULL);
|
||||
|
||||
/* B. 设置fb_info */
|
||||
/* B.1 var : LCD分辨率、颜色格式 */
|
||||
myfb_info->var.xres_virtual = myfb_info->var.xres = 128;
|
||||
myfb_info->var.yres_virtual = myfb_info->var.yres = 64;
|
||||
|
||||
myfb_info->var.bits_per_pixel = 1; /* rgb565 */
|
||||
|
||||
/* B.2 fix */
|
||||
strcpy(myfb_info->fix.id, "100ask_oled");
|
||||
myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * myfb_info->var.bits_per_pixel / 8;
|
||||
|
||||
|
||||
/* fb的虚拟地址 */
|
||||
myfb_info->screen_base = dma_alloc_wc(&spi->dev, myfb_info->fix.smem_len, &phy_addr,
|
||||
GFP_KERNEL);
|
||||
myfb_info->fix.smem_start = phy_addr; /* fb的物理地址 */
|
||||
|
||||
myfb_info->fix.type = FB_TYPE_PACKED_PIXELS;
|
||||
myfb_info->fix.visual = FB_VISUAL_MONO10;
|
||||
|
||||
myfb_info->fix.line_length = myfb_info->var.xres * myfb_info->var.bits_per_pixel / 8;
|
||||
|
||||
/* c. fbops */
|
||||
myfb_info->fbops = &myfb_ops;
|
||||
myfb_info->pseudo_palette = pseudo_palette;
|
||||
|
||||
|
||||
/* C. 注册fb_info */
|
||||
register_framebuffer(myfb_info);
|
||||
|
||||
/* D. 创建内核线程 */
|
||||
dc_pin_init();
|
||||
oled_init();
|
||||
|
||||
oled_thread = kthread_run(oled_thread_func, NULL, "oled_kthead");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spidev_remove(struct spi_device *spi)
|
||||
{
|
||||
kthread_stop(oled_thread);
|
||||
|
||||
/* A. 反注册fb_info */
|
||||
unregister_framebuffer(myfb_info);
|
||||
|
||||
/* B. 释放内存 */
|
||||
dma_free_wc(&spi->dev, myfb_info->fix.smem_len, myfb_info->screen_base,
|
||||
myfb_info->fix.smem_start);
|
||||
|
||||
/* C. 释放fb_info */
|
||||
framebuffer_release(myfb_info);
|
||||
|
||||
gpiod_put(dc_gpio);
|
||||
|
||||
/* 反注册字符设<EFBFBD><EFBFBD>?*/
|
||||
/* 反注册字符设 */
|
||||
device_destroy(spidev_class, MKDEV(major, 0));
|
||||
class_destroy(spidev_class);
|
||||
unregister_chrdev(major, "100ask_oled");
|
||||
|
||||
Reference in New Issue
Block a user