/****************************************************************************************** * hello.output.c * * based on hello.ftdi.c, LCD hello-world, and speaker PWM hello-world by Neil Gershenfeld * and button44 by Rob Hart * set lfuse to 0x5E for 20 MHz xtal * * Debounces button inputs and displays the count to the LCD and sets off alarm at odd counts * * Brian Plancher * 11/17/16 * ********************************************************************************************/ #include #include #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 long_delay() _delay_ms(1000) // delay before redraw #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 // input devices // Button PB2 #define input_port PORTB #define input_direction DDRB #define input_pins PINB #define button_pin (1 << PB2) #define button_key 6 // output devices // Speaker PA7 // DB7 PA0 // DB6 PA1 // DB5 PA2 // DB4 PA3 // E PA4 // RS PA5 #define speaker_port PORTA #define speaker_direction DDRA #define speaker_pin (1 << PA7) #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) // for debounce #define XTAL 20000000L // Crystal frequency in Hz #define OUTER_LOOPS 2000 #define INNER_LOOPS (XTAL / OUTER_LOOPS / 750) #define MAX_COUNT 32767 #define MAX_COUNT_SIZE 15 // for speaker #define SPEAKER_HZ 520 // want to cycle at 520 HZ because google says that is ideal alarm sound #define SPEAKER_CYCLE 520 //(XTAL / SPEAKER_HZ / 2) // XTAL / 520 gets us 38461 which fits in a 16 bit unsigned int! :) #define current 150 // PWM current #define off 255 // PWM off // // 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); } void clear_display(){ // // clear display // lcd_putcmd(0); lcd_putcmd(DB4); lcd_delay(); } void putintchar(uint8_t intchar){ static const char line2_0[] PROGMEM = "0"; static const char line2_1[] PROGMEM = "1"; static const char line2_2[] PROGMEM = "2"; static const char line2_3[] PROGMEM = "3"; static const char line2_4[] PROGMEM = "4"; static const char line2_5[] PROGMEM = "5"; static const char line2_6[] PROGMEM = "6"; static const char line2_7[] PROGMEM = "7"; static const char line2_8[] PROGMEM = "8"; static const char line2_9[] PROGMEM = "9"; switch(intchar){ case 1:{ lcd_putstring((PGM_P) line2_1); break; } case 2:{ lcd_putstring((PGM_P) line2_2); break; } case 3:{ lcd_putstring((PGM_P) line2_3); break; } case 4:{ lcd_putstring((PGM_P) line2_4); break; } case 5:{ lcd_putstring((PGM_P) line2_5); break; } case 6:{ lcd_putstring((PGM_P) line2_6); break; } case 7:{ lcd_putstring((PGM_P) line2_7); break; } case 8:{ lcd_putstring((PGM_P) line2_8); break; } case 9:{ lcd_putstring((PGM_P) line2_9); break; } default:{ lcd_putstring((PGM_P) line2_0); break; } } } void print_int(uint16_t intin){ if (intin >= 10){ print_int(intin / 10); } uint8_t last_digit = (uint8_t) intin % 10; putintchar(last_digit); } void draw(uint16_t display_counter){ // // go to zero position // lcd_putcmd(0); lcd_putcmd(DB5); // // print first line from flash // static const char line1[] PROGMEM = "The count is:"; lcd_putstring((PGM_P) line1); // // move to second line // lcd_putcmd(DB7+DB6); lcd_putcmd(0); // // print second line from flash // print_int(display_counter); } int main(void) { // // main // // set clock divider to /1 // CLKPR = (1 << CLKPCE); CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); // // initialize LCD pins // clear(LCD_port, DB7); output(LCD_direction, DB7); clear(LCD_port, DB6); output(LCD_direction, DB6); clear(LCD_port, DB5); output(LCD_direction, DB5); clear(LCD_port, DB4); output(LCD_direction, DB4); clear(LCD_port, E); output(LCD_direction, E); clear(LCD_port, RS); output(LCD_direction, RS); // // initialize LCD // lcd_init(); // set button for input and debounce set(input_port, button_pin); // turn on pull-up for the buttons input(input_direction, button_pin); // make button input volatile uint16_t inner_loop_cntr = 0; volatile uint16_t outer_loop_cntr = 0; volatile uint8_t btn_state = 0; volatile uint16_t display_counter = 98; // set speaker for output and timers output(speaker_direction, speaker_pin); clear(speaker_port,speaker_pin); volatile uint16_t speaker_counter = 0; volatile uint8_t speaker_state = 0; TCCR0A = ((1 << COM0B0) | (1 << COM0B1) | (1 << WGM01) | (1 << WGM00)); // set OC0B on compare match and set fast PWM mode, 0xFF TOP TCCR0B = (1 << CS00); // set timer 0 prescalar to 1 // // main loop // draw(display_counter); while (1) { // debounce the button inputs volatile uint8_t btn_flag = !pin_test(input_pins,button_pin); // invert b/c pullup if (btn_state != btn_flag){ btn_state = btn_flag; inner_loop_cntr = 0; outer_loop_cntr = 0; } if (outer_loop_cntr == OUTER_LOOPS){ outer_loop_cntr = 0; if (btn_state){ display_counter += btn_state; // see if majority of count was a bttn press display_counter = display_counter % MAX_COUNT; clear_display(); draw(display_counter); } } else{ if (inner_loop_cntr == INNER_LOOPS){ inner_loop_cntr = 0; outer_loop_cntr += 1; } else{ inner_loop_cntr++; } } // sound the alarm if in odd count if (display_counter % 2){ if (speaker_counter == SPEAKER_CYCLE){ if (speaker_state == 1){ OCR0B = current; speaker_state = 0; } else{ OCR0B = off; speaker_state = 1; } } speaker_counter = speaker_counter + 1; } // else turn it off and reset counters else{ OCR0B = off; speaker_counter = 0; speaker_state = 0; } } }