arduino / ArduinoCore-megaavr

Arduino Core for the ATMEGA4809 CPU
103 stars 62 forks source link

SoftwareSerial sends illegal characters after initialization #132

Open thorv opened 7 months ago

thorv commented 7 months ago

I am using Arduino nano every, Board lib: Arduino megaAVR Boards 1.8.8.

I tried the following program:

#include <SoftwareSerial.h>

SoftwareSerial ser(4,5);

void setup() {
  Serial.begin(9600);
  Serial.println("START");
  Serial1.begin(9600);
  ser.begin(9600);
  ser.println("ABC");
  delay(10);
  ser.println("DEF");
}

void loop() {
  while(Serial1.available()){
    Serial.write(Serial1.read());
  }
}

The expectation is that "START(CR)ABC(CR)DEF(CR)" will be displayed, but what I actually got was the following:

START
P��H�DEF

This works fine:

void setup() {
  Serial.begin(9600);
  Serial.println("START");
  Serial1.begin(9600);
  pinMode(5,OUTPUT); //add
  digitalWrite(5,HIGH); //add
  ser.begin(9600);
  ser.println("ABC");
  delay(10);
  ser.println("DEF");
}

Looking inside SoftwareSerial.cpp, it looks like the following.

void SoftwareSerial::setTX(uint8_t tx)
{
  // comments omitted
  digitalWrite(tx, _inverse_logic ? LOW : HIGH);
  pinMode(tx, OUTPUT);
  _transmitBitMask = digitalPinToBitMask(tx);
  uint8_t port = digitalPinToPort(tx);
  _transmitPortRegister = portOutputRegister(port);
}

and in wiring_digital.c

void digitalWrite(uint8_t pin, PinStatus val)
{
    // omitted
    if(port->DIR & bit_mask){

        /* Set output to value */
        // omitted

    /* Input direction */
    } else {
        /* Old implementation has side effect when pin set as input -
        pull up is enabled if this function is called.
        Should we purposely implement this side effect?
        */

        /* Get bit position for getting pin ctrl reg */
        uint8_t bit_pos = digitalPinToBitPosition(pin);

        /* Calculate where pin control register is */
        volatile uint8_t* pin_ctrl_reg = getPINnCTRLregister(port, bit_pos);

        /* Save system status and disable interrupts */
        uint8_t status = SREG;
        cli();

        if(val == LOW){
            /* Disable pullup */
            *pin_ctrl_reg &= ~PORT_PULLUPEN_bm;

        } else {
            /* Enable pull-up */
            *pin_ctrl_reg |= PORT_PULLUPEN_bm;
        }

        /* Restore system status */
        SREG = status;
    }

}

MegaAVR's digitalWrite() does not change the OUT register of PORT when pinMode is not OUTPUT (operation is different from UNO r3 etc.). However, SoftwareSerial's setTx() function is written to set the OUT register to H level using digitalWrite() before setting pinMode to OUTPUT. As a result, the OUT register is not set, the output becomes L, and it seems that the appropriate output for the UART is not obtained.