Skip to content

Commit 01156d5

Browse files
committed
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.
1 parent 532f2c3 commit 01156d5

File tree

8 files changed

+99
-1
lines changed

8 files changed

+99
-1
lines changed

stm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ SRC_C = \
4545
string0.c \
4646
malloc0.c \
4747
systick.c \
48+
pendsv.c \
4849
gccollect.c \
4950
lexerfatfs.c \
5051
led.c \

stm/led.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ void led_toggle(pyb_led_t led) {
103103
return;
104104
}
105105

106+
// XXX this assumes LED is driven by a low MCU output (true for PYBv3, false for PYBv4)
106107
if (!(port->ODR & pin)) {
107108
// turn LED off
108109
PYB_LED_OFF(port, pin);

stm/main.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "gc.h"
3131
#include "gccollect.h"
3232
#include "systick.h"
33+
#include "pendsv.h"
3334
#include "led.h"
3435
#include "servo.h"
3536
#include "lcd.h"
@@ -327,7 +328,7 @@ int readline(vstr_t *line, const char *prompt) {
327328
}
328329
}
329330
if (escape == 0) {
330-
if (c == 4 && vstr_len(line) == len) {
331+
if (c == VCP_CHAR_CTRL_D && vstr_len(line) == len) {
331332
return 0;
332333
} else if (c == '\r') {
333334
stdout_tx_str("\r\n");
@@ -435,10 +436,14 @@ void do_repl(void) {
435436
nlr_buf_t nlr;
436437
uint32_t start = sys_tick_counter;
437438
if (nlr_push(&nlr) == 0) {
439+
usb_vcp_set_interrupt_char(VCP_CHAR_CTRL_C); // allow ctrl-C to interrupt us
438440
rt_call_function_0(module_fun);
441+
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
439442
nlr_pop();
440443
} else {
441444
// uncaught exception
445+
// FIXME it could be that an interrupt happens just before we disable it here
446+
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
442447
mp_obj_print_exception((mp_obj_t)nlr.ret_val);
443448
}
444449

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

489494
nlr_buf_t nlr;
490495
if (nlr_push(&nlr) == 0) {
496+
usb_vcp_set_interrupt_char(VCP_CHAR_CTRL_C); // allow ctrl-C to interrupt us
491497
rt_call_function_0(module_fun);
498+
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
492499
nlr_pop();
493500
return true;
494501
} else {
495502
// uncaught exception
503+
// FIXME it could be that an interrupt happens just before we disable it here
504+
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
496505
mp_obj_print_exception((mp_obj_t)nlr.ret_val);
497506
return false;
498507
}
@@ -560,6 +569,10 @@ mp_obj_t pyb_rng_get(void) {
560569
return mp_obj_new_int(RNG_GetRandomNumber() >> 16);
561570
}
562571

572+
mp_obj_t pyb_millis(void) {
573+
return mp_obj_new_int(sys_tick_counter);
574+
}
575+
563576
int main(void) {
564577
// TODO disable JTAG
565578

@@ -592,6 +605,7 @@ int main(void) {
592605

593606
// basic sub-system init
594607
sys_tick_init();
608+
pendsv_init();
595609
led_init();
596610

597611
#if MICROPY_HW_ENABLE_RTC
@@ -693,6 +707,7 @@ int main(void) {
693707
rt_store_attr(m, MP_QSTR_Usart, rt_make_function_n(2, pyb_Usart));
694708
rt_store_attr(m, qstr_from_str("ADC_all"), (mp_obj_t)&pyb_ADC_all_obj);
695709
rt_store_attr(m, MP_QSTR_ADC, (mp_obj_t)&pyb_ADC_obj);
710+
rt_store_attr(m, qstr_from_str("millis"), rt_make_function_n(0, pyb_millis));
696711
rt_store_name(MP_QSTR_pyb, m);
697712

698713
rt_store_name(MP_QSTR_open, rt_make_function_n(2, pyb_io_open));

stm/pendsv.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include <stdlib.h>
2+
#include <stm32f4xx.h>
3+
4+
#include "misc.h"
5+
#include "mpconfig.h"
6+
#include "qstr.h"
7+
#include "obj.h"
8+
#include "pendsv.h"
9+
10+
static mp_obj_t pendsv_object = MP_OBJ_NULL;
11+
12+
void pendsv_init(void) {
13+
// set PendSV interrupt at lowest priority
14+
NVIC_SetPriority(PendSV_IRQn, 0xff);
15+
}
16+
17+
// call this function to raise a pending exception during an interrupt
18+
// it will wait until all interrupts are finished then raise the given
19+
// exception object using nlr_jump in the context of the top-level thread
20+
void pendsv_nlr_jump(mp_obj_t o) {
21+
pendsv_object = o;
22+
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
23+
}
24+
25+
void pendsv_isr_handler(void) {
26+
// re-jig the stack so that when we return from this interrupt handler
27+
// it returns instead to nlr_jump with argument pendsv_object
28+
__asm volatile (
29+
"ldr r0, pendsv_object_ptr\n"
30+
"ldr r0, [r0]\n"
31+
"str r0, [sp, #0]\n"
32+
"ldr r0, nlr_jump_ptr\n"
33+
"str r0, [sp, #24]\n"
34+
"bx lr\n"
35+
".align 2\n"
36+
"pendsv_object_ptr: .word pendsv_object\n"
37+
"nlr_jump_ptr: .word nlr_jump\n"
38+
);
39+
40+
/*
41+
sp[0] = r0 = exception to raise
42+
sp[6] = pc = nlr_jump
43+
uint32_t x[2] = {0x424242, 0xdeaddead};
44+
printf("PendSV: %p\n", x);
45+
for (uint32_t *p = (uint32_t*)(((uint32_t)x - 15) & 0xfffffff0), i = 64; i > 0; p += 4, i -= 4) {
46+
printf(" %p: %08x %08x %08x %08x\n", p, p[0], p[1], p[2], p[3]);
47+
}
48+
*/
49+
}

stm/pendsv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
void pendsv_init(void);
2+
void pendsv_nlr_jump(mp_obj_t o);
3+
void pendsv_isr_handler(void);

stm/stm32fxxx_it.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ void DebugMon_Handler(void)
151151
*/
152152
void PendSV_Handler(void)
153153
{
154+
extern void pendsv_isr_handler(void);
155+
pendsv_isr_handler();
154156
}
155157

156158
/**

stm/usb.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "mpconfig.h"
1212
#include "qstr.h"
1313
#include "obj.h"
14+
#include "pendsv.h"
1415
#include "usb.h"
1516

1617
#ifdef USE_DEVICE_MODE
@@ -23,6 +24,8 @@ static int dev_is_enabled = 0;
2324
static char rx_buf[64];
2425
static int rx_buf_in;
2526
static int rx_buf_out;
27+
static int interrupt_char = VCP_CHAR_NONE;
28+
mp_obj_t mp_const_vcp_interrupt = MP_OBJ_NULL;
2629

2730
void pyb_usb_dev_init(void) {
2831
#ifdef USE_DEVICE_MODE
@@ -33,17 +36,36 @@ void pyb_usb_dev_init(void) {
3336
}
3437
rx_buf_in = 0;
3538
rx_buf_out = 0;
39+
interrupt_char = VCP_CHAR_NONE;
3640
dev_is_enabled = 1;
41+
42+
// create an exception object for interrupting by VCP
43+
mp_const_vcp_interrupt = mp_obj_new_exception(qstr_from_str("VCPInterrupt"));
3744
#endif
3845
}
3946

4047
bool usb_vcp_is_enabled(void) {
4148
return dev_is_enabled;
4249
}
4350

51+
void usb_vcp_set_interrupt_char(int c) {
52+
if (dev_is_enabled) {
53+
interrupt_char = c;
54+
}
55+
}
56+
4457
void usb_vcp_receive(const char *buf, uint32_t len) {
4558
if (dev_is_enabled) {
4659
for (int i = 0; i < len; i++) {
60+
61+
// catch special interrupt character
62+
if (buf[i] == interrupt_char) {
63+
// raise exception when interrupts are finished
64+
pendsv_nlr_jump(mp_const_vcp_interrupt);
65+
interrupt_char = VCP_CHAR_NONE;
66+
continue;
67+
}
68+
4769
rx_buf[rx_buf_in++] = buf[i];
4870
if (rx_buf_in >= sizeof(rx_buf)) {
4971
rx_buf_in = 0;

stm/usb.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
#define VCP_CHAR_NONE (0)
2+
#define VCP_CHAR_CTRL_C (3)
3+
#define VCP_CHAR_CTRL_D (4)
4+
15
void pyb_usb_dev_init(void);
26
bool usb_vcp_is_enabled(void);
7+
void usb_vcp_set_interrupt_char(int c);
38
int usb_vcp_rx_any(void);
49
char usb_vcp_rx_get(void);
510
void usb_vcp_send_str(const char* str);

0 commit comments

Comments
 (0)