#define F_CPU 16000000UL /* Define CPU Frequency e.g. here its 8MHz */
#include <avr/io.h> /* Include AVR std. library file */
#include <util/delay.h> /* Include inbuilt defined Delay header file */
#include <util/atomic.h>
#include <avr/interrupt.h>
#include "millis.h"
#define LCD_Data_Dir DDRD /* Define LCD data port direction */
#define LCD_Command_Dir DDRB /* Define LCD command port direction register */
#define LCD_Data_Port PORTD /* Define LCD data port */
#define LCD_Command_Port PORTB /* Define LCD data port */
#define RS PB4 /* Define Register Select signal pin */
#define RW PB2 /* Define Read/Write signal pin */
#define EN PB3 /* Define Enable signal pin */
// Pins definition for Time Set Buttons
#define Buttons_Port PORTB
#define Buttons_Dir DDRB
#define HRs PB1 // Pin PB1 for Hours Setting
#define MINs PB0 // Pin PB0 for Minutes/Preset Seconds(0) Setting
///CLOCK VARIABLES
// initial Time display is 24:59:45 PM
char h=24;
char m=59;
char s=45; //was int
int flag=1; //PM
// Time Set Buttons
int buttonHR;
int buttonMIN;
// For accurate Time reading, use Arduino Real Time Clock and not just delay()
static uint32_t last_time, now = 0; // RTC
uint32_t current_timeElapsed = 0;
///END OF CLOCK VARIABLES
ISR(TIMER1_COMPA_vect){
timer1_millis++;
}
int main(){
LCD_init();
millis_init(F_CPU);
sei();
CLK_init();
// LCD_CatAni();
while(1){
CLK_main();
}
}
void CLK_init (){
//Sets PB1 and PB0 as input
Buttons_Dir &= (~(1<<HRs)) & (~(1<<MINs));
//Enables pull-up resistor for PB1 and PB0
Buttons_Port |= (1<<HRs) | (1<<MINs);
now=time_Elapsed_ms();
}
void CLK_main (){
LCD_Clear();
// Update LCD Display
// Print TIME in Hour, Min, Sec
LCD_Command(0x80); //setcursor to line 1
LCD_String("Time ");
if(h<10)LCD_String("0"); // always 2 digits
char hrs[2];
sprintf(hrs, "%d", h);
LCD_String(hrs);
LCD_String(":");
if(m<10)LCD_String("0");
char mins[2];
sprintf(mins, "%d", m);
LCD_String(mins);
LCD_String(":");
if(s<10)LCD_Char("0");
char secs[2];
sprintf(secs, "%d", s);
LCD_String(secs);
LCD_Command(0xc0); //setcursor to line 2
LCD_String("Gan Ke Yi PD12");
//running
// improved replacement of delay(1000)
// Much better accuracy, no more dependant of loop execution time
for ( int i=0 ;i<5 ;i++)// make 5 time 200ms loop, for faster Button response
{
while ((now-last_time)<200) //delay200ms
{
now=time_Elapsed_ms();
}
// inner 200ms loop
last_time=now; // prepare for next loop
// read Setting Buttons
buttonHR= (PINB & HRs);// Read Buttons
buttonMIN= (PINB & MINs);
// Process Button 1 or Button 2 when hit
if(buttonHR==0){
h=h+1;
}
if(buttonMIN==0){
s=0;
m=m+1;
}
//manage seconds, minutes, hours overflow
if(s==60){
s=0;
m=m+1;
}
if(m==60){
m=0;
h=h+1;
}
if(h==24){
h=0;
flag=flag;
}
if((buttonHR==0)|(buttonMIN==0))// Update display if time set button pressed
{
// Update LCD Display
// Print TIME in Hour, Min, Sec
LCD_Command(0x80); //setcursor to line 1
LCD_String("Time ");
if(h<10)LCD_String("0");// always 2 digits
LCD_String(hrs);
LCD_String(":");
if(m<10)LCD_String("0");
LCD_String(mins);
LCD_String(":");
if(s<10)LCD_String("0");
LCD_String(secs);
LCD_Command(0xc0); //setcursor to line 2
LCD_String("Precision clock");
}
} // end for
// outer 1000ms loop
s=s+1; //increment sec. counting
// ---- manage seconds, minutes, hours am/pm overflow ----
if(s==60){
s=0;
m=m+1;
}
if(m==60){
m=0;
h=h+1;
}
if(h==24){
h=0;
flag=flag;
}
///END running
}
void millis_init(unsigned long f_cpu){
unsigned long ctc_match_overflow;
//when timer1 is this value, 1ms has passed
ctc_match_overflow = ((f_cpu / 1000) / 8);
// (Set timer to clear when matching ctc_match_overflow) | (Set clock divisor to 8)
TCCR1B |= (1 << WGM12) | (1 << CS11);
// high byte first, then low byte
OCR1AH = (ctc_match_overflow >> 8);
OCR1AL = ctc_match_overflow;
// Enable the compare match interrupt
TIMSK1 |= (1 << OCIE1A);
}
unsigned long time_Elapsed_ms(){
unsigned long millis_return;
// Ensure this cannot be disrupted
ATOMIC_BLOCK(ATOMIC_FORCEON) {
millis_return = timer1_millis;
}
return millis_return;
}
void LCD_Command(unsigned char cmnd){
LCD_Data_Port= cmnd;
LCD_Command_Port &= ~(1<<RS); /* RS=0 command reg. */
LCD_Command_Port &= ~(1<<RW); /* RW=0 Write operation */
LCD_Command_Port |= (1<<EN); /* Enable pulse */
_delay_us(1);
LCD_Command_Port &= ~(1<<EN);
_delay_ms(2);
}
//LCD data write function
void LCD_Char (unsigned char char_data){
LCD_Data_Port= char_data;
LCD_Command_Port |= (1<<RS); /* RS=1 Data reg. */
LCD_Command_Port &= ~(1<<RW); /* RW=0 write operation */
LCD_Command_Port |= (1<<EN); /* Enable Pulse */
_delay_us(1);
LCD_Command_Port &= ~(1<<EN);
_delay_ms(2); /* Data write delay */
}
//LCD initialize function
void LCD_init (void){
LCD_Command_Dir = 0xFF; /* Make LCD command port direction as o/p */
LCD_Data_Dir = 0xFF; /* Make LCD data port direction as o/p */
_delay_ms(20); /* LCD Power ON delay always >15ms */
LCD_Command (0x38); /* Initialization of 16X2 LCD in 8bit mode */
LCD_Command (0x0C); /* Display ON Cursor OFF */
LCD_Command (0x06); /* Auto Increment cursor */
LCD_Command (0x01); /* clear display */
_delay_ms(2); /* Clear display command delay> 1.63 ms */
LCD_Command (0x80); /* Cursor at home position */
}
//Send string to LCD function
void LCD_String (char *str){
int i;
for(i=0;str[i]!=0;i++) /* Send each char of string till the NULL */
{
LCD_Char (str[i]);
}
}
//Send string to LCD with xy position
void LCD_String_xy (char row, char pos, char *str){
if (row == 0 && pos<16)
LCD_Command((pos & 0x0F)|0x80); /* Command of first row and required position<16 */
else if (row == 1 && pos<16)
LCD_Command((pos & 0x0F)|0xC0); /* Command of first row and required position<16 */
LCD_String(str); /* Call LCD string function */
}
void LCD_Clear(){
LCD_Command (0x01); /* clear display */
LCD_Command (0x80); /* cursor at home position */
}
void LCD_Custom_Char (unsigned char loc, byte *msg){
unsigned char i;
if(loc<8)
{
LCD_Command (0x40 + (loc*8)); /* Command 0x40 and onwards forces the device to point CGRAM address */
for(i=0;i<8;i++) /* Write 8 byte for generation of 1 character */
LCD_Char(msg[i]);
}
}
void LCD_CatAni (){
LCD_Command(0x80); //setcursor to line 1
///FRAME 1
byte image00[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B01000, B00100};
byte image01[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00011, B00011};
byte image02[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00100, B11100};
byte image03[8] = {B00000, B00000, B00000, B00000, B11011, B11111, B01110, B00100};
LCD_Custom_Char(0, image00);
LCD_Custom_Char(1, image01);
LCD_Custom_Char(2, image02);
LCD_Custom_Char(3, image03);
LCD_Command(0x80 | 0x01);
LCD_Char(0);
LCD_Command(0x80 | 0x02);
LCD_Char(1);
LCD_Command(0x80 | 0x03);
LCD_Char(2);
LCD_Command(0x80 | 0x04);
LCD_Char(3);
byte image04[8] = {B01000, B01000, B00100, B00010, B00001, B00001, B00001, B00010};
byte image05[8] = {B00011, B00111, B00001, B00011, B11111, B11111, B10000, B10000};
byte image06[8] = {B11100, B11110, B11000, B11000, B11000, B11000, B01100, B00100};
LCD_Custom_Char(4, image04);
LCD_Custom_Char(5, image05);
LCD_Custom_Char(6, image06);
LCD_Command(0xc0 | 0x01);
LCD_Char(4);
LCD_Command(0xc0 | 0x02);
LCD_Char(5);
LCD_Command(0xc0 | 0x03);
LCD_Char(6);
_delay_ms(1000);
///FRAME 2
LCD_Clear();
byte image10[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00100, B00010};
byte image11[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00110, B00111};
byte image12[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B01000, B11000};
byte image13[8] = {B00000, B00000, B00000, B00000, B00000, B10100, B01000, B00000};
LCD_Custom_Char(0, image10);
LCD_Custom_Char(1, image11);
LCD_Custom_Char(2, image12);
LCD_Custom_Char(3, image13);
LCD_Command(0x80 | 0x02);
LCD_Char(0);
LCD_Command(0x80 | 0x03);
LCD_Char(1);
LCD_Command(0x80 | 0x04);
LCD_Char(2);
LCD_Command(0x80 | 0x05);
LCD_Char(3);
byte image14[8] = {B00001, B00010, B00100, B00100, B00011, B00011, B00010, B00010};
byte image15[8] = {B00111, B01111, B00011, B00111, B11111, B11111, B00000, B00000};
byte image16[8] = {B11000, B11100, B10000, B10000, B10000, B10000, B10000, B10000};
LCD_Custom_Char(4, image14);
LCD_Custom_Char(5, image15);
LCD_Custom_Char(6, image16);
LCD_Command(0xc0 | 0x02);
LCD_Char(4);
LCD_Command(0xc0 | 0x03);
LCD_Char(5);
LCD_Command(0xc0 | 0x04);
LCD_Char(6);
_delay_ms(1000);
///FRAME 3
LCD_Clear();
byte image20[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00100, B00010};
byte image21[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B01100, B01111};
byte image22[8] = {B00000, B00000, B00000, B00000, B00001, B00001, B10000, B10000};
byte image23[8] = {B00000, B00000, B00000, B00000, B01100, B11100, B11000, B10000};
LCD_Custom_Char(0, image20);
LCD_Custom_Char(1, image21);
LCD_Custom_Char(2, image22);
LCD_Custom_Char(3, image23);
LCD_Command(0x80 | 0x03);
LCD_Char(0);
LCD_Command(0x80 | 0x04);
LCD_Char(1);
LCD_Command(0x80 | 0x05);
LCD_Char(2);
LCD_Command(0x80 | 0x06);
LCD_Char(3);
byte image24[8] = {B01000, B10000, B10000, B01000, B00111, B00111, B01100, B01010};
byte image25[8] = {B01111, B11111, B00111, B01111, B11111, B11111, B00011, B00010};
byte image26[8] = {B10000, B11000, B00000, B00000, B00000, B00000, B10000, B10000};
LCD_Custom_Char(4, image24);
LCD_Custom_Char(5, image25);
LCD_Custom_Char(6, image26);
LCD_Command(0xc0 | 0x03);
LCD_Char(4);
LCD_Command(0xc0 | 0x04);
LCD_Char(5);
LCD_Command(0xc0 | 0x05);
LCD_Char(6);
_delay_ms(1000);
///FRAME 4
LCD_Clear();
byte image30[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00100, B00010};
byte image31[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00110, B00111};
byte image32[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B01000, B11000};
byte image33[8] = {B00000, B00000, B00000, B00000, B00000, B10100, B01000, B00000};
LCD_Custom_Char(0, image30);
LCD_Custom_Char(1, image31);
LCD_Custom_Char(2, image32);
LCD_Custom_Char(3, image33);
LCD_Command(0x80 | 0x04);
LCD_Char(0);
LCD_Command(0x80 | 0x05);
LCD_Char(1);
LCD_Command(0x80 | 0x06);
LCD_Char(2);
LCD_Command(0x80 | 0x07);
LCD_Char(3);
byte image34[8] = {B00001, B00010, B00100, B00100, B00011, B00011, B00010, B00010};
byte image35[8] = {B00111, B01111, B00011, B00111, B11111, B11111, B00000, B00000};
byte image36[8] = {B11000, B11100, B10000, B10000, B10000, B10000, B10000, B10000};
LCD_Custom_Char(4, image34);
LCD_Custom_Char(5, image35);
LCD_Custom_Char(6, image36);
LCD_Command(0xc0 | 0x04);
LCD_Char(4);
LCD_Command(0xc0 | 0x05);
LCD_Char(5);
LCD_Command(0xc0 | 0x06);
LCD_Char(6);
_delay_ms(1000);
///FRAME 5
LCD_Clear();
byte image40[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B01000, B00100};
byte image41[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00011, B00011};
byte image42[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00100, B11100};
byte image43[8] = {B00000, B00000, B00000, B00000, B11011, B11111, B01110, B00100};
LCD_Custom_Char(0, image40);
LCD_Custom_Char(1, image41);
LCD_Custom_Char(2, image42);
LCD_Custom_Char(3, image43);
LCD_Command(0x80 | 0x05);
LCD_Char(0);
LCD_Command(0x80 | 0x06);
LCD_Char(1);
LCD_Command(0x80 | 0x07);
LCD_Char(2);
LCD_Command(0x80 | 0x08);
LCD_Char(3);
byte image44[8] = {B01000, B01000, B00100, B00010, B00001, B00001, B00001, B00010};
byte image45[8] = {B00011, B00111, B00001, B00011, B11111, B11111, B10000, B10000};
byte image46[8] = {B11100, B11110, B11000, B11000, B11000, B11000, B01100, B00100};
LCD_Custom_Char(4, image44);
LCD_Custom_Char(5, image45);
LCD_Custom_Char(6, image46);
LCD_Command(0xc0 | 0x05);
LCD_Char(4);
LCD_Command(0xc0 | 0x06);
LCD_Char(5);
LCD_Command(0xc0 | 0x07);
LCD_Char(6);
_delay_ms(1000);
///FRAME 6
LCD_Clear();
byte image50[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00000, B01100};
byte image51[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00011, B00011};
byte image52[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00100, B11100};
byte image53[8] = {B00000, B00000, B00000, B00000, B00000, B10100, B01000, B00000};
LCD_Custom_Char(0, image50);
LCD_Custom_Char(1, image51);
LCD_Custom_Char(2, image52);
LCD_Custom_Char(3, image53);
LCD_Command(0x80 | 0x06);
LCD_Char(0);
LCD_Command(0x80 | 0x07);
LCD_Char(1);
LCD_Command(0x80 | 0x08);
LCD_Char(2);
LCD_Command(0x80 | 0x09);
LCD_Char(3);
byte image54[8] = {B00010, B00100, B00100, B00010, B00001, B00001, B00001, B00001};
byte image55[8] = {B00011, B00111, B00001, B00011, B11111, B11111, B00000, B00000};
byte image56[8] = {B11100, B11110, B11000, B11000, B11000, B11000, B01000, B01000};
LCD_Custom_Char(4, image54);
LCD_Custom_Char(5, image55);
LCD_Custom_Char(6, image56);
LCD_Command(0xc0 | 0x06);
LCD_Char(4);
LCD_Command(0xc0 | 0x07);
LCD_Char(5);
LCD_Command(0xc0 | 0x08);
LCD_Char(6);
_delay_ms(1000);
///FRAME 7
LCD_Clear();
byte image60[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00010, B00001};
byte image61[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00011, B00011};
byte image62[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00100, B11100};
byte image63[8] = {B00000, B00000, B00000, B01010, B11111, B01110, B00100, B00000};
LCD_Custom_Char(0, image60);
LCD_Custom_Char(1, image61);
LCD_Custom_Char(2, image62);
LCD_Custom_Char(3, image63);
LCD_Command(0x80 | 0x07);
LCD_Char(0);
LCD_Command(0x80 | 0x08);
LCD_Char(1);
LCD_Command(0x80 | 0x09);
LCD_Char(2);
LCD_Command(0x80 | 0x0A);
LCD_Char(3);
byte image64[8] = {B00010, B00100, B00100, B00010, B00001, B00001, B00011, B00010};
byte image65[8] = {B00011, B00111, B00001, B00011, B11111, B11111, B00000, B00000};
byte image66[8] = {B11100, B11110, B11000, B11000, B11000, B11000, B11100, B10100};
LCD_Custom_Char(4, image64);
LCD_Custom_Char(5, image65);
LCD_Custom_Char(6, image66);
LCD_Command(0xc0 | 0x07);
LCD_Char(4);
LCD_Command(0xc0 | 0x08);
LCD_Char(5);
LCD_Command(0xc0 | 0x09);
LCD_Char(6);
_delay_ms(1000);
///FRAME 8, TEXT BEGINS
LCD_Clear();
LCD_Command(0x80);
LCD_String("Hello!");
byte image80[8] = {B00000, B00000, B00000, B10100, B01000, B00000, B00000, B00000};
byte image81[8] = {B00000, B01010, B11111, B01110, B00100, B00000, B00100, B01010};
byte image82[8] = {B00000, B00000, B00000, B00110, B00111, B00111, B00111, B01111};
byte image83[8] = {B00000, B00000, B00000, B01000, B11000, B11001, B11010, B11101};
byte image84[8] = {B00000, B10100, B01000, B00000, B00000, B00000, B00101, B00010};
LCD_Custom_Char(0, image80);
LCD_Custom_Char(1, image81);
LCD_Custom_Char(2, image82);
LCD_Custom_Char(3, image83);
LCD_Custom_Char(4, image84);
LCD_Command(0x80 | 0x09);
LCD_Char(0);
LCD_Command(0x80 | 0x0A);
LCD_Char(1);
LCD_Command(0x80 | 0x0B);
LCD_Char(2);
LCD_Command(0x80 | 0x0C);
LCD_Char(3);
LCD_Command(0x80 | 0x0D);
LCD_Char(4);
byte image85[8] = {B00110, B01000, B10000, B10001, B01011, B00111, B00011, B00001};
byte image86[8] = {B00111, B00111, B11111, B11111, B11111, B11111, B11111, B11111};
byte image87[8] = {B10000, B11000, B11000, B11000, B11000, B11000, B11100, B11100};
LCD_Custom_Char(5, image85);
LCD_Custom_Char(6, image86);
LCD_Custom_Char(7, image87);
LCD_Command(0xc0 | 0x0A);
LCD_Char(5);
LCD_Command(0xc0 | 0x0B);
LCD_Char(6);
LCD_Command(0xc0 | 0x0C);
LCD_Char(7);
_delay_ms(3000);
}
//Information
/*
Key base code and functions:
https://www.electronicwings.com/avr-atmega/lcd-custom-character-display-using-atmega-16-32-
Original Inspirations:
https://www.hackster.io/milespeterson101/an-lcd-dog-friend-b0bdee
https://create.arduino.cc/projecthub/mavrakis_the_optimist/simplest-24h-uno-digital-clock-ever-200342
Custom Glyph Generator:
https://tusindfryd.github.io/screenduino/
Arduino Uno Pinout Diagram:
https://upload.wikimedia.org/wikipedia/commons/c/c9/Pinout_of_ARDUINO_Board_and_ATMega328PU.svg
SetCursor equivalent in pure C:
https://www.microchip.com/forums/m861309.aspx
millis equivalent in pure C:
https://github.com/monoclecat/avr-millis-function
For the usual 2x16 LCD the cursor addressing takes one command byte computed like this:
For line 1: 0x80 + Column
For line 2: 0xc0 + Column
Where column ranges from 0 (0x00) to 15 (0x0f)
why Serial cannot be used:
https://forum.arduino.cc/t/serial-begin-9600-interfering-with-16x2-dispaly/390029
0, 1 are used by the Serial. You can't use for LCD at the same time.
*/