Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 24 of 24

Thread: Teensy 4.0 and single-wire serial

  1. #1
    Senior Member
    Join Date
    Jan 2014
    Posts
    111

    Teensy 4.0 and single-wire serial

    Hi,

    I'm trying to switch Teensy 4.0 serials into single-wire mode but it doesn't quite work for me. I'm trying to follow the i.MX RT1060 Processor Reference Manual and do the following:

    First I initialize Serial port 1 normally
    Code:
    Serial1.begin(57600);
    then I switch the port into single-wire operation but setting the LOOPS and RSRC flags of the Serial 1 (UART6) CTRL register
    Code:
    LPUART6_CTRL |= (LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC);
    finally I switch the port into receive mode by making sure the TXDIR flag of the same CTRL register is cleared
    Code:
    LPUART6_CTRL &= ~LPUART_CTRL_TXDIR;
    Then I connect the TX pin of Serial1 to TX pin of Serial2 and send some data in the loop from Serial2. Unfortunately none of that is received by Serial1.

    Am I missing something?

    Here is the complete code I use
    Code:
    void setup() {
      Serial.begin(115200);
      Serial1.begin(57600);
      LPUART6_CTRL |= (LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC);
      LPUART6_CTRL &= ~LPUART_CTRL_TXDIR;
      Serial2.begin(57600);
      Serial.println("Teensy 4.0 Single-Wire Serial Test");
    }
    
    void loop() {
      Serial2.write(0x02);
      if(Serial1.available()) Serial.println(Serial1.read(), HEX);
    }
    I'm using Arduino 1.8.10 and Teensyduino 1.48

  2. #2
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,344
    I keep meaning to try it, I have so far run my Dynamixel servos using a buffer like chip with a direction IO pin...

    But I keep meaning to add the code to my BioloidSerial code to allow to use a single pin.

    If/When I do it, I will probably start off similar to what you have, however some things I would check and need to address, include:

    I believe you need to configure the IOMUXC input daisy chain... In this case Page 856 in reference manual, you probably need:
    Code:
    IOMUXC_LPUART6_TX_SELECT_INPUT = 1
    And in addition I would probably update the TX pins configuration information.
    Example on T3.x I do:
    Code:
     volatile uint32_t *reg = portConfigRegister(tx_pin);
            *reg = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3) | PORT_PCR_PE | PORT_PCR_PS; // pullup on output pin;
    But of course different registers for T4... but same idea of enable the PU resistor...

  3. #3
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,950
    AFAIK - didn't see any such testing on the Beta thread.

    What pins are in use?

    The Serial1 code will have expectations/config on Rx and Tx pins. Making above change after to the alter UART function may not be effective/complete with that standard config?

    .. funny … detailed crosspost with the master

  4. #4
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,268
    I don't think you can use the core Serialx library on a modified single wire bus, you'd need to implement another library to handle it.

  5. #5
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,394
    In January, I successfully ran OneWire lib example with DS18B20 attached to T4

  6. #6
    Senior Member
    Join Date
    Jan 2014
    Posts
    111
    Thank you all for the quick reply

    Quote Originally Posted by KurtE View Post
    I believe you need to configure the IOMUXC input daisy chain... In this case Page 856 in reference manual, you probably need:
    Code:
    IOMUXC_LPUART6_TX_SELECT_INPUT = 1
    That did the trick, thanks a lot!

  7. #7
    Senior Member
    Join Date
    Jan 2014
    Posts
    111
    BTW I've noticed that in HadwareSerialX.cpp configuration of the IOMUXC for TX pins in omitted for most of the serials (except Serial5/LPUART8). Is there any particular reason for that?

    Here is an example of Serial1

    Code:
    const HardwareSerial::hardware_t UART6_Hardware = {
        0, IRQ_LPUART6, &IRQHandler_Serial1, &serial_event_check_serial1,
        CCM_CCGR3, CCM_CCGR3_LPUART6(CCM_CCGR_ON),
        {{0,2, &IOMUXC_LPUART6_RX_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
        {{1,2, nullptr, 0}, {0xff, 0xff, nullptr, 0}},
        0xff, // No CTS pin
        0, // No CTS
        IRQ_PRIORITY, 38, 24, // IRQ, rts_low_watermark, rts_high_watermark

  8. #8
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,344
    The simple answer was because when I ported over HardwareSerial early in T4 beta and some Serial ports were not working, I finally figured out about the IOMUXC_..._SELECT_INPUT settings, and at the time I was unsure if I needed to also set the ones on the OUTPUT pins as well. Turns out no. It is only needed IF the pin is used as INPUT... I was probably testing on Serial5 when I figured out some of this... Now it may not hurt to someday fill in that table if for no other reason it documents it.

    Also not sure if there would be any negative effect to set it anyway in normal HardwareSerial cases.

    Also at one point I wanted to add in some new non standard APIs, something like:
    Serial1.setDuplex(HardwareSerial::HALF_DUPLEX);

    And either another API, like Serial1.setPinDirection(HardwareSerial::TX_PIN_INP UT);

    Again these are off to top of the head apis. And actually preferred, would have the code to not need the second one, but to work off of the transmitterEnable code, which when you to a Serial1.write(), automatically switches to TX mode, and when the ISR detects that the queue is empty and last byte has output (transmit complete), to switch back to input mode...

    Actually alternative to new api, and something I thought about hacking in, might to have something like:
    #define HALF_DUPLEX_PIN 0xfe
    Serial1.transmtterEnable(HALF_DUPLEX_PIN);

    and/or alternatively if the pin passed in is the TX pin...

    But I am not sure what @Paul would think of these additions. If I were to do this, would probably also put it into T3.x code base as well...

  9. #9
    Senior Member
    Join Date
    Jan 2014
    Posts
    111
    Thanks for clarification.

    Another alternative to set the port into single-wire mode would be to add new defines for format that could be passed to begin function, but I think a new API for the single-wire operation would be cleaner (it could even be one function to enable/disable and set direction at the same time). And yes, that should deffinitely be done for T3.x as well.

  10. #10
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    Hi,
    trying to do something similar, in 9 bit mode, single wire TX2 pin connected to TX1 pin, code working is below.
    But I need to add a periodic reverse direction
    - TX2 ----> TX1
    - Reverse direction
    - TX1 ----> TX2
    ...
    How can the direction be changed ?
    Thanks.

    Code:
    void setup() {
      Serial.begin(115200);
      Serial1.begin(9600);
      IOMUXC_LPUART6_TX_SELECT_INPUT = 1;
      //LPUART6_CTRL |= (LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC); /* DONT USE, WILL FREZE */
      LPUART6_CTRL &= ~LPUART_CTRL_TXDIR;
      Serial2.begin(9600);
      Serial.println("Teensy 4.0 9bit Serial Test");
    }
    
    void loop() {
      static uint32_t x = 0x0;
      x++;
      if (x > 0x1FF)
        x = 0;
      Serial.print("x=");
      Serial.println(x, HEX);
      Serial2.write9bit(x);
      Serial2.flush();
      if (Serial1.available()) {
        Serial.print(millis());
        Serial.print(":");
        Serial.println(Serial1.read(), HEX);
      }
    }

  11. #11
    Senior Member
    Join Date
    Jan 2014
    Posts
    111
    Quote Originally Posted by phijun View Post
    How can the direction be changed ?
    You change direction by setting or clearing the LPUART_CTRL_TXDIR flag of the LPUARTx_CTRL register. For UART1 it means for receive mode:
    Code:
    LPUART6_CTRL &= ~LPUART_CTRL_TXDIR;
    and for transmit mode:
    Code:
    LPUART6_CTRL |= LPUART_CTRL_TXDIR;
    Of course you need to put UART2 into single wire mode as well.

    BTW I believe that for 9 bit you should also use the SERIAL_9N1 format in begin.

  12. #12
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    Will try, many thanks.

  13. #13
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    Almost.
    There is a remaining problem with either flush() that seems not being synchronous or a bug in the below code
    Using 8bit or 9bit shows same behavior, here is 8bit.

    Note
    There is #define SERIAL_9BIT_SUPPORT in teensy4 HardwareSerial.h
    and SERIAL1/2 _TX/2RX _BUFFER_SIZE are set to 128 in teensy4 HardwareSerial1.cpp and HardwareSerial2.cpp

    Console: with BUFFERED 8

    Code:
    Teensy 4.0 9bit Serial Single Wire with reversal test
    setMode:Serial2:TX
    setMode:Serial1:RX
    Write TX2:81:82:83:84:85:86:87:88
    Read  TX1:81:82:83:84:85:86:count=6
    setMode:Serial1:TX
    setMode:Serial2:RX
    Write TX1:1:2:3:4:5:6:7:8
    Read  TX2:1:2:3:4:5:6:count=6
    setMode:Serial2:TX
    setMode:Serial1:RX
    Write TX2:89:8A:8B:8C:8D:8E:8F:90
    Read  TX1:87:88:89:8A:8B:8C:8D:8E:count=8
    setMode:Serial1:TX
    setMode:Serial2:RX
    Write TX1:9:A:B:C:D:E:F:10
    Read  TX2:7:8:9:A:B:C:D:E:F:10:count=10
    setMode:Serial2:TX
    setMode:Serial1:RX
    Write TX2:91:92:93:94:95:96:97:98
    Read  TX1:8F:90:91:92:93:94:95:96:count=8
    setMode:Serial1:TX
    setMode:Serial2:RX
    Write TX1:11:12:13:14:15:16:17:18
    Read  TX2:11:12:13:14:15:16:count=6
    With BUFFERED 32
    Code:
    Teensy 4.0 9bit Serial Single Wire with reversal test
    setMode:Serial2:TX
    setMode:Serial1:RX
    Write TX2:81:82:83:84:85:86:87:88:89:8A:8B:8C:8D:8E:8F:90:91:92:93:94:95:96:97:98:99:9A:9B:9C:9D:9E:9F:A0
    Read  TX1:81:82:83:84:85:86:87:88:89:8A:8B:8C:8D:8E:8F:90:91:92:93:94:95:96:97:98:99:9A:9B:9C:9D:9E:count=30
    setMode:Serial1:TX
    setMode:Serial2:RX
    Write TX1:1:2:3:4:5:6:7:8:9:A:B:C:D:E:F:10:11:12:13:14:15:16:17:18:19:1A:1B:1C:1D:1E:1F:20
    Read  TX2:1:2:3:4:5:6:7:8:9:A:B:C:D:E:F:10:11:12:13:14:15:16:17:18:19:1A:1B:1C:1D:1E:count=30
    setMode:Serial2:TX
    setMode:Serial1:RX
    Write TX2:A1:A2:A3:A4:A5:A6:A7:A8:A9:AA:AB:AC:AD:AE:AF:B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:BA:BB:BC:BD:BE:BF:C0
    Read  TX1:9F:A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:AA:AB:AC:AD:AE:AF:B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:BA:BB:BC:BD:BE:count=32


    Code:
    #define SPEED     9600
    #define BUFFERED  8
    //#define BIT9
    
    
    #if defined(__IMXRT1062__) // Tensy 4.0
    volatile uint32_t *uartCtrl1;
    volatile uint32_t *uartCtrl2;
    #else // Tensy 3.x x>=2
    volatile uint8_t *uartCtrl1;
    volatile uint8_t *uartCtrl2;
    #endif
    
    void setMode(uint8_t number, uint8_t tx)
    {
    #if defined(__IMXRT1062__)
      volatile uint32_t *uartCtrl = NULL;
    #else
      volatile uint8_t *uartCtrl1 = NULL;
    #endif
      Serial.print("setMode:Serial");
      Serial.print(number);
      if (tx)
        Serial.print(":TX");
      else
        Serial.print(":RX");
      if (number == 1)
        uartCtrl = uartCtrl1;
      if (number == 2)
        uartCtrl = uartCtrl2;
      if (!uartCtrl) {
        Serial.println("setMode error");
        return;
      }
      Serial.println("");
      if (tx) {
    #if defined(__IMXRT1062__)
        *uartCtrl |= LPUART_CTRL_TXDIR;
    #else
        *uartCtrl |= UART_C3_TXDIR;
    #endif
      } else {
    #if defined(__IMXRT1062__)
        *uartCtrl &= ~LPUART_CTRL_TXDIR;
    #else
        *uartCtrl &= ~UART_C3_TXDIR;
    #endif
      }
    }
    
    void setup() {
      Serial.begin(115200);
    
      // Single wire: TX2 connected to TX1
    #ifdef BIT9
      Serial1.begin(SPEED, SERIAL_9O1);
    #else
      Serial1.begin(SPEED);
    #endif
    #if defined(__IMXRT1062__)
      uartCtrl1 = &LPUART6_CTRL;
      LPUART6_CTRL |= (LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC);
      IOMUXC_LPUART6_TX_SELECT_INPUT = 1;
    #else
      uartCtrl1 = &UART0_C3;
      UART0_C1 |= (UART_C1_LOOPS | UART_C1_RSRC);
    #endif
    
    #ifdef BIT9
      Serial2.begin(SPEED, SERIAL_9O1);
    #else
      Serial2.begin(SPEED);
    #endif
    #if defined(__IMXRT1062__)
      uartCtrl2 = &LPUART4_CTRL;
      LPUART4_CTRL |= (LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC);
      IOMUXC_LPUART4_TX_SELECT_INPUT = 2;
    #else
      uartCtrl2 = &UART1_C3;
      UART1_C1 |= (UART_C1_LOOPS | UART_C1_RSRC);
    #endif
    
    #if defined(__IMXRT1062__)
      Serial.println("Teensy 4.0 9bit Serial Single Wire with reversal test");
    #else
      Serial.println("Teensy 3.x x>=2 9bit Serial Single Wire with reversal test");
    #endif
    }
    
    void loop() {
      static uint32_t data1 = 0x0;
    #ifdef BIT9
      static uint32_t data2 = 0xff;
    #else
      static uint32_t data2 = 0x80;
    #endif
      static uint8_t reverse = 0;
      uint8_t cnt;
      int val1, val2;
    
      cnt = 0;
      if (reverse == 0) {
        setMode(2, 1); // Serial2 TX
        setMode(1, 0); // Serial1 RX
        Serial.print("Write TX2");
        for (uint8_t i = 0; i  < BUFFERED; i++) {
    #ifdef BIT9
          if (++data2 > 0x1FF)
            data2 = 0;
          Serial2.write9bit(data2);
    #else
          if (++data2 > 0xFF)
            data2 = 0;
          Serial2.write(data2);
    #endif
          Serial.print(":");
          Serial.print(data2, HEX);
        }
        Serial2.flush();
        Serial.println("");
        Serial.print("Read  TX1");
        while (Serial1.available()) {
          val1 = Serial1.read();
          Serial.print(":");
          Serial.print(val1, HEX);
          cnt++;
        }
        Serial.print(":count=");
        Serial.println(cnt);
      } else {
        setMode(1, 1); // Serial1 TX
        setMode(2, 0); // Serial2 RX
        Serial.print("Write TX1");
        for (uint8_t i = 0; i  < BUFFERED; i++) {
    #ifdef BIT9
          if (++data1 > 0x1FF)
            data1 = 0;
          Serial1.write9bit(data1);
    #else
          if (++data1 > 0xFF)
            data1 = 0;
          Serial1.write(data1);
    #endif
          Serial.print(":");
          Serial.print(data1, HEX);
        }
        Serial1.flush();
        Serial.println("");
        Serial.print("Read  TX2");
        while (Serial2.available()) {
          val2 = Serial2.read();
          Serial.print(":");
          Serial.print(val2, HEX);
          cnt++;
        }
        Serial.print(":count=");
        Serial.println(cnt);
      }
      reverse = ~reverse;
    }

  14. #14
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    And in 9bit mode write and read do not match expected 0x101, read 0x301
    Code:
    Teensy 4.0 9bit Serial Single Wire with reversal test
    setMode:Serial2:TX
    setMode:Serial1:RX
    Write TX2:100:101:102:103:104:105:106:107
    Read  TX1:100:301:302:103:304:105:count=6
    Using for val1 and val2 a additional &= 0x1ff;
    Code:
    val2 = Serial2.read();
    #ifdef BIT9
          val2 &= 0x1ff;
    #endif
    gives correct values, the teensy 4.0 lacks a &= 0x1ff somewhere ?
    Last edited by phijun; 01-18-2020 at 12:19 PM.

  15. #15
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,344
    Sorry I am not a 9 bits expert. To know the right answer.

    That is you are asking for: SERIAL_9O1

    If I understand correctly you are asking for 9 Data bits plus an ODD parity bit. So the system is probably returning 10 bits of data. If you are simply wanting/needing 9 bits of data, you might try: SERIAL_9N1

    Again I am not sure logically what is correct. If I am guessing, with how the system is working:

    If you do SERIAL_7O1 the system both T3.x and T4 will return 8 bits including the Parity bit
    If you do SERIAL_8O1 My gut tells me neither system will preserve the parity bit (9th bit), unless the define:
    Code:
    //#define SERIAL_9BIT_SUPPORT
    is uncommented.

    Why: without the define the queue is only 8 bits. My guess is if the #define is there. My quick look through T3.x Serial1 code looks like, it will NOT preserve the parity bit in this case. But guessing T4 will.
    Likewise I don't think T3.x will ever keep the parity bit on SERIAL_9x...

    But question is should we?

  16. #16
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    It's SERIAL_9O1 because the real device is using this.
    Using the &= 0x1ff is ok, but I think the parity bit(s) aren't supposed to be returned by read().

  17. #17
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    With
    #define SPEED 9600
    #define BUFFERED 64
    #define BIT9
    and adding a delay(100) after the Serial1 or 2 .flush() and the &= 0x1ff it's ok, just a bit puzzled by the need of the delay().

    Code:
    Write TX2:100:101: ... :13E:13F
    Read  TX1:100:101: ... :13E:13F:count=64
    setMode:Serial1:TX
    setMode:Serial2:RX
    Write TX1:1:2: ... :3F:40
    Read  TX2:1:2: ... :3F:40:count=64

  18. #18
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,344
    However with your setup, I am starting to get reasonable results when I switch to using Serial_9N1
    i.e. not generate or use parity bits.

    Also for double security, there are two places the 9th bit can come through, both in the data register as well as in the CTRL register (to sort of emulate the T3.x). So in your case of switching back and forth I would probably add:
    Code:
    #if defined(__IMXRT1062__)
        *uartCtrl &= ~(LPUART_CTRL_R9T8 | LPUART_CTRL_R8T9);
        *uartCtrl |= LPUART_CTRL_TXDIR;
    #else
        *uartCtrl |= UART_C3_TXDIR;
    #endif
    Not sure if similar thing in T3.x as well...

    As for only getting 30 out of 32... Probably have not yet processed the data in the RX

    @Paul - Wondering? Right now we (I) set the RX water mark to be half full (2) in begin... I was able to get this program to work as expected by setting the water mark to 0....
    Code:
    #if defined(__IMXRT1062__)
      uartCtrl1 = &LPUART6_CTRL;
      LPUART6_CTRL |= (LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC);
      LPUART6_WATER &= 0xffff;
      IOMUXC_LPUART6_TX_SELECT_INPUT = 1;
    This leaves the Watermark for TX still at 2...

    Wondering if we should change the default code to go ahead and set RX to 0?
    AND/OR should functions like: Serail1.available() also look at the WATER register for the RX Count?

  19. #19
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    New version of code, better display, and
    change int show = 1; to int show = 0;
    shows only errors.

    Code:
    #define SPEED     9600
    #define BUFFERED  8
    #define BIT9
    #define DELAY 10
    #define DATA_PER_LINE 28
    
    int check1[BUFFERED];
    int check2[BUFFERED];
    
    int show = 1;
    #define DBG(...)   { if (show) Serial.print(__VA_ARGS__); }
    #define DBGN(...)  { if (show) Serial.println(__VA_ARGS__); }
    
    
    #if defined(__IMXRT1062__) // Tensy 4.0
    volatile uint32_t *uartCtrl1;
    volatile uint32_t *uartCtrl2;
    #else // Tensy 3.x x>=2
    volatile uint8_t *uartCtrl1;
    volatile uint8_t *uartCtrl2;
    #endif
    
    void setMode(uint8_t number, uint8_t tx)
    {
    #if defined(__IMXRT1062__)
      volatile uint32_t *uartCtrl = NULL;
    #else
      volatile uint8_t *uartCtrl1 = NULL;
    #endif
      DBG("setMode:Serial");
      DBG(number);
      if (tx) {
        DBG(":TX");
      } else {
        DBG(":RX");
      }
      if (number == 1)
        uartCtrl = uartCtrl1;
      if (number == 2)
        uartCtrl = uartCtrl2;
      if (!uartCtrl) {
        DBGN("setMode error");
        return;
      }
      DBGN("");
      if (tx) {
    #if defined(__IMXRT1062__)
        *uartCtrl |= LPUART_CTRL_TXDIR;
        //*uartCtrl &= ~(LPUART_CTRL_R9T8 | LPUART_CTRL_R8T9);
    #else
        *uartCtrl |= UART_C3_TXDIR;
    #endif
      } else {
    #if defined(__IMXRT1062__)
        *uartCtrl &= ~LPUART_CTRL_TXDIR;
        //*uartCtrl &= ~(LPUART_CTRL_R9T8 | LPUART_CTRL_R8T9);
    #else
        *uartCtrl &= ~UART_C3_TXDIR;
    #endif
      }
    }
    
    void setup() {
      Serial.begin(115200);
    
      // Single wire: TX2 connected to TX1
    #ifdef BIT9
      Serial1.begin(SPEED, SERIAL_9O1);
    #else
      Serial1.begin(SPEED);
    #endif
    #if defined(__IMXRT1062__)
      uartCtrl1 = &LPUART6_CTRL;
      LPUART6_CTRL |= (LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC);
      //LPUART6_WATER &= 0xffff;
      IOMUXC_LPUART6_TX_SELECT_INPUT = 1;
    #else
      uartCtrl1 = &UART0_C3;
      UART0_C1 |= (UART_C1_LOOPS | UART_C1_RSRC);
    #endif
    
    #ifdef BIT9
      Serial2.begin(SPEED, SERIAL_9O1);
    #else
      Serial2.begin(SPEED);
    #endif
    #if defined(__IMXRT1062__)
      uartCtrl2 = &LPUART4_CTRL;
      LPUART4_CTRL |= (LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC);
      //LPUART4_WATER &= 0xffff;
      IOMUXC_LPUART4_TX_SELECT_INPUT = 2;
    #else
      uartCtrl2 = &UART1_C3;
      UART1_C1 |= (UART_C1_LOOPS | UART_C1_RSRC);
    #endif
    
    #if defined(__IMXRT1062__)
      Serial.println("Teensy 4.0 9bit Serial Single Wire with reversal test");
    #else
      Serial.println("Teensy 3.x x>=2 9bit Serial Single Wire with reversal test");
    #endif
    }
    
    void loop() {
      static uint32_t data1 = 0x0;
    #ifdef BIT9
      static uint32_t data2 = 0xff;
    #else
      static uint32_t data2 = 0x80;
    #endif
      static uint8_t reverse = 0;
      uint8_t cnt;
      int val, numb;
    
      cnt = 0;
      if (reverse == 0) {
        setMode(2, 1); // Serial2 TX
        setMode(1, 0); // Serial1 RX
        DBG("Write TX2");
        numb = 0;
        for (uint8_t i = 0; i  < BUFFERED; i++) {
    #ifdef BIT9
          if (++data2 > 0x1FF)
            data2 = 0;
          Serial2.write9bit(data2);
    #else
          if (++data2 > 0xFF)
            data2 = 0;
          Serial2.write(data2);
    #endif
          check2[i] = data2;
          DBG(":");
          DBG(data2, HEX);
          if (++numb > DATA_PER_LINE) {
            DBGN("");
            numb = 0;
          }
        }
        Serial2.flush();
        delay(DELAY);
        DBGN("");
        DBG("Read  TX1");
        numb = 0;
        cnt = 0;
        while (Serial1.available()) {
          val = Serial1.read();
    #ifdef BIT9
          val &= 0x1ff;
    #endif
          DBG(":");
          DBG(val, HEX);
          if (val != check2[cnt]) {
            Serial.println("R:W Error");
          }
          cnt++;
          if (++numb > DATA_PER_LINE) {
            DBGN("");
            numb = 0;
          }
        }
        DBG(":count=");
        DBGN(cnt);
        if (cnt != BUFFERED) {
          Serial.print("Count2 Error ");
          Serial.println(cnt);
        }
      } else {
        setMode(1, 1); // Serial1 TX
        setMode(2, 0); // Serial2 RX
        DBG("Write TX1");
        numb = 0;
        for (uint8_t i = 0; i  < BUFFERED; i++) {
    #ifdef BIT9
          if (++data1 > 0x1FF)
            data1 = 0;
          Serial1.write9bit(data1);
    #else
          if (++data1 > 0xFF)
            data1 = 0;
          Serial1.write(data1);
    #endif
          check1[i] = data1;
          if (++numb > DATA_PER_LINE) {
            DBGN("");
            numb = 0;
          }
          DBG(":");
          DBG(data1, HEX);
        }
        Serial1.flush();
        delay(DELAY);
        DBGN("");
        DBG("Read  TX2");
        numb = 0;
        cnt = 0;
        while (Serial2.available()) {
          val = Serial2.read();
    #ifdef BIT9
          val &= 0x1ff;
    #endif
          if (val != check1[cnt]) {
            Serial.println("Error");
          }
          DBG(":");
          DBG(val, HEX);
          cnt++;
          if (++numb > DATA_PER_LINE) {
            DBGN("");
            numb = 0;
          }
        }
        DBG(":count=");
        DBGN(cnt);
        if (cnt != BUFFERED) {
          Serial.print("Count1 Error ");
          Serial.println(cnt);
        }
      }
      reverse = ~reverse;
    }

  20. #20
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,344
    @phijun - for the heck of it I have sent email to @PaulStoffregen about some of these issues and asked about the possibility of resolving some of this internal to the Hardware Serial port code.

    I pushed up a new Fork/branch: https://github.com/KurtE/cores/tree/T4_UART_ENHANCE

    Which set the RX watermark to 0, plus added a half duplex mode, which knows about most of the stuff you muck with on T4. That is the Serial port tables were updated with the proper TX Input Select values (if appropriate) and it automatically set it.

    I also defined a new FORMAT value that I currently require to OR in with our other formats to say half duplex. By default the TX pin would be configured to be in RX mode. Only when you do writes to the Serial port will it be converted to TX mode and it will then write out the stuff you say to output. When the last bits are transferred out the Serial ports interrupt handler will then switch it back to RX mode...

    I hacked up your earlier test case, more or less to remove most of the switching and init stuff. Currently I have it running in 8 bit mode.

    The example code looks like:
    Code:
    #define SPEED     9600
    #define BUFFERED  32
    //#define BIT9
    
    
    void setup() {
      Serial.begin(115200);
    
      // Single wire: TX2 connected to TX1
    #ifdef BIT9
      Serial1.begin(SPEED, SERIAL_9N1 | SERIAL_HALF_DUPLEX);
      Serial2.begin(SPEED, SERIAL_9N1 | SERIAL_HALF_DUPLEX);
    #else
      Serial1.begin(SPEED, SERIAL_8N1 | SERIAL_HALF_DUPLEX);
      Serial2.begin(SPEED, SERIAL_8N1 | SERIAL_HALF_DUPLEX);
    #endif
    
    #if defined(__IMXRT1062__)
      Serial.println("Teensy 4.0 9bit Serial Single Wire with reversal test");
    #else
      Serial.println("Teensy 3.x x>=2 9bit Serial Single Wire with reversal test");
    #endif
    }
    
    void loop() {
      static uint32_t data1 = 0x0;
    #ifdef BIT9
      static uint32_t data2 = 0xff;
    #else
      static uint32_t data2 = 0x80;
    #endif
      static uint8_t reverse = 0;
      uint8_t cnt;
      int val1, val2;
    
      cnt = 0;
      if (reverse == 0) {
        Serial.print("Write TX2");
        for (uint8_t i = 0; i  < BUFFERED; i++) {
    #ifdef BIT9
          if (++data2 > 0x1FF)
            data2 = 0;
          Serial2.write9bit(data2);
    #else
          if (++data2 > 0xFF)
            data2 = 0;
          Serial2.write(data2);
    #endif
          Serial.print(":");
          Serial.print(data2, HEX);
        }
        Serial2.flush();
        Serial.println("");
        Serial.print("Read  TX1");
        while (Serial1.available()) {
          val1 = Serial1.read();
          Serial.print(":");
          Serial.print(val1, HEX);
          cnt++;
        }
        Serial.print(":count=");
        Serial.println(cnt);
      } else {
        Serial.print("Write TX1");
        for (uint8_t i = 0; i  < BUFFERED; i++) {
    #ifdef BIT9
          if (++data1 > 0x1FF)
            data1 = 0;
          Serial1.write9bit(data1);
    #else
          if (++data1 > 0xFF)
            data1 = 0;
          Serial1.write(data1);
    #endif
          Serial.print(":");
          Serial.print(data1, HEX);
        }
        Serial1.flush();
        Serial.println("");
        Serial.print("Read  TX2");
        while (Serial2.available()) {
          val2 = Serial2.read();
          Serial.print(":");
          Serial.print(val2, HEX);
          cnt++;
        }
        Serial.print(":count=");
        Serial.println(cnt);
      }
      reverse = ~reverse;
    }
    But again note: I am not sure if these changes will make it into the project code base or not. And if so, if I need to add it to T3.x as well. Note in T3.x code cases, it is a little more complex as the code for each serial port is separate, in their own source files
    so would have to replicate the changes several times.

  21. #21
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    Perhaps a word about why a reliable single wire serial with tx/rx reversal API
    in Teensyduino 'out of the box' could be a plus versus Arduino.

    Many hobby Remote Control manufacturers (Jeti, FRSky, Multiplex SLRX, Graupner, ...)
    have open their protocols so people can build their own sensors. There are even 'bridges'
    Arduino based to use a sensor from brand X on a RC Radio system brand Y. There are
    numerous libraries Arduino based available for that for many RC brands.
    But they require Single Wire Serial at various speed and some need TX/RX reversal,
    which isn't available out of the box on Arduino.

    The brand I use is 'JETI model'. They have 2 open protocols
    - JETI Telemetry Protocol 9600 baud 9O2 (an unusual format, pff, but Teensy 9O1 works)
    - JETI EX Bus Communication Protocol 125000/250000 baud 8N1 (cool, a standard format)
    Both need Single Wire Serial and TX/RX reversal. Regarding the speed of the second one a fast cpu is required.
    Teensy is a good choice for me because out of the box optionnal 9bit support, numerous Serials/I2C, HW floating ops,
    fast cpu, memory, ease of use and Single wire serial with tx/rx reversal available in the HW.
    But it requires some extra non trivial work.

    I've added the teensy 4.0 half-duplex uart registers handling from the test in my 2 JETI protocol libs for the Teensy 3.x>=5 and now Teensy 4.0.
    It works perfectly with a Teensy 3.5 and a Teensy 4.0 home made sensor with a real Jeti RX Receivers and a JETI DC-24 Radio, for both protocols.

    Even with the available(),flush(),read() (& 0x1ff required) behavior shown in the test.

    Thanks to all of you.

  22. #22
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    FYI
    The flush/available behavior can also be shown with a standard Serial test program.
    Without DELAY defined, there are errors en Teensy 4.0, for example
    Count Error 6
    At that speed with default 8N1 there is 1 byte each 9600/10/1000=0.96 milliseconds.
    A slow AVR MEGA 2560 doesn't need the delay.
    On the 600MHz fast teensy the delay is needed because the CPU is faster than bytes inflight after the flush
    are buffered in RX due to low baud ? (a logic error in the program)



    Code:
    #define SPEED         9600
    #define BUFFERED      8
    //#define DELAY         5
    #define DATA_PER_LINE 20
    
    int show = 0;
    #define DBG(...)   { if (show) Serial.print(__VA_ARGS__); }
    #define DBGN(...)  { if (show) Serial.println(__VA_ARGS__); }
    
    void setup() {
      Serial.begin(115200);
      // TX2 connected to RX1
      Serial1.begin(SPEED);
      Serial2.begin(SPEED);
    }
    
    void loop() {
      static uint8_t data2 = 0x40;
      uint8_t cnt;
      uint8_t numb;
      int val;
    
      DBGN("Write TX2");
      numb = 0;
      for (uint8_t i = 0; i  < BUFFERED; i++) {
        data2++;
        Serial2.write(data2);
        DBG(":");
        DBG(data2, HEX);
        if (++numb >= DATA_PER_LINE) {
          DBGN("");
          numb = 0;
        }
      }
      Serial2.flush();
    #ifdef DELAY
      delay(DELAY);
    #endif
      DBGN("");
      DBGN("Read  TX1");
      numb = 0;
      cnt = 0;
      while (Serial1.available()) {
        val = Serial1.read();
        DBG(":");
        DBG(val, HEX);
        cnt++;
        if (++numb >= DATA_PER_LINE) {
          DBGN("");
          numb = 0;
        }
      }
      DBG(":count=");
      DBGN(cnt);
      if (cnt != BUFFERED) {
        Serial.print("Count Error ");
        Serial.println(cnt);
      }
    }
    Last edited by phijun; 01-19-2020 at 02:31 PM.

  23. #23
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,344
    Yes as mentioned yesterday - This has to do with setting the RX WATER mark value > 0... In my branch a couple of posts up I set it to 0...

  24. #24
    Junior Member
    Join Date
    Dec 2016
    Posts
    16
    I will change my test program so speed doesn't matter

    Code:
      cnt = 0;
      while (cnt < BUFFERED) {
        if (Serial1.available()) {
          val = Serial1.read();
          DBG(":");
          DBG(val, HEX);
          cnt++;
          if (++numb >= DATA_PER_LINE) {
            DBGN("");
            numb = 0;
          }
        }
      }

Posting Permissions

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