单片机
返回首页

STM32CubeMX系列 | DAC数模转换

2023-03-21 来源:zhihu

1. DAC简介

DAC(digital to analog converter)即数模转换器,它可以将数字信号转换为模拟信号,它的功能与ADC相反。在常见的数字信号系统中,大部分传感器信号被转化成电压信号,而ADC把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由DAC输出电压模拟信号来驱动某些执行器件,使人类易于感知。如音频信号的采集和还原就是这样的一个过程 STM32F1的DAC模块是12位数字输入,电压输出型的DAC。可以配置为8位或12位模式,也可以与DMA控制器配合使用,DAC工作在12位模式下时,数据可以设置为左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压 VREF 以获得更精确的转换结果。DAC结构框图如下示:

  • VDDA 和 VSSA 是DAC模块的供电引脚, VREF+ 是DAC模块的参考电压,DAC_OUTx是DAC的输出通道;当参考电压为 VREF+ 时,DAC的输出电压是线性的(0~ VREF+ ),12位模式下DAC输出电压计算公式如下:
    DACx 输出电压 = VREF+ * ( DORx / 4095 )

  • DAC输出是受DORx 寄存器直接控制的,但是不能直接往DORx 寄存器写入数据,而是要通过DHRx间接的传给DORx 寄存器,实现对DAC输出的控制。如果未选择硬件触发,1个APB1时钟周期后,DHRx中存储的数据将自动转移到DORx 寄存器;如果选择硬件触发,将在3个APB1时钟周期后进行转移

  • 当DORx加载了DHRx内容时,模拟输出电压将在一端时间 tSETTING 后可用,具体取决于电源电压和模拟输出负载,可以从数据手册查到 tSETTING 的典型值为3us,最大值为4us,因此DAC的转换速度最快是250K左右

  • DAC可通过外部事件(定时器、外部中断线)触发转换,外部触发源列表如下示

2. 硬件设计

本实验通过D1指示灯来提示系统运行状态,K_UP用来增加DAC输入值,K_DOWN用来减少DAC输入值,输入值的改变将控制DAC_OUT1的电压输出,通过USART1将输出的电压值打印出来

  • D1指示灯

  • DAC_OUT1(PA4)

  • USART1串口

  • K_UP和K_DOWN按键

3. 软件设计

3.1 STM32CubeMX设置

  • RCC设置外接HSE,时钟设置为72M

  • PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平

  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位

  • PA0设置为GPIO输入模式、下拉模式;PE3设置为GPIO输入模式、上拉模式

  • 激活DAC_OUT1,关闭输出缓冲,不使用触发功能

  • 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码

3.2 MDK-ARM编程

  • 在dac.c文件中可以看到DAC初始化相关函数

void MX_DAC_Init(void){

  DAC_ChannelConfTypeDef sConfig = {0};

  /** DAC Initialization */

  hdac.Instance = DAC;

  if (HAL_DAC_Init(&hdac) != HAL_OK){

    Error_Handler();

  }

  /** DAC channel OUT1 config */

  sConfig.DAC_Trigger = DAC_TRIGGER_NONE;   //不使用触发功能

  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;  //输出缓冲关闭

  if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK){

    Error_Handler();

  }

}


void HAL_DAC_MspInit(DAC_HandleTypeDef* dacHandle){

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  if(dacHandle->Instance==DAC)  {

  /* DAC clock enable */

  __HAL_RCC_DAC_CLK_ENABLE();   //使能DAC时钟

  __HAL_RCC_GPIOA_CLK_ENABLE(); 

  GPIO_InitStruct.Pin = GPIO_PIN_4;

  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;  //模拟

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  }

}

创建按键驱动文件key.c 和相关头文件key.h

uint8_t KEY_Scan(uint8_t mode){

    static uint8_t key = 1;     

    if(mode == 1){                      

        key = 1;

    }


    if(key&&(KEY_UP==1||KEY_DOWN==0||KEY_LEFT==0||KEY_RIGHT==0)){

        HAL_Delay(10);                  

        key = 0;

        if(KEY_UP==1)               return KEY_UP_PRES;

        else if(KEY_DOWN==0)        return KEY_DOWN_PRES; 

        else if(KEY_LEFT==0)        return KEY_LEFT_PRES; 

        else if(KEY_RIGHT==0)       return KEY_RIGHT_PRES; 

    }

    else if(KEY_UP==0&&KEY_DOWN==1&&KEY_LEFT==1&&KEY_RIGHT==1){

        key = 1;

    }   

    return 0;

}

在main.c文件下编写DAC测试代码

int main(void){

  /* USER CODE BEGIN 1 */

  uint16_t dac_val;     //读取的DAC值

  float dac_vol;        //转换后的电压值

  uint8_t t = 0;

  uint16_t dac_setval = 0;  //DAC设置值

  uint8_t key;

  /* USER CODE END 1 */

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();

  MX_DAC_Init();

  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */

  HAL_DAC_Start(&hdac,DAC_CHANNEL_1);   //开启DAC通道1

  HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,0);  //设置初始值为0

  /* USER CODE END 2 */

  while (1){

    t++;

        key = KEY_Scan(0);

        if(key == KEY_UP_PRES){

            if(dac_setval < 4000)

                dac_setval += 200;

            HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dac_setval);

        }

        else if(key == KEY_DOWN_PRES){

            if(dac_setval > 200)

                dac_setval -= 200;

            else

                dac_setval = 0;

            HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dac_setval);

        }


        if(t == 10 || key == KEY_UP_PRES || key == KEY_DOWN_PRES){  //按键按下了或定时时间到

            dac_val = HAL_DAC_GetValue(&hdac,DAC_CHANNEL_1);

            printf('DAC_OUT1 DAC value: %drn',dac_val);

            dac_vol = dac_val * (3.3/4096);

            printf('DAC_OUT1 VOL value: %.2fVrn',dac_vol);

            printf('rn');

            t = 0;

        }

        HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);

        HAL_Delay(100);

  }

}

4. 下载验证

编译无误后下载到开发板,可用看到D1指示灯不断闪烁,同时打印出DAC通道1的DAC值和电压值,当按下K_UP按键输出电压增大,按下K_DOWN按键输出电压减小


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 红外线探测报警器

  • 短波AM发射器电路设计图

  • RS-485基础知识:处理空闲总线条件的两种常见方法

  • 如何调制IC555振荡器

  • 基于ICL296的大电流开关稳压器电源电路

  • 基于TDA2003的简单低功耗汽车立体声放大器电路

    相关电子头条文章