使用STM32H743的TIM5觸發(fā)DMA來驅(qū)動(dòng)GPIO
使用STM32H743的TIM5觸發(fā)DMA來驅(qū)動(dòng)GPIO(Bit-Banging)是一個(gè)常見的應(yīng)用。下面我將為您提供詳細(xì)的步驟和代碼實(shí)現(xiàn)。
1. 功能概述
目標(biāo)是通過TIM5的更新事件(溢出)觸發(fā)DMA,DMA將數(shù)據(jù)(通常是0x00002000 置位 PB13,0x00000000 清零 PB13)傳輸?shù)紾PIOB->BSRRL寄存器,從而控制PB13引腳的電平,而無需CPU介入。傳輸完成后,在DMA傳輸完成中斷中關(guān)閉TIM5和DMA流。
關(guān)鍵點(diǎn):
GPIOB->BSRRL是置位寄存器。寫入1 << 13會(huì)置位PB13,寫入0則無效果。清零操作需要通過GPIOB->BSRRH寄存器(寫入1 << 13)來實(shí)現(xiàn)。為了簡(jiǎn)化操作,我們通常使用GPIOB->BSRR寄存器,它是一個(gè)32位寄存器,高16位用于清零(BSRRH),低16位用于置位(BSRRL)。
置位 PB13: GPIOB->BSRR = (1 << 13); // 寫低16位
清零 PB13: GPIOB->BSRR = (1 << (13 + 16)); // 寫高16位
因此,我們的DMA目標(biāo)地址將是(uint32_t)&GPIOB->BSRR。
DMA需要傳輸?shù)臄?shù)據(jù)是:
0x00002000 (即 1 << 13) 來置位PB13(輸出高電平)。
0x20000000 (即 1 << (13 + 16)) 來清零PB13(輸出低電平)。
2. 硬件連接
MCU: STM32H743IIT6
GPIO: PB13
定時(shí)器: TIM5
DMA: 使用TIM5_UP請(qǐng)求對(duì)應(yīng)的DMA流/通道。查閱數(shù)據(jù)手冊(cè),TIM5_UP通常映射到DMA1 Stream 2或DMA1 Stream 6(Channel 6)。我們以DMA1 Stream 2為例。
3. 軟件配置步驟
a. GPIO配置
將PB13配置為推挽輸出,高速模式。
b. 定時(shí)器TIM5配置
時(shí)鐘源:內(nèi)部時(shí)鐘
分頻器(PSC):根據(jù)你的時(shí)鐘頻率和所需更新頻率計(jì)算。
自動(dòng)重載值(ARR):決定更新事件的頻率。
觸發(fā)DMA:使能更新事件DMA請(qǐng)求。
c. DMA配置
流:DMA2_Stream0
通道:DMA_CHANNEL_0 (TIM5_UP)
方向:存儲(chǔ)器到外設(shè)
源地址:存儲(chǔ)波形數(shù)據(jù)的數(shù)組地址
目標(biāo)地址:(uint32_t)&GPIOB->BSRR
數(shù)據(jù)寬度:字(32位)
傳輸模式:?jiǎn)未蝹鬏敚ɑ蜓h(huán)傳輸,但我們最后要關(guān)閉),并在傳輸完成后產(chǎn)生中斷。
d. NVIC配置
使能DMA2_Stream0的全局中斷。
4. 實(shí)現(xiàn)代碼
`c
void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma);
void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE(); // 啟用 GPIOB 時(shí)鐘
// 配置 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; // 分頻系數(shù)(假設(shè)時(shí)鐘 = 100MHz,則定時(shí)器時(shí)鐘 = 100kHz)
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
htim5.Init.Period = 999; // 自動(dòng)重裝載值(ARR),即 1000 個(gè)計(jì)數(shù) = 10ms 100hz
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_Init(&htim5);
// 配置時(shí)鐘源
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig);
// 配置主模式(Master Mode),使更新事件(UEV)觸發(fā) DMA
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; // 更新事件觸發(fā) 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 默認(rèn)使用 DMA2
// __HAL_RCC_DMA1_CLK_ENABLE();
// 配置 DMA2 Stream0(TIM5_UP 默認(rèn)使用 DMA2 Stream0)
hdma_tim5_up.Instance = DMA2_Stream0;
hdma_tim5_up.Init.Request = DMA_REQUEST_TIM5_UP; // TIM5_UP 默認(rèn)使用 DMA 通道 6
hdma_tim5_up.Init.Direction = DMA_MEMORY_TO_PERIPH; // 內(nèi)存 → 外設(shè)
hdma_tim5_up.Init.PeriphInc = DMA_PINC_DISABLE; // 外設(shè)地址不遞增
hdma_tim5_up.Init.MemInc = DMA_MINC_ENABLE; // 內(nèi)存地址遞增
hdma_tim5_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; // 32位對(duì)齊
hdma_tim5_up.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; // 32位對(duì)齊
hdma_tim5_up.Init.Mode = DMA_CIRCULAR; // 普通模式(單次)
hdma_tim5_up.Init.Priority = DMA_PRIORITY_HIGH; // 高優(yōu)先級(jí)
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);
// 關(guān)聯(lián) 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傳輸完成中斷服務(wù)函數(shù)
void DMA2_Stream0_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_tim5_up);
// HAL_TIM_Base_Stop(&htim5);
// GPIOB->BSRR = (1UL << (13 + 16));
}
// DMA傳輸完成回調(diào)函數(shù)
void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma) {
if (hdma == &hdma_tim5_up) {
// 傳輸完成,關(guān)閉定時(shí)器和DMA
HAL_TIM_Base_Stop(&htim5);
// DMA已經(jīng)在單次模式下自動(dòng)停止,這里可以不再操作
// 也可以調(diào)用 HAL_DMA_Stop(&hdma_tim5_up); 確保停止
// 可選:將PB13設(shè)置為某個(gè)確定狀態(tài),例如低電平
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}; // 設(shè)置 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); /* 設(shè)置時(shí)鐘, 400Mhz */
delay_init(400); /* 延時(shí)初始化 */
usart_init(115200); /* 初始化USART */
led_init(); /* 初始化LED */
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM5_Init();
// 啟動(dòng)定時(shí)器 + 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.實(shí)現(xiàn)效果


浙公網(wǎng)安備 33010602011771號(hào)