Wednesday, December 2, 2015
NiMH battery discharge tests
A couple weeks ago my wife got a few ornamental Christmas trees lit by RGB LEDs. Each of them takes 3 AA batteries, I bought a pack of Amazon Basics batteries since they were the best deal I could find for good quality batteries, since they are OEM eneloop batteries. The ones I received were manufactured 201412, which suggests they are 4th generation eneloops. I used 3 of the Amazon Basics batteries in 2 of the trees, and 3 Rayovacs I had lying around for the third tree. After the batteries ran out, I tested their voltages. The Amazons were consistently in the 1V range, but one of the Rayovacs was completely drained, reading 0 volts, even after the 2nd fill charge/discharge cycle. The three Rayovacs looked similar, but the 0V battery was a bit different than the other two. After looking through all of my rechargeable batteries, I realized I had 3 different kinds; the "0V" battery shown in the top of the picture above, one labeled "pre-charged", in the lower part of the photo above, and a couple labeled "ready-to-use". None of them had a capacity visible anywhere on the battery.
While I was confident that the capacity of the regular "0V" Rayovac was less than the "ready-to-use", I wanted to get a better idea of the actual capacity of the batteries. I also have some 1st-generation eneloops "3UTG" I purchased over 5 years ago that I wanted to compare to the new Amazon Basics batteries. I don't have smart battery charger/tester, but with a bunch of AVR boards around I thought it would only take a couple dozen lines of code to time the battery discharge. Before I started, I decided to see if somebody had already made a battery discharge project I could use. It didn't take long to find Denis Hennessy's battery capacity tester. I'm not a fan of the Arduino core, but if it worked, it didn't matter much if the code isn't pretty.
After reviewing Denis' code, I determined it work work well with a couple small changes. The AVR board I already had plugged into my breadboard was running at 3.3V, and Denis' code assumes an AVR running at 5.0V for the ADC calculations. I changed the code to use a "VCC" define. Even for boards running at 5V, using the exact voltage instead of assuming 5.0 could give more accurate ADC readings. I also modified the code so the "test complete!" message is output to the serial log, since wasn't hooking up a 1602 LCD (the code works fine without the LCD or DS18B20 attached). The last change I made was making the serial log output every minute instead of every second. I connected a 3.7 Ohm (nominal 4 Ohm) 15W speaker in lieu of a power resistor, and single AA battery holder for the battery.
The 3.7 Ohm load means the discharge current will be in the 300-350mA range, which is a bit higher than typical battery-powered electronics projects, but still within manufacturer specs for AA NiMH discharge rates. After a couple days, I had tested 5 different batteries:
The battery capacity tester calculates joules rather than mAh, so I've done a rough calculation in mAh since that is the typical rating for NiMH batteries. The regular Rayovac (purple line) was about 1100mAh, with the pre-charged Rayovac (green line) around 1300mAh. The ready-to-use Rayovac (red line) was around 1350mAh, which matches the capacity specified for the new Rayovac "recharge" batteries. The 1st-generation eneloop is still doing pretty well at around 1900mAh, and the Amazon Basics give a very respectable 2050mAh.
My updates to the battery capacity tester were merged a couple days ago, so you just need to download the zip from Denis' github repository. If you are not hooking up the temperature sensor, you can comment out the OneWire and DallasTemperature references. You will need a copy of the Time library, which is now maintained by Paul Stoffregen.
Tuesday, November 17, 2015
DS18b20 temperature sensor calibration and correction
I recently bought a couple of DS18b20 temperature sensors in stainless steel probes. Although I prefer to do AVR development using the Atmel AVR toolchain, I decided to test out the temperature sensors using the Arduino IDE. I found a OneWire library that will use the AVR internal pullup resistor, so all I had to do was solder on some header pins and plug into one of my Pro Mini boards.
I soldered a 2-pin male header on one of the sensors, with the red power and black ground connected for parasitic power mode. On the 2nd sensor, I connected a 3-pin header so I could use it in powered mode. I flashed NoPullupTester (after modifying it to use pin 2 for the onewire bus) to the Pro Mini, opened the serial monitor at 9600 baud, and started getting readings. At first I thought there was something wrong, as I was getting readings in the 67-68 range. Then I realized the output was in Fahrenheit; some Americans still dance to a different tune! I removed the Fahrenheit conversion from the program, re-flashed the Pro Mini, and started getting results in familiar Celsius units.
I used some ice cubes and water in a coffee mug to check the calibration of the sensors. The first problem I noticed was that the temperature readings would rise by about 0.2C once the sampling started.
I believe this was due to self-heading. I reduced the sampling interval to every 10 seconds in order to reduce the change of self-heating. Even then, my lowest readings were 0.06C, which seemed a bit high. The datasheet shows a mean error of -0.14C at 0C, and I couldn't get either of my sensors to read below 0.06C.
While it is possible that both of my sensors were off from the mean error by about +0.2C, I suspected my ice/water mixture was a bit warmer than 0C. Water's maximum density is at 3.8C, so I thought I could be getting some stratification with the water at the bottom of the mug slightly warmer than the water and ice at the top. I then set out to find the best way to make a 0C temperature reference bath, and hit the jackpot with one of Measurement Canada's lab standards.
From previous experience with Mr. Boston, I knew that chopping ice cubes could ruin a cheap blender. I didn't have a readily available supply of distilled water either, so I came up with another solution. I chipped enough frost out of the freezer to fill a blender jug. The frost is not as hard as ice, so it would be less likely to damage the blender. and since the frost forms from sublimation of water vapor, it should have few impurities.
After about 15 minutes of adding water and blending, I had a bunch of slush about the consistency of sorbet. I drained the cold water and saved it to pre-chill the temperature probes, and transferred the slush into a dewar's flask. After another 10 minutes I tested the temperature of the slush:
The other sensor gave similar results stabilizing at -0.19C. I suspect my ice bath was a bit too cold. I had added (chlorinated) municipal water to the frost in order to make the slush, which would add impurities. Those impurities would lower the bath temperature by several millikelvins as noted by Measurement Canada. I also hadn't maintained a full immersion of all the ice in water while blending, so some of the ice slush may not have come in contact with water long enough to raise it's temperature to 0C. The results were good enough though; making the perfect 0.00C reference can wait for another day.
Not quite ready to finish with the temperature sensors, after looking at the mean error graph from the datasheet, I realized I could improve the accuracy in software. The mean error looks like part of a parabolic curve, so by flattening the curve the error would be reduced. Although a quadratic equation would be ideal, for simplicity I used a linear formula which reduces the mean error to under 0.05C from -25C to +65C:
float correctTemp(float temperature) {
return temperature + 0.2 - (abs(temperature - 20) * .005);
}
I modified the test program to output the raw and corrected temperature, As expected, with a raw temperature of 20.0C, the corrected temperature was 20.2C.
While the DS18B20 sensors are cheap and reasonably accurate, they have some drawbacks. The -55C to +125C temperature range is less than diode temperature sensors that work from -65C to +200C. The Arduino libary is not very efficient since it converts the native 7.8 bit fixed-point readings into 32-bit float. It's quite simple to use, and given the low cost of the sensors, I think they are a great solution for remote temperature sensing.
I soldered a 2-pin male header on one of the sensors, with the red power and black ground connected for parasitic power mode. On the 2nd sensor, I connected a 3-pin header so I could use it in powered mode. I flashed NoPullupTester (after modifying it to use pin 2 for the onewire bus) to the Pro Mini, opened the serial monitor at 9600 baud, and started getting readings. At first I thought there was something wrong, as I was getting readings in the 67-68 range. Then I realized the output was in Fahrenheit; some Americans still dance to a different tune! I removed the Fahrenheit conversion from the program, re-flashed the Pro Mini, and started getting results in familiar Celsius units.
I used some ice cubes and water in a coffee mug to check the calibration of the sensors. The first problem I noticed was that the temperature readings would rise by about 0.2C once the sampling started.
I believe this was due to self-heading. I reduced the sampling interval to every 10 seconds in order to reduce the change of self-heating. Even then, my lowest readings were 0.06C, which seemed a bit high. The datasheet shows a mean error of -0.14C at 0C, and I couldn't get either of my sensors to read below 0.06C.
While it is possible that both of my sensors were off from the mean error by about +0.2C, I suspected my ice/water mixture was a bit warmer than 0C. Water's maximum density is at 3.8C, so I thought I could be getting some stratification with the water at the bottom of the mug slightly warmer than the water and ice at the top. I then set out to find the best way to make a 0C temperature reference bath, and hit the jackpot with one of Measurement Canada's lab standards.
From previous experience with Mr. Boston, I knew that chopping ice cubes could ruin a cheap blender. I didn't have a readily available supply of distilled water either, so I came up with another solution. I chipped enough frost out of the freezer to fill a blender jug. The frost is not as hard as ice, so it would be less likely to damage the blender. and since the frost forms from sublimation of water vapor, it should have few impurities.
After about 15 minutes of adding water and blending, I had a bunch of slush about the consistency of sorbet. I drained the cold water and saved it to pre-chill the temperature probes, and transferred the slush into a dewar's flask. After another 10 minutes I tested the temperature of the slush:
The other sensor gave similar results stabilizing at -0.19C. I suspect my ice bath was a bit too cold. I had added (chlorinated) municipal water to the frost in order to make the slush, which would add impurities. Those impurities would lower the bath temperature by several millikelvins as noted by Measurement Canada. I also hadn't maintained a full immersion of all the ice in water while blending, so some of the ice slush may not have come in contact with water long enough to raise it's temperature to 0C. The results were good enough though; making the perfect 0.00C reference can wait for another day.
Not quite ready to finish with the temperature sensors, after looking at the mean error graph from the datasheet, I realized I could improve the accuracy in software. The mean error looks like part of a parabolic curve, so by flattening the curve the error would be reduced. Although a quadratic equation would be ideal, for simplicity I used a linear formula which reduces the mean error to under 0.05C from -25C to +65C:
float correctTemp(float temperature) {
return temperature + 0.2 - (abs(temperature - 20) * .005);
}
I modified the test program to output the raw and corrected temperature, As expected, with a raw temperature of 20.0C, the corrected temperature was 20.2C.
While the DS18B20 sensors are cheap and reasonably accurate, they have some drawbacks. The -55C to +125C temperature range is less than diode temperature sensors that work from -65C to +200C. The Arduino libary is not very efficient since it converts the native 7.8 bit fixed-point readings into 32-bit float. It's quite simple to use, and given the low cost of the sensors, I think they are a great solution for remote temperature sensing.
Friday, October 30, 2015
Getting started with Jumpstart Microbox
Richard Man at Imagecraft recently offered a free Jumpstart Microbox to bloggers willing to review it. The kit includes ST Nucleo board with a STM32F030R8 ARM MCU, an add-on board, the Imagecraft compiler, and a book on embedded C programming. I've done a lot of 8-bit AVR development, and had intended to test out some of the STM32 MCUs, so I took Richard up on his offer. The price point is much higher than a $4 Maple Mini clone with stm32duino, but Richard says they're trying to compete with IAR & Keil, not arm-gcc.
Before the hardware arrived I downloaded the IDE, which is based on Code::Blocks. It includes an annoying license manager (I haven't found a licence manager I didn't hate). On Windows 7E-64, the IDE identifies itself as Imagecraft 8.12. Unlike gcc, Imagecraft is not a full C++ compiler. Once the loads, the project options require you to pick your MCU, which defaults to the STM32F030R8.
For debug/download interface it only supports SWD. All STM32 chips have a UART bootloader in ROM that allows flashing the chip without a SWD link, using software like stm32flash. When I pointed this out to Richard he replied saying they would likely add support for it. The project template includes a main.c with the implementation of putchar for printf to work. I'll look at the compiler and Jumpstart libraries in more detail in a future post.
The jumpstart tutorial book is very basic. I've been programming in C and C++ for 25 years, so there was nothing in it I didn't already know. I didn't like the examples that included preprocessor directives (i.e. #define END 90) and code. Good coding practice is to put preprocessor defines in a separate file like config.h. The tutorial doesn't mention fixed-point, which I think should be preferred over floating point in embedded systems. Richard responded that their compiler doesn't (yet) support fixed point (gcc does), so he hadn't mentioned it in the tutorial. He said it will eventually be added to the compiler, but no promises as to the release date.
I'll finish this first part of my review with saying that I think, on one hand, that it would be better to have noobs reviewing the Jumpstart Microbox since the target market is not old C programmers like me that live by bash, vi, gcc, and objdump. On the other hand, noobs aren't going to know about the UART ROM bootloader, so I suppose I make a good (and cheap) beta tester for Imagecraft.
Before the hardware arrived I downloaded the IDE, which is based on Code::Blocks. It includes an annoying license manager (I haven't found a licence manager I didn't hate). On Windows 7E-64, the IDE identifies itself as Imagecraft 8.12. Unlike gcc, Imagecraft is not a full C++ compiler. Once the loads, the project options require you to pick your MCU, which defaults to the STM32F030R8.
For debug/download interface it only supports SWD. All STM32 chips have a UART bootloader in ROM that allows flashing the chip without a SWD link, using software like stm32flash. When I pointed this out to Richard he replied saying they would likely add support for it. The project template includes a main.c with the implementation of putchar for printf to work. I'll look at the compiler and Jumpstart libraries in more detail in a future post.
The jumpstart tutorial book is very basic. I've been programming in C and C++ for 25 years, so there was nothing in it I didn't already know. I didn't like the examples that included preprocessor directives (i.e. #define END 90) and code. Good coding practice is to put preprocessor defines in a separate file like config.h. The tutorial doesn't mention fixed-point, which I think should be preferred over floating point in embedded systems. Richard responded that their compiler doesn't (yet) support fixed point (gcc does), so he hadn't mentioned it in the tutorial. He said it will eventually be added to the compiler, but no promises as to the release date.
I'll finish this first part of my review with saying that I think, on one hand, that it would be better to have noobs reviewing the Jumpstart Microbox since the target market is not old C programmers like me that live by bash, vi, gcc, and objdump. On the other hand, noobs aren't going to know about the UART ROM bootloader, so I suppose I make a good (and cheap) beta tester for Imagecraft.
Tuesday, October 27, 2015
Using a 74HC595 as a 74HC164 shift register
The 74595 and 74164 serial-in parallel-out shift registers are a popular way of adding extra outputs to a MCU. There's lots of good guides online about how to use these shift registers such as this one by Ido Gendel. I recently found a great deal on a bunch 74595s, and wondered if there is an easy way to modify circuits intended for a '164 to use the '595. The solution turned out to be as simple as connecting two pins with a resistor.
There's actually a bit more than just connecting two pins together with a resistor, but the other requirements are simple and obvious. Pin 13 on the '595 is Output Enable, and is active low, so it should be tied to ground. Pin 10 is the clear pin (like MR on the '164), and should be tied high to keep the shift register from clearing. As a small aside, when doing this on a breadboard you may want to connect pin 10 to Vcc with a 1K resistor. That way if you accidentally connect to pin 9 (Qh') instead of pin 10 you won't risk shorting out the chip and damaging it. I got my batch of '595s for about 2c ea, so I'm not concerned about frying one of them. The key part of this solution is the resistor between pin 11 (SRCLK) and 12 (RCLK). RCLK is what updates the 8 outputs, and it needs to be clocked high at least 19ns after SRCLK (Ti datasheet table 7.6).
The resistor, when combined with the parasitic capacitance of the RCLK input forms an RC circuit which delays the clock pulse. If this delay is at least 19ns, then the outputs will be automatically updated (almost) instantly after data is clocked in, just like it is on the 74164. The input high level for the '595 is 0.7 times the supply voltage. The RC time constant is based on the time to charge (or discharge) by 63%, so after adding a bit to go to 70% and a safety margin for slower operation down to 3.3V, R*C should be at least 50ns. The datasheet says the pin capacitance is typically 3pF, and a maximum of 10pF, so something in the 10-15K Ohm would probably work. The first resistor I tried was 11.3K, and the scope trace above shows the rise time on RCKL in blue compared to SRCLK in yellow. The delay to 3.5V is around 250ns, and based on the 200ns delay to reach 3.15V (63%), the total capacitance is about 18pF. Subtracting the 13pF capacitance of my scope probe leaves a total of 5pF for the pin and resistor parasitic capacitance. The net 5pF capacitance with a 11.3K would mean RCLK reaching 3.5V around 70ns after SRLK.
With this simple modification you can turn a 74HC595 into a 74HC164, and avoid having to stock your parts bin with both kinds of chips. Since the RCLK and SRCLK pins are adjacent to one another, a surface mount chip resistor could be soldered between the pins if you want the modification to be permanent. I did something similar with on-chip decoupling capacitors. SMD 0805 parts work fine with the 0.1" (2.54mm) DIP spacing, and 0603 will just work with 1.27mm SOP pin spacing. Although 0402 would be a more ideal fit for 1.27mm SOP, soldering those tiny 0402 parts is not something you'll see me doing!
There's actually a bit more than just connecting two pins together with a resistor, but the other requirements are simple and obvious. Pin 13 on the '595 is Output Enable, and is active low, so it should be tied to ground. Pin 10 is the clear pin (like MR on the '164), and should be tied high to keep the shift register from clearing. As a small aside, when doing this on a breadboard you may want to connect pin 10 to Vcc with a 1K resistor. That way if you accidentally connect to pin 9 (Qh') instead of pin 10 you won't risk shorting out the chip and damaging it. I got my batch of '595s for about 2c ea, so I'm not concerned about frying one of them. The key part of this solution is the resistor between pin 11 (SRCLK) and 12 (RCLK). RCLK is what updates the 8 outputs, and it needs to be clocked high at least 19ns after SRCLK (Ti datasheet table 7.6).
The resistor, when combined with the parasitic capacitance of the RCLK input forms an RC circuit which delays the clock pulse. If this delay is at least 19ns, then the outputs will be automatically updated (almost) instantly after data is clocked in, just like it is on the 74164. The input high level for the '595 is 0.7 times the supply voltage. The RC time constant is based on the time to charge (or discharge) by 63%, so after adding a bit to go to 70% and a safety margin for slower operation down to 3.3V, R*C should be at least 50ns. The datasheet says the pin capacitance is typically 3pF, and a maximum of 10pF, so something in the 10-15K Ohm would probably work. The first resistor I tried was 11.3K, and the scope trace above shows the rise time on RCKL in blue compared to SRCLK in yellow. The delay to 3.5V is around 250ns, and based on the 200ns delay to reach 3.15V (63%), the total capacitance is about 18pF. Subtracting the 13pF capacitance of my scope probe leaves a total of 5pF for the pin and resistor parasitic capacitance. The net 5pF capacitance with a 11.3K would mean RCLK reaching 3.5V around 70ns after SRLK.
With this simple modification you can turn a 74HC595 into a 74HC164, and avoid having to stock your parts bin with both kinds of chips. Since the RCLK and SRCLK pins are adjacent to one another, a surface mount chip resistor could be soldered between the pins if you want the modification to be permanent. I did something similar with on-chip decoupling capacitors. SMD 0805 parts work fine with the 0.1" (2.54mm) DIP spacing, and 0603 will just work with 1.27mm SOP pin spacing. Although 0402 would be a more ideal fit for 1.27mm SOP, soldering those tiny 0402 parts is not something you'll see me doing!
Friday, October 23, 2015
ATmega328p bandgap voltage reference
All modern AVRs have a bandgap voltage reference that can be used with the ADC instead of AVCC. On most AVRs it is around 1.1V, and allows for improved ADC resolution when measuring voltages below 1V. The bandgap is also used as the reference for the internal temperature sensor. Although the datasheet says the bandgap can vary between 1.0 and 1.2V, I was curious to see how much it actually varied in practice. I found many other people posting in forums looking for information on real-world bandgap measurements, but couldn't find anyone who tested more than one MCU. Since I have several ATmega328p MCUs in my electronics collection now, I decided to take some measurements.
I used the technique mentioned on Martin's blog of setting the ADC reference to the internal bandgap reference, turning on ADC, and measuring the voltage at the AREF pin. Here are my results, taken at an ambient temperature of about 20C, and using my 0.1% calibrated meter.
Pro Mini #1 (qfn): 1.045V
Pro Mini #2 (qfn): 1.058V
Generic Uno (qfp): 1.055V
Nano (qfp): 1.089V
I also measured a Pro Mini 168pa, and found it had a much lower reference voltage of 0.994V. With the measurements being all much lower than the "typical" values from the datasheet, I wondered if I might be affecting the voltages by measuring them. Searches for information on the output impedance of the bandgap reference were fruitless, though I did find a stackexchange discussion about the 32KOhm pulldown for the ADC reference. So to get an idea of the output impedance, I measured the voltage with and without a 1.4KOhm external pulldown. The AREF voltage dropped by 6mV, so even a meter with 1MOhm input impedance would affect the voltage reading by less than 10uV.
While I only have four samples (five counting Martin's), I would say that typical bandgap reference voltages are a bit lower than 1.1V. I would also guess that if I had more samples, 9/10 would be within 3% of 1070mV. If anyone has done their own measurements, please post them in the comments.
I've read a couple forum and blog posts about calibrating the bandgap reference to a known VCC, but haven't seen any code. With oversampling, it would not be difficult to measure the bandgap within 1mV (0.1% accuracy). I could use the same technique of using init sections that I did with the internal temperature sensor, so this will likely find it's way on my list of projects to work on.
Tuesday, October 20, 2015
beta picoWiring Arduino compatible library
Before getting into the technical details, I'm going to start with a bit of history of Arduino. No, I won't be talking the arduinogate dispute over who makes the hardware, it's the software I want to talk about. From the way Arduino LLC's mouthpiece Marchese Banzi talks, he and his Arduino team came up with all the ideas that now many others have copied. However the Arduino library was copied (or forked for those who prefer to speak git) from Wiring. Wiring also took some ideas from MIT's Processing. Although there have been a lot of changes to the Arduino library over the past four years, staying compatible with pre-1.0 Arduino has meant staying (mostly) compatible with Wiring.
I suspect if I had not mentioned Wiring, most people would mistake the above screenshot as coming from the Arduino IDE. But it is actually Wiring 1.0.1. Both Wiring and Arduino add a lot of code bloat and have examples of bad interface design, but I will use them for quick tests. Between the two of them I prefer Wiring, since after the Arduino fork, Wiring implemented an optimized digitalWrite that is much faster than Arduino.
While it is the rare Arduino library that is well written, when it comes to testing new electronics the convenience of having something that works is worth holding your nose over the poor coding. Although digitalWrite may not be the best way to abstract IO, it can be implemented efficiently as demonstrated by Wiring. The same API can be used on the esp8266 with CHERTS or the esp8266 Arduino core. For command-line development of Arduino code for the AVR Ino is an option, however the lack of optimization is still a problem. Therefore I have written a library that has very little overhead and is compatible with the Arduino/Wiring API.
My goals were to make the library as small and as fast as possible, while supporting the ATmega168/328 series. Since it has the fast digitalWrite, I started with some of the code from the Wiring AVR 8-bit core. I removed the millis() timer, and implemented delay() using _delay_ms. I modified the SPI class so it is a static class - instance variables unnecessarily take up memory when only one instance of the SPI class is created. The Serial class took a lot of work to minimize. Like the SPI class, I removed instance variables to make it a static class AKA singleton. The AVR UART has a hardware FIFO, so removed the software FIFO the Serial class was using.
I've posted the code, which I still consider beta, to my github account. I've done some testing with my (beta as well) piggy-prog project. Both picoWiring and my dmbs fork are git submodules, so I can easily keep piggy-prog synced with bug fixes and improvements to picoWiring. For piggy-prog, the code size improvement is significant. Building piggy-prog with the 1.6.3 Arduino IDE results in a program that requires 4.5KB of flash. Compiled with avr-gcc 4.9.2 with picoWiring, the flash size is just 1772 bytes.
I suspect if I had not mentioned Wiring, most people would mistake the above screenshot as coming from the Arduino IDE. But it is actually Wiring 1.0.1. Both Wiring and Arduino add a lot of code bloat and have examples of bad interface design, but I will use them for quick tests. Between the two of them I prefer Wiring, since after the Arduino fork, Wiring implemented an optimized digitalWrite that is much faster than Arduino.
While it is the rare Arduino library that is well written, when it comes to testing new electronics the convenience of having something that works is worth holding your nose over the poor coding. Although digitalWrite may not be the best way to abstract IO, it can be implemented efficiently as demonstrated by Wiring. The same API can be used on the esp8266 with CHERTS or the esp8266 Arduino core. For command-line development of Arduino code for the AVR Ino is an option, however the lack of optimization is still a problem. Therefore I have written a library that has very little overhead and is compatible with the Arduino/Wiring API.
My goals were to make the library as small and as fast as possible, while supporting the ATmega168/328 series. Since it has the fast digitalWrite, I started with some of the code from the Wiring AVR 8-bit core. I removed the millis() timer, and implemented delay() using _delay_ms. I modified the SPI class so it is a static class - instance variables unnecessarily take up memory when only one instance of the SPI class is created. The Serial class took a lot of work to minimize. Like the SPI class, I removed instance variables to make it a static class AKA singleton. The AVR UART has a hardware FIFO, so removed the software FIFO the Serial class was using.
I've posted the code, which I still consider beta, to my github account. I've done some testing with my (beta as well) piggy-prog project. Both picoWiring and my dmbs fork are git submodules, so I can easily keep piggy-prog synced with bug fixes and improvements to picoWiring. For piggy-prog, the code size improvement is significant. Building piggy-prog with the 1.6.3 Arduino IDE results in a program that requires 4.5KB of flash. Compiled with avr-gcc 4.9.2 with picoWiring, the flash size is just 1772 bytes.
Monday, October 12, 2015
Parasitic capacitance of AVR MCU pins
Parasitic capacitance of MCU pins is not something I've seen discussed much. Look in the electrical characteristics of an AVR datasheet, and you can find input current leakage, but not parasitic capacitance. Over a year ago I did some experiments to see how long a floating pin would stay high after switching from output high to input mode. The result, which is a factor of both leakage and capacitance, was in the tens of seconds. Now that I have a Rigol 1054Z, I can do some tests to try to specifically measure the input capacitance.
The above screen shot shows the rise time of an unconnected pin on a Pro Mini with the internal 35K pullup enabled. The Pro Mini has female headers attached (much like the full-size Arduino), and running some simple code that toggled the pullups for PORTB.
The RC time constant means that a capacitor will charge to 63% in R*C seconds. So a 1 Farad capacitor charging through a 1 Ohm resistor will take 1 second to charge to 63% of the input voltage. I'm running the Pro Mini at 5V, so 63% of that is 3.15V. The Pro Mini pin takes ~700ns to reach just over 3V. Knowing that the internal pullup on the AVR is 35K, we start with 35K * C = 700ns, and solve for C to come up with 20pF. I've read that 10pF is a typical value for IC pins, so something probably isn't right with my 20pF measurement. After a bit more searching through datasheets I found table 29-19 of the ATmega328p datasheet, in the TWI section, which says 10pF max.
I tested an Arduino Nano compatible board with no headers attached that used a QFP ATmega328p instead of the QFN chip used on the Pro Mini. The measurements equate to a parasitic capacitance of about 25pF. A second Pro Mini with male headers measured at 20pF, indistinguishable from the first Pro Mini.
After a bit of pondering, I realized my scope probe is going to have some capacitance, and after digging out the spec sheet for the Rigol RP2200 probes, I found a specification of 17 +- 5pF. So it seems most of the 20pF capacitance I measured is from my scope probes, but I wasn't sure how much. My next idea was to test an 11K resistor connected to a pin on the Pro mini which cycled between output high and output low. Here's the screen shot from the end of the resistor that was left floating:
Measuring the 63% rise time and solving for C this time gives 14pF for the capacitance. This means at least 6pF of the previous 20pF measurement is the parasitc capacitance of the AVR pin. The measured 14pF consists of the probe capacitance plus the parasitic capacitance of the resistor, so there is still the problem of measuring one or the other. You may notice the sharp 4-5ns rise at the beginning before the rise turns into a steady logarithmic curve. After seeing it on my first measurement, I repeated it to make sure it wasn't a glitch. This EDN article explains that resistors are complex components with inductance, capacitance and resistance. Charging of the small series capacitance of the carbon resistor I used may explain this initial fast rise.
Back to the problem of measuring my scope probe capacitance, I found a pdf document from Keysight that offers the simple solution of attaching two probes and comparing to the measurements from one probe. I attached two probes, solved for C again, with a result of 27pF this time. The 13pF increase should be the probe capacitance, with 1pF from the resistor lead. The EDN article said resistors with 1/4" leads have a capacitance of 1-2pF, so the 1pF measurement from the resistor I used (with 10mm leads) is reasonable.
Taking 13pF for the scope leads from the 20pF measurement of the QFN AVR pin gives a net parasitic capacitance of about 7pF. The measurement from the Nano with the QFP chip indicate a net parasitic capacitance of 12pF. Considering the datasheet specification of 10pF max per pin for the ATmega328p, the extra capacitance on the nano likely comes from the board design, as things such as the distance between traces and a ground plane can affect capacitance. For the chips themselves, both QFP and QFN, the parasitic capacitance is lower than 10pF, perhaps as low as 6-7pF per pin.
The above screen shot shows the rise time of an unconnected pin on a Pro Mini with the internal 35K pullup enabled. The Pro Mini has female headers attached (much like the full-size Arduino), and running some simple code that toggled the pullups for PORTB.
The RC time constant means that a capacitor will charge to 63% in R*C seconds. So a 1 Farad capacitor charging through a 1 Ohm resistor will take 1 second to charge to 63% of the input voltage. I'm running the Pro Mini at 5V, so 63% of that is 3.15V. The Pro Mini pin takes ~700ns to reach just over 3V. Knowing that the internal pullup on the AVR is 35K, we start with 35K * C = 700ns, and solve for C to come up with 20pF. I've read that 10pF is a typical value for IC pins, so something probably isn't right with my 20pF measurement. After a bit more searching through datasheets I found table 29-19 of the ATmega328p datasheet, in the TWI section, which says 10pF max.
I tested an Arduino Nano compatible board with no headers attached that used a QFP ATmega328p instead of the QFN chip used on the Pro Mini. The measurements equate to a parasitic capacitance of about 25pF. A second Pro Mini with male headers measured at 20pF, indistinguishable from the first Pro Mini.
After a bit of pondering, I realized my scope probe is going to have some capacitance, and after digging out the spec sheet for the Rigol RP2200 probes, I found a specification of 17 +- 5pF. So it seems most of the 20pF capacitance I measured is from my scope probes, but I wasn't sure how much. My next idea was to test an 11K resistor connected to a pin on the Pro mini which cycled between output high and output low. Here's the screen shot from the end of the resistor that was left floating:
Measuring the 63% rise time and solving for C this time gives 14pF for the capacitance. This means at least 6pF of the previous 20pF measurement is the parasitc capacitance of the AVR pin. The measured 14pF consists of the probe capacitance plus the parasitic capacitance of the resistor, so there is still the problem of measuring one or the other. You may notice the sharp 4-5ns rise at the beginning before the rise turns into a steady logarithmic curve. After seeing it on my first measurement, I repeated it to make sure it wasn't a glitch. This EDN article explains that resistors are complex components with inductance, capacitance and resistance. Charging of the small series capacitance of the carbon resistor I used may explain this initial fast rise.
Back to the problem of measuring my scope probe capacitance, I found a pdf document from Keysight that offers the simple solution of attaching two probes and comparing to the measurements from one probe. I attached two probes, solved for C again, with a result of 27pF this time. The 13pF increase should be the probe capacitance, with 1pF from the resistor lead. The EDN article said resistors with 1/4" leads have a capacitance of 1-2pF, so the 1pF measurement from the resistor I used (with 10mm leads) is reasonable.
Taking 13pF for the scope leads from the 20pF measurement of the QFN AVR pin gives a net parasitic capacitance of about 7pF. The measurement from the Nano with the QFP chip indicate a net parasitic capacitance of 12pF. Considering the datasheet specification of 10pF max per pin for the ATmega328p, the extra capacitance on the nano likely comes from the board design, as things such as the distance between traces and a ground plane can affect capacitance. For the chips themselves, both QFP and QFN, the parasitic capacitance is lower than 10pF, perhaps as low as 6-7pF per pin.
Subscribe to:
Posts (Atom)
















