完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
首先设备树文件
i2c4: i2c@ff160000 { compatible = "rockchip,rk30-i2c"; reg = <0xff160000 0x1000>; interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>; #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 invalid\n"); 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 gpio\n"); return ret; } i2c->scl_gpio = of_get_gpio(np, 1); if (!gpio_is_valid(i2c->scl_gpio)) { dev_err(&pdev->dev, "scl gpio is invalid\n"); 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 gpio\n"); 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 state\n"); 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%02x\n", 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 <linux/module.h> #include <linux/i2c.h> #include <linux/bcd.h> #include <linux/rtc.h> #include <linux/delay.h> #include <linux/wakelock.h> #include <linux/slab.h> #include <linux/of_gpio.h> #include <linux/irqdomain.h> #include <linux/rk_fb.h> #include <linux/delay.h> #include <linux/regulator/consumer.h> 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:%d\n", 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: %d\n", 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 %d\n",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, %d\n", __FILE__, __FUNCTION__, __LINE__); ker_buf = kmalloc(4096, GFP_KERNEL); i2c_oled_client = client; printk("i2c_oled addr is 0x%x\n", 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 <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include "oledfont.h" /* oled_test init * oled_test clear * oled_test clear <page> * oled_test <page> <col> <string> */ #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] != '\0') { 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] != '\0') { 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<y1;y++) { OLED_SetPos(x0,y); for(x=x0;x<x1;x++) { WriteDat(BMP[j++]); } } } void print_usage(char *cmd) { printf("Usage:\n"); printf("%s init\n", cmd); printf("%s clear\n", cmd); printf("%s x y <string>\n", cmd); printf("%s fill\n", cmd); printf("eg:\n"); printf("%s 2 0 chenxiaodan\n", cmd); printf("%s 0 0 126 7 bmp", cmd); printf("posX is 0,1,...,126\n"); printf("posY is 0,1,...,7\n"); } 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 %d\n", 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-oled\n"); 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,...,126\n"); return -1; } if (posY < 0 || posY > 7) { printf("y is 0,1,...,7\n"); 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 . 附上一张点亮的效果 ![]() 原作者:风见暗含 |
|
相关推荐
2个回答
|
|
|
|
过来看看
|
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
小黑屋| 手机版| Archiver| 电子发烧友 ( 粤ICP备14022951号 )
GMT+8, 2022-5-19 11:35 , Processed in 1.276244 second(s), Total 121, Slave 101 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com