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

Thread: Teensy 4.1 - single wire half duplex UART

  1. #1
    Junior Member
    Join Date
    Aug 2020
    Posts
    14

    Teensy 4.1 - single wire half duplex UART

    Hi all,
    does anyone know how single wire half duplex works on a teensy 4.1?
    I couldn't find anything about that.

    My project is a diy RC transmitter using a TBS crossfire transmitter module + receiver.
    The communication to the transmitter works over single wire half duplex uart with a specified protocol.

    Thank you
    Best regards from Austria

  2. #2
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    191
    Search for onewire in the library manager. It works the same as with any micro.

  3. #3
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,893
    Yes - There are a variety of ways to do it depending on your needs.

    a) Extra hardware typically using 3 IO pins. RX, TX and and TX-Enable pin. There are a variety of ways to do it, with different chip(s) and you then yo more or less emulate the functionality of RS485 like module. There is code in HardwareSerial that if you set the TX_Enable pin you can have it automatically switch to TX whenever you try to write something out and it will then switch back to RX mode when the TX buffer is empty and the last bits have been shifted out the hardware.

    b) Software. You can setup to do TX pin as bidirectional with the Uarts. I have examples of this up in my BioloidSerial code base that is an older library I updated to support Dyamixel TTL Servos which is half duplex.
    You can see some of it up in my library: https://github.com/KurtE/BioloidSeri.../dxlSerial.cpp
    Search for IMXRT...
    But in the init code I do:
    Code:
    #elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
    // Teensy4
    static IMXRT_LPUART_t *s_pkuart = nullptr;
    void dxlInit(long baud, HardwareSerial* pserial, int direction_pin, int tx_pin, int rx_pin ) {
        // Need to enable the PU resistor on the TX pin
        s_paxStream = pserial;
        s_direction_pin = direction_pin;    // save away.
        if (pserial == &Serial1) s_pkuart = &IMXRT_LPUART6;
        else if (pserial == &Serial2) s_pkuart = &IMXRT_LPUART4;
        else if (pserial == &Serial3) s_pkuart = &IMXRT_LPUART2;
        else if (pserial == &Serial4) s_pkuart = &IMXRT_LPUART3;
        else if (pserial == &Serial5) s_pkuart = &IMXRT_LPUART8;
        else if (pserial == &Serial6) {s_pkuart = &IMXRT_LPUART1; Serial.println("dxlInit Serial6"); }
        else if (pserial == &Serial7) s_pkuart = &IMXRT_LPUART7;
    //    else if (pserial == &Serial8) s_pkuart = &IMXRT_LPUART5;
        pserial->begin(baud);
        if (tx_pin != -1) {
            pserial->setTX(tx_pin);
        } else {
            tx_pin = 1; // default Serial 1 TX
        }
        if (rx_pin != -1) {
            pserial->setRX(rx_pin);
        }
        if (s_direction_pin == -1) {
            s_pkuart->CTRL |= LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC;
    /*
            s_pkuart->C1 |= UART_C1_LOOPS | UART_C1_RSRC;
            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;
    */        
        } else {
            Serial.printf("Direction Pin:%d\n", s_direction_pin);
            pserial->transmitterEnable(s_direction_pin);
        }
        setRX(0);
    }
    Note: I have played as much with this as I did with T3.x... So not sure yet if it might still want additional stuff to configure the TX pins port or not...

    And then you need the code to switch to TX mode and RX mode, which looks something like:
    Code:
    void setTX(int id){
        if (s_direction_pin != -1) {
    #if !defined(TEENSYDUINO)
            digitalWrite(s_direction_pin, HIGH);
    #endif
            return;
        }
    
        if (s_pkuart) s_pkuart->CTRL |= LPUART_CTRL_TXDIR;
    
    }
    
    
    
    void flushAX12InputBuffer(void)  {
        // First lets clear out any RX bytes that may be lingering in our queue
        while (s_paxStream->available()) {
            s_paxStream->read();
        }
    }
    
    void setRX(int id){
    
        // First clear our input buffer
    	flushAX12InputBuffer();
        //digitalWriteFast(4, HIGH);
        // Now setup to enable the RX and disable the TX
        // If we are using hardware direction pin, can bypass the rest...
        if (s_direction_pin != -1) {
    #if !defined(TEENSYDUINO)
            // Make sure all of the output has happened before we switch the direction pin.
            s_paxStream->flush();
            digitalWrite(s_direction_pin, LOW);
    #endif
            return;
        }
    
        // Make sure everything is output before switching.
        s_paxStream->flush();
        if (s_pkuart) s_pkuart->CTRL &= ~LPUART_CTRL_TXDIR;
    }
    And knowing when you wish to switch...

  4. #4
    Junior Member
    Join Date
    Aug 2020
    Posts
    14
    @bicycleguy
    Thank you very much!
    https://www.pjrc.com/teensy/td_libs_OneWire.html

    BTW what do you mean with micro?

    @KurtE

    Wow, thank you!

    Best regards!

    PS: tomorrow i will ask you a few more questions - its late now :P
    Last edited by Al3xand3r05; 08-23-2020 at 10:02 PM.

  5. #5
    Senior Member
    Join Date
    Jul 2020
    Posts
    464
    Quote Originally Posted by KurtE View Post
    Yes - There are a variety of ways to do it depending on your needs.

    Indeed - the simplest extra hardware is a wired-AND circuit. TX pin to 1N4148 cathode, RX pin to the bus wire
    and to 1N4148 anode. 4k7 resistor to Vcc somewhere on the bus.

    Since normal UART mode is inactive HIGH, this just works as open-drain bus.

    Downsides are there's local echo that you'll have to explicitly ignore, and no explicit collision detection. If
    either side powers down it jams the bus low(-ish)...

    You can defeat the local echo with something like this:
    Click image for larger version. 

Name:	half_duplex.jpg 
Views:	20 
Size:	42.5 KB 
ID:	21452
    where the "TXenable" isn't really the best name, but the same code/pinout can then work on an RS485 chip,
    assuming you tristate the TXenable and use appropriate pull-down.

    Many other ways to do the hardware.

  6. #6
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,893
    Robotis does it a couple of different ways. The one in the OpenCM9.04 is similar to what I have done for some of my own boards...

    Click image for larger version. 

Name:	screenshot.jpg 
Views:	42 
Size:	174.2 KB 
ID:	21453

  7. #7
    Junior Member
    Join Date
    Aug 2020
    Posts
    14
    Hi,
    thank you for all of your replies!

    You described different ways to do it, but what do you think would be the easiest and suitable for my needs (talk to a TBS crossfire module over their specified CRSF protocol using single wire UART) ?

    Sorry for my English - I am from Austria....

    Best regards!
    Al3xand3r05

  8. #8
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,893
    Sorry I don't really much (anything) about these modules. It might help to know what unit you are trying to setup to use.

    For example with one unit I did a quick look, sounded like you could configure the pins, in BRIDGE mode, and then it has a TX pin, RX pin and a RTS pin...

    But assuming this is not the setup, than need to know things like Baud Rate, is 3.3v signal sufficient... If so I would probably start off with the Software version stuff I mentioned, as it takes no additional hardware. Just connect up to the Serial port TX pin and a common gnd.

  9. #9
    Junior Member
    Join Date
    Aug 2020
    Posts
    14
    Hi it's a tbs crossfire transmitter module + receiver. The transmitter received data from the RC (the teensy 4.1 in this case) and sends data to it (telemetry). It's a single wire half duplex UART.
    The module gets +3,3V.
    Baud rate is 416k
    Best regards!
    Al3xand3r05

  10. #10
    Junior Member
    Join Date
    Aug 2020
    Posts
    14
    Btw, I am not allowed to publish the CRSF specifications, but I can answer your questions mostly.

  11. #11
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,893
    I am not sure there is much more for me to suggest. Especially if we have not details on the protocol.

    At one point earlier this year I suggested that we add half duplex support directly with the Hardware Serial classes and did an implementation of it in the Pull Request:
    https://github.com/PaulStoffregen/cores/pull/419

    So far this has not gone anywhere.

    But my suggestion are, try a few things and see what works for you.

    There is hopefully enough details in the BioloidSerial code I linked to earlier where it shows how to configure the UART to allow Half duplex. Plus code that then switches the TX pin into TX mode or RX Mode...

    Alternatively when I was doing some quick and dirty hacking, I have Jerry rigged a setup using hardware that has a half duplex circuit on it, like I have done it with an OpenCM 485 expansion board. and setup to get a half duplex setup. However some of these are setup for the half duplex line be driven at 5v. Which I am not sure if your radio can handle 5v TTL levels?

  12. #12
    Junior Member
    Join Date
    Aug 2020
    Posts
    14
    Hi, no this module just supports 3,3V and no TTL.
    There is a master and a slave.

    What do you think about the onewire library - could this maybe the right for me?

    Best regards!

  13. #13
    Junior Member
    Join Date
    Aug 2020
    Posts
    14
    Hi, no this module just supports 3,3V and no TTL.
    There is a master and a slave.

    What do you think about the onewire library - could this maybe the right for me?

    Best regards!

  14. #14
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,893
    Sorry I have no idea if it would work or not. My Impression is that 1-wire protocol: https://en.wikipedia.org/wiki/1-Wire is a slower 16.3K protocol and you says yours is at 416K and that it is a specific protocol, which is probably different than yours.

    Best thing I would suggest is try it and see if it works for you.

    And again if it were me, and I had the stuff to experiment with, I would probably simply try a few things to see if they work. For example lets say try hooking up one of the TX pins to your hardware, like TX3(pin 14)

    Then write a quick and dirty sketch to see if you can talk to your hardware. Again I have clues of your protocol and how much is needed to start it up. But suppose there is some simple packet, like query a register, maybe there is something like Query product ID... And I would start off seeing if I could read it. Note: Again I know nothing here, like maybe you have to send out a bunch of initialization data or reset or ??? And maybe that would be first test...

    Side note: when implementing something like a new protocol or the like it always helps to have a scope or logic analyzer to watch the data to see states of things and see what the actual data is...

    And again I am only doing an outline of code, that probably has code issues and the like and simply to experiment with. Your final code may/probably would be very different...

    But hopefully you can extract some information from this...

    Code:
    #define DEBUG_PIN 2
    #define SERIAL_TX_PIN 14
    
    uint8_t some_message[] = {0xff, 0xff}; // ******* need to setup message...
    elapsedMillis  em_last_recv;
    
    IMXRT_LPUART_t *s_pkuart = &IMXRT_LPUART2;  // underlying hardware UART for Serial3
    void setup() {
      while (!Serial && millis() < 5000) ; // wait up to 5 seconds for terminal monitor
      pinMode(DEBUG_PIN, OUTPUT);
      // Setup Serial3 for Half duplex:
      Serial3.begin(416000); // would probably want to see how close we are
      // Lets setup that IO pin to be in half duplex
      s_pkuart->CTRL |= LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC;
    
      // Lets try to enable PU resistor on that pin...
      *(portControlRegister(SERIAL_TX_PIN)) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3) | IOMUXC_PAD_HYS;
      IOMUXC_LPUART2_TX_SELECT_INPUT = 1; // need to get to right one...
      setPortRX();
      em_last_recv = 0;
    }
    
    void setPortTX() {
      digitalWrite(DEBUG_PIN, HIGH);
      s_pkuart->CTRL |= LPUART_CTRL_TXDIR;  // Set in to TX Mode...
    }
    
    void setPortRX() {
      Serial3.flush();  // Make sure we output everything first before changing state.
      s_pkuart->CTRL &= ~LPUART_CTRL_TXDIR;  // Set in to RX Mode...
      digitalWrite(DEBUG_PIN, LOW);
    }
    
    
    void loop() {
      int ch;
      int received_count = 0;
    
      // Try receiving any data
      while (em_last_recv < 500) {
        ch = Serial3.read();
        if (ch >= 0) {
          Serial.printf("%02x ", ch);
          received_count++;
          if (!(received_count & 0x1f)) Serial.println();
          em_last_recv = 0; // reset our timeout (maybe?)
        }
      }
      if (received_count) Serial.println();
    
      // output new packet
      setPortTX(); // put port in TX mode
      Serial.write(some_message, sizeof(some_message));
      setPortRX();  // put back into RX mode.
      em_last_recv = 0;
    }

  15. #15
    Junior Member
    Join Date
    Aug 2020
    Posts
    14
    Hi, thank you for your explanations! The problem is that I don't own such a transmitter module for testing and I would only buy it if I am sure that it works - it's quiet expensive for a student^^
    I have a scope (100mhz analog Tektronix) but no logic analyzer.

    Thank you for your example code!
    I will try to understand all what's in the protocol specs and then maybe write again here.

    Best regards!
    Al3xand3r05

  16. #16
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,893
    For the fun fo it I extended the code above to setup two Serial ports in half duplex mode (Serial3 and Serial4) and it is quick and dirty code, It is setup to have you jumper Pin 14 to pin 17. And when Serial3 outputs something Serial4 will receive it. When it get the \n character it echoes the whole string back which then Serial3 will the when it receives it will write it out to the Serial port...

    Code:
    #define DEBUG_PIN 8
    #define LOOP_DELAY_MS 25
    
    // Serial3 information - Say this is our Main Serial port
    #define SERIALM Serial3
    #define SERIALM_TX_PIN 14
    IMXRT_LPUART_t *s_pkuartM = &IMXRT_LPUART2;  // underlying hardware UART for Serial3
    
    // Lets define this as our Echo Serial port...
    #define SERIALE Serial4
    #define SERIALE_TX_PIN 17
    IMXRT_LPUART_t *s_pkuartE = &IMXRT_LPUART3;  // underlying hardware UART for Serial3
    
    elapsedMillis  em_last_recv;
    uint8_t receive_buffer[80];
    uint8_t receive_index = 0;
    
    void setup() {
      while (!Serial && millis() < 5000) ; // wait up to 5 seconds for terminal monitor
      pinMode(DEBUG_PIN, OUTPUT);
      pinMode(13, OUTPUT);
    
      // Setup Serial3 for Half duplex:
      SERIALM.begin(416000); // would probably want to see how close we are
      // Lets setup that IO pin to be in half duplex
      s_pkuartM->CTRL |= LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC;
    
      // Lets try to enable PU resistor on that pin...
      *(portControlRegister(SERIALM_TX_PIN)) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3) | IOMUXC_PAD_HYS;
      IOMUXC_LPUART2_TX_SELECT_INPUT = 1; // need to get to right one...
      setPortRX();
    
      // Also Setup Serial4 for Half duplex:
      SERIALE.begin(416000); // would probably want to see how close we are
      // Lets setup that IO pin to be in half duplex
      s_pkuartE->CTRL |= LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC;
    
      // Lets try to enable PU resistor on that pin...
      *(portControlRegister(SERIALE_TX_PIN)) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3) | IOMUXC_PAD_HYS;
      IOMUXC_LPUART3_TX_SELECT_INPUT = 0; // need to get to right one...
      s_pkuartE->CTRL &= ~LPUART_CTRL_TXDIR;  // Set in to RX Mode...
    
    
      em_last_recv = 0;
    }
    
    void setPortTX() {
      digitalWrite(DEBUG_PIN, HIGH);
      s_pkuartM->CTRL |= LPUART_CTRL_TXDIR;  // Set in to TX Mode...
    }
    
    void setPortRX() {
      SERIALM.flush();  // Make sure we output everything first before changing state.
      s_pkuartM->CTRL &= ~LPUART_CTRL_TXDIR;  // Set in to RX Mode...
      digitalWrite(DEBUG_PIN, LOW);
    }
    
    uint32_t loop_count = 0;
    
    void loop() {
      // Lets see if we have anything coming in on the Echo PORT
      while (SERIALE.available()) {
        uint8_t ch = SERIALE.read();
        receive_buffer[receive_index++] = ch;
        if (ch == '\n') {
          receive_buffer[receive_index] = 0;  // Null terminate.
          // BUGBUG:: Put in line instead of calls
          s_pkuartE->CTRL |= LPUART_CTRL_TXDIR;  // Set in to TX Mode...
          SERIALE.write((char*)receive_buffer);
          SERIALE.flush();
          s_pkuartE->CTRL &= ~LPUART_CTRL_TXDIR;  // Set in to RX Mode...
          receive_index = 0;
        }
        em_last_recv = 0;
      }
    
      // Lets see if we have anything coming in on the main port
      while (SERIALM.available()) {
        uint8_t ch = SERIALM.read();
        Serial.write(ch);
        em_last_recv = 0;
      }
    
      if (em_last_recv > LOOP_DELAY_MS) {
        digitalToggleFast(13);
        // output new packet
        setPortTX(); // put port in TX mode
        SERIALM.printf("Loop: %d\n", loop_count++);
        setPortRX();  // put back into RX mode.
        em_last_recv = 0;
      }
    }
    And I confirmed by Logic Analyzer that the Baud rate is more or less correct.
    Click image for larger version. 

Name:	screenshot.jpg 
Views:	17 
Size:	26.7 KB 
ID:	21475

    Not much else I can do at this point.

  17. #17
    Junior Member
    Join Date
    Aug 2020
    Posts
    14
    Hi, thank you so much for that code! I think that's the right way to go for me. Thanks again! Best regards

  18. #18
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,893
    @Paul and all,

    As Half duplex keeps coming up in different topics, I thought about migrating the half duplex support I did in another Pull request that had additional stuff, much of which was already merged in...
    So I have created a new branch: https://github.com/KurtE/cores/tree/serial_half_duplex

    Which I think I migrated the half duplex support in from that other PR, which I will close out.

    The idea is to add a new format to the format defines for SerialX.begin

    Something like: Serial1.begin(1000000, SERIAL_HALF_DUPLEX);
    And it takes care of changing the the right register to allow half duplex, plus sets up that when you do a TX it switches to TX mode and when the ISR comes in saying we have no more to transmit it switches back to RX mode.

    For T3.x it is pretty clean as I do the setup in begin and sets up the same variables used by transmitterEnable to set or clear the GPIO pin, but in this case the register and mask are setup for the bitband address of the correct bit of the register that controls the direction... So no changes to the TX or ISR needed.

    For T4.x not as easy, as we don't have bitband. Instead the transmitterEnable code uses the two different registers that Set or Clear bits in the Port register associated with the pin. Unfortunately we don't have set and clear on the Uarts CTRL register so have to set or clear that bit manually...

    Note: without these types of changes, what I have done in the past in some libraries like my Dynamixel Library (bioloidSerial) is to something like:
    Code:
    #if defined(KINETISK) || defined (KINETISL)
    static KINETISK_UART_t *s_pkuart = nullptr;
    void dxlInit(long baud, HardwareSerial* pserial, int direction_pin, int tx_pin, int rx_pin ) {
        // Need to enable the PU resistor on the TX pin
        s_paxStream = pserial;
        s_direction_pin = direction_pin;    // save away.
        if (pserial == &Serial1) s_pkuart = &KINETISK_UART0;
        else if (pserial == &Serial2) s_pkuart = &KINETISK_UART1;
        else if (pserial == &Serial3) s_pkuart = &KINETISK_UART2;
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__) 
        else if (pserial == &Serial4) s_pkuart = &KINETISK_UART3;
        else if (pserial == &Serial5) s_pkuart = &KINETISK_UART4;
    #endif
    ...
    Which again can be made to work. But it also has an unintended consequence. That is the current versions of Teensyduino are developed, such that if you don't use a SerialX object, then that object is not create, nor any of the other data structures associated with it, like RX and TX Buffers.

    But this code references all of them, even though only one of them will actually be used... But all are now included in the Sketch...

    The #else for the T4.xs is similar but different actual registers and structure...

    For the fun of it, I started playing with a test sketch to see if I can avoid bringing in all of the Serial objects, starting off with T4.x... All of the data needed is actually stored to do this is contained within the SerialX object... And it is more complex than T3.x as we may need to also setup the Hardware Input Select register, depending on the pin...

    I have done a similar hack before for SPI, but that was easier as it is a base class currently with no Vtable so I know the first entry is the pointer to the LPSPI object and the next is a pointer to our Hardware Structure.
    But Serial is a sub-class of Stream which is a sub-class of Print... So playing around to figure out where it is... Have a sketch that properly can now print out some of the information...

    Code:
    extern void printSerData(HardwareSerial *pserial);
    
    uint8_t index_imxrt_lpuart = -1;
    void setup() {
      while (!Serial) ;
      Serial.begin(115200);
      Serial1.begin(115200);
      delay(50);
      uint32_t *pserial = (uint32_t*)&Serial1;
      Serial.printf("PS:%x PU:%x PH:xx\n", &Serial1,
                    (uint32_t*)&IMXRT_LPUART6/*, &UART6_Hardware*/);
      for (uint8_t i = 0; i < 10; i++) {
        Serial.printf("%d:%x\n", i, pserial[i]);
        if ((pserial[i] >= (uint32_t)&IMXRT_LPUART1) && (pserial[i] <= (uint32_t)&IMXRT_LPUART8)) {
          index_imxrt_lpuart = i;
          Serial.println("    imxrt_lpuart index");
        }
      }
      printSerData(& Serial1);
      printSerData(& Serial2);
      printSerData(& Serial3);
      printSerData(& Serial4);
      printSerData(& Serial8);
    
    }
    
    void printSerData(HardwareSerial *pserial) {
      pserial->begin(115200); // make sure we have access.
      uint32_t *p32 = (uint32_t *)pserial;
      IMXRT_LPUART_t *port = (IMXRT_LPUART_t*)p32[index_imxrt_lpuart];
      HardwareSerial::hardware_t * phard = (HardwareSerial::hardware_t *)p32[index_imxrt_lpuart + 1];
      Serial.println("---------------------------------------------------------------");
      Serial.printf("%x %x %x\n", (uint32_t*)pserial, (uint32_t)port, (uint32_t)phard);
      Serial.flush();
      Serial.printf("    B:%x ST:%x CT: %x\n", port->BAUD, port->STAT, port->CTRL);
      Serial.flush( );
      Serial.printf("    I:%d P:%d %x %x\n", phard->serial_index, phard->tx_pins[0].pin,
                   (uint32_t)phard->tx_pins[0].select_input_register, phard->tx_pins[0].select_val);
      Serial.flush();
    
    }
    
    void loop() {
    }
    Output from sketch:
    Code:
    PS:200007fc PU:40198000 PH:xx
    0:20000124
    1:0
    2:3e8
    3:0
    4:40198000
        imxrt_lpuart index
    5:20000140
    6:0
    7:200017b8
    8:200017f8
    9:0
    ---------------------------------------------------------------
    200007fc 40198000 20000140
        B:19000008 ST:c00000 CT: 3c0000
        I:0 P:1 401f8554 0
    ---------------------------------------------------------------
    200008c0 40190000 2000085c
        B:19000008 ST:800000 CT: 3c0000
        I:1 P:8 401f8544 2
    ---------------------------------------------------------------
    20000920 40188000 20000980
        B:19000008 ST:800000 CT: 3c0000
        I:2 P:14 401f8530 1
    ---------------------------------------------------------------
    20000a48 4018c000 200009e4
        B:19000008 ST:800000 CT: 3c0000
        I:3 P:17 401f853c 0
    ---------------------------------------------------------------
    20000b0c 40194000 20000aa8
        B:19000008 ST:c00000 CT: 3c0000
        I:7 P:35 401f854c 1
    One might call this a little bit of a hack :0

    As for T3.x/LC - not so simple as our Serial objects are not controlled by structures like this. They all have their own code with hard coded registers... So not sure how I would hack this one and remove the look at the Serial pointer and have my own table to drive it....

    But now to testing some of the above new branch, and then maybe do a PR...

  19. #19
    Junior Member
    Join Date
    Oct 2020
    Posts
    1
    @KurtE
    I've tested your above half duplex sketch on a Teensy 4.1 and the loopback between TX3 and TX4 seems to work well. I'd like to use this code to control a Dynamixel AX12 but I'm not sure how best to do the level shifting (3.3V to 5v and back). I'm cautious about driving a 5V TTL with a non 5V tolerant 3.3V UART. Do you have any recommendations on the simplest hardware and circuit to achieve this? I've got a 2 channel Sparkfun level shifter but not sure if I can configure it for half duplex operation.
    Also do you think the new format eg Serial1.begin(1000000, SERIAL_HALF_DUPLEX); will be available soon?

  20. #20
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,893
    as for the new HALF_DUPLEX, hopefully Paul will pull it in for the next beta...

    Best way to setup to do half duplex to control AX12 servos. I have been doing it several different ways... Which way is best? Not sure.

    Example one of my later boards I used a: TC7WT141FU chip

    Click image for larger version. 

Name:	screenshot.jpg 
Views:	7 
Size:	60.7 KB 
ID:	22183

    I also played around with a larger chip:
    Click image for larger version. 

Name:	screenshot3.jpg 
Views:	4 
Size:	83.5 KB 
ID:	22184

    Also have done with a couple of transistors which is closer to the Robotis schematic, like:
    Click image for larger version. 

Name:	screenshot2.jpg 
Views:	6 
Size:	298.9 KB 
ID:	22185

    But for quick and dirty, you can also just try simple plug in... Like: https://www.sparkfun.com/products/12009
    Simply connect the low level side to teensy TX pin and the High level to go out to DXLs...

Posting Permissions

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