文章目录
- 一、DAC简介
- 二、DAC的结构框图
- 1. 参考电压
- 2. 触发源
- 3. DAC数据寄存器
- 4. 转换的过程
- 5. 同步转换
- 三、生成正弦波数据表
- 四、DAC程序设计
- 1. 硬件设计
- 2. 软件设计
- ①文件结构
- ②FSP配置
- ③DAC初始化函数
- ④设置DAC输出电压函数
- ⑤DAC输出正弦波
- ⑥ hal_entry入口函数
一、DAC简介
DAC即数模转换模块,顾名思义,它的作用就是把输入的数字编码,转换成对应的模拟电压输出,它的功能与ADC相反。 在常见的数字信号系统中,大部分传感器信号被化成数字信号,而ADC把电压模拟信号转换成易于计算机存储、处理的数字编码,然后将这些数字编码交给计算机进行处理处理, 由DAC生成的模拟电压常常用来驱动某些执行器件比如LED灯可以控制它的亮度,使人们易于感知。
RA6M5 具有片上DAC外设,总共有两路DAC输出通道,每路的分辨率可配置为12位, 这两个通道之间互不影响,每个通道都可以使用ELC功能可外部触发或者进行与ADC单元1的同步转换。
RA6M5的DAC特性:
-
分辨率:12-bits
-
输出通道:2 通道
-
可设置模块停止状态降低功耗
-
DAC0 和 DAC1 可以通过一个事件信号输入来开启转换
-
可通过以下方法减少 DAC 和 ADC 模块之间的干扰:
①同步D/A转换控制,使D/A转换数据的更新时间由ADC12(uint1)输入信号同步
②通过合理的控制DAC12的启动信号来减少干扰对D/A转换精度的影响
二、DAC的结构框图
RA6M5的DAC模块框图可见
框图中的“12-bit D/A”是核心部件,整个DAC外设都是围绕着DA1通道和DA0通道展开的, 左上作为参考电源以及模拟转换输出,右边有着相应的控制寄存器、总线接口、和同步转换。
1. 参考电压
AVCC0引脚作为ADC12和DAC12的电源输入引脚,AVSS0引脚作为ADC12和DAC12的接地输入引脚。 VREFH引脚作为DAC12的模拟参考高电压电源引脚。VREFL引脚作为DAC12的模拟参考低电压引脚。
分别把它们接入到电源的正负两级,可以得到DAC的输出电压范围为:0~3.3V。 如果想让输出的电压范围变宽,可以在外部加一个电压调节电路,把0~3.3V的DAC输出抬升到特定的范围即可。
在电路设计的时候不增加磁珠和电容接入到DAC12电源电路,会使得输出的电压不稳甚至输出的不是想要的电压, 这个时候可以在电路上增加磁珠和电容来减少干扰,从而优化DAC比较电压源从而使其输出稳定。
2. 触发源
可以设置软件来触发DAC、或者通过使用ELC进行触发、甚至还可以使用外部中断进行触发。最终目的是为了使得DACR.DAOEn(n=0,1)位被置1, 可以编写相应的寄存器控制代码以至于可以使用不同的方式进行触发。
当设置DACR中的DAOEn位(n = 0,1)为1时,启用DAC12并输出转换结果。当设置DACR中的DAOEn位(n = 0,1)为0时,关闭DAC输出转换。
下面是使用软件触发方式来使能DAC的代码:
/**
* @brief 软件使能并输出电压
* @param 输入DAC模块
* @retval 无
*/
void trigger_dac(dac_ctrl_t * p_api_ctrl)
{
dac_instance_ctrl_t * p_ctrl = (dac_instance_ctrl_t *) p_api_ctrl;
p_ctrl->p_reg->DACR_b.DAOE0 = 1U; //使能DAC通道一使其输出相应电压
//p_ctrl->p_reg->DACR_b.DAOE1 = 1U; //使能DAC通道二使其输出相应电压
}
3. DAC数据寄存器
DADRn(D/A数据转换寄存器):
DADRn寄存器是一个16位的读/写寄存器,它存储用于D/A转换的数据。 虽然DADRn数据寄存器有16位数据,但是只用到了前面的12位,而后四位没有用到。还可以通过DADPR.DPSEL位进行设置12位的数据设置为左对齐或右对齐的数据格式。
当设置的触发源被启动时,DADRn中的值就会被转换并输出到模拟输出引脚上。 之后每当改变DADRn中的数值时,就可以改变输出的模拟电压。
电压转换公式:
如果直接通过输入一个12位的数字值,之后进行对DAC的触发源进行操作时,那么对软件的可操作性就比较差, 这个时候可以通过对软件代码优化使得更容易操作和控制电压,以下就是12位的数字值与电压之间的转换公式:
通过上面的公式我们知道(设定的数值/4096)x(基准电压)等于需要设定的电压, X对应着需要设定的电压,Y为最终需要设定的数值,那么会有这么一个等式成立: Y/2^12 * 3.3 = X => X / 3.3 * 2^12 = Y。 通过这个公式就可以知道设置的D/A转换电压是多少,将上面的公式写成如下的函数。 其中set_voltage为需要设置的电压,dac_date为需要输入到D/A数据转换寄存器的数据。
/**
* @brief 设置当前的电压3.3V
* @param 需要控制的电压
* @retval 无
*/
void dac_Set(double voltage)
{
uint16_t dac_date;
dac_date = (uint16_t)4095*((voltage)/3.3f);
R_DAC_Write(DAC.p_ctrl, dac_date);
}
4. 转换的过程
在转换的过程中想要停止DAC转换的话,只需要将DACR.DAOEn(n=0,1)位给置0。
-
第一步在DADPR寄存器中设置我们想要转换的数据格式
-
触发使能寄存器,使得DAC使能寄存器DACR.DAOEn(n=0,1),以开始数模转换转换的结果将在一段时间t过后从输出引脚DA0上输出。如果使能寄存器一直为1的话,DAC将一直转换输出,直到将DAC使能寄存器DACR.DAOEn(n=0,1)位设置0。
-
若在转换的过程中,想要改变DAC模拟输出的数值的时候,需要将一个值写入到数据寄存器DADRn(n=0,1)中,转换的结果将在一段时间t过后从输出引脚DAn(n=0,1)上输出。
5. 同步转换
通过同步转换可以使得ADC的单元一和DAC进行同步转换, 通过两个寄存器来控制这一过程,这两个寄存器分别为:通过两个寄存器来控制这一过程这两个寄存器分别为:同步单元选择寄存器(DAADUSR寄存器)和 同步使能转换寄存器(DAADSCR寄存器)。
DAADSCR寄存器:
如果使用ELC事件链接控制器来控制的话,就不能够使能DAADSCR寄存器。 将DAADST位设置为0,允许DADRn寄存器值随时转换为模拟数据。 将DAADST位设置为1,允许D/A转换与同步D/A转换同步,使来自ADC12的输入信号同步(单元1)。 使用此位直到DADC12(单元1)完成A/D转换才开始D/A转换,即使DADRn寄存器被改变。
DAADUSR寄存器:
DAADUSR寄存器选择ADC12单元一进行D/A和A/D同步转换。 将AMADSEL1位设置为1,选择ADC单元1作为DAC的同步单元。 将AMADSEL1位设置为0,不选择ADC单元同步单元。
三、生成正弦波数据表
要输出正弦波,实质是要控制DAC以v=sin(t)的正弦函数关系输出电压,其中v为电压输出,t为时间。
而由于模拟信号连续而数字信号是离散的,所以使用DAC产生正弦波时,只能按一定时间间隔输出正弦曲线上的点,在该时间段内输出相同 的电压值,若缩短时间间隔,提高单个周期内的输出点数,可以得到逼近连续正弦波的图形,见下图,若在外部电路加上适当的电容滤波,可得到更完美的图形。
由于正弦曲线是周期函数,所以只需要得到单个周期内的数据后按周期重复即可,而单个周期内取样输出的点数又是有限的,所以为了得到呈v=sin(t)函数关系电压值的数据通常不会实时计算获取,而是预先计算好函数单个周期内的电压数据表,并且转化成以DAC寄存器表示的值。
如sin函数值的范围为[-1: +1],而RA6M5的DAC输出电压范围为[0~3.3]V,按12位DAC分辨率表示的方法,可写入寄存器的最大值为212 = 4096,即范围为[0:4096]。所以,实际输出时,会进行如下处理:
-
抬升sin函数的输出为正值:v = sin(t)+1 ,此时,v的输出范围为[0:2];
-
扩展输出至DAC的全电压范围: v = 3.3*(sin(t)+1)/2 ,此时,v的输出范围为[0:3.3],正是DAC的电压输出范围,扩展至全电压范围可以充分利用DAC的分辨率;
-
把电压值以DAC寄存器的形式表示:Reg_val = 212/3.3 * v = 211*(sin(t)+1),此时,存储到DAC寄存器的值范围为[0:4095];
-
实践证明,在sin(t)的单个周期内,取32个点进行电压输出已经能较好地还原正弦波形,所以在t∈[0:2π]区间内等间距根据上述Reg_val公式运算得到32个寄存器值,即可得到正弦波表;
-
控制DAC输出时,每隔一段相同的时间从上述正弦波表中取出一个新数据进行输出,即可输出正弦波。改变间隔时间的单位长度,可以改变正弦波曲线的周期。
四、DAC程序设计
1. 硬件设计
野火启明6M5开发板的引出引脚电路图如图所示
RA6M5有2个DAC通道,两个通道分别可以连接到 P014 和 P015 引脚上。
2. 软件设计
①文件结构
├─ ......
└─ src
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
├─ debug_uart
│ ├─ bsp_debug_uart.c
│ └─ bsp_debug_uart.h
├─ adc
│ ├─ bsp_dac.c
│ └─ bsp_dac.h
└─ hal_entry.c
②FSP配置
打开该工程的 FSP 配置界面进行配置。
首先依次点击 “Stacks” -> “Pins” -> “Peripherals” -> “DAC0” 来配置通道 DA0 对应的引脚为 P014。 如下图所示。
然后依次点击 “Stacks” -> “New Stack” -> “Analog” -> “DAC (r_dac)” 来配置DAC模块。 如下图所示。
DAC 的属性配置:
实际上除了DA0引脚选择以外,在本次实验中这些属性都不怎么需要配置,按照默认值即可。
配置完成之后可以按下快捷键“Ctrl + S”保存, 最后点右上角的 “Generate Project Content” 按钮,让软件自动生成配置代码即可。
③DAC初始化函数
/**
* @brief 初始化DAC
* @param 无
* @retval 无
*/
void DAC_Init()
{
R_DAC_Open(&g_dac0_ctrl, &g_dac0_cfg);
R_DAC_Start(&g_dac0_ctrl);
}
-
R_DAC_Open()配置单个 DAC 通道,启动通道,并提供用于 DAC API 写入和关闭函数的句柄。
-
R_DAC_Start()启动 D/A 转换输出。
④设置DAC输出电压函数
/**
* @brief 设置当前的电压
* @param 需要控制的电压
* @retval 无
*/
void DAC_SetVoltage(float voltage)
{
uint16_t dac_data;
dac_data = (uint16_t)(4095*((voltage)/3.3f));
R_DAC_Write(&g_dac0_ctrl, dac_data);
}
通过电压转换公式将输入的模拟量转换为数值量,输入到R_DAC_Write()函数里, R_DAC_Write()会将数据写入到 D/A 转换器里。
⑤DAC输出正弦波
//正弦波数据数组变量
uint16_t var[] = {
2048, 2460, 2856, 3218, 3532, 3786, 3969, 4072, 4093, 4031, 3887, 3668,
3382, 3042, 2661, 2255, 1841, 1435, 1054, 714, 428, 209, 65, 3, 24, 127,
310, 564, 878, 1240, 1636, 2048
};
/**
* @brief 生成正弦波波形
* @param 输入的值可以调节波形的周期
* @retval 无
*/
void DAC_SinWave_Cycle(uint32_t time_interval)
{
for(uint32_t i = 0 ; i < (sizeof(var)/sizeof(var[0])); i++)
{
R_DAC_Write(&g_dac0_ctrl, var[i]);
R_BSP_SoftwareDelay(time_interval, BSP_DELAY_UNITS_MILLISECONDS);
}
}
通过轮循的方式将之前python生成的正弦波数据输入到R_DAC_Write()函数里面, 并且延时一段时间。而延时时间就是输入到函数里面的数值, 通过改变这一数值就可以调节正弦波的周期以及频率。
⑥ hal_entry入口函数
/* 用户头文件包含 */
#include "led/bsp_led.h"
#include "debug_uart/bsp_debug_uart.h"
#include "dac/bsp_dac.h"
void hal_entry(void)
{
/* TODO: add your own code here */
LED_Init(); // LED 初始化
Debug_UART4_Init(); // SCI4 UART 调试串口初始化
DAC_Init(); // DAC 初始化
printf("这是一个DAC输出正弦波的实验例程\r\n");
printf("使用示波器测量 P014 引脚(DAC 0)\r\n");
while(1)
{
DAC_SinWave_Cycle(1);
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}