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

超详细OpenMV与STM32单片机通信

[复制链接]
攻城狮Melo 发布时间:2022-11-4 16:46
1.前言
7 I' [! H# P& A; q最近在做电磁炮,发现题目需要用到颜色跟踪,于是花了一点时间学了一下OpenMV,只学习OpenMV是远远不够的,还需要实现与单片机的通信,本以为很简单,在CSDN上找了一些代码,直接拿来修改粘贴,把代码看明白了,这些只花了几个小时,本以为自己已经弄明白了二者之间的通信,但是在后期把OpenMV端数据传输到单片机的时候却犯了难。我选择使用OLED显示传输的数据,在这里调试了许久,中间遇到了许多之前的学习漏洞,特在此写下博客记录学习经历。1 {. \3 I7 F. F0 V" ~: i& w$ \

, k" E- A! \% @/ |2 H  y- z2.硬件连接. u3 a8 M, l6 r
我所用到的材料如下: 四针IIC OLED,OpenMV(OV7725),STM32F103C8T6最小系统板,数据线N条(OpenMV的数据线只能用官方自带的,其他的基本都用不了),杜邦线若干。; K' M6 i8 s, d2 x

" F& G9 |7 f6 a% O3 n1.OpenMV端:由图知UART_RX—P5 ------ UART_TX—P4' g; x6 }$ H; x. ]- r) S, p0 \
20210504025305791.png
" H/ G; t/ K! H8 d3 X
; }) n8 ?: h3 X+ c  J% k8 ^2.STM32端:USART_TX—PA9 -----USART_RX—PA10; ^" m0 d9 }2 f# W" a

; V0 u" P4 e# H  z' ^ 20210504025557402.png ( w  r3 m' v/ d8 w( j+ P2 z+ b
. r$ A5 S; |# q2 H" k; L& E
3.四针OLED IIC连接:SDA—PA2-----SCL—PA1 由于使用的是模拟IIC而不是硬件IIC,可以根据个人需要修改IO口来控制SDA线和SCL线,只需要简单修改一下代码即可。1 g$ b2 K9 i1 Y4 Q
4.STM32的TX(RX)接OpenMV的RX(TX),OLED连接到STM32即可。
) r( F; P: Z" q; s3 i. b3 O: p7 P+ G0 e6 O

: @* ^' Q3 w" b" x8 t3.软件代码———OpenMV端: T  N- ?9 V( N. R- e
  1. import sensor, image, time,math,pyb
    ' k, T" B8 R6 i
  2. from pyb import UART,LED3 |4 o& d* f: a5 @! W  U+ _
  3. import json
    % b& }+ \% Q7 c4 o
  4. import ustruct
    / H' q+ X) X9 i' i* Q/ r
  5. ' _/ k! t( ~# `5 v3 V% Y1 F* D
  6. sensor.reset()6 U' i: O5 c8 G, R$ O) \
  7. sensor.set_pixformat(sensor.RGB565)
    6 x1 x, |0 `4 O2 X+ I5 R; {
  8. sensor.set_framesize(sensor.QVGA)% {+ H/ Q# f0 a5 ]
  9. sensor.skip_frames(time = 2000)
    6 j1 A. z- z3 t) v- V2 ?
  10. sensor.set_auto_gain(False) # must be turned off for color tracking( A! }. j" H3 ]
  11. sensor.set_auto_whitebal(False) # must be turned off for color tracking
    1 v; N7 Y% l6 I8 X, X/ m& r
  12. red_threshold_01=(10, 100, 127, 32, -43, 67)
    - ?1 e! s- ^5 S+ r3 F* o% f9 ?
  13. clock = time.clock()
    / V8 d, A* Q) j0 A7 {, F/ \
  14. 7 D) J# c  |( O$ @! i  C- w
  15. uart = UART(3,115200)   #定义串口3变量3 @, r" ~# c1 I2 ~& [" `
  16. uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters# g- [) a+ s9 _3 f. _

  17.   o  J; B/ Z* K4 q& S7 ^1 O+ \
  18. def find_max(blobs):    #定义寻找色块面积最大的函数7 }$ \2 S" c4 X
  19.     max_size=0; L# k6 X1 e4 C/ p0 \
  20.     for blob in blobs:
    + h  a) Q9 \( x
  21.         if blob.pixels() > max_size:
    + N4 X& `% Z# }9 c" ~: C! o2 S- }% e
  22.             max_blob=blob
    % j/ T8 b0 D2 a: b# V# j
  23.             max_size = blob.pixels()( [4 v( J/ Y# T3 G6 z6 m9 X' R
  24.     return max_blob; U- \0 D8 B' j" K- A6 r3 h

  25. ! R* d# ?; S; l
  26. + Y8 o1 t/ N0 y; c2 r3 e' T
  27. def sending_data(cx,cy,cw,ch):) W  ]8 i) P9 e9 }* N, _: _
  28.     global uart;' }) [  O, j! J* J) f$ r: N/ T
  29.     #frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
    # I  b9 s0 T0 V! v/ V
  30.     #data = bytearray(frame)
    & D' j$ N1 M( e* [
  31.     data = ustruct.pack("<bbhhhhb",      #格式为俩个字符俩个短整型(2字节)* n& G: b7 p6 M/ f& c; J- U
  32.                    0x2C,                      #帧头1
    8 H; v# V4 o2 b% C% K2 I
  33.                    0x12,                      #帧头2. s. T3 [' p9 ?% b
  34.                    int(cx), # up sample by 4   #数据1
    ! B- |  b' J% N2 i( i2 F, [  Y
  35.                    int(cy), # up sample by 4    #数据2) q* w& l$ m1 N* t5 N3 C
  36.                    int(cw), # up sample by 4    #数据11 H7 ^3 }0 C8 j3 E
  37.                    int(ch), # up sample by 4    #数据2& Q: A/ l  \0 I5 M% O) b
  38.                    0x5B)
    " `5 w. B# s3 F8 t
  39.     uart.write(data);   #必须要传入一个字节数组
    1 O0 Q: h3 {: M9 Z; a/ M! [; \

  40. & W/ V* g2 S7 A
  41. 1 R4 E9 ]# }" z$ Q
  42. while(True):" Z1 N% T% U: S2 ^- W1 Y
  43.     clock.tick()
    . d+ F& w- u% G; @$ S& A) I
  44.     img = sensor.snapshot()
      Q: y) c. H( L( p. S( g) M
  45.     blobs = img.find_blobs([red_threshold_01])
    * @  [& O: {, Y0 B+ K  S& w2 C
  46.     cx=0;cy=0;
    - c  l; m' N3 Q) L3 T. m
  47.     if blobs:
    $ ^6 U( @1 N" y# j0 F" e
  48.                 max_b = find_max(blobs)7 o: P. h9 s" }/ T4 D' g) j
  49.             #如果找到了目标颜色  @; c! ^' c% u& ^4 M
  50.             cx=max_b[5]8 u) R' l. Q. ?7 P( b, F
  51.             cy=max_b[6]
    6 d2 ?% D' a8 [0 j4 S5 d
  52.             cw=max_b[2]) c; A& E5 S9 r% U% k* `
  53.             ch=max_b[3]
    , K5 y6 x' }3 [
  54.             img.draw_rectangle(max_b[0:4]) # rect. G! L$ \( ]- d9 O8 o! C
  55.             img.draw_cross(max_b[5], max_b[6]) # cx, cy
    1 u" G7 K5 q& i+ l
  56.             FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])) E( a, W- ?5 P5 Q9 D
  57.             #sending_data(cx,cy,cw,ch)
    5 D  w& W/ ~$ e1 Q
  58.             uart.write(FH)
    ( j2 g- d# m5 T" R3 h
  59.             print(cx,cy,cw,ch)
复制代码

' @2 l! A0 O8 H" e% g; Nbytearray([, , ,])组合uart.write()的作用与直接调用sending_data(cx,cy,cw,ch)作用是一样的% i' @6 S! n+ B  n; K) e+ c

3 ^1 x( U% c9 e" d- Q" ]# y  `8 e5 Q
4.软件代码———STM32端$ k1 P; w1 N4 W* a. e0 C
工程总共包含如下文件:main.c、iic.c、iic.h、oled.c、oled.h、uart.c、uart.h。由于OLED的代码存在版权问题,需要的可以邮箱私发。. S6 a1 [+ z5 Q5 v

, M# [/ `/ U1 @: c4 X/***** oled.h *****/
5 U) r5 X3 D) p2 I9 b* H+ U$ Y, ?! ~& |, Z
  1. #ifndef __USART_H9 R0 g$ h& b$ N
  2. #define __USART_H. R" ]- y1 h+ _0 T
  3. #include "sys.h"7 J; I) l7 G, W$ s) r
  4. void USART1_Init(void);//串口1初始化并启动
    % T+ S7 C0 c1 m6 b) |
  5. #endif
复制代码

3 b; w! a6 s9 }( J$ S/***** oled.c *****/( F+ `1 @  O- P- X

) A2 z# _* j/ k/ R( a
  1. #include "uart.h"
    . f; _0 u, d" A) e7 E
  2. #include "oled.h", s3 M* P4 Y# u, i& H
  3. #include "stdio.h"0 N6 x( k: _- v% A& r. U6 W" O
  4. 9 h; `8 m% T: t8 C4 X+ s% e
  5. static u8 Cx=0,Cy=0,Cw=0,Ch=0;! n$ H+ _7 m1 Y5 H

  6. ) c& O8 U) a' E
  7. void USART1_Init(void)
    6 g5 @* s, K( r
  8. {; x  B& ?1 V6 o1 P% G
  9.                  //USART1_TX:PA 9   0 s, o" G6 E# g$ ^3 J
  10.                 //USART1_RX:PA108 V6 y: }; T( h0 }9 R" {' B
  11.                 GPIO_InitTypeDef GPIO_InitStructure;     //串口端口配置结构体变量* D& `# e2 V' J4 Z
  12.                 USART_InitTypeDef USART_InitStructure;   //串口参数配置结构体变量2 `6 s7 T" L9 J4 ~- N
  13.                 NVIC_InitTypeDef NVIC_InitStructure;     //串口中断配置结构体变量
    : ~2 s8 J. U+ F
  14. ! L+ v. T7 ]: w3 T; g7 F) N+ Z
  15.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);        
    - G* {7 _0 h0 V% w
  16.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //打开PA端口时钟/ x, O$ _8 L) o4 ?( B9 g* U

  17. 0 E+ J2 g+ Y& n; r4 R; b  j' H
  18.             //USART1_TX   PA95 a" O' m( r: Z6 Z$ S; o
  19.             GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                           //PA9) [) F  D( @& j, [& o( x
  20.                    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                   //设定IO口的输出速度为50MHz
    7 W0 j! t/ F! v3 b
  21.             GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                            //复用推挽输出' Z# {' v. N+ m% ^  ]
  22.             GPIO_Init(GPIOA, &GPIO_InitStructure);                               //初始化PA9! ?6 s: l, n" b/ o4 C! c
  23.             //USART1_RX          PA10
    # u5 O7 ~3 D9 X! m% c; E0 ~  F
  24.             GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;             //PA10
    ' N; q0 I% @4 G9 Z. `
  25.             GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入3 C3 I" ?% b$ U( O5 X
  26.             GPIO_Init(GPIOA, &GPIO_InitStructure);                 //初始化PA10
    2 C9 N, r7 p; f) ^
  27. * o& b+ C( c) H3 L5 y
  28.             //USART1 NVIC 配置
    3 l& P9 c9 @2 G% A- S
  29.             NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;2 ]! D& j. M/ l  P; h: R
  30.                 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;  //抢占优先级0' o* ?0 n' G7 ]. _+ ^. u
  31.                 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                    //子优先级2
    , V( Q7 y3 q; c3 z, y- k
  32.                 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                                                       //IRQ通道使能
    $ a3 B- V5 Y- @! ^# M# R5 C* ~% X
  33.                 NVIC_Init(&NVIC_InitStructure);                                  //根据指定的参数初始化VIC寄存器
    ; J. R$ G# g& P2 c: E. B9 j) f
  34. " q# x# r+ N# J0 Z. u" v4 g' R
  35.             //USART 初始化设置) W& z, P, \2 e2 M2 |1 f3 |
  36.                 USART_InitStructure.USART_BaudRate = 115200;                  //串口波特率为1152008 `: {9 J/ K4 f4 n7 p" L( t5 [: x
  37.                 USART_InitStructure.USART_WordLength = USART_WordLength_8b;   //字长为8位数据格式/ r) [, g* C/ W$ h& ~6 q- g" ^
  38.                 USART_InitStructure.USART_StopBits = USART_StopBits_1;        //一个停止位
    + x& u( [8 T$ u' S
  39.                 USART_InitStructure.USART_Parity = USART_Parity_No;           //无奇偶校验位/ q2 g+ A6 c7 b+ F% b
  40.                 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;   //无硬件数据流控制
    * z, ~: Y* i& ]7 a  Z- a6 j- G
  41.                 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                          //收发模式: X' X& n9 _/ N- [, R
  42.             USART_Init(USART1, &USART_InitStructure);                     //初始化串口17 Q# |4 T7 E5 x: K+ k% l

  43. + v: M5 ?5 n9 Q! q7 i2 v! @2 Q& a& ?
  44.             USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能中断0 I6 }! s3 Y4 h. Y( z; \
  45.                    USART_Cmd(USART1, ENABLE);                     //使能串口1
    # q" F/ n! X* v6 n2 x3 Z4 f% x" Z
  46.                   USART_ClearFlag(USART1, USART_FLAG_TC);        //清串口1发送标志; b  D( N, V' J" Y2 v' M) P
  47.                 * b' z$ t1 j4 S0 R
  48. }1 Y: q4 p# R6 Y0 a3 c) y
  49. % Y0 _! t" k( O$ k
  50. //USART1 全局中断服务函数" r$ P* V( E( w8 P: N
  51. void USART1_IRQHandler(void)                        
    & L0 O2 Q$ t( [+ x2 K" [% n
  52. {
    " v5 L6 U* m9 Q5 M: O3 H
  53.                 u8 com_data; 8 @% X# E6 o9 g
  54.                 u8 i;$ D" T. w3 x4 m4 f
  55.                 static u8 RxCounter1=0;
    - c4 f( h  ]. d2 x# }( d( M& Z) ~
  56.                 static u16 RxBuffer1[10]={0};
    9 y8 j4 L$ n' e
  57.                 static u8 RxState = 0;        3 r' d9 O% T& f
  58.                 static u8 RxFlag1 = 0;7 ~* n  ~& l. ?7 I) ]1 q

  59. - D4 k! B* Y3 t% d" i5 d: g
  60.                 if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)             //接收中断  
    / q- c- M: s6 A4 Q6 @8 e
  61.                 {
    0 P7 }3 Y8 Z! i: h  C+ ?. u
  62.                                 USART_ClearITPendingBit(USART1,USART_IT_RXNE);   //清除中断标志- t+ C# r7 J" }  z
  63.                                 com_data = USART_ReceiveData(USART1);
    / a9 j. B$ V/ s, q
  64.                         
    8 N7 y) o  a  K5 J, h6 O7 N
  65.                                 if(RxState==0&&com_data==0x2C)  //0x2c帧头0 p& D1 K' [1 G9 x
  66.                                 {. T& M% o- L( X' J* o* ]
  67.                                         RxState=1;
    4 @0 R% q' S7 P/ ?
  68.                                         RxBuffer1[RxCounter1++]=com_data;OLED_Refresh();8 y; }( ?6 D5 _) P9 s: t- @
  69.                                 }! i& T) Y0 U: q+ z& ]) U2 \
  70.                 ! ]  [0 l0 e) _5 G. q  X! A
  71.                                 else if(RxState==1&&com_data==0x12)  //0x12帧头' a- P5 _0 ?  m% @4 O
  72.                                 {" e* b% z0 P* ^% d
  73.                                         RxState=2;
    ' q) ]" |# n$ b' [
  74.                                         RxBuffer1[RxCounter1++]=com_data;
    , z# k& B3 b, v$ H$ @
  75.                                 }
    : f" o8 T- E# n7 A/ d) K
  76.                
    ! g$ A9 r. _. L  L) N9 ~
  77.                                 else if(RxState==2)
    6 A9 \6 ?/ \9 y! E$ U  j( `% J0 |
  78.                                 {
    ( w: V+ T3 l* g3 d- `
  79.                                         RxBuffer1[RxCounter1++]=com_data;# n( Q+ D4 ]8 N3 \, r9 Z- z

  80. . v' b& z% W5 `- [
  81.                                         if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
    ; W* }0 s# T, D: F0 @
  82.                                         {# N8 y/ A9 O6 i) K/ V0 k
  83.                                                 RxState=3;! m) p  Q+ k' X. Z
  84.                                                 RxFlag1=1;. z' z0 s3 x, u( X6 C
  85.                                                 Cx=RxBuffer1[RxCounter1-5];* {) B+ G3 v& n6 a" K
  86.                                                 Cy=RxBuffer1[RxCounter1-4];
    ) k! D5 `1 }. @6 i1 H
  87.                                                 Cw=RxBuffer1[RxCounter1-3];3 R6 N$ p" s- U# t- T1 ]0 i
  88.                                                 Ch=RxBuffer1[RxCounter1-2];
    9 I# Y" c" s6 D5 Z
  89. 2 a" b4 R# [3 c/ _) ?$ O
  90.                                         }' T5 W' `8 q1 U( {' L
  91.                                 }
    , f$ D8 |$ F$ C( b% R0 Q- Z, d
  92.                 0 O9 n7 \; p4 j# z7 b; [; t* C$ c
  93.                                 else if(RxState==3)                //检测是否接受到结束标志( ?8 J, x6 ~. h5 @
  94.                                 {
    6 n/ M% |. ~9 A6 J* C/ g. V
  95.                                                 if(RxBuffer1[RxCounter1-1] == 0x5B)
    & H6 _! W9 Q1 Z+ ^# m' G/ T5 I
  96.                                                 {
    1 e6 j% C* y& V. H4 p* A
  97.                                                                         USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断2 s, w0 x% D8 l9 P1 U
  98.                                                                         if(RxFlag1). ]* h/ |7 L+ A( J7 g
  99.                                                                         {
    , i) {: f; V- K$ w
  100.                                                                         OLED_Refresh();
    2 I) i8 @6 l. a4 E! V- R/ W3 G/ a
  101.                                                                         OLED_ShowNum(0, 0,Cx,3,16,1);
    / X9 W/ ^6 h! v8 s6 d
  102.                                                                         OLED_ShowNum(0,17,Cy,3,16,1);
    3 o7 J* n' v6 q3 ]; l
  103.                                                                         OLED_ShowNum(0,33,Cw,3,16,1);0 S9 R& ^$ a: h
  104.                                                                         OLED_ShowNum(0,49,Ch,3,16,1);5 Y( |4 F7 F+ u
  105.                                                                         }
    5 z) e% a  x0 b& b6 H
  106.                                                                         RxFlag1 = 0;
    ) B' [. k  U6 i  d) D- `) b! N; d
  107.                                                                         RxCounter1 = 0;
    ! u! O* H1 z9 r2 H6 m
  108.                                                                         RxState = 0;$ J, A7 H2 z8 l( q
  109.                                                                         USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
    * M) f! _: W' D/ x. @
  110.                                                 }
    2 r" e# n1 m$ v+ m) H: h# p- h
  111.                                                 else   //接收错误
    5 a5 P( \, m0 l5 e
  112.                                                 {
    # d5 g$ t, A1 ^0 M- P" m
  113.                                                                         RxState = 0;
    . s! h# S$ \6 \; a- q6 o$ o
  114.                                                                         RxCounter1=0;4 k  @( ]. @1 b+ h! ]$ |
  115.                                                                         for(i=0;i<10;i++)0 o8 M$ b8 ?, P' T
  116.                                                                         {- `) d( U9 A1 u) X6 p
  117.                                                                                         RxBuffer1<i>=0x00;      //将存放数据数组清零
    8 d( W: ^0 {# m7 y1 Y& p
  118.                                                                         }+ l0 F. G* c9 p1 ]! A  G% K# H
  119.                                                 }
    * N2 D+ r; r2 d& Y' r2 ^  r4 Q
  120.                                 }
    7 Z! L! j+ ?* P: i2 M
  121.         
    3 ~* Q% u* n, i' A
  122.                                 else   //接收异常1 x& z: v$ M& q/ p# V% Q2 F3 u
  123.                                 {% t- l3 F6 Q' ?4 ], H
  124.                                                 RxState = 0;: N) o# o9 A8 ]0 [& |) Q. B. B
  125.                                                 RxCounter1=0;+ F# F! Y( f- I) A( G
  126.                                                 for(i=0;i<10;i++)
    7 X/ [- C& W; Z5 L3 ]" H6 \2 q: y8 K) c
  127.                                                 {9 k8 x6 Q- T/ X2 C7 ?
  128.                                                                 RxBuffer1<i>=0x00;      //将存放数据数组清零
    ; k3 `; z: q* s' U3 u3 c4 B& y
  129.                                                 }; y7 k7 N9 f2 s+ n
  130.                                 }- I  V/ f( X6 f% N
  131. : o+ E! j: q5 Q5 [3 M. k4 T
  132.                 }
    1 y' m2 F) ~& M2 N3 R
  133.                
    8 D2 b: z% s! c/ r! Y, s
  134. }</i></i>
复制代码
& {  e% O. {/ j& v# x3 z$ O& Y; B
解释:OpenMV发送数据包给STM32,STM32利用中断接收数据并把数据存放在RxBuffer1这个数组里,并且在中断中利用OLED显示cx,cy,cw,ch四个坐标。在中断中,有如下函数:) Z+ p% N. ?1 u6 w
, m; t  A( c$ r$ Z+ J  G8 Y
  1. else if(RxState==2)
    * b7 F4 h& g4 V+ {& }0 \$ ]
  2.                                 {3 ~, }7 f6 l0 Y4 j  C
  3.                                         RxBuffer1[RxCounter1++]=com_data;
    ) D. K7 _8 t& o" X! }/ j
  4. 3 e" |/ N4 H& ^8 J
  5.                                         if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
    , C. ]  s5 \# d
  6.                                         {
    8 M7 f6 \1 F9 ~8 a0 j6 x3 z
  7.                                                 RxState=3;( y5 f4 ?' y: r4 R. P, z9 j
  8.                                                 RxFlag1=1;5 z" E: {% @* R% G+ E
  9.                                                 Cx=RxBuffer1[RxCounter-5];
    7 u, G( I. [2 F; |
  10.                                                 Cy=RxBuffer1[RxCounter-4];5 H+ z1 [. D3 e" K
  11.                                                 Cw=RxBuffer1[RxCounter-3];9 D% @- C6 s7 f* l4 u) m- H
  12.                                                 Ch=RxBuffer1[RxCounter1-2];3 e; D+ ?8 d- j$ h  [, R
  13. 1 m  R- u- f, ]
  14.                                         }; t  }0 z+ Z( @# V- g
  15.                                 }
复制代码

" _% D( i% v) o, R7 d) F3 r0 XRxBuffer1是一个装有接收OpenMV数据的数组,RxCounter1起着一个计数器的作用,当RxBuffer[RxCounter1-1]存放的数据为数据包的帧位时,说明已经接收成功整个数据包,此时RxBuffer[RxCounter1-2]存放ch坐标值,RxBuffer[RxCounter1-3]存放cw坐标值,RxBuffer[RxCounter1-4]存放cy坐标值,RxBuffer[RxCounter1-5]存放cx坐标值,此后在RxState=3过程中将这四个坐标显示出来即可。
2 o; i* n9 k0 s! w特别注意的是:STM32中断每发生一次,只会接收到一字节的数据,因此,进行七次才会接收完一整帧的数据包,这一点需要读者仔细揣摩,结合上文中说的静态变量关键字static,定义了:+ o  ?+ w/ Q) T; ]
9 Z; |7 ^5 N0 }5 R& i
  1. u8 com_data; $ K' x! u; i/ K) N
  2. u8 i;
    ( r* J- P# l, {9 F6 t
  3. static u8 RxCounter1=0;
    0 F; F  b7 Z0 E2 @6 i, U7 |
  4. static u8 RxBuffer1[10]={0};$ a9 g  j$ i; |: m) k. @- {1 D* v
  5. static u8 RxState = 0;        
    + n/ T/ Q% F' t1 C+ w5 v
  6. static u8 RxFlag1 = 0;
复制代码

6 G% T# M" G! J, h7 B' [2 }请读者仔细揣摩为什么com_data(端口接收到的数据)、i定义的是动态的(auto),而RxBuffer1(装接收到数据的静态全局数组)、RxState(状态标志变量)、RxFlag1(接受结束标志变量)定义的确实静态的,这一点并不难理解。
: ^  M7 v7 n% m, p4 a7 v4 k9 S) O0 ?/ t7 D6 b, q
5.利用PC端测试数据数据是否发送接收正常# Q, S7 F* @; R& s
在进行OpenMV与STM32的通信测试过程中,我使用了USB转TTL模块,将OpenMV(或STM32单片机)与PC端进行通信确保数据发出或者接收正常。" w8 I& L) w; k

# J% H, @$ E  D" A  H. H2 M. x( IOpenMV&&C0 \5 }: D( Z+ K- q" s/ e
OpenMV_RX接模块TX
: o9 v" j9 b- y( R; OOpenMV_TX接模块RX
# ~' s. [4 T) [" e2 w2 mOpenMV_GND接模块GND* F* j  }4 i; l0 h4 j
然后打开OpenMV,在大循环while(True)中使用语句:/ H/ [0 H7 m1 f+ g) x" u5 J$ ?; f

4 _2 P, u) U) }: k. E
  1. DATA=bytearray[(1,2,3,4,5)]' U4 H; z; z$ B. M% _7 a% }
  2. uart.write(DATA)
复制代码
/ k: a6 N$ k) S/ Z6 l9 Q
打开PC端串口助手,注意设置一样的波特率、停止位、发送字节数等,查看串口助手是否接受到了数据。7 r5 p$ g) ^7 v2 Q7 _  x' J

4 q2 ^6 v/ r3 tSTM32&&C
- n. \+ u6 s3 H' B! qSTM32_RX接模块TX
! d( T& y, v6 h! x, O# d( B, N4 g7 N4 gSTM32_TX接模块RX1 w9 ?/ `( k9 o0 F
STM32_GND接模块GND
8 r# j- @" D* Z/ m% i注意:不管是STM32与PC还是OpenMV与PC还是STM32与OpenMV通信,都要将二者的GND连接在一起。# q) S4 R* ?: r) ?& n
在main.c中先调用stdio头文件,大循环中使用如下语句:7 N( U% W5 l) s* A- y
  1. while(1)3 \0 ]6 U0 ]# B' G
  2. {6 U! k% x# ~  _- K4 o* U4 i
  3.                 printf("HelloWorld!");8 i9 `  Y9 D& m% `% \/ W; o4 P
  4. }
复制代码

) V2 L- q9 B" A打开串口助手查看是否接收到了数据。
/ C/ d# X0 Z  M* y% n# b8 u1 e9 |0 p
0 f( U6 D* X& Q6 D! S
6.学习补充 (代码看不懂的时候可以来看一下)
5 i- p9 d$ e( i; I( ]9 @补充1:static关键字(静态变量)的使用
6 ?7 b% Q0 j- r, V) m0 K1 a0 T$ {# [( z+ D$ M& `8 u* u* v7 X0 Q
static 修饰全局函数和全局变量,只能在本源文件使用。举个例子,比如用以下语句static u8 RxBuffer[10] 定义了一个名为RxBuffer的静态数组,数组元素类型为unsigned char型。在包含Rxbuffer的源文件中,Rxbuffer相当于一个全局变量,任意地方修改RxBuffer的值,RxBuffer都会随之改变。而且包含RxBuffer的函数在多次运行后RxBuffer的值会一直保存(除非重新赋值)。在C语言学**,利用static关键字求阶乘是一个很好的例子:/ E+ I# t# S( \; C; G) d3 f, G

4 W# ?9 p8 R& ]; a; T
  1. #include“stdio.h”: S0 F& _# h4 Z, e7 O8 P
  2. long fun(int n);1 L% F3 z& e7 U6 s& V4 X
  3. void main()
    0 j( A" V. Q& e8 g
  4. {
    - J" k9 P- Q: Z* W8 P7 W0 p9 B) ~+ L
  5.     int i,n;
    4 Q4 N$ ?- s+ r7 V* Q, w7 e
  6.     printf("input the value of n:");
    2 \  L' F( q( n) I
  7.     scanf("%d",&n);3 d# U8 K  I, Y8 W/ b$ i! {8 z
  8.     for(i=1;i<=n;i++)
    3 c, v0 r6 v: B# \: l: e2 l; V
  9.     {
    " q9 X' r3 A' ^
  10.         printf("%d! = %1d\n",i,fun(i));
    9 @# D3 v- m* z- Q3 |9 r2 `
  11.     }$ R- _) Y! x0 W$ I. T
  12. }
    # o3 c, M: a. @$ _/ {
  13. >long fun(int n)( O2 Y7 O, Q! ?2 g! r) c# Z4 _
  14. {4 E$ W4 F4 \  \* @# c$ z
  15.     static long p=1;
    5 r9 l' I& O; \6 Z  ~0 v9 j
  16.     p=p*n;
    / Z* X* U5 m# _) H0 z2 |
  17.     return p;
    ) Y3 u( G- I- ~( s" A
  18. }
复制代码
: F5 A9 N) z1 H2 j0 G" M* g
效果为依次输出n!(n=1,2,3…n)" X/ w, c) G( A" m# c
这个例子中,第一次p的值为1,第二次p的值变成了p x n=1 x 2=2,这个值会一直保存,如果p没有定义为静态类型,那么在第一次运算过后p的值会重新被赋值为1,这就是auto型(不声明默认为auto型)与static型的最大区别。
' v. Q1 m, r, z: w4 [7 r6 i3 c( G, e. E4 t5 m4 r, f
总结:static关键字定义的变量是全局变量,在static所包含的函数多次运行时,该变量不会被多次初始化,只会初始化一次。
. K) d* G4 Z6 Z& N( C/ T. m, w
' N3 X+ @! x# F) Q5 B补充2:extern关键字(外部变量)的使用
" F) r9 O' c3 ]) B+ W1 \3 {程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,而在函数之外定义的变量则称为外部变量,外部变量也就是我们所讲的全局变量。它的存储方式为静态存储,其生存周期为整个程序的生存周期。全局变量可以为本文件中的其他函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。
& D8 d' Q* z9 \8 L5 x) d+ u7 i如果整个工程由多个源文件组成,在一个源文件中想引用另外一个源文件中已经定义的外部变量,同样只需在引用变量的文件中用 extern 关键字加以声明即可。下面就来看一个多文件的示例:/ v. }2 v( ?& `  W8 ^
5 U* V2 M; n% i" m
  1. /****max.c****/0 y) Z$ R1 l( c. f, s6 [
  2. #include <stdio.h>
    , s9 ^, F6 `3 h$ B# S
  3. /*外部变量声明*/
    2 I0 Q& @1 T" H
  4. extern int g_X ;' t9 y; ^1 d& l' ]( I9 P
  5. extern int g_Y ;* `. D- i. S. K7 z
  6. int max()- R6 f1 o7 b+ N; f6 S- k2 Q
  7. {4 e9 b1 l6 X5 ~+ x! y
  8.     return (g_X > g_Y ? g_X : g_Y);  o5 M3 w' G" W5 R* [" e3 ?7 N) J3 x
  9. }, G  r2 [1 R7 A8 q
  10. /***main.c****/+ v. Q# o. u1 j7 _8 U
  11. #include <stdio.h>
    7 c  G: `0 W. M; v
  12. /*定义两个全局变量*/; i9 B3 |& ^+ C- L. O8 L
  13. int g_X=10;
    9 i; O$ c) D* I$ L0 B
  14. int g_Y=20;1 X! _, l9 u4 z2 E
  15. int max();
    " ]. \* k% P2 e
  16. int main(void)
    ) s: o5 }. y. w7 q2 c# V: }
  17. {6 V/ {" X: X  K6 J, q$ s7 z
  18.     int result;3 d) U5 |  J- [/ ]3 g! c4 H
  19.     result = max();
    6 Z: a. z% |, J( ]4 {3 V
  20.     printf("the max value is %d\n",result);
    6 K( }9 T+ R" t" {
  21.     return 0;' k0 j: c% ?" x7 [
  22. }
    0 P9 Z) _! Z( }" r+ r
  23. 运行结果为:
    , e6 ]7 E: Y! Z/ ^
  24. the max value is 205 b/ a( ~7 X: q  d( J2 k
复制代码

9 f1 C- _* q0 G2 X+ H% Y对于多个文件的工程,都可以采用上面这种方法来操作。对于模块化的程序文件,可在其文件中预先留好外部变量的接口,也就是只采用 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外部变量。/ o) W0 a# Z/ z- P8 ]4 v6 X5 b2 h) J
1 z" z* w. b- M
总结:extern关键字是外部变量,静态类型的全局变量,可以在源文件中调用其他文件中的变量,在多文件工程中配合头文件使用。
% C  z1 h' r3 T! b7 U) ]! i  ^5 F( e5 z+ f( C9 \
补充3:MicroPython一些库函数的解释
' {: _+ ]4 `8 y) [/ g- _" W% m; ?% w8 n0 I/ |
1.ustruct.pack函数:
0 a$ e( ]2 F1 d. e: [) {0 g: u) Nimport ustruct,在ustruct中
) k0 R; B! g: j0 s9 F9 @6 H* d: J
  1. data = ustruct.pack("<bbhhhhb",      #格式为俩个字符俩个短整型(2字节)
    5 ?, _) L1 X! A, Q1 y* I
  2.                    0x2C,                      #帧头1
    : R/ x: G+ T! Z
  3.                    0x12,                      #帧头2
    8 x, p' f2 u# ]! A% q( {
  4.                    int(cx), # up sample by 4   #数据13 `* M. Y$ g8 e
  5.                    int(cy), # up sample by 4    #数据2
    4 X8 q% o* F; ]) i* }# x
  6.                    int(cw), # up sample by 4    #数据1
    # v. T- h9 D% X% b9 E9 o: \9 n
  7.                    int(ch), # up sample by 4    #数据2+ R% z# a' w  a# B4 H9 Y2 T
  8.                    0x5B)
复制代码

$ E( w& c/ I2 [0 T2 n""bbhhhhb"简单来说就是要发送数据的声明,bbhhhhb共七个,代表发送七个数据,对照下面的表,可以知道七个数据按时序发送为unsigner char、unsigned char、short、short、short、short、unsigned char。0x2c为数据帧的帧头,即检测到数据流的开始,但是一个帧头可能会出现偶然性,因此设置两个帧头0x2c与0x12以便在中断中检测是否检测到了帧头以便存放有用数据。0x5b为帧尾,即数据帧结束的标志。
9 g8 Q, f" S! W+ _
. D5 ^0 F- y# K( C6 o7 r 20210504040156260.png
# d" d) b( ]1 }- {0 z+ W. ]2 Q  C* x: M  G6 ^1 p- X
2.bytearray([ , , , ])函数:
7 ^6 U  |3 x, C0 L1 u$ N2 F用于把十六进制数据以字节形式存放到字节数组中,以便以数据帧的形式发送出去进行通信。! G9 L( x) q4 R; Z

3 ~/ e+ O1 \4 E/ J
  1. FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])
    2 M5 A/ q' ^1 `
  2. uart,write(FH)
复制代码

( F' P4 ^' [  K5 L4 p( U7.效果展示(可以先来看效果)4 s6 k0 P9 z* @8 m% S1 c

; h8 A5 ]) H, l/ k& I1 t 20210504041702364.png 2 R7 L/ g& m) e0 N: F0 t- p7 ^" A; M. O
从上到下依次为CX,CY,CW,CH
) [/ m' N/ X; A$ n0 `, k1 |$ T; D
8.博客更新4 }0 |. A/ o2 C# O' {8 Q5 I
1.有朋友反馈OpenMv端找不到色块就会报错,解决方案如下:
% ?' Z2 ~6 ]1 T- ]+ C7 q* v; c$ T! N
  1. while(True):
    9 p4 Q  R% w$ _8 N# H5 {6 V$ \
  2.     clock.tick()( U! `+ H% c0 E8 G  {( h/ E# E  B
  3.     img = sensor.snapshot()
    + k3 N9 _% H0 b8 z% |" Y
  4.     blobs = img.find_blobs([red_threshold_01])) R4 Y) T6 I5 N3 g- X% L
  5.     cx=0;cy=0;
    ! }) ?- S# w' {$ v
  6.     if blobs:% O1 G4 Y$ h5 ]5 j% }( ~
  7.                 max_b = find_max(blobs)
    & I+ _$ q5 y' S. T' X) B/ j/ s; Z
  8.             #如果找到了目标颜色; h9 |  [  U/ ~' l; c! W
  9.             cx=max_b[5]
    ' s4 j1 `1 ]# q  \/ t: m
  10.             cy=max_b[6]5 X/ N" n2 t+ I% z8 e0 y% U
  11.             cw=max_b[2]
    / b: g0 y2 |1 |& H* T' T
  12.             ch=max_b[3]
    0 F1 L1 q, r, N& h5 i
  13.             img.draw_rectangle(max_b[0:4]) # rect
    7 |- u' B, F' ^
  14.             img.draw_cross(max_b[5], max_b[6]) # cx, cy1 f9 ^) E/ F0 B+ l& h
  15.             FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])0 @" M; j* n+ ~7 c; c/ ]
  16.             #sending_data(cx,cy,cw,ch)
    * L9 C& G5 M- k/ _( E
  17.             uart.write(FH)  J! W( D& |1 K/ j- B% g
  18.             print(cx,cy,cw,ch)
复制代码

  `) h( ?. b5 \5 V: h在以上代码中,将max_b = find_max(blobs) 移到if blobs外即可。
- @( j$ I' i' O9 n$ y! ]$ h- A# l5 _7 ]3 X2 i
2.有朋友反馈OpenMV发送数据只能发送一个字节,也就是说大于255的数据无法直接通过代码完成,现在提供以下解决方案:在STM32端代码中依次保存大于255数字的高八位和低八位最后在组合在一起即可。
% l3 d- Z; Y9 P9 o- W
% v2 I, E1 t5 \1 g/ d
  1. #if 1
    $ ^' ~! `; b$ J$ e4 \. A
  2. int main()
    ! H- @. ~0 C8 Q+ x# k% l& x
  3. {
    $ h/ ^/ ?1 U8 i; A: A! u4 D& |
  4.         #if 0
    2 i/ x' w+ R9 l
  5.         //字符型数据分成四个字节存放在数组中
    . O! \4 Y) B. N9 e& d
  6.         float m = 23.25;7 _. C; }/ j6 j
  7.         unsigned char *a;
    1 f! j) n3 \, y/ m1 F
  8.         a = (unsigned char *)&m;
    9 @% C5 l4 r) e2 Q2 Y
  9.         printf("0x%x \n0x%x \n0x%x \n0x%x \n",a[0],a[1],a[2],a[3]);+ D5 T. U7 W! M5 E1 e" Q
  10.         
    % Q7 F) [3 A5 p2 _
  11.         #endif7 C; e$ ]! I8 {2 [
  12.         
    9 ~7 z  E2 X+ H& m' S6 E
  13.         #if 1
    % O& K' ]' Q! `# W
  14.         //四个字节数据合成存放在数组中/ [$ J3 X, W0 J1 M% g
  15.         unsigned char a[]={0x00,0x00,0xba,0x41};6 t% w! X2 k: D4 {0 P# Y
  16.         float BYTE;! N2 Z: R8 q3 ?2 N
  17.         BYTE = *(float *)&a;
    % ?- R- }: O8 U- x! f
  18.         printf("%f\n",BYTE);2 h* w% c0 v7 l2 r8 o2 X
  19.         #endif
    , d: I- }! y% B" G
  20. }
    7 t) |8 A( q. S' b, }' _) |$ E
  21. #endif
复制代码

9 m, G7 L1 [1 Y2 x( `% G% F6 p5 m上述代码实现了将四个字节转换为一个浮点数的功能,同时也实现了将一个浮点数拆分为四个字节功能。在Openmv传数据时,只能传输一个字节,大于255的数无法以一字节形式发送,因此可以在Openmv端将该数据拆分成两个字节,分别发送给Stm32端,同时Stm32端对传来的数据进行合成,合成并解析为对应的数据。
+ V9 x, f2 E# g' q2 r$ o: F( F. C另一种解决方案:python传数据的1/2,单片机在乘2即可。
4 r8 o2 b3 ~5 I! F( {# _! X+ L# U0 Y" t' ~
————————————————
# n1 J- r' `: o- I# ^) M2 n版权声明:Wu__La/ T0 D% m. B$ K( h8 c' L
; k2 I. G6 B! `) a, u
8 w+ V( G, {; G4 c. h1 t, E
6 Q1 O% E/ F) |' e, F# F+ x

, [7 S! ]- D  q) r
收藏 评论0 发布时间:2022-11-4 16:46

举报

0个回答

所属标签

相似分享

官网相关资源

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