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()

运行与验证

  1. 编译烧录:使用STM32CubeIDE编译工程并烧录至目标板,确保USART2以115200波特率初始化。
  2. 连接HIL:将HIL数字I/O通道与目标板按前述连接关系接好,共地。
  3. 执行测试:运行Python脚本,脚本会依次执行输出控制和输入读取测试。
  4. 通过标准:
    输出测试中,HIL读取到的PD12电平与发送命令一致(亮=1,灭=0);
    输入测试中,STM32上报的PA0状态与HIL写入的电平一致。
    所有测试用例均显示PASS即验证通过。

常见问题

  • 串口无响应:检查USART2的GPIO配置(PA2/PA3)及中断优先级,确认HIL串口线连接正确。
  • 电平不匹配:确认PA0已启用内部上拉,且HIL输出高电平为3.3V兼容;若HIL输出为5V需加电平转换。
  • 帧校验失败:检查校验和计算是否溢出(使用8位截断),并确认上位机发送字节序与STM32端解析一致。