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代码驱动地址