<<<写在前面>>>

前段时间自己做了一套M910X的亚克力外壳,设计之初考虑到侧面能够添加自己的副屏,再加上曾做过点阵RGB,于是也设置了防止RGB点阵的位置。最初的想法是在这个设备上显示视频,但是刷新率太低了,没有做到 24fps ,放弃了显示视频,转而用了一个简单的PC机副屏,显示一些电脑性能信息,比如CPU频率,内存使用情况,硬盘使用情况,网络情况等等。另外这个副屏外面还做了一圈 RGB。
这个副屏由这几个部分组成,四块 SPI ST7735 屏幕(160*128*4)和一圈ws2812b 灯珠组成,主控是 STM32F103C8T6 和 ESP8266组成。stm32 主要作用是控制屏幕刷新和点亮 RGB ,esp8266 主要作用是通过串口发送 json 数据,让 STM32 解析显示。

这个副屏由这几个部分组成,四块 SPI ST7735 屏幕(160*128*4)和一圈ws2812b 灯珠组成,主控是 STM32F103C8T6 和 ESP8266组成。stm32 主要作用是控制屏幕刷新和点亮 RGB ,esp8266 主要作用是通过串口发送 json 数据,让 STM32 解析显示。

飞线
飞线
亚克力外壳
亚克力外壳
亚克力外壳
亚克力外壳

下面是副屏

副屏
副屏
副屏UI
副屏UI

硬件部分

SPI ST7735这部分硬件设计,四块屏幕共用一组SPI,通过不同的CS来控制不同的屏幕显示。原本有做背光控制,但是买回来的屏幕上电后背光默认开启,有面有空再研究研究啥情况。

12
12

layout

11
11

3D 图

3D_左侧USB3.0拓展卡
3D_左侧USB3.0拓展卡

关于背光控制电路

背光控制需要使用pwm调光,买回来的屏幕问了客服说不支持调光,我看了下lcd的板子,上面的背光电路被强行打开了,所以调不了。重新返工屏上线路后如下图,左侧是修改前,右侧是修改后 + 板上电路。将R1483V3断开,BLK引脚连接主板控制引脚。当BL_CTRL_PWM为低电平时 UGS<0U_{GS}<0 ,有压差导通,BLK信号高电平。裸屏背光信号BLGND导通,有电流流过,背光亮,反之背光暗。

pwm调背光
pwm调背光

软件部分

时序设计

第一次做时序打算使用GPIO外部中断,但实际测试,想用GPIO外部中断,发现中断触发时序概率性错乱导致卡死,所以换成了串口中断。

这是条失败的路

时序框图

这个时序框图不是完整的,有些细节部分没有写出来,但是大致思路是这样

时序框图
时序框图

这是实际时序

时序
时序

调试过程

过程中遇到些许问题,有时候调得头皮发麻,一整天愣是解不出来

下图是第一次进入中断时也就是发送字符长度的时候

Debug1
Debug1

放大之后是这样的

Debug1放大
Debug1放大

中断进入debug ,奇怪的是我的代码中串口发送是写在进入STM32 触发中断(GPIO6 边沿触发)后的

esp8266片段代码截图 1
esp8266片段代码截图 1

实际效果不影响 STM32 串口接收数据。这是实际效果

STM32 串口接收数据
STM32 串口接收数据
实际效果 1
实际效果 1

下面讲成功的时序

时序总图

时序总图
时序总图

时序大概是这样:

  • 8266:上电初始化->连接WiFi->主循环侦测UDP连接
  • STM32: 上电初始化->打开串口中断->进入主循环显示

调试过程

上电瞬间及初始化

此处解决8266上电过程中串口有段杂波干扰STM32串口接收

上电瞬间ESP8266串口会有一小段杂波,最初做GPIO外部中断就是为了避免这部分,但是最终想了很多办法没有解决。如下图D1通道,前面一小段就是杂波。右边的两小段下拉是连接Wi-Fi时发送的数据.

上电瞬间时序总图
上电瞬间时序总图

这里是初始化HAL库函数和四块屏幕后,进入while循环前的代码,可以看到右侧2处D1和D0通道就是串口中断产生的结果,由于一次直接收一个字符,所以把异常数据都挤掉了。猜测1处可能是拉高后打开了 串口中断,进入中断后又被拉低了。

上电初始化时序
上电初始化时序
代码截图1
代码截图1

下面是中断放大后的时序图以及对应的代码,高电平一直触发单个字节接收中断。

时序代码截图_1
时序代码截图_1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 高电平 接收数据 */
if (pin_pb6_value == 1) {
/* 回复8266,GPIO拉底 解锁 已经完成第一字节串口接收 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
/* 串口 1debug */
HAL_UART_Transmit(&huart1, &USART_RX_LINE,
atoi(USART_RX_ONE_LINE) + 1, 100);

CS++;
if(CS>4){
CS = 1;
}

/* 串口中断开启接收长度 1 */
HAL_UART_Receive_IT(&huart2, &USART_RX_ONE_LINE_LEN, 1);

}
串口中断部分

这是串口中断部分时序

串口中断放大图
串口中断放大图
串口中断代码片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/****************************************************************************
* 函数名: HAL_UART_RxCpltCallback()
* 功 能: 串口中断函数
* 输 入: None
* 全局变量 ALL
* 输 出: 无
*/

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART2) // ???????????? USART1
{
/* 串口 1debug */
HAL_UART_Transmit(&huart1, "IN UART_RxCpltCallback\n", 23, 100);
/*判断 PB6 为上升或下降沿触发*/
pin_pb6_value = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6);
/* 高电平 接收数据 */
if (pin_pb6_value == 1) {
/* 回复8266,GPIO拉底 解锁 已经完成第一字节串口接收 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
/* 串口 1debug */
HAL_UART_Transmit(&huart1, &USART_RX_LINE,
atoi(USART_RX_ONE_LINE) + 1, 100);
CS++;
if (CS > 4) {
CS = 1;
}
/* 串口中断开启接收长度 1 */
HAL_UART_Receive_IT(&huart2, &USART_RX_ONE_LINE_LEN, 1);
}
/* 低电平 接收长度 */
if (pin_pb6_value == 0) {
/* 串口 1debug */
HAL_UART_Transmit(&huart1, "pin_pb6_value == 0\n", 19, 100);
/* 回复8266,GPIO拉底 解锁 已经完成第一字节串口接收 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
/* 串口 1debug */
HAL_UART_Transmit(&huart1, "GPIO7 DOWN\n", 11, 100);
/* 串口 2 接收长度2 */
HAL_UART_Receive(&huart2, &USART_RX_ONE_LINE,
atoi(USART_RX_ONE_LINE_LEN) + 2, 0xFFFF);
/* 回复8266,GPIO拉高 已经完成第二字节串口接收 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
/* 串口 1debug */
HAL_UART_Transmit(&huart1, "GPIO7 UP\n", 9, 100);
/* 串口 1debug */
HAL_UART_Transmit(&huart1, "\n############################\n", 30,
100);
/* 串口 1debug */
HAL_UART_Transmit(&huart1, &USART_RX_ONE_LINE_LEN, 1, 100);
/* 串口 1debug */
HAL_UART_Transmit(&huart1, "\n############################\n", 30,
100);
/* 串口 1debug */
HAL_UART_Transmit(&huart1, &USART_RX_ONE_LINE,
atoi(USART_RX_ONE_LINE_LEN) + 2, 100);
/* 串口 1debug */
HAL_UART_Transmit(&huart1, "\n############################\n", 30,
100);
/* 开启下一次接收数据内容中断 */
HAL_UART_Receive_IT(&huart2, &USART_RX_LINE,
atoi(USART_RX_ONE_LINE) + 2);
}
}
}

下面是三个串口数据发送过程

第一步

获取数据长度的长度

预发送长度为1045长度的字符串,1045的长度为4,这里指的就是4

ESP8266 拉低 GPIO6 做准备,接着ESP8266发送串口数据触发STM32串口中断。

串口中断放大图1
串口中断放大图1
第二步:

获取数据的长度

上一步完成接收进入中断,STM32拉高GPIO7,进入阻塞串口接收,此时ESP8266(串口中断放大图2)ESP8266等待100ms后发送数据长度,STM32接收完成拉高GPIO7,ESP8266退出死循环进入下一步发送数据内容。STM32退出中断前打开串口中断,准备接收数据。

串口中断放大图2
串口中断放大图2

对ESP8266应代码如下,等待GPIO7被拉高。

1
2
3
4
5
6
7
8
9
10
11
12
// 打印长度 尾部带有\r
Serial.print(static_cast<int>(strlen(substring)));

delay(100);

// 打印长度
Serial.println(Tcp_len);

// 等待STM32响应,调试需注释
while (!digitalRead(4)) { //等待响应
delay(80);
}
第三步:

获取数据内容

上一步GPIO7被拉高后,ESP8266发送数据内容,STM32进入串口中断,识别到GPIO7为高,拉低GPIO7,打开串口中断,STM32准备接收下一次数据长度的长度。

串口中断放大图3
串口中断放大图3

时序设计部分到这里就结束了。

UI

UI部分是直接使用st7735驱动中自带的,简单做了下,自己做的部分主要是换字体,换成了霞鹜文楷等宽。这部分我没有去看具体显示文本的流程,暂时阶段是能用就行。

关于图像显示,我在Image转RGB565工具放了两个摁钮,可以将RGB图像转成RGB565的字符串,目前颜色部分好像还是有点问题(原本蓝色在屏上会显示出绿色),但是基本形状是正确的。

<<<文末闲语>>>

久违的代码激情占用了几乎所有的时间,后面的慢慢更新固件吧,第一次做了一个完整的双主控项目。其实一个PC机也是如此,CPU是主控,微控制器也是主控,较老的机型还分南北桥,相互访问的桥梁就是PCB上的电路。说起来PC很复杂,但也很简单。

公元2024年了,科技越来越进步,我想,CPU的终极形态是将一切的一切集成起来吧,ssd,内存,显卡,wifi等等外设,也不知道那是硬件工程师会不会失业哈哈哈哈哈哈哈。也许想得太多太远,当下还应好好进步,在深度学习和智能AI的时代,layout可能会被替代,那时会不会机器人能自建工厂自行组装呢。。。就好像生物的繁衍一样。。。