EITF11 - Digitala projekt Hastighetsmätare Hugo Backmyr 2015-05-17
Inledning Syftet med kursen är att illustrera industriellt utvecklingsarbete. Målet med projektuppgiften är en prototyp för vidareutveckling med nödvändig dokumentation. Huvuddelen av kursen består i att konstruera, bygga och testa respektive konstruktion. Så lyder institutionens kortfattade beskrivning av kursen EITF11 Digitala projekt. I kursen har vi fått pröva på att forma en idé som sedan omsatts till en ritning och tillslut en konstruktion med mjukvara. Helt enkelt en kort men innehållsrik produktutvecklingsprocess. Vi har arbetat med att utveckla en hastighetsmätare som mäter hastigheten över ett område mellan två ljusstrålar. Denna rapport beskriver processen i korthet från kravspecifikation till färdig och fungerande prototyp. Kravspecifikation Användningsområde Hastighetsmätaren skall mäta hastigheten av en leksaksbil som färdas på en bana. Krav Mätområden skall vara 10 x 10 cm. Hastighetsmätaren skall drivas av 5 V likström. Hastighetsmätaren skall använda sig av processorn AVR Mega 16. Användaren skall kunna välja om hastigheten skall skrivas ut i m/s eller km/h. Hastigheten skall skrivas ut med en decimals noggrannhet. Bilen får ej krocka med sensorerna eller själva hastighetsmätaren. Hastigheten skall kunna mätas oavsett vilket håll bilen kommer från. Hastighetsmätaren skall fungera i normal inomhusbelysning. Hastighetsmätaren skall kunna mäta hastigheter i intervallet 0 10 m/s. Hastighetsmätaren skall ha en felmarginal på högst + 5%. Hastigheten skall skrivas ut på en display inom 500 ms efter att bilen passerat den andra mätpunkten. Hårdvara Processor ATMega 16 microcontroller med 40 pin. Processorn har ett programmerbart flashminne med 16 kb vilket möjliggör lagring av den mjukvara som programmerats för att processen skall fungera. Display Alfanumerisk display. Används för att skriva ut information till användaren. Dioder Två st IR dioder med smalt riktat IR ljus används tillsammans med två st fotodioder som har sitt maximum för IR ljus. Dessa fyra dioder skapar två ljusgrindar som avgränsar det område som hastigheten mäts över. 1
Knappar En knapp används för att göra det möjligt för användaren att växla utmatningsenhet mellan m/s och km/h. Knappen är en strömbrytare som är låg när knappen inte är nedtryckt och hög när knappen är nedtryckt. Resistorer Totalt används fem stycken resistorer. Två st vid IR dioderna (50 ohm), två st vid fotodioderna (100 kohm), en vid knappen (10 kohm). Mjukvara Programvaran skrevs i programmeringsspråket C och utvecklningsmiljön Atmel Studio 6.1 användes. Med hjälp av Atmel Studio och JTAG kunde programmet felsökas och rättas rad för rad. Utförande Förberedelser Efter att det beslutats vad som skulle konstruerats skrevs en kravspecifikation för den färdiga prototypen. Kravspecifikationen utvärderades och kraven omsattes i konkreta idéer på konstruktion och vilka komponenter som behövdes. Konstruktion För att ha något att bygga efter skapades en schematisk konstruktionsritning i programmet PowerLogic (se appendix 1). Med hjälp av databladen för de olika komponenterna reddes det ut hur de skulle kopplas samman med varandra. Efter att handledaren godkänt konstruktionsritningen tilldelades gruppen material och verktygslåda. En enkel skiss över hur komponenterna skulle placeras gjordes och sedan började bygget. Komponenterna löddes fast på byggplattan och utefter ritningen kopplades pinnarna samman på komponenterna. Programmering Programmeringen gjordes steg för steg för att kunna testa och felsöka var del av programmet för sig. Först av allt skrevs all kod som hanterar displayen. Vid uppstart måste displayen initieras genom en rad kommandon och när det är gjort krävs en del metoder för att göra utskrifter. Denna kod finns under rubriken Display i källkoden (appendix 2). I nästa steg skrevs alla metoder för de olika utskrifter som görs. Dessa metoder använder sig av display metoderna för att göra själva utskrifterna. Koden återfinns under rubriken Utskrifter i källkoden (appendix 2). Dessa två delar testades tillsammans för att se att allt kring displayen och utskrifter fungerade som avsett. 2
Efter detta var det dags att skriva den kod som hanterar själva hastighetsmätningen. Detta krävde en hel del eftertanke för att få det att bli rätt. Största problemet låg i att konvertera den uppmätta hastigheten som lagras i en double variabel till tre ASCII tecken som kan skrivas ut på displayen. Koden som rör hastighetsmätningen finns under Hastighet i källkoden (appendix 2). För att processorn skall reagera på knapptryckning och när ljusstrålarna bryts utnyttjades processorns interrupt funktioner. Processorn har tre st pinnar som kan användas för interrupt dessa utnyttjades till fotodiod 1, fotodiod 2 samt knappen. Interrupts var mycket smidiga att arbeta med. Efter att nödvändiga inställningar gjorts skrevs en metod för respektive interrupt, då ett avbrott genereras, tex genom att en ljusstråle bryts, går programmet automatiskt in i metoden för det avbrottet för att sedan återgå till loopen. Avbrottsmetoderna använder sedan de tidigare nämnda delarna av koden. Det sista som kodades var klockfunktionen som används för att mäta tiden mellan att de två ljusstrålarna bryts. Processorn har en inbyggd klockfunktion som användes. En 8 bits klocka med klockfrekevens på 1 MHz tillsammans med en prescaler på 8 användes. Detta innebär att ungefär 488 gånger i sekunden når klockans räknare sitt maximum och börjar om från noll. Samtidigt som det sker genereras ett overflow interrupt som utnyttjas för att räkna tiden. Denna inställning ger en hög noggrannhet på tidsmätningen, närmare bestämt en noggrannhet på 1/488 sekund. Koden för klockan återfinns under Klocka i källkoden (appendix 2). Genom hela kodningen var databladet för processorn mycket användbart för att ta reda på hur alla inställningar skulle göras för att få den funktionalitet som önskades. Testning och felsökning Hårdvara Det enda problem som stöttes på vad gäller hårdvaran gällde fotodioderna. Fotodioderna gav inte upphov till tillräckligt stor spänningsskillnad mellan då de var belysta och ej belysta. Detta skulle orsaka problem längre fram om det inte åtgärdades. Om skillnaden mellan de två lägena var för liten skulle inte processorn märka av skillnaden och det är avgörande för att hastighetsmätaren skall fungera. Efter mycket testning och felsökning visade det sig att allt grundade sig i en felkoppling. Dioden hade kopplats baklänges, anoden och katoden skulle byta plats. När denna ändring gjorts fungerade dioderna som avsett. Mjukvara I programmeringsfasen stöttes det på problem med displayen. Trots att rätt kommandon skickades till displayen blev det ofta fel i form av att inget skrevs ut eller att vissa tecken hoppades över i utskrifterna. Efter en del felsökning konstaterades att det berodde på att processorn arbetade snabbare än vad displayen kunde hantera, vissa kommandon föll alltså bort. Detta löstes genom att mellan varje kommando lades det in ett delay på 1 ms, då hann displayen med att hantera kommandona. Klockan orsakade också en del komplikationer. Till en början användes en för stor prescaler på 256 vilket resulterade i att noggrannheten blev för låg. Olika hastigheter kunde då ge upphov till samma 3
mätresultat trots att de tydligt skilde sig åt. Efter en hel del experimenterande konstaterades det att det var just prescalern som var problemet. När denna sattes till 8 istället för 256 fungerade mätningarna. Diskussion och slutsats Vi hade redan innan projektet startade bestämt oss för ett väl avgränsat projekt. Detta underlättade arbetet de första veckorna, genom att underlätta kravspecifikationerna och därmed sparade vi tid. Utmaningar Några utmaningar som är värda att särskilt poängtera är vår brist på programmeringsvana i C samt att vi inte arbetat med datablad tidigare. När det kommer till att tyda databladen för diverse komponenter så kräver detta en viss kunskap om begrepp, förkortningar och teoretiska samband för att kunna utläsa den information som man söker. Detta kunde stundom vara svårt eftersom våra kunskaper och samlade erfarenhet i ämnet var väldigt begränsad sedan tidigare. Men eftersom det var en av de större utmaningarna så var det även här vi lärde oss som mest. Vad gäller programmeringsspråket C så var likheterna med Javaprogrammering stora eftersom vi endast skrev mycket enkel kod i C. Javaprogrammering har vi erfarenhet av från tidigare programmeringskurser och detta underlättade betydligt i programmeringsfasen. Förbättringspotential Om vi hade gjort om projektet så hade vi lagt mer fokus på att få tag i IR dioder som klarar av ett större avstånd mellan IR diod och fotodiod. Detta hade kunnat fås med hjälp av en ännu smalare ljuskägla samt att matchningen mellan IR diodens våglängd och den våglängd som fotodioden är allra känsligast för stämmer ännu bättre överens än i nuläget. Avståndet mellan dioderna hade då kunnat ökas och fler föremål hade kunnat mätas. Den nuvarande konstruktionen medger bara ett avstånd på ca 5 cm mellan dioderna utan att signalen blir för svag. Kravspecifikationer Vår hastighetsmätare uppfyller alla de krav som vi har specificerat för den här ovan. Dock är det svårt att verifiera noggrannheten i mätningarna eftersom vi inte har tillgång till ett föremål i rätt form och storlek som rör sig med en känd hastighet. Slutsats Man kan konstatera att elektronik och digitalteknik vid en första anblick kan verka mycket komplicerat och lite skrämmande. Dock kan man reltivt lätt med hjälp av tidigare kunskaper inom andra ämnen, internet och handledning från kunniga handledare ta sig förbi de första hindren och förstå en hel del. Så mycket att man faktiskt kan konstruera en enkel konstruktion som fungerar. Detta har varit ett mycket lärorikt projekt inom ett för oss helt nytt område. 4
Appendix 1 - Kopplingsschema 5
Appendix 2 - Källkod /* * speedcalc_code.c * * Created: 2015 04 15 11:27:19 * Author: digpi08 */ #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> //GLOBALA VARIABLER int unitmode; // 0 = m/s, 1 = km/h. int state; // 0 = mäter ej, 1 = mäter. double lastspeed; volatile uint8_t count; double measuredtime = 0; //DISPLAY void display_setup() { //Function set DDRA = 0xFF; DDRD = 0xE0; PORTD = 0x20; PORTA = 0x38; PORTD = 0x00; PORTD = 0x20; // Display ON PORTA = 0x0C; PORTD = 0x00; PORTD = 0xA0; void display_ready() { 6
display_write(); display_writech('r'); display_writech('e'); display_writech('a'); display_writech('d'); display_writech('y'); void display_switche() { PORTD = 0x00; PORTD = 0xA0; void display_clear() { display_command(); PORTA = 0x01; PORTD = 0x00; void display_writech(char ch) { PORTA = ch; PORTD = 0xA0; display_switche(); void display_command() { PORTD = 0x20; void display_write() { PORTD = 0xA0; //HASTIGHET void speedsetup() { state = 0; lastspeed = 0; unitmode = 0; void measurespeed() { if (state == 1) { stopclock(); lastspeed = (0.116 / (measuredtime/488.281)); 7
state = 0; charconvertspeed(); return; if (state == 0) { startclock(); state = 1; double tokmh(double ms) { double kmh = ms * 3.6; return kmh; void charconvertspeed() { double v; if (unitmode == 0) { v = lastspeed; if (unitmode == 1) { v = tokmh(lastspeed); int num1 = v/10; int vint = (int) v; int num2 = (vint % 10); int dec = (v (num1*10 + num2))*10; char cnum1 = (char) (((int) '0') +num1); char cnum2 = (char) (((int) '0') +num2); char cdec = (char) (((int) '0') +dec); printspeed(cnum1, cnum2, cdec); void switchunit() { if (unitmode == 0) { unitmode = 1; return; if (unitmode == 1) { unitmode = 0; //UTSKRIFTER 8
void printspeed(char cnum1, char cnum2, char cdec) { display_clear(); display_write(); display_writech('s'); display_writech('p'); display_writech('e'); display_writech('e'); display_writech('d'); display_writech(':'); display_writech(' '); display_writech(cnum1); display_writech(cnum2); display_writech('.'); display_writech(cdec); display_writech(' '); if (unitmode == 0) { display_writech('m'); display_writech('/'); display_writech('s'); if (unitmode == 1) { display_writech('k'); display_writech('m'); display_writech('/'); display_writech('h'); _delay_ms(3000); void printunit() { display_clear(); display_write(); if (unitmode == 0) { display_writech('u'); display_writech('n'); display_writech('i'); display_writech('t'); display_writech(':'); display_writech(' '); display_writech('m'); display_writech('/'); display_writech('s'); if (unitmode == 1) { 9
display_writech('u'); display_writech('n'); display_writech('i'); display_writech('t'); display_writech(':'); display_writech(' '); display_writech('k'); display_writech('m'); display_writech('/'); display_writech('h'); //INTERRUPT void setupinterrupt() { //sie(); //Enable global interrupt. MCUCR = MCUCR 0b00001111; //Sense control INT0,1 > rising edge. MCUCSR = MCUCSR 0b01000000; //Sense control INT2 > rising edge. GICR = GICR 0b11100000; //Enable interrupt 0, 1, 2. sei(); ISR(INT0_vect) { measurespeed(); ISR(INT1_vect) { measurespeed(); ISR(INT2_vect) { switchunit(); printunit(); _delay_ms(1000); charconvertspeed(); ISR(TIMER0_OVF_vect) { count++; //KLOCKA 10
void clocksetup() { TCCR0 = TCCR0 0b00000010; //Prescaler 8. TIMSK = TIMSK 0b00000001; // Enable overflow interrupt. void startclock() { count = 0; void stopclock() { measuredtime = count; int main(void) { display_setup(); setupinterrupt(); clocksetup(); speedsetup(); while(1) { display_ready(); _delay_ms(500); display_clear(); _delay_ms(500); 11