I am trying to use the nRF as an intermediary bootloader for another microcontroller. All that I need the nRF to do is accept a 64kB binary file from my computer (at the moment I do this over UART). Occasionally it works, but most of the time, I lose bytes, and I'm not entirely sure why.
Here is my UARTE0 initialization function:
void uarts_init(void) {
// configure
NRF_UARTE0->RXD.PTR = (uint32_t)app_vars.uart_buf_DK_RX;
NRF_UARTE0->RXD.MAXCNT = UART_BUF_SIZE;
NRF_UARTE0->TXD.PTR = (uint32_t)app_vars.uart_buf_DK_TX;
NRF_UARTE0->TXD.MAXCNT = UART_BUF_SIZE;
NRF_UARTE0->PSEL.TXD = 0x00000006; // 0x00000006==P0.6
NRF_UARTE0->PSEL.RXD = 0x00000008; // 0x00000008==P0.8
NRF_UARTE0->CONFIG = 0x00000000; // 0x00000000==no flow control, no parity bits, 1 stop bit
NRF_UARTE0->BAUDRATE = 0x04000000; // 0x004EA000==19200 baud (actual rate: 19208)
NRF_UARTE0->TASKS_STARTRX = 0x00000001; // 0x00000001==start RX state machine; read received byte from RXD register
NRF_UARTE0->SHORTS = 0x00000020; // short end RX to start RX
NRF_UARTE0->INTENSET = 0x00000010;
NRF_UARTE0->ENABLE = 0x00000008; // 0x00000008==enable
// enable interrupts
NVIC_SetPriority(UARTE0_UART0_IRQn, 1);
NVIC_ClearPendingIRQ(UARTE0_UART0_IRQn);
NVIC_EnableIRQ(UARTE0_UART0_IRQn);
}
My UARTE0 interrupt handler is a bit complicated to look at, sorry about that. Fundamentally, all that it does is upon receiving a character, it puts that character into the next index of a buffer in memory. Then it has a simple state machine: upon receiving a "\n" the nRF checks if the buffer is a specific string (this works as expected every time). If it receives that string, the next 64kB that the nRF receives from the computer should go to a separate location in memory. That's really it (for now, eventually I would like to add other strings in the ISR but I'm stuck on this one). Here is the ISR:
void UARTE0_UART0_IRQHandler(void) {
uint8_t uart_rx_byte;
// debug
app_dbg.num_ISR_UARTE0_UART0_IRQHandler++;
if (NRF_UARTE0->EVENTS_ERROR == 0x00000001) {
// clear the error and continue?
NRF_UARTE0->EVENTS_ERROR = 0x00000000;
NRF_UARTE0->TASKS_FLUSHRX = 0x00000001;
NRF_UARTE0->TASKS_STARTRX = 0x00000001;
}
if (NRF_UARTE0->EVENTS_ENDRX == 0x00000001) {
// byte received from computer
// clear
NRF_UARTE0->EVENTS_ENDRX = 0x00000000;
// debug
app_dbg.num_ISR_UARTE0_UART0_IRQHandler_ENDRX++;
if(app_vars.programmer_state == PROGRAMMER_WAIT_4_CMD_ST) {
uart_rx_byte = app_vars.uart_buf_DK_RX[0];
app_vars.uart_RX_command_buf[app_vars.uart_RX_command_idx++] = uart_rx_byte;
if((uart_rx_byte=='\n')||(uart_rx_byte=='\r')) { // \r for debugging w/ putty, \n for the python scripts
app_vars.uart_RX_command_idx = 0; // reset index to receive the next command
if(memcmp(app_vars.uart_RX_command_buf,UART_TRANSFERSRAM,sizeof(UART_TRANSFERSRAM))==0) { // enter transfer SRAM state
app_vars.programmer_state = PROGRAMMER_SRAM_LD_ST;
app_vars.uart_RX_command_idx = 0;
print_sram_started_msg();
}
// else - erroneous command, clear the buffer and reset to default state
else {
memset(app_vars.uart_RX_command_buf,0,sizeof(app_vars.uart_RX_command_buf));
app_vars.uart_RX_command_idx = 0;
app_vars.programmer_state = PROGRAMMER_WAIT_4_CMD_ST;
}
}
else if (app_vars.uart_RX_command_idx > MAX_COMMAND_LEN) { // max length exceeded w/out return character, reset buffer
memset(app_vars.uart_RX_command_buf,0,sizeof(app_vars.uart_RX_command_buf));
app_vars.uart_RX_command_idx = 0;
app_vars.programmer_state = PROGRAMMER_WAIT_4_CMD_ST;
}
}
else if (app_vars.programmer_state == PROGRAMMER_SRAM_LD_ST) {
uart_rx_byte = app_vars.uart_buf_DK_RX[0];
app_vars.instruction_memory[app_vars.uart_RX_command_idx++] = uart_rx_byte;
if(app_vars.uart_RX_command_idx == MEM_SIZE) { // finished w/ the 64kB memory
// after loading memory - reset state, index, and command buffer
app_vars.programmer_state = PROGRAMMER_SRAM_LD_DONE;
print_sram_done_msg();
app_vars.programmer_state = PROGRAMMER_WAIT_4_CMD_ST;
app_vars.uart_RX_command_idx = 0;
memset(app_vars.uart_RX_command_buf,0,sizeof(app_vars.uart_RX_command_buf));
}
}
}
}
I am running into quite a few problems, all of which involve some problem with the 64kB. First, if I use an nRF UART baud that is not exactly equal to the baud set in pyserial on the computer, the UARTE0 register indicates a buffer overflow error, which really shouldn't be happening; I think UART has a ~10% tolerance in clock rate error. This does not occur if I set the pyserial baud to exactly what the nRF wants (for instance, 19208 instead of 19200). Second, even when the baud is exact, sometimes I lose bytes... somewhere, regardless of baud. No UARTE errors are triggered (I put a breakpoint inside of an if(NRF_UARTE0->EVENTS_ERROR==1UL) statement that never fires). And when I look at the output from my computer with an FTDI chip and a logic analyzer, all of the bytes do indeed leave the computer. The third is, when I run in debug mode (F5 in Segger) it will periodically succeed and transfer the full 64kB. But, when I run continuously (ctrl+T, L), I always lose bytes. Not sure what this indicates, but it is worrisome.
It is also possible that my problem is on the pyserial side. Here is the relevant code snippet:
# Open COM port to nRF nRF_ser = serial.Serial( port=nRF_port, baudrate=250000, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS) # Open binary file from Keil with open(binary_image, 'rb') as f: bindata = bytearray(f.read()) # Need to know how long the binary payload to pad to 64kB code_length = len(bindata) - 1 pad_length = 65536 - code_length - 1 for i in range(pad_length): bindata.append(0) # Transfer payload to nRF nRF_ser.write(b'transfersram\n') print(nRF_ser.read_until()) # Send the binary data over uart nRF_ser.write(bindata) # and wait for response that writing is complete print(nRF_ser.read_until()) # Execute 3-wire bus bootloader on nRF nRF_ser.write(b'boot3wb\n') # Display confirmation message from nRF print(nRF_ser.readline())
On my nRF 52840-DK, the crystal is on and HF clock is on.
I thought that it might be a hardware issue, so I changed out cables, computer, nRF board, and I had no luck. I would prefer not to use the suite of uarte_app functions because I would like the code to be as standalone as possible and because I tried it and I actually had the same problem (it's harder to debug because I don't understand the functions). One other thing I should note: I configured the nRF to run at 1.8V. That might be relevant, but I didn't notice any difference running at 2.4V or 3.3V. It fails just as often. If anyone has any further debugging suggestions or an alternative idea for loading 64kB into the nRF's RAM (UART is fairly slow) I would appreciate it.
Thanks!