atmega8 ШИМ + Динамик + Сервопривод SG-90
Данная статья написана скорее для себя, или как веселый эффектный эксперемент с ШИМ на atmega8.
Причем, в одном примере удалось объединить как изменение частоты импульсов, так и скважность.
Просьба сильно не пинать, так как я только учусь, или, я художник - я так вижу.
Нам потребуйтся:
- 1 - atmega8 на монтажной плате
- 2 - программатор ( USBasp)
- 3 - сервопривод ( SG-90 из китая)
- 4 - Пьезопищалка ( выдрал из сломанного ИБП)
- 5 - комп с установленным софтом для компиляции и заливки в AVR
Немного теории
Широтно-импульсная модуляция (ШИМ, англ. pulse-width modulation (PWM)) — процесс управления мощностью, подводимой к нагрузке, путём изменения скважности импульсов, при постоянной частоте.
Таймер T/C1 устроен сложнее T/C0 и содержит дополнительные регистры.
TCNT1 - счётный регистр
TCCR1A - регистр управления, определяет поведение выводов OC1A и OC1B (биты 1 и 2 порта B) при совпадении значений в регистре с TCNT1A и регистрах сравнения OCR1A/OCR1B, а также для выборов режимов ШИМ
TCCR1B - регистр управления
ICR1 - в этот регистр записывается текущее состояние счётчика при появлении активного входного сигнала на выводе ICP (бит 0 порта B)
OCR1A,OCR1B - содержимое этих регистров постоянно сравнивается с содержимым счётного регистра TCNT1. В случае совпадения выполняются действия, определённые регистром TCCR1A
Регистр TCCR1A
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
COM1A1 | COM1A0 | COM1B1 | COM1B0 | - | - | PWM11 | PWM10 |
Биты COM1A1/COM1A0 и COM1B1/COM1B0 определяют поведение выводов OC1A/OC1B при совпадении значений регистров сравнения OCR1A/OCR1B со значением счётного регистра TCNT1. Возможные состояния битов показаны в таблице (случай для отключённого ШИМ)
COM 1x1 | COM 1x0 | Действие при совпадении |
---|---|---|
0 | 0 | нет реакции |
0 | 1 | Состояние на выходе OC1x меняется на противоположное |
1 | 0 | На выходе OC1x установится лог.0 |
1 | 1 | На выходе OC1x установится лог.1 |
Если в регистре TCCR1A включён широтно-импульсный модулятор, то счётчик Т/C1 работает как суммирующий и вычитающий счётчик, выполняя циклические переходы от значения 0x0000 до значения MAX и обратно до 0x0000. Число MAX рассчитывается по формуле:
MAX=2N-1,
где N - разрешающая способность ШИМ, заданная битами PWM10 и PWM11 регистра TCCR1A.
Частота fшим, с которой повторяются циклы ШИМ рассчитывается по формуле:
fшим=ft/c1/(MAX)
Частота ft/c1 выбирается при помощи битов CS10-CS12 регистра TCCR1B
Регистр TCCR1B
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
ICNC1 | ICES1 | - | - | CTC1 | CS12 | CS11 | CS10 |
Биты 0-2 используются для выбора частоты тактирования (см. таблицу в разделе
Если CTC1=1, то таймер возвращается в состояние 0x0000 по импульсу, следующему после совпадения счётчика и регистра сравнения OCR1A
Регистр ICES1 определяет каким образом должно передаваться состояние счётчика в регистр захвата ICR1 - по нарастающему (ICES1=1) или ниспадающему фронту сигнала (ICES1=0).
Бит ICNC1 активирует подавление помех.
PWM10 | PWM11 | Разрешающая способность | Значение MAX | Частота ШИМ |
---|---|---|---|---|
0 | 0 | Режим ШИМ выключен | ||
0 | 1 | 8 бит | 255 | ft/c1/255 |
1 | 0 | 9 бит | 511 | ft/c1/511 |
1 | 1 | 10 бит | 1023 | ft/c1/1023 |
При включенном режиме ШИМ поведение выходов OC1A/OC1B при совпадении значений в регистрах сравнения и счётном регистре TCNT1 задаётся битами COM1A1/COM1A0 и COM1B1/COM1B0 регистра TCCR1A следующим образом:
COM 1x1 | COM 1x0 | Действие при совпадении |
---|---|---|
0 | 0 | На выходе OC1x нет сигнала |
0 | 1 | На выходе OC1x нет сигнала |
1 | 0 | Неинвертирующий широтно-импульсный модулятор. При суммирующем подсчёте на выводе кстанавливается лог.0, при вычитающем - лог.1 |
1 | 1 | Инвертирующий широтно-импульсный модулятор. При суммирующем подсчёте на выводе кстанавливается лог.1, при вычитающем - лог.0 |
Таймеры и счётчики
Таймеры и счётчики являются одними из самых частоиспользуемых функций микроконтроллеров. Они служат для замера интервалов времени, частоты, определений широт импульсов и так далее. В Atmega8 используются 8 и 16-ти битные счётчики. Переполнение 8 битного счётчика наступает при достижении 255 итераций, 16 битного - при достижении 65535 итераций.
Если таймер работает в режиме счётчика, то он считает количество импульсов, поступивших на выбранный вход микроконтроллера. В этом случае регистр направления DDR порта должен быть настроен на вход.
Если таймер работает в качестве таймера, то частота его тактирования зависит от частоты генератора такта микроконтроллера.
За работу таймеров T/С0, T/C1, T/C2 отвечают регистры TCCR0, TCCR1, TCCR2. Первые 3 бита регистров TCCR0 и TCCR1 отвечают за конфигурацию таймера. Конгфигурация таймера в зависимости от состояния этих трёх регистров представлена в таблице.
Бит2 | Бит1 | Бит0 | Значение комбинации |
0 | 0 | 0 | Таймер/счётчик неактивен |
0 | 0 | 1 | Активен режим "Таймер". Такт таймера равен такту микроконтроллера |
0 | 1 | 0 | Активен режим "Таймер". Такт таймера равен такту микроконтроллера/8 |
0 | 1 | 1 | Активен режим "Таймер". Такт таймера равен такту микроконтроллера/64 |
1 | 0 | 0 | Активен режим "Таймер". Такт таймера равен такту микроконтроллера/256 |
1 | 0 | 1 | Активен режим "Таймер". Такт таймера равен такту микроконтроллера/1024 |
1 | 1 | 0 | Режим "Счётчик". Активный фронт сигнала - ниспадающий |
1 | 1 | 1 | Режим "Счётчик". Активный фронт сигнала - нарастающий |
Если включен режим счётчика, то при каждом импульсе на вход счётчика происходит увеличение на единицу содержимого регистра TCNTx. При изменении состояния счётчика регистра TCNTx c 0xFF на 0x00, в регистре TIFR устанавливается флаг переполнения TOVx
Регистр TIFR
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
TOV1 | OCF1A | OCF1B | - | ICF1 | - | TOV0 | - |
При работе в режиме таймера счётчик считает число тактовых импульсов, сгенерированных таймером. Время генерации тактового импульса зависит от такта микроконтроллера и делителя такта (см. таблицу выше). Например, если тактовая частота микроконтроллера 4 mHz, а делитель такта для таймера равен 8, то время генерации будет равно : 1/500.000Гц=2мкс. Таким образом для переполнения 8-битного счётчика после 256-го импульса нужно 512 мкс. Умножив количество переполнений на время 1 переполнения счётчика можно получить время с начала работы таймера
Сервопривод
Servoдвигатели - это тип электромеханических приводов, которые не вращаются постоянно,как DC / AC или шаговый двигателей, а перемещаются в определенное положение исохраняют его. Мой SG-90 имеет угол 180. Для его управления я использовал частоту 50Hz(20мс) и управляющие импульсы 1-2 мс.
Схема подклчения и управления сервоприводом.
Динамик
ТУт все просто. Скважность не нужна, управляем частотой.
Частота, Гц | Суб-контp- | Контp- | Большая | Малая | 1.00 | 2.00 | 3.00 | 4.00 | 5.00 | |
---|---|---|---|---|---|---|---|---|---|---|
Hота | октава | октава | октава | октава | октава | октава | октава | октава | октава | |
До | C | 32.70 | 65.41 | 130.82 | 261.63 | 523.25 | 1046.50 | 2093.00 | 4186.00 | |
До-диез | C | 34.65 | 69.30 | 138.59 | 277.18 | 554.36 | 1108.70 | 2217.40 | 4434.80 | |
Ре | D | 36.95 | 73.91 | 147.83 | 293.66 | 587.32 | 1174.60 | 2349.20 | 4698.40 | |
Ре-диез | D | 38.88 | 77.78 | 155.56 | 311.13 | 622.26 | 1244.50 | 2489.00 | 4978.00 | |
Ми | E | 20.61 | 41.21 | 82.41 | 164.81 | 329.63 | 659.26 | 1318.50 | 2637.00 | 5274.00 |
Фа | F | 21.82 | 43.65 | 87.31 | 174.62 | 349.23 | 698.46 | 1396.90 | 2793.80 | |
Фа-диез | F | 23.12 | 46.25 | 92.50 | 185.00 | 369.99 | 739.98 | 1480.00 | 2960.00 | |
Соль | G | 24.50 | 49.00 | 98.00 | 196.00 | 392.00 | 784.00 | 1568.00 | 3136.00 | |
Соль-диез | G | 25.95 | 51.90 | 103.80 | 207.00 | 415.30 | 830.60 | 1661.20 | 3332.40 | |
Ля | A | 27.50 | 55.00 | 110.00 | 220.00 | 440.00 | 880.00 | 1720.00 | 3440.00 | |
Си-бемоль | B | 29.13 | 58.26 | 116.54 | 233.08 | 466.16 | 932.32 | 1864.60 | 3729.20 | |
Си | H | 30.87 | 61.74 | 123.48 | 246.96 | 493.88 | 987.75 | 1975.50 | 3951.00 |
Реализация
При использовании одного счетчика можно либо рулить частотой динамика, либо скважностью сервопривода. Одновременно только несколькими счетчиками.
Динамик подлючен на "-" и на PB1(OC1A). Сервопривод управляющим на PB2(OC1B)
Я не использовал кварцевый генератор. Частота задана 1MHz.
Для создания проекта я использовал редактор geany под ubuntu.
Устанавливаем необходимое:
sudo apt-get install gcc-avr avrdude
В редакторе создаем файл servosound.c
#define MAIN_CLK 1000000
#include <avr/io.h>
#include <util/delay.h>
// расчет: Ficr1 = Fcpu / Fnote
// расчет: для II октавы
#define C 1911 // До (3 октава)
#define Cis 1804 // До диез
#define D 1703 // Ре
#define Dis 1607 // Ре диез
#define E 1517 // Ми 6066
#define F 1432 // Фа
#define Fis 1351 // Фа диез
#define G 1276 // Соль
#define Gis 1204 // Соль диез
#define A 1136 // Ля
#define Ais 1073 // Ля диез
#define B 1012 // Си
#define LE32 1*3 // 1/32 Длительность тона
#define LE16 2*3 // 1/16
#define LE16D 3*3
#define LE16T 2*2
#define LE8 4*3 // 1/8
#define LE8D 6*3
#define LE8T 4*2
#define LE4 8*3 // 1/4
#define LE4D 12*3
#define LE4T 8*2
#define LE2 16*3 // 1/2
#define LE2D 24*3
#define LE1 32*3 // 1
#define deg0 43 //0 degree
#define deg45 28 //45 degree
#define deg90 20 //90 degree
#define deg135 14 //135 degree
#define deg180 8 //180 degree
void Delay_us(unsigned char time_us) // функция задержки в us
{ register unsigned char i;
for(i = 0; i < time_us; i++) // 4 цикла
{ asm (" PUSH R0 "); // 2 цикла
asm (" POP R0 "); // 2 цикла
// 8 циклов = 1 us для 8MHz
}
}
void Delay_ms(unsigned int time_ms) // функция задержки в ms
{ register unsigned int i;
for(i = 0; i < time_ms; i++)
{ Delay_us(250);
Delay_us(250);
Delay_us(250);
Delay_us(250);
}
}
unsigned char temp;
unsigned int note;
void Set_temp(unsigned char number) // функция установки темпа и паузы
{
temp = number; // установка темпа
TCCR1A = (1 << COM1A0)| // CTC режим, использование OC1A
(0 << COM1B1)|(0 << COM1B0); // отключаем канал B
TCCR1B = (1 << WGM13)|(1 << WGM12); // звук выключен
}
void Play_LE(unsigned int sound, unsigned int oct, unsigned int LE) // функция проигрывания ноты
{ register unsigned int i;
note = sound;
if(oct>2) { for(i = 2; i <= oct; i++) { note = round(note/2); }}
if(oct<2) { for(i = 2; i >= oct; i--) { note = note*2; }}
ICR1 = note; // установка ICR1
TCNT1 = 0x0000; // очистка Timer/Counter1
TCCR1B |= (1 << WGM13)|(1 << WGM12) // CTC режим
// |(1 << CS10); // предделитель = 1
|(0 << CS12)|(0 << CS11)|(1 << CS10);
Delay_ms(LE*temp*7); // длительность ноты
TCCR1B = (1 << WGM13)|(1 << WGM12); // звук выключен
}
void servo(unsigned int degree) // функция поворота сервопривода
{
TCCR1A |= (0 << COM1A1)|(0 << COM1A0)| // Установим биты COM1B1-COM1B0:0b10, означает сброс вывода канала B при сравнении, канал A отключаем
(1 << COM1B1)|(0 << COM1B0)
|(1 << WGM11)|(0 << WGM10); // Установим биты WGM13-10:0b1110, согласно таблице это
TCCR1B |= (1 << WGM13)|(1 << WGM12) // будет режим - FAST PWM, где верхний предел счета задается битом ICR1
|(0 << CS12)|(1 << CS11)|(1 << CS10); // Битами CS12-10:0b001 задаем источник тактового сигнала для таймера МК, предделитель 64 >
TCNT1 = 0x00; // начальная установка счетчика
ICR1 = 313; // задаем период ШИМ
// по формуле fPWM=fclk_I/O/N*(1+ICR1)// вычисляем частоту ШИМ
if(degree<=180)
{
// 45 - 0 degree
// 26 - 90 degree
// 7 - 180 degree
// OCR1B=round(45-((45-7)*degree/180)); // как-бы не так, зависимость нелинейная
OCR1B=degree;
_delay_ms(300);
}
Set_temp(1); // готовим все для звука
}
int main(void)
{
DDRB |= (1 << PB1)|(1 << PB2);
PORTB = 0x00;
OCR1A=0;
Delay_ms(50);
while(1)
{
Set_temp(1); // установка темпа
// чижик-пыжик
Play_LE(A,2,LE16);
Play_LE(E,2,LE16);
Play_LE(A,2,LE16);
Play_LE(E,2,LE16);
Play_LE(A,2,LE16);
Play_LE(Gis,2,LE16);
Play_LE(Gis,2,LE16);
_delay_ms(100);
servo(deg45);
_delay_ms(100);
Set_temp(1);
Play_LE(Gis,2,LE16);
Play_LE(E,2,LE16);
Play_LE(Gis,2,LE16);
Play_LE(E,2,LE16);
Play_LE(Gis,2,LE16);
Play_LE(A,2,LE16);
Play_LE(A,2,LE16);
servo(deg180);
_delay_ms(500);
servo(deg90);
// имперский марш
Set_temp(1);
Play_LE(G,1,LE16); servo(deg90);
Play_LE(G,1,LE16); servo(deg45);
Play_LE(G,1,LE16); servo(deg90);
Play_LE(Dis,1,LE16);
Play_LE(Ais,1,LE16);
Play_LE(G,1,LE16); servo(deg90);
Play_LE(Dis,1,LE16);
Play_LE(Ais,1,LE16);
Play_LE(G,1,LE16); servo(deg45);
servo(deg90);
Play_LE(D,2,LE16); servo(deg90);
Play_LE(D,2,LE16); servo(deg45);
Play_LE(D,2,LE16); servo(deg90);
Play_LE(Dis,2,LE16);
Play_LE(Ais,1,LE16);
Play_LE(Fis,1,LE16); servo(deg45);
Play_LE(Dis,1,LE16);
Play_LE(Ais,1,LE16);
Play_LE(G,1,LE16); servo(deg180);
_delay_ms(2000);
}
}
Подклчаем USBasp и в директории с проектом выполняем:
avr-gcc -mmcu=atmega8 -I. -gdwarf-2 -DF_CPU =1000000UL -Os -o ./servosound.o ./servosound.c
avr-objcopy -O ihex ./servosound.o ./servosound.hex
avrdude -p m8 -c usbasp -U flash:w:servosound.hex
Весело подмигнув, прошивка влетает в атмегу.
Основной принцип сего состоит в том, что счетчик тикает. Режимы для OCR1A и OCR1B меняются.
Как видно из кода, появилась неприяность в виде нелинейности графика зависимости OCR1B от угла поворота.
Пример работы (Извиняюсь за качество )
использовано
http://avr.ru/ready/contr/agent/servo
http://www.atmega8.ru/wiki/view/doc.24.html
http://radioparty.ru/prog-avr/program-c/284-lesson12-music
Ilya L. / 2016