You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

STM32F103C8 ADC-DMA-UART传输异常及按键LED功能故障排查求助

Hey there, let's dive into the issues in your code that are causing functionality to break after adding ADC-DMA-UART logic. I've spotted several key problems, along with practical fixes to get everything working again:

1. Critical Blocking Issue in Button Interrupt Callback

Your button callback uses HAL_Delay(100) inside a loop, which is a major problem. HAL_Delay() relies on the SysTick interrupt to increment the uwTick counter. If your EXTI button interrupt has a priority equal to or higher than SysTick, the SysTick interrupt can't preempt the EXTI callback. This means uwTick stops updating, and HAL_Delay() will hang indefinitely—freezing your entire system.

Fix: Replace the blocking delay with a non-blocking approach using a software tick counter (no more delays in interrupt handlers).

2. Incorrect DMA Transfer Length for USART

In your HAL_ADC_ConvCpltCallback, you pass sizeof(buffer) as the transfer length to HAL_DMA_Start_IT(). Since buffer is an array of uint16_t (2 bytes each), sizeof(buffer) equals 10 bytes—but the DMA transfer count parameter expects the number of data items, not bytes. For 5 uint16_t values, you need to pass 5 instead of sizeof(buffer).

Additionally, don't manually set huart2.Instance->CR3 |= USART_CR3_DMAT; use the HAL API HAL_UART_Transmit_DMA() instead—it handles enabling the DMAT bit correctly and manages USART DMA state automatically.

3. Missing ADC DMA Re-Start (For Single Conversion Mode)

If your ADC is configured for single conversion mode (not circular), HAL_ADC_Start_DMA() will only trigger the callback once. After the first transfer, the ADC stops, so you won't get any more readings. You need to re-start the ADC DMA inside the callback to keep data flowing.

4. Potential DMA Resource Conflict

Starting a USART DMA transfer without checking if the previous transfer is complete can cause channel conflicts. Always ensure the USART DMA channel is idle before starting a new transfer.


Fixed Code Snippets

Here's how to adjust your code to resolve these issues:

1. Non-Blocking Button-Triggered LED Blink

First, add global state variables outside main():

volatile uint8_t led_fast_blink_toggles = 0;
volatile uint32_t last_blink_tick = 0;

Modify your main loop to handle blinking without blocking:

while (1) {
    // Default slow blink if no fast blink is active
    if (led_fast_blink_toggles == 0) {
        HAL_GPIO_TogglePin(GPIOB, LED_Pin);
        HAL_Delay(500);
    } else {
        // Non-blocking fast blink (100ms interval)
        if (HAL_GetTick() - last_blink_tick >= 100) {
            HAL_GPIO_TogglePin(GPIOB, LED_Pin);
            last_blink_tick = HAL_GetTick();
            led_fast_blink_toggles--;
        }
    }
}

Update the button EXTI callback to only set the blink state (no blocking code):

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
    if(GPIO_Pin == BT_Pin){
        // Start fast blink only if not already running
        if (led_fast_blink_toggles == 0) {
            led_fast_blink_toggles = 40; // 20 full flashes (on/off) = 40 toggles
            last_blink_tick = HAL_GetTick();
        }
    }
}

2. Fixed ADC-DMA-UART Callback

// ADC缓冲区
uint16_t buffer[5];

int main(void) {
    // ... (your existing initialization code)

    // Configure ADC for circular DMA (recommended for continuous readings)
    hadc1.Init.DMAContinuousRequests = ENABLE;
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)buffer, 5);

    // Remove manual USART CR3 modification
    // huart2.Instance->CR3 |= USART_CR3_DMAT;
}

// ADC回调函数 - 缓冲区满时传输至UART
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
    if (hadc->Instance == ADC1) {
        // Wait for ongoing USART DMA transfer to finish
        while (__HAL_DMA_GET_FLAG(&hdma_usart2_tx, DMA_FLAG_TCIF) == RESET);
        __HAL_DMA_CLEAR_FLAG(&hdma_usart2_tx, DMA_FLAG_TCIF);

        // Use HAL's UART DMA transmit API
        HAL_UART_Transmit_DMA(&huart2, (uint8_t*)buffer, sizeof(buffer));

        // If using single ADC mode (not circular), re-start DMA here:
        // HAL_ADC_Start_DMA(&hadc1, (uint32_t*)buffer, 5);
    }
}

Additional Tips

  • In CubeMX, set your ADC's DMA mode to Circular to avoid manually re-starting the ADC in the callback.
  • Verify interrupt priorities: Ensure SysTick has a higher priority (lower numerical value) than your EXTI interrupt to keep system ticks updating reliably.
  • Always avoid blocking functions like HAL_Delay() inside interrupt handlers—they can easily freeze your system.

内容的提问来源于stack exchange,提问作者ussrback

火山引擎 最新活动