Improving the base software

I don't know what the plans and timing for arduino-0008 are, but I've made some observations about the base software (targets/arduino and targets/libraries) and think they can be improved upon. From reading the disassembled code and measuring the time of various operations I can suggest at least the following: - Make pin data stay in flash, saves 130 or so bytes of SRAM. - Recode pin data access functions, digitalRead and digitalWrite, saves ~400 bytes of flash, digitalWrite() is nearly three times as fast. - Make delayMicroseconds and pulseIn be independent of CPU clock rate. - Fixup SoftwareSerial to be clock rate independent and generally better timed. You can now expect it to print without any garbage characters coming out. - Expose the useful bit of API for people that need to get port and bitmasks for sub-microsecond accuracy on their digital IO. - Fix delay() so it really will delay about 1ms if you do a delay 1. It could come back much sooner than you expect now.

The danger in this is that code which works under 0007 might stop under 0008 as the underlying functions get faster. People's hand crafted fudge factors would need to change. Also, there are other gains to be had that might have a minor change in semantics, such as requiring people to call pinMode() when they want to return a pin to digital use after having been used as an analog output.

On the other hand a fair bit of the help requests on the forum are for people with timing problems and people that are probably running out their SRAM and tightening up the cpu use of IO functions and RAM use could keep more of them out of trouble in the first place. (Also I was planning to make and study a native USB interfaced Arduino and had to get down to 12MHz which started me down this path. I suspect the USB requirements will be too limiting of what else can be done, but I'll see.)

Make delayMicroseconds and pulseIn be independent of CPU clock rate. Fixup SoftwareSerial to be clock rate independent

These are inherently and unavoidably a [u]function[/u] of the CPU clock rate.

I assume you mean make it possible to tell the arduino functions (perhaps via a function call or a #define) what the actual clock rate is, in the event you want to change the default?


Yes. Some functions look at F_CPU (from your preferences.txt file) in 0007 and some don't, so you can sort of use different clocks depending on what you are doing.

Recode pin data access functions, digitalRead and digitalWrite, saves ~400 bytes of flash, digitalWrite() is nearly three times as fast.

I was having some trouble with errors while using tight loops of digitalRead/write, so I wrote a patch against wiring.c to speed those functions up slightly. It uses a switch and some inlining to cut down on comparisons and function overhead respectively.

Its a start? (Keep in mind that it has only been tested for a few pins.)

Another thought that has come out of this project is that since delayMicroseconds() disables interrupts, its not safe to use in a lot of cases.

How about adding a delayMicrosecondsLazy() that is exactly the same, except it leaves interrupts enabled? You at least guarantee a minimum wait time.

I'll have to look at using a switch() there, I was under the impression that it was bigger and slower, but I didn't test it. I'd like to push that hunk of code out entirely to the pinMode() call, but that would be a semantic change.

delayMicroseconds() is a funny call. I enhanced its interrupt function, it now restores the interrupt flag instead of clearing, very handy if you already had them off, like you need to do for the SoftwareSerial class and a lot of IO functions. I suppose a delayMinMicroseconds() could have a place. As long as you don't turn off the interrupts for more than 16000 clocks or so you won't mess up the millis() counter, I think. It is my understanding that the interrupt will fire as soon as you enable them, but that they don't queue, so you can't let more than 1 accumulate.

Timing in general is hard enough that I wonder if it wouldn't be a good idea to let timer1 run at full clock speed. That would slow the millis() overflow interrupt. The millis call could then use its interrupt count and the TCNT1 number to compute. Likewise delay() and delayMicroseconds() could watch the counter register. There is a probably a good reason not to do this, the PWM rate was changed in 0007 or somewhere around there, but I don't know why.

Hey, some very good suggestions here. Many of these are things that weren't addressed earlier because we were focusing on things that were more useful for beginners. But now that we're getting more advanced users who not only care more about the precise behavior of the libraries but are starting to contribute patches, etc. (thanks guys!), it seems like a good idea to start fixing some of these things. You can see the current list of bugs/priorities at:

Keeping the pin data in flash is a clear win. It's just a question of adding the correct modifier to the declaration, right?

Way back, there were some people having problems when they tried to switch between using a pin as an analog output (PWM) and as a normal digital output, which is why the code was added to automatically turn off the PWMs in digitalWrite(). This is open for change, especially because it would be good to make digitalWrite() faster. We'd need to decide whether or not analogWrite() would still automatically enable PWM, or if there would be an PWM or ANALOGOUT or whatever parameter to pinMode(). Also, code that depends on the timing of digitalWrite() and digitalRead() (like the SoftwareSerial library) would have to be adjusted.

We should make all the code independent of CPU speed. Because most people are using Arduino with a 16 MHz clock, and because those who don't tend be advanced enough to work around it, this hasn't been a priority. However, if someone's willing to make the changes and (more importantly) test them, I'd be happy to incorporate them into the code. Especially because supporting more hardware is a long-term goal for the Arduino software.

Any improvements to the SoftwareSerial library would be much welcomed. I spent some time fighting with it, but didn't make much progress.

Which parts of the internal API would be useful to expose?

Fixing up the delay() would also be nice. It hasn't been a priority because most people using it don't seem to need much accuracy, but of course, it would be nice if it worked better.

I'd really rather not have two delayMicroseconds() functions. My reason for disabling interrupts within it is that the function should provide as much accuracy as possible (not that it necessarily does right now). Again, anyone who needs a delay that doesn't disable interrupts is probably an advanced user and could probably code up their own function. Alternatively, if there's a way to leave interrupts alone, but still get good accuracy, that would be the best.

The PWM rate was reduced because the old rate of ~30 KHz was too fast for driving motors (they or the H-bridge tend to overheat). The current rate is a compromise of being slow enough to drive motors but fast enough to fade LEDs. I'd like to add a function to set the frequency, though this might make it even harder to relay on timer1 for things like delay().

So please, keep pushing (or patching) these issues, as I'm mostly focusing on more beginner-targeted issues at the moment (e.g. rewriting the examples, automatically detecting the MCU, etc.)

The API internals to expose are the ones to let you get from a pin number to the ports and bit mask. Those are half exposed now.

Making variables stay in flash is just adding a PROGMEM to their declaration, but then you can’t access them directly anymore and must use the pgm_read_byte() and similar functions from <avr/pgmspace.h>. This is also handy for the serial code, you can force the string constants to stay in flash and still print them by adding one more print method.

What is the preferred way to send in changes? I see a “patch” handler in the developer site. Is sending patches to the svn head the best way to do that.

I’ll redo my work so far against the svn head, but the first patch is going to be fairly large. It replaces the guts of the pins file and touches all the functions in wiring.c that access pin data. After that one they can be smaller, more focused patches.

Patches against svn head are the way to go. For now, just email them to dam at mellis dot org. It's probably not the best technique, but if it's good enough for Linus, I suppose it's good enough for me. If it gets to be a problem, I may start using the patch handler on berlios that you mentioned.

I agree with jims about making the port/pin/ddr accessible, and also for port C. As I understand, there is no way to do digital I/O on port C using pinmode and digitalread/write - you have to know the PORT, PIN and DDR and use sbi and cbi.

I've written a software i2c bit-bashing library, and I'd like a user to be able to configure it to use any pins at all. Since Analog 4 and 5 are used by hardware i2c, it's convenient to test using the same pins for software i2c.

I see how jims does this configuration in the OneWire lib, but don't see any way to do it for Port C. One ugly but workable possibility is to map digital pins 14-19 onto analog 0-5, so you could do all the same stuff with port C.

IMHO, Arduino is a beautifully simple way to get started with the AVR microcontrollers, and there are very few obstacles to getting it to do anything!


I don’t know where it will end up, but in the patch I sent to mellis I added the 6 analog in pins of port C to the pin data structures so they can be used as digital pins if needed.

Sounds great, jims! (BTW - I'm playing with your OneWire lib, and trying out some other nifty Onewire devices - great work!)