I need to implement a custom serial protocol on T4.1 (https://forum.pjrc.com/threads/62819...-on-Teensy-4-1) that needs to output serial data synchronously with at least a 3.2 MHz clock (preferably higher).
Data transmission occurs in "frames" which are denoted with a high pulse of the "sync" line, followed by 20 bits of serial data, written on the rising edge of the clock.
I've gotten a bit-banging implementation working below that uses the PIT to trigger an ISR at 1.33 MHz, which then handles the changing the pin states on both the high and low clock edges. This gives an effective 667 kHz clock rate.
Code:
IntervalTimer t;
void setup() {
pinMode(3, OUTPUT);
pinMode(5, OUTPUT);
pinMode(7, OUTPUT);
pinMode(9, OUTPUT);
t.begin(callback, .75);
}
static volatile int clk = LOW;
static volatile int sync = LOW;
static volatile int ctr = 0;
static volatile int transferring = 0;
#define TOGGLE(state) (((state) == HIGH) ? LOW : HIGH)
#define EXTRACT_BIT(data, idx) (((data) >> (idx)) & 1)
// get bit corresponding to clock according to XY2-100 protocol
static int xy2_bit(int ctr, uint16_t data) {
int bit_to_write;
switch(ctr) {
case 0:
case 1:
bit_to_write = 0;
break;
case 2:
bit_to_write = 1;
break;
default:
bit_to_write = EXTRACT_BIT(data, 18 - ctr);
break;
}
return bit_to_write;
}
static volatile uint16_t next_xdata, next_ydata;
static volatile int x_bit = 0, y_bit = 0;
void callback() {
static volatile uint16_t x_data = 0x8000, y_data = 0x8000;
clk = TOGGLE(clk);
if(clk == HIGH) {
if(ctr == 0 || ctr == 1) {
if(ctr == 0) {
// begin transfer
transferring = 1;
x_data = next_xdata;
y_data = next_ydata;
}
sync = TOGGLE(sync);
}
if(ctr == 19)
transferring = 0;
if(transferring) {
x_bit = xy2_bit(ctr, x_data);
y_bit = xy2_bit(ctr, y_data);
} else {
x_bit = 0;
y_bit = 0;
}
}
if(clk == HIGH)
ctr = (ctr + 1) % 20;
digitalWriteFast(5, sync);
digitalWriteFast(7, x_bit);
digitalWriteFast(9, y_bit);
digitalWriteFast(3, clk);
}
void loop() {
while(1) {
next_xdata = next_ydata = (sin(micros() / 1e6 * 2 * M_PI * 440) + 1) / 2 * 0xffff;
}
}
I've tested this implementation and it works fine, but it appears that IntervalTimer can't go faster than a 0.75 us period by default. I need about a 5x improvement in speed to reach the 3.2 MHz target clock rate -- how could I achieve this? I've read that overclocking the PIT is possible, but I have a feeling that reducing CPU involvement through DMA or similar is the best way to go.
Thanks!