После UART, реализация TWI модуля на AVR кажется довольно корявой и без понимания I2C протокола будет не просто его освоить. Однако, если разобраться с "софтовой" эмуляцией протокола, то работа с TWI модулем уже не составит труда.
Не последне место в этом списке должно быть у официального аппнота: AVR315: Using the TWI module as I2C master
Если открыть руководство для ATmega8, на странице 156 раздел "Two-Wire Serial Interface", то там даже будет примерный набросок программы для работы с TWI:
На модуле с DS1307 есть EEPROM AT24 на 36 кБайт. Здесь: "The TWI example project" есть демонстрационая программа, которая распечатывает содержимое EEPROM в консоль UART. Выглядит это как-то так:
Там только в начале исходника нужно поправить #define F_CPU в соответствии с реальной частотой чипа.
Протокол работы c RTC DS1307, прост. Там есть внутрений регистр - счетчик адреса, и оперативка на 64 байт. Отношение к часам имеют только байты с 0х00 по 0х07, остальные на можно использовать по своему усмотрению. Учитывая, что там автономное питание, их можно использовать вместо или в паре с EEPROM.
Итак, чтобы записать данные в DS1307 , нужно сперва послать адрес начальной ячейки, и затем данные по порядку. Счетчик при этом сам индексируется.При достижении счетчиком верхней границы, он сбрасывается в ноль. Т.е. он бегает по кругу.
Чтобы прочитать данные с определенной ячейки памяти RTC нужно сначала открыть I2C сессию на запись, послать адрес, закрыть сессию, и уже потом открывать сесию на чтение, и читать по порядку.
Официальное руководство по DS130764 x 8, Serial, I2C Real-Time Clock
Используя программу из этого поста в качестве шаблона, я добавил к ней поддержку TWI модуля, и сделал так, что бы она распечатывала через UART содержимое всех 64 байт пямяти RTC DS1307
/* for ATmega8/168/328 site: http://countzero.weebly.com avr-gcc -mmcu=atmega8 -Wall -Os -o rtc.elf rtc.c avr-objcopy -O ihex rtc.elf rtc.hex avrdude -patmega8 -carduino -P/dev/ttyUSB0 -b19200 -D -Uflash:w:./rtc.hex:i */ #define F_CPU 16000000UL #define LEN 32 #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <util/twi.h> #include <stdio.h> //General Master staus codes #define START 0x08 //START has been transmitted #define REP_START 0x10 #define MT_DATA_ACK 0x28 //Master Transmitter staus codes #define MT_ADR_ACK 0x18 //SLA+W has been tramsmitted and ACK received #define MT_ADR_NACK 0x20 //SLA+W has been tramsmitted and NACK received #define MT_DATA_ACK 0x28 //Data byte has been tramsmitted and ACK received #define MT_DATA_NACK 0x30 //Data byte has been tramsmitted and NACK received #define MT_ARB_LOST 0x38 //Arbitration lost in SLA+W or data bytes #define WRITE 0x00 #define READ 0x01 #define READ_END 0x01 #define READ_NOEND 0x00 #define RTC 0xD0 char buffer[LEN]; register unsigned char IT asm("r16"); volatile unsigned char done; volatile unsigned char IDX; static inline void clearStr(char* str) { for(IT=0;IT<LEN;IT++) str[IT]=0; } static uint8_t uart_putchar(char c, FILE *stream) { if (c == '\n') uart_putchar('\r', stream); loop_until_bit_is_set(UCSRA, UDRE); UDR = c; return 0; } ISR(USART_RXC_vect) { char bf= UDR; buffer[IDX]=bf; IDX++; if (bf == ':' || IDX >= LEN) { IDX=0; done=1; } } static void blink13(uint8_t count) { PORTB |= (1<<PB5); count =(count <<1);count--; //count=(count*2)-1; for (IT=0;IT<count;IT++) { _delay_ms(500); PORTB ^= (1<<PB5); }; }; uint8_t send_i2c(uint8_t value) { TWDR = value; TWCR = (1<<TWINT) | (1<<TWEN); // wail until transmission completed and ACK/NACK has been received while(!(TWCR & (1<<TWINT))) {}; // check value of TWI Status Register. Mask prescaler bits. return ((TWSR & 0xF8) != MT_ADR_ACK) ? 1 : 0; } static uint8_t start_i2c(uint8_t d_adr) { TWCR=(1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // START while (!(TWCR & (1<<TWINT))) {}; uint8_t twst; twst = (TWSR & 0xF8); // check value of TWI Status Register. Mask prescaler bits. if ((twst != START) && (twst != REP_START)) return 1; uint8_t ret; ret=send_i2c(d_adr); return ret; }; static inline void stop_i2c() { TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO); } static uint8_t read_i2c(uint8_t END) { if (END) TWCR = (1<<TWINT)|(1<<TWEN); else TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); while(!(TWCR & (1<<TWINT))); return TWDR; // return data } /* static uint8_t check_i2c_adr(uint8_t adr) { uint8_t ret; ret=start_i2c(adr|WRITE); stop_i2c(); return ret; } */ static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); int main(void) { // USART init UBRRL=103; UCSRB=(1<<TXEN)|(1<<RXEN)|(1<<RXCIE); UCSRC=(1<<URSEL)|(3<<UCSZ0); TWBR = (F_CPU / 100000UL - 16)/2; // TWI bitrate DDRB |= (1<<PB5); // pinMode(13,OUTPUT); blink13(3); //ready indication IDX=0; done=0; sei(); stdout = &mystdout; printf("Set address rtc1307: 0x00\n"); if (!start_i2c(RTC)) { uint8_t ret; ret=send_i2c(0x00); stop_i2c(); if (!ret) printf("Set Address success!\n"); uint8_t k, data; k=0; start_i2c(RTC|READ); for(k=0;k<64;k++) { data=read_i2c(READ_NOEND); printf("address: %d value: %d\n", k,data); } stop_i2c(); } else { stop_i2c(); printf("Set Address failure!\n"); } for (;;){}; return 0; }
результат работы:
последние четыре ячейки не влезли в экран)