Operácie

Zámok na kód s IR ovládaním

Zo stránky SensorWiki

Záverečný projekt predmetu MIPS / LS2026 - Mia Dudášová


Zadanie

Sem príde text zadania, ak bolo len voľne formulované, rozpíšte ho podrobnejšie

Cieľom projektu bolo vytvoriť zámok na kód a rozšíriť ho o ovládanie infračerveným (IR) diaľkovým ovládačom. Používateľ zadá číselný kód. Ak je kód správny, servomotor sa otočí do polohy "odomknuté", čím sa simuluje mechanické odomknutie zámku. Kód je možné zadať dvomi spôsobmi, cez sériovú linku UART alebo po jednotlivých čísliciach z IR diaľkového ovládača. Stav zámku signalizujú dve LED diódy. Červená svieti pri zamknutom zámku a zelená pri odomknutom.

Vývojová doska Arduino UNO.

Literatúra:


Analýza a opis riešenia

Opíšte sem čo a ako ste spravili, ak treba, doplňte obrázkami...

Podrobne opíšte použité komponenty (okrem základnej dosky s ATmega328P procesorom), pridajte linky na datasheety alebo opis obvodu. Systém je postavený na vývojovej doske Arduino UNO R3 s mikrokontrolérom ATmega328P (16 MHz). Použité súčiastky sú servomotor SG90, IR prijímač VS1838B, dve LED diódy (červená a zelená) a dva rezistory 220 Ω. Súčiastky sú prepojené na nepájivom poli.

Riešenie je naprogramované v jazyku C v prostredí avr-gcc bez použitia cudzích knižníc, okrem knižnice UART, ktorú sme používali na cvičeniach.

V programe je uložený kód ako štvormiestne číslo. Používateľ zadáva číslice stláčaním tlačidiel na IR diaľkovom ovládači alebo písaním v sériovom termináli pomocou klávesnice. Číslice sa postupne ukladajú. Keď počet zadaných číslic dosiahne dĺžku kódu, program ho porovná s uloženým kódom. Ak sa zhoduje, rozsvieti sa zelená LED a servomotor sa uloží do polohy "odomknuté". Po určenej dobe 4 sekundy sa vráti naspäť do polohy "zamknuté" a opäť sa rozsvieti červená LED.

Celkový pohľad na zariadenie.

Nezabudnite doplniť schému zapojenia! V texte by ste mali opísať základné veci zo zapojenia, samotná schéma nie je dostačujúci opis. Mikrokontróler využíva tri periférie naraz, pričom každá je pripojená na vlastný pin. Servomotor aj IR prijímač sú napájané z 5V vývodu dosky a majú spoločnú zem s Arduinom.

Schéma zapojenia.


Algoritmus a program

Algoritmus programu využíva toto a toto, základné funkcie sú takéto a voláma ich tuto... Výpis kódu je nižšie...

Diaľkový ovládač vysiela kód modulovaným IR signálom. Prijímač ho demoduluje a na svojom vstupe ho prevedie na digitálnu úroveň. Použitý ovládač pracuje s protokolom NEC, ktorý informáciu kóduje do dĺžky medzier medzi impulzmi. V programe meriame čas medzi hranami signálu pomocou Timer0, kde jeden tik zodpovedá 64 µs. Bity sa skladajú do 32-bitových rámcov, ktorý kontrolujeme, ak príkaz a jeho negácia dajú hodnotu 0xFF je platný.

Program sa skladá z hlavnej časti sem_projekt.c a knižnice uart.c s hlavičkou uart.h, ktorú sme používali na cvičeniach na sériovú komunikáciu. Timer1 vyrába PWM signál pre servomotor, Timer0 s externým prerušením INT0 meria čas medzi hranami IR signálu.

/*
 *     Timer1 -> PWM signal pre servo
 *     Timer0 -> meranie casu medzi hranami IR signalu
 *     INT0   -> externe prerusenie z IR prijimaca D2
 */

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <util/atomic.h>
#include <stdio.h>
#include <string.h>
#include "uart.h"

#define KOD          "1234"
#define DLZKA_KODU     4      /* musi zodpovedat dlzke KOD */

#define ZAMKNUTE_SERVO_US  1000     /* sirka impulzu zamknuteho serva */
#define ODOMKNUTE_SERVO_US 2000     /* sirka impulzu odomknuteho serva */
#define CAS_ODOMKNUTIA_MS  4000

#define SERVO_PIN    PB1      /* D9 */
#define IR_PIN       PD2      /* INT0 */

#define LED_CERVENA  PC0      /* A0 */
#define LED_ZELENA   PC1      /* A1 */

/* sirka impulzu z mikrosekund na tiky Timera1 */
#define US_NA_TIKY(us)  ((uint16_t)((uint32_t)(us) * 2UL))

/*  Timer0 1 tik = 64 us  */
#define IR_TIKY_START   150  /* zaciatok spravy */
#define IR_TIKY_KONIEC  26   /* pretecenie spravy */
#define IR_TIKY_MAX     60

/*  tabulka znakov IR ovladaca  */
typedef struct {
    uint8_t kod;
    char    znak;
} tlacidlo;

static const tlacidlo tabulka_tlacidiel[] = {
    { 0x19, '0' },
    { 0x45, '1' },
    { 0x46, '2' },
    { 0x47, '3' },
    { 0x44, '4' },
    { 0x40, '5' },
    { 0x43, '6' },
    { 0x07, '7' },
    { 0x15, '8' },
    { 0x09, '9' },
    { 0x16, '*' }, /* zmazat kod */
    { 0x0D, '#' }, /* potvrdit kod */
};
#define POCET_TLACIDIEL (sizeof(tabulka_tlacidiel) / sizeof(tabulka_tlacidiel[0]))

volatile uint32_t prijaty_ramec = 0;
volatile uint8_t  ramec_pripraveny = 0;

volatile uint8_t  prijima_ramec = 0;
volatile uint8_t  pocet_bitov_ramca = 0;
volatile uint32_t rozpracovany_ramec = 0;

static char    zadany_kod[DLZKA_KODU + 1];  
static uint8_t dlzka_zadaneho_kodu = 0;

FILE uart_vystup = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);

ISR(INT0_vect)
{
    uint8_t trvanie = TCNT0;   /* tiky od minulej hrany */
    TCNT0 = 0;

    if (trvanie >= IR_TIKY_START) {   /* nova sprava */
        prijima_ramec      = 1;
        pocet_bitov_ramca  = 0;
        rozpracovany_ramec = 0;
        return;
    }

    if (!prijima_ramec) return;       

    if (trvanie >= IR_TIKY_MAX) {     /* chyba */
        prijima_ramec = 0;
        return;
    }

    rozpracovany_ramec >>= 1;
    if (trvanie >= IR_TIKY_KONIEC) rozpracovany_ramec |= 0x80000000UL;
    pocet_bitov_ramca++;

    if (pocet_bitov_ramca >= 32) {    
        prijima_ramec    = 0;
        prijaty_ramec    = rozpracovany_ramec;
        ramec_pripraveny = 1;
    }
}

/*  timeout  */
ISR(TIMER0_OVF_vect)
{
    prijima_ramec = 0;
}

/*  externe prerusenie INT0 + Timer0  */
static void nastav_ir(void)
{
    DDRD  &= ~(1 << IR_PIN);
    PORTD &= ~(1 << IR_PIN);

    EICRA = (1 << ISC01);
    EIMSK = (1 << INT0);

    TCCR0A = 0x00;
    TCCR0B = (1 << CS02) | (1 << CS00);
    TCNT0  = 0;
    TIMSK0 = (1 << TOIE0);
}

static void nastav_servo(void)
{
    DDRB |= (1 << SERVO_PIN);
    TCCR1A = (1 << COM1A1) | (1 << WGM11);
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
    ICR1 = 40000;
}

static void sirka_impulzu_serva(uint16_t us)
{
    OCR1A = US_NA_TIKY(us);
}

static void led_zamknute(void)
{
    PORTC |=  (1 << LED_CERVENA);
    PORTC &= ~(1 << LED_ZELENA);
}

static void led_odomknute(void)
{
    PORTC |=  (1 << LED_ZELENA);
    PORTC &= ~(1 << LED_CERVENA);
}

static void odomkni(void)
{
    led_odomknute();
    sirka_impulzu_serva(ODOMKNUTE_SERVO_US);
    _delay_ms(CAS_ODOMKNUTIA_MS);

    sirka_impulzu_serva(ZAMKNUTE_SERVO_US);
    _delay_ms(700);
    led_zamknute();
}

static void over_kod(void)
{
    if (strcmp(zadany_kod, KOD) == 0) {
        printf("\nSpravny kod - ODOMYKAM.\n");
        odomkni();
        printf("Zamykam.\n");
    } else {
        printf("\nNespravny kod.\n");
    }

    dlzka_zadaneho_kodu = 0;
    zadany_kod[0]       = '\0';
    printf("Zadajte kod: ");
}

static void spracuj_znak(char znak)
{
    if (znak == '#' || znak == '\r' || znak == '\n') {   /* potvrdenie kodu */
        if (dlzka_zadaneho_kodu > 0) over_kod();
    }
    else if (znak == '*' || znak == 0x08 || znak == 0x7F) {  /* zmazanie kodu */
        dlzka_zadaneho_kodu = 0;
        zadany_kod[0]       = '\0';
        printf("\n[zmazane] Zadajte kod: ");
    }
    else if (znak >= '0' && znak <= '9') {
        if (dlzka_zadaneho_kodu < DLZKA_KODU) {
            zadany_kod[dlzka_zadaneho_kodu++] = znak;
            zadany_kod[dlzka_zadaneho_kodu]   = '\0';
            uart_putc(znak);
        }
        if (dlzka_zadaneho_kodu == DLZKA_KODU) over_kod();
    }
}

static char znak_pre_kod(uint8_t kod)
{
    for (uint8_t i = 0; i < POCET_TLACIDIEL; i++)
        if (tabulka_tlacidiel[i].kod == kod)
            return tabulka_tlacidiel[i].znak;
    return 0;
}

int main(void)
{
    uart_init();
    stdout = &uart_vystup;

    nastav_servo();
    sirka_impulzu_serva(ZAMKNUTE_SERVO_US);

    DDRC |= (1 << LED_CERVENA) | (1 << LED_ZELENA);
    led_zamknute();

    nastav_ir();
    sei();

    printf("Zamok na kod\n");
    printf("Zadajte kod: ");

    for (;;)
    {
        /* vstup z IR */
        if (ramec_pripraveny) {
            uint32_t ramec;
            ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {  /* bez prerusenia */
                ramec            = prijaty_ramec;
                ramec_pripraveny = 0;
            }

            uint8_t prikaz          = (ramec >> 16) & 0xFF;
            uint8_t negovany_prikaz = (ramec >> 24) & 0xFF;

            /* kontrola (prikaz a negacia musia spolu dat 0xFF) */
			if ((uint8_t)(prikaz ^ negovany_prikaz) == 0xFF) {
				char znak = znak_pre_kod(prikaz);
				if (znak) spracuj_znak(znak);
			}
        }

        /* vstup z UART */
        if (UCSR0A & (1 << RXC0)) {
            char znak = UDR0;
            spracuj_znak(znak);
        }
    }

    return 0;
}
#define F_CPU 16000000UL   
#define BAUD 9600        
 
#include <avr/io.h>       
#include <util/setbaud.h> 
 
void uart_init(void)
{
    UBRR0H = UBRRH_VALUE;   
    UBRR0L = UBRRL_VALUE; 
 
#if USE_2X
    UCSR0A |= _BV(U2X0);         
#else
    UCSR0A &= ~(_BV(U2X0));     
#endif
 
    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);  
    UCSR0B = _BV(RXEN0) | _BV(TXEN0);    
}

void uart_putc(char c) 
{
   if (c == '\n') 
    {
       uart_putc('\r');
    }
   loop_until_bit_is_set(UCSR0A, UDRE0); 
   UDR0 = c;
   return 0;
}
#ifndef UART_H_
#define UART_H_

void uart_init( void );
void uart_putc( char c );

#endif /* UART_H_ */

Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x zdrojaky.zip:

Zdrojový kód: zdrojaky.zip

Overenie

Aplikácia.

Program bol demonštrovaný na videu.

Video:


Čo by som urobil inak

Zamyslite sa spätne nad problémom, ktorý ste riešili a napíšte, čo sa vám nepodarilo a nabudúce by ste spravili inak.


Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte.