Датчик температуры DS18B20 и STM8, работа по протоколу 1-Wire
Микросхема DS18B20 это датчик температуры, работающий по протоколу 1-wire. Это такой протокол, где и для передачи данных в обе стороны, и для питания датчика используется один провод. В этой заметке я покажу, как использовать этот датчик совместно с микроконтроллером STM8. Я использую компилятор Raisonance.
Сначала определимся с портом и ножкой (выводом, пином) микроконтроллера, которую мы выделим для обмена с датчиком. Дальше пишем макросы, которые будут включать тот или иной режим вывода микроконтроллера, читать и писать в него.
#define THERM_PORT GPIOI #define THERM_PIN GPIO_PIN_0 #define THERM_INPUT_MODE() THERM_PORT->DDR &= ~THERM_PIN #define THERM_OUTPUT_MODE() THERM_PORT->DDR |= THERM_PIN #define THERM_LOW() THERM_PORT->ODR &= (u8)(~THERM_PIN) #define THERM_HIGH() THERM_PORT->ODR |= (u8)THERM_PIN #define THERM_READ() (THERM_PORT->IDR & (vu8)THERM_PIN)
Протокол 1-Wire это такой протокол, который построен на чётко выверенных задержках между переключением уровней сигнала в проводе, по которому осуществляется передача. Задержки определяют, в какую сторону в данный момент идёт передача данных. Без правильного подбора интервалов ничего не заработает.
Понадобятся несколько интервалов, в 1, 15, 45, 60 и 480 мкс. Я просто взял логический анализатор, подключил его к шине 1-wire, и стал из программы переключать порт из 0 в 1 и обратно, при этом я делал задержку инструкцией процессора nop. По логическому анализатору можно было измерить, какая получается задержка. У меня получилось в результате измерений следующие задержки:
inline void x_delay(unsigned int i) { while (--i) _nop_(); } #define delay_1us() _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); #define delay_15us() x_delay(42) #define delay_45us() x_delay(127) #define delay_60us() x_delay(170) #define delay_480us() x_delay(1360) volatile char iCurrentTemp = 0;
В переменную iCurrentTemp будет помещаться температура. Она может быть как отрицательная, так и положительная. Размерность типа char позволит сохранять там температуру в диапазоне ±180°, что для моих целей достаточно. Вообще, датчик позволяет измерять температуру в диапазоне от -55 до +125°. Как видите, как раз влезает в char.
Теперь объявляем необходимые для опроса датчика функции. В принципе, комментарии в коде достаточно описывают то, что в нём содержится.
// Команды #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 // Режимы работы. По-умолчанию 12 бит. typedef enum { THERM_MODE_9BIT = 0x1F, THERM_MODE_10BIT = 0x3F, THERM_MODE_11BIT = 0x5F, THERM_MODE_12BIT = 0x7F } THERM_MODE; /** * @brief Сброс термодатчика * @par * Параметры отсутствуют * @retval * false - Неисправность; true - Норма */ bool therm_reset(void) { unsigned char i = 0xFF; THERM_OUTPUT_MODE(); THERM_LOW(); delay_480us(); THERM_INPUT_MODE(); delay_60us(); i = THERM_READ(); delay_480us(); // 0 означает правильный сброс, 1 - ошибка return (0 == i) ? true : false; } /** * @brief Запись бита * @param[in] bBit бит * @retval * Возвращаемое значение отсутствует */ void therm_write_bit(bool bBit) { THERM_OUTPUT_MODE(); THERM_LOW(); delay_1us(); if (bBit) { THERM_INPUT_MODE(); } delay_60us(); THERM_INPUT_MODE(); } /** * @brief Чтение бита * @par * Параметры отсутствуют * @retval * Значение бита. */ bool therm_read_bit(void) { bool bBit = 0; THERM_OUTPUT_MODE(); THERM_LOW(); delay_1us(); THERM_INPUT_MODE(); delay_15us(); if (THERM_READ()) { bBit = true; } delay_45us(); return bBit; } /** * @brief Чтение байта * @par * Параметры отсутствуют * @retval * Значение байта. */ static unsigned char therm_read_byte(void) { unsigned char i = 8; unsigned char n = 0; while (i--) { // Сдвинуть на одну позицию вправо и сохранить значение бита n >>= 1; n |= (therm_read_bit() << 7); } return n; } /** * @brief Запись байта * @param[in] byte байт * @retval * Возвращаемое значение отсутствует */ void therm_write_byte(unsigned char byte) { unsigned char i = 8; while (i--) { // Записать текущий бит и сдвинуть на 1 позицию вправо // для доступа к следующему биту therm_write_bit(byte & 1); byte >>= 1; } } /** * @brief Установить режим работы термодатчика * @param[in] mode Режим работы * @retval * Возвращаемое значение отсутствует */ void therm_init_mode(THERM_MODE mode) { therm_reset(); therm_write_byte(THERM_CMD_SKIPROM); therm_write_byte(THERM_CMD_WSCRATCHPAD); therm_write_byte(0); therm_write_byte(0); therm_write_byte(mode); }
Теперь самая главная функция. Я запрещаю прерывания во время опроса датчика, чтобы данные не искажались при обмене. Ведь в протоколе 1-Wire важны интервалы, а если процесс опроса датчика прервётся, интервалы нарушатся.
Один бит отвечает за то, отрицательная температура или положительная. Но он находится отдельно от числа, которое означает температуру. Значит, отрицательное значение нужно ещё дополнительно обработать.
Дробная часть температуры в этом коде отбрасывается, так как мне не нужна подобная точность. Но, в принципе, её можно тоже извлечь и обработать. См. документацию на DS18B20.
/** * @brief Чтение температуры. * @par * Параметры отсутствуют * @retval * Температура. */ char GetTemperature(void) { char iResult = 0; unsigned char temperature[2] = {0, 0}; unsigned char digit; short iReadLimit; // Запрет прерываний во время опроса датчика disableInterrupts(); // Сброс и сразу переход к преобразованию температуры iResult = therm_reset(); therm_write_byte(THERM_CMD_SKIPROM); therm_write_byte(THERM_CMD_CONVERTTEMP); // Ожидание завершения преобразования iReadLimit = 10; while (!therm_read_bit() && (--iReadLimit > 0)) { ; } // Сброс и чтение байт температуры therm_reset(); therm_write_byte(THERM_CMD_SKIPROM); therm_write_byte(THERM_CMD_RSCRATCHPAD); temperature[0] = therm_read_byte(); temperature[1] = therm_read_byte(); // Разрешить прерывания enableInterrupts(); digit = 0; digit = ( temperature[0] >> 4 ) & 0x0F; digit |= ( (temperature[1] & 0x0F) << 4 ) & 0xF0; // Отрицательная температура - инвертировать и прибавить 1 if (temperature[1] & 0x80) { iResult = ~digit; iResult++; } else { iResult = digit; } iCurrentTemp = iResult; return iResult; }
Автор: амдф
Дата: 30.09.2012
Избранное
Остальное
Лента atom