Felix Rodriguez & Alex Osbun
Automatic Etch-A-Sketch Controller
Introduction
For our final project, we set out to write a Controller for the classic toy, the Etch-A-Sketch. What this basically means, is that we use an Atmel micro controller to control two stepper motors connected to the knobs of the Etch-A-Sketch. Thus, an order by the chip to the motors would cause different angled lines to show up in the Etch-A-Sketch making it possible to draw complete pictures automatically.
Since we did not want to force the chip to contain just one picture, it would have to connect to a computer using the UART, and download pictures from there. Thus we will write a small drawing program in Windows that lets a user draw its schematic, and send it directly at the serial port.
The reason we chose to do this project was twofold. First, we thought it would be very interesting to actually see an Etch-A-Sketch move by itself. Secondly, we thought that the amount and diversity of the work would be ideal. First we would need to write two large programs, one in Atmel Assembly, and we’d have to build a circuit. Also we were curious on how Stepper Motors worked. Finally, since our budget was limited, it was ideal because the only two items that we needed to buy was a power supply and the Etch-A-Sketch toy.
High Level Design
Our setup has four main sections: the Computer, the Micro controller, the stepper control, and the Etch-A-Sketch, in serial order. All four sections are relatively decoupled from each other, with the exception of a small communication script in between each section. This design is based on the standard computer network design where there are seven layers separate from each other, and it has a number of advantages. First, the decoupling enabled us to work efficiently in parallel, taking separate tasks until the final testing day when we join all the pieces. Thus, Felix would take over the first two sections, the Computer, and the Micro controller program, while Alex would take the last two. Second, it has good modularity, in that we could easily switch any one of the four sections with something else that has the same communication interface, and the remaining items would not have to be changed at all.
The first step is the computer program. This would be a standard windows program written in C++. It is basically a line drawing program, with added Serial Port functionality so that it can send the points to the Micro controller when it is requested. It works by letting the user draw a series of connected lines. The program would then format the lines so that they fit in a byte (125 values, positive and negative with the extra 6 items reserved for handshaking, etc).
Then, whenever requested this collection of bytes (for the points, with x and y values alternating), are sent directly to the chip after a simple handshaking procedure. A class description of this program is attached in the appendix, and a more thorough description is also attached in the Program Design section of this document.
The second step is the Atmel Micro controller and its program. This program basically has three functions, each called by one of the three buttons on its board. The first, is a simple testing procedure that loads some specified values in memory. This can be used not only for our internal testing, but also for easy diagnostic of the system in case something breaks (similar to the auto tests in printers). The second button is more interesting, it also loads the specified values in memory, but it loads them from the UART input. Thus, it contains a compatible handshake with the computer, and then it reads values from the UART one by one until it finds the end code, or it runs out of memory.
The third button takes care of sending the necessary voltages to the next step. It loads the values from memory one by one. Then it alternates between x and y values and does the following: For x values, it converts them so that they are always positive, makes a note on its sign, sets the delay factor for y, and the counter for x. For the y values, it does the same conversion, but it only sets the delay factor for x, and no count value. The reason for this inverted formula is this:
CountX DelayY
-------- = ----------
CountY DelayX
because CountX * DelayX = DelayY * CountY
Thus we set count x, and the two delays, and the y count automatically sets to the correct value if the timers have the correct delay. The only limitation of this system is that I cannot deal with values of 0. My solution was simple – convert all zeros to ones. I can’t have a straight horizontal or vertical line, but the distortion is not very significant anyway.
Program/Hardware Design
For a better understanding of the windows program, please refer to the attached class description, and the supplied code. The general structure of the windows program is the standard Single Document/View Architecture engineered by Microsoft. This setup was preferable because it conveniently splits up different parts of the program logically. Also, Microsoft has a number of tools that support this Architecture, which simplified development.
The way it works is that the main function, called CEtchASketchApp, builds a window and creates three separate objects that control this window. Thus, when a message is sent, it goes to all three classes until it finds the one that controls that specific message.
The first object is the CetchASketchView object. It is in charge of handling all the drawing and input functions. Both of these connect to the Cpoints structure in the CetchASketchDoc (described below). The drawing basically goes through all the points in order and traces a line through them. Input basically adds to the point list. It also, however, makes sure that the points it adds are not bigger than 128 in length because that would crash the Micro Controller. Thus whenever it spots a longer distance, it splits the line into two. There is also a limit to the number of points, which is directly related to the number of points that the Atmel memory can hold.
The second object is the CetchASketchDoc. This is basically a controller for the Cpoints object. It includes file saving and loading by populating this object. It also includes a number of visual features such as warning boxes when saving, its own type of file (.eas) for easy lookup, and a feature to restart the Document completely, by clicking on new on the menu. The Cpoints object is basically an array of the Windows standard Cpoint object. It contains a number of functions for adding and viewing the points that simplify its access, along with a lookup of the maximum value in case we overextend by mistake.
The final object in the Architecture is CmainWnd. This object controls the general button management and any other item not used by the other two objects. It controls a couple of tweaks such as the About Box, which shows author comments, and the status box in the bottom. It also controls the Program Atmel button in the menu.
This menu button when called, first initializes the CserialPort object, which will be described soon. Then it takes care of handshaking – a simple Send FF, and receives the same. Once it gets past that, it then streams a string of all the points for the Atmel Controller to catch.
The Atmel Controller code as explained above, can basically be divided int three separate button sections, the initialize, the UART, and the two timers. The Initialization is fairly straightforward. It’s a linear code that basically starts up the UART and the Timer, initializes all the values, writes “Etch-A-Sketch” on the screen and goes on. The UART control is also fairly straightforward. It is basically the same circuit that was used in Lab 4. The three buttons are implemented pretty much in the same way that was described in the previous section, so I will not repeat the same here.
The two timers work generally the same way. One controls the the bottom four bits and the other upper four bits of the output pins. Thus we use the bottom four bits for the horizontal movement, and the upper four for the vertical movement. The timer values described in the previous section are used to set the interrupt delay of the timer. Thus, the interrupts alternate at differing speeds, and keep the TimCount calue as memory for how much they actually have drawn up to now.
This system means that the steppers do not actually move at the same time, but they alternate in very small steps. Thus we get a slightly jaggy picture, much like the aliasing done when drawing a diagonal line in a computer screen. The output is also sent to two ports at the same time. This was done for debugging purposes as one port can be connected to the leds and we can find what exactly is going on visually.
In order to drive the knobs on the etch-a-sketch we use a couple of stepper motors. These motors use a 12 volt supply. The manner in which our stepper motors works is rather straight forward. Each motor has 5 different color coded lines used to power and control the motor. The black line is used to supply the 12 volts to the motor. The rest of the four lines are used to control one of the four coils in the motor. For example, if "coil 1" is magnetized, then a permanent magnet aligns itself to the charged color. This action thus rotates the shaft of the motor. In order to magnetize a coil, the corresponding wire has to be connected to ground. This causes voltage to flow through the coil, thus magnetizing it. Considering that the motor had plenty of torque to move the knobs on the etch-a-sketch, we decided to power one coil at a time, as opposed to two coils, which produces more torque, but takes up more power. In order to make the motor shaft rotate clockwise, four coils were energized in succession. The order was first the red wire, then the green, brown, and white. This sequence is repeated as much as desired. In general, in order to make a line (on the etch-a-sketch) which is about half an inch long required the sequence to be repeated about 18 to 20 times. In order to make the knobs go counter clockwise the sequence is reversed (white, brown, green, then red wires sent to ground).
Well, this process seems simple enough, but we had to figure out a way to switch the current from the different coils in a quick manner, with the use of the five volts given to use by the PortA pins. In order to do this, we decided to use transistors, which would be turned on (saturated) and off by the micro controller. We ended up using a couple of transistors for each wire that was would allow current to flow through the various coils. Our initial design used Tip31C power transistors. These are npn transistors which are rather "heavy duty". One of our big problems was the inability to supply enough current to the transistor base in order to switch in one. So we eventually used 3904 transistors to help out.The 3904 amplified the current so that we had enough current to turn the larger power transistor on. We also connected 1K resistors to the base of the 3904s. This assembly is used eight times, one for each coil. The schematic for the picture is shown in figure 1.
Figure 1. This circuit is the switching circuit we used.
When 5 volts is supplied to the base of the 3904 transistor, the switch is turned "on". But it is also interesting to note that in order to have the motors give use the desired response, the high voltage given to each of these circuits had to be very short, or else the shaft of the motor would begin to vibrate, rather than turn hard and stay when having a coil connected to ground. This is probably due to the fact that the power transistor are not completely saturated. What is happening is that it is flickering on and off, thus causing the shaft to vibrate.
We also had to solve a problem of actually mounting the motors to the knobs of the etch-a-sketch. One of the initial concerns was that enough torque may not be provided by the motors. If this was the case, then we would have had to come up with some sort of gear assembly. But we decided to go with a straight coupling with the use of the white knobs which came with the etch-a-sketch. And fortunately we ended up having enough torque in order to give us what we needed. We also had to create some sort of mount for the motors. This ended up being a simple bar of metal with holes drilled in it. The mount and the coupling were creating with the help of Nathan Jauvtis, a Cornell mechanical engineering student. In the end, everything fitted just about perfectly for us.
Results of the Design
Unfortunately, we were not able to meet our main objective, which was to create a project in which we can draw something in Windows and replicate it on a etch-a-sketch with the use of the micro controller and a couple of stepper motors. But we were able to make the stepper motors actually draw on the etch-a-sketch, which ended up being very satisfying. The mount and coupling for the motors worked very well. Also, we were probably somewhat fortunate that the motors had enough torque to be able to turn the etch-a-sketch knobs. In order to make the knobs "lose", and making the probability of the straight coupling work we also had to "break in" the toy. In other words, just play with it a lot.
What we would do different next time
We spent a long time trying to get the serial connection to work in a clean fashion, and in the end I don’t believe it paid off. The plan was to create a completely separate class that could be reused for any Serial Interface I might want to create. This implied having two child threads controlling the input/output along with a myriad of generic options. If we had done this again, this functionality would not be here. We ran into some evil threading synchronization problem that took way too much time to fix.
Another item we would have done differently is gotten a better power supply. The one we had, used very little amperage and we had a hard time keeping it powered. Something better would have definitely made our life easier.Unfortunately a power supply with a larger current was several times more expensive.
We tried to do most of the project in parallel, which made things efficient. But at the same time we found it difficult to test how the various parts worked together since during the last two weeks our schedules were not compatible at all. It would have helped us greatly in completing our project completely if we had more time to test the various components together.
Link:
http://instruct1.cit.cornell.edu/courses/e...felix/Final.htm| CODE |
;***** Etch-A-Sketch Controller ******** ; Final Project EE476 ; Felix Rodriguez ; Alex Osbun ; ; The AT90S4414 will be used as a controller ; for a standard Etch-A-Sketch. It will ; load the picture from the UART and the ; corresponding windows program. Then it ; uses step motors
.include "4414.asm"
.device AT90S4414
.def savSREG =r1 ;save the status register .def switcher =r2 .def slow0 =r3 .def slow1 =r4 .def slow2 =r5 .def slow3 =r6 .def temp =r16 ;temporary register .def TXbusy =r17 ;transmit busy flag .def RXchar =r18 ;a received character .def TXflash =r19 ;text to be sent is in flash if <>0 .def memcount =r20 ;The counter for how much memory is written
.def itemp =r21 ;a temporary register for my interrupts .def itemp2 =r22 ;secpmd temporary register
.def timcount0=r23 ;The remaining changes for right stepper .def timval0 =r24 ;The value of the speed reset for right stepper .def timval1 =r25 ;The value of the speed reset for left stepper .def portaval =r26 ;the current value of the stepper control .def inp =r27
.def inv1 =r28 .def inv2 =r29
.equ baud96 =25 ;9600 baud constant for 4Mhz crystal .equ START_ETCH = 0xFE .equ START_INPUT = 0xFD .equ START_TEST = 0xFB .equ LEDS = PORTB .equ BUTTONS = PINC .equ STARTCODE = 0xFF .equ MEM = 16 ;Maximum line count, boost up to available memory! .equ SLOWVAL = 4 Delay Values .equ SLOWVAL2 = 50 .equ TIMDELAY = 3
; The stepping sequence .equ STEP1 = 0x01 .equ STEP2 = 0x02 .equ STEP3 = 0x04 .equ STEP4 = 0x08 .equ STEP1L = 0x10 .equ STEP2L = 0x20 .equ STEP3L = 0x40 .equ STEP4L = 0x80
.MACRO outfx ldi ZL, LOW(@0<<1) ;shifted becuase pgm memory is words ldi ZH, HIGH(@0<<1) lpm out UDR, r0 ;trigger the UART TX ser TXflash ;text string in flash memory ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt .ENDMACRO
.MACRO outmem ldi ZL, LOW(@0) ;ram ldi ZH, HIGH(@0) ld r0,Z out UDR, r0 ;trigger the UART TX ser TXflash ;text string in flash memory ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt .ENDMACRO
;************************************** .dseg
ram: .byte MEM;The ram area where the codes are stored
;************************************** .cseg
.org $0000 rjmp RESET ;reset entry vector reti reti reti reti reti rjmp T1Ovfl ;Timer 1 overflow; rjmp T0Ovfl ;Timer 0 overflow; reti rjmp RXdone ;UART receive done rjmp TXempty;UART buffer empty rjmp TXdone ;UART transmit done reti
;define fixed strings to be tranmitted from flash- zero terminated intro: .db "Etch-A-Sketch",0x0d, 0x0a,0x00 strt: .db STARTCODE,0x00
RESET: ldi temp, LOW(RAMEND);setup stack pointer out SPL, temp ldi temp, HIGH(RAMEND) out SPH, temp
;initial conditions clr TXbusy ;start out not busy on TX clr RXchar ;start out stopped
;make the LEDs and PORTA output ser temp out DDRB,temp out DDRA,temp
;start with the leds off out PORTA,temp out LEDS,temp
;make the BUTTONs input clr temp out DDRC,temp ;start with the timers off clr timcount0
;set the timer interrupt ldi temp, 0b10000010 out TIMSK,temp
;set the timer prescale ldi temp, TIMDELAY out TCCR0, temp out TCCR1B, temp
;reset the timer count ser temp out TCNT1H,temp out TCNT1L,temp out TCNT0,temp
;set the slowdown value ldi temp,SLOWVAL mov slow0,temp mov slow1,temp
ldi temp,SLOWVAL2 mov slow2,temp mov slow3,temp
;set the initial position of the stepper controller ldi portaval, 0b00010001
;setup UART -- enable TXempty & RXdone int, and RX, TX pins ldi temp, 0b10111000 out UCR, temp ;set baud rate to 9600 ldi temp, baud96 out UBRR, temp
;intialize text pointer BEFORE turning on interrupts ;because RESET causes the TX empty flag to be SET outfx intro sei ;rcall TXwait
TXloop: in temp, BUTTONS cpi temp, START_ETCH ;Check if the first button is pushed breq startetch
cpi temp, START_INPUT breq startinp
cpi temp, START_TEST breq starttest
out LEDS,temp rjmp TXloop
startinp: ;Hand shake our windows program out LEDS,RXchar
cpi RXchar, STARTCODE brne startinp
clr RXchar outfx strt
ldi ZL, LOW(ram) ldi ZH, HIGH(ram) clr memcount inc ZL
;store all the values in ram ram1: tst RXchar breq ram1 cpi RXchar,0xff breq ram1
cpi RXchar,0xFE breq endit
st Z, RXchar inc ZL
clr RXchar inc memcount cpi memcount,MEM brne ram1
;start with the leds off endit: ;ldi temp,0xF0 ;out LEDS,temp ;rjmp endit
ldi RXchar, 0 st Z, RXchar inc ZL
rjmp TXloop
startetch:
;This is all testing stuff %ldi timval0, 1 %ldi timval1, 100 %ldi timcount0 , 0x80 %ldi inv1, 0; %ldi inv2, 1;
ldi ZL, LOW(ram) ldi ZH, HIGH(ram)
ldi temp,126 st Z+,temp ldi temp,249 st Z+,temp ldi temp,2 st Z+,temp ldi temp,200 st Z+,temp ldi temp,0 st Z+,temp ldi temp,0 st Z+,temp
ldi temp,0x0F out LEDS,temp
TXloop2:rjmp TXloop
starttest:;This button is here for temporary testing
ldi ZL, LOW(ram) ldi ZH, HIGH(ram)
startin:ld inp, Z inc ZL tst inp breq TXloop
;eliminate zeros!! cpi inp,125 brne notzero
;estimate zero by making it 1 inc inp
;See whether its positive or negative notzero:clr inv1 cpi inp,125 brlo reverse subi inp,125 ldi temp, 255 sub temp, inp
outt1: mov timval1, temp mov timcount0 , inp
ld inp, Z inc ZL cpi inp, 0 breq TXloop2
;eliminate zeros!! cpi inp,125 brne notzero1
;estimate zero by making it 1 inc inp
notzero1:clr inv2 cpi inp, 125 brlo reverse2 subi inp,125 ldi temp, 255 sub temp, inp
out2: mov timval0, temp
wait: tst timcount0 brne wait
rjmp startin
reverse2: subi inp, -125 ser inv2 rjmp out2
reverse:subi inp, -125 ldi temp,255 sub temp,inp ser inv1 rjmp outt1
;***************************** ;UART interrupt routines
; UART needs a character TXempty:in savSREG, SREG ;save processor status tst TXflash ;Is the string in flash memory? breq TXram ;If not, it is in RAM inc ZL ;get the next char from flash lpm ;and put it in r0 rjmp TXfls TXram: inc ZL ;get the next char from RAM ld r0,Z TXfls: tst r0 ;if char is zero then exit breq TXend out UDR, r0 ;otherwise transmit it rjmp TXexit ;exit until next char TXend: clr TXbusy ;no more chars cbi UCR, UDRIE ;clear the TXempty interrupt TXexit: out SREG, savSREG ;restore proc status reti ;back to pgm
; TX done -- buffer is empty -- unused here TXdone: in savSREG, SREG ;save processor status out SREG, savSREG ;restore proc status reti ;back to pgm
; UART read a character RXdone: in savSREG, SREG ;save processor status in RXchar, UDR;get the char acter out SREG, savSREG ;restore proc status reti ;back to pgm
;***************************** ;Timer interrupt routines
t0Ovfl: in savSREG, SREG ;save processor status
tst slow2 breq goon2
dec slow2 rjmp end0u
goon2: clr itemp out PORTA, itemp tst slow0 breq goon0
dec slow0 rjmp end0u goon0: ldi itemp2,SLOWVAL mov slow0,temp ldi itemp2,SLOWVAL2 mov slow2,itemp2
tst timcount0 breq end0u dec timcount0
rCall AddC end0v: out PORTA, portaval;output to the stepper out LEDS, portaval
end0u: out TCNT0, timval0 ;250x8x.25 microSec = 0.5mSec. out SREG, savSREG ;restore proc status reti
t1Ovfl: in savSREG, SREG ;save processor status tst slow3 breq goon3 dec slow3 rjmp end1u
goon3: clr itemp2 out PORTA, itemp2
tst slow1 breq goon1
dec slow1 rjmp end1u goon1: ldi itemp2,SLOWVAL mov slow1,itemp2 ldi itemp2,SLOWVAL2 mov slow3,itemp2 tst timcount0 breq end1u
rCall AddC1
end1v: out PORTA, portaval;output to the stepper out LEDS, portaval
end1u: ser itemp2 out TCNT1H, itemp2 out TCNT1L, timval1;250x8x.25 microSec = 0.5mSec.
out SREG, savSREG ;restore proc status reti
;***************************** ;subroutine
TXwait: tst TXbusy ;now wait for the tranmission to finish brne TXwait ret
;******************************** AddC: ldi itemp,0x0F and itemp, portaval cpi itemp,STEP1 brne test2 tst inv1 breq for1
ori portaval, 0x00 + STEP4 andi portaval, 0xF0 + STEP4 ret
for1: ori portaval, 0x00 + STEP2 andi portaval, 0xF0 + STEP2 ret
test2: cpi itemp,STEP2 brne test4 tst inv1 breq for2
ori portaval, 0x00 + STEP1 andi portaval, 0xF0 + STEP1 ret
for2: ori portaval, 0x00 + STEP3 andi portaval, 0xF0 + STEP3 ret
test4: cpi itemp,STEP3 brne lastone tst inv1 breq for3
ori portaval, 0x00 + STEP2 andi portaval, 0xF0 + STEP2 ret
for3: ori portaval, 0x00 + STEP4 andi portaval, 0xF0 + STEP4 ret
lastone:tst inv1 breq for4
ori portaval, 0x00 + STEP3 andi portaval, 0xF0 + STEP3 ret
for4: ori portaval, 0x00 + STEP1 andi portaval, 0xF0 + STEP1 ret
;******************************** AddC1: ldi itemp2,0xF0 and itemp2, portaval cpi itemp2,STEP1L brne test21 tst inv2 breq for11
ori portaval, 0x00 + STEP4L andi portaval, 0x0F + STEP4L ret
for11: ori portaval, 0x00 + STEP2L andi portaval, 0x0F + STEP2L ret
test21: cpi itemp2,STEP2L brne test41 tst inv2 breq for21
ori portaval, 0x00 + STEP1L andi portaval, 0x0F + STEP1L ret
for21: ori portaval, 0x00 + STEP3L andi portaval, 0x0F + STEP3L ret
test41: cpi itemp2,STEP3L brne lastone1 tst inv2 breq for31
ori portaval, 0x00 + STEP2L andi portaval, 0x0F + STEP2L ret
for31: ori portaval, 0x00 + STEP4L andi portaval, 0x0F + STEP4L ret
lastone1:tst inv2 breq for41
ori portaval, 0x00 + STEP3L andi portaval, 0x0F + STEP3L ret
for41: ori portaval, 0x00 + STEP1L andi portaval, 0x0F + STEP1L ret
|