ADC1DMA_Handler.Init.Mode: k% ]- K1 @9 Y5 w
DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
! E- H7 g! G$ t# f3 ~" hDMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。( ^* S7 r5 X( V: N1 R0 C8 m& P
虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
$ C3 e* S! s1 Z/ L* _
; q! w1 p# k( d7 n' w6 pADC1_Handler.Init.ScanConvMode! j7 U/ s- E9 t4 Q8 d$ m B
扫描模式(ENABLE):ADC会依次扫描设置的各个rank
, t! O6 z( K0 W非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
6 ] V* ?$ O! r8 e% f即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~) o/ f3 N: M' q8 Z4 z3 a+ \
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。! X" p: w* p4 f2 }/ o4 ^
8 Z7 k! A4 Y0 S- O- B
ADC1_Handler.Init.ContinuousConvMode n8 W0 s: X/ ?3 j- p. X* q
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。2 W: P9 A0 `) U4 e# e+ _
$ E X/ h; ^8 Q& I* g
- t4 `) y! U6 U4 x6 I, i# J而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。% t4 k% Y( o6 p8 i; [; P
: d b3 c. \# @! }" _- T# M# R
) w% z. r+ W& x6 L7 {而将传输长度设置为3的时候,就进入了传输完成过半的中断。
2 } \; w/ \( s3 |+ A( t
3 y7 L' _& |5 E+ \9 @
& c1 T% v" n, W# p" n- w* }6 G
+ U1 D$ J( N9 ]( J如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
5 w( Q% \: x/ k9 e4 D! K; ^也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。
4 d" e* O6 Z: Z3 y
$ K+ [* e5 A* q7 B7 n在另外一篇里看到了这么一个表格,希望能够辅助大家理解:& U0 I* c0 w+ ^, o8 H, `; w
% b- l$ A/ V8 l% u8 }& U7 B
5 b, }% m) w3 {# B
. s$ d* Q) R9 C7 [9 y( vADC1_Handler.Init.DMAContinuousRequests( s K, j! \' K: o. `* p
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。( ]* ^7 O7 H+ `3 `2 J8 m
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。
2 g6 |5 Y# J$ p- M1 ~. O/ w
4 T- B9 W1 c2 T) m1 ?5 a- E但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……3 V i8 E3 R/ @
/ z: ^- A6 [' i+ L/ z, y
ADC1_Handler.Init.EOCSelection# y; A- z: t# T8 D
EOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……; W3 F& r5 ]2 B3 a% A0 ~
填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置
# O; D9 ^: z' d H* i
1 H$ J/ e0 R( z# _( h偶然看到一个帖子的回复:3 ^0 W9 u8 P K! o0 ~1 z! M
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。
|6 v/ h- u3 P! R4 u3 j$ A为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
% M5 g% r) x' P+ l9 F- GCACHE行的数据写进SRAM1。3 c" z2 A; j [) M) ~- u
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。6 C3 w/ L) Y3 r A, n$ D( m
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
. z" r1 u. `4 { J4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。 r) p8 S9 d! R( T; B
$ o( Z% ~+ S( U1 V5 M% p$ Z
在main函数中关闭cache,DMA采集的数据就可以更新了。
9 i3 i6 S8 O, A
) D9 g" y% z9 f- @; m. G8 T4 _- J( V0 v; m% y
/ u. k/ d# w$ X' I& ^4 U6 Z9 y p E
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。/ d/ O% N8 x( O4 M
$ G$ O0 F: O p; H7 }+ t
- #include "adc.h"
/ r2 C5 s/ W, ^. }& F9 X+ v - #include "delay.h"
: c( v. P/ r8 ` - 3 i/ m& O) L; A. z
- ADC_HandleTypeDef ADC1_Handler;//ADC句柄
5 i7 d7 n& [8 x - DMA_HandleTypeDef ADC1DMA_Handler;
; [; e& `6 h3 T* I1 ~: w - ADC_ChannelConfTypeDef ADC1_ChanConf;
3 P/ A+ W/ [. W/ y9 z$ v! t3 X - , x0 A2 B( J4 K; k% S- z, @! N# z9 G
- uint16_t buffer[128];" w$ E- B. x2 q2 `, ]8 v
- : F1 |" X0 O6 `* k; G; c$ p0 s
- ( P. ~* G( }7 C* e" d
- //初始化ADC
7 J# A! m% b8 ^/ k - //ch: ADC_channels
: x$ W+ o ~4 p/ d M- j - //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16* V/ B8 _. g, |
- void MY_ADC_Init(void)
6 H, L0 O& p0 f8 o - {
: P% Z! K3 e& u0 P% w5 T8 r' u8 J3 N - __HAL_RCC_DMA2_CLK_ENABLE(); W2 n+ |8 A3 t" m
- HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);4 T* m/ J. h* e
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);' S: Y; J( W6 {6 [
% G. k& K5 F" s- |! {9 l/ s- ADC1DMA_Handler.Instance = DMA2_Stream0;. O) b( b* t2 W: \/ g
- ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
2 y' W b* b. P/ w2 H) d - ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
) H& s# k; J8 l n - ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
0 _$ Y, C% W' s# C - ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式" `1 a/ A9 T; }4 h0 k- q* y
- ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位
- M4 |3 Q# w+ p6 l3 c - ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位+ t" Q# L$ \1 @
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束
5 Z! K+ e4 v8 t G- g - ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级
# F( k2 D" j/ L0 L - ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/, w2 H$ B; e& {. \ @ p
- - `3 n) K! F6 X1 d* t; k0 {" A/ O7 D
- HAL_DMA_Init(&ADC1DMA_Handler);
* L3 j- p4 C: Y" ? - 4 z2 O, `: |6 P: G2 F
- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来# q6 N% s& l6 V" [' R% W" P+ v
! }! i$ ]$ A9 ~% n6 J6 l7 n+ x- * P7 B& k1 M1 C, ?3 N8 _
- ADC1_Handler.Instance = ADC1;, P# S. Y, Q% u+ y X1 O
- ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ9 Z7 d+ f% ~2 n! M. `8 n
- ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式6 S% C) O# [ @+ ~9 G4 }( ?! |& `
- ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式$ A9 T% u. Z0 b& ]
- ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换 Q6 N" J. f: y# J, K( M
- ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式 }8 q$ W. t. T- b2 D
- ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
3 B, ^3 m* r: r d, L( g) n3 w - ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发- F; t* d7 ~) z9 i" o
- ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
* o+ j4 Z( u+ M4 L - ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1' E- l* V* R) B! ]# t7 s2 b$ _
- ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求
, z! D+ D5 d2 j - ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
' J3 a* T( S1 b1 m- w+ K: | - HAL_ADC_Init(&ADC1_Handler);
# ~2 Y/ B4 K) Y3 I- X, Z
) A0 l& U6 ^1 {8 L V- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道0 _. R+ ~$ z2 U# Y" ~% L) G
- ADC1_ChanConf.Rank = 1; //序列1
0 q7 t, _5 P" e9 o - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
! r- E: q. ]- R - ADC1_ChanConf.Offset = 0;5 _9 ]. a# Z; @9 q+ S
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
V5 u4 U @: X1 Q" j' v - 6 H1 m6 [ P. }' M9 C8 V
- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道
6 W( v# [' Y; F6 P% u X - ADC1_ChanConf.Rank = 2; //序列2 v2 r& g W1 z5 \/ a8 G# W* z
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间$ }/ L1 b; N; R- m; a6 X# g
- ADC1_ChanConf.Offset = 0;
. w& U9 H' U* \. L' C - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
, |% G5 d6 a' a# G6 ^
6 N, `" G0 C( w5 u M7 r- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);1 u: r r2 ^3 U/ W
- }: m: i* O2 j( F( t
* ~; {% ^4 T0 I# S2 Q$ [; W- //ADC底层驱动,引脚配置,时钟使能' q- |9 a9 q e& L- {2 f2 W' I
- //此函数会被HAL_ADC_Init()调用
( r' G( Q+ ~: C - //hadc:ADC句柄9 [7 ^0 r8 K$ f0 ?
- void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)/ N% S- j/ q* Y6 o3 x
- {
7 N8 ~4 O# m( q- @, j - GPIO_InitTypeDef GPIO_Initure;" n S/ n" ?. n C; T; v6 c
- __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟
$ K" ^; t) J8 u1 x; ^ - __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟& k. D- v( c% p- l- v- G6 L G
& S$ c4 F$ A4 O- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5
" c4 m5 P4 B. \6 }: N [ - GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟# C- Q- F9 r* B; l* C% A
- GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉( n3 _2 a5 [8 f
- HAL_GPIO_Init(GPIOA, &GPIO_Initure);
) Z- `- f- ?; ? - }, _# v: J% p1 F$ G
9 q* o( H: m5 P4 v! I) g% O7 S- void DMA2_Stream0_IRQHandler(void)# G1 l( B3 v& {# k, ^
- {
- q9 P$ b: L2 O" ?& u5 L# Q3 i - HAL_DMA_IRQHandler(&ADC1DMA_Handler);$ I4 y0 s- E# q }9 C1 o1 n' N
- }
2 {8 r L4 {) V
& ~$ k0 _4 [8 ~1 k* T" c1 q/ u- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
# x0 G0 X. L, z - {% ^5 b8 n! O2 O$ F8 K
- printf("DMA transfer complete\r\n");* u! ?* Y8 f, a' o9 M9 O+ _# d
- }- z9 {' K* W8 L$ n: X
- void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)* v8 K4 m5 z M8 n6 N
- {1 Y2 X& n a7 h/ u/ d3 @) n8 G8 o
- printf("DMA Half transfer complete\r\n");
2 c( _- y) h! C# ^+ o8 h: Y) h' i - }
6 q0 h' z6 S7 n( d0 U3 b - ) O1 M# } O8 y4 u
- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
- I! F. Z- F3 m/ f5 ~& \% U4 V - {' Z5 o5 D6 A: \4 [+ Z- h D' J$ z
- printf("DMA transfer error\r\n");: A) n7 v7 R$ s z" L, b. \$ h
- }
复制代码 / A" s' E" e! I2 R. S0 y y. X1 o
8 L H% E! N: r# y& l
- #include "sys.h"
1 R3 ?& |( J( `1 K9 M - #include "delay.h" }% h0 B9 Z- J' X
- #include "usart.h"
8 X0 G' U& Z5 g4 _0 ` - 4 T- Y. J5 c2 m! X( X. t" W
- #include "adc.h"
6 E' A: L3 C$ K$ n
3 @8 O! a, w0 a- extern uint16_t buffer[128];6 @; |7 J2 \2 J4 N$ F5 s
& n3 {8 m/ l2 \2 ?- void show()
+ ~! l: C0 D6 G" W/ |! H( }( x - {
0 _) L# a" w$ n( E - int i;( \: z) b* W, D
- printf("\r\ndata:");
# X; P' ~) f+ | - for (i = 0; i < 128; i++)
. ~2 h! B7 v& h - {
" _( Z5 M3 U8 Q2 W) Z! k - if (i % 16 == 0) printf("\r\n");6 |) D' a9 T7 f
- printf("%6d", buffer);% O" W6 s+ |( h4 U, \# m* |4 X
- buffer=0;# R) e4 D* D! E, n
- 7 r: Y. I* Z8 h
- }+ m" q5 y% `) ~" r M$ o2 u
- printf("\r\n");2 B' ~" [$ Z* C1 }% B
- }
* S3 i) d7 k l# T7 F' d7 e
3 j. c( |7 v3 l/ h! G4 Z* D
4 B9 U% g4 B( f3 w# \! H- int main(void)" B0 M* R+ {& D
- {' d7 m* p/ W% }. h$ X% \: A
- //Cache_Enable(); //打开L1-Cache5 P# x6 S. J: L4 b. f" j
- HAL_Init(); //初始化HAL库# r& O% `' |9 s4 ?7 h5 f
- Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz
* M0 P# Y t$ k1 f/ \ - delay_init(216); //延时初始化
) e' {9 H3 p& y( e( L - uart_init(115200); //串口初始化
" a) ^2 f8 u! {4 Q( M6 R, J
1 S @8 }3 w7 l, ^4 W Q! S4 z- printf("start\r\n");
6 ]& Q2 X; r) J3 o' L, Q - MY_ADC_Init(); //初始化ADC1通道54 ?4 v% `* o) ^5 R5 Z
+ M% j" Z3 n/ ?% [" q& _0 c- while (1)
3 s% b9 f, O1 l7 o - {; w, e/ c% x; [- q
- show(); p& a9 c6 K9 ^
- delay_ms(1000); e* f. l( W# {" b. v$ a
- }
& @4 J( Y" M. c - }
复制代码 ! n% n' T5 p( f' T9 y8 m
. [* Q7 t1 R; p y. r9 T' N# e
|