软件版本:VIVADO2017.4 操作系统:WIN10 64bit 硬件平台:适用米联客 ZYNQ系列开发板 米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!! 35.1 概述很多工程师都在ZYNQ上做过LINUX相关的应用和开发,在ZYNQ运行LINUX操作系统,可以实现非常细致的GUI图形界面。用户可以通过鼠标、键盘等外部设备与操作系统通过GUI界面进行人机交互,与平时使用电脑的体验相当。 但同时,也有很用户只涉及ZYNQ的裸机开发,那么在无操作系统支持的情况下,是否可以在裸机环境中构建一个GUI图形界面呢? 答案是肯定的。本例程通过利用开源GUI库μGUI,在ZYNQ裸机环境下创建一组简单的GUI界面,并通过外部液晶触摸屏实现与ZYNQ的人机交互。本例程所涉及的应用知识点如下:
本章提供两个测试工程,分别是LCD屏的RGB接口uGUI工程和LVDS接口的uGUI工程。用户可使用RGB或LVDS中的任意接口实现基于μGUI的触摸屏GUI界面设计的功能。 35.2 基本原理本例程通过开源的uGUI v0.3库设计了1个GUI界面,为该GUI界面设计了5个窗口,为每个窗口设计了标题、按键、文本、图片logo等元素,并给每个按键设定了相应的功能,如窗口间切换、LED灯控制、屏幕亮度调节等,并在1个窗口中设计了绘图功能。然后,将设计的GUI窗口通过触摸屏显示。用户通过按下触摸屏中所显示的窗口对应的按键位置,便可以实现该按键所设定的相应功能,从而实现人机交互。 35.3 LCD 触摸屏LCD触摸屏由LCD液晶屏和触摸屏两部分组成,其中LCD液晶屏用于画面显示,触摸屏用于实现触摸控制。本例程中所使用的是微雪公司的7寸LCD电容触摸屏,如下图所示。 35.3.1 液晶屏液晶屏为1024×600分辨率,这个分辨率不常用,在网上很难找到统一的标准。在该液晶屏HJ070NA-13A的手册中可以找到此液晶屏在该分辨率下的时序要求,如下图所示。 本例程采用了51.2MHz像素时钟所对应的参数设置。液晶屏的驱动方法与显示器类似,通过时钟,行、场同步信号,数据有效信号来完成,此处不作赘述。 要使液晶屏正常显示,背光源使能信号DISP要拉高,通过调节PWM的占空比可以改变背光源的亮度。该液晶屏的PWM为负极性,即低电平占空比越高,背光源越亮,PWM信号的频率范围为100Hz~200KHz。 35.3.2 触摸屏触摸屏为电容屏,采用了FT5206作为主控芯片,支持5点触控。触摸屏与ZYNQ的接口如下图所示。 35.3.3 触摸屏唤醒在触摸屏与ZYNQ的接口中,WAKE信号为低电平有效,ZYNQ通过拉低WAKE信号若干毫秒,再将其拉高来唤醒FT5206芯片,当触摸屏正常工作时WAKE信号应恒为高电平。如下图所示。 35.3.4 触摸中断INT信号也为低电平有效。当手指触摸电容屏时,INT信号会以固定频率(默认为60Hz)脉冲信号的形式输出,当手指离开电容屏,INT信号重新恢复高电平,如下图所示。 35.3.5 触摸信息获取每当INT变为低电平时,ZYNQ就立即通过I2C接口读出FT5206芯片内部记录的触摸坐标信息,完成一次触摸响应。 FT5206芯片I2C接口作为slave从设备,最高速率为400KHz,I2C地址为0x38。这里补充说明一点,关于I2C地址芯片datasheet中作了如下图所示的描述,笔者在搜集到的关于FT5206的资料中均未能找到关于I2CCON register的说明。后来,通过网络搜索发现使用过该系列芯片的记录中,所提到的I2C地址均为0x38,笔者通过尝试得到了验证。 FT5206芯片通过一系列寄存器记录与触摸相关的信息。相关寄存器如下图所示。 其中,TD_STATUS寄存器记录了触摸点数,如下图所示。 记录触摸坐标、触摸动作的寄存器如下图所示。 ZYNQ通过I2C读取这些寄存器值分为两个步骤。首先,发送需要连续读取的起始寄存器地址,如下图所示。 然后,连续读取若干个地址连续的寄存器值,如下图所示。 35.3.6 LCD触摸屏接口微雪公司的7寸LCD电容触摸屏具有两种接口,一种是24位RGB888接口,另外一种是单路8bit LVDS接口,触摸控制通过I2C实现。 LCD背面接口图: 这里需要注意:微雪公司的7寸LCD电容触摸屏的两种接口不能同时使用。默认状态使用RGB接口,如果要使用LVDS接口,需要调节电阻。 使用RGB接口:R24位置焊接10K电阻,R25不焊接元件(默认状态)。 使用LVDS接口:R25位置焊接10K电阻,R24不焊接元件。 35.3.6.1 RGB接口LCD触摸屏RGB接口定义如下图所示。其中,1~35为液晶屏接口,37~40为触摸屏接口。 35.3.6.2 LVDS接口LCD触摸屏LVDS接口(仅介绍使用的引脚)如下图所示。其中,12~15为触摸屏接口,其他为液晶屏接口。 35.4 μGUI概述μGUI是一个开源的GUI图形界面设计库,包含了窗口(window),按键(button),文本框(textbox),图片(image)等4种元素。用户可以根据它们所对应的API函数自由发挥,对这些元素的数量、颜色、大小、位置、功能等参数进行定义,实现简易的GUI界面设计。 μGUI具有一个特色,提供了专门的API函数来支持外部的二维坐标(X, Y)输入设备,例如,触摸屏。用户所创建的GUI界面可以通过这些二维坐标获取外界物体输入的交互信息(比如触摸具有某个特定功能的按键),以此为基础实现人机交互。 该库一共包含ugui.c和ugui.h两个文件。关于μGUI库更为详细的介绍以及相应API函数的使用可参考其使用手册。读者可以访问其所在网站www.embeddedlightning.com,可以下载源文件、使用手册以及example project。 35.4.1 μGUI库移植由于μGUI是一个跨平台(DSP、ARM、MCU、单片机等)的C语言库,在将μGUI的ugui.c和ugui.h文件移植到ZYNQ的ARM中,并通过SDK进行程序设计和编译之前,需要完成2个重要工作: 35.4.1.1 添加单像素点像素值设置函数1个GUI界面其实就是1幅图像,1幅图像由很多个像素点组成。要设计1个GUI界面,就需要设置其中每个像素点的像素值。简单的说,就是要给GUI界面对应图像所在存储区域的各个地址赋值。但针对不同的平台,不同的应用,GUI界面的存储方式会存在不同,因此像素值设置的方式会有区别。出于跨平台的目的,μGUI给出了这个函数的原型声明,提供了开放接口,由用户自行设计这个函数来完成GUI界面各像素点值的设置,μGUI中所有与界面设计相关的API函数都将调用这个函数。本例程在gui_window.c中所设计的函数如下: voidPixelSet(UG_S16 x, UG_S16 y, UG_COLOR c) { u32iPixelAddr; iPixelAddr = y * GUI_WIDTH + x; BufferPtr[0][iPixelAddr] = c; } 其中x, y对应像素点的二维坐标(X,Y),c为该像素点所需设置的像素值,位宽32bit。由于本例程中GUI界面位于DDR中连续的内存区域,通过GUI界面的指针BufferPtr [0]以及x, y便可计算出像素点的地址。 35.4.1.2 调整各种变量类型定义在不同的嵌入式平台中,对于各种变量类型的定义可能会存在区别,例如,int型变量在不同的平台中可能是64位、32位或者16位的。因此,需要在ugui.h中调整各变量类型的定义。ugui.h中默认的变量定义如下: typedefuint8_tUG_U8; typedefint8_tUG_S8; typedefuint16_tUG_U16; typedefint16_tUG_S16; typedefuint32_tUG_U32; typedefint32_tUG_S32; 上述变量定义与ZYNQ平台编译器一致,因此无需修改,只需在ugui.h文件中将#include "system.h"改为#include "stdint.h"即可。 35.4.1.3 GUI窗口窗口是μGUI中GUI界面必需的基本组成,不可缺少,没有窗口就无法创建GUI界面。而文本框、按键、图片等对象则是可选项,在GUI界面中可有可无。μGUI中的窗口包含了文本框、按键、图片3种基本对象,每个窗口都可以拥有若干个多种对象。简而言之,1个窗口可以包含若干个对象,而1个GUI界面可以包含若干个窗口,它们之间的关系如下图所示。 35.4.1.4 GUI界面刷新作为GUI界面,应该具有动态变化的特性。例如,不同窗口间切换、文本框内容变化、按键位置调整等。μGUI提供了一个函数UG_Update()来实现整个GUI界面的刷新。当用户通过μGUI库中某些API函数改变当前窗口中某些对象的特性后(例如,文字、颜色、大小),就必须尽快调用UG_Update()来确保这些更改被刷新到GUI界面上。若不调用UG_Update(),则GUI界面将永远不会发生任何改变。 因此,在使用μGUI库进行GUI界面设计时,为了保证其动态变化的特性,用户必须要周期性的调用UG_Update()函数,调用频率的高低可根据实际要求来确定。 35.4.1.5 人机交互GUI界面的动态变化特性,其中很大一部分都源自于人机交互,上面提到了μGUI库支持外部的二维坐标(X, Y)输入设备,由外部输入的这些二维坐标就是GUI界面所需要的人机交互信息。例如,当设备与触摸屏连接,用户触碰了触摸屏的某个区域,触摸屏将这个区域的中心二维坐标反馈给设备,设备再将坐标输入GUI界面。 那如何将人机交互与GUI界面的动态特性相关联呢?μGUI库提供了UG_TouchUpdate()函数,用于向GUI界面输入当前窗口中外部输入的二维坐标信息。通过该函数,GUI界面可判断出该坐标对应当前窗口中具体哪个对象,例如,某个按键。那当知道了某个对象被触摸后,是否应该作出,或者具体如何作出动态变化来响应这个输入坐标呢?这完全由用户自己来进行定义。μGUI库为每个窗口提供了窗口回调函数来完成这个任务,并给出了回调函数的原型声明(详情参考μGUI使用手册),函数的具体内容完全由用户自己设计。 窗口回调函数在UG_Update()中被调用,回调函数获取输入二维坐标对应的对象信息,用户可以据此自由发挥,设计出相应的动态变化效果。因此,GUI的动态变化最终将由GUI界面刷新函数UG_Update()来实现。这也说明了为何必须周期性的调用UG_Update()来确保GUI界面的动态变化特性。 35.4.2颜色空间在μGUI库中,GUI界面里每个像素点的像素值都是32位的无符号整型变量,采用ARGB888颜色空间,如下图所示。然而,最新的版本μGUI v0.3只使用了其中的RGB888部分。因此,对于用户来说,GUI界面就是一幅24位的RGB888彩色图像,高8位完全忽略。 在μGUI中,定义了上百种 RGB888的颜色种类,用户可根据自己的喜好为GUI界面中的窗口、按键、文本等对象选择相应的颜色进行设计。 35.4.3字体大小μGUI库中定义了十几种大小不同的字体,用于GUI界面中窗口、文本框、按键等对象的文本显示。 35.5 PL逻辑框架本例程的PL部分设计原理如下图所示。 31.5.1 PS设置 PS部分的设置下图所示。
35.5.2 GUI界面显示本例程中GUI界面的显示通过AXI VDMA、AXI4-S to Video Out、Video Timing Controller三个IP核完成。显示分辨率为1024*600@60Hz。 35.5.2.1 AXI VDMA设置
在本例程中,AXI DMA并不适用。原因如下: 当使用AXI DMA时,PS每次向PL发送GUI界面图像都需要通过调用函数配置PL的AXI DMA才能完成。在AXI DMA发送完成中断函数中发起下一次图像传输的方式来实现连续的图像发送。同时,在本例程中,GUI界面的刷新过程是在定时器中断函数中完成,当触摸屏以60Hz显示时,1幅图像的显示时间为17.67ms。若GUI界面刷新的时间超过触摸屏中1幅图像显示的时间时,AXI DMA图像发送完成中断将不能被立即响应执行,会导致图像显示发生中断。当GUI界面刷新完成CPU退出定时器中断后,AXI DMA图像发送才能被继续执行,重新恢复GUI界面的显示,这样在每次GUI界面刷新的时候就会产生“跳屏”的现象。总而言之,定时器中断函数的执行时间会影响AXI DMA发送中断函数的执行。笔者已使用AXI DMA验证了这个现象。 而VDMA具有free run的特点,GUI界面图像的发送可由PL的VDMA独自完成,无需PS参与,因此,定时器中断中GUI界面刷新与图像显示不会产生任何冲突,可以保证GUI界面显示的连续性。因此,在本例中使用VDMA更合适。
在本例程中,只存在PS的DDR向PL的图像传输,即Memory Map to Stream(MM2S)方向。因此,只需使能读通道,可关闭写通道来节省逻辑资源。由于GUI界面只对应DDR中的同一幅图像,只需1个图像缓存,所以将frame buffer设置为1即可。另外,将中断输出与PS连接。AXI VDMA的设置如下图所示。 35.5.2.2 Video Timing Controller 为了方便PS动态配置Video Timing Controller的显示分辨率,使能AXI-lite接口,其默认的分辨率可不用进行设置。Video Timing Controller的设置如下图所示。 35.5.2.3 AXI4-Sream to Video Out本例程中,输出至触摸屏的GUI图像为RGB888格式,AXI4-Sreamto Video Out设置如下图所示。需要引出RGB数据data、行同步信号hs、场同步信号vs以及数据有效信号de与LCD触摸屏连接。 35.5.2.4 AXI-Sream Subset ConverterAXI VDMA与AXI4-Stream to Video Out之间通过AXI Stream接口连接,由于AXI VDMA的M_AXIS_MM2S接口的数据为32位,且含有4为的tkeep信号。而AXI4-Stream to Video Out的video_in接口的数据为24位,且无tkeep信号。为了更好的将两个信号之间存在差异的Stream接口连接,通过AXI-Sream Subset Converter完成差异信号的重映射。设置如下图所示。 35.5.3 AXI PWM 本例程中,PL部分通过使用digilent公司AXI PWM的IP核来实现PWM信号的输出,用于驱动触摸屏的背光源。该ip位于user_src文件夹下,IP的路径设置如下图所示。 很多触摸屏的背光源都是由PWM信号驱动的,通过改变PWM信号的占空比可以调节触摸屏的亮度。AXI PWM通过AXI总线与PS连接,PS通过AXI4-Lite对其进行配置和控制,并可动态调节输出PWM信号的占空比。AXI PWM的设置如下图所示,只需输出1路PWM信号,由于使用的触摸屏所需的PWM信号为负极性,即低电平占空比越大,屏幕越亮,因此将极性设为负。输出的PWM信号与触摸屏连接。 35.5.4 AXI GPIO本例程中,PL部分通过AXI GPIO实现1个触摸屏触摸中断信号输入,2个按键信号输入,5个LED控制信号输出,1个液晶屏背光源使能信号输出,共计9个GPIO端口,定义如下。
每当输入的触摸中断信号或按键信号发生一次改变,AXI GPIO则会向PS触发一次中断。AXI GPIO的设置如下图所示。使能中断接口,将其与PS连接。 35.5.5 Clocking Wizard本例程中,Clocking Wizard用于提供LCD触摸屏进行GUI显示的参考时钟,该时钟被Video Timing Controller以及AXI4-Sreamto Video Out两个IP核使用,同时引出至顶层模块。将Clocking Wizard设置为MMCM(因为MMCM内部倍频系数和CLKOUT0分频系数可以为小数,比PLL更易获得所需的频率),并使能AXI-lite动态重配置接口,将动态重配置接口通过AXI总线与PS连接,方便PS随时对MMCM或PLL输出的时钟频率进行重配置,从而避免PL部分的重新编译。 Clocking Wizard设置如下图所示。注意将input clock的时钟源设置为Global buffer,因为输入时钟来自PS的FCLK_CLK0,该时钟也被其他IP所使用,在进入MMCM之前已经位于全局时钟网络BUFG。 输出时钟频率可设置也可不作设置,为了避免误解,在这里设置为所LCD显示屏需要的51.2MHz。若用户使用其他触摸屏,需要更高的分辨率,建议将clk_out1设置一个大于等于最高分辨率所对应时钟频率的值,这样可以确保时序约束的可靠性。 35.5.6 IO口35.5.6.1 RGB接口IO下面内容针对于RGB接口测试工程 1、 PL IO信号定义 本例程除了PS部分的固定IO口之外,PL部分引出的IO口如下图所示。 信号定义如下表所示。 PL部分IO口定义表 2、LCD时钟信号输出 Clocking Wizard引出的时钟信号lcd_clk,需要经过ODDR产生1个反相的时钟lcd_clk_o与LCD触摸屏连接。目的在于使输出时钟lcd_clk_o的边沿位于LCD其他控制、数据信号窗口的中心位置,使建立和保持时间最大化。 3、LCD RGB信号映射 AXI4- Sreamto Video Out输出的24位像素点数据lcd_data与LCD触摸屏RGB信号的映射关系如下所示。 4、LCD控制信号 LCD液晶屏的背光源使能信号与AXI GPIO的最高位连接,使PS可以控制背光源的开关。触摸屏唤醒信号置为1。 35.5.6.2 LVDS接口IO下面内容针对于LVDS接口测试工程 1、PL IO信号定义 本例程除了PS部分的固定IO口之外,PL部分引出的IO口如下图所示。 信号定义如下表所示。 PL部分IO口定义表 2、时钟及数据信号 下面这个模块的功能是将RGB信号转换为LVDS信号。 3、LCD控制信号 触摸屏唤醒信号置为1。 35.6 PS程序设计 PS部分程序所有的源文件如下图所示。 35.6.1 main函数main函数主要完成如下功能:
35.6.2 时钟重配置在PL中,Clocking Wizard使能了动态重配置功能,因此PS可以通过其AXI-lite接口动态重配置MMCM或者PLL,来改变其时钟的输出频率。 驱动程序 Clocking Wizard的驱动程序由clk_wizard_config.c和clk_wizard_config.h组成。 本例程中,使用了MMCM,MMCM的重配置通过Clk_Wiz_Reconfig()函数完成,该函数参考自SDK中clk_wiz_v1_1的example:xclk_wiz_intr_example.c。由于SDK关于Clocking Wizard的驱动还未完善,且MMCM/PLL内部的寄存器信息未有官方文档可参考,所以Clk_Wiz_Reconfig()不在此做具体分析。MMCM的基本结构如下图所示。 MMCM内部的压控振荡器VCO频率与MMCM的输出时钟频率的公式如下: ZYNQ 7010/7015/7020内部MMCM的VCO频率范围为如下图所示。 动态配置MMCM其实就是通过PS改变其内部倍频系数M,输入分频系数D,以及输出分频系数O的值来实现。在clk_wizard_config.h有4个重要的宏定义,如下: #define VCO_FREQ 600 #define DYNAMIC_INPUT_FREQ 100 #define DYNAMIC_OUTPUT_FREQ 51.2 #define CLK_FRAC_EN 1 其中VCO_FREQ表示MMCM工作时的VCO频率为600MHz,DYNAMIC_INPUT_FREQ表示MMCM输入的时钟频率为100MHz, DYNAMIC_OUTPUT_FREQ表示MMCM输出的时钟频率为51.2MHz,CLK_FRAC_EN表示允许CLKOUT0的分频系数为1/8精度的小数。 Clk_Wiz_Reconfig()函数根据这4个宏定义的值对MMCM的M、D、O的值进行配置,并通过Wait_For_Lock()判断各环节的输出时钟是否LOCK。用户也可以根据实际需求在允许的范围内调节VCO的频率。 35.6.3 PWM信号输出驱动程序 AXI PWM的驱动程序由pwm_config.c和pwm_config.h组成。 本例程所使用的液晶屏建议输入PWM信号频率为100Hz~200K Hz。在main函数中通过PWM_Init()函数设置初始输出PWM信号的周期和占空比。 PWM_Init函数:
上述函数都是以时钟周期数来设置。在pwm_config.h包含了PWM信号周期和占空比的时钟周期数宏定义。如下: #define PERIOD_CLOCK_NUM 409600 #define DUTY_CLOCK_NUM 204800 由于本例程中,PL部分AXI PWM的参考时钟频率为100MHz,一个时钟周期就是10ns。因此,PWM_Init所设置的PWM信号的周期为4.096ms,占空比为50%,频率约为250Hz。 为了对PWM信号的占空比进行动态调节达到改变液晶屏背光源亮度的目的,又设计了PWM_increase_duty()和PWM_decrease_duty()函数用于增大和减小PWM信号中低电平的占空比。 最后,在main函数中调用PWM_Enable()使能PWM信号输出。 35.6.4 GPIO输入输出AXI GPIO的驱动程序由gpiopl_intr.c和gpiopl_intr.h组成。 在main函数调用Gpiopl_init()函数初始化AXI GPIO,设置9个GPIO的方向,其中GPIO[0]~GPIO[2]为输入,GPIO[3]~GPIO[8]为输出。并将GPIO[3]~GPIO[8]的输出都置为0。每个GPIO的宏定义在gpiopl_intr.h中,如下所示。 #define TOUCH_INTR_MASK 0x00000001 #define BUTTON0_INTR_MASK 0x00000002 #define BUTTON1_INTR_MASK 0x00000004 #define LED1_MASK 0x00000008 #define LED2_MASK 0x00000010 #define LED3_MASK 0x00000020 #define LED4_MASK 0x00000040 #define LED5_MASK 0x00000080 #defineLCD_BL_EN_MASK 0x00000100 通过Gpiopl_Setup_Intr_System()初始化并使能AXI GPIO的输入中断,GPIO[0]~GPIO[2]的任意1个信号的输入发生1次改变将触发1次中断,GpioplIntrHandler()为GPIO的中断函数。 GpioplIntrHandler()函数:
最后,在main函数中将GPIO[8]置为1,开启液晶屏的背光源。 GPIO[3]~GPIO[7]信号的输出在gui_window.c中进行控制,用于在GUI界面中控制开发包中的LED灯。 35.6.5 I2C读取触摸信息PS的I2C接口的驱动程序由iic_intr.c和iic_intr.h组成。 在本例程中,PS的I2C接口不能工作于中断模式,其原因在于,通过I2C接口读写触摸屏是在GPIO的中断函数GpioplIntrHandler()中执行,此时若I2C接口产生中断,该中断将不能在GPIO中断中被嵌套响应,无法完成读写操作。因此,I2C接口只能工作于轮询Poll模式。 触摸屏中FT5206控制芯片的I2C地址和I2C时钟频率设置如iic_intr.h宏定义所示。I2C地址为0x38,I2C时钟频率设为400KHz,为FT5206的最高值。 #define IIC_SLAVE_ADDR 0x38 #define IIC_SCLK_RATE 400000 在main函数中调用Iic_init()函数初始化PS的I2C接口,设置I2C时钟频率。当触摸屏中断信号拉低触发GPIO产生中断后,在GPIO中断函数GpioplIntrHandler()中首先调用I2C_write()向FT5206芯片发送需要读取的寄存器首地址。在本例程中,需要从地址为0x02的寄存器开始读,因此I2C_write()发送的首地址为0x02。然后,调用I2C_read()函数从FT5206连续读取29个寄存器的值,每个寄存器的值为1个字节,一共读取29个字节的触摸信息。 I2C_write()和I2C_read()函数均以轮询模式控制I2C接口。 35.6.6 GUI界面显示GUI界面显示在PL部分由AXI VDMA,Video Timing Controller,AXI4- Sreamto Video Out三个IP协同完成。其中AXI VDMA和Video Timing Controller需要PS进行设置,AXI VDMA完成PS端DDR3中GUI界面的读取,Video Timing Controller产生通过AXI4- Sreamto Video Out进行GUI界面显示的控制时序。 35.6.6.1 AXI VDMAAXI VDMA的驱动程序由vdma_config.c和vdma_config.h组成。 在本例程中,AXI VDMA仅有MM2S方向(PS DDR到PL)的读通道工作。另外,需要让AXI VDMA工作于free run模式,因此,只使能错误中断。 在main函数中调用Vdma_Init()函数对AXI VDMA进行初始化,在Vdma_Init()中调用ReadSetup()对MM2S读通道的参数进行设置。VDMA读通道的相关设置由vdma_config.h中的宏定义所决定,如下所示。 #define IMAGE_WIDTH GUI_WIDTH #define IMAGE_HEIGHT GUI_HEIGHT #define BYTES_PER_PIXEL 4 #define NUMBER_OF_READ_FRAMES 1 #define MEM_BASE_ADDR 0x10000000 #defineBUFFER0_BASE (MEM_BASE_ADDR)
然后,在main函数中调用Vdma_Setup_Intr_System()函数,配置使能VDMA读通道的错误中断,ReadErrorCallBack()为中断函数。 最后,调用Vdma_Start()函数,启动AXI VDMA的读通道开始工作。 35.6.6.2 显示时序设置Video Timing Controller的驱动程序由vtc_config.c和vtc_config.h组成。 在本例程中,液晶屏使用了1024×600@60Hz显示分辨率,使用的时序参数如下:
在main函数中调用Vtc_init()函数对Video Timing Controller进行初始化,将上述的显示时序参数写入其中,然后使能其产生控制时序信号。 35.6.7 定时器PS定时器的驱动程序由timer_intr.c和timer_intr.h组成。 在本例程中,PS定时器用于周期性产生中断来实现GUI界面的刷新,以固定的频率的调用UG_Update()函数实现GUI的动态特性。 首先,在main函数中调用Timer_init()函数对定时器进行初始化,其中断周期由以下宏定义决定,周期为20ms,即GUI的刷新频率为50Hz,刷新频率越高,GUI的动态变化越快,用户可以根据需求进行调整。 #define TIMER_LOAD_VALUE XPAR_CPU_CORTEXA9_0_CPU_CLK_FREQ_HZ / 100 然后,在main函数中调用Timer_Setup_Intr_System()函数使能定时器的中,将中断函数设置为TimerIntrHandler()。 TimerIntrHandler()函数的过程如下:
最后,在main函数里调用Timer_start()函数启动定时器工作。 35.6.8 GUI界面设计GUI界面的驱动程序由gui_window.c、gui_window.h、image.h、ugui.c和ugui.h组成,其中ugui.c和ugui.h来自μGUI库。 在本例程中,一共设计了5个窗口,对应window1~window5。同时,也为每个窗口设计了对应的回调函数window_1_callback()~window_5_callback()。 在main函数中,调用gui_create()函数对GUI进行初始化,并创建所有的窗口及窗口所包含的所有对象。 gui_create()函数的流程如下:
GUI界面设计以窗口为基础进行,每个界面对应一个窗口,但所有的窗口都对应同一片内存区域。μGUI库中有很多API函数,这里只介绍本例程所涉及的API函数,其余的API可参考μGUI使用手册。5个窗口的设计在create_window1()~create_window5()函数中完成。 35.6.8.1 GUI初始化通过UG_Init()函数设置GUI界面的长宽分辨率,并绑定在4.1.1节所述用户自定义的像素值设置函数PixelSet()。通过UG_FillScreen()函数将所有GUI窗口的背景设置为浅灰色。 35.6.8.2 窗口1设计窗口1为GUI的欢迎界面。如下图所示。 窗口1的设计在create_window1()函数中完成,流程如下: 1)窗口创建
2)按键创建
3)文本框创建
4)图片创建 目前,最新版的μGUI v0.3仅支持在GUI界面中添加RGB565格式的BMP图像。由于本设计中所需添加的两个米联logo均为RGB888格式,为了让μGUI能支持24位RGB888的图片,需要对ugui.c文件中的UG_DrawBMP()函数进行修改,如下所示。其中通过阴影部分为添加的代码。 voidUG_DrawBMP(UG_S16xp, UG_S16yp, UG_BMP* bmp ) { UG_S16x,y,xs; UG_U8r,g,b; UG_U16* p; /*add by osrc 2017.2.18*/ UG_U32* p1; UG_U16tmp; /*add by osrc 2017.2.18*/ UG_U32 tmp1; UG_COLOR c; if ( bmp->p == NULL ) return; /* Only support 16 BPP so far */ if ( bmp->bpp == BMP_BPP_16 ) { p = (UG_U16*)bmp->p; } /*add support for 32 BPP, by osrc 2017.2.18*/ elseif(bmp->bpp == BMP_BPP_32) { p1 = (UG_U32*)bmp->p; } else { return; } xs = xp; for(y=0;y<bmp->height;y++) { xp = xs; for(x=0;x<bmp->width;x++) { /*add support for RGB888, by osrc 2017.2.18*/ if(bmp->colors == BMP_RGB565) { tmp = *p++; /* Convert RGB565 to RGB888 */ r = (tmp>>11)&0x1F; r<<=3; g = (tmp>>5)&0x3F; g<<=2; b = (tmp)&0x1F; b<<=3; c = ((UG_COLOR)r<<16) | ((UG_COLOR)g<<8) | (UG_COLOR)b; } else { tmp1 = *p1++; c = tmp1; } UG_DrawPixel(xp++ , yp , c ); } yp++; } }
添加的图片0如下所示,为米联的logo。图片各像素点的值包含在image.h的logo1_bmp数组中。 定义该图片在GUI中的结构体,如下所示。图片的指针logo1_bmp,为图片的长、宽均为65个像素,每个像素点占32bit,图片格式为RGB888。 constUG_BMP logo1 = { (void*)logo1_bmp, 65, 65, BMP_BPP_32, BMP_RGB888 };
添加的图片1如下所示,为米联客的logo。图片各像素点的值包含在image.h的logo2_bmp数组中。 定义该图片在GUI中的结构体,如下所示。图片的指针logo2_bmp,为图片的长位259像素,宽为105个像素,每个像素点占32bit,图片格式为RGB888。 constUG_BMP logo2 = { (void*)logo2_bmp, 259, 105, BMP_BPP_32, BMP_RGB888 }; 图片对象创建添加方式与图片0相同。 5)回调函数 1个窗口的回调函数当该窗口中的按键的触摸状态发生改变时,会在UG_Update()函数中被调用。在窗口1的回调函数window_1_callback()中设置了按键0的功能,当在触摸屏中按下start application now按键便会使window_1_callback()被调用,通过UG_WindowShow函数,让GUI界面刷新后切换到窗口2。在按下按键的同时,可以观察到按键0字体和背景颜色产生的变化。 35.6.8.3 窗口2设计窗口2为LED灯控制界面,如下图所示。在该窗口中,用户可以通过触摸LED1~LED5按键来控制开发板上的标识为LD1~LD5的LED灯。 窗口2的设计在create_window2()函数中完成,窗口2中的标题、按键、文本框的创建流程与create_window1()原理相同,详细可参阅工程代码,此处不作赘述。 1)回调函数 窗口2的回调函数window_2_callback()中设置了7个按键0~6的功能,其中LED1~LED5对应5个LED灯的控制功能,通过控制GPIO[3]~GPIO[7]的输出来实现LED灯的开关。当LED灯亮时,对应按键的背景颜色为红色,当LED熄灭时,对应按键的背景颜色还原为绿色。(注意miz701n只有LED1~LED4受控制,按键LED5实际无控制LD5的作用)另外,按下Previous Page按键让GUI界面切换至窗口1,按下Next Page按键让GUI界面切换至窗口3。在按下按键的同时,可以观察到各按键字体和背景颜色产生的变化。 35.6.8.4 窗口3设计窗口3为液晶屏亮度调节界面,如下图所示。在该窗口中,用户可以通过触摸加、减按键来调节液晶屏背光源的亮度。 窗口3的设计在create_window3()函数中完成,窗口3中的标题、按键、文本框的创建流程与create_window1()原理相同,详细可参阅工程代码,此处不作赘述。 1)回调函数 窗口3的回调函数window_3_callback()中设置了4个按键0~3的功能,其中“-”按键控制PWM信号低电平的占空比减小,从而降低液晶屏亮度;“+”按键控制PWM信号低电平的占空比增大,从而提高液晶屏亮度。另外,按下“Previous Page”按键让GUI界面切换至窗口2,按下“Next Page”按键让GUI界面切换至窗口4。在按下按键的同时,可以观察到各按键字体和背景颜色产生的变化。 35.6.8.5 窗口4设计窗口4为绘图界面,如下图所示。在该窗口中,可实现5点触控,用户可以通过5个手指同时触摸平面来进行简单画图。 绘图时,以每个手指触摸点为圆心,沿每个手指移动轨迹不断画圆。5个触摸点的圆形图案颜色按顺序依次为:红、黄、蓝、绿、橙。由于GUI界面存在一定刷新的时间间隔,因此绘图轨迹中间会出现间断的现象,如下图所示。这里用户自行改进来实现真实的绘图效果。 窗口4的设计在create_window4()函数中完成,窗口4中的标题、按键、文本框的创建流程与create_window1()原理相同,详细可参阅工程代码,此处不作赘述。 1)回调函数 窗口4的回调函数window_4_callback()中设置了3个按键0~2的功能,其中“Retry”按键用于清除当前窗口中的绘图结果,使用户可以重新进行绘图。另外,按下“Previous Page”按键让GUI界面切换至窗口3,按下“Next Page”按键让GUI界面切换至窗口5。在按下按键的同时,可以观察到各按键字体和背景颜色产生的变化。 35.6.8.6 窗口5设计窗口5为退出界面,如下图所示。在该窗口中,用户可以通过触摸“YES”、“NO”按键来选择是否退出回到窗口1的欢迎界面。由于窗口5的尺寸小于其他窗口,因此当GUI界面从窗口4切换到窗口5时,可以出两个窗口叠加的效果,并且当窗口5出现时,窗口4的标题栏会由蓝变灰。 窗口5的设计在create_window5()函数中完成,由于窗口5的尺寸小于1024×600,因此需要调用UG_WindowResize()函数重新设置窗口5的尺寸大小及位置。窗口5中的标题、按键、文本框的创建流程与create_window1()原理相同,详细可参阅工程代码,此处不作赘述。 1)回调函数 窗口5的回调函数window_5_callback()中设置了2个按键0~1的功能,按下“YES”按键让GUI界面切换至窗口1,按下“NO”按键让GUI界面切换至窗口4。在按下按键的同时,可以观察到各按键字体和背景颜色产生的变化。 35.7注意事项35.7.1 更改GUI分辨率若用户要接其他分辨率的触摸屏,需要更改触摸屏显示分辨率时,需要更改3个地方。
#define GUI_HEIGHT 600 #define GUI_WIDTH 1024
#define DYNAMIC_OUTPUT_FREQ 51.2 35.7.2 RGB和LVDS接口可调电阻 微雪公司的7寸LCD电容触摸屏具有两种接口,一种是24位RGB888接口,另外一种是单路8bit LVDS接口,触摸控制通过I2C实现。 LCD背面接口图: 这里需要注意:微雪公司的7寸LCD电容触摸屏的两种接口不能同时使用。默认状态使用RGB接口,如果要使用LVDS接口,需要调节电阻。 使用RGB接口:R24位置焊接10K电阻,R25不焊接元件(默认状态)。 使用LVDS接口:R25位置焊接10K电阻,R24不焊接元件。 35.7.3 提供测试例程FEP接口电压本章分别提供了RGB接口和LVDS接口的触摸屏测试例程。LCD触摸屏通过转接板连接开发板的FEP接口,开发板的FEP接口电压可调,通过核心板的可调电阻可以将FEP接口电压调整为1V8、2V5、3V3,开发板默认FEP接口电压是3V3。 RGB接口的测试例程,FPGA开发板FEP接口默认输出电压是3V3。LVDS接口的测试例程,FPGA开发板FEP接口的输出电压是2V5。 35.7.4 测试连接由于本例程所使用的LCD触摸屏接口为FPC,而开发板并不具有FPC接口。因此,需要设计一个转接板将触摸屏与开发板的40pin NEP接口连接。需要注意的是,在该触摸屏内部,I2C信号线均未接上拉电阻。因此,在转接板中需要将两个I2C信号各通过1个几KΩ的电阻上拉接至3.3V,否则I2C接口将无法正常使用。 LCD触控屏与开发板连接如下,这里需要注意,RGB接口使用的转接板和LVDS接口转接板不同,请以实际使用的接口选择转接板: 附录附件1:摄像头摄像头参数对照表 ![]() 摄像头尺寸版图: 摄像头接口定义: ![]() 附件2:VGA时序标准1280x720@60HZ 时序参数 640x480@60HZ 时序参数 附件3:如何添加驱动库(SDK库移植)在使用过程中,我们要在SDK工程中使用某个IP,而这个IP的驱动库不能在SDK中自动生成,如果直接运行程序,会报错,这时,我们需要将这个IP的驱动添加到工程中。下面说明一下添加方法,SDK的移植方法通用,这里仅作方法讲解。需要注意:当工程路径变化时,移植的SDK库的路径需要重新添加,否则会报错。 举例: 如果在vivado 2015.4的SDK中还没有Clocking Wizard的驱动函数库,缺少可以利用的API函数。而vivado 2015.4的SDK中包含了Clocking Wizard的驱动库。因此,为了便于开发,借用2015.4的驱动库进行设计。 首先,在SDK 2015.4的安装目录(如:C:\Xilinx\SDK\2015.4\data\embeddedsw\XilinxProcessorIPLib\drivers)下找到Clocking Wizard的驱动库文件夹clk_wiz_v1_1,如下图所示。(这个驱动库也可以是其他地方移植的) 将文件夹clk_wiz_v1_1复制到工程目录下的sdk_repo(自己创建的文件夹)的bsp文件夹中。然后在sdk中设置sdk_repo文件夹的路径,如下图所示。 然后更改工程bsp中的驱动函数设置,将clk_wiz_0的驱动改为clk_wiz 1.1版本,如下图所示。 重新生成bsp后,在bsp的libsrc下就会出现clk_wiz_v1_1的驱动,如下图所示。 驱动库添加进来,重新编译,就不会报错了。 |
说点什么...