UARTE in semi-duplex mode

Hi all,

I would like to have a semi-duplex communication link over one pin (+GND). The nRF will act as a pure slave device which responds to, and acknowledges, commands from a "master".Thus:

1. I set UARTE in RX mode.  PSEL.RXD is set to the (shared) pin.

2. A command is successfully received from the master.

3.  PSEL.RXD = 0xffffffff, PSEL.TXD s set to the (shared) pin.

4. A reply is successfully sent to the master.

5. Back to 1

The problem is that this only works two times. Then I get an "overrun" error. I've tried "EVERYTHING" to prevent this from happening; delays in various places, different sequences of pin role swithing (when the UARTE module is disabled) and trying to run  STOPRX (according to the 35.4 UARTE-Reception section of the device datasheet) in order to properly empty the RX buffer.

Any ideas ? Please help !

Cheers

Eric

  • A disconnected input pin defaults to '0' if I recall, which means you can't disconnect without generating some sort of framing error. Two options to try:

    • try setting the Tx/Rx pin to always Input Connected with pull-up enabled, even when transmitting
    • try setting the Rx to another (spare) pin which has pull-up enabled which is always an input

    I think the first might work as I seem to remember that the input pull-up applies to the input even when in output mode, assuming the datasheet drawing is correct; but no, I think the drawing is incorrect, the pull-up applies to the physical pin not the input buffer as shown, so the first option won't work.

  • Hi 

    In the past I have had success implementing 'one wire UART' implementations like this by connecting the TX and RX pins together externally, then you don't have to do any complicated configuration of the driver to go from TX to RX and vice versa. 

    The only drawback of this method is that any data you send will also be received (you get external loopback essentially), so you will need to take this into account when sending data. 

    Also, for more complicated UART implementations I would recommend using the nrf_libuarte driver, as it is a more robust and feature rich UART implementation that is designed to make full use of the UARTE peripheral and its EasyDMA features. 

    Best regards
    Torbjørn

  • Hi,

    Thanks for reaching out. Yes, using separate pins for RX & TX and connecting them externally is certainly an option. However, I'd rather avoid that, if possible, because I/O is already scarce for my application. 

    Enabling pull-up, in RX and/or TX mode, does affect whether you get a framing error or not. But it does not solve the overrun proplem.

    \Eric

  • Hi Torbjörn,

    Thanks for your input. As I explained to "hmolesworth" I would very much like to be able to solve this with one I/O pin only. For the same reason I have not considered the nrf_libuarte driver as this most certainly does not support semi-duplex operation. Besides, I would not call my use case a "complicated" scenario which requires an advanced driver.

    What bugs me is that you'd think that by switching the UARTE module off (as required when switching role on the I/O pin) you would automatically put it in its reset condition. Guess not ... 

    An why does the "RX flush" procedure need to be so complicated ???

    \Eric

  • Hey Eric, just to clarify I didn't mean use a separate pin for the Rx data, just a separate pin to "park" Rx in a input-high-via-pull-up state while transmitting on Tx. Any input pin will do even if used for something else as long as you can avoid things happening during that Rx interval. For example you might try the 32kHz crystal output drive pin P0.1; I haven't tried this yet but considered it for a similar issue a while ago. It all hinges whether the pull-up is applied to the pin-side or input buffer-side of the input FET switch.

    Ok I just looked at the crystal pins as that might be a special case as it doesn't seem to work, but other pins look ok, eg SDA just avoid I2C transfers during Tx activity while Rx is parked

    It works!!

     // Park Rx pin somewhere safe with a known '1' input level
     NRF_UARTE0->PSEL.RXD = PIN_I2C_SCL;
    
    // Transmit some data, be sure last tx byte has cleared the pin
    
     if (mTxDone)
     {
       // Revert Parked Rx pin here or in interrupt (not both)
       NRF_UARTE0->PSEL.RXD = PIN_UART_RX;
       mTxDone = false;
     }

    To be sure the last byte has cleared the Tx pin before turning on Rx again:

    case APP_UART_TX_EMPTY: // Event UART has completed transmission of all available data in the TX FIFO
      // Revert Parked Rx pin
      NRF_UARTE0->PSEL.RXD = PIN_UART_RX;
    or set a flag:
      mTxDone = true;
      break;

Related