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