📜 ⬆️ ⬇️

Matrix clock

Sometimes an idea in your head ripens over the years. The subconscious mind absorbs the interesting things, lays it down on the shelves, accumulates the critical mass, until all this finally results in something interesting.

It happened at that time. In the space-time continuum, a beautiful, but already useless “Handbook of mini- and micro-computers”, a red LED array of 5x8 and the birthday of a programmer friend came together.

How can you not make a watch? And not just binary, but some pozakovyristey?
')


purpose


The main goal was not just to show the time, but to show it so that I had to solve a small puzzle, have fun between diving into the code. The brain for a moment falls out of the usual decimal everyday life, so that after a few thoughtful math seconds come back, watch the time on the computer and continue programming.

Iron


Then I also didn’t have the habit of documenting such simple schemes (which I regret very much), so I just took out my favorite tiny2313 from the desk drawer, warmed up the soldering iron, and rushed!

Approximate scheme

Transistors, if I'm not mistaken - KT361 from old stocks. The findings of the matrix are connected so that it is more convenient to solder. If you still do not understand what is happening here - I can only recommend a great site respected DiHalt : easyelectronics

As a timing chain, I used an internal RC frequency generator, which, of course, cannot be done, but the past experience of creating a room clock showed that calibration by week deviation can achieve an accuracy of five minutes per year. Although, maybe I was just lucky.

Display modes


First of all, naturally, I implemented a binary mapping . Rather, binary decimal ( BCD ). So it is possible to read information from the clock a little faster, so any housewife who is accustomed to recalculate the change in the store will cope with such a display after a small workout (I taught my favorite humanitarian to count on fingers up to 1023, which she is very pleased about).


Small explanatory animation

While visiting the telecommunications museum, I noticed how numbers are easily coded in Morse code. Replaced dots with LEDs on, and dashes with disabled ones. It turned out the display in Morse code - very simple and clear. Honestly, this is my favorite mode! It still makes you think, but the smooth flow of time from one rank to another just fascinates.


Small explanatory animation and Morse code excerpt.



Well, and then I seriously wondered if it was possible to emulate an analog clock with just 40 LEDs? After all, they have a remarkable advantage - they are clear to all. Even the corner of the eye can determine the approximate time, well, a closer look allows you to find out the exact time.

A few stripped sheets, a bunch of tried-on code, and an analog display solution was found! I am not sure that the housewife with a swoop will understand everything, but the goal is achieved: after a small workout, you can determine with a cursory time with an accuracy of up to three minutes, a closer look - up to 12 seconds.


Small explanatory animation


On the clock - midnight. To visually highlight the "hour" dial - the current hour is shown by the disabled LED on the luminous circle.

To look at the clock was even more interesting, the time display method changes at midnight (optional). Time and display are configured using a pair of buttons on a simple algorithm, a block diagram of which is attached. True, the owner of the watch noted that the easiest and most convenient way to set the exact time is to turn them on at midnight.

After that, it was time for a long and painful cutting and gluing of the directory. It was necessary to do everything literally page by page, traces of hackwork immediately appeared in the form of waviness. It took almost a week! The cut-out repeated the outline of the matrix, the board, the protruding parts, the word was made exactly so as to carefully wrap up all the electronics.

Buttons and algorithm settings are hidden in the rear turn. Stickers on the buttons for the "blind" (looking only at the display) settings have added a new owner of the clock.

Instructions for use

Of course, some ideas for improvement come after the device is given to the customer . The contact of the power supply was not stable enough, it would be great to close the matrix with a light filter and make emergency power from a removable battery ... But this is next time, otherwise everything works fine.

But the story about the clock of their current owner FFormula , recorded at my request.


Future plans


There was a place for a wall clock, a DCF77 receiver was bought, SNTP synchronization was assumed as a reserve (with the help of ESP8266 ), this project will take into account past experience, think of new insidious time display methods ... For example, make the main UNIX time mode that will immediately solve the problem date display :)

Caution code!
// 2008.09.02 18:05 -> 2008.09.23 22:39 // Fuse bits: div8 disabled #include <avr/io.h> #include <avr/pgmspace.h> #include <avr/interrupt.h> #define F_CPU 8000000UL // 8 MHz #include <util/delay.h> #define countStart 9 // manually adjusted after one week #define typeCount 3 volatile unsigned char sec, keys, oldkeys, colSet, type, automatic = 0xff; volatile unsigned int time; typedef struct bitfield{ unsigned char point:1; unsigned char settings:1; unsigned char blink:1; }bitfield; bitfield flag; volatile char col = 0; #define C3 PD2 #define C5 PD3 #define C4 PD4 #define C1 PD5 #define C2 PD6 unsigned char c[5] = { (1<<C1), (1<<C2), (1<<C3), (1<<C4), (1<<C5) }; #define R2 PB7 #define R4 PB6 #define R1 PB5 #define R3 PB4 #define R7 PB3 #define R5 PB2 #define R8 PB1 #define R6 PB0 unsigned char r[8] = { (1<<R1), (1<<R2), (1<<R3), (1<<R4), (1<<R5), (1<<R6), (1<<R7), (1<<R8) }; #define CC ((1<<C1)|(1<<C2)|(1<<C3)|(1<<C4)|(1<<C5)) #define B0 PD0 #define B1 PD1 unsigned char display[5]; unsigned char clock[4]; //**************** // Time calculation and visualisation //**************** ISR (TIMER0_OVF_vect) { TCNT0 = countStart; time ++; col++; if (col == 5) col = 0; PORTD |= CC; PORTD &= ~c[col]; PORTB = display[col]; if (time == 2000) { time = 0; if (flag.point) // half second { flag.point = 0; if (colSet>1) sec++; if (sec == 60) { sec = 0; clock[0]++; if (clock[0] == 10) { clock[0] = 0; clock[1]++; if (clock[1] == 6) { clock[1] = 0; clock[2]++; if ((clock[2] == 4)&&(clock[3] == 2)) { clock[2] = 0; clock[3] = 0; if (automatic) type++; } if (clock[2] == 10) { clock[2] = 0; clock[3]++; } } } } } else { flag.point = 1; } } } //**************** // Init section //**************** void Init (void) { ACSR = (1<<ACD); //Turn Off ANA_COMP //clock[3] = 1; //clock[2] = 2; //clock[1] = 0; //clock[0] = 0; //type = 1; //flag.settings = 0; //flag.blink = 0; colSet = 4; oldkeys = 0b11; //DDRA = 0; //PORTA = 0xFF; DDRD = ~((1<<0)|(1<<1)); PORTD = 0b11; DDRB = 0xFF; TCCR0B = (1<<CS01); // presc. 8 TCNT0 = countStart; // 256-8 = 250; 8*250/8 000 000 = 0.00025 TIMSK = (1<<TOIE0); sei(); } //**************** // Keyboard and time setup //**************** void CheckKeyboard (void) { keys = PIND&0b11; if (keys != 0b11) { _delay_ms(5); if (((PIND&0b11) == keys)&&(keys!=0b11)) { oldkeys = keys; } } else if (oldkeys != 0b11) { if (oldkeys == 0b10) { if ((flag.settings == 0)&&(flag.blink == 0)) { flag.blink = 1; } else if (flag.blink) { flag.blink = 0; flag.settings = 1; colSet = 0; } else if (flag.settings) { if (colSet == 4) { colSet = 0; } else if (colSet == 2) { colSet=4; flag.settings = 0; } else { colSet++; } } } else if (oldkeys == 0b01) { if (flag.blink) { flag.blink = 0; automatic = ~automatic; } else if (flag.settings) { clock[colSet]++; if (colSet < 2) sec = 0; if ((colSet==0)&&(clock[0]==10)) { clock[0] = 0; } else if ((colSet==1)&&(clock[1]==6)) { clock[1] = 0; } else if ((colSet==2)&&(clock[2]==10)) { clock[2]=0; clock[3]++; } else if (((colSet==2)&&(clock[3]==2))&&(clock[2]==4)) { clock[2]=0; clock[3]=0; } } else { type ++; } } oldkeys = 0b11; } } //**************** // Service functions //**************** unsigned char bcd (unsigned char a) { return (((a / 10) << 4)|(a%10)); } unsigned char transform (unsigned char a) { unsigned char temp, i; temp = 0; for (i = 0; i<8; i++) if (a & (1<<i)) temp |= r[i]; return ~temp; } //**************** // BCD Visualisation //**************** void DigitalCode(void) { display[4] = transform(bcd(sec)); if ((colSet == 0)&&(flag.point)) { display [3] = 0xf0; } else if ((colSet == 1)&&(flag.point)) { display [3] = 0x0f; } else if ((flag.point)&&(flag.blink)) display[3] = 0xFF; else display[3] = 0; display[2] = transform((clock[1]<<4)|(clock[0])); if ((colSet == 2)&&(flag.point)) display[1] = 0xFF; else if ((flag.point)&&(flag.blink)) display[1] = 0xFF; else display[1] = 0; display[0] = transform((clock[3]<<4)|(clock[2])); } //**************** // Analog clocks //**************** unsigned char hours [12][3] PROGMEM = { {(1<<6)|(1<<5)|(0<<4)|(1<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)}, {(1<<6)|(1<<5)|(1<<4)|(0<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)}, {(1<<6)|(1<<5)|(1<<4)|(1<<3)|(0<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)}, {(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(0<<2), (1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)}, {(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(1<<5)|(1<<4)|(1<<3)|(0<<2)}, {(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(1<<5)|(1<<4)|(0<<3)|(1<<2)}, {(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(1<<5)|(0<<4)|(1<<3)|(1<<2)}, {(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(0<<5)|(1<<4)|(1<<3)|(1<<2)}, {(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (0<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)}, {(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2), (0<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)}, {(0<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)}, {(1<<6)|(0<<5)|(1<<4)|(1<<3)|(1<<2), (1<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2), (1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)} }; unsigned char mins [20][2] = { {0, (1<<4)}, {0, (1<<3)}, {0, (1<<2)}, {0, (1<<1)}, {1, (1<<1)}, {2, (1<<1)}, {3, (1<<1)}, {4, (1<<1)}, {4, (1<<1)}, {4, (1<<3)}, {4, (1<<4)}, {4, (1<<5)}, {4, (1<<6)}, {4, (1<<7)}, {3, (1<<7)}, {2, (1<<7)}, {1, (1<<7)}, {0, (1<<7)}, {0, (1<<6)}, {0, (1<<5)} }; unsigned char mmins[3] = { 0, (1<<4), (1<<5)|(1<<3) }; unsigned char anal (unsigned char line) { unsigned char res, min, mmin, hr; res = 0; if (((sec / 12) == (4 - line))&&flag.point) res = 1; if (flag.settings) res = 0; if (flag.point) if (flag.blink) res = 1; else if ((colSet == 0)&&(line > 1)) res = 1; else if ((colSet == 1)&&(line % 4)) res = 1; else if ((colSet == 2)&&(line <3)) res = 1; min = (clock[1]*10+clock[0]); mmin = min % 3; min = min / 3; hr = (clock[3]*10+clock[2]) % 12; if (mins[min][0] == line) res |= mins[min][1]; switch (line) { case 1: { res |= pgm_read_byte(&(hours[hr][0])); break; } case 2: { res |= mmins[mmin]; res |= pgm_read_byte(&(hours[hr][1])); break; } case 3: { res |= pgm_read_byte(&(hours[hr][2])); break; } } return transform(res); } void AnalogCode(void) { display[0] = anal(0); display[1] = anal(1); display[2] = anal(2); display[3] = anal(3); display[4] = anal(4); } //**************** // Morse Visualisation //**************** unsigned char morseArray[5][10] PROGMEM= { {0, 0, 0, 0, 0, 1, 1, 1, 1, 1}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 0, 0, 0, 0} }; unsigned char morse(unsigned char line) { unsigned char res, temp, temp1; temp = bcd(sec); temp1 = ((temp & 0xf0)>>4); temp &= 0x0f; res = (pgm_read_byte(&(morseArray[line][temp]))<<0)|(pgm_read_byte(&(morseArray[line][temp1]))<<1); res |= (pgm_read_byte(&(morseArray[line][clock[0]]))<<3)|(pgm_read_byte(&(morseArray[line][clock[1]]))<<4); res |= (pgm_read_byte(&(morseArray[line][clock[2]]))<<6)|(pgm_read_byte(&(morseArray[line][clock[3]]))<<7); if (flag.point) if (flag.blink) res |= (1<<2)|(1<<5); else if (colSet == 2) res |= (1<<5); else if (((colSet == 0)&&(line>1))||((colSet == 1)&&(line<3))) res |= (1<<2); return transform(res); } void MorseCode(void) { display[0] = morse(0); display[1] = morse(1); display[2] = morse(2); display[3] = morse(3); display[4] = morse(4); } //**************** // Type selection //**************** void PrintPrepare (void) { // Prepare display[]; if (type == typeCount) type = 0; switch (type) { case 1: { DigitalCode(); break; } case 2: { AnalogCode(); break; } case 0: { MorseCode(); break; } } if ((flag.blink)&&automatic) if (flag.point) display[0] &= ~((1<<R8)|(1<<R7)); else display[0] |= ((1<<R8)|(1<<R7)); } void main(void) { Init(); for (;;) { CheckKeyboard(); PrintPrepare(); } } 

Source: https://habr.com/ru/post/364715/


All Articles