前言
2 y: l: [% z. n+ v% O最近一段时间都在捣鼓OpenMV和Stm32的通信问题,刚开始不知道哪里出了问题,一直通信失败,明明使用TTL串口接收OpenMv发送的数据是可以在串口调试助手上显示的,但就是无法发给Stm32的USART串口。经过了差不多一周的时间,终于解决了。于是在这里记录学习记录。6 U3 S; l' I/ Z; \
# q6 ?8 W; }6 o3 H5 F. k- T
一、OpenMv配置
: A K$ q" a( u7 D1.第一种发送方法7 ~: g _/ G ?* Y! p
OpenMv代码如下2 M S' _5 M4 ~( M* w F3 G6 l
0 }: N+ a/ D0 b8 @, B- # Untitled - By: 86188 - 周二 5月 25 2021% l, u! @" i+ V7 R1 \( N' B
0 ?9 y0 Q. s" L% q+ d9 S- import sensor, image, time,pyb
$ a: {' z, f7 k+ u' R - from pyb import UART,LED
$ g0 S! ]# h k- o. u v. I K, } - import json+ y2 H$ a( r, ~# k
- import ustruct
5 e. b" x! N2 e# d6 t - . y ^0 P! a0 p' R
) M! e% L" v. f) c& N# t% b/ b
$ f$ {% P9 x/ M1 m$ y( U5 b. s
! X: l- O- P! K3 ^- #white_threshold_01 = ((95, 100, -18, 3, -8, 4)); #白色阈值
6 N7 s- |$ \! J - red_threshold_01 = ((2, 51, 11, 127, -128, 127)) #红色阈值
# d1 m( r& W0 C3 }: T6 Q: { - : X) |8 J* `8 M
- LED_R = pyb.LED(1) # Red LED = 1, Green LED = 2, Blue LED = 3, IR LEDs = 4.8 j/ ?8 `2 |0 E( ~" m) P& M
- LED_G = pyb.LED(2)" |/ [, O3 R# J. V
- LED_B = pyb.LED(3)
& M6 x" G; F' ~7 p" K - 3 w( q$ e3 P7 T% Y
- LED_R.on()( p( X, R/ h& z, e+ i- H+ O" v0 N
- LED_G.on()
* v/ T5 i0 Y0 m! Q6 p9 d8 y0 t% R - LED_B.on()
1 r- p# _4 m6 v5 }( t5 \: h; F - ) E% \3 r" A5 ~! P6 E7 x, q) V! T
9 e* @8 I! R8 `0 H# z; ]4 z, e* l
) }. j4 y! G7 |9 O- sensor.reset()) ~) K: H$ |$ K( t G+ m
- sensor.set_pixformat(sensor.RGB565)
; b# e8 q, q" \: [3 L7 R& D - sensor.set_framesize(sensor.QVGA)! l' a/ D9 J- ^; {8 ~) F+ v4 U
- sensor.skip_frames(time = 2000)' P* b7 [5 Z0 c2 ~! K$ z
- sensor.set_auto_gain(False) # must be turned off for color tracking
7 Z; T; ?5 n( o. b# Z# h - sensor.set_auto_whitebal(False) # must be turned off for color tracking: [9 a" j; S! D2 a8 q- L
- / M, u! K& f$ e7 Y* [
5 n. ^- A0 [5 D4 r- clock = time.clock()0 b4 M; t' r1 o4 E' q
- * T" a& Q! V/ h. E( i8 l
- LED_R.off(): L& P5 O/ z# u$ W7 x* j T! b
- LED_G.off()
6 [: W4 M' {8 B - LED_B.off()
8 M5 H0 n' Z3 T/ b
9 w* H) l' E) m- uart = UART(3,115200) #定义串口3变量2 J9 {6 u. {1 I6 I3 d+ p8 I
- uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
& c0 n& u' F; M0 G; H, G. d& S$ l
, q+ f& g# O( A8 K$ S# ]. r% P4 ]+ k- L' S" z/ l# ~$ j8 `) Q- u
- : G8 {7 X# C: t3 H5 Y
- def find_max(blobs): #定义寻找色块面积最大的函数! X' d" l/ V) j3 j8 H- F
- max_size=0
1 `: \' }- U( E9 v7 v3 K - for blob in blobs:/ Q/ S% f. x& F( ?
- if blob.pixels() > max_size:
' q6 d+ Q( v! y, y2 w" e - max_blob=blob
% V Q9 S& ?/ k, E7 g/ o; Z - max_size = blob.pixels()' p6 t' Y2 E& J
- return max_blob
0 A2 m( D+ C9 L
K/ p0 o% ~2 h$ H# _8 D. r2 A2 m" d, l- ) I% s8 \+ \" T. F' n+ h( C. @: q
- def send_data_packet(x,y,z,w):3 ? {! O/ ^) I
- temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型; N; C; S' R/ A$ C1 z
- 0x2C, #帧头1$ S0 b4 ~1 y8 F# k
- 0x12, #帧头25 j! T k/ l3 C0 w+ k6 g* X: i
- int(x), # up sample by 2 #数据1+ A9 j0 g; m ?) _7 }) W [
- int(y), # up sample by 2 #数据2 Q( L* E( e/ W& I' H
- int(z),$ O$ ^$ F' l& Y# [
- int(w))
7 q: _% @$ g( T4 A& S - #0x5B)& m7 F) h! A* U
- uart.write(temp); #串口发送
; V* r6 X( e4 g - 8 U& t; L N7 C; c! S
( `1 G( k2 s# K9 \' t$ a
" A- J) G/ W0 w" E9 Z$ x% ~) o- ; c, A' L1 j6 N1 F
- 5 \& l6 s1 u- r* P1 ?
- while(True):
4 F, z3 p9 H( b% z! i3 R - img = sensor.snapshot()
) b5 B4 t1 K1 s8 t4 f - #time.sleep_ms(1000)
4 w! E" J- w. T K+ L* { - #send_data_packet(x,y)
' J0 R4 d2 P7 [) @( r1 G - blobs = img.find_blobs([red_threshold_01]);
# i5 n5 V! y/ @- q
! H, w( p- s" F- cx=0;cy=0;
7 G4 l! n9 B5 y8 y3 O - if blobs:5 C" P+ {* ?5 R) N% }& Z
- #如果找到了目标颜色
: v5 Y9 I( q9 [& g% x$ x. J - max_b = find_max(blobs);/ \$ K# g; _# b
- # Draw a rect around the blob., d( m) T) E5 y$ N
- img.draw_rectangle(max_b[0:4]) # rect
$ s' M O' Q6 S+ } - #用矩形标记出目标颜色区域, V n- G- K1 k: H- r
- img.draw_cross(max_b[5], max_b[6]) # cx, cy
# K6 q# y1 J, m - #img.draw_cross(160, 120) # 在中心点画标记
' a& M9 ^4 V; |/ [3 P8 o; L* Q - #在目标颜色区域的中心画十字形标记
' ^0 [& _: H4 `, P& I: ] - cx=max_b[5];+ Y. f3 M0 B. m+ s8 D0 d5 C# ]4 a
- cy=max_b[6];
( T" y3 t& o U$ N - cw=max_b[2];+ ^" T* [3 ~$ t0 i
- ch=max_b[3];
! s: L9 B k& @5 j! K - / G$ a3 X5 V' g4 H
- #data = bytearray([x,y])0 y0 @* s4 r: ]7 o
- send_data_packet(cx,cy,cw,ch)0 U! {7 p% ~( K7 t/ D1 q" j
- #time.sleep_ms(1000)/ `" R D: i8 }7 O
复制代码
2 y o5 C7 \( w F代码作用:OpenMv使用的是python语言编写,而这段代码的主要目的就是在openmv视野种寻找红色色块,并将其中点坐标发送会Stm32,并在OLED屏幕上显示。6 Q* R4 W# R& n
3 N' c$ t/ @8 P+ w9 n9 v3 t
主要通信函数如下; X, R" g0 J& b3 m+ K
# M6 L/ K' R# i, T$ J2 \( x0 I
- def send_data_packet(x,y,z,w):
, I7 M# G2 W' o+ w2 i - temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型
8 S) s" k+ @$ Z5 u - 0x2C, #帧头1
. N* E4 S e9 m8 H! o - 0x12, #帧头20 N' ^8 z% N. X- t+ h/ _& T, I7 G# U
- int(x), # up sample by 2 #数据1
2 X6 m# J( J& R( S$ p - int(y), # up sample by 2 #数据2! z- Z! b5 j, E$ o0 A. Y
- int(z),+ ]3 C' K8 m) h, ], {3 ?4 J! e
- int(w))
% |) n6 |* j' J, F* u& N4 j( J - #0x5B)
4 Q+ ^8 u% G3 o$ {- r - uart.write(temp); #串口发送
复制代码 8 m7 N) u- |. ~7 ] r& L+ B! _
这里使用了数据包的形式发送数据,将一帧的数据包装,并发送给Stm32,此数据包中的包头非常重要,也就是0x2C以及0x12,这两个数据便与Stm32接收中断中进行判断,以确保数据的正确性。( y7 Z3 ^' c6 T8 R* h' t6 a
对于数据包格式,此等的使用规划:
" Z- T# g+ ^6 z2 i8 @0 V' }2 ~8 h% i
- #pack各字母对应类型2 C+ R) I" H/ \4 j; H% E1 M
- #x pad byte no value 1
7 u( L: D- u/ G8 h0 W n - #c char string of length 1 1
& Y; h0 L8 i+ [! B9 I8 J- M2 j - #b signed char integer 1& }% g! \ f0 Q. ^+ c9 |! G
- #B unsigned char integer 1
& W" m7 b/ v! q: [ - #? _Bool bool 1
5 @5 {% t( ?+ U1 R: \ - #h short integer 21 E7 i3 `9 v. y, o3 [7 \$ @
- #H unsigned short integer 2
3 y2 Y& K! u* I/ s - #i int integer 4
& b% X ~: r- z9 B# u3 R - #I unsigned int integer or long 48 U1 g" ~1 T1 F* c7 l+ o: V% Y; F
- #l long integer 4
. T9 `( ?0 d: t% _ - #L unsigned long long 4
0 p* l0 {+ u( B* w3 `! b - #q long long long 8* i. r5 s6 c: M! C) T8 `
- #Q unsilong long long 8
0 m- Q( z- F8 B2 b - #f float float 4
) g3 {* o1 E/ V8 w6 L1 x8 H, a - #d double float 8
. j$ Y- n: G9 T* C* i' j - #s char[] string 14 r8 z/ V+ ~! j
- #p char[] string 15 y3 O' j( y5 q
- #P void * long
复制代码
$ t1 j9 O. U# `6 p; a对于此处我所使用的"<bbhhhh",: `( o; \& X" d4 Y* z3 j2 A4 g( P9 P
第一个第二个数据为包头,便是b,字符串类型。而后面开始为数据格式,我选择的是h也就是短整型,传输给Stm32的时候,便是两个字节的数据格式。
R8 i4 i! B/ u' R) } o如图,一串完整的数据便是 2C 12 36 00 80 00 2E 00 9D 00。
9 B1 a+ c+ M3 o
. Z# f7 k0 Z2 v/ S9 n
`2 j7 [6 y) F/ Y9 I4 b* Y
% E) A, z/ Z) c7 O7 s! D, Z2.第二种发送方法* l9 f" N" o: U* [
除了以上这种以包(pack)的方式发送给stm32外,还可以使用另外的一种方法发送:: C8 v# b6 d4 d9 K
: r! s" l. z* ~2 D) G( n
- def send_data(cx,cy,w,h):
) _% N4 j' p# U! e" t7 D* c+ s/ L - datalist = [ 0x2C,0x12,cx,cy,w,h]
! E. Q( T1 Z; T2 Q9 ^ - datalist.append(sum_checkout(datalist)); I8 c4 V. e5 W+ v- l( m7 W- M
- data = bytearray(datalist)+ i6 J7 I' F8 g% p: e/ n& z. e
- return data2 c+ w, l+ ^( X% {4 C6 h A9 B2 @
- / X# x2 r3 x& D- Z6 X
- #检验数据和 * o! w+ h/ d& ]' y6 k
- def sum_checkout(data_list):9 z& ]3 _/ u. z. H ^, U+ |3 q
- data_sum = 0; h" ]& k2 C" A0 ~5 o/ X! |
- for temp in data_list:
6 B& N( Y" q1 d( O+ O - data_sum = data_sum + temp
1 t* D2 [. r3 o( T& _ - return data_sum
复制代码
i' J; r. q; f& x4 J# N" z这段代码和之前不同的是:我将0x5B 最后一位的数据校验位改成了用sum_checkout() 这段函数求和取余的方法作为该数据的数据校验位。2 f5 L- b r) R( ~% c
但是使用这样不封包的方法发送,就必须加上data = bytearray(datalist),将数据转化,才可以进行通讯。
/ N% e+ n8 L0 e1 e
7 f# v! i( `. g9 R0 _3 [" a7 \二、Stm32配置2 c* E2 [$ e, z9 W
Stm32的USART1配置如下: m) w4 x- M+ |' p4 S' m4 [
: V6 [3 E3 b9 r9 a K3 R- ! k/ E3 ?# N% {& H _! h$ \9 i% h
- u16 USART_RX_STA=0; //接收状态标记 % T7 v/ t# C# t. E; O
- ! m7 y( x# e3 M* Y
- void uart1_init(u32 bound){
) ^6 M# ]- G, a, `3 @ - //GPIO端口设置
& q& G& s" m$ _7 y5 o% c- ~ - GPIO_InitTypeDef GPIO_InitStructure;8 F$ B% v& F- z$ X5 d- ]( y- V
- USART_InitTypeDef USART_InitStructure;4 K( H0 H9 d, o
- NVIC_InitTypeDef NVIC_InitStructure; e2 v" J* V0 K
-
4 w @( ?$ V J+ B. y! L8 O( V - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟+ q& m. j- s+ T& s% F/ R2 Y' ~
; C$ D" j/ b9 S4 N/ l1 e/ \- //USART1_TX GPIOA.9
( ~- V' A' P v/ ~5 l* I6 ]6 W - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9" E9 Z. l# L% Y$ Q8 T7 J5 U
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;. B1 E; R6 T! a8 ^: ?4 C
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
0 t) i$ {' t) X# B. T F' P( r* s - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9% W; o6 }1 w% E
- # ?3 y$ I. ]' ^5 {2 [5 v
- //USART1_RX GPIOA.10初始化* \. ^7 n7 ]5 C9 I
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
2 ^( i$ A# x% G - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
1 k- h2 U5 u/ c& Q9 M" [ - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 8 A2 ~: q8 Q4 v- R
- ) U, Z) h' q3 d' ^
- //Usart1 NVIC 配置
0 {2 H0 k4 a. k" N8 I/ Y: g5 l9 V - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;8 b& D/ R$ V9 d k
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
& T0 T% p; m9 @4 k0 G - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
: q( O- \' b! r h; F4 V - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能: _$ ~+ K& U: o! g3 {! O/ @% `
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
7 L, ^3 h* p4 K" {& r% F# `, X - 9 d& J$ R) g" M" p1 N& Q
- //USART 初始化设置
9 o$ E; I9 B$ H) J* P" g - 9 Q& _, t" c5 p# D8 y. x3 x
- USART_InitStructure.USART_BaudRate = bound;//串口波特率
( B- I( D. l6 p& p - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式* K. m$ T J7 z8 O/ P$ ~
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位8 ?% F( N. j2 t+ D- T& ?9 d+ l
- USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位; T1 Y; S$ l8 X7 w8 k! Y
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制: ^ E$ `, U+ `
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
, `( @3 j* u! v% X4 w8 r: z - $ p+ I# D* u! y# I' A2 m" F0 W
- USART_Init(USART1, &USART_InitStructure); //初始化串口1" S; C' N7 G( ~9 _! J" b: G2 a# W
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
) s, t+ [) w- r+ ?* K+ M - USART_Cmd(USART1, ENABLE); //使能串口1 & l4 G% T4 ^7 n
( s4 J V" U L' x- }
复制代码 . i; S: W6 A9 k2 X3 p
相比于平常配置并未存在较大差别,而实现通信的重要步骤是在USART1的串口接收中断中。" G2 J4 k# |5 W
% Y. r/ \% B( T: G
- void USART1_IRQHandler(void)
X8 c, @& Y; T) s. t' U: | - {* ^3 l, ?7 g% m* V( E5 H d6 R
- u8 Res;
/ ? Y* i' `1 M a K
2 j* ]: [) L5 B, s- static u8 Rebuf[20]={0};8 R: X* j, V3 Q& y$ F
- static u8 i = 0;
3 {! p$ ^8 M3 E2 w0 y+ Y6 P. n/ _ - 0 _% S0 z, V, c2 B3 W
- if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET )
* d% N5 ^& W6 I6 r: y - {
" a) _- p/ {+ ?! W, b0 n/ w; t - Rebuf[i++] = USART_ReceiveData(USART1);
+ l7 @1 e1 D! R% d/ X! R/ f - if(Rebuf[0] != 0x2c)% O8 j1 v7 b% b1 l2 p4 i
- i = 0;2 m+ _4 p" H3 D1 w9 X4 Q
- if((i==2)&&Rebuf[1] != 0x12)
/ S W+ P. L1 m; ` - i = 0; U2 K% x3 R. ?% v) l! z
- if(i>=10)
3 D1 ], H6 U% ]' Q( [! E - {! ]6 M5 L2 H" _# p. O
- memcpy(OpenMV_Rx_BUF,Rebuf,i);) \# x) j. ~" |: L! J5 A" U4 X
- i = 0; j& ?6 ~7 B/ Q
- }- b* l. s0 c# L0 V& N" V$ b: E+ D
- USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除中断标志
3 U2 ?) Q! j2 ]. P7 N' a- s - }
# q) f+ v g- @# {
& d6 i0 f/ b1 e# F* t$ y- }
复制代码 * {. b9 X/ R4 V& ]$ M
其中定义了一个OpenMV_Rx_BUF[20]的数组来接收openmv发送过来的数据,需要使用extern修饰这个变量,以便于事项后续操作。. Q+ H5 P, H% B
同时memcpy()函数也是十分重要的一个内置函数,可以实现两个数组的拷贝任务。 V# X/ I m; _/ h
, F+ U) u: [" M主函数的代码如下:, J6 Y) J$ U0 X9 V4 k/ o& m4 U
, F3 k5 O m& L4 S! M8 {' n
- int main(void)
4 i* N$ J+ _. \" c- `% Q" S& V - { 5 K3 T7 J- @# u( W7 y7 k- `) m1 c
- " O5 E: d7 U2 Z. i
- delay_init(); //延时函数初始化 6 W; T! Q8 W5 K7 R
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
4 A* [1 R8 B& m3 b - uart1_init(115200); //串口初始化为115200- |7 d) v: E' L9 `) ?
- LED_Init(); //LED端口初始化
8 p. R3 m' W" w9 a - OLED_Init();
7 w+ l: O# x* D; R U1 p, _+ P' A: J - / z7 d1 H) f* A! Y% I' S6 e
- while(1)7 k) m9 H7 o8 R7 C' q$ ]' c
- {; W/ B+ O# Z: i" T0 |
-
* y- X' H& V/ x3 o( f, T - LED0=!LED0;
1 A& ]) o2 d2 o2 h9 D4 u0 a - delay_ms(200);
& r: a9 [* W$ ] |2 U3 F
- j2 D. o/ I! Q5 r" D% K$ H- OLED_Refresh_Gram();
* D7 r' Q v, s; M -
z6 L6 Q& Z9 y) D+ I - OLED_ShowNumber(0,20,OpenMV_Rx_BUF[2],3,12);: _' x% j8 `: x- Y' W6 E- m
- OLED_ShowNumber(20,20,OpenMV_Rx_BUF[4],3,12);9 L" x0 K1 Q o$ t8 W4 b( K- A
- OLED_ShowNumber(40,20,OpenMV_Rx_BUF[6],3,12);/ ?. d( J/ }1 J, a
- OLED_ShowNumber(60,20,OpenMV_Rx_BUF[8],3,12);0 X3 x1 S. ~8 x
- & q1 ^, \- s* u3 V
- 3 u( k K; @. _8 H* j" s! n
- }
: B" |6 W! t8 B$ o) ~ - }
复制代码
) {9 X* Y5 k! D# u9 o8 u前面就提到了,由于我数据包的格式设置问题,存入OpenMV_Rx_BUF[] 数组中的数据,真正有效的是第3、5、7、9位(因为选择了h类型数据格式,一个数据占2位)。
3 x/ c3 Z- u- k7 t$ ]4 h+ z8 ~2 O8 E/ J" C3 l9 n* a( w+ r* s
总结
. Y; ?& t1 A; s7 N博主我之前数据一直发送失败的原因是,博主使用了 uart.write() 这个函数,企图通过 uart.write() 这个函数实现数据发送。1 M7 x: v# p7 y) G
这个函数在实现OpenMv和PC端的通信上没有问题,可以将数据打在串口调试助手上,但在其与Stm32的通信问题上就存在问题。
' Y/ B, e+ D0 g o+ R若要使用 uart.write() 实现与stm32的通信,便要使用bytearray()函数将其转化,才可以进行通信。( t& s5 H( s+ Y6 I
3 Y5 e, P* p1 |( _ A* u- FH = bytearray([0x2C,0x12])
% O/ t' C+ E3 C, N+ f - uart.write(FH)
复制代码 , n) K) O6 A6 _: V+ h
, y! a$ L; E0 n
W y8 u0 c0 \& H: m |