Skip to content
Snippets Groups Projects
esp32-c3-uart-interface.c 5.74 KiB
Newer Older
#include "esp32-c3-uart-interface.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

#include <mdk.h>

static void clearBits(volatile uint32_t *registerAddress, uint32_t bitPositions, uint32_t numBits) {
    uint32_t mask = (uint32_t)(1 << numBits) - 1;  // Create a mask with the specified number of bits set to 1
    mask <<= bitPositions;               // Shift the mask to the correct bit positions
    *registerAddress &= ~mask;           // Clear the bits in the register using bitwise AND with the inverse of the mask
Jakob's avatar
Jakob committed
}

static void setBit(volatile uint32_t *registerAddress, uint32_t bitPositions) {
    uint32_t mask = (uint32_t)(1 << bitPositions);
    *registerAddress |= ~mask;
static void setSpecificBitsInRegister(volatile uint32_t *reg, uint32_t *bits, size_t numBits) {
  uint32_t rx_fifo = *reg;
  for (size_t i = 0; i < numBits; i++) {
      uint32_t bitPos = bits[i];
      rx_fifo |= (1 << bitPos);
  }
  *reg = rx_fifo;
}

static void init_uart_enable_clk(){
  // enable the clock for UART RAM by setting SYSTEM_UART_MEM_CLK_EN to 1;
  // enable APB_CLK for UARTn by setting SYSTEM_UARTn_CLK_EN to 1;
  uint32_t perip_clk_en0 = *C3_SYSTEM_PERIP_CLK_EN0_REG;
  perip_clk_en0 |= 1<<24;
  perip_clk_en0 |= 1<<5;
  perip_clk_en0 |= 1<<2;
  *C3_SYSTEM_PERIP_CLK_EN0_REG = perip_clk_en0;
}

static void init_uart_toggle_rst(){
  // Detailed steps explained in 26.5.2.1
  // for resetting and initilizing of uart
  // get current regestry values
  uint32_t rx_filt = *C3_UART_RX_FILT_REG(0);
  uint32_t core_value = *C3_UART_CLK_CONF_REG(0);
  // clear UARTn_RST
  rx_filt &= (uint32_t)~(1<<5);
  rx_filt &= (uint32_t)~(1<<2);
  *C3_UART_RX_FILT_REG(0) = rx_filt;
  // set UART_RST_CORE
  core_value |= 1<<23;
  *C3_UART_CLK_CONF_REG(0) = core_value;
  // set UARTn_RST
  rx_filt |= 1<<5;
  rx_filt |= 1<<2;
  *C3_UART_RX_FILT_REG(0) = rx_filt;
  // clear UARTn_RST
  rx_filt &= (uint32_t)~(1<<5);
  rx_filt &= (uint32_t)~(1<<2);
  *C3_UART_RX_FILT_REG(0) = rx_filt;
  // clear UART_RST_CORE
  core_value &= (uint32_t)~(1<<23);
  *C3_UART_CLK_CONF_REG(0) = core_value;
}

static void init_uart_clear_update(){  
  // enable register synchronization by clearing UART_UPDATE_CTRL.
  uint32_t id_value = *C3_UART_ID_REG(0);
  id_value &= (uint32_t)~(1<<30);
  *C3_UART_ID_REG(0) = id_value;
}

static void config_clock_freq(){
  //freq = clock source rate / (UART_SCLK_DIV_NUM+UART_SCLK_DIV_A/UART_SCLK_DIV_B)
  // baud rate = freq / (UART_CLKDIV + UART_CLKDIV_FRAG / 16)

  // select the clock source via UART_SCLK_SEL;
  uint32_t clockSelect = *C3_UART_CLK_CONF_REG(0);
  // making sure we start with 0
  clockSelect &= (uint32_t)~(1<<20);
  clockSelect &= (uint32_t)~(1<<21);
  // 01 is ABP_CLK with 80 MHz
  // 10 is RC_FAST_CLK with default of 17.5 MHz
  // 11 is XTAL_CLK with 40 MHz
  clockSelect |= 1<<20;
  *C3_UART_CLK_CONF_REG(0) = clockSelect;
  // selected ABP_CLK with 80 MHz

  // dividing clock to 6 Mhz (divisor should be 13+1/3)
  // UART_SCLK_DIV_NUM
  clockSelect |= 1<<12;
  clockSelect |= 1<<14;
  clockSelect |= 1<<15;

  // UART_SCLK_DIV_A
  clockSelect |= 1<<6;

  // UART_SCLK_DIV_B
  clockSelect |= 1<<1;
  clockSelect |= 1<<0;

  *C3_UART_CLK_CONF_REG(0) = clockSelect;

  // calculate Baud rate to be 9600 (6Mhz/625)
  uint32_t baudRateDivs = *C3_UART_CLKDIV_REG(0);
  baudRateDivs |= 1<<0;
  baudRateDivs |= 1<<4;
  baudRateDivs |= 1<<5;
  baudRateDivs |= 1<<6;
  baudRateDivs |= 1<<9;
  *C3_UART_CLKDIV_REG(0) = baudRateDivs;
}

static void disable_parity(){
  uint32_t uart_conf = *C3_UART_CONF0_REG(0);
  uart_conf &= (uint32_t)~(1<<0);
  uart_conf &= (uint32_t)~(1<<1);
  *C3_UART_CONF0_REG(0) = uart_conf;
}

static void set_max_data_lenght(){
  uint32_t uart_conf = *C3_UART_CONF0_REG(0);
  uart_conf |= (1<<2);
  uart_conf |= (1<<3);
  *C3_UART_CONF0_REG(0) = uart_conf;
  // wait for UART_REG(0)_UPDATE to become 0, which indicates the completion of the last synchronization;
  uint32_t reg_update = *C3_UART_ID_REG(0);
  while((bool)(reg_update & (uint32_t)(1<<31))){
    __asm__("ADDI x0, x0, 0");
  }

  // configure static registers (if any) following Section 26.5.1.2;

  // set clock freq to 6MHz
  // baud rate to 9600
  config_clock_freq();

  // configure data length via UART_BIT_NUM;
  set_max_data_lenght();

  // configure odd or even parity check via UART_PARITY_EN and UART_PARITY;
  disable_parity();
  
  // synchronize the configured values to the Core Clock domain by writing 1 to UART_REG_UPDATE.
  reg_update |= (uint32_t)(1<<31);
  *C3_UART_ID_REG(0) = reg_update;
void writeSomething(){
  uint32_t bitPositions[] = {0,1,7};
  size_t numBits = sizeof(bitPositions) / sizeof(bitPositions[0]);
  setSpecificBitsInRegister(C3_UART_FIFO_REG(0), bitPositions, numBits);

  //uint32_t rx_fifo = *C3_UART_FIFO_REG(0);
  //rx_fifo |= 1<<7;
  //rx_fifo |= 1<<6;
  //rx_fifo |= 1<<5;
  //*C3_UART_FIFO_REG(0) = rx_fifo;
}

void enable_uart_transmitter(){
  for(int i = 0; i < 100; i++){
    delay_ms(10);
    // configure TX FIFO’s empty threshold via UART_TXFIFO_EMPTY_THRHD;
    clearBits(C3_UART_CONF1_REG(0), 9, 9);
    setBit(C3_UART_CONF1_REG(0),3);

    // disable UART_TXFIFO_EMPTY_INT interrupt by clearing UART_TXFIFO_EMPTY_INT_ENA;
    clearBits(C3_UART_INT_ENA_REG(0), 1, 1);

    // write data to be sent to UART_RXFIFO_RD_BYTE;
    writeSomething();
    
    // clear UART_TXFIFO_EMPTY_INT interrupt by setting UART_TXFIFO_EMPTY_INT_CLR;
    setBit(C3_UART_INT_CLR_REG(0), 1);
    
    // enable UART_TXFIFO_EMPTY_INT interrupt by setting UART_TXFIFO_EMPTY_INT_ENA;
    setBit(C3_UART_INT_ENA_REG(0), 1);
  }
}

void reset_uart(){
  // 26.4.1 Clock and Reset
  init_uart_enable_clk();
  init_uart_toggle_rst();
}

void init_uart(){
  // from technical reference manual 543 f
  init_uart_enable_clk();
  init_uart_toggle_rst();
  init_uart_clear_update();
}