Writing to Serial Port WITHOUT Interrupts

I am debugging a particularly difficult bug in a very large application (>50 source files, >270K of object code, using I2C, SPI, all Serial ports, Ethernet, and lots of other stuff, very heavily interrupt-driven, doign real-time motion control with multiple DC servo motors, communicating with multiple external devices, etc....). I have completely exhausted ll my normal debugging methods to little avail, so I now need a way of outputting data in more-or-less real-time so get a clue WHERE the thing is crashing. The easiest way I can see to do this is to output single characters through one of the hardware serial ports, without ANY software buffering, so once the character is written to the UART, it is pretty much guaranteed to make it off-chip, even if the software crashes immediately after writing the character to the UART.

So, how can I turn off at least the Tx interrupts for one of the Serial ports, and write characters directly to the UART?

Regards,
Ray L.

Hello Ray,

Not sure to understand what you are looking for...BUT maybe this site will help you to send via UART without interruptions :

Isn't that the way that the normal Arduino Serial works? Most Arduinos have a single-char output buffer. If the buffer is empty, then a single-char Serial.write() will drop the char there and the UART starts transmitting.

It's only when the buffer is occupied that it relies on the interrupts to call the routine to send the next char from the Arduino (actually Wiring) ring buffer.

Here's the relevant function in the UARTClass.cpp for the DUE:

size_t UARTClass::write( const uint8_t uc_data )
{
  // Is the hardware currently busy?
  if (((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY) |
      (_tx_buffer->_iTail != _tx_buffer->_iHead))
  {
    // If busy we buffer
    unsigned int l = (_tx_buffer->_iHead + 1) % SERIAL_BUFFER_SIZE;
    while (_tx_buffer->_iTail == l)
      ; // Spin locks if we're about to overwrite the buffer. This continues once the data is sent

    _tx_buffer->_aucBuffer[_tx_buffer->_iHead] = uc_data;
    _tx_buffer->_iHead = l;
    // Make sure TX interrupt is enabled
    _pUart->UART_IER = UART_IER_TXRDY;
  }
  else 
  {
     // Bypass buffering and send character directly
     _pUart->UART_THR = uc_data;
  }
  return 1;
}

So long as you're using a serial port that's otherwise silent, then a single char will be sent by the hardware if your program crashes at this point.

Complete sketch:

byte debug;

// Serial1 uses USART0
// Serial2 uses USART1
// Serial3 uses USART3

void setup() {
  Serial3.begin (115200);
  
  // disable UART tx int
  USART3->US_IDR = US_IDR_TXRDY;

  debug = 'A';
}

void loop() {
  // put your main code here, to run repeatedly:
 
  while ((USART3->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY) ; // wait
  USART3->US_THR = debug++;
  if (debug > 'Z')
    debug = 'A';
}

If you don't wait for TX empty, not sure what happens.

OK, I took the link arg_newbie pointed to, and created a test sketch. It works fine for the UART (i.e. - the Programming Port), but I need to output to Serial2, which, AFAICT, is actually USART1 on the SAM3X. I am clearly missing something in the initialization, as I can't get anything to work on Serial2. Here's the code for my DebugUART "driver", and the test sketch.

// DebugUART.h
#ifndef DEBUGUART_H
#define DEBUGUART_H

#include "sam.h"

class DebugUART
{
public: 
 DebugUART() {}
 ~DebugUART() {}
 void UART_Init(int port, int baud);
 void PrintStr(char *Text);

private:
 Uart *pUart = 0;
};

#endif
// DebugUART.cpp
#include "DebugUART.h"

void DebugUART::UART_Init(int port, int baud)
{
 uint32_t ul_UART_pins_mask = 0;

 switch (port)
 {
 case 0:
 pUart = (Uart *)0x400e0800;
 //1.Multiplexing peripheral
 ul_UART_pins_mask = PIO_PA8A_URXD | PIO_PA9A_UTXD;
 PIOA->PIO_ABSR &= ~ul_UART_pins_mask; //Peripheral A selected for both line (0 is A, 1 is B)
 PIOA->PIO_PDR = ul_UART_pins_mask; //Moves pins control from PIO controller to Peripheral

 //2.UART settings (UART is disabled on reset)
 pUart->UART_BRGR = 84000000 / 16 / baud; //Clock Divisor value according to datasheet formula
 pUart->UART_MR = UART_MR_CHMODE_NORMAL | UART_MR_PAR_NO; //Normal mode, no parity
 pUart->UART_CR = UART_CR_TXEN; //Enable transmitter
 pUart->UART_IDR = 0xFFFFu; //Disable all interrupts
 PMC->PMC_WPMR = TC_WPMR_WPEN;
 PMC->PMC_PCER0 |= (1 << ID_UART); // Enable UART clock
 break;

 case 1:
 pUart = (Uart *)0x40098000;
 //1.Multiplexing peripheral
 ul_UART_pins_mask = PIO_PA10A_RXD0 | PIO_PA11A_TXD0;
 PIOA->PIO_ABSR &= ~ul_UART_pins_mask; //Peripheral A selected for both line (0 is A, 1 is B)
 PIOA->PIO_PDR = ul_UART_pins_mask; //Moves pins control from PIO controller to Peripheral
 //2.UART settings (UART is disabled on reset)
 pUart->UART_BRGR = 84000000 / 16 / baud; //Clock Divisor value according to datasheet formula
 pUart->UART_MR = UART_MR_CHMODE_NORMAL | UART_MR_PAR_NO; //Normal mode, no parity
 pUart->UART_CR = UART_CR_TXEN; //Enable transmitter
 pUart->UART_IDR = 0xFFFFu; //Disable all interrupts
 PMC->PMC_WPMR = TC_WPMR_WPEN;
 PMC->PMC_PCER0|= (1 << ID_USART0); // Enable UART clock
 break;

 case 2:
 pUart = (Uart *)0x4009c000;
 //1.Multiplexing peripheral
 ul_UART_pins_mask = PIO_PA12A_RXD1 | PIO_PA13A_TXD1;
 PIOA->PIO_ABSR &= ~ul_UART_pins_mask; //Peripheral A selected for both line (0 is A, 1 is B)
 PIOA->PIO_PDR = ul_UART_pins_mask; //Moves pins control from PIO controller to Peripheral
 //2.UART settings (UART is disabled on reset)
 pUart->UART_BRGR = 84000000 / 16 / baud; //Clock Divisor value according to datasheet formula
 pUart->UART_MR = UART_MR_CHMODE_NORMAL | UART_MR_PAR_NO; //Normal mode, no parity
 pUart->UART_CR = UART_CR_TXEN; //Enable transmitter
 pUart->UART_IDR = 0xFFFFu; //Disable all interrupts
 PMC->PMC_WPMR = TC_WPMR_WPEN;
 PMC->PMC_PCER0 |= (1 << ID_USART1); // Enable UART clock
 break;

 case 3:
 pUart = (Uart *)0x400a4000;
 //1.Multiplexing peripheral
 ul_UART_pins_mask = PIO_PD5B_RXD3 | PIO_PD4B_TXD3;
 PIOD->PIO_ABSR &= ~ul_UART_pins_mask; //Peripheral A selected for both line (0 is A, 1 is B)
 PIOD->PIO_PDR = ul_UART_pins_mask; //Moves pins control from PIO controller to Peripheral
 //2.UART settings (UART is disabled on reset)
 pUart->UART_BRGR = 84000000 / 16 / baud; //Clock Divisor value according to datasheet formula
 pUart->UART_MR = UART_MR_CHMODE_NORMAL | UART_MR_PAR_NO; //Normal mode, no parity
 pUart->UART_CR = UART_CR_TXEN; //Enable transmitter
 pUart->UART_IDR = 0xFFFFu; //Disable all interrupts
 PMC->PMC_WPMR = TC_WPMR_WPEN;
 PMC->PMC_PCER0 |= (1 << ID_USART3); // Enable UART clock
 break;
 }

}

void DebugUART::PrintStr(char *Text)
{
 int i = 0;
 while (Text[i] != 0) //loop until string termination symbol (null)
 {
 while (0 == (pUart->UART_SR & UART_SR_TXRDY))
 ; //wait until previous character is processed
 pUart->UART_THR = Text[i]; //send next character
 i++;
 }
}
//TestSketch.ino
#include <DebugUART.h>

DebugUART *pDbgUart = NULL;

void setup()
{
 Serial.begin(115200);
 Serial.iprintf("Serial is alive\n");
 delay(1000);
 Serial.iprintf("Creating DebugUART\n");
 delay(1000);
 pDbgUart = new DebugUART();
 pDbgUart->UART_Init(2, 115200);
 if (pDbgUart)
 pDbgUart->PrintStr("\nhello, world!\n");
}


void loop()
{
}

Regards,
Ray L.

In DebugUART.cpp, for Case 0, I would say it's OK because it is UART, but for case1 to case 3, you are using the UART header files instead of the USART header files. See USART header files here:

https://github.com/arduino/Arduino/blob/master/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component/component_usart.h

and see Bobcousins code, e.g;
for case 1, I think you would want to write : USART0->US_BRGR |= …..

bobcousins:
Complete sketch:

byte debug;

// Serial1 uses USART0
// Serial2 uses USART1
// Serial3 uses USART3

void setup() {
  Serial3.begin (115200);
 
  // disable UART tx int
  USART3->US_IDR = US_IDR_TXRDY;

debug = 'A';
}

void loop() {
  // put your main code here, to run repeatedly:

while ((USART3->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY) ; // wait
  USART3->US_THR = debug++;
  if (debug > 'Z')
    debug = 'A';
}




If you don't wait for TX empty, not sure what happens.

Bob,

That works! Thanks! Definitely smart to use the existing Serial drivers to do all the init, then disable the interrupts. Though I'd still like to know what my code is missing...

If you don't wait for TxRdy, I'd expect the character in the THR to be over-written, and lost.

I've modified your code slightly (and crudely):

#define ARDUINO_SERIAL	        UART
#define ARDUINO_SERIAL1	USART0
#define ARDUINO_SERIAL2	USART1
#define ARDUINO_SERIAL3	USART3

#define UART_PORT ARDUINO_SERIAL2

void setup()
{
	byte debug;

	Serial.begin(115200);
	Serial1.begin(115200);
	Serial2.begin(115200);
	Serial3.begin(115200);
	
	// disable UART tx int
	if ((uint32_t)UART_PORT == (uint32_t)ARDUINO_SERIAL)
		UART->UART_IDR = UART_IDR_TXRDY;    //US_IDR_TXRDY;
	else if((uint32_t)UART_PORT == (uint32_t)ARDUINO_SERIAL1)
		ARDUINO_SERIAL1->US_IDR = US_IDR_TXRDY;
	else if ((uint32_t)UART_PORT == (uint32_t)ARDUINO_SERIAL2)
		ARDUINO_SERIAL2->US_IDR = US_IDR_TXRDY;
	else if ((uint32_t)UART_PORT == (uint32_t)ARDUINO_SERIAL3)
		ARDUINO_SERIAL3->US_IDR = US_IDR_TXRDY;

	debug = 'A';

	while (1)
	{
		if ((uint32_t)UART_PORT == (uint32_t)ARDUINO_SERIAL)
		{
			while ((((Uart *)UART_PORT)->UART_CR & US_CSR_TXRDY) != US_CSR_TXRDY); // wait
			Serial.iprintf("Writing %c\n", debug);
			((Uart *)UART_PORT)->UART_THR = debug++;
		}
		else
		{
			while ((((Usart *)UART_PORT)->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY); // wait
			Serial.iprintf("Writing %c\n", debug);
			((Usart *)UART_PORT)->US_THR = debug++;
		}
		if (debug > 'Z')
			debug = 'A';
		delay(1000);
	}
}


void loop()
{
}

Regards,
Ray L.

Here is a c++ version. Still ugly, due to the #ifdef cr@p, but works, except Serial does not work for some reason. I'll sort that out later.

#ifndef DEBUGUART_H
#define DEBUGUART_H

#include <Arduino.h>

// Un-comment ONE of the following lines, to select the UART to use for debug
//#define ARDUINO_SERIAL	((Uart *)UART)
//#define ARDUINO_SERIAL1	((Usart *)USART0)
#define ARDUINO_SERIAL2		((Usart *)USART1)
//#define ARDUINO_SERIAL3	((Usart *)USART3)


class DebugUART
{
public:
	DebugUART() {}
	~DebugUART() {}
	void Init(uint32_t baud);
	void PutChar(char c);
	void PutStr(char *s);
};

#endif
#include "DebugUART.h"


#ifdef ARDUINO_SERIAL
#define UART_PORT ARDUINO_SERIAL
#endif

#ifdef ARDUINO_SERIAL1
#define UART_PORT ARDUINO_SERIAL1
#endif

#ifdef ARDUINO_SERIAL2
#define UART_PORT ARDUINO_SERIAL2
#endif

#ifdef ARDUINO_SERIAL3
#define UART_PORT ARDUINO_SERIAL3
#endif

void DebugUART::Init(uint32_t baud)
{
	// disable UART tx int
#ifdef ARDUINO_SERIAL
	Serial.begin(baud);
	UART_PORT->UART_IDR = UART_IDR_TXRDY;
#endif
#ifdef ARDUINO_SERIAL1
	Serial1.begin(baud);
	UART_PORT->US_IDR = US_IDR_TXRDY;
#endif
#ifdef ARDUINO_SERIAL2
	Serial2.begin(baud);
	UART_PORT->US_IDR = US_IDR_TXRDY;
#endif
#ifdef ARDUINO_SERIAL3
	Serial3.begin(baud);
	UART_PORT->US_IDR = US_IDR_TXRDY;
#endif
}

void DebugUART::PutChar(char c)
{
#ifdef ARDUINO_SERIAL
	while ((UART_PORT->UART_CR & UART_SR_TXRDY) != UART_SR_TXRDY)
		;
	UART_PORT->UART_THR = debug++;
#else
	while ((UART_PORT->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY)
		;
	UART_PORT->US_THR = c;
#endif
}


void DebugUART::PutStr(char *s)
{
	while (*s)
		PutChar(*(s++));
}

Regards,
Ray L.

MorganS:
Isn't that the way that the normal Arduino Serial works? Most Arduinos have a single-char output buffer. If the buffer is empty, then a single-char Serial.write() will drop the char there and the UART starts transmitting.

It's only when the buffer is occupied that it relies on the interrupts to call the routine to send the next char from the Arduino (actually Wiring) ring buffer.

I believe that is correct, but there is no way to know for certain whether the character gets buffered or not. by directly writing the UART, I can be certain that by the time the function returns, there is pretty much nothing that can prevent the character from going out. By sending VERY short messages (typically only two characters), and ensuring they are spaced apart from each other (which is guaranteed by the update rates of the state machines that will be outputting the data), I should, in theory, never have to wait for TxRdy, but will do so if I sometimes have to send longer messages. I already have this working in my application. While it's a PITA to interpret the data, it will be invaluable in tracing at least the current problem, as it gives me data right up to the instant the system goes wonky, which will greatly narrow the search area for the root cause. It will enable me to see the current state for each state machine, until VERY shortly before the crash.

Regards,
Ray L.

For this type of application, I would trace a timestamp, state and event. You might be able to pack state and event into one byte. The timestamp could be quite low range, so could be 1 byte, resolution maybe 100uS. Obviously it will wrap quite quickly. You could maybe use 2 bytes instead. For a rough "what is going on" type of trace this can be quite useful.

For trace up to crash, I use an in memory buffer. If each trace entry is 4 bytes, it displays nicely inside a debugger. With routines to turn trace on/off, you can create scope like methods, e.g. continous trace, single shot trace on trigger, stop trace on trigger.

With some creativity, you can assign ASCII text to events, e.g. 'S' means start, 'E' means end etc, or even use the hex 0x51 means "SI". Then it's possible to get the gist of a hex dump without even decoding it.

If you don't have JTAG, you would need a routine to dump trace buffer to serial. For Due I use Eclipse with Jantje Arduino plugin, and a Segger JLINK EDU jtag. Single-stepping through real-time code is not much use, but being able to set a break point and see the in-memory trace up to that point is really useful.

RayLivingston:
Here is a c++ version. Still ugly, due to the #ifdef cr@p, but works, except Serial does not work for some reason. I'll sort that out later.

It doesn't work because typo in the code, you read UART_CR register instead UART_SR.

while ((UART_PORT->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY)

Late to the party, but there's a polled version of the AVR HardwareSerial over here that was used with TVout. The CPP has a define for which UART to use.

Just an FYI...