ADC1DMA_Handler.Init.Mode
- S# k1 v, c% F0 X+ TDMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
& p; {6 L9 ^, y0 X) X4 BDMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。2 B* X; B6 E/ [) {2 U( U
虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
& q% o. b1 k: k9 I, l9 F% k
' |( ^' A% I# _ ]' XADC1_Handler.Init.ScanConvMode
: F; z' f! [( A; t# T. H扫描模式(ENABLE):ADC会依次扫描设置的各个rank7 D7 ~- e8 f9 K6 _6 f$ Q8 G9 m
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
9 ~8 O2 L) |5 r z6 t; [即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~$ ~4 d o! X7 u7 H E
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。0 u7 a. A( Q5 k9 z. ^
+ [/ N1 s5 o! B" p& e/ x
ADC1_Handler.Init.ContinuousConvMode
( T& W" s8 x0 n% Q& }. J/ K开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。& u0 q/ J# Q7 v2 n& I5 A* a
! g4 g0 d5 i+ a
# E. F- y: h" i7 A z) }& `
9 t% ? Q) N j! M
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。
0 w x/ D: J i @% h) T. O4 Q0 ^3 a0 n) u; X
8 F3 |$ f; w3 t% ~* n$ Z
+ _) [' g9 ]5 k$ t" {; y0 ^
而将传输长度设置为3的时候,就进入了传输完成过半的中断。, O8 n: K7 A/ a/ k
. m3 F4 m8 C9 P7 F
J* H3 e2 v/ w! \- |如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
; V9 S) t& ^ B' R也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。
5 s4 k1 [1 \7 |4 G$ ?- n
2 D6 V% f t* c! K7 X. M在另外一篇博文里看到了这么一个表格,希望能够辅助大家理解:
: K6 a8 O; I& u+ T2 ?7 [$ ~
' X% f; J+ a" w
, G5 x# r& u! D5 n
9 {% E# b9 E) ]4 S3 n$ dADC1_Handler.Init.DMAContinuousRequests3 Q0 t* S- j6 I3 U4 y
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。% v2 Z- S% M7 R
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。
7 V) u" J, C2 M
* @/ p1 J$ B- d" ^" }# g但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……. | ^7 o4 [# _( S- e' o+ M e
7 }; v( n7 _" ? g) H
ADC1_Handler.Init.EOCSelection* g' I# o" Q; ^8 u/ [0 M( j( k r
偶然看到一个帖子的回复:9 }2 l2 \3 `! l' W1 X
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。6 K1 J& p8 U' S# R5 m2 p
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY. Z( A* s, j) d: x
CACHE行的数据写进SRAM1。& n; {& |* v. u2 {7 u
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。4 y* m7 M0 U' G+ X( K( l# I
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。. O7 q& s* A! y4 C3 @
4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
0 ^* d/ c6 j% P x! m. v
. F% U0 Y A/ U$ @) V1 E在main函数中关闭cache,DMA采集的数据就可以更新了。
& T, T! [+ \8 b+ z% ~$ J
0 k }: n# A" \
+ @" C ^: d$ E3 `7 x0 @" |' V* o3 {( _$ D# i' O( o2 w: H, H* e
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。$ V# \+ w' |% Y7 L) ~+ J
- #include "adc.h"
; z, m* C+ G: f4 B/ d9 l& Z1 P0 { - #include "delay.h"( T, C1 Z# U0 Z
- 3 x" s5 s) x0 R1 B u
- ADC_HandleTypeDef ADC1_Handler;//ADC句柄; I' i0 Q* |! A+ o' z1 ~4 } d
- DMA_HandleTypeDef ADC1DMA_Handler;% p* u6 F( M8 Q) q% Y( S
- ADC_ChannelConfTypeDef ADC1_ChanConf;
1 r1 z' }: f& e8 a6 G
1 l/ c; u9 X: v% ^3 w* S- uint16_t buffer[128];: T. e: T! O& f
- " p% w: C" c, V. Y' H1 P( i
" H5 C4 E& ?4 d7 v- //初始化ADC/ A- o* E C+ a5 Z! C& q3 b
- //ch: ADC_channels
* T) M2 L2 g, B7 S0 F - //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
/ a6 Q" I# L, T# ` - void MY_ADC_Init(void)
7 X6 _, |* Q; \ - {4 |# E/ i6 q) p2 W
- __HAL_RCC_DMA2_CLK_ENABLE();
( ]7 m9 n" E( G: O - HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);6 x" r0 Y9 ?/ S- R7 M
- //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
* L$ b7 V3 T+ n2 b+ t0 |" x5 W; ^ - j" y" }2 N3 N+ y' [
- ADC1DMA_Handler.Instance = DMA2_Stream0;. I# n) W5 K! h! G7 B. T% ?
- ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;! j; Z5 V. N0 l0 v$ E
- ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
' S- M% ]0 ]7 F+ I3 X - ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
( J6 }6 t. m& J- S0 b - ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式3 Q$ q4 g9 I5 b+ i H
- ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位4 O' ^* U8 h6 Q5 h. ^5 ]
- ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位+ X& |: d7 C3 N. W
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束
1 l" V1 P8 V# `/ \, n - ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级
$ P) h2 ~6 g; E - ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/
8 U& |0 x2 `% ~/ k6 z4 p: j - % j; r/ p! C) O: @; `% n# a6 ^
- HAL_DMA_Init(&ADC1DMA_Handler);$ d' s7 ?- h& j( u) O( t
- / q" A% W; ~+ R" y( K/ n6 K* }; f `, Z
- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来
9 U; q$ }# L- Y+ c8 j+ G
( u) p# m( Z8 Z8 P0 h. D& l4 y0 y; y
! k( \! E" } A5 g& R- ADC1_Handler.Instance = ADC1;
: A! f; I4 a: p- D# H! A6 l ? - ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ; g O" E5 t3 U7 P* C/ S
- ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式
# [) i/ U5 _* H1 {4 j7 V2 Z - ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式
( i( v0 v5 k& v: [' r( | - ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换
5 F$ W1 E. [) o+ H+ i9 N' [ - ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式
1 g( l+ a" c& h9 R' @ - ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
0 d# f3 G/ Q# D& o1 C p - ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发
+ A8 d9 P1 s3 U2 D2 n) p - ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐; ^. Z; z, \& @4 W
- ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1* A( Z6 A6 r8 l( n {
- ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求) j. Y1 l* G0 U$ w" M6 C
- ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
7 a$ C2 ~4 C0 G2 e) h& f - HAL_ADC_Init(&ADC1_Handler);
* Q2 k9 g3 d" B% f3 t" [ - 4 q4 L$ c1 ^9 N9 N/ `
- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道7 r; A) _. d% g9 }. U8 }, h
- ADC1_ChanConf.Rank = 1; //序列1
1 {5 l# v# d7 e# X9 | - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
+ o" N' L" O, H4 ?- }. |$ h - ADC1_ChanConf.Offset = 0;
; `% q% Q3 Q+ K" Y6 z - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置5 o3 m1 o: N4 p& v$ R' u
: z0 M& I$ T/ j j z) z1 `- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道
5 J- E/ D! A5 h' Z+ h - ADC1_ChanConf.Rank = 2; //序列2( O' P4 Z6 ?$ y4 O) c- v
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间
: \6 [( @# R% X+ j% V S* k - ADC1_ChanConf.Offset = 0;& B3 l' P# ^0 T, y6 h* d% A
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
( e1 K8 A: ], z$ H5 ^
6 |+ r u1 a* [0 H5 X* p4 \# ?, m- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);
/ P! @" {, h; }: S - }
6 L+ {& _) V5 c7 L4 k - 8 j5 x/ h) A# Q6 F* K6 S, i
- //ADC底层驱动,引脚配置,时钟使能4 A) U2 A& @7 p* E1 {7 Z
- //此函数会被HAL_ADC_Init()调用
' R3 Y! r% q5 p6 G1 R9 d8 P E - //hadc:ADC句柄
0 X. W# o: j6 _ - void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)/ W9 E+ ]3 I1 S; {
- {
. A T5 e! \0 O- m: V2 p1 _" l9 L - GPIO_InitTypeDef GPIO_Initure;
# E8 C5 ]- r- j H, A# F) k. w+ H/ h - __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟
) v/ c/ l+ S- I( H# ]9 F; ~! V - __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
p! |; z& W O7 U
# I# y/ D4 E0 a8 g1 b- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5 V3 I) c' ~: r+ e D8 x
- GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟
2 L0 [# B" i5 P& y0 h - GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉: g. j( F+ L& G* @2 r
- HAL_GPIO_Init(GPIOA, &GPIO_Initure);
" h1 z+ ]: c0 ]. V2 F, c% \ - }8 `4 G% [6 `2 c3 g" H
% E8 f. h$ j1 w, R5 r" Z/ n- void DMA2_Stream0_IRQHandler(void)
$ a7 q' M$ F% G1 v5 y. W - {
) Q( ~0 N8 X( n3 q' G4 { - HAL_DMA_IRQHandler(&ADC1DMA_Handler);& I: k v, A% D; P: F% {5 ~
- }1 h& u' E: w" M
- 6 y. O4 }5 z6 t( F
- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)+ d- E6 F) x! [/ {6 y' H
- {7 A, H l3 G; l/ v* m
- printf("DMA transfer complete\r\n");- d( s2 U7 F5 ^5 ~2 j
- }
& U4 Y7 q/ W1 M9 F3 b - void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
* D1 X- L c+ ?- i - {; {6 |, B7 t4 Z( F0 `& `5 U6 h" E1 J
- printf("DMA Half transfer complete\r\n");3 D" T, K6 t* k7 u$ n) ?0 j2 L
- }
: ?. x4 V* `* l. n; X - " A( u% I5 `* f9 i; z
- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)/ r$ [7 L$ l: S) M* T% h
- {
( g2 A9 |! H; z6 Z; n8 X0 e, _ - printf("DMA transfer error\r\n");' g9 _5 M. R s Z5 ^ N4 c W
- }+ [6 d$ c2 V* k' e( X
复制代码- #include "sys.h"$ I/ ~" i6 ]3 v
- #include "delay.h"
0 m& U9 n% A+ f, A) [ z, Q% n1 N - #include "usart.h"
8 |/ l9 i4 s! w+ y( O& P - ( ]8 l0 d8 G; U3 W& W! u
- #include "adc.h"8 T; c$ C1 e, h S( d* q6 |) l+ g3 _" X
- 6 ^* h- I8 _* R$ {1 R
- extern uint16_t buffer[128];
/ [- }8 v) q1 a' ^+ P - " C s1 I z4 I1 u
- void show()
: U5 W/ t2 t* u" f, s( l" e2 H" p' g - {
, @% {1 u( }- A; V3 W. y/ P7 { - int i;
5 Q% p9 k7 |* o% z! O - printf("\r\ndata:");
$ _+ F1 B1 ~5 ^& y - for (i = 0; i < 128; i++)
/ k, ?3 V0 t) K, B4 G5 M3 i* K" y6 m3 l - {
: \2 T4 _- t! a% W - if (i % 16 == 0) printf("\r\n");. l j' t, O6 Q4 H6 t2 f
- printf("%6d", buffer[i]);0 U# S- w* J8 F
- buffer[i]=0;
% t2 I3 N( |2 M; K+ Z+ N+ e# g
: C5 c$ a& P3 I- c3 q- }2 y: ?5 W* P2 Q6 C- o" i
- printf("\r\n");
/ b, C5 m W' T- C2 @0 F& I - }
+ C q0 E. C, \8 x - 7 }0 W3 T" K, S
! B4 W+ E* U; l* p% q1 r1 L- int main(void)
f$ s1 Q) j& g4 k+ Z% M - {
) b9 I2 c6 Z, @; R, Y# M - //Cache_Enable(); //打开L1-Cache
% g9 i3 b; t: [+ i" B3 S* } - HAL_Init(); //初始化HAL库& D! H9 [8 e( ~# |# A! d7 }
- Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz
) d, c' q- }* X - delay_init(216); //延时初始化
% \& q1 l% [$ \5 b0 j" d# @! ~ - uart_init(115200); //串口初始化7 U0 C! o. C; ?( j n
/ j5 O: h( D; H! r7 ~) g# Z- printf("start\r\n");
' K- [+ F& R" I - MY_ADC_Init(); //初始化ADC1通道5& x$ I/ g- u/ h7 Z4 u8 Y' m M4 O
-
& ~. n: U( v7 U - while (1)" Z8 P, b: A8 h& e( p7 h
- {
+ M4 R! w L7 I' Y' _ - show();5 J5 U4 z7 X. m! [( K
- delay_ms(1000);, X' @1 Z4 Z( C0 L1 i4 p$ |
- }1 P& ]! H% S4 c4 t2 e2 E
- }
复制代码
7 @- g7 Q% E7 P————————————————7 f& `) E: z3 O$ C& ?3 _
版权声明:小盼你最萌哒- r; M9 R: V3 W; K) R
如有侵权请联系删除
1 A! X0 {9 R2 Z+ B, B; |
) u8 E* u. I9 ?+ S4 D |