1
\$\begingroup\$

Note: I am a beginner :) MCU: Atmega328p

Which program below burdens the CPU the least? Would it be a significant difference?

Objective: to make a more "efficient" serial monitor by reducing the CPU burden.

Both programs send the int num = 65534 to my PC where I can see value coming in repeatedly through a serial monitor. Program 1 uses a empty data register interrupt and program 2 does not use any interrupts.

Considering I am still using while (( UCSR0A & (1<<UDRE0)) == 0){} in uarttrasnmitinteger function within the ISR in program 1, does program 1 reduce the "workload" of the CPU compared to not using this interrupt such as in program 2 (see below)?

Program 1:

char snum[20];
int num = 65534;
        
int main (void) {
  uarttransmitenable();
  sei(); 
  while (1) {
    //no code here
  }
}

ISR(USART_UDRE_vect) {
  uarttrasnmitinteger(num, snum);
}
        
void uarttransmitenable(void) {
  UBRR0H = (BRC >> 8);
  UBRR0L = BRC;
            
  UCSR0B = (1<<TXEN0)| (1<<RXEN0) | (1<<UDRIE0);
  UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
}
            
void uarttrasnmitinteger(unsigned int num, char* snum) {
  utoa(num, snum, 10);
  unsigned int i;
  for (i=0; i<strlen(snum); i++) {
    while ((UCSR0A & (1 << UDRE0)) == 0) {}
    UDR0 = (char)snum[i];
  }
}

Program 2:

char snum[20];
int num = 65534;
        
int main (void) {
  uarttransmitenable();
  while (1) {
    uarttrasnmitinteger(num, snum); // same function used in program 1
  }
}

void uarttransmitenable(void) {
  UBRR0H = (BRC >> 8);
  UBRR0L = BRC;
            
  UCSR0B = (1 << TXEN0)| (1 << RXEN0);
  UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
\$\endgroup\$
2
  • \$\begingroup\$ Is this the only operation you will do or there are other operations and process in your project? \$\endgroup\$
    – CNA
    Jan 5 at 3:31
  • 3
    \$\begingroup\$ A while loop in an ISR is severely unwise, basically missing the whole point of an ISR \$\endgroup\$ Jan 5 at 5:06
4
\$\begingroup\$

Branching to the interrupt, saving registers, restoring registers, and then returning from the interrupt all take clock cycles. If the only thing you are doing is transmitting this serial data, then not having the interrupt is going to use less clock cycles overall.

Some other comments about the code.

  • Blocking while you send multiple characters within an interrupt will cause you to stay within the interrupt for hundreds of us to several ms depending on your baud rate. Generally speaking its good practice to enter and leave an interrupt quickly (like within a few us or 10s of us). Therefore blocking while you send multiple characters within an interrupt is generally not good practice.

  • Typically one would add characters to a FIFO and then enable the transmit data register empty interrupt. The interrupt would send only one character and then return. Once the character was sent, the interrupt would occur again and send the next character. Once the FIFO was empty the interrupt would be turned off. This would allow the characters to be sent essentially in the background while other code executes.

  • Use of a loop inside of an interrupt is usually not good practice (especially if the loop bounds are not well known). It can create the same timing problems mentioned above.

\$\endgroup\$
2
\$\begingroup\$

The way to do this is with a fifo structure. The transmit function loads your string into the fifo and turns on the interrupt. The UDRE interrupt has just one if statement. It checks if fifo is empty. If empty, it turns the interrupt off, since the transmission is done. If not empty, it takes one character out of the fifo and puts it into the UDR register to transmit it. That's it for the interrupt.

The fifo and associated functions (putting a character in, taking out, checking if full or empty) should be programmed with care to work with the possibility of being interrupted. You can reuse this structure for receiving data too.

\$\endgroup\$
1
  • \$\begingroup\$ The Arduino core code is a worked example of how to implement interrupt driven serial communications using circular buffers. It is a general solution to a general problem - ie it is not necessarily the 'best' solution in all circumstances and there is a small efficiency cost, but every line of code adds cost. \$\endgroup\$
    – Kartman
    Jan 5 at 10:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.