Projekt: Inkrementálny snímač otáčok
Zo stránky SensorWiki
Zadanie
- Vytvorte program, ktorý bude schopný regulovať otáčky motora pomocou PWM modulácie signálu
- Pomocou inkrementálneho snímača merajte otáčky motora, zobrazujte ich na LCD displej
Riešenie
Reguláciu otáčok motora sme zabezpečili pomocou PWM modulácie, pričom sme menili hodnotu striedy. Zabezpečili sme tak zmenu strednej hodnoty napätia privádzaného na motor.
PWM moduláciu sme realizovali pomocou časovača. Vybrali sme si časovač Timer2, ktorý dokázal pracovať v 8-bitovom režime. Pri frekvencii 16 MHz sme tým pádom vytvorili periódu opakovania PWM 61 Hz. Keď by sme zvolili vyššiu periódu opakovania, motor mal vplyvom induktancie pri riadení silne nelineárnu prevodovú charakteristiku.
Hodnotu striedy sme menili pomocou tlačítok integrovaných na LCD displeji. Veľkosť zmeny kroku sme zvolili: malý ±1% a veľký ±10%.
Pomocou inkrementálneho snímača otáčok sme merali hodnotu otáčok motora za sekundu. Prepočet sme realizovali pomocou časovača Timer1. Na základe zmeny hodnoty v Input Caprute registri sme generovali prerušenie, ktoré následne vypočítalo aktuálnu hodnotu otáčok motora. Pomocou príslušných funkcií v knižnici lcd.c sme túho hodnotu vzpísali na LCD displej. Pri pretečení počítadla časovača sme generovali prerušenie, ktoré detekovalo kedy motor stojí. Na LCD displej sme vtedy vypísali reťazec: stop.
Motor sme zapojili podľa schémy priloženej k zadaniu. Schému zapojenia motora a halovho inkrementálneho snímača nájdete tu: [1]
Po úspešnej realizácii projektu sme namerali prevodovú charakteristiku motora v závislosti od zmeny striedy. Meranie sme realizovali pri akcelerácii aj decelerácii motora. Krok striedy sme menili po 5%. Charakteristika je zobrazená na nasledujúcom obrázku.
Ukážka zdrojového kódu
/*
* Created: 12/12/2012 12:12:12
* Authors: Lukas Mrva (55716), Vladimir Reksak (55720)
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include "zaver.h"
#include "lcd.h"
#include <inttypes.h>
#include <stdio.h>
// deklaracia premennych
unsigned int buttons;
unsigned int strieda = 10;
unsigned int state = 0xFF;
volatile unsigned char stop=1, stop2=1;
// definovanie obsluhy preruseni
ISR(TIMER1_OVF_vect)
{
stop = stop2 = 1; // ked motor stoji, je overflow, stop aj stop2 = 1
}
ISR(TIMER1_CAPT_vect)
{
TCNT1 = 0; // po zaznamenani hodnoty v capture registri vznulujeme pocitadlo, mame cas jednej otacky
if (stop2==0) stop = 0; // ak dvakrat po sebe nastane prerusenie od pretecenia bez zaznamenania capture, motor stoji
stop2 = 0;
}
FILE mystdout = FDEV_SETUP_STREAM(lcdDataWrite, NULL, _FDEV_SETUP_WRITE); // je nova funkcia pre jeden znak
// hlavny program
int main(void)
{
stdout = &mystdout;
// inicializacia timerov, portov a displeja, povolenie preruseni
port_init();
timer2_init();
lcdInit4();
timer1_init();
asm("sei");
unsigned char lcdpresc=0;
// hlavna slucka programu
while(1)
{
delay_ms(55);
buttons=ReadButtons(); // zaznamenanie stlacenia tlacitka na LCD displeji, trva 20ms
PORTB &= 0xDF; // otestovanie tlacitka
if (buttons & (1<<3))
{
PORTB |= (0x01<<5); //blik led pri stlaceni tlacidla
strieda = strieda +10; // inkrementacia striedz - velky skok
}
if (buttons & (1<<0))
{
PORTB |= (0x01<<5); //blik led pri stlaceni tlacidla
strieda = strieda - 10; // dekrementacia striedz - velky skok
}
if (buttons & (1<<2))
{
PORTB |= (0x01<<5); //blik led pri stlaceni tlacidla
strieda++; // inkrementacia striedz - maly skok
}
if (buttons & (1<<1))
{
PORTB |= (0x01<<5); //blik led pri stlaceni tlacidla
strieda--; // inkrementacia striedz - maly skok
}
if(strieda>200) // ak podtiekla premenna
strieda = 0;
if(strieda>100) // ohranicenie maxima
strieda = 100;
if (buttons) lcdpresc=200; // po stlaceni tlacitka okamzity vypis
// zabazpecenie pomalejsieho vypisu, kazdy 10. krok
if (lcdpresc++ > 10)
{
lcdpresc=0;
lcdControlWrite(1<<LCD_CLR);
lcdControlWrite(0x40);
printf("TAU=%3d%%",strieda);
lcdControlWrite(0x40+0x80);
if (stop) printf(" stop ");
else printf("%4d rpm",(5000000/ICR1));
}
timer2_generator(strieda);
}
}
// --- FUNKCIE ---
void port_init(void)
{
// inicializacia portov
DDRB |= (1 << PORTB3)|(1 << PORTB1)|(1 << PORTB2); // PB1, PB2 - pwm vystup na servo
PORTB |= (1<<PORTB0); // pull up na ICP1
}
void timer1_init(void) // pwm na servopohon
{
TCNT1 = 0;
TCCR1B = (1 << CS11) | (1 << CS10); // nastavenie predelicky
// obsah registra: ICNC1 ICES1 – WGM13 WGM12 CS12 CS11 CS10
TIMSK1 = (1 << ICIE1) | (1 << TOIE1); // zapneme prerusenie od ICP a timer overflow
// obsah registra: – – ICIE1 – – OCIE1B OCIE1A TOIE1
}
void timer2_init(void) // inicializacia timera obsluhujuceho PWM
{
TCNT2 = 0;
OCR2A = 0x05; // zakladne nastavenie striedz - 10%
TCCR2A |= (1 << COM2A1) | (1 << WGM21) | (1 << WGM20);
// obsah registra: COM2A1 COM2A0 COM2B1 COM2B0 – – WGM21 WGM20
TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);
// obsah registra: FOC2A FOC2B – – WGM22 CS22 CS21 CS20
}
void timer2_generator(unsigned int tau) // uprava PWM, vstup do funkcie hodnota striedy [%]
{
OCR2A = (tau*2)+(tau/2);
if (tau) TCCR2A |= (1 << COM2A1);
else TCCR2A &= ~(1 << COM2A1); // ak mame striedu 0%, vypiname vystupny pin PWM modu
}
void delay_ms(unsigned int ms)
{
unsigned int index;
while (ms)
{
index = F_CPU / 5003; // presna hodnota, aby sme dostali 1ms
while (index)
{
asm volatile ("nop");
index--;
}
ms--;
}
}