linux OLED 0.91寸 SSD1316驱动例程

一个可以在linux上运行起来的oled驱动例程
需求描述:
OLED 0.91寸
驱动IC:SSD1316驱动例程

SPI模式还是I2C模式?
用户层代码,还是驱动层?上面的代码是用户层的,如果要驱动层的,可以用这个

#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define I2C_BUS_NUMBER 1
#define SSD1316_I2C_ADDR 0x3C

static struct i2c_client *ssd1316_client;

static int ssd1316_i2c_write(const char *buf, size_t count)
{
    struct i2c_msg msg;
    int ret;

    msg.addr = ssd1316_client->addr;
    msg.flags = 0;
    msg.len = count;
    msg.buf = (u8 *)buf;

    ret = i2c_transfer(ssd1316_client->adapter, &msg, 1);
    if (ret < 0) {
        printk(KERN_ERR "Failed to write to SSD1316: %d\n", ret);
        return ret;
    }

    return 0;
}

static ssize_t ssd1316_write(struct file *filp, const char __user *buf,
                             size_t count, loff_t *f_pos)
{
    char *kbuf;
    int ret;

    kbuf = kmalloc(count, GFP_KERNEL);
    if (!kbuf)
        return -ENOMEM;

    if (copy_from_user(kbuf, buf, count)) {
        kfree(kbuf);
        return -EFAULT;
    }

    ret = ssd1316_i2c_write(kbuf, count);

    kfree(kbuf);

    return ret ? ret : count;
}

static const struct file_operations ssd1316_fops = {
    .owner = THIS_MODULE,
    .write = ssd1316_write,
};

static int ssd1316_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
{
    int ret;

    ssd1316_client = client;

    // 初始化SSD1316,例如设置显示模式、亮度等

    ret = alloc_chrdev_region(&devno, 0, 1, "ssd1316");
    if (ret < 0) {
        printk(KERN_ERR "Failed to allocate device number\n");
        return ret;
    }

    cdev_init(&ssd1316_cdev, &ssd1316_fops);
    ssd1316_cdev.owner = THIS_MODULE;

    ret = cdev_add(&ssd1316_cdev, devno, 1);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add character device\n");
        unregister_chrdev_region(devno, 1);
        return ret;
    }

    return 0;
}

static int ssd1316_remove(struct i2c_client *client)
{
    cdev_del(&ssd1316_cdev);
    unregister_chrdev_region(devno, 1);

    return 0;
}

static const struct i2c_device_id ssd1316_id[] = {
    { "ssd1316", 0 },
    { },
};
MODULE_DEVICE_TABLE(i2c, ssd1316_id);

static struct i2c_driver ssd1316_driver = {
    .driver = {
        .name = "ssd1316",
    },
    .id_table = ssd1316_id,
    .probe = ssd1316_probe,
    .remove = ssd1316_remove,
};

static int __init ssd1316_init(void)
{
    return i2c_add_driver(&ssd1316_driver);
}

static void __exit ssd1316_exit(void)
{
    i2c_del_driver(&ssd1316_driver);
}

module_init(ssd1316_init);
module_exit(ssd1316_exit);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("SSD1316 OLED driver");
MODULE_LICENSE("GPL");


  • 这篇博客: OLED驱动芯片SSD1306解读中的 6800接口: 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • The parallel interface consists of 8 bi-directional data pins (D[7:0]), R/W#, D/C#, E and CS#
    接口线:
    6800双向数据接口有8个数据线,
    一个R/W#(读写控制线【低电平表示写,高电平表示读】)、
    一个D/C#(数据/命令选择线【低电平表示命令,高电平表示数据】)、
    E(总使能线)、
    CS#(片选【低电平有效】)。
    在这里插入图片描述
    (1)R/W# 线,给低电平信号表示向芯片写数据,给高电平表示从芯片读数据。
    (2)D/C#线,给低电平表示命令的读或写,给高电平表示数据的读或写。
    图标的含义:
    (1)在E引脚的下降沿期间:CS#低电平表示芯片选中(选中芯片是读写的前提):
    R/W# 低电平-----D/C#低电平表示:写命令
    R/W# 低电平-----D/C#高电平表示:写数据
    R/W# 高电平-----D/C#低电平表示:读命令
    R/W# 高电平-----D/C#高电平表示:读数据

1)写command的代码

/******************************************************************************
 * 函数名:    protocol_iic_write_cmd
 * 参数:      command(IN)        -->要写的command
 * 返回值:     NULL
 * 描述:    软件模拟IIC写命令
******************************************************************************/
static void protocol_iic_write_cmd(uint8_t command)
{
    protocol_iic_start();
    protocol_iic_write_byte(SSD1306_SLAVE_ADDR | SSD1306_IIC_WRITE);
    protocol_iic_wait_ack();
    protocol_iic_write_byte(SSD1306_IIC_CMD_MASK);
    protocol_iic_wait_ack();
    protocol_iic_write_byte(command);
    protocol_iic_wait_ack();
    protocol_iic_stop();
}


linux上实现oled驱动例程的一般步骤如下:
首先,确保你的系统中安装了I2C驱动。然后,你需要找到你的OLED屏的驱动程序(通常是一个库文件)并将其包含在你的代码中。接着,使用I2C库函数来初始化和控制OLED屏。最后,使用相应的函数将图像或文本显示在屏幕上。

需要注意的是,具体实现可能因OLED屏型号和驱动程序而异,请仔细阅读相关文档。

驱动例程参考示例

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>

/* I2C设备信息 */
#define I2C_DEVICE "/dev/i2c-1"
#define OLED_ADDR 0x3C

/* OLED控制命令 */
#define OLED_CMD_DISPLAY_OFF 0xAE
#define OLED_CMD_DISPLAY_ON 0xAF
#define OLED_CMD_SET_XY_ADDRESS 0x21
#define OLED_CMD_SET_START_LINE 0x40
#define OLED_CMD_CONTRAST_LEVEL 0x81
#define OLED_CMD_ENTIRE_DISPLAY_OFF 0xA4
#define OLED_CMD_ENTIRE_DISPLAY_ON 0xA5
#define OLED_CMD_INVERSE_DISPLAY_OFF 0xA6
#define OLED_CMD_INVERSE_DISPLAY_ON 0xA7
#define OLED_CMD_SET_NORMAL_DISPLAY 0xA4
#define OLED_CMD_SET_MUX_RATIO 0xA8
#define OLED_CMD_SET_SEGMENT_REMAP 0xA1
#define OLED_CMD_SET_COM_OUTPUT_SCAN_DIRECTION_NORMAL 0xC0
#define OLED_CMD_SET_COM_OUTPUT_SCAN_DIRECTION_REVERSE 0xC8
#define OLED_CMD_SET_OFFSET_LINE 0xD3
#define OLED_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO 0xD5
#define OLED_CMD_SET_PRECHARGE_PERIOD 0xD9
#define OLED_CMD_SET_COM_PINS_HARDWARE_CONFIGURATION 0xDA
#define OLED_CMD_SET_VCOMH_DESELECT_LEVEL 0xDB
#define OLED_CMD_PAGE_ADDRESS 0x22
#define OLED_CMD_COLUMN_ADDRESS 0x00

/* OLED设备信息 */
#define OLED_COLS 128
#define OLED_ROWS 32

/* 初始化I2C总线 */
static int i2c_init(const char *device, int address)
{
    int file;

    /* 打开设备 */
    if ((file = open(device, O_RDWR)) < 0)
    {
        printf("Failed to open %s: %s\n", device, strerror(errno));
        exit(1);
    }

    /* 设置I2C地址 */
    if (ioctl(file, I2C_SLAVE, address) < 0)
    {
        printf("Could not set I2C address to %d: %s\n", address, strerror(errno));
        exit(1);
    }

    return file;
}

/* 发送I2C数据 */
static int i2c_send(int i2c_file, uint8_t *data, int len)
{
    int ret;

    if ((ret = write(i2c_file, data, len)) != len)
    {
        printf("Error sending data through I2C: %s\n", strerror(errno));
        exit(1);
    }

    return ret;
}

/* OLED初始化 */
static void oled_init(int i2c_file)
{
    uint8_t cmd[16];

    /* 打开OLED显示 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_DISPLAY_OFF;
    i2c_send(i2c_file, cmd, 2);

    /* 设置MUX比率 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_MUX_RATIO;
    cmd[2] = 0x1F;
    i2c_send(i2c_file, cmd, 3);

    /* 设置显示偏移 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_OFFSET_LINE;
    cmd[2] = 0x00;
    i2c_send(i2c_file, cmd, 3);

    /* 设置显示区域 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_XY_ADDRESS;
    cmd[2] = 0x00;
    cmd[3] = 0x7F;
    i2c_send(i2c_file, cmd, 4);

    /* 设置显示时钟分频 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO;
    cmd[2] = 0xF0;
    i2c_send(i2c_file, cmd, 3);

    /* 设置COM输出扫描方向 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_COM_OUTPUT_SCAN_DIRECTION_NORMAL;
    i2c_send(i2c_file, cmd, 2);

    /* 设置显示偏移 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_OFFSET_LINE;
    cmd[2] = 0x00;
    i2c_send(i2c_file, cmd, 3);

    /* 关闭全局显示 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_ENTIRE_DISPLAY_OFF;
    i2c_send(i2c_file, cmd, 2);

    /* 设置是否翻转显示 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_SEGMENT_REMAP;
    i2c_send(i2c_file, cmd, 2);

    /* 设置显示正常 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_NORMAL_DISPLAY;
    i2c_send(i2c_file, cmd, 2);

    /* 设置预充电周期 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_PRECHARGE_PERIOD;
    cmd[2] = 0x22;
    i2c_send(i2c_file, cmd, 3);

    /* 设置COM引脚硬件配置 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_COM_PINS_HARDWARE_CONFIGURATION;
    cmd[2] = 0x02;
    i2c_send(i2c_file, cmd, 3);

    /* 设置VCOMH电平 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_VCOMH_DESELECT_LEVEL;
    cmd[2] = 0x07;
    i2c_send(i2c_file, cmd, 3);

    /* 设置对比度 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_CONTRAST_LEVEL;
    cmd[2] = 0xFF;
    i2c_send(i2c_file, cmd, 3);

    /* 设置显示起始行 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_SET_START_LINE | 0x00;
    i2c_send(i2c_file, cmd, 2);

    /* 开启显示 */
    cmd[0] = 0x00;
    cmd[1] = OLED_CMD_DISPLAY_ON;
    i2c_send(i2c_file, cmd, 2);

    /* 清屏 */
    oled_clear_screen(i2c_file);
}

/* OLED清屏 */
static void oled_clear_screen(int i2c_file)
{
    uint8_t cmd[32];
    int i, j;

    for (i = 0; i < OLED_ROWS / 8; i++)
    {
        /* 设置页面地址 */
        cmd[0] = 0x00;
        cmd[1] = OLED_CMD_PAGE_ADDRESS | i;
        i2c_send(i2c_file, cmd, 2);

        /* 设置列起始地址 */
        cmd[0] = 0x00;
        cmd[1] = OLED_CMD_COLUMN_ADDRESS;
        cmd[2] = 0x00;
        cmd[3] = OLED_COLS - 1;
        i2c_send(i2c_file, cmd, 4);

        /* 清空屏幕 */
        cmd[0] = 0x40;
        for (j = 0; j < OLED_COLS; j++)
        {
            cmd[j+1] = 0x00;
        }
        i2c_send(i2c_file, cmd, OLED_COLS+1);
    }
}

/* 在屏幕上输出字符串 */
void oled_print(int i2c_file, const char *str)
{
    uint8_t cmd[32];
    int i, j, k, c;
    int len = strlen(str);

    /* 先清屏 */
    oled_clear_screen(i2c_file);

    /* 逐字节输出 */
    for (i = 0, j = 0, k = 0; i < len; i++)
    {
        c = str[i];

        /* 如果是结束符,则退出 */
        if (c == '\0')
        {
            break;
        }

        /* 换行 */
        if (c == '\r' || c == '\n')
        {
            j++;
            k = 0;

            /* 如果超过显示屏幕范围,则重新清空屏幕 */
            if (j >= OLED_ROWS / 8)
            {
                oled_clear_screen(i2c_file);
                j = 0;
            }
            
            continue;
        }

        /* 设置页面地址 */
        cmd[0] = 0x00;
        cmd[1] = OLED_CMD_PAGE_ADDRESS | j;
        i2c_send(i2c_file, cmd, 2);

        /* 设置列地址 */
        cmd[0] = 0x00;
        cmd[1] = OLED_CMD_COLUMN_ADDRESS | (k*8);
        cmd[2] = OLED_COLS - 1;
        i2c_send(i2c_file, cmd, 3);

        /* 输出字符 */
        cmd[0] = 0x40;
        for (c = 0; c < 8; c++)
        {
            cmd[c+1] = font8x8_basic[(int)str[i]][c];
        }
        i2c_send(i2c_file, cmd, 9);

        /* 计算下一个字符的位置 */
        k++;
        if (k >= OLED_COLS / 8)
        {
            j++;
            k = 0;

            /* 如果超过显示屏幕范围,则重新清空屏幕 */
            if (j >= OLED_ROWS / 8)
            {
                oled_clear_screen(i2c_file);
                j = 0;
            }
        }
    }
}

int main(int argc, char **argv)
{
    int i2c_file;

    /* 初始化I2C总线 */
    i2c_file = i2c_init(I2C_DEVICE, OLED_ADDR);

    /* OLED初始化 */
    oled_init(i2c_file);

    /* 在屏幕上输出字符串 */
    oled_print(i2c_file, "Hello World!");

    /* 关闭I2C连接 */
    close(i2c_file);

    return 0;
}

要在Linux上运行起来的0.91寸OLED驱动例程,您需要遵循以下步骤:

  1. 了解SSD1316驱动IC:首先,您需要详细了解SSD1316驱动IC的规格和功能。查阅相关文档和资料,以便理解其寄存器配置、通信协议和支持的功能。
  2. 准备开发环境:在Linux上进行开发,您需要准备适当的开发环境。确保您的Linux系统已经安装了适当的编译工具链、开发库和驱动程序。
  3. 编写驱动程序:使用C或C++等编程语言,编写适用于Linux的驱动程序。您可以根据SSD1316驱动IC的规格和功能,实现必要的功能和接口。这可能涉及到与硬件进行通信的驱动程序代码,以及与图形库或终端进行交互的用户界面代码。
  4. 配置设备树:如果您的OLED显示屏连接到嵌入式设备上,您可能需要在设备树中添加相应的配置。这包括定义硬件引脚、总线接口和驱动程序的相关属性。
  5. 编译和测试:将驱动程序源代码编译为可执行文件,并在Linux系统上进行测试。确保驱动程序能够正确初始化OLED显示屏、显示图形或文本,并响应相应的操作。
  6. 调试和优化:如果在测试过程中遇到问题,您需要进行调试并进行必要的优化。使用适当的工具和调试技术,查找并修复可能存在的错误或性能问题。
  7. 集成到您的应用程序中:一旦您的OLED驱动程序可以正常工作,您可以将其集成到您的应用程序或项目中。确保在应用程序中正确调用和使用驱动程序接口,以实现所需的功能和效果。

请注意,这些步骤提供了一个一般的指导,具体的实现方式可能因您使用的Linux发行版、硬件平台和开发工具而有所不同。在实际开发过程中,您可能需要参考相关的文档、示例代码和社区支持来解决特定的问题。