歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> I2C子系統之驅動SSD1306 OLED

I2C子系統之驅動SSD1306 OLED

日期:2017/3/1 9:10:55   编辑:Linux編程

理解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_adapter

struct i2c_driver:代表著一類I2C從機設備的驅動,比如:at24cxx的驅動,不同類型的I2C從機需要注冊不同的i2c_driver,如:ssd1306的驅動不同於at24cxx的驅動。每增加一個類型的I2C從機設備,都要向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

static 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.c

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);
... ...
}

這裡就注冊了一個i2c_adapter。

要驅動ssd1306,因此對應地要提供一個i2c_driver,與i2c_adapter建立關系。

ssd1306.c

static 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\n");

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,
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

#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! \n");
}

/* [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 64

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

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\n");

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");

Copyright © Linux教程網 All Rights Reserved