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:

Code: Untitled.c Select all


#include <stdio.h>
#include <string.h>
#include "driver/uart.h"
#include "freertos/FreeRTOS.h"
#include "esp_err.h"
#include "include/soc/uart_reg.h"
#include "include/soc/rtc_cntl_reg.h"
#include "include/soc/dport_reg.h"
#include "include/soc/apb_ctrl_reg.h"

#define UART0 (0)
#define UART1 (1)
#define UART2 (2)

#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"
#define BYTE_TO_BINARY(byte) \
((byte) & 0x80000000 ? '1' : '0'), \
((byte) & 0x40000000 ? '1' : '0'), \
((byte) & 0x20000000 ? '1' : '0'), \
((byte) & 0x10000000 ? '1' : '0'), \
((byte) & 0x08000000 ? '1' : '0'), \
((byte) & 0x04000000 ? '1' : '0'), \
((byte) & 0x02000000 ? '1' : '0'), \
((byte) & 0x01000000 ? '1' : '0'), \
((byte) & 0x00800000 ? '1' : '0'), \
((byte) & 0x00400000 ? '1' : '0'), \
((byte) & 0x00200000 ? '1' : '0'), \
((byte) & 0x00100000 ? '1' : '0'), \
((byte) & 0x00080000 ? '1' : '0'), \
((byte) & 0x00040000 ? '1' : '0'), \
((byte) & 0x00020000 ? '1' : '0'), \
((byte) & 0x00010000 ? '1' : '0'), \
((byte) & 0x00008000 ? '1' : '0'), \
((byte) & 0x00004000 ? '1' : '0'), \
((byte) & 0x00002000 ? '1' : '0'), \
((byte) & 0x00001000 ? '1' : '0'), \
((byte) & 0x00000800 ? '1' : '0'), \
((byte) & 0x00000400 ? '1' : '0'), \
((byte) & 0x00000200 ? '1' : '0'), \
((byte) & 0x00000100 ? '1' : '0'), \
((byte) & 0x00000080 ? '1' : '0'), \
((byte) & 0x00000040 ? '1' : '0'), \
((byte) & 0x00000020 ? '1' : '0'), \
((byte) & 0x00000010 ? '1' : '0'), \
((byte) & 0x00000008 ? '1' : '0'), \
((byte) & 0x00000004 ? '1' : '0'), \
((byte) & 0x00000002 ? '1' : '0'), \
((byte) & 0x00000001 ? '1' : '0')

void app_main(void)
{
char *test = "HEY BIG BOI\n";

// Select CPU clock source as PLL_CLK by configuring the RTC_CNTL_SOC_CLK_SEL bit to 1
REG_SET_BIT(RTC_CNTL_CLK_CONF_REG, (BIT(27))); // Done
printf("CPU source PLL " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(READ_PERI_REG(RTC_CNTL_CLK_CONF_REG)));

// Set CPU clock to 0 to get 80 MHz PLL_CLK
DPORT_REG_CLR_BIT(DPORT_CPU_PER_CONF_REG, (BIT(0))); // Done correctly
printf("CLK reference " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(DPORT_REG_READ(DPORT_CPU_PER_CONF_REG)));
// printf("CPU CLOCK SHOULD BE ZERO: %lx\n", DPORT_REG_READ(DPORT_CPU_PER_CONF_REG));

// Enable UART1 clock
DPORT_REG_SET_BIT(DPORT_PERIP_CLK_EN_REG, DPORT_UART1_CLK_EN | DPORT_UART_MEM_CLK_EN); // Done correctly
printf("UART1 Clock " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(DPORT_REG_READ(DPORT_PERIP_CLK_EN_REG)));

// Select APB clock
REG_SET_BITS(UART_CONF0_REG(UART1), (BIT(27)), 0x01000000); // ????
printf("APB_CLK_SOURCE " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(READ_PERI_REG(UART_CONF0_REG(UART1))));

// Drive UART module by generating clock signals
WRITE_PERI_REG(UART_CLKDIV_REG(UART1), 0x0002B6); // Integer part
// WRITE_PERI_REG(UART_CLKDIV_REG(UART_CLKDIV_FRAG), 0x); // Decimal part
printf("Integral clock " BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(READ_PERI_REG(UART_CLKDIV_REG(UART1))));

// Set length of data (8 bits)
// WRITE_PERI_REG(UART_CONF0_REG(UART_BIT_NUM), 0x00000003);
printf("Length of data set is: %lx\n", READ_PERI_REG(UART_CONF0_REG(3 << 1)));

// Set length of stop bit (1 bit)
WRITE_PERI_REG(UART_CONF0_REG(UART_STOP_BIT_NUM), 0x01);

// Allocate TX memory
WRITE_PERI_REG(UART_MEM_CONF_REG(UART_TX_SIZE), 0x80);

// Enable transmitter flow control function
// WRITE_PERI_REG(UART_CONF0_REG(UART_SW_RTS), 0x01);
// WRITE_PERI_REG(UART_CONF0_REG(UART_TX_FLOW_EN), 0x01);
// WRITE_PERI_REG(UART_CONF1_REG(UART_RX_FLOW_EN), 0x00);

// Enable interrupt pin for UART_TX_DONE_INT
// WRITE_PERI_REG(UART_INT_ENA_REG(UART_TX_DONE_INT_ENA), 0x01);

// Write data into Tx buffer | UART0 TX FIFO Buffer - 0x3FF4005C
for (int i=0; i<strlen(test); i++) {
WRITE_PERI_REG(UART_MEM_TX_STATUS_REG(UART_MEM_TX_WR_ADDR), test[i]);
}

// Read number of bytes stored in Tx buffer
printf("%ld\n", READ_PERI_REG(UART_STATUS_REG(UART_TXFIFO_CNT)));


// UART controller serializes data


// UART sends data




// Start is 0 or LOW while Stop is 1 or HIGH
// UART_TX_DONE_INT - Interrupt raised when transmitter has sent out all FIFO data
}

/* void UART_SEND() {

} */

/* void UART_RECEIVE() {

} */

Re: Configuring UART registers and peripheral clock

Posted: Wed Apr 05, 2023 12:12 am
by 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
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 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
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 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
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 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
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.