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

[ATmega16][gcc] DS18B20 i PCF8538P na przerwaniach

Skyttop 18 Maj 2009 12:23 1806 2
REKLAMA
  • #1 6544204
    Skyttop
    Poziom 11  
    Witam

    Chcę skonstruować urządzenie, które oprócz zegarka mierzyłoby jednocześnie temperaturę i obydwie wielkości wyświetlałoby na wyświetlaczu LCD. Gdy programy napisałem osobno, obydwa działały, lecz gdy połączyłem je w całość, to czas odczytuję bez problemu, natomiast nie wyświetla mi się temperatura. Podejrzewam, że problem jest w przerwaniach. Poniżej kod. Proszę o pomoc.
    
    #include <avr/io.h>                // dostęp do rejestrów 
    #include <util/delay.h> 
    #include <avr/interrupt.h>
    
    #define F_CPU 8000000UL 
    #include "HD44780.c"
    
    #define UART_BAUD 19200
    #define UART_CONST (F_CPU/(16ul*UART_BAUD)-1)
    #define LOOP_CYCLES 8 //Number of cycles that the loop takes
    #define us(num) (num/(LOOP_CYCLES*(1/(F_CPU/1000000.0))))
    
    /* Thermometer Connections (At your choice) */
    #define THERM_PORT PORTD
    #define THERM_DDR DDRD
    #define THERM_PIN PIND
    #define THERM_DQ 5
    /* Utils */
    #define THERM_INPUT_MODE() THERM_DDR&=~(1<<THERM_DQ)
    #define THERM_OUTPUT_MODE() THERM_DDR|=(1<<THERM_DQ)
    #define THERM_LOW() THERM_PORT&=~(1<<THERM_DQ)
    #define THERM_HIGH() THERM_PORT|=(1<<THERM_DQ)
    
    #define THERM_CMD_CONVERTTEMP 0x44
    #define THERM_CMD_RSCRATCHPAD 0xbe
    #define THERM_CMD_WSCRATCHPAD 0x4e
    #define THERM_CMD_CPYSCRATCHPAD 0x48
    #define THERM_CMD_RECEEPROM 0xb8
    #define THERM_CMD_RPWRSUPPLY 0xb4
    #define THERM_CMD_SEARCHROM 0xf0
    #define THERM_CMD_READROM 0x33
    #define THERM_CMD_MATCHROM 0x55
    #define THERM_CMD_SKIPROM 0xcc
    #define THERM_CMD_ALARMSEARCH 0xec
    #define THERM_DECIMAL_STEPS_12BIT 625 //.0625
    
    volatile unsigned char licznik_przycisku_1, licznik_przycisku_2;
    volatile uint16_t licznik_przerwan;
    uint8_t licznik_wcisniecia, znacznik_zbocza_1=0, znacznik_zbocza_2=0;
    volatile unsigned char godziny,minuty,sekundy,czy_wybrano_zmiane;
    volatile char znak;
    volatile uint16_t digit;
    volatile uint16_t decimal;
    volatile uint8_t temperature[2];
    
    volatile char buffer[11];
    
    inline __attribute__((gnu_inline)) void therm_delay(uint16_t delay)
    {
    	while(delay--) asm volatile("nop");
    }
    
    //=======================  TWI  ===========================================
    
    void TWI_start(void) 
    { 
    	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); 
    	while(!(TWCR&(1<<TWINT))); 
    } 
    
    void TWI_stop(void) 
    { 
    	TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); 
    	while((TWCR&(1<<TWSTO))); 
    } 
    
    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; 
    }
    
    //==========================================================================
    
    //======================= 1-WIRE ===========================================
    
    uint8_t therm_reset()
    {
    	uint8_t i;
    	//Pull line low and wait for 480uS
    	THERM_LOW();
    	THERM_OUTPUT_MODE();
    	therm_delay(us(480));
    	//Release line and wait for 60uS
    	THERM_INPUT_MODE();
    	therm_delay(us(60));
    	//Store line value and wait until the completion of 480uS period
    	i=(THERM_PIN & (1<<THERM_DQ));
    	therm_delay(us(420));
    	//Return the value read from the presence pulse (0=OK, 1=WRONG)
    	return i;
    }
    
    void therm_write_bit(uint8_t bit)
    {
    	cli();
    	//Pull line low for 1uS
    	THERM_LOW();
    	THERM_OUTPUT_MODE();
    	therm_delay(us(1));
    	//If we want to write 1, release the line (if not will keep low)
    	if(bit) THERM_INPUT_MODE();
    	//Wait for 60uS and release the line
    	therm_delay(us(60));
    	THERM_INPUT_MODE();
    	sei();
    }
    
    
    uint8_t therm_read_bit(void)
    {
        cli();
    	uint8_t bit=0;
    	//Pull line low for 1uS
    	THERM_LOW();
    	THERM_OUTPUT_MODE();
    	therm_delay(us(1));
    	//Release line and wait for 14uS
    	THERM_INPUT_MODE();
    	therm_delay(us(14));
    	//Read line value
    	if(THERM_PIN&(1<<THERM_DQ)) bit=1;
    	//Wait for 45uS to end and return read value
    	therm_delay(us(45));
    	sei();
    	return bit;
    }
    
    //===========================================================================
    
    uint8_t therm_read_byte(void)
    {
    	uint8_t i=8, n=0;;
    	while(i--)
    	{
    		//Shift one position right and store read value
    		n>>=1;
    		n|=(therm_read_bit()<<7);
    		}
    		return n;
    	}
    void therm_write_byte(uint8_t byte)
    {
    	uint8_t i=8;
    	while(i--)
    	{
    		//Write actual bit and shift one position right to make
    	//	the next bit ready
    		therm_write_bit(byte&1);
    		byte>>=1;
    	}
    }
    
    void therm_read_temperature()
    {
    	// Buffer length must be at least 12bytes long! ["+XXX.XXXX C"]
    	//Reset, skip ROM and start temperature conversion
    	therm_reset();
    	therm_write_byte(THERM_CMD_SKIPROM);
    	therm_write_byte(THERM_CMD_CONVERTTEMP);
    	//Wait until conversion is complete
    	while(!therm_read_bit());
    	//Reset, skip ROM and send command to read Scratchpad
    	therm_reset();
    	therm_write_byte(THERM_CMD_SKIPROM);
    	therm_write_byte(THERM_CMD_RSCRATCHPAD);
    	//Read Scratchpad (only 2 first bytes)
    	temperature[0]=therm_read_byte();
    	temperature[1]=therm_read_byte();
    	therm_reset();
    	//Store temperature integer digits and decimal digits
    	digit=temperature[0]>>4;
    	digit|=(temperature[1]&0x7)<<4;
    	//Store decimal digits
    	decimal=temperature[0]&0xf;
    	decimal*=THERM_DECIMAL_STEPS_12BIT;
    //	Format temperature into a string [+XXX.XXXX C]
    	sprintf(buffer, "%04d.%04u C", digit, decimal);
    }
    
    //======================== PCF8583P ================================== 
    void rtc_write(unsigned char adres,unsigned char dane) 
    { 
    	TWI_start(); 
    	TWI_write(0xA0); 
    	TWI_write(adres); 
    	TWI_write(dane); 
    	TWI_stop(); 
    	_delay_ms(15); 
    } 
    
    unsigned char rtc_read(unsigned char adres) 
    { 
    	char odczyt; 
    	TWI_start();
    	TWI_write(0xA0); 
    	TWI_write(adres);
    	TWI_start(); 
    	TWI_write(0xA1); 
    	odczyt=TWI_read(0); 
    	TWI_stop(); 
    	return odczyt; 
    } 
    
    unsigned char Bcd2dec(unsigned char bcd) 
    { 
        return 10*(bcd>>4)+bcd&0x0F; 
    }
    
    void lcd_rtc_read()
    {
    	char g,m,s; 
    	LCD_GoTo(4,0); 
    	g=rtc_read(0x04);
    	LCD_WriteData(Bcd2dec(g>>4)+0x30);
    	LCD_WriteData(Bcd2dec(g&0x0f)+0x30);
    	LCD_WriteText(":"); 
    
    	LCD_GoTo(7,0);
    	m=rtc_read(0x03); 
    	LCD_WriteData(Bcd2dec(m>>4)+0x30);
    	LCD_WriteData(Bcd2dec(m&0x0f)+0x30);
    	LCD_WriteText(":"); 
    
    	s=rtc_read(0x02); 
    	LCD_GoTo(10,0);
    	LCD_WriteData(Bcd2dec(s>>4)+0x30);
    	LCD_WriteData(Bcd2dec(s&0x0f)+0x30);
    }
    
    void lcd_rtc_write(char g, char m, char s)
    {
    	rtc_write(0x02,g);
    	rtc_write(0x03,m);
    	rtc_write(0x04,s);
    }
    
    //============================================================
    
    
    
    
    SIGNAL(SIG_OVERFLOW0) //procedura obsługi przerwania zewnetrznego INT0
    {
    	TCNT0 = 0x22;
    //--------------------------------------------------------pierwszy przycisk
    	if(bit_is_clear(PINC,2) && znacznik_zbocza_1==0)
    	{
    		licznik_przycisku_1++;
    		znacznik_zbocza_1=1;
    		LCD_Clear();
    	}
    	if(bit_is_set(PINC,2))
    	{
    	 	_delay_ms(100);							//opóźnienie w celu ustabilizowania się wyłączenia styku
    		znacznik_zbocza_1=0; 
    	}
    //-------------------------------------------------------------------------
    
    //----------------------------------------------------------drugi przycisk
    	if(bit_is_clear(PINC,5) && znacznik_zbocza_2==0)
    	{
    		licznik_przycisku_2=1;
    		znacznik_zbocza_2=1;			
    	}
    //-------------------------------------------------------------------------
    
    	if(licznik_przycisku_1==0)
    	{
    		lcd_rtc_read();
    	}	
    		
    	if ((licznik_przycisku_1==1) && (czy_wybrano_zmiane==0))
    	{
    		godziny = rtc_read(0x04);
    		minuty = rtc_read(0x03);
    		sekundy = rtc_read(0x02);
    		czy_wybrano_zmiane = 1;
    	}
    	if ((licznik_przycisku_1==1) && (czy_wybrano_zmiane==1))   //ustawienie godzin
    	{
    		LCD_GoTo(4,0);
    		if (licznik_przycisku_2==1)
    		{
    			godziny++;
    			if((godziny&0x0f)>9)
    			{
    				godziny+=0x06; //korekcja dziesietna liczby BCD
    			}
    			if (godziny > 0x23)
    				godziny = 0x00;
    		}
    		LCD_WriteData(Bcd2dec(godziny>>4)+0x30);
    		LCD_WriteData(Bcd2dec(godziny&0x0f)+0x30);
    		LCD_WriteText(":"); 
    	}
    	if ((licznik_przycisku_1==2) && (czy_wybrano_zmiane==1))   //ustawienie minut
    	{
    		LCD_GoTo(7,0);
    		if (licznik_przycisku_2==1)
    		{
    			minuty++;
    			if((minuty&0x0f)>9)
    			{
    				minuty+=0x06; 										//korekcja dziesietna liczby BCD
    			}
    			if(minuty>0x59) 										//korekcja przekroczenia wartosci 59
    				minuty&=0; 											//przeniesienia na godziny nie ma
    		}
    		LCD_WriteText(":"); 
    		LCD_WriteData(Bcd2dec(minuty>>4)+0x30);
    		LCD_WriteData(Bcd2dec(minuty&0x0f)+0x30);
    		LCD_WriteText(":"); 
    	}
    	if ((licznik_przycisku_1==3) && (czy_wybrano_zmiane==1))   		//ustawienie sekund
    	{
    		LCD_GoTo(10,0);
    		if (licznik_przycisku_2==1)
    		{
    			sekundy++;
    			if((sekundy&0x0f)>9)
    			{
    				sekundy+=0x06; 										//korekcja dziesietna liczby BCD
    			}
    			if(sekundy>0x59) 										//korekcja przekroczenia wartosci 59
    				sekundy&=0; 										//przeniesienia na godziny nie ma
    		}
    		LCD_WriteText(":");
    		LCD_WriteData(Bcd2dec(sekundy>>4)+0x30);
    		LCD_WriteData(Bcd2dec(sekundy&0x0f)+0x30);
    	}		
    //-----------------------------KASOWANIE PRZYCISKU 2----------------------------
    
    	if(licznik_przycisku_2==1)
    		licznik_przycisku_2=0;
    	if(bit_is_set(PINC,5))
    	{
    	 	_delay_ms(100);							//opóźnienie w celu sutabilizowania się wyłączenia styku
    		znacznik_zbocza_2=0; 
    	}
    //-------------------------------------------------------------------------------
    
    	if(licznik_przycisku_1==4)
    	{
    		rtc_write(0x02,sekundy);
    		rtc_write(0x03,minuty);
    		rtc_write(0x04,godziny);
    		licznik_przycisku_1 = 0;
    		czy_wybrano_zmiane=0;
    	}
    
    	LCD_GoTo(0,1);
    	LCD_WriteText(buffer);
    }
    
    
    int main(void)         			// program główny 
    {
    	TCNT0 = 0x22;
    	TCCR0 = 0x02;
    	OCR0 = 250;
    	TIMSK =  1 <<TOIE0;
    	DDRC = _BV(0)|_BV(1);
    	PORTC = 0x3C;
    	LCD_Initalize(); 
    	sei();
    	while(1) 
    	{ 	
    		therm_read_temperature();
    	} 
    }
    


    Dodano po 4 [minuty]:

    Zapomniałem dodać, że program musi być zrobiony na przerwaniach. Czas konwersji temperatury czujnika DS18B20 to 750ms i gdybym nie używał przerwań, to program nie wyświetlałby sekund w stałym przedziale czasu. Mając przerwania jestem pewien, że takiego problemu nie będzie. Pozdrawiam!
  • REKLAMA
  • #2 6544333
    piti___
    Poziom 23  
    Wyłączaj przerwania w czasie zapisu i odczytu bitów transmisji 1-wire i podczas resetu 1-wire.

    Przykład:

    cli();
    send_bit();
    sei();
  • #3 6544382
    Skyttop
    Poziom 11  
    Już tak robiłem - nie działa

    Dodano po 7 [minuty]:

    Jedyne, co udało mi się zrobić to wyłączyć przerwania dla przetworzenia przez program całej funkcji therm_read_temperature(). Wtedy to działa, ale właśnie występuje ten problem, o którym już pisałem - w czasie gdy przetwarza się funkcja odczytu temperatury, czas nie jest odświeżany i raz na kilka sekund jedna sekunda na wyświetlaczu trwa zbyt długo albo zbyt krótko.
REKLAMA