Serial.end()

Hi Everyone,

I propose having a Serial.end() method..

Reasoning: Pins 0 and 1 can be used for either digital i/o or serial communication.. The docs say that once Serial.begin() is called, you can no longer use these pins for digital i/o.. With the addition of Serial.end(), this will no longer be the case and the docs could say "Once Serial.begin() is called, do not use pins 0 or 1 for digital i/o unless you first call Serial.end()." I think this fits squarely in the core Serial library as it adds symmetry (undoing effects of Serial.begin()).

I spent a couple of minutes implemented Serial.end() in Arduino 12 this morning..

The main change is the addition of the following function in wiring_serial.c:

void endSerial()
{
    //disable interrupts
    uint8_t oldSREG = SREG;
    cli();
  
#if defined(__AVR_ATmega168__)
      
      // disable rx and tx
      cbi(UCSR0B, RXEN0);
      cbi(UCSR0B, TXEN0);
      
      // disable interrupt on complete reception of a byte
      cbi(UCSR0B, RXCIE0);
#else
      
      // disable rx and tx
      cbi(UCSRB, RXEN);
      cbi(UCSRB, TXEN);
      
      // disable interrupt on complete reception of a byte
      cbi(UCSRB, RXCIE);
#endif

        //flush serial read buffer so no data is available if beginSerial is recalled
      serialFlush();

      //reset sreg to original state (re-enable interrupts)
      SREG = oldSREG;  
}

Additionally I made the following changes..

wiring.h:

void endSerial();

hardwareSerial.cpp (under public methods):

void HardwareSerial::end(void)
{
  endSerial();
}

hardwareSerial.h (under public methods):

void end(void);

Does anyone see any reason why this approach would cause problems (I haven't tested thoroughly and don't have anything but atmega168 based arduinos)?

Is there any reason that this could/should not be included in the next version of arduino software, assuming I test more thoroughly and am willing to write the reference documentation?

Thanks, Ed Baafi

I agree. I need to reuse the serial port for multiple functions and ended up writing a Serial.end function like the one you are proposing. I don't see why this is not part of the Arduino standard libraries.

I have been using this with great success, however the latest version (0016) has a different structure to the file that need to be changed, the biggest being the disappearance of wiring_serial.c.

Could anyone tell me how to integrate into the 0016 software.

Cheers

Does anyone see any reason why this approach would cause problems (I haven’t tested thoroughly and don’t have anything but atmega168 based arduinos)?

Well pin 1 is hardwired through a 1k series resistor to the USB serial convertor chip so it will act like a pull-up if the USB is ouputting a steady high and might act like a voltage divider depending on what you wire to pin 1. Pin 0 is also hardwired to the USB serial chip as an input so probably not as much an issue, but might try and send serial data to the PC if the pin 0 is inputting data from an external source.

It might be ok, but needs to be looked at to see if using pin 1 and 0 as a input or output with external components, what the effect of the USB serial connect might have on it.

Lefty

"I propose having a Serial.end() method.."

This would be a welcome feature from my point of view.

Is it true however that both pins (TX/RX) then will be fully functional? E.g. I see from the Duemillanove reference schematic that the FTDI chip is hardwired to Tx/Rx and I assume then that the Rx line will be pulled low as opposed to floating even though USB may be disconnected, but I'm more that happy to be corrected on this assumption.

To take this a step further, could the SerialEnd function be expanded to free up either Tx or Rx if the serial interface is used only for one-way communication (send or receive, but not both)?

If USB is connected, but only used partial (send or receive only) or not at all - will it still be possible to use one or both of Rx/Tx for general purpose IO?

I also think a destructor for the hardware serial class would be nice.

Coincident with the disappearance of wiring_serial.c a problem has come up with baud rate setting on some 8 MHz Arduino Pro boards.

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1245508129

I can see what the register programming error is but can't find where to correct it. Does anyone have any insight into this?

A destructor isn’t a good idea, because the Serial objects are created in a separate object file and the call to re-create them has to be made from within HardwareSerial.cpp because of local variables. It’s probably best to just add a end() to the HardwareSerial class and beef-up the begin() to be more error resistant.

I’ve been using a different Serial.begin() which fully sets up the serial port. The Arduino library leaves most of the previous settings intact and trusts that they were sane(which just bit those 8Mhz people with the U2X bit).

inline void serialOpen(const uint32_t baud=9600, const uint8_t char_bits=8, const uint8_t stop_bits=1, const uint8_t parity=0);

void setup() {
        serialOpen(9600);
}

void loop() {
        Serial.print("Hello ");
}

// open a serial port, same as "Serial.begin()"
//  parameters:
//    - baud rate ( 1953 to 1000000 baud for 8Mhz, 3906 to 2000000 for 16Mhz )
//        default = 9600
//    - character/data bits ( 5, 6, 7, or 8 )
//        default = 8
//    - stop bits ( 1 or 2 )
//        default = 1
//    - parity ( 0 = none, 1 = odd, 2 = even )
//        default = 0
inline void serialOpen(const uint32_t baud, const uint8_t data_bits, const uint8_t stop_bits, const uint8_t parity) {
        uint16_t baud_setting;
        uint8_t frame_format;
        
        // U2X mode is needed for baud rates higher than ( Hz / 16 )
        if( baud > F_CPU / 16 ) {
                // U2X mode allows for baud rates up to twice as fast as regular mode
                
              // enable U2X mode
              UCSR0A = 1 << U2X0;
                
                // U2X style ubbr calculation
                baud_setting = ( F_CPU / 4 / baud - 1 ) / 2;
        } else {
                // regular mode makes up for it's lower baud-rates by handling clock skew better
                
              // disable U2X mode
              UCSR0A = 0;
                
                // regular style ubbr calculation
                baud_setting = ( F_CPU / 8 / baud - 1 ) / 2;
        }
        
        switch(data_bits) {
                case 5: frame_format = 0; break; // 5 data bits
                case 6: frame_format = (1<<UCSZ00); break; // 6 data bits
                case 7: frame_format = (2<<UCSZ00); break; // 7 data bits
                default: frame_format = (3<<UCSZ00); // 8 data bits
        }
        
        if(stop_bits==2) frame_format |= (1<<USBS0);
        
        switch(parity) {
                case 1: frame_format |= (3<<UPM00); break; // odd parity
                case 2: frame_format |= (2<<UPM00); break; // even parity
                default: ; // disabled parity
        }
        
        // assign the baud_setting, a.k.a. ubbr ( USART Baud Rate Register )
        UBRR0H = baud_setting >> 8;
        UBRR0L = baud_setting;
        
        // enable the serial recieve pin, transmit pin, and recieve interrupt
        UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
        
        // Set frame format
        UCSR0C = frame_format; 
}

This doesn’t support some of the more exotic features of the arduino serial port like synchronous-mode and 9bit characters, but it exposes pretty much every feature I ever use.

As a side benefit, I have a lot fewer assignments to volatile addresses. Assignments to addresses that are volatile and in extended-IO space are what will make the final code bigger.

i.e.:

        UCSR0B = (1 << RXEN0) | // enable the serial recieve pin ( D0 )
                 (1 << TXEN0) | // enable the serial transmit pin ( D1 )
                 (1 << RXCIE0); // enable the serial recieve interrupt
  de:   88 e9           ldi     r24, 0x98       ; 152
  e0:   80 93 c1 00     sts     0x00C1, r24
        UCSR0B |= (1 << RXEN0) ;
  aa:   80 91 c1 00     lds     r24, 0x00C1
  ae:   80 61           ori     r24, 0x10       ; 16
  b0:   80 93 c1 00     sts     0x00C1, r24
        UCSR0B |= (1 << TXEN0);
  b4:   80 91 c1 00     lds     r24, 0x00C1
  b8:   88 60           ori     r24, 0x08       ; 8
  ba:   80 93 c1 00     sts     0x00C1, r24
        UCSR0B |= (1 << RXCIE0);
  be:   80 91 c1 00     lds     r24, 0x00C1
  c2:   80 68           ori     r24, 0x80       ; 128
  c4:   80 93 c1 00     sts     0x00C1, r24

Once the compiler finds that everything is really a constant, the serial.begin() should really only be assignments.