Table of Contents
Arduino goes TPS
For my Lighthouse model ( that I have sold in the meantime) I needed a circuit that flashes the light every 5 seconds 3x. Since the analog solution is not straightforward, I have dug out an ATTiny24 (I just had one available) from my hardware collection, switched on my AVR studio and just quickly wrote the controller software. A quick solution..
At various model exhibitions, I was asked again and again how to replicate it. Of course you can, if you … And of course, other flash sequences should be selectable. And preferably controlled by remote control, selectable including other lamps … I am surprised that among the model DIYlers the MCU knowledge is rather scarce. That's a pity.
I recently came across Burkhard Kainka’s TPS (http://www.elektronik-labor.de/Lernpakete/TPS/TPS0.html). This was just the right solution for many needs in model making. It is easy to program. Actually reduced to the bare minimum. The only thing the TPS lacks is the connection to a remote control. And also, the possibility to control one (or 2) servos the TPS does not have. Not yet, I thought to myself.
And so I started: Since I am very familiar with the Atmel processor range and also have a lot of experience with the Arduino system, it was only a small step to move the TPS functionality on to the Arduino. In addition, the Arduino is very widespread.
At first, I thought I could easily adapt the existing TPS variant written in BASCOM, but unfortunately that was not so easy. So, I've reprogrammed the entire controller functionality from scatch and also added a few command extensions for the model design people. Here I present the resulting version free of charge and for general use. If you find a bug / mistake, then just write an email, so I can correct it. I have not been able to test all possible paths yet and will try to remove the bugs as soon as possible.
And something more regarding this Arduino implementation. It can run on 2 different MCU's: On the one hand, on the Arduino itself, then even with the possibility to be programmed directly from my emulator. The 2nd variant is based on an ATtiny84. Unfortunately, this software can then only be programmed using a “standard” ISP programmer. And the TPS commands are then programmed directly as with the HOLTEK version - only via the buttons and LEDs – no PC connection. This possibility exists of course in the Arduino version as well, a good solution if you have to modify something quickly on-site close to the water with your model boats.
In addition to Burkhard’s TPS, this version has the following additional features:
- 2 RC channel outputs can be read and the PWM can be used as input for this TPS.
- 2 servo driving channels. There is the additional output for 2 RC servos. ( Unfortunately, this automatically eliminates the usage of the two PWM Input channels. )
- Extensions of the instruction set. Now there are 1 byte instructions. This makes the reading and setting of ADC, RC, PWM, and servo much more sensitive with higher resolution.
- In addition, there is a new calculation. A = B * 16 + A to calculate 1 byte.
- There are also 2 additional registers for storing values;
- And a 16-level stack has been implemented with the two usual access methods:
Push value onto stack and Pop from stack – read data and take the value off the stack.
- Regarding the Skip instruction, there is now as well the A = 0 condition
- And last but not least, you can define and start 6 different subroutines. These may then be located outside the 256-byte address space. For the Arduino, the EEPROM size is 1KB (ATMega328), while the ATTiny84 is 512 bytes in size.
The PWM signals are generated at 500Hz, the servo signals are PPM coded. (50Hz repetition frequency; and the shortest pulse is 1ms long, the longest pulse 2ms, middle position is at 1,5ms.) With the arduino variant you are able to produce some sound. The output is shared with the PWM 2 output. For selecting the pitch, you have to set the A register to a value between 36 (low C2) and 109 (C8) as in the midi specification.
Programming the ArduinoSPS is the same as the TPS variants. (But see the enhanced programming modes, too)
New since 0.10 is the serial programming mode, where you can upload a program via a serial connection. For this the INtelHEX format is used for uploading. You can generate this format by using my SPS Assembler.
A mixed operation of servo and PWM (for example, PWM.1 and Servo.2) is unfortunately not possible because the two influence each other. So, either servo output or PWM output.
Here you find the input and output connections for the TPS and their equivalents on the arduino board:
|SPS Connection||Arduino Pins|
the additional inputs and outputs for the SPS variant:
|SPS Connection||Arduino Pins|
Just a little image:
The areas shaded in yellow are the extensions of my ArduinoSPS and ATTiny84 version. In squared bracket you will find the mnemonic for the SPS assembler.
|Jump back relative
|0||NOP [NOP]||aus||1ms||0||0||A<->B [SWAP]|
|1||1||2ms||1||1||B=A [MOV]||A=B [MOV]||A=A + 1 [INC]|
|2||2||5ms||2||2||C=A [MOV]||A=C [MOV]||A=A - 1 [DEC]|
|3||3||10ms||3||3||D=A [MOV]||A=D [MOV]||A=A + B [ADD]|
|4||4||20ms||4||4||Dout=A [STA]||Din [LDA]||A=A - B [SUB]|
|5||5||50ms||5||5||Dout.1=A.1 [STA]||Din.1 [LDA]||A=A * B [MUL]|
|6||6||100ms||6||6||Dout.2=A.1 [STA]||Din.2 [LDA]||A=A / B [DIV]|
|7||7||200ms||7||7||Dout.3=A.1 [STA]||Din.3 [LDA]||A=A and B [AND]|
|8||8||500ms||8||8||Dout.4=A.1 [STA]||Din.4 [LDA]||A=A or B [OR]|
|9||9||1s||9||9||PWM.1=A [STA]||ADC.1 [LDA]||A=A xor B [XOR]|
|a||10||2s||10||10||PWM.2=A [STA]||ADC.2 [LDA]||A= not A [NOT]|
|b||11||5s||11||11||Servo.1=A [STA]||RCin.1 [LDA]||A= A % B (Rest) [MOD]|
|c||12||10s||12||12||Servo.2=A [STA]||RCin.2 [LDA]||A= A + 16 * B [BYTE]|
|d||13||20s||13||13||E=A [MOV]||A=E [MOV]||A= B - A[BSUBA]|
|e||14||30s||14||14||F=A [MOV]||A=F [MOV]|
|f||15||60s||15||15||Push A [PUSH]||Pop A [POP]|
- There are 2 additional registers (E and F)
- And there is a stack area with the 2 usual interface methods push ( add to stack) and pop (take off the stack). There are 16 level positions in this stack.
- There are also 2 new calculations; one is the remainder of a division (A = A% B) and one is an 8-bit conversion. A = A + 16 * B
- Since version 0.6, the swap command has also been added, swapping the A and B register contents.
- And a new calculation A = B - A. Especially, when you are working in an 8- bit space, it is sometimes quite cumbersome to carry out such an operations without.
|Page [PAGE]||Jump absolut (#+16*page)
|C* C>0: C=C-1;
Jump # + (16*page)
Jump # + (16*page)
|Skip if||Call # + (16*Page)
|0||0||0||0||0||A==0 [SKIP0]||0||ret [RTR]||A=ADC.1 [BLDA]|
|1||1||1||1||1||A>B [AGTB]||1||Call 1 [CASB]||A=ADC.2 [BLDA]|
|2||2||2||2||2||A<B [ALTB]||2||2 [CASB]||A=RCin.1 [BLDA]|
|3||3||3||3||3||A==B [AEQB]||3||3 [CASB]||A=RCin.2 [BLDA]|
|4||4||4||4||4||Din.1==1 [DEQ1 1]||4||4 [CASB]||PWM.1=A [BSTA]|
|5||5||5||5||5||Din.2==1 [DEQ1 2]||5||5 [CASB]||PWM.2=A [BSTA]|
|6||6||6||6||6||Din.3==1 [DEQ1 3]||6||6 [CASB]||Servo.1=A [BSTA]|
|7||7||7||7||7||Din.4==1 [DEQ1 4]||7||Servo.2=A [BSTA]|
|8||8||8||8||8||Din.1==0 [DEQ0 1]||8||Def 1 [DFSB]||Tone=A [TONE]|
|9||9||9||9||9||Din.2==0 [DEQ0 2]||9||2 [DFSB]|
|a||10||10||10||10||Din.3==0 [DEQ0 3]||10||3 [DFSB]|
|b||11||11||11||11||Din.4==0 [DEQ0 4]||11||4 [DFSB]|
|c||12||12||12||12||S_PRG==0 [PRG0]||12||5 [DFSB]|
|d||13||13||13||13||S_SEL==0 [SEL0]||13||6 [DFSB]|
|f||15||15||15||15||S_SEL==1 [SEL1]||15||restart [REST]||PrgEnd [PEND]|
- As we now have more EEPROM memory, the page area is been extended to 16 pages. So, you can now enter a program that is 256 bytes long.
- Regarding the Skip command, there is now as well the Skip command for condition A = 0.
- Via the E commands, you can now program 6 real subroutines. These are created using the Def# command. With Call# the routine is started. With Return you come back to the main program. The Def# may also be positioned above the 256 bytes in the EEPROM. So, also outside the range of the jump commands.
- Also new is the Restart command, which restarts the entire controller.
- In the F area, there are the new commands located, which work with the extended 8-bit resolution.
- FF means end of program. → and an automatic jump to program location 00.
- new is the TONE command. Output on PWM 2, using midi notes from 36 (C2) to 108 (C8) in register A as an 8-bit value.
One of the most used features of the TPS is the possibility to program the unit only with the 3 buttons and the 4 LEDs. So you don't need any other programming hard- or software. A printed command chard or one on your mobil phone would be very helpful.
Please be aware the external hardware must maybe unconnected before programming, because of some unpredictable results.
Four me, the original programming mode was a bit to clumsy, so i decide to reprogram it. The new one has a nicer interface, as you now always know where you are.
The process for every programming step is always the same and has 3 steps.
- showing the actual address
- programming the command (uppper nibble of the command/data value, this is the column of the programming table.)
- programming the data (lower nibble of the command/data value, which is the row of the table)
The programming starts always at address 0, for programming always the PRG button and for selecting value always the SEL button is used.
As in the original programming sequenz, you can step thru the program using only the PRG button.
Starting the programming with keeping PRG pushed after Reset. As a result, all LEDs will shortly blink now, indicating that you are in Programming Mode. Please release now the PRG Button. Than the programming loop will start.
- 1a first the 1 LED will blink once. After that the actual value of the upper adress nibble will be shown on the LEDs. (0.5sec)
- 1b the second LED will blink once, and after that the lower nibble of the adress pointer will be displayed. (0.5sec)
- 2 after that the third LED will blink once, to indicate the now the value of the command will be shown. Now with the SEL Button you can edit the value, with the PRG Button you can save the actual command.
- 3 the fourth LED will now blink, indicates that the data value of the command will be shown. Same as in 2, with SEL you can edit the value, with PRG you can save the value. If the command value (both upper and lower nibble) is modified, now the SPS will save the new value and all LEDs will blink. Than the current address will be incremented and the loop will be startet an 1.
If you are ready with programming or you want to cancel the programming simply press the reset button.
Start programming, press RESET and PRG, than release RESET. hold PRG until all LEDs light up.
|1111||wait until PRG is released||release PRG|
|*0001||indicator for low nibble of address||do nothing, delay of 1/4 second|
|xxxx||low nibble of address, if address is 0x00 than nothing is shown||do nothing, delay of 1/2 second|
|0010||indicator for high nibble of address||do nothing, delay of 1/4 second|
|xxxx||high nibble of address, if address is 0x00 than nothing is shown||do nothing, delay of 1/2 second|
|0100||indicator for command||do nothing, delay of 1/4 second|
|xxxx||showing the actual command||with SEL you can change the value of the command (column in programming table), with PRG you go to the next step|
|1000||indicator for data||do nothing, delay of 1/4 second|
|xxxx||showing the actual data||with SEL you can change the value of the data (row in programming table), with PRG you save the actual value of command (all LEDs will blink once) and data into the memory, and go to the next address (goto *)|