PaulStoffregen
Well-known member
Will this substantially slow the no-event yield() case?
Right now it's pretty bad with the 6 serial events and 1 usb serial.
Right now it's pretty bad with the 6 serial events and 1 usb serial.
So with this Syntax, I then assume that passing in en EventResponder is not optional. Which is probably not an issue, although with SPI there were cases where I did not really need one, but instead had a couple of methods. One that asked if the transfer was done and another waited until it was done.Code:bool transfer(const void *txBuffer, void *rxBuffer, size_t count, EventResponderRef event_responder);
Internally you'd store the EventResponderRef as a pointer, but pointer syntax is generally not accepted for Arduino APIs so a reference is used to pass it in. Then later when the operation completes, call its triggerEvent() function.
I don't see any need to pass another pointer in this case, since there's already 2 for the data. The event supports a data pointer, but most cases probably won't make any use of it.
EventResponder eresp;
SPI.transfer(mybuf, nullptr, count, eresp);
....
// Check to see if it is done
if (!eresp) {
// has not triggered yet
}
// wait for the SPI to be done.
eresp.waitForEvent(eresp, my_timeout);
Will this substantially slow the no-event yield() case?
Right now it's pretty bad with the 6 serial events and 1 usb serial.
#include <EventResponder.h>
EventResponder myevent;
EventResponder mytimerevent;
EventResponder mytimerevent2;
MillisTimer mytimer;
MillisTimer mytimer2;
void setup() {
while (!Serial) ;
Serial.println("EventResponder test");
myevent.attach(dosomething);
mytimerevent.attach(dotimer);
mytimerevent2.attach(dotimer2);
mytimer2.beginRepeat(750, mytimerevent2);
}
void loop() {
Serial.println("loop trigger dosomething");
myevent.triggerEvent();
delay(4000);
}
void dosomething(EventResponderRef event)
{
Serial.println("dosomething event");
mytimer.begin(550, mytimerevent);
}
void dotimer(EventResponderRef event)
{
Serial.println("oneshot timer event");
}
void dotimer2(EventResponderRef event)
{
Serial.println(" repeating timer event");
}
uint8_t active_spi_transfer = 0;
EventResponder eresp;
void my_func(EventResponderRef erf) {
active_spi_transfer = 0;
}
...
eresp.attachImmediate(my_func);
...
active_spi_transfer = 1;
SPI.transfer(Buffer1, nullptr, cnt, eresp);
// Do stuff to fill up buffer 2
while (active_spi_transfer) ;
...
EventResponder eresp;
...
eresp.clearEvent();
SPI.transfer(Buffer1, nullptr, cnt, eresp);
// Do stuff to fill up buffer 2
eresp.waitEvent(?);
...
// Wait for event(s) to occur. These are most likely to be useful when
// used with a scheduler or RTOS.
bool waitForEvent(EventResponderRef event, int timeout);
EventResponder * waitForEvent(EventResponder *list, int listsize, int timeout);
IMO, there should be an event-free async API. Since the EventResponder parameter apparently can't be a pointer, it needs to be a separate method.Still working with the SPI updates to use this. With your proposed way of passing the EventResponder by reference, as I mentioned in previous posting, was wondering about suggested way of doing something where you may not necessarily need something called, but more like you simply want to know if your operation completed and maybe wait...
IMO, it makes sense to keep those in addition to EventResponder.Before the EventResponder I had methods on SPI to ask of an async transfer was still active and likewise a method to wait...
Since the user needs to allocate those dummy EventResponder objects and needs to be concerned about the lifetime, that's rather ugly and kludgy.But wondering if most of the above could be done internally with the Event Responder object...
EventResponder eresp;
...
while (more data) {
// fill first buffer
erest.waitForEvent(...) ;
eresp.clearEvent();
SPI.transfer(Buffer1, nullptr, cnt, eresp);
// Do stuff to fill up buffer 2
eresp.waitEvent(?);
erest.clearEvent();
SPI.transfer(Buffer2, nullptr, cnt, eresp);
}
Take an FPGA chip.
Implement a CPU core that is deterministic.
Put as many CPUs as needed on a board.
Let them communicate with each other...
...and with the external world.
Up to 8 LM32 cores, with private code/data memory.
Programed in bare metal C, using standard GCC tool chain.
Each core runs a single task in a tight loop.
No caches, no interrupts.
ISR(vector) {
setEvent(myEvent);
}
// could run with it's own context or shared context.
void loop5() {
waitEventOr(myEvents);
// process event.
}
/*
* Example to demonstrate thread definition, semaphores, and thread sleep.
*/
#include <NilRTOS.h>
// The LED is attached to pin 13 on Arduino.
const uint8_t LED_PIN = 13;
// Declare a semaphore with an inital counter value of zero.
SEMAPHORE_DECL(sem, 0);
//------------------------------------------------------------------------------
/*
* Thread 1, turn the LED off when signalled by thread 2.
*/
// Declare a stack with 128 bytes beyond context switch and interrupt needs.
NIL_WORKING_AREA(waThread1, 128);
// Declare the thread function for thread 1.
NIL_THREAD(Thread1, arg) {
while (TRUE) {
// Wait for signal from thread 2.
nilSemWait(&sem);
// Turn LED off.
digitalWrite(LED_PIN, LOW);
}
}
//------------------------------------------------------------------------------
/*
* Thread 2, turn the LED on and signal thread 1 to turn the LED off.
*/
// Declare a stack with 128 bytes beyond context switch and interrupt needs.
NIL_WORKING_AREA(waThread2, 128);
// Declare the thread function for thread 2.
NIL_THREAD(Thread2, arg) {
pinMode(LED_PIN, OUTPUT);
while (TRUE) {
// Turn LED on.
digitalWrite(LED_PIN, HIGH);
// Sleep for 200 milliseconds.
nilThdSleepMilliseconds(200);
// Signal thread 1 to turn LED off.
nilSemSignal(&sem);
// Sleep for 200 milliseconds.
nilThdSleepMilliseconds(200);
}
}
//------------------------------------------------------------------------------
/*
* Threads static table, one entry per thread. A thread's priority is
* determined by its position in the table with highest priority first.
*
* These threads start with a null argument. A thread's name may also
* be null to save RAM since the name is currently not used.
*/
NIL_THREADS_TABLE_BEGIN()
NIL_THREADS_TABLE_ENTRY("thread1", Thread1, NULL, waThread1, sizeof(waThread1))
NIL_THREADS_TABLE_ENTRY("thread2", Thread2, NULL, waThread2, sizeof(waThread2))
NIL_THREADS_TABLE_END()
//------------------------------------------------------------------------------
void setup() {
// Start Nil RTOS.
nilSysBegin();
}
//------------------------------------------------------------------------------
void loop() {
// Not used.
}
Interesting stuff Bill Greiman :: https://github.com/greiman/NilRTOS-Arduino.
Funny 'those scientists' have atypical non-trivial hardware customized to do what is needed - so the software seems easy - with the complexity in silicon. Though with 27 Km to cover localized processing is quite distributed.
void setup() {
// put your setup code for CPU0 here, to run once:
}
void loop() {
// put your main code for CPU0 here, to run repeatedly:
}
void setup1() {
// put your setup code for CPU1 here, to run once:
}
void loop1() {
// put your main code for CPU1 here, to run repeatedly:
}
void setup2() {
// put your setup code for CPU2 here, to run once:
}
void loop2() {
// put your main code for CPU2 here, to run repeatedly:
}
at least when not running this on top of an RTOS
while (1) {
// Time for next record.
logTime += LOG_INTERVAL_TICKS;
// Wait until time for next sample.
chThdSleepUntil(logTime);
// Start ADC conversion.
writeReg(ADS_ADD, CONFIG_REG, EXAMPLE_CONFIG);
// Wait for ADC done interrupt.
msg_t rtn = chBSemWaitTimeout(&isrSem, LOG_INTERVAL_TICKS);
// Remember ADC done time.
uint32_t m = micros();
if (rtn != MSG_OK) {
Serial.println("Missing ADC ready interrupt");
while (true) {}
}
// Get a free record.
if (chSemWaitTimeout(&fifoSpace, TIME_IMMEDIATE) != MSG_OK) {
// FIFO full indicate missed point
error++;
continue;
}
// Put data in FIFO record.
FifoItem_t* p = &fifoArray[fifoHead];
p->usec = m;
p->value = readData(ADS_ADD);
p->error = error;
error = 0;
// Signal loop() there is new data.
chSemSignal(&fifoData);
// Advance FIFO head.
fifoHead = fifoHead < (FIFO_SIZE - 1) ? fifoHead + 1 : 0;
}
// Example to test Event Responder.
#include "Wire.h"
const uint8_t ADC_ALERT_PIN = 2;
const uint8_t CONFIG_REG = 1;
const uint8_t DATA_REG = 0;
const uint8_t LO_THRESH = 2;
const uint8_t HI_THRESH = 3;
const uint8_t I2C_ADD = 0x48;
const uint16_t ADC_CONFIG = 0xC5E8;
const uint16_t HI_THRESH_READY = 0x8000;
const uint16_t LO_THRESH_READY = 0x0000;
volatile uint16_t adcData = 0;
volatile uint32_t count = 0;
//------------------------------------------------------------------------------
void adcDoneInterrupt() {
// Need eventResponder call that will cause adcRead
// to be executed with interrupts enabled.
// Move count++ to adcRead when EventResponder is used.
count++;
}
//------------------------------------------------------------------------------
void adcRead() {
Wire.beginTransmission(I2C_ADD);
Wire.write(DATA_REG);
Wire.endTransmission();
Wire.requestFrom(I2C_ADD, (uint8_t)2);
// Will store data in a FIFO in real app.
adcData = (Wire.read() << 8) | Wire.read();
}
//------------------------------------------------------------------------------
void adcWriteReg(uint8_t add, uint8_t reg, uint16_t val) {
Wire.beginTransmission(add);
Wire.write(reg);
Wire.write(val >> 8);
Wire.write(val & 0xFF);
Wire.endTransmission();
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
while(!Serial);
Serial.println("Start");
// Attach Alert pin to interrupt.
pinMode(ADC_ALERT_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ADC_ALERT_PIN), adcDoneInterrupt, RISING);
// Setup for ADC done interrupts.
Wire.begin();
Wire.setClock(400000);
adcWriteReg(I2C_ADD, HI_THRESH, HI_THRESH_READY);
adcWriteReg(I2C_ADD, LO_THRESH, LO_THRESH_READY);
}
//------------------------------------------------------------------------------
void loop() {
uint32_t tmp = count;
// Start ADC conversion.
adcWriteReg(I2C_ADD, CONFIG_REG, ADC_CONFIG);
// Wait for ADC done
while (count == tmp) {}
adcRead(); // Remove when EventResponder works
Serial.print(count);
Serial.print(',');
Serial.println(adcData);
while (count > 9);
delay(500);
}
Start
1,4495
2,4433
3,4391
4,4347
5,4319
6,4433
7,4435
8,4457
9,4553
10,4498
"D:\arduino-1.8.2\hardware\teensy/../tools/arm/bin/arm-none-eabi-g++" -E -CC -x c++ -w -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -felide-constructors -std=gnu++11 -fno-rtti -mthumb -mcpu=cortex-m0plus -fsingle-precision-constant -D__MKL26Z64__ -DTEENSYDUINO=137 -DARDUINO=10802 -DF_CPU=48000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-ID:\arduino-1.8.2\hardware\teensy\avr\cores\teensy3" "-IC:\Users\kurte\Documents\Arduino\libraries\SPI" "C:\Users\kurte\AppData\Local\Temp\arduino_build_85486\sketch\Teensyview_ScreenDemo.ino.cpp" -o "C:\Users\kurte\AppData\Local\Temp\arduino_build_85486\preproc\ctags_target_for_gcc_minus_e.cpp"
In file included from C:\Users\kurte\Documents\Arduino\Teensy Tests\Teensyview_ScreenDemo\Teensyview_ScreenDemo.ino:21:0:
C:\Users\kurte\Documents\Arduino\libraries\SPI/SPI.h:25:28: fatal error: EventResponder.h: No such file or directory
compilation terminated.
#if defined(__has_include) && __has_include(<EventResponder.h>)
#warning "Have EventResponder.h."
#else
#warning "Don't have EventResponder.h."
#endif