Alright, I think I am following. Doing any calls directly to registers is somewhat new to me, but I think I understand where you are going. My signal is quite a bit faster than the ones in the example (2Mbps vs. 500kbps) and I think that's where I'm struggling a bit. Each instruction takes a significant and measurable amount of time compared to the signal I'm trying to read.
To summarize, my current method is to use cyclecounter (ARM_DWT_CYCCNT) values to measure the pulse width of the start bit, once identified I am use a periodic timer interrupt to read the signal 1/4 period before the state transition, per your post #9 on this forum (
https://forum.pjrc.com/threads/46854-manchester-decoding-teensy-3-2-with-uart)
I think what you are suggesting is instead grabbing the cyclecounter at each rising or falling edge and doing math to compare the timing between the pulses. I think this is really elegant, but I am a bit unsure how to sync with the start bit using this method. If you were able to give a piece of example code, particularly just so I can see how you set up the capture timers and ring buffer, I'd really appreciate it. I have looked through the manual for this chip, but it is a bit like reading Egyptian hieroglyphics to me. I'll have to learn more. Again, I really appreciate your replies.
I spent a lot of time on this last night and using my method, I got the signal to sync and line up the read positions correctly. Instead of reading, I'm toggling a digital output so I can visualize it on my scope, but it should be a simple instruction swap. The periodic interrupt timer seems to give a very stable behavior once enabled, but I had to fudge with the first offset value to get everything to line up. Basically when I'm looking for the start bit, I arm the periodic interrupt timer on each rising edge. If a pulse which is too short (not a start bit) is measured, the periodic interrupt timer is cancelled before the ISR is called. If a start bit is found, the timer is allowed to trigger the ISR and begin a sequence of reads. After 21 cycles, an extra long timer interrupt is generated to delay past the end of the stop bit, then the sequence is disarmed.
In the picture below, the transitions of the blue lines represent the periodic ISR triggering. This is where I'd put a digitalReadFast(). The FRFR[1-3] measurement represents the sync between the sampling and the signal start bit, target value is 3.375us. This looks great.
Unfortunately, if I put the scope in fast acquisition mode and turn on display persistence, I can see occasional blips where the sampling signal gets misaligned with the data. I've never gotten an honest capture of this on the scope, only the persistence.
And the code I used to get to where I am. I am absolutely open to suggestions/optimizations. Every reduced instruction really makes a difference in the timing of this signal. The digitalWriteFast() calls will be replaced with a digitalReadFast().
Code:
#include <avr/io.h>
#include <avr/interrupt.h>
int PinInt1 = 15;
int PinOut1 = 13;
volatile unsigned int clockCyclesRising = 0; //measure rising edge when looking for start bit
volatile unsigned int clockCyclesFalling = 0; //measure falling edge when looking for start bit
volatile unsigned int timerInterruptCount = 0; //count the number of samples completed in the periodic interrupt
unsigned int clockCyclesElapsed = 0; //calculated value of clock cycles
volatile bool intFlag = 0; //Flags when pulse has been captured by interrupts. Check if it is a start bit
volatile bool intPeriod = 0; //Flags when the periodic interrupt is running
IntervalTimer myTimer;
void setup() {
pinMode(PinInt1, INPUT);
pinMode(PinOut1, OUTPUT);
attachInterrupt(digitalPinToInterrupt(PinInt1), isrServiceRising, RISING);
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
}
void loop() {
if (intFlag == 1){
clockCyclesElapsed = clockCyclesFalling - clockCyclesRising;
if ((clockCyclesElapsed <= 1575) || (clockCyclesElapsed >= 1585)) //Pulse too short or too long to be a start pulse
{
myTimer.end(); //disarm periodic timer interrupt before it can call the ISR
intFlag = 0;
attachInterrupt(digitalPinToInterrupt(PinInt1), isrServiceRising, RISING); //arm interrupt looking for start bit
}
else //start bit is found, do nothing, periodic interrupt is already armed and counting down
{
intFlag = 0;
intPeriod = 1; //Flag for periodic interrupt
}
}
if ((intPeriod == 1) && (timerInterruptCount >= 22)) //Once 21 samples have been taken + a delay, end the timer interrupt, arm the start bit interrupt for the next sequence
{
myTimer.end();
delayNanoseconds(500);
intPeriod = 0;
timerInterruptCount = 0;
digitalWriteFast(PinOut1, 0);
attachInterrupt(digitalPinToInterrupt(PinInt1), isrServiceRising, RISING);
}
}
void isrServiceRising()
{
clockCyclesRising = ARM_DWT_CYCCNT;
myTimer.begin(isrPeriodic, 2.2); //Arm Timer Interrupt to occur fixed value after rising edge. This must be cancelled if pulse is not start pulse.
myTimer.update(1.0); //sets the next pulses to be 1us
attachInterrupt(digitalPinToInterrupt(PinInt1), isrServiceFalling, FALLING);
}
void isrServiceFalling()
{
clockCyclesFalling = ARM_DWT_CYCCNT;
intFlag = 1; //Flag that a pulse has been measured and needs to be checked if it is a start bit
detachInterrupt(digitalPinToInterrupt(PinInt1));
}
void isrPeriodic()
{
timerInterruptCount++;
if (timerInterruptCount <22)
{
digitalToggleFast(PinOut1);
}
}