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);
形参 hdac 是 ADC_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);
形参 hdac 是 DAC_HandleTypeDef 结构体类型指针变量。
形参 sConfig 是 DAC_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);
形参 hdma 是 DMA_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_0
或 DMA2_Stream4
中的 DMA_CHANNEL_0
。ADC2
使用 DMA2_Stream2
中的 DMA_CHANNEL_1
或 DMA2_Stream3
中的 DMA_CHANNEL_1
。ADC3
使用 DMA2_Stream0
中的 DMA_CHANNEL_2
或 DMA2_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 通道外设句柄。
StreamBaseAddress 和 StreamIndex 是 数据流基地址 和 索引号,这个是 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);
其中,htim 是 TIM_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);
形参 htim 是 TIM_HandleTypeDef 结构体类型指针变量。
形参 sMasterConfig 是 TIM_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 两个函数宏定义,分别是 更新定时器中断 和 使能定时器 的宏定义。
形参 htim 是 TIM_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);
形参 hdma 是 DMA 句柄,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);
形参 hdac 是 ADC_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);
形参 hdac 是 ADC_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;
}