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

利用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端发送数据???

版权声明:本文为CSDN博主「ღ 金龍戲水 ღ」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_53971060/article/details/118928956

生成海报
点赞 0

ღ 金龍戲水 ღ

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

定时器触发STM32 ADC的采样转换示例

开发板:STM32F446 Nucleo开发板IDE:  keil MDK初始化配置工具:stm32cubeMx例程内容:通过定时器触发ADC规则通道及注入通道的模数转换工作。下面基于STM32CubeMx进行些必要

电脑不识别STM32的USB虚拟串口

电脑不识别STM32的USB虚拟串口 现象 板子和电脑联调的时候发现,USB线插入板子以后电脑不识别虚拟串口,通过禁用设备再启用,可以正常工作。也可以按一下复位键才能识别。 以前似乎没有这个问题&#