I2C子系统之驱动SSD1306 OLED

时间:2021-12-20 23:25:24

理解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\n");
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子系统之驱动SSD1306 OLED

I2C子系统之驱动SSD1306 OLED

 

代码

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! \n");
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 != '\0') {
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\n");
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\n");
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");

 

I2C子系统之驱动SSD1306 OLED