I2C, dla każdego bitu trzeba od nowa adresować układ...

To forum jest dla wszystkich pasjonatów mikrokontrolerów AVR Atmela. Wymiana doświadczeń i pomoc dla początkujących w pisaniu programów zarówno w C, Asemblerze jak i BASCOM. Zapraszam znawców tematu, aby pomogli wszystkim początkującym!
ODPOWIEDZ
Awatar użytkownika
Kulpina
Użytkownik
Posty: 2037
Rejestracja: 28 lip 2005, 16:57
Lokalizacja: Sanok
Kontakt:

I2C, dla każdego bitu trzeba od nowa adresować układ...

Post autor: Kulpina » 20 sty 2010, 16:03

Napisałem taki programik:

Kod: Zaznacz cały

#include <avr\io.h>
#include <inttypes.h>
#include <avr\signal.h>
#include <avr\interrupt.h>
#include <avr\pgmspace.h>

#define I2CDir DDRC
#define I2COut PORTC
#define I2CIn  PINC
//
#define SDA 4
#define SCL 5
//
#define SET_SDA I2COut |= (1 << SDA)
#define CLR_SDA I2COut &= ~(1 << SDA)
//
#define SET_SCL I2COut |= (1 << SCL)
#define CLR_SCL I2COut &= ~(1 << SCL)
//
#define SDA_OUT I2CDir |= (1 << SDA)
#define SDA_IN  I2CDir &= ~(1 << SDA)
//
#define SCL_OUT I2CDir |= (1 << SCL)
#define SCL_IN  I2CDir &= ~(1 << SDA)
//
#define GET_SDA (I2CIn & (1 << SDA))
//
#define ACK 0
#define NOACK 1
//
void delay(void)
{
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
// funkcja generujaca sygnał start
void i2cstart(void)
{
SDA_OUT; //
SCL_OUT; // ustawienie linii SDA i SCL w tryb wyjściowy
SET_SDA; //
SET_SCL; // ustawienie na liniach SDA i SCL stanu wysokiego
delay(); // opóźnienie
CLR_SDA; 
delay();
CLR_SCL;
}
// funkcja generujaca sygnał stop
void i2cstop(void)
{
CLR_SDA;
delay();
SET_SCL;
delay();
SET_SDA;
delay();
}

// funkcja wysyłająca bajt na szynę I2C
void i2cwrite(unsigned char x)
{
unsigned char count = 9;
do
{
CLR_SCL;
if(x & 0x80) SET_SDA; else CLR_SDA;
x <<= 1;
delay();
SET_SCL;
delay();
}while(--count);
CLR_SCL;
}

// funkcja odczytujaca bajt z szyny I2C
unsigned char i2cread(unsigned char ack)
{
unsigned char count = 8, temp = 0;
SET_SDA; SDA_IN;
do
{
delay();
SET_SCL;
delay();
temp <<= 1;
if (GET_SDA) temp++;
CLR_SCL;
} while (--count);
if(ack) SET_SDA;
else CLR_SDA;
delay();
SET_SCL;
delay();
CLR_SCL;
return (temp);
}




//pamiętaj np. jeśli tablica ma 9 elementów jak ta niżej g_Wyswietlacz1[9], to pisze sie 9, ale elementy definiuje sie w jej wypadku 0...8

uint8_t g_Wyswietlacz1[9] = {0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xA8};
uint8_t g_Wyswietlacz2[9] = {0x40, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x63};
uint8_t g_Mig1nr[9] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
uint8_t g_Mig2nr[9] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
uint8_t g_temp1;

uint8_t g_sekundy;
uint8_t g_minuty;
uint8_t g_godziny;

uint8_t g_CyfryDEC[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

int main (void)
{
//wartosci poczatkowe:
/*PORTB - Wyswietlacz gorny(1) - czas
  PORTD - Wyswietlacz dolny(2) - data i temperatura*/
DDRB = 0xFF;	//Wszystkie jako wyjscia
DDRD = 0xFF;	//Wszystkie jako wyjscia
DDRC = 0xFF;	//Wszystkie jako wyjścia
PORTB = 0xFF;	//Wszystkie stan wysoki
PORTD = 0xFF;	//Wszystkie stan wysoki
PORTC = 0x7A;	//BIN1111111


TCCR0 = 1<<CS01|1<<CS00; //prescale=64
TIMSK |= 1<<TOIE0;  //zezwolenie na przerwanie od Timera0
sei(); //globalne zezwolenie na przerwania
TIMSK |= 1<<TOIE1;// odblokowanie przerwania od Timera 1
TCCR1B |= 0x02;

	for(;;) //pusta petla glowna
	{
	}


	return 0;
}





//_________________________________________
//PRZERWANIA OD TIMEROW

//Timer0 8-bit
SIGNAL(SIG_OVERFLOW0) //Przerwanie zglaszane co 320us
{
static uint8_t Ktoracyfra = 0; 	
//zmienna wskazuje na ktora siatke wyswietlacza podajemy (w danej chwili) napiecie
//PORTC.0 - CLK
//PORTC.1 - RST

TCNT0 = 215;
PORTC &= 0xFD; 	//PORTC.1 = 0, 0xFD=BIN11111101
	
	if(PORTC & 0x01) //if PORTC.0 = 1
		{
		PORTB = 0x00;	//PORTB = 00000000
		PORTD = 0x00;	//PORTD = 00000000
		PORTC &= 0xFE;	// PORTC.0 = 0
		}
		else 
		{
		PORTC |= 1; //PORTC.0 = 1
		++Ktoracyfra;
		}
	if(Ktoracyfra > 8)
		{
		Ktoracyfra = 0;
		PORTC |= 0x02; //PORTC.1 = 1
		}
	
	PORTB = g_Wyswietlacz1[Ktoracyfra] & g_Mig1nr[Ktoracyfra];
	PORTD = g_Wyswietlacz2[Ktoracyfra] & g_Mig2nr[Ktoracyfra];
	
}


//Timer1 16bit
SIGNAL(SIG_OVERFLOW1)
{

TCNT1 = 25536;

i2cstart();
i2cwrite(0x9E);
i2cwrite(0xAA);
i2cstart();
i2cwrite(0x9F);
g_temp1 = i2cread(NOACK);  //MSB temperatury, LSB mnie nie interesuje, ponieważ jest to tylko 1 bit odpowiadający za ułamkową część temperatury
i2cstop();

g_temp1 = g_temp1 & 0x7F;

uint8_t tempjednosci;
uint8_t tempdziesiatki;
for(tempjednosci = g_temp1; tempjednosci >= 10; tempjednosci = tempjednosci - 10);
tempdziesiatki = (g_temp1 - tempjednosci) / 10;

g_Wyswietlacz2[7] = g_CyfryDEC[tempjednosci];
g_Wyswietlacz2[6] = g_CyfryDEC[tempdziesiatki];

++g_Wyswietlacz1[5];

i2cstart();
i2cwrite(0x9E);
i2cwrite(0xC0);
i2cwrite(0x00);
i2cstart();
i2cwrite(0x9F);
g_sekundy = i2cread(ACK);
g_minuty = i2cread(ACK);
g_godziny = i2cread(NOACK);
i2cstop();

g_Wyswietlacz1[1] = g_sekundy;
g_Wyswietlacz1[2] = g_minuty;
g_Wyswietlacz1[3] = g_godziny;
}
Chciałem zwrócić uwagę szczególnie na to:

Kod: Zaznacz cały

g_sekundy = i2cread(ACK);
g_minuty = i2cread(ACK);
g_godziny = i2cread(NOACK);
sekundy są odczytywane poprawnie (stan wyświetlacza zmienia się co sekundę)
natomiast zamiast minut i godzin odczytywane jest 255 (BIN11111111)
Czy ktoś ma pomysł dlaczego?
Co ciekawsze w układzie z którego odczytuje wszystkie dane są poprawne, bo jeśli odczytam sekundy, potem ponownie zainicjuje transmisję i odczytam np. minuty, to nie dostaje już 11111111, tylko sensowną zmieniającą się w czasie wartość...

Dlaczego można odczytywać po jednym bajcie, a nie można kilku na raz?

ODPOWIEDZ