你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F7实现ADC采集(软件触发+DMA传输)解决了采样结果不实时更新的问题

[复制链接]
STMCU小助手 发布时间:2021-12-15 11:00
ADC1DMA_Handler.Init.Mode
6 n$ S6 ?! ^1 U7 ~DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
* `5 O+ b9 b- Q! FDMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
1 i$ A$ d$ b* ^% K! V9 I虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。' w- Y/ X2 R  K1 f) H& Y
) V, k" x) R: V- i: [9 `
ADC1_Handler.Init.ScanConvMode. z$ ~+ h1 |4 s. z
扫描模式(ENABLE):ADC会依次扫描设置的各个rank5 ~3 ^' O3 ^" W* ^0 O
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……: z% H+ {7 J1 I8 `9 I- ?# ]
即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~
" _- p9 r  ~2 l0 J" `所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。6 T, u& C) N8 O/ s. A6 ~, K0 E% d
2 [- t) L; S5 I. p
ADC1_Handler.Init.ContinuousConvMode+ w3 y, u3 d* s
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
7 u2 I# [) i6 U' `9 R5 W& q  d- c7 ~% u. ?+ O4 x, a
20200419192759329.png

* c4 K$ [& Z9 n0 B9 y7 }; o而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。
7 R" {, u3 f* l% }# {" I; n7 @
20200419192936345.png
. f$ b) `8 Q6 }! D& p2 H
# z; ~0 c  |6 b1 O
而将传输长度设置为3的时候,就进入了传输完成过半的中断。
. U: m9 g# q" v1 o; x  X5 N7 @8 {5 _. T& ~, E3 }& L! E) C( V9 X
20200419194129548.png
- P9 W4 f  T5 `( a- w
3 ]. m" y  c& r6 u/ w+ D0 }
如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
1 Y) @4 ^* T( K# c/ k" L* t  K也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。( d9 ?+ t8 P) W3 c8 i
1 r6 ?1 }: U; T* S
在另外一篇里看到了这么一个表格,希望能够辅助大家理解:( w) ~. _$ _) s% [4 t
  y5 E8 X/ R1 ~8 d! p, u  X" a: {  N. c
20200421214739891.png

4 l0 T4 R- w; S. `( g
8 F3 _: @: D' V; `! A' D2 P, m3 u7 TADC1_Handler.Init.DMAContinuousRequests+ A, |6 j5 F$ h  P
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。% w' ?* o( F- N( b- J' ^. v
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。
$ F+ E4 L8 w# i4 P8 S1 B/ Y" P4 \! T  c
但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……" `$ D( i7 q: U  @; t* K  T

& H/ \2 M4 s) }0 ^ADC1_Handler.Init.EOCSelection
) ^. g8 v, ?4 T  Y3 _* f0 l1 d1 ]$ ZEOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……
: X9 x3 |3 e! |$ d填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置/ T- n' h- d9 b: K
& m4 Z; r  u- u& u  r8 h
偶然看到一个帖子的回复:
' f9 i, }3 J) s2 I* }! n+ d当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。# `& D1 L. G! }# v$ H; F
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY2 [1 p) `! a' v4 [9 t4 v5 t' Y9 k/ A
CACHE行的数据写进SRAM1。
. D" L1 ~" k8 B- b2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。
. q2 z" X; x/ @/ r3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
' i. ^6 n3 N5 q6 c# O5 J5 a1 b: w% k/ ^4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
. J* j( b% D8 G- p/ ^' b; E, e& q5 Z! T! m# s! r  p  h
在main函数中关闭cache,DMA采集的数据就可以更新了。
# k6 E! `/ ]$ J1 J! V
% k/ h1 q8 u, A% [6 _) q
20200419215859139.png
/ L. _) T# R5 f
% A2 Q, E$ E3 H0 y. r" |
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。
4 ^+ O' o6 l! r% o( j  \
- R3 i' D2 F% O8 W2 _
  1. #include "adc.h"; v" B# {# Z- s+ x: u1 s! b/ s
  2. #include "delay.h"# s: x3 i* d9 U9 m6 q) n4 [
  3. + O8 H3 T2 o5 R7 ~: L  {4 O+ n9 r
  4. ADC_HandleTypeDef ADC1_Handler;//ADC句柄& e- [3 k9 K& I, _7 u' k
  5. DMA_HandleTypeDef ADC1DMA_Handler;
    * {, K5 W' `7 E0 W
  6. ADC_ChannelConfTypeDef ADC1_ChanConf;. K( U0 `: t. \9 t/ Q$ y' W

  7. * B2 t* d7 l; h- h
  8. uint16_t buffer[128];
    : O, v+ m0 Y* Z% {% k

  9. + X- N5 C( z$ s: G7 g
  10. % y9 p/ J* \6 k$ J7 r+ D
  11. //初始化ADC2 Y. E! s+ g$ x+ a8 e6 c( |9 b9 g
  12. //ch: ADC_channels: w& _5 b. `" T/ h( y" K
  13. //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
    $ x6 K% y, J7 J+ }% w6 {
  14. void MY_ADC_Init(void)
    ' V/ Q  {' U" H  q
  15. {
    " h+ A6 I' X6 q
  16.     __HAL_RCC_DMA2_CLK_ENABLE();6 t! y6 w: d! z7 b
  17.     HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
    - n5 f' U. ]5 [
  18.     //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
    ( \, S7 b, o+ ]5 h1 A' ?6 l
  19. 7 T" p/ m( K( o# z% {
  20.     ADC1DMA_Handler.Instance = DMA2_Stream0;" r& a0 J( b8 _) A- Q' Z' Q
  21.     ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
    ) d0 N3 W$ E( x
  22.     ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;1 d) T* X# k3 \! c% q
  23.     ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;               //外设非增量模式
    4 o4 u$ ], @4 W0 ^, P
  24.     ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                   //存储器增量模式, j; d( o, g( l
  25.     ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设数据长度:16位
    & X0 ]# I, L6 e* K# A
  26.     ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //存储器数据长度:16位* R; ^# A. B  E' @% p" g; @# o" L
  27.     ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR;                          //传输一次就结束8 I$ I  L, a) x; U* U0 X5 V
  28.     ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW;             //中等优先级3 p3 A' j+ `7 i( B3 x
  29.     ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;            /* 禁止FIFO*/+ Z1 e% f: E) T; N) \; a
  30. : q7 ?/ P4 f" r
  31.     HAL_DMA_Init(&ADC1DMA_Handler);. c- D; D+ _/ G2 s" `
  32. , S, j9 j4 [. u/ E5 u5 l+ y
  33.     __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler);                //将DMA与ADC联系起来! w2 t6 Y, P5 _4 B+ t3 y

  34. & q/ z, c0 _) v6 h) e
  35. 7 v. v, f! ]" g5 k' j* x" r/ D
  36.     ADC1_Handler.Instance = ADC1;
    6 k2 w0 z) c0 }, r+ @
  37.     ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ' d/ A6 J% G5 U' Z9 q
  38.     ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B;           //12位模式, o! X; g: B, ]. u7 T
  39.     ADC1_Handler.Init.ScanConvMode = ENABLE;                    //非扫描模式/ v  S* z* \) {) y  Z
  40.     ADC1_Handler.Init.ContinuousConvMode = ENABLE;              //关闭连续转换1 @# D  C: m3 v$ B! y
  41.     ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;           //禁止不连续采样模式0 ~% H5 }& X& c' G4 U- O
  42.     ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发  t  ^) s! V' [! y& r- L# M! A
  43.     ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START;     //软件触发
    : T7 E5 W; w7 C
  44.     ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;           //右对齐+ a' h9 d- G- ^; ]
  45.     ADC1_Handler.Init.NbrOfConversion = 2;                       //1个转换在规则序列中 也就是只转换规则序列1( U1 g* c6 ?9 H$ {
  46.     ADC1_Handler.Init.DMAContinuousRequests = ENABLE;           //关闭DMA请求1 {, O. n, `% {* k7 T' e8 U. W  N" S
  47.     ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;        
    6 r: f( K& M1 G8 B# E( |4 B
  48.     HAL_ADC_Init(&ADC1_Handler);: _3 u/ d- q! b. C5 m5 ^3 g2 [

  49. 6 y9 o& f3 \; {" _, A) S3 y$ j
  50.     ADC1_ChanConf.Channel = ADC_CHANNEL_5;                                 //通道' E5 |3 |5 S2 ^0 M7 z) _  f: X
  51.     ADC1_ChanConf.Rank = 1;                                     //序列1
    3 u1 |5 G! p) I7 |( Q
  52.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间
    - F9 Q- y5 @6 ?( z
  53.     ADC1_ChanConf.Offset = 0;
    % {% I0 O$ r3 d( v
  54.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置
    ) y# d! U: r3 H8 {, ~

  55. : n3 J2 [# l- D# C0 J$ ?/ c
  56.     ADC1_ChanConf.Channel = ADC_CHANNEL_6;                                 //通道
    7 Y7 f1 Y6 H: w) C( g) P. U# {1 Q
  57.     ADC1_ChanConf.Rank = 2;                                     //序列22 T/ r+ j* T& [* ~8 e$ f
  58.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间
    8 _; b. P% B3 s: }" N5 f7 u  C
  59.     ADC1_ChanConf.Offset = 0;% \: A# X$ E& r) F# U
  60.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置! l+ J8 {- N9 X: N$ O" J( X

  61. - A9 D; e, ^& P2 }2 e( M4 s1 |
  62.                 HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);
    , G1 \3 J! G' Q" D/ g0 T
  63. }
    " r: h1 t. P3 X7 f, ^7 z
  64. 7 v7 k0 _7 o% d& O1 N+ o. f
  65. //ADC底层驱动,引脚配置,时钟使能0 U' }  |$ C  O# c% e) P' O; Y
  66. //此函数会被HAL_ADC_Init()调用
    2 p8 D" j4 a; K% k+ @
  67. //hadc:ADC句柄( a; Q4 c" I/ L/ i; L5 k
  68. void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
    " f* \! h9 h: W$ r; l2 z- K6 G
  69. {8 U1 Q- M) W$ Q, D  E3 Y& W
  70.     GPIO_InitTypeDef GPIO_Initure;
    5 }& ]6 R; L0 a6 {5 z
  71.     __HAL_RCC_ADC1_CLK_ENABLE();            //使能ADC1时钟5 L% @0 J' v5 K) ~* U# h
  72.     __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟
    0 b: L: E' k& _

  73. % T/ F% {8 c! ]0 q. O* |2 N/ o9 R" [
  74.     GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6;          //PA5
    - o  R5 D" l( f0 C3 c& @8 x
  75.     GPIO_Initure.Mode = GPIO_MODE_ANALOG;   //模拟
    : E0 q& R! C% n) C8 r. n
  76.     GPIO_Initure.Pull = GPIO_NOPULL;        //不带上下拉7 h7 H6 O- J/ |6 Y0 L& m4 _2 V3 K
  77.     HAL_GPIO_Init(GPIOA, &GPIO_Initure);
    ; g3 Q& u; p( z* i% O
  78. }
    ! x; Z3 G$ f8 r: h

  79. ' o, s9 y; }  ^$ v7 ]& H
  80. void DMA2_Stream0_IRQHandler(void)
    ' l  Y7 s$ @1 {/ S$ k) Y- Y- P
  81. {% P. ]" Y( Z; d4 h3 g$ W' Y
  82.     HAL_DMA_IRQHandler(&ADC1DMA_Handler);
    3 P' t8 p) d: j/ p; \7 P) a9 F% u
  83. }
    : [  [6 a7 c8 m% {: _! w
  84. # \( x2 u, R9 R$ i5 Q" o& U
  85. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
    ) k2 [# r3 I0 e
  86. {
    ; b& A" N( F, g
  87.         printf("DMA transfer complete\r\n");2 w+ j1 Q; X0 c5 `
  88. }
    ) r. U6 F) o0 l* W  A
  89. void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)2 y2 [3 n6 Q3 n4 ?2 ?
  90. {
    % b  X2 }. b0 h! h- X* f. j" u$ |
  91.         printf("DMA Half transfer complete\r\n");
    ' t1 z7 m. \* g) I0 d. m/ v8 B
  92. }4 ^8 w/ e1 K* J6 g$ U& b" ]
  93. 5 s4 F. h6 d$ O; U# R
  94. void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
    3 x5 H, P8 A5 n& z5 Z$ {% Y# W
  95. {/ w+ {! J$ q1 l3 u7 y$ D
  96.         printf("DMA transfer error\r\n");
    7 p7 X" \# k& C. {# B
  97. }
复制代码
* E, G9 D( d5 }" B) d& k2 B
8 J% W' |8 N7 V7 I7 d
  1. #include "sys.h"
    * k. h( x$ S& y: R1 R5 ^. h' t# x
  2. #include "delay.h"
    5 T, }. Z. f1 e
  3. #include "usart.h"
    ! e- h* K6 J9 q2 Y0 f: E

  4. 3 g; t+ C) K( ~, f1 I  @
  5. #include "adc.h"" A7 r9 z7 i& ?* k; _0 x
  6. : g7 A$ I  ~# w, d! x# @
  7. extern uint16_t buffer[128];
    4 ]0 V8 Q8 E" e" D2 L  ~5 D
  8. " h! s: b: U1 o7 v
  9. void show()
    7 d3 e% e0 N' L1 }1 j
  10. {
    . T: Y; P! n4 `( C7 j+ ~4 x( P( f
  11.     int i;8 x. @: R: Z6 i# f; x# {9 j
  12.     printf("\r\ndata:");0 d6 }5 q% A; n0 F9 H5 G; X
  13.     for (i = 0; i < 128; i++)
    1 q' j  Y6 o  i0 r6 @$ h
  14.     {2 d1 k% }9 [  z9 S" J2 m) K
  15.         if (i % 16 == 0) printf("\r\n");
    " D4 @5 B' R7 n" Q; ?0 A1 R" r/ r$ M
  16.         printf("%6d", buffer);
    ) {8 P- K5 w$ T& s& U4 K+ T% w
  17.                         buffer=0;/ L4 s# ^% h# l4 v! M# [* l

  18. . a( |% I  w! _+ v. J( z
  19.     }
    ! l' {; p# G* G3 ?6 @8 r: q' V- \
  20.     printf("\r\n");% D2 w0 m/ R. m
  21. }4 p6 |7 _6 ^+ D* J9 }
  22. * U( ?2 U- ^5 d( t

  23. ( S5 p1 X* y( [
  24. int main(void)' M7 `4 s" E, c+ F
  25. {
    ; C8 W$ |3 I2 `2 k3 s- r' Z, G
  26.     //Cache_Enable();                 //打开L1-Cache8 l) }* E7 c$ k2 X
  27.     HAL_Init();                     //初始化HAL库/ B8 i) n& x/ K' T+ O$ \- w
  28.     Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz7 O* f. V8 q* d* K6 ?# @# ?0 u
  29.     delay_init(216);                //延时初始化
    / \- _6 M, u- L
  30.     uart_init(115200);              //串口初始化
    1 X7 o7 y* f; V% F  G4 X) M
  31. $ z4 b) Z' g( [* f8 J6 j' Y3 b9 Y
  32.     printf("start\r\n");; |3 x$ H/ ]& D. b; \; x
  33.     MY_ADC_Init();                  //初始化ADC1通道56 ~0 B( s$ V, d( V
  34. 5 S7 S5 o# h3 Z
  35.     while (1)
    7 y% n" A# {* N* f
  36.     {
    ' p  {0 k; Q9 x1 y" o
  37.         show();
    . l, y/ R# S# [1 }
  38.         delay_ms(1000);/ R' E# J/ \9 T0 c# u3 u3 A
  39.     }# U3 o/ ~1 G8 O$ O8 b
  40. }
复制代码
$ Z3 A7 R# ?5 e3 I% s
5 y/ V6 x5 `: V6 F
赞 收藏 评论0 发布时间:2021-12-15 11:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版