STM32_使用DAC雙通道同時輸出對齊的三角波和方波


一、工具

  1、硬件:STM32F429BI單片機(HAL庫)

  2、編譯環境:Atollic TrueSTUDIO for STM32 9.3.0

  3、輔助工具:STM32CubeMX

二、需求分析

  現有以下需求,需要單片機能夠同時輸出一個方波和三角波,並且使方波的高電平的中間與三角波的波峰對齊,方波的低電平中間與三角波的波谷對齊,於此同時還必須能夠在任意時刻更改兩個波形的頻率以及三角波的幅值,效果如下圖所示:

 

   首先三角波必須得使用單片機的DAC來控制輸出,只需要和一個定時器配合工作,即可實現不同頻率的三角波輸出;方波可以使用DAC輸出也可以使用定時器輸出,如果使用DAC輸出方波,這就需要單片的DAC具備至少兩個通道;如果使用定時器輸出方波就得考慮同時啟動的問題。這里我使用單片機DAC的雙通道實現。

  1、通過查閱對應的芯片手冊,可以看到關於DAC生成三角波的介紹,具體內容如下圖所示:

   這里我總結下來就是:DAC有一個用於計數的寄存器DOR,而這個寄存器不會自己自加或者自減,自加和自減需要借助定時器產生的事件來完成(通常定時器會在一個周期內產生一個事件),而[DOR寄存器的值+DHRx寄存器的值]就是當前DAC輸出的電壓AD值,幅值就是[DHRx寄存的值+MAMPx寄存器的值]。盡管單片機具備輸出三角波的功能,但在使用中發現不夠靈活,於是乎我舍棄該方式,使用DAC+DMA的方式輸出三角波和方波。

三、單片機系統時鍾配置

  1、系統時鍾配置(沒有顯示的默認),這里選擇的是外部的高速時鍾(HSE)作為時鍾源,系統時鍾頻率配置到96MHz。

 

 

四、觸發源定時器配置

  這里選擇定時器6作為觸發源,該定時器主要是為DAC提供更新事件,具體配置如下圖所示。

 

五、DAC配置

  DAC選擇通道1和通道2配置相同,觸發源選擇的是定時器6,具體配置如下圖所示:

  補充:這里配置DAC的時候需要設置“Output Buffer”為“DISABLE”,否則三角波的波谷會出現不正常的波形。

 

   DAC的兩個通道都開啟DMA且配置相同,具體配置如下圖所示:

 七、生成工程並進行完善

  1、生成工程設置

 

  2、添加個人代碼

DAC_HandleTypeDef hdac;
DMA_HandleTypeDef hdma_dac1;
DMA_HandleTypeDef hdma_dac2;

TIM_HandleTypeDef htim6;

  WAVEFORM_POINT_NUM的值要根據定時器時鍾源來靈活設定,首先做到能被4整除,再者使定時器的時鍾源能被(定時器頻率*WAVEFORM_POINT_NUM)整除,比如當前我的定時器的時鍾源為48MHz,如果輸出1KHz頻率的三角波和方波,則有(1KHz*WAVEFORM_POINT_NUM) = 48MHz/(psc*per)。

#define WAVEFORM_POINT_NUM            800
uint32_t DAC_ch1_value[WAVEFORM_POINT_NUM];
uint32_t DAC_ch2_value[WAVEFORM_POINT_NUM];

/**
  * @brief  初始化三角波數組值
  * @param  amplitude 三角波幅值對應的DA值
  * @param  p 被初始化的數組起始地址
  * @param  length 被初始化的數組長度(長度值要能被4整除)
  * @retval  none
  */
void triangle_array_init(uint16_t amplitude, uint32_t *p, uint32_t length)
{
    uint32_t i;
    float unit_value = 0;

    unit_value = (amplitude/(length/2.0));

    for( i = 0; i < length; i++)
    {
        if(i <= (length/2))
            p[i] = (i*unit_value);
        else
            p[i] = ((length - i)*unit_value);
    }
}

/**
  * @brief  初始化方波數組值
  * @param  p 被初始化的數組起始地址
  * @param  length 被初始化的數組長度(長度值要能被4整除)
  * @retval  none
  */
void square_array_init(uint32_t *p, uint32_t length)
{
    uint32_t i;

    for( i = 0; i < length; i++)
    {
        if(i < (length/4))
            p[i] = 0;
        else if(i < ((length * 3)/4))
            p[i] = 4095;
        else
            p[i] = 0;
    }
}

/**
  * @brief  波形輸出停止
  * @retval  none
  */
void waveform_stop(void)
{
    HAL_TIM_Base_Stop(&htim6);
    HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_1);
    HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_2);
}

/**
  * @brief  波形輸出開啟
  * @retval  none
  */
void waveform_start(void)
{
    HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, DAC_ch1_value, WAVEFORM_POINT_NUM, DAC_ALIGN_12B_R);
    HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, DAC_ch2_value, WAVEFORM_POINT_NUM, DAC_ALIGN_12B_R);
    HAL_TIM_Base_Start(&htim6);
}

/**
  * @brief  波形設置
  * @param  freq 波形頻率
  * @param  voltage 三角波幅值電壓(單位mV)
  * @retval  none
  */
void waveform_set(uint32_t freq, float voltage)
{
    triangle_array_init((uint16_t)((voltage*4095)/3300) ,DAC_ch1_value, WAVEFORM_POINT_NUM);
    square_array_init(DAC_ch2_value, WAVEFORM_POINT_NUM);

    __HAL_TIM_SET_AUTORELOAD(&htim6, (48000000/(freq * WAVEFORM_POINT_NUM) - 1));
}

  3、不用修改的代碼

  定時器6初始化函數的周期值會在開啟波形輸出前被重新配置,這里的值並無意義,具體內容看代碼:

/**
  * @brief TIM6 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM6_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim6.Instance = TIM6;
  htim6.Init.Prescaler = 0;
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim6.Init.Period = 0;
  htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

 

/**
* @brief TIM_Base MSP Initialization
* This function configures the hardware resources used in this example
* @param htim_base: TIM_Base handle pointer
* @retval None
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM6)
  {
    /* Peripheral clock enable */
    __HAL_RCC_TIM6_CLK_ENABLE();
  }

}

/**
* @brief TIM_Base MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param htim_base: TIM_Base handle pointer
* @retval None
*/
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM6)
  {
    /* Peripheral clock disable */
    __HAL_RCC_TIM6_CLK_DISABLE();
  }

}

  DMA初始化,具體內容看代碼:

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
  /* DMA1_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);

}

  DMA中斷函數,具體內容看代碼:   

/**
  * @brief This function handles DMA1 stream5 global interrupt.
  */
void DMA1_Stream5_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_dac1);
}

/**
  * @brief This function handles DMA1 stream6 global interrupt.
  */
void DMA1_Stream6_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_dac2);
}

 

  DAC引腳初始化,及使能時鍾,具體內容看代碼:

/**
  * @brief DAC Initialization Function
  * @param None
  * @retval None
  */
static 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_T6_TRGO; sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK) { Error_Handler(); } /** DAC channel OUT2 config */ if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2) != HAL_OK) { Error_Handler(); } }

 

/**
* @brief DAC MSP Initialization
* This function configures the hardware resources used in this example
* @param hdac: DAC handle pointer
* @retval None
*/
void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hdac->Instance==DAC)
  {
    /* Peripheral clock enable */
    __HAL_RCC_DAC_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**DAC GPIO Configuration
    PA4     ------> DAC_OUT1
    PA5     ------> DAC_OUT2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* DAC DMA Init */
    /* DAC1 Init */
    hdma_dac1.Instance = DMA1_Stream5;
    hdma_dac1.Init.Channel = DMA_CHANNEL_7;
    hdma_dac1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_dac1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_dac1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_dac1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_dac1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_dac1.Init.Mode = DMA_CIRCULAR;
    hdma_dac1.Init.Priority = DMA_PRIORITY_LOW;
    hdma_dac1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_dac1) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hdac,DMA_Handle1,hdma_dac1);

    /* DAC2 Init */
    hdma_dac2.Instance = DMA1_Stream6;
    hdma_dac2.Init.Channel = DMA_CHANNEL_7;
    hdma_dac2.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_dac2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_dac2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_dac2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_dac2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_dac2.Init.Mode = DMA_CIRCULAR;
    hdma_dac2.Init.Priority = DMA_PRIORITY_LOW;
    hdma_dac2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_dac2) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hdac,DMA_Handle2,hdma_dac2);
  }

}

/**
* @brief DAC MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param hdac: DAC handle pointer
* @retval None
*/
void HAL_DAC_MspDeInit(DAC_HandleTypeDef* hdac)
{
  if(hdac->Instance==DAC)
  {
    /* Peripheral clock disable */
    __HAL_RCC_DAC_CLK_DISABLE();

    /**DAC GPIO Configuration
    PA4     ------> DAC_OUT1
    PA5     ------> DAC_OUT2
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4|GPIO_PIN_5);

    /* DAC DMA DeInit */
    HAL_DMA_DeInit(hdac->DMA_Handle1);
    HAL_DMA_DeInit(hdac->DMA_Handle2);
  }

}

 

4、主函數

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();


  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_DAC_Init();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */

  waveform_set(1000, 200);
  waveform_start();

  HAL_Delay(20000);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    waveform_stop();
    waveform_set(200, 800);
    waveform_start();
    HAL_Delay(20000);
    waveform_stop();
    waveform_set(1000, 1200);
    waveform_start();
    HAL_Delay(20000);
    waveform_stop();
    waveform_set(4000, 200);
    waveform_start();
    HAL_Delay(20000);

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM