ADC1DMA_Handler.Init.Mode1 B; }3 b5 M' d9 I# z
DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
2 r/ L- l7 \6 S8 t; r. LDMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
( ~2 x7 |) o( X- ^虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。* k. z' y! c1 c5 D
9 A E; W+ w' o5 T6 eADC1_Handler.Init.ScanConvMode, z: p* D: U6 Z" L6 c* K" P$ }) C4 Y
扫描模式(ENABLE):ADC会依次扫描设置的各个rank) i2 u/ k4 q2 W( P$ {/ Q
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
2 V" p6 t& o' B& ^, U即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~
! @$ s% ?3 E* m1 n/ C) L7 ?6 R所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。! e. `/ v# r B3 N
; j) j, h0 H1 Q# F
ADC1_Handler.Init.ContinuousConvMode) r% z* v$ u: p, A. n( a ~
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
# J0 g c/ ]- Z% e9 N$ I) G
9 X. |, }8 w V5 l: K" w5 U( l/ c. P9 f# o5 H
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。9 F) G% R/ q) p' E/ p; A. J
0 E4 U) i; |. Y* n# m0 H
4 e3 T+ {) r, f6 x1 \; w而将传输长度设置为3的时候,就进入了传输完成过半的中断。
. [0 a3 U! L% K- m/ i! Q$ f/ X" ]8 g! [' |1 {
9 }% V( x8 P5 ]
; i9 v `: \ w- F0 P, i; n% l如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。/ {0 P. M' C/ X: |7 s
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。
2 v) u5 l Y' A8 E' G( R: ?: }/ m* ?( i1 ]3 S' W* m
在另外一篇里看到了这么一个表格,希望能够辅助大家理解:/ e1 ]' ~4 w' |, U; h# p# N, [
5 f2 j! q. R! f( Z& T9 L9 A# S4 ]2 L, A" x
8 ^1 P! T, y* s% I- S
ADC1_Handler.Init.DMAContinuousRequests# L" D( [' i) z) i
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。% F$ d' o4 [5 g! N# d( P
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。, P V6 |+ G k, @, c2 ~
+ J- ?6 S" W n; ?6 K但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……
: J I: o+ }( K9 v) z8 P0 A3 Y( P; L i, F W$ _5 c
ADC1_Handler.Init.EOCSelection1 z+ M0 o( e I: \ r
EOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……( l/ ?" M0 }0 j
填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置( z+ V* k( S C0 O- b
% q& m# t6 g1 A$ Z9 U8 y# @/ y* i0 M
偶然看到一个帖子的回复:
8 {4 H+ j' w, t, Y* V9 w当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。
- U4 ?1 Z* @* x+ W: a$ T为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
6 u. i; Y6 x4 x7 X/ D6 R' _+ \9 W$ hCACHE行的数据写进SRAM1。
6 ^% L7 z+ F- B2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。
; I. ]5 o( i+ Y# ?3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。7 w+ j+ [6 M# P+ Y: e0 @0 n* E6 N
4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。3 R6 E# N' q) [6 |
, x2 N' L2 K3 g& C在main函数中关闭cache,DMA采集的数据就可以更新了。
) c0 E; a5 W. W/ Z9 m/ b7 @2 T( [7 C! p9 a$ l2 h
- N8 C) U) H' d& t6 v. h4 T
" b7 t z, H3 z, K, x2 o0 s附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。 M! D" V: c6 }/ h" j3 t
3 u9 E* v: Z0 V
- #include "adc.h"
2 G9 @: q& Y# {; }+ ~ l - #include "delay.h"5 p4 f; h2 }, A* @& ~0 s
- 3 k: E7 M6 z1 g0 u* K5 T
- ADC_HandleTypeDef ADC1_Handler;//ADC句柄
3 m9 |) Z# K5 L2 e" U - DMA_HandleTypeDef ADC1DMA_Handler;
& a C& x7 X2 P7 W0 U s- M - ADC_ChannelConfTypeDef ADC1_ChanConf;
. [, O8 Y9 O" A
( _2 B5 w4 |1 ~$ m- uint16_t buffer[128];
, O& `# F4 O* j
* z/ } Y) a E
( f# I0 |4 R u' h% [& ^1 Z- //初始化ADC2 j5 A; H7 ]: K& G, v& e, P
- //ch: ADC_channels( ?/ d0 K, \7 c5 f* z
- //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16. l/ ~! u( ]% _0 |! \4 D5 X
- void MY_ADC_Init(void)6 O2 X. B/ ]% u) r* o0 R
- {
# ^; i% [9 H0 _) u - __HAL_RCC_DMA2_CLK_ENABLE();$ A6 [6 w4 d; [ ]5 K1 \ d% s
- HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0); c( V! n: d$ O7 v, z0 [& |! {/ m2 Q
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);! X1 s% i6 J; l) W7 E. k
( @" E4 ?7 V H/ ^4 n' C% j- ADC1DMA_Handler.Instance = DMA2_Stream0;
+ _/ P( }0 O7 k - ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;1 ~4 C/ J6 U, L; X. \% V
- ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
! k& ?6 Z7 s$ g7 h* _( D9 @3 n2 R$ l# s - ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式2 d0 U0 R' A3 t: |, c
- ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
/ x" Q- ^: M6 O/ p$ n' z - ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位. Q4 V* n9 ~& l. S7 B& I" N
- ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位( e4 @$ X- i7 G) ]
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束
. v$ y% b5 v; C2 L9 y: B - ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级
: K2 M; i9 y- X( z4 v$ A _9 A - ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/! I8 z: ~1 V- \# v4 @
7 b$ R* v5 I# E3 c$ u+ d- HAL_DMA_Init(&ADC1DMA_Handler);
U! d! O. g& Y
, C" ?5 B8 V( u H4 ?: ?' o- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来; a" _3 O% J) c+ E
# p1 D6 Y& {8 ^' w' @- ( F% S2 \8 l: C5 m) l+ B/ C( @
- ADC1_Handler.Instance = ADC1;0 i0 J r) V( i2 D$ r. |" _- v( z/ {
- ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ f5 V1 ~: [" j, _! x5 \
- ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式5 n( Z, P" M: @
- ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式
' n( ~8 p9 b+ w% o& B4 I# x8 _5 {& s - ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换8 I2 N' {$ |" z6 u. T! R8 z
- ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式8 A {1 D+ u2 ]; d- V1 ^. N. v) W$ P- U
- ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发; [. s j7 K4 Y
- ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发
# ]0 K4 i" U; V9 l- L1 z4 v - ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
7 a, E N$ i' X, { }5 g: y8 ^ - ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1
, u4 X- x# |6 [+ ^( u" a& g - ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求
8 z- O6 _" f0 I- H7 g: T - ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV; 2 N' ?- d6 j6 ~2 J6 G
- HAL_ADC_Init(&ADC1_Handler);
8 U2 N" s/ q; T0 f. Z/ A* D
1 {! @9 E. d, `$ V- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道4 @4 e( B4 D4 X: ] X* R) I4 t9 U
- ADC1_ChanConf.Rank = 1; //序列1. Z( a. h- \; h9 T! s( p# R t ?/ Q! \
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
' Y- g0 N. E% g; g" b - ADC1_ChanConf.Offset = 0;
* F* r9 i, n8 R" E/ _# W9 t - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置+ T1 W6 z; ?. e- }) ~* ?7 U6 |
" k' H5 v" w$ u, P. ^* Y- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道& L! D" @% |4 o: r% n" B( V6 ?' {
- ADC1_ChanConf.Rank = 2; //序列2! R- {1 ^# j" i( v
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间/ K2 g, U0 B3 s$ x7 z& [
- ADC1_ChanConf.Offset = 0;
' D5 B/ b( |9 x9 ?7 k$ R* L. ~6 D! i: o - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
4 m3 ^+ h: q, B. a4 x E9 s' x - - @" z1 `; m* f) J' s( S! B v% A; [) D
- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);6 {) O4 J0 F8 O6 N
- }; W9 o( k* f9 m0 j8 G" ^& [8 w
- $ d9 S( j- p7 r+ d' P4 C* Q, }. Z; ^1 K
- //ADC底层驱动,引脚配置,时钟使能/ [( g2 ] l8 V
- //此函数会被HAL_ADC_Init()调用
9 S, c: W$ k, |0 \0 w6 i+ o8 P# P - //hadc:ADC句柄
3 n) {1 t2 b$ B/ a - void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)# \# ]! Q9 {4 B. x! `& |5 ~
- {
" [* Q. T$ e/ { - GPIO_InitTypeDef GPIO_Initure;* D c1 Q1 F2 \7 y; Z+ n
- __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟; w+ @) ?0 J/ D% |) R* @$ X
- __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
, V% Z8 z1 G. I. B6 _ - : S9 I% y9 e6 O
- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5
( i: h" Y5 Y9 D$ g; G! O* D - GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟
; H% U$ d/ r/ B5 K - GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉
# K+ N2 \' D( O4 V3 h) D4 K - HAL_GPIO_Init(GPIOA, &GPIO_Initure);
- h/ f& Q. x* y4 f. n - }$ c. F4 @$ o% \! V$ G8 A% f" C
- / Q) J- q0 z6 o# z( s" h4 d0 s
- void DMA2_Stream0_IRQHandler(void)9 e& ]8 j& v% \9 J2 E7 G" Z
- {6 S" }, Y8 v% f) U
- HAL_DMA_IRQHandler(&ADC1DMA_Handler);
" Y4 s- ^' h* n: L& s5 c - }: m; _6 g; `7 K' R( u4 H
- - M1 T! d# t1 o7 I2 A
- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
' I" A: r! K/ v - {! u3 G4 B' h4 U" k
- printf("DMA transfer complete\r\n");. }, Q+ Q. k! }: `' s4 U
- }% t5 ?, O8 C0 v) i
- void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
^# L. k& ~# h) _- L - {8 a+ [4 |( g8 x8 K' [- m f7 x
- printf("DMA Half transfer complete\r\n");% W2 f9 R- g/ ^2 S/ m
- }
5 w5 r2 F& n3 g9 c8 B/ H9 m
, ~ [: J8 @5 N9 s; X! L) }* I- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
) Z1 n7 C" D9 t' S - {
, p, Z/ Y4 q: A! a5 f! ?; h/ U - printf("DMA transfer error\r\n");
5 W9 J4 m* z s0 z - }
复制代码
, {4 F6 H, p+ _. p, z0 S- w. Y! O M$ [7 _( g$ z8 K& w% X
- #include "sys.h"
# _0 F/ [2 H3 b' C7 u - #include "delay.h"7 t# B( V0 f: b
- #include "usart.h" g+ h9 L2 ?# [$ }4 M9 ^4 F
- 2 E' U l* D- H9 H8 _
- #include "adc.h"
/ B/ f% j; F% t9 ~& R3 b5 @ - $ q$ x: ]9 U' f1 q" Z
- extern uint16_t buffer[128];
6 x0 Y: }' u) u# i& Q. O# E9 v - 5 h% Q/ t9 |6 G, L
- void show()
! ]% _" U2 |, a! d. D' q - {
- M. c! V* Y: d3 r - int i;- C3 _- O* `( O0 l: i
- printf("\r\ndata:");
; b. N" o7 j& s) P- c( d6 h0 X - for (i = 0; i < 128; i++)7 ]1 d( m' @2 ~9 k2 L6 t. E2 l
- {
7 S8 R/ [2 Z" p6 w7 t, x - if (i % 16 == 0) printf("\r\n");
* Z. N* J( ]2 P, U) W9 l: {5 j6 c' K9 K - printf("%6d", buffer);6 G7 ?& {% A) W- L( [4 U- v
- buffer=0;# I; ]/ X3 w" Q1 K' r
- % I/ j' _/ L0 V, R
- }
. x1 k, N O; z/ Y% f1 D2 u - printf("\r\n");
2 b- A) _$ Z" B0 C' u - }
8 Q! ]8 r9 j! A7 W' @* ^
4 @; p" Y; n' e- / A, h- f! r( w. ]9 h# R- A
- int main(void)* b' a$ X) I+ z+ M4 }
- {
6 P2 b" [9 u X; X/ L. R - //Cache_Enable(); //打开L1-Cache! C2 L% c2 m( V4 f _
- HAL_Init(); //初始化HAL库3 [' E6 K& d+ j
- Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz- v, @+ P3 `1 v& I7 S# m
- delay_init(216); //延时初始化
& a7 P) k/ @; ]3 q' R+ j - uart_init(115200); //串口初始化- |0 _* h5 C" z5 ^: H' e
3 a8 g5 d/ t/ w% D, F0 X g- printf("start\r\n");: }% f D2 D" P: }' ^
- MY_ADC_Init(); //初始化ADC1通道55 p0 b r1 s$ q. r/ C
- ' [# N$ i8 M+ Q5 y* M
- while (1)
& `3 A* x+ I/ V - {
0 E( Y0 `8 \4 Y7 S* Q* b+ ^- R - show();0 k- a8 {. ]7 \
- delay_ms(1000);) h$ N$ W6 k8 j7 I5 Q/ `* c
- }1 u* ]1 _# D* H: `0 Z8 p) K* P
- }
复制代码 0 D* V" X9 M' v9 A* A
2 I8 F2 F' W$ d( X
|