瑞芯微Rockchip开发者社区
直播中

贾大林

7年用户 1348经验值
私信 关注
[问答]

请问一下如何去实现rk3288 i2c接口的OLED驱动呢

首先设备树文件
       i2c4: i2c@ff160000 {
                compatible = "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] != '')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
WriteDat(F6x8[c]);
x += 6;
j++;
}
}break;
case 2:
{
while(ch[j] != '')
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i+8]);
x += 8;
j++;
}
}break;
}
}
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
unsigned char wm=0;
unsigned int  adder=32*N;
OLED_SetPos(x , y);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
OLED_SetPos(x,y + 1);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
}
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;
  if(y1%8==0)
y = y1/8;
  else
y = y1/8 + 1;
for(y=y0;y {
OLED_SetPos(x0,y);
    for(x=x0;x {
WriteDat(BMP[j++]);
}
}
}
void print_usage(char *cmd)
{
    printf("Usage:n");
    printf("%s initn", cmd);
    printf("%s clearn", cmd);
    printf("%s x y n", cmd);
    printf("%s filln", cmd);
    printf("eg:n");
    printf("%s 2 0 chenxiaodann", cmd);
    printf("%s 0 0 126 7 bmp", cmd);
    printf("posX is 0,1,...,126n");
    printf("posY is 0,1,...,7n");
}
int main(int argc, char **argv)
{
    int do_init  = 0;
    int do_clear = 0;
    int do_show  = 0;
    int do_show_bmp = 0;
    int do_fill = 0;
    int posX=-1;
    int posY=-1;
    int x_end = -1;
    int y_end = -1;
    if (argc == 2 && !strcmp(argv[1], "init"))
        do_init = 1;
    if ((argc == 2) && !strcmp(argv[1], "clear"))
    {
        do_clear = 1;
    }
    if ((argc == 2) && !strcmp(argv[1], "fill"))
    {
        do_fill = 1;
    }
    if (argc == 4)
    {
        do_show = 1;
        posX = strtoul(argv[1], NULL, 0);
        posY = strtoul(argv[2], NULL, 0);
    }
    if (argc == 5)
    {
    do_show_bmp = 1;
        posX = strtoul(argv[1], NULL, 0);
        posY = strtoul(argv[2], NULL, 0);
        x_end = strtoul(argv[3], NULL, 0);
        y_end = strtoul(argv[4], NULL, 0);
printf("pos: %d %d %d %dn", posX, posY, x_end, y_end);
    }
    if (!do_init && !do_clear && !do_show && !do_fill && !do_show_bmp)
    {
        print_usage(argv[0]);
        return -1;
    }
    fd = open("/dev/i2c-oled", O_RDWR);
    if (fd < 0)
    {
        printf("can't open /dev/i2c-oledn");
        return -1;
    }
  if (do_init)
        ioctl(fd, OLED_CMD_INIT);
    else if (do_clear)
    {
        ioctl(fd, OLED_CMD_CLEAR_ALL);
    }
    else if (do_fill)
    {
        ioctl(fd, OLED_CMD_FILL, 1);
    }
    else if (do_show)
    {
        if (posX < 0 || posX > 126)
        {
            printf("x is 0,1,...,126n");
            return -1;
        }
        if (posY < 0 || posY > 7)
        {
            printf("y is 0,1,...,7n");
            return -1;
        }
OLED_ShowStr(posX, posY,argv[3],1);
    }
    else if(do_show_bmp)
    {
    printf("show bmp.n");
OLED_DrawBMP(posX, posY,x_end, y_end, (unsigned char *)BMP1);   
    }
    return 0;
}
驱动的移植,主要还是使用厂家提供的裸板程序,再按照linux的i2c框架添加进去,驱动部分就是注册一个i2c_driver结构体,里面有一个of_match_table 它表示能支持的设备的compatible属性。设备部分现在使用设备树文件,再注册完adapt之后就会去其节点下面的设备一个一个的取出去构造i2c_board_info结构体,其中设备的地址通过设备树的reg属性指定。再设备树的compatible和驱动文件的compatible匹配时就会调用probe函数,然后再probe里面把oled屏注册为一个字符设备,字符设备的fops负责设备的ioctl和write .
附上一张点亮的效果
1.png
原作者:风见暗含

回帖(2)

陈燕

2022-5-14 11:50:28
举报

北山独狼

2022-5-16 09:05:42
过来看看
举报

更多回帖

发帖
×
20
完善资料,
赚取积分