STM32 + 淘晶驰T1串口屏波形显示完整方案
一、STM32硬件连接
1. 接线方式
T1显示屏 STM32F103C8T6
TX ----------> PA3 (USART2_RX)
RX ----------> PA2 (USART2_TX)
GND ----------> GND
VCC ----------> 3.3V/5V
二、STM32固件开发
1. CubeMX配置
c
// 使用USART2与T1屏幕通信
// 波特率:115200
// 数据位:8
// 停止位:1
// 无校验位
2. 核心代码实现
c
/* t1_display.h */
#ifndef __T1_DISPLAY_H
#define __T1_DISPLAY_H
#include "stm32f1xx_hal.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// T1指令结束符
#define T1_END_CMD "\xFF\xFF\xFF"
// 波形控件ID
#define WAVE_ID 1
#define VALUE_TEXT_ID 10
#define STATUS_TEXT_ID 11
// 函数声明
void T1_Init(UART_HandleTypeDef *huart);
void T1_SendString(char *str);
void T1_SendCmd(char *cmd);
void T1_ClearWaveform(void);
void T1_AddWavePoint(uint16_t value);
void T1_AddWavePoints(uint16_t *values, uint8_t count);
void T1_UpdateValueText(float value);
void T1_UpdateStatus(char *status);
void T1_SetWaveRange(uint16_t y_min, uint16_t y_max);
#endif
c
/* t1_display.c */
#include "t1_display.h"
static UART_HandleTypeDef *t1_huart;
static char tx_buffer[128];
// 初始化
void T1_Init(UART_HandleTypeDef *huart) {
t1_huart = huart;
HAL_Delay(1000); // 等待屏幕启动
// 清空屏幕
T1_SendCmd("cls");
HAL_Delay(100);
// 设置波形范围 (ADC 12位: 0-4095)
T1_SetWaveRange(0, 4096);
// 更新状态
T1_UpdateStatus("Ready");
}
// 发送字符串到T1屏幕
void T1_SendString(char *str) {
uint16_t len = strlen(str);
HAL_UART_Transmit(t1_huart, (uint8_t*)str, len, HAL_MAX_DELAY);
}
// 发送完整指令
void T1_SendCmd(char *cmd) {
sprintf(tx_buffer, "%s%s", cmd, T1_END_CMD);
T1_SendString(tx_buffer);
}
// 清空波形
void T1_ClearWaveform(void) {
sprintf(tx_buffer, "cle %d,0", WAVE_ID);
T1_SendCmd(tx_buffer);
}
// 添加单个波形点
void T1_AddWavePoint(uint16_t value) {
sprintf(tx_buffer, "add %d,0,%d", WAVE_ID, value);
T1_SendCmd(tx_buffer);
}
// 批量添加波形点 (更高效)
void T1_AddWavePoints(uint16_t *values, uint8_t count) {
if (count == 0) return;
sprintf(tx_buffer, "addt %d,0,%d", WAVE_ID, values[0]);
for (uint8_t i = 1; i < count; i++) {
char temp[10];
sprintf(temp, ",%d", values[i]);
strcat(tx_buffer, temp);
}
T1_SendCmd(tx_buffer);
}
// 更新数值显示
void T1_UpdateValueText(float value) {
sprintf(tx_buffer, "t%d.txt=\"%.2f\"", VALUE_TEXT_ID, value);
T1_SendCmd(tx_buffer);
}
// 更新状态显示
void T1_UpdateStatus(char *status) {
sprintf(tx_buffer, "t%d.txt=\"%s\"", STATUS_TEXT_ID, status);
T1_SendCmd(tx_buffer);
}
// 设置波形显示范围
void T1_SetWaveRange(uint16_t y_min, uint16_t y_max) {
sprintf(tx_buffer, "yadd %d,0,%d,%d", WAVE_ID, y_min, y_max);
T1_SendCmd(tx_buffer);
}
3. ADC采集 + 波形显示主程序
c
/* main.c */
#include "main.h"
#include "adc.h"
#include "usart.h"
#include "t1_display.h"
#include <math.h>
#define ADC_BUFFER_SIZE 200
#define SAMPLE_RATE 100 // Hz
#define DISPLAY_RATE 20 // Hz - 屏幕刷新率
uint16_t adc_buffer[ADC_BUFFER_SIZE];
uint16_t adc_index = 0;
uint32_t last_sample_time = 0;
uint32_t last_display_time = 0;
uint8_t display_enabled = 1;
// ADC完成回调
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
if (hadc->Instance == ADC1) {
adc_buffer[adc_index] = HAL_ADC_GetValue(hadc);
adc_index = (adc_index + 1) % ADC_BUFFER_SIZE;
}
}
// 触摸回调处理 (需要在串口接收中断中解析)
void T1_TouchCallback(uint8_t page_id, uint8_t control_id) {
switch(control_id) {
case 20: // 开始按钮
display_enabled = 1;
T1_UpdateStatus("Running");
break;
case 21: // 暂停按钮
display_enabled = 0;
T1_UpdateStatus("Paused");
break;
case 22: // 清空按钮
T1_ClearWaveform();
T1_UpdateStatus("Cleared");
break;
}
}
// 解析串口接收数据 (T1触摸返回)
void USART2_RxCpltCallback(void) {
// T1触摸数据格式: touch j,page_id,control_id
// 需要解析串口接收缓冲区
// 这里省略具体解析代码
}
int main(void) {
HAL_Init();
SystemClock_Config();
// 外设初始化
MX_GPIO_Init();
MX_ADC1_Init();
MX_USART2_UART_Init();
// T1显示屏初始化
T1_Init(&huart2);
// 启动ADC DMA连续采集
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUFFER_SIZE);
// 主循环
while (1) {
uint32_t current_time = HAL_GetTick();
// 定期更新波形显示
if (display_enabled && (current_time - last_display_time >= 1000/DISPLAY_RATE)) {
last_display_time = current_time;
// 获取最新ADC值
uint16_t latest_value = adc_buffer[(adc_index - 1 + ADC_BUFFER_SIZE) % ADC_BUFFER_SIZE];
// 更新波形
T1_AddWavePoint(latest_value);
// 更新数值显示 (可选滤波)
T1_UpdateValueText(latest_value * 3.3 / 4096.0); // 转换为电压值
// 状态显示采样率
static uint16_t sample_count = 0;
sample_count++;
if (sample_count % 50 == 0) {
char status[20];
sprintf(status, "%d Hz", DISPLAY_RATE);
T1_UpdateStatus(status);
}
}
// 其他处理
// ...
HAL_Delay(1);
}
}
三、高级功能扩展
1. 双通道ADC波形显示
c
/* dual_channel_adc.c */
#define CH1_WAVE_ID 1
#define CH2_WAVE_ID 2
// 双通道ADC采集
void DisplayDualChannelWave(void) {
uint16_t ch1_value = Read_ADC_CH1();
uint16_t ch2_value = Read_ADC_CH2();
// 通道1 - 红色
sprintf(tx_buffer, "add %d,0,%d", CH1_WAVE_ID, ch1_value);
T1_SendCmd(tx_buffer);
// 通道2 - 绿色
sprintf(tx_buffer, "add %d,0,%d", CH2_WAVE_ID, ch2_value);
T1_SendCmd(tx_buffer);
// 更新数值显示
sprintf(tx_buffer, "t1.txt=\"CH1:%.2fV\"", ch1_value * 3.3 / 4096.0);
T1_SendCmd(tx_buffer);
sprintf(tx_buffer, "t2.txt=\"CH2:%.2fV\"", ch2_value * 3.3 / 4096.0);
T1_SendCmd(tx_buffer);
}
2. FFT频谱显示
c
/* fft_display.c */
#include "arm_math.h"
#define FFT_SIZE 256
#define SAMPLE_FREQ 1000 // Hz
float32_t fft_input[FFT_SIZE * 2];
float32_t fft_output[FFT_SIZE];
uint16_t spectrum_bars[64]; // 频谱柱状图数据
void ProcessFFTAndDisplay(void) {
static uint16_t sample_count = 0;
// 采集时域数据
fft_input[sample_count * 2] = adc_buffer[sample_count] * 3.3 / 4096.0; // 实部
fft_input[sample_count * 2 + 1] = 0; // 虚部
sample_count++;
if (sample_count >= FFT_SIZE) {
sample_count = 0;
// 执行FFT
arm_cfft_instance_f32 fft_instance;
arm_cfft_init_f32(&fft_instance, FFT_SIZE);
arm_cfft_f32(&fft_instance, fft_input, 0, 1);
// 计算幅度
arm_cmplx_mag_f32(fft_input, fft_output, FFT_SIZE);
// 转换为频谱柱状图 (64个柱子)
for (int i = 0; i < 64; i++) {
uint16_t start = i * 2;
uint16_t end = (i + 1) * 2;
float32_t sum = 0;
for (int j = start; j < end && j < FFT_SIZE/2; j++) {
sum += fft_output[j];
}
spectrum_bars[i] = (uint16_t)(sum * 100); // 缩放
if (spectrum_bars[i] > 4096) spectrum_bars[i] = 4096;
}
// 发送频谱数据到T1屏幕
T1_DisplaySpectrum(spectrum_bars, 64);
}
}
void T1_DisplaySpectrum(uint16_t *bars, uint8_t count) {
// 使用柱状图显示
sprintf(tx_buffer, "addt %d,0,%d", 3, bars[0]); // 控件ID=3为频谱图
for (uint8_t i = 1; i < count; i++) {
char temp[10];
sprintf(temp, ",%d", bars[i]);
strcat(tx_buffer, temp);
}
T1_SendCmd(tx_buffer);
}
3. 数据记录与回放
c
/* data_logger.c */
#define LOG_BUFFER_SIZE 1000
typedef struct {
uint16_t data[LOG_BUFFER_SIZE];
uint32_t timestamp[LOG_BUFFER_SIZE];
uint16_t index;
uint8_t recording;
} DataLogger_t;
DataLogger_t logger = {0};
// 开始记录
void StartLogging(void) {
logger.recording = 1;
logger.index = 0;
T1_UpdateStatus("Recording...");
}
// 记录数据
void LogData(uint16_t value) {
if (!logger.recording || logger.index >= LOG_BUFFER_SIZE)
return;
logger.data[logger.index] = value;
logger.timestamp[logger.index] = HAL_GetTick();
logger.index++;
}
// 回放数据
void PlaybackData(void) {
T1_ClearWaveform();
T1_UpdateStatus("Playing back...");
for (uint16_t i = 0; i < logger.index; i++) {
T1_AddWavePoint(logger.data[i]);
T1_UpdateValueText(logger.data[i] * 3.3 / 4096.0);
// 控制回放速度
HAL_Delay(10); // 100Hz回放
// 可以在这里添加暂停/停止检查
}
T1_UpdateStatus("Playback done");
}
四、性能优化技巧
1. DMA双缓冲优化
c
// 使用DMA双缓冲减少CPU干预
#define BUFFER_SIZE 256
uint16_t adc_buffer1[BUFFER_SIZE];
uint16_t adc_buffer2[BUFFER_SIZE];
volatile uint8_t buffer_ready = 0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
static uint8_t buffer_select = 0;
if (buffer_select == 0) {
// buffer1已满,处理数据
buffer_ready = 1;
buffer_select = 1;
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer2, BUFFER_SIZE);
} else {
// buffer2已满,处理数据
buffer_ready = 2;
buffer_select = 0;
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer1, BUFFER_SIZE);
}
}
2. 数据压缩传输
c
// 差值压缩算法
typedef struct {
uint16_t base_value;
int8_t diff[8]; // -128 ~ 127的差值
} CompressedData_t;
void SendCompressedWaveData(uint16_t *data, uint8_t count) {
CompressedData_t compressed;
compressed.base_value = data[0];
for (uint8_t i = 1; i < count && i < 9; i++) {
compressed.diff[i-1] = (int8_t)(data[i] - data[i-1]);
}
// 发送压缩数据
sprintf(tx_buffer, "cmp %d,%d", WAVE_ID, compressed.base_value);
for (uint8_t i = 0; i < count-1; i++) {
char temp[8];
sprintf(temp, ",%d", (int)compressed.diff[i]);
strcat(tx_buffer, temp);
}
T1_SendCmd(tx_buffer);
}
五、调试与测试
1. 串口调试函数
c
void Debug_SendToPC(char *format, ...) {
va_list args;
va_start(args, format);
char debug_buffer[128];
vsprintf(debug_buffer, format, args);
// 通过USART1发送到PC
HAL_UART_Transmit(&huart1, (uint8_t*)debug_buffer, strlen(debug_buffer), 100);
HAL_UART_Transmit(&huart1, (uint8_t*)"\r\n", 2, 100);
va_end(args);
}
2. 系统状态监控
c
void Monitor_SystemStatus(void) {
static uint32_t last_monitor_time = 0;
uint32_t current_time = HAL_GetTick();
if (current_time - last_monitor_time > 1000) {
last_monitor_time = current_time;
// 发送状态信息到PC
Debug_SendToPC("ADC Index: %d", adc_index);
Debug_SendToPC("Free Heap: %lu", xPortGetFreeHeapSize());
Debug_SendToPC("CPU Usage: %d%%", getCPUUsage());
}
}
六、项目文件结构
Project/
├── Core/
│ ├── Src/
│ │ ├── main.c
│ │ ├── t1_display.c
│ │ ├── adc_handler.c
│ │ └── fft_processor.c
│ └── Inc/
│ ├── t1_display.h
│ ├── adc_handler.h
│ └── fft_processor.h
├── Drivers/
└── USART_HMI/
└── screen_project.HMI # T1屏幕工程文件
这个方案提供了完整的STM32与淘晶驰T1串口屏的波形显示实现。可以根据具体需求调整采样率、显示点数、通信协议等参数。