Dynamically change UART baud rate

Hi,

nRF SDK 16.0, nrf52840

I'm trying to send dmx-512 frames, for that I need to send break and mark after break. For that I need to change baud rate dynamically but it's not happening. The baud rate remains to the set value of 250k. attached is the code. Otherwise the code works fine. See line 15 of the code snippet.

void send_dmx_frame(uint8_t *dmx_data)
{
    uint32_t err_code;

    
    // Send Break (Low Voltage)
    //nrf_gpio_pin_clear(TX_PIN_NUMBER); // Set TX pin low for break duration
    //nrf_delay_us(88); // Minimum break duration 

    //// Send Mark After Break (High Voltage)
    //nrf_gpio_pin_set(TX_PIN_NUMBER); // Set TX pin high for MAB duration
    //nrf_delay_us(8); // MAB duration

    // Create start sequence with modified baud rate
    NRF_UART0->BAUDRATE = (UART_BAUDRATE_BAUDRATE_Baud57600 << UART_BAUDRATE_BAUDRATE_Pos);
    //current_comm_params.baud_rate = UART_BAUDRATE_BAUDRATE_Baud57600;
    while (app_uart_put(0) != NRF_SUCCESS);

    // Change the baud rate back to 250k
    NRF_UART0->BAUDRATE = (UART_BAUDRATE_BAUDRATE_Baud250000 << UART_BAUDRATE_BAUDRATE_Pos);
    //current_comm_params.baud_rate = UART_BAUDRATE_BAUDRATE_Baud250000;

    // Send Start Code
    while (app_uart_put(DMX_START_CODE) != NRF_SUCCESS);

    // Send DMX channel data
    for (int i = 0; i < DMX_CHANNEL_COUNT; i++)
    {
        while (app_uart_put(dmx_data[i]) != NRF_SUCCESS);
    }

    // Send Inter-Frame Gap (IFG)
    nrf_delay_us(16); // Two Mark Time periods (approximately)

    //// Wait for data to be transmitted
    //while (!uart_tx_ready)
    //{
    //    // Wait for UART to finish transmitting
    //}
}

  • app_uart_put() only places a character in the fifo queue which immediately start transmission of the byte, however bit-shifting of that byte out of the uart Tx port hasn't completed and so the 2nd change to the baud rate takes place and affects the shifted bit timing immediately. Maybe try:

        // Create start sequence with modified baud rate
        NRF_UART0->BAUDRATE = (UART_BAUDRATE_BAUDRATE_Baud57600 << UART_BAUDRATE_BAUDRATE_Pos);
        while (app_uart_put(0) != NRF_SUCCESS);
        // Wait for all 10 or 11 bits to get shifted out of Tx pin
        nrf_delay_us(191); // Minimum break duration but also 11 bits at 57600baud
        // Change the baud rate back to 250k
        NRF_UART0->BAUDRATE = (UART_BAUDRATE_BAUDRATE_Baud250000 << UART_BAUDRATE_BAUDRATE_Pos);

    Edit:
    Note at 57600 baud that's more like 191uSec for 11 bits assuming 2 stop bits, which gives a 156 uSec Break with 9-bits low. 88 uSec Break would be approximately 5 bits low or 0xF0 at 57600 baud taking the start bit as the first low bit, but also a following Mark of at least 5 bits.

    You can tune the Break/Mark more accurately by selecting a baud rate which gives exact Break/Mark times required:

    // Baudrate generator for the UART is the top 20 bits of a 32-bit field; add in rounding 0.5 ls bit
     uint32_t CalculatedRegisterValue;
     uint64_t SystemClock = 16000000ULL;    // Typically 16MHz
     uint64_t MagicScaler = 32; // Preserves bits on divisions, shift 32 bits
     CalculatedRegisterValue = (uint32_t)((((uint64_t)RequiredBaudRate << MagicScaler) + (SystemClock>>1) / SystemClock) + 0x800) & 0xFFFFF000;

Related