IMX6ULL驱动OLED

先上结果
在这里插入图片描述

一、设备树 imx6ull-alientek-emmc.dts

在i2c1中追加oled节点。oled设备地址应为0x78 = 0111 1000 。
linux中i2c地址只有7位,应该为 011 1100 =0x3c 。 在i2c1中追加oled节点。

&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

	ap3216c@1e {
		compatible = "alientek,ap3216c";
		reg = < 0x1e >;
	};
	oled@3c {
		compatible = "bananapeelx,oled";
		reg = <0x3c>;
		status = "okay";  
	};
	codec: wm8960@1a {
		compatible = "wlf,wm8960";
		reg = <0x1a>;
		clocks = <&clks IMX6UL_CLK_SAI2>;
		clock-names = "mclk";
		wlf,shared-lrclk;
	};

};

采用的引脚是U4_Tx ,U4_Rx。

	pinctrl_i2c1: i2c1grp {
		fsl,pins = <
			MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
			MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
		>;
	};

二、核心代码

观察在STM32或者其他的单片机中oled的程序,我们发现都是调用以下两个函数来实现写数据。我们把HAL库库函数改成我们自己的发送函数即可。

void WriteCmd(unsigned char I2C_Command)//写命令
{
	HAL_I2C_Mem_Write(&hi2c2,OLED_ADDRESS,0x00,I2C_MEMADD_SIZE_8BIT,&I2C_Command,1,100);
}

void WriteDat(unsigned char I2C_Data)//写数据
{
	HAL_I2C_Mem_Write(&hi2c2,OLED_ADDRESS,0x40,I2C_MEMADD_SIZE_8BIT,&I2C_Data,1,100);
}

修改后的发送函数如下

void WriteCmd(unsigned char I2C_Command)//写命令
{	
	oled_write_reg( &oleddev, 0x00,  I2C_Command);
}
void WriteDat(unsigned char I2C_Data)//写数据
{
	oled_write_reg( &oleddev, 0x40,  I2C_Data);
}

最后在static int oled_probe(struct i2c_client *client, const struct i2c_device_id *id)函数调用我们的测试程序

	OLED_REGInit();
	mdelay(1000);
	OLED_ShowStr(30,2,"for long ",1);
	OLED_ShowStr(0,4,"made by banana-peel-x ",1);

三、程序验证

在这里插入图片描述
在这里插入图片描述

四、遗留问题

本次修改是基于设备树,但是在linux内核较低的版本中不支持设备树,因此我们下次去掉设备树,在 BSP 里面使用i2c_board_info 结构体来描述一个具体的 I2C 设备。

五、程序源代码

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "codetab.h"

#define OLED_ADDRESS	0x78 //通过调整0R电阻,屏可以0x78和0x7A两个地址 -- 默认0x78

void I2C_Configuration(void);
void I2C_WriteByte(uint8_t addr,uint8_t data);
void WriteCmd(unsigned char I2C_Command);
void WriteDat(unsigned char I2C_Data);
void OLED_REGInit(void);
void OLED_SetPos(unsigned char x, unsigned char y);
void OLED_Fill(unsigned char fill_Data);
void OLED_CLS(void);
void OLED_ON(void);
void OLED_OFF(void);
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize);
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N);
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);
/***************************************************************
文件名		: driver_oled.c
作者	  	: 徐建波
版本	   	: V1.0
描述	   	: OLED驱动程序
其他	   	: 无
***************************************************************/
#define OLED_CNT	1
#define OLED_NAME	"oled"
struct oled_dev {
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	struct device_node	*nd; /* 设备节点 */
	int major;			/* 主设备号 */
	void *private_data;	/* 私有数据 */
};

static struct oled_dev oleddev;
/*
 * @description	: 向oled多个寄存器写入数据
 * @param - dev:  oled设备
 * @param - reg:  要写入的寄存器首地址
 * @param - val:  要写入的数据缓冲区
 * @param - len:  要写入的数据长度
 * @return 	  :   操作结果
 */
static s32 oled_write_regs(struct oled_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	b[0] = reg;					/* 寄存器首地址 */
	memcpy(&b[1],buf,len);		/* 将要写入的数据拷贝到数组b里面 */
	msg.addr = client->addr;	/* ap3216c地址 */
	msg.flags = 0;				/* 标记为写数据 */
	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = len + 1;			/* 要写入的数据长度 */
	return i2c_transfer(client->adapter, &msg, 1);
}
/*
 * @description	: 向oled指定寄存器写入指定的值,写一个寄存器
 * @param - dev:  oled设备
 * @param - reg:  要写的寄存器
 * @param - data: 要写入的值
 * @return   :    无
 */
static void oled_write_reg(struct oled_dev *dev, u8 reg, u8 data)
{
	u8 buf = 0;
	buf = data;
	oled_write_regs(dev, reg, &buf, 1);
}
/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int oled_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &oleddev;
	return 0;
}
/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static int oled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[10];
	//u8 reg;
	//struct oled_dev *dev = (struct oled_dev *)filp->private_data;
	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}
	if(databuf[0]==0)
	{
		OLED_CLS();
	}
	else{
		OLED_ShowStr(30,4,databuf,2);
	}
	return 0;
}
/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int oled_release(struct inode *inode, struct file *filp)
{
	return 0;
}
/* oled操作函数 */
static const struct file_operations oled_ops = {
	.owner = THIS_MODULE,
	.open = oled_open,
	.write= oled_write,
	.release = oled_release,
};
 /*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0,成功;其他负值,失败
  */
static int oled_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	/* 1、构建设备号 */
	if (oleddev.major) {
		oleddev.devid = MKDEV(oleddev.major, 0);
		register_chrdev_region(oleddev.devid, OLED_CNT, OLED_NAME);
	} else {
		alloc_chrdev_region(&oleddev.devid, 0, OLED_CNT, OLED_NAME);
		oleddev.major = MAJOR(oleddev.devid);
	}
	/* 2、注册设备 */
	cdev_init(&oleddev.cdev, &oled_ops);
	cdev_add(&oleddev.cdev, oleddev.devid, OLED_CNT);
	/* 3、创建类 */
	oleddev.class = class_create(THIS_MODULE, OLED_NAME);
	if (IS_ERR(oleddev.class)) {
		return PTR_ERR(oleddev.class);
	}
	/* 4、创建设备 */
	oleddev.device = device_create(oleddev.class, NULL, oleddev.devid, NULL, OLED_NAME);
	if (IS_ERR(oleddev.device)) {
		return PTR_ERR(oleddev.device);
	}
	oleddev.private_data = client;
	OLED_REGInit();
	mdelay(1000);
	OLED_ShowStr(30,2,"for long ",1);
	OLED_ShowStr(0,4,"made by banana-peel-x ",1);
	return 0;
}
/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client 	: i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int oled_remove(struct i2c_client *client)
{
	/* 删除设备 */
	cdev_del(&oleddev.cdev);
	unregister_chrdev_region(oleddev.devid, OLED_CNT);
	/* 注销掉类和设备 */
	device_destroy(oleddev.class, oleddev.devid);
	class_destroy(oleddev.class);
	return 0;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id oled_id[] = {
	{"bananapeelx,oled", 0},  
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id oled_of_match[] = {
	{ .compatible = "bananapeelx,oled" },
	{ /* Sentinel */ }
};

/* i2c驱动结构体 */	
static struct i2c_driver oled_driver = {
	.probe = oled_probe,
	.remove = oled_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "oled",
		   	.of_match_table = oled_of_match, 
		   },
	.id_table = oled_id,
};		   

void WriteCmd(unsigned char I2C_Command)//写命令
{	
	oled_write_reg( &oleddev, 0x00,  I2C_Command);
}

void WriteDat(unsigned char I2C_Data)//写数据
{
	oled_write_reg( &oleddev, 0x40,  I2C_Data);
}

void OLED_REGInit(void)
{
	mdelay(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
	printk("oled reginit................\r\n");
}

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);
}
//--------------------------------------------------------------
// Prototype      : void OLED_ON(void)
// Calls          : 
// Parameters     : none
// Description    : 将OLED从休眠中唤醒
//--------------------------------------------------------------
void OLED_ON(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X14);  //开启电荷泵
	WriteCmd(0XAF);  //OLED唤醒
}
//--------------------------------------------------------------
// Prototype      : void OLED_OFF(void)
// Calls          : 
// Parameters     : none
// Description    : 让OLED休眠 -- 休眠模式下,OLED功耗不到10uA
//--------------------------------------------------------------
void OLED_OFF(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X10);  //关闭电荷泵
	WriteCmd(0XAE);  //OLED休眠
}
//--------------------------------------------------------------
// Prototype      : void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
// Calls          : 
// Parmeters     : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
// Description    : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
//--------------------------------------------------------------
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;
	}
}
//--------------------------------------------------------------
// Prototype      : void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
// Calls          : 
// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在codetab.h中的索引
// Description    : 显示codetab.h中的汉字,16*16点阵
//--------------------------------------------------------------
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;
	}
}
//--------------------------------------------------------------
// Prototype      : void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);
// Calls          : 
// Parameters     : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
// Description    : 显示BMP位图
//--------------------------------------------------------------
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++]);
		}
	}
}
/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init oled_init(void)
{
	int ret = 0;
	ret = i2c_add_driver(&oled_driver);
	return ret;
}
/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit oled_exit(void)
{
	i2c_del_driver(&oled_driver);
}
module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bananapeelx");

六、总结

庠序宏开千秋业!

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_45281309

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值