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

基于STM32F7进行ADC采集解决更新问题

[复制链接]
攻城狮Melo 发布时间:2023-3-14 11:26
ADC1DMA_Handler.Init.Mode
$ P/ v" G# a; `5 YDMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
% j/ \6 H+ I7 i  p5 K# T# XDMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。0 y6 k' F( k0 K; k/ ^
虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
5 K3 t! k. h* u. \/ x7 r) w

! o3 x  I: d, |. |, w; c, ~) n: tADC1_Handler.Init.ScanConvMode
& I1 I8 |7 K/ ]. G3 m
扫描模式(ENABLE):ADC会依次扫描设置的各个rank- \  p# h; [/ `3 \
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……% Y; P% b$ @  t
即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~4 d1 m6 P% j5 T+ M- z
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。: z0 k- T& F# P1 a. N1 S/ \
2 Z2 \- F3 R& {, y
ADC1_Handler.Init.ContinuousConvMode4 R* N1 W( k; c- a2 a
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
; x' \, L( b6 {6 _  F7 @( B: w: b3 H. J2 ]$ i9 u$ v$ t2 H
20200419192759329.png % O0 t# X$ z+ w

( J0 M3 j8 }# X" T, w而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。+ [, j, r; z$ V) C2 L
& L3 R! ?0 }0 O" [; n$ U
20200419192936345.png
# `! t( E" q, a
  Z0 W4 h+ r$ W  w" j! ]而将传输长度设置为3的时候,就进入了传输完成过半的中断。
$ P9 H; C& B9 j; }" {6 M2 r
20200419194129548.png
" y7 M, A" Q* o2 ~+ O
# s" f3 @  W( q; @( b/ E( p
如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
8 K. x' b. k  A' {% ]也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。
9 I* I$ v# Y$ f/ X1 Z' X) Z5 M

, {8 r! V) x" o: y% E! {$ `5 b在另外一篇博文里看到了这么一个表格,希望能够辅助大家理解:& s# S" p- u' m$ l% A! b5 K

7 F- I/ H" F5 @9 ^$ e9 U9 J0 m+ B
20200421214739891.png 7 D) r& t( `, a

8 v2 C, s8 t$ s* Z; V9 KADC1_Handler.Init.DMAContinuousRequests0 Y8 G$ f0 m! M( _
当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。5 F2 \; I- Q4 m
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。( q/ D1 A8 g' B) m  B) f  Q; ^

9 |5 ^6 e2 n5 O. \但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……8 A( e: Y' z" s7 B

% _, \" n4 x8 qADC1_Handler.Init.EOCSelection1 p) u  _9 H5 O6 y+ `0 L
偶然看到一个帖子的回复:
1 q# V7 i- w' w) |! G8 q当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。' L: }& I# ~$ d  W5 d4 d
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY) |. R0 M+ L; u
CACHE行的数据写进SRAM1。
( a% H+ p  W3 ?1 [2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。' X- z, }6 v4 N' O" W& |
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。, l/ C& @6 @% n1 _- M
4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
$ H3 v, @7 p- H
. m! R# f9 J2 {9 O在main函数中关闭cache,DMA采集的数据就可以更新了。
8 l! q7 H/ w1 B! n9 i8 J5 s3 q  M3 R9 x+ `
20200419215859139.png 9 I4 ~1 [$ v  G3 p/ ]

* i/ ~& z% j8 i- }' i: \9 k附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。
* s, n1 h) \0 M# H& h0 C) s
  1. #include "adc.h"
    7 }8 n1 y: `* E" Z
  2. #include "delay.h", t7 l) ?+ R& M% Z3 q+ a, Z, K' m
  3. + m7 l7 Q: M' z/ o) _
  4. ADC_HandleTypeDef ADC1_Handler;//ADC句柄+ q9 j" o4 p) O- M
  5. DMA_HandleTypeDef ADC1DMA_Handler;
    & p( R  \3 e' r/ \7 d& |
  6. ADC_ChannelConfTypeDef ADC1_ChanConf;
    : G3 V+ }& x! @( l6 `
  7. - }# i# l& [0 N/ `. p
  8. uint16_t buffer[128];
    ( @7 ^9 u. d9 m1 `% [& ~2 ^9 U

  9. 0 C- C: v8 I3 l

  10. $ c% `5 a& n5 j2 m1 F
  11. //初始化ADC
    * m7 G9 Q. X6 @# \# B" x+ E
  12. //ch: ADC_channels
    5 w  P/ `* Q, o7 [5 {
  13. //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_160 ]" C  Z' X) T6 x+ y) O
  14. void MY_ADC_Init(void)
    $ ^& C, Y9 o& |" I  J0 r
  15. {
    * F9 {; o, B# F6 P
  16.     __HAL_RCC_DMA2_CLK_ENABLE();
    ' i; ^# D! \7 |8 X  g
  17.     HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);7 `' Z, K7 }' q3 ]* C7 j
  18.     //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);# L$ M4 h8 k; M  S, l- o

  19. ' t+ K2 C( Q; E9 @, @, X; R% I
  20.     ADC1DMA_Handler.Instance = DMA2_Stream0;9 E. r1 E' p' n
  21.     ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
    1 P8 y* Y1 O) q0 s
  22.     ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;/ ^; S8 s9 T  I) ]4 p
  23.     ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;               //外设非增量模式. I- y- x- X0 a5 m9 R$ V
  24.     ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                   //存储器增量模式
    , y( \; C. @- C! X
  25.     ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设数据长度:16位
    2 m- a. p7 W( ^/ e: ]
  26.     ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //存储器数据长度:16位
    # o0 @3 W+ l1 @, ^
  27.     ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR;                          //传输一次就结束" S1 W6 Y' b% S8 y1 Q1 e! \
  28.     ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW;             //中等优先级
    4 Y- ?9 ?9 X7 }6 r! [" f) d8 Z
  29.     ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;            /* 禁止FIFO*/
    * _% v- w# W3 N0 O1 X1 l
  30. ' _! l' ?1 I; O
  31.     HAL_DMA_Init(&ADC1DMA_Handler);
    ( i/ H% u# O2 e6 V& \
  32. * J# ]4 a% ^# X5 \2 X
  33.     __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler);                //将DMA与ADC联系起来3 L5 u8 u3 W" D5 {  m
  34. " s5 A. ?+ B7 p' ^

  35. 6 f# W4 ?4 M7 v$ Y3 E7 R  f& w
  36.     ADC1_Handler.Instance = ADC1;. w6 E' D7 Q! B5 A/ G
  37.     ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ" K' b7 w, L2 F3 R7 Y: X" l
  38.     ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B;           //12位模式
    , \! w" M& e9 @; w& C- P3 q
  39.     ADC1_Handler.Init.ScanConvMode = ENABLE;                    //非扫描模式6 X) n7 O9 s) I4 O3 O# q% C
  40.     ADC1_Handler.Init.ContinuousConvMode = ENABLE;              //关闭连续转换
    . S# Z/ a. Y! ^. v& Q7 {: G/ k
  41.     ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;           //禁止不连续采样模式
    0 ~. T: t# L  Z. p
  42.     ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发: S% P4 V$ z; s
  43.     ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START;     //软件触发# b( X1 O3 O0 C& n4 p
  44.     ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;           //右对齐
    4 F0 X  D$ I3 ~* p4 H
  45.     ADC1_Handler.Init.NbrOfConversion = 2;                       //1个转换在规则序列中 也就是只转换规则序列1: Y& _2 ], f2 g# Q
  46.     ADC1_Handler.Init.DMAContinuousRequests = ENABLE;           //关闭DMA请求
    3 O- @/ G$ F. F; G# v# K2 ?+ a& P
  47.     ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;        
    7 e9 Q  M8 D' M% R
  48.     HAL_ADC_Init(&ADC1_Handler);
    & c  g* {# ]( |! g

  49. . j8 r( J. v. j$ I2 J
  50.     ADC1_ChanConf.Channel = ADC_CHANNEL_5;                                 //通道
    9 {8 \7 T$ v8 R2 M6 d
  51.     ADC1_ChanConf.Rank = 1;                                     //序列18 k  b3 g- h$ D; X
  52.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间3 l/ D  I# o$ |. J
  53.     ADC1_ChanConf.Offset = 0;
    ! z3 o" h% x, T4 _& ^8 ^/ |
  54.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置
    / M5 {" R' A; ]5 o( D" Z$ Q1 O. R4 ^& r

  55. 7 {( z9 o" z0 A# e8 i" R. j, a5 l
  56.     ADC1_ChanConf.Channel = ADC_CHANNEL_6;                                 //通道
    4 S+ C) R' ~6 @( j
  57.     ADC1_ChanConf.Rank = 2;                                     //序列2
    5 [! p3 I( v0 w9 S$ }! Z
  58.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间: m+ k* c' k' D7 e
  59.     ADC1_ChanConf.Offset = 0;! Z9 [/ ^. H$ h2 q. }% q
  60.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置
    7 K; Q4 y2 t% E
  61. / c+ p6 }7 U& Z- Z! E8 }
  62.                 HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);
    ) N" j) P" p( q  A
  63. }3 l1 \; r8 ^- D! S& I" F, t3 f

  64. 3 J3 y0 G; R- h; j4 r
  65. //ADC底层驱动,引脚配置,时钟使能: Q) V: y# L: e% i- ?' i+ C
  66. //此函数会被HAL_ADC_Init()调用4 M. i. m$ k0 L
  67. //hadc:ADC句柄  v) A. B2 n8 z7 y
  68. void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
    , k3 n/ V8 T3 \( ^5 M
  69. {
    5 f$ g, B5 s: g: f* e
  70.     GPIO_InitTypeDef GPIO_Initure;
    , z& `' r+ ?; ?" {9 k# E  e, `# f. X
  71.     __HAL_RCC_ADC1_CLK_ENABLE();            //使能ADC1时钟$ m. j& M  s" `, `' C& Z  `
  72.     __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟
    $ E' b* i1 S4 c5 w8 _, \
  73. / _0 M5 \; p  @* p+ b
  74.     GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6;          //PA5; n% t: x; Z: j
  75.     GPIO_Initure.Mode = GPIO_MODE_ANALOG;   //模拟0 Q) ]2 ]2 Y1 G( b2 X) u6 L1 a
  76.     GPIO_Initure.Pull = GPIO_NOPULL;        //不带上下拉
      l" x5 A3 P4 v. M
  77.     HAL_GPIO_Init(GPIOA, &GPIO_Initure);9 _3 A4 |3 F) z( M1 j
  78. }# ^0 l. P7 q0 P, q( b3 c3 F

  79. 8 ?  ^( d! N* v1 f
  80. void DMA2_Stream0_IRQHandler(void)1 a9 g$ D6 Y" T, G6 \. W) ?
  81. {
    0 a3 T  @# ]  O- b& t* i/ D
  82.     HAL_DMA_IRQHandler(&ADC1DMA_Handler);  b# t; T, p, y' y3 {% [
  83. }% Z: d% J: O$ V0 Q- y0 M& `7 @$ ]) P3 {

  84. - p4 O; [4 n) s5 b
  85. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)$ O; s4 e( K5 \% T) S
  86. {2 z8 x5 C0 b# e) v  l9 z
  87.         printf("DMA transfer complete\r\n");0 [+ E1 b( o0 A
  88. }
    ! U$ ?. g: T3 k2 H: Z; N) R
  89. void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
    ( [4 o/ E8 I. f: ~. ^: C
  90. {: R# W9 X( \0 _0 Z- }% L1 D
  91.         printf("DMA Half transfer complete\r\n");* \3 Y# _3 Q2 `1 L  t5 g  M8 F# b
  92. }7 C7 j( X; ~) I) R+ S; k

  93. # M1 O+ O2 S1 q8 j$ J
  94. void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)" H$ c0 X! f* y1 f
  95. {( H0 ~' k6 s: L7 m, t4 v. y8 `  _
  96.         printf("DMA transfer error\r\n");
    5 ?0 s9 c9 p; A; h/ J( D! R+ o
  97. }% q- A1 u4 f* n
复制代码
  1. #include "sys.h"
    $ p1 I9 Q: l- ?5 f1 w+ S1 G
  2. #include "delay.h"
    4 U( R3 B* `+ c
  3. #include "usart.h", Y- b( S0 q$ p7 x5 z

  4. - q& T' U& _/ U( V
  5. #include "adc.h"  |8 V* C' \* F
  6. 6 d0 ?+ F+ |. Y0 O' W
  7. extern uint16_t buffer[128];$ M/ ^2 E* ?& ]  m# }3 O
  8. 6 u" J5 F8 c! |" }+ c7 g/ |
  9. void show()4 p( m8 P) u: E& G" \
  10. {
    5 \7 v& x  O9 @* G3 E9 s
  11.     int i;
    - I" Z, m  u7 C6 N$ u
  12.     printf("\r\ndata:");+ b4 m# m9 [" Q: b
  13.     for (i = 0; i < 128; i++)+ B" j" L0 f  \/ O9 l9 G, g" W
  14.     {
    % U) A8 L# z+ D1 c' d
  15.         if (i % 16 == 0) printf("\r\n");
    3 W" B1 z6 g, T
  16.         printf("%6d", buffer[i]);
    & y) \. _, y, W/ u5 o' i' f/ F% ^
  17.                         buffer[i]=0;9 h* N6 l) s# C0 M" J

  18. # A* ^. E0 Z3 G  f' o
  19.     }$ {( x+ {; a) k) h
  20.     printf("\r\n");
    % K- d  t& o  [, |9 n6 J1 Q
  21. }) a( I2 `+ ~3 a% f
  22. 0 a, E; P- }: r* ?/ U, b' H
  23. # i; T5 \( |8 }- X, h3 ?
  24. int main(void)
    6 @& l8 W% m, J* R: |* W) {4 R9 X
  25. {- S% R+ Q3 `5 l+ t/ J: P
  26.     //Cache_Enable();                 //打开L1-Cache+ `8 Y3 B4 W# d. D- Z$ y1 M: B$ n. y' |
  27.     HAL_Init();                     //初始化HAL库4 }! X% ]2 n' z, ?* z
  28.     Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz: Z# \: Y- H5 u' d- f+ {# u
  29.     delay_init(216);                //延时初始化
    % u* p: z3 [; ~, K/ R8 I$ d
  30.     uart_init(115200);              //串口初始化  c( ?, r! W) W+ V, I/ q+ O
  31. 9 u0 Z$ {! N. C$ y, j. W# i7 F- S
  32.     printf("start\r\n");
    : ?9 ]" A8 A- R2 [8 d" {
  33.     MY_ADC_Init();                  //初始化ADC1通道5
    6 f. E) D5 ?8 d2 @' j- W1 X; S
  34.    
    ' w5 \9 ~7 E: p
  35.     while (1)2 l0 o; V6 L7 s4 H2 C' |
  36.     {, e) i2 ?$ t$ v! X; g- k! `
  37.         show();
    , Z8 s% T- {2 u/ i+ g8 [% n( h: \
  38.         delay_ms(1000);0 x- l' @9 U7 C* P+ O& Z# a
  39.     }
    8 y, c2 R# T  V) w5 S
  40. }
复制代码

+ o; C: \) i% ?' ]$ U$ w& h- b# Z- M————————————————6 m& y. {/ U% h% J' Q9 g. u) C9 h
版权声明:小盼你最萌哒
8 w# Z* {$ b+ J5 e/ y8 n5 q) |  b如有侵权请联系删除2 u; X+ I/ N0 Y8 K, D8 W7 n
* U8 x: ^4 r( _3 e  u4 ?* `! s  J+ r
收藏 评论0 发布时间:2023-3-14 11:26

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版