ADC1DMA_Handler.Init.Mode
6 n$ S6 ?! ^1 U7 ~DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
* `5 O+ b9 b- Q! FDMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
1 i$ A$ d$ b* ^% K! V9 I虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。' w- Y/ X2 R K1 f) H& Y
) V, k" x) R: V- i: [9 `
ADC1_Handler.Init.ScanConvMode. z$ ~+ h1 |4 s. z
扫描模式(ENABLE):ADC会依次扫描设置的各个rank5 ~3 ^' O3 ^" W* ^0 O
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……: z% H+ {7 J1 I8 `9 I- ?# ]
即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~
" _- p9 r ~2 l0 J" `所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。6 T, u& C) N8 O/ s. A6 ~, K0 E% d
2 [- t) L; S5 I. p
ADC1_Handler.Init.ContinuousConvMode+ w3 y, u3 d* s
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
7 u2 I# [) i6 U' `9 R5 W& q d- c7 ~% u. ?+ O4 x, a
* c4 K$ [& Z9 n0 B9 y7 }; o而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。
7 R" {, u3 f* l% }# {" I; n7 @. f$ b) `8 Q6 }! D& p2 H
# z; ~0 c |6 b1 O
而将传输长度设置为3的时候,就进入了传输完成过半的中断。
. U: m9 g# q" v1 o; x X5 N7 @8 {5 _. T& ~, E3 }& L! E) C( V9 X
- P9 W4 f T5 `( a- w
3 ]. m" y c& r6 u/ w+ D0 }
如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
1 Y) @4 ^* T( K# c/ k" L* t K也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。( d9 ?+ t8 P) W3 c8 i
1 r6 ?1 }: U; T* S
在另外一篇里看到了这么一个表格,希望能够辅助大家理解:( w) ~. _$ _) s% [4 t
y5 E8 X/ R1 ~8 d! p, u X" a: { N. c
4 l0 T4 R- w; S. `( g
8 F3 _: @: D' V; `! A' D2 P, m3 u7 TADC1_Handler.Init.DMAContinuousRequests+ A, |6 j5 F$ h P
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。% w' ?* o( F- N( b- J' ^. v
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。
$ F+ E4 L8 w# i4 P8 S1 B/ Y" P4 \! T c
但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……" `$ D( i7 q: U @; t* K T
& H/ \2 M4 s) }0 ^ADC1_Handler.Init.EOCSelection
) ^. g8 v, ?4 T Y3 _* f0 l1 d1 ]$ ZEOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……
: X9 x3 |3 e! |$ d填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置/ T- n' h- d9 b: K
& m4 Z; r u- u& u r8 h
偶然看到一个帖子的回复:
' f9 i, }3 J) s2 I* }! n+ d当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。# `& D1 L. G! }# v$ H; F
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY2 [1 p) `! a' v4 [9 t4 v5 t' Y9 k/ A
CACHE行的数据写进SRAM1。
. D" L1 ~" k8 B- b2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。
. q2 z" X; x/ @/ r3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
' i. ^6 n3 N5 q6 c# O5 J5 a1 b: w% k/ ^4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
. J* j( b% D8 G- p/ ^' b; E, e& q5 Z! T! m# s! r p h
在main函数中关闭cache,DMA采集的数据就可以更新了。
# k6 E! `/ ]$ J1 J! V
% k/ h1 q8 u, A% [6 _) q/ L. _) T# R5 f
% A2 Q, E$ E3 H0 y. r" |
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。
4 ^+ O' o6 l! r% o( j \
- R3 i' D2 F% O8 W2 _- #include "adc.h"; v" B# {# Z- s+ x: u1 s! b/ s
- #include "delay.h"# s: x3 i* d9 U9 m6 q) n4 [
- + O8 H3 T2 o5 R7 ~: L {4 O+ n9 r
- ADC_HandleTypeDef ADC1_Handler;//ADC句柄& e- [3 k9 K& I, _7 u' k
- DMA_HandleTypeDef ADC1DMA_Handler;
* {, K5 W' `7 E0 W - ADC_ChannelConfTypeDef ADC1_ChanConf;. K( U0 `: t. \9 t/ Q$ y' W
* B2 t* d7 l; h- h- uint16_t buffer[128];
: O, v+ m0 Y* Z% {% k
+ X- N5 C( z$ s: G7 g- % y9 p/ J* \6 k$ J7 r+ D
- //初始化ADC2 Y. E! s+ g$ x+ a8 e6 c( |9 b9 g
- //ch: ADC_channels: w& _5 b. `" T/ h( y" K
- //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
$ x6 K% y, J7 J+ }% w6 { - void MY_ADC_Init(void)
' V/ Q {' U" H q - {
" h+ A6 I' X6 q - __HAL_RCC_DMA2_CLK_ENABLE();6 t! y6 w: d! z7 b
- HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
- n5 f' U. ]5 [ - //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
( \, S7 b, o+ ]5 h1 A' ?6 l - 7 T" p/ m( K( o# z% {
- ADC1DMA_Handler.Instance = DMA2_Stream0;" r& a0 J( b8 _) A- Q' Z' Q
- ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
) d0 N3 W$ E( x - ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;1 d) T* X# k3 \! c% q
- ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
4 o4 u$ ], @4 W0 ^, P - ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式, j; d( o, g( l
- ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位
& X0 ]# I, L6 e* K# A - ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位* R; ^# A. B E' @% p" g; @# o" L
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束8 I$ I L, a) x; U* U0 X5 V
- ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级3 p3 A' j+ `7 i( B3 x
- ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/+ Z1 e% f: E) T; N) \; a
- : q7 ?/ P4 f" r
- HAL_DMA_Init(&ADC1DMA_Handler);. c- D; D+ _/ G2 s" `
- , S, j9 j4 [. u/ E5 u5 l+ y
- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来! w2 t6 Y, P5 _4 B+ t3 y
& q/ z, c0 _) v6 h) e- 7 v. v, f! ]" g5 k' j* x" r/ D
- ADC1_Handler.Instance = ADC1;
6 k2 w0 z) c0 }, r+ @ - ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ' d/ A6 J% G5 U' Z9 q
- ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式, o! X; g: B, ]. u7 T
- ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式/ v S* z* \) {) y Z
- ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换1 @# D C: m3 v$ B! y
- ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式0 ~% H5 }& X& c' G4 U- O
- ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发 t ^) s! V' [! y& r- L# M! A
- ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发
: T7 E5 W; w7 C - ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐+ a' h9 d- G- ^; ]
- ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1( U1 g* c6 ?9 H$ {
- ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求1 {, O. n, `% {* k7 T' e8 U. W N" S
- ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
6 r: f( K& M1 G8 B# E( |4 B - HAL_ADC_Init(&ADC1_Handler);: _3 u/ d- q! b. C5 m5 ^3 g2 [
6 y9 o& f3 \; {" _, A) S3 y$ j- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道' E5 |3 |5 S2 ^0 M7 z) _ f: X
- ADC1_ChanConf.Rank = 1; //序列1
3 u1 |5 G! p) I7 |( Q - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
- F9 Q- y5 @6 ?( z - ADC1_ChanConf.Offset = 0;
% {% I0 O$ r3 d( v - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
) y# d! U: r3 H8 {, ~
: n3 J2 [# l- D# C0 J$ ?/ c- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道
7 Y7 f1 Y6 H: w) C( g) P. U# {1 Q - ADC1_ChanConf.Rank = 2; //序列22 T/ r+ j* T& [* ~8 e$ f
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
8 _; b. P% B3 s: }" N5 f7 u C - ADC1_ChanConf.Offset = 0;% \: A# X$ E& r) F# U
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置! l+ J8 {- N9 X: N$ O" J( X
- A9 D; e, ^& P2 }2 e( M4 s1 |- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);
, G1 \3 J! G' Q" D/ g0 T - }
" r: h1 t. P3 X7 f, ^7 z - 7 v7 k0 _7 o% d& O1 N+ o. f
- //ADC底层驱动,引脚配置,时钟使能0 U' } |$ C O# c% e) P' O; Y
- //此函数会被HAL_ADC_Init()调用
2 p8 D" j4 a; K% k+ @ - //hadc:ADC句柄( a; Q4 c" I/ L/ i; L5 k
- void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
" f* \! h9 h: W$ r; l2 z- K6 G - {8 U1 Q- M) W$ Q, D E3 Y& W
- GPIO_InitTypeDef GPIO_Initure;
5 }& ]6 R; L0 a6 {5 z - __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟5 L% @0 J' v5 K) ~* U# h
- __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
0 b: L: E' k& _
% T/ F% {8 c! ]0 q. O* |2 N/ o9 R" [- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5
- o R5 D" l( f0 C3 c& @8 x - GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟
: E0 q& R! C% n) C8 r. n - GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉7 h7 H6 O- J/ |6 Y0 L& m4 _2 V3 K
- HAL_GPIO_Init(GPIOA, &GPIO_Initure);
; g3 Q& u; p( z* i% O - }
! x; Z3 G$ f8 r: h
' o, s9 y; } ^$ v7 ]& H- void DMA2_Stream0_IRQHandler(void)
' l Y7 s$ @1 {/ S$ k) Y- Y- P - {% P. ]" Y( Z; d4 h3 g$ W' Y
- HAL_DMA_IRQHandler(&ADC1DMA_Handler);
3 P' t8 p) d: j/ p; \7 P) a9 F% u - }
: [ [6 a7 c8 m% {: _! w - # \( x2 u, R9 R$ i5 Q" o& U
- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
) k2 [# r3 I0 e - {
; b& A" N( F, g - printf("DMA transfer complete\r\n");2 w+ j1 Q; X0 c5 `
- }
) r. U6 F) o0 l* W A - void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)2 y2 [3 n6 Q3 n4 ?2 ?
- {
% b X2 }. b0 h! h- X* f. j" u$ | - printf("DMA Half transfer complete\r\n");
' t1 z7 m. \* g) I0 d. m/ v8 B - }4 ^8 w/ e1 K* J6 g$ U& b" ]
- 5 s4 F. h6 d$ O; U# R
- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
3 x5 H, P8 A5 n& z5 Z$ {% Y# W - {/ w+ {! J$ q1 l3 u7 y$ D
- printf("DMA transfer error\r\n");
7 p7 X" \# k& C. {# B - }
复制代码 * E, G9 D( d5 }" B) d& k2 B
8 J% W' |8 N7 V7 I7 d
- #include "sys.h"
* k. h( x$ S& y: R1 R5 ^. h' t# x - #include "delay.h"
5 T, }. Z. f1 e - #include "usart.h"
! e- h* K6 J9 q2 Y0 f: E
3 g; t+ C) K( ~, f1 I @- #include "adc.h"" A7 r9 z7 i& ?* k; _0 x
- : g7 A$ I ~# w, d! x# @
- extern uint16_t buffer[128];
4 ]0 V8 Q8 E" e" D2 L ~5 D - " h! s: b: U1 o7 v
- void show()
7 d3 e% e0 N' L1 }1 j - {
. T: Y; P! n4 `( C7 j+ ~4 x( P( f - int i;8 x. @: R: Z6 i# f; x# {9 j
- printf("\r\ndata:");0 d6 }5 q% A; n0 F9 H5 G; X
- for (i = 0; i < 128; i++)
1 q' j Y6 o i0 r6 @$ h - {2 d1 k% }9 [ z9 S" J2 m) K
- if (i % 16 == 0) printf("\r\n");
" D4 @5 B' R7 n" Q; ?0 A1 R" r/ r$ M - printf("%6d", buffer);
) {8 P- K5 w$ T& s& U4 K+ T% w - buffer=0;/ L4 s# ^% h# l4 v! M# [* l
. a( |% I w! _+ v. J( z- }
! l' {; p# G* G3 ?6 @8 r: q' V- \ - printf("\r\n");% D2 w0 m/ R. m
- }4 p6 |7 _6 ^+ D* J9 }
- * U( ?2 U- ^5 d( t
( S5 p1 X* y( [- int main(void)' M7 `4 s" E, c+ F
- {
; C8 W$ |3 I2 `2 k3 s- r' Z, G - //Cache_Enable(); //打开L1-Cache8 l) }* E7 c$ k2 X
- HAL_Init(); //初始化HAL库/ B8 i) n& x/ K' T+ O$ \- w
- Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz7 O* f. V8 q* d* K6 ?# @# ?0 u
- delay_init(216); //延时初始化
/ \- _6 M, u- L - uart_init(115200); //串口初始化
1 X7 o7 y* f; V% F G4 X) M - $ z4 b) Z' g( [* f8 J6 j' Y3 b9 Y
- printf("start\r\n");; |3 x$ H/ ]& D. b; \; x
- MY_ADC_Init(); //初始化ADC1通道56 ~0 B( s$ V, d( V
- 5 S7 S5 o# h3 Z
- while (1)
7 y% n" A# {* N* f - {
' p {0 k; Q9 x1 y" o - show();
. l, y/ R# S# [1 } - delay_ms(1000);/ R' E# J/ \9 T0 c# u3 u3 A
- }# U3 o/ ~1 G8 O$ O8 b
- }
复制代码 $ Z3 A7 R# ?5 e3 I% s
5 y/ V6 x5 `: V6 F
|