Как стать автором
Обновить

Приемопередатчик HC-12 и датчик температуры DS18b20 на AVR-ассемблере

Время на прочтение26 мин
Количество просмотров17K
Всего голосов 13: ↑10 и ↓3+13
Комментарии28

Комментарии 28

Потренироваться на ассемблере сойдёт. Но в общем-то решения не очень красивые. Отключать модуль обрывая ему землю. Использовать wdt вместо таймера. HC-12 это si4463 + какой-то проц. Наверно stm8. Так что либо avr либо stm8 тут лишние.
Отключать модуль обрывая ему землю
Видимо, этим и объясняется довольно большое потребление в паузах (из за паразитной запитки модуля по цепи Tx MCU):
Измерения показали, что в паузах вся схема потребляет не более 150 мкА

Использовать wdt вместо таймера
— в AVR это типовое решение организации пауз с минимальным потреблением, ведь в этих контроллерах тактированием из программы особо не поуправляешь.

UPD: по поводу «паразитной запитки модуля по цепи Tx MCU» похоже написал не подумавши — ведь в паузе на Tx высокий уровень.
Вы правы, но хочу добавить, что лишний здесь не только AVR, но и DS18b20, так как si4463 имеет встроенный датчик температуры.
И собственно эту возможность я и использовал в своем проекте простейшего беспроводного термодатчика.
Отключать модуль обрывая ему землю.… Использовать wdt вместо таймера.… Так что либо avr либо stm8 тут лишние.

  1. В данном контексте — нормально, ИМХО. И с уровнем пауза на TxD микроконтроллера уже разобрались, так-что утечки вызваны чем-то другим. Вероятно — Z-состоянием порта B (ненастроенным после сброса).
  2. Тем не менее, WDT — тоже таймер.
  3. Это — да. И так было бы даже лучше. Но модули "чистых" трансиверов — вряд-ли продают (дурино-стайл рулит).

Автору хорошо-бы указать, к какому программатору относятся скриншоты Fuse-битов.

Автору хорошо-бы указать, к какому программатору относятся скриншоты Fuse-битов.

AS-2/3/4
За описание конвертации данных термодатчика — отдельная благодарность автору!
Очень помогло, и хорошо, что именно для ассемблера (только на нём и пишу :)))
DS18 — та еще гадость… И уж на аврке с ним работать совсем не интересно. Если на STM32 можно реализовать 1-wire на UART+DMA или на таймере с DMA, то на таких унылых МК приходится дергать прерывания, что лишний раз напрягает и без того слабенькое ядро!
Как по мне, лучше уж взять дешевый NTC, откалибровать и получить ту же точность в ±0.5…1℃, что и у DS18. Зато никакой мороки с ненормальным 1-wire (да и по цене раз в 50 дешевле выходит).
Ну, убогое ядро, которое задергали прерываниями. Ну, ИЗМЕРЕНИЕ температуры на NTC, да еще и с «точностью в ±0.5…1» (в конце концов бывают извращения и похуже, чем вычислять экспоненту в микроконтроллере). Но поинтересуйтесь ценами на проведение калибровки, хотя бы тут (там есть ссылка на прайс). Или вы всерьез думаете достичь «точность в ±0.5…1℃» калибровкой (NTC! ладно еще медного термометра) у себя на кухне?
Какая экспонента? Есть же кусочно-линейная аппроксимация! Понятно, что делить все равно придется, да, это уныло на МК вроде Cortex-M0.
А насчет метрологии тоже не надо тут перегибать! Знаем, проходили неоднократно! Дома при желании можно устроить, но приятней на работе: беру пару литров жидкого азота, заливаю в тазик, ставлю в самодельный термостат рядом с дюралевой пластиной с термодатчиками или стаканом со спиртом, в котором они плавают, — вуаля! За трое-четверо суток выбираю статистику в диапазоне -50…+20℃. Для более высоких температур можно в термостат тазик с кипятком засунуть.

Если что, таким колхозно-кустарным методом была проведена калибровка с точностью в пять сотых градуса!!!1111 См. мой ЖЖ (на быдлохабре я уже давным-давно ничего не выкладываю, ибо метать бисер перед свиньями мне надоело).
Себя то самого можно было и не обманывать. Чтобы иметь 0,05 градуса, нужно образцовое средство измерения как минимум 0,02 градуса. А это эталонный платиновый термометр, к которому еще нужен преобразователь и термостат правильной конструкции, с выравниванием поля температур по объему (за всем этим, например, сюда). С тазиками вы и градуса достоверного не получите, не то, что сотых долей. И не думайте, что я преувеличиваю — у меня, на секунду, 20-летний опыт работы в метрологической лаборатории, как раз по калибовке температурных датчиков.
И плюс к этому — ваша кусочно-линейная аппроксимация. Ознакомившись с типовой характеристикой термистора и немного посчитав, получите, что для приемлемого спрямления в пределах 0,5 градуса нужно обеспечить калибровку не менее, чем через 4 градуса (в середине диапазона, при высоких температурах можно пореже, для низких — почаще). Иначе это будет не калибровка, а теоретические гадания на кофейной гуще.
Понимаете, почему NTC никто всерьез не использует для ИЗМЕРЕНИЯ температуры? В качестве датчика для регулятора — очень часто (из-за высокой крутизны это удобно), а в качестве измерителя разве только для прикола. Даже банальный TMP36 и то намного удобнее. В пределах точности 1-2 градуса, конечно, не больше, но больше вы своей самодельщиной и не добъетесь. А 18b20 из коробки дает 0,5 градуса, без всяких тазиков.
Конечно, все это я не для вас пишу, на хамство обычно я не отвечаю, но кто-нибудь ведь может вам и поверить.
Чукча — не читатель?
У меня складывается впечатление, что аврщик — это диагноз неизлечимого заболевания!

Ваш диагноз — "STM32 головного мозга". И что?

Чем контроллеры то провинились?

Интересная статья, но какую цель Вы всё-же преследовали делая это на ассемблере и attiny2313? Уменьшение затрат? Уменьшение энергопотребления?
10 лет назад коллега реализовал ethernet+ip+udp+snmp+icmp в atmega88 на чистом ассемблере, но тогда это было продиктовано уменьшением стоимости при производстве. В современных реалиях я бы так уже не делал.

Для производства в подобной задаче я бы тоже так не делал (в других задачах, возможно, стал бы, но все зависит от задачи). Здесь речь идет не о производстве, а об одноразовых проектах «для себя» и изучении МК. И расчет был на человека, который хочет
а) изучать электронику, но не хочет вникать в нюансы построения языков высокого уровня, что само по себе требует вдумчивого изучения (и вообще это совсем другая дисциплина, чем электроника, требующая отдельной предрасположенности). И не всегда это просто — вообще говоря, для успешного обращения с МК язык надо знать изнутри не ниже уровнем, чем у грамотного бекенд разработчика «больших» систем. Но постойте, мы же изначально собирались просто цифровой термометр спроектировать, а не компиляторы изучать?
б) досконально владеть функционированием схемы, которую спроектировал и запустил. Которому хочется полностью, до команды в каждый момент времени, владеть спроектированным устройством, а не полагаться на дядю, изобретающего оптиимзирующие компиляторы.
Как-то так. Знаю, что не всегда и не всем такое надо, но я сам терпеть не могу ничего лишнего, не нужного для конкретной задачи. И вдруг кому-то тоже пригодится?
Спасибо за статью, планирую использовать модули HC-12 для радиоуправления.
В связи с этим есть вопросы — вы пробовали увеличить скорость (битрейт)? тогда, вероятно, ток будет потребляться меньшее время. На какую дальность работают модули с заводскими настройками?
1. Не пробовал, время просыпания после включения питания все равно намного больше, чем передача, потому не интересовался
2. Я там упомянул, что на дальность не испытывал, негде пока. Дополню, когда смогу такое испытание провести
3. Вопрос про направление не понял. Это просто UART, он полнодуплексный (ну, почти). Наверно, голой микрухой надо управлять (не вникал, если честно), но вам ничего переключать не требуется, все делает модуль автоматически.
Я именно тот, кто настаивает, что компилятор сделает все не хуже, а лучше, чем обычный разработчик на ассемблере.
И для начала хотел бы заметить, что для взятия абсолютного значения отрицательного целого надо просто написать i=-i; а не заниматься черной магией с использованием «глубоких знаний» в представлении отрицательных чисел в дополнительном коде.

Ну и как вишенка на торте — автором предложен неправильный способ сделать то же самое в ассемблере, что лишний раз подтверждает — не следует применять его там, где чудесно справится С. Те, кто хотят знать, как это сделать, могут посмотреть генерированный компилятором код для приведенной операции.
Вы, сдается, любитель голословных утверждений. Ни код не приводится, ни даже какой именно компилятор и с какими настройками не упоминается, ни в чем именно тут ошибка не указывается. В надежде, наверное, что оппонент проникнется вашим глубоким знанием факта генерирования компилятором некоего кода, и поверит вам на слово, что код этот единственно правильный — его же создал ВЕЛИКИЙ и МОГУЧИЙ КОМПИЛЯТОР ЯЗЫКА С!!!
ЗЫ: и кстати, тогда уж abs(i), не находите?
ЗЫЗЫ: по первому утверждению: смотря какой компилятор и какой разработчик, не кажется?
Ну что же вопрос задан — надо ответить. Я наивно полагал, что человек, обучающий ассемблеру, должен знать, как осуществляется (на низком уровне) изменение знака числа при представлении отрицательных чисел в дополнительном коде. Вы сами верно указали формулу преобразования (~xxxx)+1. Приведенный Вами код
 com valueH ;инвертируем старший байт
 neg valueL ;младший -> доп. код
не соответствует приведенной Вами формуле. Для того, чтобы понять свою ошибку, посмотрите, как преобразуется число 0xFF00 (соответствующее десятичному -256), мне почему то кажется, что 0 — не совсем правильный ответ.
А компилятор совершенно любой, я настоятельно рекомендую пользоваться gcc на сайте godbolt, вот ссылка на указанный код godbolt.org. В Ваших обозначениях должно быть:
 neg valueH
 neg valueL
 sbc valueH,__zero_reg__

Между тем, простое
if (Temp<0) {Temp=-Temp; };
решало все проблемы.
А компилятор языка С не «великий и могучий», а написан людьми, которые о системе команд того же AVR забыли больше, чем мы с Вами вместе знаем, так что он генерит совершенно правильный код.
PS. abs здесь не очень нужен, прямой код в данном случае нагляднее.
PPS. Любой современный компилятор и практически любой разработчик.
GarryC, мой дорогой!

а) не понял, почему в результате моих команд 0xFF00 преобразуется в 0. Правильный ответ — 0x0100, он получается по любой из формул, и по моей, и по вашей последовательности тоже.

б) приведенная вами последовательность neg — neg — sbc делает ровно то же самое, что и просто com — neg, только зачем-то она сначала добавляет единицу к valueH, а затем ее обратно вычитает (флаг C командой NEG устанавливается всегда, кроме случая, когда результат = 0 — цитата из руководства). Вывод — если neg заменить в первой команде на com, то последняя команда оказывается лишней. Как раз пример неудачного и неоптимального кода, не понял, зачем и откуда вы это вытащили. У меня получилось лучше:

в) меня тоже разобрало любопытство, и я путем AVR Studio+GCC смоделировал функцию, возвращающую абсолютное значение 2-байтового числа:

int aaa(void) {
int i;
i=abs(i);
return i;
}


Вот фрагмент дизасемблированного кода, ей сооветствующего:

2: int aaa(void) {
+0000001A: E090 LDI R25,0x00 Load immediate
+0000001B: E080 LDI R24,0x00 Load immediate
+0000001C: 2F29 MOV R18,R25 Copy register
+0000001D: 2F38 MOV R19,R24 Copy register
+0000001E: FF37 SBRS R19,7 Skip if bit in register set
+0000001F: C003 RJMP PC+0x0004 Relative jump
+00000020: 9530 COM R19 One's complement
+00000021: 9521 NEG R18 Two's complement
+00000022: 4F3F SBCI R19,0xFF Subtract immediate with carry
6: }
+00000023: 01C9 MOVW R24,R18 Copy register pair
+00000024: 9508 RET Subroutine return

Как видите, и сама операция и даже определение знака полностью соотвествует моим командам (сам удивился, ни сейчас не мухлевал, ни раньше не интересовался). Вся остальная муть там относится к преобразованию регистров, с чем я бы тоже поспорил — до хрена всего лишнего. Хотя, может, в реальной программе и будет покомпактней, но этот фрагмент я бы сократил вдвое, как минимум (знаю, зачем они так делали, но это как раз пример, как руками лучше получается). Интересно было бы еще отключить оптимизацию, но уже лень возиться. Так что не знаю, откуда вы взяли свой код, но я все делал правильно и кратчайшим путем — зачем, собственно, и нужен ассемблер.

GarryC, я совершенно не собираюсь гнобить ни язык С, ни компилятор, но тем более его создателей. Они сделали замечательную штуку, только не надо ее идеализировать, возводить на пьедестал и объявлять божественной истиной в последней инстанции. В жизни за все надо платить — за удобство и универсальность мы платим неоптимальным кодом. Это закон природы такой — массовый продукт всегда лучше и дешевле именно в массовом порядке, но в каждом конкретном случае руками обязательно можно сделать лучше.

Я уже признал свою неправоту и даже внес исправления в код, см. след. коммент.
Нет, я был неправ. Действительно, 0xFF00 последовательностью команд com- neg преобразуется в ноль, а вашей — в корректные $0100 (и именно вследствие того, что сказано по поводу флага С в руководстве). В примере функции то же самое делает команда SBCI, я был тороплив и невнимателен. Большое спасибо, я вам искренне благодарен, сейчас поправлю код. Но насчет того, что руками можно сделать лучше, чем сделает автомат, я от своих слов не отказываюсь.
Рад, что смогли прийти к единому мнению.

А по поводу кода, порождаемого компилятором — Вы рассмотрели голую функцию abs, если посмотреть на код, порождаемый для конкретного случая, то многие лишние пересылки исчезают — компилятор научился убирать излишнюю универсальность в конкретных условиях.
Например, для кода на С
int main(int16_t i) {
    i=abs(i);
    i=(i*5)/8;
    return i;
};

в режиме оптимизации -О2 получается
main:
        mov r18,r24
        mov r19,r25
        sbrs r19,7
        rjmp .L2
        neg r19
        neg r18
        sbc r19,__zero_reg__
.L2:
        mov r25,r19
        mov r24,r18
        lsl r24
        rol r25
        lsl r24
        rol r25
        add r24,r18
        adc r25,r19
        asr r25
        ror r24
        asr r25
        ror r24
        asr r25
        ror r24
        ret
Обратите внимание, что abs заинлайнилась, что ничуть не хуже написанного руками. Строго говоря, этот фрагмент исполняемого кода полностью совпадает с приведенным Вами, только на С он компактнее и понятнее. «А если разницы нет, зачем платить больше?».

PS. Должен признать, что компилятор не всегда прав, когда я попробовал указать (i*10)/16? он честно добавил еще один сдвиг влево и сразу за ним сдвиг вправо, что меня несколько озадачило. То есть в данном конкретном случае сокращать дробь 10/16 до 5/8 приходится программисту.
Интересное наблюдение по поводу применяемой формулы (i*5)/8, которая для вычислений требует 14 команд и 14 тактов.
Сначала я попробовал переписать ее в стиле i/2+i/8, которая потребовала 10 команд и 10 тактов, но это неверная формула.
А вот (i+i/4)/2 дает совершенно правильный результат и при этом требует все те же 10 команд и 10 тактов.
Если бы это решение нашел компилятор… но нет, его нашел человек, так что Вы отчасти правы, компилятору есть куда развиваться.
Хочу кстати отметить, что в этой задаче обработка производится в приемнике mega8, т.е. там работает аппаратный mul. И можно сделать умножение еще короче, если совсем уж приспосабливать под конкретный случай.

Как то совсем уж сложно и неэффективно. Я делал на attiny13 + простейший передатчик аналоговый за 50 рублей. Во сне — ничего не потребояется, компонентов — 0 (обвеса) только микруха, ds18b20 и мини передатчик 433mhz. Живет от 3в батарейки год, а то и больше. Приемник так же элементарен, делал его даже на esp8266 с трансляцией в сеть wifi данных)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории