diff --git a/config/pikapython-capabilities.yaml b/config/pikapython-capabilities.yaml index 820a1fc86..464508412 100644 --- a/config/pikapython-capabilities.yaml +++ b/config/pikapython-capabilities.yaml @@ -333,6 +333,15 @@ profiles: hardware-control: roots: [binding.constants, binding.container-result, binding.mutable-buffer, binding.object-argument, binding.python-callable, builtin.container-construct, builtin.len, builtin.print, builtin.scalar-convert, call.defaults, call.keyword, call.reference, container.bytearray, container.dict, container.list, container.tuple, exception.finally, flow.loop, flow.pass, iter.builtin, iter.range, library.struct-basic, library.time-basic, literal.embedded-int, logic.short-circuit, machine.adc, machine.pin, machine.pwm, module.from-import, module.python, name.global, object.user-basic, op.augmented, op.bitwise, op.compare-basic, op.membership, op.numeric-basic, op.slice, op.subscript, runtime.frozen-program, runtime.gc, scope.global-statement, value.bool, value.bytes, value.float, value.none, value.string] syntaxLevel: 6 + pico2: + roots: [binding.constants, binding.container-result, binding.mutable-buffer, binding.object-argument, binding.python-callable, builtin.container-construct, builtin.len, builtin.print, builtin.scalar-convert, call.defaults, call.keyword, call.reference, container.bytearray, container.dict, container.list, container.tuple, exception.finally, flow.loop, flow.pass, iter.builtin, iter.range, library.struct-basic, library.time-basic, literal.embedded-int, logic.short-circuit, machine.adc, machine.pin, machine.pwm, module.from-import, module.python, name.global, object.user-basic, op.augmented, op.bitwise, op.compare-basic, op.integer, op.membership, op.numeric-basic, op.slice, op.subscript, repl.auto-indent, runtime.frozen-program, runtime.gc, scope.global-statement, value.bool, value.bytes, value.float, value.none, value.string] + syntaxLevel: 6 + pico2-full: + roots: [assignment.unpack, binding.constants, binding.container-argument, binding.container-result, binding.mutable-buffer, binding.object-argument, binding.object-result, binding.python-callable, builtin.container-construct, builtin.embedded, builtin.help, builtin.len, builtin.print, builtin.scalar-convert, builtin.type, call.defaults, call.keyword, call.reference, call.variadic, container.bytearray, container.dict, container.list, container.tuple, exception.finally, flow.pass, iter.builtin, iter.range, library.aes, library.base64, library.binascii, library.fuzzypid, library.hashlib, library.hmac, library.http-client, library.json, library.math, library.modbus-codec, library.mqtt-client, library.os-basic, library.random, library.socket-basic, library.struct-basic, library.time-basic, library.unittest-basic, library.zlib, literal.embedded-int, logic.short-circuit, machine.adc, machine.pin, machine.pwm, module.from-import, module.python, object.class, object.user-basic, op.augmented, op.bitwise, op.compare-basic, op.complete, op.float, op.integer, op.membership, op.numeric-basic, op.slice, op.subscript, protocol.text-network, repl.auto-indent, runtime.frozen-program, runtime.frozen-source, runtime.gc, scope.global-statement, value.bool, value.bytes, value.float, value.int, value.none, value.string] + resourceLimits: + frontend: + functionCount: 192 + syntaxLevel: 6 interactive-minimal: roots: [builtin.print, call.positional, flow.branch, name.global, op.integer, repl.auto-indent, runtime.frozen-program] syntaxLevel: 1 diff --git a/packages/fuzzypid/fuzzypid_module.c b/packages/fuzzypid/fuzzypid_module.c index 12cd95f9d..4d16b782f 100644 --- a/packages/fuzzypid/fuzzypid_module.c +++ b/packages/fuzzypid/fuzzypid_module.c @@ -81,8 +81,8 @@ typedef struct { int mf_parameters[PIKA_FUZZYPID_MF_PARAMETER_COUNT]; PikaFuzzyPidState pids[PIKA_FUZZYPID_DOF_LIMIT]; PikaFuzzyPidInference fuzzy[PIKA_FUZZYPID_DOF_LIMIT]; - unsigned int mf_types[PIKA_FUZZYPID_DOF_LIMIT] - [PIKA_FUZZYPID_MAX_MF_TYPES]; + uint32_t mf_types[PIKA_FUZZYPID_DOF_LIMIT] + [PIKA_FUZZYPID_MAX_MF_TYPES]; float fuzzy_outputs[PIKA_FUZZYPID_DOF_LIMIT] [PIKA_FUZZYPID_MAX_FUZZY_OUTPUTS]; uint8_t directions[PIKA_FUZZYPID_DOF_LIMIT]; diff --git a/project-templates/pico2/CMakeLists.txt b/project-templates/pico2/CMakeLists.txt new file mode 100644 index 000000000..e92a7bf55 --- /dev/null +++ b/project-templates/pico2/CMakeLists.txt @@ -0,0 +1,128 @@ +cmake_minimum_required(VERSION 3.19) + +# Prefer an explicit environment/cache override, then the user's standard +# SDK location. +if(NOT PICO_SDK_PATH AND DEFINED ENV{PICO_SDK_PATH}) + set(PICO_SDK_PATH "$ENV{PICO_SDK_PATH}" CACHE PATH + "Path to the Raspberry Pi Pico SDK") +endif() +if(NOT PICO_SDK_PATH AND EXISTS "$ENV{HOME}/pico-sdk") + set(PICO_SDK_PATH "$ENV{HOME}/pico-sdk" CACHE PATH + "Path to the Raspberry Pi Pico SDK") +endif() +if(NOT PICO_SDK_PATH) + message(FATAL_ERROR + "Pico SDK was not found; set PICO_SDK_PATH or PICO_SDK_PATH environment variable") +endif() + +set(PICO_BOARD pico2 CACHE STRING "Pico board") +set(PICO_PLATFORM rp2350-arm-s CACHE STRING "Pico platform") +set(PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS 0 CACHE STRING + "Do not block startup while waiting for a USB CDC host") + +include("${PICO_SDK_PATH}/external/pico_sdk_import.cmake") + +project(pikapython_pico2 C CXX ASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +# The Pico SDK uses GNU inline assembly in its RP2350 runtime sources. +set(CMAKE_C_EXTENSIONS ON) + +pico_sdk_init() + +set(PIKA_GENERATED_DIR "${CMAKE_SOURCE_DIR}/pikapython-generated") +set(PIKA_SOURCE_MANIFEST "${PIKA_GENERATED_DIR}/source-manifest.json") +if(NOT EXISTS "${PIKA_SOURCE_MANIFEST}") + message(FATAL_ERROR + "Missing pikapython-generated/source-manifest.json; run pikapython-cli build --prebuild-only first") +endif() + +file(READ "${PIKA_SOURCE_MANIFEST}" PIKA_MANIFEST_JSON) +string(JSON PIKA_SOURCE_COUNT LENGTH "${PIKA_MANIFEST_JSON}" sources) +if(PIKA_SOURCE_COUNT EQUAL 0) + message(FATAL_ERROR "PikaPython source manifest contains no sources") +endif() +math(EXPR PIKA_SOURCE_LAST "${PIKA_SOURCE_COUNT} - 1") +set(PIKA_SOURCES) +foreach(PIKA_INDEX RANGE 0 ${PIKA_SOURCE_LAST}) + string(JSON PIKA_RELATIVE_SOURCE GET + "${PIKA_MANIFEST_JSON}" sources ${PIKA_INDEX}) + if(PIKA_RELATIVE_SOURCE STREQUAL "pikapython-packages/socket/platform_socket.c") + continue() + endif() + if(PIKA_RELATIVE_SOURCE MATCHES "/") + set(PIKA_SOURCE_PATH "${CMAKE_SOURCE_DIR}/${PIKA_RELATIVE_SOURCE}") + else() + set(PIKA_SOURCE_PATH "${PIKA_GENERATED_DIR}/${PIKA_RELATIVE_SOURCE}") + endif() + if(NOT EXISTS "${PIKA_SOURCE_PATH}") + message(FATAL_ERROR "PikaPython source is missing: ${PIKA_RELATIVE_SOURCE}") + endif() + list(APPEND PIKA_SOURCES "${PIKA_SOURCE_PATH}") +endforeach() + +string(JSON PIKA_INCLUDE_COUNT LENGTH + "${PIKA_MANIFEST_JSON}" includeDirectories) +set(PIKA_INCLUDE_DIRECTORIES) +if(PIKA_INCLUDE_COUNT GREATER 0) + math(EXPR PIKA_INCLUDE_LAST "${PIKA_INCLUDE_COUNT} - 1") + foreach(PIKA_INDEX RANGE 0 ${PIKA_INCLUDE_LAST}) + string(JSON PIKA_RELATIVE_INCLUDE GET + "${PIKA_MANIFEST_JSON}" includeDirectories ${PIKA_INDEX}) + list(APPEND PIKA_INCLUDE_DIRECTORIES + "${CMAKE_SOURCE_DIR}/${PIKA_RELATIVE_INCLUDE}") + endforeach() +endif() + +add_executable(pico2-firmware + firmware/main.c + firmware/platform.c + firmware/platform_socket.c + ${PIKA_SOURCES} +) + +target_include_directories(pico2-firmware PRIVATE + firmware + ${PIKA_INCLUDE_DIRECTORIES} +) + +target_compile_definitions(pico2-firmware PRIVATE + PIKA_FRONTEND_MODULE_LIMIT=32u + PIKA_FRONTEND_FUNCTION_LIMIT=128u + PIKA_FRONTEND_CLASS_LIMIT=16u + PIKA_FRONTEND_IDENTIFIER_BYTE_LIMIT=63u + PIKA_FRONTEND_SYMBOL_LIMIT=128u + PIKA_FRONTEND_PARAMETER_LIMIT=16u + PIKA_FRONTEND_ARGUMENT_LIMIT=16u + PIKA_FRONTEND_COLLECTION_ELEMENT_LIMIT=64u + PIKA_FRONTEND_EXCEPTION_HANDLER_LIMIT=16u + PIKA_RUNTIME_VALUE_LIMIT=64u + PIKA_REPL_MAX_LINE_BYTES=256u + PIKA_REPL_MAX_BLOCK_BYTES=4096u + PIKA_REPL_MAX_HISTORY_ENTRIES=8u + PIKA_REPL_MAX_HISTORY_BYTES=1024u + PIKA_REPL_MAX_CODE_BYTES=16384u + PIKA_REPL_MAX_OUTPUT_BYTES_PER_COMMAND=2048u + PIKA_REPL_MAX_TRACEBACK_FRAMES=8u +) + +target_compile_options(pico2-firmware PRIVATE + -Wall + -Wextra + -ffunction-sections + -fdata-sections +) + +target_link_libraries(pico2-firmware PRIVATE + pico_stdlib + hardware_adc + hardware_pwm +) + +pico_enable_stdio_usb(pico2-firmware 1) +pico_enable_stdio_uart(pico2-firmware 0) +pico_set_program_name(pico2-firmware "PikaPython Pico 2") +pico_set_program_description(pico2-firmware + "PikaPython v2 on Raspberry Pi Pico 2") +pico_add_extra_outputs(pico2-firmware) diff --git a/project-templates/pico2/README.md b/project-templates/pico2/README.md new file mode 100644 index 000000000..bce52c8a0 --- /dev/null +++ b/project-templates/pico2/README.md @@ -0,0 +1,33 @@ +# Raspberry Pi Pico 2 ARM + +该模板面向 Raspberry Pi Pico 2(RP2350A、ARM Secure Cortex-M33),使用 +`~/pico-sdk` 中的 Pico SDK 2.3.0 或兼容版本构建。目标固定为 +`rp2350-arm-s`;USB CDC 是默认的 PikaPython 输出和 REPL 传输, +`machine.Pin`、`machine.ADC` 和 `machine.PWM` 映射到 Pico SDK 的 GPIO、ADC 和 PWM。 + +## 构建 + +复制模板后执行: + +```bash +pikapython-cli install +pikapython-cli build --prebuild-only +pikapython-cli build +``` + +如果 SDK 不在 `~/pico-sdk`,设置 `PICO_SDK_PATH` 或在 CMake 配置时传入 +`-DPICO_SDK_PATH=/absolute/path/to/pico-sdk`。构建输出位于 +`.pikapython/build/pico2/`,其中 `pico2-firmware.uf2` 可拖放到 Pico 2 的 +`RPI-RP2` 启动盘。 + +首次启动后打开 USB CDC 串口即可看到 `main.py` 输出和 `>>>` 提示符。按 Ctrl-D +结束 REPL;固件随后保持运行。 + +## 支持边界 + +- 固件架构:RP2350A ARM Secure Cortex-M33。*RISCV 并未提供支持* +- SDK 后端:GPIO、ADC、PWM、时间、睡眠、临界区和 USB CDC。 +- PikaPython:默认使用 `pico2-full` capability,可安装模板列出的全部包。 +- 网络:socket、MQTT、HTTP 包可参与构建;Pico 2 本身没有网络控制器,网络操作 + 需要额外的 lwIP/外部网络硬件后端。 +- 输出:`.elf`、`.bin`、`.hex` 和 `.uf2` 均生成在 `.pikapython/build/pico2/`。 diff --git a/project-templates/pico2/firmware/main.c b/project-templates/pico2/firmware/main.c new file mode 100644 index 000000000..3b1f26d84 --- /dev/null +++ b/project-templates/pico2/firmware/main.c @@ -0,0 +1,38 @@ +#include + +#include "pico/stdlib.h" + +#include "pika_application.h" +#include "pika_platform.h" +#include "pikapython_modules.h" + +static void pika_pico2_fail(const char* message) { + (void)pika_platform_write(message, strlen(message)); + for (;;) { + tight_loop_contents(); + } +} + +int main(void) { + PikaStatus status; + + stdio_init_all(); + status = pikaPythonInit(); + if (status != PIKA_STATUS_OK) { + static const char message[] = "PikaPython init failed\r\n"; + pika_pico2_fail(message); + } + +#if PIKA_REPL_ENABLE + status = pikaPythonRepl(); + if (status != PIKA_STATUS_OK) { + static const char message[] = "PikaPython REPL failed\r\n"; + pika_pico2_fail(message); + } +#endif + + pika_application_stop(); + for (;;) { + tight_loop_contents(); + } +} diff --git a/project-templates/pico2/firmware/platform.c b/project-templates/pico2/firmware/platform.c new file mode 100644 index 000000000..2e1e35d9a --- /dev/null +++ b/project-templates/pico2/firmware/platform.c @@ -0,0 +1,506 @@ +#include +#include + +#include "hardware/adc.h" +#include "hardware/clocks.h" +#include "hardware/gpio.h" +#include "hardware/pwm.h" +#include "hardware/sync.h" +#include "pico/stdlib.h" + +#include "pika_capability_config.h" +#include "pika_platform.h" + +#if PIKA_CAPABILITY_MACHINE_PIN_ENABLE || \ + PIKA_CAPABILITY_MACHINE_ADC_ENABLE || \ + PIKA_CAPABILITY_MACHINE_PWM_ENABLE +#include "machine_platform.h" +#endif + +PikaStatus pika_platform_write(const char* data, size_t size) { + size_t index; + if (data == NULL && size != 0u) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + for (index = 0u; index < size; ++index) { + if (stdio_putchar_raw((unsigned char)data[index]) < 0) { + return PIKA_STATUS_IO_ERROR; + } + } + stdio_flush(); + return PIKA_STATUS_OK; +} + +PikaStatus pika_platform_repl_read( + uint8_t* data, + size_t capacity, + size_t* received) { + int value; + if (data == NULL || received == NULL || capacity == 0u) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + value = stdio_getchar(); + if (value < 0) { + *received = 0u; + return PIKA_STATUS_IO_ERROR; + } + data[0] = (uint8_t)value; + *received = 1u; + return PIKA_STATUS_OK; +} + +PikaStatus pika_platform_time_ticks_ms(uint32_t* ticks) { + if (ticks == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + *ticks = to_ms_since_boot(get_absolute_time()); + return PIKA_STATUS_OK; +} + +PikaStatus pika_platform_time_sleep_ms(uint32_t duration) { + sleep_ms(duration); + return PIKA_STATUS_OK; +} + +#if PIKA_CAPABILITY_MACHINE_PIN_ENABLE + +#ifndef PIKA_PICO2_PIN_OBJECT_LIMIT +#define PIKA_PICO2_PIN_OBJECT_LIMIT 16u +#endif + +typedef struct { + int64_t identifier; + PikaMachinePinMode mode; + uint8_t active; +} PikaPico2Pin; + +static PikaPico2Pin pika_pico2_pins[PIKA_PICO2_PIN_OBJECT_LIMIT]; + +static PikaStatus pika_pico2_pin_validate( + const PikaMachinePinConfig* config) { + if (config == NULL || config->identifier < 0 || + config->identifier >= 30) { + return PIKA_STATUS_VALUE_ERROR; + } + if (config->mode != PIKA_MACHINE_PIN_MODE_IN && + config->mode != PIKA_MACHINE_PIN_MODE_OUT && + config->mode != PIKA_MACHINE_PIN_MODE_OPEN_DRAIN) { + return PIKA_STATUS_VALUE_ERROR; + } + if (config->pull != PIKA_MACHINE_PIN_PULL_NONE && + config->pull != PIKA_MACHINE_PIN_PULL_UP && + config->pull != PIKA_MACHINE_PIN_PULL_DOWN) { + return PIKA_STATUS_VALUE_ERROR; + } + if (config->mode == PIKA_MACHINE_PIN_MODE_OUT && + config->pull != PIKA_MACHINE_PIN_PULL_NONE) { + return PIKA_STATUS_VALUE_ERROR; + } + if (config->mode == PIKA_MACHINE_PIN_MODE_OPEN_DRAIN && + config->pull == PIKA_MACHINE_PIN_PULL_DOWN) { + return PIKA_STATUS_VALUE_ERROR; + } + if (config->mode == PIKA_MACHINE_PIN_MODE_IN && + config->initial_value_valid != 0u) { + return PIKA_STATUS_VALUE_ERROR; + } + return PIKA_STATUS_OK; +} + +static void pika_pico2_pin_apply( + const PikaMachinePinConfig* config) { + uint gpio = (uint)config->identifier; + gpio_init(gpio); + if (config->mode == PIKA_MACHINE_PIN_MODE_OPEN_DRAIN) { + gpio_set_pulls(gpio, + config->pull == PIKA_MACHINE_PIN_PULL_UP, + false); + if (config->initial_value_valid != 0u && + config->initial_value == 0u) { + gpio_put(gpio, 0u); + gpio_set_dir(gpio, GPIO_OUT); + } else { + gpio_set_dir(gpio, GPIO_IN); + } + return; + } + gpio_set_pulls(gpio, + config->pull == PIKA_MACHINE_PIN_PULL_UP, + config->pull == PIKA_MACHINE_PIN_PULL_DOWN); + if (config->mode == PIKA_MACHINE_PIN_MODE_OUT && + config->initial_value_valid != 0u) { + gpio_put(gpio, config->initial_value != 0u); + } + gpio_set_dir(gpio, config->mode == PIKA_MACHINE_PIN_MODE_OUT); +} + +static PikaPico2Pin* pika_pico2_pin_allocate(void) { + uint32_t index; + for (index = 0u; index < PIKA_PICO2_PIN_OBJECT_LIMIT; ++index) { + if (pika_pico2_pins[index].active == 0u) { + pika_pico2_pins[index].active = 1u; + return &pika_pico2_pins[index]; + } + } + return NULL; +} + +PikaStatus pika_machine_platform_pin_open( + const PikaMachinePinConfig* config, + void** handle) { + PikaPico2Pin* pin; + PikaStatus status; + if (handle == NULL || config == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + *handle = NULL; + status = pika_pico2_pin_validate(config); + if (status != PIKA_STATUS_OK) { + return status; + } + pin = pika_pico2_pin_allocate(); + if (pin == NULL) { + return PIKA_STATUS_STORAGE_TOO_SMALL; + } + pin->identifier = config->identifier; + pin->mode = config->mode; + pika_pico2_pin_apply(config); + *handle = pin; + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_pin_configure( + void* handle, + const PikaMachinePinConfig* config) { + PikaPico2Pin* pin = (PikaPico2Pin*)handle; + PikaStatus status; + if (pin == NULL || pin->active == 0u || config == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + status = pika_pico2_pin_validate(config); + if (status != PIKA_STATUS_OK || pin->identifier != config->identifier) { + return status == PIKA_STATUS_OK ? PIKA_STATUS_INVALID_ARGUMENT : status; + } + pin->mode = config->mode; + pika_pico2_pin_apply(config); + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_pin_read(void* handle, int* value) { + PikaPico2Pin* pin = (PikaPico2Pin*)handle; + if (pin == NULL || pin->active == 0u || value == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + *value = gpio_get((uint)pin->identifier) != 0u ? 1 : 0; + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_pin_write(void* handle, int value) { + PikaPico2Pin* pin = (PikaPico2Pin*)handle; + uint gpio; + if (pin == NULL || pin->active == 0u || (value != 0 && value != 1)) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + if (pin->mode == PIKA_MACHINE_PIN_MODE_IN) { + return PIKA_STATUS_VALUE_ERROR; + } + gpio = (uint)pin->identifier; + if (pin->mode == PIKA_MACHINE_PIN_MODE_OPEN_DRAIN) { + if (value == 0) { + gpio_put(gpio, 0u); + gpio_set_dir(gpio, GPIO_OUT); + } else { + gpio_set_dir(gpio, GPIO_IN); + } + } else { + gpio_put(gpio, value != 0); + } + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_pin_close(void* handle) { + PikaPico2Pin* pin = (PikaPico2Pin*)handle; + if (pin == NULL || pin->active == 0u) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + gpio_deinit((uint)pin->identifier); + pin->active = 0u; + return PIKA_STATUS_OK; +} + +#endif + +#if PIKA_CAPABILITY_MACHINE_ADC_ENABLE + +typedef struct { + uint8_t channel; + uint8_t active; +} PikaPico2Adc; + +static PikaPico2Adc pika_pico2_adc_objects[4]; +static uint8_t pika_pico2_adc_initialized; + +static PikaStatus pika_pico2_adc_channel( + int64_t identifier, + uint8_t* channel) { + if (channel == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + if (identifier >= 0 && identifier <= 3) { + *channel = (uint8_t)identifier; + } else if (identifier >= 26 && identifier <= 29) { + *channel = (uint8_t)(identifier - 26); + } else if (identifier == 4) { + *channel = 4u; + } else { + return PIKA_STATUS_VALUE_ERROR; + } + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_adc_open( + const PikaMachineAdcConfig* config, + void** handle) { + uint32_t index; + uint8_t channel; + PikaStatus status; + if (config == NULL || handle == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + *handle = NULL; + status = pika_pico2_adc_channel(config->identifier, &channel); + if (status != PIKA_STATUS_OK) { + return status; + } + if (pika_pico2_adc_initialized == 0u) { + adc_init(); + pika_pico2_adc_initialized = 1u; + } + if (channel == 4u) { + adc_set_temp_sensor_enabled(true); + } else { + adc_gpio_init((uint)(26u + channel)); + } + for (index = 0u; index < 4u; ++index) { + if (pika_pico2_adc_objects[index].active == 0u) { + pika_pico2_adc_objects[index].channel = channel; + pika_pico2_adc_objects[index].active = 1u; + *handle = &pika_pico2_adc_objects[index]; + return PIKA_STATUS_OK; + } + } + return PIKA_STATUS_STORAGE_TOO_SMALL; +} + +PikaStatus pika_machine_platform_adc_read( + void* handle, + uint32_t* raw_value, + uint8_t* resolution_bits) { + PikaPico2Adc* adc = (PikaPico2Adc*)handle; + if (adc == NULL || adc->active == 0u || raw_value == NULL || + resolution_bits == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + adc_select_input(adc->channel); + *raw_value = adc_read(); + *resolution_bits = 12u; + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_adc_read_uv( + void* handle, + int64_t* microvolts) { + uint32_t raw_value; + uint8_t resolution_bits; + PikaStatus status; + if (microvolts == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + status = pika_machine_platform_adc_read( + handle, &raw_value, &resolution_bits); + if (status != PIKA_STATUS_OK) { + return status; + } + *microvolts = ((int64_t)raw_value * 3300000ll) / 4095ll; + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_adc_close(void* handle) { + PikaPico2Adc* adc = (PikaPico2Adc*)handle; + if (adc == NULL || adc->active == 0u) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + adc->active = 0u; + return PIKA_STATUS_OK; +} + +#endif + +uintptr_t pika_machine_platform_critical_enter(void) { + return (uintptr_t)save_and_disable_interrupts(); +} + +void pika_machine_platform_critical_leave(uintptr_t state) { + restore_interrupts((uint32_t)state); +} + +#if PIKA_CAPABILITY_MACHINE_PWM_ENABLE + +typedef struct { + uint slice; + uint gpio; + uint32_t frequency_hz; + uint16_t duty_u16; + uint8_t enabled; + uint8_t active; +} PikaPico2Pwm; + +static PikaPico2Pwm pika_pico2_pwm_objects[16]; + +static PikaStatus pika_pico2_pwm_source( + int64_t identifier, + uint* gpio) { + if (gpio == NULL || identifier < 0 || identifier >= 30) { + return PIKA_STATUS_VALUE_ERROR; + } + *gpio = (uint)identifier; + return PIKA_STATUS_OK; +} + +static PikaPico2Pwm* pika_pico2_pwm_allocate(void) { + uint32_t index; + for (index = 0u; index < 16u; ++index) { + if (pika_pico2_pwm_objects[index].active == 0u) { + pika_pico2_pwm_objects[index].active = 1u; + return &pika_pico2_pwm_objects[index]; + } + } + return NULL; +} + +static void pika_pico2_pwm_apply(PikaPico2Pwm* pwm) { + pwm_config config = pwm_get_default_config(); + uint32_t clock_hz = clock_get_hz(clk_sys); + uint32_t divider; + uint32_t top; + uint64_t denominator; + if (pwm->frequency_hz == 0u) { + return; + } + denominator = (uint64_t)pwm->frequency_hz * 65536ull; + divider = (uint32_t)(((uint64_t)clock_hz + denominator / 2ull) / + denominator); + if (divider < 1u) { + divider = 1u; + } else if (divider > 255u) { + divider = 255u; + } + top = clock_hz / (divider * pwm->frequency_hz); + if (top == 0u) { + top = 1u; + } + --top; + if (top > 65535u) { + top = 65535u; + } + pwm_config_set_clkdiv_int(&config, divider); + pwm_config_set_wrap(&config, (uint16_t)top); + pwm_init(pwm->slice, &config, false); + pwm_set_gpio_level(pwm->gpio, + (uint16_t)(((uint32_t)pwm->duty_u16 * (top + 1u)) / + 65535u)); + pwm_set_enabled(pwm->slice, pwm->enabled != 0u); +} + +PikaStatus pika_machine_platform_pwm_open( + const PikaMachinePwmConfig* config, + void** handle) { + PikaPico2Pwm* pwm; + uint gpio; + PikaStatus status; + if (config == NULL || handle == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + *handle = NULL; + status = pika_pico2_pwm_source(config->identifier, &gpio); + if (status != PIKA_STATUS_OK) { + return status; + } + pwm = pika_pico2_pwm_allocate(); + if (pwm == NULL) { + return PIKA_STATUS_STORAGE_TOO_SMALL; + } + pwm->gpio = gpio; + pwm->slice = pwm_gpio_to_slice_num(gpio); + pwm->frequency_hz = 1000u; + pwm->duty_u16 = 0u; + pwm->enabled = 0u; + gpio_set_function(gpio, GPIO_FUNC_PWM); + *handle = pwm; + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_pwm_configure( + void* handle, + const PikaMachinePwmSettings* settings) { + PikaPico2Pwm* pwm = (PikaPico2Pwm*)handle; + if (pwm == NULL || pwm->active == 0u || settings == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + if (settings->frequency_present != 0u) { + pwm->frequency_hz = settings->frequency_hz; + } + if (settings->duty_mode == PIKA_MACHINE_PWM_DUTY_U16) { + pwm->duty_u16 = settings->duty_u16; + } else if (settings->duty_mode == PIKA_MACHINE_PWM_DUTY_NS) { + uint64_t period_ns = 1000000000ull / pwm->frequency_hz; + pwm->duty_u16 = settings->duty_ns >= period_ns + ? 65535u + : (uint16_t)((settings->duty_ns * 65535ull) / + period_ns); + } + pwm->enabled = settings->enabled; + pika_pico2_pwm_apply(pwm); + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_pwm_state( + void* handle, + PikaMachinePwmState* state) { + PikaPico2Pwm* pwm = (PikaPico2Pwm*)handle; + if (pwm == NULL || pwm->active == 0u || state == NULL) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + state->frequency_hz = pwm->frequency_hz; + state->duty_u16 = pwm->duty_u16; + state->duty_ns = ((uint64_t)pwm->duty_u16 * 1000000000ull) / + (uint64_t)pwm->frequency_hz / 65535ull; + state->duty_mode = PIKA_MACHINE_PWM_DUTY_U16; + state->frequency_valid = 1u; + state->enabled = pwm->enabled; + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_pwm_disable(void* handle) { + PikaPico2Pwm* pwm = (PikaPico2Pwm*)handle; + if (pwm == NULL || pwm->active == 0u) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + pwm->enabled = 0u; + pwm_set_enabled(pwm->slice, false); + return PIKA_STATUS_OK; +} + +PikaStatus pika_machine_platform_pwm_close(void* handle) { + PikaPico2Pwm* pwm = (PikaPico2Pwm*)handle; + if (pwm == NULL || pwm->active == 0u) { + return PIKA_STATUS_INVALID_ARGUMENT; + } + pwm_set_enabled(pwm->slice, false); + gpio_set_function(pwm->gpio, GPIO_FUNC_SIO); + gpio_set_dir(pwm->gpio, GPIO_IN); + pwm->active = 0u; + return PIKA_STATUS_OK; +} + +#endif diff --git a/project-templates/pico2/firmware/platform_socket.c b/project-templates/pico2/firmware/platform_socket.c new file mode 100644 index 000000000..d9a477eba --- /dev/null +++ b/project-templates/pico2/firmware/platform_socket.c @@ -0,0 +1,145 @@ +#include "platform_socket.h" + +#include + +void pika_platform_socket_initialize(PikaPlatformSocket* socket) { + if (socket != NULL) { + socket->handle = PIKA_SOCKET_INVALID_HANDLE; + socket->timeout_milliseconds = -1; + } +} + +PikaSocketStatus pika_platform_socket_open( + PikaPlatformSocket* socket, + int family, + int type, + int protocol) { + (void)family; + (void)type; + (void)protocol; + if (socket == NULL) { + return PIKA_SOCKET_INVALID_ARGUMENT; + } + socket->handle = PIKA_SOCKET_INVALID_HANDLE; + return PIKA_SOCKET_IO_ERROR; +} + +PikaSocketStatus pika_platform_socket_close(PikaPlatformSocket* socket) { + if (socket == NULL) { + return PIKA_SOCKET_INVALID_ARGUMENT; + } + pika_platform_socket_initialize(socket); + return PIKA_SOCKET_OK; +} + +PikaSocketStatus pika_platform_socket_bind( + PikaPlatformSocket* socket, + const char* host, + uint16_t port) { + (void)host; + (void)port; + return socket == NULL ? PIKA_SOCKET_INVALID_ARGUMENT : PIKA_SOCKET_CLOSED; +} + +PikaSocketStatus pika_platform_socket_listen( + PikaPlatformSocket* socket, + int backlog) { + (void)backlog; + return socket == NULL ? PIKA_SOCKET_INVALID_ARGUMENT : PIKA_SOCKET_CLOSED; +} + +PikaSocketStatus pika_platform_socket_accept( + PikaPlatformSocket* server, + PikaPlatformSocket* accepted, + char* peer_host, + uint32_t peer_host_capacity, + uint16_t* peer_port) { + (void)accepted; + (void)peer_host; + (void)peer_host_capacity; + (void)peer_port; + return server == NULL ? PIKA_SOCKET_INVALID_ARGUMENT : PIKA_SOCKET_CLOSED; +} + +PikaSocketStatus pika_platform_socket_connect( + PikaPlatformSocket* socket, + const char* host, + uint16_t port) { + (void)host; + (void)port; + return socket == NULL ? PIKA_SOCKET_INVALID_ARGUMENT : PIKA_SOCKET_IO_ERROR; +} + +PikaSocketStatus pika_platform_socket_send( + PikaPlatformSocket* socket, + const uint8_t* data, + uint32_t length, + uint32_t* sent) { + (void)data; + (void)length; + if (sent != NULL) { + *sent = 0u; + } + return socket == NULL || (length != 0u && data == NULL) + ? PIKA_SOCKET_INVALID_ARGUMENT + : PIKA_SOCKET_IO_ERROR; +} + +PikaSocketStatus pika_platform_socket_receive( + PikaPlatformSocket* socket, + uint8_t* data, + uint32_t capacity, + uint32_t* received) { + (void)data; + (void)capacity; + if (received != NULL) { + *received = 0u; + } + return socket == NULL || (capacity != 0u && data == NULL) + ? PIKA_SOCKET_INVALID_ARGUMENT + : PIKA_SOCKET_IO_ERROR; +} + +PikaSocketStatus pika_platform_socket_set_timeout( + PikaPlatformSocket* socket, + int32_t timeout_milliseconds) { + if (socket == NULL || timeout_milliseconds < -1) { + return PIKA_SOCKET_INVALID_ARGUMENT; + } + socket->timeout_milliseconds = timeout_milliseconds; + return PIKA_SOCKET_OK; +} + +PikaSocketStatus pika_platform_socket_resolve_ipv4( + const char* host, + char* address, + uint32_t address_capacity) { + (void)host; + if (address == NULL || address_capacity < PIKA_SOCKET_ADDRESS_BYTE_LIMIT) { + return PIKA_SOCKET_INVALID_ARGUMENT; + } + address[0] = '\0'; + return PIKA_SOCKET_DNS_ERROR; +} + +PikaSocketStatus pika_platform_socket_local_port( + PikaPlatformSocket* socket, + uint16_t* port) { + if (socket == NULL || port == NULL) { + return PIKA_SOCKET_INVALID_ARGUMENT; + } + *port = 0u; + return PIKA_SOCKET_CLOSED; +} + +PikaSocketStatus pika_platform_socket_local_host( + PikaPlatformSocket* socket, + char* host, + uint32_t host_capacity) { + if (socket == NULL || host == NULL || + host_capacity < PIKA_SOCKET_ADDRESS_BYTE_LIMIT) { + return PIKA_SOCKET_INVALID_ARGUMENT; + } + host[0] = '\0'; + return PIKA_SOCKET_CLOSED; +} diff --git a/project-templates/pico2/main.py b/project-templates/pico2/main.py new file mode 100644 index 000000000..551e84c7a --- /dev/null +++ b/project-templates/pico2/main.py @@ -0,0 +1,5 @@ +from machine import Pin + +led = Pin(25, Pin.OUT, value=0) +led.on() +print("PikaPython on Raspberry Pi Pico 2") diff --git a/project-templates/pico2/pikapython.yaml b/project-templates/pico2/pikapython.yaml new file mode 100644 index 000000000..0efd2b67a --- /dev/null +++ b/project-templates/pico2/pikapython.yaml @@ -0,0 +1,48 @@ +version: 1 +dependencies: +- pikapython-aes==1.0.0 +- pikapython-base64==1.0.0 +- pikapython-binascii==1.0.0 +- pikapython-configparser==1.0.0 +- pikapython-ctypes==1.0.0 +- pikapython-eventloop==1.0.0 +- pikapython-flashdb==1.0.0 +- pikapython-fsm==1.0.0 +- pikapython-fuzzypid==1.0.0 +- pikapython-kernel==2.0.0 +- pikapython-hashlib==1.0.0 +- pikapython-hmac==1.0.0 +- pikapython-i18n==1.0.0 +- pikapython-jrpc==1.0.0 +- pikapython-json==1.0.0 +- pikapython-math==1.0.0 +- pikapython-mqtt==1.0.0 +- pikapython-modbus==1.0.0 +- pikapython-modbus-rt==1.0.0 +- pikapython-network==1.0.0 +- pikapython-machine==1.0.0 +- pikapython-requests==1.0.0 +- pikapython-os==1.0.0 +- pikapython-posixfs==1.0.0 +- pikapython-unittest==1.0.0 +- pikapython-re==1.0.0 +- pikapython-random==1.0.0 +- pikapython-socket==1.0.0 +- pikapython-struct==1.0.0 +- pikapython-time==1.0.0 +- pikapython-zlib==1.0.0 +packages: + sourceUrl: https://github.com/pikasTech/PikaPython.git + ref: v2 +capability: + configFile: .pikapython/source/config/pikapython-capabilities.yaml + profile: pico2-full +targets: + pico2: + adapter: cmake + projectFile: CMakeLists.txt + targetName: pico2-firmware + buildDirectory: .pikapython/build/pico2 + arguments: + - -DCMAKE_BUILD_TYPE=MinSizeRel +defaultTarget: pico2 diff --git a/project-templates/pico2/template.yaml b/project-templates/pico2/template.yaml new file mode 100644 index 000000000..5c3fdba26 --- /dev/null +++ b/project-templates/pico2/template.yaml @@ -0,0 +1,11 @@ +version: 1 +id: pico2 +name: Raspberry Pi Pico 2 +architecture: arm-cortex-m33 +mcu: RP2350A +adapter: cmake +machine: pico2 +projectFile: CMakeLists.txt +targetName: pico2-firmware +applicationFile: main.py +interactive: true