【星曈科技】OpenMv笔记——利用OpenMV与STM32进行串口通信

本文详细介绍了如何使用OpenMV与STM32进行串口通信,包括OpenMV的发送程序和STM32的接收中断服务函数。通过调整帧头、帧尾和数据格式,确保了数据的正确传输。在STM32端,通过串口中断接收数据并解析,最终在OLED上显示接收到的坐标信息。在遇到问题时,通过调试解决了接收缓存区大小与实际数据长度不匹配的问题。
摘要由CSDN通过智能技术生成

利用OpenMV与STM32进行串口通信

OpenMV端的程序

# Untitled - By: dell - 周一 7月 19 2021

# Blob Detection and uart transport
import sensor, image, time,pyb
import ustruct
from pyb import UART
#import json
# For color tracking to work really well you should ideally be in a very, very,
# very, controlled enviroment where the lighting is constant...
yellow_threshold   = (25, 79, 2, 39, 26, 69)
# You may need to tweak the above settings for tracking green things...
# Select an area in the Framebuffer to copy the color settings.
led3 = pyb.LED(3) # Red LED = 1, Green LED = 2, Blue LED = 3, IR LEDs = 4.
led1 = pyb.LED(1) # Red LED = 1, Green LED = 2, Blue LED = 3, IR LEDs = 4.
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.RGB565) # use RGB565.
sensor.set_framesize(sensor.QVGA) # use QQVGA for speed.
sensor.skip_frames(10) # Let new settings take affect.
sensor.set_auto_whitebal(False) # turn this off.
clock = time.clock() # Tracks FPS.

uart = UART(3, 115200)#初始化串口号及其波特率
#**************************传输数据的函数************************************
def sending_data(cx,cy):
    global uart;
    #frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
    #data = bytearray(frame)
    data = ustruct.pack("<bbhhb",      #格式为俩个字符俩个短整型(2字节)
                   0x2C,                      #帧头1
                   0x12,                      #帧头2
                   int(cx), # up sample by 4   #数据1
                   int(cy), # up sample by 4    #数据2
                   0x5B)
    uart.write(data);   #必须要传入一字节的数组,这个函数似乎不能发送单个字节,必须得一次发送多个字节
#**************************************************************************
while(True):
    img = sensor.snapshot() # Take a picture and return the image.
    led1.on()
    led3.on()#LED指示openmv正在工作
    blobs = img.find_blobs([yellow_threshold])#寻找符合条件的色块
    if len(blobs) == 1:#如果只找到一个符合条件的色块

        #print('sum :', len(blobs))
        img.draw_rectangle(blobs[0].rect())
        img.draw_cross(blobs[0].cx(), blobs[0].cy())

        output_str="[%d,%d]" % (blobs[0].cx(),blobs[0].cy())

        print('you send:',output_str)
        sending_data(blobs[0].cx(),blobs[0].cy())#发送色块框的中心点坐标
        #FH = bytearray([0x2C,0x12,blobs[0].cx(),blobs[0].cy(),0x5B])

        #uart.write(FH)
    else:
        print('not found!')
        sending_data(567,789)#如果没有找到符合条件的色块,那么发送一个不可能出现的坐标
        #FH = bytearray([0x2C,0x12,0x77,0x55,0x5B])

        #uart.write(FH)

由于我们图像大小设置成了QVGA,所以所有可能出现的坐标最大是(320,240),所以x或y最多只需要16bit,也就是两个Bit就可以完全发送出去。
在这里插入图片描述
由上图可知,格式为h,就可以表示2Bit,于是我们把数据的打包格式为h。

""bbhhb"就是要发送数据的声明,bbhhb共5个,代表发送5个数据。虽然只有5个数据,却需要发送7个字节,因为1个h就占用两个字节,openmv的发送顺序为:帧头1>帧头2>数据1的低八位>数据1的高八位>数据2的低八位>数据2的高八位。0x2c为数据帧的帧头,即检测到数据流的开始,但是一个帧头可能会出现偶然性,因此设置两个帧头0x2c与0x12以便在中断中检测是否检测到了帧头以便存放有用数据。0x5b为帧尾,即数据帧结束的标志。

下面,先让openmv与电脑通讯,通过串口调试助手看一下数据是否成功发送。
这里注意:openmv需要通过一个USB to TTL模块才能与电脑进行串口通信,openmv的RXD要连接模块的TXD,openmv的TXD要连接模块的RXD,然后再把模块与openmv共地即可。
通讯结果:
在这里插入图片描述
可以看到,由于摄像头没有检测到符合条件的色块,所以sending_data(567,789),通过串口助手明显发现数据传输成功,共有7个字节。

这样,我已经保证了openmv的串口发送程序是完全没有问题的,于是将程序烧录进openmv。
接下来,写一下stm32的接受程序

以下代码引用 乌拉~~~~ 博主的 超详细OpenMV与STM32单片机通信 (有完整版源码) 这一篇文章里的源码。感谢此博主的文章!!!!

// 串口中断服务函数//USART2 全局中断服务函数
void DEBUG_USART_IRQHandler(void)
{
  

		uint8_t com_data; //接收一个字节数据的临时变量
		uint8_t i;
	  static uint8_t RxCounter1=0;//数据缓冲区的索引
    static uint8_t RxBuffer1[10]={0};//存放数据的接收缓存区
    static uint8_t RxState = 0;	//接收标志位
		
//		static uint8_t RxFlag1 = 0;

		if( USART_GetITStatus(DEBUG_USARTx ,USART_IT_RXNE)!=RESET)  	   //接收中断  
		{
				USART_ClearITPendingBit(DEBUG_USARTx ,USART_IT_RXNE);   //清除中断标志
				com_data = USART_ReceiveData(DEBUG_USARTx );
			
				if(RxState==0&&com_data==0x2C)  //0x2c帧头
				{
					RxState=1;
					RxBuffer1[RxCounter1++]=com_data;//RxBuffer1[0]==0x2c RxCounter1==1
				}
		
				else if(RxState==1&&com_data==0x12)  //0x12帧头
				{
					RxState=2;
					RxBuffer1[RxCounter1++]=com_data;RxBuffer1[0]==0x12 RxCounter1==2
				}
		
				else if(RxState==2)//开始接收有效数据
				{
					RxBuffer1[RxCounter1++]=com_data;//全部接收完,RxCounter1==7

					if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
					{
						  RxState=3;
//						RxFlag1=1;
						Cx=(RxBuffer1[RxCounter1-4]<<8)+(RxBuffer1[RxCounter1-5]);//RxBuffer1[2]是openmv发送的第一个数据的低八位,RxBuffer1[3]是openmv发送的第一个数据的高八位
						Cy=(RxBuffer1[RxCounter1-2]<<8)+(RxBuffer1[RxCounter1-3]);RxBuffer1[4]是openmv发送的第二个数据的低八位,RxBuffer1[5]是openmv发送的第二个数据的高八位
						
					}
				}
		
				else if(RxState==3)		//检测是否接受到结束标志
				{
						if(RxBuffer1[RxCounter1-1] == 0x5B)
						{
									USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
//									if(RxFlag1)
//									{
//									
									OLED_ShowNum(0,0,Cx,3,1,2);
									OLED_ShowNum(0,2,Cy,3,1,2);
									
//									}
//									RxFlag1 = 0;
									RxCounter1 = 0;
									RxState = 0;
									USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,ENABLE);
						}
						else   //接收错误
						{
									RxState = 0;
									RxCounter1=0;
									for(i=0;i<10;i++)
									{
											RxBuffer1[i]=0xff;      //将存放数据数组清零
									}
						}
				} 
	
				else   //接收异常
				{
						RxState = 0;
						RxCounter1=0;
						for(i=0;i<10;i++)
						{
								RxBuffer1[i]=0xff;      //将存放数据数组清零
						}
				}

		}
		


}

将openmv的TXD、RXD分别和单片机的RXD、和TXD相连,然后两者共地,再在单片机上连接oled显示一下收到的数据,再把openmv连接电脑,通过IDE观测其发送的数据和oled接收的是否一致。
结果:
在这里插入图片描述
在这里插入图片描述
可以看到,我成功了。
将OLED显示的两行代码改进一下:

                        OLED_ShowChar(0,0,'(',2);
						OLED_ShowNum(8,0,Cx,3,1,2);
					    OLED_ShowChar(56,0,',',2);
						OLED_ShowNum(64,0,Cy,3,1,2);
					    OLED_ShowChar(112,0,')',2);

就成了这个效果:在这里插入图片描述
在这里插入图片描述

这里有个疑问

openmv一共发送了7个数据,所以我就把RxBuffer1[10]={0};//存放数据的接收缓存区改为了RxBuffer1[7]={0};改完之后,oled就不能正常显示数据。(下标最小得是9)
想不出来这到底是因为什么??????????????????
解决(最新代码)

// 串口中断服务函数//USART1 全局中断服务函数
void DEBUG_USART_IRQHandler(void)
{
  

		uint8_t com_data; //接收一个字节数据的临时变量
		uint8_t i;
	  static uint8_t RxCounter1=0;//数据缓冲区的索引
    static uint8_t RxBuffer1[7]={0};//存放数据的接收缓存区
    static uint8_t RxState = 0;	//接收标志位
		
//		static uint8_t RxFlag1 = 0;

		if( USART_GetITStatus(DEBUG_USARTx ,USART_IT_RXNE)!=RESET)  	   //接收中断  
		{
				USART_ClearITPendingBit(DEBUG_USARTx ,USART_IT_RXNE);   //清除中断标志
				com_data = USART_ReceiveData(DEBUG_USARTx );
			
				if(RxState==0&&com_data==0x2C)  //0x2c帧头
				{
					RxState=1;
					RxBuffer1[RxCounter1++]=com_data;//RxBuffer1[0]==0x2c RxCounter1==1
				}
		
				else if(RxState==1&&com_data==0x12)  //0x12帧头
				{
					RxState=2;
					RxBuffer1[RxCounter1++]=com_data;RxBuffer1[0]==0x12 RxCounter1==2
				}
		
				else if(RxState==2)//开始接收有效数据
				{
					RxBuffer1[RxCounter1++]=com_data;//全部接收完,RxCounter1==7

					if(RxCounter1>=7||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
					{
						  RxState=3;
//						RxFlag1=1;
						Cx=(RxBuffer1[RxCounter1-4]<<8)+(RxBuffer1[RxCounter1-5]);//RxBuffer1[2]是openmv发送的第一个数据的低八位,RxBuffer1[3]是openmv发送的第一个数据的高八位
						Cy=(RxBuffer1[RxCounter1-2]<<8)+(RxBuffer1[RxCounter1-3]);RxBuffer1[4]是openmv发送的第二个数据的低八位,RxBuffer1[5]是openmv发送的第二个数据的高八位
						
					}
				}
		
				else if(RxState==3)		//检测是否接受到结束标志
				{
						if(RxBuffer1[RxCounter1-1] == 0x5B)
						{
									USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
//									if(RxFlag1)
//									{
								  OLED_ShowChar(0,0,'(',2);
									OLED_ShowNum(8,0,Cx,3,1,2);
							    OLED_ShowChar(56,0,',',2);
									OLED_ShowNum(64,0,Cy,3,1,2);
							    OLED_ShowChar(112,0,')',2);
									
//									}
//									RxFlag1 = 0;
									RxCounter1 = 0;
									RxState = 0;
									USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,ENABLE);
						}
						else   //接收错误
						{
									RxState = 0;
									RxCounter1=0;
									for(i=0;i<7;i++)
									{
											RxBuffer1[i]=0x00;      //将存放数据数组清零
									}
						}
				} 
	
				else   //接收异常
				{
						RxState = 0;
						RxCounter1=0;
						for(i=0;i<7;i++)
						{
								RxBuffer1[i]=0x00;      //将存放数据数组清零
						}
				}

		}
		


}

疑惑存在是因为我只改了RxBuffer1的角标,而没有改动接收异常(else)里面for循环的i<10,这是要改成i<7的。
openmv脱机运行
将openmv脱机,用单片机的5v给他供电,其余连线保持不变:
在这里插入图片描述

同样可以把openmv的结果通过串口打印到电脑

只需要在while循环里加上:

while(1)
	{	
		printf("(%d,%d)",Cx,Cy);
	}	

注意
1.想要单片机与电脑通信,要另外设置特定的串口,我的开发板上单片机通过USART1和电脑通信,所以要另外设置USART1。
2.同一个串口的RXD不可以连接多个设备的TXD。比如,我的开发板的USART1如果连接到openmv的TXD,那么可能就造成程序下载不到单片机,因为电脑的TXD也连接的是USART1的RXD。

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

我这个代码的重定向是写的USART1(DEBUG_USARTx),是不是就说明,我只要使用printf函数就是向USART1的TXR端发送数据???

  • 106
    点赞
  • 963
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 106
    评论
openmvstm32串口通信的方法有很多种,下面我将以一个简单的例子来介绍一种实现方法。 首先,准备工作是需要一个开发板和一根连接openmvstm32的串口线。 在openmv中,我们可以使用pyb模块来完成串口通信的设置和数据发送。首先,我们需要导入pyb模块并初始化串口: ```python import pyb uart = pyb.UART(3, 115200) # 初始化串口3,波特率设置为115200 ``` 然后,我们可以发送数据到stm32: ```python uart.write('Hello STM32!\n') # 发送字符串 ``` 在stm32的代码中,我们使用STM32的HAL库来完成串口通信的设置和数据接收。首先,我们需要初始化串口: ```c #include "stm32f4xx_hal.h" UART_HandleTypeDef huart2; void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_RX_TX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } } ``` 然后,我们可以在while循环中接收openmv发送的数据: ```c uint8_t rx_data[10]; // 接收缓冲区 while (1) { HAL_UART_Receive(&huart2, rx_data, sizeof(rx_data), 1000); // 接收数据,超时时间为1s // 处理接收到的数据 } ``` 以上是一种实现openmvstm32串口通信的简单方法。通过配置相应的串口参数和使用适当的API函数,我们可以在openmvstm32之间进行双向通信。当然,具体的实现方法还可以根据应用的需求进行调整。
评论 106
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ღ 金龍戲水 ღ

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值