mirror of
https://github.com/micropython/micropython.git
synced 2026-01-04 11:10:14 +01:00
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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user