You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于STM32的8080总线数据捕获问题求助

STM32并行DMA捕获8080总线数据故障修复

问题描述

基于8080总线的LCD协议捕获需求,采用STM32并行输入+DMA方案,已将Timer2通道1配置为IOWRD(PA0)下降沿触发源,能正常捕获IOWR下降沿中断,但DMA始终无法启动,目标是将GPIOD端口数据复制到内存缓冲区后解析处理。

核心问题分析

  1. DMA方向配置错误:当前DMA初始化设置为DMA_MEMORY_TO_MEMORY,实际需求是从外设(GPIOD->IDR)读取数据到内存,需改为DMA_PERIPH_TO_MEMORY
  2. DMA触发逻辑错误:当前在TIM中断中手动调用HAL_DMA_Start_IT,未利用TIM硬件触发DMA的机制,应该配置TIM捕获事件直接触发DMA请求
  3. 回调函数逻辑混乱:存在重复的DMA完成回调函数,且回调中判断的DMA流与实际使用的DMA2_Stream2不匹配
  4. GPIOD输入初始化缺失:作为总线数据输入端口,GPIOD未配置为输入模式
  5. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 20:00:57