SpenceKonde / megaTinyCore

Arduino core for the tinyAVR 0/1/2-series - Ones's digit 2,4,5,7 (pincount, 8,14,20,24), tens digit 0, 1, or 2 (featureset), preceded by flash in kb. Library maintainers: porting help available!
Other
557 stars 145 forks source link

Pin Interrupts are triggering a compile error when using the Software Serial library. #299

Closed castingflame closed 3 years ago

castingflame commented 3 years ago

MCU: ATtiny3216 megaTiny library version: latest: 2.2.3

I need to change my approach on my project to use Pin Interrupts as show here; https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/PinInterrupts.md

I modified the code slightly to test it on my own board and it works fine. The problem arises when I add the Software Serial library. I get the following error;


_*WInterrupts.c.o (symbol from plugin): In function attachInterrupt

Error linking for board ATtiny3216/1616/1606/816/806/416/406 (.text+0x0): multiple definition of __vector_4 ISR.cpp.o (symbol from plugin): (.text+0x0): first defined here

collect2.exe*: error: ld returned 1 exit status Debug build failed for project 'ISR'**_


Ive researched for quite a few hours but i'm going round in circles. It seems to be an issue with the ISRs being declared in my code and again in the SoftwareSerial. I have looked at at the SoftwareSerial.o and .cpp libraries but I am completely lost.

I have looked at using AltSsoftSerial and NeoSoftSerial but both cause different errors.

Here is my version of the code, but the same applies to the demo code;

#include <SoftwareSerial.h>

#define LED             13          //PC3       15      LED Data
#define ResetBtn        4           //PB5       6       Reset button (AL) 
#define DriveBtn        5           //PB4       7       Drive button (AL)
#define M1Btn           16          //PA3       19       M1 button (AL)

volatile byte interrupt1;
volatile byte interrupt2;
volatile byte interrupt3;

void setup() {

    pinMode(ResetBtn, INPUT_PULLUP); //Ard 4  PB5
    pinMode(DriveBtn, INPUT_PULLUP); //Ard 5  PB4
    pinMode(M1Btn, INPUT_PULLUP);    //Ard 16 PA3

    PORTB.PIN4CTRL = 0b00001011; //PULLUPEN=1, ISC=3 trigger falling edge - Drive button
    PORTB.PIN5CTRL = 0b00001011; //PULLUPEN=1, ISC=3 trigger falling edge - Reset button
    PORTA.PIN3CTRL = 0b00001011; //PULLUPEN=1, ISC=3 trigger falling edge - M1 button

    Serial.begin(9600);
    delay(10);
    Serial.println("Startup");
}

void loop() {
    if (interrupt1) {
        interrupt1 = 0;
        Serial.println("ISR 1 fired - Reset Pressed");
    }
    if (interrupt2) {
        interrupt2 = 0;
        Serial.println("ISR 2 fired - Drive Pressed");
    }
    if (interrupt3) {
        interrupt3 = 0;
        Serial.println("ISR 3 fired - M1 Pressed");
    }
}

ISR(PORTB_PORT_vect) {

    byte flags = PORTB.INTFLAGS;
    PORTB.INTFLAGS = flags; //clear flags
    if (flags & B00100000) { //INTFLAGS bit position 5 (Reset) 
        interrupt1 = 1;
    }
    if (flags & B00010000) { //INTFLAGS bit position 4 (Drive)
        interrupt2 = 1;
    }
}

ISR(PORTA_PORT_vect) {

    byte flags = PORTA.INTFLAGS;
    PORTA.INTFLAGS = flags; //clear flags
        if (flags & B00001000) { //INTFLAGS bit position 3 (M1)
            interrupt3 = 1;
        }
}
SpenceKonde commented 3 years ago

Why the hell are you including software serial when you are only using the hardware serial port?!

That said - this is one of the many well-known drawbacks of SoftwareSerial - in order to support using any pin as RX (since there isn't a graceful way to include only the ISRs that are used - the compiler doesn't have any awareness of how this works - all it knows the "rules" for volatile registers (all peripheral registers)m and thart ISRs are marked (used) since there's no entry point that's visible to the compiler. So if you have a library like software serial that wants to be able to do an interrupt on any pin (meaning it needs every port pin interrupt vector)... nobody else gets to have any pin interrupts if they're using it!

And yes, this f'ing sucks.

Actually, software serial implementations in general f'ing suck.

I avoid them whenever possible... (in fact, wanting an ATTiny with 2 serial ports - the ATtiny841 and 1634 that worked with Arduino was what got me into the business of maintaining core libraries!) Though like, one of the things on my list (god known when) is to throw together a software serial implementation that is at least completely and utterly horrible like this one is; it's like... pretty basic exercise in AVR assembly (I always wondered why the arduino software serial implementation had such a lousy rep, but I didn't use it, so I never investigated. As I was getting into inline assembly lately, I got curious and decided to look at their implementation. Which is when I discovered they did it in C... It's like when you see on slow news days that have this guy guy who made some mcguyver/goldberg contraption that's both impressive for being able to do what it does, considering what it is - but also an absurd and inefficient way of doing that.

Anyway, it's almost of a right of passage for inline assembly programmers on embedded devices, and ATTinyCore's builtin software serial has serious defects, while, the only one I'm aware of for AVRxt (megaTinyCore/DxCore/MegaCoreX) that's Arduinable is... well, you've been playing with it, how do you like it? :-P

castingflame commented 3 years ago

Thanks for responding Spence.

"Why the hell are you including software serial when you are only using the hardware serial port?!"

You can't possibly think the above code is my complete project can you? As I said "I need to change my approach on my project to use Pin Interrupts .. I modified the code slightly to test it on my own board and it works fine."

This is just a version of your example code for my hardware. The Software Serial library is just added to the top as it was causing the problem and the error is reproducible if someone ran the above code.

Why use software serial? I have no choice but to use Software Serial as I am already using up the hardware serial on another port for something else along with i2c. I use all the pins of the 3216 and need the flash.

I did wonder if I could remove some of the ports that software serial is managing interrupts for but that would not help either as my software serial is on the same port as a button that needs a Pin Interrupt.

So I guess you are saying that this is a bust and I can't use the Pin Interrupts show above with the Software Serial. I guess I will have to go back to the drawing board.

Thanks again for your help.

SpenceKonde commented 3 years ago

Which pin needs to have software serial? also - board design out of your hands before assessment of available pin capabilities? That's.... something you want to try to avoid (and something that has been part of oh so many inquiries....

castingflame commented 3 years ago

Software serial is on Arduino 14,15 PA1,PA2

My buttons are on 4, 5,16 PB5, PB4, PA3

castingflame commented 3 years ago

Yes the board was is working fine. I only needed interrupts on one button but a request has come in which means all three buttons need interrupts. The standard Arduino method of attachInterrupts is tripping over itself with multiple interrupts so I was looking for something else hense Pin Interrupts.

I'll have to go back at look at the original method to see if it was just my poor approach that was the issue.

SpenceKonde commented 3 years ago

Heh, I don't suppose you can swap the hardware serial between them >.>

Actually... Hmm...

A proper software serial routine wouldn't be that bad to implement (and I suppose you have to receive there too, sadly? (you do know that one can only send or receive at a given time with software serial,, not both, right?)

castingflame commented 3 years ago

Software serial is connected to a Bluetooth serial module that is just sitting listening so I guess just receive as I do not initiate comms from the MCU side on software serial.

I can not swap hardware serial between them.

castingflame commented 3 years ago

I've now managed to sort it with the standards arduino method.

SpenceKonde commented 3 years ago

Wait a minute, I'd been thinking about a bunch of exotic ways to deal with the whole "I want to be able to use any pin, but I don't want to lose the ability to use interrupts" conflict... Then I realized there's a much less convoluted way (I think): If a software serial implementation required you to pick the ISR and call their function from it, would that be a good solution?

eg

#include <NovelSoftwareSerial.h>

mySerial NovelSoftwareSerial(MyTxPin,MyRxPin); 

// rest of sketch like normal...

ISR(PORTA_PORT_vect) {
  byte flags=PORTA.INTFLAGS;
  PORTA.INTFLAGS=flags;
  if (flags & 0b00000100) { //PA2
    mySerial.getch(); // read a char and put it in the buffer.
  } else /* test for other interrupts */
}

You'd still get data out of it with the available() and read() methods.

SpenceKonde commented 3 years ago

I think that would be pretty usable - and it comes with an even larger payoff for any parts other than the tinyAVR 0/1-series, because not only could you use the classic trick of an AC with the other side muxed to a voltage reference, turning it into an "extra interrupt source", those also let you interrupt on CCL transition...