Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Странное поведение DS18b20
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Halfback
Всем доброго времени суток!
Есть проблема с датчиками DS18b20
Заказал пару штук этих датчиков во влагозащищенном исполнении.
Подключил один из них по 2-проводной схеме - т.е. красный и черный провод объединил и посадил на "землю". Желтый провод сделал PullUP (к 3,3В через 3,3кОм) посадил на передатчик USART4.
МК - STM32F103RE (отладочная плата - Махаон).
Для упрощения жизни запустил CubeMX последней версии, настроил USART4 на HalfDuplex.
И ситуация следующая: на F0 датчик отвечает E0, а вот на остальные команды не отвечает вообще никак. Ни на 0хсс 0хBE (чтение памяти 9 байт) ни на 0x33.
Собственно, результаты смотрел в отладчике Keil и осциллографом. Скорость UART меняется адекватно, оценивал длительность импульсов согласно скорости.

Код ниже:

Код
        
uint8_t TxCmdReset=0xF0;
uint8_t RxAnswReset1;
uint8_t RxData[100];
uint8_t ow_buf[8];

HAL_HalfDuplex_EnableReceiver(&huart4);
        
    MX_UART4_Init_wBoudRate(9600); //Reset
    HAL_UART_Receive_IT(&huart4,&RxAnswReset1,1);    
    HAL_UART_Transmit(&huart4, &TxCmdReset, sizeof(TxCmdReset), 2);    
    for(i=0;i<1000;i++) {__NOP();}
        
    MX_UART4_Init_wBoudRate(115200);
    HAL_UART_Receive_IT(&huart4, RxData, sizeof(RxData));
    OW_toBits(0xCC, ow_buf);
    HAL_UART_Transmit(&huart4, (uint8_t *)ow_buf, 8, 3);
    OW_toBits(0xB4, ow_buf);
    HAL_UART_Transmit(&huart4, (uint8_t *)ow_buf, 8, 3);
    HAL_Delay(2);
    while(1);


Код
void MX_UART4_Init_wBoudRate(uint32_t BoudRate)
{
  huart4.Instance = UART4;
  huart4.Init.BaudRate = BoudRate;
  huart4.Init.WordLength = UART_WORDLENGTH_8B;
  huart4.Init.StopBits = UART_STOPBITS_1;
  huart4.Init.Parity = UART_PARITY_NONE;
  huart4.Init.Mode = UART_MODE_TX_RX;
  huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart4.Init.OverSampling = UART_OVERSAMPLING_16;
  
  HAL_HalfDuplex_Init(&huart4);
}


Функция преобразования байта в 8 байт для отправки заимствован. Тем не менее его в отладчике проверял - замечаний нет.

Код
void OW_toBits(uint8_t ow_byte, uint8_t *ow_bits) {
        uint8_t i;
        for (i = 0; i < 8; i++) {
               if (ow_byte & 0x01) {
                        *ow_bits = OW_1;
                } else {
                        *ow_bits = OW_0;
                }
                ow_bits++;
                ow_byte = ow_byte >> 1;
        }
}


Пробовал оба датчика - ведут себя одинаково. Значит есть надежда что ошибка в коде.
Может кто прояснит в чем я не прав?
k155la3
Насколько мне не изменяет (?) память, DS18B20 работают по интерфейсу OWI.
Соответствует ли формат данных фреймам символов USART - не знаю.
Но для корректной работы с датчиком временнЫе параметры запроса должны выдерживатьс.
Для начала - посмотрите осцилографом соответствие формы Вашего запроса к датчику требованиям даташита на 18B20.

По аппартаной части - есть малозаметное отличие для 18B20
DS18B20 - 3-проводное подключение.
DS18B20P - 2-проводное подключение с "паразитным" питанием.
adnega
Цитата(Halfback @ Jan 27 2016, 20:45) *
Пробовал оба датчика - ведут себя одинаково. Значит есть надежда что ошибка в коде.
Может кто прояснит в чем я не прав?

Недавно выкладывал проверялщик DS18B20 для STM32F4DISCOVERY.
Halfback
Спасибо что откликнулись.
Тайминги на соответствие даташиту посмотрю.

Могу вот что добавить: при запросе на преобразование 0хF0 (9600), 0xCC(115200), 0xBE(115200) при подключении по двухпроводной схеме (VCC и GND замыкаются и сажаются на землю, сигнальный на UART TX c подтяжкой к 3,3В через 3,3кОм) осциллограмма в точности как на этом рисунке

В этом случае проседание длиться ~600мс.
Если включаю по трехпроводной схеме то при таком же запросе проседания линии нет.
Что бы это могло значить?
adnega
Цитата(Halfback @ Jan 28 2016, 11:33) *
Что бы это могло значить?

При паразитном питании во время преобразования линию DQ нужно тащить к VCC активно: через ключ, а не пассивной подтяжкой резистором.
AHTOXA
Цитата(Halfback @ Jan 27 2016, 22:45) *
Для упрощения жизни запустил CubeMX последней версии, настроил USART4 на HalfDuplex.

Не надо HalfDuplex, приём должен идти одновременно с передачей.
k155la3
Вот подвернулось тут. Работа с DS18B20
По ссылке, любезно предоставленной Евгенич:
тут
http://karve.in/?p=1408
http://www.shelezyakin.ru/?p=104

Halfback
Частично разобрался.
Решил пока забить на UART и сделал долбатню пином. Все временные выдерки взял согласно даташиту на датчик. Всё четко как в аптеке. Свою задержку 1us замерял осциллом.

Собственно основной цикл:
Код
#define ONEWIRE_LOW()     HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET)
#define ONEWIRE_HIGH() HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET)
#define ONEWIRE_INPUT()  MX_GPIO_P10_Init(2) // IN NoPull
#define ONEWIRE_OUTPUT_PP() MX_GPIO_P10_Init(1) // OUT PP
#define ONEWIRE_OUTPUT_OD() MX_GPIO_P10_Init(0) // OUT OD
#define ONEWIRE_READPIN() HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_10)
#define DELAY_1us() {__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
                                        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
                                        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
                                        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
                                        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
                                        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();}
#define ONEWIRE_DELAY(delay_us) {for(volatile uint16_t i=0;i<delay_us-1;i++) DELAY_1us();}
......
volatile uint8_t ResetAnsw=0x55;
uint8_t RxByte[9]={0,0,0,0,0,0,0,0,0};
volatile uint8_t Temperature=0;
....
ResetAnsw=TM_OneWire_Reset();
      TM_OneWire_WriteByte(0xcc); // skip ROM command
      TM_OneWire_WriteByte(0x44); // convert T command
      ONEWIRE_OUTPUT_PP();
      ONEWIRE_HIGH();
      HAL_Delay(800); // at least 750 ms for the default 12-bit resolution
      ResetAnsw=TM_OneWire_Reset();
      TM_OneWire_WriteByte(0xcc); // skip ROM command
      TM_OneWire_WriteByte(0xbe); // read scratchpad command    
        for(uint8_t t=0;t<10;t++) RxByte[t]=TM_OneWire_ReadByte();
        Temperature = ((RxByte[1]&7)<<4)|(RxByte[0]>>4);


Но на самом деле дъявол кроется в этих функциях:
CODE
void SystemClock_Config(void)
{

RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);

HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

void MX_GPIO_P10_Init(uint8_t PC10_Mode) // 0- Out OD, 1- Out PP, 2- In
{

GPIO_InitTypeDef GPIO_InitStruct;

/* GPIO Ports Clock Enable */
__GPIOD_CLK_ENABLE();
__GPIOC_CLK_ENABLE();

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET);

/*Configure GPIO pin : PC10 */
if(PC10_Mode==0) {GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; }
else if(PC10_Mode==1) {GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;}
else if(PC10_Mode==2) {GPIO_InitStruct.Mode = GPIO_MODE_INPUT;}


GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}

uint8_t TM_OneWire_Reset(void) {
uint8_t i;

ONEWIRE_OUTPUT_OD();
ONEWIRE_LOW();
ONEWIRE_DELAY(480);
ONEWIRE_HIGH();
ONEWIRE_INPUT();
ONEWIRE_DELAY(70);
i = ONEWIRE_READPIN();
ONEWIRE_DELAY(410);
ONEWIRE_OUTPUT_OD();
return i;
}

void TM_OneWire_WriteByte(uint8_t byte) {
uint8_t i = 8;

ONEWIRE_OUTPUT_OD();
for(i=0;i<8;i++) {
if((byte&0x01)==0x01) {
ONEWIRE_LOW(); // Drive bus low
ONEWIRE_DELAY(2);
ONEWIRE_HIGH();
ONEWIRE_DELAY(55); //sample time slot for the slave
//ONEWIRE_DELAY(2); //recovery time slot
}
else {
ONEWIRE_LOW(); // Drive bus low
ONEWIRE_DELAY(90);
ONEWIRE_HIGH();
ONEWIRE_DELAY(2); //recovery time slot
}
byte=byte>>1;
ONEWIRE_HIGH();
}
}

uint8_t TM_OneWire_ReadByte(void) {
uint8_t i = 8, byte = 0;

while (i--) {
ONEWIRE_OUTPUT_OD();
ONEWIRE_LOW();
ONEWIRE_DELAY(2);
ONEWIRE_INPUT();
ONEWIRE_DELAY(6);
byte >>= 1;
byte |= (ONEWIRE_READPIN() << 7);
ONEWIRE_DELAY(50);
}
return byte;
}


Буду ли ковыряться с UART - не знаю. Слишком много времени убил на этот датчик.
adnega
Цитата(Halfback @ Jan 29 2016, 16:52) *
Частично разобрался.
...
Буду ли ковыряться с UART - не знаю. Слишком много времени убил на этот датчик.

А самое интересное еще впереди (сделать работу по прерываниям, поддерживать несколько датчиков на шине с поиском ROM и т.п.).
k155la3
Цитата(Halfback @ Jan 29 2016, 17:52) *
Частично разобрался.
Решил пока забить на UART и сделал долбатню пином. Все временные выдерки взял согласно даташиту на датчик. Всё четко как в аптеке. Свою задержку 1us замерял осциллом.
. . . .
Буду ли ковыряться с UART - не знаю. Слишком много времени убил на этот датчик.


Работа с датчиком в таком режиме предполагает отсутствие всех прерываний.
Возможно, чтобы не ограничиваться в этом, и "замутили" работу через UART.
Запретить прерывания можно только на время цикла обмена с DS18B20. цена вопроса пара миллисекунд (кажется)
И все будет феншуй.
adnega
Цитата(k155la3 @ Jan 31 2016, 17:56) *
Запретить прерывания можно только на время цикла обмена с DS18B20. цена вопроса пара миллисекунд (кажется)
И все будет феншуй.

)))
Обычно такой подход подразумевает вызов функции get_temp(), которая со всеми задержками и ожиданиями преобразования длится под секунду.
Да, можно разрешать и запрещать прерывания внутри get_temp(), но это никак не поможет работе всему, что до и после get_temp().
Напомню, что у ТС контроллер, типа STM32... а такой подход у меня вызывает сильнейшее негодование))
Уверен, что есть куча библиотек для STM32, где используются UART, TIMER и/или DMA - нужно только хорошо поискать и/или написать самому.

PS. Я на паршивеньком STM32F030F4 делал эмулятор DS18B20, а это в разы сложнее мастера, да еще на таком "жирном" МК.
jcxz
Цитата(Halfback @ Jan 29 2016, 19:52) *
Решил пока забить на UART и сделал долбатню пином.

И причём там UART??? wacko.gif А почему не на SPI зацепили?
DS18B20 вроде по 1-Wire работают. Этот интерфейс легко эмулируется на одном таймере + GPIO.

Цитата(adnega @ Jan 29 2016, 20:04) *
А самое интересное еще впереди (сделать работу по прерываниям, поддерживать несколько датчиков на шине с поиском ROM и т.п.).

С таким подходом
Цитата
#define DELAY_1us() {__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP()
;\
...
это невозможно.

Цитата(adnega @ Jan 31 2016, 23:30) *
Напомню, что у ТС контроллер, типа STM32... а такой подход у меня вызывает сильнейшее негодование))
Уверен, что есть куча библиотек для STM32, где используются UART, TIMER и/или DMA - нужно только хорошо поискать и/или написать самому.
PS. Я на паршивеньком STM32F030F4 делал эмулятор DS18B20, а это в разы сложнее мастера, да еще на таком "жирном" МК.

Таких "программистов" гнать в шею надо из команды.
rx3apf
Цитата(jcxz @ Feb 1 2016, 08:16) *
И причём там UART??? wacko.gif А почему не на SPI зацепили?
DS18B20 вроде по 1-Wire работают. Этот интерфейс легко эмулируется на одном таймере + GPIO.

И на UART тоже (в старых аппликухах от Dallas как раз через UART цепляли). В остальном - согласен.
k155la3
Цитата(adnega @ Jan 31 2016, 20:30) *
)))
Обычно такой подход подразумевает вызов функции get_temp(), которая со всеми задержками и ожиданиями преобразования длится под секунду.
. . . . .


Да. Я забыл, что при полной разрядности по температуре преобразование 750 ms. sad.gif
Леонид Иванович
Цитата(k155la3 @ Jan 31 2016, 18:56) *
Запретить прерывания можно только на время цикла обмена с DS18B20. цена вопроса пара миллисекунд (кажется)


Прерывания надо запрещать только в промежутке от окончания импульса сброса до момента поллинга "presence pulse" (это 75 мкс), а также от начала тайм-слота до считывания бита (это примерно 60 мкс). Остальное время прерывания могут как угодно растягивать процесс обмена - ни на что это не повлияет.
jcxz
Цитата(Леонид Иванович @ Feb 5 2016, 04:09) *
Прерывания надо запрещать только в промежутке от окончания импульса сброса до момента поллинга "presence pulse" (это 75 мкс), а также от начала тайм-слота до считывания бита (это примерно 60 мкс). Остальное время прерывания могут как угодно растягивать процесс обмена - ни на что это не повлияет.

За 75 или 60 мкс у Вас успеет потеряться к примеру один байт в UART-е работающем без FIFO на скорости 230400.
Прерывания вообще запрещать не нужно. Вся работа идёт в ISR. Процесс приёма бита:
1.Мастер включает pull down шины; программирует таймер на выдержку 15 мкс; выходит из ISR.
2.Получив прерывание таймера: выключает pull down шины; программирует таймер в режим capture с прерыванием/защёлкиванием значения таймера от фронта сигнала на шине; также программируется таймер на отслеживание таймаута
(на случай если удалённая сторона померла или линия залипла в "pull down"); выходит из ISR.
3.Получив прерывание таймера (таймаут): детектирует состояние ошибки обмена с датчиком - завершает процедуру обмена; выходит из ISR.
4.Получив прерывание таймера (обнаружен фронт на шине): считывает защёлкнутое значение таймера, вычисляет прошедшее время - определяет значение бита, сохраняет его; декрементирует счётчик оставшихся бит, если не 0 - программирует таймер на выдержку интервала времени до начала след. бита согласно требуемой и допустимой скорости обмена по шине; выходит из ISR.
5.Переход к п.1.

Всё! Ни одного запрета прерывания! Тем более на огромные интервалы порядка десятков мкс.
Можно немного упростить алгоритм, если не использовать режим capture таймера, а работать на обычных выдержках с принятием решений по границам состояний бит '0' и '1'. Но правильнее будет с capture.
Алгоритм передачи ещё проще, даже описывать смысла нет.
Сергей Борщ
Цитата(jcxz @ Feb 5 2016, 08:09) *
Алгоритм передачи ещё проще, даже описывать смысла нет.
Еще проще объединить прием и передачу в обмен битом. Потому что прием ничем не отличается от передачи единицы.
Леонид Иванович
Цитата(jcxz @ Feb 5 2016, 09:09) *
За 75 или 60 мкс у Вас успеет потеряться


Есть множество проектов, где запрещение прерываний на десятки мкс не является критичным. Тогда можно делать чисто программную реализуцию 1-Wire, которая намного проще для понимания, чем вариант с таймером.
jcxz
Цитата(Леонид Иванович @ Feb 5 2016, 16:55) *
Есть множество проектов, где запрещение прерываний на десятки мкс не является критичным. Тогда можно делать чисто программную реализуцию 1-Wire, которая намного проще для понимания, чем вариант с таймером.

Здесь раздел для начинающих. Т.е. - для тех кто должен учиться. Учиться делать правильно. Чтобы это потом можно было использовать в других проектах, наращивать функциональность проектов добавляя новые интерфейсы например и функции.
Да и опыта надо набираться начинающим. Как раз вот на таких примитивных интерфейсах это и можно делать.
Я считаю - если сейчас он делает тяп-ляп, лишь бы кое-как, то и потом с него толка не будет.
CrimsonPig
Цитата(jcxz @ Feb 5 2016, 16:22) *
Здесь раздел для начинающих. Т.е. - для тех кто должен учиться. Учиться делать правильно. Чтобы это потом можно было использовать в других проектах, наращивать функциональность проектов добавляя новые интерфейсы например и функции.
Да и опыта надо набираться начинающим. Как раз вот на таких примитивных интерфейсах это и можно делать.
Я считаю - если сейчас он делает тяп-ляп, лишь бы кое-как, то и потом с него толка не будет.


Можно получить четкое определение "правильно" ?
Вот у меня, например, похожий термометр был прикручен к pic12 и все микросекундные тайминги для обмена сделаны на задержках... Просто потому что не имело смысла возюкаться с таймерами и прерываниями.. Это правильно или неправильно ?
adnega
Цитата(CrimsonPig @ Feb 5 2016, 19:39) *
Можно получить четкое определение "правильно" ?

В самом общем смысле: найти такой подход, который максимизирует полезный результат.

Теперь частности: я занимаюсь фрилансом; для меня скорость и качество работ прямопропорциональны денежному вознаграждению,
которое, в отличии от наемных работников, не ограничено сверху. Если я один раз напрягусь, сделаю качественную библиотеку для
работы с DS18B20 и буду многократно без какой-либо головной боли использовать в проектах, то это позволит сократить сроки реализации
проекта и максимизировать доход. Поэтому в моем случае "правильно" - это когда библиотека написана правильно.

Если товарищ не желает погружаться в технические дебри, а хочет с минимумом усилий получить хоть какой-то результат,
то "правильно" для него - это выбрать какую-либо другую профессию, например, фасовщик фарша.
jcxz
Цитата(CrimsonPig @ Feb 5 2016, 22:39) *
Вот у меня, например, похожий термометр был прикручен к pic12 и все микросекундные тайминги для обмена сделаны на задержках...

В pic12 это конечно менее критично, так как ресурсы там сильно ограничены и каких-то серьёзных задач на нём не будет. И дальнейшего серьёзного развития проекта.
Хотя я всё равно, в любом случае, стараюсь делать правильно, т.е. - с минимальным расходованием ресурсов, а не "лишь-бы хоть как заработало".
Описанным выше образом я реализовывал похожий интерфейс на MSP430, где ресурсов много меньше чем на Cortex-M.
ILYAUL
Самый простой способ работать с 1-wire через UART . Единственное , что там надо учесть - двойную буферизацию передатчика , если работать по UDRE и с прерываниями + кварц.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.