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

[Atmega32][C] Obsługa przetwornika ADC na przerwaniach

kysieq 26 Cze 2009 14:00 2676 3
  • #1 6705990
    kysieq
    Poziom 10  
    Witam wszystkich, mam problem z obsługą przetwornika MAX1301 przez Atmege 32. Mianowicie przetwornik ten posiada 3 tryby pracy. W dwóch sygnał zegarowy podawany jest z zewnątrz, dokładniej przez linię SCLK interfejsu SPI. W trzecim trybie sygnał zegarowy podawany jest na przetwornik tylko podczas przesyłania do niego bitu startowego i odczytu zmierzonych wartości, natomiast podczas samego próbkowania uruchamia się wewnętrzny zegar. I właśnie z tym trybem mam problem. Chciałbym żeby przetwornik pracował w trybie z wewnętrznym zegarem: Internal Clock-Mode, żebym mógł w trakcie próbkowania przejść z uC w stan uśpienia. Udało mi się uruchomić przetwornik w trybie zewnętrznym: External Clock-Mode i wyświetlić zmierzone wartości na LCD. Dlatego zakładam, że problem nie leży ani po stronie obsługi SPI czy LCD, tylko przerwań zewnętrznych. W trybie wewnętrznym po skończonej konwersji wyprowadzenie SSTRB przechodzi w stan wysoki i właśnie ten sygnał chce wykorzystać do inicjacji przerwań, podłączając je na wejście INT0. Z racji tego, że czas narastania/opadania SSTRB wynosi tylko 10ns uznałem że nie ma sensu uruchamiać przerwania na zbocze narastające czy też podłączać tego sygnału na INT2. Wykrywanie przerwania początkowo robiłem poprzez "Any logical change on INT0 generates an interrupt request". Następnie poprzez tranzystor BD237 zwierałem INT0 do masy (tj. SSTRB podłaczone z bazą, INT0 na colektor, a emiter z masą). I w tym przypadku wykrywanie przerwania ani na poziom niski ani na zmianę sygnału nic nie dawało. Nie mam pojęcia co robię nie tak, dlatego byłbym wdzięczy za pomoc.

    Zamieszczam link do dokumentacji MAX1301 Link
    Przebiegi dla trybu External Clock-mode conversion, który udało mi się uruchomić:
    [Atmega32][C] Obsługa przetwornika ADC na przerwaniach

    Przebiegi dla trybu Internal Clock-Mode conversion, który chcę uruchomić:
    [Atmega32][C] Obsługa przetwornika ADC na przerwaniach

    herddef.h
    #ifndef HARDDEF_H_INCLUDED
    #define KHARDDEF_H_INCLUDED
    
    
    ////////////////////==LCD.H==//////////////////
    
    
    #define lcd_ddr		DDRA
    #define lcd_port 	PORTA
    #define lcd_pin		PINA
    
    #define	lcd_rs	 0	//rozróżnienie dane - 1 / instrukcja - 0
    #define lcd_rw		 1	//kierunek transmisji odczyt - 1 / zapis - 0
    #define lcd_e		 2	//sygnał potwierdzający
    #define lcd_db4		3
    #define lcd_db5		4
    #define lcd_db6		5
    #define lcd_db7		6
    
    ////////////////////==SPI==//////////////////
    
    #define	SPI_DDR		DDRB
    #define	SPI_PORT		PORTB
    #define	SPI_PIN		PINB
    
    #define	SCK			7
    #define	MISO			6
    #define     MOSI		5
    
    #define AC			  4
    #define AC_SSTRB		2
    
    #define AC_OFF		SPI_PORT |= _BV(AC)
    #define AC_ON		SPI_PORT &=~_BV(AC)
    
    #endif
    


    spi.h
    
    #ifndef SPI_H_INCLUDED
    #define SPI_H_INCLUDED
    
    #define START_AC	7
    #define CH2_AC		6
    #define CH1_AC		5
    #define CH0_AC		4
    #define DIF_AC		3
    #define R2_AC		2
    #define R1_AC		1
    #define R0_AC		0
    
    #define M2_AC		6			
    #define M1_AC		5
    #define M0_AC		4
    
    void transmisja (void);
    void spi_master_init(void);
    void spi_send (unsigned char data);
    void spi_ac_run (void);
    char spi_read (void);
    void write_ca_napiecie (unsigned int napiecie,unsigned int gain);
    void write_ca_prad (unsigned int prad,unsigned int gain);
    void write_ca_pomiar (unsigned int gain_prad,unsigned int gain_napiecie);
    
    unsigned char conversion_byte;
    unsigned char analog_byte; 
    unsigned char mode_byte;
    
    volatile char marker1;
    #endif
    


    spi.c
    
    #include <avr/io.h>
    #include <string.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    #include <stdlib.h>
    #include "harddef.h"
    #include "spi.h"
    #include "uart.h"
    #include "diody.h"
    #include "lcd.h"
    
    unsigned char conversion_byte=(1<<START_AC);
    unsigned char analog_byte=(1<<START_AC)|(1<<R0_AC); 
    unsigned char mode_byte=(1<<START_AC)|(1<<M1_AC)|(1<<3);
    
    extern char zmiana;
    extern char z;
    
    extern volatile unsigned char wynik_msb;
    extern volatile unsigned char wynik_lsb;
    
    void spi_master_init(void)
    {
    SPI_DDR = (_BV(MOSI)|_BV(SCK)|_BV(AC));	
    
    
    AC_OFF;												
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);					//konfiguracja rejestru SPCR
    }
    
    void spi_send (unsigned char data)		
    {
    
    SPDR=data;
    while(!(SPSR & (1<<SPIF)));
    }
    
    char spi_read (void)
    {
    while(!(SPSR & (1<<SPIF)));
    return SPDR;
    }
    
    void spi_ac_run (void)
    {
    
    AC_ON;
    
    spi_send(conversion_byte);
    
    /*spi_send(0x00);   //ten fragment kodu, objęty w w komentarz umozliwia przeprowadzenie 
                                //konwersji w trybie zewnętrznym, przy jednoczesnym wyłączeniu przerwań
    spi_send(0x00);
    wynik_msb=spi_read();
    spi_send(0x00);
    wynik_lsb=spi_read();
    */
    AC_OFF;
    
    sei();     //włączenie przerwań	
    }
    
    


    przerwania.h
    
    #ifndef INTERRUPT_H_INCLUDED
    #define INTERRUPT_H_INCLUDED
    
    void ex_interrupt(void);
    
    #endif
    



    przerwania.c
    
    #include <avr/io.h>                // dostęp do rejestrów
    #include <avr/interrupt.h>
    #include <stdlib.h>  
    #include "spi.h"
    #include "uart.h"
    #include "diody.h"
    
    void ex_interrupt(void)
    {
    
    MCUCR=(1<<ISC00);  //tu daje albo MCUCR=(1<<ISC00); żeby wykrywał każda zmianę sygnały SSTRB, 
                             //lub nie daję nic jeśli chcę wykrywanie na poziom niski na INT0
    GICR = (1<<INT0);
    
    }
    


    main.c
    
    #include <avr/io.h>
    #include <avr/interrupt.h>		//Zawiera funkcje obsługi przerwań. 
    #include <string.h> 
    #include <stdlib.h>
    #include "uart.h"
    #include "lcd.h"
    #include "diody.h"
    #include "key.h"
    #include "spi.h"
    #include "przerwania.h"
    #include <util/delay.h>
    #include "harddef.h"
    
    
    //volatile uint8_t wynik_msb,wynik_lsb;
    volatile unsigned int wynik=0;
    volatile unsigned char wynik_msb=0;
    volatile unsigned char wynik_lsb=0;
    volatile double wynik_rzeczywisty;
    
    volatile char bufor0[10];
    
    char tablie[10]; 
    
    char n=0,p=0;
    char zmiana,zmiana_wartosci,zmiennik;
    char z;
    
    
    
    unsigned char bufor20[19];
    unsigned char bufor10[5];
    
    
    
    
    void impulsator (void)
    {
    cli();
    if(bit_is_clear(PIND,impulsator_wy2))
    ca_prad+=1;
    else 
    ca_prad-=1;
    _delay_ms(100);
    
    
    lcd_goto(0,3);
    utoa(ca_prad,bufor10,10);
    lcd_write_table_text(bufor10);
    zmiana_wartosci=1;
    
    }
    
    void transmisja (void)
    {
    cli();
    
    
    AC_ON;
    spi_send(0x00);
    wynik_msb=spi_read();
    spi_send(0x00);
    wynik_lsb=spi_read();
    
    AC_OFF;
    zmiana_wartosci++;
    utoa(zmiana_wartosci,bufor10,10);	
    lcd_goto(0,3);
    lcd_write_table_text(bufor10);
    zmiennik=1;
    }
    
    
    ISR(INT0_vect) 
    {
    
    transmisja();
    }
    
    ISR(INT1_vect) 
    {
    impulsator();
    }
    
    
    
    
    //////////////////////// **** program główny **** //////////////////////////////////
    
    
    int main(void)                        
    { 
    
    //DDRx= 0 -wejscie ; 1 -wyjscie
    
    DDRD &=~ (_BV(impulsator_wy1)|_BV(impulsator_wy2)|_BV(AC_SSTRB));	
    PORTD |= _BV(impulsator_wy1)|_BV(impulsator_wy2)|_BV(AC_SSTRB);
    
    
    
    
    lcd_init();
    lcd_clear();
    
    
    ex_interrupt();
    spi_master_init();
    
    
    	
    
    	_delay_us(1);
    
    	AC_ON;
    	spi_send(analog_byte);
    	AC_OFF;
    
    	_delay_us(1);
    
    	AC_ON;
    	spi_send(mode_byte);
    	AC_OFF;
    	
    
    	spi_ac_run();
    	
    	while(1)
    	{
    
    	
    	wynik=wynik_msb;
    	wynik<<=8;
    	wynik|=wynik_lsb;
    	
    	
    	
    	utoa(wynik,bufor20,10);	
    	lcd_goto(0,0);
    	lcd_write_table_text(bufor20);
    	
    	
    	wynik_rzeczywisty=(wynik-32768)*0.000091;
    	
    	dtostrf(wynik_rzeczywisty,3,6,bufor20);
    	lcd_goto(0,1);
    	lcd_write_table_text(bufor20);
    	lcd_write_data(0x20);
    	lcd_write_data(0x20);
    		
    	for(char i=0;i<10;i++)
    	{
    	_delay_ms(100);
    	}
    	
    
    	spi_ac_run();
    
    	}
    
    
    	
    }
    
    
  • #2 6707508
    janbernat
    Poziom 38  
    "uznałem że nie ma sensu uruchamiać przerwania na zbocze narastające"
    Niesłusznie.
    O ile pamiętam avr sprawdza sprzętowo czy stan się zmienił.
    Nie napisałeś nic o kwarcu.
    Dla 16 MHz jest to 62.5ns na okres.
    Czy masz oscyloskop?
    "tranzystor BD237 zwierałem INT0 do masy "
    Chyba BC.
    Jest tam coś?
    Jakiś sygnał na kolektorze?
    Na C się nie znam ale po tym:
    
    void transmisja (void)
    {
    cli();
    
    
    AC_ON;
    spi_send(0x00);
    wynik_msb=spi_read();
    spi_send(0x00);
    wynik_lsb=spi_read();
    
    AC_OFF;
    zmiana_wartosci++;
    utoa(zmiana_wartosci,bufor10,10);   
    lcd_goto(0,3);
    lcd_write_table_text(bufor10);
    zmiennik=1;
    } 


    Czy nie powinno być sei?[
    I to budzi wątpliwości:
    _delay_ms(100);
    Procesor wtedy wchodzi w pustą pętlę- nic nie robi przez 100ms.
    A może przez ten czas coś się dzieje.
    Czyżby nie tylko w Bascomie dało się napisać program którego działania nie można opanować?
  • #3 6778463
    kysieq
    Poziom 10  
    Udało mi się uruchomić ten przetwornik. Jak zauważył janbernat niesłusznie uznałem, że nie ma sensu podpinać wyjścia oznajmującego zakończenie konwersji przez przetwornik (SSTRB) do INT2. Pracuje on ładnie zarówno gdy między ADC, a uC pośredniczy BC547C lub BD237(tranzystor NPN firmy SGS-THOMSON, akurat takie coś miałem pod ręką). Nie sprawdzałem natomiast czy bezpośrednie połączenie ADC i uC również zadziała, jak tylko znajdę trochę czasu to postaram się to sprawdzić. Problem leżał natomiast w tym, że niespełnione zostały przeze mnie jakieś zależności czasowe (jakieś - bo do tej pory nie wiem jakie). Przy kwarcu 12MHz, przetwornik działał prawidłowo tylko dla częstotliwości zegara SPI 6; 1,5; 0,375; 0,09375 MHz (co daje odpowiednio SCK Frequency = fosc/2; fosc/8; fosc/32 i fosc/128). Przy zastosowaniu kwarcu 16MHz nie udało mi się dokonać pomiaru, tj. uzyskiwałem 200 próbek o dokładnie takiej samej wartości, która znacznie przekraczała rzeczywistą. Natomiast przy prawidłowym zestawieniu połączenia próbki różniły się o kilka set mikrowoltów, co jest spowodowane zakłóceniami. Według dokumentacji MAX1301 w trybie z wewnętrznym przetwarzaniem powinien pracować w zakresie częstotliwości od 10MHz do 12,048MHz (SCLK Period: MIN 100µs, MAX 83µs), tabela poniżej
    [Atmega32][C] Obsługa przetwornika ADC na przerwaniach
    co jest raczej błędne, wydaje mi się że powinno być SCLK Period: MIN 100µs, MAX 0,83µs co odpowiada zakresowi 10 - 1,2048 MHz. Uważam tak dlatego, że na stronie 19 w akapicie dotyczącym Digital Interface pada następujące stwierdzenie :

    "The MAX1300/MAX1301 feature a serial interface that is
    compatible with SPI/QSPI and MICROWIRE devices.
    DIN, DOUT, SCLK, CS, and SSTRB facilitate bidirectional
    communication between the MAX1300/MAX1301
    and the master at SCLK rates up to 10MHz (internal
    clock mode, mode 2), 3.67MHz (external clock mode,
    mode 0), or 4.39MHz (external acquisition mode, mode
    1)."

    Stąd też uważam, że problem nie leży w zbyt dużej częstotliwości kwarcu (docelowo chcę zastosować kwarc 16MHz, a częstotliwość SPI ustawić na fosc/2 co daje 8MHz) tylko właśnie nie spełnieniu jakieś zależności czasowej (zbyt krótka/długa przerwa pomiędzy jakimiś sygnałami). Tyle tylko, że przewertowałem tą dokumentację już z kilkanaście razy i nie wiem gdzie robię błąd.

    Sam proces konwersji wygląda w ten sposób że najpierw przesyłam trzy bajty konfiguracyjne ustalające m.in. zakres pomiaru i numer wyprowadzenia z którego będzie on dokonywany. następnie po przesłaniu ostatniego bajtu włączam obsługę przerwań (instrukcja sei(); ) , oraz przechodzę z uC w tryb uśpienia (instrukcja MCUCR|=1<<SE;). Po zakończonej konwersji przez ADC zgłasza on przerwanie na wejściu INT2. W obsłudze przerwania najpierw odczekuję 1ms (16 tyś. cykli zegara) na wybudzenie się uC z trybu Power-down, a następnie czyszczę bit Sleep Enable ( MCUCR&=~(1<<SE); ) i wyłączam przerwania ( cli(); )
  • #4 6778701
    janbernat
    Poziom 38  
    "and the master at SCLK rates up to 10MHz (internal
    clock mode, mode 2)"
    To chyba dotyczy szybkości magistrali SPI.
    "(SCLK Period: MIN 100µs, MAX 83µs)".
    Trzeba przyjąć bardziej pesymistyczną wersję.
    Czyli max. szybkość magistrali 10Mhz.
    Co by się zgadzało
    "Przy kwarcu 12MHz...dla częstotliwości zegara SPI 6"
    A dla 12 już nie.
    I chyba nie "Period: MIN 100µs, MAX 0,83µs"
    A Period: MIN 100µs, MAX 83µs
    Co daje 10-12Mhz.
    Ale widać że str. 19 jest bliższa prawdy.
    No i SPI jest szybka -ale na krótkich dystansach.
    Prowadzenie ścieżek, długość i pojemność mogą mocno ograniczyć częstotliwość.
REKLAMA