• I2C子系统之驱动SSD1306 OLED


    理解I2C设备驱动框架,主要围绕四个结构体去分析就容易了。

    struct i2c_algorithm:提供I2C协议的实现的操作,如:master_xfer实现数据收发的最基本方法。

    struct i2c_adapter:每一个i2c_adapter都代表一个I2C物理接口,一个cpu可以有多个I2C接口(i2c_adapter),i2c_algorithm就是为i2c_adapter提供I2C协议的实现。每增加一个i2c接口,即是向i2c_bus_type (i2c_core.c)注册一个i2c_adapter

    struct i2c_driver:代表着一类I2C从机设备的驱动,比如:at24cxx的驱动,不同类型的I2C从机需要注册不同的i2c_driver,如:ssd1306的驱动不同于at24cxx的驱动。每增加一个类型的I2C从机设备,都要向i2c_bus_type (i2c_core.c)注册一个i2c_driver

    struct i2c_client:代表具体的某一个I2C从机设备,如:at24cxx系列的设备,有at24c01,at24c02等,每增加一个at24cxx设备,都要注册一个i2c_client。只有I2C从机设备被探测到,i2c_client才会被注册。

    这四者的关系可以分为:i2c_algorithm和i2c_adapter一起驱动I2C总线,i2c_driver和i2c_client一起实现设备驱动。

    注:linux目前只支持I2C主机模式。本文引用内核源码中i2c-algo-bit.c和i2c-gpio.c文件来讲解, i2c_driver由驱动开发者根据特定的设备提供,这里引用作者提供的ssd1306.c。i2c-algo-bit.c和i2c-gpio.c共同实现IO模拟I2C。

    i2c-algo-bit.c提供了一个i2c_algorithm,i2c-gpio.c提供了一个i2c_adapter。

    i2c-algo-bit.c通过以下代码绑定到i2c-gpio.c

    i2c-algo-bit.c

     1 static const struct i2c_algorithm i2c_bit_algo = {
     2     .master_xfer    = bit_xfer,
     3     .functionality  = bit_func,
     4 };
     5 
     6 static int i2c_bit_prepare_bus(struct i2c_adapter *adap)
     7 {
     8     ... ...
     9     adap->algo = &i2c_bit_algo;
    10     ... ...
    11     return 0;
    12 }
    13 
    14 int i2c_bit_add_bus(struct i2c_adapter *adap)
    15 {
    16     ... ...
    17     err = i2c_bit_prepare_bus(adap);
    18     ... ...
    19     return i2c_add_adapter(adap);
    20 }

    i2c-gpio.c

     1 static int __init i2c_gpio_probe(struct platform_device *pdev)
     2 {
     3     struct i2c_gpio_platform_data *pdata;
     4     struct i2c_algo_bit_data *bit_data;
     5     struct i2c_adapter *adap;
     6     ... ...
     7     pdata = pdev->dev.platform_data;
     8     ... ...
     9     i2c_bit_add_bus(adap);
    10     ... ...
    11 }

    这里就注册了一个i2c_adapter。

    要驱动ssd1306,因此对应地要提供一个i2c_driver,与i2c_adapter建立关系。

    ssd1306.c

     1 static struct i2c_driver ssd1306_driver = {
     2     .driver = {
     3             .name   = "ssd1306",
     4         },
     5         .id     = I2C_DRIVERID_I2CDEV,
     6         .attach_adapter = ssd1306_attach_adapter,
     7         .detach_client  = ssd1306_detach_client,
     8 };
     9 
    10 static int ssd1306_module_init(void)
    11 {
    12     i2c_add_driver(&ssd1306_driver);
    13     return 0;
    14 }

    i2c_driver和i2c_adapter是怎样建立关系的呢?

    i2c_bus_type (i2c_core.c)负责桥接i2c_driver和i2c_adapter建立关系,在i2c_driver和i2c_adapter注册的时候,两者都会调用driver->attach_adapter(adapter)

     1 int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
     2 {
     3     ... ...
     4     driver->attach_adapter(adapter);
     5     ... ...
     6     return 0;
     7 }
     8 
     9 static int i2c_register_adapter(struct i2c_adapter *adap)
    10 {
    11     ... ...
    12     driver->attach_adapter(adap);
    13     ... ...
    14 }

    driver->attach_adapter(adapter)实际上调用

    1 static int ssd1306_attach_adapter(struct i2c_adapter *adapter)
    2 {
    3     return i2c_probe(adapter, &addr_data, ssd1306_detect);
    4 }

    I2c_probe()函数的作用就是,探测是否存在ssd1306这个设备,是怎样探测的呢?就是通过发送从机地址到ssd1306,如果ssd1306返回应答信号,就认为探测到了。

     1 int i2c_probe(struct i2c_adapter *adapter,
     2           struct i2c_client_address_data *address_data,
     3           int (*found_proc) (struct i2c_adapter *, int, int))
     4 {
     5     ... ...
     6     i2c_probe_address(adapter,
     7                     address_data->probe[i + 1],
     8                     -1, found_proc);
     9     ... ...
    10 }

    代码太多,简化函数调用关系如下:

    1 i2c_probe_address()
    2     i2c_smbus_xfer()
    3         i2c_smbus_xfer_emulated();
    4             i2c_transfer();
    5                 adap->algo->master_xfer(adap,msgs,num);

    adap->algo->master_xfer(adap,msgs,num);实际调用的是bit_xfer()

    探测到ssd1306后,其实也就说明了探测到的I2C地址有效, 还需要注册一个描述SSD1306的i2c_client。

     1 static int ssd1306_detect(struct i2c_adapter *adapter, int address, int kind)
     2 {   
     3     printk("ssd1306_detect
    ");
     4 
     5     ssd1306_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
     6     ssd1306_client->addr    = address;
     7     ssd1306_client->adapter = adapter;
     8     ssd1306_client->driver  = &ssd1306_driver;
     9     strcpy(ssd1306_client->name, "ssd1306");
    10     
    11     i2c_attach_client(ssd1306_client);
    12     
    13     ... ...
    14 }   

    先转下话题。

    在i2c-gpio.c中,

    1 static int __init i2c_gpio_init(void)
    2 {
    3     ... ...
    4     ret = platform_driver_probe(&i2c_gpio_driver, i2c_gpio_probe);
    5     ... ...
    6 }

    这里实际上是注册了一个platform_driver,我们还要对应的为他注册一个platform_device,

    这个platform_device提供了硬件相关的设置,如指定那两个io口为SCL和SDA。

    I2c_gpio_dev.c中

     1 static struct i2c_gpio_platform_data i2c_dev = {
     2     .sda_pin = S3C2410_GPG6,
     3     .scl_pin = S3C2410_GPG5,
     4     .udelay = 0,
     5     .timeout = 0,
     6     .sda_is_open_drain = 1,
     7     .scl_is_open_drain = 1,
     8     .scl_is_output_only = 1
     9 };
    10 
    11 static struct platform_device i2c_platform_dev = {
    12     .name         = "i2c-gpio",
    13     .id           = -1,
    14     .dev = { 
    15         .release = i2c_dev_release,
    16         .platform_data = (void *)&i2c_dev,
    17     },
    18 };
    19 
    20 static int i2c_dev_init(void)
    21 {
    22     platform_device_register(&i2c_platform_dev);
    23     return 0;
    24 }

    如果platform_device和platform_driver匹配,就会调用i2c_gpio_probe()

     1 static int __init i2c_gpio_probe(struct platform_device *pdev)
     2 {
     3     struct i2c_gpio_platform_data *pdata;
     4     struct i2c_algo_bit_data *bit_data;
     5     struct i2c_adapter *adap;
     6     ... ...
     7     pdata = pdev->dev.platform_data;
     8     ... ...
     9     i2c_bit_add_bus(adap);
    10     ... ...
    11 }

    只有platform_device和platform_driver匹配才能注册i2c_adapter。

    到这里,就可以操作ssd1306了。ssd1306写一个字节的操作:

     1 static void ssd1306_write_byte(uint8_t chData, uint8_t chCmd) 
     2 {
     3     uint8_t cmd = 0x00;
     4     
     5     if (chCmd) {
     6         cmd = 0x40;
     7     } else {
     8         cmd = 0x00;
     9     }
    10 
    11     i2c_smbus_write_byte_data(ssd1306_client, cmd, chData);
    12 }

    实际上调用了i2c_smbus_write_byte_data()  

    I2c_core.c提供了几个I2C的读写函数:

    1 s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value);
    2 s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command);
    3 ... ...
    4 s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *values);
    5 s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client, u8 command,
    6                    u8 length, const u8 *values)

    运行代码

    注:由于源码的i2c-gpio-bit.c只支持具有开漏输入输出功能的IO模拟I2C, 而我的开发板已经没有具有开漏输入输出功能的IO了,只能使用普通的上啦输入输出IO,对SDA的读写操作,需要切换输入输出方向。因此我把i2c-gpio-bit.c改成普通IO操作SDA,命名为my-i2c-gpio-bit.c,同时i2c-gpio-bit.h和i2c-gpio.c也要做相应改动,分别改为my-i2c-gpio-bit.h和my-i2c-gpio.c。如果使用具有开漏输入输出功能的IO,可以直接使用i2c-gpio-bit.c,i2c-gpio-bit.h,i2c-gpio.c。

    代码

    i2c_gpio_dev.c

     1 #include <linux/module.h>
     2 #include <linux/version.h>
     3 
     4 #include <linux/init.h>
     5 
     6 #include <linux/kernel.h>
     7 #include <linux/types.h>
     8 #include <linux/interrupt.h>
     9 #include <linux/list.h>
    10 #include <linux/timer.h>
    11 #include <linux/init.h>
    12 #include <linux/serial_core.h>
    13 #include <linux/platform_device.h>
    14 #include <linux/gpio_keys.h>
    15 #include <linux/input.h>
    16 #include <linux/irq.h>
    17 #include <linux/i2c-gpio.h>
    18 
    19 #include <asm/gpio.h>
    20 #include <asm/io.h>
    21 #include <asm/arch/regs-gpio.h>
    22 
    23 
    24 /* [cgw]:  */
    25 
    26 static struct i2c_gpio_platform_data i2c_dev = {
    27     .sda_pin = S3C2410_GPG6,
    28     .scl_pin = S3C2410_GPG5,
    29     .udelay = 0,
    30     .timeout = 0,
    31     .sda_is_open_drain = 1,
    32     .scl_is_open_drain = 1,
    33     .scl_is_output_only = 1
    34 };
    35 
    36 static void i2c_dev_release(struct device * dev)
    37 {
    38     printk("i2c_dev_release! 
    ");
    39 }
    40 
    41 /* [cgw]: 分配一个平台设备 */
    42 static struct platform_device i2c_platform_dev = {
    43     .name         = "i2c-gpio",
    44     .id           = -1,
    45     .dev = { 
    46         .release = i2c_dev_release,
    47         .platform_data = (void *)&i2c_dev,
    48     },
    49 };
    50 
    51 
    52 static int i2c_dev_init(void)
    53 {
    54     /* [cgw]: 注册i2c_platform_dev平台设备 */
    55     platform_device_register(&i2c_platform_dev);
    56     return 0;
    57 }
    58 
    59 static void i2c_dev_exit(void)
    60 {
    61     /* [cgw]: 注销i2c_platform_dev平台设备 */
    62     platform_device_unregister(&i2c_platform_dev);
    63 }
    64 
    65 module_init(i2c_dev_init);
    66 module_exit(i2c_dev_exit);
    67 
    68 MODULE_LICENSE("GPL");


    ssd1306.c

      1 #include <linux/kernel.h>
      2 #include <linux/init.h>
      3 #include <linux/module.h>
      4 #include <linux/slab.h>
      5 #include <linux/jiffies.h>
      6 #include <linux/i2c.h>
      7 #include <linux/mutex.h>
      8 #include <linux/fs.h>
      9 #include <asm/uaccess.h>
     10 
     11 
     12 #define SSD1306_CMD    0
     13 #define SSD1306_DAT    1
     14 
     15 #define SSD1306_WIDTH    128
     16 #define SSD1306_HEIGHT   64
     17 
     18 static uint8_t s_chDispalyBuffer[128][8];
     19 
     20 const uint8_t c_chFont1608[95][16] = {      
     21 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
     22 {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xCC,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
     23 {0x00,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x00,0x00},/*""",2*/
     24 {0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x00,0x00},/*"#",3*/
     25 {0x00,0x00,0x0E,0x18,0x11,0x04,0x3F,0xFF,0x10,0x84,0x0C,0x78,0x00,0x00,0x00,0x00},/*"$",4*/
     26 {0x0F,0x00,0x10,0x84,0x0F,0x38,0x00,0xC0,0x07,0x78,0x18,0x84,0x00,0x78,0x00,0x00},/*"%",5*/
     27 {0x00,0x78,0x0F,0x84,0x10,0xC4,0x11,0x24,0x0E,0x98,0x00,0xE4,0x00,0x84,0x00,0x08},/*"&",6*/
     28 {0x08,0x00,0x68,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
     29 {0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x18,0x18,0x20,0x04,0x40,0x02,0x00,0x00},/*"(",8*/
     30 {0x00,0x00,0x40,0x02,0x20,0x04,0x18,0x18,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00},/*")",9*/
     31 {0x02,0x40,0x02,0x40,0x01,0x80,0x0F,0xF0,0x01,0x80,0x02,0x40,0x02,0x40,0x00,0x00},/*"*",10*/
     32 {0x00,0x80,0x00,0x80,0x00,0x80,0x0F,0xF8,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x00},/*"+",11*/
     33 {0x00,0x01,0x00,0x0D,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
     34 {0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80},/*"-",13*/
     35 {0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
     36 {0x00,0x00,0x00,0x06,0x00,0x18,0x00,0x60,0x01,0x80,0x06,0x00,0x18,0x00,0x20,0x00},/*"/",15*/
     37 {0x00,0x00,0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"0",16*/
     38 {0x00,0x00,0x08,0x04,0x08,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"1",17*/
     39 {0x00,0x00,0x0E,0x0C,0x10,0x14,0x10,0x24,0x10,0x44,0x11,0x84,0x0E,0x0C,0x00,0x00},/*"2",18*/
     40 {0x00,0x00,0x0C,0x18,0x10,0x04,0x11,0x04,0x11,0x04,0x12,0x88,0x0C,0x70,0x00,0x00},/*"3",19*/
     41 {0x00,0x00,0x00,0xE0,0x03,0x20,0x04,0x24,0x08,0x24,0x1F,0xFC,0x00,0x24,0x00,0x00},/*"4",20*/
     42 {0x00,0x00,0x1F,0x98,0x10,0x84,0x11,0x04,0x11,0x04,0x10,0x88,0x10,0x70,0x00,0x00},/*"5",21*/
     43 {0x00,0x00,0x07,0xF0,0x08,0x88,0x11,0x04,0x11,0x04,0x18,0x88,0x00,0x70,0x00,0x00},/*"6",22*/
     44 {0x00,0x00,0x1C,0x00,0x10,0x00,0x10,0xFC,0x13,0x00,0x1C,0x00,0x10,0x00,0x00,0x00},/*"7",23*/
     45 {0x00,0x00,0x0E,0x38,0x11,0x44,0x10,0x84,0x10,0x84,0x11,0x44,0x0E,0x38,0x00,0x00},/*"8",24*/
     46 {0x00,0x00,0x07,0x00,0x08,0x8C,0x10,0x44,0x10,0x44,0x08,0x88,0x07,0xF0,0x00,0x00},/*"9",25*/
     47 {0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0C,0x03,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
     48 {0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
     49 {0x00,0x00,0x00,0x80,0x01,0x40,0x02,0x20,0x04,0x10,0x08,0x08,0x10,0x04,0x00,0x00},/*"<",28*/
     50 {0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x00,0x00},/*"=",29*/
     51 {0x00,0x00,0x10,0x04,0x08,0x08,0x04,0x10,0x02,0x20,0x01,0x40,0x00,0x80,0x00,0x00},/*">",30*/
     52 {0x00,0x00,0x0E,0x00,0x12,0x00,0x10,0x0C,0x10,0x6C,0x10,0x80,0x0F,0x00,0x00,0x00},/*"?",31*/
     53 {0x03,0xE0,0x0C,0x18,0x13,0xE4,0x14,0x24,0x17,0xC4,0x08,0x28,0x07,0xD0,0x00,0x00},/*"@",32*/
     54 {0x00,0x04,0x00,0x3C,0x03,0xC4,0x1C,0x40,0x07,0x40,0x00,0xE4,0x00,0x1C,0x00,0x04},/*"A",33*/
     55 {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x11,0x04,0x0E,0x88,0x00,0x70,0x00,0x00},/*"B",34*/
     56 {0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x08,0x1C,0x10,0x00,0x00},/*"C",35*/
     57 {0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"D",36*/
     58 {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x17,0xC4,0x10,0x04,0x08,0x18,0x00,0x00},/*"E",37*/
     59 {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x17,0xC0,0x10,0x00,0x08,0x00,0x00,0x00},/*"F",38*/
     60 {0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x44,0x1C,0x78,0x00,0x40,0x00,0x00},/*"G",39*/
     61 {0x10,0x04,0x1F,0xFC,0x10,0x84,0x00,0x80,0x00,0x80,0x10,0x84,0x1F,0xFC,0x10,0x04},/*"H",40*/
     62 {0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x00,0x00,0x00,0x00},/*"I",41*/
     63 {0x00,0x03,0x00,0x01,0x10,0x01,0x10,0x01,0x1F,0xFE,0x10,0x00,0x10,0x00,0x00,0x00},/*"J",42*/
     64 {0x10,0x04,0x1F,0xFC,0x11,0x04,0x03,0x80,0x14,0x64,0x18,0x1C,0x10,0x04,0x00,0x00},/*"K",43*/
     65 {0x10,0x04,0x1F,0xFC,0x10,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x0C,0x00,0x00},/*"L",44*/
     66 {0x10,0x04,0x1F,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x1F,0xFC,0x10,0x04,0x00,0x00},/*"M",45*/
     67 {0x10,0x04,0x1F,0xFC,0x0C,0x04,0x03,0x00,0x00,0xE0,0x10,0x18,0x1F,0xFC,0x10,0x00},/*"N",46*/
     68 {0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"O",47*/
     69 {0x10,0x04,0x1F,0xFC,0x10,0x84,0x10,0x80,0x10,0x80,0x10,0x80,0x0F,0x00,0x00,0x00},/*"P",48*/
     70 {0x07,0xF0,0x08,0x18,0x10,0x24,0x10,0x24,0x10,0x1C,0x08,0x0A,0x07,0xF2,0x00,0x00},/*"Q",49*/
     71 {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x11,0xC0,0x11,0x30,0x0E,0x0C,0x00,0x04},/*"R",50*/
     72 {0x00,0x00,0x0E,0x1C,0x11,0x04,0x10,0x84,0x10,0x84,0x10,0x44,0x1C,0x38,0x00,0x00},/*"S",51*/
     73 {0x18,0x00,0x10,0x00,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x00,0x18,0x00,0x00,0x00},/*"T",52*/
     74 {0x10,0x00,0x1F,0xF8,0x10,0x04,0x00,0x04,0x00,0x04,0x10,0x04,0x1F,0xF8,0x10,0x00},/*"U",53*/
     75 {0x10,0x00,0x1E,0x00,0x11,0xE0,0x00,0x1C,0x00,0x70,0x13,0x80,0x1C,0x00,0x10,0x00},/*"V",54*/
     76 {0x1F,0xC0,0x10,0x3C,0x00,0xE0,0x1F,0x00,0x00,0xE0,0x10,0x3C,0x1F,0xC0,0x00,0x00},/*"W",55*/
     77 {0x10,0x04,0x18,0x0C,0x16,0x34,0x01,0xC0,0x01,0xC0,0x16,0x34,0x18,0x0C,0x10,0x04},/*"X",56*/
     78 {0x10,0x00,0x1C,0x00,0x13,0x04,0x00,0xFC,0x13,0x04,0x1C,0x00,0x10,0x00,0x00,0x00},/*"Y",57*/
     79 {0x08,0x04,0x10,0x1C,0x10,0x64,0x10,0x84,0x13,0x04,0x1C,0x04,0x10,0x18,0x00,0x00},/*"Z",58*/
     80 {0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFE,0x40,0x02,0x40,0x02,0x40,0x02,0x00,0x00},/*"[",59*/
     81 {0x00,0x00,0x30,0x00,0x0C,0x00,0x03,0x80,0x00,0x60,0x00,0x1C,0x00,0x03,0x00,0x00},/*"",60*/
     82 {0x00,0x00,0x40,0x02,0x40,0x02,0x40,0x02,0x7F,0xFE,0x00,0x00,0x00,0x00,0x00,0x00},/*"]",61*/
     83 {0x00,0x00,0x00,0x00,0x20,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00},/*"^",62*/
     84 {0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01},/*"_",63*/
     85 {0x00,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
     86 {0x00,0x00,0x00,0x98,0x01,0x24,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xFC,0x00,0x04},/*"a",65*/
     87 {0x10,0x00,0x1F,0xFC,0x00,0x88,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"b",66*/
     88 {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x00},/*"c",67*/
     89 {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x11,0x08,0x1F,0xFC,0x00,0x04},/*"d",68*/
     90 {0x00,0x00,0x00,0xF8,0x01,0x44,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xC8,0x00,0x00},/*"e",69*/
     91 {0x00,0x00,0x01,0x04,0x01,0x04,0x0F,0xFC,0x11,0x04,0x11,0x04,0x11,0x00,0x18,0x00},/*"f",70*/
     92 {0x00,0x00,0x00,0xD6,0x01,0x29,0x01,0x29,0x01,0x29,0x01,0xC9,0x01,0x06,0x00,0x00},/*"g",71*/
     93 {0x10,0x04,0x1F,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"h",72*/
     94 {0x00,0x00,0x01,0x04,0x19,0x04,0x19,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"i",73*/
     95 {0x00,0x00,0x00,0x03,0x00,0x01,0x01,0x01,0x19,0x01,0x19,0xFE,0x00,0x00,0x00,0x00},/*"j",74*/
     96 {0x10,0x04,0x1F,0xFC,0x00,0x24,0x00,0x40,0x01,0xB4,0x01,0x0C,0x01,0x04,0x00,0x00},/*"k",75*/
     97 {0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"l",76*/
     98 {0x01,0x04,0x01,0xFC,0x01,0x04,0x01,0x00,0x01,0xFC,0x01,0x04,0x01,0x00,0x00,0xFC},/*"m",77*/
     99 {0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"n",78*/
    100 {0x00,0x00,0x00,0xF8,0x01,0x04,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0xF8,0x00,0x00},/*"o",79*/
    101 {0x01,0x01,0x01,0xFF,0x00,0x85,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"p",80*/
    102 {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x05,0x01,0xFF,0x00,0x01},/*"q",81*/
    103 {0x01,0x04,0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x04,0x01,0x00,0x01,0x80,0x00,0x00},/*"r",82*/
    104 {0x00,0x00,0x00,0xCC,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x98,0x00,0x00},/*"s",83*/
    105 {0x00,0x00,0x01,0x00,0x01,0x00,0x07,0xF8,0x01,0x04,0x01,0x04,0x00,0x00,0x00,0x00},/*"t",84*/
    106 {0x01,0x00,0x01,0xF8,0x00,0x04,0x00,0x04,0x00,0x04,0x01,0x08,0x01,0xFC,0x00,0x04},/*"u",85*/
    107 {0x01,0x00,0x01,0x80,0x01,0x70,0x00,0x0C,0x00,0x10,0x01,0x60,0x01,0x80,0x01,0x00},/*"v",86*/
    108 {0x01,0xF0,0x01,0x0C,0x00,0x30,0x01,0xC0,0x00,0x30,0x01,0x0C,0x01,0xF0,0x01,0x00},/*"w",87*/
    109 {0x00,0x00,0x01,0x04,0x01,0x8C,0x00,0x74,0x01,0x70,0x01,0x8C,0x01,0x04,0x00,0x00},/*"x",88*/
    110 {0x01,0x01,0x01,0x81,0x01,0x71,0x00,0x0E,0x00,0x18,0x01,0x60,0x01,0x80,0x01,0x00},/*"y",89*/
    111 {0x00,0x00,0x01,0x84,0x01,0x0C,0x01,0x34,0x01,0x44,0x01,0x84,0x01,0x0C,0x00,0x00},/*"z",90*/
    112 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x3E,0xFC,0x40,0x02,0x40,0x02},/*"{",91*/
    113 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00},/*"|",92*/
    114 {0x00,0x00,0x40,0x02,0x40,0x02,0x3E,0xFC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
    115 {0x00,0x00,0x60,0x00,0x80,0x00,0x80,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x20,0x00},/*"~",94*/
    116 };
    117 
    118 static unsigned short normal_i2c[] = { 0x3C, I2C_CLIENT_END };
    119 I2C_CLIENT_INSMOD_1(ssd1306);
    120 
    121 
    122 static int ssd1306_attach_adapter(struct i2c_adapter *adapter);
    123 static int ssd1306_detach_client(struct i2c_client *client);
    124 
    125 
    126 static struct i2c_driver ssd1306_driver = {
    127     .driver = {
    128             .name    = "ssd1306",
    129         },
    130         .id     = I2C_DRIVERID_I2CDEV,
    131         .attach_adapter = ssd1306_attach_adapter,
    132         .detach_client    = ssd1306_detach_client,
    133 };
    134 
    135 struct i2c_client *ssd1306_client;
    136 
    137 static void ssd1306_write_byte(uint8_t chData, uint8_t chCmd) 
    138 {
    139     uint8_t cmd = 0x00;
    140     
    141     if (chCmd) {
    142         cmd = 0x40;
    143     } else {
    144         cmd = 0x00;
    145     }
    146 
    147     i2c_smbus_write_byte_data(ssd1306_client, cmd, chData);
    148 }
    149 
    150 void ssd1306_display_on(void)
    151 {
    152     ssd1306_write_byte(0x8D, SSD1306_CMD);  
    153     ssd1306_write_byte(0x14, SSD1306_CMD);  
    154     ssd1306_write_byte(0xAF, SSD1306_CMD);  
    155 }
    156    
    157 /**
    158   * @brief  OLED turns off
    159   *         
    160   * @param  None
    161   *         
    162   * @retval  None
    163 **/
    164 void ssd1306_display_off(void)
    165 {
    166     ssd1306_write_byte(0x8D, SSD1306_CMD);  
    167     ssd1306_write_byte(0x10, SSD1306_CMD); 
    168     ssd1306_write_byte(0xAE, SSD1306_CMD);  
    169 }
    170 
    171 void ssd1306_refresh_gram(void)
    172 {
    173     uint8_t i, j;
    174     
    175     for (i = 0; i < 8; i ++) {  
    176         ssd1306_write_byte(0xB0 + i, SSD1306_CMD);    
    177         ssd1306_write_byte(0x02, SSD1306_CMD); 
    178         ssd1306_write_byte(0x10, SSD1306_CMD);     
    179         for (j = 0; j < 128; j ++) {
    180             ssd1306_write_byte(s_chDispalyBuffer[j][i], SSD1306_DAT); 
    181         }
    182     }   
    183 }
    184 
    185 
    186 void ssd1306_clear_screen(uint8_t chFill)  
    187 { 
    188     memset(s_chDispalyBuffer,chFill, sizeof(s_chDispalyBuffer));
    189     ssd1306_refresh_gram();
    190 }
    191 
    192 /**
    193   * @brief  Draws a piont on the screen
    194   *         
    195   * @param  chXpos: Specifies the X position
    196   * @param  chYpos: Specifies the Y position
    197   * @param  chPoint: 0: the point turns off    1: the piont turns on 
    198   *         
    199   * @retval None
    200 **/
    201 
    202 void ssd1306_draw_point(uint8_t chXpos, uint8_t chYpos, uint8_t chPoint)
    203 {
    204     uint8_t chPos, chBx, chTemp = 0;
    205     
    206     if (chXpos > 127 || chYpos > 63) {
    207         return;
    208     }
    209     chPos = 7 - chYpos / 8; // 
    210     chBx = chYpos % 8;
    211     chTemp = 1 << (7 - chBx);
    212     
    213     if (chPoint) {
    214         s_chDispalyBuffer[chXpos][chPos] |= chTemp;
    215         
    216     } else {
    217         s_chDispalyBuffer[chXpos][chPos] &= ~chTemp;
    218     }
    219 }
    220       
    221 /**
    222   * @brief  Fills a rectangle
    223   *         
    224   * @param  chXpos1: Specifies the X position 1 (X top left position)
    225   * @param  chYpos1: Specifies the Y position 1 (Y top left position)
    226   * @param  chXpos2: Specifies the X position 2 (X bottom right position)
    227   * @param  chYpos3: Specifies the Y position 2 (Y bottom right position)
    228   *         
    229   * @retval 
    230 **/
    231 
    232 void ssd1306_fill_screen(uint8_t chXpos1, uint8_t chYpos1, uint8_t chXpos2, uint8_t chYpos2, uint8_t chDot)  
    233 {  
    234     uint8_t chXpos, chYpos; 
    235     
    236     for (chXpos = chXpos1; chXpos <= chXpos2; chXpos ++) {
    237         for (chYpos = chYpos1; chYpos <= chYpos2; chYpos ++) {
    238             ssd1306_draw_point(chXpos, chYpos, chDot);
    239         }
    240     }    
    241     
    242     ssd1306_refresh_gram();
    243 }
    244 
    245 
    246 /**
    247   * @brief Displays one character at the specified position    
    248   *         
    249   * @param  chXpos: Specifies the X position
    250   * @param  chYpos: Specifies the Y position
    251   * @param  chSize: 
    252   * @param  chMode
    253   * @retval 
    254 **/
    255 void ssd1306_display_char(uint8_t chXpos, uint8_t chYpos, uint8_t chChr, uint8_t chSize, uint8_t chMode)
    256 {          
    257     uint8_t i, j;
    258     uint8_t chTemp, chYpos0 = chYpos;
    259     
    260     chChr = chChr - ' ';                   
    261     for (i = 0; i < chSize; i ++) {   
    262         if (chMode) {
    263             chTemp = c_chFont1608[chChr][i];
    264         } else {
    265             chTemp = ~c_chFont1608[chChr][i];
    266         }
    267         
    268         for (j = 0; j < 8; j ++) {
    269             if (chTemp & 0x80) {
    270                 ssd1306_draw_point(chXpos, chYpos, 1);
    271             } else {
    272                 ssd1306_draw_point(chXpos, chYpos, 0);
    273             }
    274             chTemp <<= 1;
    275             chYpos ++;
    276             
    277             if ((chYpos - chYpos0) == chSize) {
    278                 chYpos = chYpos0;
    279                 chXpos ++;
    280                 break;
    281             }
    282         }       
    283     } 
    284 }    
    285 
    286 /**
    287   * @brief  Displays a string on the screen
    288   *         
    289   * @param  chXpos: Specifies the X position
    290   * @param  chYpos: Specifies the Y position
    291   * @param  pchString: Pointer to a string to display on the screen 
    292   *         
    293   * @retval  None
    294 **/
    295 void ssd1306_display_string(uint8_t chXpos, uint8_t chYpos, const uint8_t *pchString, uint8_t chSize, uint8_t chMode)
    296 {
    297     while (*pchString != '') {       
    298         if (chXpos > (SSD1306_WIDTH - chSize / 2)) {
    299             chXpos = 0;
    300             chYpos += chSize;
    301             if (chYpos > (SSD1306_HEIGHT - chSize)) {
    302                 chYpos = chXpos = 0;
    303                 ssd1306_clear_screen(0x00);
    304             }
    305         }
    306         
    307         ssd1306_display_char(chXpos, chYpos, *pchString, chSize, chMode);
    308         chXpos += chSize / 2;
    309         pchString ++;
    310     }
    311 }
    312 
    313 
    314 void ssd1306_init(void)
    315 {
    316     ssd1306_write_byte(0xAE, SSD1306_CMD);//--turn off oled panel
    317     ssd1306_write_byte(0x00, SSD1306_CMD);//---set low column address
    318     ssd1306_write_byte(0x10, SSD1306_CMD);//---set high column address
    319     ssd1306_write_byte(0x40, SSD1306_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    320     ssd1306_write_byte(0x81, SSD1306_CMD);//--set contrast control register
    321     ssd1306_write_byte(0xCF, SSD1306_CMD);// Set SEG Output Current Brightness
    322     ssd1306_write_byte(0xA1, SSD1306_CMD);//--Set SEG/Column Mapping     
    323     ssd1306_write_byte(0xC0, SSD1306_CMD);//Set COM/Row Scan Direction   
    324     ssd1306_write_byte(0xA6, SSD1306_CMD);//--set normal display
    325     ssd1306_write_byte(0xA8, SSD1306_CMD);//--set multiplex ratio(1 to 64)
    326     ssd1306_write_byte(0x3f, SSD1306_CMD);//--1/64 duty
    327     ssd1306_write_byte(0xD3, SSD1306_CMD);//-set display offset    Shift Mapping RAM Counter (0x00~0x3F)
    328     ssd1306_write_byte(0x00, SSD1306_CMD);//-not offset
    329     ssd1306_write_byte(0xd5, SSD1306_CMD);//--set display clock divide ratio/oscillator frequency
    330     ssd1306_write_byte(0x80, SSD1306_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
    331     ssd1306_write_byte(0xD9, SSD1306_CMD);//--set pre-charge period
    332     ssd1306_write_byte(0xF1, SSD1306_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    333     ssd1306_write_byte(0xDA, SSD1306_CMD);//--set com pins hardware configuration
    334     ssd1306_write_byte(0x12, SSD1306_CMD);
    335     ssd1306_write_byte(0xDB, SSD1306_CMD);//--set vcomh
    336     ssd1306_write_byte(0x40, SSD1306_CMD);//Set VCOM Deselect Level
    337     ssd1306_write_byte(0x20, SSD1306_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
    338     ssd1306_write_byte(0x02, SSD1306_CMD);//
    339     ssd1306_write_byte(0x8D, SSD1306_CMD);//--set Charge Pump enable/disable
    340     ssd1306_write_byte(0x14, SSD1306_CMD);//--set(0x10) disable
    341     ssd1306_write_byte(0xA4, SSD1306_CMD);// Disable Entire Display On (0xa4/0xa5)
    342     ssd1306_write_byte(0xA6, SSD1306_CMD);// Disable Inverse Display On (0xa6/a7) 
    343     ssd1306_write_byte(0xAF, SSD1306_CMD);//--turn on oled panel
    344 
    345     ssd1306_display_on();
    346     ssd1306_clear_screen(0xff);
    347     
    348 }
    349 
    350 static int ssd1306_detect(struct i2c_adapter *adapter, int address, int kind)
    351 {    
    352     printk("ssd1306_detect
    ");
    353 
    354     ssd1306_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
    355     ssd1306_client->addr    = address;
    356     ssd1306_client->adapter = adapter;
    357     ssd1306_client->driver  = &ssd1306_driver;
    358     strcpy(ssd1306_client->name, "ssd1306");
    359     
    360     i2c_attach_client(ssd1306_client);
    361     
    362     ssd1306_init();
    363 
    364     ssd1306_clear_screen(0x00);
    365     ssd1306_display_off();
    366 
    367     ssd1306_display_string(18, 0, "hello, Linux!", 16, 1);
    368     ssd1306_display_string(0, 16, "this is an i2c driver demo!", 16, 1);
    369     ssd1306_refresh_gram();
    370     ssd1306_display_on();
    371     
    372     return 0;
    373 }
    374 
    375 static int ssd1306_attach_adapter(struct i2c_adapter *adapter)
    376 {
    377     return i2c_probe(adapter, &addr_data, ssd1306_detect);
    378 }
    379 
    380 static int ssd1306_detach_client(struct i2c_client *client)
    381 {
    382     printk("ssd1306_detach
    ");
    383 
    384     ssd1306_display_off();
    385     ssd1306_clear_screen(0x00);
    386     
    387     i2c_detach_client(client);
    388 
    389     return 0;
    390 }
    391 
    392 
    393 static int ssd1306_module_init(void)
    394 {
    395     i2c_add_driver(&ssd1306_driver);
    396     return 0;
    397 }
    398 
    399 static void ssd1306_module_exit(void)
    400 {
    401     i2c_del_driver(&ssd1306_driver);
    402 }
    403 
    404 module_init(ssd1306_module_init);
    405 module_exit(ssd1306_module_exit);
    406 
    407 MODULE_LICENSE("GPL");

  • 相关阅读:
    搭建微信小程序服务
    Jodd
    $.post()提交了数据,return不给跳转
    Java前端Rsa公钥加密,后端Rsa私钥解密(支持字符和中文)
    Android线程间通讯的几种方式
    Spring MVC返回json数据给Android端
    后台Redis集成的博客
    Android Cookie共享到WebView避免再次登录(保持登录状态)
    Android Okhttp完美同步持久Cookie实现免登录
    Android JSON数据解析(GSON方式)
  • 原文地址:https://www.cnblogs.com/hackfun/p/5990069.html
Copyright © 2020-2023  润新知