一、开发板平台简介:
& s8 b9 P; u6 B }1、开发板资源简介8 k1 h5 E& q8 ] Z. u" o
(1)开发板主芯片型号:STM32L431RCT6(2)开发板主芯片封装:LQFP-64_10x10x05P
8 b F4 H6 ^/ ?/ w(3)开发板主芯片内核:ARM® Cortex®-M4
" T1 x' d8 W% F/ Z6 a; J(4)开发板主芯片主频:80MHz9 g% h' y6 m% F {* Q3 ^$ x
(5)开发板主芯片Flash大小:256KB
9 v2 E) \( j# y! n1 e(6)开发板主芯片RAM大小:64KB: _; R$ h) ?4 ]
/ R8 ~- ^, N, v, ?' d! k6 T
8 v$ C/ Y2 @+ H$ `: p* ~+ P5 T
8 v' }1 W7 ]; x3 `
- V% g3 y K0 L, B
- p* _8 L2 d" p7 O$ ]$ Q% C" `/ ?" W, |0 f5 Z
2、LED灯资源
, Z& k+ J0 ^0 V' q$ v2 ](1) STM32L431RCT6开发板共5个LED灯资源,其中一个红色LED为系统指示灯,指示开发板供电系统是否正常,如供电系统正常,红色LED为上电常亮状态,硬件原理图如下图所示:2 R0 n! w7 I5 y) w4 g
) ? U3 ]: E: t9 I- ~/ `
0 ?& R t. d- ~0 P
0 R N' l H3 D+ y( p8 R$ |(2)其他四个LED灯为黄绿色可控LED,高电平点亮、低电平熄灭,计划用LED常亮验证看门狗的作用,硬件原理图如下图所示:
6 F# x r. A. v A# n& N( `
' A! s# s( t" I$ m: B
# Z d' t: c1 P% O& B
# Y( t8 e1 e3 b/ {- `# o! e$ J4 O, M8 Q
5 v% P& G) {4 }' s1 z. M3、串口DMA收发工作原理
6 T" y; H4 i4 o7 A 串口全称为串行通讯接口,即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平,串口需要RXD、TXD、GND三根线进行通信。0 H/ p1 l J, g: o* r0 Q
- T- }' N4 y8 s6 V& v# c; Y
(1)最常用的RS485收发换向方法是程序换向,即由MCU的一个I/O端口控制RS485收发器件的收发使能引脚,在平时使RS485收发器件处于接收状态' F' W$ @; ?2 i$ K
4 B1 S6 R) C! Z/ {7 f5 t/ f) c (2)我们选用的STM32L431RCT6开发板串口2作为RS485通信的串口,PA1作为RS485串口收发的使能控制引脚。6 o) A+ \) M0 y
4 @) @; w, v3 t4 X0 h
(3)RS485端口使用时,需要使用USB转485模块和开发板进行通信,使用方法为USB转485模块的A接开发板的A,USB转485模块的B接开发的B,GND可不接。7 W9 \% @4 y2 h' r2 ^! w3 ^
7 _0 Y; Y3 d O1 T
RS485串口,硬件原理图如下所示:
4 j+ n) J* l$ Y, e1 u* z+ S2 }5 Y0 f
0 h! M7 _" k: S$ q1 l6 M3 r7 d
6 g! S* W' p; s7 P
9 P7 @9 a- M6 ^) G* B# W6 E& C
, h6 h, q0 S" `( g% T& `' [! s) S/ l+ X7 X+ _+ c/ B$ H* K
DMA(Direct Memory Access) :直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候, CPU 可以干其他的事情,好像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH。
3 g7 l" I) v' n5 @! Y6 G. s
/ V0 {' U5 F c& p: bDMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 个通道, DMA2 有 5 个通道,这里的通道可以理解为传输数据的一种管道。要注意的是 DMA2 只存在于大容量的单片机中。8 C& s3 Q6 k2 w% P3 T* M0 f- \
6 `; W6 h. g1 T
& D+ \' x% v+ t2 X# T) d! n* _% L" y, I X. c
二、串口DMA收发实验过程9 k& ]+ m9 q, h3 ]: y
1、新建STM32CubeMX基础工程$ ]+ r* i% B8 l- ?: P4 E
(1)打开STM32CubeMX,点击“File”-->"New Project"
3 L2 W/ O9 b, C7 j" }4 A0 ]
! }0 A& P# H% L8 |
5 R( f9 o* o$ n0 T; ~
# I3 W- @ r; t6 D! G1 {* p5 Z2 E(2)等待打开主芯片选项界面(大约1分钟时间)。
! ^ e! a+ R/ J* t. X
7 v7 n. Y% F C: L
5 w3 T9 t+ [% H9 e
6 O2 E2 o# V' q! J! Y(3)昨天搜索框中输入(或选择)所需的主芯片型号(因为我们用的是STM32L431RCT6开发板,所以此处选择STM32L431RC),然后在右下角选择STM32L431RCTx(因为开发板主芯片是STM32L431RCT6),左键双击即可打开新建的项目。7 v/ d& j8 [! F" W/ E
5 [$ q5 o0 s" O6 C9 e9 `- b) c
" s$ f" T( a$ S/ | F
) ]* x8 L/ F# v- l @. k& H- o
, X/ v$ b. X/ m: y(4)选择时钟源。1 _; ]; Q# o! R- e# y: v& m
因为开发板上有8M外部时钟,硬件原理图如下所示,所以此处选择使用外部高速时钟(HSE)。4 d6 a2 a7 Q. S
0 o9 X1 a& Y7 J: U0 Z
/ |" w9 E8 k b: Z- t/ {8 j- z
3 b2 c, F, h. J% }3 z 因为我们没有用到外部低速时钟(LSE),此处不做处理,如下图所示。
/ z+ {; f+ V0 a. D1 J/ `' L3 D. d7 ^5 H1 w
1 f+ A7 {! Y! h' D, F+ j
& w3 `6 ^6 E0 J2、配置GPIO控制LED
0 Z0 b/ u, V1 n% `, `3 Q(1)查开发板原理图得,LED1、LED2、LED3、LED4的控制引脚分别为:: e3 V1 _4 h. o d
LED1——PC0
! b* \: c! q9 @" ~+ c$ W' mLED2——PC1; k, o0 U+ j# m# ^( w2 \
LED3——PC2+ _7 C0 P5 x) E: O4 _- F( D2 |
LED4——PC3$ V- `( [( w8 X$ O9 A6 L4 u8 V
2 C2 E/ @" f- _! L7 `: ^) D(2)配置LED的控制引脚为输出,输出频率、输出方式默认即可。
; ?" d. G6 {8 G$ o- _+ _9 o- K/ J8 q鼠标左键点击PC0,选择“GPIO_Output”,表示设置该引脚为输出模式。! c; U5 \8 j% B- @9 i8 T
鼠标左键点击PC1,选择“GPIO_Output”,表示设置该引脚为输出模式。
2 ^# Q/ m5 D8 Y/ t: o( z, K鼠标左键点击PC2,选择“GPIO_Output”,表示设置该引脚为输出模式。
8 P2 U% K# g4 h: S鼠标左键点击PC3,选择“GPIO_Output”,表示设置该引脚为输出模式。% K$ B* {; P3 ^1 L
. \! j$ Y& ?4 @3 ~9 R; l, _! v b( L2 v
# a9 v& w* D1 b6 [: r1 z( k1 e
# N* M* S+ w: R8 X
0 N6 P( q, o% \ m2 t4 @" w: V
7 ]6 G3 u% A0 `, A6 m% Y( D(3)也根据自己的需求配置GPIO的参数,如输出方式、输出频率、上拉下拉等。因为GPIO控制LED的要求比较低,此处采用默认参数即可,不用修改。
* k6 K+ e8 ?; q; z, I+ R: t8 D$ p; L$ @6 Q
9 n$ t# A2 D" x* i7 X
- W' q h4 h/ V( x& v6 G3、配置PA2、PA3为串口
; x K$ G' l# u' {' D; z: l, t(1)查原理图得知,串口2使用STM32L431RCT6引脚为PA2-USART2_TX,PA3-USART2_RX,引脚设置如下:: n: t' z0 x3 N
9 |; n' V! {" k3 r
' L+ Q+ M* B2 m8 x* C, I/ O
* k5 d9 ]% h/ G& A
串口参数默认即可:波特率115200-N-8-1,并使能收发控制引脚。4 K8 w' y2 |2 \) l4 x- q6 I7 [+ P( V
2 y |( V+ O4 ~3 z
. B* x+ s' b/ {9 R
$ s. n) p" M4 \
(2)设置NVIC settings 使能接收中断。0 Y& d% _$ t3 n( T' v. c8 a+ B1 [
" m% k4 n- @' N
/ V8 e/ ]- N, ~/ \/ a/ v, B+ Z3 W$ G9 \( f
3、串口DMA设置 + X. B6 J: F3 D, x1 F
1 j: ~) p4 j! a0 ~
; A6 a' ^& f1 @) q3 I7 G
. `( T3 ?5 l+ x. c2 v
(1)根据DMA1通道预览可以得出,我们用的串口2的TX、RX分别为通道6、通道7:
% w3 R8 i5 R! h8 |1 V6 S2 o$ e0 e点击DMASettings 点击 Add 添加通道5 C1 D% ?* T; w: W8 D
选择USART_RX USART_TX 传输速率设置为中速
0 _$ p) r: f. SDMA传输模式为正常模式,即发送一次就结束。5 {) r+ z0 J/ y! t5 N. R& f5 p
DMA内存地址自增,每次增加一个Byte(字节): n6 Q1 d- V {' K
, \1 O: |. g( v4 ~& ?0 {. [! s
5 |% Y3 L$ ]- z% R
2 V4 y; y( m1 a. m (2)DMA相关参数解析
1 T M8 z+ J; Y' J6 m5 f, HDirction : DMA传输方向有四类
* R- m: ~7 I9 [+ t d外设到内存 Peripheral To Memory' p( U; w Z7 k B; E1 `- q
内存到外设 Memory To Peripheral
/ O. t5 {; ~# J7 _& n% g3 {6 O内存到内存 Memory To Memory. @" V, \- I) i7 b; H- ~
外设到外设 Peripheral To Peripheral
- o5 j, c; B5 h* @% [% O! f, M- s3 `) O( @: W% {+ U
Priority: DMA通信传输速度有四类5 V3 ?" a9 v9 n2 |$ [. j& {& _8 S
最高优先级 Very Hight
/ |8 N0 h9 h3 Q9 J( h$ k高优先级 Hight
* x, Y+ L4 f5 l/ S6 z8 o中等优先级 Medium
, N; D6 O ~5 E! |+ Y低优先级;Low
% U/ U: f3 z& k( Q' y
2 z! c# S4 y7 I' G7 ~Mode:DMA传输模式有两类
' q E' E/ Q# ^5 q( S4 a# Q8 p# ]+ P' k
6 g& h2 x* x5 c& R, J: W
" v- x9 K7 Y4 s/ a K; h y- x
Normal正常模式:当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次。, T& K' y7 ?, ~
Circle循环模式:传输完成后又重新开始继续传输,不断循环永不停止。- j! \9 U& }8 A) h
$ G: f9 n* L L. B5 T6 `0 P* wIncrement Address-DMA指针递增设置:
Z: F3 o% |3 M2 n( v, d7 q. I# t' h4 r8 H% s- _
0 x7 P" _9 ~+ n8 B
+ i% p7 C/ N# D t* V
5 {* H! W. v/ U B1 I a& x4、配置项目工程参数9 A" Y+ Q7 d+ F* }3 D
(1)配置时钟树,用于系统内部时钟,以及各个外设时钟等。此处选择外部8M晶振作为主时钟频率,内部最大倍频80MHz。3 Z5 C0 T9 R/ y" V
! T6 A, T- u- X; C
% l/ v5 b; \* _$ h) Z8 q
8 P% a. r9 d" Y t6 ?1 g# ~1 j(2)完成配置工程。- ^( l$ g7 h: l7 E' R! W+ W. \
备注:需要注意代码生成过程中的继承关系,如图所示:需要保留开发者自己编写的代码时,请根据配置设置,不然生成代码后会删除自己编写的代码(从这个方面也可以看出开发者备份自己的代码是多么的重要。)
3 Q+ h& E' F: f, l- C9 |4 z$ l' X+ H# v* Y; a1 z4 |1 H& ~/ I
9 P/ h& u: i. F/ R, ]) _+ U8 `
2 e! g4 h# H9 Z& P& w
7 R# {; Q; Z9 F8 W
, [. j/ E/ P' x1 |# p(3)生成代码。, Q- e" ]" |/ _- O
备注:使用Generate CODE生成工程代码前,请确保文件路径无中文,否则会生成项目失败。 j5 C( O" J: b: C% E
6 R. o7 c+ g, p0 v: ]( x
3 M! q( s# R1 H) m1 |' U! k1 \7 W
(4)工程代码生成成功。/ N, t1 u) n3 Z, C$ o' X3 k
$ P6 w6 V# O# A7 l; v
$ i0 K" u# Y5 s. a; f* W: Y$ p% z3 {' X. a6 z; g
, M4 B; H' P4 H/ N) o H 三、在KEIL 5中编写代码- }- O2 [9 s0 u7 U' [
1、使用KEIL 5(MDK)打开项目工程文件
( ?' z" B. e3 R+ m源码使用说明:使用前必须把项目工程复制到无中文路径的文件夹下使用。& W# s) D% _8 w$ G; Z, [* o- X4 q1 r
% Q6 j4 C% N D5 ~; c(1)找到刚才新建工程的存储路径,安装项目名称,打开项目工程.uvprojx。7 K+ I; D: C* [& t% S/ ^# e
6 E; m; \! G }
9 r8 M1 ?7 C$ g m1 C' d) R* B* t$ F
2、添加LED指示灯作为系统提示
/ W" Z6 j, s( x! Y添加每隔100ms,LED1、LED2、LED3、LED4闪烁一次的系统提示,用于提示程序运行正常。
7 ^& B* g; [% ^- h7 |- w! z8 a
& W, r9 n( f( S9 x+ d
) X4 M4 ~3 s! y# L2 A
9 s. ^# L0 W' [) z: L, S
3、添加HAL库UART DMA 发送函数代码
5 ~3 L5 t( j' A$ x5 n9 v$ x2 W) b(1)STM32 HAL函数库的串口DMA相关函数如下:' t% q9 Q( t( k0 T5 {! l/ P3 r4 u
- HAL_UART_Transmit();串口发送数据,使用超时管理机制6 e) ^; y% v$ Q0 x
- HAL_UART_Receive();串口接收数据,使用超时管理机制
q _! K$ O! a( E" }9 Y- G - HAL_UART_Transmit_IT();串口中断模式发送
! p% |- |; U( A1 g" v9 _; @ - HAL_UART_Receive_IT();串口中断模式接收
" E% B% N2 {# G7 ~3 ~ - HAL_UART_Transmit_DMA();串口DMA模式发送
$ x% Z6 }0 A8 [# M8 h - HAL_UART_Transmit_DMA();串口DMA模式接收
5 h' Z+ o' f: f# ^- l - HAL_UART_DMAPause() 暂停串口DMA
4 J6 _7 `9 T. |! Y: x$ G0 X - HAL_UART_DMAResume(); 恢复串口DMA# F- s5 ~5 g9 {' p
- HAL_UART_DMAStop(); 结束串口DMA
复制代码
. S7 `6 q2 Z* w. N(2)main.c添加RS485发送函数相关代码:
/ f+ |0 L$ _6 ]+ J" x+ h$ t9 ^6 @! _) l' j9 l" I' C4 V
' h- p; a! a; P3 O6 h; T% ^- Q# P. z. I- z. R9 U# E! f% }% F
- /* USER CODE BEGIN 0 */
0 X/ g w# q$ X* l) x - uart_rx_struct uart_rx_struct_t= {0}; //串口接收数组5 k" \; i# X" f Y4 Z9 u `+ ]
- uart_rx_struct uart2_rx_struct_t= {0}; //RS485串口接收数组 o* I. g; Y, E; P4 j- P
- /*******************************************
- w( Q$ p2 `7 w& F - *函数名:DMA_Usart_Send- {/ c9 \" w9 R) y, {" B
- *功能说明: 串口发送功能函数' |2 f9 Q5 X+ A+ C! s! ?& T' F+ X
- *形 参: buf,len
- G: u1 U( f# U1 _) u' T - *返 回 值: 无
$ f, Z4 } h; P7 _ - ********************************************/
+ L3 Q- `- I) ?+ J4 `" B6 ] -
5 S: V6 q4 _% l - void DMA_Usart2_Send(uint8_t *buf,uint8_t len)
" L ~! j# {7 y" E, ~. M - {
: _7 D, s V7 b - HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET); //设置485为发送状态% f5 d/ t1 c( h( g' U7 N% \
- if(HAL_UART_Transmit_DMA(&huart2, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函敿
& q( B2 t5 w5 C5 @: ^: W; t1 P - {$ b$ l$ U( a( `) e% a2 O/ u5 W
- Error_Handler(); //异常提醒2 B A0 u1 R% J4 \* M. \7 L
- }2 m, ^, W0 B4 O' F1 Y# T; u5 ^
- HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET); //设置485为接收状态
4 w5 }: v! ]8 b0 R$ _- D - }
# y. S% K$ }" Y& @6 h# U# |5 v4 J' e -
- W+ u! m8 ~4 W- V/ C' I4 V- j - /* USER CODE END 0 */
复制代码
- b) I3 ?& E$ q: f+ M1 T# d(3)main.c文件中添加main函数的处理,上电初始化打印一次log,然后等待接收,等串口收到数据后从原串口发出。
- r3 a8 t" x k+ T- g1 W- c
$ S. j5 n" N1 H' n4 r
L7 X8 {# u5 \6 F2 T3 O
$ C. [6 K+ Z0 {% Z: n* V
! N: C2 R4 |* T1 W* t0 }* X/ }8 K+ M* Y) A1 A
- I( q) q, @4 @, L
6 V/ g! R0 [4 q1 S! ~
5 b4 x1 y0 s. m3 ?7 `- int main(void)
+ y5 L% @( d$ Q4 [- @ - {( i. a4 N2 h) E$ J6 ?
- /* USER CODE BEGIN 1 */) ]* s. a; O4 o
- uint8_t transmit_str[50]= {"hello world,this is uart dma function!\r\n"};
( j( P( Z8 J& Z6 R - uint8_t rs485_str[50]= {"hello world,this is uart2 dma function!\r\n"};' o8 o0 ]# T: W& s9 A" d- j
- /* USER CODE END 1 */( G! |7 m8 t s
-
. {0 b, Y- H# e2 M& \" O' @, G - /* MCU Configuration--------------------------------------------------------*/1 x, s) H3 l6 ? o8 }
-
/ F8 B% c4 J8 @" C - /* Reset of all peripherals, Initializes the Flash interface and the Systick. */9 V- F# |$ F/ G- B- N8 e
- HAL_Init();
* s# b% S! ~0 Y! V -
l$ T6 t2 G- {3 t, \+ i# X - /* USER CODE BEGIN Init */! ^ I/ ^8 V; B# H. g# i* G( `! D
-
4 z' `# F( r3 P" d - /* USER CODE END Init */
# }( J2 z9 o8 ], R0 j% e- l -
. ?5 B) ]8 R- _4 {* ~ - /* Configure the system clock */
+ P4 c& T% S6 T F. c# J0 P - SystemClock_Config();2 z8 s( ]& f8 l
-
: E7 Q8 C; A$ i5 Y. b - /* USER CODE BEGIN SysInit */
1 m# |5 f6 w) N" x1 j/ f -
d$ H% v0 c e) |( E: ? - /* USER CODE END SysInit */
9 } b8 l, b. H+ H' j! ]2 U -
1 ?, Z6 J+ `2 `+ q& C+ J+ Q - /* Initialize all configured peripherals *// r6 I# d2 Q) g0 o9 x7 I( R
- MX_GPIO_Init();7 Z9 B, W0 p- T" o. w9 a
- MX_DMA_Init();
$ f5 j6 n# C- m; n4 \/ s- X - MX_USART1_UART_Init();
- S9 k% Y+ @9 z" \1 M4 c. f - MX_USART2_UART_Init();
5 @0 h( p% Q5 H# N8 F, @9 n - /* USER CODE BEGIN 2 */
+ s, W$ R6 p/ P8 | - HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET); //初始化LED灯,默认点亮
6 z+ g8 }# q2 Q. [0 i" ^0 R' ] - HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET); //初始化LED灯,默认点亮
$ l/ F! x% t6 Q( e - HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET); //初始化LED灯,默认点亮, X0 i! u6 C+ D% K
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET); //初始化LED灯,默认点亮
0 c+ m" J' P$ @ - HAL_UART_Transmit_DMA(&huart1, (uint8_t*)transmit_str, sizeof(transmit_str));
5 C6 P# S" c* ? - DMA_Usart2_Send((uint8_t*)rs485_str, sizeof(rs485_str)); //初始化log提示
" ~2 X' X, I+ L6 E) H& P - HAL_Delay(5);
% H7 K2 j9 l; M- _. g - HAL_UART_Receive_DMA(&huart1, uart_rx_struct_t.uart_rx_buf, UART_RX_LEN); // 启动DMA接收+ I. f; i: v' S8 p; D
- HAL_UART_Receive_DMA(&huart2, uart2_rx_struct_t.uart_rx_buf, UART_RX_LEN); // 启动DMA接收
2 {3 W4 z8 u; `. E& K2 l) o: E - ' N% G9 x, C1 B9 k
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能空闲中断
5 m' j/ r" }2 q* K - __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // 使能空闲中断. M. f) E& N. n+ S/ Q
-
$ u# ^# o8 m0 M- K/ U2 z% A( P7 M - /* USER CODE END 2 */5 n6 |% u: d/ V2 O0 k! w4 e
-
) R! K+ ^) j8 i" y7 A( n/ G1 L - /* Infinite loop */
2 @# U( c; x ~5 H* N/ [' f# H1 O - /* USER CODE BEGIN WHILE */
$ w( o# K S: f* X - while (1)
9 m# P @/ Z5 | E4 U - {0 S8 { L! g0 r4 ~; j2 y& f N
- /* USER CODE END WHILE */
0 ]6 F3 m7 H4 R2 z& g -
* c+ G4 O) S U - /* USER CODE BEGIN 3 */
! d" R5 r1 [6 J - HAL_Delay(100);: X7 U+ S" Y v+ k- i* \4 x
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0); //反转LED点亮熄灭切换$ ^: G( b1 G; }
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1); //反转LED点亮熄灭切换
# l4 n; j) u0 Y9 Q* s5 t - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2); //反转LED点亮熄灭切换$ ^; I/ e" a( X2 m4 k1 L! O: ?
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_3); //反转LED点亮熄灭切换: u* M2 u: V9 r, d7 n) h
- if(uart_rx_struct_t.uart_rx_flag & 0X80) //DMA接收完成9 g# \- w" e% z s9 _; H0 X
- {
0 X2 x( ?, _1 i - HAL_UART_Transmit_DMA(&huart1, uart_rx_struct_t.uart_rx_buf, uart_rx_struct_t.uart_len); // 将接收到的数据发送回去, c" q7 P+ H4 b3 O1 h' k* }
- uart_rx_struct_t.uart_rx_flag = 0;1 J u. y; A1 Z
- }
1 x9 n& h% N6 Z2 y1 T& | - if(uart2_rx_struct_t.uart_rx_flag & 0X80) //RS485 串口DMA接收完成
0 o- a- C) x" _1 Y2 r* t# V ^' N - {
8 u2 |5 k) C9 X. l - DMA_Usart2_Send((uint8_t*)uart2_rx_struct_t.uart_rx_buf, uart2_rx_struct_t.uart_len); //rs485发送
; g3 K; l$ H; T7 F' x - uart2_rx_struct_t.uart_rx_flag = 0; Z. C& }) Q) p& |- Z; K8 l
- }6 _) W8 D2 r3 I0 |6 Z3 w2 L0 l
- % M0 v+ X9 }% z' e; p
-
" x' s' P6 P6 \) d8 T - }6 u0 H! c- G: I5 \( l9 G& k( \
- /* USER CODE END 3 */# s4 R* ^9 Y8 |6 u1 S" i1 K
- }
复制代码
6 G% @1 ^( e2 b(3)main.h相关变量声明- P! ]1 `4 B! ^# y2 Z9 X# w5 D" y
& r2 h/ O$ m/ G* H7 l: g
# M6 N6 u0 b1 Y+ q0 Z& U$ M3 ?" Z- H+ y: k3 Z
- /* USER CODE BEGIN ET */0 u) K- U w5 h# J
- #define UART_RX_LEN 1024 // 一次最大接收的数据量
1 h$ b# B$ Z+ C: H - typedef struct% D2 i& g! ~( f& K* {2 g- x' ]9 X
- {! Q8 g2 F5 B$ U* C T
- uint8_t uart_rx_buf[UART_RX_LEN]; // DMA数据接收缓存数组8 d2 ^; H: B7 _6 k" S# w& J1 I
- uint16_t uart_len; //DMA数据接收长度
/ ^0 B/ V" }8 y! |7 i - uint8_t uart_rx_flag; //接收完成标志位. e5 u4 z4 u, U
- }uart_rx_struct;% P5 l) W% Q% v8 m
- extern uart_rx_struct uart_rx_struct_t;
% h* ~ r0 n) G - extern uart_rx_struct uart2_rx_struct_t;; q r) w5 {6 l# Z3 z' {
- /* USER CODE END ET */
复制代码 6 R+ }$ t" Z' o
4、添加HAL库UART DMA 接收函数代码! H/ p2 ~3 s) u2 s
(1)DMA接收设置前铺垫知识点:
$ p2 q) |$ R5 G+ {* U
* l# q+ {1 x/ k8 ~: ~5 g STM32的IDLE的中断产生条件:在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断停止。
$ ?; s2 S, U2 A' ]- B* D8 Q
) m% q- G/ k- B3 J( l m' K(2)stm32f1xx_it.c中添加添加DMA接收处理代码:2 t7 }% f& Y, y
- void USART2_IRQHandler(void)2 P: h5 v1 t0 Q
- {
4 X+ u$ E# S$ c9 p; \ m" ] - /* USER CODE BEGIN USART2_IRQn 0 */
- i& h' S% b; L5 f$ m* R2 N- w - if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET) // 空闲中断标记被置使
+ N) e/ B# |2 I" b - {
4 s4 S* m V& k; D% r+ D/ o - __HAL_UART_CLEAR_IDLEFLAG(&huart2); // 清除中断标记 P" X8 m; T, d! a' v( S
- HAL_UART_DMAStop(&huart2); // 停止DMA接收
" M/ o% R8 _8 L) J! E G" ` - uart2_rx_struct_t.uart_len = UART_RX_LEN - __HAL_DMA_GET_COUNTER(huart2.hdmarx); // 总数据量减去未接收到的数据量为已经接收到的数据量) n7 z3 P8 B1 J- i' k2 q
- uart2_rx_struct_t.uart_rx_buf[uart2_rx_struct_t.uart_len] = 0; // 添加结束笿
5 e# @6 B8 Z* f% I9 q - uart2_rx_struct_t.uart_rx_flag |= 0x80; // 标记接收结束( j* Z+ n; y, G. K& A4 u; @: ^
- HAL_UART_Receive_DMA(&huart2, uart2_rx_struct_t.uart_rx_buf, UART_RX_LEN); // 启动DMA接收/ w* d) s5 C$ g. f% h
- + }* L7 [9 z/ p [! \) W
- }
5 [* v( `7 a. v {' O$ }* p - /* USER CODE END USART2_IRQn 0 */# x8 I7 g0 }9 R Z& q
- HAL_UART_IRQHandler(&huart2);! D% D K% R' ~" V4 o% B
- /* USER CODE BEGIN USART2_IRQn 1 */
6 y, g; d% b6 r8 W; t -
' m: g/ c: Y3 Y- l9 ~7 N- z - /* USER CODE END USART2_IRQn 1 */" C6 f# H/ t. O3 R: n+ E
- }
复制代码 4 D+ f# j; L' r' s# B3 N- B
(3)至此,RS485串口DMA收发数据需要添加的代码已经完成。
6 k4 f a- N+ D
& t7 m6 ]+ Y7 ]1 ^, e' |! q+ W* L; U6 j4 ` P
5、设置编程仿真下载模式
/ I; M2 F& e$ O+ ?& x: G0 E$ u(1)选择Options for target ...>>Debug>>J-Link/J-JTRACE Cortex,点击Settings>>选择Port(SW),可以看到搜索成功SW Device,表示芯片可用,可以下载。
7 u6 O p/ f* G! Y7 u. a
$ [8 m7 U" U! F! ^$ Y8 O8 v" c
$ r& ?, O& }* ]2 R5 Q
. J$ F8 S# B( D7 p( p- B8 f7 ?(2)点击编译,完成后提示“0 error(s),0 warning(s)”。4 R: r+ b1 Q- g& \ Y* B
8 o5 y3 `3 V6 V& ^2 M, h. Z& \
( N9 D" u8 C/ }5 u" D
$ |4 h/ R/ l# O* D7 I(3)点击Download(或者快捷键F8),即可下载程序。
$ H! }) K v7 F7 \" i) q6 O+ } h; I
3 W! V4 K# s q' y% ~- b' i5 [4 z# n& I
(4) 如果下载程序后,没有看到LED1、LED2、LED3、LED4闪烁,可以按下述方式设置一下(Reset and run表示下载后自动复位和重启运行)。或者重新彻底断电再次上电(或按开发板的Reset按键复位MCU即可)。
) o$ r3 P4 \( p$ ~6 u: P) ?/ L& w, d! M v: N' W
S( k% W" N0 ^
) c1 w2 a/ @0 N8 Y6 i6、串口DMA收发实验效果展示
b3 L! c @- X) i) _ 程序烧录到开发板后,即可看到LED1、LED2、LED3、LED4初始化后每隔100ms闪烁一次,并且打开串口助手后(串口参数:波特率115200、N、8、1),通过串口助手可以看到,开发板接收到发送的数据转发出来。
$ l4 T+ Y! D1 a& U7 ]: h+ Y- A
2 q; b( e" {' S3 U
! x4 n& o+ Q8 J) J, f- ?" m6 L
1 q$ Q6 q5 e* T4 M$ ~
————————————————# ?) a) t0 U+ m7 \, O
版权声明:智能小屋ZYXC
( r5 X5 x5 a. d9 q0 t
# U# z2 B" g9 l4 q; Z
6 E% o0 P: O2 ^3 K, t, j |