前言
# d% z1 _+ a( B% s( M/ H0 L4 [. \最近一段时间都在捣鼓OpenMV和Stm32的通信问题,刚开始不知道哪里出了问题,一直通信失败,明明使用TTL串口接收OpenMv发送的数据是可以在串口调试助手上显示的,但就是无法发给Stm32的USART串口。经过了差不多一周的时间,终于解决了。于是在这里记录学习记录。. j/ K- d. `4 k
1 M( Y0 d: U, g. _' O" |
一、OpenMv配置
8 K) |: Q: K* r" a @+ A/ ]1.第一种发送方法5 q7 F" P8 [" R: L+ W/ n7 t
OpenMv代码如下1 |1 I. Y/ {3 Y, g
" C( }+ k' k: E- _
- # Untitled - By: 86188 - 周二 5月 25 2021
/ ~' ]* a$ P& E% v* o - 4 P$ T. ]& R, L0 B& Q
- import sensor, image, time,pyb
- e) G, u# r7 i( E/ L - from pyb import UART,LED
, y6 c% ~( o" t( U - import json/ _& V/ O- }1 {' [* x. h
- import ustruct
- f5 l6 A; m _6 D* R - , G v' U7 _& `3 D4 @6 @
7 P7 M, }" l# ~- g' g4 J- : e: ?/ j0 P5 U/ d5 }7 }* a
6 e3 D" \& W2 F, @/ S- #white_threshold_01 = ((95, 100, -18, 3, -8, 4)); #白色阈值$ D( g5 p$ d. x, E* P3 }% H
- red_threshold_01 = ((2, 51, 11, 127, -128, 127)) #红色阈值6 Z" d' z# J9 l/ Q
- & ~$ Y" @$ s4 H# X6 u2 v
- LED_R = pyb.LED(1) # Red LED = 1, Green LED = 2, Blue LED = 3, IR LEDs = 4.; [# ~3 j- d. n) J; U: _$ l
- LED_G = pyb.LED(2)4 {' {3 E# h* d5 E: I
- LED_B = pyb.LED(3)+ z" Q0 x: w/ b3 @; h/ v0 x8 P, F
- [8 w/ T' n& V" V9 {
- LED_R.on()
. H5 z1 ?( W/ m& U- p; N6 _ - LED_G.on()
, m6 S4 O n5 A* D0 m# c' U - LED_B.on()
( [% h, F+ `1 c* z* q* u1 r - . Q5 a! [. z; ~% d
- ( H) B" y3 }3 h. j# B. Y2 _4 A6 R
1 u- m& n0 g3 k- v1 \' x8 g% a/ _- sensor.reset()
1 [+ I/ H; ^6 B9 D; H - sensor.set_pixformat(sensor.RGB565)' ^3 S! x, ^ w; o$ W; R- Z
- sensor.set_framesize(sensor.QVGA)
* E) d& v3 `- j) O+ b - sensor.skip_frames(time = 2000)+ ^# P* n4 E7 n6 k+ B5 v" b
- sensor.set_auto_gain(False) # must be turned off for color tracking, z. ]# L \0 K7 C, x& e0 @
- sensor.set_auto_whitebal(False) # must be turned off for color tracking
* m0 j2 C( Q6 P1 F: B: K. p - . w6 T, j) e0 P. [8 Q! y8 F/ Y
+ I2 W n# ]& A' a! R- clock = time.clock()+ J/ M. H; U% o4 J+ M8 |
- : ^, k0 X# x& R1 a' t& C0 [2 e! O
- LED_R.off(). U8 v ^! ], L b
- LED_G.off()
1 Q, |8 f1 T. ^6 r - LED_B.off()4 F2 n+ g* i% ~ R
- 4 H9 d, A# g0 W
- uart = UART(3,115200) #定义串口3变量
' q4 a& g- j- v. E0 i/ V - uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
0 ?0 y6 k2 a, G& y+ l i' ] - ) Z& k- G* i% g! Q# B
- 2 R: w! W# c1 \2 p% F' G+ \* ]4 P
- : N0 {2 Q6 J" ~
- def find_max(blobs): #定义寻找色块面积最大的函数
# ~- r1 b. L# z# B: g4 H - max_size=0
) q; ^$ o' ~# _& e) i: ^ - for blob in blobs:+ f* e1 r; k* l" b9 T
- if blob.pixels() > max_size:
3 A- h u r! X, c* n* l - max_blob=blob8 n$ d0 R! W8 g$ _
- max_size = blob.pixels()
2 p1 E3 o) H% p! o! @$ B0 x - return max_blob
% J1 _6 ]4 ?5 J5 t* x: d - , v# h" }, A$ \9 }8 A
- " A9 z) f, a6 s* c" z5 b" M% G* O2 ?
- def send_data_packet(x,y,z,w):
2 L% S- O* ]7 F4 @ - temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型1 I# D- }. M3 p/ N1 Y8 s
- 0x2C, #帧头1* a; d% D( a8 Z# ]% K
- 0x12, #帧头2
/ f, e/ L1 y& L, r3 C - int(x), # up sample by 2 #数据1
" j6 y+ b* `7 ~" V, l; ~% Q - int(y), # up sample by 2 #数据23 G8 K4 w# C$ D% {! Z6 ?
- int(z),! V8 U' |* u% q7 C( X5 e3 {! X
- int(w)); P* u: ~+ R0 Q4 _5 N. B& P; f
- #0x5B)
# F, f# u( x1 @ - uart.write(temp); #串口发送7 D3 v! ?* v# ^# t; @
2 {0 w3 h2 R6 ?! G
n8 J2 [! K0 l5 s$ g2 s
0 v0 M- x8 M# q$ H% @. M# P7 ~
( K! \4 o, M, r7 j
! f, S1 q" p, W6 E: @: ?4 I3 c% Y- while(True):
" F7 M2 O9 C6 [' M( N( I' k) w - img = sensor.snapshot()
) A3 W4 p: u1 b" X# x8 ^1 B - #time.sleep_ms(1000)9 ? ?. O a1 M+ p' _
- #send_data_packet(x,y)
, @* q4 O- |. c7 o8 E' ~ - blobs = img.find_blobs([red_threshold_01]);
/ Q. K1 W: l9 d" g
1 g3 r; @$ V! X: S. c' f( u- cx=0;cy=0;" g; \) U- j5 l! S3 ]# ^
- if blobs:
6 y! U& G1 J p - #如果找到了目标颜色" T4 u R5 X, ]
- max_b = find_max(blobs);! Q8 L* w/ q( _- ^( H
- # Draw a rect around the blob.4 t# V/ \ K B* i2 g/ S! t
- img.draw_rectangle(max_b[0:4]) # rect
" b7 k$ s G3 h! r6 n% K - #用矩形标记出目标颜色区域
. G, O5 j# V; F+ V, R - img.draw_cross(max_b[5], max_b[6]) # cx, cy
& M, S# Y) o7 S g3 M - #img.draw_cross(160, 120) # 在中心点画标记' }2 i! S& ]2 F& F
- #在目标颜色区域的中心画十字形标记
5 y/ [ F7 P3 _ - cx=max_b[5];
5 V: q. t; k7 i - cy=max_b[6];
; U; K# [) v% n - cw=max_b[2];# W7 V' H1 Z% t, p
- ch=max_b[3];
+ a: u6 O& D5 `7 [ - 1 H6 s5 J0 a+ q5 |
- #data = bytearray([x,y])
* B) q# c& p# h* Q: J o9 y - send_data_packet(cx,cy,cw,ch)
- [3 p* h5 t; o - #time.sleep_ms(1000)
( t0 G+ ]- e+ [- h" b: w, f7 }
复制代码 - z, q! f! `1 V/ e4 Z# v8 t; D" {
代码作用:OpenMv使用的是python语言编写,而这段代码的主要目的就是在openmv视野种寻找红色色块,并将其中点坐标发送会Stm32,并在OLED屏幕上显示。
: f$ K/ B" p$ X! D5 I- A0 a( o2 P9 Y7 ?" d
主要通信函数如下
4 G1 A1 e0 {+ f5 a m5 n* G3 U: ?3 t1 M8 p( e# t1 e
- def send_data_packet(x,y,z,w):
* A3 X2 }1 [$ W' e( M$ c( I" Z7 F - temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型
( x. w5 C% `5 M6 d - 0x2C, #帧头1
3 o- U6 {0 }( ?, C7 L( b - 0x12, #帧头2; S1 J; Z# p8 |0 A6 N$ h3 y
- int(x), # up sample by 2 #数据1/ _- V7 K5 n1 e2 Y! `% [1 M
- int(y), # up sample by 2 #数据29 |4 @) N- d/ p" `! c3 w* D
- int(z),* e% g0 z& A9 n/ o/ \
- int(w))# {9 z& A! t. m9 l
- #0x5B)/ F6 `6 @9 L$ Y
- uart.write(temp); #串口发送
复制代码 & d8 Y; A4 P' j/ Q
这里使用了数据包的形式发送数据,将一帧的数据包装,并发送给Stm32,此数据包中的包头非常重要,也就是0x2C以及0x12,这两个数据便与Stm32接收中断中进行判断,以确保数据的正确性。+ v1 O* A% w8 S* F" D
对于数据包格式,此等的使用规划:/ {0 V6 Q0 h* o1 r5 @
+ C( Y: l$ e. R# r( s# n6 y- {
- #pack各字母对应类型
- L# i& \/ K& l/ o6 @. y - #x pad byte no value 1
; |2 X7 T& N- ]- ]1 y - #c char string of length 1 14 b2 T% }: }6 n' ~4 H4 c4 P
- #b signed char integer 1
# C" ]9 ?" q$ [; a7 F) Z1 Z; D, h - #B unsigned char integer 13 u y+ ]( [5 H+ B+ t4 {! q @
- #? _Bool bool 1
1 g1 l n$ U9 d' \ - #h short integer 2
0 G& N2 B$ ]% y& E2 y! {. y/ I/ {' o - #H unsigned short integer 2/ y, F- A5 V5 U2 q4 {. D: H
- #i int integer 47 k# p6 f( m; w+ l& [ N
- #I unsigned int integer or long 4
o; ?, W9 `. N& {6 [ - #l long integer 44 u- s V9 `( R. Y0 G
- #L unsigned long long 4 A3 s3 U) Q3 g& N) ?, f7 b2 W
- #q long long long 8, X) ?: w! X. G9 o/ e2 j
- #Q unsilong long long 8
5 k# f$ \* t& P$ r+ A% V - #f float float 4
6 _" Y H' j' Z# v - #d double float 8* A7 b' b2 U- j6 W* A( u
- #s char[] string 1( U+ H; x, z8 O4 y |% T
- #p char[] string 1/ R) H% y! T; o2 q
- #P void * long
复制代码 ( w; W A8 |* N: g+ O K
对于此处我所使用的"<bbhhhh",# V% k. ?" e; e' L/ n6 `
第一个第二个数据为包头,便是b,字符串类型。而后面开始为数据格式,我选择的是h也就是短整型,传输给Stm32的时候,便是两个字节的数据格式。 p! J5 a! t/ j
如图,一串完整的数据便是 2C 12 36 00 80 00 2E 00 9D 00。( o m7 r5 ~# c7 D% J5 x
~1 a/ x* J5 p/ h5 R1 P% i
2 K1 d, I* d3 t9 l- j" |, c+ i
Z' K) X7 O$ C l; k2.第二种发送方法- H/ u5 a+ }- V# Y2 T
除了以上这种以包(pack)的方式发送给stm32外,还可以使用另外的一种方法发送:
! |& k# F5 X x2 u! V6 @: t* j% @/ Y2 P% J
- def send_data(cx,cy,w,h):
7 T' X& R2 C2 A - datalist = [ 0x2C,0x12,cx,cy,w,h]
4 y' S5 s* K, |( }2 ` - datalist.append(sum_checkout(datalist))8 P5 t# N+ U3 O0 r; ~3 S8 Q
- data = bytearray(datalist)) T1 O$ s. V. N7 G u! m* a
- return data
( D3 f. q! O6 |4 f
+ ]6 U9 D/ C9 s0 M- #检验数据和
a/ d. r4 l* k5 i - def sum_checkout(data_list):2 H, ]& y H: P1 ^9 z' ?
- data_sum = 0
' B& E/ r- ^; [7 V) ^: ` - for temp in data_list:- D- C# A" h7 A, M. o
- data_sum = data_sum + temp
. t5 U3 i+ G; P. f2 Z4 | - return data_sum
复制代码 % A5 p2 W8 G5 f" K5 x" Y
这段代码和之前不同的是:我将0x5B 最后一位的数据校验位改成了用sum_checkout() 这段函数求和取余的方法作为该数据的数据校验位。
( y4 |* C/ ?/ \ H, f" L但是使用这样不封包的方法发送,就必须加上data = bytearray(datalist),将数据转化,才可以进行通讯。
4 u. \1 h6 c5 Z7 c
0 x0 N& S" W" n. F二、Stm32配置* l% s# l' M O4 w
Stm32的USART1配置如下:* C5 C! d: i8 N4 R0 J ]
& Q+ R% J* y2 P1 C5 X
* [& H9 P% f$ V( n8 @. o7 U- u16 USART_RX_STA=0; //接收状态标记 L' r9 e( J- f" `; [2 K4 R: I7 s
+ {1 I2 }" x2 Q1 Q9 p6 B- void uart1_init(u32 bound){
8 s" }: ~, G s0 Y6 X6 X - //GPIO端口设置
/ I; G' K" r7 v# S0 J8 z [$ G - GPIO_InitTypeDef GPIO_InitStructure;7 b, j% S t B: B$ u/ }
- USART_InitTypeDef USART_InitStructure;* ?+ q/ A9 s5 V$ {$ N' z' H
- NVIC_InitTypeDef NVIC_InitStructure;5 ]4 d u- G& J: z: S: J
-
4 y4 O2 m& E* t - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟* |0 h" N" I; D4 `# T3 h
( L; C8 I! v# i) w. s, Q$ ?0 Y0 i- //USART1_TX GPIOA.9' g2 s$ x0 S# X
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9' I* X0 R' Q: f9 r: O3 w: R% a9 n
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;0 L: ^2 Q: X" b
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
% q/ T7 D( G1 N$ J- W5 v3 ] - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.91 O( ]' R8 n# ^/ \
3 I( E" X. f$ ~: m+ J' c- //USART1_RX GPIOA.10初始化0 ]) E i9 t+ |# r
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA109 Y+ S8 h- W3 l# W
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
8 @. R8 S, N5 d - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
( |, A: o4 Y: E. E" W- L! S+ X - & m k0 |; w- `9 L5 h u
- //Usart1 NVIC 配置
" o, X5 O' V5 F5 s! I - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
' u2 h) p: y" O: H - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3) ?( ]5 a3 X; T" w$ n1 x
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
% T2 ~+ @6 |& |9 v- C' c; V V - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能- v7 A( W8 I& i8 j2 u6 M
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
. `9 J9 H% `' d) f1 t) Q, y - 7 g- i% T6 {. n* W" E
- //USART 初始化设置! F# o8 Q2 b% F
- ! ]$ @; ] G0 l+ D
- USART_InitStructure.USART_BaudRate = bound;//串口波特率2 s6 { M4 L& o6 O _, z
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式8 n' m* B2 s9 W$ t4 ]* G. k
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
, h0 @( P S; I3 f; @7 f6 F' G - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
" \9 ^$ z+ E- l) i/ }8 o - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
5 F6 l( r7 t+ c' f4 U; _ - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
& Q0 M' t3 a) T3 T7 i1 g - 5 h1 P9 I- L+ f, R* |* c |) `
- USART_Init(USART1, &USART_InitStructure); //初始化串口1
: V- u% a) F" A3 R7 g B7 W* V7 F! I - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
' ^* Q0 Z j$ ]6 H+ D2 c/ | - USART_Cmd(USART1, ENABLE); //使能串口1 8 @: _, x) o! ?2 G/ I1 H
- " I2 w/ ^" l+ q5 S7 I, ?# ^3 E5 {& n
- }
复制代码 2 x: \& u/ p w8 R+ w) l6 Y9 p/ O
相比于平常配置并未存在较大差别,而实现通信的重要步骤是在USART1的串口接收中断中。
8 W* E T% V! G, S/ P% l8 C0 P
' h- X% Q# N5 V e" `- D6 H- void USART1_IRQHandler(void)
: N7 y$ t7 i: `" M( S - {
$ D. S3 t C) Z5 x+ N" d; c - u8 Res;& t0 ] X0 r5 O% Q: ~* o
4 c$ o. K# k; q5 \: P0 v4 z p- static u8 Rebuf[20]={0};
$ W, V. r' M- N0 O$ \) T& ` - static u8 i = 0;6 D, c# U2 }" ^
4 Z- O; C# h+ U! X; t! s- if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET )( T; I6 X' e/ O1 P- |+ t! s
- {
# O. [. V1 `" y; }6 ^3 F3 B - Rebuf[i++] = USART_ReceiveData(USART1);
: C- g6 J" v( k2 `: K) ^ - if(Rebuf[0] != 0x2c)
3 Z6 `' ], {, l: k& G* Z& t. n; ] - i = 0;: a' {. e1 w" z9 G& p; [" A
- if((i==2)&&Rebuf[1] != 0x12)0 [! a5 N' M% |+ `. y
- i = 0;
$ G! G1 F* i1 l M, j3 C W - if(i>=10)
) T: l" f- e( {3 y2 k3 ] - {9 l8 J, ^' r0 N' P0 k
- memcpy(OpenMV_Rx_BUF,Rebuf,i);- d* I; W) Y [; u g; M, W( ^" Z/ E( O
- i = 0;
0 `) q& J( |- Y T - }; X) w% ^6 B% x+ E8 v, k$ _
- USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除中断标志8 x% b8 i: K. q- R1 C4 ?9 b
- }8 i2 Z; b$ e) I$ Z$ _
8 A# z& I5 i6 D- }
复制代码
5 b2 W9 p3 T/ M" y- K其中定义了一个OpenMV_Rx_BUF[20]的数组来接收openmv发送过来的数据,需要使用extern修饰这个变量,以便于事项后续操作。+ H, r7 o, C# L1 T; J. O! r9 g# j; @
同时memcpy()函数也是十分重要的一个内置函数,可以实现两个数组的拷贝任务。
- V0 P/ f" m. C1 y" \, k: k1 @9 ^: l s0 d
主函数的代码如下:: y1 Y' a: ?) F
1 a7 \. r \1 Y0 R+ _& A6 S0 @
- int main(void)
! `5 m$ K* A! p) K0 k3 L1 P" V: L - { * \1 U1 g" d" S. M. K- _
- 2 S( {$ W9 N7 m
- delay_init(); //延时函数初始化 ! t1 V& i* _; x* f% [0 l
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
% G* `% I" z3 l4 @ - uart1_init(115200); //串口初始化为115200& h+ c& F- j0 Q' o
- LED_Init(); //LED端口初始化
+ L4 s _% y$ p: d& _( [ - OLED_Init();* L' ?$ a& A/ y6 u& F. F7 A: ]
- ' y) E& s# g! {
- while(1)
^5 D6 q; P; P - {: T, N2 z1 Y& E- X, g
-
: f4 q$ G0 I2 y; G# d - LED0=!LED0;
- |, X; ?7 ~! Z( H* J5 T - delay_ms(200);& K2 N. E7 S* e6 G4 e3 M2 |
- # U+ k) A1 [* o! U) o$ ?
- OLED_Refresh_Gram();4 a( @5 j, m3 b$ R) l( P7 \( W
- $ X' R* Y; f2 k7 D9 t6 S) T
- OLED_ShowNumber(0,20,OpenMV_Rx_BUF[2],3,12);
3 j9 t4 k* V2 B8 i - OLED_ShowNumber(20,20,OpenMV_Rx_BUF[4],3,12);
1 `" V- W$ {4 r* `4 b9 M! K" \ - OLED_ShowNumber(40,20,OpenMV_Rx_BUF[6],3,12);$ f7 r; k% b1 W3 b/ i ~$ I: q: c
- OLED_ShowNumber(60,20,OpenMV_Rx_BUF[8],3,12);( ?: x; F- A& a6 s
- + V8 R( g# x& O
- ( B! [( |1 I' p% D' K4 E9 y0 `8 ~- V
- } + }( N2 q6 U6 Y6 j
- }
复制代码
1 u# H7 I' u+ I前面就提到了,由于我数据包的格式设置问题,存入OpenMV_Rx_BUF[] 数组中的数据,真正有效的是第3、5、7、9位(因为选择了h类型数据格式,一个数据占2位)。, |0 ~+ k9 E" |% ~/ q# T5 V
+ u$ U# O1 F- a, L- y( I总结# l; _6 B2 x# t( W7 c9 Q; t$ n
博主我之前数据一直发送失败的原因是,博主使用了 uart.write() 这个函数,企图通过 uart.write() 这个函数实现数据发送。
+ M, k, M0 C) p6 c这个函数在实现OpenMv和PC端的通信上没有问题,可以将数据打在串口调试助手上,但在其与Stm32的通信问题上就存在问题。* I6 \& X; y. B/ R% f) P5 S3 z
若要使用 uart.write() 实现与stm32的通信,便要使用bytearray()函数将其转化,才可以进行通信。
4 `8 C& v# T+ Z& N2 U- S+ r3 Y1 ^5 y/ L. F+ o6 ?9 S
- FH = bytearray([0x2C,0x12])2 l9 K/ y# d4 J- j- E
- uart.write(FH)
复制代码 2 T% T5 v) Q6 _/ ]
" @; G J' v& E6 t9 k9 F1 } q/ ^, j" v+ s) r
|