#include "conf.h" //Configuration settings #include "main.h" //Declarations for main //Initialize variables #ifdef ADC // Initialize variable for LDR unsigned int ldr = 0; #endif // Initialize variable for VFD brightness level unsigned int brtlvl_chg = 0; // Boolean if external interrupt occurs unsigned int update = 0; // Boolean for setting time manually with user input unsigned int edit_datetime = 0; // Initialize variables for date time and alarms unsigned int sec = 0; unsigned int min = 0; unsigned int hour = 0; unsigned int day = 1; unsigned int date = 1; unsigned int month = 1; unsigned int year = 00; unsigned int century = 20; unsigned int alarm1_sec = 0; unsigned int alarm1_min = 0; unsigned int alarm1_hour = 0; unsigned int alarm2_min = 0; unsigned int alarm2_hour = 0; unsigned int status_reg = 0; unsigned int alarm1_status = 0; unsigned int alarm2_status = 0; unsigned int control_reg = 0; // Initialize variables for temperature unsigned int temperature_lsb = 0; int temperature_msb = 0; unsigned char temp_sign = ' '; void main(void) { #ifdef ADC TRISA0 = 0x01; // RA is input (ADC) #endif TRISBbits.TRISB2 = 1; TRISBbits.TRISB4 = 1; TRISBbits.TRISB5 = 1; GIE = 0; //Disable interrupts until set up OPTION_REGbits.nRBPU = 0; // PORTB pull-ups are enabled by individual port latch values INTE = 1; // Enable RB0 as external interrupt GIE = 1; // Enable all interrupts from now on TRISC = 0x00; // Set PORTC as outputs PORTC = 0x00; // Set all PORTC pins LOW TRISD = 0x00; // Set PORTD as outputs PORTD = 0x00; // Set all PORTD pins LOW #ifdef DEBUG Initialize_UART(); //Initialize UART module UART_send_string(CLS); // Clear screen UART_send_string(CURSOR(12, 23)); // Set cursor at line 12, column 23 UART_send_string(GREEN); // Set Green UART_send_string("UART Module Initialized and active\r\n"); UART_send_string(CLRATTR); // No color #endif I2C_Master_Init(100000); // Init I2C Master with 100KHz Clock #ifdef DEBUG UART_send_string(CURSOR(13, 24)); // Set cursor at line 13, column 24 UART_send_string(GREEN); // Set Green UART_send_string("I2C Module Initialized and active\r\n"); UART_send_string(CLRATTR); // No color #endif #ifdef ADC Adc_Init(); // Init I2C Master with 100KHz Clock #ifdef DEBUG UART_send_string(CURSOR(14, 24)); // Set cursor at line 14, column 24 UART_send_string(GREEN); // Set Green UART_send_string("ADC Module Initialized and active\r\n"); UART_send_string(CLRATTR); // No color #endif #endif Lcd_Init(); #ifdef DEBUG UART_send_string(CURSOR(15, 24)); // Set cursor at line 15, column 24 UART_send_string(GREEN); // Set Green UART_send_string("LCD Module Initialized and active\r\n"); UART_send_string(CLRATTR); // No color #endif #ifdef VFD Vfd_Set_Brightness(0); // Maximum 0 - Minimum 3 #endif display_Intro(); display_Lcd_Layout(); __delay_ms(1000); /* * Set an initial date time via initial program variables * Currently 00:00:00 1/1/00 and Sunday (day of week as 1) */ //Set_Time(); //Set_Date(); //Set_DayOfWeek(Get_DayOfWeek(year, month, date)); /* * isr expects 1Hz clock from RTC. This will update clock each second * While interrupted, clock is updating and set button will be ignored. * Here we set Status register 0Eh bit4 and bit3 as 0b00 for 1Hz. * 0x40 sets bit6 as high so BBSQW is enabled and 1Hz square wave results * out the !INT/SQW pin of the device. */ Set_Sqwe(0x40); //1Hz #ifdef VFD Vfd_Set_Brightness(3); //Max brightness 3 - minimum #endif while (1) { /* * If 1Hz interrupt from RTC received, isr sets update to 1 * Update time/date/alarm/temp/other variables from RTC * Update digits on display device * reset update boolean until next RTC 1Hz interrupt * delay 500ms for a perception of ':'s blinking at 1/2 duty cycle */ if (update) { timeSeparatorOn(); Update_Current_Date_Time(); Read_Alarms_Temp(); Get_Alarm_Status(); format_Temperature(); update_Display(); update = 0; __delay_ms(500); } else { timeSeparatorOff(); } /* * Edit time when Set button is pressed (Active low) * set edit_Date_Time to 1 to enter edit date/time loop */ if (!SETB) { __delay_ms(250); edit_Date_Time(); } /* * If ADC is enabled then read from ADC channel into ldr * If VFD available set brtlvl (ldr defaulted to 0) * brtlvl is (ldr / 256) and brtlvl_chg is compared with brtlvl * New brightness level is set when a difference occurs */ #ifdef ADC // Get current LDR reading and calculate for brightness levels 0 - 3 ldr = Adc_Read(0); #ifdef VFD if (brtlvl_chg != brtlvl) { Vfd_Set_Brightness(brtlvl); brtlvl_chg = brtlvl; } #endif #endif #ifdef DEBUG UART_send_string(CURSOR(19, 1)); sprintf(buf, "%sTIME:%s\t%02d:%02d:%02d\t%sDATE:%s\t%02d/%02d/%02d\r\n", YELLOW, CLRATTR, hour, min, sec, YELLOW, CLRATTR, date, month, year); UART_send_string(buf); sprintf(buf, "%sAL1:%s\t%02d:%02d:%02d\t%sSTATUS:%s %d\r\n", YELLOW, CLRATTR, alarm1_hour, alarm1_min, alarm1_sec, YELLOW, CLRATTR, alarm1_status); UART_send_string(buf); sprintf(buf, "%sAL2:%s\t%02d:%02d\t\t%sSTATUS:%s %d\r\n", YELLOW, CLRATTR, alarm2_hour, alarm2_min, YELLOW, CLRATTR, alarm2_status); UART_send_string(buf); sprintf(buf, "%sWKDAY:%s\t%d\t\t%sDAY:%s\t%s\r\n", YELLOW, CLRATTR, day, YELLOW, CLRATTR, Get_WeekDay(day)); UART_send_string(buf); sprintf(buf, "%sTEMP:\t%s%c%c%c.%c%cC%s\t\t%sLDR:%s\t\%04d %d\r\n", YELLOW, CLRATTR, temp_sign, temp_2, temp_1, temp_0, 0xB0, CLRATTR, YELLOW, CLRATTR, ldr, brtlvl); UART_send_string(buf); sprintf(buf, "%sISR:\t%s%d", YELLOW, CLRATTR, update); UART_send_string(buf); #endif } } /* * DS3231 11h has MSB of temp and 12h has LSB of temp * MSB and LSB bits 7 have sign * MSB bits 0-7 have DATA * LSB bits 7 and 6 have DATA * 10-bits with 0.25 resolution (this program drops hundredths place) */ void format_Temperature() { // If MSB negative then get 2's complement and sign for display to '-' // otherwise MSB is positive and we set sign for display to '+' if (temperature_msb < 0) { temperature_msb *= -1; temp_sign = '-'; } else { temp_sign = '+'; } //Shift LSB fractional value 6 bits to the right so b7 & b6 are b1 & b0 temperature_lsb >>= 6; //Fractional is 0b00, 0b01, 0b10, 0b11. Multiply by 25 for 00, 25, 50, 75 temperature_lsb *= 25; } // Determine Alarm status from control register 0Fh bits 0 (A1F) and 1 (A2F) void Get_Alarm_Status() { alarm1_status = control_reg & 0x01; // Read alarm1 A1F bit alarm2_status = (control_reg >> 1) & 0x01; // Read alarm2 A2F bit } void display_Alpha(char y, char x, char c) { Lcd_Set_Cursor(y, x); Lcd_Write_Char(c); } // Separate data into tens and ones units to display with character displays void display_Digit(char y, char x, unsigned int data) { Lcd_Set_Cursor(y, x); Lcd_Write_Char(MSB(data)); Lcd_Write_Char(LSB(data)); } void display_Text(char y, char x, char* c) { Lcd_Set_Cursor(y, x); Lcd_Write_String(c); } // Provides the user program info on startup void display_Intro() { // Give an intro message on the LCD Lcd_Clear(); display_Text(1, 4, "Desk Clock"); display_Text(2, 15, "V1"); __delay_ms(1000); //display for 1sec } // Provides the user the current layout of time/date/temp for the display void display_Lcd_Layout() { // Setup time date display format. 0xDF is degree symbol Lcd_Clear(); display_Text(1, 1, "HH:mm:ss -PP.P"); display_Alpha(1, 15, 0xDF); display_Alpha(1, 16, 'C'); // Civilian Vernacular US m/d/yyyy display_Text(2, 1, " ddd MM/DD/CCYY "); } // Updates the display with values that change and not static info void update_Display() { // Display Hours display_Digit(1, 1, hour); // Display minutes display_Digit(1, 4, min); // Display seconds display_Digit(1, 7, sec); // Display day display_Digit(2, 6, month); // Display month display_Digit(2, 9, date); // Display century display_Digit(2, 12, century); // Display year display_Digit(2, 14, year); // Display day of week display_Text(2, 2, Get_WeekDay(day)); // Display temperature display_Alpha(1, 10, temp_sign); display_Digit(1, 11, temperature_msb); display_Alpha(1, 14, (MSB(temperature_lsb))); // Chime at top of hour if (min == 00 && sec == 00) { alarm(2); } // Chime at half of hour if (min == 30 && sec == 00) { alarm(1); } } /* * Called when set button is pressed and S is in lower right of display for * set mode. Immediately goes into setting the hour. Each time SETB is pressed: * minutes then seconds then hour then year then month then date then update * RTC and edit flag is cleared and clock continues as normal. Sets time * separator to display for time in case they were blanked. * * As month/date is selected, functions determine if year is leap or not, * the max number of days in each month, and displays the day of the week. * ex: 29/02/2020 is Sat and leap year with max date to select in Feb as 29. * * Order to edit is hour -> min -> sec -> year -> month -> day * Pressing Set after day results in RTC being updated with current edit values. */ void edit_Date_Time(void) { // Variable to determine if RTC is updated or not (date/time valus change) // Variables set to current rtc value unsigned int hour_org = hour; unsigned int min_org = min; unsigned int sec_org = sec; unsigned int year_org = year; unsigned int month_org = month; unsigned int date_org = date; display_Alpha(2, 16, 'S'); edit_datetime++; while (edit_datetime > 0 && edit_datetime < 7) { // Set colons if not enabled when Set button is pressed timeSeparatorOn(); switch (edit_datetime) { case 1: //Hours display_Alpha(2, 16, 'h'); if (!DECR) { decrementValue(&hour, 0, 23); display_Digit(1, 1, hour); } else if (!INCR) { incrementValue(&hour, 0, 23); display_Digit(1, 1, hour); } break; case 2: //Minutes display_Alpha(2, 16, 'm'); if (!DECR) { decrementValue(&min, 0, 59); display_Digit(1, 4, min); } else if (!INCR) { incrementValue(&min, 0, 59); display_Digit(1, 4, min); } break; case 3: //Seconds display_Alpha(2, 16, 's'); if (!DECR) { decrementValue(&sec, 0, 59); display_Digit(1, 7, sec); } else if (!INCR) { incrementValue(&sec, 0, 59); display_Digit(1, 7, sec); } break; case 4: //Year display_Alpha(2, 16, 'Y'); if (!DECR) { decrementValue(&year, 0, 99); display_Digit(2, 14, year); } else if (!INCR) { incrementValue(&year, 0, 99); display_Digit(2, 14, year); } break; case 5: //Month display_Alpha(2, 16, 'M'); if (!DECR) { decrementValue(&month, 1, 12); display_Digit(2, 6, month); } else if (!INCR) { incrementValue(&month, 1, 12); display_Digit(2, 6, month); } break; case 6: //Date display_Alpha(2, 16, 'D'); if (!DECR) { decrementValue(&date, 1, Get_Days_In_Month(year, month)); display_Text(2, 2, Get_WeekDay(Get_DayOfWeek(year, month, date))); display_Digit(2, 9, date); } else if (!INCR) { incrementValue(&date, 1, Get_Days_In_Month(year, month)); display_Text(2, 2, Get_WeekDay(Get_DayOfWeek(year, month, date))); display_Digit(2, 9, date); } break; //TODO Maybe add a case to edit century on display default: break; } if (!SETB) { __delay_ms(250); edit_datetime++; } } // TODO Edit alarms /* * Set button pressed after Date selection then we set the values in RTC. * Unset edit_datetime until next time set button is pressed and clear * field text from lower right of display. */ if (edit_datetime > 6) { Lcd_Set_Cursor(2, 16); Lcd_Write_String(" "); edit_datetime = 0; /* * If any date/time field was changed, clear updateRTC field and * update RTC. */ if (hour_org != hour || min_org != min || sec_org != sec || year_org != year || month_org != month || date_org != date) { Set_Time(); Set_DayOfWeek(Get_DayOfWeek(year, month, date)); Set_Date(); } } } void timeSeparatorOn(void) { display_Alpha(1, 3, ':'); display_Alpha(1, 6, ':'); } void timeSeparatorOff(void) { display_Alpha(1, 3, ' '); display_Alpha(1, 6, ' '); } void decrementValue(unsigned int *v, unsigned int threshold, unsigned int limit) { __delay_ms(250); if (*v == threshold) { *v = limit; } else { *v -= 1; } } void incrementValue(unsigned int *v, unsigned int lowerlimit, unsigned int upperlimit) { __delay_ms(250); if (*v + 1 > upperlimit) { *v = lowerlimit; } else { *v += 1; } } /* * interrupt service routine (isr) triggered by external interrupt pin RB0 by * RTC (DS3231) SQW output at 1Hz. The update flag is set so that the display * is updated with current RTC date/time/temp info and updates display * brightness if enabled */ void __interrupt() isr(void) { if (INTF == 1) { // External interrupt detected update = 1; INTF = 0; } }