www.postcogito.org
...sine propero notiones |
You are here: Kiko > PostsInEnglish > EnBlogEntry2008Feb24A | Printable | topic end |
and the real time clock, but no events yet. complete: we already have the TWI EEPROM, LCD, batteries and buzzer. |
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:
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;
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.
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.