ADC1DMA_Handler.Init.Mode6 M: k5 c, D( b
DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。 k% q* G# x( ~; u
DMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。. J! S1 Y4 H: ~! W/ R3 [
虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
1 }3 H+ S1 o' r& P( q) y. F' Z& _% z. r
ADC1_Handler.Init.ScanConvMode
8 Q3 A4 }% p, x% s扫描模式(ENABLE):ADC会依次扫描设置的各个rank+ d9 U d4 S! B1 O, }" s
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
$ B& ?3 P, o( _ o" {2 c即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~
3 d7 o" D( W4 p所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。; c6 J6 w2 D! g9 g3 P9 {+ z
0 G: x) e7 K, g# I2 u4 X2 IADC1_Handler.Init.ContinuousConvMode. A/ J m4 y1 i4 p0 a
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
1 D; ~* t+ T. }7 _) u6 D- B) w# |$ `1 j! Y2 J
9 G/ Q s4 { D) [" E* S( f
3 F. |. x) h9 |$ Y$ E! h
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。
$ `5 k9 ` p6 k; o/ W) j0 `4 |9 R* V2 {
8 `/ X8 P; f8 A
4 }' y- h8 c' s! _9 Q, o \# l
而将传输长度设置为3的时候,就进入了传输完成过半的中断。+ {2 \3 p8 n0 c) z& ~, E0 g
7 q# S) I8 r6 ~' W3 C& O9 v" O0 {
2 v7 l* ~& a+ ?0 Y/ c& W
如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。3 B" A% H8 m( b6 r
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。
: e, }1 i% h) | L" f/ m3 d# i; \7 F% e8 z% S! U6 k
在另外一篇博文里看到了这么一个表格,希望能够辅助大家理解:3 M2 m6 a( \+ H$ i9 j- e
* l4 U7 D- M" q* V7 [- A0 U
# E( {7 f# a1 b: b( q1 [0 d9 D( K; k9 T: N7 N/ g
ADC1_Handler.Init.DMAContinuousRequests
4 S; J) [* b* r/ t7 [3 }当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。
3 u1 p- h4 D& E4 A8 C这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。+ s+ u; K4 r+ V8 k! S# ~
) w+ y9 @4 B0 ?5 i1 i$ \6 q( x9 o但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……
. h7 y* q, h6 H/ T
. z; z$ C/ i1 Y( ]( k3 oADC1_Handler.Init.EOCSelection& z2 x+ U- W$ h" x( l& k
偶然看到一个帖子的回复:+ A' X" S7 r$ ^2 W% s
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。% g2 q: v9 `: ^+ T* S( J* U
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY8 [7 @1 `" j8 O6 O- P$ Y1 U
CACHE行的数据写进SRAM1。: I; Q% ~4 P1 A/ J4 G
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。+ b# Z6 b* C& v; K$ ^7 h9 @7 k8 O
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。, K3 W: c/ D# ]1 U' l
4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
+ r8 z& W/ m8 i' n' q' h2 ~: T! L# g8 t8 c. J4 |, u( u. H; _
在main函数中关闭cache,DMA采集的数据就可以更新了。0 }# R& W( l+ i. k2 k, i9 @, O
3 ]7 [/ \0 b4 u$ s3 l, l* c, g
; y; a- y, P+ Y. ]" D
3 c! K/ R, ^6 K9 q4 J# @3 h- B附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。8 e( }& {4 ? ~9 N4 _ E. E
- #include "adc.h"1 p; A1 H5 ?- O# _3 \
- #include "delay.h"
, F& w1 D ~2 N% {' n! {9 ^8 W
& G1 L( i9 h( c0 M r( h2 S- ADC_HandleTypeDef ADC1_Handler;//ADC句柄8 I- C6 L. i/ s0 }/ c' h+ u# I
- DMA_HandleTypeDef ADC1DMA_Handler;
* W8 o$ c, J0 M+ J+ Z6 S: I - ADC_ChannelConfTypeDef ADC1_ChanConf;
0 I, f! F( D8 h: d9 L6 c+ d8 m - - G. J" `) f6 K
- uint16_t buffer[128];+ v- k( j, h( w0 V9 G: i1 {
) A9 r5 {# ~7 j- 7 N0 _9 O F' c$ |7 u, ^& R
- //初始化ADC
+ K- f- I* a0 z5 L% o/ U7 M - //ch: ADC_channels& o( \) Q# p( j m; z1 Y. E
- //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
: R0 _2 d# d3 D - void MY_ADC_Init(void)* j0 n- \* y' l/ e/ d7 ]0 `
- {
% z1 e- a: r. d! ] - __HAL_RCC_DMA2_CLK_ENABLE();! Q: _$ N- z3 T% q1 z; H
- HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
% i2 t" ]9 f/ u; f - //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);2 N) h# W3 h/ a
7 W( a$ W* J* U5 }8 G. W- ADC1DMA_Handler.Instance = DMA2_Stream0;
1 l$ e9 f5 }& l; N* \ - ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;: x0 ^* l2 ? u3 d% ]
- ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;* \; P. [& C% v' h9 u0 p* H
- ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式3 W8 [' j+ o# J( G0 q5 a0 G$ S% o
- ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
g, T/ K% b2 w - ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位; f/ g+ f7 c. p l; v6 Q& G
- ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位/ h4 Q! |) K' ^& I
- ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; //传输一次就结束
1 X- Z4 V4 A! z9 l0 P& z8 c - ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; //中等优先级7 ]7 O2 x' M' R0 Z2 }: @" r" b
- ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*// C6 U$ K* s6 B
! i7 u8 X( d( }- HAL_DMA_Init(&ADC1DMA_Handler);/ k% g: _. Y6 v+ Q5 p+ d& i6 }
- 0 a( N+ @9 w; u* Q
- __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler); //将DMA与ADC联系起来
" w. _5 ^& `; n" U8 }( K2 o2 d( `8 ~
6 F( p! m5 Z% I- f0 }3 N+ `; h4 k- , B1 h. c0 y" |; ?. J- L
- ADC1_Handler.Instance = ADC1;
2 I6 x# l3 _8 R$ x0 ?. U+ m9 ` - ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ
- O: {. w3 P7 U+ x/ b- l - ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B; //12位模式
1 C; t# n( t+ h5 h0 N - ADC1_Handler.Init.ScanConvMode = ENABLE; //非扫描模式
* k/ t6 D' q. a# l o8 J - ADC1_Handler.Init.ContinuousConvMode = ENABLE; //关闭连续转换
6 I4 j2 X# p$ N$ t - ADC1_Handler.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式
- \5 }/ X- R2 J# w" S& C - ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
( U0 J9 _5 k' B6 Y8 Z5 W - ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发
9 i) [3 H* y8 q/ v h# O - ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
8 j1 ]- t1 e& `5 S - ADC1_Handler.Init.NbrOfConversion = 2; //1个转换在规则序列中 也就是只转换规则序列1
+ f& @7 ]/ f1 } w6 q - ADC1_Handler.Init.DMAContinuousRequests = ENABLE; //关闭DMA请求5 t( C; L& N9 m) Q2 d# A
- ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
$ {% O" H9 d" I+ W+ p0 N. L - HAL_ADC_Init(&ADC1_Handler); e% n9 K+ Z o
/ x& M7 @5 F j X& k- ADC1_ChanConf.Channel = ADC_CHANNEL_5; //通道
3 i- A. f6 f/ e) A - ADC1_ChanConf.Rank = 1; //序列1
- U% v0 D- h* |$ K - ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间& B+ N8 U! g: [. q( j4 `) V
- ADC1_ChanConf.Offset = 0;
) @! R% O: l% j2 A5 ~0 x - HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
1 w6 P0 O7 C2 C$ F, ?
0 q/ m! H3 |' J! l# l! z- ] H- ADC1_ChanConf.Channel = ADC_CHANNEL_6; //通道
$ ^" S! V3 A0 ` - ADC1_ChanConf.Rank = 2; //序列28 N7 n* N, W/ p4 Q8 l
- ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES; //采样时间8 H. `- f. |4 X
- ADC1_ChanConf.Offset = 0;% X3 {0 d' v: z- L; c4 v
- HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf); //通道配置
5 Q8 h/ p. F) |# r4 ~) v4 \& r - 6 U% o1 d; q+ O5 h5 M
- HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);2 u7 Y. g* s8 h6 Y6 M
- }
- Z" u( y, A0 e2 F8 |
9 _5 L. h! \$ e" P- //ADC底层驱动,引脚配置,时钟使能) [2 x% [- k5 q& k: P
- //此函数会被HAL_ADC_Init()调用
# \6 Y7 _+ M& ?& e. \" a4 l2 B - //hadc:ADC句柄$ F+ n$ l6 ~# g" {- c# R
- void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)4 k& `; z* z/ s; r
- {% y/ i/ M! F3 q
- GPIO_InitTypeDef GPIO_Initure;# [" [# m8 e) h. g* g
- __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟( n& y) e% S9 b0 W8 N, J" V
- __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟7 ~, y/ C8 U% L ]0 |' D6 i
/ q; X, g# C$ L, b( X! F- GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6; //PA5# Y1 s7 j( o7 {6 r/ t0 O
- GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟9 M1 t0 d S) K! B" q
- GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉3 ]* Y, z- q/ e, ]* B3 Y4 ?
- HAL_GPIO_Init(GPIOA, &GPIO_Initure);. Z# z- U; q' g# B+ q R+ _/ b7 _% j
- }- A. w' v' N! k5 O
- $ |. M9 L1 C' u
- void DMA2_Stream0_IRQHandler(void)1 K/ u Z# K6 T8 Y7 C
- {
4 t$ B; D5 M0 H- T: y - HAL_DMA_IRQHandler(&ADC1DMA_Handler);
/ W. q5 R+ f* q$ z' I, [8 @+ z - }! l1 E9 @- Y. B+ p
" ^ M9 R9 ?. E9 b5 y* A5 G' T9 Z- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
2 e' b T0 |/ u- z Z - {
7 @: ~# H' ?, ?4 c- X1 x; m - printf("DMA transfer complete\r\n");
6 L7 w0 z" F7 | - }0 V+ W, K f) K+ B
- void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
: n! C8 N( K# k: a( c0 q! C - {
y$ \0 t! ~1 a# D$ c, s% v - printf("DMA Half transfer complete\r\n");
0 q4 s0 K; y* _( x3 F - }
8 B" M# c! d' X5 p: p( p, L/ D: g - ' q8 \# t+ y e2 O: o
- void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
2 N$ r9 ^# X4 G. I5 I$ o - {
+ o, {" K8 g# \- t) G* d P - printf("DMA transfer error\r\n");
1 m4 j& S, \# K1 S- v - }; {- Z; G+ Z9 W
复制代码- #include "sys.h" d* @/ ]# }1 T: k
- #include "delay.h"9 _, c9 q( z4 r4 V$ g2 M
- #include "usart.h"
: w( Y. S8 T6 Z' G" z - ' `6 w' `$ _, a( ` {5 Q8 }7 N3 {6 O
- #include "adc.h"
Q0 _! D% R4 J' ?6 O
9 Q: l* V/ @& _5 B& p7 C& H- extern uint16_t buffer[128];. Q* }. v$ i! _# H$ r* S, o" t
- - Y0 L5 v: ~3 H8 b5 }) a/ a$ j
- void show()
! W8 l7 Q1 {+ x/ M: c - {# z+ f; {: P) A" c1 ^$ a, a4 a
- int i;4 j: l2 H7 g" r# h5 ^3 |5 q0 c) b5 Z
- printf("\r\ndata:");
. y- ]+ j. T3 f - for (i = 0; i < 128; i++)% @# C4 c% n4 r Z' ~3 C
- {6 q' ^) C8 ?% {+ l. Y2 }1 R
- if (i % 16 == 0) printf("\r\n");- G$ S. Y {. v" w7 @, v1 j
- printf("%6d", buffer[i]);& t- i1 `0 K3 L2 t5 W
- buffer[i]=0;
# L8 L" B4 Y \( q1 A0 O2 W - ; w2 C" r' ^$ b& C
- }
; q3 z8 [$ }2 h% }1 i - printf("\r\n");
1 r- X; c& f. b( O - }( a" y/ t) m' Q
- 7 r! {# b; W: Y( D( K) B+ b
. s( `) ^' f3 {3 W- int main(void)( k/ e( Y" T ` H4 I4 C T2 \8 Z$ i
- {; v+ p e* k! j0 l* O* z1 I% q
- //Cache_Enable(); //打开L1-Cache6 _' A4 n1 ]% p$ N, |/ x' [- m6 d
- HAL_Init(); //初始化HAL库
2 m. v# [* C+ O6 r; X - Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz" l% G) R5 s7 A0 w$ r {! I: }
- delay_init(216); //延时初始化( G+ _9 N* }- ^; \% z9 q- `
- uart_init(115200); //串口初始化5 a' C9 D/ Y; j- M0 ~7 t
- 5 I' @; K; X# w& B+ C% I
- printf("start\r\n");: X0 L' e) j) o
- MY_ADC_Init(); //初始化ADC1通道55 I: v8 p t+ P
- 2 D, I/ E7 R$ L2 s! i% P2 k) Y
- while (1)
" P! R/ j1 |, b& p8 p& U - { }; W4 @# Q$ G) Y' I! ]; m
- show();) i' W* Z7 }+ d9 P& U
- delay_ms(1000);
( {% L) d1 p! ]; i1 S! N8 s - }" O) P0 ?9 S& k: ~# g- k
- }
复制代码
l1 |6 F6 W. c. `, Y9 [————————————————& m5 l; R! u/ b1 z7 I' _' `0 p' g
版权声明:小盼你最萌哒
7 b& l7 U$ J+ ?. }如有侵权请联系删除
8 j0 q7 G: c e0 N5 Q5 [
; H' S/ a Y {7 h7 d; f |