/****************************************************************************************** * main_controller.c * * based on hello.ftdi.c and hello.LCD.44.DC.c by Neil Gershenfeld * and button44 by Rob Hart * set lfuse to 0x5E for 20 MHz xtal * * Recieves data over serial and displays over LCD based on the main state machine * * Brian Plancher * 12/7/16 * ********************************************************************************************/ #include #include #include #include #include #include // helper defs #define output(directions,pin) (directions |= pin) // set port direction for output #define input(directions,pin) (directions &= (~pin)) // set port direction for input #define set(port,pin) (port |= pin) // set port pin #define clear(port,pin) (port &= (~pin)) // clear port pin #define pin_test(pins,pin) (pins & pin) // test for port pin #define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set #define bit_delay_time 8.5 // bit delay for 115200 with overhead #define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay #define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay #define char_delay() _delay_ms(10) // char delay #define long_delay() _delay_ms(1000) // long delay #define lcd_delay() _delay_ms(10) // delay between commands #define strobe_delay() _delay_us(1) // delay for strobe #define cycle_delay() _delay_us(2) // cycle delay #define second_delay() _delay_us(1000000) // full second delay // serial port #define serial_port PORTA #define serial_direction DDRA #define serial_pins PINA #define serial_pin_in (1 << PA7) // LCD ports // DB7 PA0 // DB6 PA1 // DB5 PA2 // DB4 PA3 // E PA4 // RS PA5 #define LCD_port PORTA #define LCD_direction DDRA #define DB7 (1 << PA0) #define DB6 (1 << PA1) #define DB5 (1 << PA2) #define DB4 (1 << PA3) #define E (1 << PA4) #define RS (1 << PA5) // alarm out signal #define Alarm_port PORTB #define Alarm_direction DDRB #define Alarm_pin (1 << PB2) // state machine #define READY_TO_ARM 0 #define ARMING_COUNTDOWN 1 #define ARMED 2 #define TRIGGER_COUNTDOWN 3 #define ALARM_ACTIVE 4 // state machine helpers #define MAX_CODE 999999999 //uint32_t can hold 9 digits of arbitrary numbers #define MAX_COUNTDOWN 5 #define ONE_SECOND 19531 void get_char(volatile unsigned char *pins, unsigned char pin, char *rxbyte) { // // read character into rxbyte on pins pin // assumes line driver (inverts bits) // *rxbyte = 0; /* while (pin_test(*pins,pin)) // // wait for start bit // ; */ // // delay to middle of first data bit // half_bit_delay(); bit_delay(); // // unrolled loop to read data bits // if pin_test(*pins,pin) *rxbyte |= (1 << 0); else *rxbyte |= (0 << 0); bit_delay(); if pin_test(*pins,pin) *rxbyte |= (1 << 1); else *rxbyte |= (0 << 1); bit_delay(); if pin_test(*pins,pin) *rxbyte |= (1 << 2); else *rxbyte |= (0 << 2); bit_delay(); if pin_test(*pins,pin) *rxbyte |= (1 << 3); else *rxbyte |= (0 << 3); bit_delay(); if pin_test(*pins,pin) *rxbyte |= (1 << 4); else *rxbyte |= (0 << 4); bit_delay(); if pin_test(*pins,pin) *rxbyte |= (1 << 5); else *rxbyte |= (0 << 5); bit_delay(); if pin_test(*pins,pin) *rxbyte |= (1 << 6); else *rxbyte |= (0 << 6); bit_delay(); if pin_test(*pins,pin) *rxbyte |= (1 << 7); else *rxbyte |= (0 << 7); // // wait for stop bit // bit_delay(); half_bit_delay(); } // // lcd_putchar // put character in lcdbyte // void lcd_putchar(char lcdbyte) { // // set RS for data // set(LCD_port, RS); // // output high nibble // if bit_test(lcdbyte, 7) set(LCD_port, DB7); else clear(LCD_port, DB7); if bit_test(lcdbyte, 6) set(LCD_port, DB6); else clear(LCD_port, DB6); if bit_test(lcdbyte, 5) set(LCD_port, DB5); else clear(LCD_port, DB5); if bit_test(lcdbyte, 4) set(LCD_port, DB4); else clear(LCD_port, DB4); // // strobe E // strobe_delay(); set(LCD_port, E); strobe_delay(); clear(LCD_port, E); // // wait // lcd_delay(); // // output low nibble // if bit_test(lcdbyte, 3) set(LCD_port, DB7); else clear(LCD_port, DB7); if bit_test(lcdbyte, 2) set(LCD_port, DB6); else clear(LCD_port, DB6); if bit_test(lcdbyte, 1) set(LCD_port, DB5); else clear(LCD_port, DB5); if bit_test(lcdbyte, 0) set(LCD_port, DB4); else clear(LCD_port, DB4); // // strobe E // strobe_delay(); set(LCD_port, E); strobe_delay(); clear(LCD_port, E); // // wait and return // lcd_delay(); } // // lcd_putcmd // put command in lcdbyte // void lcd_putcmd(char lcdbyte) { // // clear RS for command // clear(LCD_port, RS); // // output command bits // PORTA = lcdbyte; // // strobe E // strobe_delay(); set(LCD_port, E); strobe_delay(); clear(LCD_port, E); // // wait and return // lcd_delay(); } // // lcd_putstring // put a null-terminated string in flash // void lcd_putstring(PGM_P message) { static uint8_t index; static char chr; index = 0; while (1) { chr = pgm_read_byte(&(message[index])); if (chr == 0) return; lcd_putchar(chr); ++index; } } // // lcd_init // initialize the LCD // void lcd_init() { // // power-up delay // lcd_delay(); // // initialization sequence // lcd_putcmd(DB5+DB4); lcd_putcmd(DB5+DB4); lcd_putcmd(DB5+DB4); // // 4-bit interface // lcd_putcmd(DB5); // // two lines, 5x7 font // lcd_putcmd(DB5); lcd_putcmd(DB7); // // display on // lcd_putcmd(0); lcd_putcmd(DB7+DB6+DB5); // // entry mode // lcd_putcmd(0); lcd_putcmd(DB6+DB5); } // // clear display // void clear_display(){ lcd_putcmd(0); lcd_putcmd(DB4); lcd_delay(); } void putintchar(uint32_t intchar){ static const char char0[] PROGMEM = "0"; static const char char1[] PROGMEM = "1"; static const char char2[] PROGMEM = "2"; static const char char3[] PROGMEM = "3"; static const char char4[] PROGMEM = "4"; static const char char5[] PROGMEM = "5"; static const char char6[] PROGMEM = "6"; static const char char7[] PROGMEM = "7"; static const char char8[] PROGMEM = "8"; static const char char9[] PROGMEM = "9"; switch(intchar){ case 1:{ lcd_putstring((PGM_P) char1); break; } case 2:{ lcd_putstring((PGM_P) char2); break; } case 3:{ lcd_putstring((PGM_P) char3); break; } case 4:{ lcd_putstring((PGM_P) char4); break; } case 5:{ lcd_putstring((PGM_P) char5); break; } case 6:{ lcd_putstring((PGM_P) char6); break; } case 7:{ lcd_putstring((PGM_P) char7); break; } case 8:{ lcd_putstring((PGM_P) char8); break; } case 9:{ lcd_putstring((PGM_P) char9); break; } default:{ lcd_putstring((PGM_P) char0); break; } } } void print_int(uint32_t intin){ if (intin >= 10){ print_int(intin / 10); } putintchar(intin % 10); } void drawArming(uint32_t code, uint8_t no_code){ clear_display(); // go to zero position lcd_putcmd(0); lcd_putcmd(DB5); // print first line static const char line1[] PROGMEM = "Ready to arm:"; lcd_putstring((PGM_P) line1); // move to second line lcd_putcmd(DB7+DB6); lcd_putcmd(0); // print second line if applicable if (!no_code){ print_int(code); } } void drawCodeTooLongError(){ clear_display(); // go to zero position lcd_putcmd(0); lcd_putcmd(DB5); // print first line static const char line1[] PROGMEM = "Error: Max Code"; lcd_putstring((PGM_P) line1); // move to second line lcd_putcmd(DB7+DB6); lcd_putcmd(0); // print second line static const char line2[] PROGMEM = " is 9 digits"; lcd_putstring((PGM_P) line2); } void drawNoCodeError(){ clear_display(); // go to zero position lcd_putcmd(0); lcd_putcmd(DB5); // print first line static const char line1[] PROGMEM = "Error: No Code"; lcd_putstring((PGM_P) line1); } void drawArmingCountdown(uint8_t count){ clear_display(); // go to zero position lcd_putcmd(0); lcd_putcmd(DB5); // print first line static const char line1[] PROGMEM = "Arming in:"; lcd_putstring((PGM_P) line1); // move to second line lcd_putcmd(DB7+DB6); lcd_putcmd(0); // print count print_int(count); } void drawArmed(){ clear_display(); // go to zero position lcd_putcmd(0); lcd_putcmd(DB5); // print first line static const char line1[] PROGMEM = "Alarm Armed"; lcd_putstring((PGM_P) line1); } void drawTriggered(uint32_t code, uint8_t no_code, uint8_t counter){ clear_display(); // go to zero position lcd_putcmd(0); lcd_putcmd(DB5); // print first line static const char line1[] PROGMEM = "Alarm In: "; lcd_putstring((PGM_P) line1); print_int(counter); // move to second line lcd_putcmd(DB7+DB6); lcd_putcmd(0); // print second line if applicable if (!no_code){ print_int(code); } } void drawAlarmActive(uint32_t code, uint8_t no_code){ clear_display(); // go to zero position lcd_putcmd(0); lcd_putcmd(DB5); // print first line static const char line1[] PROGMEM = "Alarm triggered:"; lcd_putstring((PGM_P) line1); // move to second line lcd_putcmd(DB7+DB6); lcd_putcmd(0); // print second line if applicable if (!no_code){ print_int(code); } } void drawWrongCode(){ clear_display(); // go to zero position lcd_putcmd(0); lcd_putcmd(DB5); // print first line static const char line1[] PROGMEM = "Error: Bad Code"; lcd_putstring((PGM_P) line1); // move to second line lcd_putcmd(DB7+DB6); lcd_putcmd(0); // print second line static const char line2[] PROGMEM = "Try Again!"; lcd_putstring((PGM_P) line2); } uint32_t addDigitToCode(char chr, uint32_t code, uint8_t no_code){ if (no_code){ code = atoi(&chr); } else{ code = code * 10 + atoi(&chr); } // if our new code is too big then we need to error if (code > MAX_CODE){ drawCodeTooLongError(); second_delay(); code = 0; } return code; } uint32_t removeDigitFromCode(uint32_t code){ if (code < 10){ code = 0; } else{ // integer division will remove last digit code = code / 10; } return code; } int main(void) { // set clock divider to /1 CLKPR = (1 << CLKPCE); CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); // enable timer overflow interrupt for Timer1 //TIMSK=(1< error if (isdigit(chr)){ temp_code = code; code = addDigitToCode(chr,code,no_code); // check for overflow error if (temp_code > code){ no_code = 1; } else{ no_code = 0; } // update LCD drawArming(code,no_code); } // else if we get a backspace remove one digit from the code else if (chr == 'B'){ temp_code = code; code = removeDigitFromCode(code); // check for no more digits if (temp_code < 10){ no_code = 1; } else{ no_code = 0; } // update LCD drawArming(code,no_code); } // else if enter then start arming countdown else if (chr == 'E'){ if (!no_code){ state = ARMING_COUNTDOWN; counter = MAX_COUNTDOWN; saved_code = code; code = 0; no_code = 1; } else{ // draw error and go back to where we were drawNoCodeError(); second_delay(); drawArming(code,no_code); } } // else if menu then go into a menu mode else if (chr == 'M'){ continue; // not implemented yet } } else if (state == ARMING_COUNTDOWN){ // test if done waiting if (counter == 0){ state = ARMED; drawArmed(); continue; } // else display the count drawArmingCountdown(counter); // delay one second second_delay(); // then decrement for next cycle counter = counter - 1; } else if (state == ARMED){ // in armed state wait to be triggered by an alarm or be disarmed if ((chr == 'A')){ state = TRIGGER_COUNTDOWN; // set the counter back to 0 TCNT1 = 0x00; counter = MAX_COUNTDOWN; } if (chr == 'C'){ state = READY_TO_ARM; code = 0; no_code = 1; } } else if (state == TRIGGER_COUNTDOWN){ // check if our timer says we have spent a second and update if (TCNT1 > ONE_SECOND){ counter = counter - 1; TCNT1 = 0x00; drawTriggered(code,no_code,counter); } // check to see if we need to sound the alarm if (counter == 0){ state = ALARM_ACTIVE; counter = MAX_COUNTDOWN; drawAlarmActive(code,no_code); } // if we get in a numerical value update the code up to the max size --> error if (isdigit(chr)){ temp_code = code; code = addDigitToCode(chr,code,no_code); // check for overflow error if (temp_code > code){ no_code = 1; } else{ no_code = 0; } drawTriggered(code,no_code,counter); } // else if we get a backspace remove one digit from the code else if (chr == 'B'){ temp_code = code; code = removeDigitFromCode(code); // check for no more digits if (temp_code < 10){ no_code = 1; } else{ no_code = 0; } drawTriggered(code,no_code,counter); } // else if enter then test to see if we disarm or wrong else if (chr == 'E'){ // if we have a code and hit enter test it if (!no_code){ if (code == saved_code){ state = READY_TO_ARM; code = 0; no_code = 1; drawArming(code,no_code); } else{ drawWrongCode(); second_delay(); drawTriggered(code,no_code,counter); } } // else draw no code error and go back to where we were else{ drawNoCodeError(); second_delay(); drawTriggered(code,no_code,counter); } } // else if we get a disarm signal then disarm else if (chr == 'C'){ state = READY_TO_ARM; code = 0; no_code = 1; drawArming(code,no_code); } } else if (state == ALARM_ACTIVE){ // make sure alarm out is high set(Alarm_port, Alarm_pin); // if we get in a numerical value update the code up to the max size --> error if (isdigit(chr)){ temp_code = code; code = addDigitToCode(chr,code,no_code); // check for overflow error if (temp_code > code){ no_code = 1; } else{ no_code = 0; } // update the LCD drawAlarmActive(code,no_code); } // else if we get a backspace remove one digit from the code else if (chr == 'B'){ temp_code = code; code = removeDigitFromCode(code); // check for no more digits if (temp_code < 10){ no_code = 1; } else{ no_code = 0; } drawAlarmActive(code,no_code); } // else if enter then test to see if we disarm or wrong else if (chr == 'E'){ // see if there is a code to enter if (!no_code){ if (code == saved_code){ state = READY_TO_ARM; code = 0; no_code = 1; clear(Alarm_port, Alarm_pin); drawArming(code,no_code); } else{ drawWrongCode(); second_delay(); drawAlarmActive(code,no_code); } } // draw error and go back to where we were else{ drawNoCodeError(); second_delay(); drawAlarmActive(code,no_code); } } // else if we get a disarm signal then disarm if (chr == 'C'){ code = 0; no_code = 1; clear(Alarm_port, Alarm_pin); drawArming(code,no_code); } } // error in weird state so reset to READY_TO_ARM else{ state = READY_TO_ARM; } // reset chr for next loop chr = 0; } }