Menu
atmega8 ШИМ + Динамик + Сервопривод SG-90

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 мс. 

 servo Схема подклчения и управления сервоприводом.

 

Динамик

ТУт все просто. Скважность не нужна, управляем частотой.

 

Частота, Гц   Суб-конт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