ADC1DMA_Handler.Init.Mode; W, N0 N, w$ S m% X M
DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
O$ ~/ W, M2 n2 R# q% R5 L5 ODMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
/ E: f4 |/ l2 g |" O+ { T- i! s虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。2 G6 @% O1 v2 X+ Z
5 \! { ?1 D! p* p% dADC1_Handler.Init.ScanConvMode9 a1 J1 n7 b2 w$ M4 _* S
扫描模式(ENABLE):ADC会依次扫描设置的各个rank9 y( S7 y$ f7 x* i
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
! `* k6 `, t" ]: L即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~, v7 B6 _4 F4 d( M- q: E! n
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。
! E. _" u) d1 R; a2 x9 b0 Q+ W# Y: \* z( Y7 P+ V- Y S0 u
ADC1_Handler.Init.ContinuousConvMode: r( b$ X2 _3 p) e, m" P+ \! ~
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
' r% g3 E7 n3 T$ ?+ Q
' N: x( g% A' u: C8 S$ k0 _3 B4 G1 q; @* o1 z$ K A
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。
. A1 |; O' c# e& p7 n$ O- W& ^) i# r3 B' f: Q. E
3 s, o- s3 d) ~! u( [而将传输长度设置为3的时候,就进入了传输完成过半的中断。
; m8 R0 A9 ^3 n' a# }3 u
7 }* Y4 u0 ^9 t. [+ }2 F
2 y- B8 G( r: H' I8 K: e O
u7 F& T+ T8 y% c0 J% d3 V- f如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。0 E& w! f5 L( x3 G! _
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。: [: t5 E: U/ j
( |2 t7 A& O7 K8 @( v在另外一篇里看到了这么一个表格,希望能够辅助大家理解:
; ^* \; E& a* [' a
# c9 y) D" s2 b# q c* Y- A* l' @% _! f1 l) Q: X1 N+ e$ e
8 N/ @7 W' T* _% ]9 a5 f! c6 p
ADC1_Handler.Init.DMAContinuousRequests J" q& e1 X8 h: b
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。
1 @) X# K5 }4 \- p- r( H1 E) s这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。
0 e8 h& u1 q4 y" B# |# Z! _9 v) j( f
但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……9 _/ ^$ _: \+ I4 v
1 i5 q- c& j! a, V" s+ v
ADC1_Handler.Init.EOCSelection
2 n& b3 G2 r* V; U; ^EOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……- ]; R3 \8 I) Q2 {5 t& {
填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置( _9 n" o/ w+ E6 P% m% J1 i: @
6 }: R: y5 S( s! `9 D# I' R" |偶然看到一个帖子的回复:
! i2 {: m! c' v; \0 t当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。
, e+ Y4 Q6 {8 n; x) Z0 Q |. N为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
/ H2 ?" x* `; l6 z# HCACHE行的数据写进SRAM1。 W3 Q4 O- ]) Q8 |; G' |% Z. D
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。: |. ]: L4 p4 U
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
5 E+ X/ B3 l8 ?( ]4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
3 ~4 s6 e. ^$ G0 ` o
" Q( G; n3 j$ e7 b+ ~$ ~! G在main函数中关闭cache,DMA采集的数据就可以更新了。. i# f1 ?6 o: {5 r2 \- X
5 c2 }! Z% h/ K* _. J# |' z5 e
2 y( y2 U- G, L
5 L/ q( E# L0 C& c9 ]$ k附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。: B5 q- C! h+ K
2 ?* q/ \ `, G( M$ y/ W: O6 i
- #include "adc.h"* N) f; ?4 r, s# ~2 r. X
- #include "delay.h" K/ ^8 ?( y- Y
# H' Z) C5 y' C/ ^% g8 p! _% y- ADC_HandleTypeDef ADC1_Handler;//ADC句柄
, ?' I) m! G9 M% l - DMA_HandleTypeDef ADC1DMA_Handler;( p4 U, d7 d9 T; w1 B! Q# ]" f
- ADC_ChannelConfTypeDef ADC1_ChanConf;
. P# \: f) [8 M
, g. s; J8 B" Y0 ~9 A T- uint16_t buffer[128];7 {+ g8 U+ i. q6 G9 ~
- ; p6 a C: V; @8 E, E6 O7 ^- H
$ R" A' t( W6 T- //初始化ADC
5 z: U' E% T! z6 s* C) D } - //ch: ADC_channels9 d# G6 y4 p' {! p2 C
- //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_166 |; D1 }/ S" q W! Y( N6 ~0 p; A3 y4 H
- void MY_ADC_Init(void)
: y' L7 ]6 Y7 t3 N - {
1 [6 G2 @* I/ g - __HAL_RCC_DMA2_CLK_ENABLE();; n% x, _- h- ~+ f R0 o9 x
- HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);4 E+ |; R1 ]7 E' a: T% H; ]
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);6 D1 ?+ u. ]; V1 c
- # e4 W- z, J2 n6 \% I. ]
- ADC1DMA_Handler.Instance = DMA2_Stream0;. A) e" B, ~" n* i$ m
- ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
0 q% o' A2 J& w$ \2 z; K - ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
?+ ?7 E( F; ?6 i9 E - ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
4 a! U: u8 t+ I# Z" x - ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
) W! _) w7 v4 F1 y - ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位1 j X/ X# i) v; Y
- ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位) W- j7 o: `) U5 ?" f6 e1 {9 ]
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束
9 a9 ~7 K$ M) U+ M: V - ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级$ o) Q4 Y. p3 y _9 O) ~2 X
- ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/" E( z) Z- A& j& z3 f. [/ t, u( L/ R
- 3 D& K0 k* _; A9 x8 a
- HAL_DMA_Init(&ADC1DMA_Handler);
" m5 C# ?1 q) b) J3 J
& O, V p7 z7 b! W5 K( r- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来- W' y2 R' p; i# p0 ]6 E
- 7 \/ C* L# X- s3 \" y8 U
. h0 U0 L$ N2 y4 c5 V3 p( Y$ f- ADC1_Handler.Instance = ADC1;
4 j- h5 u8 F, }$ ~( n9 y - ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ
- {, S* y0 {# ]) S" g/ m. y - ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式
' `9 w& k6 K( y0 \ - ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式 ]% q0 _6 m$ ?7 _$ [
- ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换$ S- F# E% i5 w: | Q! A& b
- ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式
' _5 p+ }3 O5 o& P' R) @; I - ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发9 U3 Y/ z/ w3 @2 x
- ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发
- T$ @. D8 V' I7 h" [. c2 y - ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
6 D2 t3 d$ o# P* o, x, M) Q) Q) Z - ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1
1 d- S* b6 l* {# D2 p2 d - ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求; h- v+ s2 O. o! E, V
- ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV; , ^. C: \, Y8 ?0 e( B3 m
- HAL_ADC_Init(&ADC1_Handler);- y' V @& X L( l s( T3 e8 J! x
- * k* E1 [& g* C4 {
- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道
4 H% q1 a7 p+ B' s5 t4 s- i0 a - ADC1_ChanConf.Rank = 1; //序列1
% Q; M0 A& e5 F0 Y: y! z+ J - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间/ R' _2 j" A! `" P! H$ |
- ADC1_ChanConf.Offset = 0;- S$ x. M n7 C7 L+ u1 g. t
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置1 n- _4 C7 f m7 \* U
- ' X" k; l: Y8 G) u0 ^2 V
- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道+ ^8 R& H0 F o( U: ^7 _
- ADC1_ChanConf.Rank = 2; //序列2
1 A, {. j1 D: A" D% B8 [& e - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间8 u" J3 I* K1 Q
- ADC1_ChanConf.Offset = 0;+ Z* f4 _ M3 O8 B; |
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置3 ]7 Q" y" m- u2 Z' C+ I8 D1 w
) R7 g9 z3 ?/ I' L& U% E- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);; j. O. I+ Z0 N7 @, u+ m3 Q) ^1 N& L
- }
9 I9 H. ~5 _6 d0 F4 D
3 c' F' _4 U; i* n* j0 G$ f- //ADC底层驱动,引脚配置,时钟使能
" `0 ]5 _7 y6 B6 S5 `& x% Q. Y - //此函数会被HAL_ADC_Init()调用: g' S7 y, r& V6 S7 e
- //hadc:ADC句柄
6 z8 u% X* u$ W' k! n - void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
5 \8 D/ a, R/ @ - {
; T6 Y( V1 [" o# t - GPIO_InitTypeDef GPIO_Initure;) h1 f; |& r9 |7 o
- __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟7 k: }- F% P: V: U5 D
- __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟: }5 X$ L) c/ Y$ A) {
- $ S" d, j% g" `3 l
- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA52 @8 ~ ~2 R! [0 v0 X
- GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟
. ]$ e4 }% @+ o2 i" [* c6 q( F" O - GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉% D9 N" ?" E8 f: q4 ^# ~/ Z- K9 J
- HAL_GPIO_Init(GPIOA, &GPIO_Initure);( Y% L8 h" {& y X/ _4 s; j/ X
- }# W* a: f7 G4 m8 ?" l1 O
- ' s, S# ~; p3 x7 L( ~0 H
- void DMA2_Stream0_IRQHandler(void)/ d0 ~: T+ ?2 f7 P! I
- {
8 ]5 Y4 V i7 s Q0 G - HAL_DMA_IRQHandler(&ADC1DMA_Handler);1 i, f5 y' E5 Q7 G N( E( X
- }
& G+ A( {8 i1 Z$ f- k' M
! a& \/ u5 ~/ i# m3 s% N) F8 b- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
. P8 J i( ~/ {9 M - {
: s' A7 [5 E; R" e! ^ - printf("DMA transfer complete\r\n");
; y, A. z7 ]8 g# w3 h8 N6 ~+ k - }6 h) e- ?# l, ?7 ~+ |
- void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)& w2 w {2 M" Q' S
- {
5 j2 j* P3 m/ ?7 g$ p - printf("DMA Half transfer complete\r\n");! r! D! \/ g9 c& `, _9 B P$ [. C
- }
, t& Y' g; _/ k+ m6 L I3 C% ?4 l - ' S# \ X5 Y* M+ J6 `5 h
- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)3 x$ i" Y, F( ^) [% d+ |8 l) ~' E
- {) f( z! h5 m1 \
- printf("DMA transfer error\r\n");
; Q$ e) d' |: e7 J/ [0 [ - }
复制代码 0 v- `" g3 x6 h3 @
7 \5 [- g7 L5 X, w* T6 ]) I6 Q- #include "sys.h"- s3 o; B0 r* B% {- |
- #include "delay.h". R! G. L9 M( {1 q
- #include "usart.h"9 C; Y( U: `1 ]9 w' @* I
- ! T' i! n2 S' \: P% E3 P
- #include "adc.h"1 E1 Y) w( n. P0 s" \& B% q
- . i( k# ?: ~6 ^/ r* x& ]
- extern uint16_t buffer[128];
1 o7 T' p. @2 o - * t- K+ f% }1 U/ x' ~
- void show()
" R& {0 \- k3 v1 l6 N - {0 y) Z+ H, A+ o2 ]+ X# B
- int i;
6 I; y1 i7 i: ^" a) a0 Y - printf("\r\ndata:");0 s* X* B4 T4 ~& o! m
- for (i = 0; i < 128; i++)/ {7 b) n$ P4 j& p8 j, J
- {# o+ T# p( b- r) s% S: d
- if (i % 16 == 0) printf("\r\n");( h7 E$ P, ?2 d+ Z& M
- printf("%6d", buffer);
a1 R: n! H/ K* x+ F! h+ u" b - buffer=0;4 h* \# H# x* V% |
9 Z" Z3 b* Y5 D+ u- }. y4 E% w) {2 E5 Z9 F
- printf("\r\n");# U4 Q) n# n, p# d5 f( T9 |
- }" \( q/ `6 @: G$ T% d8 f
- % H" A, H6 n+ V1 x7 Z" E
- ) O* _) o/ e# s# z
- int main(void); z+ l0 C" t$ T: {" D8 C5 L
- {
$ W! @8 |% p; M. I. D: ~' w( B - //Cache_Enable(); //打开L1-Cache
/ u0 ~8 P4 `3 Y$ Y. K4 y& q* M) { - HAL_Init(); //初始化HAL库 U; W; c6 o4 v5 r2 z' ~
- Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz# X2 E; V( p6 [; k5 S. D/ p1 R
- delay_init(216); //延时初始化1 M( H- J) G5 E4 ?; o
- uart_init(115200); //串口初始化
# w0 Z5 @) f4 D3 z - % R- u- f) Z9 |7 C: w
- printf("start\r\n");
) ~" _# `" N; m2 c$ w& K - MY_ADC_Init(); //初始化ADC1通道54 T! G6 ]3 m! j! J& m, L
$ n. Q. Z+ b/ G9 e3 u1 n8 \- while (1)7 z5 o: k9 _. }; b; m
- {0 p: _6 |- P* [
- show();
8 s$ s5 [9 F: s: H - delay_ms(1000);
, R& ?7 W9 F6 }+ C u - }
& K7 q5 |6 T- w, p - }
复制代码
+ i# ~; i+ O( ^ h% U: q3 A1 n5 K; ^' N5 F# h8 r
|