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

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

[复制链接]
攻城狮Melo 发布时间:2023-3-14 11:26
ADC1DMA_Handler.Init.Mode
# c$ f  {: d! h0 j8 r, ZDMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。7 L  z% Y* d9 G/ s: t" W3 M8 |$ L
DMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
2 E) }7 g) g, h7 \, B$ E虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。
! H" r: f6 Q) I! j% K
5 l; m( Z; d4 G& f4 ]/ y
ADC1_Handler.Init.ScanConvMode
2 I7 n5 M/ `$ [9 }; |
扫描模式(ENABLE):ADC会依次扫描设置的各个rank$ J' Y& l9 R4 c4 I8 ^
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
* C. r/ G/ Q! J8 G8 ~+ w6 y即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~/ g9 M* J5 _& Y  R8 ?! @
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。) G/ W  n4 d: O  i

8 A7 `7 I5 T9 y  `ADC1_Handler.Init.ContinuousConvMode1 E; i$ y) q/ H1 X! J
开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
" B2 k" w/ h, l# l* b% K% p& r- r; h
20200419192759329.png
4 c0 Q8 ]+ d/ j* A( ^
0 ^4 V/ k% u# G+ F) W  I! D( |5 _
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。
, K! _, a+ {# }9 Y2 [! ^
" b, Z0 M0 I: _, U. f, T: f
20200419192936345.png ' w0 J7 X7 B: N# P

' G! p6 @$ P* `7 X2 b而将传输长度设置为3的时候,就进入了传输完成过半的中断。4 u* }5 S+ a, z& S, a  U0 p; A
20200419194129548.png
+ O+ h7 q4 w6 n8 @5 ^

" K) |$ ?5 X& S# k如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。1 |. X* `3 S7 d& j) l5 W
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。8 R8 k& N! @" _
% z& p9 O8 a! g8 @1 w, d
在另外一篇博文里看到了这么一个表格,希望能够辅助大家理解:+ ^2 h7 m; d* D  K/ O

9 `0 S$ G2 M  o1 d' B0 r4 h9 o
20200421214739891.png
8 C( f' W# q# w- P) k$ ]2 P" W
; f  D! C/ o! v, s" N
ADC1_Handler.Init.DMAContinuousRequests
6 s2 [9 Q) u. @' R! X0 q" a0 G当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。
5 ^% Q) G6 ]+ C; ?8 J! V( G这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。% D% W) P8 J1 d. j  b6 g7 Y) k
( {! l2 i9 ^# u4 f8 B
但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……) |  I) d+ c/ I' T

4 K9 `, K) }+ t* P' R  h3 kADC1_Handler.Init.EOCSelection+ o0 `+ s" _1 d* Z% ]
偶然看到一个帖子的回复:. K2 {& C9 h: x
当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。  F5 C7 m. H6 ?5 V5 X- F+ C3 H
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
$ c; K: W/ a. f" q2 s' o9 C% kCACHE行的数据写进SRAM1。3 x" P6 d6 p) P' s( l3 N# M0 n9 H
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。& X7 I' W' R& H  \
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
& a7 p% y& x: g! @4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。
5 `# B; [* U; f" X5 L: c: J& G0 G& I8 {0 O# j1 R3 B) {$ Z
在main函数中关闭cache,DMA采集的数据就可以更新了。$ y; ?- D' ?6 m4 @9 u
" @0 n# }( y; G5 @& C$ L/ i9 h
20200419215859139.png
2 A! Z! r. {4 C; x

) H/ l6 D; M5 B附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。6 G2 g- Q6 p0 L. J0 Q) q
  1. #include "adc.h"
    2 u, ~6 z  T1 t
  2. #include "delay.h"
    ! n4 g" i0 A: e* j

  3. 6 t; S$ T" S) T3 K6 S0 y. N
  4. ADC_HandleTypeDef ADC1_Handler;//ADC句柄- S8 X, P  {7 z$ P
  5. DMA_HandleTypeDef ADC1DMA_Handler;
    1 R9 h' m2 O* m
  6. ADC_ChannelConfTypeDef ADC1_ChanConf;
    ! w; |: a1 D/ K6 R) |% M
  7. 5 Y5 U  O2 j, G- s' K# @! y- h, r% ]
  8. uint16_t buffer[128];3 K1 @; w, i0 j6 _2 ]+ h
  9. & M4 ?2 g0 k3 D. _% y

  10. % @' i8 v. p( J) \" Y
  11. //初始化ADC
    ) y% l' X0 H4 |/ b/ X
  12. //ch: ADC_channels
    2 Z9 f4 }8 @" c& @. W0 p
  13. //通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16: ^$ P; M1 S$ H+ S
  14. void MY_ADC_Init(void)
    , J5 K: ^6 {% x0 t, B1 \# `
  15. {- |9 v  D% }7 N3 [% G' E
  16.     __HAL_RCC_DMA2_CLK_ENABLE();! H% g# J" C7 W* _
  17.     HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);4 C9 s. Q8 Y4 [- [
  18.     //HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);3 z. ~( m, G9 H

  19. 8 f  ^& v1 S+ |$ \% \0 q
  20.     ADC1DMA_Handler.Instance = DMA2_Stream0;8 Z6 Z7 A4 K7 P$ \
  21.     ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
    ' G* z& }# O% w. a
  22.     ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
    8 @! c+ N! R5 _; n7 |
  23.     ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;               //外设非增量模式4 r1 _+ Z2 G5 X4 k( Z
  24.     ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                   //存储器增量模式
    : @# q4 \" G& c/ k  n0 A0 j; D
  25.     ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设数据长度:16位' |" J+ j% Q2 D* W( m3 k
  26.     ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //存储器数据长度:16位# o6 U* `" c$ {- x  y3 J% c; m
  27.     ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR;                          //传输一次就结束& L0 y; m- f, i+ K5 \3 B
  28.     ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW;             //中等优先级
    % d* [; I# f  Q: y  Z% f# h
  29.     ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;            /* 禁止FIFO*/
    ( i- r- {) m" G% _

  30. 7 w( W. L- m3 L- V- P4 I9 S( |
  31.     HAL_DMA_Init(&ADC1DMA_Handler);
    ( k5 [+ _/ U- I+ Z
  32. 5 ]' O0 k6 U0 J5 `4 k. v& \4 x6 i
  33.     __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler);                //将DMA与ADC联系起来1 ?+ a$ [" h& _3 G
  34. / Q( T, R& I3 A

  35. 3 p( p# ~4 j/ o& p
  36.     ADC1_Handler.Instance = ADC1;
    & L0 k1 [' t) @2 A. j
  37.     ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ$ U& I: m& |, _) X$ K* n
  38.     ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B;           //12位模式1 a% a* C* ]$ k$ W
  39.     ADC1_Handler.Init.ScanConvMode = ENABLE;                    //非扫描模式6 G% Y8 s  o. D" V3 n
  40.     ADC1_Handler.Init.ContinuousConvMode = ENABLE;              //关闭连续转换
    & C( `% `- [- q! \( R
  41.     ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;           //禁止不连续采样模式
    ( _3 v1 d& W" P; `) W1 E
  42.     ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
    " l# ~( t1 I9 _* P  e2 i+ D
  43.     ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START;     //软件触发7 H' I+ l5 M8 G& A% u3 Q- h
  44.     ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;           //右对齐
      X/ _" }3 p% K% C3 ?# W8 \+ t1 j
  45.     ADC1_Handler.Init.NbrOfConversion = 2;                       //1个转换在规则序列中 也就是只转换规则序列1
    & g' G& U# T8 g- a$ ~
  46.     ADC1_Handler.Init.DMAContinuousRequests = ENABLE;           //关闭DMA请求
    5 o- V) Z0 j( j. K/ d% R$ s0 m
  47.     ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;        
    ) b# g- u/ K; i4 ?, {8 h; h% U$ n! w
  48.     HAL_ADC_Init(&ADC1_Handler);
    ( `6 Q5 z+ |% F
  49. ! Q  X" A( X1 y
  50.     ADC1_ChanConf.Channel = ADC_CHANNEL_5;                                 //通道4 l0 O1 P* c  \
  51.     ADC1_ChanConf.Rank = 1;                                     //序列1
    : ?+ L0 l5 m1 X$ b; h
  52.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间
    0 c$ i4 V0 ?0 F' `
  53.     ADC1_ChanConf.Offset = 0;
    0 l. l1 n$ J+ M  D" f' E4 Z
  54.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置" y' m! P5 [6 C( E! ~
  55. + B+ M& o% L" X+ h
  56.     ADC1_ChanConf.Channel = ADC_CHANNEL_6;                                 //通道5 F2 D5 y4 j$ r' c
  57.     ADC1_ChanConf.Rank = 2;                                     //序列2. [7 o7 R& X" f# K1 R/ u9 t
  58.     ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间; y2 Y! {/ u/ }0 O' N' o2 X& `
  59.     ADC1_ChanConf.Offset = 0;
    ) J1 l( K% t/ W( Y7 d) C, n& t
  60.     HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置8 ~* Z, ~2 C8 z6 e

  61. ; l- X/ C( C( G% M! k
  62.                 HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);# @0 p0 t# Q, y/ p6 Z
  63. }0 C: ]+ c3 _2 q

  64. 4 C+ l+ P( d' Z& ~; ~4 u& n
  65. //ADC底层驱动,引脚配置,时钟使能
    6 m: T( |3 G& X8 e+ l
  66. //此函数会被HAL_ADC_Init()调用
    ; D3 [0 b$ U& s' U: ^
  67. //hadc:ADC句柄
    " Y, |" {& n  W; s' w' Y8 Q, X
  68. void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
    % L" `1 E5 G% t% A
  69. {
    % Q0 R: k- m. ]/ ]$ A0 F8 v
  70.     GPIO_InitTypeDef GPIO_Initure;  K9 p8 v& w& r: H: @
  71.     __HAL_RCC_ADC1_CLK_ENABLE();            //使能ADC1时钟0 Y" O" A5 V3 ]" k2 r& S
  72.     __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟% O6 N; n" W$ n: |- f

  73. $ e8 y# _+ \8 T
  74.     GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6;          //PA5
    ; W$ D7 K. o4 Y9 j' a" ?/ B
  75.     GPIO_Initure.Mode = GPIO_MODE_ANALOG;   //模拟
    4 j5 \" [! s4 `2 O2 G
  76.     GPIO_Initure.Pull = GPIO_NOPULL;        //不带上下拉$ ]* R  R2 u6 Q# f3 }
  77.     HAL_GPIO_Init(GPIOA, &GPIO_Initure);% _/ |' B! ^9 M4 b
  78. }
    ) L% s3 E3 t( L" H" o

  79. 5 s) Z+ z  s) |4 K+ V0 R! R3 @/ ]
  80. void DMA2_Stream0_IRQHandler(void)8 h8 ?3 k+ X; A* V$ c# B  Y0 u
  81. {2 e) y: u, J* N5 d8 T# _; m7 e
  82.     HAL_DMA_IRQHandler(&ADC1DMA_Handler);7 c* h0 |) {0 T1 x, m" `
  83. }  L. Y3 j6 E. U: Q3 U" J2 c7 a

  84. % F: y" Y# M: Z/ E
  85. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc). ^: G7 y9 P9 ~7 Q
  86. {
    ' D$ t- h9 b% {: N* C& C: O% j
  87.         printf("DMA transfer complete\r\n");
      G, Y" A' G! M0 t* t* C
  88. }
    6 G/ H5 K2 |) [$ T8 @, y- }
  89. void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
    & q! r: L' R  W; m7 w" o5 \' P
  90. {
    ) ~" a# l. S. M; w4 k4 W
  91.         printf("DMA Half transfer complete\r\n");
    6 H# Z* t7 Q) Q% w0 U
  92. }( s* B( ?6 t- d) |' h6 M' I3 L+ v
  93. # R7 y$ s  ?5 r. |" }2 H
  94. void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)/ O0 D* f$ }% Z! R
  95. {0 }8 A' H4 G$ ^9 n& R
  96.         printf("DMA transfer error\r\n");) k; c' R3 o- t- l9 W# }
  97. }! ]* L+ w, \1 J& Z# D  H
复制代码
  1. #include "sys.h"
    / V/ Z% ^! @2 Q) d
  2. #include "delay.h"
    , d0 N  F% [8 h0 J1 O$ s  n
  3. #include "usart.h"
    & _: k6 Y: ^+ m. z3 F

  4. , x9 e, H5 o$ L. J( p6 X# X, g
  5. #include "adc.h"
    : q+ l; G$ a3 B6 _0 P% R# D( r& C

  6. 3 q4 y( v( G# A
  7. extern uint16_t buffer[128];& g- U. ?# B& B2 G# G0 c7 }

  8. 5 t% X# p/ T& w: X, G% S- I
  9. void show()" T; a+ A$ l3 |
  10. {7 c; ~" {  X2 _- F+ n  N5 j; O5 z% M4 `
  11.     int i;
    2 n: @. _" O+ A7 R, b) ^
  12.     printf("\r\ndata:");) A1 \$ h+ e) }3 u& i2 R6 w
  13.     for (i = 0; i < 128; i++)
    $ r% d& m/ C0 H7 P( L7 `# p  g
  14.     {) t! P- m) p7 L3 ?/ H3 m+ X' H- U
  15.         if (i % 16 == 0) printf("\r\n");) M% b0 F: d6 Y1 ?
  16.         printf("%6d", buffer[i]);
    2 t7 _. N) k! ~
  17.                         buffer[i]=0;
    $ Q+ s* i. Z2 y- T
  18. 9 e2 u$ `; r( L6 P& g
  19.     }
    ' M3 y* n) g+ c9 F$ {. H; z
  20.     printf("\r\n");1 i2 _4 b0 e) a& ?3 M& e2 E5 a. G
  21. }6 L% m/ F) s7 f/ C( g
  22. * S- [# U0 x5 h6 k& Z' A

  23. 8 B  [. Y8 F3 w
  24. int main(void)
    * z4 R# H; J9 |- M
  25. {, X) P6 u, T9 Z/ g/ ~( p' T
  26.     //Cache_Enable();                 //打开L1-Cache$ n0 m* h; P7 B  F# q
  27.     HAL_Init();                     //初始化HAL库
    # U7 H& o  t9 r& b# i1 c0 \
  28.     Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz5 J& c. f6 N, C( ~7 ]& E! ~
  29.     delay_init(216);                //延时初始化3 Y! X0 N3 `9 y+ H
  30.     uart_init(115200);              //串口初始化
    " f  J/ O3 `2 b6 X7 r: s  J

  31. & o5 K, |  D- j! c; r
  32.     printf("start\r\n");! U) S& [) z9 B" `( U
  33.     MY_ADC_Init();                  //初始化ADC1通道5
    ! j3 x  h  [' ?7 k1 k
  34.    
    ) r7 k8 g* P. p2 e* z5 i* v
  35.     while (1)+ }1 H1 q. c" U0 L0 u; a& y
  36.     {- ~! U2 X8 F& X( B( ^% J7 S8 U0 U
  37.         show();
    & Z. ?% P, A: W4 C& U
  38.         delay_ms(1000);
    ) X3 e& v( K( D; u, \
  39.     }" N2 W: F+ X5 p
  40. }
复制代码
- u/ y# `; I: X- O
————————————————5 J" I$ a1 P1 @3 T$ I+ {
版权声明:小盼你最萌哒/ x2 t0 b5 C. i/ _* P
如有侵权请联系删除
( }% h3 ]4 ~- e" ^4 t9 p/ s6 R$ G- Q, B0 g) }
收藏 评论0 发布时间:2023-3-14 11:26

举报

0个回答

所属标签

相似分享

官网相关资源

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