1
\$\begingroup\$

Intro:

I have an Arduino mega doing multiple tasks , from which A function Reads data from a sensor using SPI . Every like 10,000 read or something , i get 1 error bit in the SPI data . and I have narrowed down the problem to the Interrupt generated by the Serial Receive library.

So : I want to disable the RX1 interrupt while i deal with the SPI sensor ( about 10us) , then re-enable the RX1 back to normal .

Questions:

1- Shall i use Serial1.end() then Serial1.begin() (source code shown below from arduino GitHub), my assumption is that these functions take too long to perform ( in millis seconds ). and the data in the already received and prepared to be sent in the Hardware buffer gets lost !

void HardwareSerial::begin(unsigned long baud, byte config)
{
  // Try u2x mode first
  uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2;
  *_ucsra = 1 << U2X0;

  // hardcoded exception for 57600 for compatibility with the bootloader
  // shipped with the Duemilanove and previous boards and the firmware
  // on the 8U2 on the Uno and Mega 2560. Also, The baud_setting cannot
  // be > 4095, so switch back to non-u2x mode if the baud rate is too
  // low.
  if (((F_CPU == 16000000UL) && (baud == 57600)) || (baud_setting >4095))
  {
    *_ucsra = 0;
    baud_setting = (F_CPU / 8 / baud - 1) / 2;
  }

  // assign the baud_setting, a.k.a. ubrr (USART Baud Rate Register)
  *_ubrrh = baud_setting >> 8;
  *_ubrrl = baud_setting;

  _written = false;

  //set the data bits, parity, and stop bits
#if defined(__AVR_ATmega8__)
  config |= 0x80; // select UCSRC register (shared with UBRRH)
#endif
  *_ucsrc = config;

  sbi(*_ucsrb, RXEN0);
  sbi(*_ucsrb, TXEN0);
  sbi(*_ucsrb, RXCIE0);
  cbi(*_ucsrb, UDRIE0);
}

void HardwareSerial::end()
{
  // wait for transmission of outgoing data
  flush();

  cbi(*_ucsrb, RXEN0);
  cbi(*_ucsrb, TXEN0);
  cbi(*_ucsrb, RXCIE0);
  cbi(*_ucsrb, UDRIE0);

  // clear any received data
  _rx_buffer_head = _rx_buffer_tail;
}

2- Or shall i disable serial1 interrupts manually (as shown below) , then re-enabling them . is this safe from a microcontroller standpoint ?? what happens to data in hardware and software buffer ?

 //disable Interrupts     
cbi(*_ucsrb, RXEN1);
cbi(*_ucsrb, TXEN1);
cbi(*_ucsrb, RXCIE1);
cbi(*_ucsrb, UDRIE1); 
//Do the SPI routine // 
//enable interrupts
sbi(*_ucsrb, RXEN1);
sbi(*_ucsrb, TXEN1);
sbi(*_ucsrb, RXCIE1);
cbi(*_ucsrb, UDRIE1);

Option 3:

Disable global Interrupts CLI()

do SPI routine

Enable global intruupts SEI()

\$\endgroup\$
1
\$\begingroup\$

The standard way to deal with a problem like you have is to simply use the global interrupt disable and enable. Since you already know that the UART interrupt can corrupt your SPI transaction then you should be prepared for the fact that any interrupt could corrupt the SPI transaction.

The only general case where you would not use the global interrupt disable/enable is if you were also using interrupts to support the SPI transactions. This is not the final answer to every situation because there are surely specialty applications where complex handling of interrupts allows/disallows/priorities need to be dealt with. However your case does not look to be that type of situation.

When using global interrupt enable and disables in a programming context where they may or may not be enabled when a particular piece of code is called with a critical section then it is necessary to preserve the existing state of the global interrupt system after the critical state. In this case the proper method of handling critical sections of code is to use a code sequence like the following pseudo code:

  1. Push interrupt state flag to CPU stack
  2. Disable global interrupts
  3. Critical section of code starts
  4. ....
  5. Critical section of code ends
  6. Restore interrupt flag state from CPU stack

Manipulating the interrupt enable flag like this usually needs to be done in the CPU assembly language. C compilers often provide a means to invoke the necessary assembly code or have built in macros for doing this operation.

\$\endgroup\$
0
\$\begingroup\$

Just use the the SPI transaction API.

https://www.arduino.cc/en/Tutorial/SPITransaction

Otherwise just disable the UART RXCIEn bits should be enough, but I would not assume calling Serial end / begin is a performance issue. Try using the standards first and evaluate whether it's an actual problem before optimizing. Premature optimization is the root of all evil.

\$\endgroup\$
  • \$\begingroup\$ I am using spi transaction API . \$\endgroup\$ – ElectronS Jan 17 at 15:20
  • \$\begingroup\$ other than optimization , Serial end() does clearly flush the received and pending to be sent data . so this is not a good thing \$\endgroup\$ – ElectronS Jan 17 at 15:23

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.