Page 1 of 1

Configuring UART registers and peripheral clock

Posted: Tue Apr 04, 2023 7:29 pm
by coderooni
Hi,

I've been trying for days now to configure UART by playing around with the bare registers themselves. The workflow that I believe should work is (currently, I'm only figuring out the transmission part):

1. Selecting clock source as PLL_CLK (80 MHz)
2. Enabling the UART clock
3. Selecting the APB_CLK and determining the integral part as well as the fractional part to set the baud rate
4... Configuring the stop bits, data length, allocating TX buffer memory, and writing data into the TX FIFO buffer

However, when I try to set these individual bits and/or registers, they don't necessarily work with every register. Moreover, when I try to set the clock as APB_CLK for UART0 it works but when I try it for UART1, it doesn't set that bit. The same goes for selecting the integral and fractional values; it sets the wanted values for UART0 but not for UART1.

Is there something I'm missing in the workflow, or doing something wrong to begin with? I do have further questions regarding configuring UART registers but would like to get this sorted out first.

Here's the code:
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include "driver/uart.h"
  4. #include "freertos/FreeRTOS.h"
  5. #include "esp_err.h"
  6. #include "include/soc/uart_reg.h"
  7. #include "include/soc/rtc_cntl_reg.h"
  8. #include "include/soc/dport_reg.h"
  9. #include "include/soc/apb_ctrl_reg.h"
  10.  
  11. #define UART0 (0)
  12. #define UART1 (1)
  13. #define UART2 (2)
  14.  
  15. #define BYTE_TO_BINARY_PATTERN "%c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c"
  16. #define BYTE_TO_BINARY(byte)  \
  17.   ((byte) & 0x80000000 ? '1' : '0'), \
  18.   ((byte) & 0x40000000 ? '1' : '0'), \
  19.   ((byte) & 0x20000000 ? '1' : '0'), \
  20.   ((byte) & 0x10000000 ? '1' : '0'), \
  21.   ((byte) & 0x08000000 ? '1' : '0'), \
  22.   ((byte) & 0x04000000 ? '1' : '0'), \
  23.   ((byte) & 0x02000000 ? '1' : '0'), \
  24.   ((byte) & 0x01000000 ? '1' : '0'), \
  25.   ((byte) & 0x00800000 ? '1' : '0'), \
  26.   ((byte) & 0x00400000 ? '1' : '0'), \
  27.   ((byte) & 0x00200000 ? '1' : '0'), \
  28.   ((byte) & 0x00100000 ? '1' : '0'), \
  29.   ((byte) & 0x00080000 ? '1' : '0'), \
  30.   ((byte) & 0x00040000 ? '1' : '0'), \
  31.   ((byte) & 0x00020000 ? '1' : '0'), \
  32.   ((byte) & 0x00010000 ? '1' : '0'), \
  33.   ((byte) & 0x00008000 ? '1' : '0'), \
  34.   ((byte) & 0x00004000 ? '1' : '0'), \
  35.   ((byte) & 0x00002000 ? '1' : '0'), \
  36.   ((byte) & 0x00001000 ? '1' : '0'), \
  37.   ((byte) & 0x00000800 ? '1' : '0'), \
  38.   ((byte) & 0x00000400 ? '1' : '0'), \
  39.   ((byte) & 0x00000200 ? '1' : '0'), \
  40.   ((byte) & 0x00000100 ? '1' : '0'), \
  41.   ((byte) & 0x00000080 ? '1' : '0'), \
  42.   ((byte) & 0x00000040 ? '1' : '0'), \
  43.   ((byte) & 0x00000020 ? '1' : '0'), \
  44.   ((byte) & 0x00000010 ? '1' : '0'), \
  45.   ((byte) & 0x00000008 ? '1' : '0'), \
  46.   ((byte) & 0x00000004 ? '1' : '0'), \
  47.   ((byte) & 0x00000002 ? '1' : '0'), \
  48.   ((byte) & 0x00000001 ? '1' : '0')
  49.  
  50. void app_main(void)
  51. {
  52.     char *test = "HEY BIG BOI\n";
  53.    
  54.     // Select CPU clock source as PLL_CLK by configuring the RTC_CNTL_SOC_CLK_SEL bit to 1
  55.     REG_SET_BIT(RTC_CNTL_CLK_CONF_REG, (BIT(27))); // Done
  56.     printf("CPU source PLL " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(READ_PERI_REG(RTC_CNTL_CLK_CONF_REG)));
  57.  
  58.     // Set CPU clock to 0 to get 80 MHz PLL_CLK
  59.     DPORT_REG_CLR_BIT(DPORT_CPU_PER_CONF_REG, (BIT(0))); // Done correctly
  60.     printf("CLK reference " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(DPORT_REG_READ(DPORT_CPU_PER_CONF_REG)));
  61.     // printf("CPU CLOCK SHOULD BE ZERO: %lx\n", DPORT_REG_READ(DPORT_CPU_PER_CONF_REG));
  62.    
  63.     // Enable UART1 clock
  64.     DPORT_REG_SET_BIT(DPORT_PERIP_CLK_EN_REG, DPORT_UART1_CLK_EN | DPORT_UART_MEM_CLK_EN); // Done correctly
  65.     printf("UART1 Clock " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(DPORT_REG_READ(DPORT_PERIP_CLK_EN_REG)));
  66.  
  67.     // Select APB clock
  68.     REG_SET_BITS(UART_CONF0_REG(UART1), (BIT(27)), 0x01000000); // ????
  69.     printf("APB_CLK_SOURCE " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(READ_PERI_REG(UART_CONF0_REG(UART1))));
  70.  
  71.     // Drive UART module by generating clock signals
  72.     WRITE_PERI_REG(UART_CLKDIV_REG(UART1), 0x0002B6); // Integer part
  73.     // WRITE_PERI_REG(UART_CLKDIV_REG(UART_CLKDIV_FRAG), 0x); // Decimal part
  74.     printf("Integral clock " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(READ_PERI_REG(UART_CLKDIV_REG(UART1))));
  75.  
  76.     // Set length of data (8 bits)
  77.     // WRITE_PERI_REG(UART_CONF0_REG(UART_BIT_NUM), 0x00000003);
  78.     printf("Length of data set is: %lx\n", READ_PERI_REG(UART_CONF0_REG(3 << 1)));
  79.  
  80.     // Set length of stop bit (1 bit)
  81.     WRITE_PERI_REG(UART_CONF0_REG(UART_STOP_BIT_NUM), 0x01);
  82.  
  83.     // Allocate TX memory
  84.     WRITE_PERI_REG(UART_MEM_CONF_REG(UART_TX_SIZE), 0x80);
  85.  
  86.     // Enable transmitter flow control function
  87.     // WRITE_PERI_REG(UART_CONF0_REG(UART_SW_RTS), 0x01);
  88.     // WRITE_PERI_REG(UART_CONF0_REG(UART_TX_FLOW_EN), 0x01);
  89.     // WRITE_PERI_REG(UART_CONF1_REG(UART_RX_FLOW_EN), 0x00);
  90.  
  91.     // Enable interrupt pin for UART_TX_DONE_INT
  92.     // WRITE_PERI_REG(UART_INT_ENA_REG(UART_TX_DONE_INT_ENA), 0x01);
  93.  
  94.     // Write data into Tx buffer | UART0 TX FIFO Buffer - 0x3FF4005C
  95.     for (int i=0; i<strlen(test); i++) {
  96.         WRITE_PERI_REG(UART_MEM_TX_STATUS_REG(UART_MEM_TX_WR_ADDR), test[i]);
  97.     }
  98.  
  99.     // Read number of bytes stored in Tx buffer
  100.     printf("%ld\n", READ_PERI_REG(UART_STATUS_REG(UART_TXFIFO_CNT)));
  101.    
  102.  
  103.     // UART controller serializes data
  104.  
  105.  
  106.     // UART sends data
  107.  
  108.  
  109.    
  110.  
  111.     // Start is 0 or LOW while Stop is 1 or HIGH
  112.     // UART_TX_DONE_INT - Interrupt raised when transmitter has sent out all FIFO data
  113. }
  114.  
  115. /* void UART_SEND() {
  116.  
  117. } */
  118.  
  119. /* void UART_RECEIVE() {
  120.    
  121. } */

Re: Configuring UART registers and peripheral clock

Posted: Wed Apr 05, 2023 12:12 am
by ESP_Sprite
Make sure you de-clockgate and de-reset UART1, otherwise it'll indeed not do much. Basically, do this for UART1.

Re: Configuring UART registers and peripheral clock

Posted: Wed Apr 05, 2023 12:43 am
by coderooni
ESP_Sprite wrote:
Wed Apr 05, 2023 12:12 am
Make sure you de-clockgate and de-reset UART1, otherwise it'll indeed not do much. Basically, do this for UART1.
Thank you for the link. So, reading in the register values after resetting them will result in the correct values? Does the TRM indicate resetting certain register values because as far as I've read, I didn't come across anything like that.
Also, why doesn't UART0 require the same de-clockgating and de-resetting?

Re: Configuring UART registers and peripheral clock

Posted: Thu Apr 06, 2023 12:17 am
by coderooni
Make sure you de-clockgate and de-reset UART1, otherwise it'll indeed not do much. Basically, do this for UART1.
I responded to this but it hasn't gotten approved yet? What I replied with was thank you for providing me with the link. Why do we need to de-clockgate and de-reset the peripheral clock and why does it work without it for UART0?
Also, this was not mentioned in the TRM about de-resetting or de-clockgating, may I know where to read more on this?

Re: Configuring UART registers and peripheral clock

Posted: Fri Apr 07, 2023 12:36 am
by ESP_Sprite
It works for UART0 as that is the default UART for debug messages; it already is de-clockgated by default. The TRM doesn't really seem to refer to peripheral clock-gating and reset functionality (it only documents the registers used for it), that seems like an omission and I'll file an issue to correct that.

Re: Configuring UART registers and peripheral clock

Posted: Sat Apr 08, 2023 10:39 am
by coderooni
ESP_Sprite wrote: It works for UART0 as that is the default UART for debug messages; it already is de-clockgated by default. The TRM doesn't really seem to refer to peripheral clock-gating and reset functionality (it only documents the registers used for it), that seems like an omission and I'll file an issue to correct that.
Great, thank you. In the meantime, any resources you think would be good to read about resetting and clockgating on ESP32?

Re: Configuring UART registers and peripheral clock

Posted: Sun Apr 09, 2023 2:53 am
by ESP_Sprite
I think the link I posted (to the low-level driver code) covers most of it. It's not really a complex concept.

Re: Configuring UART registers and peripheral clock

Posted: Mon Apr 17, 2023 9:29 pm
by coderooni
ESP_Sprite wrote:
Sun Apr 09, 2023 2:53 am
I think the link I posted (to the low-level driver code) covers most of it. It's not really a complex concept.
Thanks Sprite, it indeed wasn't a deep topic. I didn't get much time after that to finish what I started. I was diving into the UART section of the TRM and everything is clear except the part where it doesn't specify the sending of the UART dataframe.

Does the Tx FIFO buffer only send out its content when the Rx buffer requests to read? In that case the rtsn_out signal needs to be driven low to send the data?

Am I on the right track or completely off-track? I'm asking because the only thing I haven't figured out is how do I actually configure the registers to send the data in the Tx data buffer and if it's even possible to send to "nothing" without an Rx buffer configured at all.

Re: Configuring UART registers and peripheral clock

Posted: Tue Apr 18, 2023 1:12 am
by ESP_Sprite
From memory, the Tx fifo starts sending as soon as you write a byte to it. The idea of the FIFO is that you can write a fair amount of data into it in one go, faster than the data is sent; if you're doing the sending interrupt-based, it means you don't have to fire an interrupt every byte.

Re: Configuring UART registers and peripheral clock

Posted: Tue Apr 18, 2023 3:23 pm
by coderooni
ESP_Sprite wrote:
Tue Apr 18, 2023 1:12 am
From memory, the Tx fifo starts sending as soon as you write a byte to it. The idea of the FIFO is that you can write a fair amount of data into it in one go, faster than the data is sent; if you're doing the sending interrupt-based, it means you don't have to fire an interrupt every byte.
I see. Essentially, I'll need to set up interrupts for every state relating to the Tx FIFO.