以 CH549 为例的 51 教程 | 图文 7. 模拟信号与数字信号的互相转换

MCU Xy ⋅ 于 2021-08-19 15:41:03 ⋅ 279 阅读

:card_index: 点此回到教程主索引

目录

AD DA 介绍

ADC 实验与具体代码解析

DAC 实验与具体代码解析

AD DA 介绍

AD DA 其实是分为 ADC (Analog-to-Digital Converter) 与 DAC (Digital-to-Analog Converter)

A 即 Analog 模拟的

D 即 Digital 数字的

即,ADC 是模数转换器,DAC 是数模转换器。

以MCU的角度看ADC是用于读取电压值,而DAC是用于产生模拟电压的。

ADC 实验与具体代码解析

根据下图连接电路:
1_ad连接
如此连接的目的是让p0.7口的模拟电压可以通过电位器进行调节。

具体代码 main.c

/**
  ******************************************************************
  * @file    main.c
  * @author  甜鱼酱,xy
  * @version V1.0
  * @date    2021-5-19
  * @brief   SPI
  ******************************************************************
  * @attention
  * verimake 用于
  * 在 oled 屏幕上显示 p0.7 的电压值
  ******************************************************************
  */
#include <CH549_sdcc.h>  //ch549的头文件,其中定义了单片机的一些特殊功能寄存器
#include <CH549_OLED.h>  //其中有驱动屏幕使用的函数
#include <CH549_BMP.h>   //用于显示图片的头文件
#include <CH549_DEBUG.h> //CH549官方提供库的头文件,定义了一些关于主频,延时,串口设置,看门口,赋值设置等基础函数
#include <CH549_SPI.h>   //CH549官方提供库的头文件,定义了一些关于SPI初始化,传输数据等函数
#include <CH549_ADC.h>   //CH549官方提供库的头文件,定义了一些关于ADC初始化,采集数据等函数
/********************************************************************
* TIPS:
* adc各通道对应引脚关系
* P1.1       AIN1
* P1.2       AIN2
* P1.3       AIN3
* P1.4       AIN4
* P1.5       AIN5
* P1.6       AIN6
* P1.7       AIN7
* P0.0       AIN8
* P0.1       AIN9
* P0.2       AIN10  
* P0.3       AIN11 
* P0.4       AIN12 
* P0.5       AIN13 
* P0.6       AIN14 
* P0.7       AIN15 
*********************************************************************/
int oled_colum;
int oled_row;
/********************************************************************
* 函 数 名       : setCursor
* 函数功能         : 设置printf到屏幕上的字符串起始位置
* 输    入       : 行坐标 列坐标
* 输    出         : 无
********************************************************************/
void setCursor(int column,int row)
{
oled_colum = column;
oled_row = row;
}
/********************************************************************
* 函 数 名       : main
* 函数功能       : 主函数
* 输    入       : 无
* 输    出         : 无
*********************************************************************/
void main(void)
{
  UINT8 ch;
  float Voltage;
  CfgFsys(); //CH549时钟选择配置
  mDelaymS(20);
  /* OLED的初始化程序 */
  SPIMasterModeSet(3); //SPI主机模式设置,模式3
  SPI_CK_SET(12);      //12分频
/* 初始化OLED */
  OLED_Init();
  OLED_Clear();
  /* ADC的初始化配置 */
  ADC_ExInit(3); //ADC初始化,选择采样时钟
  /* 主循环 */
  while (1)
  {
    ch = 15;
    ADC_ChSelect(ch);  //选择通道
    ADC_StartSample(); //启动采样
    while ((ADC_CTRL & bADC_IF) == 0)
    {
      ; //查询等待标志置位
    }
    ADC_CTRL = bADC_IF; //清标志
    Voltage = (ADC_DAT / 4095.0)*5.0 ;
    /*
    adc采集到的值为ADC_DAT,由于ch549是12位ADC模数转换器因此ADC_DAT为0-4095之间的数,它表达的含义是电压值的相对高低
    因此 实际电压值的算法为ADC_DAT除以4095乘以ch549所使用的电压5v
    */
    setFontSize(16); //设置显示在oled上屏幕的字号
    setCursor(0,0);//设置printf到屏幕上的字符串起始位置
      printf_fast_f("AD CJ %fV",Voltage);//显示数据到屏幕上 CJ含义为caiji(采集)
    mDelaymS(300);
  }
}
/********************************************************************
* 函 数 名       : putchar
* 函数功能       : 将printf映射到oled屏幕输出上
* 输    入      : 字符串
* 输    出        : 字符串
********************************************************************/
int putchar( int a)
{      
  OLED_ShowChar(oled_colum,oled_row,a);
  oled_colum+=8;
  if (oled_colum>120){oled_colum=0;oled_row+=2;}
  return(a);
}

使用ADC进行采集模拟电压的步骤为:

1.使用ADC_ExInit()函数进行ADC初始化。

2.使用ADC_ChSelect()函数选择ad通道。

3.使用ADC_StartSample()函数启动采样。

4.等待采样完成。

5.获得 采样后数据 ADC_DAT。

6.由于ch549是12位ADC模数转换器因此ADC_DAT为0-4095之间的数,它表达的含义是电压值的相对高低,因此 实际电压值的算法为ADC_DAT除以4095乘以ch549所使用的电压5v。

将例程编译下载进开发板,可以看到当旋转电位器的时候,屏幕上p0.7采集到的电压发生了改变。
2_现象

DAC 实验与具体代码解析

由于ch549没有硬件DAC,可以通过使用pwm波加一个简单的积分电路达到DAC的效果。
3_电路图
左边输入的pwm经过这个由一个电阻和一个电容组成的积分电路之后就会变为一条笔直模拟电压,具体的电压值为pwm占空比*5v

根据上图连接电路,如下图所示:
4_da连接

具体代码 main.c

/**
  ******************************************************************
  * @file    main.c
  * @author  甜鱼酱,xy
  * @version V1.0
  * @date    2021-5-19
  * @brief   DA
  ******************************************************************
  * @attention
  * verimake 用于
  * 输出pwm通过积分电路达到DA的效果,在通过AD采集显示在屏幕上
  ******************************************************************
  */
#include <CH549_sdcc.h>  //ch549的头文件,其中定义了单片机的一些特殊功能寄存器
#include <CH549_OLED.h>  //其中有驱动屏幕使用的函数
#include <CH549_BMP.h>   //用于显示图片的头文件
#include <CH549_DEBUG.h> //CH549官方提供库的头文件,定义了一些关于主频,延时,串口设置,看门口,赋值设置等基础函数
#include <CH549_SPI.h>   //CH549官方提供库的头文件,定义了一些关于SPI初始化,传输数据等函数
#include <CH549_ADC.h>   //CH549官方提供库的头文件,定义了一些关于ADC初始化,采集数据等函数
#include <CH549_PWM.h>   //CH549官方提供库的头文件,定义了一些关于pwm初始化,设置等函数
/********************************************************************
* TIPS:
* adc各通道对应引脚关系
* P1.1       AIN1
* P1.2       AIN2
* P1.3       AIN3
* P1.4       AIN4
* P1.5       AIN5
* P1.6       AIN6
* P1.7       AIN7
* P0.0       AIN8
* P0.1       AIN9
* P0.2       AIN10  
* P0.3       AIN11 
* P0.4       AIN12 
* P0.5       AIN13 
* P0.6       AIN14 
* P0.7       AIN15 
*********************************************************************/
int oled_colum;
int oled_row;
/********************************************************************
* 函 数 名       : setCursor
* 函数功能         : 设置printf到屏幕上的字符串起始位置
* 输    入       : 行坐标 列坐标
* 输    出         : 无
********************************************************************/
void setCursor(int column,int row)
{
oled_colum = column;
oled_row = row;
}
/********************************************************************
* 函 数 名       : main
* 函数功能       : 主函数
* 输    入       : 无
* 输    出         : 无
*********************************************************************/
void main(void)
{
  UINT8 ch;
  UINT8 i=0;
  float Voltage;
  float Output_Voltage;
  float Output_Baud; 
  CfgFsys(); //CH549时钟选择配置
  mDelaymS(20);
/* OLED的初始化程序 */
  SPIMasterModeSet(3); //SPI主机模式设置,模式3
  SPI_CK_SET(12);      //12分频
/* 初始化OLED */
  OLED_Init();
  OLED_Clear();
/* ADC的初始化配置 */
  ADC_ExInit(3); //ADC初始化,选择采样时钟
/* PWM的初始化配置 */
  SetPWMClkDiv(1);                          //PWM时钟配置,Fsys/4,Fsys默认为12Mhz
  SetPWMCycle256Clk();                      //PWM周期 Fsys/4/256
  PWM_SEL_CHANNEL(CH3,Enable);              //对ch3,即p2.2的pwm外设初始化
  /* 主循环 */
  while (1)
  {
    /* 配置占空比 */
    SetPWM3Dat(i);//配置通道3即p2.2的占空比,占空比等于i/256占空比
    mDelaymS(1000);
    ch = 15;
    ADC_ChSelect(ch);  //选择通道
    P0_MOD_OC = 0;
    P0_DIR_PU=0;
    ADC_StartSample(); //启动采样
    while ((ADC_CTRL & bADC_IF) == 0)
    {
      ; //查询等待标志置位
    }
    ADC_CTRL = bADC_IF; //清标志
    Voltage = (ADC_DAT / 4095.0)*5.0 ;
    /*
    adc采集到的值为ADC_DAT,由于ch549是12位ADC模数转换器因此ADC_DAT为0-4095之间的数,它表达的含义是电压值的相对高地
    因此 实际电压值的算法为ADC_DAT除以4095乘以ch549所使用的电压5v
    */
    Output_Baud = (i/256.0)*100;
    setFontSize(16); //设置显示在oled上屏幕的字号
    setCursor(0,0);//设置printf到屏幕上的字符串起始位置
      printf_fast_f("PWM %f%%    ",Output_Baud);//把p2.2输出pwm波的波特率显示在屏幕上
    Output_Voltage = (i/256.0)*5.0;
    setFontSize(16); //设置显示在oled上屏幕的字号
    setCursor(0,2);//设置printf到屏幕上的字符串起始位置
      printf_fast_f("DA SC %fV    ",Output_Voltage);//把积分后的电压预测数据显示屏幕上 SC含义为shuchu1(输出)
    setFontSize(16); //设置显示在oled上屏幕的字号
    setCursor(0,4);//设置printf到屏幕上的字符串起始位置
      printf_fast_f("AD CJ %fV    ",Voltage);//显示数据到屏幕上 CJ含义为caiji(采集)
    i+=10;
  }
}
/********************************************************************
* 函 数 名       : putchar
* 函数功能       : 将printf映射到oled屏幕输出上
* 输    入      : 字符串
* 输    出        : 字符串
********************************************************************/
int putchar( int a)
{      
  OLED_ShowChar(oled_colum,oled_row,a);
  oled_colum+=8;
  if (oled_colum>120){oled_colum=0;oled_row+=2;}
  return(a);
}

DA例程其实是由pwm例程和AD例程整合而成,目的是使用上文提到的积分电路将p2.2输出的逐步提升占空比的pwm波积分为模拟电压,然后让p0.7通过ADC进行采集。

将代码编译下载进开发板。

5_da现象

oled屏幕上显示了三行数据

第一行是p2.2输出的PWM占空比,

第二行是经过积分电路之后输出模拟电压的预测值,

第三行是p0.7通过AD实际采集到模拟电压的数据值.

可以看出第二行与第三行数据差距不大,表示搭建的DAC电路成功。

成为第一个点赞的人吧 :bowtie:
回复数量: 0
    暂无评论~~
    • 请注意单词拼写,以及中英文排版,参考此页
    • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
    • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
    • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
    • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
    Ctrl+Enter