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

超详细OpenMV与STM32单片机通信

[复制链接]
攻城狮Melo 发布时间:2022-11-4 16:46
1.前言. T" N3 A0 A7 K
最近在做电磁炮,发现题目需要用到颜色跟踪,于是花了一点时间学了一下OpenMV,只学习OpenMV是远远不够的,还需要实现与单片机的通信,本以为很简单,在CSDN上找了一些代码,直接拿来修改粘贴,把代码看明白了,这些只花了几个小时,本以为自己已经弄明白了二者之间的通信,但是在后期把OpenMV端数据传输到单片机的时候却犯了难。我选择使用OLED显示传输的数据,在这里调试了许久,中间遇到了许多之前的学习漏洞,特在此写下博客记录学习经历。
- A0 `9 U9 t& l! Z4 b0 ]  ^
0 N9 H) v+ c. e2.硬件连接
6 Z9 \; _0 O4 G+ ]$ k我所用到的材料如下: 四针IIC OLED,OpenMV(OV7725),STM32F103C8T6最小系统板,数据线N条(OpenMV的数据线只能用官方自带的,其他的基本都用不了),杜邦线若干。
8 w4 I1 H) P* q0 n5 u4 u8 Z) f* U( S7 ~2 J: F: f
1.OpenMV端:由图知UART_RX—P5 ------ UART_TX—P43 S$ v& n1 h5 O2 s
20210504025305791.png
, v# u* x7 n- B" c9 q+ E1 n( T) m! S0 |4 D8 S' q
2.STM32端:USART_TX—PA9 -----USART_RX—PA108 K( W" J8 S! F5 B
4 H! C$ r  m# J, Z
20210504025557402.png
: S, i: h; G4 `4 ]7 |  S' }8 y( a% K+ M4 _' ]% i
3.四针OLED IIC连接:SDA—PA2-----SCL—PA1 由于使用的是模拟IIC而不是硬件IIC,可以根据个人需要修改IO口来控制SDA线和SCL线,只需要简单修改一下代码即可。9 w5 T& F+ M$ r2 f9 ?2 |, F
4.STM32的TX(RX)接OpenMV的RX(TX),OLED连接到STM32即可。& ]9 U, i6 |) W, ]
0 T- r5 e3 s6 U7 z

' f4 P1 O% {/ M* x' w+ r7 G* v3.软件代码———OpenMV端
, `9 j& F8 B. I* |/ n$ j8 V( x3 D3 r- D
  1. import sensor, image, time,math,pyb
    - D: q4 p9 [6 w5 i  q, [, J
  2. from pyb import UART,LED
    : ~+ j, ]: n: G+ h
  3. import json
    : w. u0 u1 Z6 I9 W: G
  4. import ustruct) ^( b5 Q/ \* i3 I, a

  5. 6 Y' x) @1 I0 U8 o, u8 ~3 P
  6. sensor.reset()9 j- Z5 ~6 c2 b
  7. sensor.set_pixformat(sensor.RGB565)
    ( c7 k3 b2 E% ~
  8. sensor.set_framesize(sensor.QVGA)* w% b0 ~8 L- n! P* `" ?8 V
  9. sensor.skip_frames(time = 2000)& p) i/ x6 a. i1 J: O# e
  10. sensor.set_auto_gain(False) # must be turned off for color tracking
    $ Q; A0 c8 E3 k. V2 _; }) Y
  11. sensor.set_auto_whitebal(False) # must be turned off for color tracking1 d% V0 |: \% y9 t( Q
  12. red_threshold_01=(10, 100, 127, 32, -43, 67). m+ `2 {! q6 O# q3 E
  13. clock = time.clock()
    # X* v2 j& a5 ~, E+ x8 @+ ]" A3 E

  14. 7 W$ _5 L' k/ B3 }! j
  15. uart = UART(3,115200)   #定义串口3变量6 G( m# F# w/ v
  16. uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters6 S* a/ n7 ?* E% L

  17. ; h  V# Z. x# ~8 i1 i
  18. def find_max(blobs):    #定义寻找色块面积最大的函数
    8 x) k3 P& v* H9 N. D) t' I* e( D
  19.     max_size=0
    ' t6 G' n: r9 s+ @
  20.     for blob in blobs:7 O5 T8 P- X1 ?. Q2 U5 b
  21.         if blob.pixels() > max_size:  H' x% ?1 X; I% h" T
  22.             max_blob=blob: K6 O3 f  a: p  f9 E
  23.             max_size = blob.pixels()1 G+ F) n3 g( G5 M3 j; p+ f
  24.     return max_blob5 G: o3 w: S: g5 r4 C# N* v
  25. - b9 V& K$ l4 J+ J! O# |  X3 e

  26. $ L# i: @2 q) r- [# P6 |
  27. def sending_data(cx,cy,cw,ch):
    ! K9 w  b* Q1 ?2 A2 Q0 h
  28.     global uart;
    1 @$ j( w  b3 u" O0 @) o, z
  29.     #frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];# y4 X5 A! e' D. D, }' }
  30.     #data = bytearray(frame)
    7 V" G- ^: @) y' c1 e
  31.     data = ustruct.pack("<bbhhhhb",      #格式为俩个字符俩个短整型(2字节)9 n+ |  |4 R" j+ B8 g) I
  32.                    0x2C,                      #帧头14 {+ o( t# h: b3 w+ L2 j
  33.                    0x12,                      #帧头2$ \& R; u: }5 S
  34.                    int(cx), # up sample by 4   #数据14 Q5 V: O: }1 F* ?* h
  35.                    int(cy), # up sample by 4    #数据2' M" F8 T) u1 {+ ]; H# ?
  36.                    int(cw), # up sample by 4    #数据1! ^4 @( Y; K( p: O
  37.                    int(ch), # up sample by 4    #数据2
    - n5 D2 I& h2 j% ]* }6 Q
  38.                    0x5B)
    , Q9 {1 I4 x; |- J5 d
  39.     uart.write(data);   #必须要传入一个字节数组/ T7 ^0 f& g8 O9 b1 y" A& }6 o
  40. ) x7 ]/ x. k3 Q, w

  41. 9 n5 g8 C% g' [! s! d. _
  42. while(True):9 t  y( i5 n' y# f9 W; y
  43.     clock.tick()) H1 |& e, G( @4 ^6 L
  44.     img = sensor.snapshot()5 |9 \5 n1 E/ w: ]: o8 `' d% [
  45.     blobs = img.find_blobs([red_threshold_01])! c& @+ d1 G1 F4 y% q+ ]1 X% e# B
  46.     cx=0;cy=0;) o' M. {1 m9 ~
  47.     if blobs:
    " A$ `5 M2 G$ G! p, X# S
  48.                 max_b = find_max(blobs)
    0 P) k+ Y2 ^1 F1 }3 I
  49.             #如果找到了目标颜色
    ' z0 J0 `' j5 N
  50.             cx=max_b[5]
    # P8 w- B. }. ]
  51.             cy=max_b[6]% ?$ a0 I; U* q, s
  52.             cw=max_b[2]
    2 T% V, e7 W! g* ~. C4 i+ D
  53.             ch=max_b[3]5 t: H2 m8 R: `3 J. p! b
  54.             img.draw_rectangle(max_b[0:4]) # rect; s3 ]8 N, U8 f* [3 |) W
  55.             img.draw_cross(max_b[5], max_b[6]) # cx, cy4 b0 ?2 ~' }" ~2 J& l8 w% }7 |2 n
  56.             FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])
    $ K$ ?3 ~% G- a) m$ P8 q1 j
  57.             #sending_data(cx,cy,cw,ch)
    . I2 e3 @: ~0 i' a9 ]+ z6 Q2 h
  58.             uart.write(FH)5 s7 g" `$ J7 H. u& N. b
  59.             print(cx,cy,cw,ch)
复制代码

0 o* `* R1 C) u, Q7 b9 M% ?4 ~$ N; sbytearray([, , ,])组合uart.write()的作用与直接调用sending_data(cx,cy,cw,ch)作用是一样的8 w3 F+ ~' s- [) I
% ]+ Z2 z+ p3 h4 [7 V

! y9 r6 p' {/ ^. \5 R4.软件代码———STM32端
4 a; `/ r) }, L( ~工程总共包含如下文件:main.c、iic.c、iic.h、oled.c、oled.h、uart.c、uart.h。由于OLED的代码存在版权问题,需要的可以邮箱私发。
! ^  j2 ?8 G8 e
# }' y& t+ I1 X) h9 h1 g/***** oled.h *****/
) Q. P  w3 ~* b" N4 J6 c, X" A. |2 z5 Q: Q3 M% n" u  w
  1. #ifndef __USART_H
    . }+ D) l, V  T4 Y0 F$ J
  2. #define __USART_H
      k/ t0 z3 X% D- t$ t4 V! K
  3. #include "sys.h"+ \. ]2 X) {; _0 |& I6 @7 C0 D; v: p
  4. void USART1_Init(void);//串口1初始化并启动) u) ^1 A% I" ~& y; M8 k8 g
  5. #endif
复制代码

/ S: [3 J7 T3 ?" w0 l* Y7 F) \' {' Z/***** oled.c *****/
( H+ [& l9 u! Y' ?" k' p: J. Q: [% R- z; U: W0 p  z1 Z2 Y" X
  1. #include "uart.h") t' a. X3 D8 ~; f" p
  2. #include "oled.h"# y# ~  o6 P5 e( `
  3. #include "stdio.h"
    ( |- W- ~/ H  s/ G3 j) i' A; I
  4. * b% _4 B2 _- o% Y0 k" k
  5. static u8 Cx=0,Cy=0,Cw=0,Ch=0;+ h: e; ?; S) u# k8 M3 U! c
  6. 0 v7 S' C1 N# l& D+ l( t4 I
  7. void USART1_Init(void)
    # K  I! [0 y  I$ O; v  q1 U
  8. {% j, Y$ G( _& }, Z
  9.                  //USART1_TX:PA 9   
    . s7 j) t* {, O9 ~, x6 h
  10.                 //USART1_RX:PA10$ t6 I/ m, m- M. s/ o
  11.                 GPIO_InitTypeDef GPIO_InitStructure;     //串口端口配置结构体变量. V, z( Y' i7 K% r8 b; R# g
  12.                 USART_InitTypeDef USART_InitStructure;   //串口参数配置结构体变量( @9 T& X& S8 u, q
  13.                 NVIC_InitTypeDef NVIC_InitStructure;     //串口中断配置结构体变量
    ; `7 m2 o/ a* p1 l+ B+ g( |
  14. 5 B) s1 P% P2 q- L% n
  15.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);        
    ( y) B& l% U$ G$ c- ^- d) F
  16.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //打开PA端口时钟
    9 A5 W8 L' i2 L! _$ s
  17. / y& h! \' J* o3 a- [2 C
  18.             //USART1_TX   PA9* i% N" D$ W9 K! d1 c( S) Z. @
  19.             GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                           //PA9( U' p9 G4 j5 {2 u9 |: F- v
  20.                    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                   //设定IO口的输出速度为50MHz
    * R( O1 q) s* x( i# D2 c# E5 I! f
  21.             GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                            //复用推挽输出
    + y$ _; F2 ~! e2 Z4 ^
  22.             GPIO_Init(GPIOA, &GPIO_InitStructure);                               //初始化PA9
    9 ]7 J: {* `0 X% ]+ n
  23.             //USART1_RX          PA10  Q% @/ a: Q$ D$ M+ ]/ ~
  24.             GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;             //PA107 F3 T$ x( S5 p
  25.             GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入% f: {! j. F- z# G3 }+ ]0 A) T
  26.             GPIO_Init(GPIOA, &GPIO_InitStructure);                 //初始化PA10
    # T* j, p# ~& y! j+ A# m, G* g. `7 ?  x

  27. 0 g7 O4 X8 G2 C) M# Z. V' R
  28.             //USART1 NVIC 配置
    . N. ^/ l) O, h" a! W* [! G
  29.             NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    2 J* b) {* J% O
  30.                 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;  //抢占优先级08 l- k" w: }1 A
  31.                 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                    //子优先级26 H; y# v$ S# n8 K
  32.                 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                                                       //IRQ通道使能
    & L- k( Q. V$ t0 }- w, k: J: ]/ |
  33.                 NVIC_Init(&NVIC_InitStructure);                                  //根据指定的参数初始化VIC寄存器0 J- F5 G. k( _2 D, e3 b2 A
  34. * v+ l" |3 |2 _. X. L
  35.             //USART 初始化设置" G0 g9 q' w; z: s8 y6 j! b- u
  36.                 USART_InitStructure.USART_BaudRate = 115200;                  //串口波特率为115200
    ( I, E" o% w5 R2 X: M
  37.                 USART_InitStructure.USART_WordLength = USART_WordLength_8b;   //字长为8位数据格式
    9 @2 B% g8 m. |7 U0 D
  38.                 USART_InitStructure.USART_StopBits = USART_StopBits_1;        //一个停止位2 [% ]) V* J0 |8 d9 A
  39.                 USART_InitStructure.USART_Parity = USART_Parity_No;           //无奇偶校验位
    , ^/ l0 f: v8 @. K; A8 P
  40.                 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;   //无硬件数据流控制
    ! R" h0 n, ?8 x8 B, @) c
  41.                 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                          //收发模式
    / a, L) |, r0 j4 r; j) ~
  42.             USART_Init(USART1, &USART_InitStructure);                     //初始化串口12 U/ P, ]( e3 Q9 L
  43. * E; i/ Z0 i# S: W& {- T" T
  44.             USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能中断8 x) H) ?5 J2 V0 f
  45.                    USART_Cmd(USART1, ENABLE);                     //使能串口1
      b  _5 G- l& I" _: i' n: z% \1 T
  46.                   USART_ClearFlag(USART1, USART_FLAG_TC);        //清串口1发送标志' P) w2 j6 ~6 J2 k2 l
  47.                
    - o( ~0 m5 O, ]4 F$ t+ o
  48. }
    : o$ e7 R. d* E& p4 {
  49. ' x# A1 G1 P1 Z  z, @% O
  50. //USART1 全局中断服务函数
    / Q( D1 U0 k+ c4 b! m4 K+ Z
  51. void USART1_IRQHandler(void)                         % S) M* g6 P+ W- y. p
  52. {
    * W4 o9 A) F# Y5 @
  53.                 u8 com_data;
    ' T: e2 m" i$ w
  54.                 u8 i;8 h" N" x0 Y2 p' f5 m, Z
  55.                 static u8 RxCounter1=0;  U7 r7 S4 H" f5 N+ H
  56.                 static u16 RxBuffer1[10]={0};* _. ]* M5 a" e! U
  57.                 static u8 RxState = 0;        : u7 v) d- ]" ?4 R2 R& s+ ?
  58.                 static u8 RxFlag1 = 0;3 c1 V! T- g) e( l$ f# g

  59. , \1 }% @7 p9 _; r/ W6 z" b5 A
  60.                 if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)             //接收中断  - G. p  Z5 k2 T( W- ^- a! k
  61.                 {
    % r2 }8 @" j* R0 x
  62.                                 USART_ClearITPendingBit(USART1,USART_IT_RXNE);   //清除中断标志* G) p8 O) V9 X3 V
  63.                                 com_data = USART_ReceiveData(USART1);7 ~! e) X0 S1 ]  A
  64.                         ' i" p/ `" H/ i9 T7 {9 J& F
  65.                                 if(RxState==0&&com_data==0x2C)  //0x2c帧头6 A  V  |! m# T* Q& w/ r: P. e. b" p
  66.                                 {
    # l- |" j2 T  N8 a5 n4 K# E
  67.                                         RxState=1;# A' ^- x/ g# z$ n% y
  68.                                         RxBuffer1[RxCounter1++]=com_data;OLED_Refresh();
    6 E& ]' Z2 ]" u7 R1 _
  69.                                 }
    8 T. J" l% Y" }" I; M8 {
  70.                 1 S, v, j. i' V
  71.                                 else if(RxState==1&&com_data==0x12)  //0x12帧头- N5 |$ p  d3 D2 O
  72.                                 {
      n, k) u3 m$ l, u1 }
  73.                                         RxState=2;' k7 z/ x1 k8 b/ m# ^
  74.                                         RxBuffer1[RxCounter1++]=com_data;
    / N4 d! l( \3 Y
  75.                                 }
    : @5 Q, N" x) w& Q' e( `
  76.                
    9 R, ~; C5 n- i* E9 ~
  77.                                 else if(RxState==2)) l2 t" z0 ^. U/ p4 t
  78.                                 {& {  {  Z  a* I  i: K
  79.                                         RxBuffer1[RxCounter1++]=com_data;
    $ p7 H1 f4 _6 f' \) `: X
  80. 5 `8 ~1 ]) P# F& ], U9 W
  81.                                         if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
    ; C1 n. d! s2 P! z
  82.                                         {
    + I$ T! }. c# J4 f; S% y; x
  83.                                                 RxState=3;  D' ^" y5 k7 x& D0 U
  84.                                                 RxFlag1=1;
    # t2 z% w* y% F9 u
  85.                                                 Cx=RxBuffer1[RxCounter1-5];: w; e& i% ~* t& T  \
  86.                                                 Cy=RxBuffer1[RxCounter1-4];! e1 j0 N. n' g
  87.                                                 Cw=RxBuffer1[RxCounter1-3];
    3 ?" \$ W* q/ K' r* \7 c
  88.                                                 Ch=RxBuffer1[RxCounter1-2];: C+ j$ W  M7 C: y
  89. - x7 F  X) f, g$ {0 A; z( a0 Y* b
  90.                                         }
    $ s, v9 R& B9 C0 d4 C
  91.                                 }, |* p* v& _" v
  92.                
    ' T- o: E) A* ?$ u: R0 d
  93.                                 else if(RxState==3)                //检测是否接受到结束标志! X6 {* _! ]+ E6 a& _" X
  94.                                 {
    : G% l* n& z* I! r" a! |
  95.                                                 if(RxBuffer1[RxCounter1-1] == 0x5B)+ S0 w# Z- @1 ?& ~- |/ R
  96.                                                 {! _1 C1 G7 H8 D
  97.                                                                         USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
    + W6 i- t1 Q7 |8 X5 ~0 H
  98.                                                                         if(RxFlag1)% I/ ?2 W$ T$ h
  99.                                                                         {
    6 j* a+ p& g. Q8 v
  100.                                                                         OLED_Refresh();9 u* P" X/ J9 h& _. V
  101.                                                                         OLED_ShowNum(0, 0,Cx,3,16,1);
    7 M- _; D& n& m/ v& H
  102.                                                                         OLED_ShowNum(0,17,Cy,3,16,1);
    ; e9 }0 y$ n7 ]2 I& V  ]" p
  103.                                                                         OLED_ShowNum(0,33,Cw,3,16,1);$ H  G! w1 Q2 @% a2 i2 }9 l
  104.                                                                         OLED_ShowNum(0,49,Ch,3,16,1);- z3 J( a9 f6 ?& B. f. f) R
  105.                                                                         }
    8 f1 I; Q5 Q7 D( ?/ j
  106.                                                                         RxFlag1 = 0;1 w( C" V. U% L
  107.                                                                         RxCounter1 = 0;
    1 C, g" ^$ T. {9 L
  108.                                                                         RxState = 0;! w8 U3 L5 `) f0 j
  109.                                                                         USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);( b4 m' d& m; y8 ]! N
  110.                                                 }
    9 u& }6 H" }6 `
  111.                                                 else   //接收错误
    9 x2 y  U* O# ?! `0 s' l  Z
  112.                                                 {
    7 F, m. h- T0 _4 G& N( N+ F' Y
  113.                                                                         RxState = 0;  q7 f8 z8 X( u+ c% t
  114.                                                                         RxCounter1=0;
    3 ^) q9 m* N5 E7 u
  115.                                                                         for(i=0;i<10;i++)& f8 }: T  \# v) C  C; U
  116.                                                                         {& ?1 |; R# C. P9 b! _
  117.                                                                                         RxBuffer1<i>=0x00;      //将存放数据数组清零6 L  D2 M. f- p' u
  118.                                                                         }
    9 c2 f/ A, ?  p" K7 w
  119.                                                 }
    4 ^8 T+ y' U8 \/ i4 [
  120.                                 } - d; Z. O8 K3 S" U
  121.         
    3 W7 `0 I; m( F3 r9 O8 |1 }, c
  122.                                 else   //接收异常, J& d8 q2 u+ p# ]0 C
  123.                                 {
    + {( k4 I5 K7 [2 p* J% ?: H8 O
  124.                                                 RxState = 0;1 O( a( X) R! B( E9 U6 @$ L
  125.                                                 RxCounter1=0;' Z1 W  l+ X% k8 J, A
  126.                                                 for(i=0;i<10;i++)7 |" s& {2 S1 t! B0 Z8 ^  g
  127.                                                 {
    # o3 v  z5 J: ^5 m* D" ~: h
  128.                                                                 RxBuffer1<i>=0x00;      //将存放数据数组清零+ o# a: Y5 Y) D; X
  129.                                                 }, p5 B/ I- r5 d" {9 s( L* p/ f8 L
  130.                                 }
    ! s% C6 f7 P6 k  `" B* J' T

  131. * b8 B- ]- Y: H! W8 N
  132.                 }
    8 q6 f& M+ V4 E5 s% j
  133.                
    % |- G+ t/ R' x
  134. }</i></i>
复制代码
( T8 g$ G% O1 c2 a! G, R- u
解释:OpenMV发送数据包给STM32,STM32利用中断接收数据并把数据存放在RxBuffer1这个数组里,并且在中断中利用OLED显示cx,cy,cw,ch四个坐标。在中断中,有如下函数:
! _7 x( N9 ]# H& K1 P6 r0 |. q5 Q4 R6 I9 k& C+ M5 o6 w( k
  1. else if(RxState==2)
    5 U4 k3 i" O! W1 V, H6 R5 ?; m
  2.                                 {
    & E) ~8 y4 T5 s, R* \8 n; q2 q
  3.                                         RxBuffer1[RxCounter1++]=com_data;
    ' P& U& Z+ d  f3 Z/ ?

  4. 0 O$ h" y& o0 c3 \. ~
  5.                                         if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束0 N& _. `: Y/ P! U
  6.                                         {
    $ ~2 C& M: r5 r5 N$ p5 b+ f7 n
  7.                                                 RxState=3;
    . L7 h; |/ a; r5 U, M, w- \
  8.                                                 RxFlag1=1;
    6 V- j9 o; c2 g; \
  9.                                                 Cx=RxBuffer1[RxCounter-5];
    % J0 D$ o' Z5 [' D
  10.                                                 Cy=RxBuffer1[RxCounter-4];9 b; ?+ w2 P# ~' a
  11.                                                 Cw=RxBuffer1[RxCounter-3];
    0 B5 z* A2 r- T& ]
  12.                                                 Ch=RxBuffer1[RxCounter1-2];
    $ ]! i6 C+ J& R

  13. ! p" i0 i& F9 ?, G# |
  14.                                         }
    ) J2 H( k/ [, v. g7 w1 a
  15.                                 }
复制代码

2 [. U  p2 L, M  BRxBuffer1是一个装有接收OpenMV数据的数组,RxCounter1起着一个计数器的作用,当RxBuffer[RxCounter1-1]存放的数据为数据包的帧位时,说明已经接收成功整个数据包,此时RxBuffer[RxCounter1-2]存放ch坐标值,RxBuffer[RxCounter1-3]存放cw坐标值,RxBuffer[RxCounter1-4]存放cy坐标值,RxBuffer[RxCounter1-5]存放cx坐标值,此后在RxState=3过程中将这四个坐标显示出来即可。  z5 g6 H7 Y9 i
特别注意的是:STM32中断每发生一次,只会接收到一字节的数据,因此,进行七次才会接收完一整帧的数据包,这一点需要读者仔细揣摩,结合上文中说的静态变量关键字static,定义了:
$ W% k+ p5 j  e) b2 h/ H) m6 K" w- P- k1 ]4 d, ~. d0 Y! Y
  1. u8 com_data; 8 t+ S" x$ Y# ]4 [6 ?: M
  2. u8 i;2 F- Y$ @6 n: r5 P& G! D
  3. static u8 RxCounter1=0;
    + g# I" E% @- O, ~* f# b8 ?
  4. static u8 RxBuffer1[10]={0};  y" F8 B" S1 v) L5 T1 X- V
  5. static u8 RxState = 0;        % _0 S9 w) f8 T3 \
  6. static u8 RxFlag1 = 0;
复制代码
9 [" G! t# Y2 ]. C( [4 H0 c
请读者仔细揣摩为什么com_data(端口接收到的数据)、i定义的是动态的(auto),而RxBuffer1(装接收到数据的静态全局数组)、RxState(状态标志变量)、RxFlag1(接受结束标志变量)定义的确实静态的,这一点并不难理解。
% f% U$ u, A; X, o) d# X& E/ W8 c3 \9 ]/ A5 L
5.利用PC端测试数据数据是否发送接收正常
7 p; E1 P& ~" `% P/ B7 X在进行OpenMV与STM32的通信测试过程中,我使用了USB转TTL模块,将OpenMV(或STM32单片机)与PC端进行通信确保数据发出或者接收正常。6 d( z) `; y7 m2 n/ ?+ e$ s
$ g3 q- a; ^/ T2 d  u
OpenMV&&C
, \+ v# m# x2 r" FOpenMV_RX接模块TX
3 {2 F2 D. K* _+ @# h; U3 z+ [4 E  _OpenMV_TX接模块RX7 g: I& s2 T0 V2 S, M1 {! N  {$ W
OpenMV_GND接模块GND
; y* |$ n  \2 y然后打开OpenMV,在大循环while(True)中使用语句:* q; W/ `8 s- U* G5 A9 x7 `
4 i; {9 ]! T& i
  1. DATA=bytearray[(1,2,3,4,5)]
    8 s+ I8 T3 n3 n7 Y2 g! m
  2. uart.write(DATA)
复制代码
# g4 R6 W9 z1 V/ w9 _
打开PC端串口助手,注意设置一样的波特率、停止位、发送字节数等,查看串口助手是否接受到了数据。
( G. Y$ a3 B- S, P9 y1 A' v3 R' q* F
/ D; v# Q2 T+ M% KSTM32&&C
+ `/ e9 U  O0 |' |* ?. S# K+ ZSTM32_RX接模块TX
$ o) P+ N& S, |! U- O" \1 z* gSTM32_TX接模块RX
# B4 b' v) p  LSTM32_GND接模块GND
# c1 j+ W8 I4 R* U8 H" B注意:不管是STM32与PC还是OpenMV与PC还是STM32与OpenMV通信,都要将二者的GND连接在一起。
( q1 F- y1 ?8 s3 C( x在main.c中先调用stdio头文件,大循环中使用如下语句:
1 W1 i% V4 Z% P% P$ M0 e
  1. while(1)3 a8 ?8 s* j, g& u1 r
  2. {
    # h0 e% `; v6 E% N
  3.                 printf("HelloWorld!");
    ; b4 q3 S1 a- _5 j
  4. }
复制代码
3 M) f) J2 n& r' I7 }& h% V+ P, N; c
打开串口助手查看是否接收到了数据。
6 w* a, c- D/ V2 m0 d
+ j3 \; r+ ?% k. T9 x9 b* U  P: ?
0 ?5 g. I9 C9 K6 t3 t6.学习补充 (代码看不懂的时候可以来看一下)
6 S6 `; t; z) j  U! a( p7 M补充1:static关键字(静态变量)的使用! @( u- l3 j; P& j- B8 P
$ S$ _! P1 R& G- h* _* }' V/ [
static 修饰全局函数和全局变量,只能在本源文件使用。举个例子,比如用以下语句static u8 RxBuffer[10] 定义了一个名为RxBuffer的静态数组,数组元素类型为unsigned char型。在包含Rxbuffer的源文件中,Rxbuffer相当于一个全局变量,任意地方修改RxBuffer的值,RxBuffer都会随之改变。而且包含RxBuffer的函数在多次运行后RxBuffer的值会一直保存(除非重新赋值)。在C语言学**,利用static关键字求阶乘是一个很好的例子:
! {) B9 X1 j* y/ k( [  F: C; u, m# N  I& S. L5 U; m! ^* S
  1. #include“stdio.h”
    7 C( T( P2 r* [
  2. long fun(int n);
    ! B& ~6 Z* }( s
  3. void main()
    % H3 o8 S6 h% w# ~* u6 }
  4. {
    1 H. j: k9 U" {- \) i
  5.     int i,n;
    # `+ n8 s  y8 w# Y
  6.     printf("input the value of n:");- p3 [2 V: Z. b8 W/ ]1 M5 [9 Z% Y8 o* Z
  7.     scanf("%d",&n);
    5 j" u% z0 F+ o! N
  8.     for(i=1;i<=n;i++)
    $ K6 N- m) U9 i: Y
  9.     {0 }7 n. ]2 R9 g, C! J1 A& G+ w' f
  10.         printf("%d! = %1d\n",i,fun(i));
    $ m; Q+ \7 S4 F7 O, e; s5 x
  11.     }( u, W+ C9 W/ z3 G7 F( g
  12. }
    0 J. U( t: q2 J$ E6 N7 o
  13. >long fun(int n)
    % i8 `/ y. p: E) u: X( d
  14. {" [: @9 v7 v: P6 z' S/ Z* o# T1 l
  15.     static long p=1;
    4 n" h' b. D& o) b- y2 w7 o' d" E/ C
  16.     p=p*n;
    ' U; J# o/ `' u' O' P4 ]: m( _
  17.     return p;  f! G1 u/ \( O  i, ?
  18. }
复制代码

8 O; i3 r' r$ y- x  E7 Z: A效果为依次输出n!(n=1,2,3…n)
; J, w' u1 Y& z8 _+ w$ m1 R这个例子中,第一次p的值为1,第二次p的值变成了p x n=1 x 2=2,这个值会一直保存,如果p没有定义为静态类型,那么在第一次运算过后p的值会重新被赋值为1,这就是auto型(不声明默认为auto型)与static型的最大区别。
9 D- e, T; b/ W: X5 K6 R. K% _( G+ ?$ O; J6 c
总结:static关键字定义的变量是全局变量,在static所包含的函数多次运行时,该变量不会被多次初始化,只会初始化一次。& C+ n  r0 e$ f& |3 F: r7 S0 X

9 j' v! n5 W5 x" k补充2:extern关键字(外部变量)的使用2 E! p; {9 I2 C; d& k' u8 B
程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,而在函数之外定义的变量则称为外部变量,外部变量也就是我们所讲的全局变量。它的存储方式为静态存储,其生存周期为整个程序的生存周期。全局变量可以为本文件中的其他函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。
0 `1 s- D+ B. `如果整个工程由多个源文件组成,在一个源文件中想引用另外一个源文件中已经定义的外部变量,同样只需在引用变量的文件中用 extern 关键字加以声明即可。下面就来看一个多文件的示例:
& E3 f4 E7 N/ s: Z2 W; U) K% {
8 Y: M7 e! G" L) B
  1. /****max.c****/( H7 o; @( ]2 t1 z8 B6 G! ?  J
  2. #include <stdio.h>
    ' t: e. H- x1 E5 ?5 u, P
  3. /*外部变量声明*/
    & t2 T+ s: I+ O/ X9 I1 r
  4. extern int g_X ;
    % l6 G; r6 d. ^$ p
  5. extern int g_Y ;
    ! t0 Q' Z. [" z. g/ p
  6. int max()5 Q3 J( d+ M  |
  7. {
    & ?9 o7 }4 l; z3 A  H
  8.     return (g_X > g_Y ? g_X : g_Y);
    / l7 B3 U  D# s6 X
  9. }7 n) D8 n$ D3 m
  10. /***main.c****/
    1 J  J6 O* [( f/ v/ @
  11. #include <stdio.h>
    , i' I; v5 \& j4 D/ e
  12. /*定义两个全局变量*/
    1 b" C" m4 }) _% J
  13. int g_X=10;+ B$ t8 B. E3 X
  14. int g_Y=20;( F$ S5 R6 f5 M% u
  15. int max();6 X6 c9 `, \1 E/ j8 x
  16. int main(void)3 J7 i9 y7 {& i5 Q2 j$ ^3 C/ [
  17. {: F- z) S% @) l* s' h& M
  18.     int result;, |1 U$ w3 A% S
  19.     result = max();
    6 Z# {$ r/ `& I" r3 ~' D
  20.     printf("the max value is %d\n",result);6 {7 `1 n. z% n) _  O) ^) b; _
  21.     return 0;9 u; d( b$ z3 |# X) E+ E
  22. }9 w3 ]+ q6 ]( m
  23. 运行结果为:
    $ y0 O9 k7 O$ d: Z# ]
  24. the max value is 20) O0 i4 N6 a8 U
复制代码
/ O. Y- @5 N# e& [  b$ |
对于多个文件的工程,都可以采用上面这种方法来操作。对于模块化的程序文件,可在其文件中预先留好外部变量的接口,也就是只采用 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外部变量。
$ c: Q, r) C8 m' U( ^! D* e, ?, P$ B7 B" L( ^5 L
总结:extern关键字是外部变量,静态类型的全局变量,可以在源文件中调用其他文件中的变量,在多文件工程中配合头文件使用。
% X( E3 K' x  d# N9 _
) q  ]. g' M  o+ c. ?补充3:MicroPython一些库函数的解释7 Y# p: l$ ~$ r, \' Y

2 Z) o# e4 R$ G* y' _; g1 u1.ustruct.pack函数:
& Y$ t5 X  U& }5 N8 N, I/ wimport ustruct,在ustruct中* Q) e3 C, \3 G2 y1 P
  1. data = ustruct.pack("<bbhhhhb",      #格式为俩个字符俩个短整型(2字节)6 X$ P/ @' r% b0 T
  2.                    0x2C,                      #帧头1
    - k5 f% Y# h) v8 w2 N
  3.                    0x12,                      #帧头2
    - T" t, J1 j# }' Y, \( c) X! S
  4.                    int(cx), # up sample by 4   #数据1
    9 _: |; A( S# r6 O- [6 \
  5.                    int(cy), # up sample by 4    #数据2/ ^  u  \5 F5 {2 ~4 R
  6.                    int(cw), # up sample by 4    #数据11 B3 v- {8 B% N; o# k6 H& Z
  7.                    int(ch), # up sample by 4    #数据2; D6 L2 p9 A! q: ~' }
  8.                    0x5B)
复制代码

. m, g7 w3 F' W1 H4 ]""bbhhhhb"简单来说就是要发送数据的声明,bbhhhhb共七个,代表发送七个数据,对照下面的表,可以知道七个数据按时序发送为unsigner char、unsigned char、short、short、short、short、unsigned char。0x2c为数据帧的帧头,即检测到数据流的开始,但是一个帧头可能会出现偶然性,因此设置两个帧头0x2c与0x12以便在中断中检测是否检测到了帧头以便存放有用数据。0x5b为帧尾,即数据帧结束的标志。+ G% \# H+ g5 s: a- v6 w2 a+ L

% H+ X' N' z  Z6 D, P3 l) z3 [% d: i 20210504040156260.png
1 p3 ]/ I  a2 v# g' R) I& _: A0 i0 M9 @6 a8 s5 c# l
2.bytearray([ , , , ])函数:
5 ?. v* s/ U5 C! ~用于把十六进制数据以字节形式存放到字节数组中,以便以数据帧的形式发送出去进行通信。3 c  E9 _3 i. _1 E; F
4 A9 c6 Z2 n5 o( i; Z
  1. FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])" S; G6 U; `3 r# M# @0 C
  2. uart,write(FH)
复制代码

( p" ]9 O5 L) }0 ~7.效果展示(可以先来看效果)
! S' ^' o; I5 d6 Q. @" z
+ f# [8 v0 v; T  z+ V 20210504041702364.png
2 \5 _) w: ^9 n2 Q$ D% n从上到下依次为CX,CY,CW,CH0 A5 s( c8 R0 [1 a% y1 \8 X
3 u% V5 d* A- A4 J& c& D( S. B: J
8.博客更新
- A1 R' g- M' ?0 D$ T) l2 h1.有朋友反馈OpenMv端找不到色块就会报错,解决方案如下:
9 P9 V# A1 u, J% ]. i
4 g8 ^* {9 N  r; F
  1. while(True):; U* k) J' E! b; o: h; [
  2.     clock.tick()
    , E6 U* E6 _9 L, l, w7 V+ e
  3.     img = sensor.snapshot()
    ( }5 D) g. G7 o, b
  4.     blobs = img.find_blobs([red_threshold_01]); y  O! D( f- S9 O# i4 d. r
  5.     cx=0;cy=0;' y9 I7 O5 k6 z; u! K
  6.     if blobs:+ z8 B+ I1 K6 ^9 E! A8 N
  7.                 max_b = find_max(blobs)* n4 r9 J3 U5 |- i  g
  8.             #如果找到了目标颜色7 n$ ]! a! t4 r  N' C) P: t
  9.             cx=max_b[5]
    + p1 B+ z5 X- r( y2 F
  10.             cy=max_b[6]& i$ ?7 A( H6 X* i: O9 H, g
  11.             cw=max_b[2]
    6 N, d3 l1 p" c# R8 n
  12.             ch=max_b[3]' q$ Y) t1 ?' X8 R$ H, }5 u- Q
  13.             img.draw_rectangle(max_b[0:4]) # rect/ ?' @. p8 S% T" }
  14.             img.draw_cross(max_b[5], max_b[6]) # cx, cy( ]7 C% G' @0 t" |
  15.             FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B]). m! h3 s9 k# k% v/ b
  16.             #sending_data(cx,cy,cw,ch)' i( \# g# D: s/ x0 _
  17.             uart.write(FH)
    / c" F6 n1 ?& V  B; M; P0 q) ~5 ]
  18.             print(cx,cy,cw,ch)
复制代码
* B9 a* Z: h  S; T' u7 G
在以上代码中,将max_b = find_max(blobs) 移到if blobs外即可。
- @4 N% K0 Z9 v
; j0 e& Q5 Z; i2 O2 y2.有朋友反馈OpenMV发送数据只能发送一个字节,也就是说大于255的数据无法直接通过代码完成,现在提供以下解决方案:在STM32端代码中依次保存大于255数字的高八位和低八位最后在组合在一起即可。
1 c0 O' k) v2 e, @* k' l6 J! y) Y1 j7 R6 K# L/ I& E& Z' Y% K
  1. #if 1
    ' B# w9 H2 J6 a' x+ _5 Z4 S. ]3 ^
  2. int main()
    % _: O! W  V$ |+ a
  3. {
    % u4 |( S$ M0 {, j2 o; e
  4.         #if 0
    7 v4 i/ f2 I* M. _
  5.         //字符型数据分成四个字节存放在数组中# q% Z6 D7 a: i! W7 f/ Q8 @" ]
  6.         float m = 23.25;
    ) {, _7 B7 \2 ^# H) \
  7.         unsigned char *a;7 O% [! Z/ R: g
  8.         a = (unsigned char *)&m;* j3 B5 d% Y; i9 q! c2 l" F8 V' j
  9.         printf("0x%x \n0x%x \n0x%x \n0x%x \n",a[0],a[1],a[2],a[3]);1 Z/ Y% L6 B7 ~# ]* Y& X
  10.         ' d2 p! M% q7 |2 S! `& o
  11.         #endif
    2 U" b' I9 e1 I  n; y
  12.         6 j5 t8 g$ U3 Q7 N' K: d) x( h
  13.         #if 1& t+ w: \+ n$ }/ ]+ `
  14.         //四个字节数据合成存放在数组中5 F/ z2 _/ L: ^: T, _) i& o
  15.         unsigned char a[]={0x00,0x00,0xba,0x41};
    + f- _! z: J* g/ L" G5 U
  16.         float BYTE;
    ) J8 A) o; I9 R1 l1 U' d
  17.         BYTE = *(float *)&a;
    1 K! V, x+ [  @. c
  18.         printf("%f\n",BYTE);0 |( h5 `" `4 @, z1 b! t" a
  19.         #endif
    ( L$ y, n1 l: H$ s+ t: w0 v/ N
  20. }
    " O  `' J+ F# b7 f
  21. #endif
复制代码
: r3 j' ]: K+ I1 X- A% |/ b" o# J9 y& ]5 R
上述代码实现了将四个字节转换为一个浮点数的功能,同时也实现了将一个浮点数拆分为四个字节功能。在Openmv传数据时,只能传输一个字节,大于255的数无法以一字节形式发送,因此可以在Openmv端将该数据拆分成两个字节,分别发送给Stm32端,同时Stm32端对传来的数据进行合成,合成并解析为对应的数据。' l- c( a& w& E$ O) \8 |" _2 w$ H
另一种解决方案:python传数据的1/2,单片机在乘2即可。
" F5 A& ~, v0 E* }" N0 {) \& }
! M: y/ I6 X3 d. I————————————————
0 |' ~" u! C' n版权声明:Wu__La
3 {% N0 C  ]! B9 x$ ?; k
& k! V7 N& A* c. K+ C6 V6 O  M, @# l# J6 f# F5 s( s

7 X( A- P2 L, R% D
% O# ]$ ~2 d* R) K9 B
赞 收藏 评论0 发布时间:2022-11-4 16:46

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版