#include <avr/pgmspace.h>
#include <Servo.h>
#define DEFAULT_MINUTES (5*60)
#define MAX_MINUTES (99*60)
#define MIN_MINUTES (1*60)
//GPIOs
const uint8_t pinStartStop = 4;
const uint8_t pinIncrement = 5;
const uint8_t pinDecrement = 6;
const uint8_t pinServo1 = 3;
const uint8_t pinServo2 = 2;
const uint8_t pinLED = LED_BUILTIN;
//timer SM states
enum TSMStates_e
{
TSM_INIT = 0,
TSM_STOPPED,
TSM_TIMING,
TSM_TIMEOUT
};
// display ////////////////////////////////////////////////////////////////
#define DIGIT_TIME 5000ul //uS mux display time per digit
#define COMMON_CATHODE 1 // comment out for common anode
#ifdef COMMON_CATHODE
#define DIGIT_OFF HIGH
#define DIGIT_ON LOW
#define SEG_OFF LOW
#define SEG_ON HIGH
#else
#define DIGIT_OFF LOW
#define DIGIT_ON HIGH
#define SEG_OFF HIGH
#define SEG_ON LOW
#endif
//segments
const uint8_t grSegPins[] = {20,16,29,25,23,19,31}; //a,b,c,d,e,f,g
//digits
const uint8_t grDigitPins[] = {21,18,17,33}; //d1, d2, d3, d4
//colon
const uint8_t pinColon = 15;
bool bDigitBlank;
uint8_t grDigits[4];
const uint8_t grSegPattern[] PROGMEM =
{
B01111110, //zero
B00110000, //one
B01101101, //two
B01111001, //three
B00110011, //four
B01011011, //five
B01011111, //six
B01110000, //seven
B01111111, //eight
B01111011 //nine
};
const uint8_t Blank = 0x00;
// servos ////////////////////////////////////////////////////////////////
Servo servo1;
Servo servo2;
// switch inputs /////////////////////////////////////////////////////////
typedef struct Switch_s
{
private:
uint8_t _pin;
uint8_t _last;
uint8_t _now;
uint32_t _tSwitch;
uint32_t _tNow;
bool _status;
public:
void begin( uint8_t pin )
{
_pin = pin;
pinMode( _pin, INPUT_PULLUP );
_last = digitalRead( _pin );
_status = false;
}//begin
bool getSwStatus()
{
bool status = _status;
_status = false;
return status;
}//setSwStatus
void run()
{
_tNow = millis();
if( _tNow - _tSwitch >= 50ul )
{
_tSwitch = _tNow;
_now = digitalRead( _pin );
if( _now != _last )
{
_last = _now;
if( _now == LOW )
_status = true;
}//if
}//if
}//run
}Switch_t;
Switch_t
sStartStop,
sIncr,
sDecr;
// variables ////////////////////////////////////////////////////////////////
bool
bColonStatus;
uint16_t
secondsCount;
uint32_t
tNow;
void setup()
{
uint8_t i;
Serial.begin(115200);
servo1.attach( pinServo1 );
servo1.write(90);
servo2.attach( pinServo2 );
servo2.write(90);
//set digit pins
for( i=0; i<4; i++ )
{
pinMode( grDigitPins[i], OUTPUT );
digitalWrite( grDigitPins[i], DIGIT_OFF );
}//for
//set segment pins
for( i=0; i<7; i++ )
{
pinMode( grSegPins[i], OUTPUT );
digitalWrite( grSegPins[i], SEG_OFF );
}//for
//colon and LED pins
pinMode( pinColon, OUTPUT );
digitalWrite( pinColon, SEG_OFF );
pinMode( pinLED, OUTPUT );
digitalWrite( pinLED, LOW );
//init the count to the default value and set up the display digits
secondsCount = DEFAULT_MINUTES;
setDigits();
//initialize the input buttons
sStartStop.begin( pinStartStop );
sIncr.begin( pinIncrement );
sDecr.begin( pinDecrement );
}//setup
void loop()
{
//update the switches
sStartStop.run();
sIncr.run();
sDecr.run();
//mux the display
MuxDigits();
//run the timer state machine
TimerStateMachine();
}//loop
void TimerStateMachine( void )
{
static uint8_t
state = TSM_INIT;
static uint32_t
tTSM = 0ul;
tNow = millis();
switch( state )
{
case TSM_INIT:
//initialize the timer, servos and LED
servo1.write(90);
servo2.write(90);
digitalWrite( pinLED, LOW );
bColonStatus = false;
secondsCount = DEFAULT_MINUTES;
setDigits();
state = TSM_STOPPED;
break;
case TSM_STOPPED:
//check if the increment or decrement buttons were pressed
if( sDecr.getSwStatus() )
{
if( secondsCount > MIN_MINUTES )
secondsCount -= 60;
setDigits();
}//if
if( sIncr.getSwStatus() )
{
if( secondsCount < MAX_MINUTES )
secondsCount += 60;
setDigits();
}//if
//has the start button been pressed?
if( sStartStop.getSwStatus() )
{
tTSM = tNow;
//LED on and swing the servos
digitalWrite( pinLED, HIGH );
servo1.write(0);
servo2.write(180);
state = TSM_TIMING;
}//if
break;
case TSM_TIMING:
//update counting
if( tNow - tTSM >= 500ul )
{
//every 500mS we toggle the colon flag and every time it shows "false" -- i.e. once
//every 1000mS -- we handle the timer
tTSM = tNow;
bColonStatus ^= true;
if( bColonStatus == false )
{
secondsCount--;
//when the count reaches zero...
if( secondsCount == 0 )
{
//we're done; LED off, servos to neutral and colon off
digitalWrite( pinLED, LOW );
servo1.write(90);
servo2.write(90);
bColonStatus = false;
state = TSM_TIMEOUT;
}//if
//update the display digits
setDigits();
}//if
}//if
//has stop button been pressed?
if( sStartStop.getSwStatus() )
{
//stop counting where we are and turn off the colon
bColonStatus = false;
state = TSM_TIMEOUT;
}//if
break;
case TSM_TIMEOUT:
//hold the timer here until another strt/stp/rst button press
if( sStartStop.getSwStatus() )
{
//go back and re-init the timer
state = TSM_INIT;
}//if
break;
}//switch
}//TimerStateMachine
void setDigits()
{
//assign numbers to each of the 4 displayed digits
uint8_t min = secondsCount / 60;
uint8_t sec = secondsCount % 60;
grDigits[0] = (min / 10) % 10; //tens of minutes
grDigits[1] = min % 10; //minutes
grDigits[2] = (sec / 10) % 10; //seconds
grDigits[3] = sec % 10;
}//setDigits
void MuxDigits()
{
static uint8_t
digIndex = 3;
static uint32_t
tDigit = 0ul;
uint32_t
tNow = micros();
if( (tNow - tDigit) >= DIGIT_TIME )
{
tDigit = tNow;
//turn off the current digit
digitalWrite( grDigitPins[digIndex], DIGIT_OFF );
//bump index to the next digit
digIndex++;
if( digIndex == 4 )
digIndex = 0;
//while the digit is off, update the segments for the new one
uint8_t mask = pgm_read_byte_near(&grSegPattern[grDigits[digIndex]]);
uint8_t i=0x40;
for( uint8_t segs=0; segs<7; segs++ )
{
digitalWrite( grSegPins[segs], (mask & i) ? SEG_ON : SEG_OFF );
i >>= 1;
}//for
//leading-zero blanking of minutes digits
bDigitBlank = false;
if( digIndex == 0 && grDigits[0] == 0 )
bDigitBlank = true;
//turn on the new digit
digitalWrite( grDigitPins[digIndex], (bDigitBlank == true) ? DIGIT_OFF : DIGIT_ON );
//colon (drive when digit 0 is being driven)
if( digIndex == 0 )
digitalWrite( pinColon, bColonStatus == true ? SEG_ON:SEG_OFF );
}//if
}//MuxDigits