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

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

[复制链接]
STMCU小助手 发布时间:2021-12-15 11:00
ADC1DMA_Handler.Init.Mode: k% ]- K1 @9 Y5 w
DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
! E- H7 g! G$ t# f3 ~" hDMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。( ^* S7 r5 X( V: N1 R0 C8 m& P
虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
$ C3 e* S! s1 Z/ L* _
; q! w1 p# k( d7 n' w6 pADC1_Handler.Init.ScanConvMode! j7 U/ s- E9 t4 Q8 d$ m  B
扫描模式(ENABLE):ADC会依次扫描设置的各个rank
, t! O6 z( K0 W非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
6 ]  V* ?$ O! r8 e% f即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~) o/ f3 N: M' q8 Z4 z3 a+ \
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。! X" p: w* p4 f2 }/ o4 ^
8 Z7 k! A4 Y0 S- O- B
ADC1_Handler.Init.ContinuousConvMode  n8 W0 s: X/ ?3 j- p. X* q
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。2 W: P9 A0 `) U4 e# e+ _

$ E  X/ h; ^8 Q& I* g
20200419192759329.png

- t4 `) y! U6 U4 x6 I, i# J而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。% t4 k% Y( o6 p8 i; [; P
20200419192936345.png

: d  b3 c. \# @! }" _- T# M# R
) w% z. r+ W& x6 L7 {而将传输长度设置为3的时候,就进入了传输完成过半的中断。
2 }  \; w/ \( s3 |+ A( t
3 y7 L' _& |5 E+ \9 @
20200419194129548.png

& c1 T% v" n, W# p" n- w* }6 G
+ U1 D$ J( N9 ]( J如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
5 w( Q% \: x/ k9 e4 D! K; ^也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。
4 d" e* O6 Z: Z3 y
$ K+ [* e5 A* q7 B7 n在另外一篇里看到了这么一个表格,希望能够辅助大家理解:& U0 I* c0 w+ ^, o8 H, `; w
% b- l$ A/ V8 l% u8 }& U7 B
20200421214739891.png
5 b, }% m) w3 {# B

. s$ d* Q) R9 C7 [9 y( vADC1_Handler.Init.DMAContinuousRequests( s  K, j! \' K: o. `* p
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。( ]* ^7 O7 H+ `3 `2 J8 m
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。
2 g6 |5 Y# J$ p- M1 ~. O/ w
4 T- B9 W1 c2 T) m1 ?5 a- E但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……3 V  i8 E3 R/ @
/ z: ^- A6 [' i+ L/ z, y
ADC1_Handler.Init.EOCSelection# y; A- z: t# T8 D
EOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……; W3 F& r5 ]2 B3 a% A0 ~
填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置
# O; D9 ^: z' d  H* i
1 H$ J/ e0 R( z# _( h偶然看到一个帖子的回复:3 ^0 W9 u8 P  K! o0 ~1 z! M
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。
  |6 v/ h- u3 P! R4 u3 j$ A为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
% M5 g% r) x' P+ l9 F- GCACHE行的数据写进SRAM1。3 c" z2 A; j  [) M) ~- u
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。6 C3 w/ L) Y3 r  A, n$ D( m
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
. z" r1 u. `4 {  J4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。  r) p8 S9 d! R( T; B
$ o( Z% ~+ S( U1 V5 M% p$ Z
在main函数中关闭cache,DMA采集的数据就可以更新了。
9 i3 i6 S8 O, A
) D9 g" y% z9 f- @; m. G
20200419215859139.png
8 T4 _- J( V0 v; m% y
/ u. k/ d# w$ X' I& ^4 U6 Z9 y  p  E
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。/ d/ O% N8 x( O4 M
$ G$ O0 F: O  p; H7 }+ t
  1. #include "adc.h"
    / r2 C5 s/ W, ^. }& F9 X+ v
  2. #include "delay.h"
    : c( v. P/ r8 `
  3. 3 i/ m& O) L; A. z
  4. ADC_HandleTypeDef ADC1_Handler;//ADC句柄
    5 i7 d7 n& [8 x
  5. DMA_HandleTypeDef ADC1DMA_Handler;
    ; [; e& `6 h3 T* I1 ~: w
  6. ADC_ChannelConfTypeDef ADC1_ChanConf;
    3 P/ A+ W/ [. W/ y9 z$ v! t3 X
  7. , x0 A2 B( J4 K; k% S- z, @! N# z9 G
  8. uint16_t buffer[128];" w$ E- B. x2 q2 `, ]8 v
  9. : F1 |" X0 O6 `* k; G; c$ p0 s
  10. ( P. ~* G( }7 C* e" d
  11. //初始化ADC
    7 J# A! m% b8 ^/ k
  12. //ch: ADC_channels
    : x$ W+ o  ~4 p/ d  M- j
  13. //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16* V/ B8 _. g, |
  14. void MY_ADC_Init(void)
    6 H, L0 O& p0 f8 o
  15. {
    : P% Z! K3 e& u0 P% w5 T8 r' u8 J3 N
  16.     __HAL_RCC_DMA2_CLK_ENABLE();  W2 n+ |8 A3 t" m
  17.     HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);4 T* m/ J. h* e
  18.     //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);' S: Y; J( W6 {6 [

  19. % G. k& K5 F" s- |! {9 l/ s
  20.     ADC1DMA_Handler.Instance = DMA2_Stream0;. O) b( b* t2 W: \/ g
  21.     ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
    2 y' W  b* b. P/ w2 H) d
  22.     ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
    ) H& s# k; J8 l  n
  23.     ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;               //外设非增量模式
    0 _$ Y, C% W' s# C
  24.     ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                   //存储器增量模式" `1 a/ A9 T; }4 h0 k- q* y
  25.     ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设数据长度:16位
    - M4 |3 Q# w+ p6 l3 c
  26.     ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //存储器数据长度:16位+ t" Q# L$ \1 @
  27.     ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR;                          //传输一次就结束
    5 Z! K+ e4 v8 t  G- g
  28.     ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW;             //中等优先级
    # F( k2 D" j/ L0 L
  29.     ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;            /* 禁止FIFO*/, w2 H$ B; e& {. \  @  p
  30. - `3 n) K! F6 X1 d* t; k0 {" A/ O7 D
  31.     HAL_DMA_Init(&ADC1DMA_Handler);
    * L3 j- p4 C: Y" ?
  32. 4 z2 O, `: |6 P: G2 F
  33.     __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler);                //将DMA与ADC联系起来# q6 N% s& l6 V" [' R% W" P+ v

  34. ! }! i$ ]$ A9 ~% n6 J6 l7 n+ x
  35. * P7 B& k1 M1 C, ?3 N8 _
  36.     ADC1_Handler.Instance = ADC1;, P# S. Y, Q% u+ y  X1 O
  37.     ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ9 Z7 d+ f% ~2 n! M. `8 n
  38.     ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B;           //12位模式6 S% C) O# [  @+ ~9 G4 }( ?! |& `
  39.     ADC1_Handler.Init.ScanConvMode = ENABLE;                    //非扫描模式$ A9 T% u. Z0 b& ]
  40.     ADC1_Handler.Init.ContinuousConvMode = ENABLE;              //关闭连续转换  Q6 N" J. f: y# J, K( M
  41.     ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;           //禁止不连续采样模式  }8 q$ W. t. T- b2 D
  42.     ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
    3 B, ^3 m* r: r  d, L( g) n3 w
  43.     ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START;     //软件触发- F; t* d7 ~) z9 i" o
  44.     ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;           //右对齐
    * o+ j4 Z( u+ M4 L
  45.     ADC1_Handler.Init.NbrOfConversion = 2;                       //1个转换在规则序列中 也就是只转换规则序列1' E- l* V* R) B! ]# t7 s2 b$ _
  46.     ADC1_Handler.Init.DMAContinuousRequests = ENABLE;           //关闭DMA请求
    , z! D+ D5 d2 j
  47.     ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;        
    ' J3 a* T( S1 b1 m- w+ K: |
  48.     HAL_ADC_Init(&ADC1_Handler);
    # ~2 Y/ B4 K) Y3 I- X, Z

  49. ) A0 l& U6 ^1 {8 L  V
  50.     ADC1_ChanConf.Channel = ADC_CHANNEL_5;                                 //通道0 _. R+ ~$ z2 U# Y" ~% L) G
  51.     ADC1_ChanConf.Rank = 1;                                     //序列1
    0 q7 t, _5 P" e9 o
  52.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间
    ! r- E: q. ]- R
  53.     ADC1_ChanConf.Offset = 0;5 _9 ]. a# Z; @9 q+ S
  54.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置
      V5 u4 U  @: X1 Q" j' v
  55. 6 H1 m6 [  P. }' M9 C8 V
  56.     ADC1_ChanConf.Channel = ADC_CHANNEL_6;                                 //通道
    6 W( v# [' Y; F6 P% u  X
  57.     ADC1_ChanConf.Rank = 2;                                     //序列2  v2 r& g  W1 z5 \/ a8 G# W* z
  58.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间$ }/ L1 b; N; R- m; a6 X# g
  59.     ADC1_ChanConf.Offset = 0;
    . w& U9 H' U* \. L' C
  60.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置
    , |% G5 d6 a' a# G6 ^

  61. 6 N, `" G0 C( w5 u  M7 r
  62.                 HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);1 u: r  r2 ^3 U/ W
  63. }: m: i* O2 j( F( t

  64. * ~; {% ^4 T0 I# S2 Q$ [; W
  65. //ADC底层驱动,引脚配置,时钟使能' q- |9 a9 q  e& L- {2 f2 W' I
  66. //此函数会被HAL_ADC_Init()调用
    ( r' G( Q+ ~: C
  67. //hadc:ADC句柄9 [7 ^0 r8 K$ f0 ?
  68. void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)/ N% S- j/ q* Y6 o3 x
  69. {
    7 N8 ~4 O# m( q- @, j
  70.     GPIO_InitTypeDef GPIO_Initure;" n  S/ n" ?. n  C; T; v6 c
  71.     __HAL_RCC_ADC1_CLK_ENABLE();            //使能ADC1时钟
    $ K" ^; t) J8 u1 x; ^
  72.     __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟& k. D- v( c% p- l- v- G6 L  G

  73. & S$ c4 F$ A4 O
  74.     GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6;          //PA5
    " c4 m5 P4 B. \6 }: N  [
  75.     GPIO_Initure.Mode = GPIO_MODE_ANALOG;   //模拟# C- Q- F9 r* B; l* C% A
  76.     GPIO_Initure.Pull = GPIO_NOPULL;        //不带上下拉( n3 _2 a5 [8 f
  77.     HAL_GPIO_Init(GPIOA, &GPIO_Initure);
    ) Z- `- f- ?; ?
  78. }, _# v: J% p1 F$ G

  79. 9 q* o( H: m5 P4 v! I) g% O7 S
  80. void DMA2_Stream0_IRQHandler(void)# G1 l( B3 v& {# k, ^
  81. {
    - q9 P$ b: L2 O" ?& u5 L# Q3 i
  82.     HAL_DMA_IRQHandler(&ADC1DMA_Handler);$ I4 y0 s- E# q  }9 C1 o1 n' N
  83. }
    2 {8 r  L4 {) V

  84. & ~$ k0 _4 [8 ~1 k* T" c1 q/ u
  85. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
    # x0 G0 X. L, z
  86. {% ^5 b8 n! O2 O$ F8 K
  87.         printf("DMA transfer complete\r\n");* u! ?* Y8 f, a' o9 M9 O+ _# d
  88. }- z9 {' K* W8 L$ n: X
  89. void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)* v8 K4 m5 z  M8 n6 N
  90. {1 Y2 X& n  a7 h/ u/ d3 @) n8 G8 o
  91.         printf("DMA Half transfer complete\r\n");
    2 c( _- y) h! C# ^+ o8 h: Y) h' i
  92. }
    6 q0 h' z6 S7 n( d0 U3 b
  93. ) O1 M# }  O8 y4 u
  94. void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
    - I! F. Z- F3 m/ f5 ~& \% U4 V
  95. {' Z5 o5 D6 A: \4 [+ Z- h  D' J$ z
  96.         printf("DMA transfer error\r\n");: A) n7 v7 R$ s  z" L, b. \$ h
  97. }
复制代码
/ A" s' E" e! I2 R. S0 y  y. X1 o
8 L  H% E! N: r# y& l
  1. #include "sys.h"
    1 R3 ?& |( J( `1 K9 M
  2. #include "delay.h"  }% h0 B9 Z- J' X
  3. #include "usart.h"
    8 X0 G' U& Z5 g4 _0 `
  4. 4 T- Y. J5 c2 m! X( X. t" W
  5. #include "adc.h"
    6 E' A: L3 C$ K$ n

  6. 3 @8 O! a, w0 a
  7. extern uint16_t buffer[128];6 @; |7 J2 \2 J4 N$ F5 s

  8. & n3 {8 m/ l2 \2 ?
  9. void show()
    + ~! l: C0 D6 G" W/ |! H( }( x
  10. {
    0 _) L# a" w$ n( E
  11.     int i;( \: z) b* W, D
  12.     printf("\r\ndata:");
    # X; P' ~) f+ |
  13.     for (i = 0; i < 128; i++)
    . ~2 h! B7 v& h
  14.     {
    " _( Z5 M3 U8 Q2 W) Z! k
  15.         if (i % 16 == 0) printf("\r\n");6 |) D' a9 T7 f
  16.         printf("%6d", buffer);% O" W6 s+ |( h4 U, \# m* |4 X
  17.                         buffer=0;# R) e4 D* D! E, n
  18. 7 r: Y. I* Z8 h
  19.     }+ m" q5 y% `) ~" r  M$ o2 u
  20.     printf("\r\n");2 B' ~" [$ Z* C1 }% B
  21. }
    * S3 i) d7 k  l# T7 F' d7 e

  22. 3 j. c( |7 v3 l/ h! G4 Z* D

  23. 4 B9 U% g4 B( f3 w# \! H
  24. int main(void)" B0 M* R+ {& D
  25. {' d7 m* p/ W% }. h$ X% \: A
  26.     //Cache_Enable();                 //打开L1-Cache5 P# x6 S. J: L4 b. f" j
  27.     HAL_Init();                     //初始化HAL库# r& O% `' |9 s4 ?7 h5 f
  28.     Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz
    * M0 P# Y  t$ k1 f/ \
  29.     delay_init(216);                //延时初始化
    ) e' {9 H3 p& y( e( L
  30.     uart_init(115200);              //串口初始化
    " a) ^2 f8 u! {4 Q( M6 R, J

  31. 1 S  @8 }3 w7 l, ^4 W  Q! S4 z
  32.     printf("start\r\n");
    6 ]& Q2 X; r) J3 o' L, Q
  33.     MY_ADC_Init();                  //初始化ADC1通道54 ?4 v% `* o) ^5 R5 Z

  34. + M% j" Z3 n/ ?% [" q& _0 c
  35.     while (1)
    3 s% b9 f, O1 l7 o
  36.     {; w, e/ c% x; [- q
  37.         show();  p& a9 c6 K9 ^
  38.         delay_ms(1000);  e* f. l( W# {" b. v$ a
  39.     }
    & @4 J( Y" M. c
  40. }
复制代码
! n% n' T5 p( f' T9 y8 m
. [* Q7 t1 R; p  y. r9 T' N# e
收藏 评论0 发布时间:2021-12-15 11:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版