[Из песочницы] Сказ о том как перфекционизм мне контроллер сбрасывал
Задумал я как-то купить йогуртницу. Да такую, чтобы йогурт делала хороший и всегда одного качества. Что для этого нужно? Во-первых, сырье, во-вторых, точная и стабильная температура, в-третьих, настройка времени приготовления. Стал я выбирать и столкнулся со следующей засадой: дешевые йогуртницы оказались нерегулируемые. То есть внутри нагревательный провод, и этот провод, по сути, подключается к сети. Какая температура будет при этом внутри йогуртницы, зависит от рук сборщика, температуры окружающей среды, фаз луны и глубины сна Ктулху (Ктулху фхтагн, кстати).
Само собой меня такая ситуация не устраивала. И еще больше меня напрягала ситуация вокруг йогуртниц, которые, меня по своим функциям и параметрам устраивали. Почему-то производители таких йогуртниц считают, что они поставляют на рынок изделия космической отрасли и цены на такие изделия должны быть соответствующие. Сильные душевные муки в процессе выбора довели меня до того, что я высказал любимой супруге свои возмущения по поводу негуманной ценовой политики производителей йогуртниц и в процессе словесных излияний выдал фразу «Да я сам за пятьсот рублей лучше сделаю», после чего в моей голове что-то щелкнуло…
Это была предыстория. А теперь история. В процессе построения чудесного аппарата я столкнулся с багом. При выводе текущей температуры на дисплей контроллер периодически уходил в ребут. То есть иногда он часами работал нормально, а иногда сбрасывался каждые несколько секунд. Так как это начало происходить после внедрения в прошивку функции опроса датчика ds18b20, то естественно, что за поиском бага я обратился к ней. И ничего не нашел. Стек не срывался, не в свои области памяти функция ничего не писала. В общем, работала, так как ей полагается. Причем отключение этой функции убирало баг, что явно указывало (так мне в тот момент казалось) на виновника торжества. Я подумал что всему виной система синхронизации.
Так как камень у меня маленький (attiny2313a) и таймеров на все примочки ну совершенно не хватает я вышел из положения, написав, диспетчер задач, который получает задачи и требуемое время задержки от функций, ставит их в очередь. Затем после отсчета нужного количества времени передает им управление. Минимальное время задержки я установил в 1 милисекунду. Что позволило мне устанавливать задержки от миллисекунды до минуты. Но если вы взглянете в даташит датчика ds18b20, то увидите, что общение с датчиком температуры иногда длится больше 500 микросекунд (reset и ожидание presence).
А что если в процессе этого в функцию общения влезет прерывание? Будет смещение тайм слота, из-за чего контроллер не сможет прочитать бит или правильно распознать presence. Выход я нашел в использовании синхронизации. Процесс простой. Когда функция опроса датчика видит что ей предстоит длительное общение с датчиком то она настраивает прерывание в регистре Б таймера 1 одновременно с прерыванием «диспетчера времени» которое расположено в регистре А таймера 1. А так как прерывания обрабатываются в порядке строгого соответствия с адресом вектора прерывания в таблице прерываний, то получается, что после процесса синхронизации функция опроса датчика получит управление сразу за прерыванием «диспетчера времени».
Ну и где тут может быть баг? Да везде. Но лично я подумал, что проблема может быть в пересечении прерываний. То есть в случае, когда в процессе выполнения одного прерывания приходит другое. Чистка кода для решения этой проблемы продолжалась до тех пор, пока я не обнаружил, что мои действия привели к тому, что проблема начала возникать даже при неподключенном датчике. Отключив функцию опроса датчика, я убедился, что теперь сбросы контроллера не зависят от ее работы. И тут меня озарила гениальная догадка. Нужно померить напряжение! Мультиметр бодро отчитался, что на контроллере присутствует 4,2 вольта. «Ну конечно «BOD» подумал я. И подключил к плате внешнее питание (до этого я довольствовался питанием от USB). Мультиметр довольно буркнул, что на плате 4,98 вольта. С чувством победы я включил плату, чтобы узреть невероятное. Баг присутствовал! Причем стал еще ядренее. Теперь контроллер сбрасывался каждые пару секунд, а иногда и за доли секунды. Поиски продолжились с удвоенной силой и привели к функции выводящей информацию на экран.
Как вы, наверное, догадываетесь, с ней все было нормально, тем более что после ее написания она была должным образом оттестирована. И, тем не менее, все говорило о том, что мой неуловимый баг кроется именно в ней. Обнаружилось, что баг возникает только при отображении страницы текущей температуры. Страницы отображения мощности, старта, времени бага не вызывали. Не найдя ничего что могло бы вызывать такое поведение я решил отталкиваться от питания. А именно того факта что при нормальном напряжении баг возникал чаще. Снизив напряжение, я обнаружил, что при 3,8 вольта на контроллере, последний работает стабильно. Ситуация рвала все шаблоны. И тут я вспомнил закон Ома. Ток пропорционален напряжению. Всегда? Нет. На светодиодах этот закон не работает. Точнее работает, но со своими прибамбасами.
Пропорциональность тока через диод не такая же, как на обычном резисторе, ибо полупроводниковые приборы в большинстве своем нелинейны. Вспомните хотя бы тиристор или диод зенера (стабилитрон). Вот для сравнения ВАХ светодиода из даташита (черная линия) и ВАХ резистивной нагрузки (красная линия)
Видно, что для резистивной нагрузки закон Ома бог и хозяин. Если напряжение возросло на 10%, то и ток возрастет на те же 10 %. Но светодиоду никто не указ. Если поднять на светодиоде напряжение всего на 10% (с 2 вольт до 2,2 вольт) его ток скаканет на все 100%, то есть в два раза! Но в моем случае главным оказалось то, что увеличение напряжения увеличивало и его просадку в момент включения диода. А таких диодов у меня оказалось 32! Четыре семисегментных индикатора по восемь светодиодов в каждом (семь сегментов и точка). Они у меня подключены не через мультиплексирование, а через сдвиговые регистры потому что:
1. Выводов котроллера attiny2313 катастрофически не хватало.
2. Меня раздражает их мерцание при подключении мультиплексированием(перфекционизм номер раз)
3. На вывод мультиплексированием расходуется много ресурсов контроллера (перфекционизм номер два)
Кроме того я подключил сдвиговые регистры с функцией отключения экрана на время обновления дисплея (перфекционизм номер три). Зачем мне сдалась эта фича — не знаю. Ведь выбранная мною логика может работать вплоть до частоты 100мгц. Соответственно данные в нее можно задвигать на полной частоте работы контроллера, которая у меня равна 10мгц. Ну кто успеет заметить перемещение битов по разрядам индикатора на такой частоте?
Идем дальше. Питание моей схемы обеспечивает регулятор L7805, вот его схема подключения.
Регулятор без проблем обеспечивает ток в 1А. На выходе с регулятора стоит конденсатор 0,1 микрофарад, который, по идее должен сглаживать колебания тока. Его заряд при 5 вольтах равен 0,5 микрокулон. При 3,8 вольта заряд соответственно 0,38 микрокулон. При 3,8 вольтах светодиоды кушают около 288мА, а при 5 вольтах около 416мА. Соответственно при поднятии напряжения с 3,8 вольта до 5 вольт заряд запасаемый конденсатором возрастет на 24 % но потребляемый ток в то же время увеличится на 30 с лишним процентов. Расчеты конечно примерные но показывают что просадка по напряжению в такой схеме тем больше чем больше напряжение питания. При 3,8 вольта просадка не являлась критичной для контроллера. А вот при 5 вольтах просадка увеличилась и начала сбрасывать контроллер. А сброс контроллера именно на странице отображения температуры происходил потому, что именно на этой странице были задействованы все индикаторы дисплея.
Решение оказалось простым. В коде нужно было просто закомментить строчки включения и выключения индикаторов sbi displayPort, offDispWire и cbi displayPort, offDispWire.
ldi XL, videoMem
cbi displayPort, shiftWire
cbi displayPort, storageWire
;sbi displayPort, offDispWire
ZpushToDisplay:
ldi temp2, 8
ld temp, X+
ZnextbitToDisplay:
cbi displayPort, dataWire
sbrc temp, 7
sbi displayPort, dataWire
sbi displayPort, shiftWire
cbi displayPort, shiftWire
lsl temp
dec temp2
cpi temp2, 0
brne ZnextbitToDisplay
cpi XL, videoMem+sizeVideoMem
brne ZpushToDisplay
sbi displayPort, storageWire
cbi displayPort, storageWire
;cbi displayPort, offDispWire
Сбросы прекратились. И жил контроллер долго и счастливо.
З.Ы. процесс поиска неуловимого бага указал на еще один косяк в схеме. Дело в том что максимальная рассеиваемая мощность сдвиговых регистров около 0,5 ватта, а ток через вывод питания или землю около 70 милиампер. Если на дисплее отображается восьмерка с точкой, то ток через сдвиговый регистр должен составить около 104 милиампер, что, как вы понимаете, является превышением. Дело не в том, что я не учел этот момент. Я учел. Но в процессе расчета резистора для светодиода поддался минутной слабости и забыл, что светодиод является нелинейным элементом и для снижения тока через него в два раза недостаточно в два раза увеличить сопротивление токоограничивающего резистора. В общем, в любой непонятной ситуации смотри ВАХ!