Ovládanie vyklápacích svetiel: Rozdiel medzi revíziami
Zo stránky SensorWiki
(5 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
Riadok 8: | Riadok 8: | ||
*pomocou príkazov cez UART (sériová linka) z počítača | *pomocou príkazov cez UART (sériová linka) z počítača | ||
[[ | [[Súbor:Nano.jpg|400px|thumb|center|Arduino NANO]] | ||
'''Literatúra:''' | '''Literatúra:''' | ||
* [ | * [https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf Datasheet k ATmega328P] | ||
Riadok 19: | Riadok 18: | ||
== Analýza a opis riešenia == | == 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 | |||
[[Súbor:Rele_doska.jpg|400px|thumb|center|Relé doska]] | |||
[[Súbor: | 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:''' | |||
[[Súbor:SchemaZapojeniaWink.jpg|400px|thumb|center|Schéma zapojenia.]] | |||
=== Algoritmus a program === | === 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 | |||
<tabs> | <tabs> | ||
<tab name=" | <tab name="main.c"><syntaxhighlight lang="c++" style="background: LightYellow;"> | ||
#define F_CPU 16000000UL | |||
#define BAUD 9600 | |||
#include <avr/io.h> | #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; | |||
} | |||
} | |||
} | |||
} | |||
</syntaxhighlight></tab> | |||
<tab name="sequences.h"><syntaxhighlight lang="c++" style="background: LightYellow;"> | |||
#ifndef SEQUENCES_H | |||
#define SEQUENCES_H | |||
int | #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 | |||
</syntaxhighlight></tab> | |||
<tab name="sequences.c"><syntaxhighlight lang="c++" style="background: LightYellow;"> | |||
#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(); | |||
} | } | ||
</syntaxhighlight></tab> | |||
<tab name="uart.h"><syntaxhighlight lang="c++" style="background: LightYellow;"> | |||
#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_ */ | |||
</syntaxhighlight></tab> | |||
<tab name="uart.c"><syntaxhighlight lang="c++" style="background: LightYellow;"> | |||
#define F_CPU 16000000UL | |||
#define BAUD 9600 | |||
#include <avr/io.h> | #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; | |||
} | |||
Zdrojový kód: [[Médiá: | |||
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; | |||
} | |||
</syntaxhighlight></tab> | |||
<tab name="OvládanieUART.pde"><syntaxhighlight lang="c++" style="background: LightYellow;"> | |||
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); | |||
} | |||
</syntaxhighlight></tab> | |||
</tabs> | |||
Zdrojový kód: [[Médiá:projektMartinLenarth.zip|zdrojaky.zip]] | |||
=== Overenie === | === Overenie === | ||
Na overenie funkcií zariadenia použijeme trochu iné zapojenie ako je uvedené vyššie. Pri prepínaní relé môžeme vidieť blikajúce led ktoré naznačujú ktoré relé je zapnuté, keď príslušná led svieti, relé je zapnuté, a taktiež môžeme vidieť točenie sa pripojeného DC motorčeka na rôzne strany podla zapnutých relé | |||
Teda k použitým komponentom pridáme: | |||
*1.5V baterku alebo iný zdroj napätia | |||
*DC motorček | |||
'''Schéma na overenie:''' | |||
[[Súbor:SchemaZapjeniaWinkOverenie.jpg|400px|thumb|center|Schéma zapojenia]] | |||
'''Reálne zapojenie:''' | |||
[[Súbor:GeminiAI-image1.jpg|400px|thumb|center|Zapojenie]] | |||
(Ak budem mať čas pred kontrolou zadania zapojím zariadenie aj do auta a pridám aj video fungovania) | |||
'''Video:''' | '''Video:''' | ||
Riadok 78: | Riadok 635: | ||
[[Category:AVR]] [[Category:MIPS]] | [[Category:AVR]] [[Category:MIPS]] |
Aktuálna revízia z 23:17, 16. máj 2025
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
Na overenie funkcií zariadenia použijeme trochu iné zapojenie ako je uvedené vyššie. Pri prepínaní relé môžeme vidieť blikajúce led ktoré naznačujú ktoré relé je zapnuté, keď príslušná led svieti, relé je zapnuté, a taktiež môžeme vidieť točenie sa pripojeného DC motorčeka na rôzne strany podla zapnutých relé
Teda k použitým komponentom pridáme:
- 1.5V baterku alebo iný zdroj napätia
- DC motorček
Schéma na overenie:

Reálne zapojenie:

(Ak budem mať čas pred kontrolou zadania zapojím zariadenie aj do auta a pridám aj video fungovania)
Video: