# 视神Mini - 基于ESP32的迷你播放器项目 **Repository Path**: zijinkunjiang/VistualDinity-MiniPlayer ## Basic Information - **Project Name**: 视神Mini - 基于ESP32的迷你播放器项目 - **Description**: 基于ESP32S3的迷你播放器,本仓库包含项目设计中的所有源码工程,供大家参考 - **Primary Language**: C/C++ - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2025-02-14 - **Last Updated**: 2025-05-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: ESP32 ## README # 视神Mini - 基于ESP32的迷你播放器项目 ## 介绍 基于ESP32S3的迷你播放器,本仓库包含项目设计中的所有源码工程,供大家参考 ## 使用说明 1. 所开源的工程基于ESP-IDF开发,所用版本为ESPIDF 5.1.2 2. 项目中的工程源码学习自正点原子,在此表示鸣谢 3. 项目中包含的PCB文件请前往立创开源广场! ## 工程项目介绍 解压发行的工程文件,您可以在压缩包中看到以下七个项目,分别是: | 工程名称 | 介绍 | |----------------------|------------------| | 00_lcd_basic | LCD测试 | | 01_key_scan | 按键测试 | | 02_fatfs_test | SD卡和fatfs测试 | | 03_chinese_font | 汉字显示(ESP32内字库)测试 | | 04_wav_player | WAVE播放器 | | 05_mjpeg_videoplayer | AVI播放器 | | 06_lvgl_basic | LVGL基本移植程序 | ## 工程分析 #### 00_lcd_basic LCD测试程序 备注:本代码学习自正点原子DNESP32开发板工程,在此表示鸣谢 **本人对本代码的修改部分如下:** 修改部分使用XL9555的引脚,支持只使用ESP32S3上的引脚 修改LCD_BUF_SIZE,不会申请全屏缓冲,节约内存使用 增加了背景颜色选项,用户刷入内容的时候可以设置刷入背景颜色 执行LCD测试程序,程序会自动初始化LCD显示屏,随后在屏幕上刷出红、绿、蓝三色。适合焊接完成后对LCD进行测试 ###### 1.LCD初始化流程 由于LCD使用SPI进行驱动,使用LCD时,请引入SPI库 需要初始化LCD时,请先初始化ESP32的SPI硬件,若要初始化SPI,请调用是spi2_init(); 随后调用lcd_init();//初始化LCD ```C spi2_init();//初始化SPI lcd_init();//初始化LCD ``` LCD驱动流程: 若需要驱动SPILCD,必然需要使用SPI和LCD进行通讯.LCD驱动流程,主程序调用驱动程序,驱动程序调用硬件SPI(或者软件模拟的SPI向LCD发送数据) ![输入图片说明](Image/Cover/LCD_Driver.png) LCD驱动程序中,以下函数实现对SPI与LCD通讯 ```C void lcd_write_cmd(const uint8_t cmd); /*发送命令到LCD*/ void lcd_write_data(const uint8_t *data, int len); /*发送多字节数据到LCD 通常用于连续刷入颜色*/ void lcd_write_data16(uint16_t data); /*写多字节数据到LCD*/ ``` ```C /** * @brief 发送命令到LCD,使用轮询方式阻塞等待传输完成(由于数据传输量很少,因此在轮询方式处理可提高速度。使用中断方式的开销要超过轮询方式) * @param cmd 传输的8位命令数据 * @retval 无 */ void lcd_write_cmd(const uint8_t cmd) { LCD_WR(0); spi2_write_cmd(MY_LCD_Handle, cmd); } /** * @brief 发送数据到LCD,使用轮询方式阻塞等待传输完成(由于数据传输量很少,因此在轮询方式处理可提高速度。使用中断方式的开销要超过轮询方式) * @param data 传输的8位数据 * @retval 无 */ void lcd_write_data(const uint8_t *data, int len) { LCD_WR(1); spi2_write_data(MY_LCD_Handle, data, len); } /** * @brief 发送数据到LCD,使用轮询方式阻塞等待传输完成(由于数据传输量很少,因此在轮询方式处理可提高速度。使用中断方式的开销要超过轮询方式) * @param data 传输的16位数据 * @retval 无 */ void lcd_write_data16(uint16_t data) { uint8_t dataBuf[2] = {0,0}; dataBuf[0] = data >> 8; dataBuf[1] = data & 0xFF; LCD_WR(1); spi2_write_data(MY_LCD_Handle, dataBuf,2); } ``` LCD初始化,流程如下: ![输入图片说明](Image/Cover/LCD_Init.png) ###### 2.LCD API 以下为常用LCDAPI,这里简单罗列 ```C void lcd_init(void); /* 初始化LCD */ void lcd_clear(uint16_t color); /* 清屏函数 */ void lcd_scan_dir(uint8_t dir); /* 设置LCD的自动扫描方向 */ void lcd_write_data(const uint8_t *data, int len); /* 发送数据到LCD */ void lcd_write_data16(uint16_t data); /* 发送16位数据到LCD */ void lcd_set_cursor(uint16_t xpos, uint16_t ypos); /* 设置光标的位置 */ void lcd_set_window(uint16_t xstar, uint16_t ystar,uint16_t xend,uint16_t yend); /* 设置窗口大小 */ void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color); /* 在指定区域内填充单个颜色 */ void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint16_t color,uint16_t bg_color); /* 显示len个数字 */ void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint16_t color,uint16_t bg_color); /* 扩展显示len个数字 */ void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint16_t color,uint16_t bg_color); /* 显示字符串 */ void lcd_draw_rectangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,uint16_t color); /* 画一个矩形 */ void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint16_t color); /* 画水平线 */ void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2,uint16_t color); /* 画线函数(直线、斜线) */ void lcd_draw_pixel(uint16_t x, uint16_t y, uint16_t color); /* 绘画一个像素点 */ void lcd_show_char(uint16_t x, uint16_t y, uint8_t chr, uint8_t size, uint8_t mode, uint16_t color,uint16_t bg_color); /* 在指定位置显示一个字符 */ void lcd_show_charImage(uint16_t x_offs,uint16_t y_offs,uint8_t width,uint16_t height,const uint8_t *DISP_ITEM,uint16_t color); //显示基本的字模 void lcd_draw_flash_bmp(uint16_t start_x,uint16_t start_y,uint16_t width,uint16_t heighth,const uint8_t *bmpdata); //显示一个Image2LCD取模的图片 ``` #### 01_key_scan key_scan提供最基本的按键扫描函数,支持简单的按键初始化,按键扫描,等待按键的函数 ![按键函数功能](Image/Cover/KEY_Scan_Func.png) ###### key_scan.h 包含按键的宏定义,包括按键的GPIO,按键按下的标志,按键指令与媒体播放程序的对接 按键GPIO,根据您的需求更改按键的GPIO编号,省略按键IO修改时需要大范围修改主程序的麻烦和疏漏 ```C //三个按键的位置定义 #define KEY1_GPIO GPIO_NUM_15 #define KEY2_GPIO GPIO_NUM_16 #define KEY3_GPIO GPIO_NUM_17 #define BOOT_KEY0_GPIO GPIO_NUM_0 ``` 按键按下的标志,按下按键时,返回.... ```C #define KEY_PRESS_NONE 0 /*没有按键按下*/ #define KEY1_PRES 1 /* KEY1按下 */ #define KEY2_PRES 2 /* KEY2按下 */ #define KEY3_PRES 3 /* KEY3按下 */ #define BOOTKEY_PRES 4 /* BOOTKEY按键 */ ``` 按键指令,对接其他程序,若按键键位有问题,请在这里修改 ```C #define KEYCMD_Player_Next BOOTKEY_PRES /*按键BOOT按下 -- 执行播放下一个 */ #define KEYCMD_Player_FWD KEY2_PRES /*按键2按下 -- 执行快进/上一个*/ #define KEYCMD_Player_REW KEY1_PRES /*按键1按下 -- 执行快退/上一个*/ #define KEYCMD_Player_PP KEY3_PRES /*按键3按下 -- 执行播放/暂停 */ ``` ###### key_init 按键初始化函数 函数说明: 初始化按键.按键初始化函数,本质上初始化的是按键的GPIO 函数: ```C void key_Init(void); //定义按键初始化函数 key_Init(); //按键初始化,调用进行按键初始化 ``` 硬件上我们使用的是下拉按键,因此按键配置如下: ```C gpio_config_t gpio_init_structure; //定义GPIO初始化结构体 //初始化KEY1--------------------------------------------------------------------------------- gpio_init_structure.intr_type = GPIO_INTR_DISABLE; //不使能中断,不需要使用中断 gpio_init_structure.mode = GPIO_MODE_INPUT; //输入模式,注意模式,输入模式 gpio_init_structure.pull_up_en = GPIO_PULLUP_ENABLE; //使能GPIO输入上拉 gpio_init_structure.pull_down_en = GPIO_PULLDOWN_DISABLE; //不需要使能下拉 gpio_init_structure.pin_bit_mask = 1ull << KEY1_GPIO; //传入配置的IO gpio_config(&gpio_init_structure); //执行一次GPIO配置 ``` KEY_Init的完整代码,请转自Key_Init();函数 ###### key_scan函数 函数说明: 进行一次轮询,检查哪个按键被按下. 函数: ```C uint8_t Get_Key_Value(void); //调用一次,进行一次按键轮询 uint8_t i = 0; i = Get_Key_Value(); //轮询一次,检查按键是否被按下 ``` 函数返回值:按下按键的键值,未按下按键返回未按下按键(0) 函数解析: 判断按键是否按下,本质上是读取按键的GPIO电平:若为高电平则表示按键未按下.若为低电平,延迟10ms消抖后再次判断按键,若还是为低电平,表示按键被按下. [备注 按键消抖:金属膜轻触按键弹起的时候可能会出现按键电平在0和1之间跳动,读取到的按键键值不稳定,可能会出现连按的现象.解决消抖的方法:短暂延迟,一般为10ms后再次读取按键键值] ```C if(gpio_get_level(BOOT_KEY0_GPIO) == 0) //按键表现为低电平 { vTaskDelay(10); //延迟10ms,消抖 if(gpio_get_level(BOOT_KEY0_GPIO) == 0) //再次读取按键电平 { KeyTemp = BOOTKEY_PRES; //消抖后还是读取到按键低电平,表示按键被触发 } } ``` 轮询读取按键: ![按键轮询读取流程](Image/Cover/KEY_Read.png) 轮询读取按键的程序源码: ```C //读回按键状态 //三键版本不支持同时按下 uint8_t Get_Key_Value(void) { uint8_t KeyTemp = 0; if(gpio_get_level(BOOT_KEY0_GPIO) == 0) { vTaskDelay(10); if(gpio_get_level(BOOT_KEY0_GPIO) == 0) { KeyTemp = BOOTKEY_PRES; } } else if(gpio_get_level(KEY1_GPIO) == 0) { vTaskDelay(10); if(gpio_get_level(KEY1_GPIO) == 0) { KeyTemp = KEY1_PRES; } } else if(gpio_get_level(KEY2_GPIO) == 0) { vTaskDelay(10); if(gpio_get_level(KEY2_GPIO) == 0) { KeyTemp = KEY2_PRES; } } else if(gpio_get_level(KEY3_GPIO) == 0) { vTaskDelay(10); if(gpio_get_level(KEY3_GPIO) == 0) { KeyTemp = KEY3_PRES; } } else { KeyTemp = 0; } return KeyTemp; } ``` ###### key_wait函数 函数说明: 进行一次轮询,检查哪个按键被按下. 函数: ```C uint8_t waitKey(void); uint8_t i = 0; i = waitKey(); //等待按键按下 ``` 函数解析: 函数调用key_scan();轮询按键按下的情况,若按键按下,跳出循环,返回按键按下的数值.若没有按键按下,继续轮询,直到按键按下. ![等待按键](Image/Cover/Key_waiting.png) ```C /*等待按键*/ /* 等待按键,并且返回按下的按键 */ uint8_t waitKey(void) { uint8_t Temp = 0; while(1) { Temp = Get_Key_Value(); if(Temp != 0) { printf("KEY ID is:%d \r\n",Temp); break; } else { Temp = 0; } vTaskDelay(100); } return Temp; } ``` #### 01_fatfs_test 测试SD卡和FATFS