AVR Chronograph från koncept till PCB (6 / 13 steg)

Steg 6: Koden



Nu till hjärnan hos projektet! Koden.

Jag ska klistra in instruktionerna här ett avsnitt i taget och kortfattat förklara vad de gör. Kom ihåg "C programmering för Microcontrollers" för en mer djupgående tutorial förklarar användningen av operatörer och sådant. ATMega328P databladet är också en bra referens för att lära sig syftet med ett register eller en bit. Jag ska försöka hålla denna del kort, men informativ. Lärande C är ett projekt av sig själv, så bli inte avskräckt om du få helt förlorad först! Jag vet att jag gjorde.

Den faktiska koden på denna sida kommer att vara i fetstil att göra det lättare att urskilja från text... Kommentarer kommer kontrollelementet.

/*
* Speed_measurement.c
*
* Skapat: 9/15/2012 8:50:23 PM
* Författare: Thomas L
*/

#define F_CPU 14.7456E6

#include < avr/io.h >
#include < util/delay.h >
#include < avr/interrupt.h >

Dessa första raderna ge bara kompilatorn lite information om vilka filer det behov och den CPU klockfrekvensen. Vi kommer att behöva använda vissa förseningar senare göra AVR bara vänta, så vi måste omfatta filen util/delay.h. Filen innehåller de faktiska försening funktioner som vi kallar i huvudprogrammet. Dröjsmål funktionerna leverera en tidsinställd försening, så kräver de oss att ge CPU klockfrekvens. #define F_CPU berättar kompilatorn hastigheten på kristalloscillator och detta nummer ges sedan för alla funktioner som behöver använda den.

/************************************************************************/
/ * förklara globala variabler * /
/************************************************************************/

unsigned int resultatet = 0;
int interruptcount = 0;
långa int tid = 0;
långa int resetcount = 0;

Detta är globala variabeldeklarationer. Om vi vill tilldela ett värde till en variabel, måste vi tilldela variabeln en först. Till exempel 'int' för heltal och är ett helt tal representeras av 16 bitar (15 antal bitar och lite tecken.) 'Unsigned int' är också ett heltal, men med ingen teckenbiten. Detta innebär att det kan bara vara positivt, men det kan vara dubbelt så stor på grund av den extra nummer lite. Dessa variabler behövs i funktionen Main () samt avbrott rutiner, så jag förklarade dem här innan funktionen Main () börjar. Detta gör dessa variabler globala, vilket innebär att de kan öppnas och ändras av någon del av programmet.

Även är på denna punkt det viktigt att observera att varje uttryck måste avsluta med ett semikolon!!! Jag glömmer dessa hela tiden, och kompilatorn ger mig fel... hela tiden.

int main(void)
{

DDRD = 0X00; //portd ingång för extern avbryter
DDRC = 0xDF; //portc utgång för 7 segment multiplexing och 1 ingång för avstånd display
DDRB = 0xFF; //portb utdata fot 7 segment i bcd
PORTD | = 0xFF; //enable portd dra upp motstånd
//(int0 and int1 require external pull up resistors or 1 interrupt will be triggered at reset)

Nu har vi funktionen Main (). Detta är början av själva programmet, och det börjar genom att konfigurera några saker. De DDRn rapporterna ovan tilldela värden till Data riktning registrerar för 3 portar. Dessa 8-bitars register konfigurera I/O pins som antingen ingångar och utgångar. En 1 i en bit ståndpunkt kommer att göra motsvarande stift en utgång och en 0 blir det en ingång. De värden som kan vara i decimaltal, binär eller hexadecimalt. Decimaltal skrivs normalt som 255. Binär och hex har ett prefix till tala kompilatorn vilken typ av nummer det är. Binär skulle vara 0b11111111. Hex vore 0xFF. Alla tre av dessa är lika med 255 och kompilatorn vård inte som du använder. Hex är normalt antal system val eftersom det är lättare att läsa än binärt, och lättare att relatera till binär än decimal... Binära motsvarande av de nummer som tilldelas varje DDR är 8 bitar som representerar data riktning 8 stift i hamnen. PORTD | = FF; tilldelning kan pull-up motstånd på de ingående stift. Om PORTD var konfigurerad som en utgång, skulle då uppdraget ändra utdata påstår av de stift

Lär dig din HEX till binära omvandlingar och engagera dem i minnet! Det är praktiskt...

En HEX Självstudiekurs: http://www.microbuilder.eu/Tutorials/Fundamentals/Hexadecimal.aspx ***

EICRA | = (1 << ISC11) | (1 << ISC01) ; //configure externa avbryter
EIMSK | = (1 << INT1) | (1 << INT0);
TCCR1B | = (1 << CS12);
//set prescaling för timer1 256

Denna den delen där jag brukar ha att dra ut i databladet för AVR och göra lite grävande. EICRA är den externa avbryta kontroll registrera A. Detta register har 4 bitar vi kan ange för att konfigurera hur avbrott utlöses av yttre mellanjobb stiften INT0 och INT1. 4 bitar har ett unikt namn som identifierar det när ställa in eller rensa bitar. ISC00 och ISC01 är avbrott känsla kontroll för INT0. Anger vilken typ av ändring krävs på INT0 stiftet att utlösa avbrottet. ISC10 och ISC11 är samma kontroll för INT1 stift. Kontrollera i databladet visar att genom att sätta ISC01 och ISC11, vi konfigurerat båda avbrott stift att utlösa i utkanten negativa kommer bara. Uttrycket (1 << ISC11) är lite SKIFT operatören att ställa in den ISC11 biten. Se "C programmering för Microcontrollers" att lära sig allt om aktörer och lite manipulation; Det är lite för mycket att gå in på här.

EIMSK (extern avbryta MaSK) registret har två bitar för att aktivera eller inaktivera externa avbrotten. Båda av dessa bitar måste anges eller avbryta rutiner kommer aldrig köra.

TCCR1B (Timer/Counter kontroll registrera 1 B) har flera bitar som kan konfigurera hur timern ska fungera. Allt vi behöver tänka på här är det prescaling värdet. Detta betyder bara, hur många CPU klocka pulser räknas innan timern ökas med 1. I det här fallet valde jag ett prescale värde av 256, företrädd av att CS12 biten (kolla databladet.) Detta delar CPU klockan med 256 innan det för timer/counter hjälper till att förhindra counter svämmar över i den tidsram som vi försöker mäta.

/************************************************************************/
/ * deklarera variabler för beräkning och * /
/************************************************************************/


unsigned int de = 0;
unsigned int tior = 0;
unsigned int hundratals = 0;
unsigned int x = 0;
Double ticsfoot = 0;
dubbel fps = 0;
dubbel fph = 0;
dubbel mph = 0;
dubbel km/h = 0;
Dubbelrum m/s = 0;
int avstånd = 0;

SEI(); //enable global avbryter

OK, en lätt avsnitt! Detta är bara några mer variabeldeklarationer och att sei() sak. "sei()" är ett kommando som känt att kompilatorn att betyda "Aktivera global avbrott." Inget avbrott kommer att utlösa om denna bit inte har angetts. "cli()" rensar lite inaktivera globala avbrott. Vi kommer att använda som man senare för att förebygga problem.

While(1)
{

/************************************************************************/
/ * få sensorn avståndet i fötter från pind 0,1,4,5 * /
/************************************************************************/

int distanceinput = (~ PIND & 0x33);
int hibits = (distanceinput >> 2);
//getting pind bitar 0,1 och 4,5 tillsammans för att
int lobits = (distanceinput & 0x03); //distance värde i BCD. bitar 2,3 är den
avstånd = (hibits + lobits); //ext avbrott stift redan.

om (avstånd == 0) avstånd = 16.

Detta lilla avsnitt läser status för 4 dip-switchar som möjligt för användaren att välja avståndet mellan sensorerna. Det finns vissa bitar manipulering pågår här eftersom de 4 ingångarna inte i följd. De variabler 'hibits' och 'lobits' är bara tillfällig lagring platser att sortera ut bitarna. Den första raden isolerar de bitarna som vi vill med den & operatör. Den andra raden skiftar bitarna över 2 ställen få den Hej bitar i rätt position. Tredje raden isolerar endast den lo bitar. Sedan i den fjärde raden i lo och Hej bitar läggs till göra dem ett nummer. Uttrycket "if" det hindrar i slutet användaren från att välja '0' och orsakar en "dela med noll" tillstånd senare.

/************************************************************************/
/ * "redo" indikator LED * /
/************************************************************************/


om (interruptcount == 0)
{
PORTC | = (1 << 3);
}
annat
{
PORTC & = (0 << 3);
}

Det här avsnittet får reda på om det finns en mätning i framsteg, och lyser klar ledde om inte. Variabeln interruptcount ökas i de avbryta rutinerna att hjälpa programmet hålla reda på vilken del av mätningen pågår. Vi får se de avbryta rutinerna i slutet av koden.

/************************************************************************/
/ * beräkningar för att hitta hastigheten i 4 enheter * /
/************************************************************************/

om (interruptcount == 2) //only beräkna när båda avbrott har inträffat
{
CLI();
//disable global avbryter

ticsfoot = (tid / avstånd);
//distance är avståndet mellan sensorer i fot - ticsfoot är counter tics eller foten
fps = (57600 / ticsfoot), //57600 är counter tics/SEK (cpu clk/prescaler)
fph = (fps * 60 * 60);
mph = (fph / 5280);
km/h = (km/h * 1.609344);
m/s = (fps * 0.3048);

EIMSK | = (1 << INT1) | (1 << INT0);
SEI();
//re-enable externa avbrott och globala avbryter
}

Om variabeln interruptcount spätt två gånger (detta sker i rutinerna som avbrott senare,) det betyder som båda avbrott har inträffat och det uppmätta värdet är redo att användas i beräkningarna. Här ser vi cli() instruktionen som inaktiverar avbrotten. Det är god praxis att göra detta när man läser en variabel som ett avbrott har förmågan att ändra. Varför? Normala 'int' variabler, till exempel är 16 bitar långa eller två byte. AVR är en 8-bitars system, så det tar två instruktioner att läsa 2 byte av ett enda värde. Det är möjligt, då för ett avbrott uppstå efter första byte läses, men innan andra. Om du försöker läsa en variabel men avbrottet inträffar när du har läst den första byten, kan rutinen avbrott ändra värdet på variabeln innan det är färdigt att läsa! Programmet kommer att återupptas där den slutade och läsa byten av variabeln med data som inte matchar den första byten! Då är det värde som du har laddat inte vad förväntas och kan orsaka problem.

Matten här är inte alltför komplicerat. Kommentarer bör vara tillräckligt för att räkna ut vad som händer. Det är där vi använder variabeln avstånd från dip-switchar. Variabeln tid kommer från rutinerna som avbrott senare.

På första avbryta inaktiveras själv i sin rutin så att den kan endast köras en gång innan mätningen är klar. Det är därför EIMSK instruktionen är det. Det aktiverar båda yttre avbrott så att oavsett vilket var första och fick funktionshindrade ska aktiveras igen.

/************************************************************************/
/ * välja utmatningsalternativ * /
/************************************************************************/

if (!( PIND & (1 << PIND6)) & & (PIND & (1 << PIND7))) //choose fot per sekund
{
Round(fps);
resultat = fps;
}
annars om (PIND & (1 << PIND6) & &! () PIND & (1 << PIND7)))
//choose meter/sekund
{
Round(MPS);
resultat = mps;
}
annars om (PIND & (1 << PIND6) & & (PIND & (1 << PIND7)))
//choose kilometer i timmen
{
Round(KPH);
resultat = km/h;
}
annat
//default miles/tim
{
Round(mph);
resultat = mph;
}

om (resultat > = 999) resultatet = 999;

Här hela avsnittet läser två sista dip-switchar för att bestämma önskad effekt enheter. Sedan lagras beroende på vilket värde behövs i variabeln resultatet.

Round () funktionen är ett enkelt sätt att få heltalsvärden som vår produktion utan att för mycket fel. Denna funktion ingår i math.h-filen som ingår i programmet av filen delay.h som vi ingår manuellt. Om du tittar till lösning explorer fönster på höger sida i ATMEL Studio, ser du projektet namn följt av "Beroenden" (märkt i bilden.) När du bygger lösningen för första gången (Klicka på bygga >> Build Solution) alla filer som ingår i programmet kommer att listas här. Du kan sedan öppna filen delay.h som vi ingår i början av koden och ta en titt på den. Om du bläddrar igenom det, bör du hitta uttrycket #include < math.h > nära toppen. Längre ner hittar du funktionen _delay_ms() som vi kallar för att skapa 1ms fördröja senare.

Den "om" i slutet förhindrar försöker visa ett tal som inte får plats på våra 3 skärmar.

/************************************************************************/
/ * dröjsmål att stoppa flera "2nd avbrott" utlöser * /
/ * utan att försena viktigaste kod * /
/************************************************************************/


ResetCount ++;

om ((resetcount > = 0x00FF) & & (interruptcount > = 2)) //resetcount övre gräns bestämmer dröjsmål
{ //before reset. 0x00FF ca 3 SEK
interruptcount = 0;
ResetCount = 0;
}

Denna del är (typ av) en timer gjord av den huvudsakliga while(1) loopen. Varje gång programmet loopar bortanför den här punkten, ökas resetcount. Beräkningarna sker bara om interruptcount är exakt 2. så, om det andra avbrottet inträffar igen brukar det röra till matten. Om interruptcount är 2 eller mer, kan inget annat mätas tills resetcount steg upp till 0x00FF ger ca 3 sekunder fördröjning för objektet att lämna sensorn området. Vi kan inte använda en normal fördröjning här eftersom vi behöver visar att visa oss det uppmätta värdet. När interruptcount återställs till 0, ready-lampan är tänd och nästa mätning kan göras.

/********************************************************************************/
/ * Visa int resultatet på 3 siffrig sju segment display * /
/ * försening ger sju segment dekoder tid att avkoda och Visa siffror * /
/********************************************************************************/

if (!( PINC & (1 << PINC5))) //to Visa avstånd inställningen på displayen
{ //only när knappen är nedtryckt
resultat = avstånd;
}
annat

Detta "om" klockor bara på knappen som är en begäran att få avståndet börvärdet visas på displayerna. Senaste fart beräkningen lagras in i resultatet varje gång avsnittet utdata enheter (ovan) körs i slingan, så resultatet endast är lika avstånd medan knappen trycks.

hundratals = (resultatet / 100), //Get 100-tals placera siffran
x = (resultatet % 100).
PORTB = (0x00|hundreds);
PORTC | = (1 << 2);
/ / Skriv siffran
_delay_ms(1);
PORTC & = (0 << 2);

TENS = (x / 10); / / få 10 's plats siffriga
x = x % 10.
PORTB = (0x00|tens);
PORTC | = (1 << 1);
/ / Skriv siffran
_delay_ms(1);
PORTC & = (0 << 1);

de = x; / / få 1: s plats siffriga
PORTB = (0x00|ones);
PORTC | = (1 << 0);
/ / Skriv siffran
_delay_ms(1);
PORTC & = (0 << 0);
}
}

Äntligen! i visningen! Nu när "resultat" är lika med ett giltigt 3 siffrigt decimal nummer, måste vi dela upp den i enstaka siffror och skicka dem till visar i sin tur. Vi får varje siffra och förvara den i en variabel kallas, tiotals eller hundratals. Hundratals placera siffran är lätt; bara dela av 100. Variabeln är en "int" typ, så ingenting efter decimalkommat lagras. "x" är en tillfällig variabel att lagra nästa behövs värde. Operatorn '%' inte uppdelningen, men Returnerar resten till "x". Detta ger oss den transeuropeiska nät och som siffran att hantera. Dividera det med 10 och upprepa för de. Det är ett enkelt sätt att separera alla dina siffror att gå till displayerna och mycket enkelt kan byggas ut för att arbeta med större siffror.

PORTB instruktionerna skriva siffrorna till produktionen stift kommer att 7-segment dekodern. Nästan samtidigt aktivera PORTC instruktionerna utdata till motsvarande bildskärmens marken transistor att lysa upp siffran. Sedan håller förseningen på displayen för 1ms innan du inaktiverar transistorn tillbaka och gå vidare till nästa siffra. Ganska coolt.

De avslutande klammerparenteserna avsluta While(1) loop kodblocket och avsluta kodblocket main () funktion.

/************************************************************************/
/ * sensor 1 avbrott * /
/************************************************************************/

ISR(INT0_vect)
{
om (interruptcount == 0)
{
TCNT1 = 0X0000;
//reset counter 0
interruptcount ++; //Increment avbrott räknas
EIMSK & = (1 << INT1) | (0 << INT0); //disable INT0

}
annat if (interruptcount == 1)
{
tid = TCNT1;
//capture räknarvärdet
interruptcount ++; //Increment avbrott räknas

}
annat resetcount = 0;

}

/************************************************************************/
/ * sensor 2 avbrott * /
/************************************************************************/

ISR(INT1_vect)
{
om (interruptcount == 0)
{
TCNT1 = 0X0000;
//reset counter 0
interruptcount ++; //Increment avbrott räknas
EIMSK & = (0 << INT1) | (1 << INT0); //disable INT1

}
annat if (interruptcount == 1)
{
tid = TCNT1;
//capture räknarvärdet
interruptcount ++; //Increment avbrott räknas

}
annat resetcount = 0;
}

Nu, de två avbryta rutiner... De är exakt samma med undantag för en instruktion, så jag ska bara tala om en.

Ser att avbryta rutinerna är utanför main () funktionen. Programmet helt slutar vad dess gör lämnar funktionen Main () och utför rutinen avbrott. Efter avbrottet returneras rutin är komplett, till Main ()-funktionen där den slutade.

Den första raden, ISR(INT0_vect), är namnet på avbrottet från databladet. Om avbrottet är aktiverat i registret över EIMSK och globala avbrott är aktiverade, programmet kommer att hoppa till den här raden när avbrottet utlöses.

Ser du att rutinerna som avbrott i princip består av en förening "om... ANNARS om "uttalande som bara gör några saker. Vanligtvis är det bäst att hålla avbrott rutiner mycket kort, ändra bara vad måste ändras och snabbt komma tillbaka till Main ()-funktionen. Använda variabeln interruptcount för att spåra antalet avbrott som har inträffat tillåter oss att göra rutinerna identiska, och kan även sensorer för att "se" target-objekt kommer från båda håll.

Oavsett vilken avbrott kör först återställer TCNT1 timern (den som vi satt prescaler för i början) sedan ökar variabeln interruptcount och sedan inaktiveras sig så att det inte kan vara gav extra gånger av en oregelbundet formad målobjekt.

Eftersom först avbryta ökat interruptcount, körs den andra en nästa par rader. Denna gång vi fånga värdet i timern TCNT1 och lagrar det i variabeln "tid" att användas i beräkningarna. Sedan ökas interruptcount igen.

Med interruptcount nu lika med 2, beräkningarna utförs och resetcount räknar upp till 0x00FF innan återställs allt för nästa mätning. Om det andra avbrottet utlöses igen av någon anledning, resetcount är satt till 0 och väntan börjar igen. Detta är hur jag förebygga flera utlösare av 2: a avbrottet för så länge som behövs för objektet vara ur vägen.

Usch, hoppas jag att åtminstone en liten känsla... I nästa steg kommer vi ladda programmet i våra chip och testa breadboarded kretsen!

Se Steg
Relaterade Ämnen

DIY ALLT I EN DATOR; Koncept till verklighet "komplett"

HejDess min första instructable. där jag ska försöka förklara visa dig hur du kan göra en billig alla i en PC.Men det är inte billigt men billigare än Orignal produkt veryyyyyyOKzzz min första bild är ett koncept bild förklara vad jag vill göra.Jag v...

Älskling, jag krympte Arduino: flytta från Arduino till ATtiny och skriva din kod i ren AVR-C

Arduino är en stor hård- och mjukvaruplattform, men med alla dess funktioner är det också lite uppsvälld och ofta behöver du bara en liten del av alla funktioner. Projektet kräver ibland för att ha mindre och billigare enheter. Vad du behöver är ATti...

Montering av Dragon Rider 500 för användning med AVR Dragon

inte länge sedan företaget Atmel kom ut med ett bra verktyg för användning med AVR microcontrollers kallas AVR Dragon. Denna lilla USB-enhet ger proffs och amatörer både förmågan att använda: I System programmering (ISP), JTAG, felsöka tråd och hög s...

Konvertera en NES gamepad till USB med Arduino

Hej!I detta instructable, jag visar dig hur du koppla in en Nintendo Entertainment System control pad till en arduino, arduino är i sin tur kör en särskild skiss som gör det möjligt att visa upp till en dator som en USB-HID-joystick!Och om detta inte...

PCB UV-exponering enhet

En UV exponering låda är ett mycket användbart att göra PCB.Jag har sökt och funnit att de är för stort och för dyrt för min använder. Dessutom kräver alla enheter en extern ström sorce.Därom bestämde jag mig att bygga en som passar bättre på mina be...

GameCube Controller till Wiimote

För det andra Instructable. Låt mig veta om något behöver fixas.Jag gjorde inte det krets eller program. Jag gör bara en guide som visar steg för steg montering av hur man gör en Gamecube till Wiimote controller.Den ursprungliga sidan finns här:http:...

Designa en Expansion PCB (Intel® IoT)

Detta Instructable har skrivits som utgångspunkt för dem som är angelägen om att använda Intel® Edison till dess fulla potential, genom att införliva det i en fullt utvecklad inbäddade projekt. För att göra detta, behöver du förmodligen att göra – so...

Snabb Digital termometer med billiga USB till TTL omvandlare och DS18B20 - utan Arduino eller Raspberry Pi

I min tidigare instructable jag gav en introduktion till Digital termometer med DS18B20 och ESP8266 nodemcu ombord med SPI OLED-skärm och programmering använder Arduino IDE. Det krävs kunskaper i programmering, grundläggande elektronik och krävs lite...

Universal programmerare för AVR: s och S51 plus ZIF socket!

Detta programmerare har utformats av företaget och har producerats minst 2500PCS av detta och det är ganska testad och standarder och nu vi inte tillverka denna version mer, här är en länk produkt (ledsen bara Farsi var tillgänglig)Universal AVR prog...

Billiga AVR/51 Development board

jag nyligen upptäckt STC microcontrollers tillverkas av TSMC. Att vara nyfiken på dessa microcontrollers började jag leta efter en utveckling styrelse för dem.Efter lite sökningar på google / ebay / AliExpress / jag beslutat att köpa denna PCB som ja...

Schweiziska AVR kniv

The Swiss AVR Knife buntar ett antal AVR programmering projekt tillsammans i en enda bekväm Altoids tuggummi tenn. På grund av den flexibilitet som erbjuds genom microcontroller programmering, ger det också en utgångspunkt för ett antal projekt baser...

LED Cube med Arduino och anpassade PCB

detta instructable Detaljer design och byggprocessen för en 5 x 5 x 5 LED cube, kontrolleras med en Arduino, som befinner sig på ett tryckt kretskort.Ytterligare information, foton och video finns på min hemsida.Den färdiga produkten visas i videon n...

PCB isolering spår fräsning med MyDIYCNC Desktop CNC maskin

Hej där,Tory @ MyDIYCNC här igen för att visa dig en annan cool projekt som är mycket lätt på din desktopen CNC-maskin. Vi ska vara fräsning ut isolering spår till Tom PCB material till manliga rapid prototyping mönsterkort. Med alla de rätta verktyg...

Lägga till knappar i musen

Jag har spelat FPS spel på min dator ett tag nu och jag fann mig ofta i behov av fler knappar på min mus. Det är därför jag köpte en gång en Logitech G9, utan i vissa spel även knapparna på det är extra 4 de tycktes vara några. Någon gång köpte senar...

En ram för att göra prisvärda & eleganta modulära styrsystem (USB till MIDI, gömde sig, eller seriella)

***JAG HAR SKAPAT EN NY INSTRUCTABLE FÖR DIY STYRENHETER:*************************************************************************************************************************************************************************************************...

Projektor lampa konvertering till LED

fick jag en projektor från en vän December 2010. Det är en kraftig D100U från 1997. Det hade inte den ursprungliga glödlampan, men allt annat fungerade bra. Jag gjorde lite efterforskningar på nätet för att se om jag kunde få en lampa för det och fic...

Hur man löda din fumeFan PCB KIT / några PCB KIT

Du är förmodligen en av de fantastiska människor som stöttat vår Kickstarter kampanj - fumeFan. Om du missade kampanjen kan du hitta mer om fumeFan på maketechnics.com.Detta instructable beskriver hur att löda den PCB KIT för varvtal styrkrets använd...

Tillägg lysdioder för sol dag lampa

Detta projekt föreslås för att öka nyttan av soldag lampa som publicerades tidigare i Instuctables:I det föreslagna projektet, är två ytterligare matriser av små lysdioder anslutna till solpanelen. Dessa tillägg LED matriser är utformade för att dra...

En enkel, låg spänning inverter för lysrör upp till 15 watt

tack till Burak Incepinar för att jag dokumentera hans design. Här är hans hemsida om du vill kolla in mer awesome mönster.Detta Instructable hjälper dig köra något fluorescerande ljus av låga spänningar, det har testats för att köra kompakt fluoresc...