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

Всё быстро воткнуто в макетку, с помощью мастера в 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 проблема отсутствует.
- 0
- 12 мая 2016, 16:40
- AnderWonder
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;
- demiurg1978
- 12 мая 2016, 16:49
- ↓
#define TMR_OCR_RIGHT OCR1B
#define TMR_OCR_LEFT OCR1A
#define TMR_COM_RIGHT COM1B1
#define TMR_COM_LEFT COM1A1
То есть, если ноль, отключаем ШИМ, и отключаем пин выхода ШИМ-а.
- demiurg1978
- 12 мая 2016, 16:54
- ↑
- ↓
Надо заметить, что настройка режимов через регистры не радует интуитивно понятным интерфейсом.В этих таймерах настройка весьма удобная. 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);
Не очень удобно, конечно, что 2-й бит вынесен в TCCRB (т.к. добавлен позднее)собсно, об это и речь
Тогда не получишь полной яркостикак ни кинь — всюду клин
Регистры OCR буферизованы, для одного цикла таймера используется строго одно значение.буферизация наверное только для таймера, выход выставляется, следующее сравнение нас ждет в BOTTOM
А это что за вырвиглазие?эээ… ну это как бы так короче, хотя так понятней. А кстати, любопытно, код генерится одинаковый? Или второй вариант преобразуется буквально
- AnderWonder
- 12 мая 2016, 17:45
- ↑
- ↓
Это будет вычислено на этапе компиляции и совершенно равносильно (даже если смотреть асм). Но читать однозначно понятнее вариант от Lifelover
Ну тогда пардон. Просто привычка со 2-го CodeVision осталась, в новых проектах на 3-м уже будет нормально, там из коробки подобный синтаксис.
- AnderWonder
- 12 мая 2016, 18:06
- ↑
- ↓
как ни кинь — всюду клин255/256 или 256/256 — в большинстве случаев один фиг.
Вообще, из самого принципа работы шима — при 256 ступеньках и диапазоне значения от 0 до 255 можем получить заполнение от 0/256 до 255/256 или от 1/256 до 256/256. А если 256 ступенек не устраивает, но достаточно одного канала шима — можно указать свой TOP. Тут ведь базовый таймер, с самыми простыми и достаточными в большинстве случаев режимами…
Если нужно что-то большее — есть, например, 261-я тинька. Там и отдельный OCR чисто под TOP и ещё очень много интересных вещей.
буферизация наверное только для таймераБуферизация специально для шима. Значение OCR запоминается в BOTTOM и используется в течении цикла.
А кстати, любопытно, код генерится одинаковый?Константные выражения в любом случае сокращаются при компиляции.
255/256 или 256/256 — в большинстве случаев один фиг...Что-то рассинхрон пошёл, если охота продолжать, то давайте вернемся к фразе
проблема тут в том, что 8-битный шим имеет 257 «положений»— что имелось ввиду, какое 257-е положение?
Значение OCR запоминается в BOTTOM и используется в течении циклазначение то может и запоминается, я говорю о моменте, когда выполняется сравнение этого значения — по-видимому это происходит в момент когда режим меняется. Как бы то ни было, эффект был, и исправить его удалось с помощью указанного кода.
- AnderWonder
- 12 мая 2016, 18:34
- ↑
- ↓
что имелось ввиду, какое 257-е положение?Имелось в виду 257 возможных установок — от 0/256 до 256/256. Если установка восьмибитная, то одно из крайних значений придётся выкинуть.
Имелось в виду 257 возможных установок — от 0/256 до 256/256боюсь, вы ошибаетесь — значений в регистре может быть ровно 256 — от 0 до 255
- AnderWonder
- 12 мая 2016, 18:52
- ↑
- ↓
Я о том и говорю. Значений в регистре — 256, а значений заполнения шима — 257. Поэтому одно приходится выкидывать.
Что значит
У меня значение ШИМ хранится в переменной unsigned char, которая тоже может имеет 256 значений. Т.е. все соответствует полностью.
значений заполнения шима?
У меня значение ШИМ хранится в переменной unsigned char, которая тоже может имеет 256 значений. Т.е. все соответствует полностью.
- AnderWonder
- 12 мая 2016, 18:59
- ↑
- ↓
Вы, извиняюсь, однозначно что-то путаете. Как это
В переменную влезает меньшеПеременная 8 бит, так же как и регистр и значений у неё ровно столько же. А причину проблемы я описал в статье — при старте таймера с 0 выход выставляется в 1, по-моему это даже в даташите написано.
- AnderWonder
- 12 мая 2016, 19:13
- ↑
- ↓
вы оперируете какими-то неведомыми понятиями. Что такое и где описано
Вообщем, пусть нас рассудят более опытные и авторитетные товарищи.
количество возможных коэффициентов заполнения, которые может генерить шимИ где написано, что их 257 ???
Вообщем, пусть нас рассудят более опытные и авторитетные товарищи.
- AnderWonder
- 12 мая 2016, 19:26
- ↑
- ↓
Коэффициент заполнения, который генерит шим — это просто коэффициент заполнения, который генерит шим. А то, что для шима с 8-битным счётчиком в принципе возможно 257 различных коэффициентов заполнения — очевидно. Описывать где либо очевидные вещи не обязательно. Но может где-то и написано, я не искал.
Ну вот давайте просто подумаем логически: значений счетчика у нас 256 (с этим вы вроде согласны). Значений коэффициента, как вы говорите 257, т.е. на 1 больше. Тогда одному из значений счетчика, должно соответствовать 2 значения коэффициента. Вопрос: какому значению счетчика? И какие это коэффициенты?
- AnderWonder
- 12 мая 2016, 19:41
- ↑
- ↓
Если это, как вы утверждаете, ШИМ двухбитный — проставьте двухбитные значения каждому из указанных значений ШИМ.
- AnderWonder
- 12 мая 2016, 20:21
- ↑
- ↓
Если брать логику таймера ATTINY — то картинке соответствуют двоичные значение PWM_OFF, 00, 01, 10, 11.
Связано это с тем, что у тактируемого двухбитным счетчиком ШИМа есть 4 таймслота, соответственно, коэффициент заполнения от 0 до 4 таймслотов. Это дает 5 значений. Аналогично с 8-битным ШИМом — 256 таймслотов, заполнение от 0 до 256 таймслотов, итого 257 значений.
Связано это с тем, что у тактируемого двухбитным счетчиком ШИМа есть 4 таймслота, соответственно, коэффициент заполнения от 0 до 4 таймслотов. Это дает 5 значений. Аналогично с 8-битным ШИМом — 256 таймслотов, заполнение от 0 до 256 таймслотов, итого 257 значений.
Значения коэффициента заполнения и не должны соответствовать значениям счётчика. Коэффициент заполнения — это количество шажков в течении которой имеем лог. «1» на выходе, делёное на количество шажков в периоде. Если количество шажков в периоде — 256 по числу состояний счётчика, то коэффициент заполнения может быть от 0/256 до 256/256 плюс промежуточные значения. Всего 257 значений.
Не понимаю вас. Откуда вы взяли какие-то промежуточные значения? Откуда они берутся? Да и это ничего не меняет — все равно каждому значению регистра должно соответствовать какое-то значение ШИМа. И если бы значений ШИМа было больше, то это значило бы, что они берутся из ниоткуда.
- AnderWonder
- 12 мая 2016, 20:28
- ↑
- ↓
Упс, я имел в виду «может быть 0/256, 256/256 плюс промежуточные значения (т.е. 1/256, 2/256 и т.д.)».
все равно каждому значению регистра должно соответствовать какое-то значение ШИМаВозможных заполнений вполне может больше чем состояний регистра, это лишь значит что не все заполнения можно выбрать при данных настройках таймера.
Опять же ошибка: не 0/256,256/256 — а 0/255,255/255. Максимальное значение 8-ми битного регистра и переменной — 255 (что в сумме с 0 дает 256 вариантов). Возможных заполнений вообще в принципе — бесконечное множество, но заполнений которые может сгенерировать конкретный МК, в конкретном случае — именно столько, сколько значений в регистре — и не больше.
- AnderWonder
- 12 мая 2016, 20:56
- ↑
- ↓
Найдите любой конвертер hex->dec (хоть калькулятор в винде) и посмотрите что он выдаст для 0xFF.
- AnderWonder
- 12 мая 2016, 21:14
- ↑
- ↓
Короче, получается так: в МК значению 0 в ШИМ регистре соответствует 1-й коэффициент заполнения на выходе. Т.о. мы получаем 256 уровней через 8 битный регистр с коэффициентами 1/256 — 100%. Что бы получить на выходе 0, нужно отключить ШИМ, т.е. это ещё один уровень, что дает в сумме 257. В итоге мы действительно получаем 257 уровней на выходе — 256 через регистр (1-257 на выходе) и PWM_OFF (0 на выходе).
Таким образом, проблема не в том, что
Таким образом, проблема не в том, что
что 8-битный шим имеет 257 «положений»и не в количестве «ступенек», а в том, что получаемое значения ШИМ сдвинуто на 1 относительно задаваемого через регистр. И поэтому 0 значение приходится получать дополнительным образом.
- AnderWonder
- 13 мая 2016, 09:03
- ↑
- ↓
Дык 257 положений никто и не просил. 257 получается именно из-за косяка, что 0 это 1 у Атмел. Если бы 0 был 0, то было бы 256 и все так чинно, благородно.
- AnderWonder
- 13 мая 2016, 18:35
- ↑
- ↓
А вдруг он поклоняется божеству коричневого кольца?
www.kinopoisk.ru/film/127063/
www.kinopoisk.ru/film/127063/
Очень странное у вас представление о том как «и должно быть». Лично для меня «должно быть» — значит просто, удобно, эффективно: указал 0 — получил 0 и никаких лишних телодвижений с отключением и включением. Но если вы предпочитаете считать баг фичей — ваше право.
- AnderWonder
- 13 мая 2016, 22:14
- ↑
- ↓
Законам физики (ну или математики, в данном случае) немного пофигу, как оно «должно быть» по твоему мнению. Так что приходится смотреть «как оно есть» и подстраиваться под это. Атмел, конечно, мог отмапить значения иначе, имея нулевой D при 0 в регистре, но тогда нельзя было бы получить D=1. Тогда тебе бы для D=1 пришлось отключать PWM и выставлять GPIO в 1.
Никакого противоречия с законами тут нет, полноценный ШИМ можно реализовать на любом количестве уровней, просто будут другие коэффициенты заполнения. На примере с 2-х битным :00 — 0%, 01 — 33%, 10 — 66%, 11 — 100%. Точно так можно сделать и для 8-бит, получив полный 256-уровневый ШИМ.
- AnderWonder
- 14 мая 2016, 10:48
- ↑
- ↓
Если это не двухбитный, тогда какой? Однобитный?)) Вы разрядностью ШИМа считаете количество таймслотов? Зачем? От этого и путаница. Как ни назови, схема которую я описал, как видите, может быть реализована, она проста, понятна и удобна в работе. Почему Atmel сделали по-другому остается вопросом, хотя у меня есть догадки.
- AnderWonder
- 14 мая 2016, 12:31
- ↑
- ↓
Если это не двухбитный, тогда какой? Однобитный?))log2(3), очевидно же. Примерно 1.6 бита.
Цифровые ШИМы по разрядности таймера считаются, сколько видел. А она определяет именно количество таймслотов. И если говорить о AVR — то там однозначно битность определяет количество таймслотов. А значит, покрыть все доступные заполнения регистром той же разрядности — невозможно.
Если разрядностью ШИМ считать количество таймслотов, тогда все понятно, количество состояний на 1 больше. Т.е. в регистре мы указываем не значение скважности, а номер таймслота по сути. Но неудобно же так, однако. В этом плане в Arduina выигрывает — analogWrite(0) дает 0.
- AnderWonder
- 14 мая 2016, 18:15
- ↑
- ↓
Короче, следующие грабли. Яркость регулируется, но вот беда: когда довожу ручку до минимума всё равно подсвечивает. Т.е. на АЦП у нас 0, а на выходе не 0. Обидно, понимаешь.… Вообще говоря проблема известная.
Инверсный режим работы ШИМ не спасет отца русской демократии?
Боюсь, отец русской демократии будет не доволен отсутствием 100% заполнения в таком случае. Но как рабочий вариант годится, согласен.
- AnderWonder
- 13 мая 2016, 12:22
- ↑
- ↓
Движок сообщества позволяет использовать форматирование.
Очень тяжело читать такую статью. Во всяком случае мне :)
Очень тяжело читать такую статью. Во всяком случае мне :)
Полтора года назад столкнулся с такой же фигней Mega88(32).
Устранил так.
ISR(TIMER2_OVF_vect) {
// Сделай проверку в лоб: выставлять 1 только если OCR1A!=0
if (OCR2B != 0) PORTB |= (1 << (PB0)); // это убрать вспышку
}
Устранил так.
ISR(TIMER2_OVF_vect) {
// Сделай проверку в лоб: выставлять 1 только если OCR1A!=0
if (OCR2B != 0) PORTB |= (1 << (PB0)); // это убрать вспышку
}
Кстати насчет вспышки я похоже что-то напутал, помню, что была, помню что боролся и поборол, но вот насчет причины её возникновения я сейчас засомневался. Я потом ещё допиливал инвертный ШИМ — т.е. что бы получить -128..0..+128 — на два светодиода, и теперь запамятовал в каком именно месте она возникала.
- AnderWonder
- 13 мая 2016, 22:25
- ↑
- ↓
Почему она возникает, я не выяснял, времени нет. Осциллограф показывал импульс, даже в PROTEUS моделирует этот всплеск. Списал все на WinAvr. Устранил, и работает. Возникает когда ШИМ должен быть =0. А он, 3W светодиод, светится прилично, что недопустимо в задании. Как то так.
А, стоп, вы это про свечение на 0. Так разобрались, см. коменты выше: если кратко — значению 0 в регистре соответствует — внезапно — 1 на выходе, соотв. 1 -2, 2 — 3 и т.д. Почему? Вопрос к Atmel.
- AnderWonder
- 14 мая 2016, 00:17
- ↑
- ↓
Недавно решая такую задачу (для Tiny45) рассмативал её с такой точки зрения: ШИМ является только переходным процессом между выключением и включением LED. Сначала на выходе 0 и ШИМ выключен, затем включаем ШИМ и наращиваем скважность, пока не будет полное заполнение, затем отключаем ШИМ и полностью включаем LED. Выключение в обратном порядке.
И камней удивило, что для включения ШИМ для канала OC1A нужно установить регистр OC1C в значение 255, хотя по ДШ по умолчанию оно должно быть и так 255.
И камней удивило, что для включения ШИМ для канала OC1A нужно установить регистр OC1C в значение 255, хотя по ДШ по умолчанию оно должно быть и так 255.
затем отключаем ШИМ и полностью включаем LEDвот это лишнее по-моему
- AnderWonder
- 14 мая 2016, 18:16
- ↑
- ↓
Используйте Phase Correct PWM режим. В таком режиме нет проблемы. Принцип работы счетчика в этом режиме реализует 257 вариантов шима
- GoldenAndy
- 15 мая 2016, 15:13
- ↓
Я просто уже столкнулся с этим, когда плавное регулирование освещения делал в коридоре. И, Евстифеев об этом писал, кстати.
- GoldenAndy
- 15 мая 2016, 22:28
- ↑
- ↓
Комментарии (63)
RSS свернуть / развернуть