39. DAC输出正弦波

一、DAC输出正弦波

  利用 STM32 的 DAC 配合 TIM 定时器,可以输出随时间变化的电压。

  先将一个可以生成正弦波的数据表保存在静态内存中,然后在 DAC 以及这块内存中间使用 DMA 建立一个通道,经过以上步骤之后,DAC 模块就可以通过 DAM 通道拿取静态内存中可以生成正弦波的数据,拿取数据,然后经过数模准换,在引脚进行输出就可以得到正弦波了。

  如果没有一定的延时,那么得到的估计就是一个变化很快的模拟量。所以这个时候就需要使用定时器 TIMER 了。DAC 在初始化的时候,可以设置成使用定时器触发,这就意味着,当定时器溢满的时候,就会触发 DAC 工作。这样一来,就可以通过改变定时器的定时时间来改变正弦波的周期了。

二、DAC对应的通道引脚

DAC_OUT1 DAC_OUT2
PA4 PA5

三、DAC输出配置步骤

3.1、使能DAC对应的GPIO时钟

  使用 DAC 时钟:

#define __HAL_RCC_DAC_CLK_ENABLE()    do { \
                                      __IO uint32_t tmpreg = 0x00U; \
                                      SET_BIT(RCC->APB1ENR, RCC_APB1ENR_DACEN);\
                                      /* Delay after an RCC peripheral clock enabling */ \
                                      tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_DACEN);\
                                      UNUSED(tmpreg); \
                                      } while(0U)

  使能对应的 GPIO 时钟:

#define __HAL_RCC_GPIOA_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)

3.2、配置DAC工作参数

  DAC 的初始化函数,其声明如下:

HAL_StatusTypeDef HAL_DAC_Init(DAC_HandleTypeDef *hdac);

  形参 hdacADC_HandleTypeDef 结构体类型指针变量,其定义如下:

typedef struct
{
  DAC_TypeDef *Instance;                // DAC寄存器基地址
  __IO HAL_DAC_StateTypeDef State;      // DAC工作状态
  HAL_LockTypeDef  Lock;                // DAC锁定对象
  DMA_HandleTypeDef  *DMA_Handle1;      // 通道1的DMA处理句柄指针
  DMA_HandleTypeDef  *DMA_Handle2;      // 通道2的DMA处理句柄指针
  __IO uint32_t ErrorCode;              // DAC错误代码
} DAC_HandleTypeDef;

  Instance指向 DAC 寄存器基地址。实际上这个基地址 HAL 库已经定义好了,可以选择范围如下:

#define DAC1                ((DAC_TypeDef *) DAC_BASE)
#define DAC                 ((DAC_TypeDef *) DAC_BASE)

  从该结构体看到该函数并没有设置任何 DAC 相关寄存器,即没有对 DAC 进行任何配置,它只是 HAL 库提供用来在软件上初始化 DAC,为后面 HAL 库操作 DAC 做好准备。从该结构体看到该函数并没有设置任何 DAC 相关寄存器,即没有对 DAC 进行任何配置,它只是 HAL 库提供用来在软件上初始化 DAC,为后面 HAL 库操作 DAC 做好准备。

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

typedef enum 
{
    HAL_OK = 0x00U,             // 成功
    HAL_ERROR = 0x01U,          // 错误
    HAL_BUSY = 0x02U,           // 忙碌
    HAL_TIMEOUT = 0x03U         // 超时
} HAL_StatusTypeDef;

3.3、DAC底层初始化

  HAL 库中,提供 HAL_GPIO_Init() 函数用于配置 GPIO 功能模式,初始化 GPIO。该函数的声明如下:

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);

  该函数的第一个形参 GPIOx 用来指定端口号,可选值如下:

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)

  第二个参数是 GPIO_InitTypeDef 类型的结构体变量,用来设置 GPIO 的工作模式,其定义如下:

typedef struct
{
  uint32_t Pin;         // 引脚号
  uint32_t Mode;        // 模式设置
  uint32_t Pull;        // 上下拉设置
  uint32_t Speed;       // 速度设置
  uint32_t Alternate;   // 复用功能设置
}GPIO_InitTypeDef;

  成员 Pin 表示 引脚号,范围:GPIO_PIN_0 到 GPIO_PIN_15。

#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */

  成员 Mode 是 GPIO 的 模式选择,有以下选择项:

#define  GPIO_MODE_ANALOG                       MODE_ANALOG             // 模式功能

  成员 Pull 用于 配置上下拉电阻,有以下选择项:

#define  GPIO_NOPULL        0x00000000U     // 无上下拉
#define  GPIO_PULLUP        0x00000001U     // 上拉
#define  GPIO_PULLDOWN      0x00000002U     // 下拉

3.4、配置DAC通道

HAL_StatusTypeDef HAL_DAC_ConfigChannel(DAC_HandleTypeDef *hdac,  const DAC_ChannelConfTypeDef *sConfig, uint32_t Channel);

  形参 hdacDAC_HandleTypeDef 结构体类型指针变量。

  形参 sConfigDAC_ChannelConfTypeDef 结构体类型指针变量,定义如下:

typedef struct
{
  uint32_t DAC_Trigger;                     // DAC触发源选择
  uint32_t DAC_OutputBuffer;                // 启用或者禁止DAC通道缓冲区
} DAC_ChannelConfTypeDef;

  成员 DAC_Trigger 用来 选择 DAC 触发源,可选值如下:

#define DAC_TRIGGER_NONE                0x00000000UL                                                     /*!< Conversion is automatic once the DAC1_DHRxxxx register has been loaded, and not by external trigger */
#define DAC_TRIGGER_T2_TRGO             (DAC_CR_TSEL1_2                                   | DAC_CR_TEN1) /*!< TIM2 TRGO selected as external conversion trigger for DAC channel */
#define DAC_TRIGGER_T4_TRGO             (DAC_CR_TSEL1_2                  | DAC_CR_TSEL1_0 | DAC_CR_TEN1) /*!< TIM4 TRGO selected as external conversion trigger for DAC channel */
#define DAC_TRIGGER_T5_TRGO             (                 DAC_CR_TSEL1_1 | DAC_CR_TSEL1_0 | DAC_CR_TEN1) /*!< TIM3 TRGO selected as external conversion trigger for DAC channel */
#define DAC_TRIGGER_T6_TRGO             (                                                   DAC_CR_TEN1) /*!< Conversion started by software trigger for DAC channel */
#define DAC_TRIGGER_T7_TRGO             (                 DAC_CR_TSEL1_1                  | DAC_CR_TEN1) /*!< TIM7 TRGO selected as external conversion trigger for DAC channel */
#define DAC_TRIGGER_T8_TRGO             (                                  DAC_CR_TSEL1_0 | DAC_CR_TEN1) /*!< TIM8 TRGO selected as external conversion trigger for DAC channel */
#define DAC_TRIGGER_EXT_IT9             (DAC_CR_TSEL1_2 | DAC_CR_TSEL1_1                  | DAC_CR_TEN1) /*!< EXTI Line9 event selected as external conversion trigger for DAC channel */
#define DAC_TRIGGER_SOFTWARE            (DAC_CR_TSEL1                                     | DAC_CR_TEN1) /*!< Conversion started by software trigger for DAC channel */

  成员 DAC_OutputBuffer 用来 启用或者禁止 DAC 通道缓冲区,可选值如下:

#define DAC_OUTPUTBUFFER_ENABLE            0x00000000U
#define DAC_OUTPUTBUFFER_DISABLE           (DAC_CR_BOFF1)

  形参 Channel用于选择要配置的通道,可选择 DAC_CHANNEL_1 或者 DAC_CHANNEL_2。

#define DAC_CHANNEL_1                      0x00000000U
#define DAC_CHANNEL_2                      0x00000010U

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

3.5、配置DMA

3.5.1、使能DMA时钟

  使能对应的 DMA 时钟。

#define __HAL_RCC_DMA2_CLK_ENABLE()     do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA2EN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA2EN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)

3.5.2、配置DMA工作参数

  DMA 的初始化函数,其声明如下:

HAL_StatusTypeDef HAL_DMA_Init(DMA_HandleTypeDef *hdma);

  形参 hdmaDMA_HandleTypeDef 结构体类型指针变量,其定义如下:

typedef struct __DMA_HandleTypeDef
{
    DMA_Stream_TypeDef *Instance;                                               // 寄存器基地址
    DMA_InitTypeDef Init;                                                       // DAM通信参数
    HAL_LockTypeDef Lock;                                                       // DMA锁对象
    __IO HAL_DMA_StateTypeDef State;                                            // DMA传输状态
    void *Parent;                                                               // 父对象状态,HAL库处理的中间变量

    void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);              // DMA传输完成回调
    void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);          // DMA一半传输完成回调
    void (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma);            // DMA传输完整的Memory1回调
    void (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);        // DMA传输半完全内存回调
    void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);             // DMA传输错误回调
    void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma);             // DMA传输中止回调

    __IO uint32_t ErrorCode;                                                    // DMA存取错误代码
    uint32_t StreamBaseAddress;                                                 // DMA通道基地址
    uint32_t StreamIndex;                                                       // DMA通道索引 
}DMA_HandleTypeDef;

  Instance:是用来 设置寄存器基地址ADC1 使用 DMA2_Stream0 中的 DMA_CHANNEL_0DMA2_Stream4 中的 DMA_CHANNEL_0ADC2 使用 DMA2_Stream2 中的 DMA_CHANNEL_1DMA2_Stream3 中的 DMA_CHANNEL_1ADC3 使用 DMA2_Stream0 中的 DMA_CHANNEL_2DMA2_Stream1 中的 DMA_CHANNEL_2

#define DMA2_Stream0        ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE)
#define DMA2_Stream1        ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE)
#define DMA2_Stream2        ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE)
#define DMA2_Stream3        ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE)
#define DMA2_Stream4        ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE)

  Parent:是 HAL 库处理中间变量,用来 指向 DMA 通道外设句柄

  StreamBaseAddressStreamIndex数据流基地址索引号,这个是 HAL 库处理的时候会自动计算,用户无需设置。

  Init,它是 DMA_InitTypeDef 结构体类型变量,该结构体定义如下:

typedef struct
{
    uint32_t Channel;               // 传输通道
    uint32_t Direction;             // 传输方向
    uint32_t PeriphInc;             // 外设地址是否自增
    uint32_t MemInc;                // 内存地址是否自增
    uint32_t PeriphDataAlignment;   // 外设数据大小
    uint32_t MemDataAlignment;      // 存储器数据大小
    uint32_t Mode;                  // 传输模式
    uint32_t Priority;              // DMA优先级
    uint32_t FIFOMode;              // FIFO模式开启或者禁止
    uint32_t FIFOThreshold;         // FIFO阈值选择
    uint32_t MemBurst;              // 存储器突发模式
    uint32_t PeriphBurst;           // 外设突发模式
}DMA_InitTypeDef;

  成员 Channel 用来 指定传输通道,可选值如下:

#define DMA_CHANNEL_0                 0x00000000U    /*!< DMA Channel 0 */
#define DMA_CHANNEL_1                 0x02000000U    /*!< DMA Channel 1 */
#define DMA_CHANNEL_2                 0x04000000U    /*!< DMA Channel 2 */
#define DMA_CHANNEL_3                 0x06000000U    /*!< DMA Channel 3 */
#define DMA_CHANNEL_4                 0x08000000U    /*!< DMA Channel 4 */
#define DMA_CHANNEL_5                 0x0A000000U    /*!< DMA Channel 5 */
#define DMA_CHANNEL_6                 0x0C000000U    /*!< DMA Channel 6 */
#define DMA_CHANNEL_7                 0x0E000000U    /*!< DMA Channel 7 */

  成员 Direction 用来 指定传输方向,可选值如下:

#define DMA_PERIPH_TO_MEMORY          0x00000000U                   // 外设到内存

  成员 PeriphInc 用来 设置外设地址是否自增,可选值如下:

#define DMA_PINC_DISABLE              0x00000000U                 /*!< Peripheral increment mode disable */

  成员 MemInc 用来 设置内存地址是否自增,可选值如下:

#define DMA_MINC_ENABLE               ((uint32_t)DMA_SxCR_MINC)   /*!< Memory increment mode enable  */

  成员 PeriphDataAlignment 用来 外设数据大小,可选值如下:

#define DMA_PDATAALIGN_BYTE           0x00000000U                  /*!< Peripheral data alignment: Byte     */
#define DMA_PDATAALIGN_HALFWORD       ((uint32_t)DMA_SxCR_PSIZE_0) /*!< Peripheral data alignment: HalfWord */
#define DMA_PDATAALIGN_WORD           ((uint32_t)DMA_SxCR_PSIZE_1) /*!< Peripheral data alignment: Word     */

  成员 MemDataAlignment 用来 存储器数据大小,可选值如下:

#define DMA_MDATAALIGN_BYTE           0x00000000U                  /*!< Memory data alignment: Byte     */
#define DMA_MDATAALIGN_HALFWORD       ((uint32_t)DMA_SxCR_MSIZE_0) /*!< Memory data alignment: HalfWord */
#define DMA_MDATAALIGN_WORD           ((uint32_t)DMA_SxCR_MSIZE_1) /*!< Memory data alignment: Word     */

  成员 Mode 用来 设置传输模式,可选值如下:

#define DMA_NORMAL                    0x00000000U                  // 普通模式
#define DMA_CIRCULAR                  ((uint32_t)DMA_SxCR_CIRC)    // 循环模式
#define DMA_PFCTRL                    ((uint32_t)DMA_SxCR_PFCTRL)  // 外设流控模式

  成员 Priority 用来 DMA 优先级,可选值如下:

#define DMA_PRIORITY_LOW              0x00000000U                 /*!< Priority level: Low       */
#define DMA_PRIORITY_MEDIUM           ((uint32_t)DMA_SxCR_PL_0)   /*!< Priority level: Medium    */
#define DMA_PRIORITY_HIGH             ((uint32_t)DMA_SxCR_PL_1)   /*!< Priority level: High      */
#define DMA_PRIORITY_VERY_HIGH        ((uint32_t)DMA_SxCR_PL)     /*!< Priority level: Very High */

  成员 FIFOThreshold 用来 设置是否开启 FIFO,可选值如下:

#define DMA_FIFOMODE_DISABLE          0x00000000U                 /*!< FIFO mode disable */
#define DMA_FIFOMODE_ENABLE           ((uint32_t)DMA_SxFCR_DMDIS) /*!< FIFO mode enable  */

  成员 FIFOMode 用来 设置 FIFO 的阈值,可选值如下:

#define DMA_FIFO_THRESHOLD_1QUARTERFULL       0x00000000U                  /*!< FIFO threshold 1 quart full configuration  */
#define DMA_FIFO_THRESHOLD_HALFFULL           ((uint32_t)DMA_SxFCR_FTH_0)  /*!< FIFO threshold half full configuration     */
#define DMA_FIFO_THRESHOLD_3QUARTERSFULL      ((uint32_t)DMA_SxFCR_FTH_1)  /*!< FIFO threshold 3 quarts full configuration */
#define DMA_FIFO_THRESHOLD_FULL               ((uint32_t)DMA_SxFCR_FTH)    /*!< FIFO threshold full configuration          */

  成员 MemBurst 用来 设置存储器突发模式,可选值如下:

#define DMA_MBURST_SINGLE             0x00000000U
#define DMA_MBURST_INC4               ((uint32_t)DMA_SxCR_MBURST_0)  
#define DMA_MBURST_INC8               ((uint32_t)DMA_SxCR_MBURST_1)  
#define DMA_MBURST_INC16              ((uint32_t)DMA_SxCR_MBURST)

  成员 PeriphBurst 用来 设置外设突发模式,可选值如下:

#define DMA_PBURST_SINGLE             0x00000000U
#define DMA_PBURST_INC4               ((uint32_t)DMA_SxCR_PBURST_0)
#define DMA_PBURST_INC8               ((uint32_t)DMA_SxCR_PBURST_1)
#define DMA_PBURST_INC16              ((uint32_t)DMA_SxCR_PBURST)

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

3.6、连接DMA和外设

  HAL 库为了处理各类外设的 DMA 请求,在调用相关函数之前,需要调用一个宏定义标识符,来连接 DMA 和外设句柄。

#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__)               \
                        do{                                                      \
                              (__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \
                              (__DMA_HANDLE__).Parent = (__HANDLE__);             \
                          } while(0U)

  其中 __HANDLE__ 是外设初始化句柄。__PPP_DMA_FIELD__ 是外设句柄结构体的成员变量名。__DMA_HANDLE__ 是 DMA初始化句柄。

  在 HAL 库中,任何一个可以使用 DMA 的外设,它的初始化结构体句柄都会有一个 DMA_HandleTypeDef 指针类型的成员变量,是 HAL 库用来做相关指向的。__PPP_DMA_FIELD__ 就是 DMA_HandleTypeDef 结构体指针类型。这句话的含义就是把 __HANDLE__ 句柄的成员变量 __PPP_DMA_FIELD__ 和 DMA 句柄 __DMA_HANDLE__ 连接起来,是纯软件处理,没有任何硬件操作。

3.7、配置定时器

3.7.1、使能定时器的时钟

  使能对应的定时器的时钟。

#define __HAL_RCC_TIM6_CLK_ENABLE()   do { \
                                      __IO uint32_t tmpreg = 0x00U; \
                                      SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM6EN);\
                                      /* Delay after an RCC peripheral clock enabling */ \
                                      tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM6EN);\
                                      UNUSED(tmpreg); \
                                      } while(0U)
#define __HAL_RCC_TIM7_CLK_ENABLE()   do { \
                                      __IO uint32_t tmpreg = 0x00U; \
                                      SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM7EN);\
                                      /* Delay after an RCC peripheral clock enabling */ \
                                      tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM7EN);\
                                      UNUSED(tmpreg); \
                                      } while(0U)

3.7.2、配置定时器工作参数

  HAL 库提供基本定时器初始化函数,它的说明如下:

HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);

  其中,htimTIM_HandleTypeDef 结构体类型指针变量(亦称定时器句柄),结构体定义如下:

typedef struct
{
    TIM_TypeDef *Instance;                              // 寄存器基地址
    TIM_Base_InitTypeDef Init;                          // 定时器初始化结构体
    HAL_TIM_ActiveChannel Channel;                      // 定时器通道
    DMA_HandleTypeDef *hdma[7];                         // DMA管理结构体

    HAL_LockTypeDef Lock;                               // 锁对象
    __IO HAL_TIM_StateTypeDef State;                    // 定时器状态
    __IO HAL_TIM_ChannelStateTypeDef ChannelState[4];   // 定时器通道状态
    __IO HAL_TIM_ChannelStateTypeDef ChannelNState[4];  // 定时器互补通道状态
    __IO HAL_TIM_DMABurstStateTypeDef DMABurstState;    // DMA溢出状态
} TIM_HandleTypeDef;

  Instance指向定时器寄存器基地址,可选值如下:

#define TIM6                ((TIM_TypeDef *) TIM6_BASE)
#define TIM7                ((TIM_TypeDef *) TIM7_BASE)

  Init定时器初始化结构体,用于配置定时器的相关参数,它的定义如下:

typedef struct
{
    uint32_t Prescaler;             // 预分频系数
    uint32_t CounterMode;           // 计数模式
    uint32_t Period;                // 自动重装载值
    uint32_t ClockDivision;         // 时钟分频因子
    uint32_t RepetitionCounter;     // 重复计数器值
    uint32_t AutoReloadPreload;     // 自动重装载值预载入功能
} TIM_Base_InitTypeDef;

  Prescaler预分频系数,即写入预分频寄存器的值,范围 0 到 65535。

  CounterMode计数器计数模式,这里基本定时器只能向上计数。

#define TIM_COUNTERMODE_UP                 0x00000000U                          /*!< Counter used as up-counter   */

  Period自动重载值,即写入自动重载寄存器的值,范围 0 到 65535。

  AutoReloadPreload自动重载预装载使能,即控制寄存器 1(TIMx_CR1)的 ARPE 位。

#define TIM_AUTORELOAD_PRELOAD_DISABLE                0x00000000U               /*!< TIMx_ARR register is not buffered */
#define TIM_AUTORELOAD_PRELOAD_ENABLE                 TIM_CR1_ARPE              /*!< TIMx_ARR register is buffered */

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

3.8、配置定时器触发ADC触发

HAL_StatusTypeDef HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim, const TIM_MasterConfigTypeDef *sMasterConfig);

  形参 htimTIM_HandleTypeDef 结构体类型指针变量

  形参 sMasterConfigTIM_HandleTypeDef 结构体类型指针变量

typedef struct
{
  uint32_t  MasterOutputTrigger;    // 定时器的主模式触发输出
  uint32_t  MasterSlaveMode;        // 定时器的主从模式
} TIM_MasterConfigTypeDef;

  成员 MasterOutputTrigger 用来配置 定时器的主模式触发输出

#define TIM_TRGO_RESET            0x00000000U                                      /*!< TIMx_EGR.UG bit is used as trigger output (TRGO)              */
#define TIM_TRGO_ENABLE           TIM_CR2_MMS_0                                    /*!< TIMx_CR1.CEN bit is used as trigger output (TRGO)             */
#define TIM_TRGO_UPDATE           TIM_CR2_MMS_1                                    /*!< Update event is used as trigger output (TRGO)                 */
#define TIM_TRGO_OC1              (TIM_CR2_MMS_1 | TIM_CR2_MMS_0)                  /*!< Capture or a compare match 1 is used as trigger output (TRGO) */
#define TIM_TRGO_OC1REF           TIM_CR2_MMS_2                                    /*!< OC1REF signal is used as trigger output (TRGO)                */
#define TIM_TRGO_OC2REF           (TIM_CR2_MMS_2 | TIM_CR2_MMS_0)                  /*!< OC2REF signal is used as trigger output(TRGO)                 */
#define TIM_TRGO_OC3REF           (TIM_CR2_MMS_2 | TIM_CR2_MMS_1)                  /*!< OC3REF signal is used as trigger output(TRGO)                 */
#define TIM_TRGO_OC4REF           (TIM_CR2_MMS_2 | TIM_CR2_MMS_1 | TIM_CR2_MMS_0)  /*!< OC4REF signal is used as trigger output(TRGO)                 */

  成员 MasterSlaveMode 用来配置 定时器的主从模式

#define TIM_MASTERSLAVEMODE_ENABLE         TIM_SMCR_MSM                         /*!< No action */
#define TIM_MASTERSLAVEMODE_DISABLE        0x00000000U                          /*!< Master/slave mode is selected */

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

3.9、启动定时器

  HAL_TIM_Base_Start() 函数是使能定时器的函数。其声明如下:

HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);

  该函数调用了 __HAL_TIM_ENABLE_IT 和 __HAL_TIM_ENABLE 两个函数宏定义,分别是 更新定时器中断使能定时器 的宏定义。

  形参 htimTIM_HandleTypeDef 结构体类型指针变量,即定时器句柄。

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

3.10、启动DMA传输

HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);

  形参 hdmaDMA 句柄SrcAddress源地址DstAddress目的地址DataLength 是要 传输的数据长度

3.11、启动DMA传输DA转换器的数据

  使能启动 DAC 转换通道函数,其声明如下:

HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel, const uint32_t *pData, uint32_t Length, uint32_t Alignment);

  形参 hdacADC_HandleTypeDef 结构体类型指针变量。

  形参 Channel用于选择要配置的通道,可选择 DAC_CHANNEL_1 或者 DAC_CHANNEL_2。

  形参 pData传输数据的指针

  形参 Length要读取的长度

  形参 Alignment 用于 指定数据对齐方式

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

3.12、停止DMA传输DA转换器的数据

  使能启动 DAC 转换通道函数,其声明如下:

HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel);

  形参 hdacADC_HandleTypeDef 结构体类型指针变量。

  形参 Channel用于选择要配置的通道,可选择 DAC_CHANNEL_1 或者 DAC_CHANNEL_2。

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

四、程序源码

  DAC 初始化函数:

DAC_HandleTypeDef g_dac_handle;

/**
 * @brief DAC初始化
 * 
 */
void DAC_Init(void)
{
    g_dac_handle.Instance = DAC;                                                // DAC寄存器基地址
    HAL_DAC_Init(&g_dac_handle);
}

  DAC 底层初始化函数:

/**
 * @brief DAC通道配置函数
 * 
 * @param hdac DAC句柄
 * @param DAC_Trigger 触发选择,可选值:[DAC_TRIGGER_NONE, DAC_TRIGGER_Tx_TRGO(x可取[2, 4, 5, 5, 6, 7, 8]), DAC_TRIGGER_EXT_IT9, DAC_TRIGGER_SOFTWARE]
 * @param DAC_OutputBuffer 输出缓冲,可选值:[DAC_OUTPUTBUFFER_ENABLE, DAC_OUTPUTBUFFER_DISABLE]
 * @param channel 通道,可选值:[DAC_CHANNEL_1, DAC_CHANNEL_2]
 */
void DAC_ConfigChannel(DAC_HandleTypeDef *hdac, uint32_t DAC_Trigger, uint32_t DAC_OutputBuffer, uint32_t channel)
{
    DAC_ChannelConfTypeDef DAC_ChannelConfStruct = {0};

    DAC_ChannelConfStruct.DAC_Trigger = DAC_Trigger;
    DAC_ChannelConfStruct.DAC_OutputBuffer = DAC_OutputBuffer;
    HAL_DAC_ConfigChannel(hdac,&DAC_ChannelConfStruct, channel);
}

  DAC 通道配置函数:

/**
 * @brief DAC通道配置函数
 * 
 * @param hdac DAC句柄
 * @param DAC_Trigger 触发选择,可选值:[DAC_TRIGGER_NONE, DAC_TRIGGER_Tx_TRGO(x可取[2, 4, 5, 5, 6, 7, 8]), DAC_TRIGGER_EXT_IT9, DAC_TRIGGER_SOFTWARE]
 * @param DAC_OutputBuffer 输出缓冲,可选值:[DAC_OUTPUTBUFFER_ENABLE, DAC_OUTPUTBUFFER_DISABLE]
 * @param channel 通道,可选值:[DAC_CHANNEL_1, DAC_CHANNEL_2]
 */
void DAC_ConfigChannel(DAC_HandleTypeDef *hdac, uint32_t trigger, uint32_t outputBuffer, uint32_t channel)
{
    DAC_ChannelConfTypeDef DAC_ChannelConfStruct = {0};

    DAC_ChannelConfStruct.DAC_Trigger = trigger;
    DAC_ChannelConfStruct.DAC_OutputBuffer = outputBuffer;
    HAL_DAC_ConfigChannel(hdac, &DAC_ChannelConfStruct, channel);
}

  生成 DAC 输出正弦波的数组函数:

uint16_t g_dac_sin_array[4096];

/**
 * @brief 生成DAC输出正弦波的数组函数
 * 
 * @param maxVoltage 最大值
 * @param samples 采样点数
 */
void DAC_CreateSinArray(double maxVoltage, uint16_t samples)
{
    int maxValue = maxVoltage * 4096 / 3.3;
    double outputData = 0;                                                      // 存放计算后的数字量
    double increment = (2 * 3.141592653589793) / samples;                       // 计算相邻两个点的x轴间隔

    if (maxValue <= (samples / 2))                                              // 数据不合法
    {
        return;
    }
  
    for (uint8_t i = 0; i < samples; i++)
    {
        // 正弦波解析式:y=Asin(ωx+Φ)+b
        outputData = maxValue * sin(i * increment) + maxValue;
        outputData = (outputData > 4095) ? 4095 : outputData;
        g_dac_sin_array[i] = outputData;
    }
}

  main() 函数:

int main(void)
{
    TIM_MasterConfigTypeDef TIM_MasterConfigStruct = {0};
    uint16_t samples = 100;

    HAL_Init();
    System_Clock_Init(8, 336, 2, 7);
    Delay_Init(168);

    DAC_Init();
    DAC_ConfigChannel(&g_dac_handle, DAC_TRIGGER_T7_TRGO, DAC_OUTPUTBUFFER_DISABLE, DAC_CHANNEL_1);

    DAC_CreateSinArray(2.5, samples);
  
    DMA_MemoryToPeripheral_Init(DMA1_Stream5 , DMA_CHANNEL_7, 16, DMA_CIRCULAR, DMA_PRIORITY_HIGH);
    __HAL_LINKDMA(&g_dac_handle, DMA_Handle1, g_dma_handle);

    TIM_Base_Init(&g_tim7_handle, TIM7, 83, 9);

    TIM_MasterConfigStruct.MasterOutputTrigger = TIM_TRGO_UPDATE;
    TIM_MasterConfigStruct.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&g_tim7_handle, &TIM_MasterConfigStruct);

    HAL_TIM_Base_Start(&g_tim7_handle);
    HAL_DAC_Start_DMA(&g_dac_handle, DAC_CHANNEL_1, (uint32_t *)g_dac_sin_array, samples, DAC_ALIGN_12B_R);
    while (1)
    {
    
    }
  
    return 0;
}
posted @ 2024-01-05 20:40  星光樱梦  阅读(35)  评论(0编辑  收藏  举报