Commit 01156d51 authored by Damien George's avatar Damien George
Browse files

stm: Add support for ctrl-C to interrupt running Python.

Using PendSV interrupt at lowest priority, code can now raise an
exception during an interrupt by calling pendsv_nlr_jump.  The exception
will be raised when all interrupts are finished.  This is used to trap
ctrl-C from the USB VCP to break out of running Python code.
parent 532f2c30
...@@ -45,6 +45,7 @@ SRC_C = \ ...@@ -45,6 +45,7 @@ SRC_C = \
string0.c \ string0.c \
malloc0.c \ malloc0.c \
systick.c \ systick.c \
pendsv.c \
gccollect.c \ gccollect.c \
lexerfatfs.c \ lexerfatfs.c \
led.c \ led.c \
......
...@@ -103,6 +103,7 @@ void led_toggle(pyb_led_t led) { ...@@ -103,6 +103,7 @@ void led_toggle(pyb_led_t led) {
return; return;
} }
// XXX this assumes LED is driven by a low MCU output (true for PYBv3, false for PYBv4)
if (!(port->ODR & pin)) { if (!(port->ODR & pin)) {
// turn LED off // turn LED off
PYB_LED_OFF(port, pin); PYB_LED_OFF(port, pin);
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "gc.h" #include "gc.h"
#include "gccollect.h" #include "gccollect.h"
#include "systick.h" #include "systick.h"
#include "pendsv.h"
#include "led.h" #include "led.h"
#include "servo.h" #include "servo.h"
#include "lcd.h" #include "lcd.h"
...@@ -327,7 +328,7 @@ int readline(vstr_t *line, const char *prompt) { ...@@ -327,7 +328,7 @@ int readline(vstr_t *line, const char *prompt) {
} }
} }
if (escape == 0) { if (escape == 0) {
if (c == 4 && vstr_len(line) == len) { if (c == VCP_CHAR_CTRL_D && vstr_len(line) == len) {
return 0; return 0;
} else if (c == '\r') { } else if (c == '\r') {
stdout_tx_str("\r\n"); stdout_tx_str("\r\n");
...@@ -435,10 +436,14 @@ void do_repl(void) { ...@@ -435,10 +436,14 @@ void do_repl(void) {
nlr_buf_t nlr; nlr_buf_t nlr;
uint32_t start = sys_tick_counter; uint32_t start = sys_tick_counter;
if (nlr_push(&nlr) == 0) { if (nlr_push(&nlr) == 0) {
usb_vcp_set_interrupt_char(VCP_CHAR_CTRL_C); // allow ctrl-C to interrupt us
rt_call_function_0(module_fun); rt_call_function_0(module_fun);
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
nlr_pop(); nlr_pop();
} else { } else {
// uncaught exception // uncaught exception
// FIXME it could be that an interrupt happens just before we disable it here
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
mp_obj_print_exception((mp_obj_t)nlr.ret_val); mp_obj_print_exception((mp_obj_t)nlr.ret_val);
} }
...@@ -488,11 +493,15 @@ bool do_file(const char *filename) { ...@@ -488,11 +493,15 @@ bool do_file(const char *filename) {
nlr_buf_t nlr; nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) { if (nlr_push(&nlr) == 0) {
usb_vcp_set_interrupt_char(VCP_CHAR_CTRL_C); // allow ctrl-C to interrupt us
rt_call_function_0(module_fun); rt_call_function_0(module_fun);
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
nlr_pop(); nlr_pop();
return true; return true;
} else { } else {
// uncaught exception // uncaught exception
// FIXME it could be that an interrupt happens just before we disable it here
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
mp_obj_print_exception((mp_obj_t)nlr.ret_val); mp_obj_print_exception((mp_obj_t)nlr.ret_val);
return false; return false;
} }
...@@ -560,6 +569,10 @@ mp_obj_t pyb_rng_get(void) { ...@@ -560,6 +569,10 @@ mp_obj_t pyb_rng_get(void) {
return mp_obj_new_int(RNG_GetRandomNumber() >> 16); return mp_obj_new_int(RNG_GetRandomNumber() >> 16);
} }
mp_obj_t pyb_millis(void) {
return mp_obj_new_int(sys_tick_counter);
}
int main(void) { int main(void) {
// TODO disable JTAG // TODO disable JTAG
...@@ -592,6 +605,7 @@ int main(void) { ...@@ -592,6 +605,7 @@ int main(void) {
// basic sub-system init // basic sub-system init
sys_tick_init(); sys_tick_init();
pendsv_init();
led_init(); led_init();
#if MICROPY_HW_ENABLE_RTC #if MICROPY_HW_ENABLE_RTC
...@@ -693,6 +707,7 @@ soft_reset: ...@@ -693,6 +707,7 @@ soft_reset:
rt_store_attr(m, MP_QSTR_Usart, rt_make_function_n(2, pyb_Usart)); rt_store_attr(m, MP_QSTR_Usart, rt_make_function_n(2, pyb_Usart));
rt_store_attr(m, qstr_from_str("ADC_all"), (mp_obj_t)&pyb_ADC_all_obj); rt_store_attr(m, qstr_from_str("ADC_all"), (mp_obj_t)&pyb_ADC_all_obj);
rt_store_attr(m, MP_QSTR_ADC, (mp_obj_t)&pyb_ADC_obj); rt_store_attr(m, MP_QSTR_ADC, (mp_obj_t)&pyb_ADC_obj);
rt_store_attr(m, qstr_from_str("millis"), rt_make_function_n(0, pyb_millis));
rt_store_name(MP_QSTR_pyb, m); rt_store_name(MP_QSTR_pyb, m);
rt_store_name(MP_QSTR_open, rt_make_function_n(2, pyb_io_open)); rt_store_name(MP_QSTR_open, rt_make_function_n(2, pyb_io_open));
......
#include <stdlib.h>
#include <stm32f4xx.h>
#include "misc.h"
#include "mpconfig.h"
#include "qstr.h"
#include "obj.h"
#include "pendsv.h"
static mp_obj_t pendsv_object = MP_OBJ_NULL;
void pendsv_init(void) {
// set PendSV interrupt at lowest priority
NVIC_SetPriority(PendSV_IRQn, 0xff);
}
// call this function to raise a pending exception during an interrupt
// it will wait until all interrupts are finished then raise the given
// exception object using nlr_jump in the context of the top-level thread
void pendsv_nlr_jump(mp_obj_t o) {
pendsv_object = o;
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}
void pendsv_isr_handler(void) {
// re-jig the stack so that when we return from this interrupt handler
// it returns instead to nlr_jump with argument pendsv_object
__asm volatile (
"ldr r0, pendsv_object_ptr\n"
"ldr r0, [r0]\n"
"str r0, [sp, #0]\n"
"ldr r0, nlr_jump_ptr\n"
"str r0, [sp, #24]\n"
"bx lr\n"
".align 2\n"
"pendsv_object_ptr: .word pendsv_object\n"
"nlr_jump_ptr: .word nlr_jump\n"
);
/*
sp[0] = r0 = exception to raise
sp[6] = pc = nlr_jump
uint32_t x[2] = {0x424242, 0xdeaddead};
printf("PendSV: %p\n", x);
for (uint32_t *p = (uint32_t*)(((uint32_t)x - 15) & 0xfffffff0), i = 64; i > 0; p += 4, i -= 4) {
printf(" %p: %08x %08x %08x %08x\n", p, p[0], p[1], p[2], p[3]);
}
*/
}
void pendsv_init(void);
void pendsv_nlr_jump(mp_obj_t o);
void pendsv_isr_handler(void);
...@@ -151,6 +151,8 @@ void DebugMon_Handler(void) ...@@ -151,6 +151,8 @@ void DebugMon_Handler(void)
*/ */
void PendSV_Handler(void) void PendSV_Handler(void)
{ {
extern void pendsv_isr_handler(void);
pendsv_isr_handler();
} }
/** /**
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "mpconfig.h" #include "mpconfig.h"
#include "qstr.h" #include "qstr.h"
#include "obj.h" #include "obj.h"
#include "pendsv.h"
#include "usb.h" #include "usb.h"
#ifdef USE_DEVICE_MODE #ifdef USE_DEVICE_MODE
...@@ -23,6 +24,8 @@ static int dev_is_enabled = 0; ...@@ -23,6 +24,8 @@ static int dev_is_enabled = 0;
static char rx_buf[64]; static char rx_buf[64];
static int rx_buf_in; static int rx_buf_in;
static int rx_buf_out; static int rx_buf_out;
static int interrupt_char = VCP_CHAR_NONE;
mp_obj_t mp_const_vcp_interrupt = MP_OBJ_NULL;
void pyb_usb_dev_init(void) { void pyb_usb_dev_init(void) {
#ifdef USE_DEVICE_MODE #ifdef USE_DEVICE_MODE
...@@ -33,7 +36,11 @@ void pyb_usb_dev_init(void) { ...@@ -33,7 +36,11 @@ void pyb_usb_dev_init(void) {
} }
rx_buf_in = 0; rx_buf_in = 0;
rx_buf_out = 0; rx_buf_out = 0;
interrupt_char = VCP_CHAR_NONE;
dev_is_enabled = 1; dev_is_enabled = 1;
// create an exception object for interrupting by VCP
mp_const_vcp_interrupt = mp_obj_new_exception(qstr_from_str("VCPInterrupt"));
#endif #endif
} }
...@@ -41,9 +48,24 @@ bool usb_vcp_is_enabled(void) { ...@@ -41,9 +48,24 @@ bool usb_vcp_is_enabled(void) {
return dev_is_enabled; return dev_is_enabled;
} }
void usb_vcp_set_interrupt_char(int c) {
if (dev_is_enabled) {
interrupt_char = c;
}
}
void usb_vcp_receive(const char *buf, uint32_t len) { void usb_vcp_receive(const char *buf, uint32_t len) {
if (dev_is_enabled) { if (dev_is_enabled) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
// catch special interrupt character
if (buf[i] == interrupt_char) {
// raise exception when interrupts are finished
pendsv_nlr_jump(mp_const_vcp_interrupt);
interrupt_char = VCP_CHAR_NONE;
continue;
}
rx_buf[rx_buf_in++] = buf[i]; rx_buf[rx_buf_in++] = buf[i];
if (rx_buf_in >= sizeof(rx_buf)) { if (rx_buf_in >= sizeof(rx_buf)) {
rx_buf_in = 0; rx_buf_in = 0;
......
#define VCP_CHAR_NONE (0)
#define VCP_CHAR_CTRL_C (3)
#define VCP_CHAR_CTRL_D (4)
void pyb_usb_dev_init(void); void pyb_usb_dev_init(void);
bool usb_vcp_is_enabled(void); bool usb_vcp_is_enabled(void);
void usb_vcp_set_interrupt_char(int c);
int usb_vcp_rx_any(void); int usb_vcp_rx_any(void);
char usb_vcp_rx_get(void); char usb_vcp_rx_get(void);
void usb_vcp_send_str(const char* str); void usb_vcp_send_str(const char* str);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment