前言5 r% M5 l" Y8 z' }
最近一段时间都在捣鼓OpenMV和Stm32的通信问题,刚开始不知道哪里出了问题,一直通信失败,明明使用TTL串口接收OpenMv发送的数据是可以在串口调试助手上显示的,但就是无法发给Stm32的USART串口。经过了差不多一周的时间,终于解决了。于是在这里记录学习记录。
" m/ _5 L* q, w
f, D" y. C4 ?6 \" q一、OpenMv配置
- ?; \. g. y. J- J( _1.第一种发送方法
. n7 w) J: L6 [ l, l8 Y% XOpenMv代码如下
5 c9 s1 [* K6 ]! a7 X; v% V
" B3 @" |! f5 y2 V% @2 K- # Untitled - By: 86188 - 周二 5月 25 2021
L/ }& D- }2 b7 ~
1 n+ R; F6 P9 w( y- import sensor, image, time,pyb0 g1 H- o H/ Z. ~
- from pyb import UART,LED, f b. j! G. m S5 g. u* F
- import json( P( l5 x2 p F* Q7 N
- import ustruct
( b0 |6 I8 r* ?/ I2 L - : v& o# n4 i: E% {
8 u% c# S1 l* q7 R" B' g
9 F' v6 u9 i B3 s/ d$ H& \6 ]
2 p% i5 i# i [ D- n* d- #white_threshold_01 = ((95, 100, -18, 3, -8, 4)); #白色阈值
+ o: V3 V2 t4 D- p$ Z( j - red_threshold_01 = ((2, 51, 11, 127, -128, 127)) #红色阈值
& w5 o; V2 S# g. n3 ]6 x) L# a u9 v - 5 e* e8 p& R' u3 j7 V9 i e$ {
- LED_R = pyb.LED(1) # Red LED = 1, Green LED = 2, Blue LED = 3, IR LEDs = 4.
8 ?9 | J- q q! K2 J" _1 `" M& g - LED_G = pyb.LED(2)
( P, D: x0 v) e% c% h4 L - LED_B = pyb.LED(3)
, B' k1 h- I0 G. W1 q - ! a( W3 U: K; }' L
- LED_R.on(): _( |6 ]: K# n8 S! n% B
- LED_G.on()
/ i4 A9 {- H I& W3 ~ - LED_B.on()! M; ]4 o2 e# m
7 {; x2 [( _( u& N% K7 n
0 l; ]4 g- @- t! `2 P1 E; l0 ]( o- U( _3 I) |2 |6 o- v, O3 t
- sensor.reset()
0 ^" s" o% P" x - sensor.set_pixformat(sensor.RGB565)
0 p1 g; K" |8 z9 j7 M( B0 z - sensor.set_framesize(sensor.QVGA)3 M) a* W; C7 N, U6 I3 Y7 M) D
- sensor.skip_frames(time = 2000)
5 f. H$ P! c4 Z' s - sensor.set_auto_gain(False) # must be turned off for color tracking
& g0 I9 H2 ]- c) }$ V - sensor.set_auto_whitebal(False) # must be turned off for color tracking0 m9 u$ v# e# s2 F- k
- ) `: R D: t% v% `4 r
- / @4 c' w: R+ P, p% q5 E5 U
- clock = time.clock()
1 I b+ x0 {/ S! a* A1 P1 B
6 j( x* o" s' Z3 w' ~; U- LED_R.off()
- x+ e% }$ y5 r' S p - LED_G.off(), S# V$ ^) o( Q- z6 R. {
- LED_B.off(): ?( u3 W, P6 A# |$ _6 c
- / Y) l" ]1 K5 P) {- A
- uart = UART(3,115200) #定义串口3变量
+ A9 y$ O/ B S& T; B1 ]/ ` - uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
9 d9 ~# T0 ~% ~8 t% T9 m
& x' W- x" q# M: O! [, K! L- & n% g, p! [& _' X& }
- 7 b0 T6 p! U: O/ A, |
- def find_max(blobs): #定义寻找色块面积最大的函数
! u9 Z7 f: B J @6 F" U' Y: i: I4 l - max_size=0
. y- {! m, A) n$ A - for blob in blobs:
- Y6 V3 g$ _) A% U - if blob.pixels() > max_size:
" I( ?" Q4 a5 X - max_blob=blob- U5 i, A! T2 I3 L9 V5 S: Y
- max_size = blob.pixels()+ p) S) m3 N0 F
- return max_blob
3 ?# L& `' M* X& J% P. N - ; X5 V! o: I; _$ Z: G0 u2 h
- % Z8 l' d3 E8 D# p8 h! ~$ I) b1 f
- def send_data_packet(x,y,z,w):
% {* E/ X. m" a7 @+ b; D# Z - temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型
+ \2 s. b/ M) s - 0x2C, #帧头1
/ T# F) u+ V/ n - 0x12, #帧头2
. ~8 A5 Y( O3 {+ ^' ~9 a - int(x), # up sample by 2 #数据1
+ G% u! q5 d0 a5 P - int(y), # up sample by 2 #数据24 W; Q, u5 l }
- int(z),
9 q& _- f3 E' b9 j! c9 ~ - int(w))
" j6 V4 y8 V2 l$ C6 j) Y - #0x5B)1 C( u0 c6 y2 s/ `. H
- uart.write(temp); #串口发送; `% d ~5 U# M4 S
- 5 E6 g7 }# T5 B8 O; D
5 z+ q. h, g8 A+ C
! z: K @6 {2 Z i j& _. s: R. l# t
( V+ F' z- P" g; Q% p; s& ~
9 h4 i- L: T+ D6 b4 v! @$ J/ a3 m- while(True):1 u7 q6 s1 d+ W7 Y4 Q
- img = sensor.snapshot()+ J. A4 ~4 J: t4 S
- #time.sleep_ms(1000)
( r7 S6 P! o2 v0 Y6 n& u - #send_data_packet(x,y)& k, M1 W+ _( F9 P
- blobs = img.find_blobs([red_threshold_01]);
; K7 i( j, D2 w K R' K
# r! c1 N1 G# h$ _- cx=0;cy=0;: M8 u2 C! y9 n! d! S
- if blobs:& W$ [) X O: m; W$ e) i
- #如果找到了目标颜色& T; n1 P2 H* t1 X. t4 H
- max_b = find_max(blobs);
$ z+ z3 z, X+ s% |% R8 d - # Draw a rect around the blob. [& Y- J5 j; F5 g7 z! Y9 a* |
- img.draw_rectangle(max_b[0:4]) # rect& Q% G- I: n) D: Y3 a
- #用矩形标记出目标颜色区域
2 U/ }; D; n7 S - img.draw_cross(max_b[5], max_b[6]) # cx, cy
" q5 t% B1 \( I2 d5 }/ _% B _: g - #img.draw_cross(160, 120) # 在中心点画标记7 Z/ m0 a1 |+ t
- #在目标颜色区域的中心画十字形标记
+ G6 [; o# T0 x# u% c - cx=max_b[5];
* S& S9 d" r( o q" n, w - cy=max_b[6];% a: W( f2 N) d4 C) l
- cw=max_b[2];
, _9 B7 t* t* P+ r- n1 S - ch=max_b[3];4 O- e) @( w# e& p" ?
4 I; N8 D. K6 p' X8 |( l, E$ P- #data = bytearray([x,y])
; H, p; \- l( a( M - send_data_packet(cx,cy,cw,ch)7 l; b/ Y# n# [4 U" o
- #time.sleep_ms(1000)
d% P; @2 ^. \8 k! z9 U" ~
复制代码
. n; |' J5 ]+ m" [ |代码作用:OpenMv使用的是python语言编写,而这段代码的主要目的就是在openmv视野种寻找红色色块,并将其中点坐标发送会Stm32,并在OLED屏幕上显示。
& o, [" j- k, F: ~4 m8 Z& o& C
: q# l5 Z3 f0 B# I$ M2 t2 j7 j主要通信函数如下
( `7 w! [' I* Y+ o1 M: b4 T6 Y6 m3 a& }; N6 t, v$ v
- def send_data_packet(x,y,z,w):
3 N8 U* U) E1 G8 A( A - temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型
8 a6 `; R. w# u, G- m7 I/ k5 D - 0x2C, #帧头1. Z7 Q# K( x7 h( x4 ^! g
- 0x12, #帧头2+ m1 S( M, I7 h) f# ?
- int(x), # up sample by 2 #数据1: Q# g; ]& t: o' J
- int(y), # up sample by 2 #数据2
5 A: X2 m# p3 @2 E2 \ - int(z),
! X% C8 u Z. |- v0 ~: a, a/ v - int(w))8 r- ?$ K6 ]6 O
- #0x5B)
, F5 }/ c3 ^+ F: v) L - uart.write(temp); #串口发送
复制代码 " |9 F" F9 I+ S. A
这里使用了数据包的形式发送数据,将一帧的数据包装,并发送给Stm32,此数据包中的包头非常重要,也就是0x2C以及0x12,这两个数据便与Stm32接收中断中进行判断,以确保数据的正确性。' P0 M; l- ~) j0 z0 Q: R0 y
对于数据包格式,此等的使用规划:; W5 @1 Q/ v o% B$ k- J
0 _: S9 ]) z9 Q) d* C
- #pack各字母对应类型
$ a7 y1 j# I% X3 x0 O5 J4 Z - #x pad byte no value 1! Y; B5 \8 P+ z" C8 Z- G; Q" Q
- #c char string of length 1 19 R- w4 x. c4 S$ ^" d
- #b signed char integer 1
4 W7 n& Z( B4 J7 B4 A2 Y4 q - #B unsigned char integer 1
2 I/ ~- O+ s5 o2 n4 A3 [3 F8 U - #? _Bool bool 1
. n: Q: q4 U- z$ t8 w" _ - #h short integer 2
* Y. L" L" v! H - #H unsigned short integer 2$ _( E' j' b8 G& I( W/ b# T9 @
- #i int integer 4
8 J. \2 [- C ?# c, | - #I unsigned int integer or long 4
( m9 H- f0 G, D; H% x' u - #l long integer 4
0 g# T o0 q! ?- z, A( Q2 {7 @ - #L unsigned long long 4" y3 [" s% `3 a
- #q long long long 8
0 G R' @ g- V. G$ h - #Q unsilong long long 8; O. ^7 j& o; T( ^, m2 y, x k
- #f float float 4; K1 f2 F1 F) X, e$ B" Q+ k @
- #d double float 8
, h0 k r( R% m) ~6 T2 {7 c - #s char[] string 17 R5 z% [5 U. V* i" x, @
- #p char[] string 18 c8 C& S( t) V- h; o: z8 }
- #P void * long
复制代码
/ p9 h: v, w" q' o' C. t! F: e3 e对于此处我所使用的"<bbhhhh",
( F" V* o) c1 o# y# X4 T第一个第二个数据为包头,便是b,字符串类型。而后面开始为数据格式,我选择的是h也就是短整型,传输给Stm32的时候,便是两个字节的数据格式。# |3 z) D1 {5 d7 _5 P
如图,一串完整的数据便是 2C 12 36 00 80 00 2E 00 9D 00。
6 u- u% D0 q: W- y* q
7 ~* {7 H8 U- z, }9 b( [; f( @
6 H/ b% D* i7 M) N( B. m& D
9 G; Y5 r) N4 d
2.第二种发送方法& ~7 I) }/ Q Q f$ U& V, S' U/ @
除了以上这种以包(pack)的方式发送给stm32外,还可以使用另外的一种方法发送:; f N6 m% D4 ~5 T9 \7 ]
+ |+ _" h5 k5 L0 u7 l+ f5 {9 F) v
- def send_data(cx,cy,w,h):+ t3 U5 D/ |: h+ P' `/ b" ]
- datalist = [ 0x2C,0x12,cx,cy,w,h]
R0 c1 c/ D) Z+ d1 G3 u - datalist.append(sum_checkout(datalist))
* F. H" G% u5 K1 c" { - data = bytearray(datalist)
7 N& {- s4 h8 g" o& F5 m4 \ - return data, _# O' _2 x3 ]' r0 J i- k
9 `) m3 Q8 {' J% P2 m: `6 I- #检验数据和
8 U. R1 f* p6 Q& F; @3 T - def sum_checkout(data_list):
7 J$ U) o$ I+ J9 V5 F - data_sum = 0$ ]$ S& i( G+ B7 l! s
- for temp in data_list:
% e; X' |" m9 l; F4 }6 F7 z3 y - data_sum = data_sum + temp
8 L: s6 N2 U6 X4 a" X i - return data_sum
复制代码 $ W, l) {( c8 Y+ u# F
这段代码和之前不同的是:我将0x5B 最后一位的数据校验位改成了用sum_checkout() 这段函数求和取余的方法作为该数据的数据校验位。9 ~3 W: S. Y) Y0 l X+ A% u
但是使用这样不封包的方法发送,就必须加上data = bytearray(datalist),将数据转化,才可以进行通讯。
. K4 M# ?7 f5 L, O
; C2 `. \) v- ~# b0 |二、Stm32配置
: k: i/ L% ^, C# D/ p5 `Stm32的USART1配置如下:, D5 l- x" A* X+ A( z9 q" |
3 h7 F4 g0 Q0 ]3 I% Q1 g2 L+ G, k; S! k- 3 x' R' |8 [2 P* m' ?
- u16 USART_RX_STA=0; //接收状态标记
) \! G \3 V& [" I1 B - $ F2 ~4 n& L9 y
- void uart1_init(u32 bound){) O( K/ M, |* u. |, Y1 F
- //GPIO端口设置- d6 e: J- \% z& A! b9 H5 Q
- GPIO_InitTypeDef GPIO_InitStructure;/ d# _8 O: i- g8 e0 {
- USART_InitTypeDef USART_InitStructure;3 K/ q& Y& A. N9 Q* s3 n/ y$ \
- NVIC_InitTypeDef NVIC_InitStructure;8 W, a0 |9 d8 {% x! P# x" S# {7 S
-
2 t9 p* S' I1 J+ i - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
5 u3 l2 I2 ]8 o8 u8 }, t5 L! ]
9 f" I4 D7 \: Z- //USART1_TX GPIOA.90 O6 Y6 h6 v' F9 L/ X3 Y" K
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
6 G* P% ^' P! m/ g5 N, j: w" m+ P - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
* a* [5 ^# o4 ^% E3 Z) G - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
( X9 y9 R3 D3 [( \" l' M' w+ O - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
$ |. E8 U# A5 s! \& K: k - , Y: a+ ~) a% h( y
- //USART1_RX GPIOA.10初始化
, q N( W; S) L& W! Y" S - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
% h/ Q4 E- K: \ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
: |# X( C( Z. ~3 O1 z' ?6 n - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 & @2 N3 |8 L! i" i$ b1 V, S( M
- % l0 R+ I% O7 t1 h/ n
- //Usart1 NVIC 配置
) B' h) n9 d0 _2 P$ S4 R; D- z - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; y% C$ B! x$ P. F$ G' H
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
K3 I4 W7 [3 \; c) c - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
! o7 o) C% }4 I - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能( i( t' _6 m( x* E; [1 f) e8 m
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器& L- n/ {1 l9 k; z/ `" V
& ?9 m) }1 a, |& U5 Y- //USART 初始化设置
# {9 F2 N- l- R& s - + f" u- Z' o# _( z0 f
- USART_InitStructure.USART_BaudRate = bound;//串口波特率1 s: }( w9 J" D. p0 T# J
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
4 @/ C" p, ~5 j% M! g9 J - USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位3 F+ [" k+ r& s/ x9 X& v6 I
- USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位1 h! x& Q& J8 a
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
1 u/ Y5 E& ^. @$ H - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式8 {1 c0 f. z, s) ^$ v' B$ ^, N
- / D: \- ]$ W# P% i+ G3 \
- USART_Init(USART1, &USART_InitStructure); //初始化串口1
8 u% Y! p. O9 @7 q3 j/ b5 R V - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断# t; R6 L6 C. B6 V/ H0 @
- USART_Cmd(USART1, ENABLE); //使能串口1 0 } u0 e I1 m
- 1 N! o4 c3 r `( E f, @5 [ T
- }
复制代码 , I3 O4 F8 S; {6 r- U/ c! U! y" |
相比于平常配置并未存在较大差别,而实现通信的重要步骤是在USART1的串口接收中断中。7 { _. b+ s! R- z5 o4 c
R. W# J" Y! T
- void USART1_IRQHandler(void)) f. {1 }! t; \7 a8 e2 j
- {
; j( d2 p, ?" i( p - u8 Res;8 `; c* i/ N! Y1 W% W3 j# s
- G! _/ v' |2 n; C# y- static u8 Rebuf[20]={0};
" B. Z7 t8 |# F' | - static u8 i = 0;
* @& Y ]- B9 I: z
7 `$ L( c$ K2 [8 _3 r Z9 b) Z+ T- if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET )* r, P' T: ?2 B1 A' S
- {
, U7 M7 _( z$ r; m% ]% m) z - Rebuf[i++] = USART_ReceiveData(USART1);
; a1 ?! x) K' e. m - if(Rebuf[0] != 0x2c)
# E N+ y5 P& q+ w - i = 0;+ s6 e: k9 h$ ^7 H9 I
- if((i==2)&&Rebuf[1] != 0x12)
0 D( u K+ t: F2 j) R7 f- j5 U - i = 0;% |( _' m$ e* g6 e; @& B
- if(i>=10)
3 |1 @( w H5 s9 j4 j4 K - {5 P' U6 a% ]% d% O+ u1 l6 |
- memcpy(OpenMV_Rx_BUF,Rebuf,i);$ ~" _9 ?8 T4 a5 n# h6 [+ Y
- i = 0;
7 B5 w: \7 }: A v - }
" j5 v# n( f# N - USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除中断标志
. l0 |3 D$ c/ x% X0 C9 ]" ?( a - }# A% c$ z1 ]2 L/ N$ z
2 C! p. {) N9 t, h- }
复制代码
) Z4 X: t. e# {7 d其中定义了一个OpenMV_Rx_BUF[20]的数组来接收openmv发送过来的数据,需要使用extern修饰这个变量,以便于事项后续操作。; m" M" w* Y1 m% E! U, S( Z- S" m
同时memcpy()函数也是十分重要的一个内置函数,可以实现两个数组的拷贝任务。 R/ Y) b5 m) z$ E7 w( U
. v; t D7 ~& @! }; X/ {; t
主函数的代码如下:( o+ K) S$ e5 w" R$ @" |4 H E
. f* D8 ~: x4 @+ I3 g& S- int main(void)
# e! M/ i, V) p: P" W - {
; N8 @. S& ?# T3 \4 a8 J4 m' ^( u - 7 f: [" n4 M7 l6 G2 [4 g/ ] C
- delay_init(); //延时函数初始化
4 d0 L% y7 K. W# u9 t - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级, k$ b& C2 g6 d M' x( w: I& Z! q( Z
- uart1_init(115200); //串口初始化为115200$ l3 X# B" B7 C! p$ P6 j
- LED_Init(); //LED端口初始化
- e: p( z# X) { - OLED_Init();
7 C4 g. {$ A- z2 N- @& ]: J+ n -
2 G8 z: }: R! I. c" L/ ?" z - while(1)
# {0 J& f! w# H+ x- [, M2 s - {
2 f4 v' k. W9 ?. ]* s0 B( n -
" U |& ?( o# ~: H* s1 a7 p - LED0=!LED0;
- Y) S) A6 e- c" \ - delay_ms(200);) B v9 A8 f: |# c9 T1 c ^9 V
- 9 Y% B, d- z9 c+ o' [
- OLED_Refresh_Gram();
! G! S$ @3 r" }% Z* C* H% `3 S -
# f) n; }; q# X+ v/ b* W1 `, I - OLED_ShowNumber(0,20,OpenMV_Rx_BUF[2],3,12);
8 e0 ]$ N6 O, B. d! ` - OLED_ShowNumber(20,20,OpenMV_Rx_BUF[4],3,12);$ V; n- B j2 o5 P( K8 T0 z
- OLED_ShowNumber(40,20,OpenMV_Rx_BUF[6],3,12);, R* l1 D( Y; j
- OLED_ShowNumber(60,20,OpenMV_Rx_BUF[8],3,12);
7 C9 h6 Y& ^- U* b - 7 M2 m# _% k% _, `
- . I8 b6 f9 B# B; ?* P; g- v
- }
2 L e% q. I5 n; l$ K - }
复制代码 $ |5 F( N2 {6 o2 I' p
前面就提到了,由于我数据包的格式设置问题,存入OpenMV_Rx_BUF[] 数组中的数据,真正有效的是第3、5、7、9位(因为选择了h类型数据格式,一个数据占2位)。
/ O1 x" `2 y8 z3 ^7 o0 m/ |- m
) w6 ^+ V: J! }( y% J8 B总结
% y4 t! [* D, E+ F3 w博主我之前数据一直发送失败的原因是,博主使用了 uart.write() 这个函数,企图通过 uart.write() 这个函数实现数据发送。
) Y5 D' ^6 k4 c9 m$ c' Q' ~* n这个函数在实现OpenMv和PC端的通信上没有问题,可以将数据打在串口调试助手上,但在其与Stm32的通信问题上就存在问题。. @ A' U- H/ I q
若要使用 uart.write() 实现与stm32的通信,便要使用bytearray()函数将其转化,才可以进行通信。, [) j J( s2 F; f* N4 `0 Y f
9 ~3 p' z: l6 _- FH = bytearray([0x2C,0x12])
t( l' u5 \) M0 Q - uart.write(FH)
复制代码 _5 ]8 E2 V8 a0 @8 Q! X
+ [: k& M/ u0 d% Z* Z& p
% G+ e6 `* k& E% s1 ^4 x
|