ATmega8 + аппаратный TWI модуль: "делаем распечатку памяти RTC DS1307"

разделы: I2C , RTC , AVR , дата: 27 сентября 2015г.

После UART, реализация TWI модуля на AVR кажется довольно корявой и без понимания I2C протокола будет не просто его освоить. Однако, если разобраться с "софтовой" эмуляцией протокола, то работа с TWI модулем уже не составит труда.

    Подробнейшие руководства по работе с TWI можно почитать здесь:
  1. chipenable.ru записи по тегу TWI
  2. AVR. Учебный Курс. Использование AVR TWI для работы с шиной IIC (i2c)

Не последне место в этом списке должно быть у официального аппнота: 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;
}

результат работы:

последние четыре ячейки не влезли в экран)