OpenMV + STM32 串口双向通信完整示例(STM32 标准库 3.5,Keil5)

单片机/PLC wes 17 hours ago (2026-07-30) 29 views
📑 本文导航

简介

本文通过一些简单的示例来介绍OpenMV与STM32控制器的串口双向通信,基于Keil5 + STM32 标准库 3.5,是入门开发的基础。

说明:
  1. STM32 使用 标准库 V3.5,USART1,115200‑8‑N‑1;中断接收,逐字节接收,'\n'作为一帧结束标记,缓冲区 128 字节。
  2. OpenMV 使用 UART1 (P4‑TX、P5‑RX);波特率 115200。
  3. 双向通信:
    • OpenMV → STM32:视觉报文,例如 B,cx,cy\r\n
    • STM32 → OpenMV:下发简单控制指令,例如 STARTSTOP,OpenMV 接收指令做简单逻辑。
  4. 硬件接线
plaintext
OpenMV P4(TX) ——> STM32 USART1‑RX(PA10)
OpenMV P5(RX) ——> STM32 USART1‑TX(PA9)
OpenMV GND ——> STM32 GND ✅必须共地!

第一部分:OpenMV 完整 Python 代码(双向串口)

python

import sensor, image, time, pyb

# UART1 P4(TX) P5(RX) 115200
uart = pyb.UART(1, 115200)

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(2000)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
clock = time.clock()

#颜色阈值,现场修改
threshold = (10, 80, 20, 127, 0, 127)
roi = (0, 160, 320, 80)

enable_run = False   #主控下发START才开始输出数据

while True:
    clock.tick()
    #接收STM32下发指令
    if uart.any():
        cmd = uart.readline()
        if cmd:
            cmd_str = cmd.decode("utf-8").strip()
            if cmd_str == "START":
                enable_run = True
            elif cmd_str == "STOP":
                enable_run = False

    img = sensor.snapshot()
    cx = -1
    cy = -1
    blobs = img.find_blobs([threshold], roi=roi, pixels_threshold=20, area_threshold=20, merge=True)
    if blobs:
        max_b = max(blobs, key=lambda x:x.pixels())
        cx = max_b.cx()
        cy = max_b.cy()
        img.draw_rectangle(max_b.rect())

    if enable_run:
        #向STM32发送视觉报文 B,cx,cy\r\n
        uart.write("B,%d,%d\r\n" % (cx, cy))

    time.sleep_ms(20)

通信说明
  • STM32 发送START,OpenMV 才开始上报视觉坐标;发送STOP停止上报。
  • OpenMV 报文固定结尾 \r\n,单片机依靠\n判断一帧接收完毕。

第二部分:STM32 标准库 3.5 代码 Keil5

工程基于标准库 STM32F10x_StdPeriph_Lib_V3.5.0;USART1 (PA9‑TX PA10‑RX);中断接收。 全局缓冲区:收到完整一行后置标志位,主循环解析报文;支持向 OpenMV 发送字符串指令。 
登录 后查看

第三部分:AprilTag 报文解析函数(标准库)

报文:TAG,id,tx,ty,tz\r\n
c

typedef struct
{
    uint8_t valid;
    int16_t id;
    float tx;
    float ty;
    float tz;
}AprilTag_Data_t;

AprilTag_Data_t apriltag;

void parse_apriltag(uint8_t *buf)
{
    apriltag.valid = 0;
    if(strstr((char*)buf,"TAG,") == NULL) return;

    int id_tmp;
    float tx_tmp,ty_tmp,tz_tmp;
    int ret = sscanf((char*)buf,"TAG,%d,%f,%f,%f",&id_tmp,&tx_tmp,&ty_tmp,&tz_tmp);
    if(ret == 4)
    {
        apriltag.id = id_tmp;
        apriltag.tx = tx_tmp;
        apriltag.ty = ty_tmp;
        apriltag.tz = tz_tmp;
        if(id_tmp != -1)
        {
            apriltag.valid = 1;
        }
    }
}

FOMO 报文解析函数

报文:F,class_id,cx,cy,score\r\n
c

typedef struct
{
    uint8_t valid;
    int16_t cls_id;
    int16_t cx;
    int16_t cy;
    float score;
}FOMO_Data_t;

FOMO_Data_t fomo_data;

void parse_fomo(uint8_t *buf)
{
    fomo_data.valid = 0;
    if(buf[0]!='F') return;

    int id,x,y;
    float sc;
    int ret = sscanf((char*)buf,"F,%d,%d,%d,%f",&id,&x,&y,&sc);
    if(ret == 4)
    {
        fomo_data.cls_id = id;
        fomo_data.cx = x;
        fomo_data.cy = y;
        fomo_data.score = sc;
        if(sc>0.6f)
        {
            fomo_data.valid = 1;
        }
    }
}


⚠️使用标准库的重要注意事项

  1. 必须共地! 没有共地必乱码、丢帧。
  2. 中断优先级:USART1 中断优先级不要设置为 0 最高,避免抢占其他关键中断。示例代码预抢占优先级 1。
  3. 缓冲区溢出保护:收到\n才认为一帧,其他情况不断填充缓冲区,满了直接丢弃。
  4. sscanf使用 float,标准库编译 Keil5 需要勾选 Use MicroLIB(Target 页面),否则浮点解析异常。
  5. OpenMV 代码不要写多余print(),print 会占用串口干扰双向通信。
  6. 双向通信指令结尾建议加上\r\n,保持协议统一。
  7. 不要在中断服务函数做复杂运算,中断只做接收,解析全部放在 main 主循环。