Ovládanie vyklápacích svetiel
Zo stránky SensorWiki
Záverečný projekt predmetu MIPS / LS2025 - Martin Lenarth
Zadanie
Cieľom bolo navrhnúť mikropočítačový systém, ktorý reaguje na stlačenie tlačidla a umožňuje riadiť pohyb vyklápacích svetiel na aute. Svetlá sa ovládajú pomocou motorčekov (dva smery pohybu), a to buď:
- pomocou jednoduchého alebo dvojitého zatlačenia tlačidla, alebo
- pomocou príkazov cez UART (sériová linka) z počítača

Literatúra:
Analýza a opis riešenia
Hlavnou úlohou systému je riadiť pohyb výklopných svetiel nezávisle od seba. Každé svetlo je ovládané dvoma výstupmi – jeden pre pohyb hore a druhý pre pohyb dole. Týmto spôsobom je možné svetlá zdvíhať, spúšťať a vykonávať rôzne blikacie sekvencie. Svetlá je možné ovládať dvoma spôsobmi – mechanickým tlačidlom alebo príkazmi cez UART.
Použite komponenty:
- Tlačidlo v aute
- Motorčeky na vyklápanie svetiel
- 4-Kanálová relé doska s optočlenom

Na detekciu klikov a časovanie pohybov sa využíva časovač TIMER0 v režime CTC, ktorý generuje prerušovanie každú 1 ms. Tým vzniká softvérový časovač pomocou premennej milliseconds.
Program rozlišuje:
- Jedno kliknutie – slúži na zapnutie alebo vypnutie svetiel (pohyb hore alebo dole).
- Dvojklik – spúšťa animovanú sekvenciu bliknutia svetiel.
- UART príkazy – umožňujú spustiť rôzne preddefinované sekvencie
Funkcie pre ovládanie svetiel sú implementované v súbore sequences.c. Tieto funkcie priamo nastavujú piny výstupov podľa požadovaného smeru pohybu a obsahujú časové oneskorenia, ktoré simulujú fyzický pohyb svetiel.
Schéma zapojenia:

Algoritmus a program
Program začína inicializáciou periférií:
- Nastavenie vstupného pinu pre tlačidlo s pull-up rezistorom.
- Nastavenie výstupných pinov pre ovládanie svetiel.
- Spustenie UART a časovača TIMER0.
- Povolenie globálnych prerušení.
Hlavná slučka vykonáva:
- Sleduje stav tlačidla, používa debounce a detekciu zmeny stavu.
- Rozlišuje jedno a dvojité kliknutie podľa časového odstupu medzi klikmi.
- Ovláda svetlá – pri jednom kliknutí sa svetlá zapnú/vypnú, pri dvojkliku sa spustí blikacia sekvencia.
- Spracováva znaky z UART – ak bol prijatý znak, vykoná sa príslušná funkcia
<tabs>
- define F_CPU 16000000UL
- define BAUD 9600
- include <avr/io.h>
- include "uart.h"
- include "sequences.h"
- include <util/delay.h>
- include <avr/interrupt.h>
- include <stdio.h>
// Pin Definitions
- define SWITCH PD2
- define L_UP PB3
- define R_UP PD5
- define L_DOWN PB4
- define R_DOWN PD6
// Timing Constants
- define BUTTON_TIME 500 // button wait time for double click
- define DEBOUNCE_DELAY 50 // debounce duration
// Headlight State HeadlightStatus headlights = {0, 0};
// Toggle Pattern Tracking int lastSwitchState = 1; int firstToggleState = 1; volatile uint32_t firstToggleTime = 0; volatile uint8_t toggleSequenceActive = 0; volatile uint8_t handledSequence = 0;
// Millisecond Counter volatile uint32_t milliseconds = 0;
// Timer Setup void setup_timer0() { TCCR0A |= (1 << WGM01); // CTC OCR0A = 249; // 1ms TIMSK0 |= (1 << OCIE0A); // Enable interrupt TCCR0B |= (1 << CS01) | (1 << CS00); // Prescaler 64 }
ISR(TIMER0_COMPA_vect) { milliseconds++; }
unsigned long millis() { unsigned long ms; uint8_t oldSREG = SREG; cli(); ms = milliseconds; SREG = oldSREG; return ms; }
// Debounce uint8_t debounceSwitch(uint8_t pin) { uint8_t stableState = (PIND & (1 << pin)) ? 1 : 0; _delay_ms(DEBOUNCE_DELAY); uint8_t newState = (PIND & (1 << pin)) ? 1 : 0; return (stableState == newState) ? newState : stableState; }
// Main Loop int main(void) { // Switch input DDRD &= ~(1 << SWITCH); PORTD |= (1 << SWITCH); // Pull-up
// Outputs DDRB |= (1 << L_UP) | (1 << L_DOWN); DDRD |= (1 << R_UP) | (1 << R_DOWN);
uart_init(); setup_timer0(); sei(); stop();
while (1) { int current = debounceSwitch(SWITCH); unsigned long now = millis();
if (current != lastSwitchState) { if (!toggleSequenceActive) { toggleSequenceActive = 1; handledSequence = 0; firstToggleState = lastSwitchState; firstToggleTime = now; } else if ((now - firstToggleTime) <= (BUTTON_TIME - DEBOUNCE_DELAY) && current == firstToggleState) { if (firstToggleState == 1) { AlternateWink(); } else { Wink(); } handledSequence = 1; toggleSequenceActive = 0; } lastSwitchState = current; }
if (toggleSequenceActive && !handledSequence && (now - firstToggleTime) > (BUTTON_TIME - DEBOUNCE_DELAY)) { toggleSequenceActive = 0; if (lastSwitchState == 0 && !areHeadlightsUp()) { raiseHeadlights(); uart_puts("ON\n"); } else if (lastSwitchState == 1 && areHeadlightsUp()) { lowerHeadlights(); uart_puts("OFF\n"); } }
if (!toggleSequenceActive && !handledSequence) { if (current == 0 && !areHeadlightsUp()) { raiseHeadlights(); uart_puts("ON\n"); } else if (current == 1 && areHeadlightsUp()) { lowerHeadlights(); uart_puts("OFF\n"); } }
if (UCSR0A & (1 << RXC0)) { char command = uart_getc();
switch (command) { case 'M': if (areHeadlightsUp()) MexicanWink(); else AlternateMexicanWink(); break;
case 'W': if (areHeadlightsUp()) Wink(); else AlternateWink(); break;
case 'L': WinkLeft(); break;
case 'R': WinkRight(); break; } } } }
</source>- ifndef SEQUENCES_H
- define SEQUENCES_H
- include <stdint.h>
typedef struct { int leftUp : 1; int rightUp : 1; } HeadlightStatus;
void moveHeadlight(uint8_t side, uint8_t direction); void raiseHeadlights(void); void lowerHeadlights(void); uint8_t areHeadlightsUp(void);
void Wink(void); void AlternateWink(void); void MexicanWink(void); void AlternateMexicanWink(void); void WinkLeft(void); void WinkRight(void);
void stop(void);
- endif
- define F_CPU 16000000UL
- define BAUD 9600
- include <avr/io.h>
- include <util/delay.h>
- include "sequences.h"
- define UP 1
- define DOWN 0
- define LEFT 1
- define RIGHT 0
- define L_UP PB3
- define R_UP PD5
- define L_DOWN PB4
- define R_DOWN PD6
- define MOVE_TIME 750
- define SEQUENCE_TIME 150
extern HeadlightStatus headlights;
static void set_pin(volatile uint8_t *port, uint8_t pin, uint8_t high) { if (high) *port |= (1 << pin); else *port &= ~(1 << pin); }
void delay(int delay) { for (int i=1; i<=delay; i++) _delay_ms(1); }
void stop() { set_pin(&PORTB, L_UP, 1); set_pin(&PORTD, R_UP, 1); set_pin(&PORTB, L_DOWN, 1); set_pin(&PORTD, R_DOWN, 1); }
static void stopLeft() { set_pin(&PORTB, L_UP, 1); set_pin(&PORTB, L_DOWN, 1); }
static void stopRight() { set_pin(&PORTD, R_UP, 1); set_pin(&PORTD, R_DOWN, 1); }
void moveHeadlight(uint8_t side, uint8_t direction) { if (side == LEFT) { set_pin(&PORTB, L_UP, !direction); set_pin(&PORTB, L_DOWN, direction); headlights.leftUp = direction; } else { set_pin(&PORTD, R_UP, !direction); set_pin(&PORTD, R_DOWN, direction); headlights.rightUp = direction; } }
void raiseHeadlights() { if (!headlights.leftUp || !headlights.rightUp) { if (!headlights.leftUp) moveHeadlight(LEFT, UP); if (!headlights.rightUp) moveHeadlight(RIGHT, UP); delay(MOVE_TIME); stop(); headlights.leftUp = UP; headlights.rightUp = UP; } }
void lowerHeadlights() { if (headlights.leftUp || headlights.rightUp) { if (headlights.leftUp) moveHeadlight(LEFT, DOWN); if (headlights.rightUp) moveHeadlight(RIGHT, DOWN); delay(MOVE_TIME); stop(); headlights.leftUp = DOWN; headlights.rightUp = DOWN; } }
uint8_t areHeadlightsUp() { return headlights.leftUp && headlights.rightUp; }
void Wink() { moveHeadlight(LEFT, DOWN); delay(MOVE_TIME); stop();
moveHeadlight(LEFT, UP); moveHeadlight(RIGHT, DOWN); delay(MOVE_TIME); stop();
moveHeadlight(LEFT, DOWN); moveHeadlight(RIGHT, UP); delay(MOVE_TIME); stop();
moveHeadlight(LEFT, UP); moveHeadlight(RIGHT, DOWN); delay(MOVE_TIME); stop();
moveHeadlight(RIGHT, UP); delay(MOVE_TIME); stop();
headlights.leftUp = UP; headlights.rightUp = UP; }
void AlternateWink() { moveHeadlight(LEFT, UP); delay(MOVE_TIME); stop();
moveHeadlight(LEFT, DOWN); moveHeadlight(RIGHT, UP); delay(MOVE_TIME); stop();
moveHeadlight(LEFT, UP); moveHeadlight(RIGHT, DOWN); delay(MOVE_TIME); stop();
moveHeadlight(LEFT, DOWN); moveHeadlight(RIGHT, UP); delay(MOVE_TIME); stop();
moveHeadlight(RIGHT, DOWN); delay(MOVE_TIME); stop();
headlights.leftUp = DOWN; headlights.rightUp = DOWN; }
void MexicanWink() { for (int i = 0; i < 2; i++) { moveHeadlight(LEFT, DOWN); delay(SEQUENCE_TIME); moveHeadlight(RIGHT, DOWN); delay(MOVE_TIME - SEQUENCE_TIME); stopLeft(); delay(SEQUENCE_TIME); stopRight();
moveHeadlight(LEFT, UP); delay(SEQUENCE_TIME); moveHeadlight(RIGHT, UP); delay(MOVE_TIME - SEQUENCE_TIME); stopLeft(); delay (SEQUENCE_TIME); stopRight(); }
headlights.leftUp = UP; headlights.rightUp = UP; }
void AlternateMexicanWink() { for (int i = 0; i < 2; i++) { moveHeadlight(LEFT, UP); delay(SEQUENCE_TIME); moveHeadlight(RIGHT, UP); delay(MOVE_TIME - SEQUENCE_TIME); stopLeft(); delay(SEQUENCE_TIME); stopRight();
moveHeadlight(LEFT, DOWN); delay(SEQUENCE_TIME); moveHeadlight(RIGHT, DOWN); delay(MOVE_TIME - SEQUENCE_TIME); stopLeft(); delay(SEQUENCE_TIME); stopRight(); }
headlights.leftUp = DOWN; headlights.rightUp = DOWN; }
void WinkLeft() { if (headlights.leftUp) { moveHeadlight(LEFT, DOWN); delay(MOVE_TIME); stopLeft(); moveHeadlight(LEFT, UP); } else { moveHeadlight(LEFT, UP); delay(MOVE_TIME); stopLeft(); moveHeadlight(LEFT, DOWN); } delay(MOVE_TIME); stopLeft(); }
void WinkRight() { if (headlights.rightUp) { moveHeadlight(RIGHT, DOWN); delay(MOVE_TIME); stopRight(); moveHeadlight(RIGHT, UP); } else { moveHeadlight(RIGHT, UP); delay(MOVE_TIME); stopRight(); moveHeadlight(RIGHT, DOWN); } delay(MOVE_TIME); stopRight(); }
</source>- ifndef UART_H_
- define UART_H_
void uart_init( void );
void uart_putc( char c ); void uart_puts( const char *s );
char uart_getc( void );
- endif /* UART_H_ */
- 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); /* 8-bit data */ UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */ }
void uart_putc(char c)
{
if (c == '\n')
{
uart_putc('\r');
}
loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
UDR0 = c;
}
void uart_puts(const char *s)
{
while (*s) {
uart_putc(*s++);
}
}
char uart_getc(void) { loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */ return UDR0; }
</source>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
Ako ste overili funkciu, napríklad... Na používanie našej aplikácie stačia dve tlačítka a postup používania je opísaný v sekcii popis riešenia. Na konci uvádzame fotku hotového zariadenia.

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