你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32 IAP固件升级(三)

[复制链接]
STMCU小助手 发布时间:2022-2-4 22:00
一、前言
前面介绍了IAP需要的一些基础知识,区域怎么划分,还有启动跳转过程等。当然如果需要实现IAP,通讯接口的驱动是不可或缺的。例如可以使用串口(USART)、以太网(ETH)、USB等通讯接口来进行数据收发。本实验就不使用复杂的通讯接口了,使用比较常用又比较简单的串口。当然将串口用好也不是那么简单的,使用串口中断接收数据效率一般比较低。所以为了提高效率,这里使用DMA(直接存储器存取)+USART(串口)接收数据。
注意:下面涉及到串口的基础配置这些就不过多解释了,直接上源码了。如果不会配置的,可以去看看其他大神写的博客,或者去看看一些教学视频。
一、串口配置
STM32F103有三个串口(USART1,USART2,USART3)。在实验中,因为USART1主要用来程序的调试,所以使用USART2来做IAP的通讯接口。
1、USART2的配置程序如下:a.初始化GPIO
+ V3 Y0 t6 S- V% G( M
  1. void USART2_GPIO_Config(void)
    - V* v1 p. d- S' q6 x5 e$ @
  2. {: k; S3 g9 W1 }: Q6 Y% j' P
  3.         GPIO_InitTypeDef GPIO_InitStructure;
    : D8 F' u+ R  |4 B
  4. ( {9 {- l3 e  G$ `2 Q; Z# l' _9 @
  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);4 v- R  q- H! X8 Y
  6.         /* 初始化 USART2_TX  ----- PA2 */
    9 e3 q3 f9 e% y
  7.         GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2;
    7 D1 \3 f* s& ]4 x- A
  8.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    9 u  o- _8 X) t
  9.         GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;       
    , @! d) {% w& d, S' T
  10.         GPIO_Init(GPIOA, &GPIO_InitStructure);7 }% C8 N+ Z' r* S* c
  11. : b, W( l) W* r. }8 ^% K
  12.         /* 初始化 USART2_RX  ----- PA3 *// F1 A1 }0 I8 t8 W: L& u/ e
  13.         GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_3;  D0 v* k7 y4 Q1 u9 J5 [& H% H2 c7 s
  14.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;( ]) |2 G2 L& \- e& c  @
  15.         GPIO_Init(GPIOA, &GPIO_InitStructure);  
    1 }" k/ I( ~: D3 s
  16. }
    & O( j2 c% i1 x7 Y6 j3 u( x
复制代码

. q0 h. E9 }. y% \) g9 h  M* d" s# g5 n/ ^' x+ ?* P! [
b.初始化串口
在STM32的串口里支持同步模式和异步模式,我门通常使用的是异步模式;详细的是使用说明参考《STM32中文参考手册》。
9 z, }  v0 `7 c' O同步模式虽然少用,但是它也是有优点的;在使用多一条同步时钟线的情况下,数据传输会更可靠,更可控。* `; R  l3 D9 d1 r5 m9 Z+ A
下面的配置禁止了同步模式,使用的是异步模式(通常使用异步模式 USART_ClockInitStructure 数据不设置也是可以的)。
  1. void USART2_Config(u32 baudrate)9 o5 D/ \# m0 _# z- H
  2. {  o9 G5 v) r, X" T+ `, p
  3.         USART_InitTypeDef USART_InitStructure;7 m  E+ }& `& k7 `; N/ i
  4.         USART_ClockInitTypeDef  USART_ClockInitStructure;4 a( |# d+ |8 T0 c% t6 w9 d
  5.         /* 初始化时钟 */9 v0 \  d; c+ F  W" ^! Y( H- D
  6.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); . Y0 k! e6 u2 F3 a& {* L* f8 _% a+ i
  7. /************************使用异步模式,这个不用配置也可以*****************************/- _+ V' h& W, [% V7 B7 N7 M. f
  8.         /* 关闭同步时钟(即关闭串口的同步模式; 需要使用同步模式,则使能这个) */7 q8 N  R; b! a% U: C
  9.         USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
    0 w+ X: D- \% q/ Q
  10.         /* 配置时钟稳态值为低电平(即空闲时,时钟线的电平为低电平) */
    + h9 ~. T) S; i  C- O" s
  11.         USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;* P9 C+ ?$ w$ h' \1 X3 e
  12.         /* 配置时钟相位,在时钟的第二个边沿进行数据捕获 */
    8 G. ^/ Q0 \: F5 r- r( C7 C
  13.         USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;
    5 o1 j' E, `6 y" e
  14.         /* 最后一个数据位不从时钟线上输出 */& b9 d- r- r' ]2 J# a
  15.         USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
    : v' q$ E, D! ]& E9 H$ f9 k
  16.         /* 初始化 USART2 的同步参数,这里没有使用同步时钟 */% @8 k  h- R' L$ Q
  17.         USART_ClockInit(USART2, &USART_ClockInitStructure);  ^; j# [& l7 P
  18. /************************使用异步模式,这个不用配置也可以*****************************/
    6 F6 A7 _% S, l3 C: V% `, w8 y
  19. 2 r3 x, B2 g; g5 m4 X; a6 F
  20.         /* 设置波特率 */
      [* G# P* }( p' x8 @7 P% E0 N
  21.         USART_InitStructure.USART_BaudRate = baudrate;
    1 t1 n- U4 H0 a3 A$ Y' v5 k8 C
  22.         /* 设置数据位长度为 8 */  t' t# o* P+ J$ f9 Q0 ~
  23.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    ! G) f9 G6 F" d! @- D8 {
  24.         /* 设置停止位是1 */
    9 z- K' [! r, U/ U3 C
  25.         USART_InitStructure.USART_StopBits = USART_StopBits_1;$ H' H; l% g2 \
  26.         /* 不适用奇偶校验 */. p" |! \* M7 ]5 r3 Y- }
  27.         USART_InitStructure.USART_Parity = USART_Parity_No ;  l- ?$ ?; z5 h) I) T" |% R) x
  28.         /* 关闭硬件流控 *// k+ M' a. p. F8 ?. T; N
  29.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;( U- U, z& E0 w7 [) X& O2 T
  30.         /* 使能 USART2 接收和接收 */8 V+ e5 q) n. i
  31.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    $ H, \7 @) m+ |1 C! B& j# c) s
  32.         /* 初始化 USART2 的异步参数,在STM32中通常使用这种传输方式 */6 V) J. ~( K# V1 d+ s4 g0 E
  33.         USART_Init(USART2, &USART_InitStructure);
    8 `) z( e! Z* a! M4 u8 q& Y. e) a7 }
  34. }
    $ i9 ?$ T. w0 S& t
复制代码
; v: C. c; D3 b  }1 r) e
, h9 K: R" o/ o, g4 D8 A; x. n
STM32F103 DMA
: P! U% S/ G9 Q8 M1、STM32F103 DMA简单介绍
在STM32里面有两个DMA控制器(DMA1和DMA2)。两个DMA控制器有一共有12个通道(DMA1 7个通道,DMA2 5个通道)。每个DMA通道对应的外设备如下图:
6 f1 m$ y1 r* a5 B; {$ j  R7 ^对于详细的DMA介绍参考《STM32中文参考手册》,这里就不过多介绍了。
2 J2 u) j+ o" l: h7 G& S; y
1627979-20200217190858504-1233226782.png

" d, h5 n; h# Q8 p1 q9 C
1627979-20200217190911146-1833204792.png
% Q. t9 o$ j0 O0 G$ w% ]7 w: L; H

- K6 E  _- d3 r' s" ~
2、DMA 使用方法
DMA的使用并不复杂,通道配置过程如下:
  • 设置外设起始地址
  • 设置内存的起始地址
  • 设置数据传输的长度(要传输多长的数据)
  • 设置数据位宽
  • 设置数据的传输方向
  • 设置传输模式
  • 设置通道的优先级
  • 使能DMA通道' E3 j2 D# c2 Y8 L$ S* F% v
3、配置 USART2 的 DMA
从上面的两张外设对应DMA通道的图片知到: USART2_RX 对应DMA1的通道6,USART2_TX 对应DMA1的通道7。
a. 配置USART2_RX的DMA
这里只写简单的配置过程。DMA的更详细的使用说明,请参考《STM32中文参考手册》。配置程序如下:
  1. void USART2_DMA_RX_Config(u8* Buffer, s32 NumData)
    3 a* z/ J% p. H  F4 e2 y) A6 Q3 L
  2. {
    % ?) J4 s+ m9 ^- q8 O4 |& t
  3.         DMA_InitTypeDef  DMA_InitStructure;
    - a3 b' W! [) R( g1 V# J3 E
  4.         /* 使能时钟 */
    ( a0 @/ R0 u+ S! v! I9 X
  5.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);             ' b- ]2 N4 s- L/ o5 w2 W
  6.         /* 将 DMA1_Channel6 设置为缺省 */
    $ M  V* h8 S% A" m4 }# V
  7.         DMA_DeInit(DMA1_Channel6);
    1 L9 G$ k$ q2 }8 G* f
  8.         /* 配置外设的地址为串口的就收寄存器的地址 */5 `* V, P) {: Y4 v/ e; D' l& d' G0 A
  9.         DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)(&(USART2->DR));       
    1 r- L3 T+ F/ S" b: W) o, L
  10.         /* 设置内存的地址 */
    / |( Q( q! s( ?; p8 r
  11.         DMA_InitStructure.DMA_MemoryBaseAddr      = (uint32_t)(Buffer);
    & D& \" v# \' t% R- X
  12.         /* 设置传输方向为 外设到内存 */9 @+ W, y9 @1 X2 y+ N5 o
  13.         DMA_InitStructure.DMA_DIR                 = DMA_DIR_PeripheralSRC;
    4 |5 O* H8 {3 P  X; z/ j+ X# u
  14.         /* 设置缓冲区Buffer的大小 */- g0 w& U; w2 m; A0 h# }
  15.         DMA_InitStructure.DMA_BufferSize          = NumData;
    2 U' Z4 h4 l/ H  U7 \: b: j! e
  16.         /* 禁止外设的的地址自增 */
    2 h  o3 z2 E! f1 v0 y
  17.         DMA_InitStructure.DMA_PeripheralInc       = DMA_PeripheralInc_Disable;% L. q4 H: J# B8 l, G* J- D
  18.         /* 使能内存地址自增 */& B# b1 @4 D2 Y' `
  19.         DMA_InitStructure.DMA_MemoryInc           = DMA_MemoryInc_Enable;7 a* w- i/ r$ o! z" ^+ H
  20.         /* 设置外设每次传输数据的大小为字节(Byte) */+ X* v* v; h' e# w
  21.         DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte;
    : [2 `3 R; u: M+ a2 l! ~. T5 _
  22.         /* 设置内存每次传输数据的大小为字节(Byte) */
    ) |0 ~  Z! h( H
  23.         DMA_InitStructure.DMA_MemoryDataSize      = DMA_MemoryDataSize_Byte;
    2 p' ~+ _+ n% Q; `% |, b
  24.         /* 将设置DMA传输的模式为循环传输 */
    1 Y2 G. C5 b1 F' H, ]
  25.         DMA_InitStructure.DMA_Mode                = DMA_Mode_Circular;: V" Y' E6 j8 z& E
  26.         /* 设置当前DMA通道的优先级为高优先级 */
    2 l$ [& d- M9 g0 y
  27.         DMA_InitStructure.DMA_Priority            = DMA_Priority_High;
    * t5 N+ A5 L% m
  28.         /* 禁止内存到内存传输 *// }5 K5 x( e6 S0 ~1 D( _
  29.         DMA_InitStructure.DMA_M2M                 = DMA_M2M_Disable;3 w5 I4 q) ?0 f& l
  30.        
    2 F& a- N' Y3 [" U2 V4 O- C
  31.         /* 初始化DMA1通道6 */' N3 K5 I* Q: U$ N/ U1 ^2 z
  32.         DMA_Init(DMA1_Channel6, &DMA_InitStructure);
    ' R: W* c# E+ h
  33. }
    " k/ [4 k1 s% S, M
复制代码
b. 配置USART2_TX的DMA
  1. void USART2_DMA_TX_Config(u8* Buffer, s32 NumData)
    , R0 K# G. [# A- h
  2. {
    & |; @% s8 N: s0 w# `1 e, R, m
  3.         DMA_InitTypeDef  DMA_InitStructure;) T  D$ P5 e) C
  4.         /* 使能时钟 */% _  U+ ~& E/ _0 ~7 K5 h' V
  5.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);            
    ( I! \% ~# s0 B2 i( e! s* n& s  \) E
  6.         /* 将 DMA1_Channel7 设置为缺省 */9 F* ~+ U3 @( Z; p- o% V9 [& M2 p& \
  7.         DMA_DeInit(DMA1_Channel7);
    : m/ F9 p# i- S, s( @
  8.         /* 配置外设的地址为串口的发送数据寄存器的地址 */2 p' T9 n7 b/ B1 i. j
  9.         DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)(&(USART2->DR));        ) X! w; q1 E. x# k
  10.         /* 设置内存的地址 */
      |: E9 o0 G! {* Q% P4 g
  11.         DMA_InitStructure.DMA_MemoryBaseAddr      = (uint32_t)(Buffer);
    7 |! d5 o& W5 l% {/ R4 W( u
  12.         /* 设置传输方向为 内存到外设 */
    - C( R* J& g$ J& z* q  o4 b' V
  13.         DMA_InitStructure.DMA_DIR                 = DMA_DIR_PeripheralDST;: p! c+ R7 [' P
  14.         /* 设置缓冲区Buffer的大小 */
    & {8 }# o: ?' `& z
  15.         DMA_InitStructure.DMA_BufferSize          = NumData;
    . m5 `  \; R  T2 j/ V2 W) o
  16.         /* 禁止外设的的地址自增 */2 Z+ p$ K* V$ }3 L( a+ c2 A
  17.         DMA_InitStructure.DMA_PeripheralInc       = DMA_PeripheralInc_Disable;' ~" ~8 s) C; m5 b8 r# L' F
  18.         /* 使能内存地址自增 */2 m% ~7 Y; T, s& `
  19.         DMA_InitStructure.DMA_MemoryInc           = DMA_MemoryInc_Enable;
    8 n; S* c. }0 N& x: h8 u- e/ t& }
  20.         /* 设置外设每次传输数据的大小为字节(Byte) */
    2 m: E: d1 O  s* X: g
  21.         DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte;! m) F2 J+ T0 V# U- }: D1 Y
  22.         /* 设置内存每次传输数据的大小为字节(Byte) */) d8 u$ l9 H. M$ y$ A. }0 }
  23.         DMA_InitStructure.DMA_MemoryDataSize      = DMA_MemoryDataSize_Byte;/ g% f0 k& n; v$ b4 T  [
  24.         /* 将设置DMA传输的模式为正常传输 */7 P$ E3 K; j1 j0 I& S; s5 ^( Q2 {
  25.         DMA_InitStructure.DMA_Mode                = DMA_Mode_Normal;
    " l; v* R! Z" Y2 j4 R; R
  26.         /* 设置当前DMA通道的优先级为高优先级 */
    + J" x/ L  _! p$ t+ l2 K
  27.         DMA_InitStructure.DMA_Priority            = DMA_Priority_High;# [# S% a: {+ S" N7 s* K" D
  28.         /* 禁止内存到内存传输 */; C7 J7 @" L; k5 |2 U
  29.         DMA_InitStructure.DMA_M2M                 = DMA_M2M_Disable;8 f3 h! r2 t5 l8 T( Y6 m
  30.         /* 初始化DMA1通道7 */
    ( w, u, M$ l. a9 f7 e1 x. C
  31.         DMA_Init(DMA1_Channel7, &DMA_InitStructure);. Z9 c  F. S9 p' q* {4 O' U  I
  32. }
复制代码

! X) Z+ A( n2 a8 `& V: x/ }# l2 Q" [  Z4 M
三、初始化、使能串口和DMA
: j8 e! H4 K% ?. v" M! @1、使能串口和DMA
使能串口和DMA的函数如下:
  1. void USART2_Enable(void)
    8 B( S1 x2 |* c# D1 u! C. V
  2. {) g1 r" Q3 R6 q- b0 b& K; U
  3.         /* 使能串口2 */8 a! ]2 E6 ?- p& t$ u7 N: ~4 W
  4.         USART_Cmd(USART2, ENABLE);
    3 e! m5 j3 W) W7 c$ y
  5.         /* 使能串口2的DMA发送 */
    - U& ~# f2 @" b$ g1 C
  6.         USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
    # z0 p  J7 q/ D9 h- ^. B( F
  7.         /* 使能串口2的DMA接收 */
    8 O* _" Y* J# B/ D# d( K
  8.         USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);) J! _- S0 z  R8 f- T1 d, T
  9.         /* 使能DMA1通道7 */3 V4 V  x5 q) a7 ?
  10.         DMA_Cmd(DMA1_Channel7, ENABLE);       
    : c- w+ F. B' D
  11.         /* 使能DMA1通道6 */
    5 W$ z4 ~+ _+ c0 B& m
  12.         DMA_Cmd(DMA1_Channel6, ENABLE);       
    $ ^/ O  o2 ~, Z) c* m) D  Z# a3 A
  13. }
    4 w1 N4 |8 B  a# N, \
复制代码

. N6 i; h9 p: Q* t$ c$ L  b. _1 E2 i9 [) v9 x/ ]4 j" |; k
2、初始化函数
USART2_DMA_TxBuff和USART2_DMA_RxBuff是一个全局数组,用于DMA发送或者接收数据;以后需要发送数据和接收数据就操作这两个数组(buffer)就可以了
  1. void USART2_Init(u32 baudrate)
    . \  J5 X/ ~8 ?
  2. {
    ; l8 p( `, i6 d5 V2 \. v. {
  3.         /* 初始化USART2的GPIO */
      Y( Q2 ]; q7 N/ Z2 K8 m
  4.         USART2_GPIO_Config();
    % H& I. o% }- `( o
  5.         /* 设置串口 */
    0 w0 `2 {( J3 R" ?' m
  6.         USART2_Config(baudrate);
    0 ]  M" F6 J4 `. M
  7.        
    4 z1 W. E5 X; y$ F* G4 \, L
  8.         /* 设置USART2的发送DMA */
    # X2 Z# h1 E5 @2 j8 j6 T
  9.         USART2_DMA_TX_Config(USART2_DMA_TxBuff, 0);
    8 C! Y8 s* h6 s1 d$ m
  10.         /* 设置USART2的接收DMA */6 s8 e' Y* S- A5 X* ~1 P. i2 l
  11.         USART2_DMA_RX_Config(USART2_DMA_RxBuff, USART2_DMA_RXBUFFLEN);9 H  b! Q8 `0 M
  12.         /* 使能 */' {( ^8 ^% Y. Z8 `% M0 }
  13.         USART2_Enable();
    4 k* n4 V: A! L9 d
  14. }: n3 h" Q! s6 P# }, a8 l+ ?0 Y+ t: m  n
复制代码
8 }% b$ E3 J# O1 A& ]
*** 配置到这里,基本就可以使用DMA接收和发送数据了;使用DMA发送数据是比较简单的,但是使用DMA接收数据,需要费一点点功夫。 ***
& x+ d3 r. a5 f+ S2 a6 y
四、DMA接收串口数据
- ^5 N+ w& J, e1、处理接收到的数据
由于使用了DMA接收数据,所以不需要再操作 USART2 的 DR 寄存器了(数据寄存器);硬件会直接将数据copy到USART2_DMA_RxBuff数组里面。又因为在接收的DMA设置函数里将DMA的模式配置成了循环模式,所以USART2_DMA_RxBuff的本质是一个环形缓冲区。$ T4 j5 B  x8 L2 u
接下来就先上程序再慢慢分析吧。接收处理的代码如下:
  1. /* 说明一下。函数里的 USART2_Service 是一个全局的函数指针,用于处理接收到的数据的 (需要怎么处理,就看业务的需求是什么了) */' y9 j- g6 `2 R% v/ Q8 E8 t# ^
  2. /* USART2_Service 函数指针定义如下: void (*USART2_Service)(u8 *buff, u16 len); */; o9 L  {, ^7 Q( i
  3. /* 用户需要自己初始化这个函数指针 */+ a& U! i% y4 m1 K( ]' X
  4. /* 可以将 USART2_Service 设置为一个回调函数的样式;在这里就使用全局的函数指针了 */8 P0 n, K  M" O! K8 J
  5. void USART2_DMA_Rx(void)" B2 k1 }% m! [
  6. {         # I- M+ W4 v5 T1 J4 [0 L0 h
  7.         u32 pos;) T2 J5 t1 K$ x4 U
  8.         /* USART2_RX_Pos 指向环形缓冲区数据的头部 */
    4 a' l$ E; U; k  x0 |
  9.         static volatile u16   USART2_RX_Pos;
    , t( c' P3 t1 P5 M, ^. V- I0 j
  10.         + A  }# A7 H1 X
  11.         /* DMA1_Channel6 -> CNDTR 接收到的数据查长度 */
    + w  G* v7 v3 u+ H: G
  12.         /* 算出pos当前的指向,缓冲区数据的尾部 */
    8 m; ]$ \- e9 ^. ^+ v" v2 Y$ {4 U
  13.         pos = USART2_DMA_RXBUFFLEN - (DMA1_Channel6 -> CNDTR);
    8 F3 t) _# G9 J1 d* U
  14.         /* 当前的pos上一次的pos大 */
    & F5 u# B  \' j3 l6 d$ e+ N
  15.         if(pos > USART2_RX_Pos)
    4 Q( V( a3 M, c1 |# _
  16.         {, o; [2 P$ H; D
  17.                 /* 算个出buff的起始地址,算出接收到数据的长度 */
    2 s" _  W5 i3 }" g6 d7 m4 P
  18.                 USART2_Service(USART2_DMA_RxBuff + USART2_RX_Pos, pos - USART2_RX_Pos);
    " _  _8 R3 ^: w/ H. h! g* I
  19.                 /* 改变buffer的起始指向 */* ]( r! y9 q: m2 f0 l! L
  20.                 USART2_RX_Pos = pos;- q6 r* K: K% d+ d0 E; V0 w
  21.         }0 Z+ ^" H# w# m: J& z4 n$ i
  22.         /* 如果上一次的pos比当前的pos大,说明有部分数据在前面 */( Z2 x7 E8 R3 ^! D* W* F
  23.         else if(pos < USART2_RX_Pos)- W% M# t6 \3 S& ^
  24.         {. }) i% y; F; ~4 m
  25.                 /* 计算buffer的起始地址,先将后面部分的数据放入到内存管理 */* [1 F! G  i: \; h; f- T
  26.                 USART2_Service(USART2_DMA_RxBuff + USART2_RX_Pos, USART2_DMA_RXBUFFLEN - USART2_RX_Pos);0 B0 l! T6 I5 H9 p
  27.                 if(pos != 0)6 Q$ T4 \/ t! p2 A& t9 o' F
  28.                 {- W, T- ^5 ~9 B: H0 _  O# J
  29.                         /* 如果头部还有数据,也将头部前面的数据放入内存管理 */
    - g1 A$ s: e/ `6 X  d' F
  30.                         USART2_Service(USART2_DMA_RxBuff, pos);. j8 e( T# }# k6 O
  31.                 }
    0 T6 ]( x) B2 C) g% R) a+ L  |
  32.                 /* 改变头部的起始指向 */
    + n5 t. c7 @8 Z; I* r6 o% i
  33.                 USART2_RX_Pos = pos;
    % M" }7 V' N) s
  34.         }
      l2 }, A9 Y- ]+ b4 y6 R, {
  35.         else
    - p- w# q& j  `" [
  36.         {;}
    0 X% f- f' w% e" x: w1 Q& }  H
  37. }' m' B% k/ H: w) J7 [9 P8 N
复制代码

3 j6 F6 R( Y" ~" r3 W+ Y
*** 下面具体分析 USART2_DMA_Rx函数的原理 ***

: I: s" ]% z. h7 ~) V2、代码块1的分析% n! `( e# C  a3 w0 J
  1.     /* 当前的pos上一次的pos大 */. u9 b! n$ s$ u3 _, w9 B
  2.     if(pos > USART2_RX_Pos)$ c* [6 c" x- C& ?
  3.     {
    5 P, q; D& k7 t7 f# n' p9 @. ?
  4.         /* 算个出buff的起始地址,算出接收到数据的长度 */! u6 y" O( }: k( Y: j# t
  5.         USART2_Service(USART2_DMA_RxBuff + USART2_RX_Pos, pos - USART2_RX_Pos);
    2 i' m# }( `5 N, q2 r
  6.         /* 改变buffer的起始指向 */
    ( i4 Y7 \) u- ^/ u/ H
  7.         USART2_RX_Pos = pos;0 Z4 G' f3 u9 ~) V
  8.     }2 T8 A8 j5 m0 u/ }  e
复制代码

  k9 K6 g( L6 [+ F# e
这部分代码是处理接收到的数据是下面的情况(也就是需要处理绿色区的数据)
4 b4 f! k9 C' C6 V1 D- i9 ]+ C$ U
1627979-20200218234327546-246178205.png
! ]$ `% Y5 W' _* k
3、代码块2的分析
+ U9 m+ P2 U/ e/ ?+ F) _8 x
  1.     /* 如果上一次的pos比当前的pos大,说明有部分数据在前面 */. F/ N  b( L6 S
  2.     else if(pos < USART2_RX_Pos)/ S3 O' z' Q) h5 R: d
  3.     {
    ! r; l: ~( |7 Q+ K; j
  4.         /* 计算buffer的起始地址,先将后面部分的数据放入到内存管理 */3 G0 S# Z! `8 n5 ~1 S3 y3 S
  5.         USART2_Service(USART2_DMA_RxBuff + USART2_RX_Pos, USART2_DMA_RXBUFFLEN - USART2_RX_Pos);0 e* {' T* u  g5 D- x+ D% b
  6.         if(pos != 0)
    7 {$ w+ }" D9 S' t# r
  7.         {
    ) ^, n# ^+ F0 v  p/ ~
  8.             /* 如果头部还有数据,也将头部前面的数据放入内存管理 */7 n5 [: l1 [. y& H- t$ Z
  9.             USART2_Service(USART2_DMA_RxBuff, pos);( F) \: I* v2 a. \! T8 H
  10.         }
    9 |2 r* c5 ^/ `3 n
  11.         /* 改变头部的起始指向 */7 j. t1 L% x7 j/ H9 ]' ~
  12.         USART2_RX_Pos = pos;
    . q3 d; [0 V& B6 N+ W
  13.     }
    ( F4 R+ o, V, {2 {
复制代码

9 M# N& A, Q- ~! w/ S
这部分代码是处理接收到的数据是下面的情况(也就是需要处理绿色区的数据)
& I4 }( B' Q+ B- v2 F0 y5 a& ~$ s

/ i: d* X$ {3 V& A  U五、总结
总结一下使用方法:
  • 调用 USART2_Init 函数初始化串口和DMA
  • 初始化 USART2_Service 函数指针;这个函数的参数有两个,一个是数据的指针,一个是接收到数据的长度
  • 通过大循环不断调用USART2_DMA_Rx函数处理接收到的数据;3 @) }) t% g3 |& J
实验现象:$ W7 u  m& M: K2 D
实验是在application环境下的,通过给USART2发送数据,USART2接收到的数据后再从USART1里打印出来。实验现象如下图:  {9 @2 o  ?7 e' S. B
左边是给USART2发送数据,右边是将接收到的数据通过USART1打印出来。
" S' m# U; Z- Z+ A
1627979-20200305224532936-1354975181.png
+ H* r, c% o4 Y5 A
***注意: ***
使用这个方法接收串口的数据有优点也有缺点
  • 优点是:效率比较高,使用比较方便
  • 缺点是:实时性的要求比较高。如果缓冲区比较小,实时性又比较低;当有大量数据连续传来时,很容易就会造成数据的覆盖。在单线程里面使用时,尽量少使用大的延时函数。
    % [, R- L+ A1 Q' `! Y7 p' d

5 |6 M/ B; @2 k+ g* ^* P
- m" E# v0 [; \( k9 Z5 W9 l
1627979-20200218235143257-1496577085.png
赞 收藏 评论0 发布时间:2022-2-4 22:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版