ADC1DMA_Handler.Init.Mode4 X& Z" O" g- O( d, l" _8 L1 C3 D) V
DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
# d% O0 D: k, a# J% b2 `DMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
& [( [. N, v6 s- H虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
+ p4 }. c* s& Z& t% A' K3 s/ M9 k. b" F I; q7 r8 ]
ADC1_Handler.Init.ScanConvMode
& n& l9 ~) C7 s( a7 t; K$ x. P扫描模式(ENABLE):ADC会依次扫描设置的各个rank! d% c4 ?) k! R* s# p; T; f! A+ u
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
8 H* J5 n; x' |. e( j8 O; m+ C即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~9 s5 U A6 b; p. @6 J+ U
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。
7 Y' T1 {7 ?% |) j. |% R( `+ p/ r* k; Y
ADC1_Handler.Init.ContinuousConvMode
. s' v) @: }: I7 K! M9 a4 U; ]开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。+ k# a2 O! Q# O! u% u2 V& \
, h$ x) K, T! t
( \! Q8 |$ p- I& ~, R% T* r6 {) c9 H$ Y2 `! u% K/ g4 f* V6 o# ^5 `7 P+ f( K
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。: G9 a) c1 s) o% g1 P
. M' n% `- Z& o" e7 _: ^* A: c. B6 @
9 T/ V8 u" m4 b1 ?/ F: v) o, X2 [, P
/ t; B/ u- z9 a8 S9 Y( P Y( W而将传输长度设置为3的时候,就进入了传输完成过半的中断。
; b8 `& ]) n! u9 ^5 |
" H* Z: s6 {9 s4 n
4 C/ A/ Y2 I9 q2 W$ l4 Q& Z如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。- u# C. S4 ]) E; o) R& d
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。
" |# l' U- C/ I9 j" [2 k( ^2 \" `( N6 O, p
在另外一篇博文里看到了这么一个表格,希望能够辅助大家理解:/ B9 }4 {. N) v2 W; E: a
4 B; }- b: D: n: i0 ~
6 V: [* t5 D- W% w+ [
0 ~4 Z! G% z1 C g4 E" K6 }
ADC1_Handler.Init.DMAContinuousRequests, @5 m$ G& {* E
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。
+ S' E `3 g/ S+ D. b这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。/ [! f! G5 i X+ w6 |
$ E, x, Q: B0 x4 Z5 x( N+ C但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……$ ?* i l: B. W
) i: g; ?1 E' R6 X
ADC1_Handler.Init.EOCSelection
4 x5 w, v9 } \偶然看到一个帖子的回复:8 }: ]. }1 ~( u7 Y
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。4 y) V1 R$ k2 y2 ~
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
8 g! `- d7 q- C3 q9 \4 @CACHE行的数据写进SRAM1。
" B8 E2 a% W. O) R) }0 u/ q2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。. [% M! u' Q8 R X/ j5 O p n
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。: Q2 d, q" m$ T4 S
4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
; H6 p8 e) V5 A7 ^5 w9 e
6 ?! q2 d q& _! `在main函数中关闭cache,DMA采集的数据就可以更新了。, A; F5 o4 a5 W, a
6 u6 I" K9 E3 @3 z. }# X, @
O% |, x$ l# E; O' X9 P
" j- `2 K K, B5 y ^5 V
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。9 R1 f7 N; o/ P; p0 ^* h
- #include "adc.h"
$ y! g- }1 ]1 ?. s0 n, a7 u - #include "delay.h"! ^8 E% L5 ?2 t$ n
- : b! m: l7 q& Z8 Q
- ADC_HandleTypeDef ADC1_Handler;//ADC句柄
5 d2 O# C- y4 V$ Q. ^ - DMA_HandleTypeDef ADC1DMA_Handler;
; }0 N; Q7 h* }; t$ D - ADC_ChannelConfTypeDef ADC1_ChanConf;
& \3 B O$ A% _7 f& g6 g
: Q3 c* _9 |9 q' W/ s. b- uint16_t buffer[128]; @3 \* n5 T% _) b6 d8 q! e3 [
& l* r6 ]' {% z( x# t
: u+ e- U( }' `$ W4 ~1 I1 [" e- //初始化ADC4 ]& L1 f% r0 K+ z- L" f
- //ch: ADC_channels
, b8 z1 d! S/ z# L/ j - //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
3 B! I! S0 R( C0 Q" h2 d5 _ - void MY_ADC_Init(void)' C8 W' r9 _- N4 M! Z
- {4 A# ]& i9 @- U+ ? n5 {' i" O
- __HAL_RCC_DMA2_CLK_ENABLE();
" g8 X9 |9 o' R2 x: p; j, K - HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);+ W% ^1 t/ ]1 h. @ h
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
! f/ E6 g+ s+ g: h. E( P; Y9 i& b - 8 h4 n( ?! a# Y9 ?5 M$ h3 r
- ADC1DMA_Handler.Instance = DMA2_Stream0;
+ ?" v5 i# l R - ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;2 R# O/ x' P" D5 }. |- r5 a0 B
- ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
% i7 ^+ |+ i* ^3 B) ~+ M - ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式8 u \ p( s. Q
- ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
3 Z- d% j3 R+ R - ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位( N( i5 h6 E# O( k) q0 A0 G" s- r- R
- ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位# n$ h5 F$ A( d% x
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束
4 k' O3 ~3 g! u" A - ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级* `5 ?/ }/ \3 g n! p- x+ |4 z+ k
- ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/
6 b" y2 [' M) S7 ]- B: l' c0 B
; w5 Q! h' D$ j4 {$ n. l+ l- HAL_DMA_Init(&ADC1DMA_Handler);9 u$ J& P" `% @+ U$ C% |
" ?$ y, w9 W5 O0 Y# }4 G% c& `/ [- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来
+ T& F: O( p. B4 l, T3 I' D7 a - p8 D4 P. b% T6 x2 c
5 V3 r8 H7 z1 C. M; q- ADC1_Handler.Instance = ADC1;
^- t- j, E2 G - ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ
1 k# A' o; O% ]* B+ H; ] - ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式
$ S& c% {( m' D) \' H - ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式" J8 [3 K5 j* E. U# e$ \5 T
- ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换- I" b2 X7 h. D2 W4 [2 M
- ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式/ J2 e" B6 I0 S; c5 J- D
- ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发! w( m& p9 m1 A: g" J6 r! G
- ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发; G p& D* `8 V% q1 @' W
- ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
4 f+ o( X+ l# _ - ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1
/ h1 l" [; B3 p* c - ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求8 N+ n3 N; c4 }+ t6 k
- ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV; : P0 E0 \- x/ O8 ?: A5 s! Z6 f
- HAL_ADC_Init(&ADC1_Handler);" r+ J! h" J2 t: b, C, e
3 A! f4 F& Q8 j% r- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道
( Y! g6 i1 v$ |7 c - ADC1_ChanConf.Rank = 1; //序列1; X7 M- R7 E. m8 O; D ^0 A
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
6 V! m& S5 f" S) p, N, M w& ?& U' E - ADC1_ChanConf.Offset = 0;
" m" O: E- ]2 q5 M! Q5 v - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置% a. n4 o$ w* T3 n( [
- 0 M8 M2 k4 \( F5 p$ |+ D" g
- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道/ J3 @8 M( j, B7 v3 v
- ADC1_ChanConf.Rank = 2; //序列2$ Q0 T! e4 O3 v: a0 a1 r
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
- ^& _$ p: v8 ?+ N8 r - ADC1_ChanConf.Offset = 0;% Z% Y6 ^. H' v, N* _+ t
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
2 l! [4 X& } M* O - 2 Q6 x6 F1 S7 t! g! r' s4 W
- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);
* D3 w9 x' m F& W - }. b0 x6 u- ]9 E+ L/ Y% n
- , j+ m& @/ {. O+ C' n. u
- //ADC底层驱动,引脚配置,时钟使能
1 t; Y S5 z0 R ]4 p+ ^ - //此函数会被HAL_ADC_Init()调用
|* p D7 a4 r6 B d9 ~ - //hadc:ADC句柄! E1 U H; b: V% j5 G
- void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)9 _9 c3 H4 Y V v9 y
- {3 [. S0 b5 ^+ ^
- GPIO_InitTypeDef GPIO_Initure;
2 L* M( t; r1 L5 f - __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟
! R: o1 _) z' P - __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟) B1 N5 a" [5 L* O, v9 {+ d3 p
- B8 G; f4 ?* k; L4 n- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5
M& e. ?% C" K$ ` - GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟 A( ~* Y( M: ^! E" b' S' E
- GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉. Q1 ]6 S; y. K+ k9 z6 c3 o
- HAL_GPIO_Init(GPIOA, &GPIO_Initure);3 E! _2 ^& \% @ o, `7 a
- }
0 u: Q! m$ D0 L$ W' Y. R& w - ' i0 s. F6 ` n8 _
- void DMA2_Stream0_IRQHandler(void)7 m7 @1 E! E2 o3 W$ b7 x! S, {
- {5 k5 C* l* y( b
- HAL_DMA_IRQHandler(&ADC1DMA_Handler);
7 n$ g! L8 f( f - }
, N& q/ M9 f( p, e - ! ` L4 i1 u- [$ H. a/ h
- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
! ]2 Y, L9 O1 v6 P& E, [ - {
1 W4 x2 Q) Y' {$ Y/ k$ } - printf("DMA transfer complete\r\n");' D' Q# L0 S: o/ d
- }
' q! D- w6 {0 }6 r: a - void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)7 _7 D* k% G% G' Z+ ^3 r' e
- {
) `% ]' F# y* ], ~3 O# ? - printf("DMA Half transfer complete\r\n");
' K* k* x9 f4 S0 X) ?& L; b0 L; m - }/ u# o7 ^, o, I; e/ K2 Q" Y# s
- # J* m. b' l1 ]9 }1 N
- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)5 ]8 f* ^( E8 g/ ]1 p
- {
1 B: Q; o- o# s' K* C% L1 p - printf("DMA transfer error\r\n");% |7 g( N/ ~- Y' m" u0 u% r
- }
- @7 D* t: V: E/ j/ q# G2 c
复制代码- #include "sys.h"
5 l/ b1 \3 g) Y+ Y0 _# I - #include "delay.h"2 ^7 B$ S, f1 h
- #include "usart.h"/ u# K y7 c f9 a! I" I( O8 J
1 s- | z- ?# o- S' I* g* n) G" }- #include "adc.h"! J0 H7 t8 v0 |
- 5 i4 M# Y! |" m3 E
- extern uint16_t buffer[128];0 n0 s6 ?: y6 l! r4 c4 P9 C
- . O, _* |4 Z% W# k$ C
- void show()2 V! \' C, v# g9 D/ E' C; y. u" Z
- {
& }) r* f# z+ ^* ?6 W: H8 T3 x$ J - int i;2 v, m0 i% ?- c* p
- printf("\r\ndata:");
& }! m. N+ r5 X8 t/ _! ` - for (i = 0; i < 128; i++)
( t: q: v* h$ l/ Y: U# r9 d3 G9 { - {
( y- A, }& B) C8 T; M - if (i % 16 == 0) printf("\r\n");& B) F, B' c1 I* Q: U9 B
- printf("%6d", buffer[i]);
7 K9 d8 M% N1 U - buffer[i]=0;
; x9 v1 g, m) T2 ^4 h
$ U4 j5 e' ~- z F- }4 ]6 `1 n1 u+ D7 k3 x. A |
- printf("\r\n");* `' f( r2 n+ n U& }/ a
- }8 ~& C% C, x) B1 O c2 x" M( u
- 0 H8 C! ?' Z: `- g8 k6 s
- a5 U' b) r0 `5 Q
- int main(void)4 d! p! F+ y5 M
- {
7 ?" B9 u2 o* m - //Cache_Enable(); //打开L1-Cache
2 f& }9 I' L7 I3 P - HAL_Init(); //初始化HAL库7 h" B2 O7 r; ?/ K( r
- Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz5 p" k8 J" H1 J; `9 [3 S9 Q7 b0 H
- delay_init(216); //延时初始化: X) d4 |: C0 f9 e: l8 s
- uart_init(115200); //串口初始化 J g% P1 c1 H2 w
- + o6 ^: \2 b8 w
- printf("start\r\n");
' }& G# t0 r; f0 Q, a3 p4 w - MY_ADC_Init(); //初始化ADC1通道5! k6 Y" d: H) S/ S
- * G# o) y; L/ {7 m: |! u6 `
- while (1)
0 s; o: J/ |2 o2 k0 c- z, K/ w2 t - {
/ b. d7 d2 K3 v4 G; O - show();. ]& C! f/ ~ H9 j. p: r1 \) z# C
- delay_ms(1000);+ w- L% X2 _! U; l' S' l' k# `$ N1 g
- }
# p4 D: X1 g n7 F! W3 { - }
复制代码
: R6 o4 n( f Z. K4 u# x————————————————
# N+ n0 d# l J3 D- e版权声明:小盼你最萌哒
; N1 h) W+ O1 m- o如有侵权请联系删除
: b& B1 ?4 s. I6 _3 ]! [
: u' V0 Z7 |! W( d! e0 u5 R. w& B4 d |