Versão em Português

Front view of the almost-complete device
The device in action showing voltage, frequency
and the real time clock, but no events yet.
The almost complete device, from above
View from above showing the hardware is almost
complete: we already have the TWI EEPROM, LCD,
batteries and buzzer.

Advances with the Power Glitch Monitor

Rec 24-feb-2008 21:31

Last time I was surprised that most things worked just fine on the first time. This time I did stumble into a few minor but annoying pitfalls.

To get the LCD running, I adapted an old set of LCD routines I had from a previous project so that they didn't need to use the RW signal. Properly adjusting the delay loops, I quickly got it working. Then I restructured the routines so that a task in the firmware's main loop takes care of moving bytes from a buffer to the LCD, so printing to the LCD is actually done by stashing data in this buffer -- the advantage being that we can print as fast as possible without needing to care about timing issues.

I was also very pleased with the fact that the LCD module used only meager 0.8mA, contributing very little to the device's overall consumption of just 8mA (meaning we're still below the 10mA budget).

But after I added the LCD, the first pitfall presented itself: the frequency measurements started to exhibit a strange variation every now and then -- it was 60.000 in a moment, then something near 63Hz, then a very low number, then back to normal. I had added accumulators to count how many "fast" and "slow" semicycles we had (that is, semicycles with periods +/- 10% larger than the expected 1,042 Timer1 ticks) and noticed that the "fast semicycle" counters incremented when it happened.

Overconfident with the good results from the initial assembly, I almost dismissed the problem as yet another oddity in the mains here, much like the flattened out wave. Then it got worse, then it got better and I got puzzled. Then I thought it was a software problem and quickly discovered that I went away when I disabled the LCD code.

A few hours of frustrating debugging led me to one particular assembler instruction: the one that moves the low 4 bits of the byte to the LCD data bus. Then it struck me: the D7 line is physically just next to the MCU's ICP pin, so my problem was electrical switching noise: whenever the character being sent to the LCD had bit 3 on and the ICP pin was zero, it triggered the ICP edge detector. Apparently, the AVR's event capture circuitry is much too sensitive.

In retrospect, perhaps the right thing to should be to move the entire LCD data bus elsewhere, say, to the mostly-unused SPI pins (I had intentionally left them unused so we could use In-System Programming and for a future expansion to use SD cards as storage). But I was tired and not in the right mood for extensive resoldering, so I simply moved the D7 line to the PD2/INT0 pin. This made the bit manipulation in the LCD routines a bit clumsy and ruined I plan I was entertaining to use INT0 as a second serial to get measurements from my UT60E serial-capable multimeter and add a very accurate self-calibration system.

Anyway, after that quick kludgy fix, everything worked just fine. Gotta remember that when designing the printed circuit board.

I also added the 24C512, a 512 kibibit (64KiB x 8bits) TWI ("two-wire interface", the name Atmel uses because I2C is trademarked) EEPROM chip. The TWI bus requires pullup resistors, but I didn't bother to add them because we could use internal pullups in the AVR pins.

While I was in the development cycle (write-burn-test) of the interrupt-driven TWI service routine, I spotted a potential timing problem: byte writes to the EEPROM can take up to 10ms to complete, so writing 32 bytes (the size of the data structure that represents an event) may take in excess of 320ms or almost 40 semicycles. This means if we have two events closer than 40 semicycles, we might be unable to save them.

Rereading the 24C512's datasheet I found an easy solution: the chip is capable of receiving a whole 128-byte page in a single TWI bus transaction and saving the whole page within a single 10ms period. So I wrote the event storage routines like this: I created an 8-entry (256 byte) event queue in RAM. Whenever we get 4 queued events (exactly one 128-byte page), the system flushes the data to the TWI EEPROM and frees the queue entries.

Four events take at least 8 semicyles to happen (one event start and one event finish four times in consecutive semicycles), but with this page write feature, we can flush 4 pending events at a time to the EEPROM in less than 15ms (the 10ms EEPROM time plus bus transmission time) or two semicycles. In other words, we can store events four times faster than they are generated, which allows us to cope with fast event trains. The excess 128 bytes in the queue takes care of buffering events in case the EEPROM is busy writing a previous page or recalling event data.

This also means that it should be safe to use an EEPROM chip with smaller page size as long as the store-to-generate speed ratio is grater than one. The 24C256 (32KiB x 8), with a 64-byte page, should be usable, yielding slightly more than 2-to-1. The 24C128 (16KiB x 8) however, with a 32-byte page, yields just slightly better than 1-to-1, which is uncomfortably close to critical but possibly doable (newer EEPROMs have faster page write times, sometimes as low as 5ms). But the same argument says that 64-kibibit EEPROMs or smaller will probably get us into trouble.

Here's the version 0.4 firmware with the following added features:

  • HD44780 LCD support in 4-bit mode
    • Beware of the changed pin assignments: D7 is now on PD2 -- see discussion above;
  • Interrput-driven I2C/TWI EEPROM page/byte read/write support for event data storage;
  • Records power failure events
  • Optionally records power surges and sags;
  • Optionally records frequency too far from the expected 60Hz
  • Buzzer beeps when power failure starts and finishes, clicks when there's a sag/surge or frequency out of range;
  • Allows to recall past events stored in EEPROM

New commands:

  • event: with no arguments tells how many stored events we have; with a numeric argument recalls and prints that event;
  • clear: clear all stored events (zeroes the counters, actually, so data gets gradually overwritten);
  • param: with a single argument, displays parameter value. With two arguments, sets value;

These commands were used during development/debugging and will be available only if you define HAVE_XTRA_CMDS:

  • start: artificially starts an event: the elapsed time counter starts and the current value of the RTC is copied to the event start time. Argument is the event type;
  • finish: finish an event, flusing it to EEPROM when we fill a page;
  • poke: specify address and value to write it directly to the TWI EEPROM;
  • fill: specify start address, fill value and increment. Fills a single TWI EEPROM page;
  • dump: specify start address and optional size. Dumps data in hex.

Some global variables can be changed by the params command:

  • 0: Bit mapped flags:
    • 1: Frequency variations also generate events;
    • 2: Voltage sags or surges also generate events;
  • 1: Raw ADC minimum value for power failure: if less than this, start a new power failure event. If more than this, cancel a power failure event if one was in effect;
  • 2: Raw ADC minimum value for voltage sag: if less than this but more than the value at parameter 1, we increment the "sag_semicycles" counter;
  • 3: Raw ADC maximum value for voltage surge: if more than this, increment the "surge_semicycles" counter;
  • 4: Raw ICR mininum value: if less than this, increment the "high frequency semicycles" counter;
  • 5: Raw ICR maximum value: if more than this, increment the "low frequency semicycles" counter;
  • 6: Beeps for this many millisseconds when power fails
  • 7: Beeps for this many millisseconds when power comes back;
  • 8: Beeps for this many millisseconds on power sags;
  • 9: Beeps for this many millisseconds on power surges;
  • 10: Beeps for this many millisseconds on low frequency;
  • 11: Beeps for this many millisseconds on high frequency.

The event generation system is working fine for power outages, but I'm not satisfied with the frequency variations/sags/surges event recordings. Since it is a rather simplistic edge detector, noise in the mains line sometimes generate dozens of events. I plan to improve this in the next version.

There are other features still to be implemented: battery monitoring, for instance. But things are mostly working and I'm reasonably happy with the results. Code size is a tad larger than I expected: at 6,496 bytes, we're already at 82% of the maximum size (8KiB - 256 bytes for the bootloader). The code could surely use some refactoring and modularization. I'll leave the device running for a couple weeks to see what kinds of event I get and how satisfied I become.

Kiko > PostsInEnglish > EnBlogEntry2008Feb24A
Creative Commons License   The content of this site is made available under the terms of a Creative Commons License, except where otherwise noted.
  O conteúdo deste site está disponibilizado nos termos de uma Licença Creative Commons, exceto onde dito em contrário.