正点原子STM32MP157Linux开发板mini驱动IIC oled 12864

1.首先测试oled的地址

一般来说12864oled驱动为ssd1306,查阅官方手册可知,
硬件7位地址为0x3c或者为0x3d 最后以为为读写位
(以前用stm32时候用使用的是模拟IIC,地址0x78,即:0x3c左移一位)
在这里插入图片描述
这里使用i2c工具来进行验证,笔者这里讲IIC_oled接上开发板的IIC5,然后使用一下命令进行验证

i2cdetect -y 0

如图所示

oled地址测试
确认地址为0x3c

2.在设备树中追加内容

&i2c5{
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&i2c5_pins_a>;
	pinctrl-1 = <&i2c5_pins_sleep_a>;
	status = "okay";
	i2c_oled@3c{
		compatible = "jack_G,i2c_oled";
		reg = <0x3c>;
	};
};

其中i2c5_pins_a和i2c5_pins_sleep_a在st官方定义的引脚中已经写好了
官方已定义

3.编写驱动文件

本次实验移植的是中景园电子的屏幕驱动,其中最核心的函数为

void OLED_WR_Byte(u8 dat,u8 mode)

这里使用正点原子使用i2c_transfer构造的函数方式来进行数据发送,方便于理解i2c通信过程(也可以使用i2c_master_send等函数)
函数修改如下

static s32 i2coled_write_regs(struct i2c_oled_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->client;
	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);
}
void OLED_WR_Byte(u8 dat,u8 mode)
{	
	if(mode)i2coled_write_regs(&i2coleddev,0x40,&dat,1);
  	else i2coled_write_regs(&i2coleddev,0x00,&dat,1);
}

效果:
在这里插入图片描述

完整代码如下:

#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 <linux/delay.h>
#include "oledfont.h"
#define I2C_OLED_CNT 1
#define I2C_OLED_NAME "i2c_oled"
#define OLED_CMD  0	
#define OLED_DATA 1	
struct i2c_oled_dev
{
	struct i2c_client *client; /* i2c 设备 */
	dev_t devid; /* 设备号 */
	struct cdev cdev; /* cdev */
	struct class *class; /* 类 */
	struct device *device; /* 设备 */
	// struct device_node *nd; /* 设备节点 */
};
static struct i2c_oled_dev i2coleddev;
static s32 i2coled_write_regs(struct i2c_oled_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->client;
	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);
}
void OLED_WR_Byte(u8 dat,u8 mode)
{	
	if(mode)i2coled_write_regs(&i2coleddev,0x40,&dat,1);
  	else i2coled_write_regs(&i2coleddev,0x00,&dat,1);
}
//坐标设置
void OLED_Set_Pos(u8 x, u8 y) 
{ 
	OLED_WR_Byte(0xb0+y,OLED_CMD);
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
	OLED_WR_Byte((x&0x0f),OLED_CMD);
}   	  

//开启OLED显示    
void OLED_Display_On(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
	OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}
//关闭OLED显示     
void OLED_Display_Off(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
	OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}		   			 
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!	  
void OLED_Clear(void)  
{  
	u8 i,n;		    
	for(i=0;i<8;i++)  
	{  
		OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
		OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
		OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   
		for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); 
	} //更新显示
}
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey)
{      	
	u8 c=0,sizex=sizey/2;
	u16 i=0,size1;
	if(sizey==8)size1=6;
	else size1=(sizey/8+((sizey%8)?1:0))*(sizey/2);
	c=chr-' ';//得到偏移后的值
	OLED_Set_Pos(x,y);
	for(i=0;i<size1;i++)
	{
		if(i%sizex==0&&sizey!=8) OLED_Set_Pos(x,y++);
		if(sizey==8) OLED_WR_Byte(asc2_0806[c][i],OLED_DATA);//6X8字号
		else if(sizey==16) OLED_WR_Byte(asc2_1608[c][i],OLED_DATA);//8x16字号
//		else if(sizey==xx) OLED_WR_Byte(asc2_xxxx[c][i],OLED_DATA);//用户添加字号
		else return;
	}
}
//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 sizey)
{
	u8 j=0;
	while (chr[j]!='\0')
	{	
		OLED_ShowChar(x,y,chr[j++],sizey);
		if(sizey==8)x+=6;
		else x+=sizey/2;
	}
}
static int i2c_oled_open(struct inode *inode, struct file *filp)
{
	OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
	OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
	OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
	OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
	OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//-not offset
	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
	OLED_WR_Byte(0x12,OLED_CMD);
	OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
	OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
	OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WR_Byte(0x02,OLED_CMD);//
	OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
	OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
	OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
	OLED_Clear();
	OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/ 
	// OLED_ShowString(0,0,"Hello World",16);	
	return 0;
}
ssize_t i2c_oled_write(struct file *file, const char __user *buf, size_t count, loff_t *position)
{
	u8 temp[128];
    OLED_Clear();
    copy_from_user(&temp, buf, count);
    //待完善,这里只用于测试
    OLED_ShowString(0, 0, temp,16);
    return 0;
}

static int i2c_oled_release(struct inode *inode, struct file *filp)
{
	// OLED_Clear();
	return 0;
}
static const struct file_operations i2c_oled_ops = {
	.owner = THIS_MODULE,
	.open = i2c_oled_open,
	.write = i2c_oled_write,
	.release = i2c_oled_release,
};
static int i2c_oled_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret=0;
	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	ret = alloc_chrdev_region(&i2coleddev.devid, 0, I2C_OLED_CNT, I2C_OLED_NAME);
	if(ret < 0) {
		pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", I2C_OLED_NAME, ret);
		return -ENOMEM;
	}
	/* 2、初始化cdev */
	i2coleddev.cdev.owner = THIS_MODULE;
	cdev_init(&i2coleddev.cdev, &i2c_oled_ops);
	/* 3、添加一个cdev */
	ret = cdev_add(&i2coleddev.cdev, i2coleddev.devid, I2C_OLED_CNT);
	if(ret < 0) {
		goto del_unregister;
	}
	/* 4、创建类 */
	i2coleddev.class = class_create(THIS_MODULE, I2C_OLED_NAME);
	if (IS_ERR(i2coleddev.class)) {
		goto del_cdev;
	}
	/* 5、创建设备 */
	i2coleddev.device = device_create(i2coleddev.class, NULL, i2coleddev.devid, NULL, I2C_OLED_NAME);
	if (IS_ERR(i2coleddev.device)) {
		goto destroy_class;
	}
	i2coleddev.client = client;
	return 0;
destroy_class:
	device_destroy(i2coleddev.class, i2coleddev.devid);
del_cdev:
	cdev_del(&i2coleddev.cdev);
del_unregister:
	unregister_chrdev_region(i2coleddev.devid, I2C_OLED_CNT);
	return  -EIO;
}
static int i2c_oled_remove(struct i2c_client *client)
{
	OLED_Clear();
	/* 注销字符设备驱动 */
	/* 1、删除cdev */
	cdev_del(&i2coleddev.cdev);
	/* 2、注销设备号 */
	unregister_chrdev_region(i2coleddev.devid, I2C_OLED_CNT); 
	/* 3、注销设备 */
	device_destroy(i2coleddev.class, i2coleddev.devid);
	/* 4、注销类 */
	class_destroy(i2coleddev.class); 
	return 0;
	return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id i2c_oled_id[] = {
	{"jack_G,i2c_oled", 0},  
	{}
};
/* 设备树匹配列表 */
static const struct of_device_id i2c_oled_of_match[] = {
	{ .compatible = "jack_G,i2c_oled" },
	{ /* Sentinel */ }
};
/* i2c驱动结构体 */	
static struct i2c_driver i2c_oled_driver = {
	 .probe = i2c_oled_probe,
	 .remove = i2c_oled_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "i2c_oled",
		   	.of_match_table = i2c_oled_of_match, 
		   },
	.id_table = i2c_oled_id,
};
static int __init i2coled_init(void)
{
	return i2c_add_driver(&i2c_oled_driver);
}
static void __exit i2coled_exit(void)
{
	i2c_del_driver(&i2c_oled_driver);
}
module_init(i2coled_init);
module_exit(i2coled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JACK");
MODULE_INFO(intree,"Y");

应用程序完整代码如下

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
    int fd, retvalue;
    char data[128];
    if(argc != 2){
        printf("Error Usage!\r\n");
        return -1;
    }
    fd = open("/dev/i2c_oled", O_RDWR);
    if(fd < 0){
        printf("file %s open failed!\r\n", argv[1]);
        return -1;
    }
    strncpy(data, argv[1], 128);    
    retvalue = write(fd, data,sizeof(data));  
    retvalue = close(fd); /* 关闭文件 */
    if(retvalue < 0){
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }    
    return 0;
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值