ADC1DMA_Handler.Init.Mode
: _7 P3 _' c0 ADMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
i) K0 G$ |0 p1 kDMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
$ i% P3 p% T1 f1 t" e虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
) w0 E* M2 w$ k `+ Z1 d) V; i( v8 j
) \. T/ {5 ?2 e! \$ J, AADC1_Handler.Init.ScanConvMode; f. U: O: Q& e6 H |
扫描模式(ENABLE):ADC会依次扫描设置的各个rank) g/ l& I& _% y) s; q: V9 F
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
# v/ p5 M9 i" A! Y( ^* r" C0 l即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~" |6 k3 z& d" l1 L: G* M+ K7 g
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。
$ g- ` u; ]( H! s) Q# G, V! R7 u: t' @
ADC1_Handler.Init.ContinuousConvMode. d- o1 A% z) B) p% D5 `) e
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
. f: B+ I4 w7 L4 |4 o+ [3 X. M
; A+ f2 D- _' K% C9 |( T! w2 @+ k5 p) r6 _- t8 I
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。0 _6 J( V+ v5 |% b0 S3 u0 l
5 t5 ]: D" V! a) L1 z' ~6 h9 B
) @1 _, M* q9 M+ P! `( g而将传输长度设置为3的时候,就进入了传输完成过半的中断。8 _2 k5 O, H" U+ Y: @
[. ^) Y3 n( \) S' M( E3 I
, L5 a2 y5 g' z6 @6 M/ j: f
) B `! t4 W$ C3 R; ^
如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
) ^2 O7 R, J A6 f, n* I也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。
3 s3 v. U( G9 U: g
. C5 J- w* j' h6 l {" p \在另外一篇里看到了这么一个表格,希望能够辅助大家理解:
) y& u3 u" w( ] i& ~$ [/ i; W) j x% v1 }/ ~
; l( K6 M% ^, F9 v
; I( j2 M: [6 {$ B. VADC1_Handler.Init.DMAContinuousRequests
2 W0 z5 W' R( ^5 l/ O当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。) r- _ _- A) d! O& }4 K: j
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。5 ^4 A+ j) e' R8 y
7 X& v0 \3 Q4 Y" b$ O1 ~
但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……
; z: l- _2 a) l; ^" ^+ i& G: x0 Z6 d5 r2 d
ADC1_Handler.Init.EOCSelection! A# \0 Z# P! X- {5 Y
EOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……* X& m" j0 m2 Y/ Q8 h3 r' a
填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置
3 T6 S( } P n+ X5 c
8 C+ e+ @1 [3 b% S+ q6 `, Y偶然看到一个帖子的回复:
9 F" H" H& g& m# K, T' w+ i当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。
* d1 b( S. q# [2 B" z为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY, y) G" I; g, N8 N
CACHE行的数据写进SRAM1。
' R v$ q# h, \7 B. l8 |3 Q/ C2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。
; K; x' w2 B: Y, T3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。3 _/ j( F7 B; G% L/ n
4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。4 Z" h) o8 _' \
$ M4 x: c- |" R在main函数中关闭cache,DMA采集的数据就可以更新了。0 R N+ i+ `$ l% e4 g7 {
+ s: V4 n/ |4 O5 Y" k3 I# w$ q
" G6 J* O' S1 |! c% q0 \4 {: m) Y3 V' P: r9 ]: g+ X
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。5 T1 q6 D8 C. c/ H+ l2 F$ x( l' ^
: B* Y' h5 V# |) E) m9 W3 S3 o# X1 L
- #include "adc.h"# O' l+ \$ N& f, |; x% |4 b+ D
- #include "delay.h"1 r5 I& L; [5 w7 P; t
- B& r( y# A y4 _
- ADC_HandleTypeDef ADC1_Handler;//ADC句柄! n7 l5 {9 N' U( q
- DMA_HandleTypeDef ADC1DMA_Handler;
" h, x) w5 \9 s* o6 m F4 ^( x - ADC_ChannelConfTypeDef ADC1_ChanConf;
: D$ ?3 u) H6 ~# x8 y$ ]3 X4 B. t
n% P4 g. u a* l/ q8 f7 p- uint16_t buffer[128];
5 N) B! o9 B6 s; _0 P
, w* S2 e+ s8 ^+ _
& Q$ B( d$ E8 Z' n4 A! j' {- //初始化ADC
) L+ ?" Z4 ^ o! h. O J q' ^/ Q - //ch: ADC_channels
6 ^5 W+ w7 C' l* g - //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
: J9 o( ~+ g' O% Y' A3 l - void MY_ADC_Init(void) W! ~" F( K( t8 V/ Y
- {7 i% i; N# r3 V% a" a$ Z
- __HAL_RCC_DMA2_CLK_ENABLE();
+ ?0 H' F) m k2 O. Q6 M - HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);- c4 c( k# n8 O1 Q& P$ `8 D
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
( M" [8 X* z0 u+ e6 l* |' C) b, X
! y" t' T, h/ z2 F' M- ADC1DMA_Handler.Instance = DMA2_Stream0;
! D- S/ T( i; l - ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;1 U( _5 |7 j5 ~$ u, K( K
- ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;: h! t$ [2 G4 z3 S) O
- ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式$ P" E6 Z3 r/ f
- ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式. ]2 X7 v9 H. g8 I! D4 S' n
- ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位/ b2 P! _5 b) A% D& g) z
- ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位
' [1 r2 A8 f) N& ^; T ] - ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束; |9 I' L! @, f1 r! a8 b
- ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级6 W. P2 R9 a6 [2 |$ \4 X
- ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/
4 V" ?# [! _- p% f2 f1 [5 z; G) c - 3 a. S c9 a% @
- HAL_DMA_Init(&ADC1DMA_Handler);
2 q: ]2 m S" c0 I, ?- q( X
+ p5 ~/ X: |) Y! w% ]- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来
0 z" t) a; d, t/ v% Q' Y
) r9 L& k4 W, A ~ x- 7 M n6 u% G+ S! |
- ADC1_Handler.Instance = ADC1;7 x/ h% P+ d9 E6 s/ w
- ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ: s' T& h( N, A5 o
- ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式
( i, C3 |7 b: m' X+ | o1 D - ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式
& w3 w; E6 |( U" a% D3 O8 R - ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换( }, F( t) S+ z$ N' k7 F
- ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式2 H9 R8 A8 _# X% d) L
- ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
5 d9 {& ?2 B( f9 y - ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发
% p* u& O' C7 @3 q) ]( x) B; d$ m - ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐6 y {, {0 B) T0 P
- ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1
+ B* X* z1 k! P - ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求
. D) L0 ^/ Z9 L- H' N' Y - ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV; 1 f1 o/ u6 f. G
- HAL_ADC_Init(&ADC1_Handler);/ ~- n: R5 N+ x" \' ]6 E! @
) ]( B2 M) m8 y' B- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道
; @* E6 ^+ Y3 D/ c( A2 v2 P - ADC1_ChanConf.Rank = 1; //序列18 u+ e" v! |# q v7 m
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
0 W! V) _5 B1 @8 K) I }0 O7 D - ADC1_ChanConf.Offset = 0;
& D- z8 J- h5 @: _; e- Q - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置/ C3 t( q. C+ Q7 c$ v9 e
0 s' e- V4 p% @# d( F: f- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道
: y- Q ~. @0 M7 E: R' k/ S - ADC1_ChanConf.Rank = 2; //序列28 j& L5 t# }( k0 J7 ?
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间: Z! u" X- p* p3 d# m- v5 W
- ADC1_ChanConf.Offset = 0;
5 N' R' I+ d! Y7 A/ g' N: L! [ - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置+ j0 W9 ^0 W, h" h. |
- 9 ]9 h6 s+ c4 P$ d
- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);4 {# p5 k$ g q& u, ?
- }
* k( q8 a7 t' K7 z: c/ u+ b( } - 3 E' a2 a V! F7 ^# l4 n$ _" \
- //ADC底层驱动,引脚配置,时钟使能
% a4 l' r' r1 L/ r - //此函数会被HAL_ADC_Init()调用
J1 Q3 k* w, H1 Q - //hadc:ADC句柄
; |! H1 L' P" K3 h$ h - void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)4 m! v; H+ K I' a# j6 O* m% L
- {, p% E$ e: V# m
- GPIO_InitTypeDef GPIO_Initure;9 O9 u/ v9 P) H6 {
- __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟
' j5 v- h( e0 ~# f" d7 B3 L! Y+ g - __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟* P5 P- _8 S o8 f4 w H* X
) X* s: s( F1 j( h/ G/ o" q! W4 X- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5: [8 G8 r# T$ E
- GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟7 s \, h' M8 F# }1 A
- GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉
* f7 W- X- x5 h% I9 O* ]; t! R - HAL_GPIO_Init(GPIOA, &GPIO_Initure);
& v4 o# O. L+ z8 P7 [; P4 _ - }
* A9 K+ M2 P6 y' j9 Z* {% ] - : t6 W: f6 E2 @6 r" n7 c& [
- void DMA2_Stream0_IRQHandler(void)! l% `9 z' ^2 _1 Z
- {
: X# X4 Q6 T/ z+ J- }% T - HAL_DMA_IRQHandler(&ADC1DMA_Handler);
% p2 S7 C6 P: o( a# a' g3 f$ T! ^ - }
5 {: K! u+ r- ?( e1 ?
( u4 t3 E4 {; \( T; t1 W- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)5 l/ L! @5 S2 [& ]' c
- {
2 `; c( V1 u) m- d6 Y6 d# ^ - printf("DMA transfer complete\r\n");
$ f. `* T3 Y0 \8 [; t - }% D9 v5 [: \4 q) C
- void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)6 k" Q4 I( t% B7 v n' Q) I' U! h
- {
& c% x1 S E. {* | v ?! Q - printf("DMA Half transfer complete\r\n");6 H) @2 ^8 u, u+ v
- }4 V* _4 C, G% I! ^9 P5 p3 T2 N& _
- 2 x, w* D( j/ [) g# L; H
- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
# J( [# b: v- j% N* _ - {
" L& M, V+ b( h - printf("DMA transfer error\r\n");
7 w* t/ Q, t$ r6 {4 }' a, @ - }
复制代码 9 ~, [* `# f5 F4 E M8 i8 }
2 T+ w% S/ E- [# x ` [- #include "sys.h"
4 s0 Q) L; |* U - #include "delay.h"7 m9 T/ L" t. ]0 h' e
- #include "usart.h". O1 D( e* e1 N# C: U
- 6 D3 J- j Q7 Z! s4 }
- #include "adc.h"$ U& P; M- b1 N+ t$ ?% r
- # b% `5 d" n g8 W/ ]
- extern uint16_t buffer[128];: D& k# W: c5 R9 |) }5 R; i& p
! |5 Q; E+ ^" y9 ~4 O& ]* G( o3 Q- void show()7 p0 s* o. g) b" @ K
- {
9 y1 o) W, o7 v/ l& f; c - int i;
* |) W* H3 r* D0 X6 C( ` - printf("\r\ndata:");, Z: Z. J4 f5 \7 G. Q
- for (i = 0; i < 128; i++)' w+ t5 j8 t4 z- i2 c
- {$ [8 Y! n5 M6 `& [; C
- if (i % 16 == 0) printf("\r\n");+ Q3 l+ C/ ~' E8 Y9 {3 j
- printf("%6d", buffer);
( s- V% j K# X! k B8 E - buffer=0;
6 ?- Y$ G8 ]$ ?+ E8 V! t R- U - 6 r) s4 B+ K8 S5 ]
- }
4 S) n# @) X+ o; O2 ~7 g - printf("\r\n");
5 O7 a+ `; N% \! a" s. N L - }' l( Q1 I0 B8 y( {0 {/ g
+ a) _+ S& t9 M' Q- % K |. y0 w4 S A. p+ n* d0 |& h
- int main(void)4 J2 [ W8 p& l7 A7 n: N
- {
{/ }7 o( v3 G" Y - //Cache_Enable(); //打开L1-Cache
# Q4 Z% J% V4 K: a5 K - HAL_Init(); //初始化HAL库
# ?5 q" }9 }7 g3 e) s2 w) ^; ]- Q - Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz
, O& U; |7 B$ R+ @+ j - delay_init(216); //延时初始化
. h2 y& H/ L3 G ` - uart_init(115200); //串口初始化/ ]9 M: Y" T1 E
- B* E; o3 M9 R- printf("start\r\n");; p9 O! p$ L- H1 w
- MY_ADC_Init(); //初始化ADC1通道5
! W h8 U2 F0 L+ Y1 f% j7 z - ; S! d% d+ Z5 N: Z: r( s
- while (1)+ R3 n! d. G0 D6 ?; m: G }! Z
- {- ~+ D+ S7 G; X
- show();
% j0 D4 L8 J! S5 ]) l" z - delay_ms(1000);6 {4 K) x8 P/ o1 R1 z0 ]
- }0 O8 K' ?. J* n* c/ r3 S
- }
复制代码
( y2 |) c7 f: t' h$ C4 b; {9 f+ ~- u
! G4 r# k' F7 w9 F3 v9 [: T# m( H/ _ |