突然想测试一下STM32单片机ADC采样速率问题,按照常规方法,可以通过ADC采样,然后将采样值打印出来。但是这种方法在处理和打印数据的时候会占用很多时间,导致处理数据的时间超过了ADC的采样时间。于是想到了ADC采样的数据用DMA功能存储,并通过串口打印。但是串口打印依然要占用单片机时间,那能不能串口数据的输出也采用 DMA功能呢?这样ADC采样的数据通过DMA直接存储,然后串口通过DMA功能直接输出采样到的数据。这样速度程序执行速度不就极大的提升了吗?说干就干,使用STM32F103C8T6单片机,标准库函数,keil5软件,编写一个测试程序。
; |4 P) S: Y5 A. w9 p
7 H0 a8 B9 x$ M6 [3 K( _ 首先实现ADC采样并通过DMA存储
, `+ d5 v. G% W9 @6 j4 P, x; F. `% D% O* g; S
- #ifndef __ADC_H9 E- w# a7 a% q4 k6 T9 S0 d* x: S
- #define __ADC_H
+ l% G6 b2 J* c" H, C4 } - 2 t m- }3 r7 ]; {" B" |
- #include "stm32f10x.h"
4 B% D" P$ p7 a5 i( x2 V+ K - // 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响2 @: c1 X5 @$ t. B4 }. i9 F( e9 C
- /********************ADC1输入通道(引脚)配置**************************// u( j$ Y2 @% e
- #define ADC_APBxClock_FUN RCC_APB2PeriphClockCmd
$ A, Y( z" q1 u+ v3 G" Z - #define ADC_CLK RCC_APB2Periph_ADC15 N0 }" T9 t& N5 F
& |# u4 X9 C- b& O4 ?3 K1 i: g T+ f- #define ADC_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd. E( @3 i s4 I
- #define ADC_GPIO_CLK RCC_APB2Periph_GPIOA: Q( a1 e1 {8 k& H
- #define ADC_PORT GPIOA
9 h8 \2 H g2 Y/ [! [6 u
+ S5 X; l5 M: J4 }- #define NOFCHANEL 1 //使用一个通道测试' O; f2 Y2 p W5 n/ c B
- ) c8 G/ M! B. }' X+ @: }2 n
- #define ADC_PIN1 GPIO_Pin_09 m) q7 f" K7 B' ~+ O
- #define ADC_CHANNEL1 ADC_Channel_0
& O) i# g% y! C3 @6 x - # ^! b$ L. U) C3 L# P
- // ADC1 对应 DMA1通道1,ADC3对应DMA2通道5,ADC2没有DMA功能
5 v8 [( \$ _7 W - #define ADC_x ADC1
& I9 J( \. n. s% ^6 A - #define ADC_DMA_CHANNEL DMA1_Channel1
& \7 u8 n* Y" I - #define ADC_DMA_CLK RCC_AHBPeriph_DMA10 l* e6 e# ^4 F( _% @# w
+ d1 A. Q1 X: R9 P8 r- // ADC1转换的电压值通过MDA方式传到SRAM
- Z6 P* [, y6 e* m7 R - extern __IO uint16_t ADC_ConvertedValue[NOFCHANEL];- E/ U; m) Q$ W8 y: |
- ; z/ p P/ ~# ?* u9 `0 z9 i' y
- /**************************函数声明********************************/1 _4 H6 {+ ]* v* Z; s
- void ADCx_Init ( void );
* L/ o! J# w9 Q# |: P - #endif /* __ADC_H */
复制代码
5 u; ]0 x# Z/ g" `; J/ I首先在头文件中定义用到的时钟和端口,如果要修改采样的AD口时,直接在头文件中修改就行,程序中就不需要修改了,方便代码的移植。下面编写ADC代码。7 E- ?, ]" Y% n; m' M6 ^
/ ^' ?. ]; o1 |- #include "bsp_adc.h"
; D- p- @2 c5 D* a0 }. i - __IO uint16_t ADC_ConvertedValue[NOFCHANEL] = {1000};
% j2 L; J, b! b. [( @- E% L - /**
5 W$ g- u- \& e1 l8 B+ O - * @brief ADC GPIO 初始化; @. z: x/ O. B
- * @param 无9 m) _9 K- [8 m6 [! J! _
- * @retval 无
3 U# j8 O# v8 _* _( [& g - */4 g2 U9 X) _) W. [2 k! {# ~2 |
- static void ADCx_GPIO_Config( void )
8 w* M+ J+ H) k - {
. p0 |' t( [0 F - GPIO_InitTypeDef GPIO_InitStructure;) W4 j- J0 I/ N* I8 s1 O, Z
- // 打开 ADC IO端口时钟8 t- Q7 |6 \) O9 v1 v/ `
- ADC_GPIO_APBxClock_FUN ( ADC_GPIO_CLK, ENABLE );
2 B5 ] `. x8 z' \ - // 配置 ADC IO 引脚模式* p- n. w, F/ {9 O3 V+ D6 [3 d
- GPIO_InitStructure.GPIO_Pin = ADC_PIN1;8 M1 ?5 P, L, [5 x1 F
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;7 X, y3 R7 n) C0 Z
- // 初始化 ADC IO
* F! c0 U5 Y. a$ W: W/ y - GPIO_Init( ADC_PORT, &GPIO_InitStructure );
' c3 L, ^7 V3 v" S - }% s1 }8 p, x0 @. q8 M* h8 ?
- /**0 p' c" f6 e2 z' A( E
- * @brief 配置ADC工作模式
1 h! B1 b G U+ E! v8 E) n3 f - * @param 无
! e# E# i { r+ \& `9 W) T - * @retval 无
F) a& ~# }. L! e - */
, f- @) c& k/ ^0 G - static void ADCx_Mode_Config( void ) g5 J, \( C" r) @2 D
- {
) N8 i3 g5 O( n7 i, r; s8 b C - DMA_InitTypeDef DMA_InitStructure;! D7 L. g0 C% [9 M0 y
- ADC_InitTypeDef ADC_InitStructure;% Y( M0 M& u% j& z- D4 f* o
. }- b0 u+ c3 {1 B9 m- // 打开DMA时钟
* r, ~& `3 [$ `% Y) _$ R! q. }; R - RCC_AHBPeriphClockCmd( ADC_DMA_CLK, ENABLE );
" |0 p! w/ C) z8 l - // 打开ADC时钟
9 j' e1 h' u" Z9 \9 z/ J/ \" b - ADC_APBxClock_FUN ( ADC_CLK, ENABLE );2 l/ u! `9 U% L
- // 复位DMA控制器
6 Q, L; u4 P4 P! k* [ - DMA_DeInit( ADC_DMA_CHANNEL );
. q0 j% j( T% x' f+ z - // 配置 DMA 初始化结构体
: h0 Q& @# X* h" Y7 ~; g - // 外设基址为:ADC 数据寄存器地址
/ q& c( W; G% D* i8 F+ a - DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 ) ( & ( ADC_x->DR ) );8 m- ]4 x& l: r0 Z4 S7 Z
- // 存储器地址9 |' f2 b" O# O9 e. \7 @" B
- DMA_InitStructure.DMA_MemoryBaseAddr = ( u32 )ADC_ConvertedValue;# P* r8 J g, i+ U/ ]
- // 数据源来自外设
- |' @" }" q" g# e' \1 h. { - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
% ^) ]# N. T; A+ } _! Z - // 缓冲区大小,应该等于数据目的地的大小" m& {' q: l% e7 F8 G0 G
- DMA_InitStructure.DMA_BufferSize = NOFCHANEL;* `4 a% u0 A( R
- // 外设寄存器只有一个,地址不用递增" l5 S1 I! C! h) K( ?% k
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;! ~$ A! z' h, ?9 [. L9 U ^" K9 x
- // 存储器地址递增, y$ \$ O" S* p C$ o, z( {8 N: t
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
) ~" f8 C5 E5 q* e* n& y - // 外设数据大小为半字,即两个字节+ ]4 l0 A) Z% o) d$ T+ U
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;, l# t7 t) d0 F q6 X [/ [
- // 内存数据大小也为半字,跟外设数据大小相同$ A, w) [ k- o, M5 W: t* d
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;& l0 T7 ~5 P4 }# u9 R0 \3 e |9 ?3 B
- // 循环传输模式! T' h6 a5 K: |) |
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
* O7 m/ p3 y/ u& W6 u - // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响; D; P" `& r' q7 m L, \
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;+ z9 d, S4 Z l& T) r7 [" o
- // 禁止存储器到存储器模式,因为是从外设到存储器# g5 ]8 r4 e1 Y/ B- ^. m0 W) u# _
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;, v D: r2 B7 i' w$ q* z! X4 ^
- // 初始化DMA
8 @5 m X. M6 {# ?0 G - DMA_Init( ADC_DMA_CHANNEL, &DMA_InitStructure );
7 V3 R# `1 w% s( v8 M - // 使能 DMA 通道5 B, K( J' I( @1 f6 S
- DMA_Cmd( ADC_DMA_CHANNEL, ENABLE );+ i6 h& ] X/ {# q. P$ P7 ^; H
- // ADC 模式配置
: G2 Q3 H6 i/ B7 q X$ Y - // 只使用一个ADC,属于单模式
0 }- s( ?7 ?4 p& O5 t - ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;. {: @* R" z% {) J; t B6 J4 A
- // 扫描模式
% R: y' B7 E2 F- |8 r0 I - ADC_InitStructure.ADC_ScanConvMode = ENABLE ;7 s4 n! T F: F2 s3 ~9 W4 M
- // 连续转换模式, z- y3 y3 V; S' q7 a7 b( G2 @
- ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
/ U$ \9 `7 C b. V$ i - // 不用外部触发转换,软件开启即可
" K7 s! B, e9 a6 V# G% Y - ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
9 j; k# V. H0 p - // 转换结果右对齐5 _3 t* ^" w1 Q" `
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;6 S( F1 C- I9 _1 R. V9 P
- // 转换通道个数
2 M% M" w& | ?. |7 `, s - ADC_InitStructure.ADC_NbrOfChannel = NOFCHANEL;
7 M+ D$ Q5 C) O+ e4 l5 p - // 初始化ADC
+ ^$ l* h7 ~$ b6 f3 G" d L7 r - ADC_Init( ADC_x, &ADC_InitStructure );3 s- O2 }6 H& U1 G0 g N0 u
- // 配置ADC时钟N狿CLK2的8分频,即9MHz9 l1 g: b8 X" ?+ g$ M% B2 |
- RCC_ADCCLKConfig( RCC_PCLK2_Div8 );
6 D% e! w0 m& B0 C5 D1 Q5 @ - // 配置ADC 通道的转换顺序和采样时间: G9 o" b+ a4 m$ M- L
- ADC_RegularChannelConfig( ADC_x, ADC_CHANNEL1, 1, ADC_SampleTime_55Cycles5 );
+ I' o: z0 I7 q$ s- G6 D5 C - // 使能ADC DMA 请求
3 f/ K' E" ]1 S: R - ADC_DMACmd( ADC_x, ENABLE );
b9 s5 ]+ r& V$ w. V, x1 w5 c' n( N - // 开启ADC ,并开始转换# e( ]% I5 W" C E
- ADC_Cmd( ADC_x, ENABLE );
7 H3 {! G2 G8 c - // 初始化ADC 校准寄存器
& I2 ]& [. b. l2 c% [ - ADC_ResetCalibration( ADC_x );
, ~- J8 a- q. } - // 等待校准寄存器初始化完成
% R, `! a0 M% p# g - while( ADC_GetResetCalibrationStatus( ADC_x ) );4 G! s2 d" R/ p" J# P
- // ADC开始校准
" N! _) E3 v0 T- Y2 V - ADC_StartCalibration( ADC_x );
2 e% I3 x& g: G* S - // 等待校准完成5 ]# n. J; n* J7 f( y3 v
- while( ADC_GetCalibrationStatus( ADC_x ) );. h* f+ ?; a1 J! |+ }( H
- // 由于没有采用外部触发,所以使用软件触发ADC转换
9 L) K: V' [! r& | - ADC_SoftwareStartConvCmd( ADC_x, ENABLE );0 R) |! {% b( I j/ {- e
- }+ @& B! J: Q2 Y5 N |1 `; f3 q
- /**
' m, D6 I. a+ x; _- G - * @brief ADC初始化
1 h' A" R# N6 {, b; [" K - * @param 无8 a; i/ O0 q9 Q4 ]8 s
- * @retval 无$ Q0 x' l! F+ s# J9 }) E) p' y
- */* A' B( W& `: \& L) Z# c3 I E
- void ADCx_Init( void )
" T+ u/ r z7 {+ z - {8 N3 F6 V c7 y) r9 y) v6 D D
- ADCx_GPIO_Config(); V* D) Z, e) o3 v" `$ j: j
- ADCx_Mode_Config();( A6 z+ a* ?) C
- }" z1 `+ A. }/ ?
- /*********************************************END OF FILE**********************/
复制代码
+ `) r; a% I3 o! ] 设置ADC为DMA传输,将采样的数据由DMA自动存储到 ADC_ConvertedValue[ ] 数组中,这里虽然只使用了一个ADC采样通道,但是定义了一个数组来存放采样结果,如果想要实现多通道采样值,只需要将其他通道的初始化代码添加上,同时将数组长度,也就是通道数修改一下就可以使用了。初始化ADC和DMA后,ADC采样并通过 DMA传输的功能就可使用了。然后串口输出数据的时候直接从ADC的采样结果的数组中取值就可以了。
5 g. g m$ J: M& F0 T1 ?# f6 i, P
下面编写串口相关代码7 _% ~, k7 C* E2 I K' i
' B6 H- `4 ~$ {/ ?/ a! `
- #ifndef __USART_H7 I; G1 `+ d! ~/ L$ ?& s8 y
- #define __USART_H# [' N: J' M+ t* y$ l& b( `, a
; N0 ^: j: _+ j* O- #include "stm32f10x.h"! l I( s+ ?: k( O1 p+ _9 F1 T/ d8 c0 k* U9 A
- #include <stdio.h>
. u0 m* b2 l) [+ t( A3 I - // 串口1-USART1
, m4 V* i6 P q* _" Q - #define DEBUG_USARTx USART17 I9 s$ [8 A9 _, }7 p: H' W
- #define DEBUG_USART_CLK RCC_APB2Periph_USART14 q! v6 Q3 j0 B& ]5 S5 x
- #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
. v, {/ q( d) A. H: y" }0 o - #define DEBUG_USART_BAUDRATE 921600
\' c7 ?7 i8 e! y& G7 q/ V: S$ u - 3 ]. f4 c) W" _8 \
- // USART GPIO 引脚宏定义
7 j% h7 n" H% D- E" z$ P4 i - #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
) O. q7 _9 @) j! F6 C - #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd# V" e0 V: @- t- h- d* S
- : u, F: ^9 {% K* x% k$ U& f
- #define DEBUG_USART_TX_GPIO_PORT GPIOA$ b* | t' j; j. N2 _
- #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9* F2 c# n. |2 q$ R# I# \' v
- #define DEBUG_USART_RX_GPIO_PORT GPIOA
5 `% v% X' n; j/ o. |) k - #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
复制代码 , v4 n5 w) h, u# n
将串口的端口和时钟也使用宏定义的方式,如果要改为其他串口输出时,直接修改头文件就行。下来初始化串口。3 {: E1 A0 b+ P, g7 B* ?8 l. Z! P
3 F! {5 R4 B) e3 G- void USART_Config( void )
4 @5 y3 T. j: h, q3 e4 {; z: B: C - {
. |/ E# s' x6 d a - GPIO_InitTypeDef GPIO_InitStructure;0 E' _ d1 X6 L2 ^1 m' ^$ _
- USART_InitTypeDef USART_InitStructure;& A: x) o, z |
- O# Y+ h7 @$ p$ `- // 打开串口GPIO的时钟- J2 T0 S4 M, _0 t# m/ Z
- DEBUG_USART_GPIO_APBxClkCmd( DEBUG_USART_GPIO_CLK, ENABLE );
! M" D/ P( ~6 g4 \/ ^: {3 F! e& B j - ' |! Z% C- `4 U
- // 打开串口外设的时钟% c: f* a- B* X' C$ h) N3 j! G! f
- DEBUG_USART_APBxClkCmd( DEBUG_USART_CLK, ENABLE );
# z8 W8 |: J0 X - & D v& W/ Z1 e8 p6 \% R2 w$ ?' [7 x
- // 将USART Tx的GPIO配置为推挽复用模式
& S' a! K& ^* i" Z U - GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
) `6 e) F+ U3 a$ _8 d - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
* K \. c8 m* `/ K - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
% ]# o, c' w5 q2 R* ^) ?- y, J7 a | - GPIO_Init( DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure );
5 u3 Z" k$ |& w; e0 X( A% W
: b4 q0 z5 X! ^- U& N- K- // 将USART Rx的GPIO配置为浮空输入模式# e- W7 M8 i) P# f) L
- GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
) W/ A% C# s: o' S! m, Y - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;+ n( { L0 w+ p4 A. H3 e3 I. S
- GPIO_Init( DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure );4 ~ s- y* z+ c7 {
- 9 g h- P) |. a' D: z* x
- // 配置串口的工作参数
6 m% O" \" p) p% ^- E" ?3 J$ w - // 配置波特率( D( i9 E' R# E; y) _6 d
- USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; T3 G; i5 c Q7 P/ B1 r$ A- a
- // 配置 针数据字长
( ]$ i- P- }9 s9 u' I - USART_InitStructure.USART_WordLength = USART_WordLength_8b;
2 S( q: U" H" ?; e8 }0 f6 x - // 配置停止位
8 J( y( ?9 ]& Z4 w9 Z8 p - USART_InitStructure.USART_StopBits = USART_StopBits_1;7 o, v8 Q o. [; ]0 t; K
- // 配置校验位
1 K( a1 ], Y8 Y/ o - USART_InitStructure.USART_Parity = USART_Parity_No ;
& a! r+ U5 ]2 G - // 配置硬件流控制9 b0 `1 O/ G. N3 c( R; }1 w1 H
- USART_InitStructure.USART_HardwareFlowControl =
( X/ s& G/ Y( `+ J0 |: n) ]% | - USART_HardwareFlowControl_None;
+ e1 L2 x% b( }" q - // 配置工作模式,收发一起
; [/ q3 J) V/ H' J% f - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;6 V: W* u1 Q/ f! j4 T9 u
- // 完成串口的初始化配置
9 i, G# t. e @5 H4 e - USART_Init( DEBUG_USARTx, &USART_InitStructure );+ _7 s7 m+ ~5 b. h" \
- ( o5 ]: A3 ]' S1 m( k0 S3 U. b
- // 使能串口
6 ^& s9 [5 F! Y: E - USART_Cmd( DEBUG_USARTx, ENABLE );# h# B: T8 L. Q9 Z+ ~9 K( Y; J
- }
复制代码
: a* u& V% d- p下来设置串口为DMA输出
- Z' U# G3 e5 r! Q, f" e" V5 z9 b. p# Y
- #ifndef __DMA_H
$ a _9 _$ x4 O/ } - #define __DMA_H
9 P* Z* D/ [1 U6 [ - #include "stm32f10x.h"
* C. ]& F/ b! `# t2 \: O6 _) j" {4 t5 v - // 串口对应的DMA请求通道
; B4 e" f- A: q5 P3 K# o - #define USART_TX_DMA_CHANNEL DMA1_Channel4
- q: _! u; P5 o) I - // 外设寄存器地址
8 Z7 ^ I9 u3 t5 l: E* Z2 N - //#define USART_DR_ADDRESS (USART1_BASE+0x04) //使用地址偏移值
6 I* }( A& v' T/ V - #define USART_DR_ADDRESS ((u32)&USART1->DR) //直接使用寄存器地址
: ?" p& c: |5 V- f - // 一次发送的数据量
& V! z$ F' ^& u3 {7 O - #define SENDBUFF_SIZE 63 F' P& }2 ~' v. Q( H# U9 O
- extern uint8_t SendBuff[SENDBUFF_SIZE];
3 ] T) [6 S1 n( }( L# c - void USARTx_DMA_Config(void);' P2 K2 C+ v7 P M! W' d
- void USARTx_DMA_Restart( void );
3 n' D. C: U* i) v4 v5 @' Z - #endif
复制代码
; w6 h1 m* `0 [) \/ i 在头文件中定义串口发送端的 DMA通道,将串口数据寄存器作为DMA源地址,将SendBuff[ ]数组中的内容作为内存地址,数据方向为内存到源,这样就直接将SendBuff[ ]数组中的数据通过DMA直接传输到了串口中。& V" E: i; w) l' T' V
: g' e, ?/ H/ [( w! G4 j. B0 F: i- void USARTx_DMA_Config(void)
* O/ X4 e/ w; S1 u - {
1 g0 c. \4 g4 J! _& O5 C - DMA_InitTypeDef DMA_InitStructure;
% g; j& B3 l" N. V - // 开启DMA时钟
+ J3 p: l! k, _. ] - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);! m0 {, C3 M4 \/ y* v4 q
- // 设置DMA源地址:串口数据寄存器地址*/
8 [( a6 U1 `! l) d3 V; q- U - DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;, C7 z- u! S* G2 i5 i
- // 内存地址(要传输的变量的指针)
2 s3 b( V2 b6 y, B: X L$ S `! f3 X8 H - DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;6 O s! q5 K: I+ {$ P9 @
- // 方向:从内存到外设 0 x- {- U& I4 e/ W
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
( V- p" Q( b. |7 D9 h" _ - // 传输大小
' z2 x7 r* i; Q! \4 y+ j - DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE ;
; x5 c' d4 a! ^) B7 @- S - // 外设地址不增 + l w. K( z5 y5 x* X6 i4 C
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;: }1 F1 ~- U2 ^
- // 内存地址自增
! ~! x6 {. p9 v( j! { - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;( C1 X' }! w7 l* k/ ]$ E# _: g
- // 外设数据单位
& A% |; v; [* S( w7 u" u) k+ r - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
# a, v J. H& G! ?- F - // 内存数据单位
" h6 O- U: N: e. d - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; ; r- w Y3 Z5 a8 V# v# e r
- // DMA模式,一次或者循环模式: i8 Y8 }9 K4 \. z4 e
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ; //发送一次数据5 \% K" @- i9 W+ C( ^
- //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环发送数据
( ^1 k8 f# E, F2 l0 S+ [) W - // 优先级:中
( u; A8 O! ?+ n1 i& | - DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 3 F6 z- w* E9 B* E0 L$ X4 G
- // 禁止内存到内存的传输
5 F% S+ _9 b: H( I$ G! T6 d - DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
6 L' e5 t+ K9 _& S) |$ E - // 配置DMA通道
5 q9 O$ T( h; X0 D) r# ^$ f - DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure); 7 E. o1 [" h; I f
- // 使能DMA
4 x) a1 C3 ^. Q f" [8 q - DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
' d" v* a1 r( x( u" r7 Q5 s, q - }
7 R% B' H0 F! E5 e - //重新启动串口 DMA/ M% f0 n! \+ t1 U/ h
- void USARTx_DMA_Restart( void )* x1 b- b! L: Z- _% j& @
- {- r3 ?% N7 G% R6 |0 H8 _' l6 G
- //重新开启DMA传输
, p! S/ V3 ~) a2 F* h! ? - DMA_Cmd( USART_TX_DMA_CHANNEL, DISABLE );
4 v! R1 f8 p1 ]+ }- \1 k - DMA_SetCurrDataCounter( USART_TX_DMA_CHANNEL, SENDBUFF_SIZE ); //重新设置传输的数据数量; z% S, a2 j( X: w
- DMA_Cmd( USART_TX_DMA_CHANNEL, ENABLE ); //开启DMA传输9 u4 [* `! c" h! Z2 D
- }
复制代码
, _+ c2 V" b! P- H) j 将DMA传输设置为单次传输模式,因为转换后的数据为16进制数据,为了方便在串口助手上观看数据,需要将16进制转换为字符串打印出来,所以每次每次在串口DMA传输数据的前,将数据转换为字符串,然后再打开 DMA传输功能,将数据通过串口发送出去。所以DMA传输时不能设置为循环发送,需要每次将数据格式转换完成后才能发送。发送完一次数据后,需要重新设置DMA传输数据的数量,然后重新启动一次 DMA传输。7 W& T1 N" w$ A' l8 R0 ]
. N' o% E) H4 o" A; I! ^
- int main( void )
! N) b. J) i3 I7 u6 K! J. |% \ - {; I' \9 B6 |- P4 R; U3 `4 _; u2 i4 ~
- char str[5];
% g7 Z7 L6 [, F/ g. t) I - /* LED 端口初始化 */
' I6 \) C8 C* L/ _6 d1 y - LED_GPIO_Config();9 d, ~4 I* @ g# U& c- E! S q3 b
- /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
: W% w: ?) F" E - USART_Config();8 N9 S5 z; s, v$ |
- ADCx_Init();9 t+ x; o5 I7 a4 P+ Z1 Y: c5 B. J
- /* DMA传输配置 */% n2 Y6 O9 [5 H: ^
- USARTx_DMA_Config();
$ q7 G) E2 K1 v2 ] - 1 S, h; y$ o& X/ ?9 [; Z1 M
- /* USART1 向 DMA发出TX请求 单次发送模式 */. W9 ]9 K8 m ~( j6 D8 C" [. d. k1 Y( g
- USART_DMACmd( DEBUG_USARTx, USART_DMAReq_Tx, ENABLE );
& P+ f- N9 }* h+ t; K - //串口发送缓冲区后面添加回车换行符
- ]- q& m" X( \! N - SendBuff[4] = '\r';! H: V# D# f1 i$ Y, K8 ^
- SendBuff[5] = '\n';
7 |7 s( W5 [! w' b5 C) ~; v, i4 F - while ( 1 )
4 j& i0 d6 q- {% U0 U - { }( @, H R4 U$ p
- //填充数据 只打印通道1数据0 {5 {5 w9 E! |! ^, B2 i6 p
- sprintf( str, "%d", ADC_ConvertedValue[0] ); //将通道1采样值转换为字符串类型3 D, ?( ^+ @, Z3 \+ h/ s# ~& a
- SendBuff[0] = str[0]; //将字符串数据存入串口DMA发送缓存区( c( A. K$ `6 O. {' a3 g o
- SendBuff[1] = str[1];1 r1 g9 r% c, y. ~; M f
- SendBuff[2] = str[2];
3 w* F4 {( s* g8 T8 q4 m - SendBuff[3] = str[3];
; k0 L) _6 }5 [- G - USARTx_DMA_Restart();
5 j; ]* k# J- Q9 I& x7 i
# V* X7 h! g% a! Q8 d- @$ k# b- SysTick_Delay_Us( 45 );/ [7 }" \( Z6 H7 k8 B- n) B1 f, e+ [
- }/ Y* c- M8 x5 Y6 Y! `- o
- }
复制代码
) K/ B( z& v- T5 C( A& Y: P. [ STM32F103单片机的AD为12位,采样值的范围为0---4095,转换为字符串后最多为4位,再加上回车换行符,总共6个字符。这样串口DMA传输数据的长度就为6位。/ P( i' ^& _8 N# r- S" x
' S+ Q+ V' x8 _( N2 G8 ^! S& _/ Q
为了测试 DMA传输的最大速度,将串口波特率设置为921600。下面开始测试代码,用函数发生器产生一个0--2V的正弦波,频率为100HZ。然后通过串口助手观察采样的数据。
3 E+ b1 _# X+ t6 ]" c
/ p4 }" G9 i$ D$ y+ Z6 |" O# o
9 f+ N1 |, s! {6 [% o8 q$ Q! n1 j9 L; S( [3 j5 S
通过串口波形显示助手观察输出的数据波形如下6 E9 V7 f c- ^7 @1 a" W: X2 j
* \2 a l. Y5 y8 ^& ]1 r5 x
# i/ F) b5 @& D1 G$ k2 w3 K! J7 d4 u' _% X
通过上面测试可以看出,在STM32F103C8T6单片机上成功实现了ADC的 DMA采集,和串口DMA发送功能。
. ]- R) v+ j7 l$ D$ E: W
u2 S/ U6 u( {& s, ~9 ~) y) C$ c6 ^0 n) ]( b
|