用户工具

站点工具


icore4tx_62
银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
V1.0 2020-11-09 gingko 初次建立









STM32CubeMX教程六十二——TOUCH实验

1.在主界面选择File–>New Project 或者直接点击ACCEE TO MCU SELECTOR 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置 在搜索栏的下面,提供的各种查找方式,可以选择芯片内核、型号等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。 3.配置RCC,使用外部时钟源 4.时基源选择SysTick 5.将PA10,PB7,PB8,PI1,PI2,PI3设置为GPIO_Output 6.引脚模式配置 7.配置定时器 定时器分配引脚如下所示 8.配置FMC 9.配置SDMMC 10.配置LTDC 11.配置CRC 12.配置FATFS 13.时钟源设置,选择外部高速时钟源,配置为最大主频。 14.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27 15.点击Code Generator,进行进一步配置

  • Copy all used libraries into the project folder
  • 将HAL库的所有.C和.H都复制到所建工程中
    • 优点:这样如果后续需要新增其他外设又可能不再用STM32CubeMX的时候便会很方便
    • 缺点:体积大,编译时间很长
  • Copy only the necessary library files
  • 只复制所需要的.C和.H(推荐)
    • 优点:体积相对小,编译时间短,并且工程可复制拷贝
    • 缺点:新增外设时需要重新用STM32CubeMX导入
  • Add necessary library files as reference in the toolchain project configuration file
  • 不复制文件,直接从软件包存放位置导入.C和.H
    • 优点:体积小,比较节约硬盘空间
    • 缺点:复制到其他电脑上或者软件包位置改变,就需要修改相对应的路径
  • 自行选择方式即可

16.然后点击GENERATE CODE 创建工程

创建成功,打开工程。


实验六十二:TOUCH实验——电阻触摸

一、 实验目的与意义

  1. 了解STM32 LTDC结构
  2. 了解STM32 LTDC特征
  3. 掌握LTDC液晶屏的使用方法
  4. 掌握LTDC应用之LCD电容触摸芯片NS2009的使用方法
  5. 掌握KEIL MDK 集成开发环境使用方法

二、 实验设备及平台

  1. iCore4T 双核心板
  2. 转接板和40P的FPC连接线。
  3. iCore4T 扩展底板
  4. iCore 4.3寸液晶屏底板
  5. Blaster(或相同功能)仿真器
  6. Micro USB线缆
  7. Keil MDK 开发平台
  8. STM32CubeMX开发平台
  9. 装有WIN XP(及更高版本)系统的计算机

三、 实验原理

电阻式触摸屏检测原理

  • 电阻式的触摸屏结构见下图。它主要由表面硬涂层、两个ITO层、间隔点以及玻璃底层构成,这些结构层都是透明的,整个触摸屏覆盖在液晶面板上,透过触摸屏可看到液晶面板。表面涂层起到保护作用,玻璃底层起承载的作用,而两个ITO层是触摸屏的关键结构,它们是涂有铟锡金属氧化物的导电层。两个ITO层之间使用间隔点使两层分开,当触摸屏表面受到压力时,表面弯曲使得上层ITO与下层ITO接触,在触点处连通电路。

  • 两个ITO涂层的两端分别引出X-、X+、Y-、Y+四个电极,见下图,这是电阻屏最常见的四线结构,通过这些电极,外部电路向这两个涂层可以施加匀强电场或检测电压。

  • 当触摸屏被按下时,两个ITO层相互接触,从触点处把ITO层分为两个电阻,且由于ITO层均匀导电,两个电阻的大小与触点离两电极的距离成比例关系,利用这个特性,可通过以下过程来检测坐标,这也正是电阻触摸屏名称的由来,如下图。

  1. 计算X坐标时,在X+电极施加驱动电压Vref,X-极接地,所以X+与X-处形成了匀强电场,而触点处的电压通过Y+电极采集得到,由于ITO层均匀导电,触点电压与Vref之比等于触点X坐标与屏宽度之比,从而:

  1. 算Y坐标时,在Y+电极施加驱动电压Vref,Y-极接地,所以Y+与Y-处形成了匀强电场,而触点处的电压通过X+电极采集得到,由于ITO层均匀导电,触点电压与Vref之比等于触点Y坐标与屏高度之比,从而:

  • 为了方便检测触摸的坐标,一些芯片厂商制作了电阻屏专用的控制芯片,控制上述采集过程、采集电压,外部微控制器直接与触摸控制芯片通讯直接获得触点的电压或坐标。

在本实验中,选用电阻触摸芯片NS2009来实现电阻触摸功能。以下为NS2009基础知识。

  • 1.电阻触摸芯片NS2009基本原理
    • NS2009是一种典型的逐次逼近型ADC(SAR ADC),包含采样/保持,模数转换,I2C数据输出功能。单电源供电,电源电压范围为2.0V~5.5V。通过控制寄存器的模拟输入(X,Y,Z)进入ADC,作为触摸屏应用,应将其配置为差分模式,以有效消除驱动器开关的寄生电阻和测量误差引起的外部干扰,并改善转换精度。
  • 2.模拟输入特性
    • MUX,ADC的模拟输入以及I2C接口电路如下图所示。

  • 控制字节顺序位C3,C2,C1,C0以及NS2009之间的配置关系如下表所示:

  • 3.差分模式
    • 当命令控制位C3为高电平时,NS2009处于X,Y,Z的测量模式,内部ADC参考电压源为差分模式,如下图所示。差分模式的优势:+REF和-REF直接输入YP,YN,可以消除由于开关导通电阻引起的测量误差。缺点是:无论是充裕的还是转换过程,驱动器都需要开启,相对于单端模式,功耗增加了。

  • 4.压力测量
    • NS2009可以测量触摸的压力,即Z方向的测量。通常,此类测量的性能要求不高,因此可以使用8位分辨率模式(但是,下面的计算是采用12位分辨率模式的)即可。有几种不同的方法可以实现压力测量。
    • 第一种方法需要知道X面板的电阻,X的测量位置,触摸屏面板在两个附加面板之间的测量值(Z1和Z2)。可以使用下列公式计算触摸电阻:

  • 第二种方法需要检测X和Y面板的面板电阻,X和Y位置以及Z1位置。可以使用下列公式计算触摸电阻:

  • 5.数字接口
    • NS2009的数据接口是I2C串行接口,满足I2C接口协议,可以实现标准模式(100K),快速模式(400K)或高速模式(3.4M),分为控制NS2009写入,读取两种命令格式 ,write命令用于输入地址和命令字节,以便在指定的NS2009配置和模式下工作,NS2009 read命令用于输出ADC转换的数据以获得与测量有关的信息。
    • 1)Write Command 写命令时序图如下

  • 地址字节的第一个字节:

  • 最低的R / W(bit0),0表示写入命令,1表示读取命令。
  • A0(Bit1)用于硬件地址的控制位,该位必须与8引脚电平一致,才能选中相应的NS2009。
  • 该软件的最高5位地址,必须输入一个固定的代码“10010”。
  • 在第二个字节全部被接收后,NS2009在第9个时钟周期发出响应信号ACK(0级),指示数据已经收到。
  • 命令字节的第二个字节:

  • C3,C2,C1,C0决定输入信号的NS2009配置以及相应的测量功能。PD1,PD0用于控制内部基准电压源和笔中断信号,如下图所示:

  • M—-模式选择位,用于设置ADC的分辨率。MODE = 0,ADC为12位模式; MODE = 1,ADC为8位模式。
  • X位(bit3和bit0)为预留位,一般设置为0。
  • 在第二个字节全部被接收后,NS2009将在第18个时钟周期发出响应信号ACK(0-电平),指示数据已收到。
  • 2)Read Command 读命令时序图如下

  • 读取命令包含3个字节,第一个字节是地址,类似于bit0为高的写命令;接下来的2个字节是NS2009中的12位(如果是8位模式,则仅为1字节数据),冗余4位为零。
  • NS2009收到地址数据的第一个字节后,在第9个时钟周期发出响应信号ACK(0电平),然后开始输出数据的第一个字节,主机收到数据的第一个字节后应发出响应主机ACK(0级),NS2009接收到掩码(masker ACK)后,ACK开始发送第二个字节的数据,主机接收到第二个字节的数据后,不用应答,此时,SDA将被拉高,也就是上图所示的掩码(masker ACK)信号。
  • 6.高速模式
    • 当主机发送的数据“00001XXX” 被NS2009接收到后,主机不需要等待响应,NS2009将进入高速模式(串行速率可以为3.4Mhz),直到主机发出停止信号。高速模式下,读/写命令格式与标准模式和速度模式相同,但无法发出STOP信号,否则高速模式将终止。
  • 7.数据格式
    • NS2009输出的数据格式是标准的二进制格式。下图给出了不同电压对应的理想编码。

  • 8.PENIRQ中断输出功能
    • PENIRQ可以通过PD0设置,PEN中断输出功能如下图所示。
    • 当PD0=0时,YN驱动打开,Y触摸屏面板连接到GND。PENIRQ 输出通过两个开关连接到XP。在待机模式下,当有触摸屏操作时,XP输入通过触摸屏下拉至GND,PENIRQ输出为低电平。当没有运动的触摸屏断开至GND时,PENIRQ为高输出。在X,Y和Z的测量过程中,PENIRQ输出为低电平;
    • PD0 = 1,禁止了PEN中断功能,无法监视触摸屏上的触摸运动。如果要重新启用中断功能PEN,需要控制PD0 = 0写入NS2009,如果最后一个控制字包含PD0 = 0,则该命令完成后将使能PEN中断输出。
    • 为避免误触发,建议处理器向NS2009发送命令时,屏蔽掉PENIRQ的中断。

四、 实验程序

1.主函数

int main(void)
{
    FATFS fatfs;
    HAL_Init();
    SystemClock_Config();
    i2c.initialize();
    axp152.initialize();
    axp152.set_dcdc1(3500);//[ARM & FPGA BK1/2/6 &OTHER]
    axp152.set_dcdc2(1200);//[FPGA INT & PLL D]
    axp152.set_aldo1(2500);//[FPGA PLL A]
    axp152.set_dcdc4(3300);//[POWER_OUTPUT]
    axp152.set_dcdc3(3300);//[FPGA BK4][Adjustable]
    axp152.set_aldo2(3300);//[FPGA BK3][Adjustable]
    axp152.set_dldo1(3300);//[FPGA BK7][Adjustable]
    axp152.set_dldo2(3300);//[FPGA BK5][Adjustable]
    MX_GPIO_Init();
    MX_CRC_Init();
    MX_LTDC_Init();
    MX_TIM12_Init();
    MX_SDMMC1_SD_Init();
    MX_FATFS_Init();
    MX_FMC_Init();	
    BSP_SDRAM_Init();
    lcd.initialize();
 
    GUI_Init();
    GUI_SetBkColor(GUI_BLACK);
    GUI_Clear();
    HAL_TIM_PWM_Start(&htim12,TIM_CHANNEL_1); 
    touch_type = gt911.initialize();//判断是否为电容触摸
    if(touch_type != TOUCH_CAP){
        touch_type = TOUCH_RES;
    }
    res = f_mount(&fatfs,"0:",1);
    if(res != RES_OK){
        GUI_SetColor(GUI_RED);
        GUI_SetFont(&GUI_FontFixedsys16);
        GUI_DispStringHCenterAt("f_mount Error!",480/2,272/2);		
        while(1){
        }
    }
    GUI_SetColor(GUI_BLUE);
    if(touch_type == TOUCH_RES){
	power_on.check_touch();
    }
    GUI_CURSOR_Show();
    frame.process();                  //界面处理
    LCD_ON;

2.进行GUI界面处理

static void frame_process(void)
{
	GUI_CURSOR_Show();
	WM_SetCreateFlags(WM_CF_MEMDEV);
        GUI_CreateDialogBox(_aDialogCreate,GUI_COUNTOF(_aDialogCreate), _cbDialog, WM_HBKWIN, 0, 0);
	while(1){
		if(systick._20ms_flag == 1){
			systick._20ms_flag = 0;
			if(touch_type == TOUCH_RES){
				ns2009.convert_pos();
			}else if(touch_type == TOUCH_CAP){
				gt911.scan(1);
			}
		}
		if(touch_flag == 1){
			touch_flag = 0;
			if(touch_type == TOUCH_RES){
				EDIT_SetValue(h_edit_0,pen.x0);
				EDIT_SetValue(h_edit_1,pen.y0);			
			}else{
				EDIT_SetValue(h_edit_0,gt911.x[0]);
				EDIT_SetValue(h_edit_1,gt911.y[0]);				
			}
		}
		GUI_Exec();
	}
}

3.convert_pos函数实现坐标变换

static int convert_pos(void)  //坐标变换
{
	int i;
	i = readxy(&pen.X,&pen.Y);
	if(i == 2) return 2;
	else if(i == 1){
		pen.x0 = 0;
		pen.y0 = 0;			
		return 1;
	}
	pen.x0 = (int)(rgb_cal.a[0] + ((rgb_cal.a[1] * pen.X +	rgb_cal.a[2]*pen.Y)/rgb_cal.a[6]));
	pen.y0 = (int)(rgb_cal.a[3] + ((rgb_cal.a[4] * pen.X +rgb_cal.a[5]*pen.Y)/rgb_cal.a[6]));
	if(pen.x0 > 0 && pen.x0 < 100 && pen.y0 > 240 && pen.y0 < 272){
		pen.x0 = 479;
		pen.y0 = 271;
	}return 0;

4.校正电阻屏系数

static int touch_adjust(void)
{
	int i;
	char  buf[100];
	do{
		GUI_SetBkColor(GUI_WHITE);
		GUI_Clear();
		get_sample(&rgb_cal,0,40,40);
		get_sample(&rgb_cal,1,RGB_XSIZE - 40,40);
		get_sample(&rgb_cal,2,RGB_XSIZE - 40,RGB_YSIZE - 40);
		get_sample(&rgb_cal,3,40,RGB_YSIZE - 40);		
		get_sample(&rgb_cal,4,RGB_XSIZE / 2,RGB_YSIZE / 2);
	}while(perform_calibration(&rgb_cal));
	for(i = 0; i < 7; i ++){
		sprintf(buf,"a[%d] : %d   \r\n",i,rgb_cal.a[i]);
		GUI_DispStringAt(buf,200,70 + 20 * i);
	}
	GUI_DispStringAt("calibration success",200,70 + 20 * i);
	for(i = 0; i < 30000000; i ++);
	GUI_Clear();
	return 0;
}

5.NS2009读XY坐标

static int   readxy(int *x, int *y) //读XY坐标
{
	int a[MAX_CNT_DATA],b[MAX_CNT_DATA];
	int i = 0,j = 0;
	unsigned short int datx[3];
	unsigned short int daty[3];
	unsigned short int temp_x,temp_y;
	unsigned int cnt = 0;
	*x = 0;
	*y = 0;
	memset(a,0,MAX_CNT_DATA);
	memset(b,0,MAX_CNT_DATA);
	for(i = 0; i < MAX_CNT_DATA; i ++){
			read_ads(&a[cnt],&b[cnt]);
			cnt ++;
			for(j = 0; j < 2100; j ++);
	}
	if(cnt < MAX_CNT_DATA - 5)return 3;
	qsort (a,MAX_CNT_DATA,sizeof(a[0]),acmp);	//快速排序,就是中值滤波
	qsort (b,MAX_CNT_DATA,sizeof(b[0]),acmp);
 
	datx[0] = a[MAX_CNT_DATA / 2 - 1];
	datx[1] = a[MAX_CNT_DATA / 2];
	datx[2] = a[MAX_CNT_DATA / 2 + 1];
 
	daty[0] = b[MAX_CNT_DATA / 2 - 1];
	daty[1] = b[MAX_CNT_DATA / 2];
	daty[2] = b[MAX_CNT_DATA / 2 + 1];
 
	if(
		abs(datx[0] - datx[1]) > 20 || 
		abs(datx[1] - datx[2]) > 20 ||
		abs(daty[0] - daty[1]) > 20 ||
		abs(daty[1] - daty[2]) > 20
		)return 2;
 	temp_x = (datx[0] + datx[1] + datx[2]) / 3;
 	temp_y = (daty[0] + daty[1] + daty[2]) / 3;
 
	*x = temp_x;
	*y = temp_y;
	return 0;
}

6.perform_calibration函数:使用五点校正法完成电阻触摸屏的校准

static int  perform_calibration(TOUCH_CALIBRATION_T *cal)
{
	int j;
	double n, x, y, x2, y2, xy, z, zx, zy;
	double det, a, b, c, e, f, i;
	float scaling = 32768.0;
// Get sums for matrix
	n = x = y = x2 = y2 = xy = 0;
	for(j = 0;j < 5;j++) {
		n += 1.0;
		x += (float)cal->x[j];
		y += (float)cal->y[j];
		x2 += (float)(cal->x[j]*cal->x[j]);
		y2 += (float)(cal->y[j]*cal->y[j]);
		xy += (float)(cal->x[j]*cal->y[j]);
	}
// Get determinant of matrix -- check if determinant is too small
	det = n*(x2*y2 - xy*xy) + x*(xy*y - x*y2) + y*(x*xy - y*x2);
	if(det < 0.1 && det > -0.1) {
		return 1;
	}
// Get elements of inverse matrix
	a = (x2*y2 - xy*xy)/det;
	b = (xy*y - x*y2)/det;
	c = (x*xy - y*x2)/det;
	e = (n*y2 - y*y)/det;
	f = (x*y - n*xy)/det;
	i = (n*x2 - x*x)/det;
// Get sums for x calibration
	z = zx = zy = 0;
	for(j=0;j<5;j++) {
		z += (float)cal->xfb[j];
		zx += (float)(cal->xfb[j]*cal->x[j]);
		zy += (float)(cal->xfb[j]*cal->y[j]);
	}
// Now multiply out to get the calibration for framebuffer x coord
	cal->a[0] = (int)(a*z + b*zx + c*zy);
	cal->a[1] = (int)((b*z + e*zx + f*zy)*(scaling));
	cal->a[2] = (int)((c*z + f*zx + i*zy)*(scaling));
// Get sums for y calibration
	z = zx = zy = 0;
	for(j=0;j<5;j++) {
z += (float)cal->yfb[j];
		zx += (float)(cal->yfb[j]*cal->x[j]);
		zy += (float)(cal->yfb[j]*cal->y[j]);
	}
// Now multiply out to get the calibration for framebuffer y coord
	cal->a[3] = (int)(a*z + b*zx + c*zy);
	cal->a[4] = (int)((b*z + e*zx + f*zy)*(scaling));
	cal->a[5] = (int)((c*z + f*zx + i*zy)*(scaling));
// If we got here, we're OK, so assign scaling to a[6] and return
	cal->a[6] = (int)scaling;
 
	for(j = 0; j < 5; j ++){
		x = 	cal->a[0] +
				((cal->a[1]*cal->x[j] + 
				cal->a[2]*cal->y[j] ) / cal->a[6]);		
		y = 	cal->a[3] +
				((cal->a[4]*cal->x[j] + 
				cal->a[5]*cal->y[j] ) / cal->a[6]);	
		if((fabs(x - cal->xfb[j]) > 10) || (fabs(y - cal->yfb[j]) > 10))
			return 1;
	}
	return 0;
}

五、 实验步骤

  1. 把仿真器与iCore4T的SWD调试口相连(直接相连或者通过转接器相连);
  2. 把iCore4T通过Micro USB线与计算机相连,为iCore4T供电;
  3. 将屏幕连接到4.3寸液晶屏底板上,并将液晶屏底板与iCore4T底板通过排线相连。
  4. 打开Keil MDK 开发环境,并打开本实验工程;
  5. 烧写程序到iCore4T上;
  6. 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。

六、 实验现象

触摸iCore4T(4.3寸TFT_LCD)电阻屏屏即显示当前触摸位置的坐标值。

icore4tx_62.txt · 最后更改: 2022/04/01 11:35 由 sean