使用STM32H743的TIM5触发DMA来驱动GPIO

使用STM32H743的TIM5触发DMA来驱动GPIO(Bit-Banging)是一个常见的应用。下面我将为您提供详细的步骤和代码实现。

1. 功能概述
目标是通过TIM5的更新事件(溢出)触发DMA,DMA将数据(通常是0x00002000 置位 PB13,0x00000000 清零 PB13)传输到GPIOB->BSRRL寄存器,从而控制PB13引脚的电平,而无需CPU介入。传输完成后,在DMA传输完成中断中关闭TIM5和DMA流。
关键点:
GPIOB->BSRRL是置位寄存器。写入1 << 13会置位PB13,写入0则无效果。清零操作需要通过GPIOB->BSRRH寄存器(写入1 << 13)来实现。为了简化操作,我们通常使用GPIOB->BSRR寄存器,它是一个32位寄存器,高16位用于清零(BSRRH),低16位用于置位(BSRRL)。
置位 PB13: GPIOB->BSRR = (1 << 13); // 写低16位
清零 PB13: GPIOB->BSRR = (1 << (13 + 16)); // 写高16位
因此,我们的DMA目标地址将是(uint32_t)&GPIOB->BSRR。
DMA需要传输的数据是:
0x00002000 (即 1 << 13) 来置位PB13(输出高电平)。
0x20000000 (即 1 << (13 + 16)) 来清零PB13(输出低电平)。
2. 硬件连接
MCU: STM32H743IIT6
GPIO: PB13
定时器: TIM5
DMA: 使用TIM5_UP请求对应的DMA流/通道。查阅数据手册,TIM5_UP通常映射到DMA1 Stream 2或DMA1 Stream 6(Channel 6)。我们以DMA1 Stream 2为例。
3. 软件配置步骤
a. GPIO配置
将PB13配置为推挽输出,高速模式。
b. 定时器TIM5配置
时钟源:内部时钟
分频器(PSC):根据你的时钟频率和所需更新频率计算。
自动重载值(ARR):决定更新事件的频率。
触发DMA:使能更新事件DMA请求。
c. DMA配置
流:DMA2_Stream0
通道:DMA_CHANNEL_0 (TIM5_UP)
方向:存储器到外设
源地址:存储波形数据的数组地址
目标地址:(uint32_t)&GPIOB->BSRR
数据宽度:字(32位)
传输模式:单次传输(或循环传输,但我们最后要关闭),并在传输完成后产生中断。
d. NVIC配置
使能DMA2_Stream0的全局中断。
4. 实现代码
`c
void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma);

void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

__HAL_RCC_GPIOB_CLK_ENABLE();  // 启用 GPIOB 时钟

// 配置 PB13 为推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}
TIM_HandleTypeDef htim5;

void MX_TIM5_Init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
__HAL_RCC_TIM5_CLK_ENABLE();
htim5.Instance = TIM5;
htim5.Init.Prescaler = 999; // 分频系数(假设时钟 = 100MHz,则定时器时钟 = 100kHz)
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
htim5.Init.Period = 999; // 自动重装载值(ARR),即 1000 个计数 = 10ms 100hz
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_Init(&htim5);

// 配置时钟源
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig);

// 配置主模式(Master Mode),使更新事件(UEV)触发 DMA
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;  // 更新事件触发 DMA
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig);

}
DMA_HandleTypeDef hdma_tim5_up;

void MX_DMA_Init(void) {
__HAL_RCC_DMA2_CLK_ENABLE(); // TIM5_UP 默认使用 DMA2
// __HAL_RCC_DMA1_CLK_ENABLE();
// 配置 DMA2 Stream0(TIM5_UP 默认使用 DMA2 Stream0)
hdma_tim5_up.Instance = DMA2_Stream0;
hdma_tim5_up.Init.Request = DMA_REQUEST_TIM5_UP; // TIM5_UP 默认使用 DMA 通道 6
hdma_tim5_up.Init.Direction = DMA_MEMORY_TO_PERIPH; // 内存 → 外设
hdma_tim5_up.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不递增
hdma_tim5_up.Init.MemInc = DMA_MINC_ENABLE; // 内存地址递增
hdma_tim5_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; // 32位对齐
hdma_tim5_up.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; // 32位对齐
hdma_tim5_up.Init.Mode = DMA_CIRCULAR; // 普通模式(单次)
hdma_tim5_up.Init.Priority = DMA_PRIORITY_HIGH; // 高优先级
hdma_tim5_up.Init.FIFOMode = DMA_FIFOMODE_DISABLE; // 禁用 FIFO
hdma_tim5_up.XferCpltCallback = HAL_DMA_XferCpltCallback;
HAL_DMA_Init(&hdma_tim5_up);

  __HAL_DMA_ENABLE_IT(&hdma_tim5_up, DMA_IT_TC);
// 关联 DMA 到 TIM5 更新事件
__HAL_LINKDMA(&htim5, hdma[TIM_DMA_ID_UPDATE], hdma_tim5_up);

// 启用 DMA 中断(可选)
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

}
// DMA传输完成中断服务函数
void DMA2_Stream0_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_tim5_up);
// HAL_TIM_Base_Stop(&htim5);
// GPIOB->BSRR = (1UL << (13 + 16));
}

// DMA传输完成回调函数
void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma) {
if (hdma == &hdma_tim5_up) {
// 传输完成,关闭定时器和DMA
HAL_TIM_Base_Stop(&htim5);
// DMA已经在单次模式下自动停止,这里可以不再操作
// 也可以调用 HAL_DMA_Stop(&hdma_tim5_up); 确保停止

    // 可选:将PB13设置为某个确定状态,例如低电平
    GPIOB->BSRR = (1UL << (13 + 16));
}

}
//GPIO_BSRR_BS13 1
//GPIO_BSRR_BR13 0 <<29
uint32_t gpio_data[] = {GPIO_BSRR_BS13, GPIO_BSRR_BR13,GPIO_BSRR_BS13,GPIO_BSRR_BS13, GPIO_BSRR_BR13}; // 设置 PB13 高电平和低电平
int main(void)
{
uint8_t len;
uint16_t times = 0;

sys_cache_enable();                         /* 打开L1-Cache */
HAL_Init();                                 /* 初始化HAL库 */
sys_stm32_clock_init(160, 5, 2, 4);         /* 设置时钟, 400Mhz */
delay_init(400);                            /* 延时初始化 */
usart_init(115200);                         /* 初始化USART */
led_init();                                 /* 初始化LED */
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM5_Init();

// 启动定时器 + DMA 传输
//HAL_TIM_Base_Start_DMA(&htim5, gpio_data, 4);

	HAL_DMA_Start(&hdma_tim5_up, (uint32_t)gpio_data, (uint32_t)&GPIOB->BSRR, sizeof(gpio_data)/sizeof(gpio_data[0]));
	__HAL_TIM_ENABLE_DMA(&htim5, TIM_DMA_UPDATE);
	HAL_TIM_Base_Start(&htim5);
 }

}
`

6.实现效果
image

posted @ 2025-08-20 14:47  李尾巴  阅读(194)  评论(0)    收藏  举报