【Linux驱动模块】七、OLED-SPI
【Linux驱动模块】七、OLED-SPI
本文最后更新于313 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com

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.在红框内输入要取模的汉字,然后点击生成字模

💡商业转载请联系作者获得授权,非商业转载请注明出处。
协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)。
使用这些免费资源的时候应遵守版权意识,切勿非法利用,售卖,尊重原创内容知识产权。未经允许严禁转载。

评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇