• OpenMV与STM32之间的通信(附源码)


    本篇文章旨在记录我电赛期间使用openmv和stm32单片机之间进行串口通信,将openmv识别到的坐标传输给单片机。背景是基于2023年全国大学生电子设计大赛E题:舵机云台追踪识别。

    单片机的串口通信原理我便不再详细讲解,下面直接上代码分析。

    值得注意的是接线:RX——>TX

                                     TX——>RX

                                     单片机和OPENMV必须共地

    非常重要!!!!

    一、串口通信传输两个数据(x坐标和y坐标) 

    (一)、 OPENMV串口通信部分

    1. import sensor, image, time,math,pyb
    2. from pyb import UART,LED
    3. import json
    4. import ustruct
    5. sensor.reset()
    6. sensor.set_pixformat(sensor.RGB565)
    7. sensor.set_framesize(sensor.QVGA)
    8. sensor.skip_frames(time = 2000)
    9. sensor.set_auto_gain(False) # must be turned off for color tracking
    10. sensor.set_auto_whitebal(False) # must be turned off for color tracking
    11. red_threshold_01=(10, 100, 127, 32, -43, 67)
    12. clock = time.clock()
    13. uart = UART(3,115200) #定义串口3变量
    14. uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
    15. def find_max(blobs): #定义寻找色块面积最大的函数
    16. max_size=0
    17. for blob in blobs:
    18. if blob.pixels() > max_size:
    19. max_blob=blob
    20. max_size = blob.pixels()
    21. return max_blob
    22. def sending_data(cx,cy,cw,ch):
    23. global uart;
    24. #frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
    25. #data = bytearray(frame)
    26. data = ustruct.pack(", #格式为俩个字符俩个短整型(2字节)
    27. 0x2C, #帧头1
    28. 0x12, #帧头2
    29. int(cx), # up sample by 4 #数据1
    30. int(cy), # up sample by 4 #数据2
    31. int(cw), # up sample by 4 #数据1
    32. int(ch), # up sample by 4 #数据2
    33. 0x5B)
    34. uart.write(data); #必须要传入一个字节数组
    35. while(True):
    36. clock.tick()
    37. img = sensor.snapshot()
    38. blobs = img.find_blobs([red_threshold_01])
    39. max_b = find_max(blobs)
    40. cx=0;cy=0;
    41. if blobs:
    42. #如果找到了目标颜色
    43. cx=max_b[5]
    44. cy=max_b[6]
    45. cw=max_b[2]
    46. ch=max_b[3]
    47. img.draw_rectangle(max_b[0:4]) # rect
    48. img.draw_cross(max_b[5], max_b[6]) # cx, cy
    49. FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])
    50. #sending_data(cx,cy,cw,ch)
    51. uart.write(FH)
    52. print(cx,cy,cw,ch)

    注意观察下图标注的部分,我不做详细讲解,但是很容易理解: 

    接下来请看STM32串口通信部分的代码:

    1. #include "uart.h"
    2. #include "oled.h"
    3. #include "stdio.h"
    4. static u8 Cx=0,Cy=0,Cw=0,Ch=0;
    5. void USART1_Init(void)
    6. {
    7. //USART1_TX:PA 9
    8. //USART1_RX:PA10
    9. GPIO_InitTypeDef GPIO_InitStructure; //串口端口配置结构体变量
    10. USART_InitTypeDef USART_InitStructure; //串口参数配置结构体变量
    11. NVIC_InitTypeDef NVIC_InitStructure; //串口中断配置结构体变量
    12. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    13. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //打开PA端口时钟
    14. //USART1_TX PA9
    15. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA9
    16. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设定IO口的输出速度为50MHz
    17. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
    18. GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9
    19. //USART1_RX PA10
    20. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA10
    21. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
    22. GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA10
    23. //USART1 NVIC 配置
    24. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    25. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ; //抢占优先级0
    26. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
    27. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
    28. NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
    29. //USART 初始化设置
    30. USART_InitStructure.USART_BaudRate = 115200; //串口波特率为115200
    31. USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
    32. USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
    33. USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
    34. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
    35. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
    36. USART_Init(USART1, &USART_InitStructure); //初始化串口1
    37. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能中断
    38. USART_Cmd(USART1, ENABLE); //使能串口1
    39. //如下语句解决第1个字节无法正确发送出去的问题
    40. USART_ClearFlag(USART1, USART_FLAG_TC); //清串口1发送标志
    41. }
    42. //USART1 全局中断服务函数
    43. void USART1_IRQHandler(void)
    44. {
    45. u8 com_data;
    46. u8 i;
    47. static u8 RxCounter1=0;
    48. static u16 RxBuffer1[10]={0};
    49. static u8 RxState = 0;
    50. static u8 RxFlag1 = 0;
    51. if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) //接收中断
    52. {
    53. USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除中断标志
    54. com_data = USART_ReceiveData(USART1);
    55. if(RxState==0&&com_data==0x2C) //0x2c帧头
    56. {
    57. RxState=1;
    58. RxBuffer1[RxCounter1++]=com_data;OLED_Refresh();
    59. }
    60. else if(RxState==1&&com_data==0x12) //0x12帧头
    61. {
    62. RxState=2;
    63. RxBuffer1[RxCounter1++]=com_data;
    64. }
    65. else if(RxState==2)
    66. {
    67. RxBuffer1[RxCounter1++]=com_data;
    68. if(RxCounter1>=10||com_data == 0x5B) //RxBuffer1接受满了,接收数据结束
    69. {
    70. RxState=3;
    71. RxFlag1=1;
    72. Cx=RxBuffer1[RxCounter1-5];
    73. Cy=RxBuffer1[RxCounter1-4];
    74. Cw=RxBuffer1[RxCounter1-3];
    75. Ch=RxBuffer1[RxCounter1-2];
    76. }
    77. }
    78. else if(RxState==3) //检测是否接受到结束标志
    79. {
    80. if(RxBuffer1[RxCounter1-1] == 0x5B)
    81. {
    82. USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
    83. if(RxFlag1)
    84. {
    85. OLED_Refresh();
    86. OLED_ShowNum(0, 0,Cx,3,16,1);
    87. OLED_ShowNum(0,17,Cy,3,16,1);
    88. OLED_ShowNum(0,33,Cw,3,16,1);
    89. OLED_ShowNum(0,49,Ch,3,16,1);
    90. }
    91. RxFlag1 = 0;
    92. RxCounter1 = 0;
    93. RxState = 0;
    94. USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
    95. }
    96. else //接收错误
    97. {
    98. RxState = 0;
    99. RxCounter1=0;
    100. for(i=0;i<10;i++)
    101. {
    102. RxBuffer1[i]=0x00; //将存放数据数组清零
    103. }
    104. }
    105. }
    106. else //接收异常
    107. {
    108. RxState = 0;
    109. RxCounter1=0;
    110. for(i=0;i<10;i++)
    111. {
    112. RxBuffer1[i]=0x00; //将存放数据数组清零
    113. }
    114. }
    115. }
    116. }

    注意观察下面的图:

    二、串口通信传输多个数据(四个点的x、y坐标同时传输给STM32单片机

    (一)OPENMV串口部分

    1. from machine import Pin
    2. import sensor, image, time, pyb
    3. #import seekfree
    4. from pyb import UART
    5. # 初始化TFT180屏幕
    6. #lcd = seekfree.LCD180(3)
    7. # 初始化摄像头
    8. sensor.reset()
    9. sensor.set_pixformat(sensor.RGB565) # 设置图像色彩格式为RGB565格式
    10. sensor.set_framesize(sensor.QQVGA) # 设置图像大小为160*120
    11. sensor.set_auto_whitebal(True) # 设置自动白平衡
    12. sensor.set_brightness(3000) # 设置亮度为3000
    13. sensor.skip_frames(time = 20) # 跳过帧
    14. uart = UART(3, 115200,timeout_char=3000) #配置串口
    15. clock = time.clock()
    16. def sending_data(cx,cy,cw,ch):
    17. global uart;
    18. data = ustruct.pack(", #格式为俩个字符俩个短整型(2字节)
    19. 0x2C, #帧头1
    20. 0x12, #帧头2
    21. int (cx1), # up sample by 4 #数据26
    22. int (cy1),
    23. int (cx2), # up sample by 4 #数据26
    24. int (cy2),
    25. int (cx3), # up sample by 4 #数据26
    26. int (cy3),
    27. int (cx4), # up sample by 4 #数据26
    28. int (cy4),
    29. 0x5B)
    30. uart.write(data); #必须要传入一个字节数组
    31. while(True):
    32. clock.tick()
    33. img = sensor.snapshot()
    34. # -----矩形框部分-----
    35. # 在图像中寻找矩形
    36. for r in img.find_rects(threshold = 10000):
    37. # 判断矩形边长是否符合要求
    38. if r.w() > 20 and r.h() > 20:
    39. # 在屏幕上框出矩形
    40. img.draw_rectangle(r.rect(), color = (255, 0, 0), scale = 4)
    41. # 获取矩形角点位置
    42. corner = r.corners()
    43. # 在屏幕上圈出矩形角点
    44. img.draw_circle(corner[0][0], corner[0][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
    45. img.draw_circle(corner[1][0], corner[1][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
    46. img.draw_circle(corner[2][0], corner[2][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
    47. img.draw_circle(corner[3][0], corner[3][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
    48. # 打印四个角点坐标, 角点1的数组是corner[0], 坐标就是(corner[0][0],corner[0][1])
    49. # 角点检测输出的角点排序每次不一定一致,矩形左上的角点有可能是corner0,1,2,3其中一个
    50. corner1_str = f"corner1 = ({corner[0][0]},{corner[0][1]})"
    51. corner2_str = f"corner2 = ({corner[1][0]},{corner[1][1]})"
    52. corner3_str = f"corner3 = ({corner[2][0]},{corner[2][1]})"
    53. corner4_str = f"corner4 = ({corner[3][0]},{corner[3][1]})"
    54. print(corner1_str + "\n" + corner2_str + "\n" + corner3_str + "\n" + corner4_str)
    55. # 显示到屏幕上,此部分会降低帧率
    56. #lcd.show_image(img, 160, 120, 0, 0, zoom=0) #屏幕显示
    57. #串口通信传输的数据
    58. cx1=(int)(corner[0][0]*10)
    59. cy1=(int)(corner[0][1]*10)
    60. cx2=(int)(corner[1][0]*10)
    61. cy2=(int)(corner[1][1]*10)
    62. cx3=(int)(corner[2][0]*10)
    63. cy3=(int)(corner[2][1]*10)
    64. cx4=(int)(corner[3][0]*10)
    65. cy4=(int)(corner[3][1]*10)
    66. FH=bytearray([0x2C,0x12,cx1,cy1,cx2,cy2,cx3,cy3,cx4,cy4,0x5B])
    67. uart.write(FH)
    68. cx1=0
    69. cy1=0
    70. cx2=0
    71. cy2=0
    72. cx3=0
    73. cy3=0
    74. cx4=0
    75. cy4=0
    76. # 打印帧率
    77. print(clock.fps())

    下面请观察这幅代码截图:

    (二)、STM32串口通信部分

    1. #include "stm32f10x.h" // Device header
    2. #include
    3. #include
    4. #include "OLED.h"
    5. #include "LED.h"
    6. #include "Serial.h"
    7. uint8_t Serial_RxData;
    8. uint8_t Serial_RxFlag;
    9. static int16_t Cx1=0,Cy1=0,Cx2=0,Cy2=0,Cx3=0,Cy3=0,Cx4=0,Cy4=0;
    10. int Cx5[16];//用于存放分段求的坐标值
    11. int Cy5[16];
    12. //static u8 RxFlag1 = 0;//串口中断接收标志位
    13. extern float Ang1,Ang2,AngFlag;
    14. extern float Angle1,Angle2;
    15. int avel_X1 ;
    16. int avel_X2 ;
    17. int avel_X3 ;
    18. int avel_X4 ;
    19. int avel_Y1 ;
    20. int avel_Y2 ;
    21. int avel_Y3 ;
    22. int avel_Y4 ;
    23. void Serial_Init(void)
    24. {
    25. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
    26. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    27. //TX
    28. GPIO_InitTypeDef GPIO_InitStructure;
    29. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    30. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    31. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    32. GPIO_Init(GPIOB, &GPIO_InitStructure);
    33. //RX
    34. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    35. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    36. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    37. GPIO_Init(GPIOB, &GPIO_InitStructure);
    38. USART_InitTypeDef USART_InitStructure;
    39. USART_InitStructure.USART_BaudRate = 115200;
    40. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    41. USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    42. USART_InitStructure.USART_Parity = USART_Parity_No;
    43. USART_InitStructure.USART_StopBits = USART_StopBits_1;
    44. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    45. USART_Init(USART3, &USART_InitStructure);
    46. USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
    47. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    48. NVIC_InitTypeDef NVIC_InitStructure;
    49. NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    50. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    51. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    52. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    53. NVIC_Init(&NVIC_InitStructure);
    54. USART_Cmd(USART3, ENABLE);
    55. }
    56. void Serial_SendByte(uint8_t Byte)
    57. {
    58. USART_SendData(USART3, Byte);
    59. while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
    60. }
    61. void Serial_SendArray(uint8_t *Array, uint16_t Length)
    62. {
    63. uint16_t i;
    64. for (i = 0; i < Length; i ++)
    65. {
    66. Serial_SendByte(Array[i]);
    67. }
    68. }
    69. void Serial_SendString(char *String)
    70. {
    71. uint8_t i;
    72. for (i = 0; String[i] != '\0'; i ++)
    73. {
    74. Serial_SendByte(String[i]);
    75. }
    76. }
    77. uint32_t Serial_Pow(uint32_t X, uint32_t Y)
    78. {
    79. uint32_t Result = 1;
    80. while (Y --)
    81. {
    82. Result *= X;
    83. }
    84. return Result;
    85. }
    86. void Serial_SendNumber(uint32_t Number, uint8_t Length)
    87. {
    88. uint8_t i;
    89. for (i = 0; i < Length; i ++)
    90. {
    91. Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
    92. }
    93. }
    94. int fputc(int ch, FILE *f)
    95. {
    96. Serial_SendByte(ch);
    97. return ch;
    98. }
    99. void Serial_Printf(char *format, ...)
    100. {
    101. char String[100];
    102. va_list arg;
    103. va_start(arg, format);
    104. vsprintf(String, format, arg);
    105. va_end(arg);
    106. Serial_SendString(String);
    107. }
    108. //USART3 全局中断服务函数
    109. void USART3_IRQHandler(void)
    110. {
    111. int com_data;
    112. u8 i;
    113. u8 Jieshou = 1;
    114. static u8 RxCounter1=0;
    115. static int RxBuffer1[16]={0};
    116. static u8 RxState = 0;
    117. static u8 RxFlag1 = 0;//串口中断接收标志位,已被移除至函数体外作为全局变量
    118. if( USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET && Jieshou == 1) //接收中断
    119. {
    120. // OLED_ShowSignedNum(1,1,520,4);
    121. USART_ClearITPendingBit(USART3,USART_IT_RXNE); //清除中断标志
    122. com_data = USART_ReceiveData(USART3);
    123. if(Jieshou == 1)
    124. {
    125. if(RxState==0&&com_data==0x2C) //0x2c帧头
    126. {
    127. RxBuffer1[RxCounter1++]=com_data;
    128. RxState=1;
    129. }
    130. else if(RxState==1&&com_data==0x12) //0x12帧头
    131. {
    132. RxBuffer1[RxCounter1++]=com_data;
    133. RxState=2;
    134. }
    135. else if(RxState==2)
    136. {
    137. RxBuffer1[RxCounter1++]=com_data;
    138. if(RxCounter1>=14||com_data == 0x5B) //RxBuffer1接受满了,接收数据结束
    139. {
    140. RxState=3;
    141. RxFlag1=1;
    142. Jieshou = 2;
    143. Cx1=RxBuffer1[RxCounter1-9];
    144. Cy1=RxBuffer1[RxCounter1-8];
    145. Cx2=RxBuffer1[RxCounter1-7];
    146. Cy2=RxBuffer1[RxCounter1-6];
    147. Cx3=RxBuffer1[RxCounter1-5];
    148. Cy3=RxBuffer1[RxCounter1-4];
    149. Cx4=RxBuffer1[RxCounter1-3];
    150. Cy4=RxBuffer1[RxCounter1-2];
    151. OLED_ShowSignedNum(1,1,Cx1,4);
    152. OLED_ShowSignedNum(2,1,Cy1,4);
    153. OLED_ShowSignedNum(3,1,Cx2,4);
    154. OLED_ShowSignedNum(4,1,Cy2,4);
    155. OLED_ShowSignedNum(1,7,Cx3,4);
    156. OLED_ShowSignedNum(2,7,Cy3,4);
    157. OLED_ShowSignedNum(3,7,Cx4,4);
    158. OLED_ShowSignedNum(4,7,Cy4,4);
    159. }
    160. }
    161. }
    162. else if(RxState==3) //检测是否接受到结束标志
    163. {
    164. if(RxBuffer1[RxCounter1-1] == 0x5B)
    165. {
    166. USART_ITConfig(USART3,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
    167. if(RxFlag1)
    168. {
    169. AngFlag=0;
    170. HuanRaoZuoBiao();
    171. //
    172. // OLED_ShowSignedNum(1,1,Cx1,4);
    173. // OLED_ShowSignedNum(2,1,Cx2,4);
    174. // OLED_ShowSignedNum(3,1,avel_X1,4);
    175. // OLED_ShowSignedNum(4,1,Cx5[0],4);
    176. AngFlag=1;
    177. RxFlag1 = 0;
    178. RxCounter1 = 0;
    179. RxState = 0;
    180. }
    181. USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
    182. }
    183. else //接收错误
    184. {
    185. RxState = 0;
    186. RxCounter1=0;
    187. for(i=0;i<10;i++)
    188. {
    189. RxBuffer1[i]=0x00; //将存放数据数组清零
    190. }
    191. }
    192. }
    193. else //接收异常
    194. {
    195. RxState = 0;
    196. RxCounter1=0;
    197. for(i=0;i<10;i++)
    198. {
    199. RxBuffer1[i]=0x00; //将存放数据数组清零
    200. }
    201. }
    202. }
    203. }

     注意观察下面这副代码截图:

    以上便是我对电赛期间OPENMV与单片机之间实现串口通信的代码实现。学者若有疑问或需要代码工程,可以私聊我。收到后我会及时回复。

  • 相关阅读:
    java8到java17的主要新增语法特性
    Android 与 Linux内核(学习ing)
    编辑器实现思路
    [深入研究4G/5G/6G专题-45]: 5G Link Adaption链路自适应-1-总体架构
    C#适配器模式
    MapReduce
    [学习笔记]Python for Data Analysis, 3E-1.序言
    企事业单位/公司电脑文件透明加密保护 | 防泄密软件\系统!
    人工智能经常损失函数和优化算法
    基于JAVA+SSM+微信小程序+MySql的图书捐赠管理系统设计与实现
  • 原文地址:https://blog.csdn.net/m0_73931287/article/details/132701860