A couple of minimalist changes. systick_isr - By default it does nothing with the event responder. Only if the user calls the attachInterrupt member of EventResponder will it install the version of the ISR that checks the list for any active ISR event Responders. T4 Yield - try to keep bitfields for things yield should test Make it such that yield hopefully can test to see there is nothing to do and returns quickly. There are some interesting limitations, that is that the serialEvent handling code. Both for USB and hardware Serial can only remove it self if the default eventHandler is called once, as I don't know anyway to detect if the default code (weak linkage) is included or if it is a user version. So waits until called to remove it self from active list. Sketches can force this by simply calling the event method right after calling begin on the serial port.teensy4-core
extern "C" volatile uint32_t systick_cycle_count; | extern "C" volatile uint32_t systick_cycle_count; | ||||
extern "C" uint32_t systick_safe_read; // micros() synchronization | extern "C" uint32_t systick_safe_read; // micros() synchronization | ||||
extern "C" void systick_isr(void) | extern "C" void systick_isr(void) | ||||
{ | |||||
systick_cycle_count = ARM_DWT_CYCCNT; | |||||
systick_millis_count++; | |||||
} | |||||
extern "C" void systick_isr_with_timer_events(void) | |||||
{ | { | ||||
systick_cycle_count = ARM_DWT_CYCCNT; | systick_cycle_count = ARM_DWT_CYCCNT; | ||||
systick_millis_count++; | systick_millis_count++; |
* your function is called only one time, based on the last trigger | * your function is called only one time, based on the last trigger | ||||
* event. | * event. | ||||
*/ | */ | ||||
extern "C" void systick_isr_with_timer_events(void); | |||||
class EventResponder; | class EventResponder; | ||||
typedef EventResponder& EventResponderRef; | typedef EventResponder& EventResponderRef; | ||||
detachNoInterrupts(); | detachNoInterrupts(); | ||||
_function = function; | _function = function; | ||||
_type = EventTypeYield; | _type = EventTypeYield; | ||||
yield_active_check_flags |= YIELD_CHECK_EVENT_RESPONDER; // user setup a yield type... | |||||
enableInterrupts(irq); | enableInterrupts(irq); | ||||
} | } | ||||
_function = function; | _function = function; | ||||
_type = EventTypeInterrupt; | _type = EventTypeInterrupt; | ||||
SCB_SHPR3 |= 0x00FF0000; // configure PendSV, lowest priority | SCB_SHPR3 |= 0x00FF0000; // configure PendSV, lowest priority | ||||
// Make sure we are using the systic ISR that process this | |||||
_VectorsRam[15] = systick_isr_with_timer_events; | |||||
enableInterrupts(irq); | enableInterrupts(irq); | ||||
} | } | ||||
// used with a scheduler or RTOS. | // used with a scheduler or RTOS. | ||||
bool waitForEvent(EventResponderRef event, int timeout); | bool waitForEvent(EventResponderRef event, int timeout); | ||||
EventResponder * waitForEvent(EventResponder *list, int listsize, int timeout); | EventResponder * waitForEvent(EventResponder *list, int listsize, int timeout); | ||||
static void runFromYield() { | static void runFromYield() { | ||||
if (!firstYield) return; | |||||
if (!firstYield) return; | |||||
// First, check if yield was called from an interrupt | // First, check if yield was called from an interrupt | ||||
// never call normal handler functions from any interrupt context | // never call normal handler functions from any interrupt context | ||||
uint32_t ipsr; | uint32_t ipsr; |
if (!serial_event_handler_checks[hardware->serial_index]) { | if (!serial_event_handler_checks[hardware->serial_index]) { | ||||
serial_event_handler_checks[hardware->serial_index] = hardware->serial_event_handler_check; // clear it out | serial_event_handler_checks[hardware->serial_index] = hardware->serial_event_handler_check; // clear it out | ||||
serial_event_handlers_active++; | serial_event_handlers_active++; | ||||
yield_active_check_flags |= YIELD_CHECK_HARDWARE_SERIAL; | |||||
} | } | ||||
} | } | ||||
if (serial_event_handler_checks[hardware->serial_index]) { | if (serial_event_handler_checks[hardware->serial_index]) { | ||||
serial_event_handler_checks[hardware->serial_index] = nullptr; // clear it out | serial_event_handler_checks[hardware->serial_index] = nullptr; // clear it out | ||||
serial_event_handlers_active--; | serial_event_handlers_active--; | ||||
if (!serial_event_handlers_active) yield_active_check_flags &= ~YIELD_CHECK_HARDWARE_SERIAL; | |||||
} | } | ||||
} | } |
void _reboot_Teensyduino_(void) __attribute__((noreturn)); | void _reboot_Teensyduino_(void) __attribute__((noreturn)); | ||||
void _restart_Teensyduino_(void) __attribute__((noreturn)); | void _restart_Teensyduino_(void) __attribute__((noreturn)); | ||||
// Define a set of flags to know which things yield should check when called. | |||||
extern uint8_t yield_active_check_flags; | |||||
#define YIELD_CHECK_USB_SERIAL 0x1 // check the USB for Serial.available() | |||||
#define YIELD_CHECK_HARDWARE_SERIAL 0x2 // check Hardware Serial ports available | |||||
#define YIELD_CHECK_EVENT_RESPONDER 0x4 // User has created eventResponders that use yield | |||||
void yield(void); | void yield(void); | ||||
void delay(uint32_t msec); | void delay(uint32_t msec); |
#endif | #endif | ||||
#endif // F_CPU | #endif // F_CPU | ||||
uint8_t usb_enable_serial_event_processing = 1; | |||||
void serialEvent() __attribute__((weak)); | void serialEvent() __attribute__((weak)); | ||||
void serialEvent() {usb_enable_serial_event_processing = 0;} | |||||
void serialEvent() {yield_active_check_flags &= ~YIELD_CHECK_USB_SERIAL;} |
extern uint8_t usb_enable_serial_event_processing; // from usb_inst.cpp | extern uint8_t usb_enable_serial_event_processing; // from usb_inst.cpp | ||||
uint8_t yield_active_check_flags = YIELD_CHECK_USB_SERIAL; // default to check USB. | |||||
void yield(void) __attribute__ ((weak)); | void yield(void) __attribute__ ((weak)); | ||||
void yield(void) | void yield(void) | ||||
{ | { | ||||
static uint8_t running=0; | static uint8_t running=0; | ||||
if (!yield_active_check_flags) return; // nothing to do | |||||
if (running) return; // TODO: does this need to be atomic? | if (running) return; // TODO: does this need to be atomic? | ||||
running = 1; | running = 1; | ||||
// USB Serail - Add hack to minimize impact... | // USB Serail - Add hack to minimize impact... | ||||
if (usb_enable_serial_event_processing && Serial.available()) serialEvent(); | |||||
if ((yield_active_check_flags & YIELD_CHECK_USB_SERIAL) && Serial.available()) serialEvent(); | |||||
// Current workaround until integrate with EventResponder. | // Current workaround until integrate with EventResponder. | ||||
if (HardwareSerial::serial_event_handlers_active) HardwareSerial::processSerialEvents(); | |||||
if (yield_active_check_flags & YIELD_CHECK_HARDWARE_SERIAL) HardwareSerial::processSerialEvents(); | |||||
running = 0; | running = 0; | ||||
EventResponder::runFromYield(); | |||||
if (yield_active_check_flags & YIELD_CHECK_EVENT_RESPONDER) EventResponder::runFromYield(); | |||||
}; | }; |