Reading the UART
Posted: Mon Mar 23, 2020 3:32 pm
I ran into a problem today with the ESP32 that had me pulling out my hair for quite some hours. I ended up devising the following test program in trying to trace it:
The ESP32 is connected to an EFM8 that sends back 2 bytes when the above single byte command (0x8D) is received. As you can see from the code, the ESP32 sends the byte and then waits for the FIFO counter to indicate it has received two bytes back before reading them. For some unknown reason the second byte is lost unless I insert something that reads the FIFO counter in between the two reads. The behavior of the ESP32 makes no sense here. I wonder if I remember reading somewhere that you have to read the FIFO counter each time between reading bytes from the FIFO? My memory is fuzzy, and I could not find mention of it when searching.
The only thing I could find from searching was the following document "Workarounds for Bugs":
https://www.espressif.com/sites/default ... p32_en.pdf
On page 9 it says "Some ESP32 peripherals are mapped to two internal memory buses (AHB & DPORT). When written via DPORT, consecutive writes to the same address may be lost. When writing the same register address (i.e., FIFO-like addresses) in sequential instructions, use the equivalent AHB address not the DPORT address. (For other kinds of register writes, using DPORT registers will give better write performance.)" The table includes the UART FIFOs in the affected items.
Well I am not sequentially writting to the FIFO but rather sequentially reading from it, so it is not the same thing. However, I noticed that my code uses UART_FIFO_AHB_REG to get the address to write to, whereas it uses UART_FIFO_REG to get the address to read from. This seems to match the ESP's own drivers and most of the code samples I looked at. However, I tried changing the read to use UART_FIFO_AHB_REG instead, and it made the bug described stop! This is totally undocumented as far as I can tell, so I don't know that it is an acceptable solution.
So I am wondering the following:
• Do you really have to read the FIFO counter between each sequential FIFO read in order to actually get the next byte, versusgetting the previous byte and losing the actual one?
• Does reading from the UART_FIFO_AHB_REG instead of the UART_FIFO_REG work around the bug in the same way that sequentially writing to it apparently does?
• If either of the above workarounds are viable, which would be better to use?
• Is there some other explanation why the code sample above malfunctions that I am missing?
Code: Select all
void app_main()
{
// Config 2 UARTs
uart_config_t uart_config = {1020833, UART_DATA_8_BITS,
UART_PARITY_DISABLE, UART_STOP_BITS_1, UART_HW_FLOWCTRL_DISABLE};
uart_param_config(UART_NUM_1, &uart_config);
uart_param_config(UART_NUM_2, &uart_config);
uart_set_pin (UART_NUM_1, GPIO_NUM_13, GPIO_NUM_14, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
uart_set_pin (UART_NUM_2, GPIO_NUM_22, GPIO_NUM_23, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
UART1.conf0.err_wr_mask = 1; // require stop bit on byte received
UART2.conf0.err_wr_mask = 1;
while (1)
{
WRITE_PERI_REG (UART_FIFO_AHB_REG (UART_NUM_1), 0x8D);
while (UART1.status.rxfifo_cnt < 2);
int b1 = READ_PERI_REG (UART_FIFO_REG (UART_NUM_1));
//int cnt = UART1.status.rxfifo_cnt;
int b2 = READ_PERI_REG (UART_FIFO_REG (UART_NUM_1));
int cnt = UART1.status.rxfifo_cnt;
// this reads first byte ok but instead of second byte it gets a copy of the first byte (while still clearing it out of the FIFO and decrementing the count)
// if the first line is uncommented and the second commented, then it reads both bytes correctly
printf ("%d - %d, %d\n", cnt, b1, b2);
vTaskDelay (100 / portTICK_RATE_MS);
}
}
The only thing I could find from searching was the following document "Workarounds for Bugs":
https://www.espressif.com/sites/default ... p32_en.pdf
On page 9 it says "Some ESP32 peripherals are mapped to two internal memory buses (AHB & DPORT). When written via DPORT, consecutive writes to the same address may be lost. When writing the same register address (i.e., FIFO-like addresses) in sequential instructions, use the equivalent AHB address not the DPORT address. (For other kinds of register writes, using DPORT registers will give better write performance.)" The table includes the UART FIFOs in the affected items.
Well I am not sequentially writting to the FIFO but rather sequentially reading from it, so it is not the same thing. However, I noticed that my code uses UART_FIFO_AHB_REG to get the address to write to, whereas it uses UART_FIFO_REG to get the address to read from. This seems to match the ESP's own drivers and most of the code samples I looked at. However, I tried changing the read to use UART_FIFO_AHB_REG instead, and it made the bug described stop! This is totally undocumented as far as I can tell, so I don't know that it is an acceptable solution.
So I am wondering the following:
• Do you really have to read the FIFO counter between each sequential FIFO read in order to actually get the next byte, versusgetting the previous byte and losing the actual one?
• Does reading from the UART_FIFO_AHB_REG instead of the UART_FIFO_REG work around the bug in the same way that sequentially writing to it apparently does?
• If either of the above workarounds are viable, which would be better to use?
• Is there some other explanation why the code sample above malfunctions that I am missing?