Vibeship-spawner-skills embedded-systems

Embedded Systems Skill

install
source · Clone the upstream repo
git clone https://github.com/vibeforge1111/vibeship-spawner-skills
manifest: hardware/embedded-systems/skill.yaml
source content

Embedded Systems Skill

Microcontroller and embedded software development

id: embedded-systems name: Embedded Systems category: hardware complexity: advanced requires_skills:

  • test-architect

description: | Patterns for embedded software development including real-time systems, memory management, hardware abstraction, interrupt handling, and debugging techniques for resource-constrained environments.

patterns:

hardware_abstraction: name: Hardware Abstraction Layer description: Separate hardware-specific code from application logic pattern: | // hal/gpio.h - Hardware Abstraction Layer interface #ifndef GPIO_HAL_H #define GPIO_HAL_H

  #include <stdint.h>
  #include <stdbool.h>

  typedef enum {
      GPIO_MODE_INPUT,
      GPIO_MODE_OUTPUT,
      GPIO_MODE_ALTERNATE,
      GPIO_MODE_ANALOG
  } gpio_mode_t;

  typedef enum {
      GPIO_PULL_NONE,
      GPIO_PULL_UP,
      GPIO_PULL_DOWN
  } gpio_pull_t;

  typedef struct {
      void* port;          // Platform-specific port register
      uint8_t pin;
      gpio_mode_t mode;
      gpio_pull_t pull;
  } gpio_config_t;

  // HAL interface - platform-agnostic
  void gpio_init(const gpio_config_t* config);
  void gpio_write(const gpio_config_t* config, bool state);
  bool gpio_read(const gpio_config_t* config);
  void gpio_toggle(const gpio_config_t* config);

  #endif

  // hal/stm32/gpio_stm32.c - STM32 implementation
  #include "hal/gpio.h"
  #include "stm32f4xx_hal.h"

  void gpio_init(const gpio_config_t* config) {
      GPIO_InitTypeDef init = {0};
      init.Pin = (1 << config->pin);

      switch (config->mode) {
          case GPIO_MODE_INPUT:
              init.Mode = GPIO_MODE_INPUT;
              break;
          case GPIO_MODE_OUTPUT:
              init.Mode = GPIO_MODE_OUTPUT_PP;
              break;
          // ... other modes
      }

      switch (config->pull) {
          case GPIO_PULL_UP:
              init.Pull = GPIO_PULLUP;
              break;
          case GPIO_PULL_DOWN:
              init.Pull = GPIO_PULLDOWN;
              break;
          default:
              init.Pull = GPIO_NOPULL;
      }

      HAL_GPIO_Init((GPIO_TypeDef*)config->port, &init);
  }

  void gpio_write(const gpio_config_t* config, bool state) {
      HAL_GPIO_WritePin(
          (GPIO_TypeDef*)config->port,
          (1 << config->pin),
          state ? GPIO_PIN_SET : GPIO_PIN_RESET
      );
  }

  // Application code uses HAL - portable across platforms
  #include "hal/gpio.h"

  static const gpio_config_t led_config = {
      .port = GPIOA,
      .pin = 5,
      .mode = GPIO_MODE_OUTPUT,
      .pull = GPIO_PULL_NONE
  };

  void blink_led(void) {
      gpio_toggle(&led_config);
  }
why: "HAL enables code reuse and easier testing"

interrupt_handling: name: Interrupt Handling Best Practices description: Safe and efficient interrupt service routines critical: true pattern: | // Interrupt handling best practices

  #include <stdint.h>
  #include <stdbool.h>

  // Rule 1: Keep ISRs short
  // Do minimal work, set flag, handle in main loop

  volatile bool data_ready = false;
  volatile uint8_t rx_buffer[256];
  volatile uint8_t rx_head = 0;

  // ISR - minimal work
  void USART1_IRQHandler(void) {
      if (USART1->SR & USART_SR_RXNE) {
          // Just store data, don't process
          rx_buffer[rx_head++] = USART1->DR;
          rx_head &= 0xFF;  // Wrap (power of 2)
          data_ready = true;
      }
  }

  // Main loop handles processing
  void main_loop(void) {
      if (data_ready) {
          data_ready = false;
          process_received_data();  // Long operation OK here
      }
  }

  // Rule 2: Use volatile for shared variables
  // Compiler doesn't optimize away reads

  // WRONG
  bool flag = false;  // Compiler may cache this

  // CORRECT
  volatile bool flag = false;

  // Rule 3: Critical sections for multi-word operations
  typedef struct {
      uint32_t timestamp;
      int16_t value;
  } sensor_data_t;

  volatile sensor_data_t sensor_data;

  void update_sensor(uint32_t ts, int16_t val) {
      // Disable interrupts for atomic update
      uint32_t primask = __get_PRIMASK();
      __disable_irq();

      sensor_data.timestamp = ts;
      sensor_data.value = val;

      __set_PRIMASK(primask);  // Restore previous state
  }

  sensor_data_t read_sensor(void) {
      sensor_data_t copy;

      uint32_t primask = __get_PRIMASK();
      __disable_irq();

      copy = sensor_data;

      __set_PRIMASK(primask);

      return copy;
  }

  // Rule 4: Avoid dynamic memory in ISRs
  // No malloc, printf, or other non-reentrant functions

  // WRONG - in ISR
  void BAD_ISR(void) {
      char* msg = malloc(100);  // Can cause heap corruption
      printf("Data: %d\n", value);  // printf is not reentrant
  }
why: "Incorrect interrupt handling causes intermittent, hard-to-debug failures"

memory_management: name: Memory Management for Embedded description: Handle limited memory safely pattern: | // Memory management for constrained systems

  // 1. Static allocation - preferred for embedded
  #define MAX_TASKS 16
  static task_t task_pool[MAX_TASKS];
  static bool task_used[MAX_TASKS] = {false};

  task_t* task_alloc(void) {
      for (int i = 0; i < MAX_TASKS; i++) {
          if (!task_used[i]) {
              task_used[i] = true;
              memset(&task_pool[i], 0, sizeof(task_t));
              return &task_pool[i];
          }
      }
      return NULL;  // Pool exhausted
  }

  void task_free(task_t* task) {
      int idx = task - task_pool;
      if (idx >= 0 && idx < MAX_TASKS) {
          task_used[idx] = false;
      }
  }

  // 2. Ring buffer - no dynamic allocation
  typedef struct {
      uint8_t* buffer;
      size_t size;
      volatile size_t head;
      volatile size_t tail;
  } ring_buffer_t;

  static uint8_t uart_buffer_data[256];
  static ring_buffer_t uart_buffer = {
      .buffer = uart_buffer_data,
      .size = sizeof(uart_buffer_data),
      .head = 0,
      .tail = 0
  };

  bool ring_buffer_write(ring_buffer_t* rb, uint8_t byte) {
      size_t next_head = (rb->head + 1) % rb->size;
      if (next_head == rb->tail) {
          return false;  // Buffer full
      }
      rb->buffer[rb->head] = byte;
      rb->head = next_head;
      return true;
  }

  bool ring_buffer_read(ring_buffer_t* rb, uint8_t* byte) {
      if (rb->head == rb->tail) {
          return false;  // Buffer empty
      }
      *byte = rb->buffer[rb->tail];
      rb->tail = (rb->tail + 1) % rb->size;
      return true;
  }

  // 3. Memory sections for linker control
  // Place in specific memory regions

  __attribute__((section(".ccmram")))
  static uint8_t fast_buffer[1024];  // In CCM RAM

  __attribute__((section(".noinit")))
  static uint32_t persistent_counter;  // Survives reset

  // 4. Stack usage monitoring
  #define STACK_CANARY 0xDEADBEEF

  void init_stack_monitor(void) {
      extern uint32_t _estack;
      extern uint32_t _Min_Stack_Size;
      uint32_t* stack_bottom = &_estack - (uint32_t)&_Min_Stack_Size/4;

      // Fill stack with pattern
      for (uint32_t* p = stack_bottom; p < &_estack - 100; p++) {
          *p = STACK_CANARY;
      }
  }

  uint32_t get_stack_usage(void) {
      extern uint32_t _estack;
      extern uint32_t _Min_Stack_Size;
      uint32_t* stack_bottom = &_estack - (uint32_t)&_Min_Stack_Size/4;

      uint32_t* p = stack_bottom;
      while (*p == STACK_CANARY && p < &_estack) {
          p++;
      }

      return (&_estack - p) * sizeof(uint32_t);
  }
why: "Memory issues cause crashes that are hard to reproduce"

state_machine: name: State Machine Implementation description: Robust state machine patterns pattern: | // State machine with entry/exit actions

  typedef enum {
      STATE_IDLE,
      STATE_ARMED,
      STATE_RUNNING,
      STATE_ERROR,
      STATE_COUNT
  } state_t;

  typedef enum {
      EVENT_ARM,
      EVENT_START,
      EVENT_STOP,
      EVENT_ERROR,
      EVENT_RESET,
      EVENT_COUNT
  } event_t;

  typedef struct {
      state_t current_state;
      void (*on_entry)(void);
      void (*on_exit)(void);
      void (*on_tick)(void);
  } state_machine_t;

  // State-specific handlers
  static void idle_entry(void) { disable_motor(); }
  static void idle_tick(void) { check_sensors(); }

  static void armed_entry(void) { arm_system(); }
  static void armed_tick(void) { check_arm_timeout(); }

  static void running_entry(void) { enable_motor(); start_control_loop(); }
  static void running_tick(void) { run_control_step(); }
  static void running_exit(void) { stop_control_loop(); }

  static void error_entry(void) { disable_motor(); set_error_led(); }

  // State definitions
  static const struct {
      void (*on_entry)(void);
      void (*on_exit)(void);
      void (*on_tick)(void);
  } state_handlers[STATE_COUNT] = {
      [STATE_IDLE]    = {idle_entry, NULL, idle_tick},
      [STATE_ARMED]   = {armed_entry, NULL, armed_tick},
      [STATE_RUNNING] = {running_entry, running_exit, running_tick},
      [STATE_ERROR]   = {error_entry, NULL, NULL},
  };

  // Transition table
  static const state_t transitions[STATE_COUNT][EVENT_COUNT] = {
      /*              ARM           START         STOP          ERROR         RESET */
      [STATE_IDLE]   = {STATE_ARMED, STATE_IDLE,   STATE_IDLE,   STATE_ERROR,  STATE_IDLE},
      [STATE_ARMED]  = {STATE_ARMED, STATE_RUNNING,STATE_IDLE,   STATE_ERROR,  STATE_IDLE},
      [STATE_RUNNING]= {STATE_RUNNING,STATE_RUNNING,STATE_IDLE, STATE_ERROR,  STATE_IDLE},
      [STATE_ERROR]  = {STATE_ERROR, STATE_ERROR,  STATE_ERROR,  STATE_ERROR,  STATE_IDLE},
  };

  static state_machine_t sm = {.current_state = STATE_IDLE};

  void sm_process_event(event_t event) {
      state_t next = transitions[sm.current_state][event];

      if (next != sm.current_state) {
          // Exit current state
          if (state_handlers[sm.current_state].on_exit) {
              state_handlers[sm.current_state].on_exit();
          }

          // Transition
          sm.current_state = next;

          // Enter new state
          if (state_handlers[sm.current_state].on_entry) {
              state_handlers[sm.current_state].on_entry();
          }
      }
  }

  void sm_tick(void) {
      if (state_handlers[sm.current_state].on_tick) {
          state_handlers[sm.current_state].on_tick();
      }
  }
why: "State machines make complex behavior predictable and testable"

rtos_patterns: name: RTOS Task Patterns description: FreeRTOS and similar RTOS patterns pattern: | // FreeRTOS task patterns

  #include "FreeRTOS.h"
  #include "task.h"
  #include "queue.h"
  #include "semphr.h"

  // 1. Producer-Consumer with Queue
  static QueueHandle_t sensor_queue;

  void sensor_task(void* params) {
      sensor_data_t data;

      while (1) {
          data = read_sensor();

          // Non-blocking send with timeout
          if (xQueueSend(sensor_queue, &data, pdMS_TO_TICKS(10)) != pdTRUE) {
              // Queue full - log or handle
              increment_dropped_counter();
          }

          vTaskDelay(pdMS_TO_TICKS(10));  // 100Hz
      }
  }

  void processing_task(void* params) {
      sensor_data_t data;

      while (1) {
          // Block until data available
          if (xQueueReceive(sensor_queue, &data, portMAX_DELAY) == pdTRUE) {
              process_data(&data);
          }
      }
  }

  // 2. Mutex for shared resource
  static SemaphoreHandle_t i2c_mutex;

  bool i2c_write_protected(uint8_t addr, uint8_t* data, size_t len) {
      if (xSemaphoreTake(i2c_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
          bool result = i2c_write(addr, data, len);
          xSemaphoreGive(i2c_mutex);
          return result;
      }
      return false;  // Timeout
  }

  // 3. Event groups for synchronization
  static EventGroupHandle_t system_events;
  #define EVENT_SENSOR_READY  (1 << 0)
  #define EVENT_CONFIG_LOADED (1 << 1)
  #define EVENT_SYSTEM_GO     (1 << 2)

  void init_task(void* params) {
      load_configuration();
      xEventGroupSetBits(system_events, EVENT_CONFIG_LOADED);

      // Wait for all subsystems ready
      xEventGroupWaitBits(
          system_events,
          EVENT_SENSOR_READY | EVENT_CONFIG_LOADED,
          pdFALSE,  // Don't clear bits
          pdTRUE,   // Wait for ALL bits
          portMAX_DELAY
      );

      xEventGroupSetBits(system_events, EVENT_SYSTEM_GO);
      vTaskDelete(NULL);  // Self-delete
  }

  // 4. Task priorities
  // Higher number = higher priority in FreeRTOS
  #define PRIORITY_CONTROL    (configMAX_PRIORITIES - 1)  // Highest
  #define PRIORITY_SENSOR     (configMAX_PRIORITIES - 2)
  #define PRIORITY_COMMS      (tskIDLE_PRIORITY + 2)
  #define PRIORITY_LOGGING    (tskIDLE_PRIORITY + 1)      // Lowest

  void app_main(void) {
      sensor_queue = xQueueCreate(10, sizeof(sensor_data_t));
      i2c_mutex = xSemaphoreCreateMutex();
      system_events = xEventGroupCreate();

      xTaskCreate(control_task, "control", 512, NULL, PRIORITY_CONTROL, NULL);
      xTaskCreate(sensor_task, "sensor", 256, NULL, PRIORITY_SENSOR, NULL);
      xTaskCreate(comms_task, "comms", 512, NULL, PRIORITY_COMMS, NULL);

      vTaskStartScheduler();
  }
why: "RTOS patterns enable reliable multitasking in embedded systems"

anti_patterns:

busy_wait: name: Busy-Wait Instead of Interrupt problem: "while(!flag); wastes CPU cycles and power" solution: "Use interrupts, DMA, or RTOS event waiting"

malloc_in_embedded: name: Dynamic Memory in Constrained System problem: "malloc can fragment heap, fail unpredictably" solution: "Use static allocation or fixed-size pools"

printf_debug: name: Printf for Debugging Timing-Critical Code problem: "Printf is slow and disrupts timing" solution: "Use trace buffers, toggle pins, or SEGGER RTT"

handoffs:

  • to: ros2-robotics when: "Need ROS2 integration" pass: "Hardware interface, message types"

  • to: fpga-design when: "Need hardware acceleration" pass: "Interface specification, timing requirements"

ecosystem: platforms: - "STM32 - ARM Cortex-M" - "ESP32 - WiFi/BT integrated" - "Nordic nRF - Low power BLE" - "Raspberry Pi Pico - RP2040"

rtos: - "FreeRTOS - Most popular" - "Zephyr - Modern, Linux Foundation" - "ThreadX - Azure RTOS" - "ChibiOS - Automotive grade"

tools: - "OpenOCD - Debug interface" - "SEGGER J-Link - Debug probe" - "PlatformIO - Development platform" - "STM32CubeIDE"