目录

前言

一、硬件连接

1.OpenMV端

2.STM32zet6端

3.msp432p401r端

二、软件实现

1.OpenMV代码讲解及实现

2.stm32zet6代码讲解及实现

3.msp432p401r代码讲解及实现

前言

本篇文章从硬件层面到软件层面详细介绍了如何实现OpenMV与STM32/msp432p401r串口通信,并介绍了一下串口通讯协议的原理,文末附上完整的源代码。

一、硬件连

1.OpenMV端

由图知RX—P5 ---TX—P4

2.STM32zet6端

本文以RX_PA3 --TX_PA2为例

3.msp432p401r端

本文以RX_P3^2--TX_P3^3 为例

 

 注意:连接引脚时一定要注意主控的TX连接OpenMV的RX,主控的RX连接OpenMV的TX

二、软件实现

1.OpenMV代码讲解及实现

运行条件:主控先通过串口给OpenMV发送char类型数据'0'作为运行模式。针对电赛需求,OpenMV只需要收到模式数据即可,可以不加帧头与帧尾,而直接判断获取的内容,详细代码如下。

# 作者 记得开心①点
# 创作日期 2023.8.7

import sensor, image, time ,math,pyb
from pyb import UART
uart = UART(3,115200)#设置串口波特率

#摄像头初始化
sensor.reset()#复位和初始化传感器。
sensor.set_pixformat(sensor.RGB565)#设置像素格式为RGB565(或GRAYSCAL)
sensor.set_framesize(sensor.VGA)#设置帧大小为VGA (480x640)
sensor.skip_frames(time = 2000)#等待设置生效
sensor.set_auto_gain(False)#关闭自动增益
sensor.set_auto_whitebal(False) #关闭白平衡
clock = time.clock()#创建一个时钟对象来跟踪FPS

#初始化三个板载灯
red_led = pyb.LED(1)
green_led = pyb.LED(2)
blue_led = pyb.LED(3)
red_led.on();#OpenMV初始化成功长亮红灯

#红色阈值
red_threshold = (0, 100, 12, 73, -123, 127)

#初始化所需标志位
i=0
pattern=-1#模式

#寻找最大色块
def find_max(blobs):
    max_blob = None
    max_size = 0
    for blob in blobs:
        if blob[2]*blob[3]>max_size:
            max_blob=blob
            max_size=blob[2]*blob[3]
    return max_blob

#获取数据
def data_get():
    global i
    global pattern
    getrx = uart.readchar()#获取char类型数据
    if getrx == 0 or getrx==1 or getrx==2 or getrx==3:
        pattern = getrx
    else:
        i=i+1
    if i>=20:#如果收到的数据不正确20次以上,OpenMV闪蓝灯
        for i in range(1,10):
            for j in range(1,30):
                blue_led.on()
            for j in range(1,30):
                blue_led.off()

#主循环
while(True):
    clock.tick()
    if uart.any():#串口有任何值发送过来
        data_get()#获取运行模式
    if pattern==0:
        img = sensor.snapshot().lens_corr(1.6)#鱼眼矫正
        blobs = img.find_blobs([red_threshold],roi=[80,80,320,320])
        if blobs:
            red_led.off()
            green_led.on()#找到色块亮绿灯
            blue_led.off()
            max_blob = find_max(blobs)#返回最大的色块
            img.draw_rectangle(max_blob.rect())#画出找到色块
            #发送数据
            data=bytearray([0xa3,0xb3,int(max_blob.cx()/2),int(max_blob.cy()/2),0xc3])
            uart.write(data)
        else:#未找到色块返回0,0n
            red_led.off()
            green_led.on()#未找到色块亮青蓝色灯
            blue_led.on()
            #发送数据
            data=bytearray([0xa3,0xb3,int(0),int(0),0xc3])
            uart.write(data)
    else:#摄像头未执行任何模式返回255,255
        red_led.on()
        green_led.on()#未执行任何模式亮黄灯
        blue_led.off()
        #发送数据
        data=bytearray([0xa3,0xb3,int(255),int(255),0xc3])
        uart.write(data)

2.stm32zet6代码讲解及实现

/*****openmv.h*****/(32部分源文件以连接的硬件命名)

​//作者 记得开心①点
//日期 2023.8.7

#ifndef __OPENMV_H
#define __OPENMV_H

#include <stdio.h>

extern int openmv_data[4];//已经声明全局变量,在任意文件直接调用即可
void openmv_Init(void);
void openmv_SendByte(uint8_t Byte);


#endif

​

/*****openmv.c*****/(32部分源文件以连接的硬件命名)

​//作者 记得开心①点
//日期 2023.8.7

#include "stm32f10x.h"                  // Device header

void openmv_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//推挽输出模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//ÉÏÀ­ÊäÈë»ò¸¡¿ÕÊäÈë
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 115200;//²¨ÌØÂÊ
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//串口模式配置
	USART_InitStructure.USART_Parity = USART_Parity_No;//无校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一位停止位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//八位(不需要校验)
	USART_Init(USART2, &USART_InitStructure);
	
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART2, ENABLE);
}

void openmv_SendByte(uint8_t Byte)
{
	USART_SendData(USART2, Byte);
	while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);//等待发送完成
}

int openmv_flag=0;
int openmv_i=0;
int openmv_data[4]={0};
int openmv_rx=0;

void USART2_IRQHandler(void)
{
	if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)//判断接受中断标志位
	{
		openmv_rx=USART_ReceiveData(USART2);//获取数据
		if(openmv_flag==0&&openmv_rx==0xa3)//判断第一个帧头
		{
			openmv_flag=1;
		}
		else if(openmv_flag==1&&openmv_rx==0xb3)判断第二个帧头
		{
			openmv_flag=2;
		}
		else if(openmv_flag==3)
		{
			openmv_flag=3;
			openmv_data[openmv_i++]=openmv_rx;//获取数据
		}
		else if(openmv_flag==3)
		{
			openmv_flag=4;
			openmv_data[openmv_i++]=openmv_rx;//获取数据
		}
		else if(openmv_flag==4&&openmv_rx==0xc3)//判断最后一个帧头
		{
			openmv_flag=0;
            openmv_i=0;
		}
		USART_ClearITPendingBit(USART2, USART_IT_RXNE);//清除接收中断标志位
	}
}

​

​

3.msp432p401r代码讲解及实现

/*****openmv4.h*****/(msp432部分源文件以连接的硬件命名)

这里baudrate_calculate文件附在其后,这个文件创建后在openmv4.h中包含即可,感谢某站up,m-RNA的msp432p401r开源视频教程。

​​//作者 记得开心①点
//日期 2023.8.7

#ifndef _openmv4_H
#define _openmv4_H

#include "sysinit.h"
#include "baudrate_calculate.h"

extern int openmv_data[8];//已经声明全局变量,在任意文件内直接调用即可
void openmv_sentdata(char mode);
void openmv4_uart_init(void);

#endif

/*****openmv4.c*****/(msp432部分源文件以连接的硬件命名)

//作者 记得开心①点
//日期 2023.8.7
#include "openmv4.h"

//初始化IO 串口2
// bound:波特率
void openmv4_uart_init(uint32_t baudRate)
{
		//固件库v3_40_01_02
		//默认SMCLK 48MHz 比特率
		const eUSCI_UART_ConfigV1 uartConfig =
			{
				EUSCI_A_UART_CLOCKSOURCE_SMCLK,				   //48mhz时钟打开
				312,										   // BRDIV = 312
				8,											   // UCxBRF = 8
				0,											   // UCxBRS = 1
				EUSCI_A_UART_NO_PARITY,						   // 无校验位
				EUSCI_A_UART_LSB_FIRST,						   // MSB First
				EUSCI_A_UART_ONE_STOP_BIT,					   //一位停止位
				EUSCI_A_UART_MODE,							   // 串口模式配置
				EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION, // Oversampling
				EUSCI_A_UART_8_BIT_LEN						   // 字长八位(无需校验位)
			};
		eusci_calcBaudDividers((eUSCI_UART_ConfigV1 *)&uartConfig, baudRate); //配置波特率

		// 1.配置GPIO复用
		MAP_GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P3, GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
		MAP_UART_initModule(EUSCI_A2_BASE, &uartConfig); // 3.初始化串口
		MAP_UART_enableModule(EUSCI_A2_BASE);			 // 4.开启串口模块
		MAP_UART_enableInterrupt(EUSCI_A2_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT); // 5.开启接收中断
		//MAP_UART_enableInterrupt(EUSCI_A2_BASE, EUSCI_A_UART_TRANSMIT_INTERRUPT);//6.开启发送中断
		MAP_Interrupt_enableInterrupt(INT_EUSCIA2);								 // 7.开启串口端口中断
}

void openmv_sentdata(char mode)
{
		//MAP_UART_transmitData(EUSCI_A2_BASE,0xa3);
		MAP_UART_transmitData(EUSCI_A2_BASE,mode);
}

int openmv_flag=0;
int rx_data=0;
int openmv_data[8]={0};
int openmv_i=0;

// Uart2接收中断
void EUSCIA2_IRQHandler(void)
{
    uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A2_BASE);
    if (status & EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG) //判断接受中断标志位
    {
			
			rx_data=MAP_UART_receiveData(EUSCI_A2_BASE);
			if(rx_data==0xA3&&openmv_flag==0)//判断头帧
			{
				openmv_flag=1;

			}
			else if(openmv_flag==1&&rx_data==0xb3)//判断第二帧
			{
				openmv_flag=2;
			}
			else if(openmv_flag==2)
			{
				openmv_flag=3;
				openmv_data[openmv_i++]=rx_data;//获取回传
			}
			else if(openmv_flag==3)
			{
				openmv_flag=4;
				openmv_data[openmv_i++]=rx_data;//获取回传
			}
			else if(openmv_flag==4&&rx_data==0xc3)//判断第三帧
			{
				openmv_flag=0;
				openmv_i=0;
			}
			MAP_UART_clearInterruptFlag(EUSCI_A2_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG); // 清除接收中断标志位
		}
}
		    
void eusci_calcBaudDividers(eUSCI_UART_ConfigV1 *uart_config, uint32_t baudRate) //固件库v3_40_01_02


{
    float maxAbsErrorInByte;
    float minAbsError;
    float error;
    uint8_t ii;
    uint16_t jj;
    uint16_t NN;
    uint32_t count;
    uint32_t clockRate;

    if (!uart_config || !baudRate) //传参错误 退出函数
    {
        //uart_warning_led(); //闪烁错误指示灯10次
        return;
    }

    if (uart_config->selectClockSource == EUSCI_A_UART_CLOCKSOURCE_SMCLK)
        clockRate = MAP_CS_getSMCLK();
    else if (uart_config->selectClockSource == EUSCI_A_UART_CLOCKSOURCE_ACLK)
        clockRate = MAP_CS_getACLK();
    else
    {
        uart_config->selectClockSource = EUSCI_A_UART_CLOCKSOURCE_SMCLK;
        clockRate = MAP_CS_getSMCLK();
    }
    if (baudRate > clockRate) //判断波特率是否大于时钟频率 是则退出函数
    {
        //uart_warning_led(); //闪烁错误指示灯10次
        return;
    }
    //var result = {UCOS16 : 0, UCBRx : 0, UCFx : 0, UCSx : 0, maxAbsError : 0};

    NN = (uint16_t)((float)clockRate / (float)baudRate); //应该是不需要floor

    minAbsError = 100000;
    for (jj = 0; jj <= 255; jj++)
    {

        maxAbsErrorInByte = 0;
        count = 0;
        for (ii = 0; ii <= 10; ii++)
        {
            count += NN + bitPosition(jj, 7 - (ii % 8));

            //error = (ii + 1) * baudPeriod - count * clockPeriod;
            error = (ii + 1) / (float)baudRate - count / (float)clockRate; //为了减少变量,改为此代码

            if (error < 0)
                error = -error;

            if (error > maxAbsErrorInByte)
                maxAbsErrorInByte = error;
        }
        if (maxAbsErrorInByte - minAbsError < -7.3e-12f) //这里就是“已知问题”
        {
            minAbsError = maxAbsErrorInByte;
            uart_config->secondModReg = jj;
        }
    }

    if (NN < 20)
    {
        uart_config->overSampling = 0;
        uart_config->clockPrescalar = NN;
        uart_config->firstModReg = 0;
    }
    else
    {
        uart_config->overSampling = 1;
        uart_config->clockPrescalar = (uint16_t)((float)NN / 16.0f); //应该是不需要floor
        uart_config->firstModReg = NN - (uart_config->clockPrescalar * 16);
    }
    //return minAbsError * baudRate * 100;
}

 /*****baudrate_calculate.h*****/

/****************************************************/
// MSP432P401R
// 串口波特率计算
// Bilibili:m-RNA
// E-mail:m-RNA@qq.com
/****************************************************/

/******************************    说明    ******************************
 *
 * 源码为TI官方编写,本人只是将JS程序移植到了C语言平台,仅作为学习使用。源码出处为:
 * http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html
 * 
 * ? 已知问题:
 * 调试时发现某些情况下,C语言的小数的大小与JS的相差较大,
 * 导致了算出的UCSx(即secondModReg)不一样,
 * 这时如果出现不能准确传输时,请换一个波特率。
 *
 * ? 需要注意:
 * 波特率不能大于时钟频率,否则会退出函数
 * 
 * *****************************   版本说明   ******************************
 * 
 * ? v1.2 2021/8/29
 * 注释掉了闪烁灯的代码
 * 
 * ? v1.1  2021/8/27
 * 添加支持固件库v3_21_00_05 
 * 
 * ? v1.0  2021/8/25
 * 仅支持固件库v3_40_01_02
 * 
 * *******************************   结束    *******************************/
 
 #ifndef __RNA_BAUDRATE_CALCULATE_H
#define __RNA_BAUDRATE_CALCULATE_H
#include <ti/devices/msp432p4xx/driverlib/driverlib.h>

//错误指示灯宏定义 方便移植使用
//MSP432P401R 有两个红灯P1.0 P2.0
//#define WARN_LED_1_PORT GPIO_PORT_P1
//#define WARN_LED_2_PORT GPIO_PORT_P2
//#define WARN_LED_1_PIN GPIO_PIN0
//#define WARN_LED_2_PIN GPIO_PIN0
//#define WARN_LED_INIT MAP_GPIO_setAsOutputPin
//#define WARN_LED_ON MAP_GPIO_setOutputHighOnPin
//#define WARN_LED_OFF MAP_GPIO_setOutputLowOnPin

#ifdef EUSCI_A_UART_7_BIT_LEN
void eusci_calcBaudDividers(eUSCI_UART_ConfigV1 *uart_config, uint32_t baudRate); //固件库v3_40_01_02
#else
void eusci_calcBaudDividers(eUSCI_UART_Config *uart_config, uint32_t baudRate); //固件库v3_21_00_05
#endif

#endif

 /*****baudrate_calculate.c*****/

/****************************************************/
// MSP432P401R
// 串口波特率计算
// Bilibili:m-RNA
// E-mail:m-RNA@qq.com
/****************************************************/

/******************************    说明    ******************************
 *
 * 源码为TI官方编写,本人只是将JS程序移植到了C语言平台,仅作为学习使用。源码出处为:
 * http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html
 *
 * ? 已知问题:
 * 调试时发现某些情况下,C语言的小数的大小与JS的相差较大,
 * 导致了算出的UCSx(即secondModReg)不一样,
 * 这时如果出现不能准确传输时,请换一个波特率。
 *
 * ? 需要注意:
 * 波特率不能大于时钟频率,否则会退出函数
 *
 * *****************************   版本说明   ******************************
 *
 * ? v1.2 2021/8/29
 * 注释掉了闪烁灯的代码
 * 
 * ? v1.1  2021/8/27
 * 添加支持固件库v3_21_00_05
 *
 * ? v1.0  2021/8/25
 * 仅支持固件库v3_40_01_02
 *
 * *******************************   结束    *******************************/

#include "baudrate_calculate.h"

//void uart_warning_led(void);

/*
 *  ======== bitPosition ========
 *  return 1(0) if the specified bit position in value is set(clear)
 */
bool bitPosition(uint16_t value, uint16_t position)
{
    if ((value & (1 << position)))
        return 1;
    return 0;
}

/*
 *  ======== eusci_calcBaudDividers ========
 *  computes the eUSCI_UART register settings for a given clock and baud rate
 *
 *      UCOS16:      the oversampling bit (0 or 1)
 *      UCBRx:       the Baud Rate Control Word
 *      UCFx:        the First modulation stage select (UCBRFx)
 *      UCSx:        the Second modulation stage select (UCBRSx)
 *      maxAbsError: the maximum TX error for the register setting above
 *
 *  The first four field names match the names used in Table 18-5,
 *  "Recommended Settings for Typical Crystals and Baudrates", of the
 *  MSP430FR57xx Family User's Guide (SLAU272A).
 */

void eusci_calcBaudDividers(eUSCI_UART_ConfigV1 *uart_config, uint32_t baudRate) //固件库v3_40_01_02


{
    float maxAbsErrorInByte;
    float minAbsError;
    float error;
    uint8_t ii;
    uint16_t jj;
    uint16_t NN;
    uint32_t count;
    uint32_t clockRate;

    if (!uart_config || !baudRate) //传参错误 退出函数
    {
        //uart_warning_led(); //闪烁错误指示灯10次
        return;
    }

    if (uart_config->selectClockSource == EUSCI_A_UART_CLOCKSOURCE_SMCLK)
        clockRate = MAP_CS_getSMCLK();
    else if (uart_config->selectClockSource == EUSCI_A_UART_CLOCKSOURCE_ACLK)
        clockRate = MAP_CS_getACLK();
    else
    {
        uart_config->selectClockSource = EUSCI_A_UART_CLOCKSOURCE_SMCLK;
        clockRate = MAP_CS_getSMCLK();
    }
    if (baudRate > clockRate) //判断波特率是否大于时钟频率 是则退出函数
    {
        //uart_warning_led(); //闪烁错误指示灯10次
        return;
    }
    //var result = {UCOS16 : 0, UCBRx : 0, UCFx : 0, UCSx : 0, maxAbsError : 0};

    NN = (uint16_t)((float)clockRate / (float)baudRate); //应该是不需要floor

    minAbsError = 100000;
    for (jj = 0; jj <= 255; jj++)
    {

        maxAbsErrorInByte = 0;
        count = 0;
        for (ii = 0; ii <= 10; ii++)
        {
            count += NN + bitPosition(jj, 7 - (ii % 8));

            //error = (ii + 1) * baudPeriod - count * clockPeriod;
            error = (ii + 1) / (float)baudRate - count / (float)clockRate; //为了减少变量,改为此代码

            if (error < 0)
                error = -error;

            if (error > maxAbsErrorInByte)
                maxAbsErrorInByte = error;
        }
        if (maxAbsErrorInByte - minAbsError < -7.3e-12f) //这里就是“已知问题”
        {
            minAbsError = maxAbsErrorInByte;
            uart_config->secondModReg = jj;
        }
    }

    if (NN < 20)
    {
        uart_config->overSampling = 0;
        uart_config->clockPrescalar = NN;
        uart_config->firstModReg = 0;
    }
    else
    {
        uart_config->overSampling = 1;
        uart_config->clockPrescalar = (uint16_t)((float)NN / 16.0f); //应该是不需要floor
        uart_config->firstModReg = NN - (uart_config->clockPrescalar * 16);
    }
    //return minAbsError * baudRate * 100;
}
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐