UART serial flush (!)

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
cemox
Posts: 15
Joined: Mon Oct 08, 2018 5:31 pm
Location: Turkey

UART serial flush (!)

Post by cemox » Sun Dec 30, 2018 3:24 pm

Hi, I am working on a project that communicates with a device via UART in half-duplex mode. The hardware for half-duplex communication works fine. My ESP module pulls control line that is connected to the 74LS241 IC high before sending the command string and pulls it back to low in order to receive the response from the device. So far so good. But, when I attempt to read from the device consecutively it can not receive the incoming data. I connected a logic analyzer to the circuit and saw that my ESP module pulls the control line before UART has completed transmitting the command string, so the last few bytes of the command string cannot reach the receiving device. Pseudo-code is like this:

1) prepare the command string
2) pull the control line high (for Tx)
3) uart.write(command string)
4) pull the control line low (for Rx)
5) read the incoming data

Step 3 should be completed before step 4.

In summary, I need something like Arduino's Serial.flush() which ensures that all the data are transmitted before executing the next line of code. How can I make sure that uart.write() function is completed before continuing program execution?

Note: I do not want to solve the problem by adding delay() !

Thanks,

User avatar
dhylands
Posts: 3228
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: UART serial flush (!)

Post by dhylands » Sun Dec 30, 2018 6:18 pm

On the pyboard, I directly read the UART_SR and wait for the tx_complete bit to be set.
I'm not familiar enough with the ESP hardware to suggest a solution, but I would look for something along those lines. Most UART hardware has some type of status bit to indicate that the bits have actually left the shift register.

User avatar
cemox
Posts: 15
Joined: Mon Oct 08, 2018 5:31 pm
Location: Turkey

Re: UART serial flush (!)

Post by cemox » Tue Jan 01, 2019 12:21 pm

Thanks for the hint, it is a good approach. I think you accessed the register by using inline assembly code. I'll try to figure out if I can do it for ESP8266.

User avatar
cemox
Posts: 15
Joined: Mon Oct 08, 2018 5:31 pm
Location: Turkey

Re: UART serial flush (!)

Post by cemox » Tue Jan 01, 2019 2:49 pm

This is an important problem. I wrote a test code like this :

Code: Select all

mypin.on()
uart1.write("this is a very long sententence")
mypin.off()
In the image, the red signal show UART1 TX pin and the yellow signal shows the state of the output pin (mypin). Since the UART has its own hardware, the output pin's state goes low long before the transmission has been completed. The best way to do it as advised in the forum, must be checking the relevant UART status bits, but I don't know how to do it.
IMG-94172.jpg
IMG-94172.jpg (213.7 KiB) Viewed 780 times

User avatar
dhylands
Posts: 3228
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: UART serial flush (!)

Post by dhylands » Wed Jan 02, 2019 1:52 am

This is my code for the STM32: https://github.com/dhylands/bioloid3/bl ... rt_port.py

The default UART code has both an Rx and Tx buffers, so if the string you write is less than the buffer length, then it will return immediately, which is what I think you're showing in the logic analyzer capture above. Do you have an ESP8266 or an ESP32?

The docs say you can pass txbuf=N to uart.init to set the tx buffer size.

I took a look at the ESP32 technical reference manual: https://www.espressif.com/sites/default ... ual_en.pdf and page 356 shows that the UART_STATUS_REG contains a field called UART_ST_UTX_OUT contains the state of the UART transmitter. Presumably, you'd want to wait until the state gets to TX_IDLE and then you'd know that the UART has finished sending data.

User avatar
cemox
Posts: 15
Joined: Mon Oct 08, 2018 5:31 pm
Location: Turkey

Re: UART serial flush (!)

Post by cemox » Wed Jan 02, 2019 7:07 am

Thank you Dave. I have ESP8266 board.

I do not think buffer size is a key factor here, because UART has its own hardware, whatever is written to tx buffer is sent asynchronously. The actual data I am sending to Dynamixel motors is only 26 bytes long, but the problem is still the same. Naturally, ESP8266 is much more faster than the UART chip sending data at 115200 baud. That's why a function like uart.flush() is a must. Interestingly, in the GitHub repository of MicroPython under esp8266 port, there is a uart.c file and in that file there is a internal function uart.flush() in that file:

Code: Select all

/******************************************************************************
 * FunctionName : uart1_tx_one_char
 * Description  : Internal used function
 *                Use uart1 interface to transfer one char
 * Parameters   : uint8 TxChar - character to tx
 * Returns      : OK
*******************************************************************************/
void uart_tx_one_char(uint8 uart, uint8 TxChar) {
    while (true) {
        uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT<<UART_TXFIFO_CNT_S);
        if ((fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) {
            break;
        }
    }
    WRITE_PERI_REG(UART_FIFO(uart), TxChar);
}

void uart_flush(uint8 uart) {
    while (true) {
        uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT<<UART_TXFIFO_CNT_S);
        if ((fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) == 0) {
            break;
        }
    }
}

This uart_flush() function is doing exactly what I need, starts a loop until tx_fifo_count is zero, but I have no idea where it is used. If I could convert this function into a python inline assembly function in my code, it would be great; but there is not much examples of xtensa assembly in the internet.

Another solution may be to port this this function to machine.uart module and rebuild the micrpython, If I can do it, it will be a good contribution to the project I think.

User avatar
cemox
Posts: 15
Joined: Mon Oct 08, 2018 5:31 pm
Location: Turkey

Re: UART serial flush (!) SOLVED

Post by cemox » Wed Jan 02, 2019 8:56 am

I solved it by making a modification to machine_uart.c file as this : I added internal function call uart_flush(self->uart_id); into uart.write function in machine_uart.c file as shown below:

Code: Select all

STATIC mp_uint_t pyb_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
    pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
    const byte *buf = buf_in;

    /* TODO implement non-blocking
    // wait to be able to write the first character
    if (!uart_tx_wait(self, timeout)) {
        *errcode = EAGAIN;
        return MP_STREAM_ERROR;
    }
    */

    // write the data
    for (size_t i = 0; i < size; ++i) {
        uart_tx_one_char(self->uart_id, *buf++);
    }

    // added by cemox, wait for completion of transmission
    uart_flush(self->uart_id);

    // return number of bytes written
    return size;
}

It works like a charm as shown in the image, hope it helps others.
solved.jpg
solved.jpg (206.69 KiB) Viewed 738 times

User avatar
dhylands
Posts: 3228
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: UART serial flush (!)

Post by dhylands » Wed Jan 02, 2019 4:02 pm

Make sure you zoom in on that last character. Even though the TX FIFO is empty, the last character may be in a shift register getting sent out. So the TX FIFO empty may not be sufficient. It really depends on the hardware.

Post Reply