logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[AVR][C][Atmega32]Komunikacja z AD5322 po SPI

zegdus 05 Lip 2011 12:23 3995 22
  • #1 9684296
    zegdus
    Poziom 10  
    Witam!
    Problem przedstawia się następująco : Atmega32 (zew. kwarc 20Mhz) wysyła dane do przetwornika C/A - AD5532 po SPI wg. poniższego kodu (pod PA0 podpięte /LDAC AD5322) :
    
    /*
     * AVR644P_LSR_AD5322_ADC.c
     *
     * Parts:
     * ATMEGA32
     * AD5322 + AD820
     * FT232RL
     * Created: 7/3/2011 5:13:47 PM
     */ 
    
    //AVR DEFINE
    #ifndef F_CPU
    #define F_CPU	20000000UL
    #endif
    
    //SPI DEFINE
    #define	DDR_SPI		DDRB
    #define PORT_SPI	PORTB
    
    #define DD_MOSI	DDB5
    #define DD_MISO DDB6
    #define DD_SCK	DDB7
    #define DD_SYNC	DDB4
    
    #define P_SYNC	PB4
    
    
    
    
    #include <avr/io.h>
    #include <util\delay.h>
    
    //----------------SPI Functions-----------------
    
    void SPI_MasterInit(void)
    {
    	DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SYNC);
    	SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0)|(1<<CPHA);
    	DDRA = 0xFF;
    }
    
    void SYNC_LO(void)
    {
    	PORT_SPI &= ~(_BV(P_SYNC));
    }
    
    void SYNC_HI(void)
    {
    	PORT_SPI |= _BV(P_SYNC);
    }
    
    void SPI_MasterTransmit(char cData)
    {
    	SPDR = cData;
    	while(!(SPSR & (1<<SPIF)))
    ;
    }
    //-------------------------Main-------------------------
    int main(void)
    {
    	void SPI_MasterInit();
        
    	while(1)
        {
    	PORTA = 0xFF;
    	SYNC_HI();
    	_delay_us(100);
    	SYNC_LO();
    	SPI_MasterTransmit(0x0F);
    	SPI_MasterTransmit(0xFF);
    	SYNC_HI();
    	PORTA = 0x00;
    	_delay_us(100);
    
        }
    }
    

    No i niestety na wyjściu przetwornika 0V. Domyślam się iż problem tkwi w 8 bitowym SPI od strony AVR. Jeśli tak czy jest opcja zmiany SPI w AVR z 8 na 16 bit?
  • #2 9684321
    tmf
    VIP Zasłużony dla elektroda
    Nie ma możliwości, ale nie ma to znaczenia. Wysyłasz kolejne 8-bitowe paczki i to powinno załatwić sprawę. Widzę tu jeden problem - pamiętaj, że aby ATMega działała w trybie master SPI, przed wejściem w ten tryb pin SS należy ustawić jako wyjście. W przeciwnym przypadku na skutek szumów może dojść do przełączenia się w tryb slave na skutek detekcji rzekomej kolizji SPI. Niezależnie od tego musisz sprawdzić ustawiony tryb SPI ATMegi i układu do którego transmitujesz dane.
  • #3 9684322
    drzasiek
    Specjalista CNC
    Po pierwsze to wpisz kod w znacznikach
     [code]KOD [/code] 
    żeby można było go jakoś przeczytać.

    Dodano po 3 [minuty]:

    A konkretnie sprawdź w DS przetwornika czy najpierw wysyłać MSB czy LSB, przy którym zboczu/poziomie następuje odczyt itd.

    Dodano po 13 [minuty]:

    I jeszcze jedno. Funkcja przyjmuje wartość 8 bitową
    void SPI_MasterTransmit(char cData)
    {
    SPDR = cData;
    while(!(SPSR & (1<<SPIF)));
    } 

    A w funkcji main wysyłasz np wartość 0xff.
    Z tym, że argument funkcji jest typu char więc nie możesz wysłać 0xff bo to to jest 255 a typ char przyjmuje wartości od -128 do 127. Używaj typu uint8_t albo unsigned char.
    A kod z twojego maina:
    
    SYNC_LO();
    SPI_MasterTransmit(0x0F);
    SPI_MasterTransmit(0xFF);
    SYNC_HI(); 
    

    Nie wysyła danej 16 bitowej tak jakbyś chciał.
    Utwórz sobie dodatkową funkcję do wysyłania danej 16 bitowej.
    Przykład:
    
    void SPI_TRANSMIT_data16(uint16_t data) {
    	SYNC_LO(); 
    	SPI_MasterTransmit(data>>8);
    	SPI_MasterTransmit(data);
            SYNC_HI(); 
    	}
    

    Z tym, że tu musisz doczytać, czy do przetwornika wysyłane są najpierw MSB czy LSB.
  • #4 9684430
    tmf
    VIP Zasłużony dla elektroda
    drzasiek napisał:

    I jeszcze jedno. Funkcja przyjmuje wartość 8 bitową
    void SPI_MasterTransmit(char cData)
    {
    SPDR = cData;
    while(!(SPSR & (1<<SPIF)));
    } 

    A w funkcji main wysyłasz np wartość 0xff.
    Z tym, że argument funkcji jest typu char więc nie możesz wysłać 0xff bo to to jest 255 a typ char przyjmuje wartości od -128 do 127. Używaj typu uint8_t albo unsigned char.


    Nieprawda. Zazwyczaj char na AVR jest unsigned, tym bardziej, że większość aplikacji kompilowana jest z -funsigned-char. Więc użycie 0xff z takim argumentem funkcji jest poprawne.

    drzasiek napisał:

    A kod z twojego maina:
    
    SYNC_LO();
    SPI_MasterTransmit(0x0F);
    SPI_MasterTransmit(0xFF);
    SYNC_HI(); 
    

    Nie wysyła danej 16 bitowej tak jakbyś chciał.
    Utwórz sobie dodatkową funkcję do wysyłania danej 16 bitowej.
    Przykład:
    
    void SPI_TRANSMIT_data16(uint16_t data) {
    	SYNC_LO(); 
    	SPI_MasterTransmit(data>>8);
    	SPI_MasterTransmit(data);
            SYNC_HI(); 
    	}
    

    Z tym, że tu musisz doczytać, czy do przetwornika wysyłane są najpierw MSB czy LSB.


    A niby czemu nie wysyła? Przecież zaproponowany przez ciebie kod robi dokładnie to samo co kod autora.
  • #5 9684464
    zegdus
    Poziom 10  
    Po pierwsze dzięki za szybką odpowiedź. PIN SS (u mnie DD_SYNC) ustawiony jako wyjście :
    DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SYNC);
    Co do zbocza z noty AD5322 (http://www.analog.com/static/imported-files/data_sheets/AD5302_5312_5322.pdf) :
    "After SYNC goes low, serial data is shifted into the device’s input shift register on the falling edges of SCLK for 16 clock pulses." zmieniłem na mode 2 (CPOL = 1, CPHA = 0).
    Kolejność bitów do przesyłu na SPI: MSB-LSB (z noty) -
    BIT 15
    (MSB)
    BIT 0
    (LSB)
    A/B BUF PD1 PD0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0.
    Gdzie A/B wybór przetwornika
    BUF - bufor ref.
    PD1 i PD0 - tryb pracy
    D11 - D0 - dane.
    Niestety nadal na wyjściu 0V.
  • #6 9684485
    drzasiek
    Specjalista CNC
    tmf napisał:
    Nieprawda. Zazwyczaj char na AVR jest unsigned, tym bardziej, że większość aplikacji kompilowana jest z -funsigned-char. Więc użycie 0xff z takim argumentem funkcji jest poprawne.

    No właśnie, zazwyczaj. A zazwyczaj to nie zawsze. Kod ma być uniwersalny. Dopisanie przed char unsigned nic nie kosztuje a może potem zaoszczędzić czasu w szukaniu błędu. No chyba, że chce ujemne wartości wysyłać do przetwornika, w co wątpię.
    Cytat:

    A niby czemu nie wysyła? Przecież zaproponowany przez ciebie kod robi dokładnie to samo co kod autora.

    Nie pisałem że nie wysyła tylko że nie wysyła tak jakby chciał. I jest to niewygodne w dodatku.
  • #7 9684643
    zegdus
    Poziom 10  
    Ciągle nie jestem przekonany co do możliwości przesyłu więcej niż jednego bajtu danych do Slave. W nocie katalogowej ATMegi32 jest napisane :
    "After shifting one byte, the SPI clock generator stops, setting the end of
    Transmission Flag (SPIF)."

    Z kolei w nocie przetwornika AD5322 :
    "Any data and clock pulses after the 16th are ignored, and no further serial data
    transfers occur until SYNC is taken high and low again."
  • #9 9684654
    tmf
    VIP Zasłużony dla elektroda
    Nawet jeśli z jakiś magicznych powodów char nie byłby unsigned to po pierwsze zapis 0xff ma ciągle tą samą reprezentację bitową, więc nic to nie przeszkadza, po drugie kompilator zgłosiłby ostrzeżenie
    Co do wysyłania, to widzę, że masz zdolność czytania w myślach, skoro wiesz co chce autor :) IMHO niewygodne jest tworzenie funkcji bez potrzeby. Na etapie debugowania powinien najpierw uruchomić układ korzystając z prostych funkcji wysyłających bajty, a dopiero potem sobie komplikować.
  • #10 9684678
    drzasiek
    Specjalista CNC
    tmf napisał:
    Nawet jeśli z jakiś magicznych powodów char nie byłby unsigned to po pierwsze zapis 0xff ma ciągle tą samą reprezentację bitową, więc nic to nie przeszkadza, po drugie kompilator zgłosiłby ostrzeżenie
    Co do wysyłania, to widzę, że masz zdolność czytania w myślach, skoro wiesz co chce autor :) IMHO niewygodne jest tworzenie funkcji bez potrzeby. Na etapie debugowania powinien najpierw uruchomić układ korzystając z prostych funkcji wysyłających bajty, a dopiero potem sobie komplikować.

    To ja mam swoje "IMHO" :) i uważam, że taka funkcja upraszcza a nie komplikuje. Zwłaszcza, że przetwornik domaga się ciągu 16 bitów więc dla wygody lepiej wrzucić w argument daną 16 bitową niż ją rozbijać "ręcznie za każdym razem" na dwie 8 bitowe i wysyłać każde z osobna. Po co, skoro może to zrobić sprawdzona i prosta funkcja?
    Do autora:
    Wysłać możesz tylko 1 bajt czyli 8 bitów. Ale to jest tylko jedna paczka. Ale kto ci zabroni wysłać zaraz drugą paczkę 8 bitów? I w ten sposób (prostą funkcją jaką ci wyżej napisałem) wysyłasz 2 razy po 8 bitów czyli 16 bitów. Teraz tylko twoja głowa, żeby wysłać to tak, aby Slave zrozumiał to jako ciąg 16 bitów. A na to pytanie masz odpowiedź w cytacie który wpisałeś:
    "Any data and clock pulses after the 16th are ignored, and no further serial data
    transfers occur until SYNC is taken high and low again."
    Czyli przetwornik "czeka na 16 bitów" a potem resztę ignoruje dopóki mu nie dasz znaku, że będzie następne 16 bitów wysyłanych. A jak to zrobi? A no "until SYNC is taken high and low again".
    Więc z tym nie ma problemu.
  • #11 9684682
    zegdus
    Poziom 10  
    Co do schematu - postaram się w najbliższym czasie wymalować. Układ trywialny - Atmega32 - programator ISP, pomiędzy Vcc AVR'a a GND C = 100nF, AREF i AVCC podciągnięty do Vcc (filtr 2xC = 100nF do GND) - SPI do AD5322 - Vref=Vdd.
  • #12 9684713
    dondu
    Moderator na urlopie...
    zegdus napisał:
    Co do schematu - postaram się w najbliższym czasie wymalować. Układ trywialny - Atmega32 - programator ISP, pomiędzy Vcc AVR'a a GND C = 100nF, AREF i AVCC podciągnięty do Vcc (filtr 2xC = 100nF do GND) - SPI do AD5322 - Vref=Vdd.

    No to gdy go załączysz otrzymasz ode mnie 20pkt, tak na zachętę, bo masz mało :)
    Tylko nie zapomnij o pinie Reset.
    A kod wkleja za pomocą znacznika SYNTAX.
  • #14 9684903
    dondu
    Moderator na urlopie...
    1. A przypominałem o pinie RESET, zobacz: http://mikrokontrolery.blogspot.com/2011/04/minimalne-podlaczanie-pinow.html

    2. 25MHZ (?) - datasheet mówi max 16MHz.

    3. Gniazdo programatora chyba źle - sprawdź:

    [AVR][C][Atmega32]Komunikacja z AD5322 po SPI

    4. AD5322 nie sprawdzałem, reszta wygląda OK.

    5. Zacznij korzystać z darmowego programu Eagle:
    http://mikrokontrolery.blogspot.com/2011/04/kicad-projektowanie-schematow-i-pytek.html

    Punkty lecą do Ciebie.
  • #15 9684974
    zegdus
    Poziom 10  
    Po pierwsze dzięki za punkty :D.
    Ad. 1. Reset zaraz poprawię.
    Ad. 2. W rzeczywistości kwarc zew. ma wartość 20Mhz (próbowałem zarówno z wewnętrznym jak i z zewnętrznym)
    Ad. 3. Rzeczywiście na schemacie zrobiłem lustro X/Y :) - ale nie ma problemu z programowaniem AVR
    Ad. 5. Nie mogłem się nigdy przekonać :)
  • #17 9685079
    drzasiek
    Specjalista CNC
    zegdus napisał:

    Ad. 2. W rzeczywistości kwarc zew. ma wartość 20Mhz (próbowałem zarówno z wewnętrznym jak i z zewnętrznym)

    Nie ma wewnętrznego kwarcu w M32, jeśli już to oscylator wewnętrzny.
    Nie chce mi się zaglądać do DS przetwornika, ale ty zobacz. Wysyłasz 0x0FFF czyli wg twojego wcześniejszego wpisu
    Cytat:
    A/B BUF PD1 PD0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0.
    Gdzie A/B wybór przetwornika
    BUF - bufor ref.
    PD1 i PD0 - tryb pracy
    D11 - D0 - dane.

    Wybierasz pewnie przetwornik A?
    BUF dajesz 0
    Tryb pracy dajesz 0 0
    I wysyłasz wartość max dla przetwornika czyli jak się nie mylnę na szybko to jest to 4095?
    Teraz tak. Jeśli masz dobrze podłączone, jeśli jesteś pewien że tryb pracy powinien być 0 0 oraz BUF 0 i to powinna być max napięcie na wyjściu.

    No więc spróbujmy napisać to jeszcze raz:
    
    /*
     * AVR644P_LSR_AD5322_ADC.c
     *
     * Parts:
     * ATMEGA32
     * AD5322 + AD820
     * FT232RL
     * Created: 7/3/2011 5:13:47 PM
     */ 
    
    //AVR DEFINE
    #ifndef F_CPU
    #define F_CPU	20000000UL
    #endif
    
    //SPI DEFINE
    #define	DDR_SPI		DDRB
    #define      PORT_SPI	        PORTB
    
    #define MOSI	PB5
    #define MISO      PB6
    #define SCK	PB7
    #define SYNC	PB4
    
    
    #include <avr/io.h>
    #include <util\delay.h>
    
    //----------------SPI Functions-----------------
    
    void SPI_MasterInit(void)
    {
    
    
    DDR_SPI |= _BV(MOSI) | _BV(SYNC) | _BV(SCK);
    DDR_SPI &=~_BV(MISO);
    PORT_SPI &= ~_BV(SCK);
    PORT_SPI|= _BV(S65_MISO) |  _BV(S65_MOSI);
    
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0)|(1<<CPHA);
    SPSR = 1;
    DDRA = 0xFF;
    }
    
    void SYNC_LO(void)
    {
    	PORT_SPI &= ~_BV(SYNC);
    }
    
    void SYNC_HI(void)
    {
    	PORT_SPI |= _BV(SYNC);
    }
    
    void SPI_MasterTransmit_BYTE(uint8_t cData)
    {
      SPDR= cData;
      while (!(SPSR & _BV(SPIF)));
    }
    void SPI_MasterTransmit_DATA16(uint16_t cData)
    {
    SYNC_LO();
    SPI_MasterTransmit_BYTE(cData>>8);
    SPI_MasterTransmit_BYTE(cData);
    SYNC_HI();
    }
    
    //-------------------------Main-------------------------
    void main(void)
    {
    	void SPI_MasterInit();
        
    	while(1)
        {
    	PORTA = 0xFF;
    	SPI_MasterTransmit_DATA16(0x0fff);
    	PORTA = 0x00;
    	_delay_us(100);
    
        }
    }
    


    Dodano po 1 [minuty]:

    Ale jednak zejdź z tą częstotliwością i daj kwarc 16 MHz. Jak już będzie działać, wtedy pobawisz się w przetaktowanie.
  • #18 9685344
    zegdus
    Poziom 10  
    drzasiek - próbowałem twojego kodu - no i niestety 0V na wyjściu -
    PORT_SPI|= _BV(S65_MISO) |  _BV(S65_MOSI);  

    powinno być
    PORT_SPI|= _BV(MISO) |  _BV(MOSI);
    ?
    Zmiana dodatkowo DORD w SPRC (L/MSB) nie przyniosła żadnych zmian. Spróbuję doczytać jeszcze w DS AD5322 - może coś przeoczyłem.
  • #19 9685364
    drzasiek
    Specjalista CNC
    Tak tak, kopiowałem fragmenty ze sprawdzonej obsługi SPI Wyświetlacza i tam miałem tak podpisane, przeoczyłem.
    No to skoro nie działa dalej, to szukaj w DS.
  • #20 9685906
    zegdus
    Poziom 10  
    Po ponownej lekturze noty katalogowej AD5322/5302/5312 udało mi się w końcu uruchomić przetwornik. Cały diabeł jak to mawiają tkwi w szczegółach. Otóż bardzo istotnym okazał się sygnał /LDAC update'ujący rejestry. W omawianym przetworniku wystarczyło ustawić go w stan niski, później w stan wysoki. Wszystkim zaangażowanym w temat serdecznie dziękuje. Poniżej działający kod:
    
    /* 
     * AVR644P_LSR_AD5322_ADC.c 
     * 
     * Parts: 
     * ATMEGA32 
     * AD5322 + AD820 
     * FT232RL 
     * Created: 7/3/2011 5:13:47 PM 
     * Author: Luchowski Grzegorz
    
     */ 
    //-----	DAC ad5302-----ad5312-----ad5322----------------------------------
    //
    //        The first bit loaded is the MSB (Bit 15)
    //				data strobing on SCLK falling edge
    //
    //------------------------------------------------------------------------
    //							Control Bits
    //
    //Bit Name		Function								Power-On Default
    //
    //15 A/B 0: Data Written to DAC A     1: Data Written to DAC B	N/A
    //14 BUF 0: Reference Is Unbuffered   1: Reference Is Buffered  0
    //13 PD1 Mode Bit												0
    //12 PD0 Mode Bit												0
    //------------------------------------------------------------------------
    //DB15 (MSB)									  DB0 (LSB)
    //
    //	A/B BUF PD1 PD0 D7 D6 D5 D4 D3 D2 D1 D0 X X X X
    //				    <-----  DATA BITS ---->
    //		
    //			AD5302 Input Shift Register Contents
    //
    //------------------------------------------------------------------------
    //
    //	A/B BUF PD1 PD0 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 X X
    //  DB0 (LSB)                                       DB15 (MSB)
    //                  <-------  DATA BITS  ------->
    //			AD5312 Input Shift Register Contents
    //
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //
    //  A/B BUF PD1 PD0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
    //  DB0 (LSB)                                          DB15 (MSB)
    //                  <-------------DATA BITS------------->
    //
    //                AD5322 Input Shift Register Contents
    //========================================================================
    //
    //				PD1/PD0 Operating Modes
    //
    //	PD1 PD0		Operating Mode
    //	 0   0  Normal Operation
    //	 0   1  Power-Down (1 kOm Load to GND)
    //	 1   0  Power-Down (100 kOm Load to GND)
    //	 1   1  Power-Down (High Impedance Output)
    //
    //________________________________________________________________________
    
    
    //AVR DEFINE 
    #ifndef F_CPU 
    #define F_CPU   20000000UL 
    #endif 
    
    //SPI DEFINE 
    #define DDR_SPI		DDRB 
    #define PORT_SPI	PORTB 
    #define LDAC_DDR	DDRA
    #define	LDAC_PORT	PORTA		
    
    #define DD_MOSI		DDB5 
    #define DD_MISO 	DDB6 
    #define DD_SCK  	DDB7 
    #define DD_SYNC  	DDB4 
    
    #define DD_LDAC		DDA0
    
    #define	P_LDAC		PA0
    #define P_SYNC   	PB4 
    
    
    #include <avr/io.h> 
    #include <util\delay.h> 
    
    
    //----------------SPI Functions----------------- 
    
    void SPI_MasterInit(void) 
    { 
    	LDAC_DDR = (1<<DD_LDAC);
    	LDAC_PORT|= _BV(P_LDAC);
        DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SYNC); 
        SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0)|(1<<CPOL); 
       	LDAC_PORT&= ~(_BV(P_LDAC));
    	LDAC_PORT|= _BV(P_LDAC);
    } 
    
    void SYNC_LO(void) 
    { 
       PORT_SPI &= ~(_BV(P_SYNC)); 
    } 
    
    void SYNC_HI(void) 
    { 
       PORT_SPI |= _BV(P_SYNC); 
    } 
    
    void SPI_MasterTransmit(uint8_t cData) 
    { 
       SPDR = cData; 
       while(!(SPSR & (1<<SPIF))) 
    ; 
    } 
    void SPI_MasterTransmit_DATA16(uint16_t cData) 
    { 
    SYNC_LO(); 
    SPI_MasterTransmit(cData>>8); 
    SPI_MasterTransmit(cData); 
    SYNC_HI(); 
    } 
    
    //Toogle LDAC 
    static void updateDAC(void)
    {
        LDAC_PORT&= ~(_BV(P_LDAC)); //LO LDAC
    	LDAC_PORT|= _BV(P_LDAC);	//HI LDAC
    }
    
    //-------------------------Main------------------------- 
    int main(void) 
    { 
       SPI_MasterInit(); 
       uint16_t data;
       data=0x0FFF;
       while(1) 
        { 
    	  SPI_MasterTransmit_DATA16(data);
    	  updateDAC();  
        } 
    } 
    
    

    Pozdrawiam!
  • #21 9687238
    drzasiek
    Specjalista CNC
    A spróbuj w funkcji
    SPI_MasterTransmit
    dać argument tak jak na początku typu char czyli
    void SPI_MasterTransmit(char cData)
    .
    I daj znać o efektach, czy jeśli wyślesz np wartość 0x0aaa to masz takie samo napięcie na przetworniku jak wtedy, gdy argument jest typu uint8_t.
  • #22 9698312
    zegdus
    Poziom 10  
    Witam po krótkiej przerwie. Otóż zmiana argumentu funkcji SPI_MasterTransmit na char w żaden sposób nie wpłynęła na wartość napięcia wyjściowego na przetworniku.
    Pozdrawiam!
REKLAMA