ArSid - Arduino Sid Synthesizer

Fri 18-Oct-19
21:13:29




Lib: Paneel

Datum: Sun 20 January 2019
Samenvatting: Met een interrupt de schakelaars en poteniometers inlezen PLUS de Leds aansturen.
 Soort Artikel: Software
Status: Afgerond



[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]
lib_panel_1-leds.jpg
1/9: lib_panel_1-leds.jpg.
lib_panel_2-schakelaars0-7.jpg
2/9: lib_panel_2-schakelaars0-7.jpg.
lib_panel_3-schakelaars8-f.jpg
3/9: lib_panel_3-schakelaars8-f.jpg.
lib_panel_4-potmeters.jpg
4/9: lib_panel_4-potmeters.jpg.
lib_panel_5-geletterd.jpg
5/9: lib_panel_5-geletterd.jpg.
lib_panel_6-adcmux_blok.jpg
6/9: lib_panel_6-adcmux_blok.jpg.
Meer
[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]

Deze Library is een schilletje om de I/O-library en heeft 3 basis-taken, namelijk het uitlezen van alle schakelaars (inclusief de dip-switches op de achterzijde), het uitlezen van de controle-potentiometers en het aansturen van de kleuren-Leds bovenaan het paneel.

Deze Library "bestuurt" daarom de Latches-print. De Latches-print is weer verbonden met de vele schakelaars (en dip-switches op de achterzijde van de ArSid) en de kleuren-Leds. De Potentiometers echter zijn rechtstreeks met de Arduino Mega verbonden.

In een standaard computer systeem (zoals een Home-Computer, PC of Studeer Computer Systeem) gebeurt deze I/O met speciale IC's, meestal een Parallel Interface Adapter (PIA), Versital Interface Adapter (VIA) of zelfs Complex Interface Adapter (CIA) genoemd. Meestal hebben deze IC's twee complete poorten van elk 8 bits, welke als ingang of als uitgang kan worden geschakeld. Naast deze twee poorten kunnen deze IC's ook andere zaken bevatten, zoals Timers en Schuifregisters (handig om een RS232 omzetter te kunnen realiseren).

De ArSid heeft meer uitgangen en ingangen nodig dan de 16 bits die zo'n speciaal I/O-IC heeft, zodat er minstens 2 van nodig zouden zijn. Aangezien zo'n IC al gauw 40 aansluitpinnen heeft, heeft dit een erg groot oppervlak op de print nodig. Onnodig (en ook kostbaar) als men bedenkt dat er in zo'n IC ook dingen zitten die niet gebruikt zullen worden (Timers en Schuifregisters). Vandaar dat de keuze is gevallen om losse registers te gebruiken.

Het in-een-matrix plaatsen van schakelaars en Leds.

De ArSid heeft een behoorlijk aantal schakel-elementen en Leds, die allemaal natuurljk aangesloten moeten worden. In tabel-vorm zijn dit er:

Aantal Schakelaars Draden AantalLeds Draden Totaal  Opmerkingen:
27 * 3-standen schakelaars 54 19 * 3-kleuren Leds 57  
1 * 12-standen schakelaars 12  
4 * 4-positie dip-switches 16  
losse pinnen Totaal 82 Totaal 57 139 pinnen Recht-toe-recht-aan
Matrix Totaal11 + 8 Totaal 8 + 8 35 pinnen In een matrix, waarbij elke rij en elke kolom naar een losse I/O pin gaat
Matrix + Decoder Totaal 4 + 8 Totaal 3 + 8 23 pinnen In een matrix, waarbij elke rij (of kolom) door een decoder aangestuurd wordt. Er is reserve voor een 20ste extra RGB-Leds.
ArSid Totaal 4 + 8 Totaal3 + 3*3 24 pinnen De Leds zijn logischer geschakeld, waardoor er 1 I/O-pin extra nodig is. Er is nu (qua matrix) wel reserve voor nog 5 extra RGB-Leds (nr.17 en 21 t/m 24). Qua schakelaars is er nu ruimte voor totaal 128 schakel-elementen. Ook hier zit behoorlijk wat reseve aantallen in.

Ook voor de schakelaars en Leds zijn er een aantal methodes om ze aan te sluiten, namelijk recht-toe-recht-aan en in een Matrix (Raster) (al of niet met decoders).

  1. Recht-toe-recht-aan: Elke schakelaar heeft een eigen ingang nodig en elke Led heeft een eigen uitgang nodig. Deze methode wordt geleerd in de begin-lessen van computers programmeren, zo ook bij de begin-lessen van de Arduino (Blink-programma, digitalWrite() en digitalRead()-functies) en het is het eenvoudigste om te begrijpen. Voor enkele schakelaars en enkele Leds is dit ook de beste oplossing.

    Voor de ArSid zouden hiervoor dan 139 losse I/O pinnen nodig zouden zijn, wat overeen komt met 18 (losse) 8-bits poorten. De Arduino Mega heeft zelf niet eens zoveel I/O pinnen en de electronica wordt te groot om hiervoor allemaal met losse registers te gebruken.

  2. In een matrix:De schakelaars (en de Leds) worden in een matrix geschakeld. Met 16 elementen zijn er dan maar 8 pinnen nodig om zo'n matrix aan te sturen, in plaats van 16 (voor elk element 1). Met 64 elementen worden dit 16 pinnen (tegen 64 om elk element afzonderlijk aan te sturen). Deze methode wordt meestal toegepast bij toetsenborden, Led-Displays en grote hoeveelheden Leds.

    Dit is ook de methode die aan de basis van de ArSid staat. MAAR ... (zie volgend punt).

  3. In een matrix met decoders: Wanneer er in de rijen (of de kolommen) van een matrix ook nog (losse) decoders worden toegepast, dan is het aantal I/O-pinnen nog verder te verminderen. 8 matrix-draden kunnen dan door 3-bits worden aangestuurd en 16 matrix-draden door 4-bits. Deze decoders zijn in de ArSid toegepast en terug te vinden op de Latches-print.

    Bij de laatste methode zijn er maar slechts 24 I/O-pins nodig. Maar bij zowel de schakelaars als de Leds zijn er bij elk gedeelte 4 pinnen niet in gebruikt (dus in totaal 8 pinnen). Hierdoor zijn de registers zelf logischer in gebruik (er zijn slechts Led-registers en schakelaar-registers en geen gecombineerd Led-Schakelaar register) en daardoor software-matig gemakkelijker te gebruiken. Ook wordt de software hierdoor een stukje sneller, omdat er geen bits (voor Leds en schakelaars) gecombineerd hoeven te worden.

De methodes zijn niet allemaal afzonderlijk uitgeprobeerd en ook zeker niet allemaal uitgebouwd. Het zal ook wel duidelijk zijn dat al vroeg goed is overdacht welke aansluit-methode er gebruikt zou gaan worden - en dat is de methode van in een matrix met decoders

Het uitlezen van de bedienings-elementen.

Er zijn twee basis methodes om schakelaars en potentiometers te kunnen uitlezen, namelijk op het moment dat ze nodig zijn, of continue uitlezen en de waarde opslaan in "tijdelijke" variabelen. Hierna wordt er alleen geschreven over schakelaars, maar voor de potentiometers geldt een soort gelijke methode.

Uitlezen op het moment dat het nodig is.

Op het moment dat de stand van een schakelaar nodig is, kunnen de I/O-poorten in hun stand worden gezet en kunnen de betreffende ingangen worden ingelezen en verder verwerkt om de juiste stand te verkrijgen.

Ook deze methode wordt geleerd in de begin-lessen van computers programmeren, zo ook bij de begin-lessen van de Arduino (Blink-programma, de analogRead() en digitalRead()-functies).

Deze methode is weliswaar recht-toe-recht-aan en erg begrijpelijk, maar op kritische momenten is deze methode tijd-verspillend.

Continue uitlezen.

Op vaste tijden worden de schakelaars ingelezen en in "tijdelijke" variabelen geplaatst. Op het moment dat de stand van een schakelaar nodig is, hoeft alleen de betreffende variabele(n) worden uitgelezen en de stand van de schakelaar is meteen beschikbaar.

Het continue scannen van de schakelaars wordt meestal in een interrupt-routine gedaan. Elke keer dat deze routine wordt doorlopen, zijn er weer twee mogelijkheden.

  1. Alle schakelaars worden uitgelezen. Dit kost weer meer tijd, maar de routine hoeft minder vaak worden opgeroepen.

  2. Per keer wordt één schakelaar(rij) ingelezen. Dit kost minder tijd, de routine kan iets vaker worden opgeroepen en er is wat overhead nodig (aan variabelen) om bij te houden welke schakelaar(rij) er de volgende keer aan de beurt is. Bij de Leds en potentiometer wordt duidelijker warom voor deze methode is gekozen.

Om een Interrupt-routine voor deze taak te gebruiken, wordt hier een Interrupt-Timer gebruikt, die op vaste tijden een Interrupt genereert en daarmee deze routine opstart.

Deze methode is weliswaar ingewikkelder om toe te passen, maar het uitlezen gebeurt verspreid in de tijd en zelfs op tijd-kritische momenten zijn de standen van de schakelaars meteen beschikbaar.

Initialisatie en de Interrupt-Timer.

In de ArSid verricht de Interrupt Routine 3 basis taken: Het inlezen van één rij schakelaars, het inlezen van één potentiometer en het schrijven naar één rij Leds. Elke losse taak gebeurt in 1 keer doorlopen van de routine. Wat hierbij help is dat er maar 16 potentiometers zijn, 16 rijen schakelaars en 8 rijen Leds (2 * 8 => 16). Dit getal 16 maakt dat het bijhouden van elke rij gemakkelijk kan gebeuren.

Panel_Init(): Maar voordat het zover is, moet het Paneel (en dus de Latches via de Decoder) wel eerst worden ge-initialiseerd. Dit gebeurt in een aparte functie. In het kort plaatst deze functie de betreffende registers (Latches) van de Leds op een begin waarde, zodat alles UIT (OFF) is. Dit gebeurt door de Poke()-functies (Poke 0,xx) en Poke(1,xx).

Verder worden met een FOR(qq;;)-statement alle Array's, die gebruikt worden om waardes van de bedienings-elementen in op te slaan, op NUL gezet voor de schakelaars (PanelSchakData[][]) en op de huidige waarde van de potentiometer (PanelPotData[]). Het Array, waarin de Led-data komt te staan (PanelLedData[][]) wordt gevuld met waardes, zodat er een leuke kleuren patroon zal verschijnen. Ook bevat dit array (per Led-rij) de waarde om de juiste Led-Rij aan te kunnen sturen - vanaf 0 t/m 7, of te wel %000 t/m %111).

De ADC-registers in de Arduino Mega worden rechtstreeks ingesteld om de potentiometers correct in te kunnen lezen vanuit de Interrupt-routine (ADMUX & ADCSRA). De waardes hiervoor zijn gehaald uit de DataSheet van de Arduino Mega. De andere ADC-registers worden niet rechtstreeks gevuld, aangezien deze al door de functie analogRead() van de juiste instellingen zijn voorzien. (Noot: Welke waardes er daadwerkelijk in de betreffende ADC-registers ingevuld moeten worden in niet verder uitgezocht - maar dit leek mij de makkelijkkste methode om toe te passen.)

De "tellers" om bij te houden welke rij er wordt gelezen, worden in het begin van de Library op NUL gezet (PanelLedCount; PanelSchakPotCount & PanelCount)

Als laatste wordt de Interrupt-Timer ingesteld met de functie Timer_Init().

Timer_Init(): Zoals de naam al suggereert, regelt deze functie het instellen van de Interrupt-Timer, zodat deze op vaste tijden een interrupt genereert. Hierbij worden er Arduino registers rechtstreeks beschreven. Het register OCR1A bepaalt in grote maten om de hoeveel tijd er een interrupt ontstaat en de waarde is proef-ondervindelijk gevonden.

Bij berekeningen achteraf blijkt het volgende. De formule hiervoor is: f OCnA = f clk_IO / (2 * N * (1 + OCRnA) )

Hierbij geldt het volgende:

  • f clk_IO = 16MHz (bij de Arduino Mega 2560).
  • N = PreScaler waarde = 1.
  • OCRnA = de waarde in het programma = 24576 (= $6000).

Invullen geeft:

  • f OCnA = 16 Mega / (2 * 1 * ( 1 + 24576) )
  • f OCnA = 16 Mega / ( 49154 )
  • f OCnA = 325.5Hz (bijna 325.5Hz, om precieser te wezen: 325.507588395Hz).

Deze frequentie komt overeen met een cyclus-tijd van 3.072msec. Of te wel, elke 3.072 milli seconde wordt er een interrupt gegenereerd. En wordt dus de volgende routine opgeroepen.

ISC(): De naam staat voor Interrupt Service Routine en deze routine verricht de 3 taken.

  • Schakelaars uitlezen: De schakelaar-rij wordt ingesteld (Poke(5,xx)) en de schakelaar kolom wordt ingelezen (Peek(4)). Er vindt een korte controle plaats of de ingelezen waarde hetzelfde is als bij een vorige keer (dat deze rij gelezen werd) (PanelSchakData[][1]), om een anti-dender systeem te krijgen. Alleen dan wordt de waarde "geldig" bevonden en opgeslagen in de uiteindelijke schakelaar-array (PanelSchakData[][0]). Deze waarde wordt nog een keer opgeslagen om bij de volgende keer als controle gebruikt te worden.

  • Leds aansturen: De Led-data uit de Led-Array (PanelLedData[][]) worden geplaatst in de Led-Registers (op de Latches) (Poke(1,xx) & Poke(0,xx)). Ondanks de teller PanelCount, gebeurt het hardware-matig instellen van de Led-rij via hetzelfde Led-Array. Deze data staat op de hoogste 4-bits van PanelLedData[][1].

  • Potentiometers uitlezen: Alle potentiometers die de Arduino kan inlezen (6 stuks bij de Uno, 16 stuks bij de Mega) komen in de Arduino bij een MultiPlexer (Mux) terecht (een soort van electronische meerkeuze schakelaar). Deze wordt in een stand gezet. Een ADC (Analog Digital Converter) leest het doorgegeven signaal en plaatst deze in een Arduino-register, zodat het klaar is om gebruikt te worden. Het omzetten wordt geheel hardware matig gedaan, maar het kost wat tijd, maximaal 14 ADC-clock-pulsen, MAAR de eerste keer kost het 25 ADC-clock-pulsen (vandaar dat alle potentiometers al ingelezen worden bij het initialiseren). Een ADC-Clock-puls is in te stellen van 20µsec t/m 1µsec.

    In de data-sheet (zie link hieronder) staat in §26 (blz 268 t/m 288) het gehele ADC-proces in detail beschreven, inclusief de beschrijving van de Arduino registers die bij het omzettings-proces nodig zijn.

    Omdat het converteren geheel hardware matig wordt gedaan, hoeft de Arduino hier geen programma-tjd aan te verspillen, behalve dan bij het wachten op de analoge-waarde. En dit wachten is wat er gebeurt bij de standaard analogRead()-functie.

    Daarom wordt in de Interrupt-routine eerst een potentiometer-waarde opgehaald (ADCH) en vervolgens wordt de volgende potentiometer gekozen (ADMUX & ADCSRB). Daarna wordt er een nieuwe omzetting gestart (ADSRA). De conversie zal dan gedaan worden, terwijl het hoofdprogramma andere dingen aan het doen is.

    Het is jammer dat de selectie door de Mux door twee registers wordt gestuurd, in plaats van één. Hierdoor is er een extra regel nodig, wat weer de nodige tijd kost.

    ADC Direct Register Write ...
    Mux Select (Table 26-4)
    Analog ADCSRBADMUXADMUX
    Channel 5 43 210
    0 0 00 000
    1 0 00 001
    2 0 00 010
    3 0 00 011
    4 0 00 100
    5 0 00 101
    6 0 00 110
    7 0 00 111
    8 1 00 000
    9 1 00 001
    A 1 00 010
    B 1 00 011
    C 1 00 100
    D 1 00 101
    E 1 00 110
    F 1 00 111

Alle resultaten die in de diverse array's zijn geplaatst worden uitgelezen met behulp van zogenaamde uitlees-functies, die hieronder worden beschreven.

Met de berekende interupt-frequentie van (afgerond) 320Hz betekent dit het volgende:

  • 320Hz / 8 Leds => 40 keer per seconde zijn alle (16) Led(s)(rijen) ververst.
  • 320Hz / 16 Leds => 20 keer per seconde zijn alle (16) potentiometers en alle (16) schakelaar(s)(groepen) een keer uitgelezen.alle Leds ververst.

Op zich vind ik dit acceptabele waardes.

Aansturen van de Kleuren Leds,

Deze functies vullen de Led-Array PanelLedData[][] met data om de Leds laten branden (of juist niet). Om deze functies beter te begrijpen op hun werking, is het nodig om te weten dat de Leds verdeeld zijn in 3 groepen van 8 Leds. Van elke groep worden de losse kleuren Rood, Groen en Blauw (RGB) aangestuurd vanuit 3 bits van de registers. Per groep is er een 4de bit als reserve aanwezig, die hier geen functie heeft, maar de aansturing wel een stuk logischer maken. Het is namelijk makkelijker om te denken per 4 bits, dan per 3 bits.

Alle Leds aangestuurd worden door 2 losse registers, namelijk nummer 0 en 1. Een opmerking vooraf, hoewel er hier wordt gesproken van 4 bits, hebben alleen de laagste 3 bits ervan effect op de kleur.

%x000  Zwart
%x001  Rood
%x010  Groen
%x011  Geel
%x100  Blauw
%x101  Paars
%x110  Cyan
%x111  Wit

  • De laagste 4 bits van register 0 bepalen de kleur voor Leds 0 t/m 7. Bit 0 voor Rood, Bit 1 voor Groen en Bit 2 voor Blauw.
  • De hoogste 4 bits van register 0 bepalen de kleur voor Leds 8 t/m 15. Bit 4 voor Rood, Bit 5 voor Groen en Bit 6 voor Blauw.
  • De laagste 4 bits van register 1 bepalen de kleur van Leds 16 t/m 19. Bit 0 voor Rood, Bit 1 voor Groen en Bit 2 voor Blauw.
  • De hoogste 4 bits van register 1 bepalen welke groep van 3 Leds er zullen branden: Namelijk: %x000 [0,8,16]; %x001 [1,9,17]; %x010 [2,10,18]; %x011 [3,11,19]; %x100 [4,12,-]; %x101 [5,13,-]; %x110 [6,14,-]; %x111 [7,15,-].
  • Met het combineren van de kleuren-bits zijn andere kleuren te maken:

De gehele led-matrix heeft daarom de volgende register-aansluitingen en functie:

RegisterBit Led-nr Led-nr Led-nr Led-nr Led-nr Led-nr Led-nr Led-nr Led-nr
0 0 Rood 0..7 R 0 R 1 R 2 R 3 R 4 R 5 R 6 R 7
0 1 Groen 0..7 G 0 G 1 G 2 G 3 G 4 G 5 G 6 G 7
0 2 Blauw 0..7 B 0 B 1 B 2 B 3 B 4 B 5 B 6 B 7
0 3 -reserved- - - - - - - - -
0 4 Rood 8..15 R 8 R 9 R 10 R 11 R 12 R 13 R 14 R 15
0 5 Groen 8..15 G 8 G 9 G 10 G 11 G 12 G 13 G 14 G 15
0 6 Blauw 8..15 B 8 B 9 B 10 B 11 B 12 B 13 B 14 B 15
0 7 -reserved- - - - - - - - -
1 0 Rood 16..23 R 16 R 17 R 18 R 19 -geen- -geen- -geen- -geen-
1 1 Groen 16..23 G 16 G 17 G 18 G 19 -geen- -geen- -geen- -geen-
1 2 Blauw 16..23 B 16 B 17 B 18 B 19 -geen- -geen- -geen- -geen-
1 3 -reserved- - - - - - - - -
1 4 Rij Bit 0 %0 %1 %0 %1 %0 %1 %0 %1
1 5 Rij Bit 1 %0 %0 %1 %1 %0 %0 %1 %1
1 6 Rij Bit 2 %0 %0 %0 %0 %1 %1 %1 %1
1 7 -niet aangesloten- x x x x x x x x
Led-Nummers 0- 8-16 1- 9-17 2-10-18 3-11-19 4-12-20 5-13-21 6-14-22 7-15-23

Om in het programma niet elke keer te hoeven nadenken over bitjes, volgordes en kleuren, moeten onderstaande functies de omrekeningen doen voor Led-nummers en de kleuren op de juiste plaats in de Led-Array PanelLedData[][] plaatsen. De data worden ook op zodanige plaatsen in het Array gezet, zodat de Interrupt-routine het minste tijd kwijt is om deze data naar de leds te sturen.

WriteLedOut(nr, L1, L2, L3): Deze schrijft de kleuren in de variabelen L1, L2 en L3, na wat controles en correcties, rechtstreeks naar de Led Nummer nr (van 0 .. 7). Deze functie gaat buiten de Interrupt-routine om en wordt vrijwel meteen overschreven door de data in de Led-Array.

Write_Led(LedNr, dd): Allereerst wordt er uit de LedNr bepaalt in welke Led-groep de Led zit (groep 0 (0..7); 1 (8..15) en 2 (16..23)). Daarna worden er enige correcties toegepast op de LedNr. De kleur in de variabele dd wordt ge-inverteerd en gecorrigeerd. Daarna vindt er in een CASE-statement een selectie plaats op de Led-groep. Elke groep zorgt voor een plaatsing van de kleur dd op de juiste plaats in de Led-Array PanelLedData[][] op de juiste bitjes.

ClearLed(Nr1, Nr2): Na een controle op de Led-nummers nr1 en nr2 worden alle Leds gedoofd (kleur is %0000) door middel van een FOR-statement en de Write_Led()-functie.

Status(ww): In het kort wordt een word (dubbele-byte) als binaire waarde op de eerste 16 Leds geplaatst. Elk bitje dat %1 is, krijgt een blauwe kleur, elk bitje dat %0 is wordt rood. Op deze manier zijn tijdens het schrijven van het complete programma tussen waardes te zien en te volgen.

Inlezen van de Schakelaars (en dip-switches).

Er is slechts één functie voor de Schakelaars die gebruikt wordt bij het uitlezen van de Schakelaar-Array.

ss=Read_Schak(SchakNr): Er wordt een controle/ correctie gedaan op de variabele SchakNr (voor de Schakelaar-rij), waarna de waarde uit de juiste plaats uit de schakelaar-array PanelSchakData[][0] wordt gehaald. Deze waarde wordt teruggegeven aan de functie. Elk bitje van deze waarde representeert een enkele schakelaar uit de betreffende schakelaar-rij.

De Schakelaar-nummering is recht-toe-recht-aan. Nr. 0 voor rij 0 t/m nr. 11 voor rij 11 (=$B). De Dip-switches achterop hebben nummer 14 (=$E) en nummer 15 (=$F).

Rij 12 (=$C) en rij 13 (=$D) zijn niet aangesloten (op de boven genoemde datum), maar kunnen in de toekomst wellicht nog worden gebruikt voor extra (interne) Dip-switches voor nog extra in te stellen functies.

Inlezen van de Potentiometers.

Er is slechts één functie voor de Potentiometers, die gebruikt wordt bij het uitlezen van de Potentiometer-Array.

pp=Read_Pot(PotNr): Er wordt een controle/ correctie gedaan op de variabele PotNr, waarna de waarde uit de juiste plaats uit de Potentiometer-array PanelPotData[] wordt gehaald. Deze waarde wordt teruggegeven aan de functie. Deze waarde representeert de stand van de betreffende Potentiometer.

De Potentiometer-nummering is recht-toe-recht-aan. Nr. 0 voor Potentiometer 0, t/m nr. 15 voor Potentiometer 15 (=$F).


Datasheet Arduino Mega: https://www.microchip.com/wwwproducts/en/ATMEGA2560. 


[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]

Afbeeldingen

lib_panel_1-leds.jpg
1/9: lib_panel_1-leds.jpg.
lib_panel_2-schakelaars0-7.jpg
2/9: lib_panel_2-schakelaars0-7.jpg.
lib_panel_3-schakelaars8-f.jpg
3/9: lib_panel_3-schakelaars8-f.jpg.
lib_panel_4-potmeters.jpg
4/9: lib_panel_4-potmeters.jpg.
lib_panel_5-geletterd.jpg
5/9: lib_panel_5-geletterd.jpg.
lib_panel_6-adcmux_blok.jpg
6/9: lib_panel_6-adcmux_blok.jpg.
lib_panel_7-timer_blok.jpg
7/9: lib_panel_7-timer_blok.jpg.
lib_panel_8-prescaler_blok.jpg
8/9: lib_panel_8-prescaler_blok.jpg.
lib_panel_9-freqcalc.jpg
9/9: lib_panel_9-freqcalc.jpg.


[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]
Broncode: [1: Paneel (.cpp)] [2: Paneel (.h)]

1: De broncode van Paneel (.cpp)

(Mon 01 January 2018) ArSid_Panel.cpp: Met een interrupt de schakelaars en poteniometers inlezen PLUS de Leds aansturen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
//=======================================================================================
//
// ArSid_Panel - The ArSid_Panel Library
//
// All Functions, needed For Handling The Front Panel (Leds and Schaks - with Interrupts).
//
//=======================================================================================

#ifndef ARSID_PANEL_CPP
#define ARSID_PANEL_CPP

// Include some ArSid_ Libraries
#include "Arduino.h"
#include "ArSid_Panel.h"
#include "Basic_Funk.h"
#include "ArSid_IO.h"
#include "ArSid_Lcd.h"

//=====================================================================================

//  33333333          33        33      33    3333333333    33
//  33      33      33  33      3333    33    33            33
//  33      33    33      33    33  33  33    33            33
//  33333333      3333333333    33    3333    33333333      33
//  33            33      33    33      33    33            33
//  33            33      33    33      33    33            33
//  33            33      33    33      33    3333333333    3333333333

const byte MaxPanelLedSchak = 16;
const byte MaxPanelPot = 16;
volatile byte PanelLedData   [MaxPanelLedSchak][2];   // = [00..15][0..1] of (0RGB 0RGB) (nnnn 0RGB)
volatile byte PanelSchakData [MaxPanelLedSchak][2];   // = [00..15][0..1] of (7654 3210) (7654 3210)
volatile byte PanelPotData   [MaxPanelPot];           // = [00..15]       of (potv alue)
volatile byte PanelLedCount = 0;
volatile byte PanelSchakPotCount = 0;
volatile byte PanelCount = 0;

void Write_LedOut (byte nr, byte L1, byte L2, byte L3)
{ // Write To The Panel RGB-Leds
  nr = nr & 0x0F;
  L1 = (L1 & 0x0F) ^ 0x0F;
  L2 = (L2 & 0x0F) ^ 0x0F;
  L3 = (L3 & 0x0F) ^ 0x0F;
  Poke(0, (L2 << 4) + L1);
  Poke(1, (nr << 4) + L3);
}

byte Read_Schak (byte SchakNr)
{ // Read The Schakelaar-Data of Schakelaar-Number SchakNr
  // Get The Actual Schaks-Data from the Array
  byte ss = PanelSchakData[SchakNr & 0x0F][0];
  return ss;
}

void Clear_Led(byte nr1, byte nr2)
{ // Clear All Panel Led's
  nr1 = minmax(nr1, 0, 23);
  nr2 = minmax(nr2, 0, 23);
  for (byte qq = nr1; qq <= nr2 ; qq = qq + 1)
      { Write_Led(qq,0);
      }
}

void Write_Led(byte LedNr, byte dd)
{ // Write To The Panel RGB-Leds LedNr := DD(0RGB)
  // Check Input Data
  byte ledpart = (LedNr >> 3) & 0x03; // Calculate LedPart $00..$07 - $08..$0F - $10..$17
  LedNr = LedNr &0x07;                // Calculate LedNr
  dd = (~dd) & 0x0F;                  // Check LedData and Invert (Due to Electronics)
  // Fill PanelLed-Array with the Data
  switch (ledpart)
    { case 0: { // LedNr. $00..$07 = 00..07   => part[0] Low Nibble
                PanelLedData [LedNr][0] = (PanelLedData [LedNr][0] & 0xF0) | dd;
                break;
              }
      case 1: { // LedNr. $08..$0F = 08..15   => part[0] High Nibble
                PanelLedData [LedNr][0] = (PanelLedData [LedNr][0] & 0x0F) | (dd << 4);
                break;
              }
      case 2: { // LedNr. $10..$17 = 16..23   => part[1] Low Nibble
                // But only mounted (in the original) are Led $11..$13 = 17..19
                PanelLedData [LedNr][1] = (PanelLedData [LedNr][1] & 0xF0) | dd;
                break;
              }
      case 3: { // LedNr. $18..$1F = 24..31   => none - Due to Electronics
                // Do Nothing
                break;
              }
    };
}

byte Read_Pot(byte PotNr)
{ // Read the value of potmeter(PotNr);
  // Get The Actual Schaks-Data from the Array
  byte pp = PanelPotData [PotNr & 0x0F];  // = [00..15] of (potv alue)
  return pp;
}

void Status(word ww)
{ // Show a word on the top-leds
  for (byte qq=0; qq<=15; qq=qq+1)
      { if ((ww & (0x8000 >> qq)) != 0)  { Write_Led(qq,4)}
        else                             { Write_Led(qq,1)}
      }
}

//=====================================================================================

void Timer_Init()
{ // Initialize Timer1
  noInterrupts();                // Disable Interrupts
  // Timer Counter Control Registers 1A & 1B
  TCCR1A = 0;                    // Make all bits of this register to 0
  TCCR1B = 0;                    // Make all bits of this register to 0

  OCR1A  = 24576;                // set Output Compare Register 1 to the desired timer count
  TCCR1B = TCCR1B | B00001000;   // turn on CTC mode (WGM12 = bit 3 - Waveform Generation Mode)
  TCCR1B = TCCR1B | B00000001;   // Set the Prescaler (CS12 CS11 CS10 = bit 2..0 - Clock Select)

  TIMSK1 = TIMSK1 | B00000010;   // set Timer Interrupt MaSK 1 to compare interrupt
                                 // (OCIE1A = bit 1 - Output Compare Match Interrupt Enable 1A)
  interrupts();                  // Enable Interrupts
}

void Panel_Init()
{ // Init LedRij Col en ColNr
  Poke(0, 0xFF); // Init LedRij Col
  Poke(1, 0x0F); // Init LedRij ColNr
  // Clear The Panel Leds and Schaks
  for (byte qq = 0; qq <MaxPanelLedSchak ; qq = qq + 1)
      { // Fill Leds with a nice colour texture
        PanelLedData [qq][0]   = (qq * 16) + (qq ^ 0x0F);
        PanelLedData [qq][1]   = (qq << 4) + (qq ^ 0x00);
        // Fill Schakelaars Data with Zeroes
        PanelSchakData [qq][0] = 0;
        PanelSchakData [qq][1] = 0;
        // Fill Potmeter Data with their analog value
        // This also initializes the ADC-parameters
        PanelPotData [qq] = analogRead(qq+1) >> 2;
      }
  // Potmeter Init
  ADMUX  = (ADMUX  & 0b11011111) | (0b00100000) ;  // Set Adler at LeftAdjusted.
  ADCSRA = (ADCSRA & 0b10111111) | (0b01000000) ;  // Now Start the new conversion.
  // Timer Values Init
  Timer_Init();
}

//=====================================================================================
// THE Interrupt Service Routine
//=====================================================================================

ISR(TIMER1_COMPA_vect)
{ // De Interrupt Service Routine - The main interrupt-routine.
  // This will handle all IO with the front-panel
  // It does 1 Led, 1 SchakRow and 1 Potmeter (at each Interrupt).

  // ADC Conversion of the previous round must be finished.

  // Read The Schaks
  Poke(5,PanelSchakPotCount);                           // Write Schakelaars Out
  byte ss = ~ Peek(4);                                  // Read  Schakelaars In
  // Now check the Schaks with the previous read (anti-bouncing)
  if (ss == PanelSchakData[PanelSchakPotCount][1])
     { // Twice the same -> The value must be valid
       PanelSchakData[PanelSchakPotCount][0] = ss;      // Store Schakelaars
     }
  // Store the value for checking in the next round.
  PanelSchakData[PanelSchakPotCount][1] = ss;           // Store Schakelaars

  // Clear The Led's, to Prevent Ghosting, before...
  Poke(0,                                       0xFF);  // L2 , L1
  Poke(1, PanelLedData [PanelCount & 0x07][1] | 0x0F);  // nr , L3
  // ... before Increasing The Counter
  PanelCount = (PanelCount + 1) & 0x0F;
  // Then Show new Led-Data
  Poke(1, PanelLedData [PanelCount & 0x07][1] );        // nr , L3
  Poke(0, PanelLedData [PanelCount & 0x07][0] );        // L2 , L1

  // Read Conversion for Current Channel - Because ADLAR is set
  // to Left Adjusted, only the High Part needed to be read
  PanelPotData[PanelSchakPotCount] = ADCH ;
  // Next Schakelaar and Analog Channel
  PanelSchakPotCount = (PanelSchakPotCount+1) & 0x0F;
  // Start Conversion ADC for Next Channel
  ADMUX  = (ADMUX  & 0b11100000) | (PanelSchakPotCount & 0b00000111) ;    // Only the lower 3 bits 210
  ADCSRB = (ADCSRB & 0b11110111) | (PanelSchakPotCount & 0b00001000) ;    // Only bit 3
  ADCSRA = (ADCSRA & 0b10111111) | (0b01000000) ;                         // Now Start the new conversion.
  // ADC Conversion will be done, while the main program is doing other stuff.
}

//=====================================================================================

// ADC Direct Register Write ...
// Mux Select (Table 26-4)
//    Ch 5 43 210 Mux
//    0  0 00 000
//    1  0 00 001
//    2  0 00 010
//    3  0 00 011
//    4  0 00 100
//    5  0 00 101
//    6  0 00 110
//    7  0 00 111
//
//    8  1 00 000
//    9  1 00 001
//    A  1 00 010
//    B  1 00 011
//    C  1 00 100
//    D  1 00 101
//    E  1 00 110
//    F  1 00 111


//=====================================================================================

#endif // ARSID_PANEL_CPP



Broncode: [1: Paneel (.cpp)] [2: Paneel (.h)]

2: De broncode van Paneel (.h)

(Mon 01 January 2018) ArSid_Panel.h: Met een interrupt de schakelaars en poteniometers inlezen PLUS de Leds aansturen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//=======================================================================================
//
// ArSid_Panel - The ArSid_Panel Library
//
// All Functions, needed For Handling The Front Panel (Leds and Schaks - with Interrupts).
//
//=======================================================================================

#ifndef ARSID_PANEL_H
#define ARSID_PANEL_H

#include "Arduino.h"

//=====================================================================================

void Panel_Init();
  // Init The Panel Leds and Schaks

void Clear_Led(byte nr1, byte nr2);
  // Clear All Panel Led's

void Write_Led(byte nr, byte dd);
  // Write To The Panel RGB-Leds LedNr := DD(0RGB)

byte Read_Schak (byte SchakNr);
  // Read The Schakelaar-Data of Schakelaar-Number SchakNr

byte Read_Pot(byte PotNr);
  // Read the value of potmeter(PotNr);

void Status(word ww);
  // Show a word on the top-leds

//=====================================================================================

#endif // ARSID_PANEL_H




Broncode: [1: Paneel (.cpp)] [2: Paneel (.h)]
[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]