基于立创·梁山派的智能LED灯带遥控器设计
简介
基于立创·梁山派的智能LED灯带遥控器,跟我的毕设一脉相承,但搭载更顶级的触摸屏
简介:基于立创·梁山派的智能LED灯带遥控器,跟我的毕设一脉相承,但搭载更顶级的触摸屏开源协议
:GPL 3.0
描述
一、前言
前些阵子我完成了毕设的代码部分以及部分的硬件部分,偶然间看到了可以免费申请立创梁山派,于是就想到把我的毕设移植到立创梁山派中。
二、项目功能
本项目是根据毕设的遥控器部分移植而来的,遥控器的具体功能就是运行用户GUI界面,用户通过操作GUI界面可以无线控制LED灯带的各种属性,如改变颜色、切换显示模式等等。
无线控制的功能通过ESP8266模块实现,使用WiFi协议栈与LED灯带控制器进行通信。
三、项目方案
通过上面分析本项目的功能需求,我们只需要准备触控屏和WiFi模块。
为了方便硬件设计,使最终的硬件更加美观,触控屏最好选择与梁山派差不多大小的型号,最终我选择了一块3.5寸的IPS电容触摸屏作为本项目的屏幕。
相比于TFT显示屏,IPS显示屏能拥有更好的可视角度,色域表现也要好与常规的LCD显示屏。触摸方案选择电容屏,是因为这种触摸方式优雅;大多数用于学习板的屏幕都喜欢使用较便宜的电阻触摸屏,电阻触摸屏的缺点就是要很大力地按(有时候要戳)才能读取到触摸点;而使用电容触摸方案的话只需轻点即可读取到触摸点。
显示屏的控制芯片为ST7796U,触摸芯片为GT911。
WiFi模块方面,因为本项目只需要实现基本的WiFi功能,即使用TCP与LED灯带控制器通信,通信包的协议也是我设计的,所以这里选择最便宜的ESP8266模块即可。
四、硬件设计
1. 显示屏
在梁山派上提供了一个TFT屏幕的排线连接座,该排线座可以使用EXMC外设对显示屏进行通信。
因此只需要设计一个与之对应的排线座连接即可。梁山派上的这个排线座不仅支持8080并口协议,还支持SPI和I2C协议,所以有一些通信管脚是不需要的。但因为我的屏幕带触摸功能,触摸芯片使用I2C协议进行通信,因此我巧妙地将排线座上I2C通信的管脚留给了触摸芯片;后面发现还差了一根复位线,这里就用SPI3的片选线代替了。
因为屏幕是通过排针连接的,所以也要设计一个与之对应的排母座进行连接。使用万用表进行测试,我发现厂家的屏幕板上面是没有I2C的上拉电阻的,因此在这边加上。
2. ESP8266模块
WiFi模块的硬件设计也不难,只要跟着乐鑫官方的硬件设计指南,设计一个ESP8266模块的最小系统即可。
ESP8266的串口会比较鸡肋,只有两个,而且两个串口的其中一根线是复用的。在硬件设计中我把ESP8266的串口0与梁山派的串口2进行连接,但因为ESP8266的串口0是下载接口,因此我设计了一个排针座;当需要往ESP8266下载程序时,断开与梁山派的连接;当正常使用时,用跳帽连接排针,实现与梁山派的通信。
五、软件设计
本项目最难的地方在于软件的设计,当中涉及到GD32单片机中EXMC外设的使用,及如何使用EXMC外设驱动LCD屏幕;为了充分发挥梁山派的性能,我往里面移植了FreeRTOS系统;GUI界面使用当下很火的LVGL库实现,该库的移植和应用也是不小的挑战。
1. 显示屏
显示屏的EXMC外设部分核心驱动代码。
/**
* @brief Initialized EXMC
* @param None
* @retval None
*/
static void ST7796_EXMCInit(void)
{
exmc_norsram_parameter_struct exmc_init_struct;
exmc_norsram_timing_parameter_struct exmc_timing_struct;
/* config FMC clock */
rcu_periph_clock_enable(EXMC_CLK);
exmc_timing_struct.asyn_access_mode = EXMC_ACCESS_MODE_B;
exmc_timing_struct.asyn_address_setuptime = 5; // 5 / 240MHz ≈ 20ns
exmc_timing_struct.asyn_data_setuptime = 4; // (4 + 1) / 240MHz ≈ 20ns
exmc_timing_struct.asyn_address_holdtime = 0; // unused
exmc_timing_struct.bus_latency = 0; // unused
exmc_timing_struct.syn_data_latency = 0; // unused
exmc_timing_struct.syn_clk_division = 0; // unused
exmc_init_struct.address_data_mux = DISABLE;
exmc_init_struct.asyn_wait = DISABLE;
exmc_init_struct.burst_mode = DISABLE;
exmc_init_struct.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;
exmc_init_struct.extended_mode = DISABLE;
exmc_init_struct.memory_type = EXMC_MEMORY_TYPE_NOR;
exmc_init_struct.memory_write = ENABLE;
exmc_init_struct.norsram_region = EXMC_BANK0_REGIONx;
exmc_init_struct.nwait_config = EXMC_NWAIT_CONFIG_BEFORE;
exmc_init_struct.nwait_polarity = EXMC_NWAIT_POLARITY_LOW;
exmc_init_struct.nwait_signal = DISABLE;
exmc_init_struct.wrap_burst_mode = DISABLE;
exmc_init_struct.write_mode = EXMC_ASYN_WRITE;
exmc_init_struct.read_write_timing = &exmc_timing_struct;
exmc_init_struct.write_timing = &exmc_timing_struct;
exmc_norsram_init(&exmc_init_struct);
exmc_norsram_enable(EXMC_BANK0_REGIONx);
}
在设置时序的时候,地址建立时间和数据建立时间在数据手册中是10ns左右,为了保险设置为20ns左右。还要注意,梁山派上的8080并口DC信号对应A10地址线,片选信号对应NE3,也就是Region3,这对于确定数据和命令的读写地址尤为重要。
下面是显示屏的数据写和命令写代码,因为我们已经配置好EXMC外设,所以向显示屏写数据就好像给单片机内存赋值一样简单。
/**
* @brief Write command
* @param cmd: command
* @retval None
*/
__inline void ST7796_WriteCmd(uint16_t cmd)
{
*( __IO uint16_t*)(EXMC_Addr_ST7796_CMD) = cmd;
}
/**
* @brief Write data
* @param data: data
* @retval None
*/
__inline void ST7796_WriteData(uint16_t data)
{
*( __IO uint16_t*)(EXMC_Addr_ST7796_DATA) = data;
}
2. WiFi模块
ESP8266使用Arduino框架进行编写,代码很简单就是负责把梁山派通过串口发送过来的数据,通过WiFi转发到LED灯带控制器端即可。
static void dump_buf_to_serial(uint8_t* buf, uint16_t size) {
Serial.print("Data: ");
for (uint16_t i = 0; i < size; i++) {
Serial.printf("%02X ", buf[i]);
}
Serial.println();
}
static void receive_package(void) {
memset(cmd_buf, 0x00, sizeof(cmd_buf));
Serial.read(cmd_buf, sizeof(cmd_buf));
for (cmd_size = sizeof(cmd_buf); !cmd_buf[cmd_size-1]; cmd_size--) {}
Serial.println("Receive from host");
dump_buf_to_serial(cmd_buf, cmd_size);
}
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PSW);
Serial.print("WiFi connecting");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(500);
}
Serial.printf("\nIP: %s\n", WiFi.localIP().toString().c_str());
}
void loop() {
if (Serial.available()) {
delay(10);
receive_package();
if (client.connect(SERVER_IP, SERVER_PORT)) {
Serial.println("Connected to server");
while (client.connected()) {
client.write(cmd_buf, cmd_size);
uint16_t timeout = TIMEOUT_MS;
while (timeout--) {
if (resp_size = client.available()) {
memset(resp_buf, 0x00, sizeof(resp_buf));
client.read(resp_buf, resp_size);
Serial.println("Receive from server");
dump_buf_to_serial(resp_buf, resp_size);
break;
}
delay(1);
}
break;
}
}
else {
Serial.println("Connected to server failed");
}
while(Serial.read() > 0);
}
}
在主机端,为了提高数据发送给ESP8266的效率,我还使用DMA进行串口发送,这样主机在串口数据发送的时候就可以完成其他任务。
dma_single_data_parameter_struct dma_init_struct;
dma_deinit(ESP8266_DMAx, ESP8266_DMA_CHy);
dma_init_struct.memory0_addr = (uint32_t)buf;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.periph_addr = (uint32_t)ESP8266_USART_DR;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_init_struct.number = size;
dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
dma_single_data_mode_init(ESP8266_DMAx, ESP8266_DMA_CHy, &dma_init_struct);
dma_channel_subperipheral_select(ESP8266_DMAx, ESP8266_DMA_CHy, DMA_SUBPERI4);
dma_flag_clear(ESP8266_DMAx, ESP8266_DMA_CHy, DMA_FLAG_FTF);
dma_channel_enable(ESP8266_DMAx, ESP8266_DMA_CHy);
3. 屏幕UI
GUI使用了当下较火的LVGL库进行设计,该库最低需要64KB的Flash空间和8KB的RAM空间,但对于性能强大的梁山派简直不在话下啦。
在使用LVGL进行GUI设计时,建议搭建一个模拟器,在模拟器中把GUI代码写好,再移植到单片机中写交互代码,这样就省去了频繁烧录的麻烦。
六、附件资料
梁山派主控和ESP8266的源码在下面的Remote_Controller_Branch1.zip压缩包中,梁山派的主控使用Keil5开发,ESP8266使用VSCode+Platformio+Arduino开发
ESP8266的相关开发资料在下面的0a-esp8266ex_datasheet_cn.pdf、esp8266-technical_reference_cn.pdf、esp-wroom-02d_esp-wroom-02u_datasheet_cn.pdf文档中
评论