Bidrag: TV dosa v1.1 - Jyrgen

Här presenteras bidragen i Svenska Elektronikforumets Elektroniktävling 2009. Instruktioner för omröstning finns också!
jyrgen
Inlägg: 84
Blev medlem: 11 juli 2006, 20:24:54
Ort: Västerås

Bidrag: TV dosa v1.1 - Jyrgen

Inlägg av jyrgen »

En tv-dosa som man zappar och ändrar volym med.

För att detta ska fungera finns en aktiveringsknapp som man måste trycka på för att man ska kunna använda dosa.
Förutom aktiveringsknappen på undersidan av dosan finns 3 knappar på ovansidan som man kan göra allt på med hjälp
av olika kombinationer t ex tryck 2 gånger på knapp 2 för att byta till kanal 5 eller att man drar över knapparna i en viss
ordning så slås text tv på.
Dessa knappar som inte är några fysiska knappar som man trycker ner utan är med hjälp av kretsen "B6TS04LT000
touch-sensor" touchknappar. Detta gör att skalet kan se ut hur som helst och förhoppningsvis ska man kunna ta vad
man har hemma för att bygga skalet, dock med begränsning på tjocklek. Blir även en lysdiod inuti så att den blir lite
häftig om man kör med genomskinligt skal. Funderar även på att göra så att man kan använda den som en myslampa
med någon knappkombination. Den planerade tiltfunktionen togs bort helt från dosan. Det finns därför ingen kod för
att använda dem färdig så den måste skrivas om denna funktion ska kunna användas.



Innehåll i inlägget:

Kretskortet:
........Schema och komponentbeskrivningar
........Kretskortet
........Montering av komponenter
Bygge av skalet
Mjukvara:
........Signalen till tv:n
........Om C-programmet för ATmega168
Resultat
Appendix:
........c-koden



Jag har använt en ATmega168 och följande:

Standardkomponenter:
65-848-82 4st Keramisk kondensator 33pF
48-137-21 1st Sockel 28-pin
67-780-13 1st Ellyt kondensator 1uF
73-266-64 1st LM317 sp-reg
73-026-49 1st Tiltsensor
xx-xxx-xx 1st Batterikontakt
60-722-84 1st Motstånd 1K

Specialkomponenter:
65-656-59 7st keramisk kondensator 0,1uF
73-026-49 3st Tiltsensor <-- Saknar i nuläget funktion.
60-717-16 1st motstånd 330ohm
75-061-73 1st LED 5mm orange
60-713-51 1st motstånd 160ohm
73-220-50 1st B6TS04LT000 touch-sensor
60-741-81 2st motstånd 47kohm
60-734-23 4st motstånd 10kohm
60-727-55 4st motstånd 2,7kohm
75-225-19 1st IR emitter
60-715-00 1st motstånd 220ohm

Totalkostnad för specialkomponenter 146,5 ex moms, ca 183kr ink. moms.


Kretskortet

Detta är mitt första riktiga kretskort som jag har ritat, har gjort ett break out board tidigare men det räknar jag inte som ett riktigt kretskort. Kortet är ritat i Eagle och sedan beställt från PCBCART. Blev ganska dyrt när man beställde bara ett kort, så ska nog hitta någon metod att tillverka egna kort i framtiden.

Kostnad för kortet samt frakt (i SEK) :
Verktygskostnad 239:-
Kortpris (Beställde endast ett) 114:-
Frakt (FedEX)198:-
Totalt 551:-


Schema och komponentbeskrivningar

Eftersom standardkomponenterna är hålmonterade väljs även alla specialkomponenter som hålmonterade. Den kapacitativa sensorn finns inte som hålmonterad så den är den enda komponent som är ytmonterad.

Spänningsregulatorn som är ritad till vänster ger 5V utspänning som driver alla komponenter.

Lysdioden är kopplad i serie med en resistans för att få ca 20mA genom dioden.

IR-dioden har våglängden 940nm men vad jag har läst så ska de flesta mottagare i TV:n klara alla våglängder runt 800-1000nm. Även denna är kopplad i serie med en resistans för att få ca 20mA.

Tiltsensorn som används är en simpel variant som leder om den tiltas mer än 33grader. Tittar man inuti består den av 3 delar. Delen längs ner i bilden är den undre delen och är skålformad på insidan. Mittendelen är en cylinder stav som står i den undre delen så att när man lutar nog mycket så vickar den åt sidan och kommer i kontakt med den övre delen.
Tanken med dessa sensorer är att de ska luta åt olika håll på kretskortet för att man ska veta vilket håll dosan sedan lutas åt. Dosan måste alltså hållas rak från början.


Bild

I schemat är de fyra tiltsensorerna utritade längst ner. Intern pull-up används så sensorerna kopplas till jord.


Kapacitativ sensor som kan användas för att göra 4st touch knappar. Man kan ta ett ledande material
t.ex. 2mm tjock aluminiumplåt eller aluminiumfolie som jag har prövat med, båda gav bra resultat. Storleken går
också att variera en del. Jag har haft folieknappar från 1x1cm till 3x3cm och det har fungerat. Jag kommer till
min dosa att använda folie då det är lite lättare att forma än plåt. Ta sedan ett icke ledande material och fäst
knapparna på ena sidan så har du knapparna på andra sidan. Dock får det icke ledande materialet inte vara
för tjockt runt 1cm som max.

Kopplingsschemat för den Kapacitativa sensorn hittas i databladet medans värdena på alla komponenter hittas i ett
breakout board som tillverkaren har. Alla värden tas rakt av förutom kapacitanserna C3, C5, C7 och C8 som ändras
från 18pF till 33pF eftersom de fås gratis bland standardkomponenterna.

Det är möjligt att kommunicera med komponenten via serie kommunikation där man kan ställa in och hämta alla
parametrar som vilken kapacitans den ska trigga på osv. Jag använder mig inte av detta. För att kalibrera sina
knappar finns det något som heter learning mode vilket man går in igenom att använda två av pinnarna på IC:n.
Under en viss tid trycker man på alla 4 knappar minst 3 gånger och om kalibreringen lyckas kan man använda
knapparna och läsa av resultatet på knapparna på de 4 out pinnarna.

Bild

För bättre upplöst bild: http://lh3.ggpht.com/_5MP8Gzl0mkA/S1NGX ... schema.JPG


Kretskortet
Detta är som sagt mitt första kretskort så finns troligtvis förbättringar att göra. Alla signal banor är ritade med bredd 12mil (0,3048mm) och alla övriga banor är ritade med bredd 24mil (0,6096mm).

Bild

Bild

Eagle filerna(.brd & .sch) finns att hämta här: http://cid-0e098143c3069cb5.skydrive.li ... %A4vlingen
OBS! Tiltsensorerna sitter för nära varandra och närliggande komponenter så om man vill använda dom bör dessa flyttas isär så de får plats ordentligt.

Montering av komponenter

Jag är endast innehavare av en icke reglerbar lödpenna med ganska grov spets. Den ytmonterade komponenten kan därför inte ödas hemma. Som tur var så gör jag examensarbete på ett företag som har börjat löda lite ytmonterat i ugn.
Först steget är att lödpasta smetas på och det spelar ingen roll att det kommer utanför paddarna. Egentligen ska man rengöra kortet också men det steget skippade jag.
Bilden är tagen genom ett förstoringsglas eftersom min kamera inte klarar av att ta tillräckligt bra bilder.

Bild

Lägg sedan på komponenten så att varje ben hamnar på sin pad. Det gör inget om den hamnar lite snett bara rätt ben hamnar på rätt pads.

Bild

In i ugnen på 180 grader tills lödpastan har smält.

Bild

Detta är ett mycket smidigt sätt att löda på och resultatet har på det lilla jag prövat blivit bra. Ugnen som används är en ”toaster oven” som kan köpas på många ställen som säljer andra köksmaskiner, denna kostade ca 800kr. Jag kan dock rekommendera att få tag på en med lampa om det finns för det är lite svårt att se när pastan smälter.

Bild

Övriga komponenter löds förhand.

Bygge av skalet

Efter att ha spenderat en halv förmögenhet på kretskortet så beslutade jag mig för att bygga dosan av material som jag hade hemma. Två relativt stora bitar i ofärgat 3mm Polymetylmetakrylat (mer känt som plexiglas) är jag innehavare av så det kommer skalet att tillverkas av.
Hade lite olika idéer om hur boxen skulle se ut. Först tänkte jag mig något som kan liknas vid en glasskulptur så prövade att forma en bit plexiglas genom att sätta in den i ugnen en stund. Detta blev dock inget vidare med resultatet att biten blev bucklig, hade lite luftbubblor i sig och inte hade den formen jag ville.
Efter det övergavs alla idéer om konstiga former och en fyrkantig låda blev nästa försök. Lådan är tänkt att bestå av två fack där det ena facket är till för batteriet och det andra är till för kretskortet. För att ”limma” plexiglas använder jag aceton vilket kan köpas i de flesta mataffärer. För att ”limma” ställer man bitarna man vill limma på varandra som jag gjort på bilden och sen tillför aceton. Jag kan rekommendera en spruta för häller man direkt ur Gripen flaskan kommer det mycket och man kan inte rikta strålen. Jag hade dock ingen spruta så jag hällde på samt lutade plattan under lite så fick det rinna in i skarven. Överflödigt aceton kan ligga kvar och torka, det enda jag såg av det efteråt var lite vita fläckar som också syns på bilden.

Bild

Det som nu händer är att acetonet löser upp plexiglaset så att när sedan acetonet dunstar så blir det fysiskt sett en del. Tittar man på skarven så är den genomskinlig och inte vit som den var när bitarna ställdes på varandra första gången. Bilden nedan är lite dålig men ska visa på att skarven är borta. Lådan är till vänster och en sida till har limmats dit. Bredvid står en vanlig bit för att jämföra med. Tittar man längst ned i bilden kan man se att det fortfarande är lite vitt kvar där har jag missat att tillfört aceton.

Bild

Om man har en ojämn skarv så går det även att lösa upp plexiglas i lite aceton och sen använda det som fogmassa. Prövade detta på en provbit och resultatet blev bra förutom att det blev bubblor i plexiglaset efteråt. Detta kan nog åtgärdas med att låta det torka i vakuum men jag var inte i behov av något sånt.

För att minska insynen i lådan lite så sandpappras lådan med ett sandpapper med finkornigheten P120. Så här ser den ut utan någonting monterat, boxen till vänster och locket till höger.

Bild

Ganska snabbt efter att allt monterats i boxen och prövats insåg jag att två saker måste göras.
Det första är att endast en lysdiod ej var tillräckligt och det fanns ingen bra plats att sätta den på. Jag fick göra en ful lösning och koppla in en till lysdiod på en ledig pinne.
Det andra var att om jag ska orka använda den här dosan så måste tiltfunktionen bort. Det var ofantligt jobbigt att koncentrera sig på att dosan fick lov att vara rak. Det andra dåliga vara att nr man tiltar dosan åt två av hållen så riktar man även bort IR dioden som då missar tv:n ganska ofta. Dessa två saker gjorde att tiltfunktionen togs bort helt och hållet.
Bilder på när allt är monterat finns under resultat lite längre ner.

Mjukvara

Detta ska bli en tv dosa så därför måste signalen som min befintliga tv dosa skickar till tv:n kopieras.

Signalen till tv:n
Det finns en del olika varianter av signaler som skickas till från tv dosa till tv. Det enklaste sättet att veta vad en tv ska ha för signal är att mäta signalen som den befintliga tv dosan skickar med ett oscilloskop.

Jag har en gammal 28-tums Mitsubishi tjock-tv. Signalen som min tv tar emot är en signal med puls distans modulation. Detta innebär att skillnaden mellan en etta och en nolla är beroende av tiden mellan två pulser. På bilden nedan ses signalen för att byta till kanal 1 som min tv dosa skickar ut. Denna signal blir efter avkodning 0000000001000111. Man läser ut detta genom att titta på mellanrummet mellan pulserna. Ett långt mellanrum(2,1ms) blir en etta och ett kort mellanrum(0.9ms) blir en nolla.

Bild

Bild

Den korta puls som skickas för att generera mellanrummen är också modulerad på ett speciellt vis. Den består av 10st 10us långa pulser med 33% pwm modulation. Varje puls tar alltså 300us att skicka.

Bild

C-programmet för ATmega168

Mjukvaran är inte kontrollerad fullt ut och det kan finnas fall då den inte fungerar som tänkt. Måste använda den en längre tid för att få reda på vilka fel som kan uppstå. Huvud funktionaliteten fungerar dock bra.

Huvud funktionalitet
När man stoppar i batteriet har man 30 sekunder på sig att sätta ihop dosan. Efter det blinkar dioderna några gånger. När dioderna slutar blinka och börjar lysa så måste man vara ganska snabb och trycka på alla knappar så många gånger man hinner med men minst 3 gånger. Dioderna börjar sen blinka igen och när det slutat blinka finns det två lägen. Lyser dioderna har kalibreringen misslyckats och det är bara att börja om från början. Är dioderna släckta så finns nu chans att testa så att alla knappar fungerar som önskat. När en knapp trycks ner så ska dioderna vara tände och när man släpper ska de vara släckta. Pröva alla knappar. När dioderna blinkar nästa gång så är kalibreringen klar och dosan kan börja användas.
En av knapparna används som aktiveringsknapp denna var från början till för att inte tiltsensorerna skulle trigga när man inte ville det. Denna är lite överflödig men den ligger fortfarande kvar dock så för att använda de tre övriga knapparna måste aktiveringsknappen vara intryckt.

Följande funktioner finns just nu inprogrammerade:
-Tryck på aktiveringsknappen för att tända dioderna
-Håll in aktiveringsknappen och sen släpp den för att släcka dioderna
-Tryck en gång på knapp för att välja kanal 1
-Tryck två gånger på knapp 1 för att välja kanal 2
-Tryck tre gånger på knapp 1 för att välja kanal 3
-Tryck en gång på knapp 2 för att välja kanal 4
-Tryck två gånger på knapp 2 för att välja kanal 5
-Tryck tre gånger på knapp 2 för att välja kanal 6
-Tryck en gång på knapp 3 för att välja kanal 7
-Tryck två gånger på knapp 3 för att välja kanal 8
-Tryck tre gånger på knapp 3 för att välja kanal 9
-Tryck på knapp ett sen två för att sappa ner en kanal
-Tryck på knapp två sen ett för att sappa upp en kanal
-Tryck på knapp 3 sen 2 och sen håll in knapp 2 för att höja volymen till knapp 2 släpps
-Tryck på knapp 2 sen 3 och sen håll in knapp 3 för att sänka volymen till knapp 3 släpps


Hela programmet består av följande filer:

main.c
touchButtons.c
touchButtons.h
command.c
command.h
LEDs.c
LEDs.h
macros.h

Nedan följer en övergripande förklaring till filerna. Källkoden hittas i Appendixet.

macros
Några macron för att sätta och läsa av värdet på främst I/O pinnar.

LEDs
Innehåller funktioner för att tända och släcka dioderna. Finns också en funktion för att blinka med dioderna. Finns även en initierings funktion som måste köras för att det ska gå att använda dioderna.

touchButtons
Innehåller funktioner för att genomföre en learning mode process, testa knapparna, aktivera/inaktivera de 3 kommando knapparna och en initierings funktion.

command
Denna fil innehåller minnet som lagrar vilken knappkombination som har tryckts in. Den innehåller även en funktion för att från knappkombination få fram vilket kommando som ska skickas till tv:n. Slutligen finns det även en funktion som skickar kommandon till tv:n.

main
Väldigt mycket kontrolleras via interrupts i programmet. Main innehåller alla interruptfunktioner. I main sker också all tidkontroll som håller reda på om man t.ex. har tryckt på en knapp snabbt eller hållit ner knappen.



Resultat


Strömförbrukning
Eftersom tanken var att använda denna som lampa är strömförbrukningen intressant att titta på. Här följer lite mätvärden på strömförbrukningen på hela dosan.

Dioder släckta & u-controller ej i power save mode 18,3mA
Dioder släckta & u-controller i power save mode 6mA
Dioder tända & u-controller ej i power save mode 53,4mA
Dioder tända & u-controller i power save mode 41,4mA

Dessa värden är lite för höga och det beror på att när jag valde att inte kommunicera seriellt med touch sensorn så kan jag inte ställa in sleep mode på den vilket gör att den drar ca 5mA.

Storlek på dosan
Dosans storlek blev enligt min mening lite för stor. Den är nu 145x48x25mm och det är framför allt längden som är för lång. Som det är nu måste man sträcka sig för att nå alla knappar. Detta skulle kunna åtgärdas genom att göra kretskortet lite kortare och framför allt lägga batteriet åt andra hållet. Dosan blir då lite bredare men jag tycker det skulle vara bättre. Knapparna skulle även kunna göras mindre och sättas tätare.


Färdiga dosan
Slutligen så ser dosan ut så här.

Bild

Bild

Den blev ganska ful när det är ljust i rummet.

Bild

Men ganska häftigt om det blir mörkt. Dock så förstör knapparna lite.

Bild

Bild

Bild

Här finns även en film på när en kalibrerings sekvens utförs, kapparna testat och sen visas några av funktionerna.
http://www.youtube.com/watch?v=OGOmdmMCqHs

Appendix

c-koden

main.c

Kod: Markera allt

#include <avr/io.h>
#include <avr/sleep.h>
#include <command.c>
#include <touchButtons.c>

volatile unsigned int timerOwerflowCounter = 0;
volatile unsigned int lastTimerOwerflowCounter = 0;
volatile unsigned int IDLEcounter = 0;
unsigned char indexOfLastButtonPressed;

void initPWM(void)
{
	//This function is used to set up a 36kHz and a 1125Hz frequency pwm to be able to generate the signal the ir diod is sending to the tv. 
	
	cli();										//Disable interups, just to be safe

	CLKPR = 0x80;								//Clock Prescaler Change Enable, Enable to change the divider fot the master clock input to the MCU
	CLKPR = 0x00;								//Set the devision factor for the master clock to 1 which changes the frequency to 8MHz

	//pwm frequency = Systemclk/(N*TOP) = (8000000/(256*1)) = 31 250hz the total period time is 32us it should be 30us
	TCCR0A |= (1 << WGM01) | (1 << WGM00);		//Fast pwm mode, TOP=0xFF
	setBit(TCCR0B,CS00);						//clk prescaler 1
	OCR0A = 170;								//generate a 66% pwm or inverted 33%
	
	//pwm frequency = Systemclk/(N*TOP) = (8000000/((1+149)*64)) = 833.333333Hz which gives 1.2ms period time
	//pwm frequency = Systemclk/(N*TOP) = (8000000/((1+299)*64)) = 416.666667Hz which gives 2.4ms period time
	setBit(TCCR1A,WGM11);						//Fast pwm mode, TOP=ICR1
	TCCR1B |= (1 << WGM12) | (1 << WGM13);		//Fast pwm mode, TOP=ICR1
	TCCR1B |= (1 << CS11) | (1 << CS10);		//Clk prescaler 64
	OCR1A = 38;									//generate a 300us high pwm
	ICR1 = 299;									//Top value. IRC1=299 => 1, ICR1=149 => 0
		
	sei();										//Enable interupts
}

void initCounter(void)
{
	//Function for setting up the counter that keeps track of: If a button is pushed or hold. If sleep mode should be entered.
	
	cli();													//Disable interups, just to be safe	
	
	CLKPR = 0x80;											//Clock Prescaler Change Enable, Enable to change the divider fot the master clock input to the MCU
	CLKPR = 0x00;											//Set the devision factor for the master clock to 1 which changes the frequency to 8MHz

	//Timer/Counter mode of operation normal mode, frequency = clk/(2*N*(1+TOP)) = (7812.5/((1+255)*2*1)) = 15.2587891Hz = 15Hz
	TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);	//Deviding the clock by 1024, gives a frequency of 8MHz/1024 = 7812,5Hz = 0,128ms
	setBit(TIMSK2,TOIE2);									//Timer/Counter2 Overflow Interrupt Enable. Is capture by TIMER2_OVF_vect interupt vector function.

	sei();
}

void sleepNow(void)  
{       
	
	clearBit(EICRA,ISC00);					//To wake the u-controller the int0 needs to be set at level trigger interupt, press the activation button to leave sleep mode
	
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   	//Sleep mode is set here, Power down i  selected which is the one with the smalles power consumption
    sleep_enable();          				//Enables the u-controller to go into sleep mode 
    sleep_mode();            				//Here the device is actually put to sleep
    sleep_disable();         				//First thing after waking from sleep is to disable sleep
											
	setBit(EICRA,ISC00);					//Return to the old interupt trigger. Activation button -> Any logical change on INT0 generates an interrupt request.
}

int main(void)
{		

	initLEDs();
	initPWM();
	
	delayMs(30000);			//30sec delay for putting the remote controll together
	initTouchButtons();
	initCounter();
	
	while(1)
	{	
		if(numberOfCommandsInMem && timerOwerflowCounter >= (lastTimerOwerflowCounter+NUMBER_OF_OVERFLOWS_FOR_A_BUTTON_CLICK)) //If there is commands to send and the time to press another button is expired, send the message.
		{	
			unsigned int commandToSend;
			commandToSend = getCommandToSendFromMem();			//Get the command that should be sent to the tv
			diodsOn();
			
			if(commandToSend == COMMAND_VOLUME_UP){
				while(!getBit(PINC,INV_MIDDLE_BUTTON)){
					sendCommandToTV(commandToSend);				//Special volume command that is sent as long as the button is hold down.
				}
			}
			else if(commandToSend == COMMAND_VOLUME_DOWN){
				while(!getBit(PINC,INV_BOTTOM_BUTTON)){
					sendCommandToTV(commandToSend);				//Special volume command that is sent as long as the button is hold down.
				}
			}
			else{
				sendCommandToTV(commandToSend); 				//Send command to the TV
			}
			IDLEcounter = 0;
		}
		else
		{
			if(IDLEcounter > 450)								//If nothing happand for 30 sec go into sleep mode 
			{
				sleepNow();	
				IDLEcounter = 0;
			}
		}
	}
}

SIGNAL(TIMER1_COMPA_vect)
{
	clearBit(TCCR0A,COM0A1);						//Normal port operation
	setBit(PORTD,INV_IR_LED);						//Turn off the IR-diod
	if(getBit(commandToSend,isSendingBitNumber))
	{
		ICR1 = 299;									//Set the ICR1 to get the right length. IRC1=299 => 1, ICR1=149 => 0
	}
	else
	{
		ICR1 = 149;									//Set the ICR1 to get the right length. IRC1=299 => 1, ICR1=149 => 0
	}
	isSendingBitNumber++;							//Move to next bit
	if(isSendingBitNumber > 16)						//If all bits are sent.
	{
		clearBit(TIMSK1,TOIE1);						//Disable the owerflow interrupt
		clearBit(TIMSK1,OCIE1A);					//Disable Output Compare A Match Interrupt
		isSendingBitNumber = 0;	
		isSending = 0;
	}
}

SIGNAL(TIMER1_OVF_vect)
{
	setBit(TCCR0A,COM0A1);						//Clear OC0A on Compare Match, set OC0A at TOP
}

SIGNAL(INT0_vect)
{
	IDLEcounter = 0;
	diodsOn();
	if(!getBit(PIND,INV_ACTIVATION_BUTTON))	//Check if the activation button is pressed.
	{
		timerOwerflowCounter = 0;	
		enableChannelButtonsInterrupt();		// Enable Pin Change Interrupt on the 3 front buttons
	}
	else
	{
		disableChannelButtonsInterrupt();		//Disable Pin Change Interrupt on the 3 front buttons
		if(timerOwerflowCounter > NUMBER_OF_OVERFLOWS_FOR_A_BUTTON_CLICK) //If the activation button is hold for a long time and than released the diods should be turned off.
		{
			diodsOff();
		}
	}
}

SIGNAL(PCINT1_vect)
{
	//Determines which front button that is pressed and then store that in the command memory.

	IDLEcounter = 0;
	if(!getBit(PINC,INV_TOP_BUTTON))								
	{
		diodsOn();
		lastTimerOwerflowCounter = timerOwerflowCounter;
		indexOfLastButtonPressed = INDEX_INV_TOP_BUTTON;
	}
	else if(!getBit(PINC,INV_MIDDLE_BUTTON))
	{
		diodsOn();
		lastTimerOwerflowCounter = timerOwerflowCounter;
		indexOfLastButtonPressed = INDEX_INV_MIDDLE_BUTTON;	
	}
	else if(!getBit(PINC,INV_BOTTOM_BUTTON))
	{
		diodsOn();
		lastTimerOwerflowCounter = timerOwerflowCounter;
		indexOfLastButtonPressed = INDEX_INV_BOTTOM_BUTTON;	
	}
	else
	{
		diodsOff();
		if(timerOwerflowCounter < (lastTimerOwerflowCounter+NUMBER_OF_OVERFLOWS_FOR_A_BUTTON_CLICK))
		{
			addCommandToCommandMem(indexOfLastButtonPressed);			//Store the command in the memory
			lastTimerOwerflowCounter = timerOwerflowCounter;
		}
	}
}

SIGNAL(TIMER2_OVF_vect)
{
	//Increase the counters
	timerOwerflowCounter++;
	IDLEcounter++;
}

command.h

Kod: Markera allt

#define COMMAND_CHANNEL_0 0b0000100101000111
#define COMMAND_CHANNEL_1 0b0000000001000111
#define COMMAND_CHANNEL_2 0b0000100001000111
#define COMMAND_CHANNEL_3 0b0001000001000111
#define COMMAND_CHANNEL_4 0b0001100001000111
#define COMMAND_CHANNEL_5 0b0010000001000111
#define COMMAND_CHANNEL_6 0b0010100001000111
#define COMMAND_CHANNEL_7 0b0011000001000111
#define COMMAND_CHANNEL_8 0b0011100001000111
#define COMMAND_CHANNEL_9 0b0000000101000111
#define COMMAND_CHANNEL_UP 0b0000101001000111
#define COMMAND_CHANNEL_DOWN 0b0001001001000111
#define COMMAND_VOLUME_UP 0b0010001001000111
#define COMMAND_VOLUME_DOWN 0b0010101001000111
#define COMMAND_TELE_TEXT 0b0000011001000111

#define NUMBER_OF_OVERFLOWS_FOR_A_BUTTON_CLICK 15

volatile unsigned char numberOfCommandsInMem = 0;
volatile unsigned char commandMem[3];
volatile unsigned char addToCommandMemEnable = 1;
volatile unsigned int commandMemCounter = 0;
volatile unsigned int commandToSend;
volatile unsigned char isSendingBitNumber = 0;
volatile unsigned char isSending = 0;
command.c

Kod: Markera allt

#include <avr/interrupt.h>
#include <command.h>
#include <macros.h>

void addCommandToCommandMem(unsigned char index)
{
	//Add command to memory if enable
	if(addToCommandMemEnable)				
	{
		commandMem[numberOfCommandsInMem] = index;	
		numberOfCommandsInMem++;
	}
}

unsigned int getCommandToSendFromMem(void)
{
	//This is the function which converts the button sequence to a command that could be sent to the tv. A sort of database
	if(commandMem[0] == 1)
	{
		commandMem[0] = 0;
		if(commandMem[1] == 1)
		{
			commandMem[1] = 0;
			if(commandMem[2] == 1)
			{
				commandMem[2] = 0;
				return COMMAND_CHANNEL_3;
			}
			return COMMAND_CHANNEL_2;
		}
		else if(commandMem[1] == 2)
		{
			commandMem[1] = 0;
			return COMMAND_CHANNEL_DOWN;
		}
		return COMMAND_CHANNEL_1;
	}
	else if(commandMem[0] == 2)
	{
		commandMem[0] = 0;
		if(commandMem[1] == 2)
		{
			commandMem[1] = 0;
			if(commandMem[2] == 2)
			{
				commandMem[2] = 0;
				return COMMAND_CHANNEL_6;
			}
			return COMMAND_CHANNEL_5;
		}
		else if(commandMem[1] == 1)
		{
			commandMem[1] = 0;
			return COMMAND_CHANNEL_UP;
		}
		else if(commandMem[1] == 3)
		{
			commandMem[1] = 0;
			return COMMAND_VOLUME_DOWN;
		}
		return COMMAND_CHANNEL_4;
	}
	else if(commandMem[0] == 3)
	{
		commandMem[0] = 0;
		if(commandMem[1] == 3)
		{
			commandMem[1] = 0;
			if(commandMem[2] == 3)
			{
				commandMem[2] = 0;
				return COMMAND_CHANNEL_9;
			}
			return COMMAND_CHANNEL_8;
		}
		else if(commandMem[1] == 2)
		{
			commandMem[1] = 0;
			return COMMAND_VOLUME_UP;
		}
		return COMMAND_CHANNEL_7;
	}
	else
	{
		return 0b0101010101010101;				//This sequence is returned if the sequence doen't exist. For debugging only
	}
}

void sendCommandToTV(unsigned int command)
{
	/*Disable all interrupts except the two counters for the signal to tv*/
	clearBit(PCMSK0,PCINT1);				//Pin Change Interrupt Disable for the touch buttons
	
	numberOfCommandsInMem = 0;
	commandToSend = command;
	
	for(int i=0;i<3;i++)					//Sending 3 times to get higher probabillity for the command to reach the tv
	{
		isSending = 1;
		TCNT1 = OCR1A+1;					//Set the clock so that overflow interrupt occurs before compare interupt
		setBit(TIFR1,OCF1A);				//Clear the interupt bit so that the interrupt doesn't executes until a new interrupt occurs
		setBit(TIMSK1,OCIE1A);				//Enable the Output Compare A Match Interrupt
		setBit(TIFR1,TOV1);					//Clear the interupt bit so that the interrupt doesn't executes until a new interrupt occurs
		setBit(TIMSK1,TOIE1);				//Enable the owerflow interrupt
		while(isSending);
	}
	
	setBit(PCMSK0,PCINT1);					//Pin Change Interrupt Enable for the touch buttons
}
touchButtons.h

Kod: Markera allt

#define INV_SETUP_TOUCH_SENSOR PC0
#define MEAS_TOUCH_SENSOR PC1
#define INV_RESET_TOUCH_SENSOR PC2
#define INV_BOTTOM_BUTTON PC3
#define INV_MIDDLE_BUTTON PC4
#define INV_TOP_BUTTON PC5

#define INV_ACTIVATION_BUTTON PD2
#define CHG_TOUCH_SENSOR PD2

#define INDEX_INV_TOP_BUTTON 1
#define INDEX_INV_MIDDLE_BUTTON 2
#define INDEX_INV_BOTTOM_BUTTON 3
LEDs.h

Kod: Markera allt

#include <util/delay.h>

#define INV_LED0 PB0
#define INV_LED1 PB1
#define INV_IR_LED PD6
touchButtons.c

Kod: Markera allt

#include <touchButtons.h>
#include <LEDs.c>

void testTouchButtons(unsigned int sec)
{
	//Used to test the touch buttons. If a button is pressed the diods will be on. If no button is pressed the diods will be off.

	for(int i=0;i<(100*sec);i++)
	{
		if(!(getBit(PINC,INV_TOP_BUTTON)) | !(getBit(PIND,INV_ACTIVATION_BUTTON)) | !(getBit(PINC,INV_MIDDLE_BUTTON)) | !(getBit(PINC,INV_BOTTOM_BUTTON)))
		{
			diodsOn();
		}
		else
		{
			diodsOff();
		}
		delayMs(10);
	}
	diodsOff();
}

void learningModeTouchSensor(void)
{
	//This is the learning sequence for the touch sensors

	toggleDiods(4,500);
	diodsOff();
	
	clearBit(PORTC,INV_SETUP_TOUCH_SENSOR);			//Initiate teaaching
	delayMs(10);
	while(getBit(PIND,CHG_TOUCH_SENSOR));
	diodsOn();														
	while(!(getBit(PIND,CHG_TOUCH_SENSOR)));		//During this period press each button 3 times.
	setBit(PORTC,INV_SETUP_TOUCH_SENSOR	);			//Do this to not enter theaching mode again after this teaching sequence
	while(getBit(PIND,CHG_TOUCH_SENSOR));
	while(!(getBit(PIND,CHG_TOUCH_SENSOR)));		//After this the teaching ends

	toggleDiods(4,500);
	
	testTouchButtons(5);							//Test to be sure that the calibration worked

	toggleDiods(4,500);
}

void disableChannelButtonsInterrupt(void)
{
	clearBit(PCMSK1,PCINT13);		//Top button -> Disable Pin Change Interrupt
	clearBit(PCMSK1,PCINT12);		//Middle button -> Disable Pin Change Interrupt
	clearBit(PCMSK1,PCINT11);		//Bottom button -> Disable Pin Change Interrupt
}

void enableChannelButtonsInterrupt(void)
{
	setBit(PCMSK1,PCINT13);		//Top button -> Enable Pin Change Interrupt
	setBit(PCMSK1,PCINT12);		//Middle button -> Enable Pin Change Interrupt
	setBit(PCMSK1,PCINT11);		//Bottom button -> Enable Pin Change Interrupt
}

void initTouchButtons(void)
{
	cli();

	/*Set output pins*/
	setBit(DDRC,MEAS_TOUCH_SENSOR);
	setBit(DDRC,INV_RESET_TOUCH_SENSOR);
	setBit(DDRC,INV_SETUP_TOUCH_SENSOR);
	
	/*Set input pins*/
	clearBit(DDRC,INV_BOTTOM_BUTTON);
	clearBit(DDRC,INV_MIDDLE_BUTTON);
	clearBit(DDRD,INV_ACTIVATION_BUTTON);
	clearBit(DDRC,INV_TOP_BUTTON);
	clearBit(DDRC,CHG_TOUCH_SENSOR);
	
	/*Activate internal pull-up for input pins*/
	setBit(PORTC,INV_BOTTOM_BUTTON);
	setBit(PORTC,INV_MIDDLE_BUTTON);
	setBit(PORTD,INV_ACTIVATION_BUTTON);
	setBit(PORTC,INV_TOP_BUTTON);
	
	/*Init values for output pins*/
	setBit(PORTC,MEAS_TOUCH_SENSOR);
	setBit(PORTC,INV_RESET_TOUCH_SENSOR);
	setBit(PORTC,INV_SETUP_TOUCH_SENSOR);
	
	/*Calibration sequence for the buttons at start-up*/
	learningModeTouchSensor();
	
	/*Activate interrupt for the activation button and enable the other interupt but don't activate it*/
	setBit(EICRA,ISC00);		//Activation button -> Any logical change on INT0 generates an interrupt request.
	setBit(EIMSK,INT0);			//Activation button -> Activate the external pin interrupt for INT0. 
	setBit(PCICR,PCIE1);		//For the 3 buttons on the front -> Pin Change Interrupt Enable 1
	
	sei();
}
LEDs.c

Kod: Markera allt

#include <LEDs.h>

void delayMs(unsigned int mSec)
{
	for(int i=0;i<mSec;i++)
	{
		_delay_ms(1);
	}
}

void initLEDs(void)
{
	/*Set output pins*/
	setBit(DDRB,INV_LED0);
	setBit(DDRB,INV_LED1);
	setBit(DDRD,INV_IR_LED);
	
	/*Init values for output pins*/
	setBit(PORTB,INV_LED0);
	setBit(PORTB,INV_LED1);
	setBit(PORTD,INV_IR_LED);
}

void toggleDiods(unsigned int NumberOfTimes, unsigned int msBetweenToggle)
{
	//Function that is used for toggle the diods 
	for(int i=0;i<NumberOfTimes;i++)
	{
		flipBit(PORTB,INV_LED0);
		flipBit(PORTB,INV_LED1);
		delayMs(msBetweenToggle);	
	}
}

void diodsOn(void)
{
	clearBit(PORTB,INV_LED0);
	clearBit(PORTB,INV_LED1);
}

void diodsOff(void)
{
	setBit(PORTB,INV_LED0);
	setBit(PORTB,INV_LED1);
}
macros.h

Kod: Markera allt

#define getBit(pin,bit_vector) ((pin) & (1<<bit_vector))
#define setBit(port,bit_vector) ((port) |= (1<<bit_vector))
#define clearBit(port,bit_vector) ((port) &= ~(1<<bit_vector))
#define flipBit(port,bit_vector) ((port) ^= (1<<bit_vector))
Låst