Skip to content

Commit

Permalink
stm: Add support for ctrl-C to interrupt running Python.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dpgeorge committed Feb 1, 2014
1 parent 532f2c3 commit 01156d5
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 1 deletion.
1 change: 1 addition & 0 deletions stm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ SRC_C = \
string0.c \
malloc0.c \
systick.c \
pendsv.c \
gccollect.c \
lexerfatfs.c \
led.c \
Expand Down
1 change: 1 addition & 0 deletions stm/led.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ void led_toggle(pyb_led_t led) {
return;
}

// XXX this assumes LED is driven by a low MCU output (true for PYBv3, false for PYBv4)
if (!(port->ODR & pin)) {
// turn LED off
PYB_LED_OFF(port, pin);
Expand Down
17 changes: 16 additions & 1 deletion stm/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "gc.h"
#include "gccollect.h"
#include "systick.h"
#include "pendsv.h"
#include "led.h"
#include "servo.h"
#include "lcd.h"
Expand Down Expand Up @@ -327,7 +328,7 @@ int readline(vstr_t *line, const char *prompt) {
}
}
if (escape == 0) {
if (c == 4 && vstr_len(line) == len) {
if (c == VCP_CHAR_CTRL_D && vstr_len(line) == len) {
return 0;
} else if (c == '\r') {
stdout_tx_str("\r\n");
Expand Down Expand Up @@ -435,10 +436,14 @@ void do_repl(void) {
nlr_buf_t nlr;
uint32_t start = sys_tick_counter;
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);
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
nlr_pop();
} else {
// 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);
}

Expand Down Expand Up @@ -488,11 +493,15 @@ bool do_file(const char *filename) {

nlr_buf_t nlr;
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);
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
nlr_pop();
return true;
} else {
// 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);
return false;
}
Expand Down Expand Up @@ -560,6 +569,10 @@ mp_obj_t pyb_rng_get(void) {
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) {
// TODO disable JTAG

Expand Down Expand Up @@ -592,6 +605,7 @@ int main(void) {

// basic sub-system init
sys_tick_init();
pendsv_init();
led_init();

#if MICROPY_HW_ENABLE_RTC
Expand Down Expand Up @@ -693,6 +707,7 @@ int main(void) {
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, 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_open, rt_make_function_n(2, pyb_io_open));
Expand Down
49 changes: 49 additions & 0 deletions stm/pendsv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#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]);
}
*/
}
3 changes: 3 additions & 0 deletions stm/pendsv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
void pendsv_init(void);
void pendsv_nlr_jump(mp_obj_t o);
void pendsv_isr_handler(void);
2 changes: 2 additions & 0 deletions stm/stm32fxxx_it.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ void DebugMon_Handler(void)
*/
void PendSV_Handler(void)
{
extern void pendsv_isr_handler(void);
pendsv_isr_handler();
}

/**
Expand Down
22 changes: 22 additions & 0 deletions stm/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "mpconfig.h"
#include "qstr.h"
#include "obj.h"
#include "pendsv.h"
#include "usb.h"

#ifdef USE_DEVICE_MODE
Expand All @@ -23,6 +24,8 @@ static int dev_is_enabled = 0;
static char rx_buf[64];
static int rx_buf_in;
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) {
#ifdef USE_DEVICE_MODE
Expand All @@ -33,17 +36,36 @@ void pyb_usb_dev_init(void) {
}
rx_buf_in = 0;
rx_buf_out = 0;
interrupt_char = VCP_CHAR_NONE;
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
}

bool usb_vcp_is_enabled(void) {
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) {
if (dev_is_enabled) {
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];
if (rx_buf_in >= sizeof(rx_buf)) {
rx_buf_in = 0;
Expand Down
5 changes: 5 additions & 0 deletions stm/usb.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#define VCP_CHAR_NONE (0)
#define VCP_CHAR_CTRL_C (3)
#define VCP_CHAR_CTRL_D (4)

void pyb_usb_dev_init(void);
bool usb_vcp_is_enabled(void);
void usb_vcp_set_interrupt_char(int c);
int usb_vcp_rx_any(void);
char usb_vcp_rx_get(void);
void usb_vcp_send_str(const char* str);
Expand Down

0 comments on commit 01156d5

Please sign in to comment.