PID Line following code size

I didn’t want to take the original thread off topic, and it is a bit old.

5628 bytes for PID line following? Sure, there should be a little library overhead, but that seems like an awful lot.

For reference, I’ve programmed a full PIC 16F882 coprocessor. You control is serially, and it drives a TB6612FNG with multiple different control schemes, controls 11 analog inputs, all of which can be read directly, and 5 of which are setup to return a digital 1/0 compared to a calibration value. As an odd useful bit, it also does it’s own PID line following routine, based off of the 5 digitized bits of line array data. The entire code is 3033bytes, or 1733 (14bit) words. All of the PID variables are signed ints, so the (8bit) chip is doing 16bit math.

When you say you stripped out all of the LCD and buzzer code, do you mean from the example? or the entire project (libraries + user code)?

Hey Darth Maker! It’s good to see you back on the forum.

Here is an AVR Studio 5.1 project based on 3pi-linefollower-pid that I made to demonstrate the ~5 KB figure I am getting:
minimal-linefollower-pid.zip (58.8 KB)

I removed all the buzzer and LCD code from the user code file (test.c). Since the library buzzer and LCD code is not getting called, it does not get linked into the final executable. I did not modify the Pololu AVR C/C++ Library; I just used the latest official version. When I compiled the project in AVR Studio 5.1, it reported that 5298 bytes of program space are being used. You can look at the .map file (which is included in the download) to see exactly how that space is being used.

It sounds like you certainly managed to fit an impressive number of features into less than 3 KB. Part of the reason why our code is larger is because the QTR sensors library is a general library that can be configured to use any set of pins. It uses malloc and free to manage the memory for the sensors. Those functions are linked in from AVR Libc and take a total of 614 bytes. There are probably other examples where the code can be optimized a lot if you know in advance exactly the code will be used for.

With 32 KB microcontrollers readily available, I wouldn’t worry about it too much, but I’d be interested to hear what you think about the AVR Studio project.

–David

I’ve not forgotten about the thread. I had to download and install AVR Studio 5, and I’m pretty sure I lost my first born child and a few siblings to the agreements. Then I had a very busy couple days.

I looked at the .map file, but I don’t know how to read it, so it does me no good.

Looking at the project dependencies, it would appear that a couple unneeded libraries are still being included by Pololu3pi.h.

#include "../OrangutanAnalog/OrangutanAnalog.h"
#include "../OrangutanBuzzer/OrangutanBuzzer.h"
#include "../OrangutanMotors/OrangutanMotors.h"
#include "../OrangutanLCD/OrangutanLCD.h"
#include "../OrangutanLEDs/OrangutanLEDs.h"
#include "../OrangutanPushbuttons/OrangutanPushbuttons.h"
#include "../OrangutanTime/OrangutanTime.h"
#include "../OrangutanSerial/OrangutanSerial.h"
#include "../OrangutanServos/OrangutanServos.h"
#include "../PololuWheelEncoders/PololuWheelEncoders.h"
#include "../OrangutanResources/OrangutanResources.h"
#include "../OrangutanDigital/OrangutanDigital.h"
#include "../OrangutanPulseIn/OrangutanPulseIn.h"
#include "../OrangutanSPIMaster/OrangutanSPIMaster.h"

The 3pi should have no need of the servo, encoder or SPI libraries, right? I can see how having that many libraries could quickly add up in memory usage though.

Hello Darth,

Including a file should not cause any code to be included from a library. In fact, the way we recommend compiling things for AVRs, only the functions that are actually used will be compiled into the final hex file.

-Paul

Yep, you’re right… I think the only Orang library used is pushbuttons.

I tried to change the three refences to the pushbutton library to direct hardware control. It doesn’t work, and I don’t know why.

...
	pololu_3pi_init(2000);
	
	DDRB &= 0b11101111;
	PORTB |= 0b00010000;
	delay_ms(5);

	//while(!button_is_pressed(BUTTON_B))
	while(!PINB4)
	{
		delay_ms(100);
	}

	// Always wait for the button to be released so that 3pi doesn't
	// start moving until your hand is away from it.
	//wait_for_button_release(BUTTON_B);
	while(PINB4);
	delay_ms(1000);

...

	//while(!button_is_pressed(BUTTON_B))
	while(!PINB4)
	{
		delay_ms(100);
	}
	//wait_for_button_release(BUTTON_B);
	while(PINB4);
}

I’m pretty sure that it almost works, I just don’t know enough about AVR programming to make it work. But that change saves over 500byte of memory.

It seems to me that the pushbutton library could be changed to much simpler macros that would use less space.

A quick look at Pololu3pi.cpp show that it’s basically just a wrapper for the Orang line sensor array. Unless the compiler optimizes it away, it mostly just takes up space. You guys don’t market the 3pi to noobs, so directly using the QTR library would seem to make more sense to me.

It might not be worthwhile to make the changes now, after it being that way for so long. At the same time, there are a lot of people (myself included) who would still like Arduino to fix the odd pin spacing, even though it would break compatibility with current shields.

This line doesn’t work in avr-gcc, because PINB4 is just a bitmask:

while(!PINB4)

Instead, my preferred method is this:

while(!(PINB & (1<<4)))

Also, I think you’ve got a parity error. The line will go low when the button is pressed and high when it is not pressed.

I think most of our customers never come close to using up the 32 KB available on the 3pi, and the Pololu3pi.cpp library makes the example code a lot simpler, so it’s nice to have.

–David

So it finally works. Thank you David. Just that change added enough to the program to make it hardly worth the savings over the library. But there are really no identifiers for the individual bits in the port (or other) registers?

Also, I realized that while the 328p is advertised to have 32K bytes, each instruction is either 16 or 32 bits. So it’s really something less than 16K words/instructions.

People really need to take a better look when comparing AVR to PIC. Too often chips such as the mega328p are compared to PIC16Fs. 16K words is the largest memory size available in a 16F, and those three chips were released in the last year. The comparisons need to be to either PIC18Fs, or even 16bit PIC24s. Sorry for the off topicness.

The header file avr/io.h defines pin numbers (e.g. PINB4 = 4) that you could use like this:

PINB & (1<<PINB4)

Of course, the compiler won’t notice if you accidentally try to use a pin number with the wrong register, so I don’t think it’s too useful.

The PIC18Fs are nice and we use them in a lot of our products, but I’m not aware of any free, optimizing C compiler for them. Microchip’s C18 either disables optimizations or requires you to pay. What compiler are you using, or are you just writing in assembly?

–David

I actually haven’t used 18Fs myself. It was just a side thought about the common hardware comparisons between AVRs and PICs.

The lack of open source C++ compilers is the major problem with PICs in general. From what I understand though, PIC18 could handle a port of GCC much better than PIC16. I actually have porting or creating a GCC++ variant for PIC on my long/very long term to-do list (after I learn how to compile a compiler in general).

For PIC16 I use the free (reverse optimizing) version of Hi-tech C. I can read assembly, but I don’t think it’s worth the time it takes to write it. The MPLAB C compilers are all based on GCC, so if you can compile it yourself, it’s available. You should get reasonable optimization too.

Along those lines, if know of any good resources for compiling compilers, I’d be grateful.

Hi Darth,

Regarding the space taken up by the library: from looking at the map file, Pololu3pi uses about 150 bytes of program space, and OrangutanPushbuttons takes another 160. You could just stick all of the stuff being done by those libraries into main() and save some of that space, but it is probably not worth making the code more complicated just to save 100 bytes or so. Most of the space (2648 bytes plus ~600 for malloc()) is taken up by PololuQTRSensors, but that is because the library does a lot of stuff in a general way.

-Paul

Maybe you’re thinking of Microchip’s compilers for the PIC24. For the PIC18, the only options I know about are the HI-TECH compiler and C18 (are there more these days?).

C18 is definitely not based on GCC and you can’t get the source code. One way you can tell it’s pretty different from GCC is that it supports a 24-bit integer type (short long!), which is pretty cool. Also, it has a procedural abstraction optimization that is very effective at reducing code size. However, there are a lot of differences that are less cool. Here are the notes we have from our company wiki:

  • No support for inline functions.
  • Inline assembly kills optimizations. Inline assembly, even really simple assembly, causes C18 to not do any optimizations in the rest of the function. Workaround: Put inline assembly in its own, small function.
  • Unused functions are linked in. Functions that are never used get linked to the final result as long as one function in that object is used. Workaround: Put optional functions in a separate C file.
  • Const does not work as it does in GCC. The only thing const means in C18 is that you can not assign to the variable. But const variables still take up space in RAM or ROM, and when you access them the compiler goes through unneeded trouble to read the variable from memory. Workaround: use #define instead of const.
  • Static parameters don’t work in Assembly. Static parameters can not be passed to functions written in MPASM because : is not a valid character in MPASM assembler’s labels; that’s the only reason it doesn’t work! See Section 3.2.3.1.2 of the C18 user’s guide.
  • Syntax error occurs if C file doesn’t end with a newline.

–David

Huh. I would have lost that bet. Even more reason to write a GCC++ compiler for PIC18.

There are a few other compilers out there for PIC18, including a C++ compiler.
http://www.sourceboost.com/Products/BoostCpp/BuyLicense/LicenseLimitations.html
As you can see, the free version is pretty harsh. On the other hand, it is much cheaper than Hi-Tech or C18.
There is also MikroC. http://www.mikroe.com/eng/products/view/7/mikroc-pro-for-pic/
I used the PIC24 version once. The free version is limited to 2K.

There is SDCC, which is an open source GCC port to several microcontroller series. I managed to compile something once, but I couldn’t figure out how to use the assembly file that it spit out (wouldn’t assemble through MPLAB’s assembler). SDCC “supports” PIC 16 and PIC18, though they refer to them by the core sizes of PIC12/14 (12 and 14bit cores) for 16fs and PIC16 (16bit core) for 18fs.
http://sdcc.sourceforge.net/ Main problem is there is no guide for how to use it, and very little user base.

I’ve always found that error to be quite funny. It doesn’t really make any sense.

Thanks for pointing out that SDCC has some kind of support for PIC18. You’ve got me thinking. On the SDCC website it says this support is “in progress”, but if it is usable then that would allow us to make a user-programmable PIC18 product.

–David

If you start something up with it, I’d love to help Alpha/Beta test it for you. At some point in the next two years I’ll probably be taking a college course that involves compiler design. That is when I’m most likely to have success with porting GCC++.

Here is a guide and links to guides for setting up SDCC and the required assembler for Linux. http://www.micahcarrick.com/pic-c-programming-linux.html

Hello again.

We now have a user-programmable PIC18 product, the P-Star 25K50 Micro. You can program it with the free version of Microchip’s XC8 compiler.

–David