首页 / 操作系统 / Linux / 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_core.c注册一个i2c_adapterstruct i2c_driver:代表着一类I2C从机设备的驱动,比如:at24cxx的驱动,不同类型的I2C从机需要注册不同的i2c_driver,如:ssd1306的驱动不同于at24cxx的驱动。每增加一个类型的I2C从机设备,都要向i2c_core.c注册一个i2c_driverstruct 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.ci2c-algo-bit.cstatic const struct i2c_algorithm i2c_bit_algo = { .master_xfer = bit_xfer, .functionality = bit_func, };static int i2c_bit_prepare_bus(struct i2c_adapter *adap) { ... ... adap->algo = &i2c_bit_algo; ... ... return 0; }int i2c_bit_add_bus(struct i2c_adapter *adap) { ... ... err = i2c_bit_prepare_bus(adap); ... ... return i2c_add_adapter(adap); }i2c-gpio.cstatic int __init i2c_gpio_probe(struct platform_device *pdev) { struct i2c_gpio_platform_data *pdata; struct i2c_algo_bit_data *bit_data; struct i2c_adapter *adap; ... ... pdata = pdev->dev.platform_data; ... ... i2c_bit_add_bus(adap); ... ... }这里就注册了一个i2c_adapter。要驱动ssd1306,因此对应地要提供一个i2c_driver,与i2c_adapter建立关系。ssd1306.cstatic struct i2c_driver ssd1306_driver = { .driver = { .name = "ssd1306", }, .id = I2C_DRIVERID_I2CDEV, .attach_adapter = ssd1306_attach_adapter, .detach_client = ssd1306_detach_client, };static int ssd1306_module_init(void) { i2c_add_driver(&ssd1306_driver); return 0; }i2c_driver和i2c_adapter是怎样建立关系的呢?I2c_core.c负责桥接i2c_driver和i2c_adapter建立关系,在i2c_driver和i2c_adapter注册的时候,两者都会调用driver->attach_adapter(adapter)int i2c_register_driver(struct module *owner, struct i2c_driver *driver) { ... ... driver->attach_adapter(adapter); ... ... return 0; }static int i2c_register_adapter(struct i2c_adapter *adap) { ... ... driver->attach_adapter(adap); ... ... }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返回应答信号,就认为探测到了。int i2c_probe(struct i2c_adapter *adapter, struct i2c_client_address_data *address_data, int (*found_proc) (struct i2c_adapter *, int, int)) { ... ... i2c_probe_address(adapter, address_data->probe[i + 1], -1, found_proc); ... ... }代码太多,简化函数调用关系如下: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。static int ssd1306_detect(struct i2c_adapter *adapter, int address, int kind) { printk("ssd1306_detect
"); ssd1306_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); ssd1306_client->addr = address; ssd1306_client->adapter = adapter; ssd1306_client->driver = &ssd1306_driver; strcpy(ssd1306_client->name, "ssd1306"); i2c_attach_client(ssd1306_client); ... ... }先转下话题。在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中static struct i2c_gpio_platform_data i2c_dev = { .sda_pin = S3C2410_GPG6, .scl_pin = S3C2410_GPG5, .udelay = 0, .timeout = 0, .sda_is_open_drain = 1, .scl_is_open_drain = 1, .scl_is_output_only = 1 };static struct platform_device i2c_platform_dev = { .name = "i2c-gpio", .id = -1, .dev = { .release = i2c_dev_release, .platform_data = (void *)&i2c_dev, }, };static int i2c_dev_init(void) { platform_device_register(&i2c_platform_dev); return 0; }如果platform_device和platform_driver匹配,就会调用i2c_gpio_probe()static int __init i2c_gpio_probe(struct platform_device *pdev) { struct i2c_gpio_platform_data *pdata; struct i2c_algo_bit_data *bit_data; struct i2c_adapter *adap; ... ... pdata = pdev->dev.platform_data; ... ... i2c_bit_add_bus(adap); ... ... }只有platform_device和platform_driver匹配才能注册i2c_adapter。到这里,就可以操作ssd1306了。ssd1306写一个字节的操作:static void ssd1306_write_byte(uint8_t chData, uint8_t chCmd) { uint8_t cmd = 0x00; if (chCmd) { cmd = 0x40; } else { cmd = 0x00; } i2c_smbus_write_byte_data(ssd1306_client, cmd, chData); }实际上调用了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,6u8 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#include <linux/module.h> #include <linux/version.h>#include <linux/init.h>#include <linux/kernel.h> #include <linux/types.h> #include <linux/interrupt.h> #include <linux/list.h> #include <linux/timer.h> #include <linux/init.h> #include <linux/serial_core.h> #include <linux/platform_device.h> #include <linux/gpio_keys.h> #include <linux/input.h> #include <linux/irq.h> #include <linux/i2c-gpio.h>#include <asm/gpio.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> /* [cgw]: */static struct i2c_gpio_platform_data i2c_dev = { .sda_pin = S3C2410_GPG6, .scl_pin = S3C2410_GPG5, .udelay = 0, .timeout = 0, .sda_is_open_drain = 1, .scl_is_open_drain = 1, .scl_is_output_only = 1 };static void i2c_dev_release(struct device * dev) { printk("i2c_dev_release!
"); }/* [cgw]: 分配一个平台设备 */ static struct platform_device i2c_platform_dev = { .name = "i2c-gpio", .id = -1, .dev = { .release = i2c_dev_release, .platform_data = (void *)&i2c_dev, }, }; static int i2c_dev_init(void) { /* [cgw]: 注册i2c_platform_dev平台设备 */ platform_device_register(&i2c_platform_dev); return 0; }static void i2c_dev_exit(void) { /* [cgw]: 注销i2c_platform_dev平台设备 */ platform_device_unregister(&i2c_platform_dev); }module_init(i2c_dev_init); module_exit(i2c_dev_exit);MODULE_LICENSE("GPL");ssd1306.c#include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/fs.h> #include <asm/uaccess.h> #define SSD1306_CMD 0 #define SSD1306_DAT 1#define SSD1306_WIDTH 128 #define SSD1306_HEIGHT 64static uint8_t s_chDispalyBuffer[128][8];const uint8_t c_chFont1608[95][16] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xCC,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/ {0x00,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x00,0x00},/*""",2*/ {0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x00,0x00},/*"#",3*/ {0x00,0x00,0x0E,0x18,0x11,0x04,0x3F,0xFF,0x10,0x84,0x0C,0x78,0x00,0x00,0x00,0x00},/*"$",4*/ {0x0F,0x00,0x10,0x84,0x0F,0x38,0x00,0xC0,0x07,0x78,0x18,0x84,0x00,0x78,0x00,0x00},/*"%",5*/ {0x00,0x78,0x0F,0x84,0x10,0xC4,0x11,0x24,0x0E,0x98,0x00,0xE4,0x00,0x84,0x00,0x08},/*"&",6*/ {0x08,0x00,0x68,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*""",7*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x18,0x18,0x20,0x04,0x40,0x02,0x00,0x00},/*"(",8*/ {0x00,0x00,0x40,0x02,0x20,0x04,0x18,0x18,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00},/*")",9*/ {0x02,0x40,0x02,0x40,0x01,0x80,0x0F,0xF0,0x01,0x80,0x02,0x40,0x02,0x40,0x00,0x00},/*"*",10*/ {0x00,0x80,0x00,0x80,0x00,0x80,0x0F,0xF8,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x00},/*"+",11*/ {0x00,0x01,0x00,0x0D,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/ {0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80},/*"-",13*/ {0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/ {0x00,0x00,0x00,0x06,0x00,0x18,0x00,0x60,0x01,0x80,0x06,0x00,0x18,0x00,0x20,0x00},/*"/",15*/ {0x00,0x00,0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"0",16*/ {0x00,0x00,0x08,0x04,0x08,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"1",17*/ {0x00,0x00,0x0E,0x0C,0x10,0x14,0x10,0x24,0x10,0x44,0x11,0x84,0x0E,0x0C,0x00,0x00},/*"2",18*/ {0x00,0x00,0x0C,0x18,0x10,0x04,0x11,0x04,0x11,0x04,0x12,0x88,0x0C,0x70,0x00,0x00},/*"3",19*/ {0x00,0x00,0x00,0xE0,0x03,0x20,0x04,0x24,0x08,0x24,0x1F,0xFC,0x00,0x24,0x00,0x00},/*"4",20*/ {0x00,0x00,0x1F,0x98,0x10,0x84,0x11,0x04,0x11,0x04,0x10,0x88,0x10,0x70,0x00,0x00},/*"5",21*/ {0x00,0x00,0x07,0xF0,0x08,0x88,0x11,0x04,0x11,0x04,0x18,0x88,0x00,0x70,0x00,0x00},/*"6",22*/ {0x00,0x00,0x1C,0x00,0x10,0x00,0x10,0xFC,0x13,0x00,0x1C,0x00,0x10,0x00,0x00,0x00},/*"7",23*/ {0x00,0x00,0x0E,0x38,0x11,0x44,0x10,0x84,0x10,0x84,0x11,0x44,0x0E,0x38,0x00,0x00},/*"8",24*/ {0x00,0x00,0x07,0x00,0x08,0x8C,0x10,0x44,0x10,0x44,0x08,0x88,0x07,0xF0,0x00,0x00},/*"9",25*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0C,0x03,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/ {0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/ {0x00,0x00,0x00,0x80,0x01,0x40,0x02,0x20,0x04,0x10,0x08,0x08,0x10,0x04,0x00,0x00},/*"<",28*/ {0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x00,0x00},/*"=",29*/ {0x00,0x00,0x10,0x04,0x08,0x08,0x04,0x10,0x02,0x20,0x01,0x40,0x00,0x80,0x00,0x00},/*">",30*/ {0x00,0x00,0x0E,0x00,0x12,0x00,0x10,0x0C,0x10,0x6C,0x10,0x80,0x0F,0x00,0x00,0x00},/*"?",31*/ {0x03,0xE0,0x0C,0x18,0x13,0xE4,0x14,0x24,0x17,0xC4,0x08,0x28,0x07,0xD0,0x00,0x00},/*"@",32*/ {0x00,0x04,0x00,0x3C,0x03,0xC4,0x1C,0x40,0x07,0x40,0x00,0xE4,0x00,0x1C,0x00,0x04},/*"A",33*/ {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x11,0x04,0x0E,0x88,0x00,0x70,0x00,0x00},/*"B",34*/ {0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x08,0x1C,0x10,0x00,0x00},/*"C",35*/ {0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"D",36*/ {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x17,0xC4,0x10,0x04,0x08,0x18,0x00,0x00},/*"E",37*/ {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x17,0xC0,0x10,0x00,0x08,0x00,0x00,0x00},/*"F",38*/ {0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x44,0x1C,0x78,0x00,0x40,0x00,0x00},/*"G",39*/ {0x10,0x04,0x1F,0xFC,0x10,0x84,0x00,0x80,0x00,0x80,0x10,0x84,0x1F,0xFC,0x10,0x04},/*"H",40*/ {0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x00,0x00,0x00,0x00},/*"I",41*/ {0x00,0x03,0x00,0x01,0x10,0x01,0x10,0x01,0x1F,0xFE,0x10,0x00,0x10,0x00,0x00,0x00},/*"J",42*/ {0x10,0x04,0x1F,0xFC,0x11,0x04,0x03,0x80,0x14,0x64,0x18,0x1C,0x10,0x04,0x00,0x00},/*"K",43*/ {0x10,0x04,0x1F,0xFC,0x10,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x0C,0x00,0x00},/*"L",44*/ {0x10,0x04,0x1F,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x1F,0xFC,0x10,0x04,0x00,0x00},/*"M",45*/ {0x10,0x04,0x1F,0xFC,0x0C,0x04,0x03,0x00,0x00,0xE0,0x10,0x18,0x1F,0xFC,0x10,0x00},/*"N",46*/ {0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"O",47*/ {0x10,0x04,0x1F,0xFC,0x10,0x84,0x10,0x80,0x10,0x80,0x10,0x80,0x0F,0x00,0x00,0x00},/*"P",48*/ {0x07,0xF0,0x08,0x18,0x10,0x24,0x10,0x24,0x10,0x1C,0x08,0x0A,0x07,0xF2,0x00,0x00},/*"Q",49*/ {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x11,0xC0,0x11,0x30,0x0E,0x0C,0x00,0x04},/*"R",50*/ {0x00,0x00,0x0E,0x1C,0x11,0x04,0x10,0x84,0x10,0x84,0x10,0x44,0x1C,0x38,0x00,0x00},/*"S",51*/ {0x18,0x00,0x10,0x00,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x00,0x18,0x00,0x00,0x00},/*"T",52*/ {0x10,0x00,0x1F,0xF8,0x10,0x04,0x00,0x04,0x00,0x04,0x10,0x04,0x1F,0xF8,0x10,0x00},/*"U",53*/ {0x10,0x00,0x1E,0x00,0x11,0xE0,0x00,0x1C,0x00,0x70,0x13,0x80,0x1C,0x00,0x10,0x00},/*"V",54*/ {0x1F,0xC0,0x10,0x3C,0x00,0xE0,0x1F,0x00,0x00,0xE0,0x10,0x3C,0x1F,0xC0,0x00,0x00},/*"W",55*/ {0x10,0x04,0x18,0x0C,0x16,0x34,0x01,0xC0,0x01,0xC0,0x16,0x34,0x18,0x0C,0x10,0x04},/*"X",56*/ {0x10,0x00,0x1C,0x00,0x13,0x04,0x00,0xFC,0x13,0x04,0x1C,0x00,0x10,0x00,0x00,0x00},/*"Y",57*/ {0x08,0x04,0x10,0x1C,0x10,0x64,0x10,0x84,0x13,0x04,0x1C,0x04,0x10,0x18,0x00,0x00},/*"Z",58*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFE,0x40,0x02,0x40,0x02,0x40,0x02,0x00,0x00},/*"[",59*/ {0x00,0x00,0x30,0x00,0x0C,0x00,0x03,0x80,0x00,0x60,0x00,0x1C,0x00,0x03,0x00,0x00},/*"",60*/ {0x00,0x00,0x40,0x02,0x40,0x02,0x40,0x02,0x7F,0xFE,0x00,0x00,0x00,0x00,0x00,0x00},/*"]",61*/ {0x00,0x00,0x00,0x00,0x20,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00},/*"^",62*/ {0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01},/*"_",63*/ {0x00,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/ {0x00,0x00,0x00,0x98,0x01,0x24,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xFC,0x00,0x04},/*"a",65*/ {0x10,0x00,0x1F,0xFC,0x00,0x88,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"b",66*/ {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x00},/*"c",67*/ {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x11,0x08,0x1F,0xFC,0x00,0x04},/*"d",68*/ {0x00,0x00,0x00,0xF8,0x01,0x44,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xC8,0x00,0x00},/*"e",69*/ {0x00,0x00,0x01,0x04,0x01,0x04,0x0F,0xFC,0x11,0x04,0x11,0x04,0x11,0x00,0x18,0x00},/*"f",70*/ {0x00,0x00,0x00,0xD6,0x01,0x29,0x01,0x29,0x01,0x29,0x01,0xC9,0x01,0x06,0x00,0x00},/*"g",71*/ {0x10,0x04,0x1F,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"h",72*/ {0x00,0x00,0x01,0x04,0x19,0x04,0x19,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"i",73*/ {0x00,0x00,0x00,0x03,0x00,0x01,0x01,0x01,0x19,0x01,0x19,0xFE,0x00,0x00,0x00,0x00},/*"j",74*/ {0x10,0x04,0x1F,0xFC,0x00,0x24,0x00,0x40,0x01,0xB4,0x01,0x0C,0x01,0x04,0x00,0x00},/*"k",75*/ {0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"l",76*/ {0x01,0x04,0x01,0xFC,0x01,0x04,0x01,0x00,0x01,0xFC,0x01,0x04,0x01,0x00,0x00,0xFC},/*"m",77*/ {0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"n",78*/ {0x00,0x00,0x00,0xF8,0x01,0x04,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0xF8,0x00,0x00},/*"o",79*/ {0x01,0x01,0x01,0xFF,0x00,0x85,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"p",80*/ {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x05,0x01,0xFF,0x00,0x01},/*"q",81*/ {0x01,0x04,0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x04,0x01,0x00,0x01,0x80,0x00,0x00},/*"r",82*/ {0x00,0x00,0x00,0xCC,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x98,0x00,0x00},/*"s",83*/ {0x00,0x00,0x01,0x00,0x01,0x00,0x07,0xF8,0x01,0x04,0x01,0x04,0x00,0x00,0x00,0x00},/*"t",84*/ {0x01,0x00,0x01,0xF8,0x00,0x04,0x00,0x04,0x00,0x04,0x01,0x08,0x01,0xFC,0x00,0x04},/*"u",85*/ {0x01,0x00,0x01,0x80,0x01,0x70,0x00,0x0C,0x00,0x10,0x01,0x60,0x01,0x80,0x01,0x00},/*"v",86*/ {0x01,0xF0,0x01,0x0C,0x00,0x30,0x01,0xC0,0x00,0x30,0x01,0x0C,0x01,0xF0,0x01,0x00},/*"w",87*/ {0x00,0x00,0x01,0x04,0x01,0x8C,0x00,0x74,0x01,0x70,0x01,0x8C,0x01,0x04,0x00,0x00},/*"x",88*/ {0x01,0x01,0x01,0x81,0x01,0x71,0x00,0x0E,0x00,0x18,0x01,0x60,0x01,0x80,0x01,0x00},/*"y",89*/ {0x00,0x00,0x01,0x84,0x01,0x0C,0x01,0x34,0x01,0x44,0x01,0x84,0x01,0x0C,0x00,0x00},/*"z",90*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x3E,0xFC,0x40,0x02,0x40,0x02},/*"{",91*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00},/*"|",92*/ {0x00,0x00,0x40,0x02,0x40,0x02,0x3E,0xFC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"}",93*/ {0x00,0x00,0x60,0x00,0x80,0x00,0x80,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x20,0x00},/*"~",94*/ };static unsigned short normal_i2c[] = { 0x3C, I2C_CLIENT_END }; I2C_CLIENT_INSMOD_1(ssd1306); static int ssd1306_attach_adapter(struct i2c_adapter *adapter); static int ssd1306_detach_client(struct i2c_client *client); static struct i2c_driver ssd1306_driver = { .driver = { .name = "ssd1306", }, .id = I2C_DRIVERID_I2CDEV, .attach_adapter = ssd1306_attach_adapter, .detach_client = ssd1306_detach_client, };struct i2c_client *ssd1306_client;static void ssd1306_write_byte(uint8_t chData, uint8_t chCmd) { uint8_t cmd = 0x00; if (chCmd) { cmd = 0x40; } else { cmd = 0x00; } i2c_smbus_write_byte_data(ssd1306_client, cmd, chData); }void ssd1306_display_on(void) { ssd1306_write_byte(0x8D, SSD1306_CMD); ssd1306_write_byte(0x14, SSD1306_CMD); ssd1306_write_byte(0xAF, SSD1306_CMD); } /** * @brief OLED turns off * * @param None * * @retval None **/ void ssd1306_display_off(void) { ssd1306_write_byte(0x8D, SSD1306_CMD); ssd1306_write_byte(0x10, SSD1306_CMD); ssd1306_write_byte(0xAE, SSD1306_CMD); }void ssd1306_refresh_gram(void) { uint8_t i, j; for (i = 0; i < 8; i ++) { ssd1306_write_byte(0xB0 + i, SSD1306_CMD); ssd1306_write_byte(0x02, SSD1306_CMD); ssd1306_write_byte(0x10, SSD1306_CMD); for (j = 0; j < 128; j ++) { ssd1306_write_byte(s_chDispalyBuffer[j][i], SSD1306_DAT); } } } void ssd1306_clear_screen(uint8_t chFill) { memset(s_chDispalyBuffer,chFill, sizeof(s_chDispalyBuffer)); ssd1306_refresh_gram(); }/** * @brief Draws a piont on the screen * * @param chXpos: Specifies the X position * @param chYpos: Specifies the Y position * @param chPoint: 0: the point turns off 1: the piont turns on * * @retval None **/void ssd1306_draw_point(uint8_t chXpos, uint8_t chYpos, uint8_t chPoint) { uint8_t chPos, chBx, chTemp = 0; if (chXpos > 127 || chYpos > 63) { return; } chPos = 7 - chYpos / 8; // chBx = chYpos % 8; chTemp = 1 << (7 - chBx); if (chPoint) { s_chDispalyBuffer[chXpos][chPos] |= chTemp; } else { s_chDispalyBuffer[chXpos][chPos] &= ~chTemp; } } /** * @brief Fills a rectangle * * @param chXpos1: Specifies the X position 1 (X top left position) * @param chYpos1: Specifies the Y position 1 (Y top left position) * @param chXpos2: Specifies the X position 2 (X bottom right position) * @param chYpos3: Specifies the Y position 2 (Y bottom right position) * * @retval **/void ssd1306_fill_screen(uint8_t chXpos1, uint8_t chYpos1, uint8_t chXpos2, uint8_t chYpos2, uint8_t chDot) { uint8_t chXpos, chYpos; for (chXpos = chXpos1; chXpos <= chXpos2; chXpos ++) { for (chYpos = chYpos1; chYpos <= chYpos2; chYpos ++) { ssd1306_draw_point(chXpos, chYpos, chDot); } } ssd1306_refresh_gram(); } /** * @brief Displays one character at the specified position * * @param chXpos: Specifies the X position * @param chYpos: Specifies the Y position * @param chSize: * @param chMode * @retval **/ void ssd1306_display_char(uint8_t chXpos, uint8_t chYpos, uint8_t chChr, uint8_t chSize, uint8_t chMode) { uint8_t i, j; uint8_t chTemp, chYpos0 = chYpos; chChr = chChr - " "; for (i = 0; i < chSize; i ++) { if (chMode) { chTemp = c_chFont1608[chChr][i]; } else { chTemp = ~c_chFont1608[chChr][i]; } for (j = 0; j < 8; j ++) { if (chTemp & 0x80) { ssd1306_draw_point(chXpos, chYpos, 1); } else { ssd1306_draw_point(chXpos, chYpos, 0); } chTemp <<= 1; chYpos ++; if ((chYpos - chYpos0) == chSize) { chYpos = chYpos0; chXpos ++; break; } } } } /** * @brief Displays a string on the screen * * @param chXpos: Specifies the X position * @param chYpos: Specifies the Y position * @param pchString: Pointer to a string to display on the screen * * @retval None **/ void ssd1306_display_string(uint8_t chXpos, uint8_t chYpos, const uint8_t *pchString, uint8_t chSize, uint8_t chMode) { while (*pchString != " ") { if (chXpos > (SSD1306_WIDTH - chSize / 2)) { chXpos = 0; chYpos += chSize; if (chYpos > (SSD1306_HEIGHT - chSize)) { chYpos = chXpos = 0; ssd1306_clear_screen(0x00); } } ssd1306_display_char(chXpos, chYpos, *pchString, chSize, chMode); chXpos += chSize / 2; pchString ++; } } void ssd1306_init(void) { ssd1306_write_byte(0xAE, SSD1306_CMD);//--turn off oled panel ssd1306_write_byte(0x00, SSD1306_CMD);//---set low column address ssd1306_write_byte(0x10, SSD1306_CMD);//---set high column address ssd1306_write_byte(0x40, SSD1306_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F) ssd1306_write_byte(0x81, SSD1306_CMD);//--set contrast control register ssd1306_write_byte(0xCF, SSD1306_CMD);// Set SEG Output Current Brightness ssd1306_write_byte(0xA1, SSD1306_CMD);//--Set SEG/Column Mapping ssd1306_write_byte(0xC0, SSD1306_CMD);//Set COM/Row Scan Direction ssd1306_write_byte(0xA6, SSD1306_CMD);//--set normal display ssd1306_write_byte(0xA8, SSD1306_CMD);//--set multiplex ratio(1 to 64) ssd1306_write_byte(0x3f, SSD1306_CMD);//--1/64 duty ssd1306_write_byte(0xD3, SSD1306_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F) ssd1306_write_byte(0x00, SSD1306_CMD);//-not offset ssd1306_write_byte(0xd5, SSD1306_CMD);//--set display clock divide ratio/oscillator frequency ssd1306_write_byte(0x80, SSD1306_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec ssd1306_write_byte(0xD9, SSD1306_CMD);//--set pre-charge period ssd1306_write_byte(0xF1, SSD1306_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock ssd1306_write_byte(0xDA, SSD1306_CMD);//--set com pins hardware configuration ssd1306_write_byte(0x12, SSD1306_CMD); ssd1306_write_byte(0xDB, SSD1306_CMD);//--set vcomh ssd1306_write_byte(0x40, SSD1306_CMD);//Set VCOM Deselect Level ssd1306_write_byte(0x20, SSD1306_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02) ssd1306_write_byte(0x02, SSD1306_CMD);// ssd1306_write_byte(0x8D, SSD1306_CMD);//--set Charge Pump enable/disable ssd1306_write_byte(0x14, SSD1306_CMD);//--set(0x10) disable ssd1306_write_byte(0xA4, SSD1306_CMD);// Disable Entire Display On (0xa4/0xa5) ssd1306_write_byte(0xA6, SSD1306_CMD);// Disable Inverse Display On (0xa6/a7) ssd1306_write_byte(0xAF, SSD1306_CMD);//--turn on oled panel ssd1306_display_on(); ssd1306_clear_screen(0xff); }static int ssd1306_detect(struct i2c_adapter *adapter, int address, int kind) { printk("ssd1306_detect
"); ssd1306_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); ssd1306_client->addr = address; ssd1306_client->adapter = adapter; ssd1306_client->driver = &ssd1306_driver; strcpy(ssd1306_client->name, "ssd1306"); i2c_attach_client(ssd1306_client); ssd1306_init(); ssd1306_clear_screen(0x00); ssd1306_display_off(); ssd1306_display_string(18, 0, "hello, Linux!", 16, 1); ssd1306_display_string(0, 16, "this is an i2c driver demo!", 16, 1); ssd1306_refresh_gram(); ssd1306_display_on(); return 0; }static int ssd1306_attach_adapter(struct i2c_adapter *adapter) { return i2c_probe(adapter, &addr_data, ssd1306_detect); }static int ssd1306_detach_client(struct i2c_client *client) { printk("ssd1306_detach
"); ssd1306_display_off(); ssd1306_clear_screen(0x00); i2c_detach_client(client); return 0; } static int ssd1306_module_init(void) { i2c_add_driver(&ssd1306_driver); return 0; }static void ssd1306_module_exit(void) { i2c_del_driver(&ssd1306_driver); }module_init(ssd1306_module_init); module_exit(ssd1306_module_exit);MODULE_LICENSE("GPL");本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-10/136246.htm
收藏该网址