Skocz do zawartości

Atmega8L + MMA7455L TWI nic nie odbiera


Mateusz

Pomocna odpowiedź

Witam.

Napisałem prosty program do odbioru danych z akcelerometru MMA7455L. W zasadzie chcę odebrać tylko 8 bits output value X adres: $06. Jednak na wyjściu funkcji read_czujnik() dostaje wartość 9C która zapewne nie jest przypadkowo wartością wpisywaną w TWI_write(0x9C); na chwilę przed odbiorem.

main.c

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>  
#include <stdio.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <compat/twi.h>
#include "hmc5883l.h"
#include "TWI_lib.h"
#include "UART_ATM8.h"

#define SetBit(x,y)	x |= (1<<y)
#define ClrBit(x,y) x &= ~(1<<y)
#define NegBit(x,y) x ^= (1<<y)

volatile unsigned char bufor_RXD=0;
volatile int receive=0;

ISR (USART_RXC_vect)
{
	bufor_RXD = UDR;
receive=1;
}

int main(void)
{
unsigned char HMC_X;
char buf[10];

USART_Init();
sei();
TWI_Init();

   while(1)
   {
	HMC_X = read_czujnik(0x06);
	while ( !( UCSRA & (1<<UDRE)) );
	UDR=HMC_X;
	_delay_ms(1000);
   }
}

TWI_lib.c

#include "TWI_lib.h"

void TWI_Init()
{
 // Czestotliwosc TWI(i2c) = 100kHz (max 100kHz) -> atmega8 taktowana wewnetrznie 8MHz (RC=8MHz)
 // ------------------------------------------------
 /* TWSR = TWS7 TWS6 TWS5 TWS4 TWS3  -   TWPS1 TWPS0 */
 TWSR =0b00000000;    // Preskaler = 1  ->> TWPS1=0 TWPS0=0
 // ------------------------------------------------------
 /* TWBR = TWBR7 TWBR6 TWBR5 TWBR4 TWBR3 TWBR2 TWBR1 TWBR0 */
 TWBR=32;
 /* Rejestr odpowiedzialny za wybór współczynnika podziału dla generatora.
    Generator ten odpowiada za czestotliwosc która jest dzielona przez
    sygnał zegarowy SCL w trybie pracy Master.
    ->> TWBR musi byc wieksze od 10 dla stabilnej pracy TWI(i2c)   
Czyli ((8MHz/100kHz)-16)/2=32  => TWBR=32 lub TWBR=0x20 lub TWBR=0b00100000 */ 
} 

void TWI()
{
TWCR=(1<<TWINT) | (1<<TWSTO) | (1<<TWEN); //Wysyłanie sekwencji stop
while(!(TWCR&(1<<TWSTO)));	//Oczekiwanie na ustawienie bitu stop
}

void TWI_write(char dane)
{
TWDR=dane;
TWCR=(1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
}

unsigned char TWI_read(unsigned char ack)
{
TWCR=(1<<TWINT) | (ack<<TWEA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
return TWDR;
}

unsigned char read_czujnik(unsigned char adres)
{
unsigned char odczyt;
TWI();
return odczyt;
}

Więc to co się dzieje ja widzę tak:

Wysyłam sygnał START potem wpisuje adres urządzenia 0x1C bo do pinu 4 podłączone jest GND i najbardziej znaczący bit jest 0 aby wpisać dane.

Następnie wpisujemy adres danej którą chcemy odczytać. Następnie znowu wysyłamy start i adres urządzenia 0x9C bo bit 7 jest teraz jedynką aby slave wysłał nam dane. Odczytujemy dane i zwracamy wartość. Ale czemu zwrócona wartość to 9C ?

Link do komentarza
Share on other sites

zamiast tego:

void TWI_stop()

{

    TWCR=(1<

    while(!(TWCR&(1<

}

spróbuj wpisać to:

void TWI_stop(void)

{

TWCR = (1<

while((TWCR & (1<

}

Ja tak mam i mi działa. Spróbuj

Link do komentarza
Share on other sites

Niestety dalej nic 🙂.

Ale jedno mnie dziwi. Schemat jest mniej więcej taki:

Ale dioda LED 1 cały czas sie świeci nie mruga ani nic takiego. Tak jakby w ogóle nic nie było wysyłane. Chyba, że dzieje się to tak szybko że tego nie widać ?

No i po drugie czemu wartość zwracana przez read jest taka sama jak adres urządzenia z którym się komunikuje?

Link do komentarza
Share on other sites

Hmm,
patrzyłem do noty katalogowej tego akcelerometru i być może dopatrzyłem się problemu 😉

tutaj: http://www.freescale.com/files/sensors/doc/data_sheet/MMA7455L.pdf na stronie 23 jest napisane że adres można tylko czytać z bitów 6:0

tak więc binarnie jest to: 11101 co w heksadecymalnym systemie wynosi: 0x1D

Więc wychodzi na to że adres układu to 0x1D, a nie tak jak miałeś 0x1C. Sprawdź, może zadziała 😉

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Jak dałem 0x1D i 0x9D to nic całkowicie nie odbierałem.

Musi być 9C bo <$1C=0001 1100> bit 0 is GND on pin 4. If the pin is programmed it cannot be left NC.

[ Dodano: 10-02-2012, 11:07 ]

Znalazłem jeden błąd ale niestety nic się nie poprawiło 🙁 z tym, że teraz dostaje na wyjściu 0x84. Nie doczytałem, że bit R/W ma być po adresie.

unsigned char read_czujnik(unsigned char adres)
{
char ITGWriteAddress = 0x1D << 1;
   char ITGReadAddress = (0x1D << 1) | 1<<0;
unsigned char odczyt;
TWI();
return odczyt;
}

Jak rozumiecie napis pod tabelka 1 na stronie 5 dokumentacji?

Link do komentarza
Share on other sites

Jak rozumiecie napis pod tabelka 1 na stronie 5 dokumentacji?

This address selection capability is not enabled at the default state.

If the user wants to use it, factory programming is required.

If activated (pin4 on the device is active).

Dosłownie czyli:

Jeżeli chcesz korzystać z IADDR0 musisz zamówić akcelerometr bezpośrednio u producenta.

Ale nie znam tego akcelerometru więc mogę się mylić ...

Link do komentarza
Share on other sites

Czyli chodzi po prostu o to, że oni muszą zmienić jakieś ustawienia żeby ta opcja była dostępna?

Czyli nie ważne co mam na tym pinie 4 to i tak adres urządzenia będzie 0x1D ?

Link do komentarza
Share on other sites

Czyli chodzi po prostu o to, że oni muszą zmienić jakieś ustawienia żeby ta opcja była dostępna?

Czyli nie ważne co mam na tym pinie 4 to i tak adres urządzenia będzie 0x1D ?

Ja tak rozumie ten tekst, ale jak podkreśliłem wyżej, nie znam tego akcelerometru.

Link do komentarza
Share on other sites

To jak nie ma nikt pomysłu co jest nie tak w moim kodzie ?

Bo jeżeli wszystko jest ok to może mechanicznie coś jest nie tak.

Link do komentarza
Share on other sites

E tam, nie jest tak źle, 0x84 to też całkiem ładny wynik 😐

Freescale rzeczywiście namieszało z tym adresem, bo zwykle podaje się go wprost, czyli parzysty dla zapisu, nieparzysty dla odczytu i te wartości wysyłane są w pierwszym bajcie po START. A tutaj jakaś inna konwencja, stąd te początkowe nieporozumienia i konieczność przesuwania.

Moim zdaniem - jeśli nie jesteś pewien komunikacji, powinieneś rozbudować diagnostykę. Teraz to nawet nie wiesz, czy w ogóle układ został zaadresowany. Twoje procedury zakładają, ze wszystko poszło OK i jadą dalej. Widzę, że radzisz sobie z obsługą I2C więc zmodyfikuj np. funkcję TWI - tak przewiduje protokół. Nie wystarczy "repeated start". Wtedy będziesz mógł z łatwością "przejechać" w pętli przez wszystkie adresy (parzyste) i zobaczyć pod jakimi coś Ci się odzywa. To pierwszy krok. Potem podobnie zmodyfikuj TWI_write, niech też oddaje wartość kodu błędu na podstawie ACK.

Druga sprawa to schemat. Jakie masz rezystory podciągające? Zbyt duże wartości (i długie linie np. do wyniesionego na zdalnym module akcelerometru) powodują, że naruszone są czasy narastania i protokół się sypie. Kilka kiloomów to max.

Link do komentarza
Share on other sites

No i sprawdzanie błędów działa 😃. Tzn są błędy.

TWI_lib.c

#include "TWI_lib.h"

#define SetBit(x,y)	x |= (1<<y)
#define ClrBit(x,y) x &= ~(1<<y)
#define NegBit(x,y) x ^= (1<<y)
int error=0;


void TWI_Init()
{
 // Czestotliwosc TWI(i2c) = 100kHz (max 100kHz) -> atmega8 taktowana wewnetrznie 8MHz (RC=8MHz)
 // ------------------------------------------------
 /* TWSR = TWS7 TWS6 TWS5 TWS4 TWS3  -   TWPS1 TWPS0 */
 TWSR =0b00000000;    // Preskaler = 1  ->> TWPS1=0 TWPS0=0
 // ------------------------------------------------------
 /* TWBR = TWBR7 TWBR6 TWBR5 TWBR4 TWBR3 TWBR2 TWBR1 TWBR0 */
 TWBR=32;
 /* Rejestr odpowiedzialny za wybór współczynnika podziału dla generatora.
    Generator ten odpowiada za czestotliwosc która jest dzielona przez
    sygnał zegarowy SCL w trybie pracy Master.
    ->> TWBR musi byc wieksze od 10 dla stabilnej pracy TWI(i2c)   
Czyli ((8MHz/100kHz)-16)/2=32  => TWBR=32 lub TWBR=0x20 lub TWBR=0b00100000 */ 
} 

void ERROR(unsigned char err)
{
	while ( !( UCSRA & (1<<UDRE)) );
	UDR=err;
}

void TWI()
{
TWCR=(1<<TWINT) | (1<<TWSTO) | (1<<TWEN); //Wysyłanie sekwencji stop
while((TWCR&(1<<TWSTO)));	//Oczekiwanie na ustawienie bitu stop
}

void TWI_write(char dane)
{
TWDR=dane;
TWCR=(1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));

if ((TWSR & 0xF8) != MMA_DATA_YES_ACK)
ERROR(MMA_DATA_ACK_NOT);
}

unsigned char TWI_read(unsigned char ack)
{
TWCR=(1<<TWINT) | (ack<<TWEA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
}

unsigned char read_czujnik(unsigned char adres)
{
char ITGWriteAddress = 0x1D << 1;
   char ITGReadAddress = (0x1D << 1) | 1<<0;
unsigned char odczyt;
TWI();
return odczyt;
}

TWI_lib.h

#ifndef TWI_LIB_H_
#define TWI_LIB_H_

#include <compat/twi.h>
#define MMA();
void TWI_write(char dane);
unsigned char TWI_read(unsigned char ack);
unsigned char read_czujnik(unsigned char adres);

#endif /* TWI_LIB_H_ */

Otrzymuje 30 08 30 84

30 to: Data byte has been transmitted; NOT ACK has been received

08 to : A START condition has been transmitted

Czyli hardware ? A może jakoś we wy trzeba zadeklarować ?

Link do komentarza
Share on other sites

Cieszymy się? Chyba nie.. ale zawsze to krok do przodu. Na razie wiemy tylko tyle, że nie przechodzi nawet twi_start. Trochę to dziwne bo ta operacja jest dość prosta. Czy START może się nie udać? Nigdy mi sie to nie zdarzyło ale.. może w sytuacji, gdy magistrala jest wciąż zajęta, tzn. zero przynajmniej na SCL? Koniecznie pokaż schemat, masz pull-up'y?

No i rozbuduj trochę obsługę błędów, przynajmniej o wypisywanie zawartości rejestru TWSR gdy coś pójdzie nie tak. 'E' to trochę maławo.. Dobrze byłoby zobaczyć status po zwałce. Rozumiem, że przestałeś dalej dopisywać diagnostykę, bo już pierwszy test się wywala, tak? Może spróbuj to tak wymyślić, by funkcje twi_xxx oddawały kody błędów - wtedy wywołujący jest w stanie podjąć jakąś akcję. Jeśli będą załatwiały błędy tylko wewnątrz siebie, to coś wyżej będzie "myślało", że sprawy mają się dobrze i może zabrnąć bardzo daleko z pozytywnym nastawieniem notorycznego optymisty.

Ups, zmieniłeś kod podczas mojego pisania. Hardware? Piny same się robią I2C po odblokowaniu tego interfejsu. Oporniki podciągające - to ważne.

EDIT: no i zakładasz z góry błędy się pojawiły - nieładnie. W funkcji write nie wystarczy sprawdzać, czy dostałeś MMA_DATA_YES_ACK bo może to być prawidłowy ACK po opearcji adresowania - jeśli to będzie pierwszy bajt po START. Po prostu przekazuj zawartość rejestru plus miejsce wystąpienia czyli np. dwa argumenty lub liczbę 16-bitową.

EDIT2: Nie dogadamy się, jeśli co chwila będziesz zmieniał swojego ostatniego posta.

Link do komentarza
Share on other sites

Podciągnięcie jest do 3.3V przez rezystory 4.7k om.

Zmieniłem program i po prostu zapisałem w TWI()

{

TWCR=(1<

while((TWCR&(1<

}

void TWI_write(char dane)

{

TWDR=dane;

TWCR=(1<

while(!(TWCR & (1<

//if ((TWSR & 0xF8) == MMA_DATA_ACK_NOT)

ERROR(TWSR & 0xF8);

}

unsigned char TWI_read(unsigned char ack)

{

TWCR=(1<

while(!(TWCR & (1<

}

unsigned char read_czujnik(unsigned char adres)

{

char ITGWriteAddress = 0x1D << 1;

char ITGReadAddress = (0x1D << 1) | 1<<0;

unsigned char odczyt;

TWI();

return odczyt;

}[/code]

A schemat jest tutaj:

Ale nie pełny bo mi się komp sformatował (tak sam!) 😃 i straciłem schemat. Jedyne co udało mi się odzyskać to rysunek płytki pcb do otworzenia w eagle ale to raczej nic nie da 😉

Link do komentarza
Share on other sites

Tak, wygląda nieźle. Spróbuj odczytać kilka różnych rejestrów i zweryfikuj zawartości - czy mają sens i są jakoś razem spójne. Jeśli tak, możesz zacząć gadać z układem na poważnie. Gratuluję.

EDIT: Sh*t, znowu edit. Nie, schemat był mi potrzebny do weryfikacji połączeń ale skoro działa, nie mam co wnikać. Czyli podsumowując: po zmianie adresów było OK tylko odczytywana wartość Ci się nie podobała?

Link do komentarza
Share on other sites

No i teraz jest problem 🙂 Bo za każdym razem dostaje wartość w hex 84 🙂

    while(1)
   {
	HMC_X = read_czujnik(0x06);
	while ( !( UCSRA & (1<<UDRE)) );
	UDR=HMC_X;
	_delay_ms(10000);
   }

Może źle wpisuje adres rejestru?

Definicja Funkcji read_czujnik

unsigned char read_czujnik(unsigned char adres)
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.