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,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 调试
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -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");
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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驱动
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
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个环境变量不一定相同,
|
# 注意: 不同的开发板不同的编译器上述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
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user