|
|
  |
Atmega48 6 ШИМ |
|
|
|
May 3 2012, 07:29
|
Группа: Участник
Сообщений: 10
Регистрация: 9-02-11
Пользователь №: 62 817

|
В даташшите на МК Atmega48 написано что у него есть 6 ШИМ. Написал программу для плавного зажигания светодиодов. вот основной кусок Код void pause (unsigned int a) { unsigned int i; for (i=a;i>0;i--); }
void init_pwm (void) {
DDRB=0b1110; DDRD=0b1100000;
TCCR1A=(1<<COM1A1)|(1<<COM1B1)|(1<<WGM10); TCCR1B=(1<<CS10)|(0<<CS11)|(0<<CS12);
TCCR0A=(1<<COM0A1)|(1<<COM0B1)|(1<<WGM00); TCCR0B=(1<<CS00)|(0<<CS01)|(0<<CS02);
OCR1A=0x00; //PB1 OCR1B=0x00; //PB2
OCR0A=0x00; //PD6 OCR0B=0x00; //PD5
} Соответственно получаем 4 ШИМ на выходах PB1, PB2, PD6 и PD5. Просьба знающих людей ответить на следующие вопросы: 1. Как получить еще 2 ШИМ? Пробовал через TCCR2A и TCCR2B как написано в даташите не получатся, и на каких вообще выходах должны быть эти 2 ШИМа? 2. Почему на выходах PB1 и PB2 напряжение плавно нарастает с 0 до 5 В и остается на 5 В, а на выходах PD6 и PD5 плавно нарастает с 0 до 5 В затем скачком сбрасывается до 0 и снова плавно нарастает. Разве они не должны одинаково работать? Спасибо огромное за помощь.
|
|
|
|
|
May 3 2012, 08:06
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (kennykiller @ May 3 2012, 10:29)  вот основной кусок Из этого куска не видно, где и что вы пишете в регистры OCR. QUOTE (kennykiller @ May 3 2012, 10:29)  1. Как получить еще 2 ШИМ? Пробовал через TCCR2A и TCCR2B как написано в даташите не получатся, и на каких вообще выходах должны быть эти 2 ШИМа? Согласно документации - на выводах PB3 и PD3. QUOTE (kennykiller @ May 3 2012, 10:29)  2. Почему на выходах PB1 и PB2 напряжение плавно нарастает с 0 до 5 В и остается на 5 В, а на выходах PD6 и PD5 плавно нарастает с 0 до 5 В затем скачком сбрасывается до 0 и снова плавно нарастает. Разве они не должны одинаково работать? Кода не видно, вероятно вы наращиваете переменную типа int и не учитываете, что OCR1A, OCR1B - 16-битные регистры, а OCR0A, OCR0B, OCR2A, OCR2B - 8-битные, и в них попадает лишь младший байт. Пишите в OCR1AL, OCR1BL - получите одинаковое поведение. P.S. ваша функция pause() перестанет работать как только вы включите оптимизацию. Либо добавьте к параметру a квалификатор volatile, либо используйте функции _delay_ms(), _delay_us() из <util/delay.h> если ваш компилятор - avr-gcc, либо __delay_cycles() из <intrinsics.h>, если ваш компилятор - ИАР.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
May 4 2012, 06:38
|
Группа: Участник
Сообщений: 10
Регистрация: 9-02-11
Пользователь №: 62 817

|
Спасибо огромнейшее, с обоими вопросами разобрался. А теперь насчет: Цитата(Сергей Борщ @ May 3 2012, 12:06)  P.S. ваша функция pause() перестанет работать как только вы включите оптимизацию. Либо добавьте к параметру a квалификатор volatile, либо используйте функции _delay_ms(), _delay_us() из <util/delay.h> если ваш компилятор - avr-gcc, либо __delay_cycles() из <intrinsics.h>, если ваш компилятор - ИАР. компилятор avr-gcc, насчет использования функций _delay_ms(), _delay_us() ясно, а вот насчет добавления volatile можно по подробнее, я просто совсем новичок в Си и в программировании, если не сложно киньте пример кода или напишите что в указанном выше исправить. Заранее спасибо.
|
|
|
|
|
May 4 2012, 08:39
|

Профессионал
    
Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980

|
Вообще volatile "заставляет" компилятор не делать никаких предположений о значении переменных. Т.е. переменная всегда берётся из её места в памяти, даже если она осталась где-то в регистрах. Хотя, если честно, то я вот этого Цитата функция pause() перестанет работать как только вы включите оптимизацию не понимаю. С какой стати? Про _delay_ms(), _delay_us() тоже вставлю свои 5 копеек. У меня они НИКОГДА нормально не работали в WinAVR. Всегда хрен знает что с ними творится. Может тоже "отрыжки" оптимизации влияют - не знаю, не исследовал их. Собственно поэтому я и не люблю этот компилятор. Может я и не прав. Хотите надёжно - делайте на таймерах. В avrLib есть по-моему готовая реализация. PS. В CvAVR delay_ms всегда работает как часы. Не сочтите за рекламу.
Сообщение отредактировал hd44780 - May 4 2012, 08:42
--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса. (с) Мария Эдуарда
|
|
|
|
|
May 4 2012, 08:48
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(hd44780 @ May 4 2012, 11:39)  Вообще volatile "заставляет" компилятор не делать никаких предположений о значении переменных. Т.е. переменная всегда берётся из её места в памяти, даже если она осталась где-то в регистрах. Не только «берется», но и «кладётся», если уж на то пошло. Вообще-то компилятор может выбросить вообще весь код, если от этого не меняется observable behavior программы, извините мне моё обычное занудство с терминологией из стандартов С/С++, а не из надписей на заборах. Холостой цикл его не меняет, так как просто задержка не «замечается» компилятром. Есть еще задержки памяти разного рода, кешей и винчестера — откуда ему об этом всём знать? Может, если переменная ляжет в регион внешнего ОЗУ меги64, к которому несколько тактов ожидания — цикл в разы удлиннится по сравнению с переменной во внутренней памяти, а тот в разы относительно переменной в регистре. Если ему обращать на это внимание, он вообще оптимизировать не сможет. Если компилятор может выкинуть весь код кроме последнего оператора тут: Код float a, b, c; int i; ... i = (int)a; i = b*sqrt(i*i + c*c) + 0.5f; // может я знаю, что этот код выше выполняется гарантированно дольше, чем нужная мне задержка — какого беса он его убрал? i = 0; То почему ему нельзя выбросить пустой цикл — ничего не делающий реального? Ещё тут почитайте. Он не только выбросить ничего не делающий с его точки зрения код имеет право, но и переставлять местами делающий что-то полезное даже с его точки зрения. Так вот volatile говорит компилятору, что он не может строить предположений о «видимости» работы с данной переменной снаружи независимо от её размещения в памяти, регистрах, ... Даже если он работает с ней только в памяти (например, pic16 просто не сильно-то и может держать переменные в регистрах и имеет команду декремента с пропуском прямо на памяти). Цитата(hd44780 @ May 4 2012, 11:39)  Про _delay_ms(), _delay_us() тоже вставлю свои 5 копеек. У меня они НИКОГДА нормально не работали. Всегда хрен знает что с ними творится. Может тоже "отрыжки" оптимизации влияют - не знаю, не исследовал их. Они всегда работали нормально (ну с учётом того, что не учитывали время в прерываниях, так этого никто и не обещал). Просто всегда нужно было включать оптимизацию и им на вход подавать константы, а не переменные, так это вроде всегда было в документации описано.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
May 4 2012, 10:05
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (kennykiller @ May 4 2012, 09:38)  если не сложно киньте пример кода или напишите что в указанном выше исправить. Добавлю к вышесказанному, так сказать, объяснение на пальцах. Ваш код задержки с точки зрения компилятора делает только одно - он делает маленькую быструю программу большой и медленной. Но ведь мы же все хотим, чтобы программы не тормозили, влезали на одну дискетку и работали как можно быстрее? Поэтому компилятор очень старается сделать получаемый из вашего исходника код как можно более быстрым и компактным. А этот ваш кусочек кода, получается, пытается сделать хорошую программу плохой! Ваш случай, конечно, самый вырожденный, но представьте такой код: UBRRL = F_CPU / BAUDRATE / 16; Вы просите компилятор сделать деление в вашей программе. Если обе константы F_CPU и BAUDRATE известны компилятору - он может вставить в вашу программу сразу результат деления и программа не будет тратить на это деление время. Она станет быстрее и короче. И ведь вы в самом деле хотите, чтобы в этом месте в программе был результат, а не процесс деления. Компилятор не обладает телепатией и не может решить, в каких местах программы вы хотите все быстро, а в каких вы хотите ее замедлить намеренно. Поэтому он делает программу как можно более быстрой (и короткой) везде. И везде его действия совпадают с вашими желаниями, кроме случая с задержкой. И чтобы обмануть его в случае с задержкой, надо объявить переменную цикла с квалификатором volatile, который говорит компилятору "делай с этой переменной все так, как написано, даже если ты знаешь, какой результат в конце концов окажется в этой переменной". CODE void pause (unsigned int a) { volatile unsigned int i; for (i=a;i>0;i--) ; } // или более коротко: void pause1 (volatile unsigned int a) { for (;a>0;a--) ; } Т.е. мы указываем ему, что мы действительно хотим чтобы он в эту переменную i записал a, а потом вычитал по единице пока не получится 0. Хотя очевидно, что в конце вычитаний i будет содержать 0. Более того, i не используется, поэтому и ноль-то в ней никому не нужен. Эта функция pause обладает только одним недостатком - время ее работы будет зависеть от уровня оптимизации и версии компилятора. Библиотечные функции _delay_xx() написаны на инлайн-ассемблере (а в новых версиях вшиты во внутренности компилятора) и их время выполнения не зависит он настроек компилятора.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
May 4 2012, 12:01
|
Группа: Участник
Сообщений: 10
Регистрация: 9-02-11
Пользователь №: 62 817

|
Цитата(Сергей Борщ @ May 4 2012, 14:05)  Добавлю к вышесказанному, так сказать, объяснение на пальцах. Ваш код задержки с точки зрения компилятора делает только одно - он делает маленькую быструю программу большой и медленной. Но ведь мы же все хотим, чтобы программы не тормозили, влезали на одну дискетку и работали как можно быстрее? Поэтому компилятор очень старается сделать получаемый из вашего исходника код как можно более быстрым и компактным. А этот ваш кусочек кода, получается, пытается сделать хорошую программу плохой! Ваш случай, конечно, самый вырожденный, но представьте такой код: UBRRL = F_CPU / BAUDRATE / 16; Вы просите компилятор сделать деление в вашей программе. Если обе константы F_CPU и BAUDRATE известны компилятору - он может вставить в вашу программу сразу результат деления и программа не будет тратить на это деление время. Она станет быстрее и короче. И ведь вы в самом деле хотите, чтобы в этом месте в программе был результат, а не процесс деления. Компилятор не обладает телепатией и не может решить, в каких местах программы вы хотите все быстро, а в каких вы хотите ее замедлить намеренно. Поэтому он делает программу как можно более быстрой (и короткой) везде. И везде его действия совпадают с вашими желаниями, кроме случая с задержкой. И чтобы обмануть его в случае с задержкой, надо объявить переменную цикла с квалификатором volatile, который говорит компилятору "делай с этой переменной все так, как написано, даже если ты знаешь, какой результат в конце концов окажется в этой переменной". Код void pause (unsigned int a) { volatile unsigned int i; for (i=a;i>0;i--) ; } // или более коротко: void pause1 (volatile unsigned int a) { for (;a>0;a--) ; } Т.е. мы указываем ему, что мы действительно хотим чтобы он в эту переменную i записал a, а потом вычитал по единице пока не получится 0. Хотя очевидно, что в конце вычитаний i будет содержать 0. Более того, i не используется, поэтому и ноль-то в ней никому не нужен. Эта функция pause обладает только одним недостатком - время ее работы будет зависеть от уровня оптимизации и версии компилятора. Библиотечные функции _delay_xx() написаны на инлайн-ассемблере (а в новых версиях вшиты во внутренности компилятора) и их время выполнения не зависит он настроек компилятора. Сергей Борщ Спасибо огромнейшее. Помог разобраться со всеми проблемами, даже с теми о которых изначально не просил =))) Даже не думал что проблема с оптимизацией так легко решается. Может посоветуешь какой нибудь учебник по программированию AVR на Си? ЗЫ не нашел на этом форуме кнопок аля Спасибо (или +в репу) или здесь таких нету?
Сообщение отредактировал kennykiller - May 4 2012, 12:16
|
|
|
|
|
May 5 2012, 08:24
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
И я ещё маленько про то, что volatile != не_делать_предположения_о_значении_и_брать_всегда_из_памяти. volatile это нечто большее. Имеем код: Код unsigned char i; void foo() { i = 0xFF; PORTB = 0xFF; while (--i) {} PORTB = 0; } Ну так вот если переменная i не объявлена volatile, то компилятор имеет полное право сделать, к примеру, такой код, в котором пожелание «переменная всегда берётся из её места в памяти, даже если она осталась где-то в регистрах» выполнено на 100%: Код foo: ldi r16, 0xFF out PORTB, r16 sts i, r16 ldi r16, 0 out PORTB, r16 loop: lds r16, i dec r16 sts i, r16 brne loop ret Несколько не то, чего ожидали, правда? Но это допустимый код. Допустимо будет сгенерировать и такое: Код unsigned char i; void foo() { i = 0xFF; while (--i) {} PORTB = 0xFF; PORTB = 0; } Все эти три куска с точки зрения компилятора эквивалентны по наблюдаемому поведению. Допустимым будет даже досчитать от 0xFF до 0xAD перед первой записью в PORTB, от 0xAC до 0x0F между записями в порт и от 0x0E до 0 после второй записи. И даже прогнать цикл, пишущий в i числа от 1 до 0xFF по нарастающей и только в конце 0. Конечно, маловероятно, чтобы какой-то компилятор сгенерировал эти коды, он просто выбросит цикл вообще. Но компилятор имеет право переставлять обращения к не- volatile переменным между собой и обращениями к volatile-переменным. А вот менять порядок обращения к volatile-переменным он не имеет права (как раз об этом в той длинной теме на sources.ru по линку в предыдущем моём сообщении). Это слово означает не только то, что значение переменной может измениться «само по себе» ( «не делать никаких предположений о значении переменных»), но и что само обращение как таковое может что-то поменять во «внешнем мире», в том числе как-то повлиять на значение этой или других volatile-переменых (пример — умножитель в MSP430). Кстати, «не делать никаких предположений о значении переменных» не запрещает компилятору выбросить из нескольких идущих подряд записей в порт все, кроме последнего. Запрещают это только возможные «побочные» эффекты самого факта записи.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|