Operácie

Ovládanie vyklápacích svetiel: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
StudentMIPS (diskusia | príspevky)
 
(4 medziľahlé úpravy od rovnakého používateľa nie sú zobrazené.)
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


[[Obrázok:ard.jpg|400px|thumb|center|Arduino NANO]]
[[Súbor:Nano.jpg|400px|thumb|center|Arduino NANO]]


'''Literatúra:'''  
'''Literatúra:'''  
Riadok 18: Riadok 18:
== Analýza  a opis riešenia ==
== Analýza  a opis riešenia ==


Opíšte sem čo a ako ste spravili, ak treba, doplňte obrázkami...
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.
Podrobne opíšte použité komponenty (okrem základnej dosky s ATmega328P procesorom), pridajte linky na datasheety alebo opis obvodu.  


[[Súbor:GeminiAI-image3.jpg|400px|thumb|center|Celkový pohľad na zariadenie.]]
'''Použite komponenty:'''
*Tlačidlo v aute
*Motorčeky na vyklápanie svetiel
*4-Kanálová relé doska s optočlenom


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.
[[Súbor:Rele_doska.jpg|400px|thumb|center|Relé doska]]


[[Súbor:GeminiAI-image2.jpg|400px|thumb|center|Schéma zapojenia.]]
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 ===


Algoritmus programu využíva toto a toto, základné funkcie sú takéto a voláma ich tuto...  
Program začína inicializáciou periférií:
Výpis kódu je nižšie...
*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="AVR C-code"><syntaxhighlight  lang="c++" style="background: LightYellow;">
<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 main(void)
#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)
{
{
  unsigned int measuredValue;
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;
}


  while (1)
void WinkLeft() {
  {
if (headlights.leftUp) {
    /*  relax  */ 
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();
}


  return(0);
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


</syntaxhighlight ></tab>
<tab name="filename.h"><syntaxhighlight  lang="c++" style="background: LightYellow;">
#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


void adc_init(void);                                   // A/D converter initialization
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
UCSR0B = _BV(RXEN0) | _BV(TXEN0);  /* Enable RX and TX */
}


unsigned int adc_read(char a_pin);
</syntaxhighlight ></tab>
</tabs>


Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x ''zdrojaky.zip'':
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á:projektMenoPriezvisko.zip|zdrojaky.zip]]
 
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 ===


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 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é
Na konci uvádzame fotku hotového zariadenia.  
 
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]]


[[Súbor:GeminiAI-image1.jpg|400px|thumb|center|Aplikácia.]]
(Ak budem mať čas pred kontrolou zadania zapojím zariadenie aj do auta a pridám aj video fungovania)


'''Video:'''
'''Video:'''
Riadok 77: Riadok 635:




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


[[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
Arduino NANO

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
Relé doska

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:

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:

  1. Sleduje stav tlačidla, používa debounce a detekciu zmeny stavu.
  2. Rozlišuje jedno a dvojité kliknutie podľa časového odstupu medzi klikmi.
  3. Ovláda svetlá – pri jednom kliknutí sa svetlá zapnú/vypnú, pri dvojkliku sa spustí blikacia sekvencia.
  4. 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:

Schéma zapojenia

Reálne zapojenie:

Zapojenie

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

Video: