/* RTC_setup_BB.c */ /* RTC settings will be downloaded at compilation */ /* so change define constants to current time. */ /* Remove RTC after setup to run on backup battery */ /* For RTC DS1307 in 12 hour mode */ // Arduino UNO R3 // AVR_ATmega328P #define F_CPU 16000000UL #include // register names/addresses and bit names/numbers #include // for the nop instruction #include void _delay_ms ( double ); void _delay_us ( double ); /* UART function prototypes */ void init_uart( void ); unsigned char getchar_uart( void ); void putchar_uart( unsigned char ); /* Useful simplified printf()-lookalike function for debuging Byte values */ void printf_byte_uart( char *string, unsigned char variable ); /* I2C function prototypes */ void I2C_Init_BB( void ); void I2C_Start_BB( void ); void I2C_Stop_BB( void ); void I2C_Write_BB( unsigned char ); unsigned char I2C_ReadACK_BB( void ); unsigned char I2C_ReadNACK_BB( void ); // DS1307 7-bit device address is 0x68, 104 #define RTC_WR_ADDRESS 0xD0 // 8-bit write address, 208 #define RTC_RD_ADDRESS 0xD1 // 8-bit read address, 209 /* I2C pin connections */ /* Redefine if other pins are used */ #define SCL_BBpin PB0 #define SCL_DDR DDRB #define SCL_PORT PORTB #define SCL_PIN PINB #define SDA_BBpin PB1 #define SDA_DDR DDRB #define SDA_PORT PORTB #define SDA_PIN PINB /* Time values to setup the RTC at compilation */ /* Example: 11:59:45 at morning. Change for actual time */ #define SECONDS 0x45 // same as BCD digits, change for current time #define MINUTES 0x59 // same as BCD digits, change for current time #define HOURS 0x11 // same as BCD digits, change for current time #define AMPM 0 // AMPM = 0 at morning, AMPM = 1 past morning, change to current void RTC_setup(); // call once to setup RTC with hour, minutes, and seconds from program. int main(void) { unsigned char Status; unsigned char hr, mn, sec; I2C_Init_BB(); init_uart(); _delay_ms (3000); // wait for console opening /* Run setup function once - do not setup with same values at every reset! */ RTC_setup(); // Remove RTC after setup to run on the backup battery while(1) { /* Read hours register */ I2C_Start_BB(); I2C_Write_BB( RTC_WR_ADDRESS); I2C_Write_BB( 0x02 ); // dummy write "Hours register" to set address pointer I2C_Start_BB(); // repeat start I2C_Write_BB( RTC_RD_ADDRESS); hr = (I2C_ReadNACK_BB()) & 0x1F ; // the last and only data byte to read (NACK) I2C_Stop_BB(); printf_byte_uart( "Hours %x\r\n", hr ); // debug hex output /* Read minutes register */ I2C_Start_BB(); I2C_Write_BB( RTC_WR_ADDRESS); I2C_Write_BB( 0x01 ); // dummy write "Minutes register" to set address pointer I2C_Start_BB(); // repeat start I2C_Write_BB( RTC_RD_ADDRESS); mn = (I2C_ReadNACK_BB()) & 0x7F; // the last and only data byte to read (NACK) I2C_Stop_BB(); printf_byte_uart( "Minutes %x\r\n", mn ); // debug hex output I2C_Start_BB(); I2C_Write_BB( RTC_WR_ADDRESS); I2C_Write_BB( 0x00 ); // dummy write "seconds register" to set address pointer I2C_Start_BB(); // repeat start I2C_Write_BB( RTC_RD_ADDRESS); sec = (I2C_ReadNACK_BB()) & 0x7F ; // the last and only data byte to read (NACK) I2C_Stop_BB(); printf_byte_uart( "Seconds %x\r\n", sec ); // debug hex output _delay_ms (1000); // check every second } return 0; /* never reached */ } /* *********************************** */ /* FUNCTIONS */ /* *********************************** */ /* UART FUNCTIONS */ void init_uart( void ) { // Bits in UCSR0B register: "RXCIE0 TXCIE0 UDRIE0 RXEN0 TXEN0 UCSZ02 RXB80 TXB80" UCSR0B |= (1 << (RXEN0)) | (1 << (TXEN0)); // "---11---" // Bits in UCSR0C register: "UMSEL01 UMSEL00 UPM01 UPM00 USBS0 UCSZ01/UCPHA0 UCSZ01/UCPHA0 UCPOL0" UCSR0C |= (1 << (UCSZ01)) | (1 << (UCSZ00)); // "-----11-" UBRR0 = 103; // Baud prescaler 103 -> 9600 Baud } unsigned char getchar_uart( void ) { unsigned char ch; // Bits in UCSR0A register: "RXC0 TXC0 UDRE0 FE0 DOR0 UPE0 U2X0 MPCM0" // wait until a byte is ready to read while( ( UCSR0A & ( 1 << (RXC0) ) ) == 0 ){} // grab the byte from the serial port ch = UDR0; return ch; } void putchar_uart( unsigned char ch ) { // Bits in UCSR0A register: "RXC0 TXC0 UDRE0 FE0 DOR0 UPE0 U2X0 MPCM0" // wait until the port is ready to be written to while( ( UCSR0A & ( 1 << (UDRE0) ) ) == 0 ){} // write the byte to the serial port UDR0 = ch; } /* Useful simplified printf() function for debuging Byte values */ void printf_byte_uart( char *string, unsigned char variable ) { char i, k, m, a, b; char digits[] ="0123456789ABCDEF"; for(i = 0 ; ; i++) { k = string[i]; if( k == '\0') break; // at end of string if( k == '%') // insert variable in string { i++; k = string[i]; switch(k) { case 'd': // %d byte as signed if( variable > 127) { putchar_uart('-'); variable = -variable; } else putchar_uart(' '); // no break! case 'u': // %u byte as unsigned a = variable/100; putchar_uart( digits[a] ); // print 100's b = variable%100; a = b/10; putchar_uart( digits[a]); // print 10's a = b%10; putchar_uart( digits[a]); // print 1's break; case 'x': // byte as hexadecimal a = variable/16; putchar_uart( digits[a] ); // print hi hex a = variable%16; putchar_uart( digits[a]); // print low hex break; case 'b': // %b byte as binary for( m = 0 ; m < 8 ; m++ ) { if (!(variable & 0x80 )) putchar_uart('0'); else putchar_uart('1'); variable = variable << 1; } break; case 'c': // %c byte as 'char' putchar_uart( variable ); break; case '%': putchar_uart('%'); // %% -> % break; default: // most format specifiers are not implemented putchar_uart('!'); } } else putchar_uart(k); } } /* I2C FUNCTIONS */ void I2C_Init_BB(void) { SDA_PORT &= (~( 1 << SDA_BBpin )); // port = 0 SCL_PORT &= (~( 1 << SCL_BBpin )); // port = 0 SDA_DDR &=( ~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA high SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high } void I2C_Start_BB( void ) { /* SDA _ |__ 100 SCL __ |_ 110 */ SDA_DDR &= (~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA high SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); // slow I2C clock SDA_DDR |= ( 1 << SDA_BBpin ); // direction 1 = output, port = 0 -> SDA low _delay_us (10); SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); } //send stop signal void I2C_Stop_BB( void ) { /* SDA _ __| 001 SCL __ _| 011 */ SDA_DDR |= ( 1 << SDA_BBpin ); // direction 1 = output, port = 0 -> SDA low SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); SDA_DDR &= (~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA high _delay_us (10); } void I2C_Write_BB( unsigned char data) { /* Write Byte A6 A5 A4 A3 A2 A1 A0 R/W ACK D7 D6 D5 D4 D3 D2 D1 D0 ACK ___ ___ ___ ___ ___ ___ ___ ___ ___ SDA _ |___| |___| |___| |___| |___| |___| |___| |___| | | _ _ _ _ _ _ _ _ _ _ SCL _ _|1|_ _|2|_ _|3|_ _|4|_ _|5|_ _|6|_ _|7|_ _|8|_ _|9|_ _ */ unsigned char i, ack = 0; SDA_DDR |= ( 1 << SDA_BBpin ); // direction 1 = output, port = 0 -> SDA low SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low for ( i=0; i<8 ; i++ ) { // change data bit if( data & 0x80 ) SDA_DDR &= (~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA high else SDA_DDR |= ( 1 << SDA_BBpin ); // direction 1 = output, port = 0 -> SDA low _delay_us (10); SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); data = data << 1; } // Time for slave to ACK SDA_DDR &= (~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA high SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); ack = ( SDA_PIN & ( 1 << SDA_BBpin) ); // now read ack bit ("0") from slave // printf_byte_uart( "Did write get 0 as ack bit? %b\r\n", ack ); // use for debug SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); SDA_DDR |= ( 1 << SDA_BBpin ); // direction 1 = output, port = 0 -> SDA low } //read byte with ACK, use to read consecutive Bytes unsigned char I2C_ReadACK_BB(void) { /* Read Byte D7 D6 D5 D4 D3 D2 D1 D0 ACK ___ ___ ___ ___ ___ ___ ___ ___ ___ SDA |_?_| |_?_| |_?_| |_?_| |_?_| |_?_| |_?_| |_?_| | |_ _ _ _ _ _ _ _ _ _ SCL _|1|_ _|2|_ _|3|_ _|4|_ _|5|_ _|6|_ _|7|_ _|8|_ _|9|__ */ unsigned char data = 0x00; unsigned char i; for (i=0 ; i<8 ; i++ ) { SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low SDA_DDR &= (~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA as input _delay_us (100); SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); if( SDA_PIN & ( 1 << SDA_BBpin ) ) // read data bit data |= ( 0x80 >> i ) ; SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); } // Put Ack SDA_DDR |= ( 1 << SDA_BBpin ); // direction 1 = output, port = 0 -> SDA low _delay_us (10); SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); return data; } //read byte with NACK, use with last Byte read unsigned char I2C_ReadNACK_BB( void ) { unsigned char data = 0x00; unsigned char i; for (i=0 ; i<8 ; i++ ) { SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low SDA_DDR &= (~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA as input _delay_us (10); SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); if( SDA_PIN & ( 1 << SDA_BBpin ) ) // read data bit data |= ( 0x80 >> i ) ; SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); } // Put NAck SDA_DDR &= (~( 1 << SDA_BBpin )); // direction 0 = input, pull-up 1 -> SDA high _delay_us (100); SCL_DDR &= (~( 1 << SCL_BBpin )); // direction 0 = input, pull-up 1 -> SCL high _delay_us (10); SCL_DDR |= ( 1 << SCL_BBpin ); // direction 1 = output, port = 0 -> SCL low _delay_us (10); SDA_DDR |= ( 1 << SDA_BBpin ); // direction 1 = output, port = 0 -> SDA low return data; } /* Call once, after that RTC should run from its battery backup */ void RTC_setup() { /* Set RTC "Seconds register", (0x01) with "stop/!run" bit */ /* Format: "stop/!run sss ssss" 00-59 */ I2C_Start_BB(); I2C_Write_BB( RTC_WR_ADDRESS); I2C_Write_BB( 0x00 ); // address pointer for "Seconds register" with stop/!run bit I2C_Write_BB( SECONDS & 0x7F ); // mask seconds, and force !run/stop to run (run = 0) I2C_Stop_BB(); /* Set RTC "Hours register", (0x02) */ /* Format: "0 12/!24 !a/p h hhhh" 00-12 a-p ; 00-23 */ I2C_Start_BB(); I2C_Write_BB( RTC_WR_ADDRESS); I2C_Write_BB( 0x02 ); // set address pointer for "Hours register" I2C_Write_BB( ( HOURS & 0x3F) | 0x40 | ( AMPM << 5 ) ); I2C_Stop_BB(); /* Set RTC "Minutes register", (0x01) */ /* Format: "0 mmm mmmm" 00-59 */ I2C_Start_BB(); I2C_Write_BB( RTC_WR_ADDRESS); I2C_Write_BB( 0x01 ); // set address pointer for "Minutes register" I2C_Write_BB( MINUTES & 0x7F ); I2C_Stop_BB(); } /* *********************************** */ /* HARDWARE */ /* *********************************** */ /* Chip ATMega328 Arduino Uno R3 stackable header _______ Digital: _____/ \__ Analog: ______________ ______________ txd ->-|D00 >RXD A5|- | \/ | rxd -<-|D01 -(D01)-|03 PD1/TXD PC3 26|-(A3)- -|D04 A1|- -(D02)-|04 PD2/INT0 PC2 25|-(A2)- -|D05~ A0|- -(D03)-|05 PD3/INT1/PWM PC1 24|-(A1)- -|D06~ | Power: -(D04)-|06 PD4 PC0 23|-(A0)- -|D07 Vin|- +5V ---|07 VCC GND 22|--- Gnd | GND|--- GND Gnd ---|08 GND AREF 21|--- Vin SCL --<-|D08 GND|- Xtal |X|--|09 PB6/OSC1 AVCC 20|--- +5V SDA -<>-|D09~ +5V|--- +5V 16MHz |X|--|10 PB7/OSC2 SCK/PB5 19|-(D13)->- Bell -|D10~ +3.3V|- -(D05)-|11 PD5/PWM MISO/PB4 18|-(D12)- -|D11~ Res|- -(D06)-|12 PD6/PWM PWM/MOSI/PB3 17|-(D11)- -|D12 IOREF|- -(D07)-|13 PD7 PWM/SS'/PB2 16|-(D10)- Bell -<-|D13 LED --- | SCL-<-(D08)-|14 PB0 PWM/PB1 15|-(D09)-<>-SDA -|GND | |______________________________| -|AREF | -|SCL | -|SDA | |________________| */ /* __________ | Tiny RTC | | | -<--| DS | SCL-->-| SCL | SDA-<>-| SDA | +5V ----| VCC | GND ----| GND | |__________| RTC DS1307 registers 0x00: "Seconds register" Format: "stop/!run sss ssss" 00-59 0x01: "Minutes register" Format: "0 mmm mmmm" 00-59 0x02: "Hours register" Format: "0 12/!24 !a/p h hhhh" 00-12 a-p ; 00-23 0x03: "Day register" Format: "0000 0 ddd" 1-7 0x04: "Date register" Format: "00 DD DDDD" 01-28/29/30/31 0x05: "Month register" Format: "000 M MMMM" 01-12 0x06: "Year register" Format: "yyyy yyyy" 00-99 0x07: "Control register" Format: "OUT 00 SQWE 00 RS1 RS2" 1, 4096, 8192, 32768 Hz */