正点原子Linux开发板 spi内核驱动 0.96寸ips屏教程
正点原子Linux开发板 spi内核驱动 0.96寸ips屏教程
- 首先选择模块
- 设备树配置
- spi驱动程序(用的spi_driver)
- app
最近做下底层SPI驱动来驱动IPS屏,本来想实现这种效果:
后面想着暂时不做frambuffer,用用spi巩固一下吧,于是就有了本次记录。
首先选择模块
去tb某店买了两块ips屏幕,一块0.96寸,一块1.3寸(当时 想着两个都用下,大的显示的多些)
显示屏如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200620172251526.JPG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1JheW1vbmRfOTdf,size_16,color_FFFFFF,t_70
拿到代码后,找的stm32的1.3寸屏程序作为参考,开始移植1.3寸屏到linux上去。折腾了一上午加半个下午,显示屏就是没亮?what 我把每个发过去的spi数据都打印出来,都是对的,我惊了。脑袋嗡嗡的了,罢了 换0.96寸的屏,重新配置初始化命令,刷新屏幕,亮了!我崩溃了,合着屏幕是坏的? 示例程序有问题? 后面检查发现spi->mode = SPI_MODE_2; /*MODE0,CPOL=0,CPHA=0 //出问题的地方!!!*/
spi模式需要改。
驱动1.3寸显示屏请移步如下链接:
1.3寸ips显示屏驱动地址
0.96寸的屏幕,以下是代码部分:
设备树配置
//功能引脚pinctrl_ipsRes: ipsRes { //屏幕复位u引脚fsl,pins = <MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0x10B0 /* LED0 */>;}; pinctrl_ipsDc: ipsDc { //屏幕dc(data or command)u引脚fsl,pins = <MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x10B0 >;};..............//附加节点&ecspi3 {fsl,spi-num-chipselects = <1>;cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>; /* cant't use cs-gpios! */pinctrl-names = "default";pinctrl-0 = <&pinctrl_ecspi3>;status = "okay";spidev: ipsTft@0 {compatible = "alientek,ipsTft";spi-max-frequency = <1000000000>; //频率无脑给的很大,但是刷新频率快不起来reg = <0>;};
};
spi驱动程序(用的spi_driver)
#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 <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/spi/spi.h>
#define ipsTft_CNT 1
#define ipsTft_NAME "ipsTft"
#define LCD_W 240
#define LCD_H 240
#define X_MAX_PIXEL 160
#define Y_MAX_PIXEL 80
#define RED 0xf800
#define GREEN 0x07e0
#define BLUE 0x001f
#define WHITE 0xffff
#define BLACK 0x0000
#define YELLOW 0xFFE0
#define GRAY0 0xEF7D //»ÒÉ«0 3165 00110 001011 00101
#define GRAY1 0x8410 //»ÒÉ«1 00000 000000 00000
#define GRAY2 0x4208 //»ÒÉ«2 1111111111011111
u8 buf[9] = {RED, GREEN, BLUE,WHITE, BLACK, YELLOW,GRAY0 ,GRAY1 ,GRAY2
};
struct ipsTft_dev {dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */struct device_node *nd; /* 设备节点 */int major; /* 主设备号 */void *private_data; /* 私有数据 */int dc_gpio; /* 片选所使用的GPIO编号 */int res_gpio; /*ips屏幕复位引脚*/int cs_gpio; /*磁力计的cs*/
};
static struct ipsTft_dev ipsTftdev;
void ipsTft_reginit(struct ipsTft_dev *dev);
// struct spi_lcd_cmd{// u8 reg_addr; // command// u8 len; //需要从spi_lcd_datas数组里发出数据字节数// int delay_ms; //此命令发送数据完成后,需延时多久// }cmds[] = {// {0x36, 1, 0},// {0x3A, 1, 0},// {0xB2, 5, 0},// {0xB7, 1, 0},// {0xBB, 1, 0},// {0xC0, 1, 0},// {0xC2, 1, 0},// {0xC3, 1, 0},// {0xC4, 1, 0},// {0xC6, 1, 0},// {0xD0, 2, 0},// {0xE0, 14, 0},// {0xE1, 14, 0},// {0x21, 0, 0},// {0x11, 0, 0},// {0x29, 0, 0},// };
// u8 spi_lcd_datas[] = {// 0x00, // 0x05,// 0x0c,0x0c,0x00,0x33,0x33,// 0x35,// 0x19,// 0x2c,// 0x01,// 0x12, // 0x20, // 0x0F, // 0xA4,0xA1,// 0xD0,0x04,0x0D,0x11,0x13,0x2B,0x3F,0x54,0x4C,0x18,0x0D,0x0B,0x1F,0x23,// 0xD0,0x04,0x0C,0x11,0x13,0x2C,0x3F,0x44,0x51,0x2F,0x1F,0x1F,0x20,0x23,// };
struct spi_lcd_cmd{u8 reg_addr; // commandu8 len; //需要从spi_lcd_datas数组里发出数据字节数int delay_ms; //此命令发送数据完成后,需延时多久
}cmds[] = {{0x11, 0, 120},{0x21, 0, 0},{0x21, 0, 0},{0xB1, 3, 0},{0xB2, 3, 0},{0xB3, 6, 0},{0xB4, 1, 0},{0xC0, 3, 0},{0xC1, 1, 0},{0xC2, 2, 0},{0xC3, 2, 0},{0xC4, 2, 0},{0xC5, 1, 0},{0xE0, 16, 0},{0xE1, 16, 0},{0x3A, 1, 0},{0x36, 1, 0},{0x29, 0, 0},
};
u8 spi_lcd_datas[] = {0x05,0x3A,0x3A,0x05,0x3A,0x3A,0x05,0x3A,0x3A,0x05,0x3A,0x3A,0x03,0x62,0x02,0x04,0xC0,0x0D,0x00,0x8D,0x6A,0x8D,0xEE,0x0E,0x10,0x0E,0x02,0x03,0x0E,0x07,0x02,0x07,0x0A,0x12,0x27,0x37,0x00,0x0D,0x0E,0x10,0x10,0x0E,0x03,0x03,0x0F,0x06,0x02,0x08,0x0A,0x13,0x26,0x36,0x00,0x0D,0x0E,0x10,0x05, 0xA8,
};
/** @description : 向ipsTft多个寄存器写入数据* @param - dev: ipsTft设备* @param - reg: 要写入的寄存器首地址* @param - val: 要写入的数据缓冲区* @param - len: 要写入的数据长度* @return : 操作结果*/
static s32 ipsTft_write_regs(struct ipsTft_dev *dev,u8 *buf, u8 len)
{int ret;struct spi_message m;struct spi_transfer *t;struct spi_device *spi = (struct spi_device *)dev->private_data;t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); /* 申请内存 *//* 发送要写入的数据 */t->tx_buf = buf; /* 要写入的数据 */t->len = len; /* 写入的字节数 */spi_message_init(&m); /* 初始化spi_message */spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */ret = spi_sync(spi, &m); /* 同步发送 */kfree(t); /* 释放内存 */return ret;
}
/** @description : 向ipsTft指定寄存器写入指定的值,写一个寄存器* @param - dev: ipsTft设备* @param - reg: 要写的寄存器* @param - data: 要写入的值* @return : 无*/
static void ipsTft_write_onereg(struct ipsTft_dev *dev, u8 buf)
{ipsTft_write_regs(dev,&buf, 1);//spi_write(dev,&buf, 1);
}
/*funciton: 写一个命令
*/
void write_command(struct ipsTft_dev *dev, u8 cmd)
{// dc , command:0gpio_set_value(dev->dc_gpio, 0); ipsTft_write_onereg(dev,cmd);
}
/*funciton: 写一个数据
*/
void write_data(struct ipsTft_dev *dev, u8 data)
{gpio_set_value(dev->dc_gpio, 1);ipsTft_write_onereg(dev,data);
}
/*funciton: 写一个数据
*/
static void write_datas(struct ipsTft_dev *dev, int data,int len)
{gpio_set_value(dev->dc_gpio, 1);ipsTft_write_regs(dev,(u8 *)&data,len);
}
/** @description : 打开设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件,file结构体有个叫做pr似有ate_data的成员变量* 一般在open的时候将private_data似有向设备结构体。* @return : 0 成功;其他 失败*/
static int ipsTft_open(struct inode *inode, struct file *filp)
{filp->private_data = &ipsTftdev; /* 设置私有数据 */ipsTft_reginit(&ipsTftdev);return 0;
}
/** @description : 关闭/释放设备* @param - filp : 要关闭的设备文件(文件描述符)* @return : 0 成功;其他 失败*/
static int ipsTft_release(struct inode *inode, struct file *filp)
{return 0;
}
/* ipsTft操作函数 */
static const struct file_operations ipsTft_ops = {.owner = THIS_MODULE,.open = ipsTft_open,.release = ipsTft_release,
};
void Address_set(struct ipsTft_dev *dev,unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2)
{ write_command(dev,0x2a);write_data(dev,x1>>8);write_data(dev,x1);write_data(dev,x2>>8);write_data(dev,x2);write_command(dev,0x2b);write_data(dev,y1>>8);write_data(dev,y1);write_data(dev,y2>>8);write_data(dev,y2);write_command(dev,0x2C);
}
/*刷屏函数
*/
void LCD_Clear(struct ipsTft_dev *dev)
{u16 i,j; Address_set(dev,0,0,LCD_W-1,LCD_H-1);for(i=0;i<LCD_W;i++){for (j=0;j<LCD_H;j++){//write_datas(dev,0xF800,2); //全红 write_data(dev,0xF800>>8);write_data(dev,0xF800&0xFF);}}
}
/*************************************************0.96'*************************************************/
void Lcd_SetRegion(struct ipsTft_dev *dev,u16 x_start,u16 y_start,u16 x_end,u16 y_end)
{ write_command(dev,0x2a);write_data(dev,0x00);write_data(dev,x_start+1);write_data(dev,0x00);write_data(dev,x_end+1);write_command(dev,0x2b);write_data(dev,0x00);write_data(dev,y_start+0x1A);write_data(dev,0x00);write_data(dev,y_end+0x1A); write_command(dev,0x2c);
}
/*************************************************
0.96'显示屏填充函数
*************************************************/
void Lcd_Clear_small(struct ipsTft_dev *dev,u16 Color)
{ unsigned int i,m;Lcd_SetRegion(dev,0,0,X_MAX_PIXEL-1,Y_MAX_PIXEL-1);write_command(dev,0x2C);for(i=0;i<X_MAX_PIXEL;i++)for(m=0;m<Y_MAX_PIXEL;m++){ //LCD_WriteData_16Bit(Color);write_data(dev,Color>>8);write_data(dev,Color&0xFF);}
}
/** ipsTft内部寄存器初始化函数 * @param : 无* @return : 无*/
void ipsTft_reginit(struct ipsTft_dev *dev)
{int i, j, n;gpio_set_value(ipsTftdev.res_gpio, 0);mdelay(500);gpio_set_value(ipsTftdev.res_gpio, 1);mdelay(500);n = 0; // n用于记录数据数组spi_lcd_datas的位置//发命令,并发出命令所需的数据for (i = 0; i < ARRAY_SIZE(cmds); i++) //命令{write_command(dev, cmds[i].reg_addr);for (j = 0; j < cmds[i].len; j++) //发出命令后,需要发出的数据if(cmds[i].len!=0)write_data(dev, spi_lcd_datas[n++]);printk("the n is %d\n",n);if (cmds[i].delay_ms) //如有延时则延时mdelay(cmds[i].delay_ms);}n=0;while(n<9){Lcd_Clear_small(dev,*(buf+n));n++;mdelay(1000);}printk("ips init finish!\n");
}/** @description : spi驱动的probe函数,当驱动与* 设备匹配以后此函数就会执行* @param - client : spi设备* @param - id : spi设备ID* */
static int ipsTft_probe(struct spi_device *spi)
{int ret = 0;/* 1、构建设备号 */if (ipsTftdev.major) {ipsTftdev.devid = MKDEV(ipsTftdev.major, 0);register_chrdev_region(ipsTftdev.devid, ipsTft_CNT, ipsTft_NAME);} else {alloc_chrdev_region(&ipsTftdev.devid, 0, ipsTft_CNT, ipsTft_NAME);ipsTftdev.major = MAJOR(ipsTftdev.devid);}/* 2、注册设备 */cdev_init(&ipsTftdev.cdev, &ipsTft_ops);cdev_add(&ipsTftdev.cdev, ipsTftdev.devid, ipsTft_CNT);/* 3、创建类 */ipsTftdev.class = class_create(THIS_MODULE, ipsTft_NAME);if (IS_ERR(ipsTftdev.class)) {return PTR_ERR(ipsTftdev.class);}/* 4、创建设备 */ipsTftdev.device = device_create(ipsTftdev.class, NULL, ipsTftdev.devid, NULL, ipsTft_NAME);if (IS_ERR(ipsTftdev.device)) {return PTR_ERR(ipsTftdev.device);}/* 获取设备树中cs片选信号 */ipsTftdev.nd = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000");if(ipsTftdev.nd == NULL) {printk("ecspi3 node not find!\r\n");return -EINVAL;}/* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */ipsTftdev.cs_gpio = of_get_named_gpio(ipsTftdev.nd, "cs-gpio", 0);if(ipsTftdev.cs_gpio < 0) {printk("can't get cs-gpio");return -EINVAL;}ipsTftdev.nd = of_find_node_by_path("/ipsRes");if(ipsTftdev.nd == NULL) {printk("res-gpio node not find!\r\n");return -EINVAL;}ipsTftdev.res_gpio = of_get_named_gpio(ipsTftdev.nd, "res-gpio", 0);if(ipsTftdev.res_gpio < 0) {printk("can't get res-gpio");return -EINVAL;}ipsTftdev.nd = of_find_node_by_path("/ipsDc");if(ipsTftdev.nd == NULL) {printk("ipsDcgpio node not find!\r\n");return -EINVAL;}ipsTftdev.dc_gpio = of_get_named_gpio(ipsTftdev.nd, "dc-gpio", 0);if(ipsTftdev.dc_gpio < 0) {printk("can't get ipsDc-gpio");return -EINVAL;}/* 3、设置GPIO1_IO20为输出,并且输出高电平 */ret = gpio_direction_output(ipsTftdev.cs_gpio, 1);if(ret < 0) {printk("can't set cs gpio!\r\n");}ret = gpio_direction_output(ipsTftdev.res_gpio, 1);if(ret < 0) {printk("can't set res gpio!\r\n");}ret = gpio_direction_output(ipsTftdev.dc_gpio, 1);if(ret < 0) {printk("can't set dc gpio!\r\n");}/*初始化spi_device */spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/spi_setup(spi);ipsTftdev.private_data = spi; /* 设置私有数据 *//* 初始化ipsTft内部寄存器 */ipsTft_reginit(&ipsTftdev); return 0;
}
/** @description : spi驱动的remove函数,移除spi驱动的时候此函数会执行* @param - client : spi设备* @return : 0,成功;其他负值,失败*/
static int ipsTft_remove(struct spi_device *spi)
{/* 删除设备 */cdev_del(&ipsTftdev.cdev);unregister_chrdev_region(ipsTftdev.devid, ipsTft_CNT);/* 注销掉类和设备 */device_destroy(ipsTftdev.class, ipsTftdev.devid);class_destroy(ipsTftdev.class);return 0;
}
/* 传统匹配方式ID列表 */
static const struct spi_device_id ipsTft_id[] = {{"alientek,ipsTft", 0}, {}
};
/* 设备树匹配列表 */
static const struct of_device_id ipsTft_of_match[] = {{ .compatible = "alientek,ipsTft" },{ /* Sentinel */ }
};
/* SPI驱动结构体 */
static struct spi_driver ipsTft_driver = {.probe = ipsTft_probe,.remove = ipsTft_remove,.driver = {.owner = THIS_MODULE,.name = "ipsTft",.of_match_table = ipsTft_of_match, },.id_table = ipsTft_id,
};
/** @description : 驱动入口函数* @param : 无* @return : 无*/
static int __init ipsTft_init(void)
{return spi_register_driver(&ipsTft_driver);
}
/** @description : 驱动出口函数* @param : 无* @return : 无*/
static void __exit ipsTft_exit(void)
{spi_unregister_driver(&ipsTft_driver);
}
module_init(ipsTft_init);
module_exit(ipsTft_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("raymond");
insmode 一下就行了
正点原子Linux开发板 spi内核驱动 0.96寸ips屏教程相关推荐
- 基于正点原子Linux开发板(ALIENTEK I.MX6U ALPHA V2.2)的个人自学记录
基于正点原子Linux开发板(ALIENTEK I.MX6U ALPHA V2.2)的个人自学记录 硬件环境 正点原子Linux开发板(ALIENTEK I.MX6U ALPHA V2.2) 底板+核 ...
- 正点原子 linux 开发板学习 uboot 篇 一
源码选择 出厂可以直接使用 原子出厂源码, 出厂源码会随时修复bug或者添加新的驱动以兼容正点原子的其他模块 学习的时候 U-Boot 烧写与启动 uboot 启动位置根据拨码开关 烧写 通过.imx ...
- 正点原子Linux开发板——Qt串口上位机实验
前言: 最近在学习嵌入式qt开发,然后跟着教程编写了一个简单的串口上位机程序,在编写的时候还算比较顺利,但在调试的时候花了点功夫,折腾了一下午.最后还是理清了思路,解决了问题,特写此博客进行记录和总结 ...
- 解决正点原子Linux开发板配置静态IP重启后失效的问题
先修改vi /etc/network/interfaces,设置静态IP 保存 修改开机自启动文件 vi /etc/rc.local 在 exit0 之前添加这条命令: /etc/init.d/net ...
- STM32 I2C驱动0.96寸OLED屏
本文以STM32F103C8T6最小系统板为例 OLED屏为128*64像素 驱动芯片SSD1306 本文讲解标准库 硬件IIC和软件IIC 驱动OLED 硬件IIC ...
- STM32Mini基于SPI接口的0.96寸OLED屏数据显示
文章目录 一.实验资料准备 1.下载工程包 2.引脚接法 3.字模软件准备 4.了解SPI(串行外设接口) (1)SPI的定义 (2)SPI的连接方式 (3)SPI的通讯过程 5.了解OLED屏的滚屏 ...
- Linux开发环境配置--正点原子阿尔法开发板
Linux开发环境配置–正点原子阿尔法开发板 文章目录 Linux开发环境配置--正点原子阿尔法开发板 1.网络环境设置 1.1添加网络适配器 1.2虚拟网络编辑器设置 1.3Ubuntu和Windo ...
- Day1: 正点原子mini-linux 开发板 开机测试
Day1: 正点原子mini-linux 开发板 开机测试 参考文档 01[正点原子]I.MX6U用户快速体验V2.6.pdf 硬件原理图: 02.开发板原理图/IMX6ULL_MINI_V1.7(M ...
- rt smart操作系统在“正点原子-阿尔法”开发板开箱使用
1.准备 正点原子阿尔法linux开发板,SD卡,网络,USB线.由于正点原子的开发板和ART-PI-smart开发板使用的是一样的MPU,所以可以运行ART-PI-smart的程序. 下载ART-P ...
最新文章
- 从 AlphaGo 到具有人类智慧的 AI 究竟有多远?François Chollet 有了一些新想法
- jquery改变html元素的样式,给input标签赋值,onclick中文传参问题等
- String类中的equals方法与Object类中的equals方法的不同点
- 31 天重构学习笔记29. 去除中间人对象
- Java中main函数只能调用同类中的静态方法?
- CentOS6.5系统重启后宕机
- Python type函数和isinstance函数区别 - Python零基础入门教程
- html ul高度自适应,如何让div中的ul元素自适应
- 冷知识 —— 物种大交换
- 定时上传文件至ftp服务器,CuteFTP FTP文件的定时上传图文教程
- 【转】那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程
- echarts 图表不能占满全屏
- 国庆怎么玩?国庆去面试!
- mac 设置阿里企业邮箱
- 一名渗透工程师所必备的技能(一)
- python计算最大公约数函数_python如何求解两数的最大公约数
- 水深则流缓,人贵则语迟,有悟性的人请进
- 龙芯软件开发 10 --龙芯2E指令
- 公共场所安全蹭网andwifi热点软件构建安全网络!
- 解决问题:Pr文件导入器检测到的文件结构不一致已禁止读取和写入此文件的元数据无法将XMP数据写入输出文件
热门文章
- centOs6.5版Linux系统中搭建Samba服务(附搭建Samba设置及相关配置参考)
- Android Watchdog 狗子到底做了啥
- 静静观看 莆田系,南方科技大学 钉在耻辱柱上。
- 万能dialog弹窗。
- 通过https协议发送skype信息给朋友python
- rubygems_本周在我们的雷达上:RubyGems和爱Angular的原因
- 石墨烯+新能源:光伏领域应用潜力巨大
- Ubuntu18.04.6 LTS安装vnc(xfce桌面或GNOME原生桌面),及实现离线Ubuntu服务器安装软件
- Ubuntu 20.04 安装Mellanox RDMA网卡驱动与带宽/时延测试
- java 求1到100的和