Псевдоаппаратный 1-wire на STM32
Jul. 21st, 2015 02:39 pmНаконец-то дошли руки допилить "псевдоаппаратный" 1-wire (сосфорж,
гитхаб).
Реализация довольно простая: через таймер TIM2 и DMA, разве что сброс происходит через прерывание (но эта процедура выполняется один раз перед отправкой пакета данных, так что не страшно). Канал 4 таймера TIM2 используется одновременно как выход и вход: TIM2_CH4 работает в режиме ШИМ-выхода, а TIM2_CH3 — в режиме ШИМ-захвата. Соответственно, используются два буфера DMA: канал 7 DMA1 последовательно заполняет из буфера передачи регистр TIM2_CCR4 (этот регистр буферизуется, чтобы его содержимое обновлялось лишь по окончанию передачи предыдущего байта); канал 1 DMA1 по событию захвата CCR3 считывает содержимое TIM2_CCR3 в другой буфер.
По истечению захвата DMA генерирует прерывание, внутри обработчика которого отключается все ненужное и устанавливается флаг готовности принятых данных. Дальше уже обработчик анализирует буфер и выдает нужную информацию (использую простейший конечный автомат, который по завершению приема запускает функцию-обработчик, для нее выделена отдельная глобальная переменная).
"Высокоуровневые" функции для 1-wire почти неизменными были взяты из реализации на STM8 (но там я из-за отсутствия DMA делал все на прерываниях). А вот с низкоуровневыми я помучился... Это уже сейчас кажется, что вроде бы ничего там страшного нет, но поначалу были сложности реализации.
Таймер TIM2 работает на частоте 1МГц, в его регистр TIM2_ARR при сбросе заносится 1000 (1мс), а при передаче данных — 100 (100мкс — средняя длительность передачи бита данных). Регистр TIM2_CCR4 при сбросе имеет значение 500, при передаче данных — 10 (передача единицы) или 60 (передача нуля), при приеме данных — 5.
Соответственно, при удачном сбросе генерируемый датчиками нулевой импульс приводит к тому, что в TIM2_CCR3 заносится значение больше 550 (обычно где-то 650). Если же значение меньше этой барьерной величины (т.е. 500), делаем вывод, что датчиков на шине нет.
Передача данных происходит в соответствии с рекомендованными в даташите длительностями импульсов. Прием инициируется короткой (на 5мкс) подтяжкой шины данных к нулю, а дальше уже датчик "дотягивает" импульс до нужного значения (если длительность импульса меньше 10мкс, то передается единица, иначе передается нуль).
Базовый функционал я реализовал на своей китайской макетке с STM32F103RBT6 (собственно, сниппеты из репозитория stm32samples под нее и пишутся). Реализовано: считывание идентификатора датчика с занесением уникальных идентификаторов в буфер (до 8 датчиков); запуск (широковещательный) измерения температуры; опрос всех датчиков с сохраненными в буфере идентификаторами (если датчик только один, посылается широковещательная команда).
Температура вычисляется в десятых долях градуса Цельсия (т.к. DS18S20 позволяет измерять с точностью не лучше ±0.5°C, а DS18B20 — не лучше ±0.125°C). Для различия DS18S20 и DS18B20 я проверяю байт[4] "блокнота" (scratchpad): у DS18S20 он равен 0xFF, а DS18B20 хранит там конфигурационный регистр, старший бит которого всегда равен нулю. В принципе, этим регистром можно изменять разрядность DS18B20, но я пока это не буду реализовывать: все равно нет смысла опрашивать датчики чаще, чем раз в полминуты, так что 750мс будет длиться преобразование или же 93.75мс — безразлично.
Если при опросе N датчиков не было обнаружено определенного, то будет возвращено значение ERR_TEMP_VAL, равное 200000 (что явно выходит за пределы возможностей датчика).
Теперь все это нужно прикрутить к ИК-контроллеру, чтобы иметь возможность замерять "теплые" температуры (скажем, по разности температур стенок криостата и окружающего воздуха можно судить о глубине вакуума в криостате).
P.S. Странно: не могу сделать hg push, Опять какие-то косяки с сосфоржем...
гитхаб).
Реализация довольно простая: через таймер TIM2 и DMA, разве что сброс происходит через прерывание (но эта процедура выполняется один раз перед отправкой пакета данных, так что не страшно). Канал 4 таймера TIM2 используется одновременно как выход и вход: TIM2_CH4 работает в режиме ШИМ-выхода, а TIM2_CH3 — в режиме ШИМ-захвата. Соответственно, используются два буфера DMA: канал 7 DMA1 последовательно заполняет из буфера передачи регистр TIM2_CCR4 (этот регистр буферизуется, чтобы его содержимое обновлялось лишь по окончанию передачи предыдущего байта); канал 1 DMA1 по событию захвата CCR3 считывает содержимое TIM2_CCR3 в другой буфер.
По истечению захвата DMA генерирует прерывание, внутри обработчика которого отключается все ненужное и устанавливается флаг готовности принятых данных. Дальше уже обработчик анализирует буфер и выдает нужную информацию (использую простейший конечный автомат, который по завершению приема запускает функцию-обработчик, для нее выделена отдельная глобальная переменная).
"Высокоуровневые" функции для 1-wire почти неизменными были взяты из реализации на STM8 (но там я из-за отсутствия DMA делал все на прерываниях). А вот с низкоуровневыми я помучился... Это уже сейчас кажется, что вроде бы ничего там страшного нет, но поначалу были сложности реализации.
Таймер TIM2 работает на частоте 1МГц, в его регистр TIM2_ARR при сбросе заносится 1000 (1мс), а при передаче данных — 100 (100мкс — средняя длительность передачи бита данных). Регистр TIM2_CCR4 при сбросе имеет значение 500, при передаче данных — 10 (передача единицы) или 60 (передача нуля), при приеме данных — 5.
Соответственно, при удачном сбросе генерируемый датчиками нулевой импульс приводит к тому, что в TIM2_CCR3 заносится значение больше 550 (обычно где-то 650). Если же значение меньше этой барьерной величины (т.е. 500), делаем вывод, что датчиков на шине нет.
Передача данных происходит в соответствии с рекомендованными в даташите длительностями импульсов. Прием инициируется короткой (на 5мкс) подтяжкой шины данных к нулю, а дальше уже датчик "дотягивает" импульс до нужного значения (если длительность импульса меньше 10мкс, то передается единица, иначе передается нуль).
Базовый функционал я реализовал на своей китайской макетке с STM32F103RBT6 (собственно, сниппеты из репозитория stm32samples под нее и пишутся). Реализовано: считывание идентификатора датчика с занесением уникальных идентификаторов в буфер (до 8 датчиков); запуск (широковещательный) измерения температуры; опрос всех датчиков с сохраненными в буфере идентификаторами (если датчик только один, посылается широковещательная команда).
Температура вычисляется в десятых долях градуса Цельсия (т.к. DS18S20 позволяет измерять с точностью не лучше ±0.5°C, а DS18B20 — не лучше ±0.125°C). Для различия DS18S20 и DS18B20 я проверяю байт[4] "блокнота" (scratchpad): у DS18S20 он равен 0xFF, а DS18B20 хранит там конфигурационный регистр, старший бит которого всегда равен нулю. В принципе, этим регистром можно изменять разрядность DS18B20, но я пока это не буду реализовывать: все равно нет смысла опрашивать датчики чаще, чем раз в полминуты, так что 750мс будет длиться преобразование или же 93.75мс — безразлично.
Если при опросе N датчиков не было обнаружено определенного, то будет возвращено значение ERR_TEMP_VAL, равное 200000 (что явно выходит за пределы возможностей датчика).
Теперь все это нужно прикрутить к ИК-контроллеру, чтобы иметь возможность замерять "теплые" температуры (скажем, по разности температур стенок криостата и окружающего воздуха можно судить о глубине вакуума в криостате).
P.S. Странно: не могу сделать hg push, Опять какие-то косяки с сосфоржем...
Re: Хмм
Date: 2016-06-02 03:51 pm (UTC)Я использую F4, там с I2C вообще все гладко
Посмотрел эту opencm3, а там больше половины файлов для stm32 просто пустые, даже не смешно. Впрочем, разработчик честно предупреждает: The API of the library is NOT yet considered stable! Please do not rely on it, yet!
Re: Хмм
Date: 2016-06-02 04:58 pm (UTC)Да и наплевать, мне оно не нужно: я эту библиотеку использую по-минимуму. Все равно основную долю работы делаю непосредственно на регистрах — это проще, чем ковыряться в исходниках библиотеки, выискивая, какие же функции надо вызывать для того-сего.
Полным-полно людей в сети пользуются libopencm3 на STM32. Потому что если не она, то придется самому все делать — в т.ч. заголовочные файлы писать (как я для STM8 делал, т.к. вообще ничего нет: sdcc умеет компилять, но никаких готовых заголовочных файлов не предоставляет), а это уж точно не смешно.
А еще opencm3 пользую из-за usb. Возможно, когда-нибудь руки до сети дойдут — там вроде тоже что-то есть.
Re: Хмм
Date: 2016-06-02 05:07 pm (UTC)Странный вывод, чес говоря по API там большой разницы с Кубом не увидел.
Имхо, исключительно дело привычки, даже стиль кода примерно одинаковый.
Re: Хмм
Date: 2016-06-03 05:37 am (UTC)Re: Хмм
Date: 2016-06-03 11:39 am (UTC)