it looks quite a bit more complex to do Pin Change Interrupts
It can be because you only get one interrupt per 8 inputs so you then have to figure out which input(s) changed. In this case though I'm suggesting that the ISR just reads the entire port (8 bits) and that's the value that gets sent to the PC with the time stamp. For example,
00001000 = lane 4 changed
01000000 = lane 7 changed
00010001 = lanes 1 and 5 changed at the same time (or within the resulotion of the system)
So this scales perfectly to 8 lanes with no change to the Arduino software, or indeed the PC software because you would write that to handle 8 lanes from the start.
a bit-field is effectively a group of 0/1's which designate state
Correct, if you send 2, 3 etc for the lane number you A) have to figure out which pins changed in the ISR and B) can't have multiple lanes represented in a single buffer entry. By sending a bit field in a single byte you offload that decision to the PC and can have simultaneous lane events.
in the ISR there is a lower-level way of accessing the pin states
Yep you just read the port.
circular buffer
Fancy phrase for an array with read and write pointers that "wrap around". The basic idea is like this
#define BUFFER_SIZE 256
#define ENTRY_SIZE 4
#define MAX_ENTIRES BUFFER_SIZE / ENTRY_SIZE
unsigned char buffer[BUFFER_SIZE];
int rd_ptr = 0;
int wr_ptr = 0;
int n_entries = 0;
write_entry (unsigned char lanes, long timestamp) { // assumes 24-bit timestamp
if (n_entries < MAX_ENTRIES {
buffer[(wr_ptr++) & 0xff] = lanes;
buffer[(wr_ptr++) & 0xff] = timestamp >> 16; // save MS byte
buffer[(wr_ptr++) & 0xff] = (timestamp >> 8) & 0x0000ff; // save middle byte
buffer[(wr_ptr++) & 0xff] = timestamp & 0x0000ff; // save LS byte
n_entries++;
} else {
// oops we're not quick enuff :-(
}
}
read_and_tx_entry () {
if (n_entires != 0) {
serial_tx ((buffer[rd_ptr++) & 0xff)
serial_tx ((buffer[rd_ptr++) & 0xff)
serial_tx ((buffer[rd_ptr++) & 0xff)
serial_tx ((buffer[rd_ptr++) & 0xff) }}
There's probably a 1000 errors there but that's the general idea. You write to the wr_ptr offset in the array and read from the rd_ptr offset. The (wr_ptr++) & 0xff clears any overflow past 8 bits, so the offset wraps around from 255 to 0 rather than counting on to 256. That's faster than
if (wr_ptr > xx)
wr_ptr = 0;
And the same technique will work with any power of 2 so you can have a buffer size of 16, 32, 64, 128 etc. Just change the mask.
Rob