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ę:
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.
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 :)
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.
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.
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