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.
Features
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 Arduino_TPS 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.
Limitations
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. The sound output is also affected by this, since the sound is on the Servo 2 (PWM 2) output.
Hardware connections
Here you find the input and output connections for the TPS and their equivalents on the arduino board:
TPS Connection | Arduino Pins |
---|---|
Din1..4 | D0..D3 |
Dout1..4 | D4..D7 |
PWM1,2 | D9,10 |
ADC1,2 | A1,2 |
SW_PRG | D8 |
SW_SEL | D11 |
the additional inputs and outputs for the TPS variant:
TPS Connection | Arduino Pins |
---|---|
RC1,2 | D18, D19 (A4, A5) |
Servo1,2 | D9, D10 |
Tone | D10 |
Just a little image:
Instruction Table
The areas highlighted in yellow are the extensions of my ATTiny_TPS and the Arduino_TPS version. The corresponding mnemonics of the SPS assembler are in square brackets.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|---|
n.n. | Port [DOUT] |
Delay [WAIT] |
Jump back relative [RJMP] |
A=# [LDA] |
=A | A= | A=Ausdruck | |
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] | A=A SHR 1 [SHR] ab V 11 |
|
f | 15 | 60s | 15 | 15 | Push A [PUSH] | Pop A [POP] | A=A SHL 1 [SHL] ab V 11 |
- There are 2 additional registers (E and F)
- and there is a stack with the 2 methods push and pop. There is space for 16 values in the stack.
- There are also 2 new calculations, one for the remainder of a division (A= A % B) and one for an 8-bit conversion. A = A + 16 * B
- As of version 0.6, the swap command has also been added, which swaps A and B registers.
- And a new calculation A = B - A. Especially when you are in the 8-bit space, it is sometimes quite cumbersome to carry out this operation.
- from version 11 there are now also shift operations. These shift the contents of register A either to the left SHL (which corresponds to a multiplication by 2) or to the right (SHR). That would then be a division by 2. Warning: although we are in 4-bit space here, these operations are 8-bit capable.
8 | 9 | a | b | c | d | e | f | |
---|---|---|---|---|---|---|---|---|
Page [PAGE] | Jump absolut (#+16*page) [JMP] |
C* C>0: C=C-1; Jump # + (16*page) [LOOPC] |
D* D>0:D=D-1; Jump # + (16*page) [LOOPC] |
Skip if | Call # + (16*Page) [Call] |
Callsub/Ret | Byte Befehle | |
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] | int. LED on |
e | 14 | 14 | 14 | 14 | S_PRG==1 [PRG1] | 14 | int. LED off | |
f | 15 | 15 | 15 | 15 | S_SEL==1 [SEL1] | 15 | restart [REST] | PrgEnd [PEND] |
Additional features in the Arduino_TPS version:
- Because we have enough EEPROM, the page area is extended to 16 pages. So extends TPS to 256 bytes
- In the skip instructions there is also the skip on A=0 command.
- 6 real subprograms can now also be created via the E commands. These are created using the Def# command. The routine is started with Call#. With return you come back. The Def# memory may also be above the 256 bytes in the EEPROM. So also outside the range of the jump commands.
- The restart command, with which the entire controller is restarted, this is also new.
- Commands that work with 8-bit resolution are located in area F.
- FF means end of program. → automatic jump to 0.
- NEW Tone command: emits a tone. The midi notes are the basis. Midi notes from 36 (C2) to 108 (C8) are supported. The output is PWM 2. The basis is the A register as an 8-bit value.
InField programming
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 TPS 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.
LEDs | Comment | next Step |
---|---|---|
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 *) |
Download
here you can download the actual released version of the Arduino_TPS:
| last Release als ZIP
The sourc codes of this is hosted on github: Arduino_TPS Source
Earlier versions are available on my Gogs server
Arduino SPS Source