Hi everyone. I am trying to implement a code which has to output values at a very high rate. I figured out that Arduino should be good enough for that as it has 16 MHz clock. I tried simple few lines of code just to see the time it takes to write data. So i used micros() to get the time just before the write command, and micros() just after the write command. I printed the time on serial monitor, and I got two values, 64 and 220. If I am not wrong, it means the arduino is taking almost 156us to execute one write statement,one serial printing statement, and one time calculating statement.
int led = 13;
long int time;
void setup() {
Serial.begin(9600);
pinMode(led, OUTPUT);
time = micros(); //this gives output as 64
Serial.println(time);
digitalWrite(led, HIGH);
time = micros(); //this gives output as 220
Serial.println(time);
}
void loop() {
}
I think 156us is too much, because according to 16MHz clock the timescale should be in nanoseconds. I think I am missing something while checking the time with this method. Or is this really the speed of arduino? For my program I need data to be output around every 10us. Is it possible?
Well, depends on what you mean by "data to be output". If you are using Serial at 9600 baud, you'll be fairly fast up to 64 bytes, but the buffer only sends out 1 byte every millisecond or so. At 115200 baud, the picture is a little better, but that will send out one byte every 87 microseconds or so.
Remember, you are writing in C++, and while the clock is 16 MHz, many statements require a number of cycles, and libraries such as Serial will tend to take a great many cycles... keeping track of a pointer for the character going into the buffer, a pointer for the character being sent out, and other housekeeping duties, not the least of which is overhead for the subroutine call and exit.
For my program I need data to be output around every 10us.
(10/1000000)/(1/16000000) = ~160 machine instructions per value. With so little CPU time available there is an extremely low probability you will be able to use the Arduino functions.
Is it possible?
You have provided no details making it impossible to answer the question.
Actually even my reference to the baud is irrelevant here as the UART does the sending asynchronously. But it's still the serial.print that's hogging the time. If you trace the code right through the for each byte being sent to the uart, it probably accounts for that missing time.
size_t Print::print(long n, int base)
{
if (base == 0) {
return write(n);
} else if (base == 10) {
if (n < 0) {
int t = print('-');
n = -n;
return printNumber(n, 10) + t;
}
return printNumber(n, 10);
} else {
return printNumber(n, base);
}
}
calls printNumber()
size_t Print::printNumber(unsigned long n, uint8_t base) {
char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
char *str = &buf[sizeof(buf) - 1];
*str = '\0';
// prevent crash if called with base == 1
if (base < 2) base = 10;
do {
unsigned long m = n;
n /= base;
char c = m - base * n;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while(n);
return write(str);
}
which calls write(s)
size_t Print::write(const uint8_t *buffer, size_t size)
{
size_t n = 0;
while (size--) {
n += write(*buffer++);
}
return n;
}
which calls the hardware serial write(c)
size_t HardwareSerial::write(uint8_t c)
{
int i = (_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE;
// If the output buffer is full, there's nothing for it other than to
// wait for the interrupt handler to empty it a bit
// ???: return 0 here instead?
while (i == _tx_buffer->tail)
;
_tx_buffer->buffer[_tx_buffer->head] = c;
_tx_buffer->head = i;
sbi(*_ucsrb, _udrie);
// clear the TXC bit -- "can be cleared by writing a one to its bit location"
transmitting = true;
sbi(*_ucsra, TXC0);
return 1;
}
Actual transmission is interrupt driven:
ISR(UART_UDRE_vect)
{
if (tx_buffer.head == tx_buffer.tail) {
// Buffer empty, so disable interrupts
#if defined(UCSR0B)
cbi(UCSR0B, UDRIE0);
#else
cbi(UCSRB, UDRIE);
#endif
}
else {
// There is more data in the output buffer. Send the next byte
unsigned char c = tx_buffer.buffer[tx_buffer.tail];
tx_buffer.tail = (tx_buffer.tail + 1) % SERIAL_BUFFER_SIZE;
#if defined(UDR0)
UDR0 = c;
#elif defined(UDR)
UDR = c;
#else
#error UDR not defined
#endif
}
}