PWM

Seznámení s ovládáním servomotorů (PWM signál)
Řešené příklady :
•    Převeďte signál s otočení potenciometru přes AD převodník na otočení servomechanismu.

Zadání

Příklad 1
Pro ověření funkce otočte servomotor na polohu 0° po 1s na 90°,  po 1s na 180°.
Příklad 2
Otočte servomotorem dle otočení potenciometru.

Nápověda

Princip PWM
PWM signál je realizován pomocí timeru a dvou pomocných registrů ICRx a OCRxA. Hodnota registru ICRx určuje periodu (frekvenci) PWM signálu - je to hodnota do které má timer počítat, než dojde k vynulování. Hodnota OCRxA určuje tzv. střídu (doba trvání 1 k době trvání 0) - do té doby než timer napočítá do této hodnoty je na výstupu logická 1 a při dosažení této hodnoty je na výstupu logická 0..

PWM.png

Ovládání servomotoru
Signál pro ovládaní servomotoru je přesně definován jako PWM signál s periodou 20ms a čas trvání jednotkového impulsu určuje natočení serva. Hodnota 1,5ms je středová poloha servomotoru a krajní polohy jsou pak 0,6ms a 2,4ms (může se mírně lišit dle výrobce).

PWM-servo.png

Inicializace timeru pro přesne PWM:

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 2000,000 kHz
// Mode: Ph. & fr. cor. PWM top=ICR1
// OC1A output: Non-Inv.
// OC1B output: Discon.
// OC1C output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
// Compare C Match Interrupt: Off
TCCR1A=0x80;
TCCR1B=0x12;
TCNT1H=0x00;
TCNT1L=0x00;
//********************************************************************
//  Vypocet frekvence PWM :
// ------------------------
//                            1
//     poz. frekvence  = ---------------- /2= 50Hz
//                      1
//                -------- * ICR1
//                 2000 000
//
//    po uprave: (Clock je fr. timeru tj. Fr. procesoru/N  ,
//                kde N je preddelicka 1, 8, 64, 256, nebo 1024)
//            
//
//                    Clock (Hz)
//       ICR1 = ----------------------
//               2 * poz. frekvence PWM
//
// 
//
// Vypocet OCR1 (na urceni stridy):
// ----------------------------------
//
//
//                Clock (Hz)  *   Poz. cas
//       OCR1 = ----------------------------
//                            2
//*********************************************************************

// ICR1=20000; pro 16MHz
ICR1=18432;  // pro 14,745600 Hz


Funkce pro zadávání v čase řídícího impulsu

void servo_us(unsigned int uSec)
  {
  // 0,9216 = 1us
if (uSec<600) uSec=600;
if (uSec>2400) uSec=2400;

OCR1A=(922*(uSec/100))/10;    
        // hlidat aby nepretekl int (cca 65000)

        // za cenu zaokrouhleni 921,6 na 922

//OCR1A=0.9216*uSec;  //pouziti float = presne, ale zabira hodne pameti
 
  }

Funkce pro zadávání úhlu

void servo_poloha(unsigned char poloha)
  {
 // 921,6 = cca 922 =  1ms
 // 0,9216 = 1us
 // stred = 1500 us
 // 0st= 600us
 // 180st= 2400us

if (poloha<0) poloha=0;
if (poloha>180) poloha=180;

servo_us((poloha*10)+600);

  }

Řešení

 Příklad 1
#define F_CPU 14745600UL   
#include <avr/io.h>
#include <string.h>
#include <util/delay.h>


void servo_us(unsigned int uSec)
  {
  // 0,9216 = 1us
if (uSec<600) uSec=600;
if (uSec>2400) uSec=2400;

OCR1A=(922*(uSec/100))/10;     // hlidat aby nepretekl int (cca 65000)
                   // za cenu zaokrouhleni 921,6 na 922

//OCR1A=0.9216*uSec;  //pouziti float = presne, ale zabira hodne pameti
 
  }



void servo_poloha(unsigned char poloha)
  {
 // 921,6 = cca 922 =  1ms
 // 0,9216 = 1us

 // stred = 1500 us
 // 0st= 600us
 // 180st= 2400us


if (poloha<0) poloha=0;
if (poloha>180) poloha=180;

servo_us((poloha*10)+600);

  }



int main(void)
{

PORTB=0x00;
DDRB=0xB0;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 2000,000 kHz
// Mode: Ph. & fr. cor. PWM top=ICR1
// OC1A output: Non-Inv.
// OC1B output: Discon.
// OC1C output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
// Compare C Match Interrupt: Off
TCCR1A=0x80;
TCCR1B=0x12;
TCNT1H=0x00;
TCNT1L=0x00;


// ICR1=20000; pro 16MHz
ICR1=18432;  // pro 14,745600 Hz

while (1)
      {
// 921,6 = cca 922 =  1ms
//

  servo_poloha(0);
  _delay_ms(1000);
  servo_poloha(90);
  _delay_ms(1000);
   servo_poloha(180);
  _delay_ms(1000);
      };
}

Příklad 2
nutná inicializace AD převodníku., pak převod otočeni potenciometru na otočení servomotoru je :

uhel = read_adc(1);

uhel =  uhel / (1024/180);
servo_poloha(uhel);