Elektroda.pl
Elektroda.pl
X
CControls
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

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

tigiscus 08 Lut 2010 22:42 4151 4
  • #1 08 Lut 2010 22:42
    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:

    Code:
    #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

    0 4
  • CControls
  • Pomocny post
    #2 09 Lut 2010 01:41
    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

    Code:

    #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;
    }

    0
  • CControls
  • Pomocny post
    #3 09 Lut 2010 10:05
    czmi3l
    Poziom 13  

    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.

    0
  • Pomocny post
    #4 01 Mar 2010 23:46
    Radkoo
    Poziom 11  

    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

    0
  • #5 03 Mar 2010 21:12
    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.

    0