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

STM32G0-Platformio+libopencm3-多通道ADC与DMA使用

[复制链接]
STMCU小助手 发布时间:2023-2-22 18:36
使用Platformio平台的libopencm3开发框架来开发STM32G0,以下为多通道ADC与DMA的使用。

1 S. c; ?' {( t
1 新建项目
  • 建立adc_dma项目
    / s, y! x% [* L% B
在PIO的Home页面新建项目,项目名称adc_dma,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;
  • 项目建立完成后在src目录下新建main.c主程序文件;
  • 修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:

    5 h+ |! A4 G5 j* t  d2 l8 M* P$ X  M
  1. 1upload_protocol = cmsis-dap& v+ r, K) a0 e, q
  2. 2debug_tool = cmsis-dap
复制代码
4 m' w* Y+ r4 B% E

3 _5 M$ @' |+ N2 编写程序2.1 ADC 设置
这里设置PA0、PA1、PA2、PA3四个引脚为ADC:
  1. 1static void adc_setup(void)5 l2 f- j6 B5 x8 t$ _
  2. 2{
    ; h" c; y: X! g( k7 Z/ F9 d, T; w
  3. 3    rcc_periph_clock_enable(RCC_ADC);
    - q" F: w* [1 O2 J* d" F
  4. 4    rcc_periph_clock_enable(RCC_GPIOA);& s& ]' {+ y1 Z% C5 B; y
  5. 5
    . x) R! D0 s- ^7 L4 z
  6. 6    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO0);  [7 q2 J& o( e& E
  7. 7    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO1);" h( t- A7 n* I" ]+ a
  8. 8    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO2);
    ; G* Z: Q2 W7 |  h9 u/ f
  9. 9    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO3);
    0 b, D. a# X: a5 v6 L. m* z" b/ a
  10. 10$ u, i4 U0 u2 S7 H% s
  11. 11    adc_power_off(ADC1);
    4 Z5 x) E/ H8 Y8 c% o
  12. 12    adc_set_clk_prescale(ADC1, ADC_CCR_PRESC_DIV2);
    * E+ B" ]$ d( k' {! h4 {! `/ z
  13. 13    adc_set_single_conversion_mode(ADC1);( ]9 i& I5 R, ]3 ^; Q
  14. 14    adc_set_right_aligned(ADC1);3 {7 I$ L  c  p$ `
  15. 15    adc_set_sample_time_on_all_channels(ADC1, ADC_SMPTIME_160DOT5);. t5 S4 n9 m) X' s) U6 }0 w
  16. 16: y, l, e9 d9 A0 n/ w+ n0 D% r
  17. 17    uint8_t channel_array[16] = {0};6 }6 T+ L1 v8 g2 U, f+ S7 Y; c& y5 [
  18. 18    channel_array[0] = 0;
    ' ]4 M5 t! L# v/ ]& V7 k  [
  19. 19    channel_array[1] = 1;
    * E6 r6 i4 s, f" d
  20. 20    channel_array[2] = 2;
      ~# \9 u) A" t2 O6 P
  21. 21    channel_array[3] = 3;) `3 M" {% T2 P
  22. 22    adc_set_regular_sequence(ADC1, ADC_CHAN_CNT, channel_array);/ ?" x$ J2 ]8 b- d0 c; P* W/ K3 C/ C* x
  23. 23    adc_enable_dma_circular_mode(ADC1);1 Y. p6 D( |  M* a/ }
  24. 24    adc_set_resolution(ADC1, ADC_CFGR1_RES_12_BIT);" Y+ g3 h1 o9 r' M  q/ U; i
  25. 25    adc_power_on(ADC1);# \* s" ]1 G8 c2 m4 d. y
  26. 26/ o6 c# h8 ?2 f/ H% A8 R
  27. 27    /* Wait for ADC starting up. */
    " u8 b( V& B$ v% D! g
  28. 28    delay_ms(10);   w' P% L; Z: ~' @* T# w( @# o! H
  29. 29}
复制代码

' v5 M+ e8 q9 J  i. {2.2 DMA配置
  1. 1static void dma_setup(void *data, int size)& U: }: p, r1 `9 _, K. E9 V
  2. 2{
    & H1 D* b$ U6 T% D  ?0 g& w# G
  3. 3    dma_channel_reset(DMA1, DMA_CHANNEL1);( q8 Z1 U) W7 c) V1 R, I+ Z! S
  4. 4    dma_set_peripheral_address(DMA1, DMA_CHANNEL1, (uint32_t)&ADC_DR(ADC1));
    ( F0 h. [1 l7 q
  5. 5    dma_set_memory_address(DMA1, DMA_CHANNEL1, (uint32_t)data);' j/ |" U1 l' N3 _2 S: t1 i
  6. 6    dma_set_number_of_data(DMA1, DMA_CHANNEL1, size);
    7 a7 b$ [* }, E5 D2 A
  7. 7    dma_set_read_from_peripheral(DMA1, DMA_CHANNEL1);4 B3 d& B: O- I7 I6 \# Y
  8. 8    dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL1);
    8 a1 |- y: g/ U6 J' A
  9. 9    dma_set_peripheral_size(DMA1, DMA_CHANNEL1, DMA_CCR_PSIZE_16BIT);
    ) w: a/ Q/ o. d# Q+ U
  10. 10    dma_set_memory_size(DMA1, DMA_CHANNEL1, DMA_CCR_MSIZE_16BIT);
    , S% l4 l# y- c- e1 G8 f
  11. 11    dma_enable_circular_mode(DMA1, DMA_CHANNEL1);; ?7 Q$ M) B# f! c
  12. 12    dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL1);
    # q! c% R6 r! l9 `7 \) k2 l
  13. 13    dma_enable_channel(DMA1, DMA_CHANNEL1);
    # m2 W) U) ?; U' S
  14. 14
    : h6 [7 b& m0 D" d/ X/ ]5 U2 l- H; X
  15. 15    dmamux_reset_dma_channel(DMAMUX1, DMA_CHANNEL1);
    ( d- g1 S! Q0 n* `# \" ?
  16. 16    dmamux_set_dma_channel_request(DMAMUX1, DMA_CHANNEL1, DMAMUX_CxCR_DMAREQ_ID_ADC);+ T5 K$ |1 |3 X/ I% e* u( z* Q
  17. 17}
复制代码
/ I% q6 G& i: g7 V; R
! l! H. C- m( V9 d5 x7 s! m3 I2 ?$ o
主要是设置DMA的外设地址为ADC数据寄存器 ADC_DR;并设置内存地址为定义的buff,size为需要缓存的数据大小:
  1. 1#define ADC_CHAN_CNT        4
    ! G1 [' x0 J5 Z1 S. ^
  2. 2#define ADC_FILETER_SIZE    32# A( n! T( E1 ~
  3. 3# V: e+ V( G3 {6 a
  4. 4int16_t adc_values[ADC_FILETER_SIZE*ADC_CHAN_CNT];
复制代码

! F, w7 k7 ]7 J; q2.3 ADC配置为DMA读取和Timer触发
  • 定时器设置

    ) H4 s  B9 @( S# A9 `3 D
  1. 1void tim3_setup(void)3 E  W" E0 g6 \. B4 }
  2. 2{
    & t6 l% t# X7 f5 r
  3. 3    /* Enable TIM3 clock. */
    2 x. r- P2 K( G: d% f: G1 i
  4. 4    rcc_periph_clock_enable(RCC_TIM3);8 s" M/ r4 W5 Z6 u. M* {$ p
  5. 5, j! c, Z7 c5 Z. ]+ P! s
  6. 6    /* Enable TIM3 interrupt. */; O+ c; c4 _" J0 {( w) N
  7. 7    nvic_enable_irq(NVIC_TIM3_IRQ);
    ' p( _+ ?9 M# R2 W. S
  8. 8
    1 H4 W0 V: h9 k4 b9 s% o
  9. 9    /* Reset TIM3 peripheral to defaults. */$ E: |; T2 L3 U( ]
  10. 10    rcc_periph_reset_pulse(RST_TIM3);
    1 \1 e9 t# s0 i1 t
  11. 11( h( @4 d. r/ o/ k& L
  12. 12    /* Timer global mode:
    7 m9 h. O7 ]8 a
  13. 13     * - No divider$ E  x2 l/ F: J" I3 T3 }0 S+ _
  14. 14     * - Alignment edge
    7 ~$ ^8 K* }6 y* d% S7 I5 i2 h7 Y
  15. 15     * - Direction up
    5 R0 U' ?- c( A% a( p/ t  S
  16. 16     * (These are actually default values after reset above, so this call( q) L' \1 N, B: E
  17. 17     * is strictly unnecessary, but demos the api for alternative settings)1 o* x$ v  ?/ m( d7 s3 }
  18. 18     */
    3 C: I  c/ V; r( u" L+ ?  F
  19. 19    timer_set_mode(TIM3, TIM_CR1_CKD_CK_INT," O+ j( K6 `0 B
  20. 20                   TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
    9 Z  _8 ~% O. d0 a+ L
  21. 21
    6 [: h+ L" R/ S; H4 X& o' D  J
  22. 22    /*
    9 x8 A( ~2 I$ ^
  23. 23     * Please take note that the clock source for STM32 timers
    * j2 i; |  g! U' A
  24. 24     * might not be the raw APB1/APB2 clocks.  In various conditions they7 W5 K7 {1 i' x
  25. 25     * are doubled.  See the Reference Manual for full details!7 M1 n6 m4 B* S- U5 z4 [1 t( ]
  26. 26     * In our case, TIM3 on APB1 is running at double frequency, so this+ n1 Z. c: C( ?
  27. 27     * sets the prescaler to have the timer run at 5kHz, _9 ~+ f3 |# b3 p3 {1 t
  28. 28     */0 w% {% t) f$ Y8 E4 y: i( u
  29. 29    timer_set_prescaler(TIM3, 64-1);
    2 q! G, W' V3 F  P' E9 N
  30. 30
    ! B, n: K/ Y* }
  31. 31    /* Disable preload. */2 H* Y4 o$ E0 H8 c: B% }" G  s! m$ W
  32. 32    timer_disable_preload(TIM3);
    , C( C2 m, n* R! N( ^, L/ U& Q
  33. 33    timer_continuous_mode(TIM3);
    8 D5 A. q; t2 W& M) u/ t" B( B/ x
  34. 340 q+ T- P) v  [+ V+ R4 Y$ }+ W" Q
  35. 35    timer_set_period(TIM3, 20000-1); //100Hz
    1 z0 g  w7 z1 t  u6 K# `
  36. 360 O9 _; v1 X; {3 `. ~" k
  37. 37    timer_set_master_mode(TIM3, TIM_CR2_MMS_UPDATE);6 b. _! M& y& I. z
  38. 38! p) k9 y* W! d5 Y( v
  39. 39    timer_enable_irq(TIM3, TIM_DIER_UIE);
      O4 j% M1 ~$ K$ b+ c
  40. 40}
    5 J  Y- O" M! q1 h4 f8 f
  41. 41- S" n- w0 v$ `6 @  o/ y( g
  42. 42void tim3_enable_counter(bool en)
    4 W& i1 v5 a+ J( V/ p2 a
  43. 43{
    - ^( T; j- M5 v, k7 W  l: A
  44. 44    if(en){
    $ _" u- M$ ]1 W/ R! }& m: C; q8 K! m
  45. 45        timer_enable_counter(TIM3);
    * O* F4 I/ j  q: V) j  d
  46. 46    }else{1 ^: F/ U* l+ G. ~5 q" @- e
  47. 47        timer_disable_counter(TIM3);
    # T, i( v) H, T$ v' s, k4 I) V. ~
  48. 48    }
    * b- r- `3 N# l
  49. 49}
    2 Z' q6 V6 U, s4 x
  50. 50" V+ L% x$ A0 z. U6 ?7 D
  51. 51void tim3_isr(void); u7 B9 }2 L* d8 }( J" u1 m
  52. 52{
    - `. _& W4 C& x: ^; \( J/ |: s% S
  53. 53    if (timer_get_flag(TIM3, TIM_SR_UIF)) {
    / A4 c- q/ Y/ N8 I+ K5 m' D  Z
  54. 54
    , E. c. }# {6 `$ S' x) s& Y3 P
  55. 55        /* Clear compare interrupt flag. */
    $ D4 H9 C3 S: }( c
  56. 56        timer_clear_flag(TIM3, TIM_SR_UIF);3 a& P$ W" V9 q& X% J9 p$ @3 ^7 G
  57. 57    }
    4 B$ j- E2 s- ^6 P* ?, d! O
  58. 58}
复制代码
7 Z! r3 j2 G' p1 J% P6 m4 h7 f4 b

' t: J; m( L1 C  @
  • DMA设置和Timer触发
    7 c' s: C" n3 c' P8 N
  1. 1    rcc_periph_clock_enable(RCC_DMA);
    ) f- p: d1 X, S/ ?8 \% |
  2. 2    nvic_set_priority(NVIC_DMA1_CHANNEL1_IRQ, 3);
    $ Z; W3 j. K  ~, b) f9 L! q/ x
  3. 3    nvic_enable_irq(NVIC_DMA1_CHANNEL1_IRQ);/ g0 X/ c0 a, Q( M& n- k" W
  4. 4
    ( s: J# B, R4 j5 k; S3 g
  5. 5    adc_setup();' `9 o$ G& J1 U' p
  6. 6" S& V+ ~9 V4 l+ Z0 t/ ?9 s
  7. 7    dma_setup(adc_values, ADC_CHAN_CNT*ADC_FILETER_SIZE);$ o% L& S1 Z- B& N1 t0 z5 r
  8. 8
    9 k  g' m  G; y+ D" M
  9. 9    adc_enable_overrun_interrupt(ADC1);3 v% V' Q" g' I1 m: p$ h8 B5 w. K# l
  10. 105 w; M5 z* M) Y# S+ I
  11. 11    adc_enable_dma(ADC1);
    , E8 C' }. r/ Q; ^, o0 X9 w9 ^
  12. 12# F$ Y' C" g+ E" d) ~8 o# ?
  13. 13    ADC_CFGR1(ADC1) = (ADC_CFGR1(ADC1) & ~(0x3<<10)) | (0x1<<10);    // Hardware trigger detection on the rising edge; P+ t  ~) E* q2 T& ?( G" ~4 L
  14. 14    ADC_CFGR1(ADC1) = (ADC_CFGR1(ADC1) & ~ADC_CFGR1_EXTSEL) | (3<<ADC_CFGR1_EXTSEL_SHIFT);    // toggle by tim3
    , a/ n! l; s/ z0 d1 W/ a1 v# w" O
  15. 15
    $ ?* N3 A$ l3 u2 ^( j
  16. 16    tim3_setup();5 c  D) K4 I8 |9 a" p# M9 |2 Z
  17. 179 e$ O; ]. Y  P  z+ g9 @- O4 g
  18. 18    adc_start_conversion_regular(ADC1);
    # F6 R! G: i% w9 G
  19. 19
    % K5 b9 w7 Y5 _2 z
  20. 20; j* J# X" s' a" ]1 r
  21. 21    tim3_enable_counter(true);
    ( w- f9 K, k- p
  22. 22
    $ [% p! u4 k$ z' u1 \
  23. 23    delay_ms(100);
复制代码

- p$ L3 d5 d+ U: @" L1 t4 ~8 v4 k6 T
DMA中断时候即准备好读取ADC数据,因此在DMA中断中先把定时器关闭,读取数据后再次打开:
  1. 1void dma1_channel1_isr(void)
    * M% o' T' B6 i5 R8 i& w9 @
  2. 2{1 l! l% k# c$ g+ v# ^2 _$ U! ]6 p
  3. 35 @( F# F; S& ^& f+ q+ Y
  4. 4    if ((DMA1_ISR &DMA_ISR_TCIF1) != 0) {% P2 C# ~. a; K9 G) \% ^# O
  5. 5        DMA1_IFCR |= DMA_IFCR_CTCIF1;* D0 Y4 h, z  _( H% m! ?
  6. 6    }
    3 j3 f2 v! ~% z" w$ K
  7. 7/ k/ Q' R1 R" q9 T0 s8 a$ _/ D
  8. 8    tim3_enable_counter(false);  F% o! b* n( q6 n
  9. 90 E3 I( l' L' Q, J* Q: [
  10. 10}
复制代码

6 c0 X: o) f, k: Q6 x2.4 ADC读取
  D/ y' p4 [! {; \  J1 A
  1. 1void adc_sample(void)/ W( V9 {( F+ K1 I$ V, b5 P
  2. 2{
    $ c7 h/ g; p$ X
  3. 3    uint32_t sum_val1 = 0;+ I; x. ?* ^2 }2 l, e
  4. 4    uint32_t sum_val2 = 0;/ W3 {- r1 Q5 e0 y/ J8 Q2 P
  5. 5    uint32_t sum_val3 = 0;
    7 t& y* d+ p0 d$ ]. l
  6. 6    uint32_t sum_val4 = 0;  p  ~& p, _. |. z" K6 _  Z
  7. 7) b) E& t* {0 Y- T  [0 U+ ?
  8. 8    for(int i=0; i<ADC_FILETER_SIZE; i++){
    ) {5 ?4 E2 M* h) Z
  9. 9        sum_val1 += adc_values[ADC_CHAN_CNT*i + 0];
    , j% h: b/ Z( W8 G" Y
  10. 10        sum_val2 += adc_values[ADC_CHAN_CNT*i + 1];/ A% M8 h# a0 Q$ I( o* e
  11. 11        sum_val3 += adc_values[ADC_CHAN_CNT*i + 2];
    - N% p2 C1 s7 O9 H9 n! z' v9 Y
  12. 12        sum_val4 += adc_values[ADC_CHAN_CNT*i + 3];
    ( r. Q; S, T6 e
  13. 13    }
    ; i: F1 D+ `0 o5 J& r
  14. 14
    - X$ b4 A& u$ ]% n" v. F
  15. 15    uint32_t filter_val1 = sum_val1/ADC_FILETER_SIZE;5 I7 K& N" v4 v+ `6 A0 C
  16. 16    uint32_t filter_val2 = sum_val2/ADC_FILETER_SIZE;# ~: [+ I* v8 H1 V2 X
  17. 17    uint32_t filter_val3 = sum_val3/ADC_FILETER_SIZE;
    ) u& n' I6 w9 `  D0 B
  18. 18    uint32_t filter_val4 = sum_val4/ADC_FILETER_SIZE;/ s2 v, o8 R; e7 {# a) N# a
  19. 19
    8 _9 E* E$ V$ Z& [# p& G% K
  20. 20    printf("adc:%d  %d  %d  %d\r\n", filter_val1, filter_val2, filter_val3, filter_val4);$ A. T2 W9 X# Q+ R7 G
  21. 210 V- S( I$ z5 V) @: o
  22. 22    tim3_enable_counter(true);; Y! s! s$ X: N$ ]& i' E: C; T' ~
  23. 23}
复制代码

# F8 e+ F0 G% a- o" Z4 h. S! B5 |) S
读取时候按照通道的顺序从buff中取出,这里做了简单的过滤;
) X! {+ b% _) N; T* D( V
3 烧写测试
将程序烧写到开发板,然后打开串口可以看到四个ADC通道的数据,在PA0/PA1/PA3/PA4四个引脚连接不同电压可以看到变化:
微信图片_20230222183559.png

9 g. z+ B% ~( {' \" W7 Z
转载自:MakerInChina.cn
; ~' a& ]$ [$ A; b2 H3 J
& R: j+ r6 C& h' H
赞 收藏 评论0 发布时间:2023-2-22 18:36

举报

0个回答

所属标签

相似技术帖

官网相关资源

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