/* RTC_bell.c */ /* chimes for bell */ /* 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 ); /* Bell function prototypes */ void init_bell( void ); void ring_bell( unsigned char ); /* 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( void ); void I2C_Start( void ); void I2C_Stop( void ); void I2C_Write( unsigned char ); unsigned char I2C_ReadACK( void ); unsigned char I2C_ReadNACK( void ); unsigned char I2C_GetStatus( 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 int main(void) { unsigned char Status; unsigned char hr, mn, old_mn = 0; init_bell(); init_uart(); _delay_ms (3000); // wait for console opening I2C_Init(); while(1) { /* Read hours register */ I2C_Start(); I2C_Write( RTC_WR_ADDRESS); I2C_Write( 0x02 ); // dummy write "Hours register" to set address pointer I2C_Start(); // repeat start I2C_Write( RTC_RD_ADDRESS); hr = (I2C_ReadNACK()) & 0x1F ; // the last and only data byte to read (NACK) I2C_Stop(); // printf_byte_uart( "Hours %x\r\n", hr ); /* Hex is same as BCD */ hr = (hr & 0xF) + 10*( hr >> 4); // from BCD to Binary number printf_byte_uart( "Hours %u\r\n", hr ); // debug decimal output /* Read minutes register */ I2C_Start(); I2C_Write( RTC_WR_ADDRESS); I2C_Write( 0x01 ); // dummy write "Minutes register" to set address pointer I2C_Start(); // repeat start I2C_Write( RTC_RD_ADDRESS); mn = (I2C_ReadNACK()) & 0x7F; // the last and only data byte to read (NACK) I2C_Stop(); // printf_byte_uart( "Minutes %x\r\n", mn ); /* Hex is same as BCD */ mn = (mn & 0xF) + 10*( mn >> 4); // from BCD to Binary number printf_byte_uart( "Minutes %u\r\n", mn ); // debug decimal output /* Check if it's time to ring the bell! */ if ( (mn == 0) && (old_mn == 59)) ring_bell( hr ); old_mn = mn; _delay_ms (1500); // check every 1.5 seconds } return 0; /* never reached */ } /* *********************************** */ /* FUNCTIONS */ /* *********************************** */ void init_bell( void) { DDRB |= ( 1 << PB5 ); // Bell pin is output PORTB |= ( 1 << PB5 ); // idle is high } void ring_bell( unsigned char strikes ) { unsigned char i; for(i = 0; i < strikes; i++) { PORTB &= ~( 1 << PB5 ); // negative edge starts Bell sound _delay_ms (100); PORTB |= ( 1 << PB5 ); // idle back to high printf_byte_uart( "Bell ", 0 ); // debug print Bell text _delay_ms (3000); // sounding time _delay_ms (1000); // pause time } printf_byte_uart( "\r\n", 0 ); // debug print, newline } /* 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 Byte values debuging */ 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(void) { //set SCL frequency to < 100kHz the max freq of DS1307 // Bits in TWSR register: "TWS7 TWS6 TWS5 TWS4 TWS3 - TWPS1 TWPS0" TWSR = 0x00; // "xxxxx-00" prescaler set to divide by one // TWBR register: frequency division byte TWBR = 73; // SCLfreq = CPUfreq / (16+2*TWBR*Presc) = 16*10^6 / (16+2*73*1) = 99 kHz //enable TWI // Bits in TWCR register: "TWINT TWEA TWSTA TWSTO TWWC TWEN - TWIE" TWCR = (1 << TWEN); // "-----1--" } void I2C_Start( void ) { // Bits in TWCR register: "TWINT TWEA TWSTA TWSTO TWWC TWEN - TWIE" TWCR = (1 << TWINT)|(1 << TWSTA)|(1 << TWEN); // "1-1--1--" while ((TWCR & (1 << TWINT)) == 0); // "?-------" wait for done } //send stop signal void I2C_Stop( void ) { // Bits in TWCR register: "TWINT TWEA TWSTA TWSTO TWWC TWEN - TWIE" TWCR = (1 << TWINT)|(1 << TWSTO)|(1 << TWEN); // "1--1-1--" } void I2C_Write( unsigned char data) { TWDR = data; // Bits in TWCR register: "TWINT TWEA TWSTA TWSTO TWWC TWEN - TWIE" TWCR = (1 << TWINT)|(1 << TWEN); // "1----1--" while ((TWCR & (1 << TWINT)) == 0); // "?-------" wait for done } //read byte with ACK, use for consecutive Bytes read unsigned char I2C_ReadACK(void) { // Bits in TWCR register: "TWINT TWEA TWSTA TWSTO TWWC TWEN - TWIE" // with ACK set TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWEA); // "11---1--" while ((TWCR & (1 << TWINT)) == 0); // "?-------" wait for done return TWDR; } //read byte with NACK, use with last Byte read unsigned char I2C_ReadNACK( void ) { // Bits in TWCR register: "TWINT TWEA TWSTA TWSTO TWWC TWEN - TWIE" // no acknowledge bit set, NOT ACK TWCR = (1 << TWINT)|(1 << TWEN); // "1----1--" while ((TWCR & (1 << TWINT)) == 0); // "?-------" wait for done return TWDR; } /* Use as debuging tool during development */ /* Use with: printf_byte_uart("message text %x", I2C_GetStatus()) */ unsigned char I2C_GetStatus( void ) { unsigned char Status; // Bits in TWSR register: "TWS7 TWS6 TWS5 TWS4 TWS3 - TWPS1 TWSP0" Status = TWSR & 0xF8; // "-----000" mask status return Status; // TWI masked status code for debug /* Status codes for debuging TRANSMITTER MODE 0x08 A start condition has been set 0x08 A repeated start condition has been set 0x18 SLA+W (slave address +write bit) has been sent. ACK has been received. 0x20 SLA+W (slave address +write bit) has been sent. NOT ACK has been received. 0x28 Data byte has been transmitted. ACK has been received. 0x38 Data byte has been transmitted. NOT ACK has been received. 0x38 Arbitration lost in SLA+W or data bytes. RECIEVER MODE 0x08 A start condition has been set 0x10 A repeated start condition has been set 0x38 Arbitration lost in SLA+R (slave address plus read bit) or NOT ACK bit. 0x40 SLA+R (slave address +write bit) has been sent. ACK has been received. 0x48 SLA+R (slave address +write bit) has been sent. NOT ACK has been received. 0x50 Data byte has been received. ACK has been returned. 0x58 Data byte has been received. NOT ACK has been returned. */ } /* *********************************** */ /* HARDWARE */ /* *********************************** */ /* Chip ATMega328 Arduino Uno R3 stackable header _______ Digital: _____/ \__ Analog: ______________ ______________ txd ->-|D00 >RXD A5|->-- SCL | \/ | rxd -<-|D01 - SDA Res---|01 PC6/RES' ATM328 SCL/PC5 28|-(A5)->-- SCL -|D02 A3|- rxd -<-(D00)-|02 PD0/RXD SDA/PC4 27|-(A4)-<>- SDA -|D03~ A2|- txd ->-(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 -|D08 GND|- Xtal |X|--|09 PB6/OSC1 AVCC 20|--- +5V -|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 --- | -(D08)-|14 PB0 PWM/PB1 15|-(D09)- -|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 */