ADC1DMA_Handler.Init.Mode
0 a0 y$ q7 c( ^% Q$ SDMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
* v, N0 g$ v K/ t) `DMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。3 d9 T( L2 @- i" e3 ^
虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
+ `) R; P9 N$ _9 S) F: C+ F6 [* m7 v3 o- g% d S+ F& V2 b
ADC1_Handler.Init.ScanConvMode
+ D# \# K" z& g0 l @: D2 u. z5 h扫描模式(ENABLE):ADC会依次扫描设置的各个rank
: x1 I, ~) u# L' k! `非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……) H ^- g/ U) U/ E4 l
即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~
% f; r- G8 S9 {* I5 p+ F- V所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。* B7 p& `7 h8 _* I4 i/ H, N
/ Z9 K9 }% C- w C7 M7 w' z4 I# w
ADC1_Handler.Init.ContinuousConvMode, |, H6 w _( ?. z2 w
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
( d; u& b+ m3 m) r# \3 F- u( V- `9 s# }" X
; c2 T" c# j( d; }, v而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。6 ]( i* _2 M; f0 X. f5 v1 S
3 @5 U. q2 O5 ^' U7 R* O
9 x/ J5 t2 [# Q' {+ I3 P3 h* B$ ^
而将传输长度设置为3的时候,就进入了传输完成过半的中断。: p$ B3 V, f2 l: N3 M2 {+ }
& W+ Y/ H+ a7 C/ l& f1 V/ F6 ^& d9 ^7 U0 D7 J; \4 e1 h/ i9 ?! I
t. ?: ^8 K+ k* _# H) E1 }1 ^
如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
- k1 Z( z& W: D! H! _+ k" }7 a, Y也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。. f) w. q! f2 N
' _' x4 }' |7 u9 o3 T" y在另外一篇里看到了这么一个表格,希望能够辅助大家理解:* b9 c9 x9 a; }* l4 @
: V6 h2 y% K+ X' b+ q; A3 \# R1 `8 g
1 @ `' ^# }) i7 [( ]7 ?4 i w
' {: m! W( a a, [0 {! b( PADC1_Handler.Init.DMAContinuousRequests1 n2 {5 g4 Y0 d, H3 H% E
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。# G: w& u$ v/ L' M
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。& Q/ @! I# j' R) |1 _
1 A( v6 o% ?( }' U但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……
3 U0 i3 T2 X, Q, d0 L; s% ^' b( ]" a J4 m6 n/ g; q
ADC1_Handler.Init.EOCSelection
3 o% B8 ?, B: ~4 TEOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……' |. B7 V t# Y/ w1 V
填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置% N$ d& A5 N3 `
& d. a) y" E9 r7 |
偶然看到一个帖子的回复:( x( r# L$ Y9 L c% v5 z7 _& u
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。) V5 c" k. o4 f8 B" X
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY' q+ X2 H U* S U$ q. V
CACHE行的数据写进SRAM1。- I0 o5 D t3 y @
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。# N8 n1 Q! ~- b7 x6 j2 C' V! r
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
! P9 N+ s4 q! [5 y6 B- u9 H! D4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
/ d e. f; @9 g' o5 G7 t) j+ {0 Q+ E: Y+ `& q8 w& a1 x: A
在main函数中关闭cache,DMA采集的数据就可以更新了。/ }2 n. i& L6 g) h5 I, u
" t, l/ {8 C8 B6 M8 [# V4 s+ f) j; B/ Q& z: L4 Q9 Q
- t7 l; ` T6 p7 d, c: {9 q
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。
; |8 D8 L! ]! x
: o- V( B5 k S) g! { \9 T- V3 d. b- #include "adc.h"
n8 T( I( Q; f. m% J - #include "delay.h"
# @5 H* T& k2 W- ^, ~
* Q+ i6 k! {" n: U; K3 S. g- ADC_HandleTypeDef ADC1_Handler;//ADC句柄1 ^. W1 p5 j) ^ A( [( a6 p
- DMA_HandleTypeDef ADC1DMA_Handler;
% r3 ]3 @& ]4 {! N, u# o$ v# ^, ^ - ADC_ChannelConfTypeDef ADC1_ChanConf;
! ~9 v2 y$ ~+ L# W - ' K0 m+ O& M0 j. [: T0 J
- uint16_t buffer[128];
?; Q( o1 s0 s - , p! i' X/ V3 G, o
- / m) s) _+ p* y. n9 P3 P0 `
- //初始化ADC
, O: F8 v( t. {9 I - //ch: ADC_channels' _; Y' k! \& }8 k; E5 W
- //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
3 N; v2 T6 e5 o2 m! K3 [0 s - void MY_ADC_Init(void)$ \, V$ S4 L+ x% y: `/ V5 p. k
- {' F) d6 a+ i; ~
- __HAL_RCC_DMA2_CLK_ENABLE();
+ Q8 o, t5 Z7 e7 e9 | - HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);( k9 H) Q3 S, x, [. q1 y4 U
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
4 w! E! D7 M5 e - 1 Y# ^4 s6 ]( ?
- ADC1DMA_Handler.Instance = DMA2_Stream0;
7 R- J8 `: g6 `# X. f7 u - ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;! r. i% F+ o; I4 O, N7 q" ?! o4 x' R
- ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
/ A: y9 [' U5 s - ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
1 {4 @+ x; Y: q0 h8 w6 [# f - ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
; G: i: @* \& |6 `8 t - ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位, M5 n. ^3 D: ?8 S6 R t( @' A v
- ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位8 x& @+ |3 n. i
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束0 o, R. |4 q# P' D; i
- ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级6 r/ V6 j: l8 `2 h8 C h
- ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/6 a0 e) G. h7 f2 T
- ; M: a4 f# h' o7 M
- HAL_DMA_Init(&ADC1DMA_Handler);& J/ j/ d! ~8 @: s( A
- 4 S) `( d' `6 {
- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来
6 X w% H# ?+ @
7 {: M$ \% U4 s; c3 u! G# T, j/ x$ a, Z- ' U7 f' @5 w. S: S
- ADC1_Handler.Instance = ADC1;
( E* T3 w. G7 h - ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ
4 I; M# B5 A& Y" }6 _- v1 d; o/ v - ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式1 p; k% S9 }& F# J! w' P R
- ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式
; N& m7 \* t& ]) q0 p5 a0 V |( ` - ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换0 Q$ }" ~/ r$ ~' H7 a
- ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式1 l* n0 @9 m$ k3 T3 e( ?. _
- ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
s: k& x- S7 ~; B) C - ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发5 P) c/ o9 v/ D' E/ A# y
- ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐/ j1 [8 s% v7 g: |
- ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1; f) W% r' h, h; B* @, [+ w
- ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求
" Z, u) F5 ~) w% t - ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
3 x8 N) o( g9 l9 S7 K - HAL_ADC_Init(&ADC1_Handler);
# S5 D3 T5 n2 q - $ `/ g) d( y7 k& \" ]0 p0 q
- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道) ?: L6 r; w2 [) I% B
- ADC1_ChanConf.Rank = 1; //序列1) \* F9 }9 f% E( D. _, r z8 k8 d
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间/ V7 z) l; g0 {3 k
- ADC1_ChanConf.Offset = 0;
1 Z6 U% ?9 S# o8 _6 l - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置2 j" }0 k* C; H9 y
, c. M$ E3 {) I+ |) {- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道
& Z( X2 ?% c8 z. w - ADC1_ChanConf.Rank = 2; //序列2
6 P( e% W( i R - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
4 f0 M4 C. j7 A4 w* W9 w - ADC1_ChanConf.Offset = 0;+ T- h( Y9 a) x
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
1 Z4 M: Q5 |- D - ( y( N" }7 L" R. {
- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);
Z2 o0 O( W6 D5 J N+ B - }/ y; [" r; W' X2 e! }) M9 l s- Z2 ?
- 5 E) b9 Q- T/ Z" g8 _
- //ADC底层驱动,引脚配置,时钟使能' S2 E. ^3 I$ ~/ R/ u# ~$ s6 d
- //此函数会被HAL_ADC_Init()调用- V( A/ n/ r6 t, b, i2 v
- //hadc:ADC句柄4 F' C$ ]& O" C8 o. m. K5 o
- void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)( a8 w# L3 P5 E1 X7 n0 R8 D6 h0 ?
- {
; z, b/ q6 `2 x9 ? - GPIO_InitTypeDef GPIO_Initure;- i2 a ^9 y3 F8 S; b9 }
- __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟
8 h7 |8 I8 f8 ^$ T. F' P - __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟: }7 z7 } I3 W5 l6 y
4 \- R5 J5 @8 o+ L u- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5
8 g* g% x# U! |7 i - GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟! j! `2 w d6 P+ [
- GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉
9 \# I, [9 G5 {1 _. ^ - HAL_GPIO_Init(GPIOA, &GPIO_Initure);
& U! i# x5 L. O' P# H - }
) O5 ^0 w# Y( u M8 H0 c% Q& ]
5 B/ D- J3 m4 r! Z/ i- void DMA2_Stream0_IRQHandler(void)
$ s) }3 M5 a. d" C - {' [. A# n2 ?- R- K
- HAL_DMA_IRQHandler(&ADC1DMA_Handler);* x' K+ R# a9 F
- }
( Q2 s+ ^1 f9 O2 ?' d - 1 s5 K# y+ d( g& l2 K
- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
; C/ u+ u$ m. } - {, X m y0 \. j; y: H/ f' a
- printf("DMA transfer complete\r\n");7 i$ N8 L" i; H' y: L" E
- }
9 H) B& X& C }/ m- K t - void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)1 D2 ^& r8 i& T8 F0 _. |+ M
- {+ _3 X+ P% R8 U) s: A/ b' [
- printf("DMA Half transfer complete\r\n");4 D& P, f7 s/ [- C$ k' Q2 Z5 I
- }+ q6 i# F' H! u
i# T: t: c0 i4 a" e) h! ~* {- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
5 g# @0 a3 d w" Y( t - {
1 A# D" J6 H! F9 I - printf("DMA transfer error\r\n");
6 J8 d9 k$ N5 D! Z! b; V2 o( x - }
复制代码 ! P/ d( h# y* x. J4 L
8 j2 s; ]7 u0 S- #include "sys.h" ~" y u% e; v& u
- #include "delay.h"
( d; T: d/ M8 A( M - #include "usart.h"
5 J7 a) d* O# ?. p& `% N, m& S
% z) b4 `" S5 \) r; W$ B- #include "adc.h"6 r; O/ b4 t4 D, |1 [
- / ^. H0 }5 q V4 S* t3 g
- extern uint16_t buffer[128];/ A7 {6 V8 r& r& y7 D
- 5 `. {4 J* Z0 H4 S% g; x8 ~
- void show()* x( K; w' D) Q+ y- z
- {5 x' v5 l, l) r$ y3 Z
- int i;( ]) e$ Q/ j$ P4 B) y5 t6 x
- printf("\r\ndata:");7 X9 J3 e1 K5 v, r: g1 I! P6 `8 A* |. V
- for (i = 0; i < 128; i++)/ @" s3 \6 K* H- S" G
- {! I, e, z& Y& }+ v7 Q- L. w' q7 }
- if (i % 16 == 0) printf("\r\n");2 |7 L% }) |- w( }5 |
- printf("%6d", buffer);
9 @, C' F2 X: r8 I - buffer=0;
9 S) z% I2 k: V3 N
& S- W' v5 k( m5 v" J0 \4 F8 ~) \7 G- }+ O# q. Z9 b8 K
- printf("\r\n");+ n& Q7 W9 V' ?& _6 c( U( R5 }
- }! c* Q$ |" r' e9 W
: b+ ~4 m) @% [. C$ K0 u6 h* u
4 d" E( c$ O" Y O9 p- int main(void)
h6 X$ {: l) ~% Z( n - {2 G: [: ~. A0 I) |2 d
- //Cache_Enable(); //打开L1-Cache
+ p! @3 X+ B# U) W8 T; X4 J( `+ D - HAL_Init(); //初始化HAL库0 E" c. G% w \2 P. S7 D3 y# a8 G" `
- Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz
* \; Q" W" U3 M8 n; | - delay_init(216); //延时初始化
+ d0 Y# ~3 V2 y ]8 Y1 w/ u - uart_init(115200); //串口初始化
4 y. y7 ~/ e6 p% z
, H l+ a4 L( L- q# L! ]) {& H) K/ @- printf("start\r\n");5 z! x8 x& E2 {6 v& o7 O6 G* b" k; G
- MY_ADC_Init(); //初始化ADC1通道5/ g$ K$ C7 `9 B1 S, L* A
- 1 w" v( y3 A9 i2 x# ^7 q( Z2 j
- while (1)
5 R: K: N! |3 ?# o8 |, W - {; B1 O! {5 I: s
- show();
+ n. |/ r/ r5 ?) ~6 T - delay_ms(1000);
2 H% a% y/ p) ?" f/ Y2 k - }/ h+ T& x; T) C3 x- X
- }
复制代码
) I! D9 M$ }( I) o$ J6 a4 l: ~, p. r8 n/ r) ]& x9 |
|