| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-11-09 | gingko | 初次建立 | \\ \\ \\ \\ \\ \\ \\ \\ ===== STM32CubeMX教程六十二——TOUCH实验 ===== 1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR {{ :icore4t:icore4t_cube_63_1.png?direct |}} 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置 在搜索栏的下面,提供的各种查找方式,可以选择芯片内核、型号等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。 {{ :icore4t:icore4t_cube_63_2.png?direct |}} 3.配置RCC,使用外部时钟源 {{ :icore4t:icore4t_cube_63_3.png?direct |}} 4.时基源选择SysTick {{ :icore4t:icore4t_cube_63_4.png?direct |}} 5.将PA10,PB7,PB8,PI1,PI2,PI3设置为GPIO_Output {{ :icore4t:icore4t_cube_63_5.png?direct |}} 6.引脚模式配置 {{ :icore4t:icore4t_cube_63_6.png?direct |}} 7.配置定时器 {{ :icore4t:icore4t_cube_63_7.png?direct |}} 定时器分配引脚如下所示 {{ :icore4t:icore4t_cube_63_8.png?direct |}} 8.配置FMC {{ :icore4t:icore4t_cube_63_9.png?direct |}} 9.配置SDMMC {{ :icore4t:icore4t_cube_63_10.png?direct |}} {{ :icore4t:icore4t_cube_63_11.png?direct |}} 10.配置LTDC {{ :icore4t:icore4t_cube_63_12.png?direct |}} {{ :icore4t:icore4t_cube_63_13.png?direct |}} 11.配置CRC {{ :icore4t:icore4t_cube_63_14.png?direct |}} 12.配置FATFS {{ :icore4t:icore4t_cube_63_15.png?direct |}} 13.时钟源设置,选择外部高速时钟源,配置为最大主频。 {{ :icore4t:icore4t_cube_63_16.png?direct |}} {{ :icore4t:icore4t_cube_63_17.png?direct |}} 14.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27 {{ :icore4t:icore4t_cube_63_18.png?direct |}} 15.点击Code Generator,进行进一步配置 {{ :icore4t:icore4t_cube_63_19.png?direct |}} * **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 创建工程 {{ :icore4t:icore4t_cube_63_20.png?direct |}} 创建成功,打开工程。 \\ \\ \\ ===== 实验六十二:TOUCH实验——电阻触摸 ===== ==== 一、 实验目的与意义 ==== - 了解STM32 LTDC结构 - 了解STM32 LTDC特征 - 掌握LTDC液晶屏的使用方法 - 掌握LTDC应用之LCD电容触摸芯片NS2009的使用方法 - 掌握KEIL MDK 集成开发环境使用方法 ==== 二、 实验设备及平台 ==== - iCore4T 双核心板 - 转接板和40P的FPC连接线。 - iCore4T 扩展底板 - iCore 4.3寸液晶屏底板 - Blaster(或相同功能)仿真器 - Micro USB线缆 - Keil MDK 开发平台 - STM32CubeMX开发平台 - 装有WIN XP(及更高版本)系统的计算机 ==== 三、 实验原理 ==== === 电阻式触摸屏检测原理 === * 电阻式的触摸屏结构见下图。它主要由表面硬涂层、两个ITO层、间隔点以及玻璃底层构成,这些结构层都是透明的,整个触摸屏覆盖在液晶面板上,透过触摸屏可看到液晶面板。表面涂层起到保护作用,玻璃底层起承载的作用,而两个ITO层是触摸屏的关键结构,它们是涂有铟锡金属氧化物的导电层。两个ITO层之间使用间隔点使两层分开,当触摸屏表面受到压力时,表面弯曲使得上层ITO与下层ITO接触,在触点处连通电路。 {{ :icore4t:icore4t_arm_hal_62_1.png?direct&200 |}} * 两个ITO涂层的两端分别引出X-、X+、Y-、Y+四个电极,见下图,这是电阻屏最常见的四线结构,通过这些电极,外部电路向这两个涂层可以施加匀强电场或检测电压。 {{ :icore4t:iCore4T_ARM_HAL_62_2.png?direct&600 |}} * 当触摸屏被按下时,两个ITO层相互接触,从触点处把ITO层分为两个电阻,且由于ITO层均匀导电,两个电阻的大小与触点离两电极的距离成比例关系,利用这个特性,可通过以下过程来检测坐标,这也正是电阻触摸屏名称的由来,如下图。 {{ :icore4t:iCore4T_ARM_HAL_62_3.png?direct&600 |}} - 计算X坐标时,在X+电极施加驱动电压Vref,X-极接地,所以X+与X-处形成了匀强电场,而触点处的电压通过Y+电极采集得到,由于ITO层均匀导电,触点电压与Vref之比等于触点X坐标与屏宽度之比,从而: {{ :icore4t:iCore4T_ARM_HAL_62_4.png?direct&200 |}} - 算Y坐标时,在Y+电极施加驱动电压Vref,Y-极接地,所以Y+与Y-处形成了匀强电场,而触点处的电压通过X+电极采集得到,由于ITO层均匀导电,触点电压与Vref之比等于触点Y坐标与屏高度之比,从而: {{ :icore4t:iCore4T_ARM_HAL_62_5.png?direct&200 |}} * 为了方便检测触摸的坐标,一些芯片厂商制作了电阻屏专用的控制芯片,控制上述采集过程、采集电压,外部微控制器直接与触摸控制芯片通讯直接获得触点的电压或坐标。 === 在本实验中,选用电阻触摸芯片NS2009来实现电阻触摸功能。以下为NS2009基础知识。 === * 1.电阻触摸芯片NS2009基本原理 * NS2009是一种典型的逐次逼近型ADC(SAR ADC),包含采样/保持,模数转换,I2C数据输出功能。单电源供电,电源电压范围为2.0V~5.5V。通过控制寄存器的模拟输入(X,Y,Z)进入ADC,作为触摸屏应用,应将其配置为差分模式,以有效消除驱动器开关的寄生电阻和测量误差引起的外部干扰,并改善转换精度。 * 2.模拟输入特性 * MUX,ADC的模拟输入以及I2C接口电路如下图所示。 {{ :icore4t:iCore4T_ARM_HAL_62_6.png?direct&800 |}} * 控制字节顺序位C3,C2,C1,C0以及NS2009之间的配置关系如下表所示: {{ :icore4t:iCore4T_ARM_HAL_62_7.png?direct&800 |}} * 3.差分模式 * 当命令控制位C3为高电平时,NS2009处于X,Y,Z的测量模式,内部ADC参考电压源为差分模式,如下图所示。差分模式的优势:+REF和-REF直接输入YP,YN,可以消除由于开关导通电阻引起的测量误差。缺点是:无论是充裕的还是转换过程,驱动器都需要开启,相对于单端模式,功耗增加了。 {{ :icore4t:iCore4T_ARM_HAL_62_8.png?direct&400 |}} * 4.压力测量 * NS2009可以测量触摸的压力,即Z方向的测量。通常,此类测量的性能要求不高,因此可以使用8位分辨率模式(但是,下面的计算是采用12位分辨率模式的)即可。有几种不同的方法可以实现压力测量。 * 第一种方法需要知道X面板的电阻,X的测量位置,触摸屏面板在两个附加面板之间的测量值(Z1和Z2)。可以使用下列公式计算触摸电阻: {{ :icore4t:iCore4T_ARM_HAL_62_9.png?direct&400 |}} * 第二种方法需要检测X和Y面板的面板电阻,X和Y位置以及Z1位置。可以使用下列公式计算触摸电阻: {{ :icore4t:iCore4T_ARM_HAL_62_10.png?direct&600 |}} {{ :icore4t:iCore4T_ARM_HAL_62_11.png?direct&600 |}} * 5.数字接口 * NS2009的数据接口是I2C串行接口,满足I2C接口协议,可以实现标准模式(100K),快速模式(400K)或高速模式(3.4M),分为控制NS2009写入,读取两种命令格式 ,write命令用于输入地址和命令字节,以便在指定的NS2009配置和模式下工作,NS2009 read命令用于输出ADC转换的数据以获得与测量有关的信息。 * 1)Write Command 写命令时序图如下 {{ :icore4t:iCore4T_ARM_HAL_62_12.png?direct&800 |}} * 地址字节的第一个字节: {{ :icore4t:iCore4T_ARM_HAL_62_13.png?direct&800 |}} * 最低的R / W(bit0),0表示写入命令,1表示读取命令。 * A0(Bit1)用于硬件地址的控制位,该位必须与8引脚电平一致,才能选中相应的NS2009。 * 该软件的最高5位地址,必须输入一个固定的代码“10010”。 * 在第二个字节全部被接收后,NS2009在第9个时钟周期发出响应信号ACK(0级),指示数据已经收到。 * 命令字节的第二个字节: {{ :icore4t:iCore4T_ARM_HAL_62_14.png?direct&800 |}} * C3,C2,C1,C0决定输入信号的NS2009配置以及相应的测量功能。PD1,PD0用于控制内部基准电压源和笔中断信号,如下图所示: {{ :icore4t:iCore4T_ARM_HAL_62_15.png?direct&800 |}} * M----模式选择位,用于设置ADC的分辨率。MODE = 0,ADC为12位模式; MODE = 1,ADC为8位模式。 * X位(bit3和bit0)为预留位,一般设置为0。 * 在第二个字节全部被接收后,NS2009将在第18个时钟周期发出响应信号ACK(0-电平),指示数据已收到。 * 2)Read Command 读命令时序图如下 {{ :icore4t:iCore4T_ARM_HAL_62_16.png?direct&800 |}} * 读取命令包含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输出的数据格式是标准的二进制格式。下图给出了不同电压对应的理想编码。 {{ :icore4t:iCore4T_ARM_HAL_62_17.png?direct&600 |}} * 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的中断。 {{ :icore4t:iCore4T_ARM_HAL_62_18.png?direct&600 |}} ==== 四、 实验程序 ==== === 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; } ==== 五、 实验步骤 ==== - 把仿真器与iCore4T的SWD调试口相连(直接相连或者通过转接器相连); - 把iCore4T通过Micro USB线与计算机相连,为iCore4T供电; - 将屏幕连接到4.3寸液晶屏底板上,并将液晶屏底板与iCore4T底板通过排线相连。 - 打开Keil MDK 开发环境,并打开本实验工程; - 烧写程序到iCore4T上; - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 ==== 六、 实验现象 ==== 触摸iCore4T(4.3寸TFT_LCD)电阻屏屏即显示当前触摸位置的坐标值。