本帖最后由 正点原子运营 于 2024-5-7 10:02 编辑
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板
2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版
6)正点原子STM32技术交流QQ群:725095144
本章将使用TIM7的更新事件用于TRGO触发DAC Channel1输出DMA1 Stream5 Channel7从使用软件算法生成的正弦波数据,以输出正弦波。通过本章的学习,读者将学习到DAC、TIM、DMA的使用。 本章分为如下几个小节: 39.1 硬件设计 39.2 程序设计 39.3 下载验证
39.1 硬件设计 39.1.1 例程功能 1. 按下WKUP按键,PA4引脚输出正弦波1(幅值3.3V,频率1KHz,采样点100) 2. 按下KEY0按键,PA4引脚输出正弦波2(幅值3.3V,频率10KHz,采样点10) 3. LED0闪烁,提示程序正在运行
39.1.2 硬件资源 1. LED LED0 - PB5 2. 正点原子2.8/3.5/4.3/7/10寸TFTLCD模块 3. 按键 WKUP - PA0 KEY0 - PE4 4. DAC Channel1 - PA4 5. DMA1 Stream5 Channel7 6. TIM7
39.1.3 原理图 本章实验使用的DAC为STM32F103的片上资源,因此没有对应的连接原理图。
39.2 程序设计 39.2.1 HAL库的TIM驱动 本章实验中将使用TIM7的更新事件产生TRGO事件,该TRGO事件将用于触发DAC,其具体的步骤如下: ①:初始化TIM ②:配置TIM主模式 在HAL库中对应的驱动函数如下: ①:初始化TIM 请见第16.2.1小节中初始化TIM的相关章节。 ②:配置TIM主模式 该函数用于配置TIM的主模式,其函数原型如下所示: - HAL_StatusTypeDefHAL_TIMEx_MasterConfigSynchronization(
- TIM_HandleTypeDef*htim,
- TIM_MasterConfigTypeDef*sMasterConfig);
复制代码该函数的形参描述,如下表所示: 表39.2.1.1 函数HAL_TIMEx_MasterConfigSynchronization()形参描述 该函数的返回值描述,如下表所示: 表39.2.1.2 函数HAL_TIMEx_MasterConfigSynchronization()返回值描述 该函数使用TIM_MasterConfigTypeDef类型结构体指针传入了TIM主模式的配置参数,该结构体的定义如下所示: - typedef struct
- {
- uint32_tMasterOutputTrigger; /* 触发输出选择 */
- uint32_t MasterSlaveMode; /* 主从模式 */
- }TIM_MasterConfigTypeDef;
复制代码该函数的使用示例,如下所示: - #include"stm32f1xx_hal.h"
- void example_fun(void)
- {
- TIM_MasterConfigTypeDef config = {0};
-
- /* 配置TIM主模式 */
- config.MasterOutputTrigger= TIM_TRGO_RESET;
- config.MasterSlaveMode =TIM_MASTERSLAVEMODE_ENABLE;
- HAL_TIMEx_MasterConfigSynchronization(&tim_handle, &config);
- }
复制代码
39.2.2 HAL库的DAC驱动 本章实验与第四十章一样,使用DAC通道1(PA4引脚)输出电压,不同之处在于,本章使用使用DMA自动将DAC通道1待输出的数据写入DAC的数据保持寄存器,以输出正弦波。其中对DAC的操作请见第四十章中HAL库的DAC驱动的相关内容,本小节仅介绍DAC使用DMA的相关步骤,其具体的步骤如下: ①:开启DAC DMA转换 在HAL库中对应的驱动函数如下: ①:开启DAC DMA转换 该函数用于开启DAC DMA转换,其函数原型如下所示: - HAL_StatusTypeDef HAL_DAC_Start_DMA( DAC_HandleTypeDef *hdac,
- uint32_t Channel,
- uint32_t *pData,
- uint32_t Length,
- uint32_t Alignment);
复制代码该函数的形参描述,如下表所示: 该函数的返回值描述,如下表所示: 该函数的使用示例,如下所示: - #include"stm32f1xx_hal.h"
- void example_fun(void)
- {
- uint16_t buf;
-
- /* 开启DAC DMA传输 */
- HAL_DAC_Start_DMA(&dac_handle,
- DAC_CHANNEL_1,
- buf,
- sizeof(buf) / sizeof(buf),
- DAC_ALIGN_12B_R);
- }
复制代码
39.2.3DAC驱动 本章实验的DAC驱动主要负责向应用层提供DAC的初始化和使能DAC输出指定幅值、频率的正弦波。本章实验中,DAC的驱动代码包括dac.c和dac.h两个文件。 DAC驱动中,DAC的初始化函数,如下所示: - /**
- *@brief 初始化DAC DMA波形输出
- *@param 无
- *@retval 无
- */
- voiddac_dma_wave_init(void)
- {
- /* 配置DAC */
- g_dac_dma_handle.Instance = DAC_DMA_DACX;
- HAL_DAC_Init(&g_dac_dma_handle);
- }
- /**
- *@brief HAL库DAC初始化MSP函数
- *@param hdac: DAC句柄
- *@retval 无
- */
- voidHAL_DAC_MspInit(DAC_HandleTypeDef*hdac)
- {
- GPIO_InitTypeDef gpio_init_struct = {0};
- DAC_ChannelConfTypeDef dac_channel_conf_struct = {0};
- TIM_MasterConfigTypeDef tim_master_config_struct = {0};
-
- if (hdac->Instance == DAC_DMA_DACX)
- {
- DAC_DMA_DACX_CLK_ENABLE();
- DAC_DMA_DACX_CHY_GPIO_CLK_ENABLE();
- DAC_DMA_DACX_DMA_CLK_ENABLE();
- DAC_DMA_DACX_TIMX_CLK_ENABLE();
-
- /* 配置DAC输出引脚 */
- gpio_init_struct.Pin = DAC_DMA_DACX_CHY_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_ANALOG;
- gpio_init_struct.Pull = GPIO_NOPULL;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(DAC_DMA_DACX_CHY_GPIO_PORT, &gpio_init_struct);
-
- /* 配置DAC通道 */
- dac_channel_conf_struct.DAC_Trigger =DAC_TRIGGER_T7_TRGO;
- dac_channel_conf_struct.DAC_OutputBuffer =DAC_OUTPUTBUFFER_DISABLE;
- HAL_DAC_ConfigChannel(hdac, &dac_channel_conf_struct, DAC_DMA_DACX_CHY);
-
- /* 配置DMA */
- g_dac_dma_dma_handle.Instance = DAC_DMA_DACX_DMACX;
- g_dac_dma_dma_handle.Init.Direction =DMA_MEMORY_TO_PERIPH;
- g_dac_dma_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;
- g_dac_dma_dma_handle.Init.MemInc = DMA_MINC_ENABLE;
- g_dac_dma_dma_handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;
- g_dac_dma_dma_handle.Init.MemDataAlignment =DMA_MDATAALIGN_HALFWORD;
- g_dac_dma_dma_handle.Init.Mode = DMA_CIRCULAR;
- g_dac_dma_dma_handle.Init.Priority = DMA_PRIORITY_VERY_HIGH;
- HAL_DMA_Init(&g_dac_dma_dma_handle);
-
- __HAL_LINKDMA(hdac, DMA_Handle1, g_dac_dma_dma_handle);
-
- HAL_NVIC_SetPriority(DAC_DMA_DACX_DMACX_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(DAC_DMA_DACX_DMACX_IRQn);
-
- /* 配置TIM */
- g_dac_dma_tim_handle.Instance = DAC_DMA_DACX_TIMX;
- HAL_TIM_Base_Init(&g_dac_dma_tim_handle);
-
- tim_master_config_struct.MasterOutputTrigger=TIM_TRGO_UPDATE;
- HAL_TIMEx_MasterConfigSynchronization( &g_dac_dma_tim_handle,
- &tim_master_config_struct);
- }
- }
复制代码可以看到本章实验DAC驱动中的DAC初始化函数与第三十九章“DAC输出实验”中对DAC的初始化基本一致,不过本章配置DAC Channel1的触发源为TIM7的TRGO事件,同时还配置了DMA。 DAC驱动中使能DAC输出指定幅值、频率的正弦波的函数,如下所示: - /**
- *@brief 使能DAC DMA波形输出
- *@param ndtr: DMA数据流传输一次数据项的数目
- *@param arr: DAC触发定时器的自动重装载值
- *@param psc: DAC触发定时器的预分频器数值
- *@retval 无
- */
- voiddac_dma_wave_enable(uint16_t ndtr, uint16_t arr, uint16_t psc)
- {
- /* 配置TIM */
- g_dac_dma_tim_handle.Init.Prescaler = psc;
- g_dac_dma_tim_handle.Init.Period = arr;
- HAL_TIM_Base_Init(&g_dac_dma_tim_handle);
- HAL_TIM_Base_Start(&g_dac_dma_tim_handle);
-
- /* 重新开启DAC DMA传输 */
- HAL_DAC_Stop_DMA(&g_dac_dma_handle, DAC_DMA_DACX_CHY);
- HAL_DAC_Start_DMA( &g_dac_dma_handle,
- DAC_DMA_DACX_CHY,
- (uint32_t *)g_dac_sin_buf,
- ndtr,
- DAC_ALIGN_12B_R);
- }
复制代码该函数配置了TIM7的溢出频率,因此TIM7的溢出频率就决定了DAC输出正弦波的频率,同时也开启了DAC DMA传输。
39.2.4 实验应用代码 本章实验的应用代码,如下所示: - /* 正弦波数据缓冲区 */
- uint16_t g_dac_sin_buf[4096];
- /**
- *@brief 生成正弦数据
- *@param maxval : 正弦的振幅
- *@param samples: 正弦一个周期的采样点个数
- *@retval 无
- */
- static voiddac_creat_sin_buf(uint16_t maxval, uint16_t samples)
- {
- uint16_t i;
- double w;
- uint16_t outdata;
-
- w = (2 * 3.1415926) / samples; /* ω=2π/T */
-
- for (i=0; i<samples; i++)
- {
- outdata = maxval * sin(w * i + 0) + maxval; /* y=Asin(ωx+φ)+b */
- if (outdata > 4095) /* 限制上限 */
- {
- outdata = 4095;
- }
- g_dac_sin_buf = outdata;
- }
- }
- int main(void)
- {
- uint8_t t = 0;
- uint8_t key;
-
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
- delay_init(72); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
- led_init(); /* 初始化LED */
- key_init(); /* 初始化按键 */
- lcd_init(); /* 初始化LCD */
- dac_dma_wave_init(); /* 初始化DAC DMA波形输出 */
- dac_creat_sin_buf(2048 - 1, 100); /* 生成正弦数据 */
- dac_dma_wave_enable(100, 10 - 1, 72 - 1); /* 使能DAC DMA波形输出 */
-
- lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
- lcd_show_string(30, 70, 200, 16, 16, "DAC DMASine Wave TEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
-
- lcd_show_string(30, 110, 200, 16, 16, "WK_UP:Wave1KEY0:Wave2", RED);
-
- while (1)
- {
- key = key_scan(0);
- if (key == WKUP_PRES)
- {
- /* 生成正弦数据 */
- dac_creat_sin_buf(2048 - 1, 100);
- /* 使能DAC DMA波形输出 */
- dac_dma_wave_enable(100, 10 - 1, 72 - 1);
- }
- else if (key == KEY0_PRES)
- {
- /* 生成正弦数据 */
- dac_creat_sin_buf(2048 - 1, 10);
- /* 使能DAC DMA波形输出 */
- dac_dma_wave_enable(10, 10 - 1, 72 - 1);
- }
-
- if (++t == 20)
- {
- t = 0;
- LED0_TOGGLE();
- }
-
- delay_ms(10);
- }
- }
复制代码可以看到,在实验应用代码中,定义了函数dac_creat_sin_buf(),该函数用于生成正弦波数据,并保存至数组g_dac_sin_buf中。在完成DAC初始化后,便生成了一组正弦波数据,并调用函数dac_dma_wave_enable()使能DAC Channel1输出指定的正弦波,随后便通过扫描到的不同按键值,输出不同频率的正弦波。
39.3 下载验证 在完成编译和烧录操作后,便可分别按下KEY0按键和WKUP按键控制DAC Channel1输出不同类型的正弦波,DAC Channel1输出的正弦波可通过示波器观察PA4引脚(DAC Channel1输出引脚)看到。 |