基于STM32的8080总线数据捕获问题求助
STM32并行DMA捕获8080总线数据故障修复
问题描述
基于8080总线的LCD协议捕获需求,采用STM32并行输入+DMA方案,已将Timer2通道1配置为IOWRD(PA0)下降沿触发源,能正常捕获IOWR下降沿中断,但DMA始终无法启动,目标是将GPIOD端口数据复制到内存缓冲区后解析处理。
核心问题分析
- DMA方向配置错误:当前DMA初始化设置为
DMA_MEMORY_TO_MEMORY,实际需求是从外设(GPIOD->IDR)读取数据到内存,需改为DMA_PERIPH_TO_MEMORY - DMA触发逻辑错误:当前在TIM中断中手动调用
HAL_DMA_Start_IT,未利用TIM硬件触发DMA的机制,应该配置TIM捕获事件直接触发DMA请求 - 回调函数逻辑混乱:存在重复的DMA完成回调函数,且回调中判断的DMA流与实际使用的DMA2_Stream2不匹配
- GPIOD输入初始化缺失:作为总线数据输入端口,GPIOD未配置为输入模式
- TIM DMA请求未开启:未启用TIM2通道1的DMA捕获请求
修正后的代码实现
1. TIM2初始化修正(添加DMA触发与GPIOD配置)
static void MX_TIM2_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; TIM_ClockConfigTypeDef sClockSourceConfig; TIM_MasterConfigTypeDef sMasterConfig; TIM_IC_InitTypeDef sConfigIC; /* 外设时钟使能 */ __TIM2_CLK_ENABLE(); __GPIOD_CLK_ENABLE(); /* 外设中断初始化 */ HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM2_IRQn); /**TIM2 GPIO配置 PA0/WKUP ------> TIM2_CH1 */ GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF1_TIM2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /**GPIOD配置为输入模式(总线数据/控制信号) PD0-PD10 ------> 8080总线数据/控制信号 */ GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); htim2.Instance = TIM2; htim2.Init.Prescaler = 0; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 0xFFFFFFFF; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim2); sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig); HAL_TIM_IC_Init(&htim2); sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig); /* 输入捕获配置 */ sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; sConfigIC.ICFilter = 0; HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1); // 开启TIM2通道1的DMA捕获请求 __HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_CC1); }
2. DMA初始化修正(修改传输方向)
static void MX_DMA_Init(void) { __HAL_RCC_DMA2_CLK_ENABLE(); hdma_tim2_ch1.Instance = DMA2_Stream2; hdma_tim2_ch1.Init.Channel = DMA_CHANNEL_6; // 修改为外设到内存方向 hdma_tim2_ch1.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_tim2_ch1.Init.PeriphInc = DMA_PINC_DISABLE; hdma_tim2_ch1.Init.MemInc = DMA_MINC_ENABLE; hdma_tim2_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; // GPIOD->IDR为32位寄存器 hdma_tim2_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; hdma_tim2_ch1.Init.Mode = DMA_NORMAL; hdma_tim2_ch1.Init.Priority = DMA_PRIORITY_HIGH; hdma_tim2_ch1.Init.FIFOMode = DMA_FIFOMODE_DISABLE; __HAL_LINKDMA(&htim2, hdma[TIM_DMA_ID_CC1], hdma_tim2_ch1); if (HAL_DMA_Init(htim2.hdma[TIM_DMA_ID_CC1]) != HAL_OK) { Error_Handler(); } HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 1); HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); // 提前启动DMA,等待TIM硬件触发 HAL_DMA_Start_IT(&hdma_tim2_ch1, (uint32_t)&(GPIOD->IDR), (uint32_t)buffer, DMA_BUFFER_SIZE); }
3. 中断与回调函数修正
// TIM2中断处理:仅处理捕获状态,无需手动启动DMA void TIM2_IRQHandler(void) { HAL_TIM_IRQHandler(&htim2); } // DMA完成统一回调函数 void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma) { if (hdma->Instance == DMA2_Stream2) { uint8_t A1; uint8_t CS1; uint8_t CS2; uint8_t data_byte = 0; // 提取低8位数据 data_byte = (uint8_t)(buffer[0] & 0xFF); // 提取控制信号 A1 = (buffer[0] >> 8) & 0x01; CS1 = (buffer[0] >> 9) & 0x01; CS2 = (buffer[0] >> 10) & 0x01; // 解析LCD命令/数据 if ((!CS1) && (!A1)) { LCD1_write_cmd(data_byte); } if ((!CS1) && (A1)) { LCD1_write_dat(data_byte); } if ((!CS2) && (!A1)) { LCD2_write_cmd(data_byte); } if ((!CS2) && (A1)) { LCD2_write_dat(data_byte); } // 重新启动DMA,等待下一次触发 HAL_DMA_Start_IT(&hdma_tim2_ch1, (uint32_t)&(GPIOD->IDR), (uint32_t)buffer, DMA_BUFFER_SIZE); } } // DMA中断处理函数 void DMA2_Stream2_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_tim2_ch1); }
关键修正说明
- 硬件触发DMA:通过
__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_CC1)开启TIM捕获事件触发DMA,无需在中断中手动启动 - DMA方向修正:确保数据从GPIOD外设寄存器传输到内存缓冲区
- GPIOD输入配置:添加总线数据端口的输入模式初始化
- 回调函数统一:合并重复的回调逻辑,确保与实际使用的DMA流匹配,并在回调完成后重新启动DMA以持续捕获数据
内容的提问来源于stack exchange,提问作者Andre Ahmed
相关产品推荐
相关产品推荐

