Tag Archives: I2C

MPPT Solar Charger – Revision F

While the solar charger was originally intended to be used as a standalone device, it can just as well be integrated into other projects. In such applications, the user interface can be left away without sacrificing functionality other than the display and rotary encoder.

One project doing just that is MeshPoint, a rugged wifi hotspot for disaster and outdoor areas. But in most cases, the main application needs to be able to communicate with the solar charger. It wants to know if power is harvested, how full the battery is and so on. It needs to be able to enable and disable the various power outputs and maybe to control the fan. Or maybe it wants to make use of the charger’s real time clock and calendar. Or store some configuration data on the charger’s EEPROM. Possibilities are endless.

And when it comes to external communication interfaces, all the chargers up to revision D had little to offer other than the USB interface. Depending on your project, adding USB host functionality may be inconvenient or even totally out of the question. There was always the possibility to somehow tap the display’s I2C port but there was no extra header on the board for that.

Revision E has finally changed that. The charger now comes with two extra headers, one for I2C and one for SPI. As of now, the PIC acts as a master on both of those buses but slave functionality can be (and is planned to be) added in software. Hardware-wise it is even possible to update the charger’s firmware through those interfaces. I must admit that this is not the number one priority to be implemented but its nice to know that the possibility is there.

With the implementation of the USB bootloader next on the agenda I also upgraded the microcontroller to the newly introduced PIC18F47J53. It is extremely similar to the previously used PIC1846J50 and entirely pin compatible so it didn’t require any changes to the board. But it has twice as much flash memory (now 128kB) which allows for a feature-rich USB bootloader without runing into any issues memory-wise.

Another nice thing about it is that it has many more PWM modules which means that all four power outputs can now be PWM controlled. Think of LED lighting which is probably one of the main uses of those outputs. As a bonus, it also comes with an 12bit ADC which means four times the resolution on the temperature sensors.

When modifying the software to fit the new board and microcontroller, I noticed that my pin choice for the external SPI slave select signal was somewhat unlucky. I happend to pick one of the few pins without PPS (peripheral pin select) functionality which can be a problem if you try to use that interface with the charger configured as slave. The fix was easy, just swapped two pins but required a slightly modified board. That’s why the current version is now revision F.

Click here for the previous post in this series.

Low Power User Interface

20161007_userinterfacereva_001

As you may have noticed I’m quite busy working on the MPPT Solar Charger project. The latest version uses a 4 lines x 20 characters LCD that connects via I2C as well as a rotary encoder with a push button.

20160904_solarcharger_006

While the display got its own little board, the encoder connected directly with the solar charger where its signals aredebounced in hardware and then routed to the PIC.

20160905_solarcharger_015

After a few little fixes (see my last post) that worked but I had to use the signal intended for controlling the  backlight brightness for the display’s reset signal. I don’t want to run any more wires to the display and there are no spare pins on the PIC anyway so I had to come up with another solution.

20160905_solarcharger_017

And it was not very elegant that the display was powered off by just cutting the entire power supply. Having a digital enable/disable signal would be preferable

20161012_userinterfacereva_014

Design a universal User Interface

I then decided to design an new board that also includes the rotary encoder with all the necessary debouncing. That way I get a quite universal, easy to use user interface that I can use for other projects as well. That eliminates the need to design the same functionality (think debouncing) again and again. The size of the board is mainly given by the size of the display so there is plenty of board space.

20161012_userinterfacereva_011

So far so good but that doesn’t solve the actual problems yet. I still need a way to control both the backight as well as the reset signal without needing any more singnals. Since the display connects via I2C it was quite straight forward to use I2C communication for those purposes as well. My first thought was to add a I2C port expander just like I’ve done on the solar charger board. Those chips aren’t expensive but they typically provide 8 or 16 extra GPIO pins which seemed a bit wasteful. And PWM control of the backlight would require a great deal of I2C communication which puts a burdon on the microcontroller.

20161007_userinterfacereva_002

I also considered adding a sub-dollar PIC16 which could be programmed as an I2C slave and control both the backlight and the reset signal (as well as giving the option of getting the encoder input via I2C). But I wasn’t eager to write any software for this thing.

20161007_userinterfacereva_003

Requirement: Ultra Low Power State

Another design requirement was to keep this a very low-power design. To be sure: when the display and especially the backlight are on it will consume several milliamps to several dozen milliamps. Illumination doesn’t work without power, such is life. But when the user interface is not in use, we need a way to put it in a very low power state where the display as well as the rotary encoder is off but the push button still works in order to wake up the microcontroller. In that state the user interface must not use more than a few microamps. Furthermore the encoder signals must assume a high impedance state when the user interface is disabled.

20161012_userinterfacereva_012

My final solution was as follows:

  • Use the previous (backlight) PWM signal as an enable signal. Power can now always stay on. When the enable signal is high, the display is on, when enable is low, the user interface goes into its low-power state.
  • Add a dual I2C digipot to control both the reset signal as well as the backlight.

The reset signal is connected directly to the wiper of one of the two digipots. Of course any intermediate digipot values don’t make sense. The digipot should always be either at its minimum or its maximum.

The backlight brightness is controlled by the other digipot via an op-amp and a n-channel mosfet.

The debouncing is basically unchanged. Like in the solar charger design, a 74HC126 quad tri-state buffer is used together with some resistors and capacitors.

Since there are 4 gates on the 74HC126 and I only need to debounce 3 signals, there is still one gate left. I used that gate to power the display as well as the encoder and the encoder’s gates(without the push button which is always powered on). So the enable signal only controls the input of that gate. I thought that’s a nice and elegant solution. Well, if you think so too, think again. More on that later.

Testing

20161007_userinterfacereva_010

First of all nothing worked at all. I asserted the enable signal and nothing happened. Nothing was powered on. The error was soon found. I made a design error with the result that the respective gate was permanently in a high-impedance state. Somehow I had tied the gate’s enable input to ground instead of VCC. The mistake was corrected by cutting the ground connection and conecting the respective pin to the enable singal itself which was available from the pin right next to it. Not pretty (see below) but problem solved.

20161007_userinterfacereva_006

Then ok, power was there but when I tried to turn off the reset singal (i.e. pulling the reset signal high) nothing happened – again. Again, the reason was simple. The digipot’s upper terminal was floating instead of being connected to VCC. On the schematic the wire touched the respective pin but there was no connection. While that was really hard to notice on the schematic I should have noticed the problem when I laid out the board. There was obviously no connection to that pin.

20161007_userinterfacereva_007

This was a bit harder to fix than the previous error. There were no signals to cut but the digipot’s pins are much more closely spaced and the right signal was not to be found nearby. I ended up soldering a thin wire accross the digipot to a resistor that is connected to VCC on the upper end.

20161007_userinterfacereva_005

Even less pretty but problem solved anyway. Turning on the backlight basically worked as intended but the load was a bit much for the 74HC126. While it can supply up to 35mA according to the data sheet, its output voltage is nowhere near its positive supply rail when it supplies that much current. Even with a more modest 5mA load the gate’s output voltage was about half a volt below the input voltage. As a result, the display as well as all the other components only get to see about 2.8 volts instead of 3.3. This didn’t pose any problems so far but it’s not nice.

Another issue I noticed was that the LM321 op amp I had chosen is not a rail-to-rail op amp. The LM321 is inexpensive and I had quite a few left from another project so I used it without giving it much thought. I measured that the output voltage doesn’t get closer than about 1.4 volts of the positive supply rail. Since the n-mosfet used to control the backlight has a very low threshold voltage that didn’t pose a problem but the LM321 is not really a good choice here. While it does work from as little as 3 volts it’s made for split supply and higher voltages, say plus/minus 12 volts. I’ll use a low-voltage rail-to-rail type next time.

Software

Getting any MIDAS I2C display to work is a nightmare. They make nice, pretty and affordable displays. That’s why I buy them. And once you’ve managed to properly initialize them they are easy to work with. But getting them initialized with the right settings is always a lengthy trial-and-error process. I’ve ranted about that before but I feel the need to do it once again.

The entire initialization sequence needs to be written in one single I2C transmission. At least for me it didn’t work otherwise. The data sheet says absolutely nothing about that.

userinterfacestartupsequence

The data sheet doesn’t even mention the initialization sequence. Nor the timing of the reset singnal. The reset signal needs to be pulled low for 10ms. One then needs to wait for 5ms before the initialization sequence is sent. There is not one word about that it in the current datasheet. There is an older version in the net somewhere that mentions those timing issues and at least gives a sample initialization sequence.

userinterfacestartupsequence2

There are various contrast settings that must be set properly via I2C commands but there is very little information on those commands in the data sheet. I wrote an email to their customer support asking if there is a list of commands the display supports. That was more than a month ago and I still didn’t get any answer. As so often, google was my friend and others have faced similar problems before so I managed to find some commands that make the display work properly. I’m sending 10 or so bytes to the display but for most of them I have not a clue what they actually mean or what they are supposed to do. And the commands for this black-on-white display are different to the (otherwise identical) white-on-blue display. Have fun…

Debouncing

I’ve devoted one of my very first posts to the much-neglected issue of debouncing. The logic is pretty much the same here except that I use different (non-inverting) gates here. The reason for that is the need for 3-state outputs as mentioned before.

debouncing_pushbutton_press

Unfortunately the 74HC126 doesn’t have schmitt-triggered inputs so I need to implement the necessary feedback myself via the 680k resistors.

Debouncing something like a pushbutton is quite easy. Just use a relatively long time constant (i.e. large resistors and capacitors) and all will be fine. That will introduce some delay in the output signal but as a rule of thumb anything below 50ms is not noticable. And a time constant of a few milliseconds is totally sufficient so you might end up with something like 10ms of delay. No human user will ever notice it and it’s more than enough to perfectly debounce any switch that I’ve seen.

debouncing_pushbutton_release

With rotary encoders things get much trickier. The basic circuit is exactly the same but choosing the right component values is more difficult. You can’t press a button more than a few times a second but a rotary encoder might produce pulses just milliseconds or even less apart.

debouncing_ccw

So you face the challenge of debouncing the signal while still letting relatively fast transitions pass. If you use a time constant of a few milliseconds like with the push button you will not be able to detect when the encoder is turned quickly.

debouncing_cw2

The data sheet of the Burns encoder I’m using here recommends using two 10k resistors together with a 10nF cap. I’ve increased the value of one resistor to 22k but have otherwise followed that recommendation.  So the time constant is now a few hunderd microseconds.

debouncing_ccw2

Toghether with the positive feedback via the 680k resistors that works very well. I expected to tweak those values a bit but found that they work so well that I just leave them as they are.

debouncing_clean

I’ve inserted plenty of scope screenshots here to give you an idea of what the undebounced and debounced signals look like. Even with the same encoder there’s plenty of variation as you can see. But with the hardware debouncing applied here we get a clean, digital output singal nevertheless.

debouncing_typical

Power consumption

Very low standby power consumption was one of the key design goals. When in low-power mode only the push button pull-up and the 74HC126 is supplied with power. Everything else, the display, the op-amp, the digipot and the pull-up for the rotary encoder are all completely powered off.

As long as the push button is not pressed, no power flows there so the only power consuming device is the 74HC126. Its datasheet specifies a maximum static current consumption of 8 microamps (worst) at room temperature.  There’s no typical figure so that’s all the information I had. When I measured it, this is what I got.

20161012_userinterfacereva_018

That’s 4 nanoamps  at 3.3 volts which is nothing really. Actually the measurement fluctuated between about 0 and 15 nanoamps. I thought something is not hooked up correctly but when I pushed the button the current jumped to 326 microamps which is just what I expected since there is a 10k pull-up resistor conected to that button.

20161012_userinterfacereva_019

So the measurement works and it seems that the 8 microamps given in the data sheet are just a very conservative worst case. I mean CMOS is close to zero-power when static and only a single tri-state gate is active so essentially no current flows. Cool.

Next steps

After the two hardware fixes this user interface works quite as intended. Using the spare gate to supply the rest of the circuit with power was not as good an idea than I thought. On the positive side the debouncing works just perfect with the chosen component values. And as mentioned, the op-amp was not a great choice for the job at hand.

But I like the look and feel of this universal user interface and think the overall concept is good. So there will be a Rev B of this board with those issues solved.

Here you find the eagle files as well as PDFs of the schematic and board. The two errors are already corrected but I haven’t actually built them so check carefully yourself. I’ve also re-exported the gerbers so they, too, already include the fix.

Ultrasonic Anemometer Part 24: New Microcontroller and Software Controlled Gain

It’s been almost three weeks since my last post and some further progress has been made. I’ve upgraded the microcontroller and can now control the gain of the second amplifier stage in software. But let’s look at the changes in some more detail.

20160625_AnemometerDriver_008

New microcontroller: PIC32MX250

I mentioned last time that I was running out of code space, i.e. flash memory on the PIC32MC220. That particular model has only 32k of flash and I’m using the free version of the XC32 compiler. That free version only performs limited amounts of optimization and therefore produces rather large and slow code compared to the standard and professional version. But the other two are rather costly (around USD 500 and 1000, respectively) and are not really an option here.  And at least so far I’ve always had all the speed I needed so the only issue was flash memory.

So the straight forward solution was to upgrade the microcontroller to an otherwise identical model with more memory. The 250 I’m using now has four times more of both flash (now 128k) and ram (now 32k). I unsoldered the old chip using a hot air gun and soldered in a PIC32MX250 in its place. Now we have all the flash we need to continue our journey.

20160625_AnemometerDriver_005

Getting I2C to work

My design uses a dual op amp for the necessary amplification of the received signal. The first stage is ground-referenced and uses a simple resistive divider to set the gain (currently set at 11).

The second stage is biased to 1.65V (i.e. half way between ground and 3.3 volts) and has its gain controlled by a I2C digipot. The idea is to have the PIC control the digipot so it can adjust the gain as needed to compensate for any effect that might change the amplitude of the received signal.

So in order to control our digipot we first need to get the I2C interface working on the PIC. There are two (identical) I2C peripherals on the PIC32MX2xx and this design uses one of them (#2) exclusively to communicate to the digipot. The other one (#1) is then available to interface to the outside world.

I’ve written a set of simple functions to use the I2C interface. So far I’ve only implemented  master transmit mode since that’s the only thing we need here.

  • void app_i2c_internal_init(void);
  • bool app_i2c_internal_write(uint8_t address, uint8_t *data, uint8_t length);
  • void app_i2c_internal_writeDigipot(uint8_t value);

The functions are entirely non-blocking so they can be called from within the interrupt service routines that do the measurement.

Fixing a design bug

Unfortunately, I made a mistake in the schematic when I referenced the feedback loop of the second op amp stage to ground instead of the mentioned 1.65 volts. Now let’s look at what that did to the signal. First, below is the signal with the digipot at 0 (of 256). The gain is set to one and all is well.

AmpError_Pot0

But at a setting of 64, some clipping starts to occur as shown below.

AmpError_Pot64

With the gain turned up to 4 with a digipot setting of 192 things get really uggly with a completely unrecognizable output signal.

AmpError_Pot192

I was extremely lucky that the correct signal was available right next to the pin where I needed it so all I had to do was to cut the ground connection with a carving knife and to connect the correct signal using a tiny bit of wire.  The change is hardly visible on the photo below. Can you spot it? It’s almost at the center of the photo at pin 5 (bottom right) of the small 8-pin IC. It now connects to the 10k resistor at the bottom instead of the ground plane on its right.

20160626_AnemometerDriver_009

Controlling the gain in software

With that error fixed we can now set the gain as we please. Here some examples. Yellow is the (unamplified) input signal whereas green is the output of the (fixed) first amplifier stage.  The purple signal is calculated as the difference between the DAC+ and DAC- signals. Those two signals is what we feed into the PIC to be measured.

With the gain at 1 (digipot at 0) we get a about 2.6 volts peak-peak. A bit little but sufficient.

Amplifier_Pot0

This is probably about what we want. Digipot at 50 resulting in a peak-peak amplitude of a bit above 3 volts.

Amplifier_Pot50

Needless to say that we can jerk the gain up far more than that if we want.  Below is an example where a gain of 4 results in a seriously clipped signal.

Amplifier_Pot192

Now let’s look a a situation where such a high gain might actually be useful. In the example below I’ve lowered the input voltage to only 5 volts. As a result the received signal is only 90mV in amplitude instead of the 220mV seen above. That’s easily compensated with a bit of help from our digipot. With a setting of 170 we get a nice, clean 3 volt output signal.

Amplifier_Vin5_Pot170

Calculating the necessary gain

For the examples above I’ve explicitly set the gain in the code. But what we really want is to have the software calculate and set the necessary gain automatically.

So I’ve implemented a simple algorithm to do so. I set some target value for the peak-to-peak amplitude of the signal measured by the DAC. Knowing the amplitude and digipot setting of the last measurement I calculate the optimal gain and digipot setting for the next measurement.

AmplitudesGain1

Above is the signal without this automatic gain setting in place (gain=1). As you can see, the peak-to-peak amplitudes vary quite a bit between transducer pairs. So the gain calculations, too, work on a transducer pair basis. Each direction gets its own setting to compensate for different transducer sensitivities.

Below is a screenshot of the result. Notice how now all transducer pairs have identical (and somewhat larger) amplitudes.

EqualAmplitudes

That’s it for today. Click here for my next post.