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
Projekt taktiež využíva aplikáciu Processing na vytvorenie dialógového okna a spomínanú sériovú komunikáciu cez UART
#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;
}
}
}
}
#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();
}
#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;
}
import processing.serial.*;
import g4p_controls.*;
Serial myPort;
GLabel lblStatus;
GButton buttonMexican, buttonWink, buttonLeft, buttonRight;
boolean ledState = false;
void setup() {
size(300, 250);
createGUI();
println(Serial.list());
myPort = new Serial(this, Serial.list()[0], 9600);
myPort.clear();
}
void draw() {
background(230);
}
void serialEvent(Serial p) {
String incoming = p.readStringUntil('\n');
if (incoming != null) {
incoming = trim(incoming);
println(incoming);
if (incoming.equals("ON")) {
lblStatus.setText("Lights are up");
} else if (incoming.equals("OFF")) {
lblStatus.setText("Lights are down");
}
}
}
public void buttonMexican_click(GButton source, GEvent event) {
if (myPort != null) {
myPort.write('M');
}
}
public void buttonWink_click(GButton source, GEvent event) {
if (myPort != null)
myPort.write('W');
}
public void buttonLeft_click(GButton source, GEvent event) {
if (myPort != null)
myPort.write('L');
}
public void buttonRight_click(GButton source, GEvent event) {
if (myPort != null)
myPort.write('R');
}
public void createGUI() {
G4P.messagesEnabled(false);
G4P.setGlobalColorScheme(GCScheme.BLUE_SCHEME);
G4P.setMouseOverEnabled(true);
G4P.setDisplayFont("Arial Black", G4P.PLAIN, 14);
surface.setTitle("Headlight Control");
buttonMexican = new GButton(this, 20, 40, 120, 40);
buttonMexican.setText("Mexican Wink");
buttonMexican.setLocalColorScheme(GCScheme.BLUE_SCHEME);
buttonMexican.addEventHandler(this, "buttonMexican_click");
buttonWink = new GButton(this, 160, 40, 120, 40);
buttonWink.setText("Wink");
buttonWink.setLocalColorScheme(GCScheme.BLUE_SCHEME);
buttonWink.addEventHandler(this, "buttonWink_click");
buttonLeft = new GButton(this, 20, 120, 120, 40);
buttonLeft.setText("Wink Left");
buttonLeft.setLocalColorScheme(GCScheme.BLUE_SCHEME);
buttonLeft.addEventHandler(this, "buttonLeft_click");
buttonRight = new GButton(this, 160, 120, 120, 40);
buttonRight.setText("Wink Right");
buttonRight.setLocalColorScheme(GCScheme.BLUE_SCHEME);
buttonRight.addEventHandler(this, "buttonRight_click");
lblStatus = new GLabel(this, 0, 190, 300, 30);
lblStatus.setText("Ready");
lblStatus.setTextAlign(GAlign.CENTER, null);
}
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.