0646e607b5
The stm32 and nrf ports already had the behaviour that they would first check if the script exists before executing it, and this patch makes all other ports work the same way. This helps when developing apps because it's hard to tell (when unconditionally trying to execute the scripts) if the resulting OSError at boot up comes from missing boot.py or main.py, or from some other error. And it's not really an error if these scripts don't exist.
382 lines
9.7 KiB
C
382 lines
9.7 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "py/lexer.h"
|
|
#include "py/runtime.h"
|
|
#include "py/stackctrl.h"
|
|
#include "py/gc.h"
|
|
#include "py/mphal.h"
|
|
#include "gccollect.h"
|
|
#include "lib/utils/pyexec.h"
|
|
#include "lib/mp-readline/readline.h"
|
|
#include "lexermemzip.h"
|
|
|
|
#include "Arduino.h"
|
|
|
|
#include "servo.h"
|
|
#include "led.h"
|
|
#include "uart.h"
|
|
#include "pin.h"
|
|
|
|
extern uint32_t _heap_start;
|
|
|
|
void flash_error(int n) {
|
|
for (int i = 0; i < n; i++) {
|
|
led_state(PYB_LED_BUILTIN, 1);
|
|
delay(250);
|
|
led_state(PYB_LED_BUILTIN, 0);
|
|
delay(250);
|
|
}
|
|
}
|
|
|
|
void NORETURN __fatal_error(const char *msg) {
|
|
for (volatile uint delay = 0; delay < 10000000; delay++) {
|
|
}
|
|
led_state(1, 1);
|
|
led_state(2, 1);
|
|
led_state(3, 1);
|
|
led_state(4, 1);
|
|
mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14);
|
|
mp_hal_stdout_tx_strn(msg, strlen(msg));
|
|
for (uint i = 0;;) {
|
|
led_toggle(((i++) & 3) + 1);
|
|
for (volatile uint delay = 0; delay < 10000000; delay++) {
|
|
}
|
|
if (i >= 16) {
|
|
// to conserve power
|
|
__WFI();
|
|
}
|
|
}
|
|
}
|
|
|
|
void nlr_jump_fail(void *val) {
|
|
printf("FATAL: uncaught exception %p\n", val);
|
|
__fatal_error("");
|
|
}
|
|
|
|
void __assert_func(const char *file, int line, const char *func, const char *expr) {
|
|
|
|
printf("Assertion failed: %s, file %s, line %d\n", expr, file, line);
|
|
__fatal_error("");
|
|
}
|
|
|
|
mp_obj_t pyb_analog_read(mp_obj_t pin_obj) {
|
|
uint pin = mp_obj_get_int(pin_obj);
|
|
int val = analogRead(pin);
|
|
return MP_OBJ_NEW_SMALL_INT(val);
|
|
}
|
|
|
|
mp_obj_t pyb_analog_write(mp_obj_t pin_obj, mp_obj_t val_obj) {
|
|
uint pin = mp_obj_get_int(pin_obj);
|
|
int val = mp_obj_get_int(val_obj);
|
|
analogWrite(pin, val);
|
|
return mp_const_none;
|
|
}
|
|
|
|
mp_obj_t pyb_analog_write_resolution(mp_obj_t res_obj) {
|
|
int res = mp_obj_get_int(res_obj);
|
|
analogWriteResolution(res);
|
|
return mp_const_none;
|
|
}
|
|
|
|
mp_obj_t pyb_analog_write_frequency(mp_obj_t pin_obj, mp_obj_t freq_obj) {
|
|
uint pin = mp_obj_get_int(pin_obj);
|
|
int freq = mp_obj_get_int(freq_obj);
|
|
analogWriteFrequency(pin, freq);
|
|
return mp_const_none;
|
|
}
|
|
|
|
#if 0
|
|
// get lots of info about the board
|
|
static mp_obj_t pyb_info(void) {
|
|
// get and print unique id; 96 bits
|
|
{
|
|
byte *id = (byte*)0x40048058;
|
|
printf("ID=%02x%02x%02x%02x:%02x%02x%02x%02x:%02x%02x%02x%02x\n", id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11]);
|
|
}
|
|
|
|
// get and print clock speeds
|
|
printf("CPU=%u\nBUS=%u\nMEM=%u\n", F_CPU, F_BUS, F_MEM);
|
|
|
|
// to print info about memory
|
|
{
|
|
printf("_sdata=%p\n", &_sdata);
|
|
printf("_edata=%p\n", &_edata);
|
|
printf("_sbss=%p\n", &_sbss);
|
|
printf("_ebss=%p\n", &_ebss);
|
|
printf("_estack=%p\n", &_estack);
|
|
printf("_etext=%p\n", &_etext);
|
|
printf("_heap_start=%p\n", &_heap_start);
|
|
}
|
|
|
|
// GC info
|
|
{
|
|
gc_info_t info;
|
|
gc_info(&info);
|
|
printf("GC:\n");
|
|
printf(" %u total\n", info.total);
|
|
printf(" %u used %u free\n", info.used, info.free);
|
|
printf(" 1=%u 2=%u m=%u\n", info.num_1block, info.num_2block, info.max_block);
|
|
}
|
|
|
|
#if 0
|
|
// free space on flash
|
|
{
|
|
DWORD nclst;
|
|
FATFS *fatfs;
|
|
f_getfree("0:", &nclst, &fatfs);
|
|
printf("LFS free: %u bytes\n", (uint)(nclst * fatfs->csize * 512));
|
|
}
|
|
#endif
|
|
|
|
return mp_const_none;
|
|
}
|
|
|
|
#endif
|
|
|
|
#define RAM_START (0x1FFF8000) // fixed for chip
|
|
#define HEAP_END (0x20006000) // tunable
|
|
#define RAM_END (0x20008000) // fixed for chip
|
|
|
|
#if 0
|
|
|
|
void gc_helper_get_regs_and_clean_stack(mp_uint_t *regs, mp_uint_t heap_end);
|
|
|
|
mp_obj_t pyb_gc(void) {
|
|
gc_collect();
|
|
return mp_const_none;
|
|
}
|
|
|
|
mp_obj_t pyb_gpio(int n_args, mp_obj_t *args) {
|
|
//assert(1 <= n_args && n_args <= 2);
|
|
|
|
uint pin = mp_obj_get_int(args[0]);
|
|
if (pin > CORE_NUM_DIGITAL) {
|
|
goto pin_error;
|
|
}
|
|
|
|
if (n_args == 1) {
|
|
// get pin
|
|
pinMode(pin, INPUT);
|
|
return MP_OBJ_NEW_SMALL_INT(digitalRead(pin));
|
|
}
|
|
|
|
// set pin
|
|
pinMode(pin, OUTPUT);
|
|
digitalWrite(pin, mp_obj_is_true(args[1]));
|
|
return mp_const_none;
|
|
|
|
pin_error:
|
|
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "pin %d does not exist", pin));
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_gpio_obj, 1, 2, pyb_gpio);
|
|
|
|
#if 0
|
|
mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
|
|
mp_obj_t *items = mp_obj_get_array_fixed_n(arg, 4);
|
|
uint8_t data[4];
|
|
data[0] = mp_obj_get_int(items[0]);
|
|
data[1] = mp_obj_get_int(items[1]);
|
|
data[2] = mp_obj_get_int(items[2]);
|
|
data[3] = mp_obj_get_int(items[3]);
|
|
usb_hid_send_report(data);
|
|
return mp_const_none;
|
|
}
|
|
#endif
|
|
|
|
#endif // 0
|
|
|
|
STATIC mp_obj_t pyb_config_source_dir = MP_OBJ_NULL;
|
|
STATIC mp_obj_t pyb_config_main = MP_OBJ_NULL;
|
|
STATIC mp_obj_t pyb_config_usb_mode = MP_OBJ_NULL;
|
|
|
|
mp_obj_t pyb_source_dir(mp_obj_t source_dir) {
|
|
if (mp_obj_is_str(source_dir)) {
|
|
pyb_config_source_dir = source_dir;
|
|
}
|
|
return mp_const_none;
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_1(pyb_source_dir_obj, pyb_source_dir);
|
|
|
|
mp_obj_t pyb_main(mp_obj_t main) {
|
|
if (mp_obj_is_str(main)) {
|
|
pyb_config_main = main;
|
|
}
|
|
return mp_const_none;
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_1(pyb_main_obj, pyb_main);
|
|
|
|
STATIC mp_obj_t pyb_usb_mode(mp_obj_t usb_mode) {
|
|
if (mp_obj_is_str(usb_mode)) {
|
|
pyb_config_usb_mode = usb_mode;
|
|
}
|
|
return mp_const_none;
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_1(pyb_usb_mode_obj, pyb_usb_mode);
|
|
|
|
#if 0
|
|
|
|
mp_obj_t pyb_delay(mp_obj_t count) {
|
|
delay(mp_obj_get_int(count));
|
|
return mp_const_none;
|
|
}
|
|
|
|
mp_obj_t pyb_led(mp_obj_t state) {
|
|
led_state(PYB_LED_BUILTIN, mp_obj_is_true(state));
|
|
return state;
|
|
}
|
|
|
|
#endif // 0
|
|
|
|
#if 0
|
|
char *strdup(const char *str) {
|
|
uint32_t len = strlen(str);
|
|
char *s2 = m_new(char, len + 1);
|
|
memcpy(s2, str, len);
|
|
s2[len] = 0;
|
|
return s2;
|
|
}
|
|
#endif
|
|
|
|
int main(void) {
|
|
// TODO: Put this in a more common initialization function.
|
|
// Turn on STKALIGN which keeps the stack 8-byte aligned for interrupts
|
|
// (per EABI)
|
|
#define SCB_CCR_STKALIGN (1 << 9)
|
|
SCB_CCR |= SCB_CCR_STKALIGN;
|
|
|
|
mp_stack_ctrl_init();
|
|
mp_stack_set_limit(10240);
|
|
|
|
pinMode(LED_BUILTIN, OUTPUT);
|
|
led_init();
|
|
|
|
// int first_soft_reset = true;
|
|
|
|
soft_reset:
|
|
|
|
led_state(PYB_LED_BUILTIN, 1);
|
|
|
|
// GC init
|
|
gc_init(&_heap_start, (void*)HEAP_END);
|
|
|
|
// MicroPython init
|
|
mp_init();
|
|
mp_obj_list_init(mp_sys_path, 0);
|
|
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script)
|
|
mp_obj_list_init(mp_sys_argv, 0);
|
|
|
|
readline_init0();
|
|
|
|
pin_init0();
|
|
|
|
#if 0
|
|
// add some functions to the python namespace
|
|
{
|
|
mp_store_name(MP_QSTR_help, mp_make_function_n(0, pyb_help));
|
|
mp_obj_t m = mp_obj_new_module(MP_QSTR_pyb);
|
|
mp_store_attr(m, MP_QSTR_info, mp_make_function_n(0, pyb_info));
|
|
mp_store_attr(m, MP_QSTR_source_dir, mp_make_function_n(1, pyb_source_dir));
|
|
mp_store_attr(m, MP_QSTR_main, mp_make_function_n(1, pyb_main));
|
|
mp_store_attr(m, MP_QSTR_gc, mp_make_function_n(0, pyb_gc));
|
|
mp_store_attr(m, MP_QSTR_delay, mp_make_function_n(1, pyb_delay));
|
|
mp_store_attr(m, MP_QSTR_led, mp_make_function_n(1, pyb_led));
|
|
mp_store_attr(m, MP_QSTR_LED, (mp_obj_t)&pyb_led_type);
|
|
mp_store_attr(m, MP_QSTR_analogRead, mp_make_function_n(1, pyb_analog_read));
|
|
mp_store_attr(m, MP_QSTR_analogWrite, mp_make_function_n(2, pyb_analog_write));
|
|
mp_store_attr(m, MP_QSTR_analogWriteResolution, mp_make_function_n(1, pyb_analog_write_resolution));
|
|
mp_store_attr(m, MP_QSTR_analogWriteFrequency, mp_make_function_n(2, pyb_analog_write_frequency));
|
|
|
|
mp_store_attr(m, MP_QSTR_gpio, (mp_obj_t)&pyb_gpio_obj);
|
|
mp_store_attr(m, MP_QSTR_Servo, mp_make_function_n(0, pyb_Servo));
|
|
mp_store_name(MP_QSTR_pyb, m);
|
|
}
|
|
#endif
|
|
|
|
#if MICROPY_MODULE_FROZEN
|
|
pyexec_frozen_module("boot.py");
|
|
#else
|
|
if (!pyexec_file_if_exists("/boot.py")) {
|
|
flash_error(4);
|
|
}
|
|
#endif
|
|
|
|
// Turn bootup LED off
|
|
led_state(PYB_LED_BUILTIN, 0);
|
|
|
|
// run main script
|
|
#if MICROPY_MODULE_FROZEN
|
|
pyexec_frozen_module("main.py");
|
|
#else
|
|
{
|
|
vstr_t *vstr = vstr_new(16);
|
|
vstr_add_str(vstr, "/");
|
|
if (pyb_config_main == MP_OBJ_NULL) {
|
|
vstr_add_str(vstr, "main.py");
|
|
} else {
|
|
vstr_add_str(vstr, mp_obj_str_get_str(pyb_config_main));
|
|
}
|
|
if (!pyexec_file_if_exists(vstr_null_terminated_str(vstr))) {
|
|
flash_error(3);
|
|
}
|
|
vstr_free(vstr);
|
|
}
|
|
#endif
|
|
|
|
// enter REPL
|
|
// REPL mode can change, or it can request a soft reset
|
|
for (;;) {
|
|
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
|
|
if (pyexec_raw_repl() != 0) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (pyexec_friendly_repl() != 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("MPY: soft reboot\n");
|
|
|
|
// first_soft_reset = false;
|
|
goto soft_reset;
|
|
}
|
|
|
|
// stub out __libc_init_array. It's called by mk20dx128.c and is used to call
|
|
// global C++ constructors. Since this is a C-only projects, we don't need to
|
|
// call constructors.
|
|
void __libc_init_array(void) {
|
|
}
|
|
|
|
// ultoa is used by usb_init_serialnumber. Normally ultoa would be provided
|
|
// by nonstd.c from the teensy core, but it conflicts with some of the
|
|
// MicroPython functions in string0.c, so we provide ultoa here.
|
|
char * ultoa(unsigned long val, char *buf, int radix)
|
|
{
|
|
unsigned digit;
|
|
int i=0, j;
|
|
char t;
|
|
|
|
while (1) {
|
|
digit = val % radix;
|
|
buf[i] = ((digit < 10) ? '0' + digit : 'A' + digit - 10);
|
|
val /= radix;
|
|
if (val == 0) break;
|
|
i++;
|
|
}
|
|
buf[i + 1] = 0;
|
|
for (j=0; j < i; j++, i--) {
|
|
t = buf[j];
|
|
buf[j] = buf[i];
|
|
buf[i] = t;
|
|
}
|
|
return buf;
|
|
}
|