What gameroom would be complete without a real working traffic light? Well … probably all of them, but I wanted one anyway. I found this jewel on Ebay:
The next step was making it behave like a real traffic light with the proper sequence etc. I’ve been playing around with electronics and programming microcontrollers for a little while now, but making this light work correctly would be my first “useful” project.
My goal was to have a traffic light controller board that would :
Fit into the light housing
Perform different light sequences (Standard, Flashing Red, Flashing Yellow)
Not catch on fire
Given me a reason to etch my first circuit board
In order to save board size I chose the Atmel ATTiny15L microcontroller for the most bang for the buck in an 8 pin package. The only downside was that I was forced to program it in assembly language and I wasn’t able to use WinAVR as I normally do. So the basic components started out as the microcontroller, a voltage regulator (7805) to feed it and a push button to swap between light sequences. The outputs from the microcontroller go to three optoisolators (MOC3010) which in turn drive three triacs (BTA10) to feed power to the three lamps connected via a small screw terminal block. The finished circuit :
Here are the parts used with their Jameco part numbers
|3||220 ohm ½ watt resistors||various|
|4||330 ohm ¼ watt resistors||various|
|1||7805 Voltage regulator||51262|
|2||100 uf electro-capacitors||198969PS|
|1||DC Female Power Connector (2.1mm)||137672|
I designed the circuit using Eagle Layout Editor (http://www.cadsoftusa.com/). It measures 2″ x 4″ with four mounting holes to match the posts in the traffic light. The finished board with components looks like :
I used the laser printer / etch method as described on this site http://electrons.psychogenic.com/modules/arms/art/10/pcb_howto.php to produce the circuit board.
The microcontroller is setup to interrupt itself every 164 milliseconds. On the interrupt it looks around to see if it needs to update the light pattern based on the variables of Tick (running count), State (which pattern we are on), Phase (which part of the pattern) and ChangeTicks (what tick we need to look for the next phase change on). The other thing that happens at interrupt is to check the status of the push button. After 2 successive button down polls we move on to the next light sequence / State. Anyway, here’s the code:
.include "C:Program FilesAtmelAVR ToolsAvrAssembler2Appnotestn15def.inc" ; PB0 (pin 5) = red Light ; PB1 (pin 6) = yellow Light ; PB2 (pin 7) = green Light ; PB4 (pin 2) = switch input .def Temp =r16 .def State =r17 ; indicates which light pattern we are on .def Phase = r18 ; indicates which part of the pattern we are on .def Ticks =r19 ; used to keep track of clock ticks .def ChangeTicks = r20 ; indicates on which tick we need to look for a phase change .def ButtonTicks = r21 ; keeps track of the push button for switch de-bounce .equ RedLamp = 0 .equ YellowLamp = 1 .equ GreenLamp = 2 .org $0000 rjmp start ; reset .org OVF0addr ; TIMER0 overflow rjmp click ; jump to click start: ;----- Setup ports ------- ldi Temp,0x07 out DDRB,Temp ; Set Output pins cbi PortB,RedLamp cbi PortB,YellowLamp cbi PortB,GreenLamp ;-------- Initial Values -------- ldi State,0x01 ldi Phase,0x01 ldi Ticks,0x00 ldi ChangeTicks,0x00 ldi ButtonTicks,0x00 ; Setup Timer ; Internal clock = 1.6 mhz ldi Temp,5 out TCCR0,Temp ; Timer clock = system clock / 1024 ldi Temp,1<<TOV0 out TIFR,Temp ; Clear TOV0/ clear pending interrupts ldi Temp,1<<TOIE0 out TIMSK,Temp ; Enable Timer/Counter0 Overflow Interrupt sei ; enable global interupt rjmp loop loop: rjmp loop ; endless loop waiting for clock interrupts click: sbis PinB,4 rjmp ButtonDown ldi ButtonTicks,0x00 inc Ticks cp Ticks,ChangeTicks BRGE Change reti ButtonDown: inc ButtonTicks cpi ButtonTicks,0x02 BREQ ButtonChange cpi ButtonTicks,0x03 ; no not let ButtonTicks overload if held down BRGE ButtonOverload inc Ticks cp Ticks,ChangeTicks BRGE Change reti ButtonOverload: ldi ButtonTicks,0x02 ; so will go to 3 on next button down and loop inc Ticks cp Ticks,ChangeTicks BRGE Change reti ButtonChange: inc State ldi Temp,0x04 cpse State,Temp ; skip next if equal rjmp ButtonChange2 ldi State,0x01 ButtonChange2: ldi Phase,0x01 ldi Ticks,0x00 ldi ChangeTicks,0x00 rjmp Change Change: ; handles branching between the states cpi State,0x01 BREQ StateNormal cpi State,0x02 BREQ StateFlashRed cpi State,0x03 BREQ StateFlashYellow rjmp StateNormal ;catchall ; 6 Ticks = 1 second -- really it's .98304 seconds StateNormal: cpi Phase,0x01 BREQ StateNormal1 cpi Phase,0x02 BREQ StateNormal2 cpi Phase,0x03 BREQ StateNormal3 rjmp StateNormal1 ;catchall StateNormal1: sbi PortB,RedLamp cbi PortB,YellowLamp cbi PortB,GreenLamp ldi Ticks,0x00 ldi Phase,0x02 ldi ChangeTicks,36 reti StateNormal2: cbi PortB,RedLamp cbi PortB,YellowLamp sbi PortB,GreenLamp ldi Ticks,0x00 ldi Phase,0x03 ldi ChangeTicks,36 reti StateNormal3: cbi PortB,RedLamp sbi PortB,YellowLamp cbi PortB,GreenLamp ldi Ticks,0x00 ldi Phase,0x01 ldi ChangeTicks,12 reti StateFlashRed: cpi Phase,0x01 BREQ StateFlashRed1 cpi Phase,0x02 BREQ StateFlashRed2 rjmp StateFlashRed1 ;catchall StateFlashRed1: sbi PortB,RedLamp cbi PortB,YellowLamp cbi PortB,GreenLamp ldi Ticks,0x00 ldi Phase,0x02 ldi ChangeTicks,3 reti StateFlashRed2: cbi PortB,RedLamp cbi PortB,YellowLamp cbi PortB,GreenLamp ldi Ticks,0x00 ldi Phase,0x01 ldi ChangeTicks,3 reti StateFlashYellow: cpi Phase,0x01 BREQ StateFlashYellow1 cpi Phase,0x02 BREQ StateFlashYellow2 rjmp StateFlashYellow1 ;catchall StateFlashYellow1: cbi PortB,RedLamp sbi PortB,YellowLamp cbi PortB,GreenLamp ldi Ticks,0x00 ldi Phase,0x02 ldi ChangeTicks,3 reti StateFlashYellow2: cbi PortB,RedLamp cbi PortB,YellowLamp cbi PortB,GreenLamp ldi Ticks,0x00 ldi Phase,0x01 ldi ChangeTicks,3 reti
It works great! I mounted a small push button on top of the light that allows me to swap between the standard green, yellow, red sequence to a rapid flashing red to a rapid flashing yellow pattern.