前言
+ j$ }3 l. x4 b5 `8 H最近一段时间都在捣鼓OpenMV和Stm32的通信问题,刚开始不知道哪里出了问题,一直通信失败,明明使用TTL串口接收OpenMv发送的数据是可以在串口调试助手上显示的,但就是无法发给Stm32的USART串口。经过了差不多一周的时间,终于解决了。于是在这里记录学习记录。
4 j P. [0 G9 }% _* l* W+ I+ {/ x2 M$ P i5 S/ k
一、OpenMv配置
0 ?7 e5 |! J, K4 e: [) I1.第一种发送方法
6 I' Q; ^1 T% E" @% |3 S1 YOpenMv代码如下
5 G4 o2 n4 R2 Z
2 T- T5 a/ G8 M- # Untitled - By: 86188 - 周二 5月 25 2021! A* C5 p% ~; o5 P. a
; _4 g" s- d* \1 M% B, S7 m( S- import sensor, image, time,pyb
; ~/ F( Y# G5 N7 Q: b2 \7 ] - from pyb import UART,LED6 X& G* @4 n" Y2 l
- import json$ Y, H/ s. o& F0 p. {- W9 x) Z4 G9 F
- import ustruct
' a' \7 H- X2 F9 F& _* Y - 2 {6 I" t g- H
- % a, l" V4 K8 d; r% S+ r
( P P* o4 V8 z+ J) b, H4 | |0 X
8 k8 N+ I0 n( L$ d- #white_threshold_01 = ((95, 100, -18, 3, -8, 4)); #白色阈值: @, r$ L0 \0 L/ a2 o* k4 m# p
- red_threshold_01 = ((2, 51, 11, 127, -128, 127)) #红色阈值 P, [1 w5 V/ f X3 g- a) u( s
' {! B% U( ~/ N0 [) g5 ~- d- LED_R = pyb.LED(1) # Red LED = 1, Green LED = 2, Blue LED = 3, IR LEDs = 4.# |# c Q, P, [- H2 E
- LED_G = pyb.LED(2)5 l' L8 \$ z3 H; t
- LED_B = pyb.LED(3)( X- _ U& c& f* j3 A0 O4 t7 q
- $ B" f0 }% c' i, s7 J5 Q4 m
- LED_R.on()7 ]7 Z2 ], W* Z1 D% _
- LED_G.on()/ s3 f6 J1 v2 j! t! l" F
- LED_B.on()
?5 O+ e2 r$ G' C' F& |" B - " {0 k# `4 M* i: ]$ F
- + f, K' {; R+ E ~5 y
- ) z9 W& e4 H+ B/ k
- sensor.reset()
, v' L# A" M( ` L: s# ~; A" u - sensor.set_pixformat(sensor.RGB565)# A& R0 I% m( Q8 n5 h
- sensor.set_framesize(sensor.QVGA); V9 \2 B; s& a: M2 r
- sensor.skip_frames(time = 2000)
1 w' m" R/ \7 Q; \7 O/ ? - sensor.set_auto_gain(False) # must be turned off for color tracking
+ |5 }! f" l& I n" q - sensor.set_auto_whitebal(False) # must be turned off for color tracking( M% x! f/ U; S
- & h; M. e2 N' e4 [5 N. [3 a
- : E1 |$ D) f5 \4 e
- clock = time.clock()
1 t) t9 k9 B- I y; j( m - + B4 f5 e+ r/ \& h: h' s& M% g
- LED_R.off()
8 C; t! p6 V9 D. `: Q% D - LED_G.off()% v- e9 `* |& p4 N: s4 d
- LED_B.off()
9 e A9 l0 L2 A. n. R - 7 k L+ ~! q$ e9 h2 c& l
- uart = UART(3,115200) #定义串口3变量
6 ~- K: V% l z2 a - uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters* ~; V1 n1 T& B
- 7 S1 h; e1 g* |. V
: W8 F) i& q5 A% d% Q- 2 c* v/ \$ @9 m2 @6 V/ H
- def find_max(blobs): #定义寻找色块面积最大的函数3 b& D7 ?) [+ ?5 H
- max_size=0/ v" a* ]) _' z- J( f
- for blob in blobs:7 v* p J" t/ g' J
- if blob.pixels() > max_size:5 c3 F# i" g9 Z: i" n& W/ z
- max_blob=blob4 _$ k/ ]2 h* U: _
- max_size = blob.pixels()3 W$ q8 a0 R. n. X6 q
- return max_blob) f4 T$ [, [/ p9 A* E9 l
8 a' h- s0 ^1 F
7 |' s. d7 ^" N2 G7 f' R- def send_data_packet(x,y,z,w):) S, L* l" ^6 u
- temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型
6 A3 ^0 X( F) E/ B - 0x2C, #帧头1, u- X- L/ z$ s. i" u( N+ `3 z
- 0x12, #帧头2
/ Z4 u# O: x* z5 ?& U$ e+ e - int(x), # up sample by 2 #数据1; n7 ^- b8 [' }2 w$ j" t7 |9 J
- int(y), # up sample by 2 #数据2
1 w9 M8 Y# u3 c9 r2 o+ V$ s: S - int(z),: b& k: v9 q4 o) S) y7 M
- int(w))! G8 s) t. |0 k$ i6 k& m
- #0x5B)6 A$ n5 ?" I1 G
- uart.write(temp); #串口发送
' T# S b- j# f M8 J& t9 g
1 y7 k- w5 [3 r- - B2 I: ~4 L* z* k' f! a
8 o, E, G2 Q6 i9 \8 {6 S
5 c$ B H3 C) r
" w/ c- E Y5 ?" P7 |' J0 W- while(True):1 D9 j4 x; K i
- img = sensor.snapshot()
: n* V6 j ?) J2 O& {: g& o* u1 Z - #time.sleep_ms(1000)" Q0 \1 B. l! j/ Z$ j: A/ \
- #send_data_packet(x,y)4 X1 X% X6 ?$ \0 T
- blobs = img.find_blobs([red_threshold_01]);) _1 m4 q" v0 O. F X; v
2 \; e& F4 \' ^% n- cx=0;cy=0;
5 j" O* C/ U3 o( d - if blobs:- C1 G- G- a M8 `
- #如果找到了目标颜色 D( U8 N2 F; \7 q% _8 R
- max_b = find_max(blobs);
. L7 Z+ T# j8 b5 @- y, F+ t- o" J - # Draw a rect around the blob.
6 e- R7 z/ q3 c2 ?$ z - img.draw_rectangle(max_b[0:4]) # rect
' j5 ~5 v ~3 ?6 A - #用矩形标记出目标颜色区域, @! ?3 {. `3 ^/ s
- img.draw_cross(max_b[5], max_b[6]) # cx, cy
1 L8 G& E: H% A1 g8 X u - #img.draw_cross(160, 120) # 在中心点画标记/ }6 \3 A) V. X5 w1 F
- #在目标颜色区域的中心画十字形标记
+ y6 a- f# N* u - cx=max_b[5];
z% x* `! n* \) H6 I - cy=max_b[6];
2 N6 w% v+ @0 Z6 L1 _. ^; a/ f+ t - cw=max_b[2];
2 D+ s, Z D4 H4 @: Z+ Z9 X% _ - ch=max_b[3];
n- J K0 B8 Y" C' ] - ; s" w. N6 q. ~' w
- #data = bytearray([x,y])
: Z" P* M, D( r - send_data_packet(cx,cy,cw,ch)" g1 A+ G6 ~; V) w" ?. O1 T
- #time.sleep_ms(1000) l* d) J( n. Z; D
复制代码
& ?4 R9 d* t/ Q代码作用:OpenMv使用的是python语言编写,而这段代码的主要目的就是在openmv视野种寻找红色色块,并将其中点坐标发送会Stm32,并在OLED屏幕上显示。
- C3 Y- F9 |/ B! o( {( ?; }7 Z# Y. K6 v5 n+ G& j0 [) D: X
主要通信函数如下3 }$ a# Z5 x8 P) @) r+ n/ `
1 j0 I* R% l/ r G
- def send_data_packet(x,y,z,w):' f! ^- V2 g6 J) G- ~4 u
- temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型% O- j; j+ j& r& x' A+ z: V" V% E! v
- 0x2C, #帧头14 C l4 X$ Q+ l3 S! L
- 0x12, #帧头2& J! W% C* M, d) j( Z! a9 v6 _5 F
- int(x), # up sample by 2 #数据1; z: N, f) ?1 r; j. c; @
- int(y), # up sample by 2 #数据2
' Y7 u! p/ Z# e/ Z/ n& v$ r - int(z),
3 ~" } K: `3 g; N3 \% B/ v, k9 ~6 ~ - int(w))
/ `; o2 {8 U9 Y N, o* w - #0x5B)
0 e4 M5 P/ D9 Q/ q; F1 ~! V: E - uart.write(temp); #串口发送
复制代码
' \( o" s# W4 m这里使用了数据包的形式发送数据,将一帧的数据包装,并发送给Stm32,此数据包中的包头非常重要,也就是0x2C以及0x12,这两个数据便与Stm32接收中断中进行判断,以确保数据的正确性。6 q$ L2 u ~, `( k! q
对于数据包格式,此等的使用规划:
; V$ Y& q7 }5 D2 v* Y* ~, C
$ u/ o- v7 c6 {% _- #pack各字母对应类型( ?- @3 h& L2 }4 K7 l5 b
- #x pad byte no value 1+ h, E5 C$ Z% I: g3 F" o- |, P
- #c char string of length 1 1
& q* ?7 O7 p% z2 e9 B& B( V - #b signed char integer 13 m( r# Y% R6 \/ S, B; n$ {4 {
- #B unsigned char integer 1* z. x% f, B, K9 O" u- t4 \
- #? _Bool bool 1 u" m( l; y4 Y- f
- #h short integer 2
) t1 q2 G5 I% V% a - #H unsigned short integer 20 z+ j# J+ b( S+ H0 H( s J" {* N
- #i int integer 4
$ K$ V2 |- h9 d# P- i, g4 L - #I unsigned int integer or long 46 @' q; f8 |5 C1 M* Y9 h9 ^
- #l long integer 4* {9 ~/ Q7 k, l1 }$ c3 H5 i
- #L unsigned long long 40 R3 @/ ?# P+ P
- #q long long long 8
" t; B @) L2 u* q1 P; J7 h - #Q unsilong long long 8$ b& f/ q7 y" _3 w
- #f float float 48 N9 d& W I& x$ ~) r1 e( b
- #d double float 8
; M9 H6 h% c, a - #s char[] string 1
' k! N; |' f. q. `, o5 ~ - #p char[] string 1/ \1 M) X+ z6 R0 \7 C
- #P void * long
复制代码 8 G0 O; @* l& Q4 E, C
对于此处我所使用的"<bbhhhh",
( n: a. S5 z% U. b: |- k) u第一个第二个数据为包头,便是b,字符串类型。而后面开始为数据格式,我选择的是h也就是短整型,传输给Stm32的时候,便是两个字节的数据格式。
X& I: A; s c1 b5 t: y. T2 E1 Y如图,一串完整的数据便是 2C 12 36 00 80 00 2E 00 9D 00。) E/ W! y5 d. w Z. z, ]
: G( J* x* `( h5 H. i) \; A
) f/ ?. b4 x8 Q3 L1 ~) Z# u$ H* i6 ?- c l+ c! x) }0 O/ {$ g
2.第二种发送方法/ a. i9 a2 D, m; U0 O( _
除了以上这种以包(pack)的方式发送给stm32外,还可以使用另外的一种方法发送:) Y! o g+ }1 h/ @$ U
0 M) c. O. `/ v- S+ u# o# ?- def send_data(cx,cy,w,h):
5 U+ }, j2 d- }" Y( W* p - datalist = [ 0x2C,0x12,cx,cy,w,h]
+ y4 K( V1 _# m4 Y; K - datalist.append(sum_checkout(datalist))5 Q( ]! H7 D* W I8 p' T3 R
- data = bytearray(datalist)" t8 O! ]- e! J% i$ C- r3 d! h
- return data2 T% l- O$ N+ C7 t
- , M, R+ e1 `' h: ^5 r# F
- #检验数据和
4 c/ ~5 G/ z5 q+ `; q6 p# o* E - def sum_checkout(data_list):+ A+ A$ w) u4 G" |$ O9 c3 a$ f& Z
- data_sum = 0
* c6 r5 g* b2 z) j9 U - for temp in data_list:4 X$ u% ^0 L0 o. a3 V, D+ Y4 F
- data_sum = data_sum + temp( ]) F% {' h. l/ O# a2 _* D
- return data_sum
复制代码
) Q3 C, ?' P+ J/ ]3 t这段代码和之前不同的是:我将0x5B 最后一位的数据校验位改成了用sum_checkout() 这段函数求和取余的方法作为该数据的数据校验位。( M, y: B% h, I6 }$ h: w6 o! y
但是使用这样不封包的方法发送,就必须加上data = bytearray(datalist),将数据转化,才可以进行通讯。
' r0 S4 _3 Q4 \8 f
( D7 C3 m* o0 n二、Stm32配置
: ^+ Q. J3 Y0 F3 J0 TStm32的USART1配置如下:
) @0 |) q U7 J1 F! I
1 p, _* ]% i# T6 k. f- , i( t3 a K( _% A$ l) s, R+ D0 b
- u16 USART_RX_STA=0; //接收状态标记
# [ W+ J$ a8 f/ R
- Z( S- g9 v1 s- U5 h7 v+ i- void uart1_init(u32 bound){9 q+ K) v. J! `% y
- //GPIO端口设置+ W; ]7 Y0 R6 @. P1 k R
- GPIO_InitTypeDef GPIO_InitStructure;6 k9 z4 L5 e) y4 w3 `3 s, C
- USART_InitTypeDef USART_InitStructure;
7 a2 h$ H. [" u" n" @& i - NVIC_InitTypeDef NVIC_InitStructure;
: p5 n7 X/ m; M: G; ~' t& b -
5 f! Y y1 A" \ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟8 k6 V7 U3 V! m+ l% L
- B' I& N$ }8 x* I# y- //USART1_TX GPIOA.9
1 G o9 a0 a# a" h - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
# A$ Y6 a' O% F& I4 x - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+ v5 W' J1 @" W! L' N - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
. Z$ J" c" D. o( ^3 n8 X* ^ - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.98 V, K" x" [+ \6 h8 M8 }
- " f/ g; Q }( `8 i7 a' r9 h
- //USART1_RX GPIOA.10初始化( j/ d- r5 l7 s6 w3 w
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
7 ~9 k% p9 Z( N# W4 J - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
5 }+ j) u; f* n3 \1 a. K - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
# a4 B! O: @- R7 t/ K O& i& i& H8 @
+ k s/ C( W: A$ c) ^0 s0 D! v- //Usart1 NVIC 配置4 e/ r; K# q' L# k& ~
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
6 h0 Q7 ^, l0 A' N& l8 O) J) t) v - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
: B* f8 Z8 V% n6 g4 { - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3/ h2 y( B7 z% a/ \3 B- c
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能5 _8 p& d2 L# l4 |9 M6 T( w
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
( X+ q- W6 p8 {5 n, ? s, ~ - 3 }/ y6 p1 k' r4 N5 |! R& J+ b. S
- //USART 初始化设置
7 j9 h2 a3 B; ?1 G, ]9 S
3 G1 Q u2 H) B' H6 \- USART_InitStructure.USART_BaudRate = bound;//串口波特率
( k; Q. ~( U$ U g, X7 m9 o - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
' A" d x8 Z( k T- w7 M - USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
2 g4 f0 ?, H/ c: O/ G/ N! h* \ - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
. y( t/ }- z: C" Y1 I5 f - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制* r, }( D) @ _3 r7 H; R& S# V4 Y
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式4 P6 Q9 n! `5 O: W4 z( j% p
, H S$ x d# x- F0 y- USART_Init(USART1, &USART_InitStructure); //初始化串口19 g, M, z: X% ?9 g! {9 E% h3 b- _
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
! K9 ]8 n, G' ` - USART_Cmd(USART1, ENABLE); //使能串口1 0 p+ \9 t* I; `& m* g% n5 z% L
! |: U& d( Q, L7 _9 P4 O- }
复制代码
( j" R0 ~5 {$ U5 L, @相比于平常配置并未存在较大差别,而实现通信的重要步骤是在USART1的串口接收中断中。
3 t1 y C) q) ]: [! R7 E8 C$ W6 `& k* t& e
- void USART1_IRQHandler(void)
! }0 t+ e0 ~" a7 x6 a - {: Y2 \* K, @. C
- u8 Res;
- A0 M# c. g* y
0 F5 ?) {) P4 X7 v7 V- U- static u8 Rebuf[20]={0};
" L. v. y7 v$ M2 z5 ^8 d5 P; W$ w - static u8 i = 0;7 N' A8 P, D" x$ X6 U; ?/ V
' Z3 E) q' e7 E/ T; z- if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET )
& I9 h( b7 l5 W: _/ ~: n - {
5 d1 {( D4 j/ s; u. w4 o - Rebuf[i++] = USART_ReceiveData(USART1);
8 n# U# y2 J7 O0 [! S; y/ B - if(Rebuf[0] != 0x2c)) f- R, a4 ?- Q% v) Y
- i = 0;
0 C* D3 f1 {% W - if((i==2)&&Rebuf[1] != 0x12)
4 o; v3 l8 m9 b% c0 @5 S6 r, [) \ - i = 0;
, K6 x. V! o1 f- j: K; w/ @ - if(i>=10)
! T9 I" z: X& J% q; A& A - {0 N" y: N& B6 g5 c ^
- memcpy(OpenMV_Rx_BUF,Rebuf,i);
4 R' [) Z2 k0 f3 `) u% d7 A - i = 0;5 j/ d0 I1 x2 L& o4 d
- }( _& ?" Y4 E9 {, t3 V/ D) R
- USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除中断标志
$ @! ^5 e9 M/ g) } - }* J7 y! ]+ b$ h% G! w- ]8 k/ y
, w* s: }( R' B8 G- }
复制代码 1 d- q! S. d9 p
其中定义了一个OpenMV_Rx_BUF[20]的数组来接收openmv发送过来的数据,需要使用extern修饰这个变量,以便于事项后续操作。
+ l& r! h% ?& v同时memcpy()函数也是十分重要的一个内置函数,可以实现两个数组的拷贝任务。( g7 V( V6 A1 H4 W. ?6 u% u
& G& A4 U' a9 z- F; ?& c主函数的代码如下:* g# h- M: `4 \- W
9 r, t2 G$ H0 d& H
- int main(void)
+ z$ `7 n% T. M, z( n3 B4 b - {
# K1 z, S& C1 T; O( [1 o, o - % v2 q! p( N/ J' [
- delay_init(); //延时函数初始化
; X0 ?9 L, s: _1 x - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
6 ^& _& x) x2 V - uart1_init(115200); //串口初始化为115200
2 |% l' e4 e2 [: E3 v - LED_Init(); //LED端口初始化
G+ P$ P9 Y l8 l; a+ C - OLED_Init();; u; J# v4 ]0 D5 s8 g& R
-
, ^) ^- Q1 \. z9 p* ~' l - while(1)6 X- u9 [& F5 x& i+ N8 G: ?
- {
) w0 s: W7 N2 ^5 a% C) O6 p* |( y -
. u! x" J) f8 a6 Z, ]2 \8 D - LED0=!LED0;2 k R+ j8 Q# t5 R1 I( J, W' R
- delay_ms(200);
' s7 t0 _; `: T; z; y/ E! X3 ]/ ?
, }( j4 D. M2 u7 ^# h% w: v- OLED_Refresh_Gram();) F! y- k2 B) @0 \
- - z+ x+ p: W' X/ q) ]% g; V
- OLED_ShowNumber(0,20,OpenMV_Rx_BUF[2],3,12);5 @7 A; Y% Y" _
- OLED_ShowNumber(20,20,OpenMV_Rx_BUF[4],3,12);
) T2 v' I7 \0 D3 _ - OLED_ShowNumber(40,20,OpenMV_Rx_BUF[6],3,12);* l; D5 Q _. F2 ^4 f g- {
- OLED_ShowNumber(60,20,OpenMV_Rx_BUF[8],3,12);
) I/ B4 Q0 [& y -
# R/ A, S6 z/ j9 ] ^' X# H, i
+ ~. q# @6 E. K" K$ q- }
; ^; S1 z3 f* [( }# \; e - }
复制代码 9 m9 Z6 U/ m( Z7 E' I% x
前面就提到了,由于我数据包的格式设置问题,存入OpenMV_Rx_BUF[] 数组中的数据,真正有效的是第3、5、7、9位(因为选择了h类型数据格式,一个数据占2位)。
3 K0 F# Z; a; F8 z6 F2 i# O. F- o: e4 {
总结
2 b: y; f% `/ K2 E+ m' @博主我之前数据一直发送失败的原因是,博主使用了 uart.write() 这个函数,企图通过 uart.write() 这个函数实现数据发送。
8 H$ a1 p" x8 O9 K" b这个函数在实现OpenMv和PC端的通信上没有问题,可以将数据打在串口调试助手上,但在其与Stm32的通信问题上就存在问题。
, m7 N: f9 e9 S8 n若要使用 uart.write() 实现与stm32的通信,便要使用bytearray()函数将其转化,才可以进行通信。
, k$ y0 L% G" n" O4 u. `6 L
6 b8 z$ P# |% a: Z# f- FH = bytearray([0x2C,0x12])4 H& | `9 L2 u. h( I6 C
- uart.write(FH)
复制代码
* S9 Y& I* A- B/ ]
9 ]- K% ?4 M, C: n, d) w2 i; ^" n7 Q$ Y8 f. a" Z. }0 y8 s
|