首先设备树文件
i2c4: i2c@ff160000 {
compa
tible = "rockchip,rk30-i2c";
reg = <0xff160000 0x1000>;
interrupts =
;
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default", "gpio";
pinctrl-0 = <&i2c4_sda &i2c4_scl>;
pinctrl-1 = <&i2c4_gpio>;
gpios = <&gpio7 GPIO_C1 GPIO_ACTIVE_LOW>, <&gpio7 GPIO_C2 GPIO_ACTIVE_LOW>;
clocks = <&clk_gates6 15>;
rockchip,check-idle = <1>;
status = "disabled";
};
&i2c4 {
status = "okay";
i2c_oled@3c {
compatible = "rk3288,i2c-oled";
reg = <0x3c>;
};
};
//i2c控制器注册
static const struct of_device_id rockchip_i2c_of_match[] = {
{ .compatible = "rockchip,rk30-i2c", .data = NULL, },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_i2c_of_match);
static struct platform_driver rockchip_i2c_driver = {
.probe = rockchip_i2c_probe,
.remove = rockchip_i2c_remove,
.shutdown = rockchip_i2c_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = "rockchip_i2c",
.pm = ROCKCHIP_I2C_PM_OPS,
.of_match_table = of_match_ptr(rockchip_i2c_of_match),
},
};
static int rockchip_i2c_probe(struct platform_device *pdev)
struct rockchip_i2c *i2c = NULL;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->adap.algo = &rockchip_i2c_algorithm;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取内存资源
i2c->regs = devm_ioremap_resource(&pdev->dev, res);
of_property_read_u32(np, "rockchip,check-idle", &i2c->check_idle);
if (i2c->check_idle) {
i2c->sda_gpio = of_get_gpio(np, 0);
if (!gpio_is_valid(i2c->sda_gpio)) {
dev_err(&pdev->dev, "sda gpio is invalidn");
return -EINVAL;
}
ret = devm_gpio_request(&pdev->dev, i2c->sda_gpio, dev_name(&i2c->adap.dev));
if (ret) {
dev_err(&pdev->dev, "failed to request sda gpion");
return ret;
}
i2c->scl_gpio = of_get_gpio(np, 1);
if (!gpio_is_valid(i2c->scl_gpio)) {
dev_err(&pdev->dev, "scl gpio is invalidn");
return -EINVAL;
}
ret = devm_gpio_request(&pdev->dev, i2c->scl_gpio, dev_name(&i2c->adap.dev));
if (ret) {
dev_err(&pdev->dev, "failed to request scl gpion");
return ret;
}
i2c->gpio_state = pinctrl_lookup_state(i2c->dev->pins->p, "gpio");
if (IS_ERR(i2c->gpio_state)) {
dev_err(&pdev->dev, "no gpio pinctrl staten");
return PTR_ERR(i2c->gpio_state);
}
pinctrl_select_state(i2c->dev->pins->p, i2c->gpio_state);
gpio_direction_input(i2c->sda_gpio); //设置为输入
gpio_direction_input(i2c->scl_gpio); //设置为输入
pinctrl_select_state(i2c->dev->pins->p, i2c->dev->pins->default_state); //选择默认状态
}
ret = i2c_add_adapter(&i2c->adap);//i2c核心注册adapt
/*
i2c_register_adapter(adapter); -->
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev); -->
internal_of_i2c_register_devices(adap); -->
for_each_available_child_of_node(adap->dev.of_node, node) {
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue;
of_i2c_register_device(adap, node);--> //i2c-core.c
//分配、设置、注册i2c_board_info结构体,最后通过i2c_new_device注册进内核
struct i2c_board_info info = {};
addr = of_get_property(node, "reg", &len);
info.irq = irq_of_parse_and_map(node, 0);
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
result = i2c_new_device(adap, &info);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
}
*/
i2c->clk = devm_clk_get(&pdev->dev, NULL);//获取时钟
i2c->irq = ret = platform_get_irq(pdev, 0);//得到中断号
ret = devm_request_irq(&pdev->dev, i2c->irq, rockchip_i2c_irq, 0, dev_name(&i2c->adap.dev), i2c);//申请中断
ret = clk_prepare(i2c->clk);
i2c->i2c_rate = clk_get_rate(i2c->clk);
rockchip_i2c_init_hw(i2c, 100 * 1000);
of_i2c_register_devices(&i2c->adap);
i2c_driver注册:
i2c_add_driver(&oled_driver);
i2c_register_driver(THIS_MODULE, driver)
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
//当注册返回时,驱动核心层已经调用了probe来匹配那些符合但是为绑定的设备
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver);
if (res)
return res;
//对于每一个适配器,都调用__process_new_driver
//对于每一个适配器,调用它的函数确定address_list里的设备是否存在,如果存在,再调用detect进一步确定、设置,然后i2c_new_device
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
i2c_do_add_adapter(data, to_i2c_adapter(dev));
i2c_detect(adap, driver);
for (i = 0; address_list != I2C_CLIENT_END; i += 1) {
dev_dbg(&adapter->dev, "found normal entry for adapter %d, ""addr 0x%02xn", adap_id, address_list);
temp_client->addr = address_list;
i2c_detect_address(temp_client, driver);
err = i2c_check_addr_validity(addr);//检查地址
//简单的确认i2c总线上是否有这个设备
/* Make sure there is something at this address */
if (!i2c_default_probe(adapter, addr))
return 0;
//回调传进来的detect函数
err = driver->detect(temp_client, &info);
client = i2c_new_device(adapter, &info);
}
oled驱动程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int major;
static struct class *class;
static struct i2c_client *i2c_oled_client;
static unsigned char *ker_buf;
#define OLED_CMD_INIT 0x100001
#define OLED_CMD_CLEAR_ALL 0x100002
#define OLED_CMD_SHOW_STR 0x100003
#define OLED_CMD_SHOW_CN 0x100004
#define OLED_CMD_SHOW_BMP 0x100005
#define OLED_CMD_SET_POS 0X100006
#define OLED_CMD_FILL 0x100007
static int posX, posY;
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
int ret=-1;
char buf[2];
buf[0]=addr;
buf[1]=data;
ret = i2c_master_send(i2c_oled_client, buf, 2);
if (ret <0)
printk("i2c_master_send err:%dn", ret);
}
void WriteCmd(unsigned char I2C_Command)//写命令
{
I2C_WriteByte(0x00, I2C_Command);
}
void WriteDat(unsigned char I2C_Data)//写数据
{
I2C_WriteByte(0x40, I2C_Data);
}
void OLED_Init(void)
{
//DelayMs(100); //这里的延时很重要
msleep(100);
WriteCmd(0xAE); //display off
WriteCmd(0x20); //Set Memory Addressing Mode
WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
WriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
WriteCmd(0xc8); //Set COM Output Scan Direction
WriteCmd(0x00); //---set low column address
WriteCmd(0x10); //---set high column address
WriteCmd(0x40); //--set start line address
WriteCmd(0x81); //--set contrast control register
WriteCmd(0xff); //亮度调节 0x00~0xff
WriteCmd(0xa1); //--set segment re-map 0 to 127
WriteCmd(0xa6); //--set normal display
WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
WriteCmd(0x3F); //
WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
WriteCmd(0xd3); //-set display offset
WriteCmd(0x00); //-not offset
WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
WriteCmd(0xf0); //--set divide ratio
WriteCmd(0xd9); //--set pre-charge period
WriteCmd(0x22); //
WriteCmd(0xda); //--set com pins hardware configuration
WriteCmd(0x12);
WriteCmd(0xdb); //--set vcomh
WriteCmd(0x20); //0x20,0.77xVcc
WriteCmd(0x8d); //--set DC-DC enable
WriteCmd(0x14); //
WriteCmd(0xaf); //--turn on oled panel
}
void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd((x&0x0f)|0x01);
}
void OLED_Fill(unsigned char fill_Data)//全屏填充
{
unsigned char m,n;
for(m=0;m<8;m++)
{
WriteCmd(0xb0+m); //page0-page1
WriteCmd(0x00); //low column start address
WriteCmd(0x10); //high column start address
for(n=0;n<128;n++)
{
WriteDat(fill_Data);
}
}
}
void OLED_CLS(void)//清屏
{
OLED_Fill(0x00);
}
void OLED_ON(void)
{
WriteCmd(0X8D); //设置电荷泵
WriteCmd(0X14); //开启电荷泵
WriteCmd(0XAF); //OLED唤醒
}
void OLED_OFF(void)
{
WriteCmd(0X8D); //设置电荷泵
WriteCmd(0X10); //关闭电荷泵
WriteCmd(0XAE); //OLED休眠
}
static long oled_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case OLED_CMD_INIT:
OLED_Init();
break;
case OLED_CMD_CLEAR_ALL:
OLED_CLS();
break;
case OLED_CMD_SET_POS:
posX=arg & 0xff;
posY=(arg>>8) & 0xff;
printk("oled pos x:%d, pos y: %dn", posX, posY);
OLED_SetPos(posX, posY);
break;
case OLED_CMD_FILL:
OLED_Fill(0xff);
break;
}
return 0;
}
static ssize_t oled_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int ret;
if (count > 4096)
return -EINVAL;
ret = copy_from_user(ker_buf, buf, count);
if (ret >0)
printk("get user disp data err %dn",ret);
WriteDat(buf[0]);//写数据
return 0;
}
static struct file_operations oled_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = oled_ioctl,
.write = oled_write,
};
static int oled_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
printk("%s, %s, %dn", __FILE__, __FUNCTION__, __LINE__);
ker_buf = kmalloc(4096, GFP_KERNEL);
i2c_oled_client = client;
printk("i2c_oled addr is 0x%xn", client->addr);
major = register_chrdev(0, "i2c-oled", &oled_fops);
class = class_create(THIS_MODULE, "i2c-oled");
device_create(class, NULL, MKDEV(major, 0), NULL, "i2c-oled");
return 0;
}
static int oled_remove(struct i2c_client *client)
{
device_destroy(class, MKDEV(major, 0));
class_destroy(class);
unregister_chrdev(major, "i2c-oled");
kfree(ker_buf);
return 0;
}
static const struct i2c_device_id oled_id[] = {
{ "oled", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, oled_id);
static struct of_device_id oled_dt_ids[] = {
{ .compatible = "rk3288,i2c-oled" },
{},
};
struct i2c_driver oled_driver = {
.driver = {
.name = "i2c_oled",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(oled_dt_ids),
},
.probe = oled_probe,
.remove = oled_remove,
.id_table = oled_id,
};
static int __init i2c_oled_init(void)
{
return i2c_add_driver(&oled_driver);
}
static void __exit i2c_oled_exit(void)
{
i2c_del_driver(&oled_driver);
}
late_initcall(i2c_oled_init);
module_exit(i2c_oled_exit);
MODULE_AUTHOR("teefirefly@gmail.com");
MODULE_DESCRIPTION("Firefly vga edid driver");
MODULE_LICENSE("GPL");
测试程序
#include
#include
#include
#include
#include
#include
#include
#include
#include "oledfont.h"
/* oled_test init
* oled_test clear
* oled_test clear
* oled_test
*/
#define OLED_CMD_INIT 0x100001
#define OLED_CMD_CLEAR_ALL 0x100002
#define OLED_CMD_SHOW_STR 0x100003
#define OLED_CMD_SHOW_CN 0x100004
#define OLED_CMD_SHOW_BMP 0x100005
#define OLED_CMD_SET_POS 0X100006
#define OLED_CMD_FILL 0x100007
int fd;
inline static void OLED_SetPos(unsigned char x, unsigned char y)
{
ioctl(fd,OLED_CMD_SET_POS,((y&0xff)<<8)|(x&0xff));
}
inline static void WriteDat(unsigned char data)
{
//ioctl(fd, OLED_CMD_SHOW_STR,data);
write(fd, &data, 1);
}
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
unsigned char c = 0,i = 0,j = 0;
switch(TextSize)
{
case 1:
{
while(ch[j] != '