1.开始
我想在荔枝派上驱动oled屏,但是在内核中写驱动对我来说肯定是非常麻烦的,所以我退而求其次,想在应用层中操作通用i2c接口来实现oled的驱动程序。
我买的OLED是中景园经典款,四针IIC接口oled。首先我参考了NanoPi的Matrix库,参考他的IIC应用层函数。我发现他多include了一个i2c-dev.h 并且这个函数的内容是比我/usr/inlucde/linux/i2c-dev.h中的内容多的,他多了以下几个函数
static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, int size, union i2c_smbus_data *data) static inline __s32 i2c_smbus_write_quick(int file, __u8 value) static inline __s32 i2c_smbus_read_byte(int file) static inline __s32 i2c_smbus_write_byte(int file, __u8 value) static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value) static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value) static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value) /* Returns the number of read bytes */ static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values) static inline __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length, __u8 *values) /* Returns the number of read bytes */ static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 *values) static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length, __u8 *values) /* Returns the number of read bytes */ static inline __s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length, __u8 *values)
我在网上查询了这些函数,这个是一种smbus协议,是对应用层i2c的进一步封装。我可以使用这个快速的对i2c数据进行读写。
之后我就参考了别人的OLED代码,开始了移植。
2.问题出现
我发现我设置好了从机地址之后,进行读写一直出现No Such Device Or Addres 的错误。经过一天的寻找,我终于发现了错误所在。
在网上的那个oled程序,都是将0x78作为从机地址。并且数据手册中也是这样写的,那么我理所当然将0x78作为从机地址。但是接下来我们看数据手册以及代码片段
ssd1306:
数据手册片段:
ssd1306的读写函数片段:
/*********************OLED写数据************************************/ void OLED_WrDat(unsigned char IIC_Data) { IIC_Start(); IIC_Send_Byte(0x78); //D/C#=0; R/W#=0 IIC_Wait_Ack(); IIC_Send_Byte(0x40); //write data IIC_Wait_Ack(); IIC_Send_Byte(IIC_Data); IIC_Wait_Ack(); IIC_Stop(); }
mpu6050:
数据手册
代码片段:
//IIC读一个字节 //reg:寄存器地址 //返回值:读到的数据 u8 MPU_Read_Byte(u8 reg) { u8 res; IIC_Start(); IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令 IIC_Wait_Ack(); //等待应答 IIC_Send_Byte(reg); //写寄存器地址 IIC_Wait_Ack(); //等待应答 IIC_Start(); IIC_Send_Byte((MPU_ADDR<<1)|1);//发送器件地址+读命令 IIC_Wait_Ack(); //等待应答 res=IIC_Read_Byte(0);//读取数据,发送nACK IIC_Stop(); //产生一个停止条件 return res; }
大家发现错误没有? 那就是对两个器件写地址的时候,oled的地址没有左移!那么问题就出现在这里,linux中设置好了从机地址后,他的发送时会自动根据读写来对地址进行左移后或上0、1的操作。
所以我这个0x78就是他的已经左移一位了的地址,那么我给linux中设置的从机地址应该是0x78右移一位得出的0x3C!现在我就可以正常读写了。
3.代码
下面附上我修改的i2c代码驱动地址。