czwartek, 26 czerwca 2014

ATmega8 I2C + ekspander PCF8574P

     W poście pokażę jak obsłużyć sprzętową magistralę I2C na mikrokontrolerze ATmega8. Procesor będzie komunikował się z  ekspanderem PCF8574P. Wybrałem to urządzenie ponieważ akurat był pod ręką i uważam, że będzie najłatwiejszy w obsłudze.


       Całość połączyłem na płytce stykowej. Schemat myślę, że nie jest potrzebny. Należy połączyć piny SDA i SCL urządzeń. Ważne by obie linie podciągnąć do zasilania rezystorem np. 4k7. Aby były widoczne efekty naszej pracy do portów P0-P7 ekspandera należy podłączyć diody z dobranym rezystorem. Zwracam też uwagę, że diody zapalamy stanem niskim na pinie ekspandera. Wiąże się to z wydajnością prądową portów ekspandera. Dla stanu niskiego jest to 20mA, a dla stanu wysokiego 200uA. W moim przypadku  zapalam diody mimo wszystko stanem wysokim ponieważ użyłem wyświetlacza siedmio-segmentowego ze wspólną katodą . Do testów wystarcza ;)

Zaczynamy od ustawienia adresu ekspandera. W nocie katalogowej mamy taką informację:


        Bity A0, A1, A2 ustawia się sprzętowo. Ustawiłem zera, czyli 3 piny scalaka zwarłem do masy. Tak ustawiony adres ma wartość  0x40 wyrażony w hex.

     
       Kod do obsługi magistrali zapożyczyłem z biblioteki Petera fleury. Opiera on się na dokumentacji dostarczonej przez Atmel dla ATmega8. Poniżej jest pokazany algorytm działania magistrali TWI.
        Należy pamiętać, że PCF8574P pracuje z maksymalną częstotliwością 100kHz. Częstotliwość z jaką ma pracować nasz układ obliczamy ze wzoru poniżej.


       Poniżej pokazane są wartości preskalera jakie można ustawić.



Poniżej znajduje się kod programu do obsługi I2C.
Z czasem spróbuję go wkleić w bardziej przyjaznej formie ;)

/********************************************************************************/

#include <avr/io.h>
#include <compat/twi.h>
#include "i2c.h"

/*** Inicjalizacja magistarli I2C ***/
void i2c_init(void){

TWSR = 0; // wartosć preskalera 1
TWBR = 0x10; // ustawianie wartosci rejestru TWBR
}

/*** Start magistrali, ustawienie adresu i kierunku transmisji ***/
unsigned char i2c_start(unsigned char adres){

uint8_t twst;

// send START condition
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

// wait until transmission completed
while(!(TWCR & (1<<TWINT)));

// check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8;
if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;

// send device address
TWDR = adres;
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.
twst = TW_STATUS & 0xF8;
if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;

return 0;
}

/*** Wysyła bajt na magistrale I2C ***/
unsigned char i2c_write( unsigned char data ){

    uint8_t   twst;

// send data to the previously addressed device
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN);

// wait until transmission completed
while(!(TWCR & (1<<TWINT)));

// check value of TWI Status Register. Mask prescaler bits
twst = TW_STATUS & 0xF8;
if( twst != TW_MT_DATA_ACK) return 1;
return 0;
}
/*** Koniec transmisji I2C ***/
void i2c_stop(void){

    /* send stop condition */
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

// wait until stop condition is executed and bus released
while(TWCR & (1<<TWSTO));
}

/*************************************************************************
 Read one byte from the I2C device, request more data from device

 Return:  byte read from I2C device
*************************************************************************/
unsigned char i2c_readAck(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
while(!(TWCR & (1<<TWINT)));

    return TWDR;
}

/*************************************************************************
 Read one byte from the I2C device, read is followed by a stop condition

 Return:  byte read from I2C device
*************************************************************************/
unsigned char i2c_readNak(void)
{
TWCR = (1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));

    return TWDR;
}

/*******************************************************************************/

/*******************************************************************************/
#include <avr/io.h>
#include <util/delay.h>
#include "i2c.h"

#define pcf8574_adres 0x40       // adres ekspandera

int main(void){

uint8_t num, i = 0x00;
uint8_t lcd[]={          // tablica cyfr
     0x77,        // 0
0x14, // 1
0xb3, // 2
0xb6,        // 3
0xd4, // 4
0xe6,       // 5
0xe7,        // 6
0x34,       // 7
0xf7,         // 8
0xf6          // 9
};
i2c_init();

while(1){

i2c_start(pcf8574_adres);
for(i = 0; i<10; i++){            // wyświetla kolejne cyfry co 300ms
i2c_write(lcd[i]);
_delay_ms(300);
}
i2c_stop();
}
}

/********************************************************************************/

       Tak napisany i wgrany kod daje następujący efekt :)

     Należy pamiętać, żeby do konkretnych projektów z ekspanderem używać wyświetlacza ze wspólną anodą. Wtedy pamiętajmy też aby ograniczyć prąd wyjść poprzez dodanie rezystorów. 

Brak komentarzy :

Prześlij komentarz