基于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 interrupts
		case USCI_I2C_UCALIFG:   	break;         // Vector 2: ALIFG
		case USCI_I2C_UCNACKIFG:                	   // Vector 4: NACKIFG
			// NACK中断的处理
			UCB1CTLW0 |= UCTXSTP;           // pull up SCL
			break;
		case USCI_I2C_UCSTTIFG:  	break;         // Vector 6: STTIFG
		case USCI_I2C_UCSTPIFG:  	break;         // Vector 8: STPIFG
		case USCI_I2C_UCRXIFG3:  	break;         // Vector 10: RXIFG3
		case USCI_I2C_UCTXIFG3:  	break;         // Vector 12: TXIFG3
		case USCI_I2C_UCRXIFG2:  	break;         // Vector 14: RXIFG2
		case USCI_I2C_UCTXIFG2:  	break;         // Vector 16: TXIFG2
		case USCI_I2C_UCRXIFG1:  	break;         // Vector 18: RXIFG1
		case USCI_I2C_UCTXIFG1:  	break;         // Vector 20: TXIFG1
		case USCI_I2C_UCRXIFG0:                 	// Vector 22: RXIFG0
				// 接收完成中断的处理
				break;
		
		case USCI_I2C_UCTXIFG0:  				   // Vector 24: TXIFG0
				// 发送完成中断的处理
				break;
		case USCI_I2C_UCBCNTIFG:    break;    		// Vector 26: BCNTIFG
		case USCI_I2C_UCCLTOIFG: 	break;         // Vector 28: clock low timeout
		case USCI_I2C_UCBIT9IFG: 	break;         // Vector 30: 9th bit
		default: 					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.mfbz.cn/a/461141.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NCP1380BDR2G芯片中文资料规格书PDF数据手册引脚图图片参数功能价格

产品描述: NCP1380 是一款高性能器件,旨在为准谐振转换器供电。该控制器基于专属的谷锁闭系统,可以在功率负载变轻时进行切换并降低开关频率。这样将产生稳定的运行,即使在漏极-源极谷中总是触发的开关事件下也是如此。此系统可在…

关于数据文件上传到服务器的格式及上传实现的方法

文件上传的格式: 第一种:form-data格式的: let fm new FormData; fm.append(file,file) fm.append(filename, ) // 在请求体中进行添加请求头的信息 axios.post(https://127.0.0.1:8888/upload_single,fm,{ headers:{ …

SPI机制详解

SPI机制详解 什么是SPI机制? SPI:Service Provider Interface,中文直译:服务提供者接口,它通过在ClassPath路径下的META-INF/service文件夹中查找文件,并自动加载文件里所定义的类 在面向对象的设计原则…

踩坑(乱改配置,电脑都打不开,无奈暴力重装)文末有惊喜喔

总结我的论文项目的傻逼开端。(想的很好,思路也对,也做了,但是过程和结果好像并不是想象中那么容易) 故事讲解: 本来我只有一台电脑,这个电脑上面东西比较杂。学习资料呀,笔记呀&a…

【使用postman测试python接口】

打开python服务 设置postman如下,并发送: postman新建请求设置请求方式为post设置地址、raw、json方式、内容如下 结果: python如下: from flask import Flask, request, jsonifyapp Flask(__name__) # 实例化对象app.route…

JVM理解学习

参考视频 运行时数据区 JVM架构总览图 绿色的:方法区,堆,是所有线程共享的 黄色的: 虚拟机栈,本地方法栈,程序计数器,是线程私有的 程序计数器 程序计数器是一块较小的内存空间,物…

macbook安装brew出现错误解决办法

我是使用国内的源安装brew的时候: /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 我选择了 1: 就出错了,后来切换为2重新安装就好了 安装完成后提示获取不到系统版本: Failed to co…

Linux使用Docker部署Registry结合内网穿透实现公网远程拉取推送镜像

文章目录 1. 部署Docker Registry2. 本地测试推送镜像3. Linux 安装cpolar4. 配置Docker Registry公网访问地址5. 公网远程推送Docker Registry6. 固定Docker Registry公网地址 Docker Registry 本地镜像仓库,简单几步结合cpolar内网穿透工具实现远程pull or push (拉取和推送)…

Linux服务器(Debian系)包含UOS安全相关巡检shell脚本

#!/bin/bash# Define output file current_date$(date "%Y%m%d") # Gets the current date in YYYYMMDD format output_file"server_security_inspection_report_${current_date}.txt"# Empty the file initially echo > $output_file# 获取巡检时间 (…

Hadoop学习1:概述、单体搭建、伪分布式搭建

文章目录 概述基础知识Hadoop组件构成Hadoop配置文件 环境准备配置Hadoop配置下载配置环境变量 Hadoop运行模式Standalone Operation(本地)官方DemoWordCount单词统计Demo Pseudo-Distributed Operation(伪分布式模式)配置修改启动…

NCV4275CDT50RKG稳压器芯片中文资料规格书PDF数据手册引脚图图片价格功能

产品概述: NCV4275C 是一款低漏稳压器,可用于严酷汽车环境。它包括了较宽的运行温度范围和输出电压范围。输出调节为 5.0 V 或 3.3 V,额定输出电流为 450 mA。它还提供过电流保护、超温保护和可编程微处理器重置等多种功能。NCV4275C 采用 D…

Python Learn day05

Python Learn day05 本文主要讲解 继承、多态、定制类 继承和多态 什么是继承 当新类想要拥有现有类的功能结构,可以使用继承。继承的前提是新类 is a 现有类,即: 子类 is 父类 总是从某个类继承: class Myclass(object):pass…

Vue+OpenLayers7入门到实战:OpenLayers如何使用全屏控件,来实现地图容器的全屏和退出全屏功能

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7入门到实战 前言 本章介绍如何使用OpenLayers7在地图上使用地图全屏控件,来控制地图容器的全屏和退出全屏的功能。 注意:这里的全屏控件全屏指的是地图容器全屏,并非整个网页全屏。 网页整体全屏和指定网页节点全屏可以参…

十五、计算机视觉-sobel算子

文章目录 前言一、sobel算子的概念二、sobel算子的计算方式三、具体实现 前言 上节课我们学习了梯度的知识,学习了如何去计算梯度,本节我们继续学习计算梯度的方法,本节我们学习使用Sobel算子计算梯度,这与上节课梯度计算方法有所…

ARMv8架构特殊寄存器介绍-0

一、zero 寄存器 零寄存器用作源寄存器时读取零,用作目标寄存器时丢弃结果。您可以在大多数指令中使用零寄存器,但不是所有指令。二、sp寄存器 在ARMv8架构中,要使用的堆栈指针的选择在某种程度上与Exception级别。默认情况下,异…

大数据Doris(六十九):项目线上表现

文章目录 项目线上表现 一、查询响应时间

java学习之路-程序逻辑控制

目录 1.分支结构 1.1 if语句 栗子 判断奇数还是偶数 判断一个年份是否为闰年 1.2switch语句 栗子 2. 循环结构 2.1while 循环 栗子 2.2break和continue break continue 2.3for循环 基本语法 栗子 2.4 do while 循环 3.输入输出 3.1输出 3.2从键盘输入 栗子…

基于FPGA的光纤通信系统的实现的优化技巧与方法

逻辑电路基本框架回顾 跨时钟域同步技术 读写操作相互独立时钟域 A 和 B 不需要一致的相位由专门逻辑控制读写操作的切换 高速数据的乒乓缓存技术

SimpleDateFormat类 --java学习笔记

SimpleDateFormat 代表简单日期格式化,可以用来把日期对象、时间毫秒值格式化成我们想要的形式 常见构造器和方法: pattern 代表需要应用的时间格式—— 时间格式的常见符号: 时间格式的应用举例: import java.text.SimpleDate…

集合系列(二) -List接口详解

一、List简介 List 的数据结构就是一个序列,存储内容时直接在内存中开辟一块连续的空间,然后将空间地址与索引对应。 以下是List集合简易架构图 由图中的继承关系,可以知道,ArrayList、LinkedList、Vector、Stack都是List的四个…