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

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

[复制链接]
STMCU小助手 发布时间:2021-12-15 11:00
ADC1DMA_Handler.Init.Mode& t/ i( U/ D& V5 s1 _
DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。$ D# [; u2 E7 a4 B3 k! l+ p8 |2 n- u
DMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
! M! U/ _: y4 {7 l虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
+ e$ L0 s  t6 l% |  E6 ^* U9 B5 v' m; E: f! I1 X* ?1 V
ADC1_Handler.Init.ScanConvMode; z* [; o6 C4 g5 A) Q+ L/ ~2 u/ q9 S
扫描模式(ENABLE):ADC会依次扫描设置的各个rank6 [; _- Q- g4 B- w2 r( ^5 k' z
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
$ Y# X9 ^4 j$ x0 h* `+ j即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~2 M) p$ ?! r! m* u) K' f5 h
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。
2 q0 q1 K2 b4 T7 i
. M( x# Q' m0 S$ u/ G7 B+ yADC1_Handler.Init.ContinuousConvMode
' ?& \  V1 K+ p; r, H; p4 D% c开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。9 l5 d0 H, w5 v4 f3 \: Y( Y" Q
) b8 a, R9 `) Z; c
20200419192759329.png

. }% V' X$ j4 F, k而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。; H9 T8 E" o2 c
20200419192936345.png

5 w6 Q( _" w& |4 h
* o2 \9 l* E: Y6 N9 O而将传输长度设置为3的时候,就进入了传输完成过半的中断。* X6 G" p1 n5 I

! {$ d4 ]4 O6 h: |/ u$ Q
20200419194129548.png
  o" L6 n+ x4 w; D1 T

' }4 a. O; P% u; d2 U% j如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。9 u7 ^% c; ]/ P! q! A! b
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。6 w$ S/ B$ H! |; a3 e" V
% ]+ U5 b; r2 O. o6 t! @5 b
在另外一篇里看到了这么一个表格,希望能够辅助大家理解:( ?6 X, V* Q, o

6 P+ s' b0 q% \/ z
20200421214739891.png
6 Z8 }  R8 T! C
+ l! Y$ U; I2 J) t
ADC1_Handler.Init.DMAContinuousRequests! K6 w/ X! k( a6 q6 _! ]
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。- K8 A3 L- |8 c
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。
, d. S$ Y3 r  G+ o- ?$ z
4 O: K8 N( M9 C但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……# Q. a( m  h3 d8 ^( _
$ {3 V! J/ @% G& H7 u
ADC1_Handler.Init.EOCSelection  B' C4 l! V$ H) n
EOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……4 ?: e" c  x8 {2 r; f
填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置
2 q2 D9 J* v9 W( \+ H4 S5 V( A3 l) _6 B& B" ^( }
偶然看到一个帖子的回复:
5 r. l1 E& {- E. [8 M* h. ?& `当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。
+ o8 S, m# q. j# _* |  a$ m为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY2 ~( F$ P/ E7 l- H: l- P
CACHE行的数据写进SRAM1。
" O4 r. @& J- d6 t& ?4 g2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。
$ ~0 P' o& u2 }0 K) ?3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
4 p* r; |, _" ^, |) Z( X/ g( R# p4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。3 U9 c( V: a  k& E5 |" A! E0 G
' ]( \) I$ x/ k
在main函数中关闭cache,DMA采集的数据就可以更新了。4 d( D' L9 i0 M# R
% k' h" q5 y; x1 b8 k
20200419215859139.png

! I% d, E3 Q1 b0 @) j5 E) K6 U/ p
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。
$ g) y9 u3 S/ s4 O/ g6 h; F& P0 t/ ~( h) n( i5 V
  1. #include "adc.h"
    + ?" L' _. d5 ^6 p- {
  2. #include "delay.h"/ x6 h( A$ K- b7 n0 l( D; W: o/ F- f

  3. # J# r" {. e6 ~$ E# o% w" V* ]
  4. ADC_HandleTypeDef ADC1_Handler;//ADC句柄: ]5 j# E6 K7 ^4 l0 A- ?
  5. DMA_HandleTypeDef ADC1DMA_Handler;
    / f" {& `" r/ M; d
  6. ADC_ChannelConfTypeDef ADC1_ChanConf;5 q. ^7 G2 n; U' J6 R! g
  7. # H2 x6 u3 r; e0 x! q' [
  8. uint16_t buffer[128];
    : t. A* d& N: L8 K- ^) L

  9. 3 Q* J6 T8 }; {- m6 ?" B
  10. 1 y0 _7 e( E" ^# n1 O1 _4 @( C
  11. //初始化ADC* m) H  v3 j! v5 ]- E0 O# G1 }
  12. //ch: ADC_channels
    / w9 C' \; F. L1 V
  13. //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16% o  L2 C; y2 Q1 h$ @
  14. void MY_ADC_Init(void)
    $ H8 x2 u; J- M
  15. {+ T8 ]# D- ^: y5 V+ Z+ m
  16.     __HAL_RCC_DMA2_CLK_ENABLE();
    ! T, p  S9 W7 S! V
  17.     HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);# [5 H% \/ X# y% l* N
  18.     //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
    ( J8 g' u" K- K5 j* b5 D$ u; H2 L
  19. 5 _) r7 J% P( t1 z  Q( P; P, U7 u
  20.     ADC1DMA_Handler.Instance = DMA2_Stream0;9 O+ {( k/ ?% P7 G5 l- w
  21.     ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;  [: H8 s+ G) l: s
  22.     ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;# g& Z0 t& {9 U# p: l
  23.     ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;               //外设非增量模式+ a1 b" g  z1 |! _& [  ^
  24.     ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                   //存储器增量模式# [* y- p6 T  u& A3 K
  25.     ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设数据长度:16位! W3 w! I  Z2 i3 S
  26.     ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //存储器数据长度:16位* o) y9 I# A1 t, c' G( V
  27.     ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR;                          //传输一次就结束: J0 ]+ N) ~: V
  28.     ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW;             //中等优先级9 ?2 U$ {, M) t) N% e2 P* z
  29.     ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;            /* 禁止FIFO*/+ S7 O# z4 r; b

  30. 6 ]$ Y7 L0 S7 s$ D: g
  31.     HAL_DMA_Init(&ADC1DMA_Handler);
    7 ?; ?% m0 w' Y% E

  32. 8 Z( U) X% j0 y# A1 n
  33.     __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler);                //将DMA与ADC联系起来1 i4 C" ^) _2 j9 B

  34.   G# e7 N, q: f+ C, w$ p! O
  35. # P6 N. C" g/ L  L: I. M
  36.     ADC1_Handler.Instance = ADC1;
    # C: i5 r3 K; a; V8 z" J
  37.     ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ# S8 P/ D! W5 R- V- _; }5 O
  38.     ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B;           //12位模式
      M" @( ~: t6 z9 M9 |
  39.     ADC1_Handler.Init.ScanConvMode = ENABLE;                    //非扫描模式
    - \# u4 B* ^' z- x
  40.     ADC1_Handler.Init.ContinuousConvMode = ENABLE;              //关闭连续转换
    7 p. ]1 M9 D2 J+ R+ q$ d
  41.     ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;           //禁止不连续采样模式# X' G8 ^/ u, u4 W( l4 y! J, u
  42.     ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发7 g; q  A# a6 V$ F5 ^& Q
  43.     ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START;     //软件触发; T5 l0 l- E3 P$ `9 z; k. F
  44.     ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;           //右对齐
    / v( L0 j# x1 O6 P
  45.     ADC1_Handler.Init.NbrOfConversion = 2;                       //1个转换在规则序列中 也就是只转换规则序列15 t8 @5 }  Y2 K+ S$ a  Q
  46.     ADC1_Handler.Init.DMAContinuousRequests = ENABLE;           //关闭DMA请求
    ( N' O9 [* n/ ^% R7 y, k
  47.     ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;        3 F; \; x7 {( K! _. v
  48.     HAL_ADC_Init(&ADC1_Handler);: ]/ a6 t# D3 a& {/ S

  49. 6 Q- @# O2 P; y9 z/ L
  50.     ADC1_ChanConf.Channel = ADC_CHANNEL_5;                                 //通道" g: a8 `0 y9 o% g$ u6 q& S
  51.     ADC1_ChanConf.Rank = 1;                                     //序列1. ~; C0 p/ U5 Y* Q( m
  52.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间! ?5 y$ Q1 z1 ]
  53.     ADC1_ChanConf.Offset = 0;2 \% K' o6 `/ Q: b) d' L9 i% D- x
  54.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置
    3 ^/ C+ Y! f3 w

  55. % J& h! W) [" M; q; ~2 h5 e: I
  56.     ADC1_ChanConf.Channel = ADC_CHANNEL_6;                                 //通道
    & q- p( \( N7 ?( H4 Y: Y
  57.     ADC1_ChanConf.Rank = 2;                                     //序列2
    & b" g% l6 Q9 n. N& p4 {
  58.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间# N4 i1 k2 D+ ~4 E3 T
  59.     ADC1_ChanConf.Offset = 0;
    ; ~# n3 A0 S- e; u- H
  60.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置0 ]# i6 i1 ], H/ g4 _& I: y8 \
  61. ! f; B, i; r, a* F) T5 E
  62.                 HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);. R. G: d9 `$ |( m6 w* [/ Q, f
  63. }, c  e0 v. V  i3 Z: E8 B5 F
  64. , q; [! [/ F3 I' v
  65. //ADC底层驱动,引脚配置,时钟使能7 o$ A2 U1 K) {0 h% \
  66. //此函数会被HAL_ADC_Init()调用  g# I% ^( E3 {1 I
  67. //hadc:ADC句柄
    & |; z5 Z9 U6 Q
  68. void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)2 L- k5 }, T' j
  69. {* L  f' p/ ~, ]3 i- Z* G
  70.     GPIO_InitTypeDef GPIO_Initure;
    ) r1 s4 K  ~. n: _3 R: [2 [
  71.     __HAL_RCC_ADC1_CLK_ENABLE();            //使能ADC1时钟8 o; X% G8 D3 |* U3 o% [' b
  72.     __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟
    : D! H/ O& k" ]% x/ H$ p6 H
  73. . B3 v1 y7 \4 e+ L! z1 h: e
  74.     GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6;          //PA5" S1 Y) w$ T# s3 V" [
  75.     GPIO_Initure.Mode = GPIO_MODE_ANALOG;   //模拟. G. ?( ]7 j: F
  76.     GPIO_Initure.Pull = GPIO_NOPULL;        //不带上下拉
    5 z; Z  W% D' }3 }8 u
  77.     HAL_GPIO_Init(GPIOA, &GPIO_Initure);6 T+ c' q4 x* U. Z3 X0 @
  78. }' o$ O* h# }; ?. U" g: T
  79. 1 e0 A# f! r, q7 I: g
  80. void DMA2_Stream0_IRQHandler(void)3 b8 I( e1 ~6 E$ K% y) E( s& [' w
  81. {5 M1 f- s- R0 n# s
  82.     HAL_DMA_IRQHandler(&ADC1DMA_Handler);
    * j( F$ `; P* R- j. j  f  |
  83. }
    " h% n4 g/ K8 t6 V' f: {! r

  84. : m5 q) P" p& _: ^" I+ b
  85. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
    4 Q- k9 Q6 s5 {* j* q2 w* F8 _
  86. {7 n, r8 F6 g( b0 u# E  o1 T4 L
  87.         printf("DMA transfer complete\r\n");9 n/ h7 H; e5 x4 T
  88. }# I/ `( [! I9 K( l2 N' H
  89. void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)4 p5 Y8 w4 _, }' E
  90. {
    : ?- h5 e6 L: }/ |+ T, h+ g* T
  91.         printf("DMA Half transfer complete\r\n");/ I. i! k4 \. M0 }0 l
  92. }
      ^6 w- E" X, t9 f8 x: r

  93. 4 u) B( g- w& W9 v, j  s- Z% k/ N5 f
  94. void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
    ' Q! a+ z; [2 w! C  O( p* G
  95. {
    1 ^: Z4 z$ s% A' A5 `- T( f
  96.         printf("DMA transfer error\r\n");
    ' {7 i6 S4 |1 }7 y9 ~
  97. }
复制代码

" d  y, O/ h0 }+ H0 ~+ z! Y5 I+ ]
: j2 }3 p  s1 }
  1. #include "sys.h"0 o! N9 M" N8 Y/ t& k: U
  2. #include "delay.h"
    ( x' i1 l2 c; J- b  s# V  P8 v/ M, G
  3. #include "usart.h"
    4 M* A9 i  }0 u# c/ P2 u( D4 C  P' q* O

  4. " d- D+ |1 S- z: ~3 |) E
  5. #include "adc.h"
    % o4 F* i# o' |3 ]) d

  6. 5 Y* D% N" R: w. P. m9 t
  7. extern uint16_t buffer[128];8 k5 a9 \# n& i* }* ]) }! l  T
  8. 0 e9 i# k0 [4 Q8 a2 p
  9. void show(): F+ o; W. l1 z5 e
  10. {
    $ D2 F2 o( [0 N# L5 c5 U4 P
  11.     int i;. x4 N& q8 {! e" h, w& O0 M+ H
  12.     printf("\r\ndata:");* b) r5 t* ~. X& `# u' l
  13.     for (i = 0; i < 128; i++)
    : `3 q1 s& y, b, `5 n
  14.     {8 R2 I$ ^$ z' B: k: k
  15.         if (i % 16 == 0) printf("\r\n");
    1 R2 o8 W0 E9 ^
  16.         printf("%6d", buffer);
    ) d( O9 S2 O+ p; s1 M8 ~
  17.                         buffer=0;
    ) A6 e7 i! ]5 p1 H& n8 B% w1 A  f

  18. & `; U3 C8 Z  J. N" q; L. }6 q
  19.     }
    - G. z2 v& s8 B# R' P$ \/ W- G" k
  20.     printf("\r\n");. y* _! x& Y4 p8 `& ~
  21. }
      Z: R) V* Z! C) e  H' P

  22. - \9 M) Q6 U; {3 q5 n4 m4 T, Q
  23. 2 _! z# ~  p% D3 h
  24. int main(void)
    . x( n6 r1 Z  B' N4 D6 i& f" t& @
  25. {
    / P& v* \9 H3 b& r* e: \1 S4 s
  26.     //Cache_Enable();                 //打开L1-Cache
    8 C2 o; b3 ?  M% z7 j
  27.     HAL_Init();                     //初始化HAL库
    6 g: h8 U1 O2 Y. v
  28.     Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz
    " p: ^# ?6 f: A$ J  k* d+ i
  29.     delay_init(216);                //延时初始化
    . y2 g  h, \, b6 [' Q
  30.     uart_init(115200);              //串口初始化
    ! w! `* ~5 y' j. H
  31. ' I$ J- ~- y  `7 {/ Q
  32.     printf("start\r\n");4 y1 n1 h1 s+ Y7 J
  33.     MY_ADC_Init();                  //初始化ADC1通道5
    6 P/ E. C* H( R( J. b1 B
  34. ' |7 l& B6 h+ o& K, ~9 P* t
  35.     while (1)
    9 a( B0 k8 I# W1 X7 ^: L
  36.     {; X' F( \' {+ Y/ r; J* |& O% @
  37.         show();
    3 W: ]  P7 d* f1 S; A& {
  38.         delay_ms(1000);
    ( @9 x8 s, K- G% a$ }
  39.     }( Z& ~, _# T/ d' r1 `8 \
  40. }
复制代码
9 V- c0 p, F. L/ ?: D; m- j
/ l, D% K1 ?3 U5 J
收藏 评论0 发布时间:2021-12-15 11:00

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版