/* * BasicFlipClockLib.cpp * * Created: 8/7/2017 2:41:20 PM * Author : Spencer Hamblin * This is the main application code for the Basic Flip Clock designed and built by Spencer Hamblin * License : MIT, Do what ever you want with this code. But if you fuck it up it's on you. */ #include "BasicFlipClockLib.h" void initalSetUp() { //cli(); _delay_ms(100);//Gives time every thing to settle //------------SET UP IO--------------// DDRB = 0b11000111;//Set whats an output or input, 1 is output PINB = 0b00000000;//Value if input. PORTB = 0b00000000;//1 to drive high or enable pullup DDRC = 0b00001111;//0b00101111; PINC = 0b00000000; PORTC = 0b00000000; DDRD = 0b11110011; PIND = 0b00000000; PORTD = 0b00000000; //Flip SCL Until SDA is high to clear the state of RTC I2C brown outs while(!PINC4){ PORTC &= 0b11011111; _delay_us(3); PORTC |= 0b00100000; _delay_us(3); } //----------Set up I2C-------------------// I2C_MODE_FAST; //-----------Setup I2C GPIO--------------// I2CStart(I2C_GPIO_WRITE); I2CDataSend(0x00);//Control byte to select Register I2CDataSend(0xFF);//Data for 00h IODIR I2CDataSend(0b11111100);//Data for 01h IPOL, Sets polarity of input, inverts I2CDataSend(0b00000000);//Data for 02h GPINTEN I2CDataSend(0x00);//Data for 03h DEFVAL I2CDataSend(0x00);//Data for 04h INTCON I2CDataSend(0b00000000);//Data for 05h IOCON I2CDataSend(0xFF);//Data for 06h GPPU I2CDataSend(0x00);//Data for 07h INTF (READ-ONLY) I2CDataSend(0x00);//Data for 08h INTCAP (READ-ONLY) I2CDataSend(0x00);//Data for 09h GPIO I2CDataSend(0x00);//Data for 0Ah OLAT I2CStop(); //----------Setup RTC--------------// //Make sure RTC is in 24 hour mode, initialise more registers I2CStart(I2C_RTC_WRITE); I2CDataSend(0x0E);//Control byte to select Register //I2CDataSend(0x00);//Data for 03h Day //I2CDataSend(0x00);//Data for 04h Date //I2CDataSend(0x00);//Data for 05h Month //I2CDataSend(0x00);//Data for 06h Year //I2CDataSend(0x00);//Data for 07h Alarm 1 Seconds //I2CDataSend(0x00);//Data for 08h Alarm 1 Minutes //I2CDataSend(0x00);//Data for 09h Alarm 1 hours //I2CDataSend(0x00);//Data for 0Ah Alarm 1 Day/Date //I2CDataSend(0x00);//Data for 0Bh Alarm 2 Minutes //I2CDataSend(0x00);//Data for 0Ch Alarm 2 hours //I2CDataSend(0x00);//Data for 0Dh Alarm 2 Day/Date I2CDataSend(0b00011001);//Data for 0Eh Control I2CDataSend(0b00000000);//Data for 0Fh Control/Status //I2CDataSend(0x00);//Data for 10h Aging Offset //I2CDataSend(0x00);//Data for 11h MSB of Temp //I2CDataSend(0x00);//Data for 12h LSB of Temp I2CStop(); //sei();//Enables interrupts setLightsPercent(0); } uint8_t I2CStart(uint8_t address) { TWCR = (1<= 100)break; } return(TWDR); //TWCR = (1< 0) { _delay_ms(1000); S--; } } void delay_mS(unsigned int mS)//Wait time in miliseconds { while(mS > 0) { if(mS >= 1000) { _delay_ms(1000); mS -= 1000; }else if(mS >= 100) { _delay_ms(100); mS -= 100; }else if(mS >= 10) { _delay_ms(10); mS -= 10; }else { _delay_ms(1); mS -= 1; } } } //void tone(unsigned int frequencey = 0) //{ ////OCR2A-PB3 OCR2B-PD3 ////OCR2A formula (F_CPU/(frequency*2N)) -1 // ////Set pins to make sure it's ready to go //DDRD |= 0b01000000;//sets pin to out put //PORTD &= 0b10111111;//Makes sure pin is off //PIND &= 0b10111111;//Probably not needed but should be 0 // //if(frequencey == 0)TCCR0A = 0b00000010; //else //{ ////maximum = F_CPU/(2*1024*(1+1)); //TCCR0A = 0b01000010;//Enables OCR0A and sets CTC mode // ////TCCR0B = 0b00000101;//Sets pre-scaler at 256 ////OCR0A = (8000000/(frequencey * 512))-1; ////OCR0A = 1; // ////return; // ////Forumla used to determine upper bound for prescaler: F_CPU/(2*Prescaler*(1+1)) //if(frequencey < (F_CPU/4096))//1953 Hz upper bound for this prescaler //{ //TCCR0B = 0b00000101;//Sets pre-scaler at 1024 //OCR0A = ((F_CPU/frequencey)-2056)/2056; //} //else if(frequencey < (F_CPU/1024) )//7812 Hz upper bound for this prescaler //{ //TCCR0B = 0b00000100;//Sets pre-scaler at 256 //OCR0A = ((F_CPU/frequencey)-512)/512; //} //else if(frequencey < (F_CPU/256) )//31250 Hz upper bound for this prescaler //{ //TCCR0B = 0b00000011;//Sets pre-scaler at 64 //OCR0A = ((F_CPU/frequencey)-128)/128; //} //else if(frequencey < (F_CPU/32) )//250000 Hz upper bound for this prescaler //{ //TCCR0B = 0b00000010;//Sets pre-scaler at 8 //OCR0A = ((F_CPU/frequencey)-16)/16; //} //else if(frequencey < (F_CPU/4) )//2MHz upper bound for this prescaler //{ //TCCR0B = 0b00000001;//Sets pre-scaler at 8 //OCR0A = ((F_CPU/frequencey)-2)/2; //} //} //} void tone(unsigned int frequencey = 0) { //OCR2A-PB3 OCR2B-PD3 //OCR2A formula (F_CPU/(frequency*2N)) -1 //Set pins to make sure it's ready to go DDRD |= 0b01000000;//sets pin to out put PORTD &= 0b10111111;//Makes sure pin is off PIND &= 0b10111111;//Probably not needed but should be 0 if(frequencey == 0)TCCR0A = 0b00000010; else { //maximum = F_CPU/(2*1024*(1+1)); TCCR0A = 0b01000010;//Enables OCR0A and sets CTC mode //TCCR0B = 0b00000101;//Sets pre-scaler at 256 //OCR0A = (8000000/(frequencey * 512))-1; //OCR0A = 1; //return; //Forumla used to determine upper bound for prescaler: F_CPU/(2*Prescaler*(1+1)) TCCR0B = 0b00000100;//Sets pre-scaler at 256 //OCR0A = ((F_CPU/frequencey)-512)/512; OCR0A = 5; } } void beep(unsigned int mS)//Beep function for alarm. { tone(2700); delay_mS(mS); tone(0); } void chirp() { tone(1700); delay_mS(100); tone(); } void setLightsPercent(uint8_t brightness)//Set brightness based on 0-100% { //TCNT1 OCR1A OCR1B //Set pins to make sure it's ready to go DDRB |= 0b00000010;//sets pin to out put PORTB &= 0b11111101;//Makes sure pin is off PINB &= 0b11111101;//Probably not needed but should be 0 ICR1 = 0xFFFF;//Set the top value of the 16 bit counter if(brightness == 0){ TCCR1A = 0b00000010;//Disables counter output }else if(brightness >= 100) { TCCR1A = 0b10000010; TCCR1B = 0b00011001; TCCR1C = 0x00;//Register to force compare match, not needed OCR1A = 0xFFFF; }else { TCCR1A = 0b10000010; TCCR1B = 0b00011001; TCCR1C = 0x00;//Register to force compare match, not needed //NEEEDS WORK FLOAT CONVERSION ISSUE OCR1A = (0xFFFF * ((brightness * 1.0f)/(100.0f)));//Set percentage of value } } unsigned int timeDifferenceToBrightness(uint8_t seconds,uint8_t minutes, uint8_t hours, uint8_t alarmMinute,uint8_t alarmHour, uint8_t startBeforeMinutes = 60, unsigned int max = 0xFFFF){//Converts difference between alarm time to brightness setting unsigned int totalMinutesAlarm = (((unsigned int) alarmHour) * 60) + alarmMinute; unsigned int totalMinutes = (((unsigned int) hours) * 60) + minutes; unsigned int output; /*if(totalMinutesAlarm < startBeforeMinutes){ }else{ if(totalMinutes > totalMinutesAlarm){//Trigers if the time is after alarm time output = 0; }else if((totalMinutes + startBeforeMinutes) >= totalMinutesAlarm){////Trigers if time is in window of startbefore and alarmtime }else{ output = 0; } }*/ output = totalMinutesAlarm - totalMinutes; output *= 60;//Convert to pesudo seconds for higher resolution output += (seconds); output = 3600 - output; output *= 18; output += 730;//Add up to 736 to close approximation gap for brightness //output = output/max; return output; } void setLight16Bit(unsigned int brightness){//Set brightness directly based on 16 bit input //TCNT1 OCR1A OCR1B //Set pins to make sure it's ready to go DDRB |= 0b00000010;//sets pin to out put PORTB &= 0b11111101;//Makes sure pin is off PINB &= 0b11111101;//Probably not needed but should be 0 ICR1 = 0xFFFF;//Set the top value of the 16 bit counter if(brightness == 0){ TCCR1A = 0b00000010;//Disables counter output }else { TCCR1A = 0b10000010; TCCR1B = 0b00011001; TCCR1C = 0x00;//Register to force compare match, not needed //NEEEDS WORK FLOAT CONVERSION ISSUE OCR1A = (brightness);//Set percentage of value } } void getTime(uint8_t &seconds, uint8_t &minutes, uint8_t &hours, uint8_t rtcRegister) { uint8_t rawSeconds = 0; uint8_t rawMinutes = 0; uint8_t rawHours = 0; I2CStart(I2C_RTC_WRITE);//Set register index to read from I2CDataSend(rtcRegister); I2CStart(I2C_RTC_READ); rawSeconds = I2CDataRead(true); rawMinutes = I2CDataRead(true); rawHours = I2CDataRead(false); I2CStop(); hours = (0x0F & rawHours); if(0b00100000 & rawHours){ hours += 20; }else if(0b00010000 & rawHours){ hours +=10; } rawMinutes &= 0b01111111; minutes = ((rawMinutes >> 4) * 10) + (0x0F & rawMinutes); //minutes = (0x0F & rawMinutes); rawSeconds &= 0b01111111; seconds = ((rawSeconds >> 4) * 10) + (0x0F && rawSeconds); } void setTime(uint8_t minutes, uint8_t hours, uint8_t rtcRegister) { uint8_t rawMinutes = 0; uint8_t rawHours = 0; while(minutes >= 10){ rawMinutes += 0b00010000; minutes -= 10; } if(minutes > 0)rawMinutes += minutes; //while(hours >= 10){ // rawHours += 0b00010000; // hours -= 10; //} if(hours >= 20){ rawHours = 0b00100000; hours -= 20; }else if(hours >=10){ rawHours = 0b00010000; hours -= 10; } if(hours > 0)rawHours += hours; I2CStart(I2C_RTC_WRITE); I2CDataSend(rtcRegister); I2CDataSend(0x00);//Zero out seconds I2CDataSend(rawMinutes); I2CDataSend(rawHours); I2CStop(); } //void getAlarm(uint8_t &minutes, uint8_t &hours) //{ //uint8_t rawMinutes = 0; //uint8_t rawHours = 0; // //I2CStart(I2C_RTC_WRITE);//Set register index to read from //I2CDataSend(0x0b); //I2CStart(I2C_RTC_READ); //rawMinutes = I2CDataRead(true); //rawHours = I2CDataRead(false); //I2CStop(); // ////rawHours &= 0b00111111; // // //hours = (0x0F & rawHours); // //if(0b00100000 & rawHours){ //hours += 20; //}else if(0b00010000 & rawHours){ //hours +=10; //} // //rawMinutes &= 0b01111111; //minutes = ((rawMinutes >> 4) * 10) + (0x0F & rawMinutes); //} // //void setAlarm(uint8_t minutes,uint8_t hours) //{ //uint8_t rawMinutes = 0; //uint8_t rawHours = 0; // //while(minutes >= 10){ //rawMinutes += 0b00010000; //minutes -= 10; //} // //if(minutes > 0)rawMinutes += minutes; // ////while(hours >= 10){ //// rawHours += 0b00010000; //// hours -= 10; ////} // //if(hours >= 20){ //rawHours = 0b00100000; //hours -= 20; //}else if(hours >=10){ //rawHours = 0b00010000; //hours -= 10; //} // //if(hours > 0)rawHours += hours; // //I2CStart(I2C_RTC_WRITE); //I2CDataSend(0x0b);//Select Alarm 2 Register for minutes //I2CDataSend(rawMinutes); //I2CDataSend(rawHours); //I2CStop(); //} #define maxMinutesDay 3599//Really it's 3600 but it ticks over at that point //bool timeWindowAfter(uint8_t startMinutes,uint8_t startHours,uint8_t movingMinutes,uint8_t movingHours,uint8_t windowRange)//Returns false if movingTime is out of range of startTime by more than windowRange //{ //int startTotal = startMinutes + ( 60 * (int) startHours); //int moveTotal = movingMinutes + ( 60 * (int) movingHours); // ////if((startTotal + windowRange) > maxMinutesDay){ //// startTotal += windowRange; //// moveTotal += windowRange; ////} // //if(moveTotal > (startTotal + windowRange)){ //return false; //}else if( moveTotal > startTotal){ //return true; //} // //return false; //} bool timeWindowBefore(uint8_t startMinutes,uint8_t startHours,uint8_t movingMinutes,uint8_t movingHours,uint8_t windowRange)//Returns false if movingTime is out of range of startTime by more than windowRange { //return timeWindowAfter(movingMinutes,movingHours,startMinutes,startHours, windowRange); int moveTotal = startMinutes + ( 60 * (int) startHours); int startTotal = movingMinutes + ( 60 * (int) movingHours); //if((startTotal + windowRange) > maxMinutesDay){ // startTotal += windowRange; // moveTotal += windowRange; //} if(moveTotal > (startTotal + windowRange)){ return false; }else if( moveTotal > startTotal){ return true; } return false; } //bool timeWindowBefore(uint8_t startMinutes,uint8_t startHours,uint8_t movingMinutes,uint8_t movingHours,uint8_t windowRange)//Returns false if movingTime is out of range of startTime by more than windowRange //{ //int startTotal = startMinutes * startHours; //int moveTotal = movingMinutes * movingHours; // //if((startTotal - windowRange) < windowRange){ //startTotal += windowRange; //moveTotal += windowRange; //} // //if(moveTotal < (startTotal - windowRange)){ //return false; //}else if(moveTotal < startTotal){ //return true; //} // //return false; //} uint8_t getSwitchState() { //Switches polarity of switches as needed. Order 0b:0,0,DOWN,UP,SET ALARM,SET TIME,LIGHT EN,ALARM EN I2CStart(I2C_GPIO_WRITE); I2CDataSend(0x09);//Register to read from I2CStart(I2C_GPIO_READ); uint8_t switchState = I2CDataRead(false); I2CStop(); return switchState; } bool capSensor() { //Capacitive Sensor is on PD3 DDRD |= 0b00001000;//Sets as output PORTD |= 0b00001000;//Sets it to drive high _delay_ms(1); DDRD &= 0b11110111;//Set pin to input, an intermediate state exsistes with a pull up until portd is set PORTD &= 0b11110111;//Disable pull up _delay_us(200);//150 uS found to work well with 3 layers of 1/8 ply wood return (PIND & 0b00001000); } void simpleDispUpdate(uint8_t newDispState[10]) { PORTB &= 0b00111010; DDRB |= 0b11000101; PORTD &= 0b01001100; DDRD |= 0b10110011; PORTC &= 0b11110000; DDRC |= 0b00001111; for(uint8_t i = 0;i < 5;i++ ) { switch(i){ case 0: PORTD &= 0b01001100; PORTD |= 0b00000001;//Select the first segment break; case 1: PORTD &= 0b01001100; PORTD |= 0b00000010;//2 break; case 2: PORTD &= 0b01001100; PORTD |= 0b00010000;//3 break; case 3: PORTD &= 0b01001100; PORTD |= 0b00100000;//4 break; case 4: PORTD &= 0b01001100; PORTD |= 0b10000000;//5 break; default: break; } //Step 1 //PORTB |= 0b00000000; PORTC |= (0b00001111 & newDispState[i]); delay_mS(DISPLAY_DELAY_MS); //Step 2 PORTB &= 0b00111010; PORTC &= 0b11110000; PORTB |= (0b11000100 & newDispState[i + 5]); //PORTC |= 0b00000000; delay_mS(DISPLAY_DELAY_MS); //Step 3 PORTB &= 0b00111010; PORTC &= 0b11110000; PORTB |= 0b00000001;//Flip disable bit PORTC |= (0b00001111 & ~newDispState[i]); delay_mS(DISPLAY_DELAY_MS); //Step 4 PORTB &= 0b00111011;//Leave in bit 0 to set disable bit PORTC &= 0b11110000; PORTB |= (0b11000101 & ~newDispState[i+5]); //PORTC |= 0b00000000; delay_mS(DISPLAY_DELAY_MS); PORTB &= 0b00111010;//Clear ports just in case PORTC &= 0b11110000; } } void displayUpdate(uint8_t *newDispState) { uint8_t blankDisplay[10] = {0,0,0,0,0,0,0,0,0,0}; displayUpdate(newDispState,blankDisplay,false); } void displayUpdate(uint8_t *newDispState, uint8_t *oldDispState) { displayUpdate(newDispState,oldDispState,true); } void displayUpdate(uint8_t *newDispState, uint8_t *oldDispState, bool smartUpdate = true)//Rename to displayUpdate and use over loads { uint8_t displayStateMask [10] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};//Set mask for default set all digits state if(smartUpdate){//If smart update is true compare old and new for(int i = 0; i < 10; i++){//Loop through and compare the arrays to create a mask of what's chanaged. displayStateMask[i] = newDispState[i] ^ oldDispState[i]; } } PORTB &= 0b00111010; DDRB |= 0b11000101; PORTD &= 0b01001100; DDRD |= 0b10110011; PORTC &= 0b11110000; DDRC |= 0b00001111; for(uint8_t i = 0;i < 5;i++ ) { switch(i){ case 0: PORTD &= 0b01001100; PORTD |= 0b00000001;//Select the first segment break; case 1: PORTD &= 0b01001100; PORTD |= 0b00000010;//2 break; case 2: PORTD &= 0b01001100; PORTD |= 0b00010000;//3 break; case 3: PORTD &= 0b01001100; PORTD |= 0b00100000;//4 break; case 4: PORTD &= 0b01001100; PORTD |= 0b10000000;//5 break; default: break; } //Step 1 //PORTB |= 0b00000000; PORTC |= (0b00001111 & ~newDispState[i] & displayStateMask[i]); delay_mS(DISPLAY_DELAY_MS); //Step 2 PORTB &= 0b00111010; PORTC &= 0b11110000; PORTB |= (0b11000100 & ~newDispState[i + 5] & displayStateMask[i+5]); //PORTC |= 0b00000000; delay_mS(DISPLAY_DELAY_MS); //Step 3 PORTB &= 0b00111010; PORTC &= 0b11110000; PORTB |= 0b00000001;//Flip disable bit PORTC |= (0b00001111 & ~newDispState[i] & displayStateMask[i]); delay_mS(DISPLAY_DELAY_MS); //Step 4 PORTB &= 0b00111011;//Leave in bit 0 to set disable bit PORTC &= 0b11110000; PORTB |= (0b11000101 & ~newDispState[i+5] & displayStateMask[i+5]); //PORTC |= 0b00000000; delay_mS(DISPLAY_DELAY_MS); PORTB &= 0b00111010;//Clear ports just in case PORTC &= 0b11110000; } for(int i = 0; i < 10; i++){ oldDispState[i] = newDispState[i]; } } void alarmStatUpdate(bool alarmState) { } void timeToDisplayFormat(uint8_t *newDispState, uint8_t minutes, uint8_t hours, bool alarmState) { uint8_t hoursTemp = hours;//Used for am/pm adjustments if(hoursTemp > 12)hoursTemp -= 12;//Converts Military time to AM/PM time else if(hoursTemp == 0) hoursTemp = 12; uint8_t hourUpper = hoursTemp/10;//!0's place in hours uint8_t hourLower = (hoursTemp - (hourUpper * 10)); uint8_t minutesUpper = minutes/10;//10's place in minutes uint8_t minutesLower = (minutes - (minutesUpper * 10)); uint8_t workingNumber = 0;//Upper limit should be 9 after it is converted to BCD for(uint8_t i = 0; i < 4;i++){ switch(i){ case 0: workingNumber = hourUpper; break; case 1: workingNumber = hourLower; break; case 2: workingNumber = minutesUpper; break; case 3: workingNumber = minutesLower; break; default: break; } switch(workingNumber){//Set bytes in input array for display state case 0: newDispState[i] = 0b00001111;//Set port C newDispState[i + 5] = 0b11000000;//Set port B break; case 1: newDispState[i] = 0b00000110;//Set port C newDispState[i + 5] = 0b00000000;//Set port B break; case 2: newDispState[i] = 0b00001011;//Set port C newDispState[i + 5] = 0b10000100;//Set port B break; case 3: newDispState[i] = 0b00001111;//Set port C newDispState[i + 5] = 0b00000100;//Set port B break; case 4: newDispState[i] = 0b00000110;//Set port C newDispState[i + 5] = 0b01000100;//Set port B break; case 5: newDispState[i] = 0b00001101;//Set port C newDispState[i + 5] = 0b01000100;//Set port B break; case 6: newDispState[i] = 0b00001101;//Set port C newDispState[i + 5] = 0b11000100;//Set port B break; case 7: newDispState[i] = 0b00000111;//Set port C newDispState[i + 5] = 0b00000000;//Set port B break; case 8: newDispState[i] = 0b00001111;//Set port C newDispState[i + 5] = 0b11000100;//Set port B break; case 9: newDispState[i] = 0b00000111;//Set port C newDispState[i + 5] = 0b01000100;//Set port B break; default: break; } } //Set alarm indicator and AM/PM if(hours > 11){//Set PM bit if it's noon or after 13 military is 1pm newDispState[4] = 0b00000001; }else{ newDispState[4] = 0; } if(alarmState){//Sets or clears alarm indicator newDispState[4 + 5] = 0b00000100; }else{ newDispState[4 + 5] = 0; } } void softAlarm(){ for(uint8_t i = 0; i < 5; i++){ delay_mS(300); chirp(); setLight16Bit(0xFFFF); delay_mS(300); chirp(); setLight16Bit(0); } } void setDisplay(uint8_t *newDispState, uint8_t &minutes, uint8_t &hours, uint8_t &oldMinutes, uint8_t &oldHours,bool alarmEn){ if( (oldMinutes != minutes) | (oldHours != hours) ){//Check to see if a display update is needed timeToDisplayFormat(newDispState, minutes, hours, alarmEn); simpleDispUpdate(newDispState); oldMinutes = minutes; oldHours = hours; } } /*void copyDispState(uint8_t *newDispState, uint8_t *oldDispState) { for(uint8_t i = 0; i < 5; i++){ *(oldDispState + i) = *(newDispState + i); } }*/