Jag vet inte om det finns 4-till-16-dekoders, däremot fanns det förr i rätt stor kapsel, typ en 74-logikkrets i en 24-pinnars DIL-kapsel av den breda modellen (som används för DIL-kapslar med än fler poler, alltså stor som t.ex. 2716/2732 eprom) men jag skulle tro att den slutat tillverkas.
Däremot har t.ex. den klassiska 3-till-8-dekodern
74xx138 (klicka->datablad) tre enableingångar varav två är inverterade. Utan ytterligare extra kretsar så kan man koppla dessa enableingångar så att man avkoda ytterligare två bitar till tre 74xx138-dekodrar, d.v.s. 5-till-24-avkodning. Med en inverterare och fyra 74xx138 kan man få 5-till-32-avkodning, och med fler inverterare och åtta 74xx138 kan man få 6-till-64-avkodning. Om du inte har nån krets med en inverterare "över" så kan du bygga en inverterare med transistor + motstånd om det tar mindre plats. Jag är inte helt säker men tror för övrigt att det finns inverterare att köpa "styckvis" som ytmonterade komponenter. Med en inverterare 5-till-32, utan inverterare 5-till-24 eller t.ex. 6-till-32 så sparar du några pinnar jämfört med decimaldekodrarna. Å andra sidan kanske decimaldekodrarna du tittat på också har enableingångar som går att använda på samma sätt? För övrigt går det också bra att seriekoppla 138:or så att utgångarna från en 138:a matar enableingångarna på ytterligare åtta 138:or för att få 6-till-64 och ha enableingångarna kvar.
Om du använder mos-varianter av grindarna så bör utgångarna kunna driva både npn-steg och pnp-steg. 74xx138 har inverterade utgångar så den lämpar sig att driva PNP-steg. OBS att om du vill att kopplingen ska vara "TTL-säker", d.v.s. fungera även om någon ersätter en 74HC138 med en 74LS138, så räcker det inte med bara seriemotstånd till basen in på PNP-transistorerna utan du behöver också ha ett motstånd mellan bas och emitter med värdena valda så att transistorn inte kan börja leda om utgången från 74xx138'an ligger på 2,4V eller högre. (OBS: 2,4V tog jag ur huvudet, jag tror det är specen för lägsta nivå en godkänd etta får ha i TTL-logik, men kan minnas fel). Det är kanske inte särskilt viktigt att klara LS-kretsar, däremot kan motståndet mellan emitter och bas göra kretsen mindre störkänslig. Utan det motståndet börjar transistorn leda så fort utgången från 138'an ligger under ca 4,4V medan med motståndet kan klart lägre spänning krävas. Med 200mA switchat med relativt hög frekvens kan det nog uppstå en del spikar som ett sådant motstånd kan tänkas hjälpa till att klara.
OBS rent allmänt att du bör ha rätt bra avkoppling med både "stor+liten" kondensator i matningen till muxkretsen, och kretskortsbanor med lågt spänningsfall mellan avkopplingskondensatorerna och drivtransistorernas emittrar.
Du kan PWM'a på två sätt, det ena är att köra kortare pulser och lägre pauser i muxningen för tangentbordet, det andra är som jag föreslog först, d.v.s. att variera 5V-matningen.
Om du vill spara I/O-pinnar för den sida som inte kan använda dekodrar så skulle du kunna använda skiftregister, både för lysdioddrivningen och avläsningen. Ett sånthär skiftregister skulle kunna användas för att driva lysdioderna är troligtvis
74HC595. Samtidigt som befintlig data i skiftregistrets latchar skickas till utgångarna så skiftas ny data in i skiftregistret redo att latchas samtidigt som du växlar signalerna till dekodrarna. På samma sätt kan du läsa av tangenternas signaler med lämpligt skiftregister. Latcha in tangentbordsdata i t.ex.
74xx165 och läs ur. Latcha avläsningen strax före du slår om signalen till dekodrarna, så har kretsen fått maximal tid på sig att stabilisera sig sedan föra dekoderomslagningen.
Pseudokod:
start:
nollställ räknare för utdata till dekodrar
ytttre_loop:
Skicka utdata till dekodrar och skicka latchpuls till lysdioddrivskiftregister
nollställ inre loopräknare
läs lysdioddata från tabell indexerad av räknaren för utdata till dekodrar
inre_loop:
sätt utdata till lysdiodskiftregister från bit noll på tempvariabel för lysdioddata
skifta tempvariabel för lysdioddata ett steg åt höger
kort paus
läs indata från tangetbordsskiftregister
slå om klockpuls till båda skiftregistren
kortare paus
slå om klockpuls till båda skiftregistren
räkna upp inre loopräknare, hoppa till inre_loop ifall räknaren inte nått tillräckligt högt
skriv avläst tangentbordsdata i tabell indexerad av räknaren för utdata till dekodrar
räkna upp räknare för utdata till dekodrar, hoppa till yttre_loop om räknaren inte nått tillräckligt högt
hoppa till start:
Det kan kanske vara bra om all denna kod körs på nån slags timerintterupt och kommunikationen med omvärlden inte körs på interrupt. Annars kommer ljusstyrkan på lysdioderna att flimmra beroende på hur mycket tid annan kod / interruptkod tar.
OBS att jag är nästan helt säker på att jag är "off-by-one" för tangentbordsavläsning och/eller lysdiodskiftregisterdrivningen. Om jag fattat rätt så är det en 32-bitars mikrokontroller du använder, då är det enklast att bara skifta utdatat lagom mycket efter att det lästs från tabellen före inre loopen börjar snurra, och på motsvarande sätt skifta indatat rätt innan det skrivs i tabellen efter att inre loppen snurrat klart.
Det du behöver hålla reda på är på vilken flank skiftregistren stegar, så att du läser av 74xx165-skiftregistret och skickar data till 74xx595-skiftregistret i samband med den klockpulsflank som inte skiftar datat. Det här gäller också latchpulsen till 74xx595 som inte bör ha aktiv flank samtidigt som klockpulsen har aktiv flank.
Praktiska studier visar att det blir dåligt om man gör fel på klockflanken och latchflanken till skiftregistren. För 20+ år sen byggde jag en serieöverföring till en discoljusanläggning och beroende på om man gör fel på klockpulsen och/eller latchpulsen så blev effekten att tända lampor "smittade av sig" på efterföljande kanal, med viss felkoppling tonande upp-ned sakta efter interferenser mellan frekvenserna i serieöverföringen och nätfrekvensen, med annan felkoppling mer on/off-mässigt tändes/släcktes också styrt efter interferenser mellan serieöverföring och nätfrekvens.