3. GPIO输入输出HIL测试
任务目标
在HIL(硬件在环)平台上,通过上位机控制STM32的GPIO引脚,完成输入输出逻辑验证:上位机向STM32发送输出控制指令,STM32驱动LED引脚输出高/低电平;同时STM32读取按键引脚电平状态并上报上位机,以此验证GPIO输入输出功能的正确性。
硬件连接与依赖
- 目标板:STM32F407VET6(或其他Cortex-M4系列,引脚定义需对应修改)
- HIL设备:支持数字I/O通道的HIL机箱(如NI PXI、Speedgoat等),通过DB37或SCSI线缆连接至目标板
- 连接关系:
HIL数字输出通道0 → STM32 PA0(按键输入引脚,外部上拉)
HIL数字输入通道0 ← STM32 PD12(LED输出引脚)
GND共地 - 软件依赖:STM32CubeMX生成的HAL库工程,HIL上位机使用Python 3.8+(需安装
pylib或对应HIL厂商SDK)
STM32端代码(核心逻辑)
以下代码基于STM32CubeMX生成的HAL库,在main.c的main()函数中实现GPIO输入输出轮询逻辑。PA0配置为输入模式(上拉),PD12配置为推挽输出。
/* main.c 中用户代码区(USER CODE BEGIN 2 至 USER CODE END 2) */
#include <string.h>
// 定义通信帧格式(简化版,通过USART2与上位机通信)
#define FRAME_HEADER 0xAA
#define CMD_SET_LED 0x01 // 数据字节: 0=灭, 1=亮
#define CMD_GET_KEY 0x02 // 返回数据: 0=未按下, 1=按下
uint8_t rx_buffer[4];
uint8_t rx_index = 0;
// 简单帧解析:AA CMD DATA CHECKSUM(校验=CMD+DATA)
void ProcessCommand(uint8_t cmd, uint8_t data) {
if (cmd == CMD_SET_LED) {
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, data ? GPIO_PIN_SET : GPIO_PIN_RESET);
} else if (cmd == CMD_GET_KEY) {
uint8_t key_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
uint8_t resp[4] = {FRAME_HEADER, CMD_GET_KEY, key_state, CMD_GET_KEY + key_state};
HAL_UART_Transmit(&huart2, resp, 4, 100);
}
}
// 在while(1)循环中调用
void GPIO_HIL_Poll(void) {
uint8_t byte;
if (HAL_UART_Receive(&huart2, &byte, 1, 0) == HAL_OK) {
rx_buffer[rx_index++] = byte;
if (rx_index == 1 && byte != FRAME_HEADER) {
rx_index = 0; // 未收到帧头,重新同步
return;
}
if (rx_index == 4) {
// 校验:第4字节 = 第2字节 + 第3字节
if (rx_buffer[2] == (rx_buffer[1] + rx_buffer[3])) {
ProcessCommand(rx_buffer[1], rx_buffer[2]);
}
rx_index = 0;
}
}
}
// main() 中 while(1) 内调用:
/* USER CODE BEGIN WHILE */
while (1) {
GPIO_HIL_Poll();
HAL_Delay(1);
}
/* USER CODE END WHILE */
HIL上位机Python脚本
以下脚本通过串口发送命令并读取响应,控制STM32的LED输出并读取按键状态。需根据实际HIL设备替换hil_write_digital和hil_read_digital为对应SDK函数。
import serial
import time
# 配置串口(根据实际端口修改)
ser = serial.Serial('COM5', 115200, timeout=0.1)
def send_cmd(cmd, data):
"""发送 AA CMD DATA CHECKSUM"""
frame = bytes([0xAA, cmd, data, (cmd + data) & 0xFF])
ser.write(frame)
time.sleep(0.02)
def read_response():
"""读取并解析响应帧,返回 (cmd, data) 或 None"""
if ser.in_waiting >= 4:
resp = ser.read(4)
if resp[0] == 0xAA and resp[3] == (resp[1] + resp[2]) & 0xFF:
return resp[1], resp[2]
return None
# 测试序列
test_cases = [
(0x01, 1, "LED点亮"), # 命令置1
(0x01, 0, "LED熄灭"), # 命令置0
]
print("=== GPIO输出测试 ===")
for cmd, data, desc in test_cases:
send_cmd(cmd, data)
# 通过HIL读取PD12引脚电平(替换为实际HIL读取函数)
# hil_read_digital(0) 返回0或1
actual = hil_read_digital(0) # 需替换为实际SDK调用
expected = data
status = "PASS" if actual == expected else "FAIL"
print(f"{desc}: 期望={expected}, 实际={actual} → {status}")
print("\n=== GPIO输入测试 ===")
# 通过HIL控制PA0引脚电平(替换为实际HIL写入函数)
for input_level in [0, 1]:
hil_write_digital(0, input_level) # 需替换为实际SDK调用
time.sleep(0.05)
send_cmd(0x02, 0x00)
resp = read_response()
if resp and resp[0] == 0x02:
actual = resp[1]
expected = input_level
status = "PASS" if actual == expected else "FAIL"
print(f"PA0={input_level}: 期望={expected}, 实际={actual} → {status}")
else:
print("未收到有效响应")
ser.close()
运行与验证
- 编译烧录:使用STM32CubeIDE编译工程并烧录至目标板,确保USART2以115200波特率初始化。
- 连接HIL:将HIL数字I/O通道与目标板按前述连接关系接好,共地。
- 执行测试:运行Python脚本,脚本会依次执行输出控制和输入读取测试。
- 通过标准:
输出测试中,HIL读取到的PD12电平与发送命令一致(亮=1,灭=0);
输入测试中,STM32上报的PA0状态与HIL写入的电平一致。
所有测试用例均显示PASS即验证通过。
常见问题
- 串口无响应:检查USART2的GPIO配置(PA2/PA3)及中断优先级,确认HIL串口线连接正确。
- 电平不匹配:确认PA0已启用内部上拉,且HIL输出高电平为3.3V兼容;若HIL输出为5V需加电平转换。
- 帧校验失败:检查校验和计算是否溢出(使用8位截断),并确认上位机发送字节序与STM32端解析一致。