目录
前言
本篇文章从硬件层面到软件层面详细介绍了如何实现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;
}