19.【linux驱动】IIC驱动(OLED屏)

IIC系列文章
1.【linux驱动】IIC驱动-硬件、协议
2.【linux驱动】IIC驱动OLED屏(GPIO 模拟)
3.【linux驱动】IIC驱动(4418读取EEPROM:24AA025E48T-I/OT)
4.【linux驱动】IIC驱动OLED屏

driver

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/gpio.h>
#include <mach/soc.h>
#include <linux/delay.h>
#include <mach/platform.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/irq.h>    //含有IRQ_HANDLED\IRQ_TYPE_EDGE_RISING
#include <linux/interrupt.h> //含有request_irq、free_irq函数
#include "iic.h"

MODULE_LICENSE("GPL");

dev_t devid;
struct cdev char_dev;
struct class * char_class;
int buffer_size = 100;
char * char_data;
int iic_int = 0;

#define _SETDATA(p, d)		(((struct i2c_register *)p)->IDSR = d)
#define _GETDATA(p)			(((struct i2c_register *)p)->IDSR)

void * base;
void * reset_addr;
void * clk_base;

struct IIC_DEV{
	u8 addr;    	 //对方硬件地址
	u8 mode;    	 //传输模式
	u8 *data;   	 //写入或者读取的数据
	int data_count;  //传输数据数量
	int data_idx;    //数据读写位置
	bool is_irq;	 //是否收到中断
};

struct IIC_DEV iic_dev;

static inline void	iic_set_clock(void)
{
	int cksrc         = 1; //pclk/256
	int ckscl         = 0xe;
	unsigned int ICCR = 0;
	ICCR              = readl(base+I2C_ICCR_OFFS);
	ICCR              &=  ~( 0x0f | 1 << ICCR_CLK_SRC_POS);			//清除发送时钟相关寄存器
	ICCR              |=  ((cksrc << ICCR_CLK_SRC_POS) | (ckscl)); //设置时钟源
	writel(ICCR,(base+I2C_ICCR_OFFS));
}

static inline void start_dev(unsigned int mode){
	unsigned int ICCR = 0,ICSR = 0;

	ICSR = readl(base+I2C_ICSR_OFFS);
	ICSR  =  (1<<ICSR_OUT_ENB_POS);
	writel(ICSR,(base+I2C_ICSR_OFFS)); // 这里设置一遍,确保输出启用下面数据才可以写入IDSR

	writel(iic_dev.addr,(base+I2C_IDSR_OFFS));// 设置从机地址

	ICCR  = readl(base+I2C_ICCR_OFFS);
	ICCR &= ~(1<<ICCR_ACK_ENB_POS);
	ICCR |= 1 << ICCR_IRQ_ENB_POS;
	writel(ICCR,(base+I2C_ICCR_OFFS));

	ICSR  = readl(base+I2C_ICSR_OFFS);
	ICSR |= (mode & 3) << ICSR_MOD_SEL_POS; // 主机发送
	ICSR |= 1 << ICSR_SIG_GEN_POS;  // 开始传输
	ICSR |= 1 << ICSR_OUT_ENB_POS;  // 使能输出
	writel(ICSR,(base+I2C_ICSR_OFFS));// 启动发送数据
}

static inline int i2c_wait_dev(int wait)
{
	unsigned int ICSR = 0;

	do {
		ICSR = readl(base+I2C_ICSR_OFFS);
		if ( !(ICSR & (1<<ICSR_BUS_BUSY_POS)) &&  !(ICSR & (1<<ICSR_ARI_STA_POS)) )
			return 0;
	    mdelay(1);
	} while (wait-- > 0);
	return -1;
}

static inline void trans_dev(unsigned int ack)
{
	unsigned int ICCR = 0;

	ICCR = readl(base+I2C_ICCR_OFFS);
	ICCR &= ~(1<<ICCR_ACK_ENB_POS);
	ICCR |= ack << ICCR_ACK_ENB_POS;
	writel(ICCR, (base+I2C_ICCR_OFFS));

	ICCR = readl((base+I2C_ICCR_OFFS));
	ICCR &=  (~ (1 << ICCR_IRQ_PND_POS));
	ICCR |=  1<<ICCR_IRQ_CLR_POS;
	ICCR |=  1<<ICCR_IRQ_ENB_POS;
	writel(ICCR, (base+I2C_ICCR_OFFS));
}

static void stop_sig(void)
{
	unsigned int ICSR = 0, STOP = 0;

	trans_dev(0);						//启动传输
	udelay(1);
	STOP  = readl(base+I2C_STOP_OFFS);
	STOP |= 1<<STOP_ACK_GEM_POS;
	writel(STOP, base+I2C_STOP_OFFS);	//发送停止信号
	udelay(1);
	ICSR  = iic_dev.mode << ICSR_MOD_SEL_POS;
	writel(ICSR, (base+I2C_ICSR_OFFS));	//清除寄存器
}


static bool is_ack(int is_ack){
	unsigned int ICSR = 0xff;
	ICSR= readl(base+I2C_ICSR_OFFS);
	if ((ICSR & 1) == 0 || !is_ack){
		return true;
	}else{
		printk("ack not receive\n");
	}
	ICSR = ICSR >> 1;
	if ((ICSR & 1) == 0){
		printk("start/stop\n");
	}else{
		printk("0x00 addr\n");
	}
	ICSR = ICSR >> 1;
	if ((ICSR & 1) == 0){
		printk("start/stop 2\n");
	}else{
		printk("match addr\n");
	}
	ICSR = ICSR >> 1;
	if ((ICSR & 1) == 0){
		printk("arbitration ok\n");
	}else{
		printk("arbitration fail\n");
	}
	return false;
}

static irqreturn_t iic_irq(int irq,void* dev_id)
{
	unsigned int ICCR = 0;
	ICCR  = readl((base+I2C_ICCR_OFFS));
	ICCR  &= (~ (1 << ICCR_IRQ_ENB_POS));	//禁用中断
	ICCR  |= 1<<ICCR_IRQ_CLR_POS;			//清除中断
	writel(ICCR, (base+I2C_ICCR_OFFS));

	if(is_ack(iic_dev.data_idx == 0)){
		int ack  = (iic_dev.data_count <= iic_dev.data_idx + 1) ? 0: 1;// 不是最后一个都要发ack
		if (iic_dev.mode == I2C_TXRXMODE_SLAVE_TX \
			|| iic_dev.mode == I2C_TXRXMODE_MASTER_TX){

			if(iic_dev.data_idx == iic_dev.data_count){
				// stop
				stop_sig();
			}else{
				//write
				writel(iic_dev.data[iic_dev.data_idx],(base+I2C_IDSR_OFFS));// 设置数据
				trans_dev(0);
			}
		}else{
			//read
			if(iic_dev.data_idx == iic_dev.data_count){
				// stop
				iic_dev.data[iic_dev.data_idx - 1] = _GETDATA(base);
				stop_sig();
			}else{
				if(iic_dev.data_idx == 0) // 第一次ack
				{
				}else{
					iic_dev.data[iic_dev.data_idx - 1] = _GETDATA(base);
				}
				trans_dev(ack);
			}
		}
		iic_dev.data_idx++;
	}else{
		printk("canot find device:%x\n", iic_dev.addr);
	}
    return (IRQ_HANDLED);
}

static int write(u8 slave_addr,u8 * data,u8 count)
{
	int wait_time = 500;

	iic_dev.addr       = (slave_addr & ~0x1);
	iic_dev.data       = data;
	iic_dev.mode       = I2C_TXRXMODE_MASTER_TX;
	iic_dev.data_count = count;
	iic_dev.data_idx   = 0;
	iic_dev.is_irq     = false;

	iic_set_clock();		//设置iic时钟
	if (i2c_wait_dev(wait_time) != -1){
		start_dev(I2C_TXRXMODE_MASTER_TX);
	}else{
		stop_sig();
		printk("device is busy\n");
		return -1;
	}

	wait_time = 0;
	while(iic_dev.data_idx <= iic_dev.data_count\
		&& WAIT_ACK_TIME > wait_time){
		mdelay(10);
		wait_time += 10;
	}
	if(iic_dev.data_idx <= iic_dev.data_count)
	{
		stop_sig();
		printk("write time out\n");
	}
	return 0;
}

static int read(u8 slave_addr,u8 * read_buffer, u8 count)
{
	int wait_time = 500;

	iic_dev.addr       = (slave_addr | 0x1);
	iic_dev.data       = read_buffer;
	iic_dev.mode       = I2C_TXRXMODE_MASTER_RX;
	iic_dev.data_count = count;
	iic_dev.data_idx   = 0;
	iic_dev.is_irq     = false;

	iic_set_clock();		//设置iic时钟
	if (i2c_wait_dev(wait_time) != -1){
		start_dev(I2C_TXRXMODE_MASTER_RX);
	}else{
		stop_sig();
		printk("device is busy\n");
		return -1;
	}

	wait_time = 0;
	while(iic_dev.data_idx <= iic_dev.data_count\
		&& WAIT_ACK_TIME > wait_time){
		mdelay(10);
		wait_time += 10;
	}
	if(iic_dev.data_idx <= iic_dev.data_count)
	{
		stop_sig();
		printk("read time out\n");
	}
	return 0;
}

static inline void i2c_bus_off(void)
{
	unsigned int ICSR = 0;

	ICSR &= ~(1<<ICSR_OUT_ENB_POS);
	writel(ICSR, (base+I2C_ICSR_OFFS));
}

static long ioctl(struct file * fl, unsigned int cmd, unsigned long arg){
	unsigned int dir,size,i;
	unsigned char dev_addr;
	dir = _IOC_DIR(cmd);
	size = _IOC_SIZE(cmd);
	i = copy_from_user(&dev_addr,(unsigned char *)arg,1);
	i = size - 1;

	if(dir == _IOC_WRITE){
		while(i > 0){
			i = copy_from_user(char_data,(unsigned char *)arg + 1,i);
		}
		write(dev_addr,char_data,size - 1);
	}else{
		read(dev_addr,char_data,size - 1);
		while(i > 0){
			i = copy_to_user((unsigned char *)arg + 1, char_data, i);
		}
	}
	return 0;
}

struct file_operations my_opts = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = ioctl
};

static int __init iic_init(void){
	unsigned int reset;
	int ret = 0;

    devid = MKDEV(241, 1);								//换算设备号
    ret = register_chrdev_region(devid, 1, "char_test");//注册设备,在/proc/drivers下面可以看到
    if (ret < 0)
        goto err0;

    cdev_init(&char_dev,&my_opts);						//绑定opt结构体
    char_dev.owner = THIS_MODULE;
    ret = cdev_add(&char_dev,devid,1);					//注册字符设备驱动
    if (ret < 0)
    	goto err1;

    char_class = class_create(THIS_MODULE,"char_test"); //在/sys/class中创建文件夹
    device_create(char_class,NULL,devid,NULL,"char_test_dev_%d",1);//在上一步文件夹中创建char_test_dev_1

    char_data = kzalloc(buffer_size,GFP_KERNEL);

	base       = (void *)ioremap(IIC_BASE,0xf0);			//映射地址
	clk_base   = (void *)ioremap(CLK_BASE,0xf0);			//映射地址
	reset_addr = (void *)ioremap(RESET_BASE,0xf0);			//映射地址

	nxp_soc_gpio_set_io_pull_enb(i2c_gpio[0],0);//禁用上拉
	nxp_soc_gpio_set_io_pull_enb(i2c_gpio[1],0);//禁用上拉
	nxp_soc_gpio_set_io_func(i2c_gpio[0], 1);//设置gpio功能
	nxp_soc_gpio_set_io_func(i2c_gpio[1], 1);//设置gpio功能

	writel(0x1 << 3,clk_base);// 使能时钟
	reset  = readl(reset_addr);
	reset &= ~(0x1 << 20);
	writel(reset,reset_addr);// 复位硬件
	mdelay(1);
	reset |= 0x1 << 20;
	writel(reset,reset_addr);// 结束复位

	i2c_bus_off();

	if (!request_irq(IRQ_PHY_I2C0,iic_irq,IRQF_DISABLED | IRQF_SHARED,"iic_irq0",(void *)1)){
        printk("irq registed %d\n", IRQ_PHY_I2C0);
        iic_int = IRQ_PHY_I2C0;
    }else{
    	printk("irq regist fail %d\n",IRQ_PHY_I2C0);
    }
	printk("iic init\n");
    return 0;

	err1:
	    unregister_chrdev_region(devid, 1);
    err0:
        return ret;
}

static void __exit iic_exit(void){
	if(iic_int){
		free_irq(IRQ_PHY_I2C0,(void *)1);
	}
	nxp_soc_gpio_set_io_func(i2c_gpio[0], 0);//恢复gpio功能
	nxp_soc_gpio_set_io_func(i2c_gpio[1], 0);//恢复gpio功能
	iounmap(base);
	iounmap(clk_base);
	iounmap(reset_addr);
	unregister_chrdev_region(devid, 1);
	cdev_del(&char_dev);
	device_destroy(char_class,devid);
	class_destroy(char_class);
	printk("iic exit\n");
}

module_init(iic_init);
module_exit(iic_exit);

application

#include <stdio.h>
#include <linux/ioctl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include "codetab.h"

#define	Brightness	0xCF 
#define X_WIDTH 	128
#define Y_WIDTH 	64

int fd;
/*********************OLED写数据************************************/ 
inline int OLED_WrDat(unsigned char IIC_Data)
{
	int ret,cmd;
	unsigned char data[] = {0x78,0x40,IIC_Data};
	cmd = _IOC(_IOC_WRITE,0x00,0x00,0x03);
	ret = ioctl(fd,cmd,data);
	if(ret < 0){
		perror("ioctl error");
		return ret;
	}
	return 0;
}
/*********************OLED写命令************************************/
inline int OLED_WrCmd(unsigned char IIC_Command)
{
	int ret,cmd;
	unsigned char data[] = {0x78,0x00,IIC_Command};
	cmd = _IOC(_IOC_WRITE,0x00,0x00,0x03);
	ret = ioctl(fd,cmd,data);
	if(ret < 0){
		perror("ioctl error");
		return ret;
	}
	return 0;
}
/*********************OLED 设置坐标************************************/
void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 
	OLED_WrCmd(0xb0+y);
	OLED_WrCmd(((x&0xf0)>>4)|0x10);
	OLED_WrCmd((x&0x0f)|0x01);
} 
/*********************OLED全屏************************************/
void OLED_Fill(unsigned char bmp_dat) 
{
	unsigned char y,x;
	for(y=0;y<8;y++)
	{
		OLED_WrCmd(0xb0+y);
		OLED_WrCmd(0x01);
		OLED_WrCmd(0x10);
		for(x=0;x<X_WIDTH;x++)
		OLED_WrDat(bmp_dat);
	}
}
/*********************OLED复位************************************/
void OLED_CLS(void)
{
	unsigned char y,x;
	for(y=0;y<8;y++)
	{
		OLED_WrCmd(0xb0+y);
		OLED_WrCmd(0x01);
		OLED_WrCmd(0x10);
		for(x=0;x<X_WIDTH;x++)
		OLED_WrDat(0);
	}
}
/*********************OLED初始化************************************/
void OLED_Init(void)
{
	OLED_WrCmd(0xae);//--turn off oled panel
	OLED_WrCmd(0x00);//---set low column address
	OLED_WrCmd(0x10);//---set high column address
	OLED_WrCmd(0x40);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WrCmd(0x81);//--set contrast control register
	OLED_WrCmd(Brightness); // Set SEG Output Current Brightness
	OLED_WrCmd(0xa1);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WrCmd(0xc8);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WrCmd(0xa6);//--set normal display
	OLED_WrCmd(0xa8);//--set multiplex ratio(1 to 64)
	OLED_WrCmd(0x3f);//--1/64 duty
	OLED_WrCmd(0xd3);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WrCmd(0x00);//-not offset
	OLED_WrCmd(0xd5);//--set display clock divide ratio/oscillator frequency
	OLED_WrCmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WrCmd(0xd9);//--set pre-charge period
	OLED_WrCmd(0xf1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WrCmd(0xda);//--set com pins hardware configuration
	OLED_WrCmd(0x12);
	OLED_WrCmd(0xdb);//--set vcomh
	OLED_WrCmd(0x40);//Set VCOM Deselect Level
	OLED_WrCmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WrCmd(0x02);//
	OLED_WrCmd(0x8d);//--set Charge Pump enable/disable
	OLED_WrCmd(0x14);//--set(0x10) disable
	OLED_WrCmd(0xa4);// Disable Entire Display On (0xa4/0xa5)
	OLED_WrCmd(0xa6);// Disable Inverse Display On (0xa6/a7) 
	OLED_WrCmd(0xaf);//--turn on oled panel
	OLED_Fill(0x00); //初始清屏
	OLED_Set_Pos(0,0);
} 
/***************功能描述:显示6*8一组标准ASCII字符串	显示的坐标(x,y),y为页范围0~7****************/
void OLED_P6x8Str(unsigned char x,unsigned char y,unsigned char ch[])
{
	unsigned char c=0,i=0,j=0;
	while (ch[j]!='\0')
	{
		c =ch[j]-32;
		if(x>126){x=0;y++;}
		OLED_Set_Pos(x,y);
		for(i=0;i<6;i++)
		OLED_WrDat(F6x8[c][i]);
		x+=6;
		j++;
	}
}
/*******************功能描述:显示8*16一组标准ASCII字符串	 显示的坐标(x,y),y为页范围0~7****************/
void OLED_P8x16Str(unsigned char x,unsigned char y,unsigned char ch[])
{
	unsigned char c=0,i=0,j=0;
	while (ch[j]!='\0')
	{
		c =ch[j]-32;
		if(x>120){x=0;y++;}
		OLED_Set_Pos(x,y);
		for(i=0;i<8;i++)
		OLED_WrDat(F8X16[c*16+i]);
		OLED_Set_Pos(x,y+1);
		for(i=0;i<8;i++)
		OLED_WrDat(F8X16[c*16+i+8]);
		x+=8;
		j++;
	}
}
/*****************功能描述:显示16*16点阵  显示的坐标(x,y),y为页范围0~7****************************/
void OLED_P16x16Ch(unsigned char x,unsigned char y,unsigned char N)
{
	unsigned char wm=0;
	unsigned int adder=32*N;
	OLED_Set_Pos(x , y);
	for(wm = 0;wm < 16;wm++)
	{
		OLED_WrDat(F16x16[adder]);
		adder += 1;
	}
	OLED_Set_Pos(x,y + 1);
	for(wm = 0;wm < 16;wm++)
	{
		OLED_WrDat(F16x16[adder]);
		adder += 1;
	} 	  	
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
void Draw_BMP(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_Set_Pos(x0,y);
    for(x=x0;x<x1;x++)
	    {      
	    	OLED_WrDat(BMP[j++]);
	    }
	}
}

static void OLED_ON(void)
{
	OLED_WrCmd(0X8D);  //设置电荷泵
	OLED_WrCmd(0X14);  //开启电荷泵
	OLED_WrCmd(0XAF);  //OLED唤醒
}

int main(){
	int ret,cmd;
	fd = open("/dev/char_test_dev_1",O_RDWR);
	ret = fd;
	if(ret < 0){
		perror("open /dev/char_test_dev_1 error");
		return ret;
	}
	OLED_Init();
	OLED_Fill(0xff); //初始清屏
	close(ret);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_16054639/article/details/106734062