协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
embedded-stm32【开发与工程】Best practices for embedded C/C++ development on STM32 microcontrollers using the HAL, covering peripherals, DMA, interrupts, memory constraints, and hardware-focused testing. Use when writing STM32 HAL code, configuring peripherals generated by STM32CubeMX, working with interrupts or DMA, debugging with SWD/JTAG, managing memory-constrained firmware…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
This skill covers firmware development for STM32 microcontrollers using the STM32 HAL, including project structure, peripheral and interrupt handling, memory and timing constraints, and testing strategies for hardware-focused code.
.ioc file; generate the HAL initialization code.USER CODE BEGIN/END blocks; put application logic in separate files.HAL_*_Init() calls in main()/MX_*_Init() and avoid ad hoc reconfiguration elsewhere in the code.HAL_*_Callback functions, EXTI/DMA/timer IRQ handlers) short; set flags or push to a queue and defer real work to the main loop or an RTOS task.Core/Src/main.c, Drivers/) from hand-written application code so regenerating with CubeMX doesn't clobber custom logic — only edit inside /* USER CODE BEGIN */ ... /* USER CODE END */ markers in generated files.motor_driver.h with motor_set_speed()) so application logic can be unit-tested on a host build without real peripherals.SYSCLK, HCLK, PCLK1/PCLK2 and their max rates), pin mappings, peripheral ownership, and interrupt priority assignments in a single reference (README or header comments) — this is the first thing a debugging session needs.HAL_*_Init()/HAL_*_MspInit() calls or ad hoc register writes across multiple files.HAL_OK, HAL_ERROR, HAL_BUSY, HAL_TIMEOUT) and handle timeout/error cases explicitly — a silently ignored HAL_TIMEOUT from HAL_UART_Transmit is a classic source of "it works on my desk" bugs.HAL_UART_Transmit, HAL_I2C_Master_Receive without _IT/_DMA suffix) out of time-critical paths like control loops or ISRs.volatile only for memory shared with an ISR or memory-mapped hardware registers; volatile is not a substitute for a proper memory barrier or critical section when data is shared between contexts./* USER CODE BEGIN Includes */
#include "main.h"
#include <string.h>
#define RX_BUF_SIZE 128
static uint8_t rx_buf[RX_BUF_SIZE];
static volatile uint8_t rx_ready = 0;
static volatile uint16_t rx_len = 0;
extern UART_HandleTypeDef huart2;
extern DMA_HandleTypeDef hdma_usart2_rx;
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */
void app_uart_start_receive(void)
{
/* Enable idle-line interrupt so a packet of unknown length completes
* the transfer without waiting for the buffer to fill. */
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
if (HAL_UART_Receive_DMA(&huart2, rx_buf, RX_BUF_SIZE) != HAL_OK) {
Error_Handler();
}
}
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void USART2_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
HAL_UART_DMAStop(&huart2);
rx_len = RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
rx_ready = 1; /* Deferred: main loop processes the packet. */
/* Re-arm for the next packet. */
HAL_UART_Receive_DMA(&huart2, rx_buf, RX_BUF_SIZE);
return;
}
HAL_UART_IRQHandler(&huart2);
}
/* USER CODE END 4 */
/* Main loop excerpt: heavy work deferred out of the ISR. */
void app_main_loop(void)
{
if (rx_ready) {
rx_ready = 0;
uint16_t len = rx_len;
/* Copy out or parse rx_buf[0..len) here. Do NOT touch rx_buf
* again until this point, since DMA may already be refilling it. */
(void)len;
}
}
__disable_irq()/__enable_irq(), or taskENTER_CRITICAL() under an RTOS), atomics, or lock-free queues — never assume a multi-byte read/write is atomic.malloc/new) inside interrupt handlers; allocation is neither deterministic nor guaranteed reentrant-safe.NVIC_SetPriority) — a mis-prioritized interrupt can starve time-critical peripherals or violate FreeRTOS's configMAX_SYSCALL_INTERRUPT_PRIORITY constraint.uxTaskGetStackHighWaterMark() under FreeRTOS) — stack overflow on embedded targets typically corrupts silently.const so the linker places them in flash instead of consuming scarce RAM.uint8_t, int32_t, uint32_t) for anything hardware-facing (register values, protocol fields, buffer sizes) instead of int/long, whose width isn't guaranteed.assert() or a custom configASSERT-style macro) for impossible hardware states in debug builds, compiled out in release builds if code size is tight.HardFault_Handler, etc.) useful: capture the reset reason (RCC->CSR), relevant fault status registers (SCB->CFSR, SCB->HFSR), and firmware build version/hash so a field crash is diagnosable after the fact.USER CODE blocks, so the next regeneration silently deletes the changes.