Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 27

Thread: manchester decoding teensy 3.2 with uart

  1. #1
    Junior Member
    Join Date
    Jul 2014
    Posts
    13

    manchester decoding teensy 3.2 with uart

    Hi,

    I've got a 250kbaud manchester signal that i'm trying to decode with the teensy 3.2 hardware serial. The signal's manchester clock is 500Khz, so a serial of 500kBaud creates the same high/low time for 1/2 manchester period. I used the code below to receive the data then transmit it back out the same serial port. So far the data looks exactly as the incoming data when viewing rx and tx on a scope, but with one problem: It gets cut off when, it seems, there is a received stop bit.

    After looking over the datasheet i'm striking out how to disable the stop bit on the serial port. Any ideas? I want it to receive until it times out, then work through the task of decoding the manchester which is straightforward

    Code:
    #define HWSERIAL Serial1
    void setup() {
      delay(1000);
      HWSERIAL.begin(500000); // setting SERIAL_8N1 to disable parity doesn't help
    }
    int incomingByte;
      
    void loop() {
     
      if (HWSERIAL.available()) {
        incomingByte = HWSERIAL.read();
        HWSERIAL.write(incomingByte);
        incomingByte = 0;
      
      }
    }


    atmel reference on doing the same thing:
    http://www.atmel.com/images/atmel-42...ation-note.pdf

    screenshot showing the early cutoff
    Click image for larger version. 

Name:	20171008_200854~01.jpg 
Views:	150 
Size:	205.4 KB 
ID:	11716

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,698
    Not looking - I don't know if the stop bit is hardware handled or can be turned off.

    However there is a SoftwareSerial library that might give you what you need running on the same pins?

  3. #3
    I doubt if hardware or software Serial is suitable for this task. A serial link is based on a fixed number of bits to be read.

    If the Teensy is fast enough then using digitalRead() combined with an interrupt should be more suitable.

    Of course, using serial interface is much easier to handle. But I doubt if it is suitable principally.

  4. #4
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,101
    Right now I don't think it is set up for 0 stop bits. If you look at Arduino Serial.begin there is a speed and config option, Serial.begin(speed, config) where config looks like SERIAL_8N1 (which is the default so you don't need to specify it). If I look in the Teensy Core libs at HardwareSerial.h there is no config setting for 0 stop bits. That's all I can tell you about what's available. To be honest I don't know if zero stop bits is even possible over serial. Everthing I ever seen is 1 stop bit.

    Maybe someone else can chime in on whether the stop bit is really the problem.

    Anyway. In looking at your reference document it looks like you can configure control register C (CTRLC) on the transceiver to have a stop bit mode for both encode and decode (sections 3.1 and 4.1).

    Another thing you don't need to use HWSERIAL as serial1. you can just use Serial1.begin etc.

    DISCLAIMER: Until this post I never heard of Manchester encoding/decoding before. Sorry I could be of more help.

  5. #5
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,698
    Quote Originally Posted by NikoTeen View Post
    I doubt if hardware or software Serial is suitable for this task. A serial link is based on a fixed number of bits to be read.

    If the Teensy is fast enough then using digitalRead() combined with an interrupt should be more suitable.

    Of course, using serial interface is much easier to handle. But I doubt if it is suitable principally.
    I was just hypothesizing about the ...\hardware\teensy\avr\libraries\SoftwareSerial - but it looks to max out at 115200 baud as written - not sure if there is math and CPU speed to hit faster?

    As 'software' 'serial' it does however use manual port read I/O for transfer and that exposes things like this where it disposes of the STOP bit - skipping that would have prevented the loss of the bit from the next byte:

    Code:
        // skip the stop bit
        tunedDelay(_rx_delay_stopbit);
        DebugPulse(_DEBUG_PIN2, 1);

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,567
    I'm afraid there simply isn't any way to disable the stop bit, or cause the serial hardware to look for more than 11 bits.

    SoftwareSerial on Teensy 3.x simply uses the regular hardware serial. AltSoftSerial probably isn't a good option.

    Something that might work would involve using timer input capture. FreqMeasure and PulsePosition have code which does this, to measure when the transitions occur within a waveform. If you can record a 16 bit relative timestamp for each waveform edge into a big buffer, then perhaps you could write some code to analyze the time between rising and falling edges to decode the original data.

    500 kHz is going to be a challenge. If you need to run an interrupt routine for each waveform edge, that gives you very little time to do the work. Maybe it could be done on 180 MHz Teensy 3.6, but even that would be tricky coding. This is probably a job for DMA. It can easily handle 500000 captures/sec. But that's adding a challenging project to get the DMA set up properly, on top of the already not-so-trivial work to analyze the captured timestamps.

    Anyway, if you're going to make this work, that's probably the most realistic path.

  7. #7
    Senior Member
    Join Date
    May 2017
    Posts
    208
    Although manchester encoding is just a hardware encoding method the underlying data stream is usually a synchronous data stream. If you look at the linked Atmel document, you will see that the first thing they do is put the USART into synchronous mode. A synchronous data stream may typically start with alternating ones and zeros to sync up clock recovery. Your simple example using asynchronous hardware probably works for a while during this preamble of ones and zeros as the bits fall into the correct positions to provide the low start bit and high stop bit. Once your data stream starts sending real data, you probably get a framing error and the output cuts off because the input data was received invalid.

  8. #8
    Junior Member
    Join Date
    Jul 2014
    Posts
    13
    Thank you very much for all the responses.

    Originally I did attempt to do an edge triggered ISR to find highs/lows but as you said Paul, there's not much time in the ISR to do much although it may be possible. I've got a teensy 3.6 on the way which will hopefully give me more cpu time to work with between ISRs.

    So 3 questions:
    1) Are there any examples of using DMA triggered off an ISR to do periodic sampling at ideally .5 or so usec and stashing that somewhere in ram?
    2) Can I run noInterrupts() during an isr without causing problems? Let's say I want to use the ISR to capture the first pulse then dump digitalFastReads to ram.
    3) I haven't done anything with the teensy3.6 so far, is there a good high speed timer already configured to increment faster than 1usec? If not which timer is it?
    Last edited by thefatmoop; 10-10-2017 at 01:44 PM.

  9. #9
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    531
    If I understand this page http://www.cypress.com/blog/psoc-ins...anchester-code correctly, decoding should be quite simple.

    Click image for larger version. 

Name:	Unbenannt.PNG 
Views:	76 
Size:	40.4 KB 
ID:	11724

    According to the figure above the following should work:

    1) Attach an edge interrupt to the input pin.
    2) In the isr you simply start a delay timer (e.g TeensyDelay (https://github.com/luni64/TeensyDelay) which uses a FTM Timer).
    3) In the callback function of the delay timer you read out the input pin to get the current bit and append it to your output


    Since you know the frequency of the manchester signal you don't have to recover it. The exact value of the delay should not be very important I'd say.

  10. #10
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,567
    Quote Originally Posted by thefatmoop View Post
    1) Are there any examples of using DMA triggered off an ISR to do periodic sampling at ideally .5 or so usec and stashing that somewhere in ram?
    You could take a look at OctoWS2811, and try to do the same in reverse (swap the source and destination registers, so it reads GPIO instead of writes).

    But really, what you want is for the signal edges to capture a rapidly increasing timer, rather than trying to rapidly sample the signal itself. Then you can subtract each pair of values to get the time between edges. The FTM timers have this capability built in, completely in hardware. FreqMeasure and PulsePosition have the most relevant code.

    2) Can I run noInterrupts() during an isr without causing problems? Let's say I want to use the ISR to capture the first pulse then dump digitalFastReads to ram.
    Yes, you can do that, but it's probably not the best way.

    The interrupts use priority levels, so when your interrupt runs, all others at the same and lower priority (higher numbers) can not run. But other interrupts with higher priority (lower numerical values) are able to interrupt yours. If you want to completely control the CPU, simply configuring your interrupt for highest priority (value 0) is the best way. Then you don't need to bother with noInterrupts().

    But no matter how well you do this, it will be subject to some software latency. The FTM timer input capture is the only way that avoids all software latency and gives you highly accurate results.

    3) I haven't done anything with the teensy3.6 so far, is there a good high speed timer already configured to increment faster than 1usec? If not which timer is it?
    Both systick and the arm cycle counter do this.

    But again, you really want to use one of the FTM timers. They can clock at F_BUS, which is slower but still plenty fast enough. Their ability to capture the rapidly increasing timer value to another register completely done in hardware is the key feature you should use. I know fast sampling of the signal is easier to understand, but you really should do yourself a favor and learn how the timer input capture works. It really is the best way, and I just don't know how much more I can do to repeatedly make this point....

  11. #11
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    Quote Originally Posted by PaulStoffregen View Post
    but you really should do yourself a favor and learn how the timer input capture works. It really is the best way, and I just don't know how much more I can do to repeatedly make this point....
    That's indeed the best way. Perhaps this feature should be supported by a easy to use library ? Or a simple Howto ?

  12. #12
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,698
    Indeed I expect the timers w/DMA may evolve to a better solution.

    The read is bitbanged and may not scale to 5X the current speed - but the github.com/PaulStoffregen/SoftwareSerial receive code does seem to just read the port bits with fixed time delays with the code shown.

    Write does use the Serial port hardware when present - but the read doesn't seem to have that deference.

  13. #13
    Member
    Join Date
    Aug 2013
    Location
    Ohio
    Posts
    88

    How about SPI?

    Totally speculative idea: Use a hardware SPI port set to a bit rate about 4 (or maybe 8) times the bit rate of the Manchester signal, set up to run flat-out continuously, storing the incoming bits in a RAM buffer. Software decode can examine the bits in the RAM buffer to approximately identify signal edge timing well enough to extract a signal.

    It would be even better if the application layer encapsulated in the Manchester transmission has error checking (or better yet error correction)...

    A reasonably thought-out bit-banging state machine should be able to run quickly enough in a Teensy 3.2 to allow processing the resulting data buffers (assuming not too much semantic complexity).

  14. #14
    Junior Member
    Join Date
    Jul 2014
    Posts
    13
    SPI needs a clock? Manchester clock is xor'ed in the signal and isn't always visible depending on the bits being transferred

    Thanks I'll check out using an ISR with FTM combined with Luni's note. Teensy 3.6 should get here today

  15. #15
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    531
    I played around a bit with Manchester encoding/decoding. Unfortunately the procedure from my post #9 does not work. The picture is a bit unclear (or - more likely - I didn't read careful enough). Anyway, you can not simply use the edges of the data signal to trigger the delay, you need to either recover the clock from the signal or just assume its frequency and sync it to the first edge of the signal. You then use the clock transitions (not the data transitions) to start the delay as seen in the picture.

    If you want to use TeensyDelay you need to do a small change in the lib. In config.h lines 158ff I calculate the FTM prescaler to get about 0.5 Ás per tick. This probably is too coarse for your application. You can simply comment that out and manually set a smaller prescaler instead. Calculation of the reload value will work with any prescaler.

    ---
    For my experiments I wrote a little encoder which outputs a constant string ("Hello Manchester") at a clock rate of about 500kHz (Teensy 3.2). Might be useful:

    Code:
    void setup()
    {
        constexpr unsigned dataPin = 0;
        constexpr unsigned clockPin = 1;   // clock output not necessary but useful for debugging
    
        pinMode(dataPin, OUTPUT);
        digitalWriteFast(dataPin, LOW);
    
        pinMode(clockPin, OUTPUT);
        digitalWriteFast(clockPin, LOW);
    
        const char buf[] = "Hello Manchester";
    
        noInterrupts();  // need a clean signal here
    
        while (1)
        {
            for (int byteCnt = 0; byteCnt < sizeof(buf); byteCnt++)  // loop over chars in buffer
            {
                uint8_t current = buf[byteCnt];
                for (int bitCnt = 0; bitCnt < 8; bitCnt++)           // loop over bits in current char    
                {
                    uint8_t isSet = (current & 0x80) != 0x80;        // get MSB 
                    digitalWriteFast(clockPin, HIGH);                // clock output not necessary but useful for debugging
                    digitalWriteFast(dataPin, (isSet^HIGH));         // data XOR clock
                    delayMicroseconds(1);                            // we want a ~500kHz clock  
    
                    current <<= 1;                                   // prepare next bit
                    digitalWriteFast(clockPin, LOW);                 // clock 
                    digitalWriteFast(dataPin, (LOW^isSet));         // data XOR clock
                    delayMicroseconds(1);
                }
            }
            delayMicroseconds(100);
        }
    }
    
    void loop() {}
    The generated Manchester code seems to be ok. At least it is understood by my logic analyzer:

    Click image for larger version. 

Name:	manchester1.PNG 
Views:	63 
Size:	39.0 KB 
ID:	11743

    Click image for larger version. 

Name:	Manchester2.PNG 
Views:	56 
Size:	37.2 KB 
ID:	11744

  16. #16
    Junior Member
    Join Date
    Jul 2014
    Posts
    13
    I think you're mostly right though on post 9. As per manchester 802.3 if you wait 3/4th of a period, your digital read will reveal the manchester encoded bit. The first few bits of the manchester data i'm receiving is a header sends all 0's or 1's for 4 or so bits. Interrupting on the first edge and setting up a periodic interrupt to read the pin state. But after the first interrupt, the edge interrupts need to be disabled and read periodically based off time interrupt, the message is <3 bytes, so dead reckoning time should work.

    But yes the micro can't get off from the manchester clock or it'll read junk data. Otherwise using an ISR on every edge would work, the edge width must be known to determine how many manchester clock cycles elapsed.

    Do you see any problems with that? Of course I need to sit down and play with the 3.6 for a while

  17. #17
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,698
    Quote Originally Posted by thefatmoop View Post
    I think you're mostly right though on post 9. As per manchester 802.3 if you wait 3/4th of a period, your digital read will reveal the manchester encoded bit. The first few bits of the manchester data i'm receiving is a header sends all 0's or 1's for 4 or so bits. Interrupting on the first edge and setting up a periodic interrupt to read the pin state. But after the first interrupt, the edge interrupts need to be disabled and read periodically based off time interrupt, the message is <3 bytes, so dead reckoning time should work.

    But yes the micro can't get off from the manchester clock or it'll read junk data. Otherwise using an ISR on every edge would work, the edge width must be known to determine how many manchester clock cycles elapsed.

    Do you see any problems with that? Of course I need to sit down and play with the 3.6 for a while
    This is what the linked SoftwareSerial code does - fixed timing and adjust from bit #1 a half a clock:
    Code:
        // Wait approximately 1/2 of a bit width to "center" the sample
        tunedDelay(_rx_delay_centering);
        DebugPulse(_DEBUG_PIN2, 1);
    It checks on 'interrupt' at the start, but doesn't disable one . . . but it is the boilerplate of a bitbang trial.

  18. #18
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    531
    Do you see any problems with that? Of course I need to sit down and play with the 3.6 for a while
    I did a quick proof of principle sketch with a T3.2. The periodic timer is better than using a delay timer since it doesn't accumulate errors. It works and is simple to implement but as Paul mentioned in #10 the unavoidable software latencies generate some risk of getting out of sync. I wouldn't use this method for production code.

    Code:
    constexpr unsigned dataPin = 3;
    constexpr unsigned testPin1 = 0;
    constexpr unsigned testPin2 = 1;
    
    char current = 0;
    volatile char buf[20];
    volatile unsigned bitCnt = 0;
    volatile unsigned byteCnt = 0;
    
    IntervalTimer pit;
    KINETISK_PIT_CHANNEL_t* PIT_Channel;
    bool stop = true;
    
    // Periodic timer interrupt
    void PIT_ISR()
    {
    	if (byteCnt < sizeof(buf))  
    	{
    		digitalWriteFast(testPin1, HIGH);               // debugging 
    		if (!digitalReadFast(dataPin)) current |= 0x01; // read the bit
    		if (bitCnt < 7)
    		{
    			current <<= 1;
    			bitCnt++;
    		}
    		else // store the byte and prepare for the next one
    		{
    			buf[byteCnt++] = current;				
    			current = 0; 
    			bitCnt = 0;
    		}
    		digitalWriteFast(testPin1, LOW); // debugging   
    	}
    	else
    	{
    		PIT_Channel->TCTRL = 0; //stop PIT
    	}
    }
    
    // pin interrupt
    void PIN_ISR()
    {
    	if (stop) return;   // just a quick hack to avoid looking up the correct register settings :-)
    	stop = true;
    
    	// To align the timer with the clock we need to manually adjust the first timer period. 
    	PIT_Channel->TCTRL = 0;     // to immediately change LDVAL the timer need to be stopped first. Otherwise LDVAL gets buffered and will be loaded at the next zero transition
    	PIT_Channel->LDVAL = 285;   // adjust, so that the first PIT_ISR() is called at the correct time. 
    	PIT_Channel->TCTRL = 3;     // start the PID
    
    	// Set the timer reload values for the follwing periods according to the frequency of the Manchester signal
    	constexpr float freq = 456E3;
    	constexpr unsigned cycles = F_BUS / freq;
    	PIT_Channel->LDVAL = cycles; // This value will be buffered and is loaded after at the first timer zero transition
    
    	//The PIT will miss the first clock cycle (can not choose the first LDVAL small enough, maybe a T3.6 would be quick enough) 
    	//To avoid handling a 7bit byte we simply call the first PIT_ISR manually (after a short delay)
    	volatile int dummy; for (int i = 0; i < 5; i++) { dummy *= dummy; }
    	PIT_ISR();
    }
    
    // this simply waits until it detects 15Ás bus inactivity. 
    void sync()
    {
    	int i = 0;
    	while (i < 15)
    	{
    		i = (digitalReadFast(dataPin) == LOW) ? 0 : (i + 1);
    		delayMicroseconds(1);
    	}
    }
    
    void retrigger()
    {
    	sync();
    	stop = false;
    
    	// Trigger signal for the logic analyzer
    	digitalWriteFast(testPin2, HIGH);
    	delayMicroseconds(5);
    	digitalWriteFast(testPin2, LOW);
    
    	bitCnt = 0;
    	byteCnt = 0;
    }
    
    void setup()
    {
    	Serial.begin(0);
    
    	//setup pins
    	pinMode(dataPin, INPUT_PULLUP);
    	pinMode(testPin1, OUTPUT);
    	pinMode(testPin2, OUTPUT);
    	digitalWriteFast(testPin1, LOW);
    	digitalWriteFast(testPin2, LOW);
    
    	// setup interval timer
    	pit.priority(0);           // highest 
    	pit.begin(PIT_ISR, 1E6);   // let teensyduino grab a free timer and do all the initialization work. 
    
    	// Get hold of the register block for the used timer 
    	PIT_Channel = KINETISK_PIT_CHANNELS + ((IRQ_NUMBER_t)pit - IRQ_PIT_CH0);
    	PIT_Channel->TCTRL = 0;   // stop the timer 
    		
    	
    	stop = true;
    	attachInterrupt(dataPin, PIN_ISR, FALLING);
    
    	// wait for pause between data block, start afterwards
    	retrigger();
    }
    
    
    void loop()
    {
    	if (byteCnt == sizeof(buf)) // buffer complete
    	{
    		Serial.println((char*)buf);  
    
    		retrigger();  // wait for next data block
    	}
    }
    Testpin 1 in the screenshot below is HIGH during the periodic interrupt. As you see there is not much headroom for jitter. Might be better on a T3.6.

    Click image for larger version. 

Name:	manchester 2.PNG 
Views:	137 
Size:	50.0 KB 
ID:	11747

    Here the output:
    Click image for larger version. 

Name:	decoder.PNG 
Views:	116 
Size:	34.7 KB 
ID:	11749

  19. #19
    Senior Member
    Join Date
    Feb 2013
    Posts
    181
    Hi,

    I am working on a project where a sensor responds with manchester signal when you request an eeprom read. Seems this code will work great, I am just not sure how I would adjust it to work with a 100kbs signal versus 500kbs (I assume that is what the above code is using). I am not well versed in how to set the timers, and don't see an obvious place it is set. My code also just needs to be 1/0, I assume I would just ignore the typecast to char and read the binary data directly? Thanks for this code and hope it will save me a lot of headaches trying to figure out how to make it work. For reference the sensor I am using is OPB9000, if you want to see the datasheet.

  20. #20
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    531
    Wow, the OPB9000 is probably the most complicated reflective optical sensor I've ever seen... but I assume you have a good reason for using it.

    The code above was meant to check the feasibility of decoding a Manchester signal with simple interrupts. If you want to use it I recommend very thorough testing.

    I assume I would just ignore the typecast to char and read the binary data directly
    Yes

    I am not well versed in how to set the timers, and don't see an obvious place it is set.
    The only thing you need to adjust are the lines

    Code:
    PIT_Channel->LDVAL = 285;   // adjust, so that the first PIT_ISR() is called at the correct time.
    and
    Code:
    constexpr float freq = 456E3;
    (both in PIN_ISR)

    You need to play with the LDVAL value until the first edge is captured at the right time. The value in the second line determines the signal frequency in Hz. In the example it is a little bit smaller than 500kHz to account for the interrupt latencies. I'd set that to something like 95E3. You'll need an oscilloscope or a logic analyzer to get the timings right.

    Be warned: If you don't have experience with this kind of coding it might be a frustrating experience getting it going.
    Last edited by luni; 12-17-2017 at 09:35 PM.

  21. #21
    Senior Member
    Join Date
    Feb 2013
    Posts
    181
    No real experience, but thats the fun in figuring it out. I assumed that is what the 456E3 was. Guess my only two last points of confusion are why the PIT timer is set to 1E6 and are you reading the PIT clock, if so, what PIN is this on a T3.2? I guess the other option is to ignore the clock and just read all the values and then parse it out into pairs and determine the bit from that, since 11 will always be 0101 from the raw data.
    Currently I read the read request with a scope and manually figure it out (there are only 6 bits of interest). Using it because of its small form factor, but ability to calibrate it helps. Thanks for your quick reply.

  22. #22
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    531
    Guess my only two last points of confusion are why the PIT timer is set to 1E6
    Oh, that is just a trick. When I wrote the code it was not possible to change the period of a running timer. So I needed to get hold of the underlying register block.
    Teensyduino grabs one of the 4 PIT timers and initializes the registers in the begin function. So the only reason to call begin is to get hold of the allocated register block. As soon as I have the block I stop the timer. The 1E6 is just a dummy value.

    Since a couple of days it is possible to change the period with a library function so this strange code is not necessary anymore.

  23. #23
    Senior Member
    Join Date
    Feb 2013
    Posts
    181
    Thanks, and I adapted your code and it worked. Now to hook it up to the scope to make sure its the right values. I have searched for the answer and maybe its not possible, but how did you get the clock signal into your logic analyzer. Is the clock being output on a pin, i assumed PIT timers were internal. I assume I will need this signal in my scope to line up with the signal from the OPB9000. Thanks again, love the fact this forum is filled with so many smart and helpful ppl to help people like me who need it.

  24. #24
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    531
    That was quick :-)
    For testing the decoder I generated the manchester signal with the sketch shown in #15 of this thread. It outputs the clock for debugging. For optimizing your timer values you need to look at Testpin1. The manchester signal is captured at the rising edge of testPin1 (see PIT_ISR).

  25. #25
    Senior Member
    Join Date
    Feb 2013
    Posts
    181
    Awesome, thanks, I knew I was missing something. Will try it now.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •