-
1. Data: 2016-07-03 21:46:11
Temat: I2C/TWI - poddaję się...
Od: badworm <n...@p...pl>
Czołem!
Wracam do tematu, który tu poruszałem na początku kwietnia, a mianowicie
komunikacji pomiędzy AVRem (Mega 8, docelowo Mega 324) a wyświetlaczem
OLED z kontrolerem SSD1308. O ile komunikacja po I2C zrealizowanym
programowo działa prawidłowo, choć z niewiadomych przyczyn po
skompilowaniu programu nowszą wersją GCC prędkość transmisji spada
kilkunastokrotnie (częstotliwość zegara na SCL obniża się z 80kHz do
zaledwie 5kHz), o tyle z komunikacją za pośrednictwem sprzętowego I2C
jest jakiś grubszy problem. Kod wysyłający dane na sprzętowe I2C sam w
sobie jest dobry. Prosty program, wysyłający dane do układu PCF8574
(ekspander portu I2C) działa aż miło, w przebiegach praktycznie nie
widać różnicy czy to programowe czy sprzętowe I2C. Podmiana procedur
obsługi I2C w docelowym kodzie z obsługą OLED kończy się porażką. Sam
program jako całość żyje, bo diodka podpięta pod PB3 i taktowana
przerwaniem timera mruga prawidłowo. Jeśli wyświetlacz jest już
podłączony w momencie załączania zasilania, to efekt jest taki, że coś
idzie nie tak w programie, bo nie pojawia się stan wysoki na PB0, mający
za zadanie potrzymywać pracę stabilizatora zasilającego cały układ.
Muszę więc cały czas trzymać wciśnięty przycisk "power on", podczas gdy
normalnie wystarcza naciśnięcie go na chwilę a potem jego rolę przejmuje
właśnie PB0. Jeśli natomiast spróbuję podłączyć moduł z wyświetlaczem do
pracującego już procesora, to z tego co pokazuje oscyloskop, momentalnie
na pysk leci sygnał na SCL.
Jakieś pomysły co z tym fantem zrobić? :(
Przebieg I2C sprzętowego dla PCF8574:
http://transport.prohost.pl/kolejowy/pliki/tek00031.
png
Przebieg dla I2C programowego dla PCF8574:
http://transport.prohost.pl/kolejowy/pliki/tek00029.
png
Przebieg dla I2C programowego dla OLED:
http://transport.prohost.pl/kolejowy/pliki/tek00026.
png
Przebieg dla I2C sprzętowego dla OLED ale bez podłączonego wyświetlacza:
http://transport.prohost.pl/kolejowy/pliki/tek00019.
png
Najśmieszniejsze w tym wszystkim jest to, że to właśnie SSD1308
oficjalnie obsługuje I2C, a Atmel tę magistralę nazywa (ze względów
licencyjnych) TWI, więc prędzej możnaby się spodziewać odstępstw od
wzorcowego I2C ze strony procka niż kontrolera w wyświetlaczu...
Funkcje obsługujące I2C sprzętowe:
//TEST I2C SPRZĘTOWEGO
void twiinit(void)
{
//set SCL to 400kHz
TWSR = 0x00;
TWBR = 0x0C;
//enable TWI
TWCR = (1<<TWEN);
}
void twistart(void)
{
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);
}
void twistop(void)
{
TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}
void twiwrite(uint8_t u8data)
{
TWDR = u8data;
TWCR = (1<<TWINT)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);
}
Funkcje obsługi I2C programowego:
// Wyliczenie czasu opóźnienia połówkowego i ćwiartkowego (cykle)
#define I2C_nhalf (F_CPU/I2C_SPEED/2)
// Funkcja dłuższych opóźnień
#if I2C_nhalf < 3
// Nic
#elif I2C_nhalf < 8
static void i2c_xdelay(void)
{
NOP();
}
#else
#define I2C_delayloops (1+(I2C_nhalf-8)/3)
#if I2C_delayloops > 255
#error Przyspiesz - bo sie nie wyrabiam ;)
#endif
static void i2c_xdelay(void)
{
asm volatile( \
"delayus8_loop%=: \n\t"\
"dec %[ticks] \n\t"\
"brne delayus8_loop%= \n\t"\
: :[ticks]"r"(I2C_delayloops) );
}
#endif //I2C_nhalf >= 3
// Opóźnienia dla I2C
static inline void i2c_hdelay(void)
{
#if I2C_nhalf < 1
return; // To jest funkcja inline, jeśli składa się tylko z "return"
jest usuwana podczas optymalizacji
#elif I2C_nhalf < 2
NOP();
#elif I2C_nhalf < 3
asm volatile(
"rjmp exit%=\n\t"
"exit%=:\n\t"::);
#else
i2c_xdelay();
#endif
}
// Ustawienie i zerowanie wyjścia
static inline void i2c_sdaset(void)
{
DDR(I2C_PORT) &= ~(1<<I2C_SDA);
PORT(I2C_PORT) |= 1<<I2C_SDA;
}
static inline void i2c_sdaclear(void)
{
PORT(I2C_PORT) &= ~(1<<I2C_SDA);
DDR(I2C_PORT) |= 1<<I2C_SDA;
}
// Pobieranie danej z wyprowadzenia portu
// Zwraca: bajt będący odpowiednikiem fizycznego stanu portu, z
wyzerowanaymi wszystkimi bitami poza sda
static inline uint8_t i2c_sdaget(void)
{
return PIN(I2C_PORT) & (1<<I2C_SDA);
}
// Zerowanie i ustawianie zegara
static inline void i2c_sclset(void)
{
PORT(I2C_PORT) |= 1<<I2C_SCL;
}
static inline void i2c_sclclear(void)
{
PORT(I2C_PORT) &= ~(1<<I2C_SCL);
}
// Warunek startu
void i2c_start(void)
{
// Konieczne jeśli chcę użyć start bez stop
i2c_sdaset();
i2c_hdelay();
i2c_sclset();
i2c_hdelay();
// Normalna sekwencja startu
i2c_sdaclear();
i2c_hdelay();
i2c_sclclear();
//TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
//while (!(TWCR & (1<<TWINT)));
}
// Warunek stop
void i2c_stop(void)
{
i2c_sdaclear();
i2c_hdelay();
i2c_sclset();
i2c_hdelay();
i2c_sdaset();
i2c_hdelay();
//TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
//while ((TWCR & (1<<TWSTO)));
}
// Transmisja bajtu
// Zwraca: 0 jeśli było ack, wartość 1<<I2C_SDA jeśli nie było ACK
uint8_t i2c_send(uint8_t data)
{
uint8_t n;
for(n=8; n>0; --n)
{
if(data & 0x80)
i2c_sdaset();
else
i2c_sdaclear();
data <<= 1;
i2c_hdelay();
i2c_sclset();
i2c_hdelay();
i2c_sclclear();
}
// ack
i2c_sdaset();
i2c_hdelay();
i2c_sclset();
i2c_hdelay();
n = i2c_sdaget();
i2c_sclclear();
return n;
}
// Pobranie bajtu
uint8_t i2c_get(uint8_t ack)
{
uint8_t n, temp=0;
i2c_sdaset();
for(n=8; n>0; --n)
{
i2c_hdelay();
i2c_sclset();
i2c_hdelay();
temp<<=1;
if(i2c_sdaget())
temp++;
i2c_sclclear();
}
// ack
if(ack == I2C_ACK)
i2c_sdaclear();
else
i2c_sdaset();
i2c_hdelay();
i2c_sclset();
i2c_hdelay();
i2c_sclclear();
return temp;
}
--
Pozdrawiam Bad Worm badworm[maupa]post{kropek}pl
GG#2400455 ICQ#320399066
-
2. Data: 2016-07-03 22:08:07
Temat: Re: I2C/TWI - poddaję się...
Od: janusz_k <J...@o...pl>
W dniu 2016-07-03 o 21:46, badworm pisze:
> Czołem!
>
> Wracam do tematu, który tu poruszałem na początku kwietnia, a mianowicie
> komunikacji pomiędzy AVRem (Mega 8, docelowo Mega 324) a wyświetlaczem
> OLED z kontrolerem SSD1308. O ile komunikacja po I2C zrealizowanym
> programowo działa prawidłowo, choć z niewiadomych przyczyn po
> skompilowaniu programu nowszą wersją GCC prędkość transmisji spada
> kilkunastokrotnie (częstotliwość zegara na SCL obniża się z 80kHz do
> zaledwie 5kHz), o tyle z komunikacją za pośrednictwem sprzętowego I2C
> jest jakiś grubszy problem. Kod wysyłający dane na sprzętowe I2C sam w
> sobie jest dobry. Prosty program, wysyłający dane do układu PCF8574
> (ekspander portu I2C) działa aż miło, w przebiegach praktycznie nie
> widać różnicy czy to programowe czy sprzętowe I2C. Podmiana procedur
> obsługi I2C w docelowym kodzie z obsługą OLED kończy się porażką. Sam
> program jako całość żyje, bo diodka podpięta pod PB3 i taktowana
> przerwaniem timera mruga prawidłowo. Jeśli wyświetlacz jest już
> podłączony w momencie załączania zasilania, to efekt jest taki, że coś
> idzie nie tak w programie, bo nie pojawia się stan wysoki na PB0, mający
> za zadanie potrzymywać pracę stabilizatora zasilającego cały układ.
> Muszę więc cały czas trzymać wciśnięty przycisk "power on", podczas gdy
> normalnie wystarcza naciśnięcie go na chwilę a potem jego rolę przejmuje
> właśnie PB0. Jeśli natomiast spróbuję podłączyć moduł z wyświetlaczem do
> pracującego już procesora, to z tego co pokazuje oscyloskop, momentalnie
> na pysk leci sygnał na SCL.
>
> Jakieś pomysły co z tym fantem zrobić? :(
>
> Przebieg I2C sprzętowego dla PCF8574:
> http://transport.prohost.pl/kolejowy/pliki/tek00031.
png
>
> Przebieg dla I2C programowego dla PCF8574:
> http://transport.prohost.pl/kolejowy/pliki/tek00029.
png
Nie wiem czy zauważyłeś ale sprzętowo masz 200khz
a programowo 83khz, sam tek ci to na dole pokazuje
zmniejsz prędkość sprzętowego twi i powinno być dobrze.
--
Pozdr
Janusz_K