1.首先测试oled的地址
一般来说12864oled驱动为ssd1306,查阅官方手册可知,
硬件7位地址为0x3c或者为0x3d 最后以为为读写位
(以前用stm32时候用使用的是模拟IIC,地址0x78,即:0x3c左移一位)
这里使用i2c工具来进行验证,笔者这里讲IIC_oled接上开发板的IIC5,然后使用一下命令进行验证
i2cdetect -y 0
如图所示
确认地址为0x3c
2.在设备树中追加内容
&i2c5{
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c5_pins_a>;
pinctrl-1 = <&i2c5_pins_sleep_a>;
status = "okay";
i2c_oled@3c{
compatible = "jack_G,i2c_oled";
reg = <0x3c>;
};
};
其中i2c5_pins_a和i2c5_pins_sleep_a在st官方定义的引脚中已经写好了
3.编写驱动文件
本次实验移植的是中景园电子的屏幕驱动,其中最核心的函数为
void OLED_WR_Byte(u8 dat,u8 mode)
这里使用正点原子使用i2c_transfer构造的函数方式来进行数据发送,方便于理解i2c通信过程(也可以使用i2c_master_send等函数)
函数修改如下
static s32 i2coled_write_regs(struct i2c_oled_dev *dev, u8 reg, u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->client;
b[0] = reg; /* 寄存器首地址 */
memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */
msg.addr = client->addr; /* ap3216c地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = b; /* 要写入的数据缓冲区 */
msg.len = len + 1; /* 要写入的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
}
void OLED_WR_Byte(u8 dat,u8 mode)
{
if(mode)i2coled_write_regs(&i2coleddev,0x40,&dat,1);
else i2coled_write_regs(&i2coleddev,0x00,&dat,1);
}
效果:
完整代码如下:
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/delay.h>
#include "oledfont.h"
#define I2C_OLED_CNT 1
#define I2C_OLED_NAME "i2c_oled"
#define OLED_CMD 0
#define OLED_DATA 1
struct i2c_oled_dev
{
struct i2c_client *client; /* i2c 设备 */
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
// struct device_node *nd; /* 设备节点 */
};
static struct i2c_oled_dev i2coleddev;
static s32 i2coled_write_regs(struct i2c_oled_dev *dev, u8 reg, u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->client;
b[0] = reg; /* 寄存器首地址 */
memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */
msg.addr = client->addr; /* ap3216c地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = b; /* 要写入的数据缓冲区 */
msg.len = len + 1; /* 要写入的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
}
void OLED_WR_Byte(u8 dat,u8 mode)
{
if(mode)i2coled_write_regs(&i2coleddev,0x40,&dat,1);
else i2coled_write_regs(&i2coleddev,0x00,&dat,1);
}
//坐标设置
void OLED_Set_Pos(u8 x, u8 y)
{
OLED_WR_Byte(0xb0+y,OLED_CMD);
OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
OLED_WR_Byte((x&0x0f),OLED_CMD);
}
//开启OLED显示
void OLED_Display_On(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON
OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF
OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址
OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
} //更新显示
}
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey)
{
u8 c=0,sizex=sizey/2;
u16 i=0,size1;
if(sizey==8)size1=6;
else size1=(sizey/8+((sizey%8)?1:0))*(sizey/2);
c=chr-' ';//得到偏移后的值
OLED_Set_Pos(x,y);
for(i=0;i<size1;i++)
{
if(i%sizex==0&&sizey!=8) OLED_Set_Pos(x,y++);
if(sizey==8) OLED_WR_Byte(asc2_0806[c][i],OLED_DATA);//6X8字号
else if(sizey==16) OLED_WR_Byte(asc2_1608[c][i],OLED_DATA);//8x16字号
// else if(sizey==xx) OLED_WR_Byte(asc2_xxxx[c][i],OLED_DATA);//用户添加字号
else return;
}
}
//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 sizey)
{
u8 j=0;
while (chr[j]!='\0')
{
OLED_ShowChar(x,y,chr[j++],sizey);
if(sizey==8)x+=6;
else x+=sizey/2;
}
}
static int i2c_oled_open(struct inode *inode, struct file *filp)
{
OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//-not offset
OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_WR_Byte(0x02,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
OLED_Clear();
OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
// OLED_ShowString(0,0,"Hello World",16);
return 0;
}
ssize_t i2c_oled_write(struct file *file, const char __user *buf, size_t count, loff_t *position)
{
u8 temp[128];
OLED_Clear();
copy_from_user(&temp, buf, count);
//待完善,这里只用于测试
OLED_ShowString(0, 0, temp,16);
return 0;
}
static int i2c_oled_release(struct inode *inode, struct file *filp)
{
// OLED_Clear();
return 0;
}
static const struct file_operations i2c_oled_ops = {
.owner = THIS_MODULE,
.open = i2c_oled_open,
.write = i2c_oled_write,
.release = i2c_oled_release,
};
static int i2c_oled_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret=0;
/* 注册字符设备驱动 */
/* 1、创建设备号 */
ret = alloc_chrdev_region(&i2coleddev.devid, 0, I2C_OLED_CNT, I2C_OLED_NAME);
if(ret < 0) {
pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", I2C_OLED_NAME, ret);
return -ENOMEM;
}
/* 2、初始化cdev */
i2coleddev.cdev.owner = THIS_MODULE;
cdev_init(&i2coleddev.cdev, &i2c_oled_ops);
/* 3、添加一个cdev */
ret = cdev_add(&i2coleddev.cdev, i2coleddev.devid, I2C_OLED_CNT);
if(ret < 0) {
goto del_unregister;
}
/* 4、创建类 */
i2coleddev.class = class_create(THIS_MODULE, I2C_OLED_NAME);
if (IS_ERR(i2coleddev.class)) {
goto del_cdev;
}
/* 5、创建设备 */
i2coleddev.device = device_create(i2coleddev.class, NULL, i2coleddev.devid, NULL, I2C_OLED_NAME);
if (IS_ERR(i2coleddev.device)) {
goto destroy_class;
}
i2coleddev.client = client;
return 0;
destroy_class:
device_destroy(i2coleddev.class, i2coleddev.devid);
del_cdev:
cdev_del(&i2coleddev.cdev);
del_unregister:
unregister_chrdev_region(i2coleddev.devid, I2C_OLED_CNT);
return -EIO;
}
static int i2c_oled_remove(struct i2c_client *client)
{
OLED_Clear();
/* 注销字符设备驱动 */
/* 1、删除cdev */
cdev_del(&i2coleddev.cdev);
/* 2、注销设备号 */
unregister_chrdev_region(i2coleddev.devid, I2C_OLED_CNT);
/* 3、注销设备 */
device_destroy(i2coleddev.class, i2coleddev.devid);
/* 4、注销类 */
class_destroy(i2coleddev.class);
return 0;
return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id i2c_oled_id[] = {
{"jack_G,i2c_oled", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id i2c_oled_of_match[] = {
{ .compatible = "jack_G,i2c_oled" },
{ /* Sentinel */ }
};
/* i2c驱动结构体 */
static struct i2c_driver i2c_oled_driver = {
.probe = i2c_oled_probe,
.remove = i2c_oled_remove,
.driver = {
.owner = THIS_MODULE,
.name = "i2c_oled",
.of_match_table = i2c_oled_of_match,
},
.id_table = i2c_oled_id,
};
static int __init i2coled_init(void)
{
return i2c_add_driver(&i2c_oled_driver);
}
static void __exit i2coled_exit(void)
{
i2c_del_driver(&i2c_oled_driver);
}
module_init(i2coled_init);
module_exit(i2coled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JACK");
MODULE_INFO(intree,"Y");
应用程序完整代码如下
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd, retvalue;
char data[128];
if(argc != 2){
printf("Error Usage!\r\n");
return -1;
}
fd = open("/dev/i2c_oled", O_RDWR);
if(fd < 0){
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
strncpy(data, argv[1], 128);
retvalue = write(fd, data,sizeof(data));
retvalue = close(fd); /* 关闭文件 */
if(retvalue < 0){
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}