// // // phil_T_LCD.c // // 115200 baud // // set lfuse to 0x5E for 20 MHz xtal. This divides clock by 8, giving 2.5 MHz clock frequency. // The divider is set to /1 in the main program, running the chip at 20 MHz, just at the top // of the specified range. The chip should receive 4.5-5.5 V in order to operate above 10 MHz. //F. Phil Brooks III // 10/23/19 // Harvard Chemistry and Chemical Biology // Based on code by // Neil Gershenfeld // 12/8/10 // (c) Massachusetts Institute of Technology 2010 // This work may be reproduced, modified, distributed, // performed, and displayed for any purpose. Copyright is // retained and must be preserved. The work is provided // as is; no warranty is provided, and users accept all // liability. //--------------------------------------------------------------------------------------------------- //#include #include #include #include #include #include #include #include #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 serial_pin_in (1 << PA0) #define serial_pin_out (1 << PA1) #define serial_interrupt (1 << PCIE0) #define serial_interrupt_pin (1 << PCINT0) #define serial_interrupt_flag (1 << PCIF0) //needed to clear flag #define button_interrupt (1 << PCIE1) #define button_interrupt_pin (1 << PCINT10) #define button_interrupt_flag (1 << PCIF1) #define button_pin (1 << PB2) #define DB7 (1 << PA7) #define DB6 (1 << PA6) #define DB5 (1 << PA5) #define DB4 (1 << PA4) #define E (1 << PA3) #define RS (1 << PA2) #define lcd_delay() _delay_ms(5) //delay between LCD commands #define strobe_delay() _delay_us(1) //delay for strobe #define max_buffer 100 //Reserved for future temperature logging functionality #define ADC_N 100 //number of readings to average. volatile char buffer[max_buffer] = {0};//Reserved for future temperature logging functionality volatile int buffer_index = 0; volatile uint8_t count = 0; //For repeating ADC. Max is 255. (unsigned) volatile long t_acc = 0; volatile char sleep_flag = 0; //must be volatile so it isn't optimized out of main(). // // lcd_putchar // put character in lcdbyte // void lcd_putchar(char lcdbyte) { // // set RS for data // PORTA |= RS; // // output high nibble // if (lcdbyte & (1 << 7)) PORTA |= DB7; else PORTA &= ~DB7; if (lcdbyte & (1 << 6)) PORTA |= DB6; else PORTA &= ~DB6; if (lcdbyte & (1 << 5)) PORTA |= DB5; else PORTA &= ~DB5; if (lcdbyte & (1 << 4)) PORTA |= DB4; else PORTA &= ~DB4; // // strobe E // strobe_delay(); PORTA |= E; strobe_delay(); PORTA &= (~E); // // wait // lcd_delay(); // // output low nibble // if (lcdbyte & (1 << 3)) PORTA |= DB7; else PORTA &= ~DB7; if (lcdbyte & (1 << 2)) PORTA |= DB6; else PORTA &= ~DB6; if (lcdbyte & (1 << 1)) PORTA |= DB5; else PORTA &= ~DB5; if (lcdbyte & (1 << 0)) PORTA |= DB4; else PORTA &= ~DB4; // // strobe E // strobe_delay(); PORTA |= E; strobe_delay(); PORTA &= (~E); // // wait and return // lcd_delay(); } // // lcd_putcmd // put command in lcdbyte // void lcd_putcmd(char lcdbyte) { // // clear RS for command // PORTA &= ~RS; // // output command bits // Set relevant PORTA bits, clear unset bits, and leave non-LCD bits alone PORTA &= (~(DB4 | DB5 | DB6 | DB7 | E | RS)); PORTA |= ((DB4 | DB5 | DB6 | DB7 | E | RS) & lcdbyte); // // strobe E // strobe_delay(); PORTA |= E; strobe_delay(); PORTA &= (~E); // // wait and return // lcd_delay(); } // // lcd_putstring // put a null-terminated string // void lcd_putstring(char *message) { // // print a null-terminated string static uint8_t index; static char chr; index = 0; while (1) { chr = 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); } 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 (*pins & pin) { ;// wait for start bit } // delay to middle of first data bit //half_bit_delay(); //causes problems receiving o, w with fixed code. May have been legacy of bug. bit_delay(); // unrolled loop to read data bits if (*pins & pin) *rxbyte |= (1 << 0); else *rxbyte |= (0 << 0); bit_delay(); if (*pins & pin) *rxbyte |= (1 << 1); else *rxbyte |= (0 << 1); bit_delay(); if (*pins & pin) *rxbyte |= (1 << 2); else *rxbyte |= (0 << 2); bit_delay(); if (*pins & pin) *rxbyte |= (1 << 3); else *rxbyte |= (0 << 3); bit_delay(); if (*pins & pin) *rxbyte |= (1 << 4); else *rxbyte |= (0 << 4); bit_delay(); if (*pins & pin) *rxbyte |= (1 << 5); else *rxbyte |= (0 << 5); bit_delay(); if (*pins & pin) *rxbyte |= (1 << 6); else *rxbyte |= (0 << 6); bit_delay(); if (*pins & pin) *rxbyte |= (1 << 7); else *rxbyte |= (0 << 7); // wait for stop bit bit_delay(); half_bit_delay(); } void put_char(volatile unsigned char *port, unsigned char pin, char txchar) { // send character in txchar on port pin // assumes line driver (inverts bits) // start bit *port &= (~pin); bit_delay(); // // unrolled loop to write data bits if (txchar & (1<<0)) *port |= pin; else *port &= (~pin); bit_delay(); if (txchar & (1<<1)) *port |= pin; else *port &= (~pin); bit_delay(); if (txchar & (1<<2)) *port |= pin; else *port &= (~pin); bit_delay(); if (txchar & (1<<3)) *port |= pin; else *port &= (~pin); bit_delay(); if (txchar & (1<<4)) *port |= pin; else *port &= (~pin); bit_delay(); if (txchar & (1<<5)) *port |= pin; else *port &= (~pin); bit_delay(); if (txchar & (1<<6)) *port |= pin; else *port &= (~pin); bit_delay(); if (txchar & (1<<7)) *port |= pin; else *port &= (~pin); bit_delay(); // // stop bit *port |= pin; bit_delay(); } void put_string(volatile unsigned char *port, unsigned char pin, char *str) { // // print a null-terminated string static int index; index = 0; do { put_char(port, pin, str[index]); ++index; } while (str[index] != 0); } ISR(PCINT0_vect) { // // pin change interrupt handler // static char chr; get_char(&PINA, serial_pin_in, &chr); if (chr == 'T') { MCUCR |= (1 << SM0); //ADC noise reduction mode. sleep_flag = 1; //set sleep flag for main loop to catch. } GIFR = serial_interrupt_flag; //clear pending serial interrupts caused by data transmission. } ISR(INT0_vect) { MCUCR |= (1 << SM0); //ADC noise reduction mode. sleep_flag = 1; //set sleep flag for main loop to catch. GIFR = (1 << INTF0); //clear button interrupt flag //(catches bouncing, but not release of button, since // user release of button is much slower than this interrupt handler. } ISR(ADC_vect) { static char T[16]=""; //ADC conversion complete interrupt handler //read ADC in correct order. MCUCR &= ~(1 << SE); //disable sleep (recommended to prevent accidental sleep) static int t; t = ADCL;//must read low register first, then high. t |= (ADCH<<8); t_acc += t; //Start next conversion if not at ADC_N yet if (++count < ADC_N) { MCUCR |= (1 << SM0); //Set ADC noise reduction mode. sleep_flag = 1; //set sleep flag for main program. //Entering sleep from interrupt handler has the potential to set up recursive interrupts. //Recursion on a microcontroller is dangerous. Don't use it if you don't have to. } else { // // clear display // lcd_putcmd(0); lcd_putcmd(DB4); // // go to zero position // lcd_putcmd(0); lcd_putcmd(DB5); lcd_putstring("Temp: "); t_acc = (((t_acc/10-271*(ADC_N/10))*18)+32*ADC_N)/(ADC_N); ltoa(t_acc, T, 10); lcd_putstring(T); lcd_putchar(223); lcd_putchar('F'); // // move to second line // lcd_putcmd(DB7+DB6); lcd_putcmd(0); lcd_putstring("Stay Warm!"); t_acc = 0; count = 0; } } int main(void) { // // main // // set clock divider to /1: Operate at 20 MHz // CLKPR = (1 << CLKPCE); CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); // // initialize output pins // PORTA |= serial_pin_out; DDRA |= serial_pin_out; //Initialize LCD pins DDRA |= (DB7 | DB6 | DB5 | DB4 | RS | E); PORTA &= (~(DB7 | DB6 | DB5 | DB4 | RS | E)); // // set up pin change interrupt on input pin // GIMSK |= serial_interrupt; GIMSK |= (1 << INT0); //enable INT0 external interrupts PCMSK0 |= serial_interrupt_pin; MCUCR |= (1 << ISC01); //set external interrupt for falling edge on INT0 pin PORTB |= (1 << PB2); //turn on pull-up resistor PRR &= (~(1<