OpenMV与Arduino通信—串口


所有要进行串口通信的设备, GND必须相连 (共地)

——>因为我们进行串口通信的设备是一个具有高电平和低电平的电压信号,所以进行通讯的设备必须接地,这样才有相应的高电平和低电平产生

注意:ArduinoTX0RX1引脚如果外接了别的设备,那么在我们上传代码到Arduino时是一定会失败的(上传时用到了这两个引脚),所以我们上传时要要先把导线移除掉


在发送数据时,为了避免打断Arduino正在进行的工作,聪明的Arduino工程师选择把串口输入的数据放入Arduino的串行数据缓冲区中,等到Arduino完成工作后,就可以检测串行数据缓冲区中是否有数据,进而处理串口数据


Arduino的串口函数

  	Serial.begin(); //开启串行通信接口并设置通信波特率

    Serial.end();    //关闭通信串口

    Serial.available();//判断串口缓冲器是否有数据装入

    Serial.read();    //读取串口数据

    Serial.peek();    //返回下一字节(字符)输入数据,但不删除它

    Serial.flush();    //清空串口缓存

    Serial.print();    //写入字符串数据到串口

    Serial.println();   //写入字符串数据+换行到串口

    Serial.write();     //写入二进制数据到串口

    Serial.SerialEvent();//read时触发的事件函数

    Serial.readBytes(buffer,length);//读取固定长度的二进制流

    Serial.println(incomingByte, DEC);//打印接到数据十进制表示的ascii码。  HEX 十六进制表示

关于Arduino的串口函数详解:
Arduino - 串口操作函数与示例代码大全(转载)

Arduino - 串口操作函数与示例代码大全


接收函数Serial.read()

如果串行数据缓冲区有数据,这个函数会读取串行数据缓冲区的第一个字节,数据读取位置移动到下一个数据缓冲区,也就是说如果继续读取的话会读取下一个数据缓冲区的第一个字节.
如果数据缓冲区没有数据,将返回-1


检测串口Serial.available()

当串行数据缓冲区中有数据时,函数Serial.available()的返回值为非0,我们可以以此为判断条件,来处理串口数据!

 if(softSerial.available())  // 如果串口缓存区中有数据
{
    
    
	......
}

串口通信例程

OpenMV寻找视野中最大的色块,并把色块的中心坐标发送给Arduino

该例程用到了软件串口
在这里插入图片描述

OpenMV代码

import sensor, image, time
import json
from pyb import UART
# 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   = (55, 98, -128, 127, 23, 127)
# You may need to tweak the above settings for tracking green things...
# Select an area in the Framebuffer to copy the color settings.

sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.RGB565) # use RGB565.
sensor.set_framesize(sensor.QQVGA) # 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 find_max(blobs):
    max_size=0
    for blob in blobs:
        if blob.pixels() > max_size:# blob.pixels()返回从属于色块(int)一部分的像素数量。
            max_blob=blob
            max_size = blob.pixels()
    return max_blob
    
while(True):
    clock.tick() # Track elapsed milliseconds between snapshots().
    img = sensor.snapshot() # Take a picture and return the image.

    blobs = img.find_blobs([yellow_threshold])
    
    if blobs:
        print('视野中的色块数sum : %d'% len(blobs))
        data=[]
        
        # 寻找视野中最大的色块
        max_blob=find_max(blobs)
        
        # 框出最大的色块,并在中心画十字
        img.draw_rectangle(max_blob.rect()) # rect
        img.draw_cross(max_blob.cx(), max_blob.cy()) # cx, cy
        
        # 将最大色块的中心坐标加在传输数组data[]后
        data.append( (max_blob.cx(),max_blob.cy()) )

        #{(1,22),(-3,33),(22222,0),(9999,12),(0,0)}
        data_out = json.dumps(set(data))    # ujson.dumps(obj):返回表示为JSON字符串的 obj 。
                                            # set() 函数创建一个无序不重复元素集
        
        uart.write(data_out +'\n')
        
        print('you send:',data_out)
    
    
    else:
        print("not found!")


Arduino代码

#include <SoftwareSerial.h>

//设置软件串口
SoftwareSerial softSerial(10, 11); // RX, TX
typedef struct
{
    
    
  int data[50][2] = {
    
    {
    
    0,0}};
  int len = 0;
}List;
List list;

void setup() 
{
    
    
  // put your setup code here, to run once:
  softSerial.begin(115200); // 设置软件串口波特率,必须与OpenMV中的一致
  Serial.begin(115200); // 设置波特率
}

void loop() 
{
    
    
  if(softSerial.available())  // 如果串口缓存区中有数据
  {
    
    
    getList();  // 将数据存储到结构体的数组list.data[][]中
    for (int i=0; i<list.len; i++)  //  遍历结构体数组
    {
    
    
      Serial.print(list.data[i][0]);    // 打印中心的x坐标cx()
      Serial.print('\t'); // 打印制表符  
      Serial.println(list.data[i][1]);  //打印中心的y坐标cy()
    }
    Serial.println("============");
    clearList();  //清空内存
  }

}


String detectString() // 判断传入的字符串能否被接收
{
    
    
  // 我们传入的数据形式 you send: {(90, 101)}
  while(softSerial.read() != '{');  // 如果数据不以'{'开头,那么我们会一直进入死循环

  //返回"{}"中的字符串
  return(softSerial.readStringUntil('}'));
                                          // readStringUntil函数可用于从设备接收到的数据中读取信息。读取到的数据信息将以字符串形式返回
                                          // 当函数读取到终止字符后,会立即停止函数执行。此时函数所返回的字符串为”终止字符”前的所有字符信息
}


void clearList()//清空内存
{
    
    
  memset(list.data, sizeof(list.data),0);
  list.len = 0;
}

void getList()  // 重点是把数据存储在结构体中的数组list.data[][]中!
{
    
    
  String s = detectString();// 将返回的字符串({}中的元素)赋值给s
                      
  // 此时s中的数据形如:(90, 101)
  String numStr = "";
  for(int i = 0; i<s.length(); i++) // 遍历刚刚返回的字符串"s"
  {
    
    
    if(s[i]=='(') // 读到开头
    {
    
    
      numStr = "";
    }
    else if(s[i] == ',')  // 读到数据分割标记','
    {
    
    
      // toInt()将有效字符串转换为整数。输入字符串应以整数开头。如果 String 包含非整数,则该函数将停止执行转换。
      list.data[list.len][0] = numStr.toInt();  // 将刚刚拼接好的数据str(色块的cx())用toInt函数转换成整形,存储在结构体数组list.data[list.len][0]中
      
      numStr = "";  // 清空numstr,方便接受','后的另一组数据
    }
    else if(s[i]==')') // 读到结尾
    {
    
    
      list.data[list.len][1] = numStr.toInt();  // 将刚刚拼接好的数据str(色块的cy())用toInt函数转换成整形,存储在结构体数组list.data[list.len][1]中
      numStr = "";
      list.len++; // 长度+1,读取下一组坐标数组
    }
    else  // 读到数据
    {
    
    
      numStr += s[i]; // 相当于把读到的数字(此时为char型)按顺序拼接到numstr后
    }
  }
}


注意:对于不同的数据,我们只需要更改String detectString()void getList()中的判断条件即可

结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_59466249/article/details/125426159