123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 |
- //
- // ^ Vcc MSP430G2452 GND ^ Vcc ^ Vcc
- // | /-------------\ | | |
- // +--- -| Vcc U GND |- ---+ | | 47 k === 100 nF
- // 0 -| P1.0 XTAL |- ------+ Crystal | | |
- // 1 -| P1.1 XTAL |- --|O|-+ 32768 Hz | --- GND
- // 2 -| P1.2 TEST |- -------------------|--o TEST +--------+
- // 3 -| P1.3 ~RST |- -------------------+--o ~RST | |
- // 4 -| P1.4 P1.7 |- BUTTON_1 connected to Vcc | === 10 uF
- // 5 -| P1.5 P1.6 |---------------------------------+ |
- // 6 -| P2.0 P2.5 |- BUTTON_2 connected to Vcc | LED - /| Coil-
- // 7 -| P2.1 P2.4 |- SR_RCK (latch clock) / \|/ | | | Speaker
- // SR_SI -| P2.2 P2.3 |- SR_SCK (shift clock) v/ --- - \| (16 R)
- // \-------------/ v | |
- // 0..7 LED row select --- GND --- GND
- //
- //
- // Vcc Vcc +----------------+ Vcc
- // ^ SI RCK ^ | Vcc ^ | RCK ^
- // | Al | GND | SCK | | | Ar | GND | SCK |
- // | | | | | | | | | | | | | | | |
- // /------------------------\ /------------------------\
- // |Vcc QA SI OE RST H | |Vcc QA SI OE RST H |
- // |> MC74HC595 | |> MC74HC595 |
- // |QB QC QD QE QF QG QH GND| |QB QC QD QE QF QG QH GND|
- // \------------------------/ \------------------------/
- // | | | | | | | | | | | | | | | |
- // Bl Cl Dl El Fl Gl Hl GND Br Cr Dr Er Fr Gr Hr GND
- // (left 8 LED columns) (right 8 LED columns)
- //
- //
- // A B C D E F G H
- // 0 o o o o o o o o * A C *
- // 1 o o o o o o o o 4 * F *
- // 2 o o o o o o o o 2 * * 3 the label is on
- // 3 o o o o o o o o * D H * the right side:
- // 4 o o o o o o o o 7 * * 5 (LD788BS-SS22)
- // 5 o o o o o o o o * B * 6
- // 6 o o o o o o o o 1 * G *
- // 7 o o o o o o o o 0 * E *
- //
- // 0..7 (-) cathode pin assignment as
- // A..H (+) anode seen from the top
- #include <stdint.h>
- #include <msp430g2452.h>
- #include "glyphs.h"
- #include "tune.h"
- #define SR_SI BIT2 // Serial Data Input
- #define SR_SCK BIT3 // Clock Input
- #define SR_RCK BIT4 // Storage Register Clock Input
- #define BUZZER BIT6
- #define BUTTON_1 BIT7
- #define BUTTON_2 BIT5
- #define DISPLAYING_LIMIT 3 // Seconds
- #define SHINING_LIMIT 60 // Seconds
- #define SHINING_WARN_TICKS 4
- static uint8_t fb[ 16 ]; // LED frame buffer
- static volatile uint8_t ticks = 0, seconds = 0, minutes = 0, hours = 0;
- static volatile uint8_t alarm_hour = 0, alarm_minute = 0;
- static volatile uint8_t ticks_at_button_press = 0;
- static volatile uint8_t displaying_watchdog = 0;
- static volatile uint8_t shining_watchdog = 0;
- static volatile bool alarm_is_active = false;
- static volatile bool go_to_sleep = false;
- static volatile bool tick_happened = false;
- static volatile uint16_t tune_index = 0;
- static volatile uint8_t tune_count = 0;
- enum TState { SLEEPING, SHINING,
- TIME_DISPLAYING, TIME_HOUR_EDITING, TIME_MINUTE_EDITING,
- ALARM_DISPLAYING, ALARM_HOUR_EDITING, ALARM_MINUTE_EDITING,
- ALARM_IS_ACTIVE_DISPLAYING, ALARM_IS_ACTIVE_EDITING,
- BLINKING, PLAYING };
- static volatile TState state = TIME_DISPLAYING;
- static void config_registers()
- {
- WDTCTL = WDTPW | WDTHOLD;
- DCOCTL = CALDCO_16MHZ;
- BCSCTL1 = CALBC1_16MHZ;
- BCSCTL3 |= LFXT1S_0 | XCAP_3; // Use Crystal as ACLK + 12.5 pF caps
- WDTCTL = WDT_ADLY_16; // Interval timer mode using ACLK clock source
- IE1 |= WDTIE;
- P1OUT = 0x00;
- P2OUT = 0x00;
- P1DIR = 0xFF & ~BUTTON_1 & ~BUZZER; // All output except BUTTON_1 and BUZZER
- P2DIR = 0xFF & ~BUTTON_2; // All output except BUTTON_2
- P1OUT &= ~BUTTON_1; // select resistor to be pull-down
- P2OUT &= ~BUTTON_2;
- P1REN |= BUTTON_1; // enable resistor
- P2REN |= BUTTON_2;
- //P1IES &= ~BUTTON_1; // select the positive edge (low -> high transition) to cause an interrupt
- //P2IES &= ~BUTTON_2;
- //P1IFG &= ~BUTTON_1; // Clear any pending interrupt flags
- //P2IFG &= ~BUTTON_2;
- //P1IE |= BUTTON_1; // enable button interrupt
- //P2IE |= BUTTON_2;
- P1SEL |= BIT6; // enable Timer0_A Out1 function
- TACCTL1 = OUTMOD_7; // Reset/set |~~|_____
- TACTL = TASSEL_2 | MC_1 | TACLR; // SMCLK + Up mode + Clear timer
- TACCR0 = 1; // period
- TACCR1 = 0; // 2 is permanent on, 0 is off when TACCR0 is 1.
- }
- static uint8_t shift_row( int8_t y )
- {
- uint8_t on_led_count = 0;
- P2OUT &= ~SR_RCK;
- for ( uint8_t x = 0; x < 16; ++x ) {
- P2OUT &= ~SR_SCK;
- if ( fb[ 15 - x ] & ( 0x01 << y ) ) {
- P2OUT |= SR_SI;
- ++on_led_count;
- } else {
- P2OUT &= ~SR_SI;
- }
- P2OUT |= SR_SCK;
- }
- return on_led_count;
- }
- static void select_row( int8_t y )
- {
- // Turn all rows off:
- P1OUT |= 0x3F;
- P2OUT |= 0x03;
- // Strobe columns:
- P2OUT |= SR_RCK;
- // Select respective row:
- if ( y >= 0 ) {
- if ( y < 6 ) {
- P1OUT &= ~( 0x01 << y );
- } else {
- P2OUT &= ~( 0x01 << ( y - 6 ) );
- }
- }
- }
- static void sleep()
- {
- // Set columns low:
- P2OUT &= ~SR_RCK;
- for ( uint8_t x = 0; x < 16; ++x ) {
- P2OUT &= ~SR_SCK;
- P2OUT &= ~SR_SI;
- P2OUT |= SR_SCK;
- }
- // Strobe columns:
- P2OUT |= SR_RCK;
- // Set rows low:
- P1OUT &= ~0x3F;
- P2OUT &= ~0x03;
- // Set the rest low:
- P2OUT &= ~( SR_SI | SR_SCK | SR_RCK );
- TACCR0 = 1;
- TACCR1 = 0; // Discharge the 10 uF capacitor, otherwise the
- __delay_cycles( 16000 ); // device consumes over 40 uA in sleep mode.
- // Change Buzzer to input:
- P1DIR &= ~BUZZER;
- // Go to sleep:
- LPM3;
- }
- static void clear_fb()
- {
- for ( uint8_t i = 0; i < sizeof( fb ); ++i ) {
- fb[ i ] = 0;
- }
- }
- static void on_fb()
- {
- fb[ 0 ] = 0x00;
- fb[ 1 ] = 0x3C;
- fb[ 2 ] = 0x24;
- fb[ 3 ] = 0x3C;
- fb[ 4 ] = 0x42;
- fb[ 5 ] = 0x81;
- fb[ 6 ] = 0xFF;
- fb[ 7 ] = 0x00;
- fb[ 8 ] = 0x00;
- fb[ 9 ] = 0x24;
- fb[ 10 ] = 0x18;
- fb[ 11 ] = 0x42;
- fb[ 12 ] = 0x3C;
- fb[ 13 ] = 0x81;
- fb[ 14 ] = 0x7E;
- fb[ 15 ] = 0x00;
- }
- static void off_fb()
- {
- fb[ 0 ] = 0x00;
- fb[ 1 ] = 0x3C;
- fb[ 2 ] = 0x24;
- fb[ 3 ] = 0x3C;
- fb[ 4 ] = 0x42;
- fb[ 5 ] = 0x81;
- fb[ 6 ] = 0xFF;
- fb[ 7 ] = 0x00;
- fb[ 8 ] = 0x00;
- fb[ 9 ] = 0x42;
- fb[ 10 ] = 0x24;
- fb[ 11 ] = 0x18;
- fb[ 12 ] = 0x18;
- fb[ 13 ] = 0x24;
- fb[ 14 ] = 0x42;
- fb[ 15 ] = 0x00;
- }
- static void render_fb()
- {
- const bool is_off_interval = ( ticks - ticks_at_button_press + 64 ) % 64 > 32;
- if ( ( state == PLAYING && ticks > 32 ) ||
- ( state == ALARM_IS_ACTIVE_EDITING && is_off_interval ) )
- {
- clear_fb();
- return;
- }
- uint8_t hour, minute;
- bool display_blinking_point = false;
- switch ( state ) {
- case TIME_DISPLAYING:
- case TIME_HOUR_EDITING:
- case TIME_MINUTE_EDITING:
- case BLINKING:
- case PLAYING:
- hour = hours;
- minute = minutes;
- display_blinking_point = true;
- break;
- case ALARM_DISPLAYING:
- case ALARM_HOUR_EDITING:
- case ALARM_MINUTE_EDITING:
- hour = alarm_hour;
- minute = alarm_minute;
- break;
- }
- if ( state == ALARM_IS_ACTIVE_DISPLAYING || state == ALARM_IS_ACTIVE_EDITING ) {
- if ( alarm_is_active ) {
- on_fb();
- } else {
- off_fb();
- }
- } else {
- uint8_t digit_1 = hour / 10;
- uint8_t digit_2 = hour % 10;
- uint8_t digit_3 = minute / 10;
- uint8_t digit_4 = minute % 10;
- if ( ( state == TIME_HOUR_EDITING || state == ALARM_HOUR_EDITING ) && is_off_interval ) {
- digit_1 = digit_2 = 10; // none
- } else if ( digit_1 == 0 ) {
- digit_1 = 10; // none
- }
- if ( ( state == TIME_MINUTE_EDITING || state == ALARM_MINUTE_EDITING ) && is_off_interval ) {
- digit_3 = digit_4 = 10; // none
- }
- fb[ 0 ] = glyph[ digit_1 ][ 0 ];
- fb[ 1 ] = glyph[ digit_1 ][ 1 ];
- fb[ 2 ] = glyph[ digit_1 ][ 2 ];
- fb[ 3 ] = 0;
- fb[ 4 ] = glyph[ digit_2 ][ 0 ];
- fb[ 5 ] = glyph[ digit_2 ][ 1 ];
- fb[ 6 ] = glyph[ digit_2 ][ 2 ];
- fb[ 7 ] = 0;
- fb[ 8 ] = 0;
- fb[ 9 ] = glyph[ digit_3 ][ 0 ];
- fb[ 10 ] = glyph[ digit_3 ][ 1 ];
- fb[ 11 ] = glyph[ digit_3 ][ 2 ];
- fb[ 12 ] = 0;
- fb[ 13 ] = glyph[ digit_4 ][ 0 ];
- fb[ 14 ] = glyph[ digit_4 ][ 1 ];
- fb[ 15 ] = glyph[ digit_4 ][ 2 ];
- if ( display_blinking_point && ticks < 32 ) {
- fb[ seconds >> 2 ] |= 192;
- }
- if ( alarm_is_active ) {
- fb[ 15 ] |= 192;
- }
- }
- }
- static void display_fb()
- {
- for ( int8_t y = 7; y >= 0; --y ) {
- uint8_t on_led_count = shift_row( y ) + 2; // Without adding some value, single dots are to dark.
- const uint8_t max_led_count = 14;
- if ( on_led_count > max_led_count ) {
- on_led_count = max_led_count;
- }
- select_row( y );
- const uint16_t delay = 357; // 16E6 / ( 300 * 14(=max_led_count) ) / 8 rows = 400.16 Hz
- for ( uint8_t i = 0; i < on_led_count; ++i ) {
- __delay_cycles( delay );
- }
- select_row( -1 );
- for ( uint8_t i = 0; i < max_led_count - on_led_count; ++i ) {
- __delay_cycles( delay );
- }
- }
- }
- int main( void )
- {
- config_registers();
- clear_fb();
- _enable_interrupts();
- while ( true ) {
- if ( go_to_sleep ) {
- go_to_sleep = false;
- state = SLEEPING;
- sleep();
- }
- if ( tick_happened ) {
- tick_happened = false;
- switch ( state ) {
- case SHINING:
- if ( shining_watchdog + SHINING_WARN_TICKS > SHINING_LIMIT ) {
- TACCR1 = ticks == 0 ? 0 : 2;
- }
- break;
- case BLINKING:
- if ( seconds < 30 ) {
- TACCR1 = seconds % 2 ? 0 : 2;
- } else {
- TACCR1 = ( ticks & 0x0F ) == 0 ? 2 : 0;
- }
- break;
- case PLAYING:
- if ( ( ticks & 0x01 ) == 0 ) {
- const uint16_t period = scale[ tune[ tune_index ] ];
- TACCR0 = period;
- TACCR1 = period >> 1;
- ++tune_index;
- if ( tune_index == sizeof( tune ) ) {
- tune_index = 0; // be kind, rewind :)
- ++tune_count;
- if ( tune_count == 2 ) {
- tune_count = 0 ;
- go_to_sleep = true; // All attempts were in vain.
- }
- }
- }
- break;
- }
- render_fb();
- }
- if ( state != SHINING ) {
- display_fb(); // Does busy waiting.
- }
- }
- }
- #pragma vector = WDT_VECTOR
- __interrupt void WDT_ISR( void )
- {
- // called 64 times per second. Consumes about 2.6 uA @ 3.3 V. Device works down to 2.4 V.
- tick_happened = true;
- ++ticks;
- if ( ticks == 64 ) {
- ticks = 0;
- ++seconds;
- if ( seconds == 60 ) {
- seconds = 0;
- ++minutes;
- if ( minutes == 60 ) {
- minutes = 0;
- ++hours;
- if ( hours == 24 ) {
- hours = 0;
- //seconds = 1; // correction for too slow quarz
- __delay_cycles( 16000000 ); // correction for too fast quarz
- }
- }
- if ( alarm_is_active && hours == alarm_hour && minutes == alarm_minute ) {
- state = BLINKING;
- P1DIR |= BUZZER;
- TACCR0 = 1;
- TACCR1 = 0; // 2 is permanent on, 0 is off when TACCR0 is 1.
- LPM3_EXIT;
- } else if ( state == BLINKING ) {
- state = PLAYING; // Play a delightful tune.
- tune_index = 0;
- }
- }
- switch ( state ) {
- case TIME_DISPLAYING:
- case ALARM_IS_ACTIVE_DISPLAYING:
- case ALARM_DISPLAYING:
- ++displaying_watchdog;
- if ( displaying_watchdog > DISPLAYING_LIMIT ) {
- go_to_sleep = true;
- }
- break;
- case SHINING:
- ++shining_watchdog;
- if ( shining_watchdog > SHINING_LIMIT ) {
- go_to_sleep = true;
- }
- break;
- }
- }
- enum TButtonState { RELEASED, PRESSED, HELD };
- static volatile TButtonState button1_state = RELEASED;
- if ( ( P1IN & BUTTON_1 ) == BUTTON_1 ) {
- if ( button1_state == RELEASED ) {
- button1_state = PRESSED;
- switch ( state ) {
- case SLEEPING:
- state = TIME_DISPLAYING;
- LPM3_EXIT;
- break;
- case SHINING:
- if ( shining_watchdog + SHINING_WARN_TICKS > SHINING_LIMIT ) {
- shining_watchdog = 0;
- } else {
- go_to_sleep = true;
- }
- break;
- case TIME_DISPLAYING:
- state = alarm_is_active ? ALARM_DISPLAYING : ALARM_IS_ACTIVE_DISPLAYING;
- break;
- case ALARM_IS_ACTIVE_DISPLAYING:
- case ALARM_DISPLAYING:
- go_to_sleep = true;
- break;
- case TIME_HOUR_EDITING:
- hours = ++hours;
- if ( hours == 24 ) {
- hours = 0;
- }
- break;
- case TIME_MINUTE_EDITING:
- minutes = ++minutes;
- if ( minutes == 60 ) {
- minutes = 0;
- }
- seconds = 0;
- ticks = 0;
- break;
- case ALARM_IS_ACTIVE_EDITING:
- alarm_is_active ^= 1;
- break;
- case ALARM_HOUR_EDITING:
- alarm_hour = ++alarm_hour;
- if ( alarm_hour == 24 ) {
- alarm_hour = 0;
- }
- break;
- case ALARM_MINUTE_EDITING:
- alarm_minute = alarm_minute + 5;
- if ( alarm_minute == 60 ) {
- alarm_minute = 0;
- }
- break;
- case BLINKING:
- case PLAYING:
- go_to_sleep = true;
- break;
- }
- displaying_watchdog = 0;
- ticks_at_button_press = ticks;
- } else if ( button1_state == HELD && ( ( ticks & 0x07 ) == 0 ) ) {
- switch ( state ) {
- case TIME_HOUR_EDITING:
- hours = ++hours;
- if ( hours == 24 ) {
- hours = 0;
- }
- break;
- case TIME_MINUTE_EDITING:
- minutes = ++minutes;
- if ( minutes == 60 ) {
- minutes = 0;
- }
- seconds = 0;
- ticks = 0;
- break;
- case ALARM_HOUR_EDITING:
- alarm_hour = ++alarm_hour;
- if ( alarm_hour == 24 ) {
- alarm_hour = 0;
- }
- break;
- case ALARM_MINUTE_EDITING:
- alarm_minute = alarm_minute + 5;
- if ( alarm_minute == 60 ) {
- alarm_minute = 0;
- }
- break;
- }
- displaying_watchdog = 0;
- ticks_at_button_press = ticks;
- } else if ( ( ticks - ticks_at_button_press + 64 ) % 64 > 24 ) {
- button1_state = HELD;
- }
- } else {
- button1_state = RELEASED;
- }
- static volatile TButtonState button2_state = RELEASED;
- if ( ( P2IN & BUTTON_2 ) == BUTTON_2 ) {
- if ( button2_state == RELEASED ) {
- button2_state = PRESSED;
- bool start_blinking_with_OFF_period = true;
- switch ( state ) {
- case SLEEPING:
- state = SHINING; // Consumes about 20 mA in SHINING state.
- P1DIR |= BUZZER;
- TACCR0 = 1;
- TACCR1 = 2; // 2 is permanent on, 0 is off when TACCR0 is 1.
- shining_watchdog = 0;
- LPM3_EXIT;
- break;
- case SHINING:
- if ( shining_watchdog + SHINING_WARN_TICKS > SHINING_LIMIT ) {
- shining_watchdog = 0;
- } else {
- go_to_sleep = true;
- }
- break;
- case TIME_DISPLAYING:
- state = TIME_HOUR_EDITING;
- break;
- case TIME_HOUR_EDITING:
- state = TIME_MINUTE_EDITING;
- break;
- case TIME_MINUTE_EDITING:
- state = TIME_DISPLAYING;
- break;
- case ALARM_DISPLAYING:
- start_blinking_with_OFF_period = false;
- // Fall through intended.
- case ALARM_IS_ACTIVE_DISPLAYING:
- state = ALARM_IS_ACTIVE_EDITING;
- break;
- case ALARM_IS_ACTIVE_EDITING:
- state = alarm_is_active ? ALARM_HOUR_EDITING : ALARM_IS_ACTIVE_DISPLAYING;
- start_blinking_with_OFF_period = false;
- break;
- case ALARM_HOUR_EDITING:
- state = ALARM_MINUTE_EDITING;
- break;
- case ALARM_MINUTE_EDITING:
- //state = ALARM_IS_ACTIVE_DISPLAYING;
- state = ALARM_DISPLAYING;
- break;
- case BLINKING:
- case PLAYING:
- go_to_sleep = true;
- break;
- }
- displaying_watchdog = 0;
- if ( start_blinking_with_OFF_period ) {
- //ticks_at_button_press = ( ticks + 31 ) % 64; // immediately off for 1/2 second ____|~~~~|____|~~
- ticks_at_button_press = ( ticks + 16 ) % 64; // first off time is 1/4 second __|~~~~|____|~~~~
- } else {
- //ticks_at_button_press = ticks; // immediately on for 1/2 second ~~~~|____|~~~~|__
- ticks_at_button_press = ( ticks + 48 ) % 64; // 1/4 second delay before blinking starts ~~|____|~~~~|____
- }
- }
- } else {
- button2_state = RELEASED;
- }
- // The interrupt flag is cleared automatically.
- }
- #pragma vector = PORT1_VECTOR
- __interrupt void PORT1_ISR( void )
- {
- //P1IFG &= ~BUTTON_1; // clear the interrupt flag
- }
- #pragma vector = PORT2_VECTOR
- __interrupt void PORT2_ISR( void )
- {
- //P2IFG &= ~BUTTON_2; // clear the interrupt flag
- }
- #pragma vector = USI_VECTOR
- __interrupt void USI_ISR( void ) {}
- #pragma vector = ADC10_VECTOR
- __interrupt void ADC10_ISR( void ) {}
- #pragma vector = TIMER0_A1_VECTOR
- __interrupt void TIMER0_A1_ISR( void ) {}
- #pragma vector = TIMER0_A0_VECTOR
- __interrupt void TIMER0_A0_ISR( void ) {}
- #pragma vector = COMPARATORA_VECTOR
- __interrupt void COMPARATORA_ISR( void ) {}
- #pragma vector = NMI_VECTOR
- __interrupt void NMI_ISR( void ) {}
|