利用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
暂无评论