发布: 16_使用Framebuffer改造OLED驱动

This commit is contained in:
weidongshan
2022-05-07 19:04:48 +08:00
parent 40e8946c00
commit 2edcc02318
12 changed files with 521 additions and 54 deletions

View File

@@ -53,12 +53,19 @@
创建内核线程周期性地把Framebuffer中的数据通过SPI发送给OLED。 创建内核线程周期性地把Framebuffer中的数据通过SPI发送给OLED。
内核线程 创建内核线程:
* 参考文件`include\linux\kthread.h`
* 参考函数kernel_thread、kthread_create、 kthread_run
* 参考文章https://blog.csdn.net/qq_37858386/article/details/115573565 * 参考文章https://blog.csdn.net/qq_37858386/article/details/115573565
* kthread_create创建内核线程线程处于"停止状态",要运行它需要执行`wake_up_process`
* kthread_run创建内核线程并马上让它处于"运行状态"
* kernel_thread
### 2.3 调试 ### 2.3 调试

View File

@@ -41,7 +41,7 @@
#define OLED_IOC_INIT 123 #define OLED_IOC_INIT 123
#define OLED_IOC_SET_POS 124 #define OLED_IOC_SET_POS 124
//<EFBFBD><EFBFBD>? 表示命令为1表示数据 //为0 表示命令为1表示数据
#define OLED_CMD 0 #define OLED_CMD 0
#define OLED_DATA 1 #define OLED_DATA 1
@@ -70,11 +70,13 @@ static void spi_write_datas(const unsigned char *buf, int len)
/********************************************************************** /**********************************************************************
* 函数名称<EFBFBD><EFBFBD>?oled_write_cmd * 函数名称 oled_write_cmd
* 功能描述<EFBFBD><EFBFBD>?oled向特定地址写入数据或者命<EFBFBD><EFBFBD>? * 输入参数:@uc_data :要写入的数据 * 功能描述 oled向特定地址写入数据或者命
@uc_cmd:<3A><>?则表示写入数据<EFBFBD><EFBFBD>?表示写入命令 * 输入参数:@uc_data :要写入数据
@uc_cmd:为1则表示写入数据为0表示写入命令
* 输出参数:无 * 输出参数:无
* <EFBFBD><EFBFBD>?<3F><>?值: <20><>? * 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容 * 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* ----------------------------------------------- * -----------------------------------------------
* 2020/03/04 V1.0 芯晓 创建 * 2020/03/04 V1.0 芯晓 创建
***********************************************************************/ ***********************************************************************/
@@ -86,17 +88,19 @@ static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd)
} }
else else
{ {
oled_set_dc_pin(1);//拉高,表示写入数<EFBFBD><EFBFBD>? } oled_set_dc_pin(1);//拉高,表示写入数
}
spi_write_datas(&uc_data, 1);//写入 spi_write_datas(&uc_data, 1);//写入
} }
/********************************************************************** /**********************************************************************
* 函数名称<EFBFBD><EFBFBD>?oled_init * 函数名称 oled_init
* 功能描述<EFBFBD><EFBFBD>?oled_init的初始化包括SPI控制器得初始<EFBFBD><EFBFBD>? * 输入参数:无 * 功能描述 oled_init的初始化包括SPI控制器得初始
* 输参数<EFBFBD><EFBFBD>?初始化的结果 * 输参数:无
* <EFBFBD><EFBFBD>?<3F><>?值: 成功则返<E58899><E8BF94>?否则返<E58899><E8BF94>?1 * 输出参数: 初始化的结果
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容 * 返 回 值: 成功则返回0否则返回-1
* 修改日期 版本号 修改人 修改内容
* ----------------------------------------------- * -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建 * 2020/03/15 V1.0 芯晓 创建
***********************************************************************/ ***********************************************************************/
@@ -148,11 +152,13 @@ static int oled_init(void)
//坐标设置 //坐标设置
/********************************************************************** /**********************************************************************
* 函数名称<EFBFBD><EFBFBD>?OLED_DIsp_Set_Pos * 函数名称 OLED_DIsp_Set_Pos
* 功能描述:设置要显示的位<EFBFBD><EFBFBD>? * 输入参数:@ x 要显示的column address * 功能描述:设置要显示的位
* 输入参数:@ x 要显示的column address
@y :要显示的page address @y :要显示的page address
* 输出参数<EFBFBD><EFBFBD>?<3F><>? * <20><>?<3F><>?值 * 输出参数:
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容 * 返 回 值:
* 修改日期 版本号 修改人 修改内容
* ----------------------------------------------- * -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建 * 2020/03/15 V1.0 芯晓 创建
***********************************************************************/ ***********************************************************************/
@@ -201,7 +207,8 @@ spidev_write(struct file *filp, const char __user *buf,
ker_buf = kmalloc(count, GFP_KERNEL); ker_buf = kmalloc(count, GFP_KERNEL);
err = copy_from_user(ker_buf, buf, count); 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); kfree(ker_buf);
return count; return count;
} }
@@ -253,7 +260,7 @@ static int spidev_remove(struct spi_device *spi)
{ {
gpiod_put(dc_gpio); gpiod_put(dc_gpio);
/* 反注册字符设<EFBFBD><EFBFBD>?*/ /* 反注册字符设*/
device_destroy(spidev_class, MKDEV(major, 0)); device_destroy(spidev_class, MKDEV(major, 0));
class_destroy(spidev_class); class_destroy(spidev_class);
unregister_chrdev(major, "100ask_oled"); unregister_chrdev(major, "100ask_oled");

View File

@@ -37,14 +37,74 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/gpio/consumer.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_INIT 123
#define OLED_IOC_SET_POS 124 #define OLED_IOC_SET_POS 124
//<EFBFBD><EFBFBD>? 表示命令为1表示数据 //为0 表示命令为1表示数据
#define OLED_CMD 0 #define OLED_CMD 0
#define OLED_DATA 1 #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 * 函数名称 oled_write_cmd
* 功能描述<EFBFBD><EFBFBD>?oled向特定地址写入数据或者命<EFBFBD><EFBFBD>? * 输入参数:@uc_data :要写入的数据 * 功能描述 oled向特定地址写入数据或者命
@uc_cmd:<3A><>?则表示写入数据<EFBFBD><EFBFBD>?表示写入命令 * 输入参数:@uc_data :要写入数据
@uc_cmd:为1则表示写入数据为0表示写入命令
* 输出参数:无 * 输出参数:无
* <EFBFBD><EFBFBD>?<3F><>?值: <20><>? * 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容 * 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* ----------------------------------------------- * -----------------------------------------------
* 2020/03/04 V1.0 芯晓 创建 * 2020/03/04 V1.0 芯晓 创建
***********************************************************************/ ***********************************************************************/
@@ -86,17 +148,19 @@ static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd)
} }
else else
{ {
oled_set_dc_pin(1);//拉高,表示写入数<EFBFBD><EFBFBD>? } oled_set_dc_pin(1);//拉高,表示写入数
}
spi_write_datas(&uc_data, 1);//写入 spi_write_datas(&uc_data, 1);//写入
} }
/********************************************************************** /**********************************************************************
* 函数名称<EFBFBD><EFBFBD>?oled_init * 函数名称 oled_init
* 功能描述<EFBFBD><EFBFBD>?oled_init的初始化包括SPI控制器得初始<EFBFBD><EFBFBD>? * 输入参数:无 * 功能描述 oled_init的初始化包括SPI控制器得初始
* 输参数<EFBFBD><EFBFBD>?初始化的结果 * 输参数:无
* <EFBFBD><EFBFBD>?<3F><>?值: 成功则返<E58899><E8BF94>?否则返<E58899><E8BF94>?1 * 输出参数: 初始化的结果
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容 * 返 回 值: 成功则返回0否则返回-1
* 修改日期 版本号 修改人 修改内容
* ----------------------------------------------- * -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建 * 2020/03/15 V1.0 芯晓 创建
***********************************************************************/ ***********************************************************************/
@@ -148,11 +212,13 @@ static int oled_init(void)
//坐标设置 //坐标设置
/********************************************************************** /**********************************************************************
* 函数名称<EFBFBD><EFBFBD>?OLED_DIsp_Set_Pos * 函数名称 OLED_DIsp_Set_Pos
* 功能描述:设置要显示的位<EFBFBD><EFBFBD>? * 输入参数:@ x 要显示的column address * 功能描述:设置要显示的位
* 输入参数:@ x 要显示的column address
@y :要显示的page address @y :要显示的page address
* 输出参数<EFBFBD><EFBFBD>?<3F><>? * <20><>?<3F><>?值 * 输出参数:
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容 * 返 回 值:
* 修改日期 版本号 修改人 修改内容
* ----------------------------------------------- * -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建 * 2020/03/15 V1.0 芯晓 创建
***********************************************************************/ ***********************************************************************/
@@ -201,7 +267,8 @@ spidev_write(struct file *filp, const char __user *buf,
ker_buf = kmalloc(count, GFP_KERNEL); ker_buf = kmalloc(count, GFP_KERNEL);
err = copy_from_user(ker_buf, buf, count); 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); kfree(ker_buf);
return count; 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) static int spidev_probe(struct spi_device *spi)
{ {
dma_addr_t phy_addr;
/* 1. 记录spi_device */ /* 1. 记录spi_device */
oled = spi; oled = spi;
@@ -246,14 +380,67 @@ static int spidev_probe(struct spi_device *spi)
/* 3. 获得GPIO引脚 */ /* 3. 获得GPIO引脚 */
dc_gpio = gpiod_get(&spi->dev, "dc", 0); 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; return 0;
} }
static int spidev_remove(struct spi_device *spi) 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); gpiod_put(dc_gpio);
/* 反注册字符设<EFBFBD><EFBFBD>?*/ /* 反注册字符设 */
device_destroy(spidev_class, MKDEV(major, 0)); device_destroy(spidev_class, MKDEV(major, 0));
class_destroy(spidev_class); class_destroy(spidev_class);
unregister_chrdev(major, "100ask_oled"); unregister_chrdev(major, "100ask_oled");

View File

@@ -566,6 +566,10 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
15_编写OLED驱动_上机实验_STM32MP157 15_编写OLED驱动_上机实验_STM32MP157
``` ```
* 2021.05.07 发布"SPI子系统"
```shell
16_使用Framebuffer改造OLED驱动
```

View File

@@ -0,0 +1,75 @@
# 使用Framebuffer改造OLED驱动 #
* 源码:
![image-20220429174003143](pic/77_src_oled_framebuffer.png)
## 1. 思路
![image-20220429173507474](pic/76_framebuffer_and_oled_gram.png)
假设OLED的每个像素使用1位数据表示:
* Linux Framebuffer中byte0对应OLED上第1行的8个像素
* OLED显存中byte0对应OLED上第1列的8个像素
为了兼容基于Framebuffer的程序驱动程序中分配一块FramebufferAPP直接操作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 调试
配置内核,把下列配置项去掉:
![image-20220125212414098](pic/78_disable_framebuffer_console.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -7,7 +7,7 @@
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, # 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册 # 请参考各开发板的高级用户使用手册
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4
all: all:
make -C $(KERN_DIR) M=`pwd` modules make -C $(KERN_DIR) M=`pwd` modules

View File

@@ -37,14 +37,74 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/gpio/consumer.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_INIT 123
#define OLED_IOC_SET_POS 124 #define OLED_IOC_SET_POS 124
//<EFBFBD><EFBFBD>? 表示命令为1表示数据 //为0 表示命令为1表示数据
#define OLED_CMD 0 #define OLED_CMD 0
#define OLED_DATA 1 #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 * 函数名称 oled_write_cmd
* 功能描述<EFBFBD><EFBFBD>?oled向特定地址写入数据或者命<EFBFBD><EFBFBD>? * 输入参数:@uc_data :要写入的数据 * 功能描述 oled向特定地址写入数据或者命
@uc_cmd:<3A><>?则表示写入数据<EFBFBD><EFBFBD>?表示写入命令 * 输入参数:@uc_data :要写入数据
@uc_cmd:为1则表示写入数据为0表示写入命令
* 输出参数:无 * 输出参数:无
* <EFBFBD><EFBFBD>?<3F><>?值: <20><>? * 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容 * 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* ----------------------------------------------- * -----------------------------------------------
* 2020/03/04 V1.0 芯晓 创建 * 2020/03/04 V1.0 芯晓 创建
***********************************************************************/ ***********************************************************************/
@@ -86,17 +148,19 @@ static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd)
} }
else else
{ {
oled_set_dc_pin(1);//拉高,表示写入数<EFBFBD><EFBFBD>? } oled_set_dc_pin(1);//拉高,表示写入数
}
spi_write_datas(&uc_data, 1);//写入 spi_write_datas(&uc_data, 1);//写入
} }
/********************************************************************** /**********************************************************************
* 函数名称<EFBFBD><EFBFBD>?oled_init * 函数名称 oled_init
* 功能描述<EFBFBD><EFBFBD>?oled_init的初始化包括SPI控制器得初始<EFBFBD><EFBFBD>? * 输入参数:无 * 功能描述 oled_init的初始化包括SPI控制器得初始
* 输参数<EFBFBD><EFBFBD>?初始化的结果 * 输参数:无
* <EFBFBD><EFBFBD>?<3F><>?值: 成功则返<E58899><E8BF94>?否则返<E58899><E8BF94>?1 * 输出参数: 初始化的结果
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容 * 返 回 值: 成功则返回0否则返回-1
* 修改日期 版本号 修改人 修改内容
* ----------------------------------------------- * -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建 * 2020/03/15 V1.0 芯晓 创建
***********************************************************************/ ***********************************************************************/
@@ -148,11 +212,13 @@ static int oled_init(void)
//坐标设置 //坐标设置
/********************************************************************** /**********************************************************************
* 函数名称<EFBFBD><EFBFBD>?OLED_DIsp_Set_Pos * 函数名称 OLED_DIsp_Set_Pos
* 功能描述:设置要显示的位<EFBFBD><EFBFBD>? * 输入参数:@ x 要显示的column address * 功能描述:设置要显示的位
* 输入参数:@ x 要显示的column address
@y :要显示的page address @y :要显示的page address
* 输出参数<EFBFBD><EFBFBD>?<3F><>? * <20><>?<3F><>?值 * 输出参数:
* 修改日期 版本<E78988><E69CAC>? 修改<E4BFAE><E694B9>? 修改内容 * 返 回 值:
* 修改日期 版本号 修改人 修改内容
* ----------------------------------------------- * -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建 * 2020/03/15 V1.0 芯晓 创建
***********************************************************************/ ***********************************************************************/
@@ -201,7 +267,8 @@ spidev_write(struct file *filp, const char __user *buf,
ker_buf = kmalloc(count, GFP_KERNEL); ker_buf = kmalloc(count, GFP_KERNEL);
err = copy_from_user(ker_buf, buf, count); 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); kfree(ker_buf);
return count; 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) static int spidev_probe(struct spi_device *spi)
{ {
dma_addr_t phy_addr;
/* 1. 记录spi_device */ /* 1. 记录spi_device */
oled = spi; oled = spi;
@@ -246,14 +380,67 @@ static int spidev_probe(struct spi_device *spi)
/* 3. 获得GPIO引脚 */ /* 3. 获得GPIO引脚 */
dc_gpio = gpiod_get(&spi->dev, "dc", 0); 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; return 0;
} }
static int spidev_remove(struct spi_device *spi) 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); gpiod_put(dc_gpio);
/* 反注册字符设<EFBFBD><EFBFBD>?*/ /* 反注册字符设 */
device_destroy(spidev_class, MKDEV(major, 0)); device_destroy(spidev_class, MKDEV(major, 0));
class_destroy(spidev_class); class_destroy(spidev_class);
unregister_chrdev(major, "100ask_oled"); unregister_chrdev(major, "100ask_oled");