使用D/A转换器灵活控制电压并使用Arduino输出模拟信号的方法

Arduino具有输出数字信号和PWM信号等功能,但不具备输出模拟信号的功能。这次,我们将为您介绍能够实现这种模拟信号输出的D/A转换器。

 

目录

  1. 什么是D/A转换器?
  2. 如何通过Arduino使用D/A转换器?
  3. 尝试用Arduino控制D/A转换器
  4. 连接设备数字和人类模拟的桥梁——D/A转换器

 

1. 什么是D/A转换器?

 

数字信号是由二进制数字0和1表示的信号。在电子电路领域,有时也用“LOW/HIGH”或“ON/OFF”来表示。而自然界中的现象和人类能够感知的信号都属于模拟信号。模拟信号的特点是其大小、时间和数量是连续的。

D/A转换器是一种能够将数字信号转换为模拟信号并输出的转换器。

 

音频和视频属于模拟信号。然而,近年来,音频处理和图像、视频处理开始需要数字信号了。处理模拟信号时不支持滤波处理和压缩,而数字信号则支持。将处理后的数字信号转换回模拟信号就需要用到D/A转换器。

 

2. 如何通过Arduino使用D/A转换器?

Arduino包括Arduino Uno、Arduino Leonardo和Arduino Nano等多种产品。
Arduino Due是内置D/A转换器的Arduino之一。不过Due的外形与Arduino Uno不同,工作电压为3.3V,使用方法也有所不同。

如果要使用已经很普及的Arduino Uno来输出模拟信号的话,就需要外置D/A转换器IC。

 

3. 尝试用Arduino控制D/A转换器

这次,我们不使用内置D/A转换器的扩展板,而是尝试将D/A转换器IC直接连接到Arduino,看看能否输出模拟信号。我们使用的是ROHM的D/A转换器IC“BH2219FVM”(8bit分辨率,双通道)。

由于BH2219FVM为MSOP8封装,所以我们将使用转换板来使其适配万用板。

 

与I2C和SPI方式的D/A转换器不同,这种DA转换器通过三线制串行通信运行。由于它是自有方式,并不符合特定协议,因此需要自己创建串行通信处理,以便D/A转换器在草图中正常工作。

一边查看BH2219FVM的技术规格书一边与Arduino连接。将示波器探头连接到输出引脚并确认波形

 

BH2219FVM的技术规格书中提供了详细的使用方法。除了最大额定值和电气特性外,技术规格书中还提供怎样施加信号才工作的操作规范,需要一边阅读这些内容一边思考处理方法。

参考:D/A转换器 标准8bit分辨率 双通道/三通道型|ROHM

 

BH2219FVM的控制方法很简单。将输出模拟信号的端口(4bit)和8bit数据(4bit+8bit=12bit)以1bit为单位依次传输至DI引脚。BH2219FVM在CLK引脚的上升沿读取DI引脚。12bit传输完成后,如果LD引脚ON,则输出与数据对应的模拟信号。

下面以向AO1端口输出2.5V电压为例进行说明。

根据上表“通道设置(BH2219FVM)”,前4位表示“AO1端口”的固定值是“0000”。
接下来的数据是表示“2.5V”的8位值,通过
“256 × 输出电压 ÷ 电源电压 = 数据”
进行计算。结果是:
“256 × 2.5[V] ÷ 5[V] = 128”。

由于2.5V是“电源电压的一半”,所以数据是“8位能表示的数字(256)的一半”,也就是“128”。用二进制表示时,128是“10000000”。

通过使DI引脚按照CLK引脚ON的时序依次ON/OFF(1/0),将“0000”和“10000000”发送给BH2219FVM。最后,只要使LD引脚ON、OFF,BH2219FVM即可输出2.5V的模拟信号。

下面的草图是通过串行通信将数据从Arduino发送至BH2219FVM,并将“锯齿波”输出至BH2219FVM的AO1引脚。

#define LD 8
#define CLK 7
#define DI 6

boolean D[12];

//将输入的数值转换为二进制并保存在数组D0~D7中
int binary(int out)
{
  int i = 0;
  for (i = 0; i < 8; i++) {
    D[i] = out % 2;
    out = out / 2;
  }
}

//将输出端口指定结果保存在数组D8~D11中
int chSelect(int outPin)
{
  int i;
  if (outPin == 1) {
    for (i = 8; i < 12; i++)
    {
      D[i] = LOW;
    }
  }
  else if (outPin == 2)
  {
    D[8] = HIGH;
    for (i = 9; i < 12; i++)
    {
      D[i] = LOW;
    }
  }
}

//将数组输出到DAC
void outConfirm()
{
  int i;
  for (i = 11; i >= 0; i--)
  {
    digitalWrite(DI, D[i]);
    digitalWrite(CLK, HIGH);
    digitalWrite(DI, LOW);
    digitalWrite(CLK, LOW);
  }
  digitalWrite(LD, HIGH);
  digitalWrite(LD, LOW);
}

//将输出值和输出端口传递给每个变量
int operationOut(int value, int ch)
{
  binary(value);
  chSelect(ch);
  outConfirm();
}

void setup() {

  //将DA转换器用的引脚初始化
  pinMode(LD, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(DI, OUTPUT);
  digitalWrite(LD, LOW);
}

void loop() {
  int i;

  //输出锯齿波
  for (i = 0; i <= 255; i++) {
    operationOut(i, 1); //固定为AO1输出
  }
}

这个草图更注重易于理解,所以效率并不是非常好,转换周期有点慢。

下面对草图中的每块工作逐一进行解释。

#define LD 8
#define CLK 7
#define DI 6

boolean D[12];

首先,将要连接的D/A转换器的引脚和Arduino的引脚编号建立关联。LD是引脚8,CLK是引脚7,DI是引脚6。接下来,定义了数组D,用来保存要发送到DI引脚的12bit信息。为了保存12bit分别对应的0/1,需要12个数组元素。由于只保存0和1,所以我们使用boolean数据类型。

void setup() {

  //将DA转换器用的引脚初始化
  pinMode(LD, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(DI, OUTPUT);
  digitalWrite(LD, LOW);
}

通过setup函数来设置要使用的引脚的工作。三个引脚都用于输出。在setup函数中将LD引脚指定为LOW,旨在防止其状态不稳定。

void loop() {
  int i;

  //输出锯齿波
  for (i = 0; i <= 255; i++) {
    operationOut(i, 1); //AO1出力
  }
}

通过loop函数,将“要输出的数据”和“端口编号”传递给operationOut函数(该函数用来将信号输出至后述的D/A转换器)。对于“要输出的数据”,使用for语句设置了从0到255逐步增加的值。由于每次调用loop函数时都会重复该工作,因此输出的电压是“锯齿波”。

int operationOut(int value, int ch)
{
  chSelect(ch);
  binary(value);
  outConfirm();
}

通过operationOut函数已将参数传递给执行实际处理的函数。然后调用三个函数:将端口编号(高4位)扩展为0或1的chSelect函数、将要输出的数据(低8位)扩展为0或1的binary函数、以及将数组D的内容传输给D/A转换器的outConfirm函数。

int chSelect(int outPin)
{
  int i;
  if (outPin == 1) {
    for (i = 8; i < 12; i++)
    {
      D[i] = LOW;
    }
  }
  else if (outPin == 2)
  {
    D[8] = HIGH;
    for (i = 9; i < 12; i++)
    {
      D[i] = LOW;
    }
  }
}

通过chSelect函数进行高4位的操作。当从参数中指定端口编号1时,0被保存在数组D的元素8到元素11中。当从参数中指定端口编号2时,1被保存在数组D的元素8中,0被保存在元素9到元素11中。

int binary(int out)
{
  int i = 0;
  for (i = 0; i < 8; i++) {
    D[i] = out % 2;
    out = out / 2;
  }
}

通过binary函数将传来的十进制数转换为8位二进制数,并将其保存在D[0]到D[7]中。在二进制转换中多用该技术。通过for语句重复执行“求除以2后的余数(最低位是0还是1?)、除以2(舍去最低位)”,以“从最低位依次转换为二进制数”。

void outConfirm()
{
  int i;
  for (i = 11; i >= 0; i--)
  {
    digitalWrite(DI, D[i]);
    digitalWrite(CLK, HIGH);
    digitalWrite(DI, LOW);
    digitalWrite(CLK, LOW);
  }
  digitalWrite(LD, HIGH);
  digitalWrite(LD, LOW);
}

通过最后的outConfirm函数,将数组D中的数据传输给D/A转换器并输出模拟信号。
数组D中0/1的排列顺序与传输给D/A转换器时的顺序相反。因此,for语句从11到0按降序循环,并以正确的顺序输出数组D的内容。在循环中,也同时执行CLK处理。for语句结束后,当加上LD引脚的上升沿信号时,将从D/A转换器输出与传输数据对应的模拟信号。

下面是用示波器观测到的每个点的电压图。

用Arduino使BH2219FVM工作时的波形,输出了来自D/A转换器输出引脚的19Hz锯齿波。

 

查看DI引脚(上)和CLK引脚(下)的波形。可以看到DI引脚是如何根据CLK的动作进行ON/OFF的。

 

将波形放大,可以看到信号是如何被发送的。图像显示180的信号被发送到D/A转换器的AO1引脚。根据该信号输出3.52V的电压。

 

control-voltage-with-da-converter-10

 

4. 连接设备数字和人类模拟的桥梁——D/A转换器

随着数字化的进步,D/A转换器被广泛应用于AV设备、通信设备、电机驱动设备等各种产品中。

通过在Arduino中增加使用了D/A转换器的模拟信号处理功能,使其能够在各种场景中大显身手

在本文的案例中,数据传输方式可能看起来有点复杂,但如果使用支持I2C或SPI等通用数据传输标准的D/A转换器,用起来就更轻松、更简单了。在您需要模拟输出的时候,不妨尝试一下D/A转换器!

DevicePlus 编辑团队
DevicePlus 编辑团队

Device Plus适用于所有热爱电子和机电一体化的发烧友,目标是成为深受这些发烧友喜爱的网站。

https://deviceplus.jp

数字晶体管是削减元器件、创建简单电路的第一步

安全使用三端稳压器必备的散热基础知识

相关文章

  1. how-to-make-eobot_02_19

    “魅力四射”的机器人制作【下篇】

  2. “魅力四射”的机器人制作【上篇】

  3. 用Arduino和TOF距离传感器制作甜甜圈播放器【后篇】

  4. 用Arduino和TOF距离传感器制作甜甜圈播放器【前篇】

  5. 1-1

    Arduino电子制作总结!基于微控制器的电子制作基础之基础篇

  6. 用Arduino制作的太阳能电池板供电数字养殖箱【前篇】

    用Arduino制作的太阳能电池板供电数字养殖箱【前篇】

  7. 在最后一刻停下来!用Arduino和距离传感器制作小鸡赛车!(第3篇•最终篇)

  8. 在最后一刻停下来!用Arduino和距离传感器制作小鸡赛车!(第2篇)

Arduino入门指南

基础知识

EMC


TECH INFO

  • Sugiken老师的电机驱动器课堂
  • 重点必看
  • 技术分享
  • Arduino入门指南
  • Raspberry Pi初学者指南
  • 技术动态
PAGE TOP