Here I’ll try to explain the workings of the software. It’s written in C using the student edition of Microchip’s MCC16 compiler. I’m not C expert so this has been a bit of a learning experience. I’ve years of experience programming in Coldfusion and also Javascript and Actionscript, but C was a new one for me.
I started off with BoostC but it drove me crazy as the documentation seems to assume that you are already a C expert. This is especially true of the libraries which all seem to be half written examples that you can use to develop your own. That’s great if you have loads of C experience. I don’t and I just found it plain hard work. The Microchip compiler documentation on the other hand explains a lot more about what’s going on, the libraries are all available as source code which is great and in general I found it a lot easier to get going with. The Microchip compiler also seems to be a lot more “standard” which means that examples and references for other C ports seem to work with little or no changes.
This is not to say that BoostC isn’t any good, loads of people recommend it on various forums, I even bought it. It’s just that for me right now it seems like hard work.
Anyway, on to the workings of the clock.
There are two routines
Interrupt;
- Writing to the MAX6956
- Multiplexing the LEDs
- Debouncing inputs
- 1hz time keeping interrupt from RTC
- Periodic time check from RTC
Main line;
- Processing inputs
- Calculating LED display
- Testing for alarm condition
Display
The 60 clock face LEDs are split into three groups of 20 (a sector), this is because the MAX6956 has 20 outputs in PDIP package. The display is multiplexed using three PIC output pins to switch the common anode to each sector via a transistor.
The interrupt routine runs at around 150Hz and writes out a full sector via i2c to the MAX6956 each time it is called, incrementing the sector counter each call. This results in around a 50hz refresh rate for each sector.
Internally the display represented as 2 buffers of 3 sectors of 3 chars (8 bit)
unsigned char gbl_ledBuffer[2][3][3];
Each bank represents a sector of the clock face (0-19, 20-39, 40-59) the last four bits of the 3rd char in each bank is wasted.
The two buffers are used to double buffer the display so that I can happily write to one whilst the interrupt routine reads from the other. I use a global var semaphores to hold the index of the buffer being written to and also to flag when it is safe to flip buffers.
Input
I tried to maintain as simple an interface as I could, it’s a clock after all.
There are 5 buttons not including the emergency reset.
Time – hold to set.
Alarm – hold to set, quick push to arm.
Snooze – push when alarm on for 9 more minutes.
Hours – push or hold in conjunction with Time or Alarm to set.
Mins – push or hold in conjunction with Time or Alarm to set.
The system uses a software debounce routine based on Scott Dattalos example code.
It uses an array as a ring buffer to periodically read the status of portb and can cope with 8 inputs simultaneously. The inputs are sampled at around 150Hz as part of the interrupt routine that handles the LED driver multiplex. Each sample is written to the next location in the array, looping at the end. A button being held down should fill the array with 1’s in it’s bit position in around 80ms plus or minus a bit of bounce which seems fast enough to allow for quick taps of the buttons whilst still being reliable.
//Global : ring buffer for debouncing
unsigned char gbl_dbounce_buff[12];
//Global : current byte to write
unsigned char gbl_dbounce_idx;
void set_dbounce(){
//De-Bounce inputs (portB.1.2.3.4)
//Put not portb state into the ring buffer : We only really need 1st 4 bits
gbl_dbounce_buff[gbl_dbounce_idx] = ~PORTB;
//Increment the counter
gbl_dbounce_idx++;
//reset de-bounce ctr
if(gbl_dbounce_idx >= DBOUNCE_LEN) gbl_dbounce_idx = 0;
}
The main line code which runs a lot more slowly calls a function getDebounce() that AND’s all the arrays values together. The idea being that each input represents a particular bit of the result so we just use a mask to find the status of the button we want to use.
An added bonus is that if we store the result of the previous time we called getDebounce() then next time we call it we can very simple determine if the status of any of the inputs has changed without having to test each one in turn. eg. if(old != new) then something has changed.
There are 60 display LEDs plus 2 indicators to show status of alarm set, AM/PM. On the later version I used an RGB LED and used the extra colour to show that we are in time set mode.