ADC1DMA_Handler.Init.Mode
# c$ f {: d! h0 j8 r, ZDMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。7 L z% Y* d9 G/ s: t" W3 M8 |$ L
DMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
2 E) }7 g) g, h7 \, B$ E虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
! H" r: f6 Q) I! j% K5 l; m( Z; d4 G& f4 ]/ y
ADC1_Handler.Init.ScanConvMode2 I7 n5 M/ `$ [9 }; |
扫描模式(ENABLE):ADC会依次扫描设置的各个rank$ J' Y& l9 R4 c4 I8 ^
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
* C. r/ G/ Q! J8 G8 ~+ w6 y即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~/ g9 M* J5 _& Y R8 ?! @
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。) G/ W n4 d: O i
8 A7 `7 I5 T9 y `ADC1_Handler.Init.ContinuousConvMode1 E; i$ y) q/ H1 X! J
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
" B2 k" w/ h, l# l* b% K% p& r- r; h
4 c0 Q8 ]+ d/ j* A( ^0 ^4 V/ k% u# G+ F) W I! D( |5 _
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。
, K! _, a+ {# }9 Y2 [! ^
" b, Z0 M0 I: _, U. f, T: f
' w0 J7 X7 B: N# P
' G! p6 @$ P* `7 X2 b而将传输长度设置为3的时候,就进入了传输完成过半的中断。4 u* }5 S+ a, z& S, a U0 p; A
+ O+ h7 q4 w6 n8 @5 ^
" K) |$ ?5 X& S# k如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。1 |. X* `3 S7 d& j) l5 W
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。8 R8 k& N! @" _
% z& p9 O8 a! g8 @1 w, d
在另外一篇博文里看到了这么一个表格,希望能够辅助大家理解:+ ^2 h7 m; d* D K/ O
9 `0 S$ G2 M o1 d' B0 r4 h9 o
8 C( f' W# q# w- P) k$ ]2 P" W; f D! C/ o! v, s" N
ADC1_Handler.Init.DMAContinuousRequests
6 s2 [9 Q) u. @' R! X0 q" a0 G当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。
5 ^% Q) G6 ]+ C; ?8 J! V( G这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。% D% W) P8 J1 d. j b6 g7 Y) k
( {! l2 i9 ^# u4 f8 B
但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……) | I) d+ c/ I' T
4 K9 `, K) }+ t* P' R h3 kADC1_Handler.Init.EOCSelection+ o0 `+ s" _1 d* Z% ]
偶然看到一个帖子的回复:. K2 {& C9 h: x
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。 F5 C7 m. H6 ?5 V5 X- F+ C3 H
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
$ c; K: W/ a. f" q2 s' o9 C% kCACHE行的数据写进SRAM1。3 x" P6 d6 p) P' s( l3 N# M0 n9 H
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。& X7 I' W' R& H \
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
& a7 p% y& x: g! @4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
5 `# B; [* U; f" X5 L: c: J& G0 G& I8 {0 O# j1 R3 B) {$ Z
在main函数中关闭cache,DMA采集的数据就可以更新了。$ y; ?- D' ?6 m4 @9 u
" @0 n# }( y; G5 @& C$ L/ i9 h
2 A! Z! r. {4 C; x
) H/ l6 D; M5 B附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。6 G2 g- Q6 p0 L. J0 Q) q
- #include "adc.h"
2 u, ~6 z T1 t - #include "delay.h"
! n4 g" i0 A: e* j
6 t; S$ T" S) T3 K6 S0 y. N- ADC_HandleTypeDef ADC1_Handler;//ADC句柄- S8 X, P {7 z$ P
- DMA_HandleTypeDef ADC1DMA_Handler;
1 R9 h' m2 O* m - ADC_ChannelConfTypeDef ADC1_ChanConf;
! w; |: a1 D/ K6 R) |% M - 5 Y5 U O2 j, G- s' K# @! y- h, r% ]
- uint16_t buffer[128];3 K1 @; w, i0 j6 _2 ]+ h
- & M4 ?2 g0 k3 D. _% y
% @' i8 v. p( J) \" Y- //初始化ADC
) y% l' X0 H4 |/ b/ X - //ch: ADC_channels
2 Z9 f4 }8 @" c& @. W0 p - //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16: ^$ P; M1 S$ H+ S
- void MY_ADC_Init(void)
, J5 K: ^6 {% x0 t, B1 \# ` - {- |9 v D% }7 N3 [% G' E
- __HAL_RCC_DMA2_CLK_ENABLE();! H% g# J" C7 W* _
- HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);4 C9 s. Q8 Y4 [- [
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);3 z. ~( m, G9 H
8 f ^& v1 S+ |$ \% \0 q- ADC1DMA_Handler.Instance = DMA2_Stream0;8 Z6 Z7 A4 K7 P$ \
- ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
' G* z& }# O% w. a - ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
8 @! c+ N! R5 _; n7 | - ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式4 r1 _+ Z2 G5 X4 k( Z
- ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
: @# q4 \" G& c/ k n0 A0 j; D - ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位' |" J+ j% Q2 D* W( m3 k
- ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位# o6 U* `" c$ {- x y3 J% c; m
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束& L0 y; m- f, i+ K5 \3 B
- ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级
% d* [; I# f Q: y Z% f# h - ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/
( i- r- {) m" G% _
7 w( W. L- m3 L- V- P4 I9 S( |- HAL_DMA_Init(&ADC1DMA_Handler);
( k5 [+ _/ U- I+ Z - 5 ]' O0 k6 U0 J5 `4 k. v& \4 x6 i
- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来1 ?+ a$ [" h& _3 G
- / Q( T, R& I3 A
3 p( p# ~4 j/ o& p- ADC1_Handler.Instance = ADC1;
& L0 k1 [' t) @2 A. j - ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ$ U& I: m& |, _) X$ K* n
- ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式1 a% a* C* ]$ k$ W
- ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式6 G% Y8 s o. D" V3 n
- ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换
& C( `% `- [- q! \( R - ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式
( _3 v1 d& W" P; `) W1 E - ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
" l# ~( t1 I9 _* P e2 i+ D - ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发7 H' I+ l5 M8 G& A% u3 Q- h
- ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
X/ _" }3 p% K% C3 ?# W8 \+ t1 j - ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1
& g' G& U# T8 g- a$ ~ - ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求
5 o- V) Z0 j( j. K/ d% R$ s0 m - ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
) b# g- u/ K; i4 ?, {8 h; h% U$ n! w - HAL_ADC_Init(&ADC1_Handler);
( `6 Q5 z+ |% F - ! Q X" A( X1 y
- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道4 l0 O1 P* c \
- ADC1_ChanConf.Rank = 1; //序列1
: ?+ L0 l5 m1 X$ b; h - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
0 c$ i4 V0 ?0 F' ` - ADC1_ChanConf.Offset = 0;
0 l. l1 n$ J+ M D" f' E4 Z - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置" y' m! P5 [6 C( E! ~
- + B+ M& o% L" X+ h
- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道5 F2 D5 y4 j$ r' c
- ADC1_ChanConf.Rank = 2; //序列2. [7 o7 R& X" f# K1 R/ u9 t
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间; y2 Y! {/ u/ }0 O' N' o2 X& `
- ADC1_ChanConf.Offset = 0;
) J1 l( K% t/ W( Y7 d) C, n& t - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置8 ~* Z, ~2 C8 z6 e
; l- X/ C( C( G% M! k- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);# @0 p0 t# Q, y/ p6 Z
- }0 C: ]+ c3 _2 q
4 C+ l+ P( d' Z& ~; ~4 u& n- //ADC底层驱动,引脚配置,时钟使能
6 m: T( |3 G& X8 e+ l - //此函数会被HAL_ADC_Init()调用
; D3 [0 b$ U& s' U: ^ - //hadc:ADC句柄
" Y, |" {& n W; s' w' Y8 Q, X - void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
% L" `1 E5 G% t% A - {
% Q0 R: k- m. ]/ ]$ A0 F8 v - GPIO_InitTypeDef GPIO_Initure; K9 p8 v& w& r: H: @
- __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟0 Y" O" A5 V3 ]" k2 r& S
- __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟% O6 N; n" W$ n: |- f
$ e8 y# _+ \8 T- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5
; W$ D7 K. o4 Y9 j' a" ?/ B - GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟
4 j5 \" [! s4 `2 O2 G - GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉$ ]* R R2 u6 Q# f3 }
- HAL_GPIO_Init(GPIOA, &GPIO_Initure);% _/ |' B! ^9 M4 b
- }
) L% s3 E3 t( L" H" o
5 s) Z+ z s) |4 K+ V0 R! R3 @/ ]- void DMA2_Stream0_IRQHandler(void)8 h8 ?3 k+ X; A* V$ c# B Y0 u
- {2 e) y: u, J* N5 d8 T# _; m7 e
- HAL_DMA_IRQHandler(&ADC1DMA_Handler);7 c* h0 |) {0 T1 x, m" `
- } L. Y3 j6 E. U: Q3 U" J2 c7 a
% F: y" Y# M: Z/ E- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc). ^: G7 y9 P9 ~7 Q
- {
' D$ t- h9 b% {: N* C& C: O% j - printf("DMA transfer complete\r\n");
G, Y" A' G! M0 t* t* C - }
6 G/ H5 K2 |) [$ T8 @, y- } - void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
& q! r: L' R W; m7 w" o5 \' P - {
) ~" a# l. S. M; w4 k4 W - printf("DMA Half transfer complete\r\n");
6 H# Z* t7 Q) Q% w0 U - }( s* B( ?6 t- d) |' h6 M' I3 L+ v
- # R7 y$ s ?5 r. |" }2 H
- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)/ O0 D* f$ }% Z! R
- {0 }8 A' H4 G$ ^9 n& R
- printf("DMA transfer error\r\n");) k; c' R3 o- t- l9 W# }
- }! ]* L+ w, \1 J& Z# D H
复制代码- #include "sys.h"
/ V/ Z% ^! @2 Q) d - #include "delay.h"
, d0 N F% [8 h0 J1 O$ s n - #include "usart.h"
& _: k6 Y: ^+ m. z3 F
, x9 e, H5 o$ L. J( p6 X# X, g- #include "adc.h"
: q+ l; G$ a3 B6 _0 P% R# D( r& C
3 q4 y( v( G# A- extern uint16_t buffer[128];& g- U. ?# B& B2 G# G0 c7 }
5 t% X# p/ T& w: X, G% S- I- void show()" T; a+ A$ l3 |
- {7 c; ~" { X2 _- F+ n N5 j; O5 z% M4 `
- int i;
2 n: @. _" O+ A7 R, b) ^ - printf("\r\ndata:");) A1 \$ h+ e) }3 u& i2 R6 w
- for (i = 0; i < 128; i++)
$ r% d& m/ C0 H7 P( L7 `# p g - {) t! P- m) p7 L3 ?/ H3 m+ X' H- U
- if (i % 16 == 0) printf("\r\n");) M% b0 F: d6 Y1 ?
- printf("%6d", buffer[i]);
2 t7 _. N) k! ~ - buffer[i]=0;
$ Q+ s* i. Z2 y- T - 9 e2 u$ `; r( L6 P& g
- }
' M3 y* n) g+ c9 F$ {. H; z - printf("\r\n");1 i2 _4 b0 e) a& ?3 M& e2 E5 a. G
- }6 L% m/ F) s7 f/ C( g
- * S- [# U0 x5 h6 k& Z' A
8 B [. Y8 F3 w- int main(void)
* z4 R# H; J9 |- M - {, X) P6 u, T9 Z/ g/ ~( p' T
- //Cache_Enable(); //打开L1-Cache$ n0 m* h; P7 B F# q
- HAL_Init(); //初始化HAL库
# U7 H& o t9 r& b# i1 c0 \ - Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz5 J& c. f6 N, C( ~7 ]& E! ~
- delay_init(216); //延时初始化3 Y! X0 N3 `9 y+ H
- uart_init(115200); //串口初始化
" f J/ O3 `2 b6 X7 r: s J
& o5 K, | D- j! c; r- printf("start\r\n");! U) S& [) z9 B" `( U
- MY_ADC_Init(); //初始化ADC1通道5
! j3 x h [' ?7 k1 k -
) r7 k8 g* P. p2 e* z5 i* v - while (1)+ }1 H1 q. c" U0 L0 u; a& y
- {- ~! U2 X8 F& X( B( ^% J7 S8 U0 U
- show();
& Z. ?% P, A: W4 C& U - delay_ms(1000);
) X3 e& v( K( D; u, \ - }" N2 W: F+ X5 p
- }
复制代码 - u/ y# `; I: X- O
————————————————5 J" I$ a1 P1 @3 T$ I+ {
版权声明:小盼你最萌哒/ x2 t0 b5 C. i/ _* P
如有侵权请联系删除
( }% h3 ]4 ~- e" ^4 t9 p/ s6 R$ G- Q, B0 g) }
|