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

[Atmega32][C] Zewnętrzny przetwornika ADC na SPI.

kysieq 31 Lip 2008 14:16 4613 1
  • #1 5395238
    kysieq
    Poziom 10  
    Witam Wszystkich. Mam problem z obsługą zewnętrznego przetwornika ADC. W swojej pracy chcę zastosować 12bit przetwornik MAX1270 (link do dokumentacji Link), którego wynik przetwarzania miałby się wyświetlać poprzez RS232 na komputerze (na razie stosuje do tego celu terminal - "Terminal by Bra@y++").
    Schemat układu wygląda jak ten zamieszczony w dokumentacji MAX1270, zmodyfikowany jedynie o połączenie wyprowadzenia SSTRB z INT0, co zaznaczyłem na rysunku poniżej).
    [Atmega32][C] Zewnętrzny przetwornika ADC na SPI.

    Udało mi się zrobić komunikację poprzez RS232 jak i transmisję przez SPI. Problem polega na tym, że wyskakują błędne wartości, np. jeśli ustawię przetwornik do pracy w zakresie od 0V do 10V, to zwiększając napięcie od zera, zwiększa się wskazywana przez przetwornik wartość. Dochodząc do napięcia około 4,2V przetwornik pokazuje wartość dziesiętną około 2040, po czym jak mu się zwiększy napięcie to zaczyna wyświetlać wartości od zera ale od teraz zwiększając napięcie zwiększa poprawnie wskazania do maksymalnej wartości czyli 4094 dla 10V (co dla 12bitowego przetwornika zdaje się byś prawidłowe). Nie wiem dlaczego on mi się prawie w połowie zakresu napięcia zeruje, wg. rysunku zamieszczonego poniżej powinien zaczynać od zera i dochodzić do wartości 4094, a nie od zera gdzieś do 2040 i potem od 0 do 4094. Z tego co zauważyłem to zawsze pierwszy pomiar jest prawidłowy - zaraz po włączeniu układu czy po resecie, a potem zaczyna pokazywać takie głupoty (np. jeśli na jego wejście jest podane około 4,3V to wartość jaką pokazuje oscyluje w granicach 200, ale po wykonaniu resetu czy też wyłączeniu i ponownym włączeniu układu pierwszy przeprowadzony pomiar jest poprawny i wynosi około 1700, a potem znowu pokazuje mi wartości w okolicy 200).
    Na wejście przetwornika podaję stałe napięcie +10V, które reguluje potencjometrem. w przypadku przetwornika korzystam z wewnętrznego napięcia odniesienia oraz z wewnętrznego zegara.
    Dodam że od niedawna zajmuje się samymi uC jak i programowaniem w C (konkretnie w WinaAVR 2071221).

    Format danych wychodzących z przetwornika:
    [Atmega32][C] Zewnętrzny przetwornika ADC na SPI.

    Przebieg konwersji:
    [Atmega32][C] Zewnętrzny przetwornika ADC na SPI.

    Poniżej zamieszczam kod programu:
    -UART
    
    #define    CPU_Hz     12000000               // częstotliwość zegara (kwarcu) w Hz 
    #define    BAUD  	 14400               	// prędkość transmisji dla RS232 
    #define    MYUBRR	 CPU_Hz/16/BAUD-1
    
    void USART_init(unsigned int ubrr)       // INICJALIZACJA
    { 
       UBRRH = (unsigned char) (ubrr>>8);   // ustawienie prędkości 
       UBRRL = (unsigned char) ubrr; 
       UCSRB = (1<<RXEN)|(1<<TXEN);         // transmisja dwukierunkowa, odbieranie/nadawanie 
       UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);      // ustawienie 8 bitów danych i 1 bit stopu 
       //UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0)|(1<<UPM1); // ustawienie 8 bitów danych ,1 bit stopu i bit parzystości
    } 
    
    
    void USART_out(char data)    // WYSYŁANIE ZNAKU
    { 
       while (!(UCSRA & (1<<UDRE)));		//*Wait for empty transmit buffer*/
       UDR = data;							//*Put data into buffer, sends the data*/
    } 
    
    
    unsigned char USART_in(void)    // ODBIÓR ZNAKU 
    { 
       while (!(UCSRA & (1<<RXC))); 	//Wait for data to be received
       return UDR; 						//Get and return received data from buffer
    } 
    
    
    void usart_tablica(char *tablica) 	//WYSYŁANIE TABLICY
    {    
    	unsigned char i=0;
    	while(tablica[i]) 
    		{
            USART_out(tablica[i]);
    		i++;
            }
    } 
    
    


    -SPI
    
    #define	SPI_DDR		DDRB
    #define	SPI_PORT	PORTB
    #define	SPI_PIN		PINB
    
    #define	SCK		7
    #define	MISO		6
    #define    MOSI		5
    
    #define AC_SSTRB	2
    
    
    
    #define AC			3
    #define AC_OFF		SPI_PORT |= _BV(AC)
    #define AC_ON		SPI_PORT &=~_BV(AC)
    
    
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //FUNKCJE BITÓW REJESTRU SPCR//
    //
    //SPIE	(1<<7)-włączenie obsługi przerwania dla SPI, na zakończonie transmisj, albo utraty panowania nad magistralą 
    //SPE	(1<<6)-właczenie modułu SPI
    //DORD	(1<<5)-bitowa kolejność transmisji; jesli 1 to pierwszym przesyłanym bitem jest LSB z rejestru SPDR, jeśli 0 to wysylany jest najpierw MSB
    //MSTR	(1<<4)-wybór trybu pracy; 0 - urzadzenie pracuje jako slave, 1 jako master
    //CPOL	(1<<3)-polaryzacja sygnału zegarowego
    //CPHA	(1<<2)-faza sygnału zegarowego
    //SPR1	(1<<1)-wybór częstotliwości taktowania transmisji
    //SPR0	(1<<0)-jak wyżej
    //
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    //
    // MAX 1270 CONTROL BYTE FORMAT
    //
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
    #define START_AC	7
    #define SEL2_AC		6
    #define SEL1_AC		5
    #define SEL0_AC		4
    #define RNG_AC		3
    #define BIP_AC		2
    #define PD1_AC		1
    #define PD0_AC		0
    
    
    void spi_master_init(void)
    {
    SPI_DDR |= (_BV(MOSI)|_BV(SCK)|_BV(AC));	//ustawienie wyprowadzeń jako wyjścia; MISO jest ustawiane automatycznie jako wejscie i jej znacznik byłby i tak ignorowany
    
    AC_OFF;												//ustawienie stanu wysokieg na wyprowadzeniach 
    SPCR = (1<<SPE)|(1<<MSTR);					//konfiguracja rejestru SPCR
    }
    
    void spi_send (char data)				
    {
    SPDR=data;
    while(!(SPSR & (1<<SPIF)));
    }
    
    char spi_read (void)
    {
    while(!(SPSR & (1<<SPIF)));
    return SPDR;
    }
    
    void spi_ac_run (void)
    {
    unsigned char control_byte;
    control_byte= (1<<START_AC)|(1<<RNG_AC)|(1<<PD1_AC);
    
    cli();
    AC_ON;
    //spi_send(0X8C);
    spi_send(control_byte);
    AC_OFF;
    sei();
    }
    
    


    MAIN
    
    #include <avr/io.h>
    #include <avr/interrupt.h>		 
    #include <string.h> 
    #include <stdlib.h>
    #include "uart.h"
    #include "spi.h"
    #include <util/delay.h>
    
    volatile int wynik=0;
    volatile char wynik_msb=0;
    volatile char wynik_lsb=0;
    char bufor0[7];
    char marker2;
    
    
    void ex_interrupt(void)
    {
    
    MCUCR |= _BV(ISC01)|_BV(ISC00);
    GICR |= _BV(INT0);
    
    }
    
    
    void transmisja (void)
    {
    AC_ON;
    
    spi_send(0x00);
    wynik_msb=spi_read();   //MSB
    
    spi_send(0X00);
    wynik_lsb=spi_read();    //LSB
    AC_OFF;
    
    marker2=1;
    }
    
    
    SIGNAL (SIG_INTERRUPT0)
    {
    transmisja();
    }
    
    
    //////////////////////// **** program główny **** //////////////////////////////////
    
    
    int main(void)                        
    { 
    
    //DDRx= 0 -wejscie ; 1 -wyjscie
    DDRD=0Xff; 		//ustawienie portu D jako wyjście  DIODY PD2-PD6
    DDRD &=~_BV(AC_SSTRB);
    PORTD=0xff;
    
    DDRB=0Xff; 		
    PORTB=0xff;
    
    
    USART_init(MYUBRR); 			//inicjalizacja USARTa
    
    
    ex_interrupt();
    spi_master_init();
    spi_ac_run();
    
    while(1)
    
    	{
    	if(marker2)
    	{
    		marker2=0;
    		
    
    		//wynik=wynik_msb<<4|wynik_lsb>>4;
    		wynik=wynik_msb;
    		wynik<<=4;
    		wynik|=(wynik_lsb>>4)&0x0f;
    
    
    		itoa(wynik,bufor0,10);
    		usart_tablica(bufor0);
    
    		USART_out(13);
    		USART_out(13);
    		
    		
    		
    	_delay_ms(900);
    	dioda3;
    	spi_ac_run();
    	}
    	
    	}
    		
    }


    Za pomoc z góry dziękuje.

    Poprawiłem tytuł:
    https://www.elektroda.pl/rtvforum/topic1015361.html
    [c_p]
  • #2 5395451
    BoskiDialer
    Poziom 34  
    1/ Zmień marker2 na volatile - inaczej kompilator może tak zoptymalizować kod, że pętla główna wykona się tylko raz (chociaż po opisie można wnioskować, że nie to jest problemem)
    2/ wynik_msb, wynik_lsb zmień na unsigned char, a wynik na unsigned int (patrząc po sposobie wyliczania "wynik" nie powinno to mieć znaczenia)
    3/ Przy odczycie z SPI nie musisz mieć pętli oczekiwania (oczekujesz przy wysyłaniu) - do odbioru i tak musisz coś wysłać. Najczęściej wysyłanie i odbiór łączy się w jedną funkcję, wtedy flaga SPIF jest kasowana przez odczyt z SPDR po wysłaniu bajtu (odebrany bajt nie musi być wykorzystywany). Tutaj też raczej nie będzie leżał błąd.
    4/ Zaciekawiła mnie nota ze strony 7: "Serial Data Output. Data is clocked out on the falling edge of SCLK. High impedance when CS is high." - można by z tego wnioskować, że pierwszy bit wyniku powinien pojawić się jako drugi bit, chociaż piszesz, że wartości dochodzą do 4094
    5/ Zmniejsz szybkość SPI - aktualnie masz na wyjściu 3MHz (f/4), w nocie jest podany zakres od 0.1 do 2MHz - tu może być problem.
    6/ Zastanawiam się nad trybem uśpienia przetwórnika, w razie niepowodzenia można wyłączyć tryb usypiania.
REKLAMA