GlobalVent / wiki

central place organize jamvent
GNU General Public License v3.0
3 stars 0 forks source link

photon sim: i2c slave #138

Closed ralphBellofattoSTX closed 4 years ago

ralphBellofattoSTX commented 4 years ago

create a base i2c slave class and state machine for the photon to be able emulate 2 sets of 3 i2c devices.

Eventually these drive classes that emulate each i2c device.

ralphBellofattoSTX commented 4 years ago

Starting with temporary wiring of the photon i2c device (differs from the board being layed out). image

A simple programming change can move these pins when we get boards and are able to populate them.

first cut will be to connect the rpi to the device and run the i2cdetect program on the pi and get the bit banger on the ph0ton to ack it.

ralphBellofattoSTX commented 4 years ago

started on the class definitions and getting clean compile out of the photon workbench.

NOTE: I was not able to get the debugger to work. I suspect that this requires an external Jtag debug device which would better work on the photon Argon.

note to self, for more complex programs, develop on the argon then switch for production.

ralphBellofattoSTX commented 4 years ago

I2c state machine to reference image

ralphBellofattoSTX commented 4 years ago

note to self, using ssstream (ostringstream) objects to deal with debug io, causes the the flash to overflow....

so.... drags in too much gorp... need another strategy for doing debug with high speed gorpage...

Need to define my own class to handle the debug print...

This also means a basic debug io technique i frequently use for debug io is not compatable with the small mem footprint.

ralphBellofattoSTX commented 4 years ago

had a setback.

Downloading to address = 0x080a0000, size = 462792
_dfu-util: Last page at 0x08110fc7 is not writeable
make[2]: *** [program-dfu] Error 74

Even after factor reset and reboot of host machine.

so photon not programmable any more.

Prior to this malfunction, was seeing issues with being able to even see 100khz i2c pulses on the photon.

It was not even seeing the i2c clock transitions.

So, we may need to slow down the clock.

Found this little bit of advice for doing that. https://www.raspberrypi-spy.co.uk/2018/02/change-raspberry-pi-i2c-bus-speed/

ralphBellofattoSTX commented 4 years ago

got some trace info on how frequently we can listen for io in a polling loop. wrote this minimal loop:

void loop() {
    // turn on "led" pin (built-in blue LED light)
    digitalWrite(led, HIGH);
    digitalWrite(led, LOW);
    return;

and we see: SDS00001

and: SDS00002

so with a digital write we can do ~3.3us

and when we exit "loop" and then return, a full 1ms has passed by...

also if we try to NOT exit loop, then we are no longer able to program the device.

so,,, this is going to be tricker than we thought.

ralphBellofattoSTX commented 4 years ago

so, bottom line, is a bitbanger i2c slave is not going to work unless we can get called in loop faster.. a lot faster...

ralphBellofattoSTX commented 4 years ago

interrupts:

PhotonNot supported on the Photon (you can't use attachInterrupt on these pins):
D0, A5 (shared with SETUP button)
No restrictions on the Photon (all of these can be used at the same time):
D5, D6, D7, A2, WKP, TX, RX
Shared on the Photon (only one pin for each bullet item can be used at the same time):
D1, A4
D2, A0, A3
D3, DAC
D4, A1

So, we need to be selective with the interrupt io...

and possibly what we attach to it....

need to make a test bench where we hm... demonstrate interrupts with something.

there is also this little note:

NOTE: pinMode() MUST be called prior to calling attachInterrupt() to set the desired mode for the interrupt pin (INPUT, INPUT_PULLUP or INPUT_PULLDOWN).

but one of the things we need to see is falling and rising SDA with SCL high, for start and stop. and we also need to change directions on the SDA pin.

image

ralphBellofattoSTX commented 4 years ago

with digital write fast we get a much shorter time between writes. (40ns)....

SDS00004

ralphBellofattoSTX commented 4 years ago

we might be able to combine the digtalFastWrite with an interrupt strategy, this may mean we have to select different pins....

some obvious questions,

What happens when we have an attached interrupt and then reverse the direction the an io pin. Do we have to cancel the interrupt.

What is the timing overhead of the set interrupt....? is it feasible to enable the SDA interrupt on the rising edge of SCL and disable it on the falling edge?

Going to have to get more timing info before we can have confidence in a design.

ralphBellofattoSTX commented 4 years ago

this suggests the following measurements.

measure the delay between the two waves..

ralphBellofattoSTX commented 4 years ago

here is the i2c timing we need to catch. SDS00007

alcohen commented 4 years ago

BTW, I’m pretty sure the debugger works on the VS Code-based version of the Particle IDE.

On Fri, Jun 5, 2020 at 5:56 PM ralph bellofatto notifications@github.com wrote:

started on the class definitions and getting clean compile out of the photon workbench.

NOTE: I was not able to get the debugger to work. I suspect that this requires an external Jtag debug device which would better work on the photon Argon.

note to self, for more complex programs, develop on the argon then switch for production.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/GlobalVent/wiki/issues/138#issuecomment-639844872, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAOIMWXDJQ4MKJJ5GRRN23RVFSZHANCNFSM4NR26O7Q .

ralphBellofattoSTX commented 4 years ago

@alcohen yes, that is where i ran into the debugger capabilities. It is looking for some USB debugging device and I suspect a jtag connection for the photon, which will take over 5 of of the digital IO pins. So, i'll experiment with the argon.

however, a more immediate issue here is what i discovered above, the time interval in the "loop" is 1ms from loop to loop, so we can't really poll i2c in there. so this will need to be some sort of interrupt based setup, and there are limits on the io pins, so if we did not pick 4 i2c pins that can have an independent interrupt on them, then it is time to take out the Xacto knife and wire wrap wire to reroute a couple of pins.

Polling for 100Khz signals is just not going to work.

alcohen commented 4 years ago

Ah, sorry, I meant debugging over the USB.

Do you think the slow speeds are a function of the RTOS?

On Thu, Jun 11, 2020 at 7:55 PM ralph bellofatto notifications@github.com wrote:

@alcohen https://github.com/alcohen yes, that is where i ran into the debugger capabilities. It is looking for some USB debugging device and I suspect a jtag connection for the photon, which will take over 5 of of the digital IO pins. So, i'll experiment with the argon.

however, a more immediate issue here is what i discovered above, the time interval in the "loop" is 1ms from loop to loop, so we can't really poll i2c in there. so this will need to be some sort of interrupt based setup, and there are limits on the io pins, so if we did not pick 4 i2c pins that can have an independent interrupt on them, then it is time to take out the Xacto knife and wire wrap wire to reroute a couple of pins.

Polling for 100Khz signals is just not going to work.

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/GlobalVent/wiki/issues/138#issuecomment-642987366, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAOIMSYRRXTPDO6AKODVN3RWFVH5ANCNFSM4NR26O7Q .

ralphBellofattoSTX commented 4 years ago

debugging over usb seems to require an openocd compatible device, which the vscode plugin can't find with just the photon attached.

As far as the speeds, yes i believe it is due to the RTOS, two issues.

This causes the program to completely miss the i2c events unless they are really-really slow.

So, either I need to see if either

ralphBellofattoSTX commented 4 years ago

some other options are (manual mode). https://docs.particle.io/reference/device-os/firmware/photon/#manual-mode

and system thread: https://docs.particle.io/reference/device-os/firmware/photon/#system-thread

So, lets see how those work before I try to rework the entire control loop.

ralphBellofattoSTX commented 4 years ago

with system mode and persistence turned on.

SYSTEM_MODE(MANUAL);
int led = D6;
// SETUP function runs one time at start when device is powered on
void setup() {
    // set "led" pin to be an output
    pinMode(led, OUTPUT);
}

// LOOP function runs in repeating loop (after setup finishes)
void loop() {
    // turn on "led" pin (built-in blue LED light)
    digitalWriteFast(led, HIGH);
    digitalWriteFast(led, LOW);
}

quite a bit of jitter. between 1-8us and variable... seems 8us is the max...

SDS00008

ralphBellofattoSTX commented 4 years ago

system thread enabled: ( in both these cases, the occasional long pulse is interesting..

SYSTEM_THREAD(ENABLED);
int led = D6;
// SETUP function runs one time at start when device is powered on
void setup() {
    // set "led" pin to be an output
    pinMode(led, OUTPUT);
}

// LOOP function runs in repeating loop (after setup finishes)
void loop() {
    // turn on "led" pin (built-in blue LED light)
    digitalWriteFast(led, HIGH);
    digitalWriteFast(led, LOW);
}

SDS00010

ralphBellofattoSTX commented 4 years ago

To do an i2c slave at 100khz we need to detect and edge and respond within 5us, so even this mode is no viable unless we slow down the I2c bus from the rpi.

Which is doable for testing....

ralphBellofattoSTX commented 4 years ago

Both system thread and manual mode. The ocasional pulse that is 5us long is interesting.

That implies that sometimes that something happens to streach the pulse. Either an interrupt or something else.

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
int led = D6;
// SETUP function runs one time at start when device is powered on
void setup() {
    // set "led" pin to be an output
    pinMode(led, OUTPUT);
}

// LOOP function runs in repeating loop (after setup finishes)
void loop() {
    // turn on "led" pin (built-in blue LED light)
    digitalWriteFast(led, HIGH);
    digitalWriteFast(led, LOW);
}

SDS00011

ralphBellofattoSTX commented 4 years ago

single thread block... streached hi pulse is still a problem... https://docs.particle.io/reference/device-os/firmware/photon/#system-thread

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
int led = D6;
// SETUP function runs one time at start when device is powered on
void setup() {
    // set "led" pin to be an output
    pinMode(led, OUTPUT);
}

// LOOP function runs in repeating loop (after setup finishes)
void loop() {
    SINGLE_THREADED_BLOCK() {
        // code here is executed atomically, without task switching
        // or interrupts
        digitalWriteFast(led, HIGH);
        digitalWriteFast(led, LOW);
    }
}

SDS00012

ralphBellofattoSTX commented 4 years ago

trying ATOMIC_BLOCK -- the extended pulse goes away....

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
int led = D6;
// SETUP function runs one time at start when device is powered on
void setup() {
    // set "led" pin to be an output
    pinMode(led, OUTPUT);
}

// LOOP function runs in repeating loop (after setup finishes)
void loop() {
    ATOMIC_BLOCK() {
        // code here is executed atomically, without task switching
        // or interrupts
        digitalWriteFast(led, HIGH);
        digitalWriteFast(led, LOW);
    }
}

SDS00013

ralphBellofattoSTX commented 4 years ago

next, interrupt latency, how fast can we do an interrupt and then turn around a reply within an atomic block.

This suggests an interrupt based approach, rather than polling in the device..

ralphBellofattoSTX commented 4 years ago

interrupt latency test: (quite a bit of jitter), the nominal 2us is ok, but sometimes it is quite a bit longer... which would cause the i2c slave to miss samples.

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
int IOUT = D6;
int IIN  = D5;
int TOUT = A0;
// SETUP function runs one time at start when device is powered on
unsigned cnt = 0;
void pinChangeInt() {
    digitalWriteFast(TOUT, cnt);
}
void setup() {
    // set "led" pin to be an output
    pinMode(TOUT, OUTPUT);
    pinMode(IOUT, OUTPUT);
    pinMode(IIN,  INPUT_PULLUP);
    attachInterrupt(IIN, pinChangeInt, CHANGE);      

}
// LOOP function runs in repeating loop (after setup finishes)
void loop() {
    ATOMIC_BLOCK() {
        // code here is executed atomically, without task switching
        // or interrupts
        cnt++;
        digitalWriteFast(IOUT, cnt);
    }
    delay(1);
}

SDS00016 SDS00017

ralphBellofattoSTX commented 4 years ago

can we play with interrupt priorities? (yes). and it improves the latency...

    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      

SDS00018 SDS00019

So, now to come up with a design that will work based on interrupts.

Next proof of concept, is can we trigger an interrupt, on the falling edge of SDA (start) and then disconnect the interrupt and process the i2c transaction while polling in the interrupt handler.

And then reenable the stop event or a timeout of 9*4 bit times.

Do time functions work in the interrupt handler?

dcstraney commented 4 years ago

Thanks for the interrupt summary, was very useful. Only one conflict luckily, will be bridging A0 to A4 to allow RPi_SDA to get an interrupt: also checking in a new version of the simulator schematic to GitHub which shows the rework.

ralphBellofattoSTX commented 4 years ago

doing proof of concept to see if we can handle the entire cycle within an interrupt routine.

To detect the start and stop we have to interrupt on the SDA pin which also requires us to reverse direction while we are handling the interrupt.

// SETUP function runs one time at start when device is powered on
unsigned cnt = 0;
void pinChangeInt() {
    unsigned d;
    d=pinReadFast(IIN);
    pinMode(IIN,OUTPUT);
    pinMode(IIN,INPUT_PULLUP);
    digitalWriteFast(TOUT, d);
}
void setup() {
    // set "led" pin to be an output
    pinMode(TOUT, OUTPUT);
    pinMode(IOUT, OUTPUT);
    pinMode(IIN,  INPUT_PULLUP);
    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      

}
// LOOP function runs in repeating loop (after setup finishes)
void loop() {
    ATOMIC_BLOCK() {
        // code here is executed atomically, without task switching
        // or interrupts
        cnt++;
        digitalWriteFast(IOUT, cnt);
    }
    delay(1);
}

causes the device to become un-programmable without a physical reset


_dfu-util: No DFU capable USB device available
make[2]: *** [program-dfu] Error 74
make[1]: *** [modules/photon/user-part] Error 2
make: *** [flash-user] Error 2
The terminal process terminated with exit code: 2

so 
ralphBellofattoSTX commented 4 years ago

we need to detect the start and stop conditions.

https://user-images.githubusercontent.com/38866041/84447669-a4a8b800-ac16-11ea-8f7c-e5f0da4f3691.png

ralphBellofattoSTX commented 4 years ago

testing this sequence, can we disable the interrupt during the cycle and then re-enable it when reversing the SDA output:

This blows our time budget of 3us..

    digitalWriteFast(TOUT, 1);
    #if 1
    detachInterrupt(IIN);
    d=pinReadFast(IIN);
    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      
    #endif
    digitalWriteFast(TOUT, 0);

SDS00020

ralphBellofattoSTX commented 4 years ago

this test, doing the detatch in the interrupt routine and then re-arm.

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
int IOUT = D6;
int IIN  = D5;
int TOUT = A0;
// SETUP function runs one time at start when device is powered on
unsigned cnt = 0;
void pinChangeInt() {
    unsigned d;
    digitalWriteFast(TOUT, 1);
    detachInterrupt(IIN);
    d=pinReadFast(IIN);
    digitalWriteFast(TOUT, 0);
}
void setup() {
    // set "led" pin to be an output
    pinMode(TOUT, OUTPUT);
    pinMode(IOUT, OUTPUT);
    pinMode(IIN,  INPUT_PULLUP);
    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      

}
// LOOP function runs in repeating loop (after setup finishes)
void loop() {
    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      

    ATOMIC_BLOCK() {
        // code here is executed atomically, without task switching
        // or interrupts
        cnt++;
        digitalWriteFast(IOUT, cnt);
    }
    delay(1);
}

SDS00022

That leaves us with 1us left to do all our processing...

we can re-arm once we see the stop transaction, and hope we get around to re-arm at the end,

there is a possibility we may miss 2 back to back i2c transactions...

ralphBellofattoSTX commented 4 years ago

cost of a SCL line reversal: 11us, yikes, that is way over the 3-5us budget we have.

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
int IOUT = D6;
int IIN  = D5;
// SETUP function runs one time at start when device is powered on
void setup() {
    // set "led" pin to be an output
    pinMode(IOUT, OUTPUT);

}
// LOOP function runs in repeating loop (after setup finishes)
void loop() {
    ATOMIC_BLOCK() {
        digitalWriteFast(IOUT, 1);
        pinMode(IIN,  OUTPUT);
        pinMode(IIN,  INPUT_PULLUP);
        digitalWriteFast(IOUT, 0);
    }
    delay(1);
}

SDS00023

alcohen commented 4 years ago

If it’s of any interest... yesterday I started playing with an $11 Nucleo board from ST which features ths chip. Easy to develop on using VisualGDB, debugging works through the USB. One nice thing is that it has two i2c ports that can be slaves. I’m programming using C on mbed framework from ARM, so it does have an OS, but having “real” i2c might be nice. If this is of possible interest I can do some measurements.

ralphBellofattoSTX commented 4 years ago

@alcohen interesting, can the slaves be programmed to respond to multiple i2c addresses and how does one interface to it to support different "commands" to a given i2c device?

Can you point to a link for the documentation on how to use the i2c devices as a slave, and in our use case multiple slaves?

ralphBellofattoSTX commented 4 years ago

the timing measurements done so far indicate that we are definitely going to have to slow down the i2c clock.

It supposedly is possible to slow down the i2c clock with a boot time parameter. https://www.raspberrypi-spy.co.uk/2018/02/change-raspberry-pi-i2c-bus-speed/

this example only shows speeding up the clock to 400Mhz, we need to slow it down to 20 mhz. so a simple proof of concept is in order.

ralphBellofattoSTX commented 4 years ago

with a line turnaround time of 11us, we need a 1/2 clock time of that time plus some time to actually do some calculation, so that leaves us with at least 12us or a 24us clock... or a i2c baud rate of 50Mhz. so 20Mhz should more than cover it. we can try to tighten it up later.

so at 20Mhz, we get a duty cycle of 50us and a 1/2 clock cycle of 25us..

The entire cycle of handling the i2c is going to have to be done inside an interrupt handler, which won't return until either the stop condition, or a timeout.

Our longest read data is a command device address + 3 data bytes (to read the adc)... so that is start+(93)+stop (so 29 bits). This means we are holding off interrupts for 2950us, 1.45ms.

hopefully that does not cause problems with the RTOS on the photon...

That should get us started. setting the boot parameter as follows:

dtparam=i2c_arm=on,i2c_arm_baudrate=20000

Proof that we actually did reduce the i2c bit rate. SDS00024

ralphBellofattoSTX commented 4 years ago

we now have proof that we can do a timeout inside a interrupt handler, and the time functions work.

#include <stdint.h>
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
int IOUT = D6;
int IIN  = D5;
int TOUT = A0;

/**
 * @brief get the total time in ms since we started.
 * 
 * @return total number of microseconds since we started.
 *          NOTE: this handles the roll over if called more than once an hour.
 */
uint64_t microsNow() {          // time in microseconds...
    static uint32_t lastNow_us=0;  // check for overflow.
    static uint64_t totalNow_us=0;
    uint64_t delta_us;
    uint32_t now_us = micros();
    if (now_us >= lastNow_us) 
        delta_us = now_us-lastNow_us;
    else                // we rolled over..
        delta_us = (0x100000000ull - lastNow_us) + now_us;
    lastNow_us = now_us;
    totalNow_us += delta_us;
    return(totalNow_us);
}
void pinChangeInt() {
    uint64_t start = microsNow();
    uint64_t end = start + 1500;   // 1.5ms timeout
    digitalWriteFast(TOUT,1);
    while (microsNow() < end) 
        ;
    digitalWriteFast(TOUT,0);

}
void setup() {
    Serial.begin(9600);
    microsNow();            // prime the pump...
    // set "led" pin to be an output
    pinMode(TOUT, OUTPUT);
    pinMode(IOUT, OUTPUT);
    pinMode(IIN,  INPUT_PULLUP);
    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      

}
// LOOP function runs in repeating loop (after setup finishes)
unsigned cnt = 0;
void loop() {
    microsNow();            // and call this regularly... micros overflows every 71 minutes
    ATOMIC_BLOCK() {
        cnt++;
        digitalWriteFast(IOUT, cnt & 1);
    }
    delay(10);
}

SDS00025

ralphBellofattoSTX commented 4 years ago

next prove we can reverse the io of the interrupt pin from inside the interrupt AND keep or re-enable the interrupt handler.

ralphBellofattoSTX commented 4 years ago

proof of concept we can detatch and retatch from the interrupt in the interrupt handler and reverse io direction AND have a timeout.

#include <stdint.h>
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
int IOUT = D6;
int IIN  = D5;
int TOUT = A0;

/**
 * @brief get the total time in ms since we started.
 * 
 * @return total number of microseconds since we started.
 *          NOTE: this handles the roll over if called more than once an hour.
 */
uint64_t microsNow() {          // time in microseconds...
    static uint32_t lastNow_us=0;  // check for overflow.
    static uint64_t totalNow_us=0;
    uint64_t delta_us;
    uint32_t now_us = micros();
    if (now_us >= lastNow_us) 
        delta_us = now_us-lastNow_us;
    else                // we rolled over..
        delta_us = (0x100000000ull - lastNow_us) + now_us;
    lastNow_us = now_us;
    totalNow_us += delta_us;
    return(totalNow_us);
}
void pinChangeInt() {
    uint64_t start = microsNow();
    uint64_t end = start + 500;   // 1.5ms timeout
    digitalWriteFast(TOUT,1);
    detachInterrupt(IIN);
    pinReadFast(IIN);
    pinMode(IIN,  OUTPUT);
    pinMode(IIN,  INPUT_PULLUP);

    while (microsNow() < end) 
        ;

    digitalWriteFast(TOUT,0);
    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      

}
void setup() {
    Serial.begin(9600);
    microsNow();            // prime the pump...
    // set "led" pin to be an output
    pinMode(TOUT, OUTPUT);
    pinMode(IOUT, OUTPUT);
    pinMode(IIN,  INPUT_PULLUP);
    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      

}
// LOOP function runs in repeating loop (after setup finishes)
unsigned cnt = 0;
void loop() {
    microsNow();            // and call this regularly... micros overflows every 71 minutes
    ATOMIC_BLOCK() {
        cnt++;
        digitalWriteFast(IOUT, cnt & 1);
    }
    delay(10);
}

SDS00001

ralphBellofattoSTX commented 4 years ago

timing of the detatch, read io and line reversal. So we should be able

Entirely in the interrupt.

#include <stdint.h>
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
int IOUT = D6;
int IIN  = D5;
int TOUT = A0;

/**
 * @brief get the total time in ms since we started.
 * 
 * @return total number of microseconds since we started.
 *          NOTE: this handles the roll over if called more than once an hour.
 */
uint64_t microsNow() {          // time in microseconds...
    static uint32_t lastNow_us=0;  // check for overflow.
    static uint64_t totalNow_us=0;
    uint64_t delta_us;
    uint32_t now_us = micros();
    if (now_us >= lastNow_us) 
        delta_us = now_us-lastNow_us;
    else                // we rolled over..
        delta_us = (0x100000000ull - lastNow_us) + now_us;
    lastNow_us = now_us;
    totalNow_us += delta_us;
    return(totalNow_us);
}
void pinChangeInt() {
    uint64_t start = microsNow();
    uint64_t end = start + 500;   // 1.5ms timeout
    digitalWriteFast(TOUT,1);
    detachInterrupt(IIN);
    pinReadFast(IIN);
    pinMode(IIN,  OUTPUT);
    pinMode(IIN,  INPUT_PULLUP);
    digitalWriteFast(TOUT,0);
    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      

}
void setup() {
    Serial.begin(9600);
    microsNow();            // prime the pump...
    // set "led" pin to be an output
    pinMode(TOUT, OUTPUT);
    pinMode(IOUT, OUTPUT);
    pinMode(IIN,  INPUT_PULLUP);
    attachInterrupt(IIN, pinChangeInt, CHANGE,0);      

}
// LOOP function runs in repeating loop (after setup finishes)
unsigned cnt = 0;
void loop() {
    microsNow();            // and call this regularly... micros overflows every 71 minutes
    ATOMIC_BLOCK() {
        cnt++;
        digitalWriteFast(IOUT, cnt & 1);
    }
    delay(10);
}

SDS00002

alcohen commented 4 years ago

Great Ralph!

Should we be concerned at all about a 1.5 ms ISR?

On Sat, Jun 13, 2020 at 6:10 PM ralph bellofatto notifications@github.com wrote:

timing of the detatch, read io and line reversal. So we should be able

  • detect the falling start condition,
  • detatch the interrupt handler
  • read the io
  • poll the io pins for 1.5ms
  • perform the i2c transaction

Entirely in the interrupt.

include

SYSTEM_THREAD(ENABLED); SYSTEM_MODE(MANUAL); int IOUT = D6; int IIN = D5; int TOUT = A0;

/**

  • @brief get the total time in ms since we started.
  • @return total number of microseconds since we started.
  • NOTE: this handles the roll over if called more than once an hour. */ uint64_t microsNow() { // time in microseconds... static uint32_t lastNow_us=0; // check for overflow. static uint64_t totalNow_us=0; uint64_t delta_us; uint32_t now_us = micros(); if (now_us >= lastNow_us) delta_us = now_us-lastNow_us; else // we rolled over.. delta_us = (0x100000000ull - lastNow_us) + now_us; lastNow_us = now_us; totalNow_us += delta_us; return(totalNow_us); } void pinChangeInt() { uint64_t start = microsNow(); uint64_t end = start + 500; // 1.5ms timeout digitalWriteFast(TOUT,1); detachInterrupt(IIN); pinReadFast(IIN); pinMode(IIN, OUTPUT); pinMode(IIN, INPUT_PULLUP); digitalWriteFast(TOUT,0); attachInterrupt(IIN, pinChangeInt, CHANGE,0);

} void setup() { Serial.begin(9600); microsNow(); // prime the pump... // set "led" pin to be an output pinMode(TOUT, OUTPUT); pinMode(IOUT, OUTPUT); pinMode(IIN, INPUT_PULLUP); attachInterrupt(IIN, pinChangeInt, CHANGE,0);

} // LOOP function runs in repeating loop (after setup finishes) unsigned cnt = 0; void loop() { microsNow(); // and call this regularly... micros overflows every 71 minutes ATOMIC_BLOCK() { cnt++; digitalWriteFast(IOUT, cnt & 1); } delay(10); }

[image: SDS00002] https://user-images.githubusercontent.com/38866041/84579995-e1a0b600-ada0-11ea-82ae-7f124db8ce3c.png

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/GlobalVent/wiki/issues/138#issuecomment-643684591, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAOIMRMECWJNQ3HI5RE63TRWP2NJANCNFSM4NR26O7Q .

ralphBellofattoSTX commented 4 years ago

@alcohen since the photon is not doing anything else and appears to be still functional even with holding the interrupt for this long, I think we are ok.

The underlying gas simulation should be ok with this too. It is designed to not rely on a fixed simulation time interval, as is the control algorithm in on the pi.

More to the point, when using this platform for simulation, both the CPLD and the rpi need to slow the i2c bit rate down to 20Khz, rather than the default 100Khz.

As a result the minimum polling loop to get data will be a bit longer than the 12ms target that we originally had.

We have 3x10 bit times to send off the commands to retrieve the data and 910 bit times to pickup the data and periodically we have another 210 + 6*10 bit times to read temperatures on top of that.

in between each of those we have a 10ms delay. if we are careful to overlap things. I think that will end up with a data collection rate of around 16ms when doing simulation.

@stephmusinsky This study also implies that you need to slow down the I2c clock to be able to test in this mode (in addition to changing to the i2c address).

We also have to fit in a lower frequency temperature. collection for the two pressure sensors. I think we can accommodate this by adding a trigger for pressure collection at times we know that are not significant, for example, right after we close the valves for the reservoir and are waiting for the inhalation cycle.

We have this issue anyway, even with the faster i2c speeds as we have to accommodate the 10us delay between triggering the temperature data collection and retrieving the data.

The Photon OS overhead, time it takes to some of the required operations and interrupt jitter on the photon seems to have surprised all of us here.

But i believe i have a path forward.

ralphBellofattoSTX commented 4 years ago

@alcohen P.S. I think i may just have re-invented a way to characterize Internet of things real time response and suitability for use in devices.

I do believe i have the information that should allow me to complete this design.

I'll need to verify that the pins for the DCA are compatable with taking interrupts, otherwise, it is time for the Xacto knife and yellow wires.

ralphBellofattoSTX commented 4 years ago

checking pin outs and interrupts:

image

PhotonNot supported on the Photon (you can't use attachInterrupt on these pins):
D0, A5 (shared with SETUP button)
No restrictions on the Photon (all of these can be used at the same time):
D5, D6, D7, A2, WKP, TX, RX
Shared on the Photon (only one pin for each bullet item can be used at the same time):
D1, A4
D2, A0, A3
D3, DAC
D4, A1

We need A0 and D2 according to the schematic to capture the start condition. the above doc says we can't point to an interrupt at the same time.

Fortunately this board has all the traces on the surface... so we should be able to cut and rewire

image

The board will work until we need to use it with the CPLD at the same time, Then it will need some minor surgery. I would suggest here. image

Cut the two traces and then do a cross over to the vias.

dcstraney commented 4 years ago

I already did the interrupt comparisons and surgery. Look at the text that's just below the frame in your screenshot of the schematic. A0 is bridged to A4 so that A4 can be used instead - you can even see the dotted line showing the connection, right there.

ralphBellofattoSTX commented 4 years ago

@dcstraney ok, i see it, then i only need to do bridge across two pads...

Ok, i'll set the software to use A0 and A4.

also i'm a little confused what is going on with this: image

ralphBellofattoSTX commented 4 years ago

since this design is doing 2 i2c slave devices, it is going to have to do the same dca interrupt for both the clpd slave as well as the rpi slave, otherwise, when we are servicing one it will miss the other.

so, time to design the top interrupt handler and the exit and timeout conditions.

ralphBellofattoSTX commented 4 years ago

design point for the next step. I need an interrupt handler that *.checks for a start event on either device

once we have a start event we should be getting data back to back regularly.

ralphBellofattoSTX commented 4 years ago

note about I2c speeds. we can reprogram the i2c speed down to

dtparam=i2c_arm=on,i2c_arm_baudrate=10000

going to 5000hz oddly resulted in a 333khz

dtparam=i2c_arm=on,i2c_arm_baudrate=5000

So this puts a lower limit on how slow we can run this to debug the logic and get debug telemetry.

testing looks like we can go down to (8Khz, before it reverts to 333khz...

It is actually somewhere around 7530 hz, but 8k is close enough and divides nicely

well, thats good to know...

ralphBellofattoSTX commented 4 years ago

@dcstraney @alcohen ok, here is an interesting problem, the rpi has problems booting when i have the photon connected via its usb cable...

It worked for a while, but now it won't boot up if the from a complete power shut-down

I can boot up the rpi and then connect to the usb on the photon and it appears to be ok.

and I can initiate a software reboot, but cycling the power while the photon is connected via the usb does NOT work.

interesting...