赞
踩
最近做下底层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>; }; };
#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; // command u8 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:0 gpio_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 一下就行了声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。