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 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[i] != I2C_CLIENT_END; i += 1) {
									dev_dbg(&adapter->dev, "found normal entry for adapter %d, ""addr 0x%02x\n", adap_id, address_list[i]);
									temp_client->addr = address_list[i];
									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:%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("[email protected]");
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] != '\0')
			{
				c = ch[j] - 32;
				if(x > 126)
				{
					x = 0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
					WriteDat(F6x8[c][i]);
				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\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 .

附上一张点亮的效果
rk3288 i2c接口的OLED驱动_第1张图片

你可能感兴趣的:(linux驱动)