0.目录
-
项目要求
-
实现功能
-
项目分析
-
实现思路
-
灯板实现
-
实物展示
-
心得体会
-
附件
1.项目要求
可以定时的电子沙漏,要求设置不同的时长,在LCD屏幕上显示时间,在灯板上显示沙漏效果。
2.实现功能
-
通过按键设置定时时长,最大支持99分99秒。
-
在LCD上显示当前时间,时间分辨率为100ms。
-
在灯板上实现沙漏效果。
3.项目分析
M5StickC PLUS 是M5StickC的大屏幕版本,主控采用ESP32-PICO-D4模组,具备蓝牙4.2与WIFI功能,小巧的机身内部集成了丰富的硬件资源,如红外、RTC、麦克风、LED、IMU、按键、蜂鸣器、PMU等,在保留原有M5StickC功能的基础上加入了无源蜂鸣器,同时屏幕尺寸升级到1.14寸、135*240分辨率的TFT屏幕。
项目中使用了2个按键、TFT-LCD显示屏以及G0、G25、G26三个IO口用于连接灯板控制。
4.实现思路
项目使用VS Code和PlatformIO进行开发,开发板选择espressif32。在platformio.ini中配置下载波特率为upload_speed = 1500000,否则可能无法下载成功。
4.1 LCD驱动
安装bodmer/TFT_eSPI@2.4.30库,进行TFT-LCD屏幕的控制,修改User_Setup.h中屏幕配置,主要内容如下:
#define TFT_WIDTH 135
#define TFT_HEIGHT 240
#define TFT_MOSI 15
#define TFT_SCLK 13
#define TFT_CS 5
#define TFT_DC 23
#define TFT_RST 18
4.2 电源管理
M5Stick的电源结构如下图。
主控ESP32通过i2c与电源管理芯片AXP192连接,需要先使能AXP192的输出端口LDO2才能打开LCD屏幕背光,否则无法显示内容。
直接移植M5Stick官方Demo中的AXP192库,通过以下代码初始化并使能AXP192输出。
AXP192 Axp = AXP192();
Axp.begin();
4.3 中文字库
LCD中的中文采用仿宋字体,时间显示采用数码管字体,英文使用TFT_eSPI的默认字体,字库制作见下文。
字库主要通过Processing实现,官网下载速度较慢,可以在CSDN上找绿色版。程序需要在无特殊字符的路径下才能打开。字库配置的主要步骤参见使用Arduino TFT_eSPI库自定义中文字库 - 哔哩哔哩 (bilibili.com)。
4.4 定时器
沙漏的定时采用定时器中断进行,定时器每100ms中断一次,将标志位置1。在主循环中判断标志位进行时间处理。
//定时器时间(ms)
#define TIMER_SET_MS 100
//定时器变量
hw_timer_t *timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
//定时器中断函数
void IRAM_ATTR InterruptProc_OnTimer()
{
portENTER_CRITICAL_ISR(&timerMux);
flag_timer = true;
portEXIT_CRITICAL_ISR(&timerMux);
timerAlarmDisable(timer);
}
定时器开关通过以下函数:
timerAlarmEnable(timer); //打开定时器
timerAlarmDisable(timer); //关闭定时器
4.5 按键
按键的读取同样采用中断方式,按键消抖时间200ms,配置引脚为内部上拉,下降沿触发中断。
//引脚定义
#define GPIO_KEY_A 37
#define GPIO_KEY_B 39
//按键防抖时间
#define KEY_DELTA_TIME 200
//按键按下时间
volatile unsigned long triggerTime_KeyA = 0; //按键按下,第一次触发中断的时刻
volatile unsigned long triggerTime_KeyB = 0; //按键按下,第一次触发中断的时刻
//按键初始化
pinMode(GPIO_KEY_A, INPUT_PULLUP); //设置引脚输入模式并打开内部上拉
pinMode(GPIO_KEY_B, INPUT_PULLUP); //设置引脚输入模式并打开内部上拉
//按键中断初始化
attachInterrupt(GPIO_KEY_A, InterruptProc_KEY_A, FALLING); // 由高变低时触发中断
attachInterrupt(GPIO_KEY_B, InterruptProc_KEY_B, FALLING); // 由高变低时触发中断
// 按键A中断函数
void InterruptProc_KEY_A()
{
if (millis() - triggerTime_KeyA > KEY_DELTA_TIME)
{
detachInterrupt(GPIO_KEY_A);
flag_keyA = true;
}
}
// 按键B中断函数
void InterruptProc_KEY_B()
{
if (millis() - triggerTime_KeyB > KEY_DELTA_TIME)
{
detachInterrupt(GPIO_KEY_B);
flag_keyB = true;
}
}
5.灯板实现
灯板上共有2组接口J1与J2,J1与上级控制器连接,J2与下级灯板连接,实现灯板的级联。
本次实际到手的灯板为黑色PCB版本。
VCC与GND分别连接M5接口中的5V输出与GND,其他引脚配置如下:
#define GPIO_DIN 25
#define GPIO_RCK 26
#define GPIO_SCK 0
编写HC595库实现HC595芯片的驱动,主要函数为void HC595_Write(uint8_t data, bool fresh)。
void HC595_Write(uint8_t data, bool fresh)
{
for (int i = 0; i < 8; i++)
{
digitalWrite(GPIO_DIN, (data >> i) & 0x01);
digitalWrite(GPIO_SCK, LOW);
digitalWrite(GPIO_SCK, HIGH);
}
if (fresh == true)
{
HC595_Refresh();
}
}
在此基础上编写LED_Board库实现灯板的控制。
灯板可以通过点扫描与行扫描实现任意图形的刷新,通过视觉暂留使得人眼能看到完整的图形,由于点扫描的效率较慢,因此最终选择行扫描。
void LED_8x8_WriteRow(uint8_t data, uint8_t rowNum)
{
HC595_Write(0x01 << (7 - rowNum), false);
HC595_Write(data, false);
}
void LED_8x16_DrawPicByRow(uint8_t *datas)
{
for (int i = 0; i < 8; i++)
{
if ((datas[i] != 0) || (datas[i + 8] != 0))
{
LED_8x16_Clear();
LED_8x8_WriteRow(datas[i + 8], i);
LED_8x8_WriteRow(datas[i], i);
HC595_Refresh();
}
}
LED_8x16_Clear();
}
灯板的沙漏共有9种情况,提前设置将像素点内容写入数组,计时时间每进行12.5%进行一次变换。
// 沙漏效果
uint8_t LED_State1[16] = {0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t LED_State2[16] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
uint8_t LED_State3[16] = {0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03};
uint8_t LED_State4[16] = {0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07};
uint8_t LED_State5[16] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F};
uint8_t LED_State6[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F};
uint8_t LED_State7[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F};
uint8_t LED_State8[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F};
uint8_t LED_State9[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
6.实物展示
沙漏效果参见B站视频。
7.心得体会
-
M5的按键手感较差,单手操作时无法按动。
-
灯板的像素点较小,实现起来的效果较差。
-
M5Stick的外设丰富,可以满足日常DIY的需求。
8.附件
附件见百度网盘
链接:https://pan.baidu.com/s/1iO__s-6gfKX9yP41C5seJg?pwd=w1pk
提取码:w1pk