nRF52832 Product Specification V1.4, page 344 lists predefined baud rates only.
Is it possible to use other values for custom baud rate, and if so, what is the relationship between the Value and the actual generated baud rate?
nRF52832 Product Specification V1.4, page 344 lists predefined baud rates only.
Is it possible to use other values for custom baud rate, and if so, what is the relationship between the Value and the actual generated baud rate?
Such a good question, and highlights the fun the hardware design engineer must have been having that day .. this is what I use, inclusive of the rounding
// Baud rate calculation on nRF51 and nRF52832 // =========================================== // // Calculate BAUDRATE value settings for the black-magic 20-bit baud rate generator uint32_t CalculatedRegisterValue; uint64_t SystemClock = 16000000ULL; // Typically 16MHz uint64_t MagicScaler = 32; // Preserves bits on divisions, shift 32 bits // Baudrate generator for the UART is the top 20 bits of a 32-bit field; add in rounding 0.5 ls bit CalculatedRegisterValue = (uint32_t)((((uint64_t)RequiredBaudRate << MagicScaler) / SystemClock) + 0x800) & 0xFFFFF000;
So does it work? Well it doesn't exactly match the datasheet quoted values:
typedef struct {
uint32_t QuotedRegisterValue;
uint32_t RequiredBaudRate;
uint32_t QuotedActualBaudRate;
} BaudCheck_t;
// Quoted values from nRF52832 v1.4 datasheet
static BaudCheck_t BaudCheck[] = {
{0x0004F000, 1200, 1205},
{0x0009D000, 2400, 2396},
{0x0013B000, 4800, 4808},
{0x00275000, 9600, 9598},
{0x003AF000, 14400, 14401},
{0x004EA000, 19200, 19208},
{0x0075C000, 28800, 28777},
{0x009D0000, 38400, 38369},
{0x00EB0000, 57600, 57554},
{0x013A9000, 76800, 76923},
{0x01D60000, 115200, 115108},
{0x03B00000, 230400, 231884},
{0x04000000, 250000, 250000},
{0x07400000, 460800, 457143},
{0x0F000000, 921600, 941176},
{0x10000000, 1000000, 1000000}};
#define NUM_BAUD_TESTS (sizeof(BaudCheck)/sizeof(BaudCheck[0]))
static void BaudRateDivisorTest(void)
{
// Baud rate calculation on nRF51 and nRF52832
// ===========================================
//
// Calculate BAUDRATE value settings for the black-magic 20-bit baud rate generator
uint32_t OkCountRegister = 0, FailCountRegister = 0, CalculatedRegisterValue;
uint32_t OkCountActualBaud = 0, FailCountActualBaud = 0;
uint32_t BaudIndex, ActualBaudRate;
uint64_t SystemClock = 16000000ULL; // Typically 16MHz
uint64_t MagicScaler = 32; // Preserves bits on divisions, shift 32 bits
for (BaudIndex = 0; BaudIndex < NUM_BAUD_TESTS; BaudIndex++)
{
// Baudrate generator for the UART is the top 20 bits of a 32-bit field; add in rounding 0.5 ls bit
CalculatedRegisterValue = (uint32_t)((((uint64_t)BaudCheck[BaudIndex].RequiredBaudRate << MagicScaler) / SystemClock) + 0x800) & 0xFFFFF000;
if ( BaudCheck[BaudIndex].QuotedRegisterValue == CalculatedRegisterValue)
{
OkCountRegister++;
}
else
{
FailCountRegister++;
}
ActualBaudRate = (uint32_t)(((uint64_t)CalculatedRegisterValue * SystemClock) >> MagicScaler);
if ( BaudCheck[BaudIndex].QuotedActualBaudRate == ActualBaudRate)
{
OkCountActualBaud++;
}
else
{
FailCountActualBaud++;
}
}
while(1) ;
}
The values either exactly match the data sheet or are close; that may be a code issue or a datasheet issue, who knows but I think the h/w guy was teasing us..
I checked the nRF52840 Product Specification, and interestingly there have been some additions and corrections, but 10 of the 26 quoted values still seem incorrect, although now not by much. I'm also curious as to whether the baud rate generator is indeed only 20 bits, given that all 32 bits are writable .. I may test this if I get time.
// Quoted values from nRF52832 v1.4 datasheet - 16 correct, 10 incorrect
static BaudCheck_t BaudCheck[] = {
// -------Documentation--------- -------Calculated---------
// Register Required Actual Register Actual Ok? Index
// ========== ======== ====== ========== ====== ==== =====
{0x0004F000, 1200, 1205, 0x0004F000, 1205, " Ok"}, // 0
{0x0009D000, 2400, 2396, 0x0009D000, 2395, " Ok"}, // 1
{0x0013B000, 4800, 4808, 0x0013B000, 4806, " Ok"}, // 2
{0x00275000, 9600, 9598, 0x00275000, 9597, " Ok"}, // 3
{0x003AF000, 14400, 14401, 0x003B0000, 14404, " -"}, // 4
{0x004EA000, 19200, 19208, 0x004EA000, 19195, " Ok"}, // 5
{0x0075C000, 28800, 28777, 0x0075F000, 28793, " -"}, // 6
{0x009D0000, 38400, 38369, 0x009D5000, 38406, " -"}, // 7
{0x00EB0000, 57600, 57554, 0x00EBF000, 57601, " -"}, // 8
{0x013A9000, 76800, 76923, 0x013A9000, 76797, " Ok"}, // 9
{0x01D60000, 115200, 115108, 0x01D7E000, 115203, " -"}, // 10
{0x03B00000, 230400, 231884, 0x03AFB000, 230392, " -"}, // 11
{0x04000000, 250000, 250000, 0x04000000, 250000, " Ok"}, // 12
{0x07400000, 460800, 457143, 0x075F7000, 460800, " -"}, // 13
{0x0F000000, 921600, 941176, 0x0EBEE000, 921600, " -"}, // 14
{0x10000000, 1000000, 1000000, 0x10000000, 1000000, " Ok"}, // 15
// Extra and changed values from nRF52840 Product Specification
{0x003B0000, 14400, 14414, 0x003B0000, 14404, " Ok"}, // 16
{0x0075F000, 28800, 28829, 0x0075F000, 28793, " Ok"}, // 17
{0x00800000, 31250, 31250, 0x00800000, 31250, " Ok"}, // 18
{0x009D5000, 38400, 38462, 0x003B0000, 38406, " Ok"}, // 19
{0x00E50000, 56000, 55944, 0x00E56000, 55999, " -"}, // 20
{0x00EBF000, 57600, 57762, 0x00EBF000, 57601, " Ok"}, // 21
{0x01D7E000, 115200, 115942, 0x01D7E000, 115203, " Ok"}, // 22
{0x03AFB000, 230400, 231884, 0x03AFB000, 230392, " Ok"}, // 23
{0x075F7000, 460800, 470588, 0x075F7000, 460800, " Ok"}, // 24
{0x0EBED000, 921600, 941176, 0x0EBEE000, 921600, " -"}}; // 25
I added rounding on the system clock, but that doesn't affect results:
// Baudrate generator for the UART is the top 20 bits of a 32-bit field; add in rounding 0.5 ls bit CalculatedRegisterValue = (uint32_t)(((((uint64_t)RequiredBaudRate << MagicScaler) + (SystemClock >> 1)) / SystemClock) + 0x800) & 0xFFFFF000;
nRF52832 (rev 2) NRF_UARTE0->BAUDRATE values and corresponding baud rates.
Bits 11-0 are not used.
nrf52832_uarte_baud_rates.txt
nRF52832 (rev 2) NRF_UARTE0->BAUDRATE values and corresponding baud rates.
Bits 11-0 are not used.
nrf52832_uarte_baud_rates.txt
Hello, may I ask how this corresponding table was calculated or from which document you obtained it? Thank you!
No document, I'm afraid, but here is the code I wrote to generate the table.
#define RESULT_STRING_LENGTH 3 // "Yes" or " -"
typedef struct {
uint32_t QuotedRegisterValue;
uint32_t RequiredBaudRate;
uint32_t QuotedActualBaudRate;
uint32_t CalculatedRegisterValue;
uint32_t CalculatedActualBaudRate;
char DoesRegisterMatch[RESULT_STRING_LENGTH];
} BaudCheck_t;
// Quoted values from nRF52832 v1.4 datasheet - 16 correct, 10 incorrect
static BaudCheck_t BaudCheck[] = {
// -------Documentation--------- -------Calculated---------
// Register Required Actual Register Actual Ok? Index
// ========== ======== ====== ========== ====== ==== =====
{0x0004F000, 1200, 1205, 0x0004F000, 1205, " Ok"}, // 0
{0x0009D000, 2400, 2396, 0x0009D000, 2395, " Ok"}, // 1
{0x0013B000, 4800, 4808, 0x0013B000, 4806, " Ok"}, // 2
{0x00275000, 9600, 9598, 0x00275000, 9597, " Ok"}, // 3
{0x003AF000, 14400, 14401, 0x003B0000, 14404, " -"}, // 4
{0x004EA000, 19200, 19208, 0x004EA000, 19195, " Ok"}, // 5
{0x0075C000, 28800, 28777, 0x0075F000, 28793, " -"}, // 6
{0x009D0000, 38400, 38369, 0x009D5000, 38406, " -"}, // 7
{0x00EB0000, 57600, 57554, 0x00EBF000, 57601, " -"}, // 8
{0x013A9000, 76800, 76923, 0x013A9000, 76797, " Ok"}, // 9
{0x01D60000, 115200, 115108, 0x01D7E000, 115203, " -"}, // 10
{0x03B00000, 230400, 231884, 0x03AFB000, 230392, " -"}, // 11
{0x04000000, 250000, 250000, 0x04000000, 250000, " Ok"}, // 12
{0x07400000, 460800, 457143, 0x075F7000, 460800, " -"}, // 13
{0x0F000000, 921600, 941176, 0x0EBEE000, 921600, " -"}, // 14
{0x10000000, 1000000, 1000000, 0x10000000, 1000000, " Ok"}, // 15
// Extra and changed values from nRF52840 Product Specification
{0x003B0000, 14400, 14414, 0x003B0000, 14404, " Ok"}, // 16
{0x0075F000, 28800, 28829, 0x0075F000, 28793, " Ok"}, // 17
{0x00800000, 31250, 31250, 0x00800000, 31250, " Ok"}, // 18
{0x009D5000, 38400, 38462, 0x003B0000, 38406, " Ok"}, // 19
{0x00E50000, 56000, 55944, 0x00E56000, 55999, " -"}, // 20
{0x00EBF000, 57600, 57762, 0x00EBF000, 57601, " Ok"}, // 21
{0x01D7E000, 115200, 115942, 0x01D7E000, 115203, " Ok"}, // 22
{0x03AFB000, 230400, 231884, 0x03AFB000, 230392, " Ok"}, // 23
{0x075F7000, 460800, 470588, 0x075F7000, 460800, " Ok"}, // 24
{0x0EBED000, 921600, 941176, 0x0EBEE000, 921600, " -"}}; // 25
#define NUM_BAUD_TESTS (sizeof(BaudCheck)/sizeof(BaudCheck[0]))
static void BaudRateDivisorTest(void)
{
// Baud rate calculation on nRF51 and nRF52832
// ===========================================
//
// Calculate BAUDRATE value settings for the black-magic 20-bit baud rate generator
uint32_t OkCountRegister = 0, FailCountRegister = 0, CalculatedRegisterValue;
uint32_t OkCountActualBaud = 0, FailCountActualBaud = 0;
uint32_t BaudIndex, ActualBaudRate;
uint64_t SystemClock = 16000000ULL; // Typically 16MHz
uint64_t MagicScaler = 32; // Preserves bits on divisions, shift 32 bits
for (BaudIndex = 0; BaudIndex < NUM_BAUD_TESTS; BaudIndex++)
{
// Baudrate generator for the UART is the top 20 bits of a 32-bit field; add in rounding 0.5 ls bit
CalculatedRegisterValue = (uint32_t)(((((uint64_t)BaudCheck[BaudIndex].RequiredBaudRate << MagicScaler) + (SystemClock>>1)) / SystemClock) + 0x800) & 0xFFFFF000;
BaudCheck[BaudIndex].CalculatedRegisterValue = CalculatedRegisterValue;
if ( BaudCheck[BaudIndex].QuotedRegisterValue == CalculatedRegisterValue)
{
OkCountRegister++;
memcpy(BaudCheck[BaudIndex].DoesRegisterMatch, " Ok", RESULT_STRING_LENGTH);
}
else
{
FailCountRegister++;
}
ActualBaudRate = (uint32_t)(((uint64_t)CalculatedRegisterValue * SystemClock) >> MagicScaler);
BaudCheck[BaudIndex].CalculatedActualBaudRate = ActualBaudRate;
if ( BaudCheck[BaudIndex].QuotedActualBaudRate == ActualBaudRate)
{
OkCountActualBaud++;
}
else
{
FailCountActualBaud++;
}
}
while(1) ;
}
static void SetupUART(const uint32_t BaudRate);
static void StartUART_Transmit(const uint8_t * const pMsg, const uint16_t MsgLength);
static void DisableUART(void);
static uint16_t SetupUART_Receive(uint8_t * const pMsg, const uint16_t MaxMsgLength);
static uint16_t WaitUART_Receive(void);
static NRF_UARTE_Type *pUART = NRF_UARTE0;
#define PIN_UART_TX PIN_FEATHER_TXD // P0.25
#define PIN_UART_RX PIN_FEATHER_RXD // P0.24
#define MAX_TX_PACKET_SIZE 128 //2048 nRF52832 8-bit, nRF52840 16-bit
static uint8_t TxMsg[MAX_TX_PACKET_SIZE] = "Baudrate generator for the UART is the top 20 bits of a 32-bit field; add in rounding 0.5 ls bit";
#define TX_PACKET_SIZE sizeof(TxMsg)
#define RX_PACKET_SIZE (TX_PACKET_SIZE - 1)
static uint8_t RxMsg[RX_PACKET_SIZE] = "";
uint32_t RequiredBaudRate = 1000000UL;
uint32_t MaxOkdBaudRate = 0UL;
uint32_t CalculatedRegisterValue;
uint32_t RegisterValueAtMaxBaud = 0;
uint64_t SystemClock = 16000000ULL; // Typically 16MHz
uint64_t MagicScaler = 32; // Preserves bits on divisions, shift 32 bits
void UART_BaudTest(void)
{
// Enable crystal osc
for (RequiredBaudRate = 1000000; RequiredBaudRate < 2000000; )
{
// Baudrate generator for the UART is the top 20 bits of a 32-bit field; add in rounding 0.5 ls bit
CalculatedRegisterValue = (uint32_t)(((((uint64_t)RequiredBaudRate << MagicScaler) + (SystemClock >> 1)) / SystemClock) + 0x800) & 0xFFFFF000;
// Setup UART from scratch for each baud rate test
SetupUART(CalculatedRegisterValue); // NRF_UART_BAUDRATE_230400);
SetupUART_Receive(RxMsg, sizeof(RxMsg));
StartUART_Transmit(TxMsg, TX_PACKET_SIZE);
WaitUART_Receive();
DisableUART();
if ((memcmp(TxMsg, RxMsg, RX_PACKET_SIZE) == 0) && (pUART->EVENTS_ERROR == 0) && (pUART->ERRORSRC == 0x0000))
{
MaxOkdBaudRate = RequiredBaudRate;
RegisterValueAtMaxBaud = CalculatedRegisterValue;
//break;
}
RequiredBaudRate += 20;
}
while (1)
;
}
static void SetupUART(const uint32_t BaudRate)
{
// Disable the UARTE
pUART->ENABLE = 0;
// Configure UARTE with no flow control, no parity bit and selected baud rate
pUART->CONFIG = 0;
pUART->BAUDRATE = BaudRate;
// Select TX and RX pin default disconnected mode to avoid sending break indication on disabling uart ie NRF_GPIO_PIN_INPUT_DISCONNECT on Rx
nrf_gpio_cfg(PIN_UART_RX, NRF_GPIO_PIN_DIR_INPUT, NRF_GPIO_PIN_INPUT_CONNECT, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(PIN_UART_TX, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_CONNECT, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
// Select TX and RX pins; not for Loopback testing or 1-wire or RS485 a single pin can be used for both Rx and Tx
pUART->PSEL.TXD = PIN_UART_TX;
pUART->PSEL.RXD = PIN_UART_RX; // For single pin loopback use PIN_UART_TX here, normal uart use PIN_UART_RX
// Disable all interrupts and clear events
pUART->INTENCLR = 0xFFFFFFFFUL;
pUART->EVENTS_TXSTARTED = 0;
pUART->EVENTS_TXSTOPPED = 0;
pUART->EVENTS_RXSTARTED = 0;
pUART->EVENTS_ERROR = 0;
pUART->ERRORSRC = 0x000F; // Write '1's to clear any errors left set
pUART->EVENTS_ENDTX = 0;
pUART->EVENTS_ENDRX = 0;
// Enable the UART, note 0x04 for UART and 0x08 for UARTE with DMA
pUART->ENABLE = 0x08;
}
STATIC_ASSERT (UARTE_ENABLE_ENABLE_Enabled == 0x08, "UARTE_ENABLE_ENABLE_Enabled == 0x08 Failed");
static void StartUART_Transmit(const uint8_t * const pMsg, const uint16_t MsgLength)
{
// Configure transmit buffer and start the transfer
pUART->TXD.MAXCNT = MsgLength;
pUART->TXD.PTR = (uint32_t)pMsg;
pUART->TASKS_STARTTX = 1;
__DSB();
// Wait until the transfer start event is indicated
while (pUART->EVENTS_TXSTARTED == 0) ;
// Wait until the transfer is complete
while (pUART->EVENTS_ENDTX == 0) ; //add timeout here also add LEDs blinking during tests
// Stop the UART TX
pUART->TASKS_STOPTX = 1;
__DSB();
// Wait until we receive the stopped event
while (pUART->EVENTS_TXSTOPPED == 0) ;
}
static void DisableUART(void)
{
// De-Select TX and RX pins
pUART->PSEL.TXD = 0x80000000;
pUART->PSEL.RXD = 0x80000000;
// Disable the UARTE
pUART->ENABLE = 0;
}
static uint16_t SetupUART_Receive(uint8_t * const pMsg, const uint16_t MaxMsgLength)
{
volatile uint32_t Timeout;
// Clear receive buffer
memcpy(pMsg, "-----------------", MaxMsgLength);
// Configure receive buffer and start reception
pUART->RXD.MAXCNT = MaxMsgLength;
pUART->RXD.PTR = (uint32_t)pMsg;
pUART->EVENTS_ENDRX = 0;
pUART->TASKS_STARTRX = 1;
__DSB();
// Wait until the transfer start event is indicated
while (pUART->EVENTS_RXSTARTED == 0) ;
return 0;
}
static uint16_t WaitUART_Receive(void)
{
// Wait until the transfer is complete
//while (pUART->EVENTS_ENDRX == 0) ; //add timeout here also add LEDs blinking during tests
// or just delay ..
nrf_delay_ms(1);
// Stop the UART RX
pUART->TASKS_STOPRX = 1;
__DSB();
// Return packet when postLastRxByteTime indicates end-of-packet timeout
return 0;
}
Thank you for your reply. I'm not sure if we are referring to the same table. What I'm referring to is how the data in the "nrf52832_uarte_baud_rates.txt" document was generated. The file "nrf52832_uarte_baud_rates.txt" was previously provided by Tim Schuerewegen in this post.
Thank you!
That I don't know; maybe ask Tim directly. The code I provide can be used to generate a similar table if you want to check for differences.
Hi, Tim. In the "nrf52832_uarte_baud_rates.txt", could you please explain how the corresponding table was calculated or from which technical document it was obtained?
Thank you!