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

NUCLEO-G071RB测评报告—5.ADC和DAC的测评

[复制链接]
子曰好人 发布时间:2019-1-19 12:21
本帖最后由 子曰好人 于 2019-1-19 12:25 编辑 ( V* T# |* m# E; q( ~2 t$ l8 \
# }# r' Y$ C$ Z' s/ Q# E! X
本来是年末了,应该比较悠闲才对,可是最近却有点忙,芯片的测评报告也没及时的更新,部分原因也是在想怎么写好测评报告。

/ M! |5 p% p( J( A0 }9 ?  i8 g5 c" [
今天就来测评一下做板子模拟功能相关的部分—ADC和DAC。

' m  `; i* g5 P5 c1 J  _
首先来罗列一下G0芯片ADC的特性:
1.    采样率:2.5MSample/s max (12bitresolution),在12位采样精度的情况下400ns就能完成一次采样,相比于其他M0+内核的芯片算是快的了。
2.    支持硬件过采样功能,过采样的提出是为了提高ADC的采样精度,而损失部分ADC采样速率,在某些需要高精度ADC的场合非常有优势。
3.    在采样率为1Msample/s时,功耗仅为118uA
4.    非常灵活的触发源,软件触发,timer触发以及IO触发
5.    每个ADC均拥有三个模拟看门狗。
6.    自校正功能。
6 G! y6 ~- }& f3 M  |/ T3 @
DAC特性:
1.    支持8位和12位模式
2.    一个DAC模块,嵌入了两个DAC转换器
3.    包含低功耗采样保持功能
4.    可控制外部偏移电路,代替电位器。
5.    可作为随机信号发生器。
! D3 g& j- L1 i0 G- R
本次测评目标是通过DAC生成尽可能高频率的正弦波并由ADC采集出来。

7 S$ y( b+ j3 G
可以通过cubemx生成默认NUCLEO-G071RB的工程,长这个样子。
cubemx生成NUCLEO默认工程.jpg
默认生成的工程配置了唤醒,低速晶振,低功耗串口以及调试接口,我们可以在这个基础上添加内容。不过串口需要修改一下,我们常用的串口波特率为115200或者9600,工程默认配置是209700,而我们常用的串口工具不支持这个波特率。
低功耗串口工作模式修改.jpg
ADC配置:
ADC配置.jpg
& [! P/ i! ~1 k, T3 y8 n) Q
ADCDMA配置.jpg
! F+ _+ H2 \0 i0 r, r: f5 ^
这里选择的是ADC通道0,采样精度为12位,数据右对齐方式,仅开启了DAM连续请求,本次测评准备用DMA触发。设置ADC触发源为timer1,我的打算是后面跑跑电机,所以让adc和高级定时器关联起来。: r! j- p0 I- Q) n# K

0 s6 d9 q& R; T, Z5 \
DAC配置
DAC配置.jpg , C6 _1 M. w- U/ r& l
DACDMA配置.jpg
DAC的配置就比较简单了,选择仅连接到外部引脚,输出buffer使能,通过timer6的更新事件触发,其他默认即可。现在有各种各样数据可视化工具出现在工程师的眼前,但是在没有这些工具之前想要看到mcu内部变量的变化趋势可是要费一定精力的。串口虽简单易用,但是占用mcu资源;据我了解的不占mcu资源的JScope(SEGGER家的),简单的应用倒是可以还可以使用,对数据观测量较大或者数据改变速率较快时使用则需要更强大的调试器(Jlink Pro)加持,对于大多数工程师来说购买正版Jlink还是有点贵贵的。这个时候mcu自带DAC就大大帮了忙,通过示波器的电压探头就能以较高帧率去观察mcu内部变量的变化趋势。
$ d* \. l( E1 n1 ZTimer6配置
Tiemr6配置.jpg $ G) ]9 \( ~% z9 ?7 I; E
Timer6是普通定时器,只能向上计数,可以产生更新事件,本次测评用timer6触发DAC的DMA转换。

. Q- p" k" W! Z  T
Timer1配置
Tiemr1配置.png
Tiemr1CH4配置.png
在ST的mcu家族中的定时器里,Timer1永远是最高配置,拥有更多强大的功能,特别适用于电机控制,另外就是G0系列的Timer1可以倍频,达到MCU最大主频的2倍,即以128MHz的频率运行。
本次测评暂时只使用定时器1作为ADC的触发源。  T1 v- {, q; m" K1 n4 Q6 A
代码:8 h% V5 K& Y$ Z* U: M( ~  }
  1. uint32_t adc_value = 0;8 W6 y$ z* c& |$ y; E2 o* Q, v
  2. float ui_f32adc = 0.0f;& s) F* \5 l' Z
  3. const uint16_t g_SineWave128[] = {
    " m, U( D! D7 s! @8 l
  4.     2047, 2147, 2248, 2347, 2446, 2544, 2641, 2737, 2830, 2922, 3012, 3099, 3184, 3266, 3346, 3422,
    / l& F6 D: F2 S# V, O& ]
  5.     3494, 3564, 3629, 3691, 3749, 3803, 3852, 3897, 3938, 3974, 4006, 4033, 4055, 4072, 4084, 4092,
    . i6 w- x, k; z* s$ B
  6.     4094, 4092, 4084, 4072, 4055, 4033, 4006, 3974, 3938, 3897, 3852, 3803, 3749, 3691, 3629, 3564,. R0 i! B9 J' F* b
  7.     3494, 3422, 3346, 3266, 3184, 3099, 3012, 2922, 2830, 2737, 2641, 2544, 2446, 2347, 2248, 2147,% j- D( Z8 ]7 t* ~
  8.     2047, 1947, 1846, 1747, 1648, 1550, 1453, 1357, 1264, 1172, 1082, 995, 910, 828, 748, 672,+ \1 O, h: \6 C) J6 @7 D4 O
  9.     600, 530, 465, 403, 345, 291, 242, 197, 156, 120, 88, 61, 39, 22, 10, 2,7 P' p/ c* ?8 V/ l4 r
  10.     0, 2, 10, 22, 39, 61, 88, 120, 156, 197, 242, 291, 345, 403, 465, 530,
    . \. v9 N/ C6 }
  11.     600, 672, 748, 828, 910, 995, 1082, 1172, 1264, 1357, 1453, 1550, 1648, 1747, 1846, 1947,
    : W# K3 c2 f4 Q/ P2 V& z
  12. };
    $ y9 {% e8 v8 C, e$ j4 c2 V% }
复制代码
主函数代码
  1. int main(void)
    # B2 G  W0 F( Z. z% i
  2. {7 s/ A$ O. H# V
  3.     /* USER CODE BEGIN 1 */; [7 z& f$ t/ L: r( n( F8 \
  4. ! R# g3 d8 l: v& U  `8 ^/ {' v
  5.     /* USER CODE END 1 */
    1 ~0 L% `. v: x# {2 G

  6. * t0 c. T  Z4 y
  7.     /* MCU Configuration--------------------------------------------------------*/! Q$ z/ @7 S; T) E8 E
  8. * L! |( @) w( |  }# Y
  9.     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    ! _3 a; S2 ]9 E
  10.     HAL_Init();
    9 i0 C$ i% L& @  p( C8 m
  11. . y& w  S2 t  C- O9 H
  12.     /* USER CODE BEGIN Init */2 Y* O( X, D& `% C

  13. 9 _$ x% @5 ~) ]& s4 A/ I; N: Y! x
  14.     /* USER CODE END Init */
    . z! O' x6 a. W/ y2 |3 D1 q  n

  15. 9 D( C# [: F2 W$ q# W5 h( O2 {- ^
  16.     /* Configure the system clock */$ f8 u- z: O/ S- j
  17.     SystemClock_Config();
    5 k) z' E4 O7 r& O0 \

  18. - ^. B! y" ^3 H+ T( O- V7 C5 i
  19.     /* USER CODE BEGIN SysInit */4 n* h1 R' p) U: \  o
  20. : ~; p& w5 ^' Z
  21.     /* USER CODE END SysInit */% ~" ~. P3 c% A7 C0 G8 h& h
  22. 0 K7 d2 B+ U2 ?5 ]8 @2 r
  23.     /* Initialize all configured peripherals */
    % O& g9 o7 M! ^1 ^7 O& H3 O- p# v
  24.     MX_GPIO_Init();
    + ?1 h9 @7 r3 l' ]. j! F
  25.     MX_DMA_Init();1 j4 R1 m) e- G; P/ K
  26.     MX_TIM1_Init();
    # e; n2 U# N7 a0 D: Y0 C9 X
  27.     MX_TIM6_Init();+ f( }4 ]9 X, F3 t' A
  28.     MX_ADC1_Init();
    / f, R2 `- e. w" n" e
  29.     MX_DAC1_Init();3 c5 W" Y& H' ^: a; c
  30.     MX_LPUART1_UART_Init();
    * k: ~- W0 ?. y. s' E6 V
  31.     /* USER CODE BEGIN 2 */* x$ ^  |4 D. \: z1 m# f
  32.     HAL_ADCEx_Calibration_Start(&hadc1);
    ( V& f3 }+ }4 ]5 T& S1 l
  33.     HAL_ADC_Start_DMA(&hadc1,&adc_value,1);
    2 W8 K& X( J- s3 M4 i
  34.     HAL_DAC_Start_DMA(&hdac1,DAC1_CHANNEL_1,(uint32_t *)g_SineWave128,128,DAC_ALIGN_12B_R);7 q: n) r: d9 ^9 A+ U* u" g
  35.     HAL_TIM_Base_Start(&htim1);) R5 E. O# T1 N2 D/ j% a
  36.     HAL_TIM_Base_Start(&htim6);
    0 |+ F- H, \" {2 q
  37.     /* USER CODE END 2 */
    : ?9 c; @( z* B2 D: y! l( R! k! `6 i
  38. : a9 t% V/ F8 q! R  P, X
  39.     /* Infinite loop */, D' y# o/ ~3 i' K8 `
  40.     /* USER CODE BEGIN WHILE */& ~; m) V  l; U: C! ^0 G
  41.     while (1)
    & e% `5 R# V3 h. B# j& W$ c
  42.     {
    " A4 `8 g# L: o& {% j. O
  43.         ui_f32adc = (float)((adc_value&0xfff)*3.3/4096);
    : g8 d7 {9 t( U7 ^
  44.         printf("adcvalue: %.2f\r\n",ui_f32adc);% P2 b! t$ ]' Q* {0 S
  45.         /* USER CODE END WHILE */
    # z0 }! S* s' h2 d% e$ A, ]

  46. - I4 W0 S0 _& z& |5 C1 g
  47.         /* USER CODE BEGIN 3 */; C# t0 W8 W+ k5 N: s9 c/ W
  48.     }% c$ V9 ~, l) B% d: _
  49.     /* USER CODE END 3 */' @, |! q- t5 B9 E7 B
  50. }
复制代码
串口重定向:
  1. #include <stdio.h>' \( q4 J" |( X/ _
  2. int fputc(int ch, FILE *f)
    - D8 L# p7 Q* [
  3. {
    * W% |/ J3 ~6 v- w2 j0 K' w4 p
  4.     HAL_UART_Transmit(&hlpuart1, (uint8_t *)&ch, 1, 0xFFFF);) b6 \# Y% C6 M7 Y6 K' Q/ c
  5.     return ch;9 X- R! V, _1 X+ u) l# e
  6. }
    % |2 w$ f# U5 _+ \7 ~
复制代码
现在可以通过示波器和Jscope分别看到DAC和ADC的状态了,通过串口也能看到的部分信息。
串口接收到的DAC数据.jpg
' E8 o# {; b3 \! e" M8 D
DAC以较低频率触发(10Hz),timer6配置为不分频,计数值为49999。可以看到在低频时可以看到DAC输出的0V并不是0V,有40mV的偏移,满幅电压是正常的3.3V。
, z- A8 t. s$ @7 j2 Q
10Hz.jpg
DAC低频传输数据.jpg
4 F  x5 s5 o$ e
500Hz正弦波,Tiemr6配置为不分频,计数值为999
500Hz.jpg
DAC500Hz正弦波.jpg
500Hz的正弦波已经让普通Jlink+JScope无能为力了,再后面的JScope波形也就不贴出来了,其实通过香农定理也能预测到这个结果,普通Jlink+JScope最高采样率才1KHz,而香农定理提出要能还原信号原来的状态,采样频率至少要是信号频率的两倍,这里恰好两倍,不能还原DAC输出的正弦波也算是正常。
5KHz.jpg
10KHz.jpg
20KHz.jpg
30KHz.jpg
39KHz.jpg
, v- b  D8 _& D- ^9 k, L. H0 k
DAC输出的最大正弦波频率为39.48KHz,这里可以计算的到DAC更新频率为39.48KHz*128=5.053MHz,有DMA的加持就算是很高频的数据也能采集到了。
5 {" B& O& B- b4 ~% G
' N- A7 Y8 x" e) ^& D/ C" @, ]

1 U5 L  N) A" i$ K[color=inherit !important]" [) g0 N1 Z5 h! r; q

8 {( z' x6 Z/ _( O6 i) a  J- J5 V% D; C# O4 {% W2 V1 c

  p5 ?* q& [- B' H2 C+ y) o7 U
赞 1 收藏 1 评论4 发布时间:2019-1-19 12:21

举报

4个回答
STMCU-管管 回答时间:2019-1-23 16:49:20
多谢楼主分享!
jimmlinster1234 回答时间:2020-11-17 14:39:10
多谢楼主分享
wyxy163@126.com 回答时间:2020-11-18 08:50:59
提示: 作者被禁止或删除 内容自动屏蔽
子曰好人 回答时间:2020-11-18 09:20:47
数码小叶 发表于 2020-11-18 08:506 U7 A8 q+ l) Y" ~
最大正弦波频率为39.48KHz,这个应该加上限制条件,不然远不止这个频率。顺带吐槽下ST的设计,自带的波形生 ...
1 Z$ z  m" q* O( i9 k/ x6 n5 T* }
限制条件就是dac电压幅值不降低的情况下输出的最大频率,这个地方确实描述有点问题,感谢指正
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版