第34章 高级定时器-DMA控制实验
第三十四章 高级定时器-DMA控制实验
1. 硬件设计
TIM和DMA均输入内部资源,无需硬件设计,还使用了按键,已经分析过了
2. 软件设计
2.1 编程大纲
-
TIM和DMA相关参数宏定义
-
TIM GPIO及DMA Mode配置
-
TIM Mode配置
-
主函数测
2.2 代码分析
2.2.1 相关参数宏定义
#ifndef __ATIM_H
#define __ATIM_H
#include "stm32f10x.h"
/* TIM1输出比较通道1 */
#define ATIMx TIM1
#define ATIM_CLK RCC_APB2Periph_TIM1
#define ATIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define ATIM_CH1_GPIO_PORT GPIOA
#define ATIM_CH1_GPIO_PIN GPIO_Pin_8
/* DMA配置*/
#define ATIM_DMA_ADDRESS TIM1_BASE+0x34
#define ATIM_DMA_BufferSize 3
#define ATIM_DMA_CLK RCC_AHBPeriph_DMA1
#define ATIM_DMA_Channel DMA1_Channel2
void ATIMx_Init(void);
#endif /* __ATIM_H */
2.2.2 高级定时器初始化,DMA初始化
static void ATIM_GPIO_Init(void)
{
// CH1 GPIO Configuration
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(ATIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ATIM_CH1_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ATIM_CH1_GPIO_PORT, &GPIO_InitStructure);
// DMA Configuration
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(ATIM_DMA_CLK, ENABLE);
DMA_DeInit(ATIM_DMA_Channel);
// DMA配置
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(ATIM_DMA_ADDRESS); // 设置DMA源地址:TIM的CCR寄存器
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ATIM_Buffer; // 设置DMA目的地址:ATIM_Buffer
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 设置DMA传输方向:从内存到外设
DMA_InitStructure.DMA_BufferSize = ATIM_DMA_BufferSize; // 设置DMA传输数据长度
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(ATIM_DMA_Channel, &DMA_InitStructure);
}
// DMA初始化
// DMA时钟使能
RCC_AHBPeriphClockCmd(TIM_DMA_CLK, ENABLE);
DMA_DeInit(TIM_DMA_STREAM);
// 设置DMA源地址:TIM的CCR寄存器地址
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(TIM_DMA_ADDRESS) ;
// 内存地址(要传输的变量的指针)
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)aSRC_Buffer;
// 方向:从内存到外设
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
// 传输数目
DMA_InitStructure.DMA_BufferSize = TIM_DMA_BUFSIZE; // 3
// 外设地址不增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// 内存地址自增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
// 外设数据单位
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
// 内存数据单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
// DMA模式,一次或者循环模式
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// 优先级:中
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
// 禁止内存到内存的传输
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable ;
// 配置DMA通道
DMA_Init(TIM_DMA_STREAM, &DMA_InitStructure);
}
2.2.3 高级定时器模式配置
static void ATIM_Mode_Init(void)
{
// 时基结构初始化
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB2PeriphClockCmd(ATIM_CLK, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 1000-1;
TIM_TimeBaseStructure.TIM_Prescaler = 7200-1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(ATIMx, &TIM_TimeBaseStructure);
// 输出PWM模式配置
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(ATIMx, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(ATIMx, TIM_OCPreload_Enable);
TIM_Cmd(ATIMx, ENABLE);
// DMA使能
DMA_Cmd(ATIM_DMA_Channel, ENABLE);
TIM_DMACmd(ATIMx, TIM_DMA_CC1, ENABLE);
TIM_CtrlPWMOutputs(ATIMx, ENABLE); // 使能PWM输出
}
2.2.4 主函数
#include "stm32f10x.h"
#include "atim.h"
#include "key.h"
extern uint16_t ATIM_Buffer[3];
int main(void)
{
KEY_GPIO_Init();
ATIMx_Init();
while(1)
{
if(KEY_Scan(KEY1_GPIO, KEY1_GPIO_PIN) == KEY_ON)
{
ATIM_Buffer[0] = 800;
ATIM_Buffer[1] = 800;
ATIM_Buffer[2] = 100;
}
if(KEY_Scan(KEY2_GPIO, KEY2_GPIO_PIN) == KEY_ON)
{
ATIM_Buffer[0] = 300;
ATIM_Buffer[1] = 300;
ATIM_Buffer[2] = 100;
}
}
}
检查按键按下后,修改数组的值,从而改变PWM波形
3. 小结
3.1 实验目标
- 配置高级定时器生成PWM信号。
- 使用DMA来自动更新PWM的占空比。
- 按下不同的按键来改变PWM信号的占空比。
3.2 硬件要求
- STM32微控制器(例如STM32F4系列)。
- 两个按键,连接到GPIO端口。
- 一个LED或示波器,连接到PWM输出引脚以观察PWM信号。
3.3 实验步骤
1. 系统时钟配置
确保系统时钟配置正确,以提供足够的时钟给定时器和DMA。
2. GPIO配置
配置用于PWM输出的GPIO引脚及按键引脚。
3. 定时器配置
配置高级定时器以生成PWM信号。
4. DMA配置
配置DMA以在定时器更新事件时自动更新PWM的占空比。
5. 按键扫描
配置GPIO扫描按键状态,并根据按键状态改变PWM波形。
6. 主程序
实现主程序逻辑,管理按键输入和PWM波形更新。
3.4 代码示例
以下代码示例基于STM32F4系列微控制器,使用HAL库(硬件抽象层)来配置硬件资源。根据你的具体微控制器型号和开发环境,你可能需要调整代码。
#include "stm32f4xx_hal.h"
// 定义按键状态
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_PIN_0
#define KEY2_GPIO_PORT GPIOA
#define KEY2_GPIO_PIN GPIO_PIN_1
// 定义PWM占空比数组
uint16_t aSRC_Buffer[3] = {1000, 1000, 100};
// 函数声明
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_TIM1_Init(void);
static void MX_NVIC_Init(void);
// 定时器和DMA句柄
TIM_HandleTypeDef htim1;
DMA_HandleTypeDef hdma_tim1_up;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM1_Init();
MX_NVIC_Init();
// 启动定时器和DMA
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t*)aSRC_Buffer, 3);
while (1)
{
if (HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == GPIO_PIN_SET)
{
// 按下按键1
aSRC_Buffer[0] = 1000;
aSRC_Buffer[1] = 1000;
aSRC_Buffer[2] = 100;
}
else if (HAL_GPIO_ReadPin(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == GPIO_PIN_SET)
{
// 按下按键2
aSRC_Buffer[0] = 300;
aSRC_Buffer[1] = 300;
aSRC_Buffer[2] = 100;
}
HAL_Delay(100); // 延时去抖动和减少CPU占用
}
}
// 定时器1初始化函数
static void MX_TIM1_Init(void)
{
TIM_OC_InitTypeDef sConfigOC = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 8399; // 根据时钟频率调整
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 9999; // 根据PWM频率调整
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
HAL_TIM_PWM_Init(&htim1);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始占空比
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
}
// DMA初始化函数
static void MX_DMA_Init(void)
{
__HAL_RCC_DMA2_CLK_ENABLE();
hdma_tim1_up.Instance = DMA2_Stream5;
hdma_tim1_up.Init.Channel = DMA_CHANNEL_6;
hdma_tim1_up.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim1_up.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim1_up.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim1_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_tim1_up.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_tim1_up.Init.Mode = DMA_CIRCULAR;
hdma_tim1_up.Init.Priority = DMA_PRIORITY_LOW;
hdma_tim1_up.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_tim1_up);
__HAL_LINKDMA(&htim1, hdma[TIM_DMA_ID_UPDATE], hdma_tim1_up);
}
// GPIO初始化函数
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1; // 配置按键引脚
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置PWM输出引脚
GPIO_InitStruct.Pin = GPIO_PIN_8; // 根据你的引脚配置
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// NVIC初始化函数
static void MX_NVIC_Init(void)
{
HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
}
// 时钟配置函数
void SystemClock_Config(void)
{
// 根据你的系统时钟设置代码
}
// DMA中断处理函数
void DMA2_Stream5_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_tim1_up);
}
3.5 代码解释
-
MX_TIM1_Init
: 初始化定时器1为PWM模式,设置PWM周期和初始占空比。 -
MX_DMA_Init
: 初始化DMA,设置内存到外设传输模式,配置为循环模式。 -
MX_GPIO_Init
: 配置GPIO引脚用于按键输入和PWM输出。 -
main
函数: 启动PWM和DMA,监控按键状态,并根据按键输入改变PWM占空比。每次修改aSRC_Buffer
后,DMA会自动更新PWM信号。 -
SystemClock_Config
: 配置系统时钟,根据你的实际硬件和时钟频率进行设置。
2024.9.14 第一次修订,后期不再维护
2025.1.28 修订代码
本文作者:hazy1k
本文链接:https://www.cnblogs.com/hazy1k/p/18413818
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步