Update port development guide to focus on minimal and STM32 ports

- Replace Alif-specific examples with minimal and STM32 port patterns
- Add minimal port examples for simplest implementation patterns
- Use STM32 as the comprehensive reference for full-featured ports
- Keep Alif only as reference for commit progression analysis
- Update build system examples to show both minimal and STM32 approaches
- Reference established ports (STM32, minimal, RP2) throughout
Andrew Leech
2025-06-14 15:40:30 +10:00
parent c4a0ed1690
commit 8214f5dfc8

@@ -1,6 +1,6 @@
# MicroPython New Port Development Guide
This comprehensive guide walks through creating a new MicroPython port, using the Alif port development as a case study. The guide is based on analysis of commit `ccc5935234` and subsequent development commits.
This comprehensive guide walks through creating a new MicroPython port. The guide uses the minimal port as the simplest reference implementation and the STM32 port as a comprehensive example. Recent port development patterns were analyzed from the Alif port's commit progression.
## Overview
@@ -20,7 +20,7 @@ Before starting, ensure you have:
## Development Phases
The Alif port development followed this logical progression:
Most successful ports follow this logical progression:
1. **Foundation** - Core infrastructure and basic I/O
2. **System Services** - Timing, interrupts, memory management
3. **Peripheral Support** - Progressive addition of hardware interfaces
@@ -53,55 +53,59 @@ Create these essential directories:
- Use `MICROPY_CONFIG_ROM_LEVEL` for feature sets (MINIMAL/BASIC/EXTRA/FULL)
- Define platform string: `#define MICROPY_PY_SYS_PLATFORM "yourport"`
**Template from Alif**:
**Minimal Configuration (from minimal port)**:
```c
// Options controlling how MicroPython is built, overriding defaults in py/mpconfig.h
// Minimal port uses the simplest possible configuration
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_MINIMUM)
// Type definitions - minimal port uses standard C types
typedef int32_t mp_int_t;
typedef uint32_t mp_uint_t;
typedef long mp_off_t;
// Required for all ports
#define MICROPY_HW_BOARD_NAME "minimal"
#define MICROPY_HW_MCU_NAME "unknown-cpu"
```
**Full-Featured Configuration (STM32 port pattern)**:
```c
// STM32 port uses feature levels based on available flash/RAM
#include <stdint.h>
#include <alloca.h>
// board specific definitions
#include "mpconfigboard.h"
#include "stm32_hal.h" // Your hardware HAL
// Feature level (STM32 uses different levels for different chips)
#ifndef MICROPY_CONFIG_ROM_LEVEL
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES)
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
#endif
// Hardware features
#define MICROPY_HW_ENABLE_UART_REPL (1)
#define MICROPY_HW_ENABLE_USBDEV (1)
// Type definitions (STM32 pattern)
typedef intptr_t mp_int_t; // must be pointer size
typedef uintptr_t mp_uint_t; // must be pointer size
typedef intptr_t mp_off_t; // type for offsets
// USB configuration
#define MICROPY_HW_USB_CDC (1)
#define MICROPY_HW_USB_VID (0xf055)
#define MICROPY_HW_USB_PID (0x9802)
// Memory and performance
#define MICROPY_ALLOC_PATH_MAX (128)
#define MICROPY_QSTR_BYTES_IN_HASH (1)
// MicroPython emitters (for ARM Cortex-M)
#define MICROPY_EMIT_THUMB (1)
#define MICROPY_EMIT_THUMB_ARMV7M (1)
#define MICROPY_EMIT_INLINE_THUMB (1)
// Core Python features
// Python features (STM32 enables based on flash size)
#define MICROPY_ENABLE_GC (1)
#define MICROPY_HELPER_REPL (1)
#define MICROPY_REPL_AUTO_INDENT (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_SCHEDULER_DEPTH (8)
// Platform identification
#define MICROPY_PY_SYS_PLATFORM "yourport"
// Hardware features (STM32 pattern)
#define MICROPY_HW_ENABLE_RTC (1)
#define MICROPY_HW_ENABLE_ADC (1)
#define MICROPY_HW_ENABLE_DAC (1)
#define MICROPY_HW_ENABLE_USB (1)
#define MICROPY_HW_HAS_SWITCH (1)
// Extended modules
// Extended modules (STM32 includes many by default)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/yourport/modmachine.c"
#define MICROPY_VFS (1)
// Type definitions
typedef intptr_t mp_int_t;
typedef uintptr_t mp_uint_t;
typedef intptr_t mp_off_t;
#define MICROPY_PY_MACHINE_PIN_BASE (1)
#define MICROPY_PY_MACHINE_PULSE (1)
#define MICROPY_PY_MACHINE_I2C (1)
#define MICROPY_PY_MACHINE_SPI (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/stm32/modmachine.c"
// Board hooks (define these as no-ops initially)
#ifndef MICROPY_BOARD_STARTUP
@@ -153,44 +157,75 @@ MICROPY_FLOAT_IMPL = double
- Pin manipulation macros
- Timing functions
**Template from Alif**:
**Minimal HAL (from minimal port)**:
```c
#include "py/ringbuf.h"
#include "shared/runtime/interrupt_char.h"
#include "irq.h"
// Minimal port shows the bare minimum required
static inline void mp_hal_set_interrupt_char(char c) {}
// Atomic sections (critical sections)
// Required timing functions (minimal stubs)
static inline mp_uint_t mp_hal_ticks_ms(void) { return 0; }
static inline void mp_hal_delay_ms(mp_uint_t ms) { (void)ms; }
```
**Full HAL (STM32 pattern)**:
```c
#include "py/mphal.h"
#include STM32_HAL_H
// STM32 uses the HAL's critical section functions
#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq()
#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state)
// Event handling for efficient wait
#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) \
do { \
if ((TIMEOUT_MS) < 0) { \
__WFE(); \
} else { \
/* TODO: implement timeout */ \
} \
} while (0)
// STM32 implements proper timing using SysTick
extern volatile uint32_t systick_ms;
static inline mp_uint_t mp_hal_ticks_ms(void) {
return systick_ms;
}
// Declare pin type and manipulation functions
extern const mp_obj_type_t pin_type;
typedef struct _pin_obj_t {
mp_obj_base_t base;
qstr name;
// ... hardware-specific fields
} pin_obj_t;
// STM32 pin handling
#define mp_hal_pin_obj_t const pin_obj_t*
#define mp_hal_get_pin_obj(o) pin_find(o)
#define mp_hal_pin_od_low(p) mp_hal_pin_low(p)
#define mp_hal_pin_od_high(p) mp_hal_pin_high(p)
```
#### B. `mphalport.c` (HAL Implementation)
**Purpose**: Implements the hardware abstraction functions.
**Key Functions** (analyze STM32/RP2 implementations):
- `mp_hal_delay_ms()` / `mp_hal_delay_us()`
- `mp_hal_ticks_ms()` / `mp_hal_ticks_us()`
- `mp_hal_stdout_tx_strn()` (debug output)
- `mp_hal_stdin_rx_chr()` (console input)
**Key Functions to Implement**:
**From minimal port (bare minimum)**:
```c
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
// Minimal: just write to UART
for (size_t i = 0; i < len; ++i) {
uart_tx_char(str[i]);
}
}
```
**From STM32 port (full implementation)**:
```c
void mp_hal_delay_ms(mp_uint_t ms) {
// STM32: proper delay with event handling
mp_uint_t start = mp_hal_ticks_ms();
while (mp_hal_ticks_ms() - start < ms) {
MICROPY_EVENT_POLL_HOOK
}
}
int mp_hal_stdin_rx_chr(void) {
// STM32: check multiple input sources
for (;;) {
if (MP_STATE_PORT(pyb_stdio_uart) != NULL &&
uart_rx_any(MP_STATE_PORT(pyb_stdio_uart))) {
return uart_rx_char(MP_STATE_PORT(pyb_stdio_uart));
}
MICROPY_EVENT_POLL_HOOK
}
}
```
### Step 4: Main Entry Point
@@ -198,68 +233,105 @@ typedef struct _pin_obj_t {
**Purpose**: Main application loop and MicroPython initialization.
**Common Pattern Analysis** (all ports follow this structure):
**Minimal Implementation (from minimal port)**:
```c
#include "py/runtime.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "py/mphal.h"
#include "py/stackctrl.h"
#include "shared/runtime/pyexec.h"
// Hardware-specific includes
#include "your_hardware_headers.h"
// Memory layout (defined in linker script)
extern uint8_t __StackTop, __StackLimit;
extern uint8_t __GcHeapStart, __GcHeapEnd;
void _start(void) {
// 1. Hardware initialization
SystemInit(); // Clock setup, etc.
// Minimal port shows the absolute minimum required
int main(int argc, char **argv) {
mp_stack_ctrl_init();
mp_stack_set_limit(STACK_LIMIT);
// 2. Board-specific startup
MICROPY_BOARD_STARTUP();
#if MICROPY_ENABLE_GC
gc_init(heap, heap + sizeof(heap));
#endif
// 3. Initialize interrupt system
SysTick_Config(SystemCoreClock / 1000);
mp_init();
// 4. Early hardware init
#if MICROPY_REPL_EVENT_DRIVEN
pyexec_event_repl_init();
for (;;) {
int c = mp_hal_stdin_rx_chr();
if (pyexec_event_repl_process_char(c)) {
break;
}
}
#else
pyexec_friendly_repl();
#endif
mp_deinit();
return 0;
}
```
**Full Implementation (STM32 pattern)**:
```c
// STM32 main.c pattern - comprehensive initialization
int main(void) {
// STM32 HAL init
HAL_Init();
// Configure clocks
SystemClock_Config();
// Basic hardware init
powerctrl_check_enter_bootloader();
// Board-specific initialization
MICROPY_BOARD_EARLY_INIT();
// 5. Initialize peripherals
uart_init(); // For REPL
flash_init(); // For filesystem
// Enable caches (STM32-specific)
#if defined(MICROPY_BOARD_STARTUP)
MICROPY_BOARD_STARTUP();
#endif
// 6. Set up MicroPython memory management
mp_stack_set_top(&__StackTop);
mp_stack_set_limit(&__StackTop - &__StackLimit - 1024);
gc_init(&__GcHeapStart, &__GcHeapEnd);
// Initialize systick for timing
systick_init();
pendsv_init();
// 7. Main execution loop
// Initialize storage
#if MICROPY_HW_ENABLE_STORAGE
storage_init();
#endif
// GC init with STM32's heap calculation
#if MICROPY_ENABLE_GC
gc_init(&_heap_start, &_heap_end);
#endif
// Initialize stack pointer for stack checking
mp_stack_set_top(&_estack);
mp_stack_set_limit((char*)&_estack - (char*)&_sstack - STACK_LIMIT);
// Main MicroPython loop
for (;;) {
// Initialize MicroPython runtime
#if MICROPY_HW_ENABLE_STORAGE
storage_init();
#endif
// STM32 resets peripherals on soft reset
machine_init();
// Run MicroPython
mp_init();
// Initialize subsystems
readline_init0();
// STM32 runs boot.py and main.py
#if MICROPY_VFS
pyexec_file_if_exists("boot.py");
pyexec_file_if_exists("main.py");
#endif
// Execute boot scripts
pyexec_frozen_module("_boot.py", false);
// Execute user scripts
int ret = pyexec_file_if_exists("boot.py");
if (ret & PYEXEC_FORCED_EXIT) {
goto soft_reset_exit;
}
// Main REPL loop
// Run REPL
for (;;) {
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
if (pyexec_raw_repl() != 0) break;
if (pyexec_raw_repl() != 0) {
break;
}
} else {
if (pyexec_friendly_repl() != 0) break;
if (pyexec_friendly_repl() != 0) {
break;
}
}
}
@@ -300,7 +372,7 @@ void nlr_jump_fail(void *val) {
**Build System Architecture Choices**:
1. **Pure Make** (STM32, SAMD, Alif)
1. **Pure Make** (STM32, SAMD, NRF, minimal)
- Traditional approach for bare-metal ports
- Direct control over build process
- No external build system dependencies
@@ -331,52 +403,28 @@ void nlr_jump_fail(void *val) {
**Key Insight**: The Make wrapper pattern allows MicroPython to maintain a consistent `make` interface across all ports while accommodating vendor requirements. Users always run `make BOARD=xxx` regardless of underlying build system.
**Template from Alif (Pure Make)**:
**Minimal Makefile (from minimal port)**:
```makefile
################################################################################
# Initial setup of Makefile environment
BOARD ?= DEFAULT_BOARD
BOARD_DIR ?= boards/$(BOARD)
BUILD ?= build-$(BOARD)
ifeq ($(wildcard $(BOARD_DIR)/.),)
$(error Invalid BOARD specified: $(BOARD_DIR))
endif
# Minimal port Makefile - simplest possible build
include ../../py/mkenv.mk
include mpconfigport.mk
include $(BOARD_DIR)/mpconfigboard.mk
# qstr definitions (must come before including py.mk)
QSTR_DEFS += qstrdefsport.h
# qstr definitions (minimal port keeps it simple)
QSTR_DEFS = qstrdefsport.h
# include py core make definitions
include $(TOP)/py/py.mk
include $(TOP)/extmod/extmod.mk
################################################################################
# Project specific settings
CROSS_COMPILE ?= arm-none-eabi-
GIT_SUBMODULES += lib/tinyusb
# Include paths
INC += -I.
INC += -I$(TOP)
INC += -I$(BUILD)
INC += -I$(BOARD_DIR)
# Source files
SRC_C = \
main.c \
mphalport.c \
modmachine.c \
machine_pin.c \
# Hardware-specific sources
SRC_C += \
your_hardware_drivers.c \
uart_core.c \
mphalport.c
OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
@@ -385,10 +433,66 @@ all: $(BUILD)/firmware.elf
$(BUILD)/firmware.elf: $(OBJ)
$(ECHO) "LINK $@"
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(SIZE) $@
include $(TOP)/py/mkrules.mk
```
**Full-Featured Makefile (STM32 pattern)**:
```makefile
# STM32 Makefile pattern - comprehensive build system
BOARD ?= PYBV10
BOARD_DIR ?= boards/$(BOARD)
# If the build directory is not given, make it reflect the board name.
BUILD ?= build-$(BOARD)
include ../../py/mkenv.mk
-include mpconfigport.mk
include $(BOARD_DIR)/mpconfigboard.mk
# Configure for STM32 MCU family
CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]')
STARTUP_FILE ?= lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Source/Templates/gcc/startup_$(CMSIS_MCU_LOWER).s
# Select the cross compile prefix
CROSS_COMPILE ?= arm-none-eabi-
INC += -I.
INC += -I$(TOP)
INC += -I$(BUILD)
INC += -I$(BOARD_DIR)
INC += -Ilwip_inc
# Basic source files
SRC_C = \
main.c \
system_stm32.c \
stm32_it.c \
mphalport.c \
SRC_O = \
$(STARTUP_FILE) \
gchelper.o \
# Add STM32 HAL drivers
SRC_STM32 = \
$(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal.c \
hal_cortex.c \
hal_dma.c \
hal_gpio.c \
hal_rcc.c \
)
OBJ = $(PY_O)
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_O))
OBJ += $(addprefix $(BUILD)/, $(SRC_STM32:.c=.o))
include $(TOP)/py/py.mk
```
### Step 6: Board Configuration
#### A. `boards/YOUR_BOARD/mpconfigboard.h`
@@ -396,14 +500,33 @@ include $(TOP)/py/mkrules.mk
**Purpose**: Board-specific hardware definitions.
```c
// Board-specific configuration
// Minimal board configuration (from minimal port)
#define MICROPY_HW_BOARD_NAME "minimal"
#define MICROPY_HW_MCU_NAME "Cortex-M4"
#define MICROPY_HW_BOARD_NAME "Your Board Name"
#define MICROPY_HW_MCU_NAME "Your MCU"
// STM32 board configuration pattern (from PYBV10)
#define MICROPY_HW_BOARD_NAME "PYBv1.0"
#define MICROPY_HW_MCU_NAME "STM32F405RG"
// Pin definitions
#define MICROPY_HW_UART_REPL_TX pin_A9
#define MICROPY_HW_UART_REPL_RX pin_A10
// Crystal frequencies
#define MICROPY_HW_CLK_PLLM (12)
#define MICROPY_HW_CLK_PLLN (336)
#define MICROPY_HW_CLK_PLLP (2)
#define MICROPY_HW_CLK_PLLQ (7)
// UART config for REPL
#define MICROPY_HW_UART_REPL PYB_UART_1
#define MICROPY_HW_UART_REPL_BAUD 115200
// Hardware features
#define MICROPY_HW_HAS_SWITCH (1)
#define MICROPY_HW_HAS_FLASH (1)
#define MICROPY_HW_HAS_SDCARD (1)
#define MICROPY_HW_HAS_LCD (1)
#define MICROPY_HW_ENABLE_RNG (1)
#define MICROPY_HW_ENABLE_RTC (1)
#define MICROPY_HW_ENABLE_DAC (1)
#define MICROPY_HW_ENABLE_USB (1)
// Flash configuration
#define MICROPY_HW_FLASH_SIZE_MB (2)
@@ -436,12 +559,20 @@ LD_FILES = mcu/your_mcu.ld
**Purpose**: Define interrupt priority levels.
**STM32 Pattern** (`ports/stm32/irq.h`):
```c
// IRQ priority definitions
#define IRQ_PRI_SYSTICK (1)
#define IRQ_PRI_UART (6)
#define IRQ_PRI_USB (6)
#define IRQ_PRI_PENDSV (15) // Lowest priority
// STM32 uses NVIC priority grouping with 4 bits for preemption
// Higher number = lower priority
#define IRQ_PRI_SYSTICK NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
#define IRQ_PRI_UART NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 12, 0)
#define IRQ_PRI_FLASH NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 12, 0)
#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 14, 0)
#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 15, 0)
// For simpler MCUs, use raw priority values
#define IRQ_PRI_SYSTICK (0x40) // Highest priority
#define IRQ_PRI_UART (0x80)
#define IRQ_PRI_PENDSV (0xc0) // Lowest priority
```
#### B. `pendsv.c` & `pendsv.h` (PendSV Handler)
@@ -482,44 +613,52 @@ __GcHeapEnd = __StackLimit;
**Purpose**: Implements the `machine` module.
**Common Pattern** (analyze STM32 implementation):
**STM32 Implementation Pattern**:
```c
// STM32's modmachine.c - demonstrates the include file pattern
#include "py/runtime.h"
#include "extmod/modmachine.h"
#include "drivers/dht/dht.h"
// Forward declarations for machine classes
extern const mp_obj_type_t machine_pin_type;
#if MICROPY_PY_MACHINE
// STM32 resets via NVIC
static mp_obj_t machine_reset(void) {
NVIC_SystemReset();
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset);
MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset);
// STM32 uses HAL to get unique ID
static mp_obj_t machine_unique_id(void) {
// Return unique device ID
return mp_obj_new_bytes((const byte*)&your_unique_id, sizeof(your_unique_id));
byte *id = (byte *)MP_HAL_UNIQUE_ID_ADDRESS;
return mp_obj_new_bytes(id, 12);
}
static MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id);
MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id);
static const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_machine) },
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) },
{ MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) },
// Machine classes
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) },
};
static MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table);
// STM32 freq functions
static mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) {
if (n_args == 0) {
// Get frequency
return mp_obj_new_int(HAL_RCC_GetSysClockFreq());
} else {
// Set frequency (STM32 specific)
mp_int_t freq = mp_obj_get_int(args[0]);
if (!set_sys_clock_source(freq)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid freq"));
}
return mp_const_none;
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq);
const mp_obj_module_t machine_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&machine_module_globals,
};
// The actual module uses MICROPY_PY_MACHINE_INCLUDEFILE
// This allows extmod/modmachine.c to handle the standard parts
// while the port adds its own specifics in modmachine.c
#include MICROPY_PY_MACHINE_INCLUDEFILE
MP_REGISTER_MODULE(MP_QSTR_machine, machine_module);
#endif // MICROPY_PY_MACHINE
```
### Step 10: Pin Support
@@ -617,12 +756,30 @@ MP_DEFINE_CONST_OBJ_TYPE(
**Purpose**: Implement storage backend for filesystem.
**Common Pattern**: Implement a flash driver that provides:
- Block read/write interface
- Sector erase functionality
- Integration with `extmod/vfs.c`
**STM32 Storage Pattern**:
Study the Alif `ospi_flash.c` and `alif_flash.c` implementations.
STM32 implements storage through `storage.c` and `flashbdev.c`:
```c
// STM32 storage.c pattern
static const mp_obj_base_t pyb_flash_obj = {&pyb_flash_type};
// STM32 defines a block device interface
static mp_obj_t pyb_flash_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE);
FLASH_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE);
return mp_const_none;
}
// Register with VFS
MP_DEFINE_CONST_FUN_OBJ_3(pyb_flash_readblocks_obj, pyb_flash_readblocks);
```
**Key Points**:
- Implement block device protocol (readblocks, writeblocks, ioctl)
- Handle flash programming constraints (erase before write)
- Register with VFS for filesystem support
### Step 12: USB MSC Support
@@ -647,7 +804,8 @@ SRC_C += machine_i2c.c
2. **Add hardware driver** (if needed):
```makefile
ALIF_SRC_C += drivers/source/i2c.c
# STM32 adds HAL drivers
SRC_STM32 += $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_hal_i2c.c
```
#### B. Enable in Configuration
@@ -660,23 +818,23 @@ ALIF_SRC_C += drivers/source/i2c.c
#### C. Implement Peripheral Class
**Pattern Analysis** (from Alif I2C implementation):
**STM32 Peripheral Pattern** (I2C example):
1. **Define object structure**:
1. **Define object structure** (from STM32):
```c
typedef struct _machine_i2c_obj_t {
mp_obj_base_t base;
uint32_t i2c_id;
I2C_Type *i2c; // Hardware registers
mp_hal_pin_obj_t scl; // Pin objects
mp_hal_pin_obj_t sda;
uint32_t freq; // Configuration
uint32_t timeout;
I2C_HandleTypeDef *i2c; // STM32 HAL handle
bool is_initialised;
uint8_t scl;
uint8_t sda;
} machine_i2c_obj_t;
```
2. **Create instance table**:
2. **Create instance table** (STM32 pattern):
```c
// STM32 uses a macro to define I2C objects
#define I2C_OBJ(n) &machine_i2c_obj[n]
static machine_i2c_obj_t machine_i2c_obj[] = {
#if defined(MICROPY_HW_I2C0_SCL)
[0] = {{&machine_i2c_type}, 0, (I2C_Type *)I2C0_BASE, MICROPY_HW_I2C0_SCL, MICROPY_HW_I2C0_SDA},
@@ -726,7 +884,7 @@ For Bluetooth-enabled hardware:
### Step 16: Multi-core Support
For multi-core MCUs (like Alif Ensemble):
For multi-core MCUs (RP2 has dual-core support):
1. **Implement Open-AMP backend** (`mpmetalport.c`, `mpremoteprocport.c`)
2. **Add core-specific configurations**
@@ -792,7 +950,7 @@ When vendor SDKs require CMake:
### Hardware Integration
- **Follow existing peripheral patterns** - don't reinvent APIs
- **Handle hardware quirks gracefully** (see Alif I2C zero-length transfer workaround)
- **Handle hardware quirks gracefully** (see STM32 DMA constraints, RP2 PIO limitations)
- **Add proper error handling** and timeouts
### Build System