How to generate a PWM signal for accurate LED brightness control
APOLLO181 LED brightness at two different PWM duty cycles
This is a simple program to implement a LED dimmer: a PWM signal
is used to light the leds at varying brightness according to a 4-bit linear scale made of sixteen steps.
PWM, or Pulse Width Modulation, is a great method of controlling analog
circuits by using the digital output of a microprocessor.
The PWM signal consists of a modulated square wave. The modulation
can change the clock cycle of the signal (frequency) or the percentage of the power delivered during each clock cycle
(duty cycle). The amplitude of the signal remains stable during the time.
If we drive a LED with a PWM digital signal, we can effectively control the light output (brightness) by
varying the duty cycle: for example if we use a 33% of duty cycle over a short duration of time, we will turn the LED ON for
the 33% of the cycle and OFF for the remaining 67% of that time period, so the LED will appear less bright because it does
receive a third of the power.
Since humans perceive brightness not in a linear way but logarithmically
like hearing response, we need to implement a PWM signal in which the duty cycle varies according to a logarithmic curve.
Because implementing transcendental functions via algorithm is cumbersome
and the values we need to cover are only sixteen, the most efficient way is to create a sort of look-up table for
all the sixteen possible duty cycle values. A duty cycle with 8-bit resolution, with a range from 0 to 255, looks
enough for our purpose.
Magnitude
Input
Magnitude
PWM
8-bit
8-bit
8-bit
Scale
Switches
Scale
%
Dec
Binary
Hex
(Linear)
Bin Code
(Logarithmic)
DC Value
DC Value
DC Value
DC Value
1
0000
1,00
100%
255
1111 1111
FF
2
0001
0,75
75%
191
1011 1111
BF
3
0010
0,60
60%
153
1001 1001
99
4
0011
0,50
50%
127
0111 1111
7F
5
0100
0,42
42%
106
0110 1010
6A
6
0101
0,35
35%
90
0101 1010
5A
7
0110
0,30
30%
76
0100 1100
4C
8
0111
0,25
25%
63
0011 1111
3F
9
1000
0,21
21%
52
0011 0100
34
10
1001
0,17
17%
43
0010 1011
2B
11
1010
0,14
14%
34
0010 0010
22
12
1011
0,10
10%
26
0001 1010
1A
13
1100
0,07
7%
19
0001 0011
13
14
1101
0,05
5%
12
0000 1100
0C
15
1110
0,02
2%
5
0000 0101
05
16
1111
0,00
0%
0
0000 0000
00
To much simplify the algorithm I preferred to program the eight switches
of APOLLO181 using the Johnson Counter sequence (i.e. 0000; 0001; 0011; 0111; 1111; 1110; 1100; 1000 ...), in which only one switch
changes status for each brightness magnitude step. This is a convenient way to
fast switch between 16 distinct states for
controlling the duty cycle of the PWM signal.
Magnitude
Johnson
Magnitude
PWM
8-bit
8-bit
8-bit
Scale
Counter
Scale
%
Dec
Binary
Hex
(Linear)
Bin Code
(Logarithmic)
DC Value
DC Value
DC Value
DC Value
1
00000000
1,00
100%
255
1111 1111
FF
2
00000001
0,75
75%
191
1011 1111
BF
3
00000011
0,60
60%
153
1001 1001
99
4
00000111
0,50
50%
127
0111 1111
7F
5
00001111
0,42
42%
106
0110 1010
6A
6
00011111
0,35
35%
90
0101 1010
5A
7
00111111
0,30
30%
76
0100 1100
4C
8
01111111
0,25
25%
63
0011 1111
3F
9
11111111
0,21
21%
52
0011 0100
34
10
11111110
0,17
17%
43
0010 1011
2B
11
11111100
0,14
14%
34
0010 0010
22
12
11111000
0,10
10%
26
0001 1010
1A
13
11110000
0,07
7%
19
0001 0011
13
14
11100000
0,05
5%
12
0000 1100
0C
15
11000000
0,02
2%
5
0000 0101
05
16
10000000
0,00
0%
0
0000 0000
00
The important factor here is the refresh rate: if we use a frequency that is too much low, we will turn
the light on and off too slowly and the viewer will see the LED ‘flickering’ and not a stable light.
So we need a clock cycle beyond the limit
of the flicker perception of human eye, and anything faster than 100 Hz would be probably OK. Thus we will adopt in this program
an approximate cycle of 180 Hz, that has a period time of 5555 microseconds.
Being 3 MHz the clock of APOLLO181 and because every instruction takes four clock cycles,
each instruction takes 1/3 * 4 = 1,333333 µs to be executed.
We will need then to create a precise routine of delay which shall last in total 5555 µs, that approximately
equals to 5555/1,333333 = 4166, about 4096 instructions that consist of 256 cycles of 16 instructions
each.
Here APOLLO181 program:
00
E7
ACC = IN PORT [7]
01
B0
Compare ACC with REG0
02
1A
ACC = A#
03
C0
IF ZERO GOTO 0A#
04
E7
ACC = IN PORT [7]
05
20
REG [0] = ACC
06
E8
ACC = IN PORT [8]
07
2A
REG [A] = ACC
08
10
ACC = 0#
09
01
GOTO 10#
0A
E8
ACC = IN PORT [8]
0B
BA
Compare ACC with REGA
0C
15
ACC = 5#
0D
CB
IF ZERO GOTO B5#
0E
16
ACC = 6#
0F
00
GOTO 06#
10
3A
ACC = REG [A]
11
A8
Compare ACC with 8#
12
10
ACC = 0#
13
C5
IF ZERO GOTO 50#
14
3A
ACC = REG [A]
15
AC
Compare ACC with C#
16
16
ACC = 6#
17
C5
IF ZERO GOTO 56#
18
3A
ACC = REG [A]
19
AE
Compare ACC with E#
1A
1C
ACC = C#
1B
C5
IF ZERO GOTO 5C#
1C
3A
ACC = REG [A]
1D
AF
Compare ACC with F#
1E
11
ACC = 1#
1F
CE
IF ZERO GOTO E1#
20
30
ACC = REG [0]
21
A8
Compare ACC with 8#
22
18
ACC = 8#
23
C6
IF ZERO GOTO 68#
24
30
ACC = REG [0]
25
AC
Compare ACC with C#
26
1E
ACC = E#
27
C6
IF ZERO GOTO 6E#
28
30
ACC = REG [0]
29
AE
Compare ACC with E#
2A
14
ACC = 4#
2B
C7
IF ZERO GOTO 74#
2C
30
ACC = REG [0]
2D
AF
Compare ACC with F#
2E
17
ACC = 7#
2F
CE
IF ZERO GOTO E7#
30
3A
ACC = REG [A]
31
A7
Compare ACC with 7#
32
10
ACC = 0#
33
C8
IF ZERO GOTO 80#
34
3A
ACC = REG [A]
35
A3
Compare ACC with 3#
36
16
ACC = 6#
37
C8
IF ZERO GOTO 86#
38
3A
ACC = REG [A]
39
A1
Compare ACC with 1#
3A
1C
ACC = C#
3B
C8
IF ZERO GOTO 8C#
3C
3A
ACC = REG [A]
3D
A0
Compare ACC with 0#
3E
1D
ACC = D#
3F
CE
IF ZERO GOTO ED#
40
30
ACC = REG [0]
41
A7
Compare ACC with 7#
42
18
ACC = 8#
43
C9
IF ZERO GOTO 98#
44
30
ACC = REG [0]
45
A3
Compare ACC with 3#
46
1E
ACC = E#
47
C9
IF ZERO GOTO 9E#
48
30
ACC = REG [0]
49
A1
Compare ACC with 1#
4A
14
ACC = 4#
4B
CA
IF ZERO GOTO A4#
4C
30
ACC = REG [0]
4D
A0
Compare ACC with 0#
4E
1A
ACC = A#
4F
CA
IF ZERO GOTO AA#
50
1F
ACC = F#
51
21
REG [1] = ACC
52
1F
ACC = F#
53
22
REG [2] = ACC
54
1A
ACC = A#
55
05
GOTO 5A#
56
1F
ACC = F#
57
21
REG [1] = ACC
58
1B
ACC = B#
59
22
REG [2] = ACC
5A
10
ACC = 0#
5B
06
GOTO 60#
5C
19
ACC = 9#
5D
21
REG [1] = ACC
5E
19
ACC = 9#
5F
22
REG [2] = ACC
60
16
ACC = 6#
61
6
GOTO 66#
62
1F
ACC = F#
63
21
REG [1] = ACC
64
17
ACC = 7#
65
22
REG [2] = ACC
66
1C
ACC = C#
67
06
GOTO 6C#
68
1A
ACC = A#
69
21
REG [1] = ACC
6A
16
ACC = 6#
6B
22
REG [2] = ACC
6C
12
ACC = 2#
6D
07
GOTO 72#
6E
1A
ACC = A#
6F
21
REG [1] = ACC
70
15
ACC = 5#
71
22
REG [2] = ACC
72
18
ACC = 8#
73
07
GOTO 78#
74
1C
ACC = C#
75
21
REG [1] = ACC
76
14
ACC = 4#
77
22
REG [2] = ACC
78
1E
ACC = E#
79
07
GOTO 7E#
7A
1F
ACC = F#
7B
21
REG [1] = ACC
7C
13
ACC = 3#
7D
22
REG [2] = ACC
7E
14
ACC = 4#
7F
08
GOTO 84#
80
14
ACC = 4#
81
21
REG [1] = ACC
82
13
ACC = 3#
83
22
REG [2] = ACC
84
1A
ACC = A#
85
08
GOTO 8A#
86
1B
ACC = B#
87
21
REG [1] = ACC
88
12
ACC = 2#
89
22
REG [2] = ACC
8A
10
ACC = 0#
8B
09
GOTO 90#
8C
12
ACC = 2#
8D
21
REG [1] = ACC
8E
12
ACC = 2#
8F
22
REG [2] = ACC
90
16
ACC = 6#
91
09
GOTO 96#
92
1A
ACC = A#
93
21
REG [1] = ACC
94
11
ACC = 1#
95
22
REG [2] = ACC
96
1C
ACC = C#
97
09
GOTO 9C#
98
13
ACC = 3#
99
21
REG [1] = ACC
9A
11
ACC = 1#
9B
22
REG [2] = ACC
9C
12
ACC = 2#
9D
0A
GOTO A2#
9E
1C
ACC = C#
9F
21
REG [1] = ACC
A0
10
ACC = 0#
A1
22
REG [2] = ACC
A2
18
ACC = 8#
A3
0A
GOTO A8#
A4
15
ACC = 5#
A5
21
REG [1] = ACC
A6
10
ACC = 0#
A7
22
REG [2] = ACC
A8
1E
ACC = E#
A9
0A
GOTO AE#
AA
10
ACC = 0#
AB
21
REG [1] = ACC
AC
10
ACC = 0#
AD
22
REG [2] = ACC
AE
40
SET ALU to 0#
AF
31
ACC = REG [1]
B0
70
ACC = Logic f (ACC, 0#)
B1
23
REG [3] = ACC
B2
32
ACC = REG [2]
B3
70
ACC = Logic f (ACC, 0#)
B4
24
REG [4] = ACC
B5
90
SET NO CARRY
B6
31
ACC = REG [1]
B7
2E
REG [E] = ACC
B8
32
ACC = REG [2]
B9
2F
REG [F] = ACC
BA
10
ACC = 0#
BB
F7
OUT PORT [7] = ACC
BC
F8
OUT PORT [8] = ACC
BD
F9
OUT PORT [9] = ACC
BE
FA
OUT PORT [A] = ACC
BF
3E
ACC = REG [E]
C0
49
SET ALU to 9#
C1
51
ACC = Arithm f (ACC, 1#, CARRY)
C2
2E
REG [E] = ACC
C3
3F
ACC = REG [F]
C4
50
ACC = Arithm f (ACC, 0#, CARRY)
C5
2F
REG [F] = ACC
C6
1A
ACC = A#
C7
DC
IF CARRY GOTO CA#
C8
1A
ACC = A#
C9
0B
GOTO BA#
CA
90
SET NO CARRY
CB
33
ACC = REG [3]
CC
2E
REG [E] = ACC
CD
34
ACC = REG [4]
CE
2F
REG [F] = ACC
CF
1F
ACC = F#
D0
F7
OUT PORT [7] = ACC
D1
F8
OUT PORT [8] = ACC
D2
F9
OUT PORT [9] = ACC
D3
FA
OUT PORT [A] = ACC
D4
3E
ACC = REG [E]
D5
49
SET ALU to 9#
D6
51
ACC = Arithm f (ACC, 1#, CARRY)
D7
2E
REG [E] = ACC
D8
3F
ACC = REG [F]
D9
50
ACC = Arithm f (ACC, 0#, CARRY)
DA
2F
REG [F] = ACC
DB
1F
ACC = F#
DC
DD
IF CARRY GOTO DF#
DD
1F
ACC = F#
DE
0C
GOTO CF#
DF
10
ACC = 0#
E0
00
GOTO 00#
E1
30
ACC = REG [0]
E2
A0
Compare ACC with 0#
E3
12
ACC = 2#
E4
C6
IF ZERO GOTO 62#
E5
10
ACC = 0#
E6
02
GOTO 20#
E7
3A
ACC = REG [A]
E8
AF
Compare ACC with F#
E9
1A
ACC = A#
EA
C7
IF ZERO GOTO 7A#
EB
10
ACC = 0#
EC
03
GOTO 30#
ED
30
ACC = REG [0]
EE
AF
Compare ACC with F#
EF
12
ACC = 2#
F0
C9
IF ZERO GOTO 92#
F1
10
ACC = 0#
F2
04
GOTO 40#
DISCLAIMER & CREDIT:
All data here reproduced are for educational and non-commercial purpose, following fair-use guidelines.
This is an INDIPENDENT AND UNOFFICIAL hobby site. Either
Dr. Peter R. Rony or the Blacksburg group or Computer History Museum (Mountain View, CA) or other third-party DO NOT
HAVE ANY ASSOTIATION with this work.
Author G.G. DOESN'T EARN ANYTHING FROM ADVERTISEMENT. This site is not in the business
of making money. This site is visible thanks to the Free Web Hosting Tripod Service, so it is ad-supported: advertisement
contents, costs and revenues are full managed by the service itself. Author does not have any involvement in them. The advertising
links in the Site pages and in the pop-up windows are not Author's property. They can change and the Author is non-responsible
about their contents and working. The Author is not responsible about the linked sites. The information presented here
is just that: INFORMATION. Use it at your own risk and for only non commercial purpose. The information here presented is
believed to be technically correct and everything presented on this site is done so in good faith. Anyhow you (the reader)
are responsible for anything that you might do as a result of reading this article. You assume complete and total responsibility
for your actions! Author is not responsible for any misuse or damage coming from the reading and using this information.
Text and images from original
typewritten Bugbooks I and II in 1974 are permission courtesy of Dr. Peter R. Rony, the original author and sole copyright
owner of the Bugbooks I, II, IIA, III, V, and VI.
The background image on the
header of each page of the site is "Sunset over western South America" photographed
on 12 April 2011 by an Expedition 27 crew member on the International Space Station. (Image credit: NASA). On
it I have merged titles and a my photo of TIL302 displays.
Texas Instruments data are
Texas Instruments Copyright and reported by Courtesy of Texas Instruments.
TERM OF USE: With clear exception for texts and images
which are not author's property, Gianluca G. freely authorizes you the downloading, printing and reproducing of
APOLLO181 data, texts and images ONLY for non-commercial usage and ONLY if you give a clear reference to its source and project name, without any right to resell or redistribute them or to compile or create derivative works.
Any rights not expressly granted herein are reserved.