linux下驱动SSD1306(128*64)

背景

芯片:imx6ull

通信协议:IIC

外设:SSD1396(128*64)

设备树配置忽略,根据硬件参数配置

【驱动】

probe函数注册相关的dev设备,主要是文件操作的相关接口

/*字符设备操作函数集*/
static struct file_operations oled_chr_dev_fops =
	{
		.owner = THIS_MODULE,
		.open = oled_open,
		.write = oled_write,
		.unlocked_ioctl = oled_ioctl,
		.release = oled_release,
};

open操作函数集

/*字符设备操作函数集,open函数实现*/
static int oled_open(struct inode *inode, struct file *filp)
{
	// printk("\n oled_open \n");

	/*向 oled 发送配置数据,让oled处于正常工作状态*/
	oled_init();
	return 0;
}

/*初始化i2c
*返回值,成功,返回0。失败,返回 -1
*/
static int oled_init(void)
{
	printk(KERN_EMERG"oled_init");
	int error = 0;
	error |= i2c_oled_send_cmd(0xAE);//--display off
	error |= i2c_oled_send_cmd(0x00);//---set low column address
	error |= i2c_oled_send_cmd(0x10);//---set high column address
	error |= i2c_oled_send_cmd(0x40);//--set start line address  
	error |= i2c_oled_send_cmd(0xB0);//--set page address
	error |= i2c_oled_send_cmd(0x81); // contract control
	error |= i2c_oled_send_cmd(0xFF);//--128   
	error |= i2c_oled_send_cmd(0xA1);//set segment remap 
	error |= i2c_oled_send_cmd(0xA6);//--normal / reverse
	error |= i2c_oled_send_cmd(0xA8);//--set multiplex ratio(1 to 64)
	error |= i2c_oled_send_cmd(0x3F);//--1/32 duty
	error |= i2c_oled_send_cmd(0xC8);//Com scan direction
	error |= i2c_oled_send_cmd(0xD3);//-set display offset
	error |= i2c_oled_send_cmd(0x00);//
	error |= i2c_oled_send_cmd(0xD5);//set osc division
	error |= i2c_oled_send_cmd(0x80);//
	error |= i2c_oled_send_cmd(0xD8);//set area color mode off
	error |= i2c_oled_send_cmd(0x05);//
	error |= i2c_oled_send_cmd(0xD9);//Set Pre-Charge Period
	error |= i2c_oled_send_cmd(0xF1);//	
	error |= i2c_oled_send_cmd(0xDA);//set com pin configuartion
	error |= i2c_oled_send_cmd(0x12);//	
	error |= i2c_oled_send_cmd(0xDB);//set Vcomh
	error |= i2c_oled_send_cmd(0x30);//	
	error |= i2c_oled_send_cmd(0x8D);//set charge pump enable
	error |= i2c_oled_send_cmd(0x14);//	
	error |= i2c_oled_send_cmd(0xAF);//--turn on oled panel
	printk(KERN_EMERG"oled_init finish");
	if (error < 0)
	{
		/*初始化错误*/
		printk(KERN_DEBUG "\n oled_init error \n");
		return -1;
	}
	return 0;
}

write函数,没有怎么使用上;写文件操作,主要使用ioctl函数

ioctl的定义

#define IOCTL_CLEAN_SCREEN _IO('d', 1)
#define IOCTL_W_CMD _IOW('d', 2, char)
#define IOCTL_W_DATA _IOW('d', 3, char)

ioctl注册的函数集实现

static long oled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
{
	#if 1
	char tmp = NULL;
	int ret = 0;
	
	switch(cmd){
		case IOCTL_CLEAN_SCREEN:
			OLED_ClearFullScreen();
			break;
		case IOCTL_W_CMD:
			tmp = arg;
			i2c_oled_send_cmd(tmp);
			break;
		case IOCTL_W_DATA:
			tmp = arg;
			i2c_oled_send_data(tmp);
			break;
		default:
			printk(KERN_EMERG"IOCMD not found");
			return -ENOTTY; // 当未定义的命令号时返回错误
	}
	#endif
	return 0;
}

SSD1306的写操作主要用到两种命令

写数据:0x00+data

写命令:0x40+cmd

//oled写命令
static int i2c_oled_send_cmd(u8 cmd)
{
	int error = 0;
	error |= i2c_write_oled(oled_client,0x00,cmd);
	
	if(error){
		printk(KERN_EMERG "\n i2c_oled_send_cmd error \n");
		return -1;
	}
	//printk(KERN_EMERG "i2c_oled_send_cmd cmd:0x%x\n",cmd);
	return 0;
}

//oled写数据
static int i2c_oled_send_data(u8 data)
{
	int error = 0;
	error |= i2c_write_oled(oled_client,0x40,data);
	if(error){
		printk(KERN_DEBUG "\n i2c_oled_send_data error \n");
		return -1;
	}
	//printk(KERN_EMERG "i2c_oled_send_data data:0x%x\n",data);
	return 0;
}

static int i2c_write_oled(struct i2c_client *oled_client, u8 address, u8 data)
{
	int error = 0;
	u8 write_data[2];
	struct i2c_msg send_msg; //要发送的数据结构体

	/*设置要发送的数据*/
	write_data[0] = address;
	write_data[1] = data;

	printk(KERN_EMERG"data0:0x%x data1:0x:%x\n",write_data[0],write_data[1]);
	/*发送 iic要写入的地址 reg*/
	send_msg.addr = oled_client->addr; //mpu6050在 iic 总线上的地址
	send_msg.flags = 0;					  //标记为发送数据
	send_msg.buf = write_data;			  //写入的首地址
	send_msg.len = 2;					  //reg长度

	/*执行发送*/
	error = i2c_transfer(oled_client->adapter, &send_msg, 1);
	if (error != 1)
	{
		if(error == -ENXIO){
			printk(KERN_EMERG "\n i2c_transfer ENXIO \n");
		}else if(error == -EIO){
			printk(KERN_EMERG "\n i2c_transfer EIO \n");
		}else if(error == -ETIMEDOUT){
			printk(KERN_EMERG "\n i2c_transfer ETIMEDOUT \n");
		}else{
			printk(KERN_EMERG "\n i2c_error\n");
		}
		printk(KERN_EMERG "\n i2c_transfer error11:%d \n",error);
		return -1;
	}
	//printk(KERN_EMERG "\n i2c_transfer error:%d \n",error);
	return 0;
}

【应用】

#define IOCTL_CLEAN_SCREEN _IO('d', 1)
#define IOCTL_W_CMD _IOW('d', 2, char)
#define IOCTL_W_DATA _IOW('d', 3, char)

#define OLED_CMD 1
#define OLED_DATA 0
#define Max_Column	128
#define Max_Row		64

void OLED_WR_Byte(unsigned char dat,unsigned int cmd)
{
	char temp_data;
	if(cmd){
		temp_data = dat;
		ioctl(fd, IOCTL_W_CMD,temp_data);
		printf("[app]cmd:0x%x\n",temp_data);	
	}else{
		temp_data = dat;
		ioctl(fd, IOCTL_W_DATA,temp_data);
		printf("[app]data:0x%x\n",temp_data);
	}
}

void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 	OLED_WR_Byte(0xb0+y,OLED_CMD);
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
	OLED_WR_Byte((x&0x0f),OLED_CMD); 
}

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{      	
	unsigned char c=0,i=0;	
		c=chr-' ';//得到偏移后的值			
		if(x>Max_Column-1){x=0;y=y+2;}
		if(Char_Size ==16)
			{
			OLED_Set_Pos(x,y);	
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
			OLED_Set_Pos(x,y+1);
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
			}
			else {	
				OLED_Set_Pos(x,y);
				for(i=0;i<6;i++)
				OLED_WR_Byte(F6x8[c][i],OLED_DATA);
				
			}
}

void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size)
{
	unsigned char j=0;
	while (chr[j]!='\0')
	{		OLED_ShowChar(x,y,chr[j],Char_Size);
			x+=8;
		if(x>120){x=0;y+=2;}
			j++;
	}
}

void main(void)
{
    fd = open("/dev/I2C2_oled", O_RDWR);
    if(fd < 0)
    {
		printf("open file : %s failed !\n", argv[0]);
		return -1;
	}
	
	if (ioctl(fd, IOCTL_CLEAN_SCREEN, 0) < 0) 
	{
        printf("ioctl  IOCTL_CLEAN_SCREEN failed\n");
    }

    OLED_ShowString(6,3,"0.96' OLED TEST",16);
    leep(2);
    /*关闭文件*/
    close(fd);
}

【调试】

1、加载完设备树,可以先检查设备树目录有没添加成功;

2、用i2cdetect -y -a 0检测设备有没连接成功

        当时我调试有个异常,说明书写着设备地址为0x78,但我一直找不到相应的设备,怀疑i2cdetect只支持最多显示0x77地址的所有设备,我强硬地往0x78上写入数据也提示写入失败;

后来我反复拔插设备,用i2cdetect 发现0x3c地址也跟随变化,我往0x3c写入数据也是正常,后来也可以正常显示

3、该应用程序太过粗糙,应该优化为ioctl通过结构体的方式,与驱动通信

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值