搜索
bottom↓
回复: 0

《ATK-DFPGL22G之FPGA开发指南_V1.0》第三十七章 DAC输出实验

[复制链接]

出0入234汤圆

发表于 2023-7-29 18:08:01 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2023-7-29 18:07 编辑

1)实验平台:正点原子 DFZU2EG_4EV MPSoC开发板
2)购买链接:https://item.taobao.com/item.htm?&id=692368045899
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-340252-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子FPGA交流群:994244016 lQLPJxaFi2zaB4UWWrDAMgIsFEW2pwLb3abnwDMA_90_22.png
lQDPJxaFi2nfFizMjM0CbLCPlxn_FVheIQLb3aGrwFQA_620_140.jpg

lQLPJxaFi2nfFhLMkM0BXrDNvOUyeU_FPgLb3aGvQNIA_350_144.png


第三十七章 DAC输出实验


本章介绍使用APM32E103的DAC输出指定的电压值。通过本章的学习。读者将学习到DAC的使用。
本章分为如下几个小节:
37.1 硬件设计
37.2 程序设计
37.3 下载验证


37.1 硬件设计
37.1.1 例程功能

1. DAC的输出作为ADC的输入,并在LCD上显示DAC输出电压的数字量和模拟量和ADC采集到电压的模拟量
2. 按下KEY_UP和KEY0按键,可分别增加和减少DAC的输出
3. 可通过USMART直接设置DAC输出的电压值
4. LED0闪烁,指示程序正在运行
37.1.2 硬件资源
1. LED
        LED0 - PB5
2. 按键
        KEY0 - PE4
        KEY_UP - PA0
3. USART1(PA9、PA10连接至板载USB转串口芯片上)
4. 正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
5. ADC1
        通道1 - PA1
6. DAC
        通道1 - PA4
37.1.3 原理图
本章实验使用的DAC为APM32E103的片上资源,因此没有对应的连接原理图。
37.2 程序设计
37.2.1 Geehy标准库的DAC驱动

本章实验主要使用DAC的通道1(PA4引脚)输出指定的电压值(0V~3.3V),其具体的步骤如下:
①:配置DAC的通道1
②:使能DAC的通道1
③:配置DAC通道1的保持寄存器,以输出指定电压
在Geehy标准库中对应的驱动函数如下:
①:配置DAC通道
该函数用于配置DAC通道的相关参数,其函数原型如下所示:
void DAC_Config(uint32_t channel, DAC_Config_T* dacConfig);
该函数的形参描述,如下表所示:
lQLPJxd2JsDTDDDMtM0C57C5zs5e_AzJUgS55KfUgMYA_743_180.png
表37.2.1.1 函数DAC_Config()形参描述

该函数的返回值描述,如下表所示:
lQLPJw2x80IY6RA0zQLlsEXtpjz-5fC_BLnk6n0AowA_741_52.png
表37.2.1.2 函数DAC_Config()返回值描述

该函数使用的DAC_Config_T类型的结构体变量传入DAC通道的配置参数,该结构体的定义如下所示:
typedef enum
{
    DAC_TRIGGER_NONE                                = 0x00000000,   /* 禁用DAC通道触发 */
    DAC_TRIGGER_TMR6_TRGO                        = 0x00000004,   /* 定时器6的TRGO事件 */
    DAC_TRIGGER_TMR8_TRGO                        = 0x0000000C,   /* 定时器8的TRGO事件 */
    DAC_TRIGGER_TMR7_TRGO                        = 0x00000014,   /* 定时器7的TRGO事件 */
    DAC_TRIGGER_TMR5_TRGO                        = 0x0000001C,   /* 定时器5的TRGO事件 */
    DAC_TRIGGER_TMR2_TRGO                        = 0x00000024,   /* 定时器2的TRGO事件 */
    DAC_TRIGGER_TMR4_TRGO                        = 0x0000002C,   /* 定时器4的TRGO事件 */
    DAC_TRIGGER_EINT9                                = 0x00000034,   /* 外部中断线9 */
    DAC_TRIGGER_SOFT                                = 0x0000003C    /* 软件触发 */
} DAC_TRIGGER_T;

typedef enum
{
    DAC_WAVE_GENERATION_NONE                = 0x00000000,   /* 不产生波形 */
    DAC_WAVE_GENERATION_NOISE                = 0x00000040,   /* 产生噪声波形 */
    DAC_WAVE_GENERATION_TRIANGLE        = 0x00000080    /* 产生三角波波形 */
} DAC_WAVE_GENERATION_T;

typedef enum
{
    DAC_LFSR_MASK_BIT11_1                        = 0x00000000,   /* 屏蔽LFSP[11:1] */
    DAC_LFSR_MASK_BIT11_2                        = 0x00000100,   /* 屏蔽LFSP[11:2] */
    DAC_LFSR_MASK_BIT11_3                        = 0x00000200,   /* 屏蔽LFSP[11:3] */
    DAC_LFSR_MASK_BIT11_4                        = 0x00000300,   /* 屏蔽LFSP[11:4] */
    DAC_LFSR_MASK_BIT11_5                        = 0x00000400,   /* 屏蔽LFSP[11:5] */
    DAC_LFSR_MASK_BIT11_6                        = 0x00000500,   /* 屏蔽LFSP[11:6] */
    DAC_LFSR_MASK_BIT11_7                        = 0x00000600,   /* 屏蔽LFSP[11:7] */
    DAC_LFSR_MASK_BIT11_8                        = 0x00000700,   /* 屏蔽LFSP[11:8] */
    DAC_LFSR_MASK_BIT11_9                        = 0x00000800,   /* 屏蔽LFSP[11:9] */
    DAC_LFSR_MASK_BIT11_10                        = 0x00000900,   /* 屏蔽LFSP[11:10] */
    DAC_LFSR_MASK_BIT11                                = 0x00000A00,   /* 屏蔽LFSP[11] */
    DAC_LFSR_MASK_NONE                                = 0x00000B00,   /* 不屏蔽LFSP */

    DAC_TRIANGLE_AMPLITUDE_1                = 0x00000000,   /* 三角波幅值等于1 */
    DAC_TRIANGLE_AMPLITUDE_3                = 0x00000100,   /* 三角波幅值等于3 */
    DAC_TRIANGLE_AMPLITUDE_7                = 0x00000200,   /* 三角波幅值等于7 */
    DAC_TRIANGLE_AMPLITUDE_15                = 0x00000300,   /* 三角波幅值等于15 */
    DAC_TRIANGLE_AMPLITUDE_31                = 0x00000400,   /* 三角波幅值等于31 */
    DAC_TRIANGLE_AMPLITUDE_63                = 0x00000500,   /* 三角波幅值等于63 */
    DAC_TRIANGLE_AMPLITUDE_127                = 0x00000600,   /* 三角波幅值等于127 */
    DAC_TRIANGLE_AMPLITUDE_255                = 0x00000700,   /* 三角波幅值等于255 */
    DAC_TRIANGLE_AMPLITUDE_511                = 0x00000800,   /* 三角波幅值等于511 */
    DAC_TRIANGLE_AMPLITUDE_1023                = 0x00000900,   /* 三角波幅值等于1023 */
    DAC_TRIANGLE_AMPLITUDE_2047                = 0x00000A00,   /* 三角波幅值等于2047 */
    DAC_TRIANGLE_AMPLITUDE_4095                = 0x00000B00    /* 三角波幅值等于4095 */
} DAC_MASK_AMPLITUDE_SEL_T;

typedef enum
{
    DAC_OUTPUT_BUFFER_ENBALE                = 0x00000000,   /* 禁用输出缓存 */
    DAC_OUTPUT_BUFFER_DISABLE                = 0x00000002    /* 使能输出缓存 */
} DAC_OUTPUT_BUFFER_T;

typedef struct
{
    DAC_TRIGGER_T                                trigger;            /* 触发 */
    DAC_OUTPUT_BUFFER_T                        outputBuffer;       /* 输出缓存 */
    DAC_WAVE_GENERATION_T                waveGeneration;     /* 产生波形 */
    DAC_MASK_AMPLITUDE_SEL_T        maskAmplitudeSelect;/* 屏蔽LFSR位/三角波幅 */
} DAC_Config_T;
该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_dac.h"

void example_fun(void)
{
    DAC_Config_T dac_init_struct;
   
    /* 配置DAC通道1 */
    dac_init_struct.trigger                                 = DAC_TRIGGER_NONE;
    dac_init_struct.outputBuffer                 = DAC_OUTPUT_BUFFER_DISABLE;
    dac_init_struct.waveGeneration                 = DAC_WAVE_GENERATION_NONE;
    dac_init_struct.maskAmplitudeSelect = DAC_LFSR_MASK_BIT11_1;
    DAC_Config(DAC_CHANNEL_1, &dac_init_struct);
}
②:使能DAC通道
该函数用于使能DAC通道,其函数原型如下所示:
void DAC_Enable(DAC_CHANNEL_T channel);
该函数的形参描述,如下表所示:
lQLPJxGrrLJsITBozQLmsKDywJ08OggrBLnkybwAxgA_742_104.png
表37.2.1.3 函数DAC_Enable()形参描述

该函数的返回值描述,如下表所示:
lQLPJw2VwW8JKRAzzQLmsNa3ZFszsl1pBLnk1AxAxgA_742_51.png
表37.2.1.4 函数DAC_Enable()返回值描述

该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_dac.h"

void example_fun(void)
{
    /* 使能DAC通道1 */
    DAC_Enable(DAC_CHANNEL_1);
}
③:配置DAC通道数据保持寄存器
该函数用于配置DAC通道的数据保持寄存器,其函数原型如下所示:
void DAC_ConfigChannel1Data(DAC_ALIGN_T align, uint16_t data);
void DAC_ConfigChannel2Data(DAC_ALIGN_T align, uint16_t data);
该函数的形参描述,如下表所示:
lQLPJx99m-04gZDMg80C5bCFvwTiytQjXgS55N8pAGgA_741_131.png
表37.2.1.5 函数DAC_ConfigChannelnData()形参描述

该函数的返回值描述,如下表所示:
lQLPJwvhMrJTUjA0zQLksERlTIFeefngBLnktPPAxgA_740_52.png
表37.2.1.6 函数DAC_ConfigChannelnData()返回值描述

该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_dac.h"

void example_fun(void)
{
    /* 配置DAC通道1的保持寄存器 */
    DAC_ConfigChannel1Data(DAC_ALIGN_12BIT_R, 1024);
}
37.2.2 DAC驱动
本章实验的DAC驱动主要负责向应用层提供DAC的初始化和配置其输出指定电压的函数。本章实验中,DAC的驱动代码包括dac.c和dac.h两个文件。
DAC驱动中,DAC的初始化函数,如下所示:
/**
* @brief       初始化DAC
* @note        DAC使用VREF作为参考电压,将VSSA接地,可得到DAC的输出电压范围为:0-VREF。
*              DAC输出计算公式为:DAC输出=VREF * (DATAOCHx/4095)
*              
* @param       outx: 要初始化的通道. 1,通道1; 2,通道2
* @retval      无
*/
void dac_init(uint8_t outx)
{
    DAC_Config_T dac_init_struct;
    GPIO_Config_T gpio_init_struct;
   
    /* 使能时钟 */
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_DAC);           /* 使能DAC时钟 */
/* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
   
    /* APM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
    gpio_init_struct.pin = (outx == 1)? GPIO_PIN_4 : GPIO_PIN_5;
    gpio_init_struct.speed = GPIO_SPEED_50MHz;                /* 高速 */
    gpio_init_struct.mode = GPIO_MODE_ANALOG;                 /* 模拟输入 */
    GPIO_Config(GPIOA, &gpio_init_struct);                    /* 初始化DAC引脚 */
   
    /* 配置DAC */
dac_init_struct.trigger = DAC_TRIGGER_NONE;               /* 不使用触发功能 */
/* DAC1输出缓冲关闭 */
    dac_init_struct.outputBuffer = DAC_OUTPUT_BUFFER_DISABLE;
dac_init_struct.waveGeneration = DAC_WAVE_GENERATION_NONE;/* 不使用波形发生 */

/* 屏蔽、幅值设置 */
dac_init_struct.maskAmplitudeSelect = DAC_LFSR_MASK_BIT11_1;
/* 初始化DAC通道1 */
    DAC_Config(DAC_CHANNEL_1, &dac_init_struct);
   
    switch(outx)
    {
        case DAC_CHANNEL1:
        {
            DAC_ConfigChannel1Data(DAC_ALIGN_12BIT_R, 0);     /* 配置DAC通道1 */
            DAC_Enable(DAC_CHANNEL_1);                        /* 使能指定的ADC1 */
            break;
        }
        case DAC_CHANNEL2:
        {
            DAC_ConfigChannel2Data(DAC_ALIGN_12BIT_R, 0);     /* 配置DAC通道2 */
            DAC_Enable(DAC_CHANNEL_2);                        /* 使能指定的ADC2 */
            break;
        }
        default:
        {
            break;
        }
    }
}
从上面的代码中可以看出,DAC的初始化函数会根据传入的参数outx自动配置并使能DAC的通道1或通道2,同时将DAC通道对应的输出引脚配置为模拟模式。
DAC驱动中,配置DAC通道输出指定电压的函数,如下所示:
/**
* @brief       设置通道1/2输出电压
* @param       outx: 1,通道1; 2,通道2
* @param       vol : 0~3300,代表0~3.3V
* @retval      无
*/
void dac_set_voltage(uint8_t outx, uint16_t vol)
{
    uint32_t dacdata;
   
    dacdata = vol * 4095 / 3300;                          /* 将电压值转换为数字量 */
   
    if (dacdata > 4095)                                   /* 限制最大值 */
    {
        dacdata = 4095;
    }
   
    if (outx == 1)
{
/* DAC通道1输出的电压 */
        DAC_ConfigChannel1Data(DAC_ALIGN_12BIT_R, dacdata);
    }
    else
{
/* DAC通道2输出的电压 */
        DAC_ConfigChannel2Data(DAC_ALIGN_12BIT_R, dacdata);
    }
}
该函数将输入的电压模拟量转换为DAC输出的数字量后,将该值写入指定DAC通道的数据保持寄存器。
37.2.3 实验应用代码
本章实验的应用代码,如下所示:
int main(void)
{
    uint8_t key;
    uint16_t dacdata = 0;
    uint8_t t = 0;
    uint16_t dacoutdata;
    uint16_t dac_voltage;
    uint16_t adcdata;
    uint16_t adc_voltage;
   
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4);  /* 设置中断优先级分组为组4 */
    sys_apm32_clock_init(15);                         /* 配置系统时钟 */
    delay_init(120);                                  /* 初始化延时功能 */
    usart_init(115200);                               /* 初始化串口 */
    usmart_dev.init(120);                             /* 初始化USMART */
    led_init();                                       /* 初始化LED */
    lcd_init();                                       /* 初始化LCD */
    key_init();                                       /* 初始化按键 */
    adc_init();                                       /* 初始化ADC */
    dac_init(1);                                      /* 初始化DAC1_OUT1通道 */
   
    lcd_show_string(30, 50, 200, 16, 16, "APM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "DAC TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY_UP:+  KEY0:-", RED);
    lcd_show_string(30, 150, 200, 16, 16, "DAC VAL:", BLUE);
    lcd_show_string(30, 170, 200, 16, 16, "DAC VOL:0.000V", BLUE);
    lcd_show_string(30, 190, 200, 16, 16, "ADC VOL:0.000V", BLUE);
   
    while (1)
    {
        t++;
        key = key_scan(0);                            /* 扫描按键 */
        
/* KEY_UP按键按下,DAC输出增加200 */
        if (key == WKUP_PRES)                       
        {
            if (dacdata < 4000)
            {
                dacdata += 200;
            }
            DAC_ConfigChannel1Data(DAC_ALIGN_12BIT_R, dacdata);
        }
/* KEY0按键按下,DAC输出减少200 */
        else if (key == KEY0_PRES)                                          
        {
            if (dacdata > 200)
            {
                dacdata -= 200;
            }
            else
            {
                dacdata = 0;
            }
            DAC_ConfigChannel1Data(DAC_ALIGN_12BIT_R, dacdata);
        }
        
        if ((t == 10) || (key == WKUP_PRES) || (key == KEY0_PRES))
        {

/* 获取DAC输出电压的数字量 */
            dacoutdata = DAC_ReadDataOutputValue(DAC_CHANNEL_1);
/* 显示DAC输出电压的数字量 */
            lcd_show_xnum(94, 150, dacoutdata, 5, 16, 0, BLUE);
/* 计算实际输出的电压值(扩大1000倍) */
            dac_voltage = (dacoutdata * 3300) / 4095;
/* 显示DAC输出电压值的整数部分 */
            lcd_show_xnum(94, 170, dac_voltage / 1000, 1, 16, 0, BLUE);
/* 显示DAC输出电压值的小数部分(保留三位小数) */   
            lcd_show_xnum(110, 170, dac_voltage % 1000, 3, 16, 0x80, BLUE);
/* 获取ADC采集到电压的数字量 */
            adcdata = adc_get_result_average(ADC_ADCX_CHY, 10);
/* 计算实际电压值(扩大1000倍) */
            adc_voltage = (adcdata * 3300) / 4095;
/* 显示ADC采集电压值的整数部分 */
            lcd_show_xnum(94, 190, adc_voltage / 1000, 1, 16, 0, BLUE);
/* 显示ADC采集电压值的小数部分(保留三位小数) */
            lcd_show_xnum(110, 190, adc_voltage % 1000, 3, 16, 0x80, BLUE);
            
            LED0_TOGGLE();
            t = 0;
        }
        
        delay_ms(10);
    }
}
可以看到,应用代码中不仅初始化了DAC的通道1(PA4引脚)同时还使能了ADC的通道1(PA1引脚)用于采集DAC通道1的输出电压。在完成初始化后,便不断地扫描按键并将DAC输出电压的模拟量和数字量以及ADC采集到电压的模拟量在LCD上进行显示,此时若扫描到KEY0按键,则减少DAC输出电压的数字量,若扫描到KEY_UP按键,则增加DAC输出电压的数字量。
37.3 下载验证
在完成编译和烧录操作后,可以看到LCD上实时刷新显示着DAC输出电压的模拟量和数字量以及ADC采集到电压的模拟量,此时可以将PA4引脚(DAC通道1输出引脚)和PA1引脚(ADC通道1采集引脚)通过杜邦线相连,在按下KEY0或KEY_UP按键来调整DAC通道1的输出电压,可以看到LCD上显示的DAC输出电压的模拟量和数字量以及ADC采集到电压的模拟量也随之变化,并且ADC采集到电压的模拟量也十分接近DAC输出电压的模拟量。

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 16:01

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表