// hourglass kit with 57 LEDs driven by STC15W201 (this is a pruned version of hourglass.c: it easily fits in an STC15W201) // it has different patterns to the original kit-supplied hourglass animation // ...and with better multiplexing, and switch debouncing. // if you have the STC15W204, which has 4K flash memory instead of the -201's 1K, see hourglass.c - it has many more patterns // ceptimus June 2022 // compile with sdcc hourglass-201b.c // flash to chip using stc8prog -p /dev/ttyUSB0 -r 50 -e -f hourglass-201b.ihx // (omit -r 50 if you don't have an auto-powercycle adapter) // // LEDs are connected in a 5x6 matrix with two LEDs of opposite polarity at each node: so could do 5x6x2 = 60 LEDs, but the last three are missing. // LED numbering and multiplex stage: // // -P10 +P10 -P11 +P11 -P12 +P12 -P13 +P13 -P14 +P14 -P15 +P15 stage // +P30 1 11 21 31 41 51 0 (P30 is also RXD) // -P30 6 16 26 36 46 56 1 // // +P31 2 12 22 32 42 52 2 (P31 is also TXD) // -P31 7 17 27 37 47 57 3 // // +P33 3 13 23 33 43 53 4 // -P33 8 18 28 38 48 5 // // +P36 4 14 24 34 44 54 6 // -P36 9 19 29 39 49 7 // // +P37 5 15 25 35 45 55 8 // -P37 10 20 30 40 50 9 // // The low-numbered LEDs are the bottom of the hourglass, using the standard kit-supplied program. // // Push button on P32. P30 is also RXD, P31 also TXD. P54(RST) and P55 unused. // // Physical layout: // // 51 1 // 45 8 // 52 40 14 2 // 46 36 19 9 // 53 41 33 23 15 3 // 47 37 31 26 20 10 // 54 42 34 30 29 28 24 16 4 // 48 38 32 27 21 11 // 55 43 35 25 17 5 // 49 39 22 12 // 56 44 18 6 // 50 13 // 57 7 #include<8052.h> __sfr __at (0x8E) AUXR; __sfr __at (0x91) P1M1; __sfr __at (0x92) P1M0; __sfr __at (0xB1) P3M1; __sfr __at (0xB2) P3M0; __sfr __at (0xD6) T2H; __sfr __at (0xD7) T2L; __sfr __at (0xAF) IE2; #define uint8_t unsigned char typedef void(*functionPointer)(); __code uint8_t port3bitTable[5] = {0,1,3,6,7}; volatile uint8_t columns[10]; volatile uint8_t pushButton = 0; // interrupt routine, besides multiplexing the LEDs, debounces the push button and sets this flag on a fresh press unsigned int patternStage; // used by the various patternX() functions to keep track of progress __code uint8_t knightRider[] = {3,15,23,27,28,29,33,41,53}; // LEDs on centre axis of hourglass __code uint8_t perimeter[] = {0,1,2,3,4,5,6,12,17,21,24,26,27,25,22,18,13,7}; // perimeter of lower triangle. upperPerimeter[n] = 56 - perimeter[n] void T2INT(void) __interrupt 12 __using 1 { // timer 2 interrupt set to happen at 3600 Hz, so overall multiplex cycle at 360 Hz static uint8_t pushButtonDebounce = 0; static uint8_t stage = 9; // multiplex LEDs (ten rows of 6 columns) uint8_t m0, m1, p; if (!P3_2) { // if button pressed if (!pushButtonDebounce) { // ...and was released for previous 50ms pushButton = 1; // set flag for main routine } pushButtonDebounce = 180; // 50ms } else if (pushButtonDebounce) { // run timer when button is released --pushButtonDebounce; } m0 = columns[stage] & 0x3F; m1 = m0 ^ 0x3F; // for lit bits m0=1, m1=0 (push-pull); for unlit bits m0=0; m1=1 (input only); P1_6 and P1_7 remain m0=0,m1=0 (standard quasi-bidirectional) p = stage & 0x01 ? m0 : m1; P1M0 = 0x00; // set all 'column' (P1) I/O to input only P1M1 = 0x3F; P3M0 = 0x00; // set all 'row' (P3) I/O input only P3M1 = 0xCB; P1 = p; // preset column output bits high or low P1M0 = m0; P1M1 = m1; // switch lit column bits to push-pull mode, other bits remain input only m0 = 0x01 << port3bitTable[stage >> 1]; m1 = m0 ^ 0xCB; // will set selected I/O to push-pull mode p = (stage & 0x01) ? m1 : m0; // output bits high or low P3 = p | 0x04; // preset row output bit high or low; P3_2 (push button input) always high P3M0 = m0; P3M1 = m1; // set push-pull mode for that row output bit if (!stage) { // each interrupt selects next of 10 rows. stage = 9; } else { --stage; } } // code-space saving kludge: prevent linker from linking in integer divide ( / ) and integer modulus ( % ) functions by never using them, and instead do this repeated subtraction uint8_t div; unsigned int divMod(unsigned int n, uint8_t d) { div = 0; // on return, div = n / d; while (n >= d) { n -= d; div++; } return n; // return n % d; } uint8_t rowMask(uint8_t n) { n = divMod(n, 10); div = 0x01 << div; // div now holds mask return n > 4 ? ((n - 5) << 1) | 1 : n << 1; // row } void setLED(uint8_t n, uint8_t on) { // n:0 to 56; on:true or false n = rowMask(n); // row if (on) { columns[n] |= div; } else { columns[n] &= ~div; } } void toggleLED(uint8_t n) { // n:0 to 56; toggle between lit and unlit n = rowMask(n); // row columns[n] ^= div; } void delayMillis(uint8_t ms) { uint8_t i; unsigned int j; for (i = 0; i < ms; i++) { for (j = 0; j < 804; j++) { // 804 seems about right with default FOSC = 11.0592 MHz __asm nop __endasm; } } } // patterns void ptn0(void) { // light LED 56 to 0 in descending order for 0.1s if (!patternStage) { patternStage = 57; } --patternStage; setLED((uint8_t)patternStage, 1); delayMillis(100); setLED((uint8_t)patternStage, 0); } void ptn1(void) { // knight rider KITT-style bouncing LEDs on hourglass centre axis uint8_t led; led = patternStage < 9 ? knightRider[patternStage] : knightRider[16 - patternStage]; setLED(led, 1); delayMillis(125); setLED(led, 0); if (++patternStage > 15) { patternStage = 0; } } void ptn2(void) { // cycle around perimeters of upper and lower hourglass triangles uint8_t led; led = perimeter[patternStage]; setLED(led, 1); setLED(56 - led, 1); delayMillis(56); setLED(led, 0); setLED(56 - led, 0); if (++patternStage > 17) { patternStage = 0; } } void ptn3(void) { // like pattern 0, but toggle if (!patternStage) { patternStage = 57; } --patternStage; toggleLED((uint8_t)patternStage); delayMillis(100); } void ptn4(void) { // like pattern 1, but toggle toggleLED(patternStage < 9 ? knightRider[patternStage] : knightRider[16 - patternStage]); delayMillis(125); if (++patternStage > 15) { patternStage = 0; } } void ptn5(void) { // like pattern 2. but toggle uint8_t led; led = perimeter[patternStage]; toggleLED(led); toggleLED(56 - led); delayMillis(56); if (++patternStage > 17) { patternStage = 0; } } const functionPointer patterns[] = {ptn0,ptn1,ptn2,ptn3,ptn4,ptn5}; void main() __using 0 { uint8_t i, pattern = 0; AUXR &= 0xE3; // bit 4, T2R is low (Timer 2 not running); bit 3, T2_C/T low so timer counter 2 works as timer; bit 2 T2x12 is low, so clock source for timer 2 is FOSC/12 // when T2R == 0 (timer 2 not running), writes to T2H and T2L actually write to the hidden registers RL_T2H and RL_T2L (the 16-bit auto-reload value) T2L = 0x00; T2H = 0xFF; // count from 0xFF00 to 0x10000 (256 counts) before interrupt, so interrupt frequency = FOSC/12/256 = 11059200/12/256 = 3600 Hz AUXR |= 0x10; // run timer 2 IE2 |= 0x04; // set bit2 ET2 to enable interrupts from timer 2 EA = 1; // enable interrupts while(1) { if (pushButton) { pushButton = 0; if (++pattern > 5) { pattern = 0; } for (i = 0; i < 57; i++) { setLED(i, 0); } patternStage = 0; } patterns[pattern](); } }