任务要求
1.本任务需要用ESP32板测量IO扩展板上的PWM信号。
2.在LCD上以图形化的方式显示游戏摇杆的变化。
3.通过游戏摇杆的拨动,能够触及LCD的全屏幕。
一、环境配置
1.Arduino IDE 2.0:
Arduino IDE是一个用于编写和上传代码到Arduino开发板的软件,它可以很方便地下载各种库,而且简单易用、按键极少、逻辑简单、易于配置,对新手非常友好,而且在2.0版本中加入了代码补全,界面也很简洁漂亮。
2.硬禾学堂ESP32 WiFI模块:
硬禾学堂为“2023寒假在家练”制作了一个模块,该模块板载了:
- ESP-S2-MINI_WIFI模组
- 内置 ESP32S2 系列芯片,Xtensa® 单核 32 位 LX7 微处理器
- 内置芯片叠封 4 MB flash,可叠封 2 MB PSRAM
- 37 个 GPIO,具有丰富的外设
- 板载 PCB 天线
3.输入、输出扩展版:
包含:
- 按键、旋转编码器输入 - 以模拟信号的方式
- 双电位计控制输入 - 以数字信号的方式
- RGB三色LED显示
- 1.44寸128*128 LCD,SPI总线访问
- MMA7660三轴姿态传感器
- 电阻加热
- 温度传感器
- 与ESP32-S2核心模块的接口
二、程序实现:
程序均使用c语言编写
1.模块的实现:
(1)显示屏的使用:
首先需要下载TFT_eSPI库,可以从github上下载,也可以在Arduino IDE上直接下载,在IDE的库管理里直接搜索“TFT_eSPI”,点击第一个直接安装即可。
使用方式如下:
找到文件夹里的“User_Setup.h”文件,根据自己屏幕的驱动芯片、分辨率和引脚进行初始化配置。
// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER // Generic driver for common displays
//#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
#define ST7735_DRIVER //此拓展板的屏幕是由ST7735驱动,所以取消此行注释
//#define ILI9163_DRIVER // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
//#define ST7796_DRIVER
//#define SSD1351_DRIVER
//#define SSD1963_480_DRIVER
//#define SSD1963_800_DRIVER
//#define SSD1963_800ALT_DRIVER
//#define ILI9225_DRIVER
//#define GC9A01_DRIVER
接下来选择屏幕分辨率
// 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
然后配置引脚
#define TFT_MOSI 21
#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 RST pin)
到现在屏幕配置算是已经完成了可以简单测试一下
#include<SPI.h>
#include<TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
void setup() {
tft.init();
tft.fillScreen(TFT_BLACK); //将屏幕变成黑色
}
void loop() {
}
(2)通过IO端口测量此手柄产生的PWM信号的频率和占空比
以下为手柄部分的电路图:
可以看出手柄实际上是两个滑动变阻器进行分压,从电路整体来看是个振荡器+比较器输出pwm波形,所以我们可以从pwm的频率和占空比得知滑动变阻器的变化,进而反推出遥感的位置变化
测量思路:
与stm32的硬件支持和丰富的库函数相比,esp32在输入捕获上稍微麻烦一些,这里我采用定时器与外部中断的方式测量
- 使用外部中断对上升沿和下降沿都进行中断
- 每次发生中断都记录下当前的时间
- 中断服务程序中记录下当前电平
- 脉宽即为触发下降沿的时间减去上次触发上升沿的时间
- 周期即为触发上升沿的时间减去上一次触发上升沿的时间
- 占空比=脉宽/周期
- 频率=1/周期
(3)将手柄方向变化以图形化的方式呈现
这一步由于时间紧迫我做得十分的粗糙,建议使用移植lvgl,并将遥感作为输入设备接入
我使用的是TFT_eSPI里移动光标在屏幕上画小圆点的方式呈现,具体展示见开头视频
2.整体实现
设计思路和程序流程图:
整体代码:
#include <Arduino.h>
#include <SPI.h>
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
volatile unsigned long raiseTime = 0; //前一次上升沿时间
volatile unsigned long fallTime = 0; //前一次下降沿时间
volatile double duty = 0; //占空比
volatile double fre = 0; //频率
int pwmPin = 2; //信号输入的管脚
unsigned int x = 50, y = 50; //设置光标的初始坐标
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; //自旋锁
void changeISR()
{
auto now = micros();
if(digitalRead(pwmPin)) //现在是高
{
portENTER_CRITICAL_ISR(&mux); //进入临界区
auto total = now - raiseTime;
fre = 1e6/(double)total;
auto h = fallTime - raiseTime;
duty = h/(double)total;
portEXIT_CRITICAL_ISR(&mux); //离开临界区
raiseTime = now;
}
else
{
fallTime = now;
}
}
void setup() {
Serial.begin(115200);
Serial.println();
Serial.print("begin");
pinMode(pwmPin, INPUT);
attachInterrupt(digitalPinToInterrupt(pwmPin), changeISR, CHANGE);
tft.init(); //初始化
tft.setRotation(0); //屏幕旋转方向(横向)
tft.fillScreen(TFT_BLACK);
}
void loop() {
delay(50);
tft.fillScreen(TFT_BLACK);
portENTER_CRITICAL(&mux);
double f = fre;
double d = duty;
tft.setCursor(5,5);
tft.println(f);
tft.setCursor(5,8);
tft.println(d);
if (f < 299) {
x--;
}
else if (f > 301) {
x++;
}
if (d < 0.57) {
y--
}
else if (d > 0.59) {
y++;
}
tft.setCursor(x, y);
tft.print(0);
portEXIT_CRITICAL(&mux);
}
三、后记
除了选择Arduino IDE进行开发,还可以使用VSCODE下载esp-idf插件开发,也是十分优雅,但是我在一开始尝试的时候遇到了一些奇怪的bug就换成Arduino IDE了,确实要省心很多。
由于时间匆忙,本来想移植lvgl来实现此项目的,我已经完成了ui设计并成功显示在lcd屏幕上了,但最后在接入物理摇杆作为鼠标的时候遇到了一些困难,为了在规定时间内完成就换成这种十分不优雅的方式呈现。目前我还在继续研究,今后我还会继续完善这个项目的。