ADC1DMA_Handler.Init.Mode
+ A" G% O2 k4 qDMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
# P2 B: J g* kDMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
' N% W0 o- P8 {- r* R Y虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。3 d2 d; P8 ?+ T: Y
9 l8 O* X# J; v
ADC1_Handler.Init.ScanConvMode% D0 ?) ]) e9 F2 c' G; C2 _
扫描模式(ENABLE):ADC会依次扫描设置的各个rank2 t, z3 [% c/ W: d+ n
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……3 g3 Y) K" C( @) D5 R' G7 ]
即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~; S3 {3 ]0 }# y O r% e% I5 g3 T
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。
+ o3 G3 H* v) c# @2 t- }" ]4 s4 }7 C/ O' |; ?6 L8 z
ADC1_Handler.Init.ContinuousConvMode+ ]/ p/ R; O+ U* N+ f
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
( X' t' Y& I! u, v- [: l+ B* o C3 Z; y: {5 L
4 u4 X4 R" q4 A
+ `: o7 v& ~3 o% j+ l0 F) g6 p
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。
( O8 C$ Z# L( v" A$ v0 j5 q+ ^+ Z
8 I2 A3 h' h2 S/ m/ \8 J" P, B6 a1 \; g: R# j9 y! c
而将传输长度设置为3的时候,就进入了传输完成过半的中断。- n( P0 ~! |5 r" R! Z
5 W: l( @+ _0 w8 O
- _6 S! }$ T: P' v7 s6 [& t1 Z8 k如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。" P' J2 C0 s+ l; Q
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。$ ], Z+ K+ Z2 @8 j4 y
, R/ _. _, l9 t; ^) V
在另外一篇博文里看到了这么一个表格,希望能够辅助大家理解: U% y0 x; h4 e% f( p; ]
, _6 w7 E! ]; h! x+ f: J8 ~1 I
& Q/ {" O2 n9 E" K, w% K, K' T
' |$ B8 U* a$ p8 o# ^ADC1_Handler.Init.DMAContinuousRequests- W; n9 u" [6 [5 x D
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。 ^* M5 s+ p% m
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。% K0 {% c8 d' m, S
2 l9 u' n" P5 |/ s; ?) U
但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……1 A; e$ @9 C5 |" `+ p+ L
5 Y/ K' Y e3 U
ADC1_Handler.Init.EOCSelection
6 Q5 x# F4 U" Y, ^1 @6 t8 z+ ]偶然看到一个帖子的回复:" h+ \8 q* X# e; |+ n& ^
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。0 ^3 r/ u- E8 m* \- n* L
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
) U/ l# ]9 F. ~8 CCACHE行的数据写进SRAM1。
# T1 S" O* r# m3 g' Q( E- r2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。4 M) T @# B# d
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
& v& O/ C2 @5 b4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。4 w" h; y* F* K5 S/ [. l$ y1 x
: Z" T2 W! T8 R- E, d7 P! Q在main函数中关闭cache,DMA采集的数据就可以更新了。$ ?0 e' Q3 `7 r C
& W h1 s& \2 f- I" ?
1 }- ^, Q" e1 }! k7 |
3 P% K" J# X! @3 W
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。
9 g* T. b; c2 v5 O5 N- #include "adc.h"
+ @" s& c3 q+ l* T3 z - #include "delay.h"
& U& a! {& K* O9 e( A m5 ^0 I - ; A2 Y# v: I/ m, ?8 q
- ADC_HandleTypeDef ADC1_Handler;//ADC句柄
V' g* _. e$ L9 e! ^ - DMA_HandleTypeDef ADC1DMA_Handler;" G( Q6 o) L; s6 L/ k
- ADC_ChannelConfTypeDef ADC1_ChanConf;# r/ o0 K- V& M: y. X
- & q) N! j- L1 Y% r3 T, Z
- uint16_t buffer[128];- x& u* S% ^1 ^% I7 T* ]
- 8 f* b9 X5 U, N) \7 @" a4 l1 k
/ `; i. E. w9 D+ `& ~- B- //初始化ADC/ [% Z- R; ?- s* I6 w
- //ch: ADC_channels! {6 K' O8 Y0 J! v2 G/ z; c
- //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_166 z e1 m+ f% {/ f
- void MY_ADC_Init(void)) N$ E/ M# H. e( t2 U. m- q
- {$ [' K9 S) ~) ?
- __HAL_RCC_DMA2_CLK_ENABLE();9 B8 n- K- W1 a' n: {/ [& i5 x
- HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);: H% V& \: x7 ?( R
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);# J2 o4 Z8 R, H/ M/ w& g2 ?
- / v8 f2 W- _$ _ c, y) b8 X
- ADC1DMA_Handler.Instance = DMA2_Stream0;
* D1 ?# X% [3 Z - ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
1 I4 _! h! a: v) b! e2 c& t G+ | - ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;9 E/ l3 }, s( i9 t$ s; h4 O! f) U. \
- ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式2 V/ q" z3 V% V& u1 I1 _1 B
- ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
; l1 h) k" J) C0 B - ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位
_( K5 O% b* y - ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位; i) B! d* y, K1 k5 i; J
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束9 \+ P: v: k& c, v, l2 f
- ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级* `6 K0 Q+ P, J/ S2 {* U7 v6 N
- ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/
8 F% x1 b& f9 K
7 p( \' O# d) }6 z8 v4 W- HAL_DMA_Init(&ADC1DMA_Handler);1 k$ P( u# f# N+ {
3 J3 x5 ^2 T% M, g+ v8 t- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来0 U- ^% D/ |1 C3 M# q7 A
0 k* i/ L) L) K8 F& {6 G- # \5 l2 z9 o5 T. n( _, m. i N
- ADC1_Handler.Instance = ADC1;/ [9 K2 ^1 p& a
- ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ
" ^( H/ ^3 R; h- y) }6 R - ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式
# W5 w# _- p) F' m& J6 y" \ - ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式
( o7 U" T0 ?) D9 v - ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换% d$ N' a* g* H( P5 V
- ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式; J% v; |2 n; Q
- ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
( I2 ]2 Z: u, x+ A& t( ^ - ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发! @5 t- S7 x# |# |$ D
- ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
# ^5 ^1 t/ b ?9 Q" o - ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1
5 H8 |; E1 R1 N1 @; P! i" o* x9 o - ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求
/ c2 t, a/ f( V/ o5 t$ r - ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV; * j& ]: I- K' G% j% B9 u* }
- HAL_ADC_Init(&ADC1_Handler);0 ~6 D8 e3 |+ |/ t- x% B2 |' \
- , i! K/ o" Z; Y/ G0 t& \; y
- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道
* j3 d. A& [) P3 \6 Z2 q8 r - ADC1_ChanConf.Rank = 1; //序列1
( J+ ~. k3 b( {0 S1 S- V6 f - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
+ y8 s6 t! S S" O3 J7 R. n* S - ADC1_ChanConf.Offset = 0;
4 N' w7 n4 [ y$ F, U& Q - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置) i/ {; J& Z& j P2 l8 @& i
- ( q8 [2 n) q4 x+ v, a. l
- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道6 x0 a3 {7 H5 C1 h% } e
- ADC1_ChanConf.Rank = 2; //序列2/ i* y, X* }7 @; X6 ?) x7 u2 g
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
# z; }- S) i# C+ w - ADC1_ChanConf.Offset = 0;
$ X' V7 f. l7 ?& T! S# n# ]: { - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置5 {7 f& k, M9 X: v8 E
2 M! v* W2 F5 _- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);6 q6 \" q1 I, a3 q, I
- }7 ?% W9 j0 K9 B+ f% v4 x! m
- * O% k. l4 C; `. X& @5 ~2 D
- //ADC底层驱动,引脚配置,时钟使能
( Y! L* P8 r- A. w" l; \2 ?; D - //此函数会被HAL_ADC_Init()调用
: f' }. E g- e% A* `4 U: ? - //hadc:ADC句柄1 v0 U2 n2 k/ I0 `0 O* K
- void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)' t! W, ?; q6 } j- h6 \
- {9 E, a) t. Q% ?- S1 }
- GPIO_InitTypeDef GPIO_Initure;3 H$ l- ?* p& J- @6 b
- __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟
. K9 c+ V9 a S' R1 Q - __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
7 _1 ?* j2 O% ]9 [1 H; z0 c - % z9 B9 ]& @- A
- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5; k! |% [4 g* F8 E9 V" E2 n
- GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟
; q) h% g4 x% R/ d3 @# Y. E+ | - GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉1 L4 s% F. N9 c8 I. Z% G; O
- HAL_GPIO_Init(GPIOA, &GPIO_Initure);
6 P; q; e$ Y7 [3 N - }7 _% d4 e. _4 u7 S
% X" V# U, E* r- void DMA2_Stream0_IRQHandler(void)
0 ^+ }4 C; p0 Q2 u: O - {+ b: G. I. ~9 s! W9 {+ ^! X* L
- HAL_DMA_IRQHandler(&ADC1DMA_Handler);
+ E6 E& b/ g- ] - }; f& p/ R% U: Q+ y$ x2 I# {
8 S2 V0 r7 n: D7 m9 p( C3 [- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
3 A; \# I$ E7 _, m8 {8 C q - {$ {1 [" |9 N: {9 R9 j1 o
- printf("DMA transfer complete\r\n");
( F: h$ \) R+ j1 P! E' W3 J; v - }5 {6 p4 x, r5 y, m$ ?+ l7 i' Q
- void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)& h+ D6 f, F9 P/ t% M; x
- {5 [% m5 L( o6 d3 o/ B
- printf("DMA Half transfer complete\r\n");
- h) C! j2 @3 A4 Q - }, f+ _: [$ X1 [0 ^6 O
& |$ D( L6 D0 A- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
% q! z$ ~5 g1 v+ ~( d - {" Q% ? R' D. f9 a9 s5 U
- printf("DMA transfer error\r\n");. b# a- ^% M- _+ C( Y7 p, u6 d2 o
- }' n; I7 T7 j9 l0 }/ {* `; o0 e
复制代码- #include "sys.h"6 Q; v( z: g/ C' @
- #include "delay.h"
4 P; f6 W3 e d) J - #include "usart.h": E# p1 u0 Q' K4 B: H+ `
- $ s7 d: c" x: i3 c
- #include "adc.h"# t% @) H2 K6 O2 A
- - _. [# K# f3 ?: H6 H: k9 a9 L/ n
- extern uint16_t buffer[128];2 p6 M1 k" c2 x$ f$ F6 x
3 I% i6 [& n V4 Z- void show(); w. M8 Q( X/ ]1 l! B
- {6 r4 W6 F2 b, @7 ]( J p+ u) x C( Z
- int i;
% B- ^/ H; S' R& i. [6 T - printf("\r\ndata:");( P& w2 \% _* X
- for (i = 0; i < 128; i++)
+ U3 h6 a* `5 y {! v2 f. {8 V* r _3 ` - {' U& n$ `5 @ [) k$ {
- if (i % 16 == 0) printf("\r\n");
0 T) A; {2 w. l+ c: }3 | - printf("%6d", buffer[i]);
1 L* h3 S4 G9 Z# W) x - buffer[i]=0;3 P, m2 o' [$ s2 O& p7 K
- ' t* F3 F' T6 F; [
- }
0 N' _" i0 u7 G1 S" i- q M$ ~ A - printf("\r\n");: z7 H c) K# V& M
- }
, K: V b2 N8 D8 F( T9 u- o! V - 5 z4 u6 ?: m- ~6 Q
- 9 s8 U9 M3 P: X0 B; o2 Q
- int main(void)) T6 `0 x. p$ f" h* f0 \. r5 D
- {
6 r% g) X5 K! g }) W: | - //Cache_Enable(); //打开L1-Cache9 B- p* a+ q& c9 Q4 z7 A) A
- HAL_Init(); //初始化HAL库
( X/ U3 @1 ]/ p6 o7 x- J - Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz
" R$ C2 I; { d7 l - delay_init(216); //延时初始化
# [% v+ c- Z$ c* X3 q) j6 V" P+ m - uart_init(115200); //串口初始化7 v2 g/ G7 m) @4 m# e" t* W. [' ^9 Z
- + p' |' c; [, v" ]8 j
- printf("start\r\n");
) b, N; `% z" W+ V1 M - MY_ADC_Init(); //初始化ADC1通道5: [" Y3 i3 q# F h+ q# H' Q
- " {9 V, c0 P8 l" j7 W: J$ x
- while (1)
5 V& ]# Z: `% A, V8 h - {
- o4 `& G9 i% E% F$ Y' ] - show();9 ^% v ]7 R; e
- delay_ms(1000);+ m6 n7 c- T6 V' p
- }
$ k1 Y* z, }0 F2 [% V6 B/ W - }
复制代码
4 G5 U/ k( j( v. ]; T————————————————' k4 T$ c1 J5 m3 I2 ~
版权声明:小盼你最萌哒/ C# E9 O: w$ [' L
如有侵权请联系删除+ U; N+ n+ ]! I
/ Y8 M& S# P# h# |/ A/ i |