前言
- \3 Y2 W9 b, m g6 o7 J6 z3 q最近一段时间都在捣鼓OpenMV和Stm32的通信问题,刚开始不知道哪里出了问题,一直通信失败,明明使用TTL串口接收OpenMv发送的数据是可以在串口调试助手上显示的,但就是无法发给Stm32的USART串口。经过了差不多一周的时间,终于解决了。于是在这里记录学习记录。
, t; G; U4 r3 R; o: c# b4 f: P6 b+ ^9 ]/ y
一、OpenMv配置
( W# o. _: N0 O1.第一种发送方法
* d- U, i2 X- J B4 J& |1 AOpenMv代码如下' s8 u( l. @( L' d8 V) C4 J
0 I( V+ S5 P% |6 d; V J
- # Untitled - By: 86188 - 周二 5月 25 2021
/ E" z. P( Y( {8 P - 4 b2 l4 \, e6 A* S9 Y/ L# r* z/ E
- import sensor, image, time,pyb0 R4 M4 _9 ^* [
- from pyb import UART,LED
. H: r3 a3 N" F2 f8 f - import json9 s6 i, L( r% h5 _
- import ustruct6 M) ?/ _1 h' s" D7 J# |) ~% p
- 9 s1 I* m& C0 j7 ^
- $ V- {! ?% l+ b$ i3 F, v
2 ~2 o+ c3 X6 b# Z- 3 J9 E0 V% v: w& z5 q
- #white_threshold_01 = ((95, 100, -18, 3, -8, 4)); #白色阈值. i, M# | I S, x' @& g
- red_threshold_01 = ((2, 51, 11, 127, -128, 127)) #红色阈值
) g: n- }* R% x ]
8 \8 \: z& k( G7 a- LED_R = pyb.LED(1) # Red LED = 1, Green LED = 2, Blue LED = 3, IR LEDs = 4.
; O) _, Q+ ?+ B; V. R: \5 Y, G - LED_G = pyb.LED(2)
' k1 k2 [2 u7 A u$ v4 P1 O, ~) ~ - LED_B = pyb.LED(3)
% p2 n& k: R3 ?+ B1 y# Z+ d1 U0 U- {% }
" b5 l. m0 Y8 c- LED_R.on(). c( [ w0 _3 p) \
- LED_G.on()
! Z: \' \- T2 F1 Y0 J* v$ d3 F f - LED_B.on()
/ O( `- D% `2 D7 p( |- f( R
& `2 Z: ]( `. [8 C
; E' o& K5 F% ]0 R6 U6 B8 u0 ]) I6 K- 1 `' g7 Q# V+ U8 w* _, _6 ?/ y
- sensor.reset()
/ C n9 z* c! R: D! w9 `8 { - sensor.set_pixformat(sensor.RGB565) p" L9 s4 K6 n5 i- J5 t7 a
- sensor.set_framesize(sensor.QVGA)
m7 h8 }- V' i+ _2 i5 |' d5 }# m - sensor.skip_frames(time = 2000)7 s1 n4 R# O; T2 I* t2 J4 I: c
- sensor.set_auto_gain(False) # must be turned off for color tracking0 j1 W8 j; R( ] h6 i
- sensor.set_auto_whitebal(False) # must be turned off for color tracking
4 k3 W# D( K* C. n. |, s5 ^0 M
& w( I$ W% }2 Y& g- Q& ]- ; o2 c" X, s* R
- clock = time.clock()& P! l$ H# Z8 P/ Y7 R7 z. y. M# [
- 4 i7 \6 U6 k2 k
- LED_R.off()
2 {! o2 n& j; h! { O0 k. e6 J - LED_G.off()
" `6 D* A5 n6 c2 n; m - LED_B.off() V# ?* c6 s, \, c0 K9 m: F
- - t; N: `. p4 a% e6 N- c
- uart = UART(3,115200) #定义串口3变量4 e( R) r- E; ?$ F4 V5 j
- uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
9 x. k9 i) F' W* {% y) l - . O5 V! p; g7 J+ C2 ^
8 G" z* W0 P- b
0 K' @2 T$ j6 }: a* a+ ^; q8 A a- def find_max(blobs): #定义寻找色块面积最大的函数+ A+ n" H6 \ z& I; p5 ]* J5 @0 f
- max_size=0
1 e/ l0 O9 O: N9 [6 Z G3 o5 [0 v% ? - for blob in blobs:
+ e7 {0 O6 a+ w$ X6 k+ I4 @ - if blob.pixels() > max_size:
' a& o7 {+ R$ V; @ - max_blob=blob% p& ^4 K5 w% B& d; Z
- max_size = blob.pixels()
. y6 L# j- q: K5 T/ }8 y2 ^; j - return max_blob
$ `( G) b& R8 Q - 4 X0 @- [8 ]) S. Z L8 P. o
- 5 [; ?& D |7 I; w$ B: H- T; ~
- def send_data_packet(x,y,z,w):# W' C { B8 \* f Q# u2 Q9 O+ n
- temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型
% J( a/ J$ ]# v3 u, ` - 0x2C, #帧头13 j0 f. L; [4 m, y
- 0x12, #帧头25 a0 e5 x! y' L1 _7 o: g3 A
- int(x), # up sample by 2 #数据1
2 `& \ a" f8 S" ]7 n4 V1 \ - int(y), # up sample by 2 #数据2- c" w& q: l# a: n# s
- int(z),
2 K5 O7 A) B. d! X# h! d6 J - int(w))
/ s; S3 P' O; O4 F+ L - #0x5B)5 L; S2 u% Z( u, A7 O9 t$ ?6 ?) W. W
- uart.write(temp); #串口发送# D8 l. w; }- Q3 Y+ P
- 5 e+ Q$ r7 B9 d
" ]7 i' {% y4 e0 j
1 L, j: ?8 S9 B+ p( }! z1 Z4 L* o- + q$ n/ l3 j; z6 }% ]1 E
- & L: ]0 M U& H8 l( a4 D0 ?
- while(True):& B0 m& G7 l) K# j2 k
- img = sensor.snapshot()8 x) I; ~1 q& A+ w# k
- #time.sleep_ms(1000)
; `0 W* V8 k* M4 X - #send_data_packet(x,y)0 S1 n2 j8 \1 k; Y+ S* b% g
- blobs = img.find_blobs([red_threshold_01]);) T) W7 i8 z- }, E* F9 z
* p5 y& P& v7 K/ N# v3 a- cx=0;cy=0;% F- m4 X4 [0 ?' |- n
- if blobs:
$ O" U. T1 [" l/ k - #如果找到了目标颜色
6 k+ l/ Q# z9 i }6 P8 F( p( i, b - max_b = find_max(blobs);- \/ q* Y# V. x$ ]: e
- # Draw a rect around the blob.
4 A" P8 H- @. W2 F& [ - img.draw_rectangle(max_b[0:4]) # rect( Z4 P# U( T4 W7 L4 h2 j6 S
- #用矩形标记出目标颜色区域
; V# E4 X: b5 r3 \$ n - img.draw_cross(max_b[5], max_b[6]) # cx, cy1 {; Q& z! ^1 z/ c. w$ c; f' `% n% E
- #img.draw_cross(160, 120) # 在中心点画标记
; r1 L8 j: g+ r% _/ ]) i - #在目标颜色区域的中心画十字形标记* {- U; k A" ]- L1 B% f; N7 G6 W
- cx=max_b[5];
" Q) ^3 f7 l* q3 i - cy=max_b[6];
! j8 [3 T( O! F* m2 q1 K - cw=max_b[2];
# c# G8 l& [1 M4 z7 N - ch=max_b[3];
2 u/ _0 U/ ~% Z- l - : T6 M: I8 U1 k: X3 a" S
- #data = bytearray([x,y])* e* ]( h E3 p3 S7 S; ], F7 ~
- send_data_packet(cx,cy,cw,ch)
6 T" ?8 j# x* [5 I0 i$ {0 E. ]) g- l - #time.sleep_ms(1000)/ G, K. z A+ Q
复制代码
5 i2 Z- t4 V! ^ g; [/ _# J Y3 N代码作用:OpenMv使用的是python语言编写,而这段代码的主要目的就是在openmv视野种寻找红色色块,并将其中点坐标发送会Stm32,并在OLED屏幕上显示。
( A, i1 z$ v4 B* B2 e* C% H: Z
: y9 g9 z7 o) C1 F7 O0 ^8 b主要通信函数如下
2 s7 M) s5 M' o {9 a) v/ k: M% r( I6 u7 d
- def send_data_packet(x,y,z,w):. c. e$ `4 p4 h% K. k4 m9 e. w
- temp = ustruct.pack("<bbhhhh", #格式为俩个字符俩个整型/ q* B4 B; e9 k7 C* F$ g( `
- 0x2C, #帧头15 M% r- p' w) Q
- 0x12, #帧头2; Z7 Z, |0 Z/ I2 X) b
- int(x), # up sample by 2 #数据1+ k8 K0 N( ^5 X. o
- int(y), # up sample by 2 #数据2& ]0 y I- I9 C) z. i
- int(z),2 B: q3 y7 t4 |
- int(w))( {2 U7 B& ^& x9 c0 n* m
- #0x5B)3 D5 o2 W6 W! M7 E# t. M
- uart.write(temp); #串口发送
复制代码 , V. I! U( P5 {+ z" v7 E* y+ A) u
这里使用了数据包的形式发送数据,将一帧的数据包装,并发送给Stm32,此数据包中的包头非常重要,也就是0x2C以及0x12,这两个数据便与Stm32接收中断中进行判断,以确保数据的正确性。
5 [, J6 |/ D {% ~对于数据包格式,此等的使用规划:
6 K( N, V0 }8 e+ `7 d& i' P
( X; A: V, c! i- ~* Q" `- #pack各字母对应类型
& X- G0 j5 D# G9 H - #x pad byte no value 1
; s" J0 S5 X! } Q w, T - #c char string of length 1 1( f6 \+ z5 O) I" Y) }5 Q
- #b signed char integer 1
8 e% Y" Q, E6 ? - #B unsigned char integer 1$ M* q" Y1 j+ W2 |7 y: ~0 H
- #? _Bool bool 14 w0 I, S: t8 T3 K7 K# l
- #h short integer 2
H4 c, w# F, o! l - #H unsigned short integer 2
. P X m6 \2 i5 q1 |* T - #i int integer 4
: N* U$ T4 F0 G' b& D# |2 g - #I unsigned int integer or long 46 P2 c: }; A) m0 r5 @
- #l long integer 4! O* ~- } f/ h
- #L unsigned long long 4
% g6 r* Y1 u9 f' M0 m u - #q long long long 8" Q% w" U* R8 ]9 e5 V" W+ P: V: n
- #Q unsilong long long 84 Y( G0 O- S4 @8 w9 E/ g, y1 G. a- \
- #f float float 48 h3 h4 P0 }1 ~5 ~& i( k
- #d double float 8# S7 Y1 `) ?! {; L% W8 Z! Q
- #s char[] string 16 u& |- k4 v% v/ s2 v* p* b$ m$ k; q
- #p char[] string 1
% a7 _" t. P. `1 g - #P void * long
复制代码
( _7 X4 o/ \7 e4 n对于此处我所使用的"<bbhhhh", i* S' ]' c \3 q j; S4 q
第一个第二个数据为包头,便是b,字符串类型。而后面开始为数据格式,我选择的是h也就是短整型,传输给Stm32的时候,便是两个字节的数据格式。# f+ Q! M6 q$ \$ a, t
如图,一串完整的数据便是 2C 12 36 00 80 00 2E 00 9D 00。- ~9 m8 E+ S- A! ]
! b/ J% M" G7 @4 O" J% M- z
' k) g6 R; B- v% j2 m8 j& T" O- k3 m8 a/ Z# \0 h8 x
2.第二种发送方法0 {# p8 c7 l' a$ `" e1 s1 f, q
除了以上这种以包(pack)的方式发送给stm32外,还可以使用另外的一种方法发送:* l: n# R7 n1 j% W5 A0 ]
, I1 x5 r% T- R6 O5 p- def send_data(cx,cy,w,h):
4 o; w4 I0 D5 b; C - datalist = [ 0x2C,0x12,cx,cy,w,h]3 `$ X% ^# s) j
- datalist.append(sum_checkout(datalist))
9 J: ~9 ]. u- B! W - data = bytearray(datalist)% q2 Z; X5 }, _. a2 D v5 W
- return data* T) v% Y1 O/ Q, v E
0 h) ~4 R, C9 p+ F: V- #检验数据和
* {. j/ K- E0 H! ?5 v9 j } - def sum_checkout(data_list):
; E6 K) g. u- w, u: t - data_sum = 04 I2 a) l- u' I- s [3 i% J+ w
- for temp in data_list:, N) e" |" n% e8 ]1 J* U8 E# o
- data_sum = data_sum + temp$ M7 Z- K2 r' N2 z* S: e$ \
- return data_sum
复制代码
# j) ^* ?7 _# @这段代码和之前不同的是:我将0x5B 最后一位的数据校验位改成了用sum_checkout() 这段函数求和取余的方法作为该数据的数据校验位。$ q& G$ d- s8 J0 {) g
但是使用这样不封包的方法发送,就必须加上data = bytearray(datalist),将数据转化,才可以进行通讯。
# ~: I6 K2 x4 I* u: }. X# x
( ]& u% _7 P5 Q2 R. |" H二、Stm32配置
6 S X' H; f, ]+ I0 ]Stm32的USART1配置如下:% [2 f7 p" \* Y T
2 R3 g2 b I$ C$ x
# r8 J4 a" u5 P8 _- A$ S* g3 O- u16 USART_RX_STA=0; //接收状态标记
/ D) |0 D S; \$ K5 n- r* | - 1 r- K1 q% L1 P( Z6 P* b. E! y
- void uart1_init(u32 bound){& q) u5 E; v& u( v
- //GPIO端口设置
; A6 g8 h6 p" y/ r8 U6 i - GPIO_InitTypeDef GPIO_InitStructure;, L" M' l9 A% X
- USART_InitTypeDef USART_InitStructure;
# x2 K- X# W7 F - NVIC_InitTypeDef NVIC_InitStructure;
4 C5 x( H5 `/ b/ l -
1 @4 D4 g# u7 u# s5 q - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
; g; I A4 \7 ]3 P1 u
; w* ^$ X" d, I" _5 X6 `- //USART1_TX GPIOA.9
, A7 R+ r6 J" E8 C - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
( q' _6 p4 @8 A! e2 v - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
$ i: U/ p2 b! y( \$ p+ U. N - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
% L" l9 e- K. a# S; E - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9, R. ~0 r& p" j* N* {! f/ a9 Y8 w
! T% G+ T( t7 S- //USART1_RX GPIOA.10初始化: l$ i2 Y. V; W5 |
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
7 f( \+ Y% ^, d - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
* I8 A2 e5 \. p& f( b* S7 I - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 $ {! N# ^. J$ g7 ]
; E, ~; r% _) \' T K' I- //Usart1 NVIC 配置
4 Q C2 o: v9 T& y$ _ - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;3 M, R, o, B# e% s( Z% Q3 F8 O" J# P
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
. \# H$ }% F5 h- H8 _$ e+ R - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3+ t) V/ q4 {8 N5 ]) K, ?
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
. l4 B4 f( R% b - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
4 {, t. e3 g9 U - 0 Y8 D! Q* h: s6 l! r! K
- //USART 初始化设置
# Q4 f2 d# G6 f' K7 m& e
, B. h+ a0 A6 g- USART_InitStructure.USART_BaudRate = bound;//串口波特率
6 [- Z' o& Z3 N+ F: ?: T+ I/ o* E - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式2 A5 r( z' ]8 n7 T: ~ U
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
^' c/ ] E. c - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位8 c1 V. i% Z6 l, F9 [) c5 m
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制7 M b, O# G9 Q; x- m: @: X1 P) I
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
! C0 [0 v: K+ t& B8 S9 N# G - # h3 |7 [5 N% }/ O/ G
- USART_Init(USART1, &USART_InitStructure); //初始化串口1
1 T4 Z2 I# A% Q' {, e( \8 c6 B - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断& @& S: z: G. }- A& X- T
- USART_Cmd(USART1, ENABLE); //使能串口1
& S6 s$ B: R& h8 Y' D" h# U6 N' @ - # K2 H) X; H R& C
- }
复制代码 ) h: f4 u3 l) s& e$ E
相比于平常配置并未存在较大差别,而实现通信的重要步骤是在USART1的串口接收中断中。
: {0 o3 U: T/ B4 j& g( V7 G0 Q# h0 V: D+ |. l$ V) n
- void USART1_IRQHandler(void)
' n+ Y' `7 a7 B! I - {3 Z6 I% y7 T' \
- u8 Res;* W- `& N& p }- a: g2 B
- . O0 i$ F! Q/ w( E
- static u8 Rebuf[20]={0};. M3 s, w" I6 \& B7 P
- static u8 i = 0;
, x9 X+ M8 z0 ^$ P2 U$ w4 n - 2 }2 K" M1 e" G7 ^ B
- if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET )% f- M* L( q2 i1 l: v
- {
: g3 y9 L( g( h: v( b - Rebuf[i++] = USART_ReceiveData(USART1);
/ |3 q' ?$ p9 k2 O. j+ |8 P - if(Rebuf[0] != 0x2c)* {/ Z1 Y: M2 r
- i = 0;- J/ _0 g$ f4 o
- if((i==2)&&Rebuf[1] != 0x12)+ A" f8 i" E4 r: l& B7 K
- i = 0;
7 T( K/ J$ A- K! j4 @ - if(i>=10)5 t2 E. _- S; ~) s6 S; u
- { p% {7 N+ M3 I0 l2 F2 H
- memcpy(OpenMV_Rx_BUF,Rebuf,i);
( r: L( ~4 m: v5 Y8 e- @+ N( N - i = 0;
, [7 t, L; W$ H! ~. c0 q+ ^ - } S& y3 T/ N7 _8 v
- USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除中断标志
{" S k7 a+ l5 j2 V - }' R/ ?. `4 v) | M
$ C, \* o! A/ U% ^/ A0 ^2 c- }
复制代码
( Q" F5 I1 n5 m a; q其中定义了一个OpenMV_Rx_BUF[20]的数组来接收openmv发送过来的数据,需要使用extern修饰这个变量,以便于事项后续操作。$ s: p/ l5 J0 x
同时memcpy()函数也是十分重要的一个内置函数,可以实现两个数组的拷贝任务。$ k. _3 N; b. q8 G% [; }' w8 w
$ l: g8 S- O; y3 ^ i主函数的代码如下:' a3 {/ f9 Q k# L; ^4 K. v. v( I
# v2 R( h7 ~5 a% k- int main(void)! Z. s" ~+ F; L. P( ^
- { : X; r* O" i% n; R
-
2 ^" W- Q& N) d - delay_init(); //延时函数初始化 8 y( t, h( w2 q6 G& B
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
/ U) O' @4 E* T+ ? - uart1_init(115200); //串口初始化为115200 r$ {/ T& Q$ {2 ~3 J! b
- LED_Init(); //LED端口初始化
: f& q, f; _0 i7 w5 ^- Y5 T - OLED_Init();
( ^1 t* p. k+ X) f* z -
) o' @& N1 b. ?1 c3 V$ i - while(1)6 h* ~8 A% K, C0 a$ X) B E
- {
. a7 Z! S% Q, e6 w5 p - ! D* f2 Z, s5 H; p1 r) e
- LED0=!LED0;
. u O7 l, W* E- u: N/ @3 U. L; x* Y - delay_ms(200);
; ? t+ v" C# G w( h5 V
4 m$ c2 F1 S ?2 b+ M8 ]5 E4 I- OLED_Refresh_Gram();
: @0 J% z# b$ ]( O -
4 \; c4 s/ b! d; s% ^. ]* j$ f& _3 a - OLED_ShowNumber(0,20,OpenMV_Rx_BUF[2],3,12);
2 C- q2 T1 L" E" B- n - OLED_ShowNumber(20,20,OpenMV_Rx_BUF[4],3,12);' U5 f0 ^7 j! h+ s; ~
- OLED_ShowNumber(40,20,OpenMV_Rx_BUF[6],3,12);
" X& X" n: O1 Z3 g: F0 N* ? - OLED_ShowNumber(60,20,OpenMV_Rx_BUF[8],3,12);
0 P: }9 s# K) I -
0 c2 D3 j4 Y& ?% D" F! p( o
" S% E' _9 r8 X" b* ~, u- } % E- K6 C( R& u1 t" }4 o
- }
复制代码
, ^. q, i! M) p s2 P1 @! Q前面就提到了,由于我数据包的格式设置问题,存入OpenMV_Rx_BUF[] 数组中的数据,真正有效的是第3、5、7、9位(因为选择了h类型数据格式,一个数据占2位)。
: j; I! h- g8 g9 X0 V) u W; Z
, \) i. E* p; g5 R( P! t% V, a总结7 v+ g8 E+ g2 C9 I6 ^8 x
博主我之前数据一直发送失败的原因是,博主使用了 uart.write() 这个函数,企图通过 uart.write() 这个函数实现数据发送。
* v N7 r1 O* |9 A i6 w这个函数在实现OpenMv和PC端的通信上没有问题,可以将数据打在串口调试助手上,但在其与Stm32的通信问题上就存在问题。
# @% w( C' Z/ E# V5 S若要使用 uart.write() 实现与stm32的通信,便要使用bytearray()函数将其转化,才可以进行通信。! t Q1 \$ d- R
) |+ C: V0 i) o; t" B2 H
- FH = bytearray([0x2C,0x12])) Y0 j$ ^2 ]& P
- uart.write(FH)
复制代码 5 G* u" P$ t* t' {" ?2 y i
" p' i, [4 H9 t l
& l( Z) b/ @! e9 K7 ]) G. d8 i
|