你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

超详细OpenMV与STM32单片机通信

[复制链接]
攻城狮Melo 发布时间:2022-11-4 16:46
1.前言' W7 e3 s) a# n9 ?8 G. Z& a$ i# Y
最近在做电磁炮,发现题目需要用到颜色跟踪,于是花了一点时间学了一下OpenMV,只学习OpenMV是远远不够的,还需要实现与单片机的通信,本以为很简单,在CSDN上找了一些代码,直接拿来修改粘贴,把代码看明白了,这些只花了几个小时,本以为自己已经弄明白了二者之间的通信,但是在后期把OpenMV端数据传输到单片机的时候却犯了难。我选择使用OLED显示传输的数据,在这里调试了许久,中间遇到了许多之前的学习漏洞,特在此写下博客记录学习经历。& m! g' E$ S& S: W: z# v
; ]) Q' m7 N4 \5 k2 }9 O
2.硬件连接
4 k# \4 z0 k! B1 a# n4 F6 y/ x我所用到的材料如下: 四针IIC OLED,OpenMV(OV7725),STM32F103C8T6最小系统板,数据线N条(OpenMV的数据线只能用官方自带的,其他的基本都用不了),杜邦线若干。
7 B2 i, N, A  ?0 N1 h+ R5 R2 j0 ^. K2 i9 {. a$ }
1.OpenMV端:由图知UART_RX—P5 ------ UART_TX—P4$ N* t3 T) M+ O# e4 G+ c7 e
20210504025305791.png 0 m8 L3 {% N/ g4 {
0 w6 n0 Q- O7 J
2.STM32端:USART_TX—PA9 -----USART_RX—PA100 H' T! E+ Q6 K, R1 X
) K1 Y0 ?+ O; c$ M
20210504025557402.png ( F( [) H8 X- c# x

7 W' W0 `9 x4 L2 T% @- N6 Z, B8 q, _3.四针OLED IIC连接:SDA—PA2-----SCL—PA1 由于使用的是模拟IIC而不是硬件IIC,可以根据个人需要修改IO口来控制SDA线和SCL线,只需要简单修改一下代码即可。
4 r+ i- J- X! N4.STM32的TX(RX)接OpenMV的RX(TX),OLED连接到STM32即可。2 G- b2 H/ S' z( p: a* |0 u
  s( g: y. F) s
: l& Q# u0 ]: L, n& n% J! L4 b$ ~9 k
3.软件代码———OpenMV端/ U2 j- [: S* Z0 G+ X+ g2 I- k+ _, |
  1. import sensor, image, time,math,pyb
      i$ d: v7 r, b3 f0 c; e0 g& J
  2. from pyb import UART,LED0 O6 h) w3 r4 Z6 r
  3. import json
    + j5 n+ R) B0 E, W( q
  4. import ustruct  C  x7 J& o; i  m* ^
  5.   [8 e. ~# ^5 H/ S
  6. sensor.reset()7 Z& n$ T6 `. Q# `$ d
  7. sensor.set_pixformat(sensor.RGB565)
    - V9 Y8 b, j) @, h- H
  8. sensor.set_framesize(sensor.QVGA): q. W' k5 a4 r: W' ^
  9. sensor.skip_frames(time = 2000)( V/ u8 o  {+ Z  L! {
  10. sensor.set_auto_gain(False) # must be turned off for color tracking
    % S) @6 W2 m- ]" @! U
  11. sensor.set_auto_whitebal(False) # must be turned off for color tracking: ~- s/ C1 n( o/ S
  12. red_threshold_01=(10, 100, 127, 32, -43, 67)/ ]- S0 F- L4 m2 c; }
  13. clock = time.clock()8 l$ Q  A" ^$ a, M

  14. 5 h0 l. M9 s( r* V- V  \/ s& y# ^
  15. uart = UART(3,115200)   #定义串口3变量
    3 U( E* \' a" p9 i' [
  16. uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
    . h) E& ~  d. \( p2 Z

  17.   S& c* g9 [! n# |% t5 w# i- g
  18. def find_max(blobs):    #定义寻找色块面积最大的函数" E! q/ Z( G$ Y" \# c) C" k
  19.     max_size=0
    7 C% [4 u0 _! A& L- S
  20.     for blob in blobs:
    - |8 T; ~  {# ?" ~5 E3 M& z
  21.         if blob.pixels() > max_size:
    / @* w7 x5 V% ^
  22.             max_blob=blob  g4 l$ t$ H8 j; u8 v2 |
  23.             max_size = blob.pixels()
    ; N  F4 [, u( w
  24.     return max_blob$ O" X9 U+ Y' G" P3 s! j9 E

  25. + Q$ f& r" A7 R+ j5 Y- j8 @7 l

  26. ! O1 Z) L# V0 p' a/ R9 ^: h
  27. def sending_data(cx,cy,cw,ch):3 d' }  u0 C+ y2 S, H/ O% n
  28.     global uart;
    * Y. D7 k% N" b/ t1 U. S. G. Y, X* U
  29.     #frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
    - `5 @+ p; v: q5 I3 t7 [) e
  30.     #data = bytearray(frame), |% X$ x( J: V  ^- E) t
  31.     data = ustruct.pack("<bbhhhhb",      #格式为俩个字符俩个短整型(2字节)
    6 \. J- y# e! R2 s' r. L$ U  |
  32.                    0x2C,                      #帧头1# j8 [+ _1 r: O0 I7 X6 f+ W
  33.                    0x12,                      #帧头2
    ! ?2 Y* w4 b+ Z! g
  34.                    int(cx), # up sample by 4   #数据1
    # m9 N, F+ n2 K* ]( v' O. R$ A2 y
  35.                    int(cy), # up sample by 4    #数据2. H4 Z: |1 D% B
  36.                    int(cw), # up sample by 4    #数据1$ U" T: h+ h* X# i8 [. l9 f
  37.                    int(ch), # up sample by 4    #数据2# Q, m4 G. o8 {7 ]1 w2 V0 @
  38.                    0x5B): K3 q0 `6 n1 W. T3 H0 q+ ]  r" \5 S: r
  39.     uart.write(data);   #必须要传入一个字节数组
    2 ~" h) W; R9 L% k# u3 `7 d
  40. , d5 z5 Z* n* X5 A

  41. . x. V( T8 Q) `# {6 a
  42. while(True):
    ; ]) a6 d9 R+ t4 p5 H4 X
  43.     clock.tick()
    . a6 \& s( ~* Z$ E$ v8 K
  44.     img = sensor.snapshot()& U8 b/ \* \- R% J1 l: J
  45.     blobs = img.find_blobs([red_threshold_01])( b- Q# p8 T) \
  46.     cx=0;cy=0;, [6 O# C! q& d- v0 ^1 H! k
  47.     if blobs:( p6 B  m0 ?, t$ Q2 X
  48.                 max_b = find_max(blobs)5 @: {! v4 V5 X; |
  49.             #如果找到了目标颜色
    7 d3 t! H3 \8 r. Q; {5 F: [/ S' c1 w
  50.             cx=max_b[5]
    " V5 p- j% n8 o$ i& H- a! l, p" H; E
  51.             cy=max_b[6], a8 i0 P! K, T" A/ H) r+ i
  52.             cw=max_b[2]
    * G3 F+ `$ s7 X) P- P/ v' H3 P5 o
  53.             ch=max_b[3]- Y0 b3 R5 a& z/ V, x2 M
  54.             img.draw_rectangle(max_b[0:4]) # rect7 F4 v0 Y, Y9 M
  55.             img.draw_cross(max_b[5], max_b[6]) # cx, cy
    : F$ ]# T5 F( r, R  ?
  56.             FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])
    5 J- l5 V. k; k2 u, e) T
  57.             #sending_data(cx,cy,cw,ch)7 J) S! r! ~2 q, F4 n
  58.             uart.write(FH)
    6 X3 j7 N& J+ U& G1 G
  59.             print(cx,cy,cw,ch)
复制代码

' a# Z+ h* C7 ?" M. fbytearray([, , ,])组合uart.write()的作用与直接调用sending_data(cx,cy,cw,ch)作用是一样的
, B" B2 N9 g5 x. }, Y4 ~3 F/ L4 H1 q- W: K7 u; J; p
" Z5 `- K- g: ~! i
4.软件代码———STM32端
. V7 ^- s! p8 K, f工程总共包含如下文件:main.c、iic.c、iic.h、oled.c、oled.h、uart.c、uart.h。由于OLED的代码存在版权问题,需要的可以邮箱私发。
! e% q; ~6 M8 H- I" Y$ R% ~
& Q# C3 @0 s# ~/***** oled.h *****/
7 }6 }' H4 \1 @% b, u0 p0 m7 E' r6 A* Y5 B* N* E
  1. #ifndef __USART_H. ^# w# o. K# I6 J
  2. #define __USART_H' ?. [) o8 s5 T1 I  s. v( N
  3. #include "sys.h"4 E+ O' b9 _+ x4 l
  4. void USART1_Init(void);//串口1初始化并启动5 _# ]2 U! ~- B( u, r
  5. #endif
复制代码
& O# e0 F, H  ?
/***** oled.c *****/
7 t' @! s8 M8 Y9 B' U) w, a5 F! O* Y7 |% J3 S6 _; q3 L
  1. #include "uart.h"/ W+ |+ j6 g# d0 Z4 Y& P' W
  2. #include "oled.h". f( i+ X" L9 L, G2 o# W8 j$ \
  3. #include "stdio.h"% d5 ~$ _  {+ e; z* x% h) k

  4. 5 t+ y5 J- H! D- t) w# T- f7 \9 a
  5. static u8 Cx=0,Cy=0,Cw=0,Ch=0;9 d" d5 C. ~  K+ _, u9 ^
  6.   b% {7 T1 U6 y* U
  7. void USART1_Init(void)
    $ z- M$ p8 ^$ [) Q
  8. {- e5 w# ?( m$ }4 g' n
  9.                  //USART1_TX:PA 9   " a0 y: S; D+ e% {
  10.                 //USART1_RX:PA10
      b: s* y8 _& d* x0 H+ w
  11.                 GPIO_InitTypeDef GPIO_InitStructure;     //串口端口配置结构体变量
    , d; S8 l' Z3 y. I! y0 ~- R
  12.                 USART_InitTypeDef USART_InitStructure;   //串口参数配置结构体变量$ A) `$ G& X* B# x' o5 Y7 O
  13.                 NVIC_InitTypeDef NVIC_InitStructure;     //串口中断配置结构体变量
    6 A$ r  x2 @, Y6 L$ |

  14. ; B% Y) Z+ m) l: U' x" T! L
  15.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);        
    5 f8 l6 S2 {9 x! ]3 C( o7 _; |
  16.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //打开PA端口时钟9 M" {0 d; Q4 `' S0 c9 [! w- Y

  17. , S+ e: d7 |+ L" V: N) A
  18.             //USART1_TX   PA9. Q! ^+ \& q" Q$ |1 C0 z0 P; S
  19.             GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                           //PA9& C" u2 @$ G3 b1 @. P6 I
  20.                    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                   //设定IO口的输出速度为50MHz: W; e7 S) s2 r$ Q, M; v7 }
  21.             GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                            //复用推挽输出
    $ M3 `  ?* l# T# |
  22.             GPIO_Init(GPIOA, &GPIO_InitStructure);                               //初始化PA9: ]# C9 D9 D7 Z
  23.             //USART1_RX          PA10) n/ b" N1 F) F' U9 c+ r, @
  24.             GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;             //PA106 Y, f9 Q5 Q0 _% [7 ^- O, [
  25.             GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入: T3 p7 H, m0 o$ R& Q% d
  26.             GPIO_Init(GPIOA, &GPIO_InitStructure);                 //初始化PA10 % }" u/ z! c6 o2 w3 }& {: ~
  27. : H/ N! K( I8 N
  28.             //USART1 NVIC 配置2 Y3 t: {& P- M" }
  29.             NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    0 `2 f# V2 I) R0 ]: x
  30.                 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;  //抢占优先级0
    # w/ _: h" Q6 y1 L' n. I: G3 l& M
  31.                 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                    //子优先级2
    / q+ B8 K4 Z7 T( ?: `+ e# c
  32.                 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                                                       //IRQ通道使能0 \' ^; f+ J! n
  33.                 NVIC_Init(&NVIC_InitStructure);                                  //根据指定的参数初始化VIC寄存器+ @. {/ I+ y! l* T: v" K6 _' V
  34. 9 r9 T$ {. [, z/ E( W% p& f
  35.             //USART 初始化设置
    - C: T9 F4 u+ P) ?
  36.                 USART_InitStructure.USART_BaudRate = 115200;                  //串口波特率为115200
    2 R$ }& ~3 G$ r, \/ Q
  37.                 USART_InitStructure.USART_WordLength = USART_WordLength_8b;   //字长为8位数据格式
    $ p# g2 q5 l& y8 |. l
  38.                 USART_InitStructure.USART_StopBits = USART_StopBits_1;        //一个停止位
    7 q8 {  N' \7 q+ K+ Y
  39.                 USART_InitStructure.USART_Parity = USART_Parity_No;           //无奇偶校验位9 h- R0 K/ Q6 F* j- M
  40.                 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;   //无硬件数据流控制: M; S% m+ ]) o" I" D8 S1 U, B
  41.                 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                          //收发模式
    1 E; J* ^- i0 V: L  p: g+ p, ~
  42.             USART_Init(USART1, &USART_InitStructure);                     //初始化串口11 h3 o. s6 Q! }
  43. " x. W8 M1 s7 A+ X7 y% n2 s4 {$ l
  44.             USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能中断% z% |0 n2 U) h; C2 Z/ p" y
  45.                    USART_Cmd(USART1, ENABLE);                     //使能串口1
    0 f, X3 q) ]$ P" K
  46.                   USART_ClearFlag(USART1, USART_FLAG_TC);        //清串口1发送标志! e% c! e/ X7 j* Z% Q
  47.                
    7 _. {* @. X8 V1 b
  48. }
    / ]* I( H: @& q
  49. 8 s7 n( a8 L7 w2 o. ]8 P/ z
  50. //USART1 全局中断服务函数
    ( a4 Q1 Q% H5 k! ~8 e# ]
  51. void USART1_IRQHandler(void)                           P" E4 a# ]( O) C' U
  52. {: Q9 X2 l8 X+ B; h8 T* H* i" `* j
  53.                 u8 com_data; # a0 J8 i- f. v% a2 N0 C% k4 D
  54.                 u8 i;, j2 u7 T1 `7 d; F
  55.                 static u8 RxCounter1=0;
    * M! A% f6 ~+ n4 v3 m# V& r
  56.                 static u16 RxBuffer1[10]={0};9 m+ F1 z3 z2 f* U# B
  57.                 static u8 RxState = 0;        * j: o. F! p" k8 \- G
  58.                 static u8 RxFlag1 = 0;
    # X/ Q- s; g; Y2 z
  59. 7 ]3 t# w+ j: i9 G2 H3 u: j* C6 ^
  60.                 if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)             //接收中断  9 g6 L' t4 G" D+ |9 @, z' p0 }
  61.                 {" s/ P2 M7 _- F! X; r
  62.                                 USART_ClearITPendingBit(USART1,USART_IT_RXNE);   //清除中断标志3 |+ Q/ n' c" x  G+ ~1 Q. P* {
  63.                                 com_data = USART_ReceiveData(USART1);: y6 F9 I8 F3 y, Z, z: b
  64.                         
    + t3 r2 P0 w2 C3 v2 O9 ?- J) F
  65.                                 if(RxState==0&&com_data==0x2C)  //0x2c帧头9 s" }. q9 g& _- [" d# Y
  66.                                 {
    + @/ w8 K$ H+ a$ }" G4 X
  67.                                         RxState=1;  m0 Y4 T& ^( p* e& J0 k5 x
  68.                                         RxBuffer1[RxCounter1++]=com_data;OLED_Refresh();+ o5 r8 k, T8 O: I
  69.                                 }
    % H- y! S% U% N- Q; L  E5 J6 m3 n
  70.                
    0 c+ L) m/ T7 F$ q, z
  71.                                 else if(RxState==1&&com_data==0x12)  //0x12帧头
    # w" q5 D9 D: M& Q
  72.                                 {) e3 T7 h( B4 \8 K0 D2 T
  73.                                         RxState=2;
    0 d4 I/ b* ~7 G. c0 g7 l
  74.                                         RxBuffer1[RxCounter1++]=com_data;
    + O: ^9 k6 q: R0 x
  75.                                 }
    ; m% m8 Z  Y1 ~  a9 c
  76.                
    * V+ i- q' T6 A0 U; m& }
  77.                                 else if(RxState==2)
    3 H; ]+ d5 y  J
  78.                                 {
    4 K5 L4 D2 k1 T  g
  79.                                         RxBuffer1[RxCounter1++]=com_data;
    6 J( U6 K" V, T9 j
  80. 4 V' ~' n- L9 E0 d
  81.                                         if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
    / A3 L% w9 d5 S! X8 f) a' F- ]
  82.                                         {* Q, s: D+ B2 L6 [, c
  83.                                                 RxState=3;- M4 f9 n; y( a4 q/ I9 q- I
  84.                                                 RxFlag1=1;/ ~1 n/ Q8 [, K) ~1 j
  85.                                                 Cx=RxBuffer1[RxCounter1-5];( |- T8 B# T2 o' A0 u) [5 K- n; w6 d$ ]
  86.                                                 Cy=RxBuffer1[RxCounter1-4];# b& o& p1 Q! q  J. }; T1 v
  87.                                                 Cw=RxBuffer1[RxCounter1-3];
    5 S1 G( j, N& g5 N) C
  88.                                                 Ch=RxBuffer1[RxCounter1-2];: ?& q+ M' z$ M" C/ r. Y
  89. / V: s& \6 C9 n
  90.                                         }5 b8 \$ M$ W' n* d
  91.                                 }
    ! K$ x" @5 ]8 m, U/ V: \
  92.                 0 {7 h2 L8 b% ]. P7 P5 ^* v9 X! ?
  93.                                 else if(RxState==3)                //检测是否接受到结束标志
    % W; ]' ]! ^" Z9 q
  94.                                 {5 Q% j9 ^- [$ Q* W  E/ Q
  95.                                                 if(RxBuffer1[RxCounter1-1] == 0x5B)
      @9 I6 h3 r  P2 w  ]
  96.                                                 {
    # p" A, \+ k1 {8 j% W
  97.                                                                         USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
    7 R2 L% _7 N  J# U' c
  98.                                                                         if(RxFlag1), h( Q# s5 N: [  i
  99.                                                                         {* G. a1 F! n+ \  c! L
  100.                                                                         OLED_Refresh();- x4 X9 c0 \& K) d* ]4 \3 o
  101.                                                                         OLED_ShowNum(0, 0,Cx,3,16,1);6 j2 L# A/ e2 s$ ], H* c% Y( `
  102.                                                                         OLED_ShowNum(0,17,Cy,3,16,1);
    ! |7 \$ t+ O2 z/ s; c  n
  103.                                                                         OLED_ShowNum(0,33,Cw,3,16,1);$ v# D( C1 y: [+ ~% @* @  m
  104.                                                                         OLED_ShowNum(0,49,Ch,3,16,1);& }- }- O0 T+ e8 [, ~
  105.                                                                         }
    5 o+ Q8 R8 O! W* X
  106.                                                                         RxFlag1 = 0;
    ; [* V. W' D( `( U
  107.                                                                         RxCounter1 = 0;+ h$ o: V2 F9 Q8 X7 I. l) l1 \
  108.                                                                         RxState = 0;- V0 j8 S/ I' j9 C1 U7 b/ U+ @" f0 f% s
  109.                                                                         USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
    ; d# ^# B6 P& H8 s
  110.                                                 }
    1 v& l+ O0 t. J
  111.                                                 else   //接收错误5 e% g9 C  S4 [
  112.                                                 {
    3 V  d; w  A0 K! I. v
  113.                                                                         RxState = 0;
    $ Y; P9 u! ?' p# L/ B) w
  114.                                                                         RxCounter1=0;
    . b+ d: h/ H" Y" ^$ y3 |
  115.                                                                         for(i=0;i<10;i++)
    % u* K7 `+ M" d- B& a- }- l- `
  116.                                                                         {/ @  k4 f: a0 W
  117.                                                                                         RxBuffer1<i>=0x00;      //将存放数据数组清零" W+ t1 k2 R9 E0 y
  118.                                                                         }" F& {+ C  ?7 ^+ v% `4 O, ^
  119.                                                 }
    , A6 e8 B7 N' S$ n% K
  120.                                 }
    1 i: k* S: }6 O+ ]: z4 a5 E3 e1 }* I
  121.         
    0 k3 ^9 r: g1 M2 k, W
  122.                                 else   //接收异常' C* e# T1 m7 t# Z
  123.                                 {% l9 f) k2 N9 k0 s
  124.                                                 RxState = 0;
    # v6 N9 w% }! \
  125.                                                 RxCounter1=0;; Z4 a9 ~+ C2 B
  126.                                                 for(i=0;i<10;i++)
    ' {0 Y( i5 d( @3 p
  127.                                                 {
    ) O# q4 t" P4 K- L
  128.                                                                 RxBuffer1<i>=0x00;      //将存放数据数组清零/ J, D/ i9 x) ~: x3 @6 B- D) K
  129.                                                 }
    * u! L& _1 _8 |, c+ z5 }) g
  130.                                 }" c  a) p9 g- z/ x

  131. ; Q9 n* y5 f9 I. x/ d; I5 d9 w
  132.                 }2 d9 ?; Z: h4 i5 A, ^2 g
  133.                 " g- F% G  @& M4 D+ z6 i% L
  134. }</i></i>
复制代码

2 f) m/ Z: K4 V3 t* q( x; m1 ?! @解释:OpenMV发送数据包给STM32,STM32利用中断接收数据并把数据存放在RxBuffer1这个数组里,并且在中断中利用OLED显示cx,cy,cw,ch四个坐标。在中断中,有如下函数:: g1 k7 ]$ d! v$ @: F
* D0 \$ W; P  J8 p/ S. A( Q7 M
  1. else if(RxState==2)
    " E- M) L, S3 A  W1 a% n& E1 R
  2.                                 {8 j4 c' u+ E3 x! T
  3.                                         RxBuffer1[RxCounter1++]=com_data;
    3 w' Z6 W: B+ R' v7 Z: o2 b
  4. + [0 U0 R2 ?5 _" s' e1 I% P
  5.                                         if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
    2 l: Q( P& R; T" _" w1 R
  6.                                         {
    ' ]/ ^/ \9 c8 I3 H/ W4 f
  7.                                                 RxState=3;
    ; l6 i3 }. e! O  Y
  8.                                                 RxFlag1=1;
    4 q0 a. S* Q. G  p
  9.                                                 Cx=RxBuffer1[RxCounter-5];
    7 }) K) p; r# s4 y1 p2 U0 O
  10.                                                 Cy=RxBuffer1[RxCounter-4];
    8 A  c3 t2 X1 H$ o
  11.                                                 Cw=RxBuffer1[RxCounter-3];( _4 j. Q7 I/ f( u9 |$ S, b
  12.                                                 Ch=RxBuffer1[RxCounter1-2];+ T0 r; r' a$ K* ~) O1 x
  13. $ g9 x6 t7 @4 C* Z+ g3 x! r* d7 I: |4 r
  14.                                         }
    7 w- c) N- k0 y" a% x5 X
  15.                                 }
复制代码
/ G6 M1 F) K) h2 x( Z
RxBuffer1是一个装有接收OpenMV数据的数组,RxCounter1起着一个计数器的作用,当RxBuffer[RxCounter1-1]存放的数据为数据包的帧位时,说明已经接收成功整个数据包,此时RxBuffer[RxCounter1-2]存放ch坐标值,RxBuffer[RxCounter1-3]存放cw坐标值,RxBuffer[RxCounter1-4]存放cy坐标值,RxBuffer[RxCounter1-5]存放cx坐标值,此后在RxState=3过程中将这四个坐标显示出来即可。
* x' Y4 S* S# p# {8 Z9 O" U特别注意的是:STM32中断每发生一次,只会接收到一字节的数据,因此,进行七次才会接收完一整帧的数据包,这一点需要读者仔细揣摩,结合上文中说的静态变量关键字static,定义了:2 k. g" y! o7 |5 E% D

4 {: u2 \  A* m5 ]
  1. u8 com_data; & N: l9 ]: I1 w# O: F. o* j
  2. u8 i;1 z+ I3 J5 [+ b& g+ Z! f# a( B) [
  3. static u8 RxCounter1=0;3 w4 c. B! u% K* l4 K
  4. static u8 RxBuffer1[10]={0};6 ~) e$ M! g+ V& t4 C( c1 N) e2 V
  5. static u8 RxState = 0;        
    , ~) ^+ y! r4 G! e* N: a, a/ W
  6. static u8 RxFlag1 = 0;
复制代码

8 k  D# f* P, ?1 ?2 }& d0 y请读者仔细揣摩为什么com_data(端口接收到的数据)、i定义的是动态的(auto),而RxBuffer1(装接收到数据的静态全局数组)、RxState(状态标志变量)、RxFlag1(接受结束标志变量)定义的确实静态的,这一点并不难理解。. ^1 _! C# A8 y7 l; k; G4 K
( [" _7 _  J& S5 K! z9 c
5.利用PC端测试数据数据是否发送接收正常
+ v! L- q. L5 B( D( _7 V在进行OpenMV与STM32的通信测试过程中,我使用了USB转TTL模块,将OpenMV(或STM32单片机)与PC端进行通信确保数据发出或者接收正常。
0 ]+ z) I0 M* i2 c/ R0 g$ i* [8 n$ _7 O( }
OpenMV&&C# w3 J! s  z& V' x
OpenMV_RX接模块TX
$ J0 s6 m* N% z& v9 IOpenMV_TX接模块RX
" ^3 P" m' h' L- n7 dOpenMV_GND接模块GND
) d* i( E  z' n8 F  f% N  E1 D然后打开OpenMV,在大循环while(True)中使用语句:1 j. M, Y0 y5 t/ \  O+ {

. F, P6 z( T( U/ K; W3 C
  1. DATA=bytearray[(1,2,3,4,5)]
    - R6 K; C" C% n. R  K
  2. uart.write(DATA)
复制代码
3 H9 C; W2 n5 u1 n9 u
打开PC端串口助手,注意设置一样的波特率、停止位、发送字节数等,查看串口助手是否接受到了数据。# z! k! l. L, N2 U9 X% h9 b

4 @) y! G8 S5 V) ?; g) U+ JSTM32&&C
/ @* `9 p! |- hSTM32_RX接模块TX  t! r/ Z2 c! \3 U2 q3 O
STM32_TX接模块RX
5 Y4 A/ Z. e0 e7 K1 r5 |5 CSTM32_GND接模块GND
. U4 \$ ^% B2 }( Z注意:不管是STM32与PC还是OpenMV与PC还是STM32与OpenMV通信,都要将二者的GND连接在一起。0 V: H1 s! x% U( S8 R1 T
在main.c中先调用stdio头文件,大循环中使用如下语句:3 ^! A( `/ v9 R6 R% z5 b3 J* [" N
  1. while(1)
    $ J0 C, ~" ~+ O% ~  K, a: }
  2. {  s+ s( _$ ?( h- o2 s
  3.                 printf("HelloWorld!");( G! K) @. Z3 W" U% p; B
  4. }
复制代码
( [+ \: s/ h8 r! z# \/ n
打开串口助手查看是否接收到了数据。
% E; `. \5 T/ f' j# ?: M7 T
3 a$ |# }  E9 D% x$ E+ A5 Z
5 ^3 O, k) R" a0 q6 C$ X6.学习补充 (代码看不懂的时候可以来看一下)
9 W7 v6 _+ M) O0 `( k补充1:static关键字(静态变量)的使用/ T0 K, I- @& R/ D) ?! f8 O8 N

! ]9 [+ ]% k. M! J7 wstatic 修饰全局函数和全局变量,只能在本源文件使用。举个例子,比如用以下语句static u8 RxBuffer[10] 定义了一个名为RxBuffer的静态数组,数组元素类型为unsigned char型。在包含Rxbuffer的源文件中,Rxbuffer相当于一个全局变量,任意地方修改RxBuffer的值,RxBuffer都会随之改变。而且包含RxBuffer的函数在多次运行后RxBuffer的值会一直保存(除非重新赋值)。在C语言学**,利用static关键字求阶乘是一个很好的例子:( }# f9 B( G/ `# \6 J% M

$ ^9 ^7 R0 \9 Y7 ]/ R( u
  1. #include“stdio.h”
    6 u6 D) `7 P5 U3 w$ E
  2. long fun(int n);( K) i# ^" Y9 N
  3. void main()
    8 r* x8 J& Q9 Q5 h) B
  4. {
    - H7 p( Y$ x7 J3 }  x
  5.     int i,n;
    * k: D5 ]# ^: t" O9 j
  6.     printf("input the value of n:");
    + e0 S! G1 M) k. q$ I0 V
  7.     scanf("%d",&n);
    $ q$ K# w5 o4 X  r( R0 Y
  8.     for(i=1;i<=n;i++)
    , ?7 n. `! _; O) h' |
  9.     {( j3 g- p5 a  G+ l# o
  10.         printf("%d! = %1d\n",i,fun(i));
    7 }/ G$ N; S2 b/ T
  11.     }
    6 j$ Q# G, j3 l$ B* N& ?
  12. }
    ; x0 n+ Q! l( G& M4 S
  13. >long fun(int n)
    ( L/ }" |5 g( |9 s/ u: O8 T
  14. {
    / s* g6 P4 E& I) R! M  ~! j
  15.     static long p=1; 6 ^& g, m4 i* e. {3 `
  16.     p=p*n;2 s# _. s+ h& a# g/ D4 U6 c
  17.     return p;
    1 C* A/ \7 m1 c1 r2 _: B
  18. }
复制代码

1 Y. G) h* |1 _# @, `0 J- Y3 E5 K效果为依次输出n!(n=1,2,3…n): |' h6 [6 M. `4 I8 r+ Q! U/ |
这个例子中,第一次p的值为1,第二次p的值变成了p x n=1 x 2=2,这个值会一直保存,如果p没有定义为静态类型,那么在第一次运算过后p的值会重新被赋值为1,这就是auto型(不声明默认为auto型)与static型的最大区别。
9 i0 A# L# z+ L7 ]/ }2 |: H: d" u' a% g6 \5 N2 X- O
总结:static关键字定义的变量是全局变量,在static所包含的函数多次运行时,该变量不会被多次初始化,只会初始化一次。
1 R$ `( R* h8 \6 z- |4 [
: [" c3 {% Z6 `. ]1 h补充2:extern关键字(外部变量)的使用0 o2 C* p: z+ U3 p
程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,而在函数之外定义的变量则称为外部变量,外部变量也就是我们所讲的全局变量。它的存储方式为静态存储,其生存周期为整个程序的生存周期。全局变量可以为本文件中的其他函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。$ [' p/ V3 Q6 e. N+ f: V3 }( V
如果整个工程由多个源文件组成,在一个源文件中想引用另外一个源文件中已经定义的外部变量,同样只需在引用变量的文件中用 extern 关键字加以声明即可。下面就来看一个多文件的示例:* l  ?. y+ \+ s+ b* N0 |

' J8 ~1 D: v7 c9 N* a0 l
  1. /****max.c****/5 M- P( O8 @# u9 `0 x  }0 {
  2. #include <stdio.h>0 m. \, _# u8 [7 a: W
  3. /*外部变量声明*/
    5 E1 Q2 F  i4 k; _  w) F6 J7 ^
  4. extern int g_X ;
    : l6 l- M% M4 ]$ \. m! R1 S
  5. extern int g_Y ;
    + E- N4 A. P7 X' |7 H3 c( S
  6. int max()
    % Y) ?9 \7 a3 y$ K
  7. {
    % S: k6 f. \" T+ p6 G
  8.     return (g_X > g_Y ? g_X : g_Y);
    9 _" C' G6 O; @0 M
  9. }8 r# }% e0 n3 P# }
  10. /***main.c****/# ~5 |7 h) Z6 a2 u, V1 m, q8 x
  11. #include <stdio.h>  ?" K! u* l, R6 n
  12. /*定义两个全局变量*/
    ' o' t. q9 c5 g2 k- S( k, B. @
  13. int g_X=10;; t3 C7 W6 Y3 |, `1 F
  14. int g_Y=20;
    & L% D4 {6 r5 O  @6 K3 r2 {
  15. int max();# l, {. z* u+ m0 ~! |# w5 H( ~
  16. int main(void)
    & |- u; ~7 A8 K7 F  T- w8 ^
  17. {
    2 {  H$ U$ S' a8 u* t7 c1 t
  18.     int result;
    2 b6 c  y. P7 [. X4 e; ^
  19.     result = max();
    : K! K% h; O& X* t' Q& S* y0 @
  20.     printf("the max value is %d\n",result);$ j  A* ]2 C. Y7 Q
  21.     return 0;
    4 W8 f5 ?! ?2 D( X: n: |" |/ b
  22. }6 x% j8 G8 B( h5 `1 j' r
  23. 运行结果为:: L6 O4 L% M# v$ O
  24. the max value is 209 g4 Q1 [: E, }: A- }& b
复制代码
+ k( J/ M/ A7 d% X. p0 M
对于多个文件的工程,都可以采用上面这种方法来操作。对于模块化的程序文件,可在其文件中预先留好外部变量的接口,也就是只采用 extern 声明变量,而不定义变量,max.c 文件中的 g_X 与 g_Y 就是如此操作的。比如想要在主函数中调用usart.c中的变量x,usart.c中有着这样的定义:static u8 x=0在usart.h中可以这样写:extern u8 x在main.c中包含usart.h头文件,这样在编译的时候就会在main.c中调用x外部变量。* p' Q& o% |/ `
7 h$ G( z# b, I2 q2 ^8 M
总结:extern关键字是外部变量,静态类型的全局变量,可以在源文件中调用其他文件中的变量,在多文件工程中配合头文件使用。/ U  t& r$ {- [+ @9 ~2 b) Z4 s
$ A& B5 ?+ ^: m/ x
补充3:MicroPython一些库函数的解释1 b. t/ e4 n+ ]# ]$ T6 T9 M2 m

# P2 s% i8 {" ]1 c8 z& d5 r1.ustruct.pack函数:9 L" x# ~6 Z0 k6 v1 \3 j7 }! T& t8 C$ i
import ustruct,在ustruct中
$ Y0 ~& H- r8 ~7 O. s8 ^
  1. data = ustruct.pack("<bbhhhhb",      #格式为俩个字符俩个短整型(2字节)
    2 G7 }, x! }8 u& j7 _) l3 X: u" I
  2.                    0x2C,                      #帧头1# b. p7 K, a4 i6 h) i* h5 V2 ]
  3.                    0x12,                      #帧头2
    6 S/ \: [1 R4 O
  4.                    int(cx), # up sample by 4   #数据1
    / ^9 I/ `' Z4 [. A- I5 u
  5.                    int(cy), # up sample by 4    #数据25 c3 S7 B1 ?/ }* k# x
  6.                    int(cw), # up sample by 4    #数据1$ B- S/ {5 R# \
  7.                    int(ch), # up sample by 4    #数据21 C. }3 a% l7 s+ V; t
  8.                    0x5B)
复制代码
- u5 }) Z' Y3 T( ]0 u) Z: g
""bbhhhhb"简单来说就是要发送数据的声明,bbhhhhb共七个,代表发送七个数据,对照下面的表,可以知道七个数据按时序发送为unsigner char、unsigned char、short、short、short、short、unsigned char。0x2c为数据帧的帧头,即检测到数据流的开始,但是一个帧头可能会出现偶然性,因此设置两个帧头0x2c与0x12以便在中断中检测是否检测到了帧头以便存放有用数据。0x5b为帧尾,即数据帧结束的标志。
4 f  q: D8 t8 k% x- x0 O' d1 D1 u8 A' z2 I/ M4 |9 ?1 s' O
20210504040156260.png & W5 a0 t$ i9 _0 q

3 X% r& c6 @; y2.bytearray([ , , , ])函数:
% z) s; q' Z" d; T( P. ]' e用于把十六进制数据以字节形式存放到字节数组中,以便以数据帧的形式发送出去进行通信。4 B( N: ?$ b4 a# Z' F" W3 W

  A0 o0 m4 z3 N3 \' x2 T2 c
  1. FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])
    ; n, u' I. D( [+ g  f1 d" {8 [3 }
  2. uart,write(FH)
复制代码

' l. p4 z. Z( x, m" f, _7.效果展示(可以先来看效果)) I% U5 J* i0 k" s" N- x% J, I2 a
2 k. F' H6 `' W3 R7 A
20210504041702364.png
# X- F6 e9 e/ E: p' Q: U从上到下依次为CX,CY,CW,CH. T5 J4 R2 N! A+ \& U0 r
" x( s2 m8 M9 H% o& l: ^
8.博客更新3 R; O# p2 o' n1 V8 ]# [3 i+ J
1.有朋友反馈OpenMv端找不到色块就会报错,解决方案如下:9 J! P- O- x  b4 f' d; D

; T9 l. b9 F+ k; [" h% K7 A
  1. while(True):
    0 y( o" r+ I. U( G
  2.     clock.tick()
    & K1 [4 \. G) o" U! b5 j7 k
  3.     img = sensor.snapshot()9 i+ u& d/ x' w" n. z: S- d
  4.     blobs = img.find_blobs([red_threshold_01])
    + I( [( m4 I7 o* f% t, _  B
  5.     cx=0;cy=0;8 Y! C4 t/ x, k% |, A8 g
  6.     if blobs:% W6 h& F9 l4 [  k( g
  7.                 max_b = find_max(blobs)$ G6 O' u. u$ ~/ @4 H
  8.             #如果找到了目标颜色" j+ N- c) k! I. R+ w
  9.             cx=max_b[5]
    5 V& h6 k' j5 V+ o; U, r* y
  10.             cy=max_b[6]
    / T) @( v1 }7 n* v* l9 T) J) S
  11.             cw=max_b[2]( E5 Q; X8 m0 _, A* Q4 M5 z
  12.             ch=max_b[3]
    / l4 w: `, P. ~5 u9 f
  13.             img.draw_rectangle(max_b[0:4]) # rect4 U  n' F! y9 z, p8 Z) M- q  k
  14.             img.draw_cross(max_b[5], max_b[6]) # cx, cy! B- `' ]- x# p3 ?) j/ v3 v- d
  15.             FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])
    9 L9 n* ?1 f3 T  P
  16.             #sending_data(cx,cy,cw,ch)
    3 @. q5 o9 e1 ~: S
  17.             uart.write(FH)
    5 h1 z; Q# ]7 k* Y* o
  18.             print(cx,cy,cw,ch)
复制代码
+ _; ^! @7 J9 L( i6 ^# B9 H
在以上代码中,将max_b = find_max(blobs) 移到if blobs外即可。
0 \  b( y( ?% M$ T' p
- e3 o0 P$ p4 d7 {( ]3 {: R2.有朋友反馈OpenMV发送数据只能发送一个字节,也就是说大于255的数据无法直接通过代码完成,现在提供以下解决方案:在STM32端代码中依次保存大于255数字的高八位和低八位最后在组合在一起即可。! F! C- \7 A% Z) f; _" n; P
0 h& W% r) @: [( c+ \6 [) M0 x
  1. #if 1 3 m! n4 s$ b! u. E
  2. int main()
    - J0 H0 z9 ~0 _6 ?
  3. {# G, w- w: P; N" B
  4.         #if 0
    4 K( M$ H* X( H. W1 m0 s; m: v( r
  5.         //字符型数据分成四个字节存放在数组中
    ' `, Q* Y2 u* S0 J2 v7 E& {
  6.         float m = 23.25;6 [/ t* L7 _+ j4 M, q& m2 f
  7.         unsigned char *a;
    . \7 E, u; w- F+ q) y) ]9 I# h3 ~
  8.         a = (unsigned char *)&m;( z5 w# e9 j$ K5 x+ S
  9.         printf("0x%x \n0x%x \n0x%x \n0x%x \n",a[0],a[1],a[2],a[3]);
    ' S3 n, Z/ t: B+ D  L
  10.         
    " N) G7 M! g. K3 r
  11.         #endif) j5 t' I' R2 o
  12.         & r% c0 I7 L' r* n5 K
  13.         #if 13 B9 _% v% W: a
  14.         //四个字节数据合成存放在数组中
    9 J  }* W& c# K! u4 w" q" N
  15.         unsigned char a[]={0x00,0x00,0xba,0x41};
    - X2 K; L  R4 B( Y4 C4 i( I
  16.         float BYTE;& {9 N: d/ b9 z. U9 b4 f; x+ K# d+ u
  17.         BYTE = *(float *)&a;# g  c- f1 b. y9 @( {, S5 g5 J
  18.         printf("%f\n",BYTE);$ I/ ~( B. G" u" A% d% ?
  19.         #endif+ l( p2 z9 f7 E9 N, n
  20. }
    7 M! K7 J9 B$ O" M& s# v2 R
  21. #endif
复制代码
* n3 r7 Z! n; S2 [7 W
上述代码实现了将四个字节转换为一个浮点数的功能,同时也实现了将一个浮点数拆分为四个字节功能。在Openmv传数据时,只能传输一个字节,大于255的数无法以一字节形式发送,因此可以在Openmv端将该数据拆分成两个字节,分别发送给Stm32端,同时Stm32端对传来的数据进行合成,合成并解析为对应的数据。! _3 e: ?. G  z/ ?$ q# ?0 p
另一种解决方案:python传数据的1/2,单片机在乘2即可。( [, S. V# B3 G( `% f

- D8 ^4 I" g% T5 W————————————————
7 {: q% x, N7 H& M0 A版权声明:Wu__La6 n6 Y7 V: Z; i" L" K7 F6 u) j3 p

: [# ]$ C8 \) l7 _1 Q- Z, o
9 m' Y) d* L- c
' k. `! m0 H, ~( x( c& ?  N* J/ j5 W0 R$ I5 ~2 O# V
收藏 评论0 发布时间:2022-11-4 16:46

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版