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

2023-11-02 02:31

本文主要是介绍正点原子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 ONOLED_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 OFFOLED_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 panelOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_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 registerOLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current BrightnessOLED_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 displayOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 dutyOLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)OLED_WR_Byte(0x00,OLED_CMD);//-not offsetOLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequencyOLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/SecOLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge periodOLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 ClockOLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configurationOLED_WR_Byte(0x12,OLED_CMD);OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomhOLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect LevelOLED_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/disableOLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disableOLED_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;
}

这篇关于正点原子STM32MP157Linux开发板mini驱动IIC oled 12864的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/327482

相关文章

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

开发板NFS挂载文件目录

文章目录 序NFS1. 安装 NFS 服务器和客户端在服务器上(NFS 服务器端)在客户端上(NFS 客户端) 2. 配置 NFS 服务器创建共享目录编辑 `/etc/exports` 文件启动 NFS 服务 3. 在客户端挂载 NFS 共享创建挂载点挂载 NFS 共享验证挂载 4. 设置开机自动挂载5. 解决权限问题 序 本节主要实现虚拟机(服务器)与开发板(客户端)通过N

驱动(RK3588S)第七课时:单节点设备树

目录 需求一、设备树的概念1、设备树的后缀名:2、设备树的语法格式3、设备树的属性(重要)4、设备树格式举例 二、设备树所用函数1、如何在内核层种获取设备树节点:2、从设备树上获取 gpio 口的属性3、获取节点上的属性只针对于字符串属性的4、函数读取 np 结点中的 propname 属性的值,并将读取到的 u32 类型的值保存在 out_value 指向的内存中,函数的返回值表示读取到的

驱动安装注册表指令

HKCR: HKEY_CLASSES_ROOT HKCU: HKEY_CURRENT_USER HKLM: HKEY_LOCAL_MACHINE HKU: HEKY_USER HER: 相对根键

UMDF驱动安装

VS2013 + WDF8.1,UMDF驱动选择User Mode Driver,不要选User Mode Driver 2.0,否则Win7安装有问题,如图 另外,在驱动安装时不要忘记WUDFUpdate_<主版本号><次版本号>.dll文件,具体文件名在INF中查找。此文件可在WDF的安装目录中找到。注意:在WDF的安装目录中会有3个WUDFUpdate_xxx.dll文件,x86,x6

安卓开发板_联发科MTK开发评估套件串口调试

串口调试 如果正在进行lk(little kernel ) 或内核开发,USB 串口适配器( USB 转串口 TTL 适配器的简称)对于检查系统启动日志非常有用,特别是在没有图形桌面显示的情况下。 1.选购适配器 常用的许多 USB 转串口的适配器,按芯片来分,有以下几种: CH340PL2303CP2104FT232 一般来说,采用 CH340 芯片的适配器,性能比较稳定,价

电脑驱动分类

电脑驱动程序(驱动程序)是操作系统与硬件设备之间的桥梁,用于使操作系统能够识别并与硬件设备进行通信。以下是常见的驱动分类: 1. 设备驱动程序 显示驱动程序:控制显卡和显示器的显示功能,负责图形渲染和屏幕显示。 示例:NVIDIA、AMD 显示驱动程序。打印机驱动程序:允许操作系统与打印机通信,控制打印任务。 示例:HP、Canon 打印机驱动程序。声卡驱动程序:管理音频输入和输出,与声卡硬件

麒麟系统安装GPU驱动

1.nvidia 1.1显卡驱动 本机显卡型号:nvidia rtx 3090 1.1.1下载驱动 打开 https://www.nvidia.cn/geforce/drivers/ 也可以直接使用下面这个地址下载 https://www.nvidia.com/download/driverResults.aspx/205464/en-us/ 1.1.3安装驱动 右击,

windows10 卸载网络驱动以及重新安装

右键桌面此电脑的图标,点击管理,设备管理器—网络适配器,找到下图中的驱动(不同的系统或者显卡会导致网卡驱动名称与下图不一样,多为Realtek开头),右键选择卸载设备,然后重启电脑,系统会自动重新安装驱动 新电脑首次安装驱动: 根据主板厂家,比如华硕,进入华硕官网,点击服务支持,点击下载中心,选择型号,点击右侧驱动程序和工具软件,选择windows版本,下载相应的驱动,下载完之后在对应文件中找