Confliction between PololuWheelEncoders and OrangutanPulseIn

Project:
I am making a radio controlled bristlebot, and planning to use a Baby Orangutan to read in 3 PWM signals, and use them to control 2 rotary encoded motors. I must say I am impressed with your libraries and the Orangutan, as I was able to get software installed along with positional motor control working within an evening. However, when I tried adding code for reading PWM, I encountered an error while building the project. I simplified the code to narrow down the problem.

Hardware:
Using the Baby Orangutan B-328

Setup:
I’m using Atmel Studio 7 (7.0.1188). I created the project using “File -> New Project” and selecting “Baby Orangutan B-328”

Problem:
This code in main.c produces an error while compiling or linking.

#include <pololu/orangutan.h>

//-------------------------------------------------------------------------------------
int main()
{
	encoders_init(IO_B0, IO_B1, IO_B2, IO_B3);

	unsigned char pulseChannels[] = {IO_D0, IO_D1};
	pulse_in_start(pulseChannels, 2);
}

This is the error:

Error		multiple definition of `__vector_3'	orangutan_app1	C:\home\david\libpololu-avr\src\PololuWheelEncoders\PololuWheelEncoders.cpp	256
Error		multiple definition of `__vector_4'	orangutan_app1	C:\home\david\libpololu-avr\src\PololuWheelEncoders\PololuWheelEncoders.cpp	256
Error		multiple definition of `__vector_5'	orangutan_app1	C:\home\david\libpololu-avr\src\PololuWheelEncoders\PololuWheelEncoders.cpp	256

Question:
What is the best way to fix this problem? I searched for similar issues, and it seems it may be conflicts with interrupts. If this requires modifying library source code, what changes should I make, and how would I recompile it most easily?

Thanks!

Hello.

Thank you for your interest in the Baby Orangutan, and for the detailed and organized explanation of your problem.

Yes, the OrangutanPulseIn and PololuWheelEncoders components of the Pololu USB AVR C/C++ Library define their own interrupt service routine (ISR) for the pin-change interrupts on the AVR. The AVR can only have one ISR for each interrupt, so these libraries conflict.

Library modifications

You can probably make your program work by modifying the library. I would recommend that you change the ISRs in OrangutanPulseIn.cpp and PololuWheelEncoders.cpp to just be normal functions instead of real ISRs. You would have to get rid of all the lines that refer to interrupt vectors PCINT0_vect, PCINT1_vect, PCINT2_vect, and PCINT3_vect. Also, define your ISRs with extern "C" so you can call them from your C program. So the ISR definition in each file would look something like:

extern "C" void pulse_in_isr(void)
{
    // ISR code here
}

Then, in main.c, you need to define an actual ISR that calls these functions. Something like this should work:

void pulse_in_isr(void);
void encoders_isr(void);
ISR(PCINT0_vect)
{
  pulse_in_isr();
  encoders_isr();
}
ISR(PCINT1_vect,ISR_ALIASOF(PCINT0_vect));
ISR(PCINT2_vect,ISR_ALIASOF(PCINT0_vect));

Compiling the modified library

We usually compile the library on Ubuntu using Ubuntu’s AVR GCC cross compiler and standard tools like Bash and GNU Make. However, I think it will be easier for you to instead just copy all the library files that you need into your own Atmel Studio project and compile them as part of that project. You should not use our templates; you would need to make a new project by selecting File -> New Project -> C/C++ -> GCC C++ Executable Project. This kind of project can have both C and C++ source files in it. I recommend that you add your main.c file to it, try to build the project, and then fix compilation and linker errors by adding needed .cpp and .h files from the source code of the library to your project until everything builds. If you have some familiarity with C or C++ already, I think the errors will not be too hard to fix, and you can post the errors here if you need help.

While you are developing this, it might be good to move or rename the “pololu” folder in Atmel Studio’s AVR GCC toolchain so that you can make sure that you are using your own copies of the Pololu AVR C/C++ Library header files instead of using the main library’s header files. However, since you don’t need to modify any of those header files, it will not be too bad if you accidentally use them. The “pololu” folder that gets installed by our library in Atmel Studio’s toolchain can be found here, assuming you installed Atmel Studio in the default location:

C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\include\pololu

ISR performance

Since your new ISR would call functions in other source files, the compiler might be forced to save/restore many registers that are not actually used, and this would slow down the ISR. There is a good chance that your application will work fine anyway, but if you want to speed up the ISRs, you might consider enabling GCC’s link-time optimization (LTO) feature. However, I have not looked into whether link-time optimization is actually supported in Atmel Studio. (It is supported in the Arduino IDE, another environment for AVR programming that is compatible with the Baby Orangutans.)

–David

Ok, I’ve done this and got it compiling with no errors, though I can’t test it on the device for now. I looked and didn’t see the Link-time optimization. Maybe I can combine the PololuWheelEncoders and OrangutanPulseIn files as an alternate solution. With this solution of combining ISRs, it appears to me that we will be running extra code on interrupts. Is the code prepared to handle this? Is this because we are limited in our number of interrupts?

Thanks for the help.

I am glad you were able to get it to compile.

There is a good chance that the extra overhead in the ISR will not make a noticeable difference in your application, so I would recommend testing the code first before you try to optimize it.

It looks like there are 12 extra registers that avr-gcc stores on the stack when it is calling an opaque function in an ISR. Each register takes 4 cycles to save and restore, so that is 48 cycles, or 2.4 microseconds. There will also be a few cycles of overhead to call the functions and return from them.

Even though Atmel Studio does not have a feature for link-time optimization, the compiler they are using does. I was able to build a program with LTO today in Atmel Studio 7.0.1006 by adding the -flto command line argument to Project Properties -> AVR/GNU C Compiler -> Miscellaneous -> Other flags. I also had to copy a file named specs-avr5 from one part of Atmel Studio to another so that GCC’s lto-wrapper program could find it. Since it did not work out of the box, I would consider Atmel Studio’s LTO to be an experimental feature.

Yes, you could combine PololuWheelEncoders and OrangutanPulseIn into one file, or you could make some private variables for the libraries be globally visible so that you can copy all the code for those ISRs into the ISR you defined.

Yes, by combining the ISRs likes that, they are going to run more often than they normally would. The encoder ISR will run whenever there is a change in your PWM input pins, and the PWM ISR will run whenever there is a change in your encoder input pins. The ISR code can handle that.

If you want to avoid running the extra code, you might consider changing your pin assignments to make sure that there is no pin-change interrupt vector that handles events for both the libraries. For example, you might put all of the encoder inputs on PCINT0_vect and all of the PWM inputs on PCINT1_vect if that is feasible. The mapping between pins and pin-change interrupts is documented in the “External Interrupts” section of the ATmega328P datasheet.

–David

I had the time to give this all a shot this weekend, after fixing many other small bugs in my code. It all went pretty smoothly and is working well. I just have a few notes for others that may want to do something similar.

The initialize for OrangutanPulseIn clears the initialization of PololuWheelEncoders. Just initialize OrangutanPulseIn first, then PololuWheelEncoders, and they will both work fine.

I used PD0, PD1, and PD2 for my PWM in pins. For some reason my PD1 didn’t work, and I’m not sure why, so I just switched to using PD7, and all 3 channels worked without issue.

My update loop tested at around 500 cycles per second, and I only need 50 per second, so I didn’t need to bother with doing any micro optimizations, or try the link time optimization.

Thanks for all your help with this. I am impressed by the knowledge and helpfulness of Pololu. I’ve also got to commend you on your libraries, as I was able to implement everything I needed, while keeping my code clean, and operating on a high level. I’m on to designing the mechanics of this project now that all the electronics and code are sorted out.

I am glad you were able to get your program working.

On the Baby Orangutan, pin PD1 controls the red user LED and it is also the serial transmit (TX) pin for the AVR’s UART. If you are using the red LED in your program or using the AVR’s UART, that would explain why PD1 did not work for PWM input.

–David