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




