OLED 简介
OLED,即有机发光二极管(Organic Light Emitting Diode),属于一种电流型的有机发光器件,是通过载流子的注入和复合而致发光的现象,发光强度与注入的电流成正比。OLED 由于同时具备自发光、不需要背光、对比度高、厚度薄、视角广和反应速度快等,因此被广泛应用到挠性面板。OLED 的使用温度范围广,构造及制程较简单。OLED 由于自发光,因此显示效果较好。
OLED模块硬件设计
使用的是 4 线 SPI 接口:CS、MOSI、MISO、CLK。实际上,我们只需要把数据发给 OLED,不需要从 OLED 读取数据,所以 MISO引脚用不到。另外,还需要一个 GPIO 来表示发送给 OLED 的是命令,还是数据。
主机跟 OLED 之间的简化连接图,如下:
我使用的OLED模块还需要接入res复位引脚。
OLED操作相关函数
发命令、发数据:
通过 SPI 接口发送给 OLED 的信息,可以是命令,也可以是数据:
⚫ 当 DC 引脚是低电平时,是命令;
⚫ 当 DC 引脚是高电平时,是数据。
/**********************************************************************
* 函数名称: oled_write_cmd
* 功能描述: oled向特定地址写入数据或者命令
* 输入参数:@uc_data :要写入的数据
@uc_cmd:为1则表示写入数据,为0表示写入命令
* 输出参数:无
* 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/04 V1.0 芯晓 创建
***********************************************************************/
static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd)
{
if(uc_cmd==0)
{
//*GPIO4_DR_s &= ~(1<<20);//拉低,表示写入指令
gpiod_set_value(oled_dc, 0);
}
else
{
//*GPIO4_DR_s |= (1<<20);//拉高,表示写入数据
gpiod_set_value(oled_dc, 1);
}
// spi_writeread(ESCPI1_BASE,uc_data);//写入
spi_write(oled_dev, &uc_data, 1);
}
初始化函数:
初始化 OLED 并不复杂,芯片手册中列出了需要发出的一系列命令,如下:
上图为 OLED 屏幕要求的初始化流程,我们按照流程对 OLED 屏幕进行初始化。
static int oled_hardware_init(void)
{
u8 i;
u8 data[] = {0xAE, 0x00, 0x10, 0x40, 0x81, 0xCF, 0xA1, 0xC8, 0xA6,
0xA8, 0x3F, 0xD3, 0x00, 0xD5, 0x80, 0xD9, 0xF1, 0xDA,
0x12, 0xDB, 0x40, 0x20, 0x02, 0x8D, 0x14, 0xA4, 0xA6, 0xAF};
gpiod_set_value(oled_rst, 1);
mdelay(100);
gpiod_set_value(oled_rst, 0); // 复位
mdelay(200);
gpiod_set_value(oled_rst, 1);
for (i = 0; i < sizeof(data); i++)
{
oled_write_cmd_data(data[i], OLED_CMD);
}
OLED_DIsp_Clear();
return 0;
}
像素显示:
OLED 上有 128*64 个像素,每个像素只有 2 种状态:亮、灭。如下图所示:
怎么控制某个像素的状态呢?OLED 内部有显存。显存中每个字节用来表示一列的 8 个像素,注意:是一列,不是一行。
显存中数据和像素的对应关系见上图,初始化好 OLED 后,只要往 OLED 显存中写入数据即可显示。怎么写入数据?有 2 个问题要解决:
① 怎么发地址?
对于 64 行像素,可以分为 8 页,每页对应 8 行。
要寻址显存中某个字节时,先确定它是处于哪页(Page),再确定它处于哪列(Col)。
通过命令发出 Page 和 Col 即可,后面再详细介绍。
② 怎么发数据?
让 DC 为高电平,通过 SPI 发送数据即可。
设置地址函数:
OLED 主控的手册里介绍了三种地址模式,我们常用的是页地址模式(Page
addressing mode),它把显存的 64 行分为 8 页,每页对应 8 行;选中某页后,
再选择某列,然后就可以往里面写数据了,每写一个数据,地址就会加 1,一直
写到最右端的位置,它会自动跳到最左端。通过命令来实现发送页地址和列地址,其中列地址分为两次发送,先发送低字节,再发送高字节。
//坐标设置
/**********************************************************************
* 函数名称: OLED_DIsp_Set_Pos
* 功能描述:设置要显示的位置
* 输入参数:@ x :要显示的列column address 0-127
@ y :要显示的page address 0-7
* 输出参数: 无
* 返 回 值:
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_Set_Pos(int x, int y)
{
oled_write_cmd_data(0xb0 + y, OLED_CMD); // `0xb0`:选择页地址; `y`:页数
oled_write_cmd_data((x & 0x0f), OLED_CMD); //取出列地址低4位,
oled_write_cmd_data(((x & 0xf0) >> 4) | 0x10, OLED_CMD); // 取出列地址高4位,在右移4位; `0x10`:列地址高位的起始偏移量。
}
清屏函数:
把显存全部写为 0,即可清屏。如下:它一页一页地发送数据,第 137 行设置地址,第 138~139 行发送 128 个数据,代码如下:
/**********************************************************************
* 函数名称: OLED_DIsp_Clear
* 功能描述: 整个屏幕显示数据清0
* 输入参数:无
* 输出参数: 无
* 返 回 值:
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
static void OLED_DIsp_Clear(void)
{
unsigned char x, y;
for (y = 0; y < 8; y++)
{
OLED_DIsp_Set_Pos(0, y);
for (x = 0; x < 128; x++)
oled_write_cmd_data(0, OLED_DATA); /* 清零 */
}
}
显示字符的函数:
要显示一个字符,要先得到它的点阵,即字模。
然后在显存中根据字模修改数据,让对应的点亮、灭。
我们使用 8*16 的字模,以字符“A”为例,看看如何在 OLED 上显示出来,
如下图所示:
假设要在(x, y)位置显示字符 A,要做的事为:
① 使用命令,发出位置(x, y);
② 发出 8 个数据,对应字符的上半部;
③ 使用命令,发出位置(x, y+1);
④ 发出 8 个数据,对应字符的下半部。
代码如下,
/**********************************************************************
* 函数名称: OLED_DIsp_Char
* 功能描述:在某个位置显示字符 1-9
* 输入参数:@ x :要显示的column address
@ y :要显示的page address
@ c :要显示的字符的ascii码
* 输出参数: 无
* 返 回 值:
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_Char(int x, int y, unsigned char c)
{
int i = 0;
/* 得到字模 */
//偏移校正:计算的是字符`c`与空格字符的ASCII码(32)差值,这通常用于将字符`c`映射到`oled_asc2_8x16`数组中的正确索引位置。
const unsigned char *dots = oled_asc2_8x16[c - ' '];
char pos[2];
#if 0
/* 发给OLED */
OLED_DIsp_Set_Pos(x, y);
/* 发出8字节数据 */
for (i = 0; i < 8; i++)
oled_write_cmd_data(dots[i], OLED_DATA);
#endif
pos[0] = x;
pos[1] = y;
ioctl(fd, OLED_SET_XY, pos);
ioctl(fd, OLED_SET_DATAS | (8<<8), dots); //16位
#if 0
OLED_DIsp_Set_Pos(x, y+1);
/* 发出8字节数据 */
for (i = 0; i < 8; i++)
oled_write_cmd_data(dots[i+8], OLED_DATA);
#endif
pos[0] = x;
pos[1] = y+1;
ioctl(fd, OLED_SET_XY, pos);
ioctl(fd, OLED_SET_DATAS | (8<<8), &dots[8]);
}
显示字符串的函数:
需要注意的是换行:OLED 一行只能显示 16 个字符(8*16 点阵),如果字符
太长,要换行。
代码如下 :
/**********************************************************************
* 函数名称: OLED_DIsp_String
* 功能描述: 在指定位置显示字符串
* 输入参数:@ x :要显示的column address
@y :要显示的page address
@str :要显示的字符串
* 输出参数: 无
* 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_String(int x, int y, char *str)
{
unsigned char j=0;
while (str[j])
{
OLED_DIsp_Char(x, y, str[j]);//显示单个字符
x += 8;
if(x > 127)
{
x = 0;
y += 2;
}//移动显示位置
j++;
}
}
显示汉字的函数:
/**********************************************************************
* 函数名称: OLED_DIsp_CHinese
* 功能描述:在指定位置显示汉字
* 输入参数:@ x :要显示的column address
@ y :要显示的page address
@ no :要显示的汉字编号
* 输出参数: 无
* 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no)
{
unsigned char t, adder=0;
char pos[2];
/* 发送上半截字符显示位置 */
pos[0] = x;
pos[1] = y;
ioctl(fd, OLED_SET_XY, pos);
/* 每个汉字的点阵数据由32个字节组成(16个用于上半部,16个用于下半部) */
/* 发送上半截字符 */
for(t=0;t<16;t++)
{
//oled_write_cmd_data(hz_1616[no][t*2],OLED_DATA);
ioctl(fd, OLED_SET_DATAS | (1<<8), &hz_1616[no][t*2]);
adder+=1;
}
/* 发送下半截字符显示位置 */
pos[0] = x;
pos[1] = y+1;
ioctl(fd, OLED_SET_XY, pos);
/* 发送下半截字符 */
for(t=0;t<16;t++)
{
// oled_write_cmd_data(hz_1616[no][t*2+1],OLED_DATA);
ioctl(fd, OLED_SET_DATAS | (1<<8), &hz_1616[no][t*2+1]); //奇位置
adder+=1;
}
}
编写测试函数:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "font.h"
#define OLED_SET_XY 99
#define OLED_SET_XY_WRITE_DATA 100
#define OLED_SET_XY_WRITE_DATAS 101
#define OLED_SET_DATAS 102 /* 102为低8位, 高16位用来表示长度 */
int fd;
/**********************************************************************
* 函数名称: OLED_DIsp_Char
* 功能描述:在某个位置显示字符 1-9
* 输入参数:@ x :要显示的column address
@y :要显示的page address 0 2 4 6
@c :要显示的字符的ascii码
* 输出参数: 无
* 返 回 值:
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_Char(int x, int y, unsigned char c)
{
int i = 0;
/* 得到字模 */
const unsigned char *dots = oled_asc2_8x16[c - ' '];
char pos[2];
#if 0
/* 发给OLED */
OLED_DIsp_Set_Pos(x, y);
/* 发出8字节数据 */
for (i = 0; i < 8; i++)
oled_write_cmd_data(dots[i], OLED_DATA);
#endif
pos[0] = x;
pos[1] = y;
ioctl(fd, OLED_SET_XY, pos);
ioctl(fd, OLED_SET_DATAS | (8<<8), dots);
#if 0
OLED_DIsp_Set_Pos(x, y+1);
/* 发出8字节数据 */
for (i = 0; i < 8; i++)
oled_write_cmd_data(dots[i+8], OLED_DATA);
#endif
pos[0] = x;
pos[1] = y+1;
ioctl(fd, OLED_SET_XY, pos);
ioctl(fd, OLED_SET_DATAS | (8<<8), &dots[8]);
}
/**********************************************************************
* 函数名称: OLED_DIsp_String
* 功能描述: 在指定位置显示字符串
* 输入参数:@ x :要显示的column address
@y :要显示的page address 0 2 4 6
@str :要显示的字符串
* 输出参数: 无
* 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_String(int x, int y, char *str)
{
unsigned char j=0;
while (str[j])
{
OLED_DIsp_Char(x, y, str[j]);//显示单个字符
x += 8;
if(x > 127)
{
x = 0;
y += 2;
}//移动显示位置
j++;
}
}
/**********************************************************************
* 函数名称: OLED_DIsp_CHinese
* 功能描述:在指定位置显示汉字
* 输入参数:@ x :要显示的column address
@ y :要显示的page address
@ no :要显示的汉字编号
取模格式:阴码,逐列式,占阵32,索引32,逆向取模,十六进制
* 输出参数: 无
* 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no)
{
unsigned char t, adder=0;
char pos[2];
/* 发送上半截字符显示位置坐标 */
pos[0] = x;
pos[1] = y;
ioctl(fd, OLED_SET_XY, pos); // OLED_SET_XY发送坐标标志
/* 每个汉字的点阵数据由32个字节组成(16个偶数部分用于上半部,16个奇数部分用于下半部) */
/* 发送上半截字符 */
for(t=0;t<16;t++)
{
//字节大小放在左移八位的位置
ioctl(fd, OLED_SET_DATAS | (1 << 8), &hz_1616[no][t * 2]); //偶位置数据
adder+=1;
}
/* 发送下半截字符显示位置 */
pos[0] = x;
pos[1] = y+1; //向下加一页
ioctl(fd, OLED_SET_XY, pos); // OLED_SET_XY发送坐标标志
/* 发送下半截字符 */
for(t=0;t<16;t++)
{
ioctl(fd, OLED_SET_DATAS | (1<<8), &hz_1616[no][t*2+1]); //奇位置数据
adder+=1;
}
}
/* 测试函数 */
void OLED_DIsp_Test(void)
{
int i;
int start_pos = 3, end_pos = 4; // 显示汉字的起始位置,终止位置
/* 显示字符串 */
OLED_DIsp_String(0, 0, "Hello, Heiweilu!");
// OLED_DIsp_String(0, 2, "");
// OLED_DIsp_String(0, 4, "");
/* 显示汉字 */
for (i = start_pos; i < end_pos+1; i++)
{
OLED_DIsp_CHinese(i*16, 6, i);
}
}
/*
* ./oled_test /dev/myoled
*/
int main(int argc, char **argv)
{
int buf[2];
if (argc != 2)
{
printf("Usage: %s /dev/xxx\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
printf(" can not open %s\n", argv[1]);
return -1;
}
OLED_DIsp_Test();
return 0;
}
修改设备树
添加引脚节点
添加设备节点
程序
oled_drv.c
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/list.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
/* 宏 */
#define OLED_SET_XY 99
#define OLED_SET_XY_WRITE_DATA 100
#define OLED_SET_XY_WRITE_DATAS 101
#define OLED_SET_DATAS 102 /* 102为低8位, 高16位用来表示长度 */
//为0 表示命令,为1表示数据
#define OLED_CMD 0
#define OLED_DATA 1
struct spi_device *oled_dev;
static struct gpio_desc *oled_dc;
static struct gpio_desc *oled_rst;
static int major;
static struct class *oled_class;
static char data_buf[1024];
u8 OLED_GRAM[144][8];
/* 写数据 */
static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd)
{
if(uc_cmd==0)
{
//*GPIO4_DR_s &= ~(1<<20);//拉低,表示写入指令
gpiod_set_value(oled_dc, 0);
}
else
{
//*GPIO4_DR_s |= (1<<20);//拉高,表示写入数据
gpiod_set_value(oled_dc, 1);
}
// spi_writeread(ESCPI1_BASE,uc_data);//写入
spi_write(oled_dev, &uc_data, 1);
}
/* 向OLED写数据 */
static void oled_write_datas(unsigned char *buf, int len)
{
//*GPIO4_DR_s |= (1<<20);//拉高,表示写入数据
gpiod_set_value(oled_dc, 1);
// spi_writeread(ESCPI1_BASE,uc_data);//写入
spi_write(oled_dev, buf, len); //向SPI线上的OLED设备发送数据
}
/* 更新显存到OLED */
void OLED_Refresh(void)
{
u8 i, n;
for (i = 0; i < 8; i++)
{
oled_write_cmd_data(0xb0 + i, OLED_CMD); // 设置行起始地址
oled_write_cmd_data(0x00, OLED_CMD); // 设置低列起始地址
oled_write_cmd_data(0x10, OLED_CMD); // 设置高列起始地址
for (n = 0; n < 128; n++)
oled_write_cmd_data(OLED_GRAM[n][i], OLED_DATA);
}
}
/* 坐标设置 */
void OLED_DIsp_Set_Pos(int x, int y)
{
oled_write_cmd_data(0xb0 + y, OLED_CMD);
oled_write_cmd_data((x & 0x0f), OLED_CMD);
oled_write_cmd_data(((x & 0xf0) >> 4) | 0x10, OLED_CMD);
}
/* 清屏 */
static void OLED_DIsp_Clear(void)
{
u8 x, y;
for (y = 0; y < 8; y++)
{
for (x = 0; x < 128; x++)
{
OLED_GRAM[x][y] = 0; // 清除所有数据
}
OLED_DIsp_Set_Pos(0, y);
for (x = 0; x < 64; x++)
oled_write_cmd_data(0, OLED_DATA); /* 清零 */
}
OLED_Refresh(); // 更新显示
}
/* 反显函数 */
void OLED_ColorTurn(u8 i)
{
if (i == 0)
{
oled_write_cmd_data(0xA6, OLED_CMD); // 正常显示
}
if (i == 1)
{
oled_write_cmd_data(0xA7, OLED_CMD); // 反色显示
}
}
/* 屏幕旋转180度 */
void OLED_DisplayTurn(u8 i)
{
if (i == 0)
{
oled_write_cmd_data(0xC8, OLED_CMD); // 正常显示
oled_write_cmd_data(0xA1, OLED_CMD);
}
if (i == 1)
{
oled_write_cmd_data(0xC0, OLED_CMD); // 反转显示
oled_write_cmd_data(0xA0, OLED_CMD);
}
}
/* 初始化 */
static int oled_hardware_init(void)
{
u8 i;
u8 data[] = {0xAE, 0x00, 0x10, 0x40, 0x81, 0xCF, 0xA1, 0xC8, 0xA6,
0xA8, 0x3F, 0xD3, 0x00, 0xD5, 0x80, 0xD9, 0xF1, 0xDA,
0x12, 0xDB, 0x40, 0x20, 0x02, 0x8D, 0x14, 0xA4, 0xA6, 0xAF};
gpiod_set_value(oled_rst, 1);
mdelay(100);
gpiod_set_value(oled_rst, 0); // 复位
mdelay(200);
gpiod_set_value(oled_rst, 1);
for (i = 0; i < sizeof(data); i++)
{
oled_write_cmd_data(data[i], OLED_CMD);
}
OLED_DIsp_Clear();
return 0;
}
/* cmd = OLED_SET_XY_WRITE_DATA, buf[0] = x, buf[1] = y, buf[2] = data
* cmd = OLED_SET_XY_WRITE_DATAS, buf[0] = x, buf[1] = y, buf[2] = len, buf[3...] = datas
*/
static long oled_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
const void __user *from = (const void __user *)arg;
char param_buf[3];
int size;
int err;
switch (cmd & 0xff) // 取出低八位 cmd:102为低8位, 高16位用来表示长度
{
case OLED_SET_XY:
{
err = copy_from_user(param_buf, from, 2);
OLED_DIsp_Set_Pos(param_buf[0], param_buf[1]);
break;
}
case OLED_SET_XY_WRITE_DATA:
{
err = copy_from_user(param_buf, from, 3);
OLED_DIsp_Set_Pos(param_buf[0], param_buf[1]);
oled_write_cmd_data(param_buf[2], OLED_DATA);
break;
}
case OLED_SET_XY_WRITE_DATAS:
{
err = copy_from_user(param_buf, from, 3);
size = param_buf[2];
err = copy_from_user(data_buf, from+3, size);
OLED_DIsp_Set_Pos(param_buf[0], param_buf[1]);
oled_write_datas(data_buf, size);
break;
}
case OLED_SET_DATAS:
{
size = cmd >> 8; //右移8位取出大小
err = copy_from_user(data_buf, from, size);
oled_write_datas(data_buf, size);
break;
}
}
return 0;
}
/* 定义自己的file_operations结构体 */
static struct file_operations oled_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = oled_ioctl,
};
static int oled_probe(struct spi_device *spi)
{
printk("====%s====\n", __FUNCTION__);
oled_dev = spi;
/* register_chrdev */
major = register_chrdev(0, "oled", &oled_fops);
/* class_create */
oled_class = class_create(THIS_MODULE, "oled_class");
/* device_create */
device_create(oled_class, NULL, MKDEV(major, 0), NULL, "myoled");
/* spi oled init */
oled_dc = gpiod_get(&spi->dev, "dc", GPIOD_OUT_HIGH);
oled_rst = gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
oled_hardware_init(); // 初始化OLED
OLED_ColorTurn(0); // 0正常显示,1 反色显示
OLED_DisplayTurn(0); // 0正常显示 1 屏幕翻转显示
return 0;
}
static int oled_remove(struct spi_device *spi)
{
printk("====%s====\n", __FUNCTION__);
device_destroy(oled_class, MKDEV(major, 0));
class_destroy(oled_class);
unregister_chrdev(major, "oled");
gpiod_put(oled_dc);
gpiod_put(oled_rst);
return 0;
}
static const struct of_device_id oled_of_match[] = {
{.compatible = "100ask,oled"},
{}
};
static struct spi_driver oled_driver = {
.driver = {
.name = "oled",
.of_match_table = oled_of_match,
},
.probe = oled_probe,
.remove = oled_remove,
//.id_table = oled_spi_ids,
};
int oled_init(void)
{
return spi_register_driver(&oled_driver);
}
static void oled_exit(void)
{
spi_unregister_driver(&oled_driver);
}
module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL v2");
oled_test.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "font.h"
#define OLED_SET_XY 99
#define OLED_SET_XY_WRITE_DATA 100
#define OLED_SET_XY_WRITE_DATAS 101
#define OLED_SET_DATAS 102 /* 102为低8位, 高16位用来表示长度 */
int fd;
/**********************************************************************
* 函数名称: OLED_DIsp_Char
* 功能描述:在某个位置显示字符 1-9
* 输入参数:@ x :要显示的column address
@y :要显示的page address 0 2 4 6
@c :要显示的字符的ascii码
* 输出参数: 无
* 返 回 值:
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_Char(int x, int y, unsigned char c)
{
int i = 0;
/* 得到字模 */
const unsigned char *dots = oled_asc2_8x16[c - ' '];
char pos[2];
#if 0
/* 发给OLED */
OLED_DIsp_Set_Pos(x, y);
/* 发出8字节数据 */
for (i = 0; i < 8; i++)
oled_write_cmd_data(dots[i], OLED_DATA);
#endif
pos[0] = x;
pos[1] = y;
ioctl(fd, OLED_SET_XY, pos);
ioctl(fd, OLED_SET_DATAS | (8<<8), dots);
#if 0
OLED_DIsp_Set_Pos(x, y+1);
/* 发出8字节数据 */
for (i = 0; i < 8; i++)
oled_write_cmd_data(dots[i+8], OLED_DATA);
#endif
pos[0] = x;
pos[1] = y+1;
ioctl(fd, OLED_SET_XY, pos);
ioctl(fd, OLED_SET_DATAS | (8<<8), &dots[8]);
}
/**********************************************************************
* 函数名称: OLED_DIsp_String
* 功能描述: 在指定位置显示字符串
* 输入参数:@ x :要显示的column address
@y :要显示的page address 0 2 4 6
@str :要显示的字符串
* 输出参数: 无
* 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_String(int x, int y, char *str)
{
unsigned char j=0;
while (str[j])
{
OLED_DIsp_Char(x, y, str[j]);//显示单个字符
x += 8;
if(x > 127)
{
x = 0;
y += 2;
}//移动显示位置
j++;
}
}
/**********************************************************************
* 函数名称: OLED_DIsp_CHinese
* 功能描述:在指定位置显示汉字
* 输入参数:@ x :要显示的column address
@ y :要显示的page address
@ no :要显示的汉字编号
取模格式:阴码,逐列式,占阵32,索引32,逆向取模,十六进制
* 输出参数: 无
* 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2020/03/15 V1.0 芯晓 创建
***********************************************************************/
void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no)
{
unsigned char t, adder=0;
char pos[2];
/* 发送上半截字符显示位置坐标 */
pos[0] = x;
pos[1] = y;
ioctl(fd, OLED_SET_XY, pos); // OLED_SET_XY发送坐标标志
/* 每个汉字的点阵数据由32个字节组成(16个偶数部分用于上半部,16个奇数部分用于下半部) */
/* 发送上半截字符 */
for(t=0;t<16;t++)
{
//字节大小放在左移八位的位置
ioctl(fd, OLED_SET_DATAS | (1 << 8), &hz_1616[no][t * 2]); //偶位置数据
adder+=1;
}
/* 发送下半截字符显示位置 */
pos[0] = x;
pos[1] = y+1; //向下加一页
ioctl(fd, OLED_SET_XY, pos); // OLED_SET_XY发送坐标标志
/* 发送下半截字符 */
for(t=0;t<16;t++)
{
ioctl(fd, OLED_SET_DATAS | (1<<8), &hz_1616[no][t*2+1]); //奇位置数据
adder+=1;
}
}
/* 测试函数 */
void OLED_DIsp_Test(void)
{
int i;
int start_pos = 3, end_pos = 4; // 显示汉字的起始位置,终止位置
/* 显示字符串 */
OLED_DIsp_String(0, 0, "Hello, Heiweilu!");
// OLED_DIsp_String(0, 2, "");
// OLED_DIsp_String(0, 4, "");
/* 显示汉字 */
for (i = start_pos; i < end_pos+1; i++)
{
OLED_DIsp_CHinese(i*16, 6, i);
}
}
/*
* ./oled_test /dev/myoled
*/
int main(int argc, char **argv)
{
int buf[2];
if (argc != 2)
{
printf("Usage: %s /dev/xxx\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
printf(" can not open %s\n", argv[1]);
return -1;
}
OLED_DIsp_Test();
return 0;
}
font.h
#ifndef _FONT_H_
#define _FONT_H_
const unsigned char oled_asc2_8x16[95][16]=
{
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// 0
{0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00},//!1
{0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//"2
{0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00},//#3
{0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00},//$4
{0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00},//%5
{0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10},//&6
{0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//'7
{0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},//(8
{0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},//)9
{0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},//*10
{0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00},//+11
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00},//,12
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01},//-13
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},//.14
{0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00},///15
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},//016
{0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//117
{0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},//218
{0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00},//319
{0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00},//420
{0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00},//521
{0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00},//622
{0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00},//723
{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},//824
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00},//925
{0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},//:26
{0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00},//;27
{0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},//<28
{0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00},//=29
{0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},//>30
{0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00},//?31
{0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00},//@32
{0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},//A33
{0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},//B34
{0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},//C35
{0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},//D36
{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},//E37
{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},//F38
{0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},//G39
{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},//H40
{0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//I41
{0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},//J42
{0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},//K43
{0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},//L44
{0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00},//M45
{0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},//N46
{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},//O47
{0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},//P48
{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00},//Q49
{0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},//R50
{0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},//S51
{0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//T52
{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//U53
{0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},//V54
{0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00},//W55
{0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},//X56
{0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//Y57
{0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},//Z58
{0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},//[59
{0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},//\60
{0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},//]61
{0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//^62
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},//_63
{0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//`64
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20},//a65
{0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},//b66
{0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},//c67
{0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20},//d68
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00},//e69
{0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//f70
{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},//g71
{0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//h72
{0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//i73
{0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},//j74
{0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00},//k75
{0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//l76
{0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},//m77
{0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//n78
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//o79
{0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00},//p80
{0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80},//q81
{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},//r82
{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},//s83
{0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00},//t84
{0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},//u85
{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00},//v86
{0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00},//w87
{0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00},//x88
{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00},//y89
{0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},//z90
{0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40},//{91
{0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},//|92
{0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00},//}93
{0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//~94
};
const unsigned char hz_1616[][32] = {
{0x02, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x22, 0x42, 0x22, 0x42, 0x32, 0x42, 0x2A, 0x42, 0x26, 0x42, 0x22, 0x42, 0x22, 0x42, 0x22, 0x42, 0x22, 0x42, 0xE2, 0xFF, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00}, /*"百",0*/
{0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xE2, 0x1F, 0x22, 0x08, 0x22, 0x08, 0x22, 0x08, 0xE2, 0x1F, 0x02, 0x00, 0x02, 0x40, 0x02, 0x80, 0xFE, 0x7F, 0x00, 0x00, 0x00, 0x00}, /*"问",1*/
{0x00, 0x00, 0xFE, 0xFF, 0x02, 0x10, 0x22, 0x08, 0x42, 0x06, 0x82, 0x01, 0x72, 0x0E, 0x02, 0x10, 0x22, 0x08, 0x42, 0x06, 0x82, 0x01, 0x72, 0x4E, 0x02, 0x80, 0xFE, 0x7F, 0x00, 0x00, 0x00, 0x00}, /*"网",2*/
{0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0xF8, 0xFF, 0x07, 0x00, 0x40, 0x10, 0x20, 0x0C, 0x18, 0x03, 0x0F, 0x40, 0x08, 0x80, 0xC8, 0x7F, 0x08, 0x00, 0x08, 0x01, 0x28, 0x06, 0x18, 0x18, 0x00, 0x00}, /*"你",0*/
{0x10, 0x40, 0x10, 0x22, 0xF0, 0x15, 0x1F, 0x08, 0x10, 0x16, 0xF0, 0x61, 0x00, 0x00, 0x80, 0x00, 0x82, 0x40, 0x82, 0x80, 0xE2, 0x7F, 0x92, 0x00, 0x8A, 0x00, 0x86, 0x00, 0x80, 0x00, 0x00, 0x00}, /*"好",1*/
};
#endif
Makefile
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH, 比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册
#KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o oled_test oled_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order oled_test
# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o
obj-m += oled_drv.o
上机测试
取模教程
1.汉字取模:
1. 打开取模软件,然后模式选择字符模式,然后点击选项
2.汉字取模设置如下,点击确定。
3.在红框内输入要取模的汉字,然后点击生成字模
评论