0 项目要求
游戏手柄控制LCD上的信息
- IO扩展板上有一个用X、Y二轴电位计制作的游戏手柄,这两个电位计串接在一个振荡电路中,两个电位计的变化会改变阻值,从而改变生成的PWM信号的频率和占空比。
- 通过单片机的IO端口测量这个PWM信号的频率和占空比的变化,就能够判断出电阻的变化,进而判断出游戏手柄的方向变化。
要求:本任务需要用ESP32板测量IO扩展板上的PWM信号,在LCD上以图形化的方式显示游戏摇杆的变化,通过游戏摇杆的拨动,能够触及LCD的全屏幕。
1 开发环境搭建
采用PlatformIO平台进行开发。该平台以VSCode插件的形式呈现,无论是编写代码,上板调试都十分的方便快捷,故采用该平台完成项目的开发。
在PlatformIO首次创建项目时会下载一系列的硬件开发库,如果不使用众所周知的方法,整个过程非常耗时。因此,我采用了离线安装的方法,教程参见下方的链接。
【Arduino IDE太难用?5分钟"离线"安装PlatformIO,无需等待,编程体验原地起飞】 https://www.bilibili.com/video/BV1xY4y1b7un/?share_source=copy_web&vd_source=b761846b0a45a5a9d73b01fe244a9a11
2 项目设计思路
需要获取摇杆移动的方向,并让LCD屏幕上的正方形沿着摇杆的方向运动。
2.1 PWM波形捕获
首先要获知摇杆运动的方向。经过外围电路的设计,游戏摇杆能够输出一路PWM波形。当摇杆沿横向运动时,PWM波的频率会发生变化。当摇杆沿纵向运动时,PWM波的占空比会发生变化,因此需要设计程序来检测PWM波的频率与占空比。
2.1.1 PWM波频率测量
使用定时器0产生计数器数满的中断,每20us检测一次PWM波的频率和占空比。使用脉冲计数器(pcnt)来检测PWM波的频率。将脉冲计数器设置为上升沿计数模式。每20us内的PWM波形的上升沿个数近似与PWM波周期个数相同,因此可以用pcnt的上升沿计数来代替PWM波的频率。
使用ESP-IDF中的pcnt库函数对pcnt进行配置,配置代码如下:
pcnt_config_t pcntFreqConfig = { }; // Instance of pulse counter
pcntFreqConfig.pulse_gpio_num = PCNT_INPUT_SIG_IO; // pin assignment for pulse counter = GPIO 15
pcntFreqConfig.pos_mode = PCNT_COUNT_INC; // count rising edges (=change from low to high logical level) as pulses
pcntFreqConfig.counter_h_lim = PCNT_H_LIM_VAL; // set upper limit of counting
pcntFreqConfig.unit = PCNT_FREQ_UNIT; // select ESP32 pulse counter unit 0
pcntFreqConfig.channel = PCNT_CHANNEL_0; // select channel 0 of pulse counter unit 0
pcnt_unit_config(&pcntFreqConfig); // configur rigisters of the pulse counter
pcnt_counter_pause(PCNT_FREQ_UNIT); // pause puls counter unit
pcnt_counter_clear(PCNT_FREQ_UNIT); // zero and reset of pulse counter unit
pcnt_event_enable(PCNT_FREQ_UNIT, PCNT_EVT_H_LIM); // enable event for interrupt on reaching upper limit of counting
pcnt_isr_register(CounterOverflow, NULL, 0, &user_isr_handle); // configure register overflow interrupt handler
pcnt_intr_enable(PCNT_FREQ_UNIT); // enable overflow interrupt
//pcnt_set_filter_value(PCNT_FREQ_UNIT, PCNT_FILTER_VAL); // set damping, inertia
pcnt_filter_enable(PCNT_FREQ_UNIT); // enable counter glitch filter (damping)
pcnt_counter_resume(PCNT_FREQ_UNIT); // resume counting on pulse counter unit
每进入定时器0中断后,读取pcnt计数器中的值将其作为频率的替代值并对pcnt清零。代码如下所示。
2.1.2 PWM波占空比测量
占空比测量主要依赖于定时器1与GPIO中断来实现。由于PWM波的周期可以由频率得知,而频率测量方法已经由上文给出,因此只需测量PWM波维持在高电平的时间,具体测量思路如下图所示:
2.2 LCD屏幕的编程
采用基于Arduino库的TFT_eSPI库对LCD屏幕进行编程。
首先修改配置文件User_setup.h
选择屏幕的型号
#define ST7735_DRIVER // Define additional parameters below for this display
在对应的型号处更改屏幕的横向与纵向像素个数
// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation
// #define TFT_WIDTH 80
#define TFT_WIDTH 128
// #define TFT_WIDTH 172 // ST7789 172 x 320
// #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
// #define TFT_HEIGHT 160
#define TFT_HEIGHT 128
// #define TFT_HEIGHT 240 // ST7789 240 x 240
// #define TFT_HEIGHT 320 // ST7789 240 x 320
// #define TFT_HEIGHT 240 // GC9A01 240 x 240
使能此项,原因如注释所写
// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or stray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:
// #define ST7735_INITB
// #define ST7735_GREENTAB
// #define ST7735_GREENTAB2
#define ST7735_GREENTAB3
// #define ST7735_GREENTAB128 // For 128 x 128 display
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
// #define ST7735_ROBOTLCD // For some RobotLCD arduino shields (128x160, BGR, https://docs.arduino.cc/retired/getting-started-guides/TFT)
// #define ST7735_REDTAB
// #define ST7735_BLACKTAB
// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset
在ESP32开发板位置处修改屏幕引脚
// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins
#define TFT_MOSI 21 // In some display driver board, it might be written as "SDA" and so on.
#define TFT_SCLK 41
#define TFT_CS 13 // Chip select control pin
#define TFT_DC 17 // Data Command control pin
#define TFT_RST 18 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 22 // LED back-light
修改RGB颜色顺序
#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
使用如下的函数就能在坐标x,y处绘制边长为w,h的长方形了
void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
2.3 使用摇杆控制正方形
使用串口调试发现摇杆在X方向上的位移量和PWM波频率近似呈线性变化,Y轴方向上的位移量和PWM波占空比近似呈线性变化。因此可以通过PWM波的频率和占空比通过线性函数反算出游戏摇杆偏移量进而得到LCD上正方形的偏移量
整体流程图如下图所示。
3 问题解决
在实际上板调试后发现,正方形在移动过程中经常出现抖动。画出占空比随时间变化的曲线后发现,PWM波的占空比经常出现因大幅度的跳变而产生的毛刺,这也是导致正方形出现抖动的原因,这可能是由于摇杆的硬件不理想特性造成的。
采用软件滤波来解决这一问题。具体而言,是限制相邻两次占空比之间的数值变化不能超过某一阈值。如果超过,则强行将最新的一次占空比的值赋值成与上一次占空比数值相等。这种解决方法取得了较好的效果。
4 未来计划
目前屏幕在刷新时会出现闪烁,未来会考虑解决这一问题。
5 工程文件
完整的工程文件包含较多库文件,大小为12MB,超过了附件上传的限制,烦请从下方网盘链接下载。
链接:https://pan.baidu.com/s/1nM4fNh8nwxZLOJy8Nle9mg?pwd=rx7e
提取码:rx7e