ADC1DMA_Handler.Init.Mode
9 e: \5 |- T) U4 xDMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。' L7 m4 I/ F ^5 N7 h; z
DMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。9 Z# E" R' i: O# m! d
虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
! J3 Z# y1 S) U1 R5 v. g! u4 c/ C9 N0 w, V- ?+ h
ADC1_Handler.Init.ScanConvMode ^" I# j* W) b9 x
扫描模式(ENABLE):ADC会依次扫描设置的各个rank
- J& P( \) [7 ^, E4 G非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……* \! t7 t Q! f8 N s! K
即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~- P$ q, f3 g2 c% Z+ j3 t
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。* U; c7 x, Z9 G, H9 g
! F9 B5 {1 B+ e8 |% J5 w4 LADC1_Handler.Init.ContinuousConvMode
' _6 G G7 D1 f2 m开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
! K" V' [- ?* J" P9 v x' O' V0 F0 s0 F" c$ f' D6 B h
) d5 _8 I4 d4 A' [: `
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。$ o* \1 X, C+ P( r
) |$ U, x5 N9 I! ~0 M+ o9 V3 ], k* a1 |: m+ J/ r6 H) j
而将传输长度设置为3的时候,就进入了传输完成过半的中断。/ e1 \) W/ s& b# h! x
7 Y7 [1 G! u2 l% }1 ?8 h6 d& { J7 H+ S1 H$ Q
' L/ a+ J. W! B
如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
* X' q3 p$ {" }8 I" w也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。' b% Q' d1 w) f; X, n; Z
6 f8 `: b/ A8 [: F8 c
在另外一篇里看到了这么一个表格,希望能够辅助大家理解:
& U% G! y% h" P8 p8 p7 |! b5 q2 c- N6 B
. _# C0 }$ X( n
4 q1 ^. C8 Z. Q5 U2 WADC1_Handler.Init.DMAContinuousRequests& [' y6 _7 s% V: d$ {( Z% [
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。
: X' L$ q+ c4 |! {! v- T. e& @- G这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。+ E ?) J- v: _
. @$ q- Q2 F! l/ D- M: H. h2 l但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……
! c) O+ {; v! t7 V/ x: l8 S3 Z- m! T4 S. {9 c
ADC1_Handler.Init.EOCSelection
# I `1 ]4 d0 ]' T5 _EOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……
1 { c9 J: H9 S7 f, _填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置
2 w3 P Q Z" k1 X
b6 { M! D& E- H. X. d偶然看到一个帖子的回复:) ~8 c, v& S4 W& z# F
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。
3 F( D5 L4 d! X" E为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
4 |+ @4 X. v$ K2 \2 I" NCACHE行的数据写进SRAM1。! O5 y1 S$ `# E; F2 Q! t
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。
9 C8 T+ v4 s9 L4 [) W! `# r! f3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
2 g( `3 ^; U5 e1 T4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
; N- Z$ ]6 v& T+ ?5 J% B# n7 J! x% b- D
在main函数中关闭cache,DMA采集的数据就可以更新了。# d; m+ H; [# V' O4 M" I
; O$ ^9 l! Z" I, g3 n3 a3 w7 S1 ~; _. i0 {) @5 c( Q# k
* r8 W7 q1 R: p+ V附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。
- }% |$ q$ a3 |- T/ n' ~' k# E1 h6 C& F6 o, n5 Z% \: N' p: S
- #include "adc.h"
- R: w: j. B7 C - #include "delay.h"0 A1 E$ k* s3 M" W
3 g6 I; w' H% D! ] Z- ADC_HandleTypeDef ADC1_Handler;//ADC句柄: a7 k$ `6 [5 O, h
- DMA_HandleTypeDef ADC1DMA_Handler;5 S% N" H5 Q! b- i" Q# W
- ADC_ChannelConfTypeDef ADC1_ChanConf;
+ L* W+ l* Q2 M7 W/ ?
& f3 j$ S+ Y/ e& O/ ~: M- uint16_t buffer[128];
, a' x5 |$ ~: z3 t0 H0 Q8 j - $ Q% e: Q$ q; Q4 F) A% U! L
- 3 M/ I. @* o1 o$ l4 {3 A' b
- //初始化ADC
/ v) j' ~# {+ f - //ch: ADC_channels) Z H& u5 t8 E$ M6 j/ M* A
- //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16( i' z, A, Z+ l5 M3 M, f5 T: c/ o# E
- void MY_ADC_Init(void)
- W' L, B% f8 i0 p - {; {* T! R- U3 S' R: {( S$ }- D7 x
- __HAL_RCC_DMA2_CLK_ENABLE();
# R" k4 W1 U- y: ]- Z/ p - HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);4 W4 q- Y! p+ M- g% C$ E
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);- V8 @" z. n2 o" _' u1 T
- 4 Q8 U. n. l# Q" M# r* V; s
- ADC1DMA_Handler.Instance = DMA2_Stream0;
+ |. q, R8 v& f8 [% K- u* } - ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;) u E- @/ h) Z; H4 t8 o/ ]+ U8 z/ P
- ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
! | v* T1 H. n4 p, r - ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
" `2 Q. ^3 \ p# L. W2 W - ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式, F% M$ t1 s. I' g1 S. E/ k+ D. C
- ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位
. |- `* ~/ G4 c* r: o6 ?4 Z - ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位
! W9 H* d$ ]) E+ O# q* |, {- s - ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束
) b0 T/ B) Q0 A* F) r+ o - ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级
* P7 B( c4 `, n. y! p; f9 V - ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/$ n# D5 Y1 Z( f/ _8 Y" k0 v
- $ m0 B5 Z9 s3 U4 b4 y+ g* x8 \
- HAL_DMA_Init(&ADC1DMA_Handler);8 C) B" E/ @9 a$ v- F/ f" g% U, a( G
; M0 S/ ~. W: q1 g- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来
+ h0 }6 Y) T8 P: H8 @& `$ L - : ~4 s% K- E* _
- 2 \2 r% S% Q$ |5 S7 \: Q+ O
- ADC1_Handler.Instance = ADC1;! t- ]+ g8 n" L, h6 V
- ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ/ h {" n7 Z3 p$ ?/ `
- ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式
: O0 h+ M/ M; ~$ q! K - ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式
+ e- e2 u5 U+ f* C4 B ^( o - ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换
2 Q9 {6 M2 f' c' Q1 P - ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式
- V% Y$ {4 ] U. W, s - ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
/ C4 A$ a9 b: A, l9 n - ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发
- i( c$ H- k$ D5 ^ - ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
/ m/ h. Z3 h2 t3 i2 M! F* y" C# ? - ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1
/ e: |. L& P- p" g - ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求+ F" W# D/ d% m- _
- ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
, ]- w8 h' w" a# q, b2 p7 C - HAL_ADC_Init(&ADC1_Handler);
7 D# ~+ E! e+ T0 ]% C% F - 4 F& ?, d7 \& z8 l
- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道* v; l9 O) n& r; J" `: ?' i2 G2 M, c
- ADC1_ChanConf.Rank = 1; //序列1# t8 b6 v) I+ [ M
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
" P8 A* U9 ]7 @2 Y$ h - ADC1_ChanConf.Offset = 0;& [& }* x9 s3 a( w0 O8 E" N
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置$ c3 E. g+ i7 u0 T' v
# A" L" T; C: q! B$ r! {! a- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道 t+ X& w/ L2 t& Y, ~8 }
- ADC1_ChanConf.Rank = 2; //序列2
" z! h' N: P# k8 N6 p# g/ Z - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
# }# v; ~( o [$ l8 { - ADC1_ChanConf.Offset = 0;
p. \- _5 p x1 R6 o8 { - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
! N [& O5 q1 v$ F. S% Y
2 i5 Z9 N, h8 N' _- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);
' \( t( g& N' K( r - }9 N3 x' ]" M F; C
- & i; g7 h1 e, h6 l: J
- //ADC底层驱动,引脚配置,时钟使能
. W7 O! u" ~" A0 \' l - //此函数会被HAL_ADC_Init()调用! Y3 B5 q m( O o+ B
- //hadc:ADC句柄, f. l8 V" o3 T
- void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
2 f/ m R3 p& ~ d( m - {4 n9 Y+ Y6 F& I8 d; n" L
- GPIO_InitTypeDef GPIO_Initure;
: m' j3 [5 N: j: l) C - __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟% G0 ]" T; P4 e! {2 n! }
- __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟$ {0 i' d9 T0 |" Z! L; x( @- c6 q
( ], a: o5 [# k1 `- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5% V4 P1 E- R2 K1 P" a) `! F5 u) u
- GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟7 C: C% B- A, h/ k7 L3 d8 R
- GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉- }8 r: ?- ~5 Q0 Z1 K5 Q- k9 J
- HAL_GPIO_Init(GPIOA, &GPIO_Initure);
3 _" C8 f+ l3 p- }, { - }. L( f8 V# P1 y, f' E- C
5 k4 }2 }& ^0 Z5 T' t9 Q- void DMA2_Stream0_IRQHandler(void)' U, H- M# @: g6 J4 Y
- {
2 l5 O, {6 ]3 n5 ]) C - HAL_DMA_IRQHandler(&ADC1DMA_Handler);; Z$ ]4 B8 O$ K4 w* E
- }6 L; i9 H. m6 I+ V- Z6 ?2 E
$ ?' G) W3 \- K X" T$ u: }$ F- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
( j$ Y$ w! Q( L# R5 }) P, r - {8 c3 a! B j9 h- N- E, W$ e( ?
- printf("DMA transfer complete\r\n");
' c* I! ]7 S! v; j( c* Q h - }6 C7 [! f+ l+ G& j# F
- void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc) a0 M1 ]; v! E/ ~' [4 |
- {
8 i* e; z1 d: `" B - printf("DMA Half transfer complete\r\n");
[" G# M8 J: I/ ]& I/ B - }( R) a; d( c, Y5 a' q) m
- @5 ]: v) _/ A: f- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
/ R5 W# ~7 }. y4 { p9 q' A! m - {2 I; }$ @* |5 B) a% I" `8 b! T
- printf("DMA transfer error\r\n");/ \" W/ f4 {- j5 u( J, R1 ~
- }
复制代码 1 G% D- @7 D9 m" H
* ~/ m3 j$ Q9 h8 I- #include "sys.h"
e% a g9 m9 Q' _ - #include "delay.h"
% ~7 s) ~3 h* N3 y- c; I+ n3 K( F* | - #include "usart.h"
% \ Z# \) ]; H% p
' W _3 k5 f- L( c0 i P: s: R- #include "adc.h"
, ~! F) Q/ Z4 S- ]7 C) S2 V6 _
0 G% Z6 ~8 K0 N- extern uint16_t buffer[128];6 ~; s6 U1 ?" t3 @
- / }" T- E4 f3 F; z
- void show()4 N$ m4 f0 ^ q) _* x/ _) J
- {
& T0 b0 i8 K& X/ T) J0 }9 Q - int i;$ ~' Z& X/ Z- J% H e$ u, O. Z
- printf("\r\ndata:");
2 F v$ P7 \5 `0 A: N- n' ` - for (i = 0; i < 128; i++)2 r) r) Y) g/ _1 \/ X# ]
- {( }4 P" q* w4 c6 @# t# \
- if (i % 16 == 0) printf("\r\n");' t3 Y$ L0 t+ e5 ^
- printf("%6d", buffer); }5 E) [5 i6 h' _, Y
- buffer=0; p y& {! Z r
8 K9 z6 s/ s( h5 I1 x) h& d- }
& G2 s1 _$ F. j. h# E# ~ - printf("\r\n");
4 a I3 E2 T+ t \1 _. W - }9 ?# d& m8 f+ h
- k" Z7 h9 D9 f
* O. j4 `+ F. o4 y# t- int main(void)
o' R4 P- x/ ~* g- s1 U: y - {( h7 q: i, d: U' g
- //Cache_Enable(); //打开L1-Cache- B' a$ x7 f% T1 m. @
- HAL_Init(); //初始化HAL库
" [1 I* o+ i2 \" Z7 @4 ] - Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz' K7 c8 d4 h7 D( K4 V' g. M
- delay_init(216); //延时初始化
. A' o4 s7 Y* H9 d! i* n! M% ^ - uart_init(115200); //串口初始化
+ ?5 M. H$ l$ O& f( m
, {' u% w- w9 B- printf("start\r\n");
8 ?1 Q# P: [# o# r& Z9 F {$ l; d. Z - MY_ADC_Init(); //初始化ADC1通道58 \5 [3 `* f: u5 ~0 H' |* g0 u$ [2 }
- " o- O2 S4 J2 c5 k4 E" Q
- while (1)) T0 A0 H" T" \1 x
- {& R2 u w0 w) t$ F0 U0 s* L) Z
- show();8 `: W9 q2 l) P) D" A
- delay_ms(1000);
- G6 o8 Z1 n5 Q# @. U' G - }" `+ L/ I' ~; t( W( P1 I7 W
- }
复制代码
# O, L3 ^* L0 t" M) \2 r
% o* {7 W" h- Q5 }9 u, k7 D |