基于GT911触控IC的电容屏在MSP430上的驱动

背景

最近参加公司一个电池测试仪的项目,负责电容屏驱动开发,电容屏的触控IC是汇顶科技的GT911,电容屏的总线接口是I2C
因为项目沟通方面的失误,本应接到主控芯片的电容屏,被连到了MSP430这款负责供电管理的MCU,领导说等PCB改版太慢了,让我就在MSP430上开发驱动,于是电容屏接入系统的方式就变成了这样:
电容屏连接到系统的方式
本来写一份驱动就行,现在得MSP430和主机各写一部分,而且两边还要加一些SPI总线处理。

MSP430的I2C驱动调试

首先要打通MSP430跟电容屏之间的I2C通信。

I2C控制器的初始化

分配I2C控制器对应的GPIO管脚

需要配置PxSEL0和PxSEL1寄存器,二者的相同位置的一对bit可以确定一个GPIO管脚是复用成4种功能的哪一种,我们根据datasheet选择合适的值,就能将其配置成I2C的SCL,同理SDA要用另外一对bit
根据PxSEL配置管脚功能

配置I2C工作模式和波特率

这需要I2C控制器在复位状态下才能进行,因此需要先设置UCBxCTLW0的UCSWRST位,配置完毕后再清零相应bit位。

工作模式需要先设置UCBxCTLW0的UCMODE_3位,表示该USCI_B模块工作在I2C模式下,再设置UCMST位,表示I2C扮演Master角色。

波特率需要先设置UCBxCTLW0的UCSSEL__SMCLK位,表示时钟源选择SMCLK,再将UCB1BRW寄存器的值配成20,表示将SMCLK(我的是8M)分频20倍,就得到了400K,是GT911能支持的最高时钟频率。

配置I2C接收完成、发送完成、收发NACK中断

因为承载I2C功能的是USCI_B模块,因此中断向量要这样写

#pragma vector = USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
{switch(__even_in_range(UCB1IV, USCI_I2C_UCBIT9IFG)){case USCI_NONE:          		break;         // Vector 0: No interruptscase USCI_I2C_UCALIFG:   	break;         // Vector 2: ALIFGcase USCI_I2C_UCNACKIFG:                	   // Vector 4: NACKIFG// NACK中断的处理UCB1CTLW0 |= UCTXSTP;           // pull up SCLbreak;case USCI_I2C_UCSTTIFG:  	break;         // Vector 6: STTIFGcase USCI_I2C_UCSTPIFG:  	break;         // Vector 8: STPIFGcase USCI_I2C_UCRXIFG3:  	break;         // Vector 10: RXIFG3case USCI_I2C_UCTXIFG3:  	break;         // Vector 12: TXIFG3case USCI_I2C_UCRXIFG2:  	break;         // Vector 14: RXIFG2case USCI_I2C_UCTXIFG2:  	break;         // Vector 16: TXIFG2case USCI_I2C_UCRXIFG1:  	break;         // Vector 18: RXIFG1case USCI_I2C_UCTXIFG1:  	break;         // Vector 20: TXIFG1case USCI_I2C_UCRXIFG0:                 	// Vector 22: RXIFG0// 接收完成中断的处理break;case USCI_I2C_UCTXIFG0:  				   // Vector 24: TXIFG0// 发送完成中断的处理break;case USCI_I2C_UCBCNTIFG:    break;    		// Vector 26: BCNTIFGcase USCI_I2C_UCCLTOIFG: 	break;         // Vector 28: clock low timeoutcase USCI_I2C_UCBIT9IFG: 	break;         // Vector 30: 9th bitdefault: 					break;}
}

注意,NACK中断一定要使能,且在中断里发送stop condition,这会让SCL信号重新拉高,不这样做的话,一旦I2C地址输错,就会出现很诡异的现象:SCL一直为低。

其他两个中断在下面讲。

I2C控制器的发送、接收流程

I2C发送流程

主循环的处理流程
  1. 记录tx_buf、tx_buf_len、tx_buf_offset到全局变量
  2. 将slave的I2C地址写入UCBxI2CSA寄存器
  3. 设置UCBxCTLW0的UCTR位,表示自己扮演Transmitter,同时设置UCTXSTT位,在线路上生成start condition
  4. 等待tx_buf_offset == tx_buf_len
发送完成中断的处理流程

第一次I2C发送完成中断貌似是由I2C控制器发送完slave地址后触发的,后面的中断都是buf内第tx_buf_offset个字节发送完毕触发,不过中断里面的流程是一样的:

  1. tx_buf_offset是否等于tx_buf_len
  2. 是,说明buf发送完毕,设置UCBxCTLW0的UCTXSTP,发送stop condition,清零UCBxIFG寄存器的UCTXIFG位,主循环稍后会退出
  3. 否,说明buf还未发完,将第tx_buf_offset个字节送入UCBxTXBUF寄存器,该寄存器的内容会被发送到线路,然后tx_buf_offset++

I2C接收流程

  1. 记录rx_buf、rx_buf_len、rx_buf_offset到全局变量
  2. 将slave的I2C地址写入UCBxI2CSA寄存器
  3. 清零UCBxCTLW0的UCTR位,表示自己扮演Receiver,同时设置UCTXSTT位,在线路上生成start condition
  4. 等待rx_buf_offset == rx_buf_len
接收完成中断的处理流程

I2C控制器扮演receiver角色时,发送完slave地址貌似不会进入发送完成中断,当然也不会进入接收完成中断,所以就简单一些:

  1. rx_buf_offset是否等于rx_buf_len
  2. 是,说明buf接收完毕,设置UCBxCTLW0的UCTXSTP,发送stop condition,将UCBxRXBUF的内容存入rx_buf[rx_buf_offset],主循环稍后会退出
  3. 否,说明buf还未收完,将将UCBxRXBUF的内容存入rx_buf[rx_buf_offset],然后rx_buf_offset++

电容屏的MSP430驱动调试

电容屏的初始化

分配I2C之外的RST和INT管脚

I2C只是数据传输通道,但电容屏还需要RST(复位)管脚来获悉什么时候复位其内部状态,还需要INT(中断)管脚来主动告知MSP430触摸事件的发生,快来通过I2C读取触摸坐标吧

RST是输出管脚,且GT911要求默认为高电平,因此要先设置PxDIR的特定bit表示输出,再设置PxOUT的相应bit表示输出高电平。

INT按理说是输入管脚,但GT911还用它来配置slave地址,因此要先配置成输出,在复位一段约定的时间后,输出一个高/低的电平,让GT911知道自己的I2C地址是0x14还是0x5D,地址配好之后,再将该管脚配置成输入。

电容屏的复位流程

  1. 执行上面的管脚分配和初始化流程
  2. 延迟5ms后,拉低RST管脚,复位GT911
  3. 延迟20ms后,让INT管脚输出低电平,GT911从而知道自己的I2C地址是0x5D
  4. 延迟1ms后,拉高RST管脚
  5. 延迟10ms后,将RST管脚配置成输入,确保其不会无意中复位GT911
  6. 让INT管脚输出低电平
  7. 延迟50ms后,将INT管脚配置成输入

电容屏的初始化流程

电容屏的固件初始化
  1. 延迟5ms后,读取0x8140(CMD)寄存器获取GT911的chip PID
  2. 向CMD寄存器写入0x02复位GT911的固件
  3. 从0x8047(CFG_DATA)寄存器读取GT911的固件版本号
  4. 计算GT911的配置参数的校验和
  5. 将配置参数写到CFG_DATA起始的寄存器空间
  6. 将校验和写入0x80FF(CFG_CHECKSUM)寄存器
  7. 延迟1ms后,向CMD寄存器写入0x0,使得固件退出复位状态,进入坐标读取状态
  8. 向0x814E(COOR_ADDR)寄存器写0,清除上报的坐标信息
电容屏的中断初始化

GT911默认是下降沿触发,且是浮动输入

  1. 设置PxIES的相应bit,表示下降沿触发
  2. 清零PxREN的相应bit,表示浮动输入,不使能上拉or下拉电阻
  3. 设置PxIE相应bit,使能INT管脚的中断

电容屏的坐标读取流程

中断响应

为了最小化中断耗时,只在中断里更新一个触摸事件计数器,并清零中断,其他都在主循环里完成

#pragma vector = PORT2_VECTOR
__interrupt void GPIO_PORT2_ISR(void)
{switch(__even_in_range(P2IV, P2IV_P2IFG7)){case P2IV_NONE:     break;case P2IV_P2IFG0:   break;case P2IV_P2IFG1:	break;case P2IV_P2IFG2:  	break;case P2IV_P2IFG3:g_touch_event_cnt++;P2IFG &= ~TOUCH_INT;break;case P2IV_P2IFG4:  	break;case P2IV_P2IFG5:  	break;case P2IV_P2IFG6:  	break;case P2IV_P2IFG7:  	break;default:            break;}
}

坐标读取流程

  1. 检测当前触摸事件技术,为0则立即退出
  2. 读取COOR_ADDR寄存器,获得当前的触屏是否有坐标数据,有几个手指的坐标数据
  3. 如果COOR_ADDR寄存器的bit7为0,说明没数据,立即向COOR_ADDR写0,使得GT911恢复工作
  4. 再检查COOR_ADDR的bit[3:0],为0说明所有手指离开,如果不为0,说明有手指触摸
  5. 读取从0x814F(COOR_DATA)寄存器开始的8*N个手指的坐标数据
  6. 向COOR_ADDR写0,使得GT911恢复工作

8字节坐标数据的格式:

/* 读取触摸点坐标数据,从0x814F寄存器开始读取* 其中每一个触摸点使用8个寄存器来描述* 以第一个触摸点为例,各寄存器描述信息如下:* 0x814F: 触摸点id* 0x8150: 触摸点X轴坐标低位字节* 0x8151: 触摸点X轴坐标高位字节* 0x8152: 触摸点Y轴坐标低位字节* 0x8153: 触摸点Y轴坐标高位字节* 0x8154~0x8155: 触摸点的大小信息,我们不需要* 0x8156: 保留*/

主机侧的Linux接收流程

这块交给外包做了,我只做了些指导,略。

电容屏的数据上报到主机侧

使用SPI上报,因为之前的SPI口仅用于供电管理,因为可扩展性非常差,为了复用SPI通道及其通信代码,对相关数据结构和收发函数做了优化:

SPI数据结构的扩展

typedef struct
{uint8_t	cmd;			/*命令码控制字*/union {struct {uint8_t	dc:1;			/*适配器在位状态*/uint8_t	bat:	1;			/*电池在位状态*/uint8_t 	full: 1;			/*电池电量满状态*/uint8_t 	ad: 1;			/*电池电量数据模式*/uint8_t 	reserve_4: 1;		/*保留位4*/uint8_t 	reserve_5: 1;		/*保留位5*/uint8_t 	reserve_6: 1;		/*保留位6*/uint8_t 	pd: 1;     			/*操作电源*/uint8_t	battery_data;		/*电池电量数据*/};struct {uint8_t	state;uint8_t	x_l;uint8_t	x_h;uint8_t y_l;uint8_t y_h;uint8_t	rsv1;uint8_t	rsv2;};uint8_t data[7];};
} spi_pack;

为了最小化扩展带来的代码改动,用到了匿名联合体匿名结构体,这个在我之前的文章中有描述。

供电管理的SPI上报流程

uint8_t  power_report(uint8_t opt)
{spi_pack power_pack = {0};power_pack.cmd = SPI_CMD_WRITE;if(opt == POWER_DOWN){power_pack.pd = POWER_DOWN;} else {power_pack.pd = POWER_NORMAL;}DEBUG("spi-0:0x%02x, 1:0x%02x, 2:0x%02x\r\n",power_pack.cmd,power_pack.data[0],power_pack.data[1]);spi_sent(&power_pack, sizeof(power_pack));
}

触摸坐标的SPI上报流程

static int touch_report(uint8_t state, uint16_t x, uint16_t y)
{spi_pack touch_pack = {0};touch_pack.cmd = SPI_CMD_TOUCH;touch_pack.state = state;touch_pack.x_l = x & 0xff;touch_pack.x_h = (x >> 8) & 0xff;touch_pack.y_l = y & 0xff;touch_pack.y_h = (y >> 8) & 0xff;DEBUG("spi-touch: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\r\n",touch_pack.cmd, touch_pack.data[0], touch_pack.data[1],touch_pack.data[2], touch_pack.data[3], touch_pack.data[4]);spi_sent(&touch_pack, sizeof(touch_pack));return 0;
}

最终效果

在调通流程后,发现默认配置参数存在坐标x轴和y轴弄反、触摸灵敏度差等问题,反馈给FAE后,更新了一版配置参数,效果好多了。

F581触屏

总结

  1. 裸机开发在功能较多时非常不便,最好先移植个RTOS,磨刀不误砍柴工。
  2. 不要因为可以马上开始就选择步行,下地库取车能更快抵达目的地。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/544590.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

三.使用java的API文档

在Java中,API是指“应用程序接口”(Application Programming Interface)。Java API是Java编程语言中提供的类和接口的集合,用于开发各种类型的应用程序。类比C的STL(标准模板库)。 通俗理解就当做些封装好…

17.搜索二维矩阵Ⅱ

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1: 输入:matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,2…

【SQL Server】实验七 数据完整性

1 实验目的 掌握实体完整性、参照完整性和用户自定义完整性约束的创建方法。掌握完整性约束的运行检查机制。掌握参照完整性的级联删除和修改方法。掌握正确设计关系模式完整性约束的方法。 2 实验内容 2.1 掌握实体完整性约束的创建和使用方法 创建表时定义由一个属性组成…

BigDecimal保留两位小数失败问题

文章目录 背景问题解决如何测试代码 背景 测试时发现在线swagger测试会自动处理BigDecimal小数点后面的数字,就是有零的会都给你去掉,比如9.000与9.500到最后都会被swagger处理成9跟9.5。使用postman测是最准的,测出来的就是9.000跟9.500。 …

pytorch 入门基础知识一(Pytorch 01)

一 深度学习基础相关 深度学习三个主要的方向:计算机视觉,自然语言,语音识别。 机器学习核心组件:1 数据集(data),2 前向传播的model(net),3 目标函数(loss), 4 调整模型参数和优化函数的算法…

Vue3+TypeScript 学习回顾,温故而知新

文章简介: (1)简介: 在 Vue3 中编码规范如下: 编码语言: JavaScript代码风格: 组合式API选项式、API简写形式: setup语法糖 (2)复习内容: 1.核心: ref、reactive、computed、w…

阿里EMO模型:AI生成表情丰富的视频

引言 在数字多媒体的时代,人们对于互动性和个性化视频内容的需求不断增长。阿里巴巴的EMO(Emote Portrait Alive)模型,作为一项前沿的人工智能技术,正引领着这一领域的革新之路。 EMO模型概述 EMO模型是阿里巴巴智能计…

​​SQLiteC/C++接口详细介绍之sqlite3类(十)

返回目录:SQLite—免费开源数据库系列文章目录 上一篇:SQLiteC/C接口详细介绍之sqlite3类(九) 下一篇:​​SQLiteC/C接口详细介绍之sqlite3类(十一) 30.sqlite3_enable_load_extension&#x…

手机中的8款万能App推荐!

目录 1.全能AI工具箱——HuluAI 2.AI视频生成——巨日禄 3.全能办公套件——鲸鲮Office 4.视频音频转换器——VideotoMP3Converter 5.特效滤镜摄影——PicsArt 6.智能工具箱——SmartTools 7.手机视频编辑软件——KineMaster 8.安卓版万能文档阅读器——AllDocumentRea…

实现兼容性良好的前端页面开发

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

切面条-蓝桥杯?-Lua 中文代码解题第1题

切面条-蓝桥杯?-Lua 中文代码解题第1题 一根高筋拉面,中间切一刀,可以得到2根面条。 如果先对折1次,中间切一刀,可以得到3根面条。 如果连续对折2次,中间切一刀,可以得到5根面条。 那么&#xf…

sqlalb第二十五关通关笔记

知识点: or and # 被过滤了有回显可以用联合注入这里可以利用双写绕过(亲测有效,) oorranandd这样的话可以使用错误注入(不演示了,有兴趣可以试一下) 又是一个id输入 测试是什么类型的注入 构…