UART通信——OpenMV端与MCU端

引入

本人前段时间在做智能车竞赛时碰到一个串口通信的任务,具体是要实现OpenMV端与MCU端的通信。现在看来这是一个比较简单且比较基础的任务,但当时我作为一个从来没有写过通信任务的小白,在完成这个任务的过程中还是遇到了很多问题的,现在就来跟大家分享一下我写UART通信的过程中遇到的问题以及我是怎么解决的。

代码实现

开始分享前先分别放上MCU端与OpenMV端的代码。

MCU端(收):

void uart_rx_interrupt_handler(void)//中断函数
{ 
    uart_query_byte(UART_INDEX, &get_data);                                     // 查询式接收数据
    fifo_write_buffer(&uart_data_fifo, &get_data, 1);                           // 将数据写入 fifo 中
}


void get_uartdata(void)
{
       fifo_data_count = fifo_used(&uart_data_fifo);                           // 查看 fifo 是否有数据
       if(fifo_data_count != 0)
       {
         if(get_states==0)
         { 
           fifo_read_buffer(&uart_data_fifo, fifo_get_data, &fifo_data_count, FIFO_READ_AND_CLEAN); 
           if(fifo_get_data[0]==0xB7) //包头0xB7
           {
             get_states=1;             
           }
           else  get_states=0;
           fifo_get_data[0]=0;
         }
         else if(get_states==1)
         {
           fifo_read_buffer(&uart_data_fifo, fifo_get_data, &fifo_data_count, FIFO_READ_AND_CLEAN); 
           memcpy(my_data, fifo_get_data, sizeof(my_data));
           get_states=2;
         }
         else if(get_states==2)
         {
           fifo_read_buffer(&uart_data_fifo, fifo_get_data, &fifo_data_count, FIFO_READ_AND_CLEAN); 
           if(fifo_get_data[0]==0x99) 包尾0x99
           {
             get_states=0;
             uart_write_string(UART_INDEX, "get");         
             fifo_get_data[0]=0;
           }
           else  
           {
             get_states=0;
             fifo_get_data[0]=0;
           }
         }
       }
}

OpenMV端(发):

baotou=[0xB7]#包头
baowei=[0x99]#包尾,都是用于验证数据集的准确性

def send_data(data):
    uart.write(bytearray(baotou))
    time.sleep_ms(100)
    uart.write(bytearray([data]))
    time.sleep_ms(100)
    uart.write(bytearray(baowei))
    time.sleep_ms(100)

MCU端(发):

/*在这次的任务中MCU端对OpenMV端发送数据的要求不高,
  发送数据的目的仅仅是作为接收到OpenMV端数据的一种响应,所以只要调用库里对应的write函数就行*/
uart_write_string(UART_INDEX, "get"); 
//一定要注意不能连续给同一个串口写数据,最好要有100ms以上的时间间隔,不然数据容易出错

OpenMV端(收):

get_signal = 'get' #MCU正确接收到数据之后返回字符串“get”,get_signal用于验证MCU端是否正确读取数据
uart_num = uart.any()       # 获取当前串口数据数量
  if(uart_num):
     get_data = uart.read(uart_num).strip().decode()  # 读取串口数据
     time.sleep_ms(100)#给足够的时间来读取uart发送来的数据
     if (get_data ==get_signal):
        ……

代码解析

MCU端(收)

1. 首先要将MCU读取串口数据的函数放在对应的UART中断里,且要将UART中断的优先级设置为最高,这样才能保证单片机能及时读取串口的数据而不造成数据丢失。我这里直接调用了逐飞库里对应的读串口数据函数。大家可以看到我MCU端的UART通信是基于fifo的,大家可以通过下面这篇文章来了解基于fifo的UART通信:UART的FIFO功能

2. 成功读取到数据之后,需要对数据进行解析。解析方法要和你定义的数据包格式相适应。我这里的数据包格式是“包头+所需数据+包尾”

3. 包头和包尾的内容可以自定义,最好定义为“所需数据”里不可能出现的数。需要特别注意的是,接收端认为的包头和包尾一定要和发送端定义的包头和包尾一致,不一致的话接收端不能正常解析数据。

4. 在get_uartdata函数中,我将数据解析过程分为三种状态。状态1是解析串口数据的包头,如果和自定义数据包的包头不一致,那么这个数据一定不是我需要的,丢弃。如果是我所需要的,则进入“所需数据”读取状态,即状态2。读取完成之后进入状态3解析包尾,如果该数据包的包尾与自定义包尾不一致,则说明UART通信的过程中数据受到了干扰或出现了丢包异常,此时整段数据丢弃,重新进入状态1。

OpenMV端(发):

OpenMV端作为发送端时,只需要定义好包头包尾,然后使用uart.write函数将数据发出即可。

MCU端(发):

因为在我这里MCU端只需要发送一个字符串“get”来作为成功接收到“所需数据”之后的响应,所以就没有对“get”这个数据添加包头包尾,实际测试过程中也没有出现过错误。(可能是因为发送的数据并不多,如果读者发送的数据比较多的话,最好还是加上包头包尾来校验。)

OpenMV端(收):

这里有一点是需要特别注意的,OpenMV端直接接收到的数据是字节串的格式,还不能和我们的字符串进行比较匹配,需要对数据进行解码,不解码就直接进行比较是会报错的。

uart.read(uart_num).strip().decode()

.strip()的意思是先对字符串变量进行去除首尾空白符操作

.decode()的意思是将目标二进制数据bytes转为目标字符串str类型,为解码过程。

优化改进

在写这篇文章的时候,我突然发现OpenMV端发送和MCU端接收的代码和逻辑都可以优化一下。

数据收发的思路还是不变的,数据包的格式也不变。

OpenMV端(发),修改如下:

baotou=[0xB7]#包头
baowei=[0x99]#包尾,都是用于验证数据集的准确性

def send_data(data):
    data_packet = []
    data_packet.append(baotou)
    data_packet.append(data)
    data_packet.append(baowei)
    uart.write(bytearray(data_packet))
    time.sleep_ms(100)

这样才是真正将包头、所需数据、包尾打包成一个数据包发送,OpenMV端发送数据的次数从3次变成了1次,MCU端也只需要进入一次UART中断并解析一次数据包,效率大大提高。

MCU端(收)的修改就是在原来的基础上把数据解析的操作一体化(原来是分三次进行,if 和else if不能同时满足,所以是三次):先匹配包头,接着接收数据,然后在接收到的数据中搜索并匹配包尾(可以使用for循环逐个匹配),找到包尾后发送“get”响应OpenMV。MCU端(收)的优化思路大概就是这样,代码就不演示了,不会太难,读者有需要的话可以在原来MCU接收端代码的基础上修改。

以上就是本人解决MCU端与OpenMV端之间UART通信的思路与策略,读者如有任何疑问都可以在评论区留言。感谢阅读!

你可以通过串口通信来连接OpenMV和NodeMCU。首先,你需要确保OpenMV和NodeMCU都具备串口功能,并且使用相同的波特率进行通信。 在OpenMV上,你可以使用Python编程语言来配置和控制串口通信。你需要导入`uart`模块,并使用`uart.init`函数来初始化串口。例如,以下代码将初始化一个波特率为115200的串口: ```python import uart uart.init(baudrate=115200) ``` 然后,你可以使用`uart.write`函数发送数据到NodeMCU。例如,以下代码将发送字符串"Hello"到串口: ```python uart.write('Hello') ``` 在NodeMCU上,你需要使用Arduino IDE或其他适当的开发环境进行编程。你可以使用`Serial`库来配置和控制串口通信。首先,你需要调用`Serial.begin`函数初始化串口,并指定相同的波特率。 例如,在Arduino IDE中,以下代码将初始化一个波特率为115200的串口: ```c void setup() { Serial.begin(115200); } ``` 然后,你可以使用`Serial.print`或`Serial.write`函数接收来自OpenMV的数据。例如,以下代码将从串口读取数据,并打印到串口监视器: ```c void loop() { if (Serial.available()) { char data = Serial.read(); Serial.print(data); } } ``` 这样,OpenMV和NodeMCU之间就可以进行串口通信了。你可以根据需要发送和接收数据。记得在代码中进行错误处理和适当的数据解析。希望这些信息能对你有所帮助!如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值