ADC1DMA_Handler.Init.Mode& t/ i( U/ D& V5 s1 _
DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。$ D# [; u2 E7 a4 B3 k! l+ p8 |2 n- u
DMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
! M! U/ _: y4 {7 l虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
+ e$ L0 s t6 l% | E6 ^* U9 B5 v' m; E: f! I1 X* ?1 V
ADC1_Handler.Init.ScanConvMode; z* [; o6 C4 g5 A) Q+ L/ ~2 u/ q9 S
扫描模式(ENABLE):ADC会依次扫描设置的各个rank6 [; _- Q- g4 B- w2 r( ^5 k' z
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
$ Y# X9 ^4 j$ x0 h* `+ j即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~2 M) p$ ?! r! m* u) K' f5 h
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。
2 q0 q1 K2 b4 T7 i
. M( x# Q' m0 S$ u/ G7 B+ yADC1_Handler.Init.ContinuousConvMode
' ?& \ V1 K+ p; r, H; p4 D% c开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。9 l5 d0 H, w5 v4 f3 \: Y( Y" Q
) b8 a, R9 `) Z; c
. }% V' X$ j4 F, k而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。; H9 T8 E" o2 c
5 w6 Q( _" w& |4 h
* o2 \9 l* E: Y6 N9 O而将传输长度设置为3的时候,就进入了传输完成过半的中断。* X6 G" p1 n5 I
! {$ d4 ]4 O6 h: |/ u$ Q o" L6 n+ x4 w; D1 T
' }4 a. O; P% u; d2 U% j如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。9 u7 ^% c; ]/ P! q! A! b
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。6 w$ S/ B$ H! |; a3 e" V
% ]+ U5 b; r2 O. o6 t! @5 b
在另外一篇里看到了这么一个表格,希望能够辅助大家理解:( ?6 X, V* Q, o
6 P+ s' b0 q% \/ z6 Z8 } R8 T! C
+ l! Y$ U; I2 J) t
ADC1_Handler.Init.DMAContinuousRequests! K6 w/ X! k( a6 q6 _! ]
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。- K8 A3 L- |8 c
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。
, d. S$ Y3 r G+ o- ?$ z
4 O: K8 N( M9 C但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……# Q. a( m h3 d8 ^( _
$ {3 V! J/ @% G& H7 u
ADC1_Handler.Init.EOCSelection B' C4 l! V$ H) n
EOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……4 ?: e" c x8 {2 r; f
填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置
2 q2 D9 J* v9 W( \+ H4 S5 V( A3 l) _6 B& B" ^( }
偶然看到一个帖子的回复:
5 r. l1 E& {- E. [8 M* h. ?& `当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。
+ o8 S, m# q. j# _* | a$ m为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY2 ~( F$ P/ E7 l- H: l- P
CACHE行的数据写进SRAM1。
" O4 r. @& J- d6 t& ?4 g2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。
$ ~0 P' o& u2 }0 K) ?3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
4 p* r; |, _" ^, |) Z( X/ g( R# p4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。3 U9 c( V: a k& E5 |" A! E0 G
' ]( \) I$ x/ k
在main函数中关闭cache,DMA采集的数据就可以更新了。4 d( D' L9 i0 M# R
% k' h" q5 y; x1 b8 k
! I% d, E3 Q1 b0 @) j5 E) K6 U/ p
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。
$ g) y9 u3 S/ s4 O/ g6 h; F& P0 t/ ~( h) n( i5 V
- #include "adc.h"
+ ?" L' _. d5 ^6 p- { - #include "delay.h"/ x6 h( A$ K- b7 n0 l( D; W: o/ F- f
# J# r" {. e6 ~$ E# o% w" V* ]- ADC_HandleTypeDef ADC1_Handler;//ADC句柄: ]5 j# E6 K7 ^4 l0 A- ?
- DMA_HandleTypeDef ADC1DMA_Handler;
/ f" {& `" r/ M; d - ADC_ChannelConfTypeDef ADC1_ChanConf;5 q. ^7 G2 n; U' J6 R! g
- # H2 x6 u3 r; e0 x! q' [
- uint16_t buffer[128];
: t. A* d& N: L8 K- ^) L
3 Q* J6 T8 }; {- m6 ?" B- 1 y0 _7 e( E" ^# n1 O1 _4 @( C
- //初始化ADC* m) H v3 j! v5 ]- E0 O# G1 }
- //ch: ADC_channels
/ w9 C' \; F. L1 V - //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16% o L2 C; y2 Q1 h$ @
- void MY_ADC_Init(void)
$ H8 x2 u; J- M - {+ T8 ]# D- ^: y5 V+ Z+ m
- __HAL_RCC_DMA2_CLK_ENABLE();
! T, p S9 W7 S! V - HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);# [5 H% \/ X# y% l* N
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
( J8 g' u" K- K5 j* b5 D$ u; H2 L - 5 _) r7 J% P( t1 z Q( P; P, U7 u
- ADC1DMA_Handler.Instance = DMA2_Stream0;9 O+ {( k/ ?% P7 G5 l- w
- ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0; [: H8 s+ G) l: s
- ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;# g& Z0 t& {9 U# p: l
- ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式+ a1 b" g z1 |! _& [ ^
- ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式# [* y- p6 T u& A3 K
- ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位! W3 w! I Z2 i3 S
- ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位* o) y9 I# A1 t, c' G( V
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束: J0 ]+ N) ~: V
- ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级9 ?2 U$ {, M) t) N% e2 P* z
- ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/+ S7 O# z4 r; b
6 ]$ Y7 L0 S7 s$ D: g- HAL_DMA_Init(&ADC1DMA_Handler);
7 ?; ?% m0 w' Y% E
8 Z( U) X% j0 y# A1 n- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来1 i4 C" ^) _2 j9 B
G# e7 N, q: f+ C, w$ p! O- # P6 N. C" g/ L L: I. M
- ADC1_Handler.Instance = ADC1;
# C: i5 r3 K; a; V8 z" J - ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ# S8 P/ D! W5 R- V- _; }5 O
- ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式
M" @( ~: t6 z9 M9 | - ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式
- \# u4 B* ^' z- x - ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换
7 p. ]1 M9 D2 J+ R+ q$ d - ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式# X' G8 ^/ u, u4 W( l4 y! J, u
- ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发7 g; q A# a6 V$ F5 ^& Q
- ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发; T5 l0 l- E3 P$ `9 z; k. F
- ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
/ v( L0 j# x1 O6 P - ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列15 t8 @5 } Y2 K+ S$ a Q
- ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求
( N' O9 [* n/ ^% R7 y, k - ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV; 3 F; \; x7 {( K! _. v
- HAL_ADC_Init(&ADC1_Handler);: ]/ a6 t# D3 a& {/ S
6 Q- @# O2 P; y9 z/ L- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道" g: a8 `0 y9 o% g$ u6 q& S
- ADC1_ChanConf.Rank = 1; //序列1. ~; C0 p/ U5 Y* Q( m
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间! ?5 y$ Q1 z1 ]
- ADC1_ChanConf.Offset = 0;2 \% K' o6 `/ Q: b) d' L9 i% D- x
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
3 ^/ C+ Y! f3 w
% J& h! W) [" M; q; ~2 h5 e: I- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道
& q- p( \( N7 ?( H4 Y: Y - ADC1_ChanConf.Rank = 2; //序列2
& b" g% l6 Q9 n. N& p4 { - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间# N4 i1 k2 D+ ~4 E3 T
- ADC1_ChanConf.Offset = 0;
; ~# n3 A0 S- e; u- H - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置0 ]# i6 i1 ], H/ g4 _& I: y8 \
- ! f; B, i; r, a* F) T5 E
- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);. R. G: d9 `$ |( m6 w* [/ Q, f
- }, c e0 v. V i3 Z: E8 B5 F
- , q; [! [/ F3 I' v
- //ADC底层驱动,引脚配置,时钟使能7 o$ A2 U1 K) {0 h% \
- //此函数会被HAL_ADC_Init()调用 g# I% ^( E3 {1 I
- //hadc:ADC句柄
& |; z5 Z9 U6 Q - void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)2 L- k5 }, T' j
- {* L f' p/ ~, ]3 i- Z* G
- GPIO_InitTypeDef GPIO_Initure;
) r1 s4 K ~. n: _3 R: [2 [ - __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟8 o; X% G8 D3 |* U3 o% [' b
- __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
: D! H/ O& k" ]% x/ H$ p6 H - . B3 v1 y7 \4 e+ L! z1 h: e
- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5" S1 Y) w$ T# s3 V" [
- GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟. G. ?( ]7 j: F
- GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉
5 z; Z W% D' }3 }8 u - HAL_GPIO_Init(GPIOA, &GPIO_Initure);6 T+ c' q4 x* U. Z3 X0 @
- }' o$ O* h# }; ?. U" g: T
- 1 e0 A# f! r, q7 I: g
- void DMA2_Stream0_IRQHandler(void)3 b8 I( e1 ~6 E$ K% y) E( s& [' w
- {5 M1 f- s- R0 n# s
- HAL_DMA_IRQHandler(&ADC1DMA_Handler);
* j( F$ `; P* R- j. j f | - }
" h% n4 g/ K8 t6 V' f: {! r
: m5 q) P" p& _: ^" I+ b- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
4 Q- k9 Q6 s5 {* j* q2 w* F8 _ - {7 n, r8 F6 g( b0 u# E o1 T4 L
- printf("DMA transfer complete\r\n");9 n/ h7 H; e5 x4 T
- }# I/ `( [! I9 K( l2 N' H
- void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)4 p5 Y8 w4 _, }' E
- {
: ?- h5 e6 L: }/ |+ T, h+ g* T - printf("DMA Half transfer complete\r\n");/ I. i! k4 \. M0 }0 l
- }
^6 w- E" X, t9 f8 x: r
4 u) B( g- w& W9 v, j s- Z% k/ N5 f- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
' Q! a+ z; [2 w! C O( p* G - {
1 ^: Z4 z$ s% A' A5 `- T( f - printf("DMA transfer error\r\n");
' {7 i6 S4 |1 }7 y9 ~ - }
复制代码
" d y, O/ h0 }+ H0 ~+ z! Y5 I+ ]
: j2 }3 p s1 }- #include "sys.h"0 o! N9 M" N8 Y/ t& k: U
- #include "delay.h"
( x' i1 l2 c; J- b s# V P8 v/ M, G - #include "usart.h"
4 M* A9 i }0 u# c/ P2 u( D4 C P' q* O
" d- D+ |1 S- z: ~3 |) E- #include "adc.h"
% o4 F* i# o' |3 ]) d
5 Y* D% N" R: w. P. m9 t- extern uint16_t buffer[128];8 k5 a9 \# n& i* }* ]) }! l T
- 0 e9 i# k0 [4 Q8 a2 p
- void show(): F+ o; W. l1 z5 e
- {
$ D2 F2 o( [0 N# L5 c5 U4 P - int i;. x4 N& q8 {! e" h, w& O0 M+ H
- printf("\r\ndata:");* b) r5 t* ~. X& `# u' l
- for (i = 0; i < 128; i++)
: `3 q1 s& y, b, `5 n - {8 R2 I$ ^$ z' B: k: k
- if (i % 16 == 0) printf("\r\n");
1 R2 o8 W0 E9 ^ - printf("%6d", buffer);
) d( O9 S2 O+ p; s1 M8 ~ - buffer=0;
) A6 e7 i! ]5 p1 H& n8 B% w1 A f
& `; U3 C8 Z J. N" q; L. }6 q- }
- G. z2 v& s8 B# R' P$ \/ W- G" k - printf("\r\n");. y* _! x& Y4 p8 `& ~
- }
Z: R) V* Z! C) e H' P
- \9 M) Q6 U; {3 q5 n4 m4 T, Q- 2 _! z# ~ p% D3 h
- int main(void)
. x( n6 r1 Z B' N4 D6 i& f" t& @ - {
/ P& v* \9 H3 b& r* e: \1 S4 s - //Cache_Enable(); //打开L1-Cache
8 C2 o; b3 ? M% z7 j - HAL_Init(); //初始化HAL库
6 g: h8 U1 O2 Y. v - Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz
" p: ^# ?6 f: A$ J k* d+ i - delay_init(216); //延时初始化
. y2 g h, \, b6 [' Q - uart_init(115200); //串口初始化
! w! `* ~5 y' j. H - ' I$ J- ~- y `7 {/ Q
- printf("start\r\n");4 y1 n1 h1 s+ Y7 J
- MY_ADC_Init(); //初始化ADC1通道5
6 P/ E. C* H( R( J. b1 B - ' |7 l& B6 h+ o& K, ~9 P* t
- while (1)
9 a( B0 k8 I# W1 X7 ^: L - {; X' F( \' {+ Y/ r; J* |& O% @
- show();
3 W: ] P7 d* f1 S; A& { - delay_ms(1000);
( @9 x8 s, K- G% a$ } - }( Z& ~, _# T/ d' r1 `8 \
- }
复制代码 9 V- c0 p, F. L/ ?: D; m- j
/ l, D% K1 ?3 U5 J
|