niedziela, 26 listopada 2017

[32] STM32F4 - DS18B20, wiele czujników, obsługa alarmu

Ten post chciałbym poświęcić na opisanie sposobu podłączenia wielu czujników na linii One Wire oraz obsługę alarmu od czujnika DS18B20.

[Źródło: http://www.st.com/en/evaluation-tools/stm32f4discovery.html]

Program:


Tutaj należy zmodyfikować kilka funkcji z poprzedniej biblioteki oraz dodać kilka nowych do biblioteki.

Funkcja uruchamiająca czujniki wygląda następująco:

  1. uint8_t ds18b20Init(uint8_t mode)
  2. {
  3.     int i = 0, j=0;
  4.     uint8_t deviceDat[8];
  5.     /* If skip rom then we adress all devices on one line
  6.      * with the same setting */
  7.     if(mode == SKIP_ROM)
  8.     {
  9.         /* Send Reset pulse then wait for Presence Pulse */
  10.         if(ds18b20ResetDevice() != 1)
  11.         {
  12.             return 0;
  13.         }
  14.         ds18b20WriteByte(COM_SKIP_ROM);
  15.         ds18b20WriteByte(COM_WRITE_SCRATCHPAD);
  16.         /* Set Th high temp alarm for 50 */
  17.         ds18b20WriteByte(0x32);
  18.         /* Set Tl low temp alarm for -10 */
  19.         ds18b20WriteByte(0x8A);
  20.         ds18b20WriteByte(RESOLUTION_12BIT);
  21.     }
  22.     else if(mode == NO_SKIP_ROM)
  23.     {
  24.         /* find devices, read ROM */
  25.         for(i=1;i<=DS18B20_CONNECTED_DEV;i++)
  26.         {
  27.             if(ds18b20SearchRom(deviceDat))
  28.             {
  29.                 ds18b20_data.Dev_Cnt++;
  30.                 memcpy(ds18b20_data.Dev_ID[ds18b20_data.Dev_Cnt-1],deviceDat,sizeof(deviceDat));
  31.             }
  32.             else
  33.             {
  34.                 break;
  35.             }
  36.         }
  37.         /* pass all settings to devices on line */
  38.         for(i=1;i<=ds18b20_data.Dev_Cnt;i++)
  39.         {
  40.             if(ds18b20ResetDevice() != 1)
  41.             {
  42.                 return 0;
  43.             }
  44.             /* Use for adres one of connected sensors */
  45.             ds18b20WriteByte(COM_MATCH_ROM);
  46.             for(j=0;j<=7;j++)
  47.             {
  48.                 ds18b20WriteByte(ds18b20_data.Dev_ID[i-1][j]);
  49.             }
  50.             ds18b20WriteByte(COM_WRITE_SCRATCHPAD);
  51.             /* Set Th high temp alarm for 50 */
  52.             ds18b20WriteByte(0x32);
  53.             /* Set Tl low temp alarm for -10 */
  54.             ds18b20WriteByte(0x8A);
  55.             ds18b20WriteByte(RESOLUTION_12BIT);
  56.         }
  57.     }
  58.     return 1;
  59. }

Tutaj wprowadzona została modyfikacja. Pierwsza część adresująca wszystkie urządzenia nie została zmieniona. Doszła natomiast druga część dotycząca ustawienia konkretnych danych dla każdego z czujników. Wyszukiwanie odbywa się za pomocą funkcji SEARCH ROM. Następnie do układu przesyłana jest komenda MATCH ROM. Jej zadaniem jest zaadresowanie konkretnego urządzenia. Na jej wywołanie ma odpowiedzieć układ, którego adres ROM zostanie wysłany. Każde inne podłączone układy podrzędne będą czekać na sygnał RESET PULSE.

Modyfikacji uległa także funkcja przesyłająca komendą READ SCRATCHPAD:

Tutaj tak jak poprzednio wpisujemy adres ROM układu który nas interesuje. Po czym przechodzimy do wpisania komendy READ SCRATCHPAD i odczytania umieszczonych w nim wartości:

  1. void ds18b20ReadScratchpad(uint8_t mode, uint8_t *data, uint8_t deviceNumber)
  2. {
  3.     uint8_t i;
  4.     ds18b20ResetDevice();
  5.     if(mode == SKIP_ROM)
  6.     {
  7.         ds18b20WriteByte(COM_SKIP_ROM);
  8.     }
  9.     else if(mode == NO_SKIP_ROM)
  10.     {
  11.         ds18b20WriteByte(COM_MATCH_ROM);
  12.        
  13.         for(i=0;i<=7;i++)
  14.         {
  15.             ds18b20WriteByte(ds18b20_data.Dev_ID[deviceNumber-1][i]);
  16.         }
  17.     }
  18.     ds18b20WriteByte(COM_READ_SCRATCHPAD);
  19.     for(i=0;i<8;i++)
  20.     {
  21.         data[i] = ds18b20ReadByte();
  22.     }
  23. }

W przypadku funkcji odpowiedzialnej za konwersję temperatury również należy przygotować wersję odczytującą adres ROM układu.

  1. void ds18b20DevConTemp(uint8_t mode, uint8_t deviceNumber)
  2. {
  3.     uint8_t i = 0;
  4.     ds18b20ResetDevice();
  5.     if(mode == SKIP_ROM)
  6.     {
  7.         ds18b20WriteByte(COM_SKIP_ROM);
  8.     }
  9.     else if(mode == NO_SKIP_ROM)
  10.     {
  11.         ds18b20WriteByte(COM_MATCH_ROM);
  12.        
  13.         for(i=0;i<=7;i++)
  14.         {
  15.             ds18b20WriteByte(ds18b20_data.Dev_ID[deviceNumber-1][i]);
  16.         }
  17.     }
  18.     ds18b20WriteByte(COM_CONVERT_T);
  19.     /*
  20.      * Delay to wait for convert temperature finish.
  21.      * With resolution of 12, time to end is give or take 750ms
  22.      * */
  23.     HAL_Delay(800);
  24. }

Ustawianie alarmu dla wybranego czujnika:

  1. uint8_t ds18b20SetAlarm(uint8_t Dev[DS18B20_CONNECTED_DEV][8], uint8_t sensor, uint8_t highAlarm, uint8_t lowAlarm)
  2. {
  3.     uint8_t j=0;
  4.     if(ds18b20ResetDevice() != 1)
  5.     {
  6.         return 0;
  7.     }
  8.     ds18b20WriteByte(COM_MATCH_ROM);
  9.     for(j=0;j<=7;j++)
  10.     {
  11.         ds18b20WriteByte(ds18b20_data.Dev_ID[sensor][j]);
  12.     }
  13.     ds18b20WriteByte(COM_WRITE_SCRATCHPAD);
  14.     /* Set Th high temp alarm */
  15.     ds18b20WriteByte(highAlarm);
  16.     /* Set Tl low temp alarm */
  17.     ds18b20WriteByte(lowAlarm);
  18.     ds18b20WriteByte(RESOLUTION_12BIT);
  19.     return 1;
  20. }

Funkcja powyżej pobiera urządzenie, które zostało wcześniej odczytane. Na linie przesyłana jest komenda MATCH ROM. Po niej zostanie przesłany adres konkretnego układu. Wobec tego tylko on przyjmie kolejne komendy jak i ustawienie poszczególnych alarmów.

Po ustaleniu wybranego czujnika na generowanie alarmu należy sprawdzić czy dany czujnik wywołał alarm. Można tego dokonać w podobny sposób jak wywoływanie komendy SEARCH ROM. Jedyną różnicą w tym przypadku jest to, że przy wywołaniu komendy ALARM SEARCH odpowie tylko czujnik, który wygenerował alarm.

  1. uint8_t ds18b20Search(uint8_t *Addr, uint8_t command)
  2. {
  3.       uint8_t id_bit_number = 1;
  4.       uint8_t last_zero = 0;
  5.       uint8_t rom_byte_number = 0;
  6.       uint8_t search_result = 0;
  7.       uint8_t id_bit = 0;
  8.       uint8_t cmp_id_bit = 0;
  9.       uint8_t rom_byte_mask = 1;
  10.       uint8_t search_direction = 0;
  11.       /* If last call was not the last one */
  12.       if (!ds18b20_data.LastDeviceFlag)
  13.       {
  14.           /* 1-Wire reset */
  15.           if(ds18b20ResetDevice() == 1)
  16.           {
  17.               ds18b20WriteByte(command);
  18.           }
  19.       }
  20.       /* loop for search */
  21.       do
  22.       {
  23.           /* read a bit */
  24.           id_bit = ds18b20ReadBit();
  25.           cmp_id_bit = ds18b20ReadBit();
  26.           /* check for no devices on one wire line */
  27.           if ((id_bit == 1) && (cmp_id_bit == 1))
  28.           {
  29.               break;
  30.           }
  31.           else
  32.           {
  33.               /* all coupled devices have 0 or 1 */
  34.               if (id_bit != cmp_id_bit)
  35.               {
  36.                   /* write bit value for search */
  37.                   search_direction = id_bit;
  38.               }
  39.               else
  40.               {
  41.                   if (id_bit_number < ds18b20_data.LastDiscrepancy)
  42.                   {
  43.                       search_direction = ((ds18b20_data.ROM_NO[rom_byte_number] & rom_byte_mask) > 0);
  44.                   }
  45.                   else
  46.                   {
  47.                       search_direction = (id_bit_number == ds18b20_data.LastDiscrepancy);
  48.                   }
  49.                   if (search_direction == 0)
  50.                   {
  51.                       last_zero = id_bit_number;
  52.                       /* Check for last family disperancy */
  53.                       if (last_zero < 9)
  54.                       {
  55.                           ds18b20_data.LastFamilyDiscrepancy = last_zero;
  56.                       }
  57.                   }
  58.               }
  59.               /* set or clear the bit in the ROM byte, with mask  */
  60.               if (search_direction == 1)
  61.               {
  62.                   ds18b20_data.ROM_NO[rom_byte_number] |= rom_byte_mask;
  63.               }
  64.               else
  65.               {
  66.                   ds18b20_data.ROM_NO[rom_byte_number] &= ~rom_byte_mask;
  67.               }
  68.               /* serial number, search direction */
  69.               ds18b20WriteBit(search_direction);
  70.               /*
  71.                * increment byte counter
  72.                * next shift mask
  73.                * */
  74.               id_bit_number++;
  75.               rom_byte_mask <<= 1;
  76.               if (rom_byte_mask == 0)
  77.               {
  78.                   rom_byte_number++;
  79.                   rom_byte_mask = 1;
  80.               }
  81.           }
  82.       }while(rom_byte_number < 8);
  83.       /* If the search was successful */
  84.       if (!(id_bit_number < 65))
  85.       {
  86.           ds18b20_data.LastDiscrepancy = last_zero;
  87.           /* check last device */
  88.           if (ds18b20_data.LastDiscrepancy == 0)
  89.           {
  90.               ds18b20_data.LastDeviceFlag = 1;
  91.           }
  92.           search_result = 1;
  93.       }
  94.       if (!search_result || !ds18b20_data.ROM_NO[0])
  95.       {
  96.           ds18b20_data.LastDiscrepancy = 0;
  97.           ds18b20_data.LastDeviceFlag = 0;
  98.           ds18b20_data.LastFamilyDiscrepancy = 0;
  99.           search_result = 0;
  100.       }
  101.       else
  102.       {
  103.           for (int i = 0; i < 8; i++)
  104.           {
  105.               Addr[i] = ds18b20_data.ROM_NO[i];
  106.           }
  107.           /* If there is alarm then display ROM RAW of device */
  108.           if(command == COM_ALARM_SEARCH)
  109.           {
  110.               sprintf(str1,"Device deploy alarm: %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
  111.                       Addr[0], Addr[1], Addr[2], Addr[3], Addr[4], Addr[5], Addr[6], Addr[7]);
  112.               HAL_UART_Transmit(&huart2,(uint8_t*)str1,strlen(str1),0x1000);
  113.           }
  114.       }
  115.       return search_result;
  116. }

Funkcje wywoływane w pętli głównej wyglądają następująco:

  1. void ds18b20InitReadData(void)
  2. {
  3.     uint8_t buffer[60];
  4.     uint8_t status = 0;
  5.     uint8_t i = 0;
  6.     /* Init all sensors */
  7.     status = ds18b20Init(NO_SKIP_ROM);
  8.     if(status == 1)
  9.     {
  10.         sprintf((char*)buffer,"Initialization status OK\r\n");
  11.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  12.     }
  13.     else
  14.     {
  15.         sprintf((char*)buffer,"Initialization status Error\r\n");
  16.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  17.     }
  18.     sprintf((char*)buffer,"Number of devices on line: %d\r\n", ds18b20_data.Dev_Cnt);
  19.     HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  20.     /* Get device informations */
  21.     for(i=1;i<=ds18b20_data.Dev_Cnt;i++)
  22.     {
  23.         sprintf((char*)buffer,"Device number: %d\r\n", i);
  24.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  25.         sprintf((char*)buffer,"ROM RAW: %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
  26.             ds18b20_data.Dev_ID[i-1][0], ds18b20_data.Dev_ID[i-1][1], ds18b20_data.Dev_ID[i-1][2], ds18b20_data.Dev_ID[i-1][3],
  27.             ds18b20_data.Dev_ID[i-1][4], ds18b20_data.Dev_ID[i-1][5], ds18b20_data.Dev_ID[i-1][6], ds18b20_data.Dev_ID[i-1][7]);
  28.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  29.         sprintf((char*)buffer,"Family CODE: 0x%02X\r\n", ds18b20_data.Dev_ID[i-1][0]);
  30.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  31.         sprintf((char*)buffer,"ROM CODE: 0x%02X%02X%02X%02X%02X%02X\r\n",
  32.                 ds18b20_data.Dev_ID[i-1][6], ds18b20_data.Dev_ID[i-1][5],
  33.                 ds18b20_data.Dev_ID[i-1][4], ds18b20_data.Dev_ID[i-1][3],
  34.                 ds18b20_data.Dev_ID[i-1][2], ds18b20_data.Dev_ID[i-1][1]);
  35.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  36.         sprintf((char*)buffer,"CRC: 0x%02X\r\n", ds18b20_data.Dev_ID[i-1][7]);
  37.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  38.     }
  39.     /* set alarm on one device */
  40.     ds18b20SetAlarm(ds18b20_data.Dev_ID, 0x01, 0x14, 0x8A);
  41. }
  42. void ds18b20ReadTempFromDev(void)
  43. {
  44.     uint8_t buffer[60];
  45.     uint8_t i;
  46.     uint8_t device[8];
  47.     uint16_t readDataRaw;
  48.     float tempValue;
  49.     char sign;
  50.     for(i=1;i<=ds18b20_data.Dev_Cnt;i++)
  51.     {
  52.         ds18b20DevConTemp(NO_SKIP_ROM, i);
  53.     }
  54.     for(i=1;i<=ds18b20_data.Dev_Cnt;i++)
  55.     {
  56.         ds18b20ReadScratchpad(NO_SKIP_ROM, device, i);
  57.         sprintf((char*)buffer,"READ SCRATCHPAD SEN:%d - %02X %02X %02X %02X %02X %02X %02X %02X; ",
  58.                 i, device[0], device[1], device[2], device[3], device[4], device[5], device[6], device[7]);
  59.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  60.         readDataRaw = ((uint16_t)device[1]<<8)|device[0];
  61.         if(ds18b20ReadSign(readDataRaw))    {   sign='-';   }
  62.         else                                {   sign='+';   }
  63.         tempValue = ds18b20GetTemp(readDataRaw);
  64.         sprintf((char*)buffer,"RegVal: 0x%04X - %c%.2f\r\n", readDataRaw, sign, tempValue);
  65.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  66.     }
  67. }
  68. void ds18b20AlarmSearch(void)
  69. {
  70.     uint8_t device[8];
  71.     uint8_t buffer[60];
  72.     while(ds18b20Search(device, COM_ALARM_SEARCH))
  73.     {
  74.         sprintf((char*)buffer,"Alarm deploy!\r\n");
  75.         HAL_UART_Transmit(&huart2,(uint8_t*)buffer,strlen((char*)buffer),0x1000);
  76.     }
  77. }

Funkcja ds18b20InitReadData odczytuje parametry z czujników, dodatkowo ustawiając alarmy oraz rozdzielczość. Następnie funkcja ds18b20ReadTempFromDev odczytuje scratchpada oraz pobiera temperaturę. No i na końcu sprawdzenie czy któryś z podłączonych czujników wywołał alarm ds18b20AlarmSearch.

Ilość podłączonych czujników definiuje się w pliku ds18b20.h:

  1. #define DS18B20_CONNECTED_DEV 2

Dane przechowywane są w strukturze:

  1. typedef struct ds18b20SensorsData_t{
  2.     uint8_t LastDeviceFlag;
  3.     uint8_t LastDiscrepancy;
  4.     uint8_t LastFamilyDiscrepancy;
  5.     uint8_t ROM_NO[8];
  6.     uint8_t Dev_ID[DS18B20_CONNECTED_DEV][8];
  7.     uint8_t Dev_Cnt;
  8. }ds18b20SD_t;
  9. ds18b20SD_t ds18b20_data;

Po uruchomieniu programu testowego i poprawnym podłączeniu na komputer będą wysyłane następujące informacje:


Bibliotekę można pobrać pod tym linkiem. Po jego otwarciu należy wejście w folder STM32 a następnie STM32F4_Discovery.