理解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 != '