1.9 串口绘图 - 实验篇
1.9 串口绘图 - 实验篇
Sarzn- 利用 UART, 单片机向电脑端发送数据, 实现指定函数值绘制并显示灯状态
- 利用 UART 电脑端向单片机发送指令让单片机点亮 LED
LED1: PG1, 原理图中低电平点亮 UART2: PD5 PD6, 在 A
板预留蓝牙接口
时钟树 HSE 配置为 12M 不要忘记
CubeMX
配置LED1:
PG1

代码
把我们要用到的板级支持包 User目录从 GitHub 仓库拷过来.
记得做如下修改
导入库 1
2
3
4
5
6/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
定义用户变量 1
2
3
4
5
6/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t tx_buffer[12];
uint8_t rx_buffer[10];
/* USER CODE END PV */
写回调函数 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/* USER CODE BEGIN PFP */
/**
* @brief HAL 库 UART 接收 DMA 空闲中断
* @param Buffer UART编号
* @param Length 长度
*/void Serialplot_Call_Back(uint8_t *Buffer, uint16_t Length)
{
if (rx_buffer[0] == '0')
{
BSP_LED_1(BSP_LED_Status_DISABLED);
}
else if (rx_buffer[0] == '1')
{
BSP_LED_1(BSP_LED_Status_ENABLED);
}
else if (rx_buffer[0] == '2')
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
}
}
/* USER CODE END PFP */
初始化 UART2 1
2
3/* USER CODE BEGIN 2 */
Uart_Init(&huart2, rx_buffer, 10, Serialplot_Call_Back);
/* USER CODE END 2 */
主程序 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/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
static uint32_t flag;
if (flag == 2500)
{
flag = 0;
}
float tmp_data = ((float)flag / 1000.0f) * ((float)flag / 1000.0f);
float led_status = !HAL_GPIO_ReadPin(LED1_GPIO_Port, LED1_Pin);
memcpy(&tx_buffer[0], &tmp_data, 4);
memcpy(&tx_buffer[4], &led_status, 4);
// 帧尾:00 00 80 7F
tx_buffer[8] = 0x00;
tx_buffer[9] = 0x00;
tx_buffer[10] = 0x80;
tx_buffer[11] = 0x7F;
UART_Send_Data(&huart2, tx_buffer, 12);
flag++;
HAL_Delay(0);
/* USER CODE END WHILE */
串口绘图软件我推荐使用SerialPlot, 但是这个只有 Windows 版本, 如果在 mac 上使用你要下载源码自己build 一下, 不想这么麻烦的话 mac 上也可以使用功能更全的跨平台的Serial Studio
来到串口绘图软件, 如果实在没有的话可以使用网页版 https://captainkaz.github.io/web-serial-plotter/
使用 Serial Studio 的话因为它没有自带的解析二进制浮点数的模式所以我们要手动写一个 js 解析
1 | function parse(frame) { |
如果使用这个网页版的话自定义解析器插入这一段代码: 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// ===== 配置区(只改这里) =====
var CHANNELS = 4; // 通道数
var LITTLE_ENDIAN = true; // true=小端(STM32), false=大端
var BYTES_PER_VAL = 4; // float=4, double=8
var HEADER_BYTES = 0; // 帧头字节数,没有填 0
var FOOTER_BYTES = 4; // 帧尾字节数,没有填 0
var HEADER_VALUE = []; // 帧头字节值,不校验填 []
var FOOTER_VALUE = [0x00,0x00,0x80,0x7F]; // 帧尾字节值,不校验填 []
// ==================
var dataBytes = CHANNELS * BYTES_PER_VAL;
var FRAME_LEN = HEADER_BYTES + dataBytes + FOOTER_BYTES;
if (uint8ArrayData.length < FRAME_LEN) {
return { values: null, frameByteLength: 0 };
}
if (HEADER_BYTES > 0 && HEADER_VALUE.length === HEADER_BYTES) {
for (var h = 0; h < HEADER_BYTES; h++) {
if (uint8ArrayData[h] !== HEADER_VALUE[h]) {
return { values: null, frameByteLength: 1 };
}
}
}
if (FOOTER_BYTES > 0 && FOOTER_VALUE.length === FOOTER_BYTES) {
var footStart = HEADER_BYTES + dataBytes;
for (var f = 0; f < FOOTER_BYTES; f++) {
if (uint8ArrayData[footStart + f] !== FOOTER_VALUE[f]) {
return { values: null, frameByteLength: 1 };
}
}
}
var dv = new DataView(
uint8ArrayData.buffer,
uint8ArrayData.byteOffset,
uint8ArrayData.byteLength
);
function readValue(off, n, le) {
return (n === 8) ? dv.getFloat64(off, le) : dv.getFloat32(off, le);
}
var values = [];
for (var i = 0; i < CHANNELS; i++) {
values.push(readValue(HEADER_BYTES + i * BYTES_PER_VAL, BYTES_PER_VAL, LITTLE_ENDIAN));
}
return { values: values, frameByteLength: FRAME_LEN };









