Show by Label

Monday, November 14, 2016

Demistifying Rotary Encoders (some more)

For an Arduino based project, I wanted to use a rotary decoder to control a menu based structure.

As you are probably aware, there are dozens of solutions available, typically one more complex than the other, in an attempt to make it reliable and fast.

There are two schools of thought. You use the recognoition is the main loop construct, or you use interrupts. In the first case, you need to carefully design the main loop, because if you don't get the timing right, the recognition of a twis of the decoder will be slow, or can be missed. This makes for a very poor user interface (U/I).

Using an interupt to recognise movement can be more responsive, unfortunately, almost all interrupt based solutions use two interrupts. On the Arduino Nano or Mini-Pro, there are only two external interrupts available, so using both can be a problem. The good news is that you really don't need two interrupts.

If you look at the datasheet, you are presented with perfectly modeled wave forms of the two switches that are the heart of the decoder. Here is a picture of a typical in-expensive decoder switch, and the waveforms.



Image result for rotary encoder switchImage result for rotary encoder switch













First off, the real wave-forms are not perfectly symmetrical, the output is depending on the mechanical construction and the rotation speed. The other important bit of information is that practically, you rotate the switch from indent to indent.

Here is a screen shot from a one indent move forward made with a Logic Analyzer:
And here is the screen shot of moving one indent anti-clockwise, or back.


Notice the different pulse width of the A and the B switch in both cases.

The challenge is to not only detect a rotation movement, but also the direction, and then in such a way that you can also rotate the switch very fast and be correct.

In the following Arduino sketch, I use one interrupt on the rising edge of the A-switch, and then sample the level of the B-switch. As you can see above, a clock-wise (to the right) rotation will cause the A-switch to become High before the B-switch. If you turn the other way, the B-switch is already high when the A-switch becomes high.

In order to track and visualize what is going on, I added some statements in the code that will generate a trigger pulse so the Logic Analyzer or scope will help us with the timing relationships.

Here is the sketch:

/* Software Debouncing - Mechanical Rotary Encoder */

#include <FaBoLCD_PCF8574.h>             //include the i2c bus interface and LCD driver code

//---- initialize the i2c/LCD library
FaBoLCD_PCF8574 lcd;                     //with this, there are no further code changes writing to the LCD

#define encoderPinA 2                    //encoder switch A
#define encoderPinB 4                    //encoder switch B
#define encoderPushButton  5             //encoder push button switch
#define Trigger 6                        //Trigger port for Logic Analyzer or Scope

volatile int encoderPos = 0;
volatile int oldencoderPos = 0;

void setup() {
  pinMode(Trigger, OUTPUT);
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  pinMode(encoderPushButton, INPUT); 
  attachInterrupt(digitalPinToInterrupt(encoderPinA), rotEncoder, RISING); //int 0 
  lcd.begin(16, 2);                      //set up the LCD's number of columns and rows
  lcd.clear();                           //clear dislay
  lcd.setCursor(0,0);                    //set LCD cursor to column 0, row O (start of first line)
  lcd.print("Rotary Encoder");
  lcd.setCursor(0,1);                    //set LCD cursor to column 0, row 1 (start of second line)
  lcd.print(encoderPos);
}

void rotEncoder(){
  boolean rotate;
  delayMicroseconds(300);                //approx. 0.75 mSec to get past any bounce
                                         //delay() does not work in an ISR
  //send entry Trigger pulse
  digitalWrite(Trigger, HIGH);
  delayMicroseconds(10);
  digitalWrite(Trigger, LOW);
 
  rotate = digitalRead(encoderPinA);           //Read the A-switch again
  if (rotate == HIGH) {                        //if still High, knob was really turned
    if (rotate == digitalRead(encoderPinB)) {  //determine the direction by looking at B
      encoderPos--;
    } else {                                  
      encoderPos++;
    }
  }
  //send exit Trigger pulse
  digitalWrite(Trigger, HIGH);
  delayMicroseconds(10);
  digitalWrite(Trigger, LOW); 
}


void loop() {
  //loop until we get an interrupt that will change the encoder position counter
  if (encoderPos != oldencoderPos) {
    lcd.setCursor(0,1);
    lcd.print(encoderPos);
    lcd.print("      ");
    oldencoderPos = encoderPos;
  }
}

And here is a screen shot of a forward indent with the trigger pulses:

As you can see from this data, it takes the Arduino 755 uSec from the A-switch rising edge recognition to the entry in the Interrupt Service Routine (ISR). It then only needs 14.2 uSec to do the rotation recognition.

To put this into perspective, so you get an idea of the relative "blinding" speed of a 16MHz Arduino in relation to slow moving switches:


 Turning the knob as fast as I can produces this picture:


More later...

Sunday, October 2, 2016

Building a Bench Tracking Dual Voltage Supply

For some of my experiments and tinkering, mostly with op-amps, I wanted to have another power supply that would give me a precise dual-tracking complimentary voltage, up to +/- 30V.

Here are a few design goals I set for myself.
1. True 0 to +/- 30V.
2. Non tracking mode to set two different voltages for the negative and the positive supplies.
3. Precise dual tracking within 1% or better.
4. Precise Voltage level setting at 10mV levels.
5. Accurate display of output Voltages with less than 0.01% error.
6. Pretty good current limiting setting with a visual indicator. (not at a precise exact value, but good  enough because I don't want to blow-up an expensive device.)
7. Pretty good constant current/voltage operation.
8. Low noise and stability without going to extremes.
9. Small package, using the same housing as my other supplies and DC Load.
10. Maximum current between 80-100mA for each supply separately.
11. Using components like voltage display and transformers to be used with a drastically different design. (just in case I wanted something complete different)
12. Some protection against blowing things up and doing stupid things myself.


For a while, I was contemplating a simple tracking LM317/337 supply, and I looked around of what designs where out there on the Web. There were surprisingly few, actually, and none fitted my bill.
Eventually, I started to piece some things together myself, but by the time I added the bells and whistles I wanted, things were getting complicated quickly.  Rather than scrapping the whole idea, I continued as a learning experience to see how far I could get this to work. In the back of my mind however, I always considered starting all over with a more traditional supply design, so I made sure most of the more expensive components could be re-used.

Here is the circuit diagram of the complete supply. Looks pretty wild when you look at it initially, but when I'll go through the building blocks it's actually not that bad. Note that I made an error by naming the LM337 a 377.

Let's just start with the positive voltage supply, and dive right in, the rest follows. The output voltage is regulated and set by IC7, an LM317AHVT, which is the high voltage version. To get a regulated 30V at the output, I need to supply several volts more. When the transformer is not loaded much, the voltage can get to levels that are too high for the standard LM317, which is why I use the "H" version.

R28 is used in combination with R27, the 10 Turn potmeter to set the output level. R28 also makes sure that there is some minimum current flowing to keep the regulation in check. That only works with higher output voltages, so I use a J-FET, Q5, used here as a constant current source, to ensure that the LM317 always sees an 8mA or higher current. The J-FET needs a few volt to work with, and I decided to give it -8V, because I can use that voltage level in other places as well.

The adjust voltage setting is stabilized with C17, but that means that you also need D17, to protect the LM from the C17 discharge levels going the wrong way. To make sure that I can regulate down to 0V, I have to overcome the reference voltage of the LM317, which is 1.25V. Initially, I used a -1.25V voltage reference to create that counter-balance, but I was not too happy with how that worked. D25 and D26 in combination with the -8V will do the same and actually clamp the negative supply at the Source of Q5 at about -1.3V. That's close enough. If you want to quickly remove the output level of the supply, you need a way to remove the output voltage. I used a switch (S3) across the Volt Adjust potmeter, to do that. And that pretty much covers the positive voltage setting.

If you now look at the equivalent circuit on the negative side, around IC6, an LM377T, you'll see exactly the same circuit, with the Tracking Switch S2 in the position shown. Because IC6, the LM377, does not come in a high voltage version, I had to use another LM377 (IC3) as a pre-regulator to limit the voltage going in to IC6. IC3 limits the maximum voltage of about -40V to a -36V level which is safe for the 377 and provides plenty regulation head-room. Using another LM377 may look like an overkill, but the 5 components (The 377, a protection diode, two resistors and a capacitor) costs are really minimal. Yes I could have used D14 to go across all three LM377's, but that's the way the circuit developed.

Let's switch our attention to the current limiting section, and we'll use the positive supply again. IC4, yet another LM317 is used as the current limiting device. The current limiting is depending on the voltage of the current shunt resistor, R12. The 12 Ohm value will limit the current to a maximum of 104mA. To make that current start from 0mA, I used the same circuit around D13, Q2 and the negative supply of -8V to do that. The variable current limiting settings are accomplished with a normal 1 turn potmeter R17, in combination with R16, to make the potmeter effective over the complete range of 1K. D11 and D12 limit that range to 1.3V, and that creates a pretty accurate way of setting the current limit. Q2, another J-FET, also functions here as a constant current source of about 8 mA, keeping IC4 into regulation at all times.

To get an indication of the entering into the Current Limiting or Constant Current mode, I used the circuit with Q3 and a red LED. Q3 measures the voltage drop over the LM317, and if it goes over a certain level (> 0.6V, when the limiting gets tripped), the LED with be turned on. Simple but effective.

The negative supply is again a mirror copy of the positive portion.

OK, let's move our attention to the dual voltage tracking circuit. I used a simple method with two precisely matched 10K resistors to create a virtual ground level at the midpoint. After testing the result, I found that I still needed an adjustment trimmer R30. The level at the wiper goes to the inverting input of an op-amp, IC8, and that compares that with the true ground. The op-amp will make sure that it's output is driven such that the inputs are equal. The output goes to the Tracking On/Off switch, and when that is flipped, it actually takes over from the potmeter setting of the negative supply. The negative supply will now follow (track) the output level of the positive supply, also when the positive supply goes into current limiting. I have selected the TLE2141 op-amp for this job, because it can handle the supply voltages of -36V plus +8V = 44V.

The positive and negative outputs have C22/C18 and C23/C24 to filter unwanted noise. I kept C23/C24 as low as possible to protect them from dumping their load into my precious DUT circuit. D15, D14 and D5 are an insurance for my stupidity, if there are capacitors in the DUT that want to dump their charge back into the supply. They are protection for the LM317/337 devices. D18 and D19 are protection for reverse voltages that I may accidentally try to dump into the supply.

The supporting team is made up of transformer TR1 to supply the main voltage of the supply. I have used R1, R2 and R3, which are PTC's to add a level of protection for over currents. They are self-healing.

The main supplies are rectified with a full bridge filtered by reservoirs C6/C5 and C12/C9 to remove high frequency noise. Both R7 and R6 make sure that the reservoirs are emptied relatively quickly, so no voltages are present for very long when the mains is switched off. They will also put a minimum load on the transformer to protect for voltages that may become to high when there is no load.

To minimize the development of heat and use normal regulators for the +/- 8volt supplies, I used a separate transformer. These print transformers are relatively inexpensive and small, and the +/- 8V supplies are now independent of any voltage swings on the main supply. The filter circuits around IC1 and IC2 are text book stuff.

The last element is the voltage display. I found a module that has a real DMM "inside", is very accurate and works up to 33V.   Voltmeter
(if the links is broken, search for "LED 5 digit DC 0-33.000V Digital Meter)

These displays typically generate a lot of switching noise that you really don't want to have injected into the power supply rails. At the same time, I wanted to use this voltmeter to measure the positive supply as well as the negative supply. Unfortunately, these meters only handle positive voltages. In order to switch the volt meter from one output to the other, the power for the meter needed to be floating from the main power. So, I needed a third transformer to isolate the power rails and I could then do the switching with S1. S1 applies the positive output voltage to the plus input and the ground to the minus input, and then reverses this for the negative supply (positive input is now ground, and the input ground is now the minus output supply. Simple and effective.

There is one caveat with a tracking supply like this one. The negative supply tracks the positive one. If the current limiting for the positive supply kicks in, the negative supply will follow. However, when the current limit for the negative supply kicks in, the positive supply will stay at it's set level, creating an unbalanced output situation. I will need to find a way to make the positive supply follow the negative supply.

After I finished building the supply, have been using it for a few months now, and I'm very happy with it. The voltage level shown on the display is very accurate, it really acts like a good DMM, and so is the tracking accuracy which is well below 0.1%. During my experimenting, I find myself grabbing this supply more and more, even though I sometimes find the output dropping because I pull too much current from it. Hmm... Maybe this is a hint to the other design that I have been contemplating for a while and will have even more accurate voltage and current setting? In the mean-time, this works for me.

Here is a picture of the main circuit board in an earlier stage, when I was still using the 1.25 references (the SMD parts), and without the current limit indicators. It has been modified quite a bit since then.

All parts within the dotted rectangles on the circuit diagram are mounted on the metal back-panel of the enclosure.

I didn't make pictures of the enclosure yet, but I will when I'm back home and also update the picture below.

Sorry for the bad focus, I can do better!



Enjoy!











Friday, September 30, 2016

Building a Milli-Ohm Meter

I already mentioned the SCULLYCOM Youtube series from Louis Scully, and a while ago I prototyped this design to see how well it would fit my needs.

Build a Milliohm Meter
Update

This is my circuit of the prototype I built, based on the first video.

Initially, since I used my DMM as a display, I did not use the x10 multiplier. I was so happy with the accuracy that I decided to build the real thing, so I ordered and added the INA106, because it makes the read-out a bit easier without having to do the math.

The unit worked very well, and even as a prototype, using my DMM as the display, it has been very useful. When I found out that Greg (the same one from the Milli-Voltmeter PCB), also created a PCB for this project, I was sold and decided to built one on his PCB. This is detailed in the update.

This project is also a work in progress for me, because I'm still building my BOM to be able to order the required parts when I'm back home.

Here is Greg's version of the circuit diagram, that I will use as the reference for the BOM.


The BOM for this project, using mostly UK suppliers, is now available here:  BOM
And also on Greg's site here : BOM2

I use Mouser and/or DigiKey myself, because Farnell and the likes do not want to sell to hobbyists in other countries. Many alternative suppliers do not have some of these parts available. You can use several of the Farnell part numbers that Louis provided to look up parts with Mouser, and otherwise the description will help. There are some price differences with Mouser and DigiKey parts, most are less expensive, some significantly so, especially the .1% 25PPM resistors.

The PCB itself can be ordered here : PCB

Note that there have been a number of revisions, currently at 1.5, and there may be more.

Here is some information about the above parts:

C1..4 (22uF/25V) have some physical restraints to fit on the PCB. The dimensions are diameter 5mm, height 7mm (not so critical), lead space 2mm.

C5 (can be 150, 220, 470 or 1000uF/35V) and also has some restraints. The dimensions are diameter 10mm, height 12,5mm(not so critical), lead space 5mm.

IC2 and IC5 are from Linear Technology and Mouser does not carry that brand. In that case, I usually mix my order between Mouser and DigiKey and keep an eye on the free shipping limit of 50 Euro's or more. The IST version of IC2, the LT3092, is the preferred version, because it has better specifications.

Resistors R1 through R9 determine the constant current of 100mA. A precise and clean current is the Achilles heel for the design of this meter. There are four options for R2, 3, 4 and 5.  Louis has the Welwyn RC55Y series listed, but they cost a whopping 2.56 Euro's a piece. He also has the Holsworthy H856 series listed and they cost 1.72 Euro's. Mouser has the Neohm types as an alternative available. They have exactly the same specifications in term of precision (0.1%) and are also 15 PPM/C grade, however, the other two may have other better specs in general. There is a price difference though. The NEOHM version for R1 is YR1B63R4CC, and for R2..R5 is YR1B56R24CC, and they cost about 0.20 to 0.25 Euro each, depending on the value. Greg, the maker of the PCB, uses Vishay resistors from the MRS25000 series. They only have 1% precision and a TC of 50PPM/K. They cost 0.27 Euro's each. To get the required specification, he ordered a large number and selected the best ones.
Up to you to decide if your budget allows it and if you really need that little bit extra.

R12 and R13, the adjustment trimmers are quite special physically, so make sure you order the right type: 3296P-1-104F and 3296-1-101LF. There is also a 3366 version available that looks the same but is a little smaller and also seems to fit the PCB layout. (on the revision 1.5, Greg used both types as you can see from the pictures)

I have not decided myself yet if I will use the LCD meter as a display, or continue with using one of my other DMM's to display the value. The 6.5 digit Milli-Volt Meter, also designed by Louis that I'm building as well would be perfect for this job. Because I will only occasionally have a need for the milli-Ohm meter, a dedicated and rather pricey panel meter seems like a waist to me and also makes the enclosure larger. Besides, I have had some bad experiences of the LCD driver switching noise coming from these displays getting injected to my critical signals.

This decision will determine what enclosure I will use, but that will have to wait until I have the PCB and tested some things. The added benefit of using another DMM for display is that the panel meter  Louis selected only goes to 2V, and that limits the maximum value of the resistor you can measure to a maximum of 2 Ohm.

The next update will be towards the middle of November, when I have all the parts, so stay tuned for more.





Thursday, September 29, 2016

Building a 6 Digit Milli-Volt Meter

There are a large number of updates in the software, scroll down to see them.


As I mentioned in one of my other posts about the Kelvin-Varley Divider, I wanted to have a higher resolution voltmeter than I currently have. I happened to stumble on a nice set of Youtube videos from Louis Scully from Scullcom Electronics. He has described a set of very nice instruments, one of them is a 6.5 digit milli-Volt meter.

One usual caveat! The links I provide may at times no longer work, so my apologies in advance if you land in the 404 land of no returns.

Here are the links to the videos, now up to 4 in total.

Part 1 of the 6.5 digit Voltmeter
Part 2
Part 3
Part 4
Mk2

The design is quite simple and should be able to be built if you have a bit more than average skills.
The good news is that a follower of this design, Greg, has provided a PCB through OSH Park that greatly enhances the input section of the DVM, which is the Achilles heel of such a project.

Here is the website that details the implementation using that PCB:
Barbouri Millivolt-Meter Project

I will use that PCB, but I have a few changes in mind for the version I am going to built and I'll go through these elements here.

The front end of the design is the most critical. I'll implement that by following the PCB design. For the processor part, I originally wanted to use the same Arduino Nano that Louis Scully is using, but since the PCB layout is for an Arduino Pro Mini I'll use that. However, I have no need to drive a multi-color back light LCD, and I also do not foresee any other enhancements that will eat up the Arduino ports, so I see no need to use the Display42 PCB from Greg with the I2C MCP23017-E/SP chip.

To cut down on the amount of wires going from the Arduino the the LCD, there are 6, I am using a module that is available on eBay for a very small price : LCD Interface

The LCD that I'll end up using will be this one :
16 x 2 White on Blue LCD












My design will also power the unit from a battery, to avoid grounding issues and provide a clean supply to begin with. However, rather than using normal batteries, I will use a  rechargeable cell, and I want to provide a way to charge them while using the instrument, and also when not in use. To keep taps on the voltage level or discharge level while not on mains, I need a monitor that will warn me when the voltage is getting too low.

Here is the circuit that I use to implement the button debounce, and the power section.




One word of caution before I dive in. The DC chassis part is not quite like the Eagle symbol I used in this diagram. There is no short between the plus and minus when there is no plug inserted! The minus is however isolated from the chassis when there is no plug inserted, keeping everything isolated from the chassis. If the plug is inserted, there is an indication on the LCD, so you won't forget.

You'll notice that I deviated from the design Louis used for the two buttons. I have quite some experience now interfacing buttons if you have followed my Raspberry Pi posts. The processor of the Pi runs at 900MHz or more (yes, no typo), and interfacing with something as slow as a button has its challenges. Especially for inexpensive buttons. You'll be amazed how noisy they can be.

In any case, the filtering that I use to get rid of most switch bounce noise is by using an R/C filter on both edges (closing and opening). I always prefer to use active high buttons or switches because they avoid all sorts of power-on problems. When the switch/button is open, the capacitor is at ground level. Closing the contact will charge the capacitor through the 10K series resistor, creating a nice and clean rising edge (R/C) towards the input of the Arduino. Releasing the switch will cause the capacitor to discharge through the 10K series resistor plus the 1K to ground, again creating a nice R/C slope that will filter the high speed bounce noise. In software we can use a little delay to get rid of the slower bounce transitions, and together this will create clean signals to the Arduino program without having to resort to Schmidt-trigger gates or Flip-Flops.

One word of caution. Don't make the debounce capacitor much larger than 10nF. If you don't have 10nF, you can go as low as 1nF. The reason for this is that the slower the R/C slope, the more time the signal stays in the undefined area between digital "high's" and "low's" and that can cause glitches for the processor again.  If I peaked your button interest, have a look here : Debouncing buttons There is a lot more to buttons than you may think.

The power design is quite simple, and I've used that before. The two Schottkey (low drop) diodes D2 (this diode can actually be a 1N400X type) and D3 will decide which supply is feeding the Voltmeter. If the mains is connected (providing the 15-30V DC), D2 will have the higher voltage so it will win. If there is no mains connection, the battery supplies the voltage. The resistor (R1) in parallel of D3 determines the (re)charge current of the Ni-CAD cell. The charging current is about 0.1 x C for a 250mAh cell. Depending on the capacity of the cell(s) you're using you may need to change the value of R1 so it is within the (re)charging specification of the cell(s).

In order to keep an eye on the charge level of the cell, I have added a few parts to allow the Arduino to measure the voltage level. You don't want to run into a situation where the voltage is too low, and you will introduce errors in the measurements. Besides, you don't wat to be caught with an empty battery when you're in the middle of something. R2 and R4 create a 3:1 voltage divider with easy to find resistors. You can create 20K with 2 x 10K in series. (don't use less than 10K for R4, or it will negatively influence the ADC conversion) C4 is a small filter to get rid of noise and the output goes to one of the Arduino ADC inputs. The rest is done in software and I have also designed some battery level symbols to make it look nice.

The complete multi-meter draws less than 60 mA. About 26mA of that is used by the LCD display. It is safe to use the L part for the 12V regulator, and even for the 5V regulator on the PCB.

If you are already an Arduino user, you may have the Mini Pro, and you also may have the required programming cable. If not, here is a source that provides both as a bundle :
Arduino Pro Mini with interface

Here is a picture of the small interface board that will turn the LCD module into an i2c capable interface, reducing the number of wires, and to stay compatible with the PCB.



You need to install a new LCD library to get the i2c driver, and I selected the library from here :
i2c / LCD library

This library is tailored for a particular interface board, the FaBo #212 LCD I2C Brick, but the only difference is the i2c address with the board I have.

First of all, you need to know the i2c address of your board.
I used a little sketch to do that:
i2c address scanner

My address turned out to be 0x27, while the FaBo brick uses 0x20.
After you have installed the new i2c-LCD library on your system, you need to edit the  FaBoLCD_PCF8574.h file that is in the library source section, and change this line :

#define PCF8574_SLAVE_ADDRESS 0x27 ///< PCF8574 Default I2C Slave Address = 20


Here is a picture of the finished project. I actually build two units, because one can never have enough voltmeters. My design and the changes I made allows me to position these meters very close to my prototypes, and without and power wires attached. I can also make floating measurements, because nothing is connected to the housing. (the DC socket inputs are isolated from the chassis if no power plug is inserted)



Below is a link to the copy of the Arduino sketch. There are many changes to the original code, so have a good look at what has been changed in case you use different hardware.

I have been playing with the two units, to see what the accuracy is and what I could change in the user interface.

I must say that I am very impressed with the accuracy! I have two calibrated voltage reference units, and also a new/freshly factory calibrated 4.5 digit bench multi-meter. The accuracy and precision of this design is astounding for such a simple and inexpensive tool.


I was a little apprehensive about feeding the LCD display with the same 5V supply as the rest of the logic. These displays are notorious for introducing spikes and noise, so I was on guard for trouble.

When I got my scope attached, I was not surprised by the noise I found, so I started with decoupling the 5V power where it enters the display module. I used a Tantalum 3.3uF together with a 100nF to begin with, because the i2c and the LCD do not have any decoupling.
Here is how that looks:

Unfortunately, this did not reduce the nasty spikes on the reference voltage and the main 5V by much. Looking into it some more, I found that the switching of the LTC_CS line to start/stop the AD conversion cycle turned out to be the culprit. Here is a screenshot:


The top trace (A) is the LTC_CS signal coming from D10 at the Arduino PCB. The bottom trace (B) is the 4.096V reference, AC coupled. The spikes are clearly caused by the switching at the digital port. They are a few nSec in width, so I selected a 4n7F capacitor that slowed the edge down enough to not cause a spike anymore. I mounted that capacitor on the PCB of the Arduino, with one leg soldered to D10 and the other end to the unused GND mounting hole just next to it.:


And this is the result:


I noticed a potential bug in the code, related to the averaging of the results. The Spi_Read function discards a reading of the ADC if it's not ready, but the main loop code counts it as a valid sample, which could result in wrong measurements. I have fixed the code below, but I could not find instances of this error when I looked for it with a Logic Analyzer.

While I had that out, I looked at the timing in more details, to see if there were any potential conflicts.

First of all, this is a picture of the ADC sampling window:

Here you can see that the CS is going down to start the cycle and the MISO is ready 1.25uS later, virtually at the same time. There is 1.5uS between that event and the first clock going high. This is after I already eliminated the little delay in the original Spi_Read code. It is not needed. You can see here the four data bytes getting read, and the actual data presented on the MISO. Note that at the third clock, we are reading the third status bit (SIG), and that indicates a V-in signal > 0. The data towards the end is the "real" 28 bit data, of which the last 4 are the "extra" sub LSB's and they are discarded in the main loop after the averaging. (look at the datasheet for details)

I selected 8 samples to be averaged in my code, and then prepare the result to go to the LCD. As I mentioned earlier, these LCD's are very noisy. In our case, this has no real influence, because the LTC2400 is put to sleep after we have read the data, and made the CS pin high again, as you can see above.

Here is a screen shot that shows the end of the data (channel 5) going through the  I2C bus to the LCD, and the beginning of another acquisition cycle:

You can see that we have a "quiet" period after the LCD got updated, and the start of a new ADC acquisition, which is 0.129 mSec (T1-T2). The total loop time, from LCD update to LCD update is 1.5 Seconds in my case. Sending the results to the LCD only takes about 36 mSec.

Here is a picture of a complete loop:

The "dead" time, averaging the results and sending it to the LCD is only 0.22 Seconds.

After playing with the meter, I got more and more dissatisfied with the flopping around of the last 3 digits, even when I has a stable voltage reference connected to it.

At first I played around with the averaging, but that is really no solution for a system that has 24 bit resolution. The reason is the inherent amount of noise when you are down to the micro-Volt level. Below is a sample of my 2.5V reference, using my non-calibrated voltmeter (the reference is calibrated as having 2.49993V)

Averaging does not have as much influence as you may think. There is still quite abit ofnoise.

So, still not good enough. I then looked at smoothing, but that was not good enough either, so I turned to filtering. I tried a few approaches, and then really investigated an Infinite Input Response (IIR) filter design. And that showed a lot of promise:


This filter is based on the "weighing" of the new samples, based on a division. The divisor is fixed and above I used a factor of 64. This means that a new sample only contributes to 1/64th of the value to the averaged total. This is great when you have a stable signal, but what if the input voltage changes?
You could of course reduce the weighing factor, and here is one example with factor 4.


An order of magnitude better then averaging. But I was not satisfied yet. I then looked at resetting the averaged result if the sample was significantly different enough from the averaged result. I used a 5 sample input filter to avoid spikes resetting the filter, and that worked very well. If a new voltage was applied, it only took 5 cycles of 0.165 Sec. to switch to the new input.



I have just about zero experience with filters, and this extra code I designed myself, but I was convinced that there are better methods available. I eventually found the Kalman filter, which is used a lot, and seemed perfect for my application.

Knowing nothing about it, I searched and found a very nice tutorial on Youtube that explains the Kalman filter very very well, even for total Dummies like myself. (look for Michel van Biezen - Special Topics - The Kalman Filter) I wrote a simplistic version of the filter based on his explanation but was not satisfied with the result. I also worked with another example I found, but that had the same problem. Neither of them worked with relatively quick input changes, as an example when switching from 2.5V to 5.0V. They both took several seconds to show the new value. Bummer!

So Kalman looked great on paper and in simulation (using Excel), but in reality, using my Volt Meter, it was a lot worse than the IIR filter I already used. However, I stole a concept from the Kalman filter, namely the Gain calculation. This is a dynamically calculated weighing factor, so I wrote some code around my IIR filter that accomplished what I wanted. Details are in the code. The result is fantastic, I think!

If I now connect the meter to a really stable voltage, like from a reference, 5 decimal digits are rock solid with only the 6th flopping around due to the noise. When I switch from one reference voltage to another one, within a few cycles, the voltage is updated and within a second or so the 5 digits are rock solid again.

When I tested the ability to set a voltage manually with a power supply (one of those I built in the other Forum posts), I was amazed how well the response to my tuning and the accuracy was, but also I saw how noisy my power supplies turned out to be. That's what you get when you are using a 24bit ADC with micro-voltage resolution. Oops!

In any case, with this filter in place, I also added a separate calibration function, to calibrate the Volt Meter to my Voltage Standard. I already use the (Zero) Null Volt calibration to null the input level, but I now also can tune the meter to my Voltage Reference.

The accuracy is now much better as well.

With all this done, I no longer need to reduce the number of decimal digits, so that code went into the bit-bucket. In the process of going through the filter designs, I also saw a way to get to a more stable and precise acquisition delay for the LTC2400. The delay is now dynamically calculated.

Because I needed to store the calibration factor for the voltage reference, I needed to store a floating point number in the EEPROM. Turns out, the library we already used has that feature, so I could clean-up the code, and send two more functions into the bit-bucket.

My dual-button press now drives the zero calibration and the reference calibration.

With all these changes and loop tuning, the main loop time is now about 165mS, so the display is very responsive.

V3.11 update:
I found a bug in the filter calculation, based on a rounding error. This is caused by diving a long by a float, and the result going into a long again. The solution was to use a float as the result. The compounded rounding error cause the filter result to be a little bit below the raw averaged input level, as you can see in the filter graph above.
Here is a shot of the result after I fixed the code:


The difference between the filter result after 1000 samples, and the calculated median value in Excel is now very, very small.

I also added the filter weight exponent multiplier to the display.

V3.12 update:
Because the linearity of the meter was not as good as I had hoped, I created a way to measure the value of the ADR4540B chip, and updated that factor in the code. To measure that voltage with my still un-tweaked meter, I calibrated the meter with my 5V0 reference, to get as close as possible. In order to do that, I created a special cal functions for all my reference voltages. Now you can select any of them, just update the cal factors.

To do this calibration, I let my 2 meters and the reference warm-up. Then I did a fresh null calibration, and a 5V0 calibration. I then measured the ADR reference voltage for a few minutes to get the best possible filtered voltage. I had already added a pin on my PCB to do that easily. This voltage was then used to update the constant in the code.  I did the same for my other meter. After updating the v-ref constant, I did the measurement again to verify and tweak if required. Finally, I did the ref voltage calibration again. After trying all 4 reference voltages, I had the best linearity result with the 10V reference.

After this calibration the linearity is even better.
Not surprisingly, the 10V is spot on :

Reference Voltage     Measured Voltage     Delta                   %
2.49993 V                  2.49953 V                  -400uV           -0.016%
5.00181 V                  5.001515 V                -295uV          +0.059%
7.50547 V                  7.50534 V                 -130uV           +0.0017%
10.00673 V                10.00672 V                -10uV            +0.00001%

That is good enough for me. If I have e need to really measure in the sub-volt range, I can still do the 2.5V cal.

The LTC chip can actually measure an input range of +/- 12.5% of the reference voltage, so what you can do with this feature is to null the meter with a voltage you want to monitor, say a 2.5V reference, and you can then measure the drift with uV resolution over time. (this includes the drift of the meter itself as well of course, but still)

Here is the latest version of my code: https://github.com/paulvee/6-digit-milli-voltmeter :

I am sure you will enjoy this tool!

Have fun!