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);
msg.addr = client->addr;
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;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
};
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);
msg.addr = client->addr;
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);
}
void OLED_Display_On(void)
{
OLED_WR_Byte(0X8D,OLED_CMD);
OLED_WR_Byte(0X14,OLED_CMD);
OLED_WR_Byte(0XAF,OLED_CMD);
}
void OLED_Display_Off(void)
{
OLED_WR_Byte(0X8D,OLED_CMD);
OLED_WR_Byte(0X10,OLED_CMD);
OLED_WR_Byte(0XAE,OLED_CMD);
}
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD);
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);
else if(sizey==16) OLED_WR_Byte(asc2_1608[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);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0x10,OLED_CMD);
OLED_WR_Byte(0x40,OLED_CMD);
OLED_WR_Byte(0x81,OLED_CMD);
OLED_WR_Byte(0xCF,OLED_CMD);
OLED_WR_Byte(0xA1,OLED_CMD);
OLED_WR_Byte(0xC8,OLED_CMD);
OLED_WR_Byte(0xA6,OLED_CMD);
OLED_WR_Byte(0xA8,OLED_CMD);
OLED_WR_Byte(0x3f,OLED_CMD);
OLED_WR_Byte(0xD3,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xd5,OLED_CMD);
OLED_WR_Byte(0x80,OLED_CMD);
OLED_WR_Byte(0xD9,OLED_CMD);
OLED_WR_Byte(0xF1,OLED_CMD);
OLED_WR_Byte(0xDA,OLED_CMD);
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);
OLED_WR_Byte(0x40,OLED_CMD);
OLED_WR_Byte(0x20,OLED_CMD);
OLED_WR_Byte(0x02,OLED_CMD);
OLED_WR_Byte(0x8D,OLED_CMD);
OLED_WR_Byte(0x14,OLED_CMD);
OLED_WR_Byte(0xA4,OLED_CMD);
OLED_WR_Byte(0xA6,OLED_CMD);
OLED_Clear();
OLED_WR_Byte(0xAF,OLED_CMD);
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)
{
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;
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;
}
i2coleddev.cdev.owner = THIS_MODULE;
cdev_init(&i2coleddev.cdev, &i2c_oled_ops);
ret = cdev_add(&i2coleddev.cdev, i2coleddev.devid, I2C_OLED_CNT);
if(ret < 0) {
goto del_unregister;
}
i2coleddev.class = class_create(THIS_MODULE, I2C_OLED_NAME);
if (IS_ERR(i2coleddev.class)) {
goto del_cdev;
}
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();
cdev_del(&i2coleddev.cdev);
unregister_chrdev_region(i2coleddev.devid, I2C_OLED_CNT);
device_destroy(i2coleddev.class, i2coleddev.devid);
class_destroy(i2coleddev.class);
return 0;
return 0;
}
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" },
{ }
};
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;
}
|