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

[C]ATmega 8 + DS18B20 - błąd odczytu temperatury

tigiscus 08 Lut 2010 22:42 4667 4
REKLAMA
  • #1 7665625
    tigiscus
    Poziom 2  
    Witam

    Niedawno postanowiłem zrobić termometr na Atmedze8, podpiąłem wyświetlacz LCD, zaprogramowałem (posiłkując się Link) i działa. Następnie zakupiłem DS18B20 i tu zaczęły się schody - ciągle pojawia mi się 127.9 na wyświetlaczu, po odpięciu czujnika pokazuje się -0.1 więc wnioskuję, ęe jest odczyt z czujnika. Czujnik mam podpięty przez opornik 4,7 kΩ (połączyłem tak jak w tym linku podłączenie - schemat drugi). Obecnie Atmega działa na wewnętrzym kwarcu 4Mhz. Czy mogłby ktoś zerknąć na poniższy kod i wskazać ewentualne błędy? Wiem, że podobny temat był już nie raz poruszany ale szukałem mn w tych tematach:

    Link1

    Link2

    Link3

    i niestety dalej nic :(

    Oto mój kod:

    #include <util/delay.h>
    #include <avr/io.h>
    #include "lcd.c"
    
    #define PIN_1WIRE 0
    #define PORT_1WIRE PINB
    
    #define OUT_1WIRE_LOW PORT_1WIRE&=~(1<<PIN_1WIRE);
    #define OUT_1WIRE_HIGH PORT_1WIRE|=1<<PIN_1WIRE;
    #define DIR_1WIRE_IN DDRB&=~(1<<PIN_1WIRE);
    #define DIR_1WIRE_OUT DDRB|=1<<PIN_1WIRE;
    
    #define F_CPU 4000000UL 
    
    void delayus(unsigned int __count)
    {
    	__asm__ volatile (
    		"1: sbiw %0,1" "\n\t"
    		"brne 1b"
    		: "=w" (__count)
    		: "0" (__count)
    	); 
    }
    
    unsigned char uc_1Wire_ResetPulse(void)
    {
    	unsigned char ucPresenceImpulse;
    	OUT_1WIRE_LOW;
    	DIR_1WIRE_OUT;
    	delayus(500);
    	DIR_1WIRE_IN;
    	delayus(30);
    	if(bit_is_clear(PORT_1WIRE,PIN_1WIRE))
    		ucPresenceImpulse = 1;
    	else
    		ucPresenceImpulse = 0;
    	
    	delayus(470);
    	
    	if(bit_is_set(PORT_1WIRE,PIN_1WIRE))
    		ucPresenceImpulse = 1;
    	else
    		ucPresenceImpulse = 0;
    		
    	return ucPresenceImpulse;
    }
    
    void v1Wire_SendBit(char cBit)
    {
    	DIR_1WIRE_OUT;
    	delayus(5);
    	
    	if(cBit == 1)
    		DIR_1WIRE_IN;
    	
    	delayus(80);
    	DIR_1WIRE_IN;
    }
    
    unsigned char uc1Wire_ReadBit(void)
    {
    	unsigned char ucBit;
    	
    	DIR_1WIRE_OUT;
    	delayus(2);
    	DIR_1WIRE_IN;
    	delayus(15);
    	
    	if(bit_is_set(PORT_1WIRE,PIN_1WIRE))
    		ucBit = 1;
    	else
    		ucBit = 0;
    	
    	return ucBit;
    }
    
    void v1Wire_SendByte(char ucByteValue)
    {
    	unsigned char ucCounter;
    	unsigned char ucValueToSend;
    	
    	for(ucCounter =0;ucCounter <8;ucCounter++)
    	{
    		ucValueToSend = ucByteValue>>ucCounter;
    		ucValueToSend &= 0x01;
    		v1Wire_SendBit(ucValueToSend);
    	}
    	delayus(100);
    }
    
    unsigned char uv1Wire_ReadByte(void)
    {
    	unsigned char ucCounter;
    	unsigned char ucReadByte = 0;
    	for(ucCounter=0;ucCounter<8;ucCounter++)
    	{
    		if(uc1Wire_ReadBit())
    			ucReadByte |= 0x01<<ucCounter;
    		delayus(15);
    	}
    	return ucReadByte;
    }
    
    int main(void)
    {
    
    DDRD=0xFF;
    unsigned char ucReset;
    char cTempH =0, cTempL =0;
    
    float fTemp = 0;
    lcd_init(LCD_DISP_ON);
    lcd_puts("Rozpoczecie \n programu");
    
    char w_temp[16] ="{0}";
    _delay_ms(1000);
    
    while(1)
    {
    	lcd_clrscr();
    	
    	lcd_puts("zaczynam odzyt\ntemperatury");
    	_delay_ms(500);
    	
    	ucReset = uc_1Wire_ResetPulse();
    	if(ucReset == 1)
    	{
    		v1Wire_SendByte(0xCC);
    		v1Wire_SendByte(0x44);
    		_delay_ms(750);
    		ucReset = uc_1Wire_ResetPulse();
    		v1Wire_SendByte(0xCC);
    		v1Wire_SendByte(0xBE);
    		cTempL = uv1Wire_ReadByte();
    		cTempH = uv1Wire_ReadByte();
    		ucReset = uc_1Wire_ResetPulse();
    		
    		fTemp = (float)(cTempL+(cTempH<<8))/16;
    		dtostrf(fTemp,1,1,w_temp);
    		lcd_clrscr();
    		lcd_puts(w_temp);
    		_delay_ms(2000);
    	}
    	else
    	{
    		lcd_clrscr();
    		lcd_puts("Brak odczytu");
    	}
    	
    	_delay_ms(1000);
    }
    }


    Portu D używam do połączenia z wyświetlaczem lcd, w pliku "lcd.c" mam obsługe wyświetlania i to działa bez problemu

    DS18B20 jest podpięty do portu B pin 0

    Siedzę z tym już parę dni i nic...


    inventco.eu - temat poprawiłem - regulamin p.7.2
  • REKLAMA
  • Pomocny post
    #2 7666234
    morson
    Poziom 14  
    Temat ten był już wałkowany n razy

    Zerknij do datasheet'a DS'a

    Wydaje mi się że czas po odebraniu bitu jest zbyt krótki, z tego co pamiętam to tam min 75 us powinno być

    Tu masz kawałek działającego kodu, przerób sobie pod swoje komendy
    
    #define ONEWIRE_PIN PB2
    #define ONEWIRE_PORT PINB
    #define ONEWIRE_DDR DDRB
    
    static uint8_t onewire_reset()
    {
     ONEWIRE_DDR |= (1<<ONEWIRE_PIN);
     _delay_us(480);
     ONEWIRE_DDR &= ~(1<<ONEWIRE_PIN);
     _delay_us(70);
     uint8_t ret = ~(ONEWIRE_PORT & (1<<ONEWIRE_PIN));
     _delay_us(410);
     return ret;
    }
    
    static void _onewire_write_bit(uint8_t value)
    {
     uint8_t sreg = SREG;
     cli();
     ONEWIRE_DDR |= (1<<ONEWIRE_PIN);
     _delay_us(1);
     if(value)
     {
      ONEWIRE_DDR &= ~(1<<ONEWIRE_PIN);
      _delay_us(59);
     }
     else
     {
      _delay_us(59);
      ONEWIRE_DDR &= ~(1<<ONEWIRE_PIN);
     }
     SREG = sreg;
     _delay_us(30);
    }
    
    static uint8_t _onewire_read_bit()
    {
     uint8_t sreg = SREG;
     cli();
     ONEWIRE_DDR |= (1<<ONEWIRE_PIN);
     _delay_us(1);
     ONEWIRE_DDR &= ~(1<<ONEWIRE_PIN);
     _delay_us(15);
     uint8_t ret = ONEWIRE_PORT & (1<<ONEWIRE_PIN);
     SREG = sreg;
     _delay_us(75);
     return ret;
    }
    
    static void onewire_write_byte(uint8_t value)
    {
     for(uint8_t i = 0; i < 8; i++)
     {
      _onewire_write_bit(value & 0x01);
      value = value >> 1;
     }
    }
    
    static uint8_t onewire_read_byte()
    {
     uint8_t ret = 0;
     for(uint8_t i = 0; i < 8; i++)
     {
      ret = ret >> 1;
      if(_onewire_read_bit()) ret |= 0x80;
     }
     return ret;
    }
    
  • REKLAMA
  • Pomocny post
    #3 7666723
    czmi3l
    Poziom 14  
    Kompilujesz w winavr?
    Jeżeli tak, to:
    1. Czy w makefilu zmieniłeś F_CPU na 4MHz?
    2. Czy nie masz ustawionego fusebita odpowiadającego za dzielenie system clocka przez 8?

    ad 1. Jeżeli masz wewnętrzny rezonator ustawiony na 4MHz, to w makefile zmień F_CPU = 8000000 na
    F_CPU = 4000000.
    ad 2. Odznacz fuse bit system clock devide 8 (jakoś tak się nazywa)


    Najprawdopodobniej masz coś skopane z czasami. Powyższe powinno Ci pomóc.
  • REKLAMA
  • Pomocny post
    #4 7771558
    Radkoo
    Poziom 12  
    1. Trzeba uważać na funkcje delayms i delayus, ponieważ mają one ograniczenia - tzn. nie można tam wstawić dowolnie duże liczby. Szczegóły w delay.h

    2. U mnie ciekawy problem się pojawił w czasie konwersji temperatury - ponieważ układ był zasilany pasożytniczo. Nieważne jak kombinowałem z pullupami - zawsze to samo. W końcu po wystawieniu "1" na nóżce procesora na czas konwersji układ zadziałał - wcześniej z pullupen teoretycznie też powinien. Nie wiem jednak jakie jest źródło tego fenomenu - może zbyt mały prąd??

    Pozdrawiam Radek
  • #5 7779853
    tigiscus
    Poziom 2  
    Przepraszam za odpowiedz tak pozno ale sesja troche czasu mi zajela. Naszczescie jestem juz po.

    Zmienilem F_CPU = 4000000 w makefile jednak dalej nie pomoglo.

    Zauwazylem, ze jesli po rozkazie konwersji temp ustawie czas _delay_us(185) (lub mniej) termometr wskazuje 85.0, powyzej tego opoznienia wskazuje ciagle 127.9..

    Moze ktos spotkal sie z podobnym problemem? co moze byc tego powodem?


    Juz mam rozwiazanie - wystarczylo zamienic oprnik 4,7 kΩ na 3kΩ i wszystko zaczelo dzialac. Dzieki za pomoc.
REKLAMA