This post shows how did I build a simple real time clock using PIC12F1822, 1602 LCD and DS3231 with Car MP3 IR remote control (NEC protocol RC).
Before this project I made a NEC remote control decoder using PIC12F1822 MCU:
Extended NEC Protocol Decoder Using PIC12F1822 Microcontroller
Time and calendar are displayed on 1602 LCD with the help of a serial-in parallel-out shift register (74HC595, 74HC164, CD4094 .....) as what I made in the example below:
Interfacing PIC12F1822 microcontroller with LCD display
The DS3231 uses I2C protocol to interface with the master device which is in our example the PIC12F1822 MCU which has one I2C module.
The I2C protocol uses only two lines: SCL (Serial Clock) and SDA (Serial Data) which are in the PIC12F1822 pin RA1 and pin RA2 respectively.
Hardware Required:
- PIC12F1822 microcontroller
- DS3231 board - datasheet
- 1602 LCD screen
- 74HC595 shift register (74HC164 and CD4094 can be used)
- IR receiver
- 10K ohm variable resistor
- 47uF capacitor
- 5V supply source
- Breadboard
- Jumper wires
The IR receiver has 3 pins: GND, VCC and OUT. Pin OUT is connected to pin RA3 of the PIC12F1822.
The LCD screen with the 74HC595 shift register are connected to pins: RA0 (Enable), RA4 (Data) and RA5 (Clock).
The DS3231 board SCL and SDA are connected to pins RA1 and RA2 respectively.
In this project the PIC12F1822 uses its internal oscillator and MCLR pin is configured as a digital pin.
The Code:
The C code below was tested with CCS C compiler version 5.051.
The hardware I2C module of the PIC12F1822 MCU is initialized and configured using the function below with a speed of 100KHz:
#use I2C(master, I2C1, FAST = 100000)
master: set the microcontroller to the master mode
I2C1: use first I2C module.The DS3231 works with BCD format only and to convert the BCD to decimal and vise versa I used the 2 functions below. Before displaying (after reading from DS3231), the data have to be converted from BCD to decimal, and before writing to the DS3231 (after editing the parameters) the data have to be converted from decimal to BCD:
int8 bcd_to_decimal(number)
int8 decimal_to_bcd(number)
Each function returns the converted value of the variable number.
void DS3231_display() : displays time and calendar data, before displaying time and calendar data are converted from BCD format to decimal format using the function bcd_to_decimal(number) .
int8 edit(x, y, parameter) : I used this function to edit time calendar parameters (minutes, hours, date, month and year). I used a variable named i to distinguish between the parameters:
i = 0, 1 : time hours and minutes respectively
i = 2, 3, 4: calendar date, month and year respectively
After the edit of time and calendar, the data have to be converted back to BCD format using the function decimal_to_bcd(number) and written back to the DS3231.
void blink() : this small function works as a delay except that it is interrupted by the IR remote control codes for buttons 1, 2 and 3. When called and without pressing any of the three button the total time is 10 x 25ms = 250ms. With this function we can see the blinking of the selected parameter with a frequency of 2Hz. So a delay of 250ms comes after the print of the selected parameter and after that delay a 2 spaces is printed which makes the parameter disappears from the LCD and another 250ms delay comes after the print of the 2 spaces.
The remote control buttons and their codes are shown in the image below:
Finally the interfacing of the LCD with the PIC12F1822 needs a small driver which can be found in the the following topic:
3-Wire LCD driver for CCS PIC C compiler
The complete C code is the one below.
/* Interfacing PIC12F1822 MCU with DS3231 with NEC IR remote control (Car MP3) http://ccspicc.blogspot.com/ electronnote@gmail.com */ //LCD module connections #define LCD_DATA_PIN PIN_A5 #define LCD_CLOCK_PIN PIN_A4 #define LCD_EN_PIN PIN_A0 //End LCD module connections #include <12F1822.h> #fuses NOMCLR INTRC_IO PLL_SW #use delay(clock=32000000) #use fast_io(A) #include <3WireLCD.c> // 3-Wire LCD driver source file #define IR_Sensor PIN_A3 #use I2C(master, I2C1, FAST = 100000) int1 nec_ok = 0; int8 i, second, minute, hour, date, month, year; int32 nec_code; void nec_remote_read(){ int8 j; int16 count = 0; // Check 9ms pulse (remote control sends logic high) while(!input(IR_Sensor) && (count < 9500)) count = GET_TIMER1(); if((count > 9499) || (count < 8500)) return; // Check 4.5ms space (remote control sends logic low) SET_TIMER1(0); count = 0; while((input(IR_Sensor)) && (count < 5000)) count = GET_TIMER1(); if((count > 4999) || (count < 1500)) return; if(count < 3000){ // Check if previous code is repeated SET_TIMER1(0); count = 0; while(!input(IR_Sensor) && (count < 650)) count = GET_TIMER1(); if((count > 649) || (count < 500)) return; // Check if the repeated code is for button 2 or 3 if((nec_code == 0x40BF807F || nec_code == 0x40BF40BF)){ nec_ok = 1; // Decoding process is finished with success disable_interrupts(INT_RA3); // Disable the external interrupt return; } } // Read message (32 bits) for(j = 0; j < 32; j++){ SET_TIMER1(0); count = 0; while(!input(IR_Sensor) && (count < 650)) count = GET_TIMER1(); if((count > 649) || (count < 500)) return; count = 0; SET_TIMER1(0); while((input(IR_Sensor)) && (count < 1800)) count = GET_TIMER1(); if( (count > 1799) || (count < 400)) return; if( count > 1000) // If space width > 1ms bit_set(nec_code, (31 - j)); // Write 1 to bit (31 - j) else // If space width < 1ms bit_clear(nec_code, (31 - j)); // Write 0 to bit (31 - j) } disable_interrupts(INT_RA3); nec_ok = 1; return; } #INT_RA // RA port interrupt on change void ra_isr(void){ SET_TIMER1(0); SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_8); // Configure Timer 1 to increment every 1 us nec_remote_read(); SETUP_TIMER_1(T1_DISABLED); // Stop Timer1 module clear_interrupt(INT_RA); } int8 bcd_to_decimal(number){ // Convert BCD to decimal function return((number >> 4) * 10 + (number & 0x0F)); } int8 decimal_to_bcd(number){ // Convert decimal to BCD function return(((number / 10) << 4) + (number % 10)); } void ds3231_display(){ // Convert data from BCD format to decimal format second = bcd_to_decimal(second); minute = bcd_to_decimal(minute); hour = bcd_to_decimal(hour); date = bcd_to_decimal(date); month = bcd_to_decimal(month); year = bcd_to_decimal(year); // End conversion lcd_goto(13, 1); printf(lcd_out,"%02u", second); // Display seconds lcd_goto(10, 1); printf(lcd_out,"%02u", minute); // Display minutes lcd_goto(7, 1); printf(lcd_out,"%02u", hour); // Display hours lcd_goto(7, 2); printf(lcd_out,"%02u", date); // Display date lcd_goto(10, 2); printf(lcd_out,"%02u", month); // Display month lcd_goto(15, 2); printf(lcd_out,"%02u", year); // Display year } void _reset(){ nec_ok = 0; // Reset decoding process enable_interrupts(INT_RA3_H2L); // Enable RA3 interrupt (High to low) } void blink(){ int8 j = 0; while((!nec_ok || (nec_code != 0x40BF00FF) && (nec_code != 0x40BF40BF) && (nec_code != 0x40BF807F)) && (j < 10)){ j++; delay_ms(25); } } int8 edit(x, y, parameter){ _reset(); while(TRUE){ if(nec_ok && nec_code == 0x40BF40BF){ // If button RB1 is pressed parameter++; if(i == 0 && parameter > 23) // If hours > 23 ==> hours = 0 parameter = 0; if(i == 1 && parameter > 59) // If minutes > 59 ==> minutes = 0 parameter = 0; if(i == 2 && parameter > 31) // If date > 31 ==> date = 1 parameter = 1; if(i == 3 && parameter > 12) // If month > 12 ==> month = 1 parameter = 1; if(i == 4 && parameter > 99) // If year > 99 ==> year = 0 parameter = 0; } if(nec_ok && nec_code == 0x40BF807F){ if(i == 0 && parameter < 1) parameter = 24; if(i == 1 && parameter < 1) parameter = 60; if(i == 2 && parameter < 2) parameter = 32; if(i == 3 && parameter < 2) parameter = 13; if(i == 4 && parameter < 1) parameter = 100; parameter--; } lcd_goto(x, y); printf(lcd_out,"%02u", parameter); if(nec_ok){ delay_ms(200); _reset(); } blink(); lcd_goto(x, y); lcd_out(" "); // Display two spaces blink(); if(nec_ok && nec_code == 0x40BF00FF){ lcd_goto(x, y); printf(lcd_out,"%02u", parameter); i++; // Increment 'i' for the next parameter return parameter; // Return parameter value and exit } } } void main() { setup_oscillator(OSC_8MHZ | OSC_PLL_ON); // Set internal oscillator to 32MHz (8MHz and PLL) set_tris_a(0x0E); // Configure RA1, RA2 & RA3 as inputs port_a_pullups(8); // Enable pin RA3 internal pull-up lcd_initialize(); // Initialize LCD module lcd_cmd(LCD_CLEAR); // LCD Clear enable_interrupts(GLOBAL); // Enable global interrupts clear_interrupt(INT_RA); // Clear RA IOC flag bit enable_interrupts(INT_RA3_H2L); // Enable RA3 interrupt (High to low) lcd_goto(1, 1); lcd_out("TIME: : :"); lcd_goto(1, 2); lcd_out("DATE: / /20"); while(TRUE){ if(nec_ok == 1){ // If a NEC remote code was received if(nec_code == 0x40BF00FF){ // If the remote code is for button 1 i = 0; hour = edit(7, 1, hour); minute = edit(10, 1, minute); date = edit(7, 2, date); month = edit(10, 2, month); year = edit(15, 2, year); // Convert decimal to BCD minute = decimal_to_bcd(minute); hour = decimal_to_bcd(hour); date = decimal_to_bcd(date); month = decimal_to_bcd(month); year = decimal_to_bcd(year); // End conversion // Write data to DS3231 RTC i2c_start(); // Start I2C protocol i2c_write(0xD0); // DS3231 address i2c_write(0); // Send register address i2c_write(0); // Reset seconds and start oscillator i2c_write(minute); // Write minute value to DS3231 i2c_write(hour); // Write hour value to DS3231 i2c_write(1); // Write day value (not used) i2c_write(date); // Write date value to DS3231 i2c_write(month); // Write month value to DS3231 i2c_write(year); // Write year value to DS3231 } _reset(); // Call _reset function } i2c_start(); // Start I2C protocol i2c_write(0xD0); // DS3231 address i2c_write(0); // Send register address i2c_start(); // Restart I2C i2c_write(0xD1); // Initialize data read second = i2c_read(1); // Read seconds from register 0 minute = i2c_read(1); // Read minutes from register 1 hour = i2c_read(1); // Read hour from register 2 i2c_read(1); // Read day from register 3 (not used) date = i2c_read(1); // Read date from register 4 month = i2c_read(1); // Read month from register 5 year = i2c_read(0); // Read year from register 6 i2c_stop(); // Stop I2C protocol DS3231_display(); // Display time & calendar delay_ms(50); } } // End of code.Small video: