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

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

[复制链接]
STMCU小助手 发布时间:2021-12-15 11:00
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
20200419192759329.png
( l/ c. P9 f# o5 H
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。9 F) G% R/ q) p' E/ p; A. J
20200419192936345.png

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 {
20200419194129548.png
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& T
20200421214739891.png
9 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
20200419215859139.png

- 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
  1. #include "adc.h"
    2 G9 @: q& Y# {; }+ ~  l
  2. #include "delay.h"5 p4 f; h2 }, A* @& ~0 s
  3. 3 k: E7 M6 z1 g0 u* K5 T
  4. ADC_HandleTypeDef ADC1_Handler;//ADC句柄
    3 m9 |) Z# K5 L2 e" U
  5. DMA_HandleTypeDef ADC1DMA_Handler;
    & a  C& x7 X2 P7 W0 U  s- M
  6. ADC_ChannelConfTypeDef ADC1_ChanConf;
    . [, O8 Y9 O" A

  7. ( _2 B5 w4 |1 ~$ m
  8. uint16_t buffer[128];
    , O& `# F4 O* j

  9. * z/ }  Y) a  E

  10. ( f# I0 |4 R  u' h% [& ^1 Z
  11. //初始化ADC2 j5 A; H7 ]: K& G, v& e, P
  12. //ch: ADC_channels( ?/ d0 K, \7 c5 f* z
  13. //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16. l/ ~! u( ]% _0 |! \4 D5 X
  14. void MY_ADC_Init(void)6 O2 X. B/ ]% u) r* o0 R
  15. {
    # ^; i% [9 H0 _) u
  16.     __HAL_RCC_DMA2_CLK_ENABLE();$ A6 [6 w4 d; [  ]5 K1 \  d% s
  17.     HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);  c( V! n: d$ O7 v, z0 [& |! {/ m2 Q
  18.     //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);! X1 s% i6 J; l) W7 E. k

  19. ( @" E4 ?7 V  H/ ^4 n' C% j
  20.     ADC1DMA_Handler.Instance = DMA2_Stream0;
    + _/ P( }0 O7 k
  21.     ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;1 ~4 C/ J6 U, L; X. \% V
  22.     ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
    ! k& ?6 Z7 s$ g7 h* _( D9 @3 n2 R$ l# s
  23.     ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;               //外设非增量模式2 d0 U0 R' A3 t: |, c
  24.     ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                   //存储器增量模式
    / x" Q- ^: M6 O/ p$ n' z
  25.     ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设数据长度:16位. Q4 V* n9 ~& l. S7 B& I" N
  26.     ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //存储器数据长度:16位( e4 @$ X- i7 G) ]
  27.     ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR;                          //传输一次就结束
    . v$ y% b5 v; C2 L9 y: B
  28.     ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW;             //中等优先级
    : K2 M; i9 y- X( z4 v$ A  _9 A
  29.     ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;            /* 禁止FIFO*/! I8 z: ~1 V- \# v4 @

  30. 7 b$ R* v5 I# E3 c$ u+ d
  31.     HAL_DMA_Init(&ADC1DMA_Handler);
      U! d! O. g& Y

  32. , C" ?5 B8 V( u  H4 ?: ?' o
  33.     __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler);                //将DMA与ADC联系起来; a" _3 O% J) c+ E

  34. # p1 D6 Y& {8 ^' w' @
  35. ( F% S2 \8 l: C5 m) l+ B/ C( @
  36.     ADC1_Handler.Instance = ADC1;0 i0 J  r) V( i2 D$ r. |" _- v( z/ {
  37.     ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ  f5 V1 ~: [" j, _! x5 \
  38.     ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B;           //12位模式5 n( Z, P" M: @
  39.     ADC1_Handler.Init.ScanConvMode = ENABLE;                    //非扫描模式
    ' n( ~8 p9 b+ w% o& B4 I# x8 _5 {& s
  40.     ADC1_Handler.Init.ContinuousConvMode = ENABLE;              //关闭连续转换8 I2 N' {$ |" z6 u. T! R8 z
  41.     ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;           //禁止不连续采样模式8 A  {1 D+ u2 ]; d- V1 ^. N. v) W$ P- U
  42.     ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发; [. s  j7 K4 Y
  43.     ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START;     //软件触发
    # ]0 K4 i" U; V9 l- L1 z4 v
  44.     ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;           //右对齐
    7 a, E  N$ i' X, {  }5 g: y8 ^
  45.     ADC1_Handler.Init.NbrOfConversion = 2;                       //1个转换在规则序列中 也就是只转换规则序列1
    , u4 X- x# |6 [+ ^( u" a& g
  46.     ADC1_Handler.Init.DMAContinuousRequests = ENABLE;           //关闭DMA请求
    8 z- O6 _" f0 I- H7 g: T
  47.     ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;        2 N' ?- d6 j6 ~2 J6 G
  48.     HAL_ADC_Init(&ADC1_Handler);
    8 U2 N" s/ q; T0 f. Z/ A* D

  49. 1 {! @9 E. d, `$ V
  50.     ADC1_ChanConf.Channel = ADC_CHANNEL_5;                                 //通道4 @4 e( B4 D4 X: ]  X* R) I4 t9 U
  51.     ADC1_ChanConf.Rank = 1;                                     //序列1. Z( a. h- \; h9 T! s( p# R  t  ?/ Q! \
  52.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间
    ' Y- g0 N. E% g; g" b
  53.     ADC1_ChanConf.Offset = 0;
    * F* r9 i, n8 R" E/ _# W9 t
  54.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置+ T1 W6 z; ?. e- }) ~* ?7 U6 |

  55. " k' H5 v" w$ u, P. ^* Y
  56.     ADC1_ChanConf.Channel = ADC_CHANNEL_6;                                 //通道& L! D" @% |4 o: r% n" B( V6 ?' {
  57.     ADC1_ChanConf.Rank = 2;                                     //序列2! R- {1 ^# j" i( v
  58.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间/ K2 g, U0 B3 s$ x7 z& [
  59.     ADC1_ChanConf.Offset = 0;
    ' D5 B/ b( |9 x9 ?7 k$ R* L. ~6 D! i: o
  60.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置
    4 m3 ^+ h: q, B. a4 x  E9 s' x
  61. - @" z1 `; m* f) J' s( S! B  v% A; [) D
  62.                 HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);6 {) O4 J0 F8 O6 N
  63. }; W9 o( k* f9 m0 j8 G" ^& [8 w
  64. $ d9 S( j- p7 r+ d' P4 C* Q, }. Z; ^1 K
  65. //ADC底层驱动,引脚配置,时钟使能/ [( g2 ]  l8 V
  66. //此函数会被HAL_ADC_Init()调用
    9 S, c: W$ k, |0 \0 w6 i+ o8 P# P
  67. //hadc:ADC句柄
    3 n) {1 t2 b$ B/ a
  68. void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)# \# ]! Q9 {4 B. x! `& |5 ~
  69. {
    " [* Q. T$ e/ {
  70.     GPIO_InitTypeDef GPIO_Initure;* D  c1 Q1 F2 \7 y; Z+ n
  71.     __HAL_RCC_ADC1_CLK_ENABLE();            //使能ADC1时钟; w+ @) ?0 J/ D% |) R* @$ X
  72.     __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟
    , V% Z8 z1 G. I. B6 _
  73. : S9 I% y9 e6 O
  74.     GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6;          //PA5
    ( i: h" Y5 Y9 D$ g; G! O* D
  75.     GPIO_Initure.Mode = GPIO_MODE_ANALOG;   //模拟
    ; H% U$ d/ r/ B5 K
  76.     GPIO_Initure.Pull = GPIO_NOPULL;        //不带上下拉
    # K+ N2 \' D( O4 V3 h) D4 K
  77.     HAL_GPIO_Init(GPIOA, &GPIO_Initure);
    - h/ f& Q. x* y4 f. n
  78. }$ c. F4 @$ o% \! V$ G8 A% f" C
  79. / Q) J- q0 z6 o# z( s" h4 d0 s
  80. void DMA2_Stream0_IRQHandler(void)9 e& ]8 j& v% \9 J2 E7 G" Z
  81. {6 S" }, Y8 v% f) U
  82.     HAL_DMA_IRQHandler(&ADC1DMA_Handler);
    " Y4 s- ^' h* n: L& s5 c
  83. }: m; _6 g; `7 K' R( u4 H
  84. - M1 T! d# t1 o7 I2 A
  85. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
    ' I" A: r! K/ v
  86. {! u3 G4 B' h4 U" k
  87.         printf("DMA transfer complete\r\n");. }, Q+ Q. k! }: `' s4 U
  88. }% t5 ?, O8 C0 v) i
  89. void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
      ^# L. k& ~# h) _- L
  90. {8 a+ [4 |( g8 x8 K' [- m  f7 x
  91.         printf("DMA Half transfer complete\r\n");% W2 f9 R- g/ ^2 S/ m
  92. }
    5 w5 r2 F& n3 g9 c8 B/ H9 m

  93. , ~  [: J8 @5 N9 s; X! L) }* I
  94. void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
    ) Z1 n7 C" D9 t' S
  95. {
    , p, Z/ Y4 q: A! a5 f! ?; h/ U
  96.         printf("DMA transfer error\r\n");
    5 W9 J4 m* z  s0 z
  97. }
复制代码

, {4 F6 H, p+ _. p, z0 S- w. Y! O  M$ [7 _( g$ z8 K& w% X
  1. #include "sys.h"
    # _0 F/ [2 H3 b' C7 u
  2. #include "delay.h"7 t# B( V0 f: b
  3. #include "usart.h"  g+ h9 L2 ?# [$ }4 M9 ^4 F
  4. 2 E' U  l* D- H9 H8 _
  5. #include "adc.h"
    / B/ f% j; F% t9 ~& R3 b5 @
  6. $ q$ x: ]9 U' f1 q" Z
  7. extern uint16_t buffer[128];
    6 x0 Y: }' u) u# i& Q. O# E9 v
  8. 5 h% Q/ t9 |6 G, L
  9. void show()
    ! ]% _" U2 |, a! d. D' q
  10. {
    - M. c! V* Y: d3 r
  11.     int i;- C3 _- O* `( O0 l: i
  12.     printf("\r\ndata:");
    ; b. N" o7 j& s) P- c( d6 h0 X
  13.     for (i = 0; i < 128; i++)7 ]1 d( m' @2 ~9 k2 L6 t. E2 l
  14.     {
    7 S8 R/ [2 Z" p6 w7 t, x
  15.         if (i % 16 == 0) printf("\r\n");
    * Z. N* J( ]2 P, U) W9 l: {5 j6 c' K9 K
  16.         printf("%6d", buffer);6 G7 ?& {% A) W- L( [4 U- v
  17.                         buffer=0;# I; ]/ X3 w" Q1 K' r
  18. % I/ j' _/ L0 V, R
  19.     }
    . x1 k, N  O; z/ Y% f1 D2 u
  20.     printf("\r\n");
    2 b- A) _$ Z" B0 C' u
  21. }
    8 Q! ]8 r9 j! A7 W' @* ^

  22. 4 @; p" Y; n' e
  23. / A, h- f! r( w. ]9 h# R- A
  24. int main(void)* b' a$ X) I+ z+ M4 }
  25. {
    6 P2 b" [9 u  X; X/ L. R
  26.     //Cache_Enable();                 //打开L1-Cache! C2 L% c2 m( V4 f  _
  27.     HAL_Init();                     //初始化HAL库3 [' E6 K& d+ j
  28.     Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz- v, @+ P3 `1 v& I7 S# m
  29.     delay_init(216);                //延时初始化
    & a7 P) k/ @; ]3 q' R+ j
  30.     uart_init(115200);              //串口初始化- |0 _* h5 C" z5 ^: H' e

  31. 3 a8 g5 d/ t/ w% D, F0 X  g
  32.     printf("start\r\n");: }% f  D2 D" P: }' ^
  33.     MY_ADC_Init();                  //初始化ADC1通道55 p0 b  r1 s$ q. r/ C
  34. ' [# N$ i8 M+ Q5 y* M
  35.     while (1)
    & `3 A* x+ I/ V
  36.     {
    0 E( Y0 `8 \4 Y7 S* Q* b+ ^- R
  37.         show();0 k- a8 {. ]7 \
  38.         delay_ms(1000);) h$ N$ W6 k8 j7 I5 Q/ `* c
  39.     }1 u* ]1 _# D* H: `0 Z8 p) K* P
  40. }
复制代码
0 D* V" X9 M' v9 A* A
2 I8 F2 F' W$ d( X
赞 收藏 评论0 发布时间:2021-12-15 11:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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