Перипетии нуба с ШИМом на Attiny13

Возникла давеча простенькая задачка. Нужен был регулируемый вручную ШИМ для теста одной идейки. Под рукой тинька 13 — т.е. цепляем переменник на АЦП и выводим что нам нужно. Казалось бы — проще только светодиодом помигать. Однако. Смеркалось.
Всё быстро воткнуто в макетку, с помощью мастера в CodeVision несколькими щелчками собран проект. Для теста на выход — светодиод. Включаю: горит на полную, кручу резистор — реакции нет. Шустро проверяю все соединения, питание, схему, прошивку — эффект остается. После трехкратного повторения тех же действий возникло недоумение. Беру другую тиньку, прошиваю, включаю — болт. Похоже проблема не в чипе. Некоторое время созерцаю код сгенерированный CodeVision — придраться не к чему. Открываю datasheet, вникаю. Изучил ADC, проверил — всё по канонам. Перешёл к ШИМ.
Краткое описание устройства аппаратного ШИМ на Attiny13 для начинающих:
ШИМ сделан на базе таймера. Т.е. у таймера есть несколько режимов работы, два из них относятся к ШИМу (FastPWM и Phase Correct PWM). Таймер настраивается с помощью двух регистров: TCCR0A,TCCR0B. В них задается режим, частота (делитель), какие каналы используются (есть два — 0A и 0B), режим работы выхода(прямой, инвертированный). Значение ШИМа задаются в регистрах OCR0A и OCR0B — соответственно для каждого канала. Есть ещё у ШИМа такая настройка — чем определяется максимальное значение таймера(TOP), при достижении которого он сбрасывается и бежит с начала — это может быть либо 0xFF, либо значение в регистре OCR0A. У меня был установлен второй режим и значение ШИМа я задавал в регистре же OCR0A.
Немало времени ушло у меня пока я нашёл свою ошибку и ещё больше пока догнал её смысл. Хотя сейчас всё кажется очевидным. Для тех кто, как и я, в танке — TOP должен быть 0xFF. Надо заметить, что настройка режимов через регистры не радует интуитивно понятным интерфейсом. Так вышеозначенный режим определяется битами WGM02:0, два из которых находятся в регистре TCCR0A(00,01), а третий(02) в TCCR0B. Правда мастер CodeVision при начальной настройке здесь наше всё, но когда нужно что-то подправить уже в процессе вот тут-то и приходится поднапрячься.
Короче, следующие грабли. Яркость регулируется, но вот беда: когда довожу ручку до минимума всё равно подсвечивает. Т.е. на АЦП у нас 0, а на выходе не 0. Обидно, понимаешь. Причина такого поведения в том, что в тот момент, когда таймер сбрасывается в 0, на выходе чип выставляет 1, и хотя значение ШИМа у нас задано 0, и уже в следующем такте он это видит и обнуляет выход, но вот этого несчастного скачка достаточно что бы светодиодик светил. Дискомфорт — ты ждешь на выходе чистый 0, а тут тебе гребеночка такая. Вообще говоря проблема известная. Решение приходит в голову практически сразу: когда меняем значение ШИМа, добавляем проверочку на 0 — при оном отключаем ШИМ совсем. Воникает вопрос: как отключать? Можно останавливать таймер. Не лучший вариант: а вдруг на этот таймер что-нибудь ещё посажено? Второй ШИМ, например, или прерывания, или отсчет времени до взрыва? Можно отключать выход таймера — это уже получше, и просто и понятно, получается примерно так:
if(OCR0A==0)TCCR0A&=0x3F;
else TCCR0A=0x83;

Как вариант можно менять режима работы самого пина выход/вход.
P.S.
Из комментариев к статье были получены ещё такие решения:
1. Если не принципиально получать 100% заполнения ШИМ, то можно использовать инверсный режим работы выхода;
2. При работе ШИМ в режиме Phase Correct PWM проблема отсутствует.

Комментарии (63)

RSS свернуть / развернуть

void Set_Proc_Motor_Stop (void)
{
   clr_bit (P_CH_RIGHT_DDR, P_CH_RIGHT);
   clr_bit (P_CH_RIGHT_PORT, P_CH_RIGHT);

   clr_bit (P_CH_LEFT_DDR, P_CH_LEFT);
   clr_bit (P_CH_LEFT_PORT, P_CH_LEFT);

   clr_bit (PWM_RIGHT_DDR, PWM_RIGHT);
   clr_bit (PWM_LEFT_DDR,  PWM_LEFT);

   clr_bit (TCCR1A, TMR_COM_RIGHT);
   TMR_OCR_RIGHT = 0;

   clr_bit (TCCR1A, TMR_COM_LEFT);
   TMR_OCR_LEFT = 0;

   all_leds_off ();
   direction = DIRECTION_STOP;
   motor_mode = MOTOR_STOP;
   _proc_motor = PROC_MOTOR_IDLE;
}

      case PROC_MOTOR_BRAKING: // Торможение.
         if (handle_soft_timer (ST_PROC_MOTOR))
         {
            switch (direction)
            {
               case DIRECTION_RIGHT:
                  if (TMR_OCR_RIGHT > 0)
                     TMR_OCR_RIGHT--;
                  else
                     Set_Proc_Motor_Stop ();
                  break;

               case DIRECTION_LEFT:
                  if (TMR_OCR_LEFT > 0)
                     TMR_OCR_LEFT--;
                  else
                     Set_Proc_Motor_Stop ();
                  break;
            }
         }
         break;
0

#define TMR_OCR_RIGHT   OCR1B
#define TMR_OCR_LEFT    OCR1A

#define TMR_COM_RIGHT   COM1B1
#define TMR_COM_LEFT    COM1A1


То есть, если ноль, отключаем ШИМ, и отключаем пин выхода ШИМ-а.
0
Надо заметить, что настройка режимов через регистры не радует интуитивно понятным интерфейсом.
В этих таймерах настройка весьма удобная. 0-й бит включает ШИМ, 1-й — включает быстрый ШИМ, 2-й означает что верхнее значение указано в OCRA. Не очень удобно, конечно, что 2-й бит вынесен в TCCRB (т.к. добавлен позднее), но он и нужен нечасто.

Вообще говоря проблема известная. Решение приходит в голову практически сразу
Можно ещё использовать инвертирующий режим и загружать в OCR 255-n вместо n. Тогда не получишь полной яркости. Вообще, проблема тут в том, что 8-битный шим имеет 257 «положений».

Регистры OCR буферизованы, для одного цикла таймера используется строго одно значение.

TCCR0A&=0x3F
А это что за вырвиглазие? Чтобы сбросить биты, пишешь TCCR0A &= ~((1<<COM0A1)|(1<<COM0B1));, чтобы вернуть — TCCR0A |= (1<<COM0A1)|(1<<COM0B1);
+1
Не очень удобно, конечно, что 2-й бит вынесен в TCCRB (т.к. добавлен позднее)
собсно, об это и речь
Тогда не получишь полной яркости
как ни кинь — всюду клин
Регистры OCR буферизованы, для одного цикла таймера используется строго одно значение.
буферизация наверное только для таймера, выход выставляется, следующее сравнение нас ждет в BOTTOM
А это что за вырвиглазие?
эээ… ну это как бы так короче, хотя так понятней. А кстати, любопытно, код генерится одинаковый? Или второй вариант преобразуется буквально
0
Это будет вычислено на этапе компиляции и совершенно равносильно (даже если смотреть асм). Но читать однозначно понятнее вариант от Lifelover
0
Ну тогда пардон. Просто привычка со 2-го CodeVision осталась, в новых проектах на 3-м уже будет нормально, там из коробки подобный синтаксис.
0
Никакого отношения к CodeVision или чему-то типа IAR или KEIL тут нет=) Только синтаксис С.
0
Хотя если такое генерит сам CodeVision...=)
0
Мастер CodeVision во второй версии генерил код инициализации в таком духе, в 3-й версии переделали.
0
как ни кинь — всюду клин
255/256 или 256/256 — в большинстве случаев один фиг.
Вообще, из самого принципа работы шима — при 256 ступеньках и диапазоне значения от 0 до 255 можем получить заполнение от 0/256 до 255/256 или от 1/256 до 256/256. А если 256 ступенек не устраивает, но достаточно одного канала шима — можно указать свой TOP. Тут ведь базовый таймер, с самыми простыми и достаточными в большинстве случаев режимами…
Если нужно что-то большее — есть, например, 261-я тинька. Там и отдельный OCR чисто под TOP и ещё очень много интересных вещей.

буферизация наверное только для таймера
Буферизация специально для шима. Значение OCR запоминается в BOTTOM и используется в течении цикла.

А кстати, любопытно, код генерится одинаковый?
Константные выражения в любом случае сокращаются при компиляции.
0
255/256 или 256/256 — в большинстве случаев один фиг...
Что-то рассинхрон пошёл, если охота продолжать, то давайте вернемся к фразе
проблема тут в том, что 8-битный шим имеет 257 «положений»
— что имелось ввиду, какое 257-е положение?
Значение OCR запоминается в BOTTOM и используется в течении цикла
значение то может и запоминается, я говорю о моменте, когда выполняется сравнение этого значения — по-видимому это происходит в момент когда режим меняется. Как бы то ни было, эффект был, и исправить его удалось с помощью указанного кода.
0
что имелось ввиду, какое 257-е положение?
Имелось в виду 257 возможных установок — от 0/256 до 256/256. Если установка восьмибитная, то одно из крайних значений придётся выкинуть.
0
Имелось в виду 257 возможных установок — от 0/256 до 256/256
боюсь, вы ошибаетесь — значений в регистре может быть ровно 256 — от 0 до 255
0
Я о том и говорю. Значений в регистре — 256, а значений заполнения шима — 257. Поэтому одно приходится выкидывать.
0
Что значит
значений заполнения шима
?
У меня значение ШИМ хранится в переменной unsigned char, которая тоже может имеет 256 значений. Т.е. все соответствует полностью.
0
Значит возможных в принципе значений. В переменную влезает меньше, поэтому используются не все возможные. В зависимости от режима 0/256 или 256/256 выкидывается.
0
Вы, извиняюсь, однозначно что-то путаете. Как это
В переменную влезает меньше
Переменная 8 бит, так же как и регистр и значений у неё ровно столько же. А причину проблемы я описал в статье — при старте таймера с 0 выход выставляется в 1, по-моему это даже в даташите написано.
0
Нет, это ты путаешь количество значений счётчика (их 256) и количество возможных коэффициентов заполнения, которые может генерить шим (их 257). Даташит я читал, там всё очень хорошо описано.
0
вы оперируете какими-то неведомыми понятиями. Что такое и где описано
количество возможных коэффициентов заполнения, которые может генерить шим
И где написано, что их 257 ???
Вообщем, пусть нас рассудят более опытные и авторитетные товарищи.
0
Коэффициент заполнения, который генерит шим — это просто коэффициент заполнения, который генерит шим. А то, что для шима с 8-битным счётчиком в принципе возможно 257 различных коэффициентов заполнения — очевидно. Описывать где либо очевидные вещи не обязательно. Но может где-то и написано, я не искал.
0
Ну вот давайте просто подумаем логически: значений счетчика у нас 256 (с этим вы вроде согласны). Значений коэффициента, как вы говорите 257, т.е. на 1 больше. Тогда одному из значений счетчика, должно соответствовать 2 значения коэффициента. Вопрос: какому значению счетчика? И какие это коэффициенты?
0
Для наглядности, варианты заполнения двухбитного шим:
▁▁▁▁▁▁▁▁▁ 0%
▁▇▁▁▁▇▁▁▁ 25%
▁▇▇▁▁▇▇▁▁ 50%
▁▇▇▇▁▇▇▇▁ 75%
▇▇▇▇▇▇▇▇▇ 100%
Их не четыре, а пять.
0
Если это, как вы утверждаете, ШИМ двухбитный — проставьте двухбитные значения каждому из указанных значений ШИМ.
0
Если брать логику таймера ATTINY — то картинке соответствуют двоичные значение PWM_OFF, 00, 01, 10, 11.
Связано это с тем, что у тактируемого двухбитным счетчиком ШИМа есть 4 таймслота, соответственно, коэффициент заполнения от 0 до 4 таймслотов. Это дает 5 значений. Аналогично с 8-битным ШИМом — 256 таймслотов, заполнение от 0 до 256 таймслотов, итого 257 значений.
0
Значения коэффициента заполнения и не должны соответствовать значениям счётчика. Коэффициент заполнения — это количество шажков в течении которой имеем лог. «1» на выходе, делёное на количество шажков в периоде. Если количество шажков в периоде — 256 по числу состояний счётчика, то коэффициент заполнения может быть от 0/256 до 256/256 плюс промежуточные значения. Всего 257 значений.
0
Не понимаю вас. Откуда вы взяли какие-то промежуточные значения? Откуда они берутся? Да и это ничего не меняет — все равно каждому значению регистра должно соответствовать какое-то значение ШИМа. И если бы значений ШИМа было больше, то это значило бы, что они берутся из ниоткуда.
0
Упс, я имел в виду «может быть 0/256, 256/256 плюс промежуточные значения (т.е. 1/256, 2/256 и т.д.)».
все равно каждому значению регистра должно соответствовать какое-то значение ШИМа
Возможных заполнений вполне может больше чем состояний регистра, это лишь значит что не все заполнения можно выбрать при данных настройках таймера.
0
Опять же ошибка: не 0/256,256/256 — а 0/255,255/255. Максимальное значение 8-ми битного регистра и переменной — 255 (что в сумме с 0 дает 256 вариантов). Возможных заполнений вообще в принципе — бесконечное множество, но заполнений которые может сгенерировать конкретный МК, в конкретном случае — именно столько, сколько значений в регистре — и не больше.
0
Возможных в принципе заполнений шима при TOP=0xFF — ровно 257, от 0/256 до 256/256 и никак иначе. 8-битный регистр в зависимости от настроек каким-либо образом маппися на заполнения от 0/256 до 255/256 или от 1/256 до 256/256. От 0/255 до 255/255 было бы при TOP=0xFE, т.е. WGM2:0=111 и OCRA = 0xFE.
0
Найдите любой конвертер hex->dec (хоть калькулятор в винде) и посмотрите что он выдаст для 0xFF.
0
Вообщем, пусть нас рассудят более опытные и авторитетные товарищи.
Этот товарищ опытный. Что до авторитетности — воооон там, в разделе «Люди» он занимает четвертое место.
0
Короче, получается так: в МК значению 0 в ШИМ регистре соответствует 1-й коэффициент заполнения на выходе. Т.о. мы получаем 256 уровней через 8 битный регистр с коэффициентами 1/256 — 100%. Что бы получить на выходе 0, нужно отключить ШИМ, т.е. это ещё один уровень, что дает в сумме 257. В итоге мы действительно получаем 257 уровней на выходе — 256 через регистр (1-257 на выходе) и PWM_OFF (0 на выходе).
Таким образом, проблема не в том, что
что 8-битный шим имеет 257 «положений»
и не в количестве «ступенек», а в том, что получаемое значения ШИМ сдвинуто на 1 относительно задаваемого через регистр. И поэтому 0 значение приходится получать дополнительным образом.
0
Начало — правильно. Продолжение — проблема именно в том, что 257 положений через 8-битный регистр ты никак не задашь. Приходится от одного отказываться. Атмел отказались от нуля, который можно реализовать отключением ШИМ вообще.
0
Дык 257 положений никто и не просил. 257 получается именно из-за косяка, что 0 это 1 у Атмел. Если бы 0 был 0, то было бы 256 и все так чинно, благородно.
0
Тебе уже 20 сообщений подряд толкуют, что это не косяк, а так и должно быть. Как говорится, «если ты нашел баг в компиляторе — ищи ошибку в своем коде».
P.S. Ждем статью «перипетии нуба с кольцевым буфером», где выяснится, что с кольцевыми буферами такая же проблема.
0
А вдруг он поклоняется божеству коричневого кольца?
www.kinopoisk.ru/film/127063/
0
Мсье знает толк…
0
Очень странное у вас представление о том как «и должно быть». Лично для меня «должно быть» — значит просто, удобно, эффективно: указал 0 — получил 0 и никаких лишних телодвижений с отключением и включением. Но если вы предпочитаете считать баг фичей — ваше право.
0
Законам физики (ну или математики, в данном случае) немного пофигу, как оно «должно быть» по твоему мнению. Так что приходится смотреть «как оно есть» и подстраиваться под это. Атмел, конечно, мог отмапить значения иначе, имея нулевой D при 0 в регистре, но тогда нельзя было бы получить D=1. Тогда тебе бы для D=1 пришлось отключать PWM и выставлять GPIO в 1.
0
Никакого противоречия с законами тут нет, полноценный ШИМ можно реализовать на любом количестве уровней, просто будут другие коэффициенты заполнения. На примере с 2-х битным :00 — 0%, 01 — 33%, 10 — 66%, 11 — 100%. Точно так можно сделать и для 8-бит, получив полный 256-уровневый ШИМ.
0
Это уже не двухбитный шим, так как в нем всего 3 таймслота.
0
Если это не двухбитный, тогда какой? Однобитный?)) Вы разрядностью ШИМа считаете количество таймслотов? Зачем? От этого и путаница. Как ни назови, схема которую я описал, как видите, может быть реализована, она проста, понятна и удобна в работе. Почему Atmel сделали по-другому остается вопросом, хотя у меня есть догадки.
0
Если это не двухбитный, тогда какой? Однобитный?))
log2(3), очевидно же. Примерно 1.6 бита.

Цифровые ШИМы по разрядности таймера считаются, сколько видел. А она определяет именно количество таймслотов. И если говорить о AVR — то там однозначно битность определяет количество таймслотов. А значит, покрыть все доступные заполнения регистром той же разрядности — невозможно.
0
Если разрядностью ШИМ считать количество таймслотов, тогда все понятно, количество состояний на 1 больше. Т.е. в регистре мы указываем не значение скважности, а номер таймслота по сути. Но неудобно же так, однако. В этом плане в Arduina выигрывает — analogWrite(0) дает 0.
0
В нормальном МК сделано так как должно быть, а не так как кажется «удобно» нубам. За то AVR и ценим.
0
Та вот не пофиг? при таком заполнении шима на выходе дросселя всё равно 0 будет.
0
Короче, следующие грабли. Яркость регулируется, но вот беда: когда довожу ручку до минимума всё равно подсвечивает. Т.е. на АЦП у нас 0, а на выходе не 0. Обидно, понимаешь.… Вообще говоря проблема известная.

Инверсный режим работы ШИМ не спасет отца русской демократии?
0
Боюсь, отец русской демократии будет не доволен отсутствием 100% заполнения в таком случае. Но как рабочий вариант годится, согласен.
0
Движок сообщества позволяет использовать форматирование.
Очень тяжело читать такую статью. Во всяком случае мне :)
0
О, спасибо за подсказку, учту.
0
Полтора года назад столкнулся с такой же фигней Mega88(32).
Устранил так.
ISR(TIMER2_OVF_vect) {
// Сделай проверку в лоб: выставлять 1 только если OCR1A!=0
if (OCR2B != 0) PORTB |= (1 << (PB0)); // это убрать вспышку
}
0
Кстати насчет вспышки я похоже что-то напутал, помню, что была, помню что боролся и поборол, но вот насчет причины её возникновения я сейчас засомневался. Я потом ещё допиливал инвертный ШИМ — т.е. что бы получить -128..0..+128 — на два светодиода, и теперь запамятовал в каком именно месте она возникала.
0
Почему она возникает, я не выяснял, времени нет. Осциллограф показывал импульс, даже в PROTEUS моделирует этот всплеск. Списал все на WinAvr. Устранил, и работает. Возникает когда ШИМ должен быть =0. А он, 3W светодиод, светится прилично, что недопустимо в задании. Как то так.
0
А, стоп, вы это про свечение на 0. Так разобрались, см. коменты выше: если кратко — значению 0 в регистре соответствует — внезапно — 1 на выходе, соотв. 1 -2, 2 — 3 и т.д. Почему? Вопрос к Atmel.
0
Ага там при 0 он переключается. Надо просто учесть это при разработке. другого контроллера всё равно нет :)
0
Недавно решая такую задачу (для Tiny45) рассмативал её с такой точки зрения: ШИМ является только переходным процессом между выключением и включением LED. Сначала на выходе 0 и ШИМ выключен, затем включаем ШИМ и наращиваем скважность, пока не будет полное заполнение, затем отключаем ШИМ и полностью включаем LED. Выключение в обратном порядке.
И камней удивило, что для включения ШИМ для канала OC1A нужно установить регистр OC1C в значение 255, хотя по ДШ по умолчанию оно должно быть и так 255.
0
затем отключаем ШИМ и полностью включаем LED
вот это лишнее по-моему
0
А осцилографом смотрел? А то я свой никак не доделаю, думаю там будет не полное заполнение.
0
У себя смотрел, заполнение полное, чистая 1 на выходе.
0
Используйте Phase Correct PWM режим. В таком режиме нет проблемы. Принцип работы счетчика в этом режиме реализует 257 вариантов шима
0
Кстати, да, однако.
0
Я просто уже столкнулся с этим, когда плавное регулирование освещения делал в коридоре. И, Евстифеев об этом писал, кстати.
0
Кстати, не 257, а 256 или 255 как ни считай.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.