用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/UitbreidenOS/UitKit --skill embedded命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Guidelines and instructions for Agent execution state rollback rules
Guidelines and instructions for Agent execution step counters limits
Guidelines and instructions for Agent execution timeout limits setups
基于 SOC 职业分类
正在显示 SKILL.md
| name | embedded |
| description | Develop embedded systems firmware and hardware interfaces with Claude Code |
Writing firmware for microcontrollers (STM32, ESP32, nRF52, RP2040), designing FreeRTOS task architectures, implementing HAL peripheral drivers, writing interrupt service routines, configuring DMA transfers, optimizing code for memory-constrained devices, or debugging timing-sensitive issues in bare-metal or RTOS environments.
Linux-based embedded systems (Raspberry Pi, Yocto) where standard Linux programming applies. High-level IoT connectivity without firmware concerns (use iot.md). FPGA HDL design. General C/C++ application development on desktop hardware. Scripting for embedded Linux devices where Python/shell is appropriate.
Understanding the linker sections is essential for debugging hard faults and sizing firmware:
Flash (read-only, persistent):
.text — compiled machine code
.rodata — read-only constants (const char*, lookup tables)
.data — initial values for initialized globals (copied to RAM at startup)
RAM (read-write, volatile):
.data — initialized globals (copied from flash at startup)
.bss — uninitialized globals (zero-filled at startup)
.heap — grows upward (malloc region)
[gap]
.stack — grows downward from top of RAM
Hard fault on cortex-M → check PSP/MSP for stack overflow into heap
Calculate stack usage with FreeRTOS uxTaskGetStackHighWaterMark() to find the minimum stack headroom observed since task creation. Set task stacks 20-30% larger than measured peak.
Check firmware size before flashing:
arm-none-eabi-size build/firmware.elf
# text: flash usage | data: initialized globals | bss: uninitialized globals
volatile for Hardware RegistersThe compiler is allowed to cache register reads in CPU registers and eliminate repeated loads it deems "redundant." volatile prevents this:
// Wrong — compiler may read USART_SR once and cache it
while (!(USART1->SR & USART_SR_TXE));
// Correct — USART1->SR is volatile, re-read each loop iteration
// (CMSIS headers already define register structs as volatile)
while (!(USART1->SR & USART_SR_TXE));
// Shared flag between ISR and main loop — must be volatile
volatile bool g_data_ready = false;
void USART1_IRQHandler(void) {
g_data_ready = true; // set in ISR
}
void main_loop(void) {
if (g_data_ready) { // read in main — volatile prevents caching
process_data();
g_data_ready = false;
}
}
For atomic access to multi-byte variables shared with ISRs, disable interrupts around the critical section:
uint32_t read_counter_atomic(void) {
__disable_irq();
uint32_t val = g_counter;
__enable_irq();
return val;
}
ISRs run with preemption disabled (or at elevated priority on Cortex-M). Violations cause priority inversion, missed deadlines, and hard faults:
vTaskDelay, HAL_Delay, osDelay)malloc, pvPortMalloc — can deadlock if heap mutex is held)printf (internally uses HAL blocking delays)xQueueSend — use xQueueSendFromISR with BaseType_t pxHigherPriorityTaskWokenstatic QueueHandle_t g_sensor_queue;
void SPI1_IRQHandler(void) {
BaseType_t higher_prio_task_woken = pdFALSE;
SensorReading reading = read_dma_buffer();
xQueueSendFromISR(g_sensor_queue, &reading, &higher_prio_task_woken);
// Yield to higher priority task immediately if it was unblocked
portYIELD_FROM_ISR(higher_prio_task_woken);
}
Task priorities: higher number = higher priority on most ports. Assign carefully:
Priority 5 (highest) — critical real-time tasks (motor control, safety)
Priority 4 — sensor acquisition
Priority 3 — data processing
Priority 2 — communication (UART, BLE, WiFi)
Priority 1 — logging, housekeeping
Priority 0 — idle task (never block, never starve)
Queue for ISR-to-task data transfer:
void sensor_task(void *params) {
SensorReading reading;
for (;;) {
// Block until ISR sends data — no CPU burn
if (xQueueReceive(g_sensor_queue, &reading, pdMS_TO_TICKS(100)) == pdTRUE) {
process_reading(&reading);
} else {
// Timeout — sensor may have failed, log warning
log_warn("Sensor timeout");
}
}
}
Mutex for shared resource protection:
static SemaphoreHandle_t g_spi_mutex;
bool spi_transceive(uint8_t *tx, uint8_t *rx, size_t len) {
if (xSemaphoreTake(g_spi_mutex, pdMS_TO_TICKS(50)) != pdTRUE) {
return false; // timeout — another task holds bus
}
HAL_SPI_TransmitReceive(&hspi1, tx, rx, len, HAL_MAX_DELAY);
xSemaphoreGive(g_spi_mutex);
return true;
}
Never call vTaskDelay(0) in a loop to yield — use taskYIELD() for an explicit yield without a time delay.
DMA transfers data between peripheral and memory without CPU involvement. Use for SPI, UART, ADC when transfer size > 4 bytes:
// ADC with DMA — continuous conversion, no CPU involvement
void adc_dma_init(void) {
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DMAContinuousRequests = ENABLE;
HAL_ADC_Init(&hadc1);
// DMA callback fires when buffer is full
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)g_adc_buffer, ADC_BUFFER_SIZE);
}
// Double-buffering — process first half while DMA fills second half
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc) {
// Process g_adc_buffer[0..HALF]
process_adc_samples(g_adc_buffer, ADC_BUFFER_SIZE / 2);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
// Process g_adc_buffer[HALF..END]
process_adc_samples(&g_adc_buffer[ADC_BUFFER_SIZE / 2], ADC_BUFFER_SIZE / 2);
}
SPI DMA for display or sensor burst reads — start transfer, yield task, resume in callback:
static SemaphoreHandle_t g_spi_dma_done;
void spi_dma_send(uint8_t *data, size_t len) {
HAL_SPI_Transmit_DMA(&hspi1, data, len);
xSemaphoreTake(g_spi_dma_done, portMAX_DELAY); // block until DMA complete
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
BaseType_t woken = pdFALSE;
xSemaphoreGiveFromISR(g_spi_dma_done, &woken);
portYIELD_FROM_ISR(woken);
}
Avoid floating-point on MCUs without FPU (Cortex-M0, M0+). Q16.16 format: upper 16 bits = integer part, lower 16 bits = fractional part.
typedef int32_t fixed16_t; // Q16.16
#define FLOAT_TO_FIXED(x) ((fixed16_t)((x) * 65536.0f))
#define FIXED_TO_FLOAT(x) ((float)(x) / 65536.0f)
#define FIXED_MUL(a, b) ((fixed16_t)(((int64_t)(a) * (b)) >> 16))
#define FIXED_DIV(a, b) ((fixed16_t)(((int64_t)(a) << 16) / (b)))
// PID controller in fixed-point
fixed16_t pid_update(PIDController *pid, fixed16_t setpoint, fixed16_t measured) {
fixed16_t error = setpoint - measured;
pid->integral += error;
fixed16_t derivative = error - pid->prev_error;
pid->prev_error = error;
return FIXED_MUL(pid->kp, error)
+ FIXED_MUL(pid->ki, pid->integral)
+ FIXED_MUL(pid->kd, derivative);
}
The watchdog resets the MCU if the main loop or critical task stops feeding it — essential for unattended deployments:
// Initialize independent watchdog — 4s timeout
void watchdog_init(void) {
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64;
hiwdg.Init.Reload = 2000; // 2000 * (64/32000Hz) = 4 seconds
HAL_IWDG_Init(&hiwdg);
}
// Feed in main loop or highest-priority task
void watchdog_feed(void) {
HAL_IWDG_Refresh(&hiwdg);
}
// In FreeRTOS: create a watchdog task that blocks on a flag set by all critical tasks
void watchdog_task(void *params) {
for (;;) {
// Wait for all critical tasks to signal alive
xEventGroupWaitBits(g_alive_bits, ALL_TASKS_ALIVE_MASK,
pdTRUE, pdTRUE, pdMS_TO_TICKS(2000));
watchdog_feed();
}
}
Design FreeRTOS firmware for STM32F4 reading temperature/pressure from BMP388 via SPI DMA, processing in a task, outputting formatted readings over UART:
spi_dma_send and spi_dma_receive use DMA with a semaphore signaled in HAL_SPI_TxRxCpltCallback. Sensor driver calls these — zero CPU cycles during transfer.EXTI interrupt fires on BMP388 data-ready pin. ISR calls xQueueSendFromISR with a trigger token into g_sensor_queue. No processing in ISR.xQueueReceive(g_sensor_queue). On trigger, initiates SPI DMA read, blocks on DMA semaphore, converts raw ADC values using fixed-point Q16.16 math (no FPU needed on M4 in this config for portability), posts result to g_result_queue.g_result_queue, formats reading as binary frame, calls HAL_UART_Transmit_DMA.