Random mix of pages and files
2023.08.28 22:30:29 end old HW Gilhad


Tester na integráče řady 74HCxx se 14 nebo 16 piny pomocí Arduina.

Tohle je záloha stavu, v jakém jsem to nechal 2018-07-22. Je rozumná šance, že je to funkční. Protože pracuju na 40-pinové variantě pro Arduino Mega, kde půjde dělat víc věcí, tak se k tomuto asi už nevrátím nikdy, tak to aspoň někde vystavím. Například na 74HCxx-tester-14-16 a celý balík taky 74HCxx-tester-14-16.tar.gz

Konstrukce je popsána v Construction: - piny D3..D12, A0..A2 jsou připojeny k patici přez odpor 500 Ω a ta je přez 200 kΩ připojena ke GND. Tlačítko s PullUp odporem na A7, 2 WS2812B NeoPixel LEDs na A3, OLED na I2C.

Pomocí externích odporů jde u pinů přepínáním nezi INPUT a INPUT_PULLUP vyvolat přepínání vstupů chipu mezi 0 a 1 a zároveň čtením zjistit, zda to nemá chip jako výstup (když mě přetáhne jinam), bez rizika pro Arduino i chip.

Testy jsou uloženy v textovém souboru knowledge.txt , pomocí compile_knowledge.py se zněj udělají zdrojáky knowledge.* a ty se pat přikompilují k programu. (Jde o kompaktní uložení do PROGMEM.)

U8g a utility je nemodifikovaná knihovna pro OLED - asi budete chtít novější verzi

Tester for 74HCxxx in 14 or 16 pin chips


  • Based on manipulation of Arduino pull-ups, so both arduino and tested chip are protected from shortcuts.
  • (74HC00 have
    • input: LOW in 0-2.1V, HIGH in 2.4-5V at 25C, input leak current -1..+1 uA,
    • output: LOW in 0-0.15V, HIGH in 4.5-5V for 4.mA current, max output current -20..+20mA
    • static power current 1uA + pin sink/source currents
    • other chips are similar
  • Arduino pull-ups are 20k-50k, current up to 20mA
    • output 0..0.9V, 4.2-5V
    • digitalRead LOW in 0-1.5V / HIGH in 3-5V
    • input leak 1uA


  • 74HC can be pulldown with 2.1V/1uA = 2.1MOhm, pullup with (5-2.4)V/1uA = 2.6MOhm, but it is safe to drive it even by Arduino directly, as it take just 1uA max per pin anyway.
  • Arduino can safely connect one active output to 0-5V over 5V/20mA=250 Ohm, maximum 5 pins/port. Same current limit for 74HC.
  • To be in really safe margin, lets use:
    • 500 Ohm resistor between each Arduino pin and 74HC pin as current limiter (so no shortcut is possible, even Arduino by mistake set pinMode(OUTPUT) somehow - it should not anyway)
    • external resistor 200k on each 74HC pin as pulldown
  • For 74HC Input we have:
    • (200k * (1+1)uA = 0.4V drop max from leaking both units) => 0.4V < 2.1V as LOW input for 74HC
    • and suppose 50k Arduino internal pullups, when applied, so voltage is min 4.2V * (1 - (50k+0.5k)/(50k+0.5k+200k)) = 3.35V (3.6V for 30k pull-ups) => 3.35V > 2.4V as HIGH input for 74HC
    • so 74HC inputs can be safely driven HIGH/LOW just by turning Arduino pull-ups on/off
  • For 74HC output we have:
    • max current 10mA, if something fail and Arduino is set to output and different value - still safe
    • current 5V/200k = 25uA if HIGH and "driven down" by 200k Ohm, drop on limiter 1uA/500 Ohm ~ 0V => voltage at least 4.5V > 3V needed for Arduino read HIGH
    • current -5V/30.5k = -163uA if LOW and "driven up" by 30k (one extreme case of pull-ups), 0.15V + 163uA*0.5kOhm = 0.23V => Voltage max 0.23V < 1.5V for reading LOW
    • current -5V/50.5k = -99uA if LOW and "driven up" by 50k (other extreme case of pull-ups), 0.15V + (5V - 100uA*0.5kOhm) = 0.2V => Voltage max 0.2V < 1.5V for reading LOW


  • We can manipulate 74HC inputs just by Arduino pull-ups, while we are able safely and correctly read both 74HC inputs and outputs (even if set in conflicting way with "pull-ups driving")


  • Socket for Arduino Mini (USB powered, Serial used for communication with PC, if possible)
  • 14-pin and 16-pin sockets for tested IC
    • GND common
    • PWR connected over 100 Ohm to 5V on Arduino (simple protection against hard shortcut on bad IC)
    • other pins connected in paralel and over 500 Ohm to Arduino pins D3..D12, A0..A2 (D13 is pulled down with LED so cannot be used for testing)
      • only this pins are tested, in order, so pin 1 on both socket is bit 0, pin 2 in bit 1 ... skipping PWR and GND so pin 8 on 14-pins is connected to pin 7 on 16-pins and to bit 6
      • in following text "all pins" means "all applicable pins packed to 0 based array without holes"
  • Push button for Full Test (so it can work even with just power source)
  • 2 serial RGB LEDS (WS2812B) for simple status indication
  • I2C connector with SDA,SDC,+5V,GND for connecting aditional inputs (like rot. encoder or some keyboard) and outputs (like LCD, OLED ...)

Testing unknown IC:

  • The tester have 2 sockets (14 and 16 pins respectivelly), put the IC in appropriate socket, pin 1 to hole 1, select Full 14-pin Test or Full 16-pin Test if selection possible, otherwise just press button for Full Test.
  • Full Test first try to identify size and some simple characteristics of the IC (obvious Inputs and Outputs) to restrict number of tests needed.
  • Scans go over all pins
  • First goes 16-pin scan (34 test combinations)
    • all pins are driven LOW and tested
    • one HIGH is walked around and all pins tested on each position
    • all pins are driven HIGH and tested
    • one LOW is walked around and all pins tested on each position
  • Initial scan results
    • if some pin have other value, that is driven to it, then it must be OUTPUT and is marked so
    • if driving some pin make change in any other pin(s), then the driven pin is INPUT and is marked so
    • if one of last two pins is marked OUTPUT or INPUT, then it is 16-pins IC and only 16-pins IC are considered from that point (otherwise is possible, that last 2 pins are 3-state or part of combined inputs, so 14-pin variant is not proved yet)
    • the pattern of known INPUTs and OUTPUTs is used to restrict following search
    • scanned values are NOT cached, as they may conflict with some tests of IC with memory/registers/... set by different sequence
    • if 14-pins variant is not ruled out AND INPUTs|OUTPUTs covers full range, 14-pins test is run first, then 16-pins test. Otherwise 16-pin test is runned first. (maybe will change later, if experimental data would suggest faster search that way)
  • Note, that result of one combination can be stored in uint16_t
    • so comparing few such values is much faster, than setting 12/14 different pinMode(), then do the same number of digitalRead() (both indirect via array) and then comparing result
      • so it is faster do filtering by some RAM values before actually testing first rows of data on IC
  • Test goes over all selected/filtered chips in knowledgebase in order,
    • until match is found (and so we know the chip and the chip is not faulty and stop the test, signal OK and output chip name if possible),
    • or until end of data (so the chip is faulty or some not known variant - so signal FAIL and if there is some I/O device present the chip can be examined manually)
  • If only one chip (or group) is manually selected for test, the test does only over the select range.
  • For manual testing unknown chip the console allows to set different pins up or down and see the result.
  • Also full row can be set, or all known inputs can be set as one command. If less values is entered, then only first part is set, if more values is entered only appropriate part from start is used.
  • (Next version may contain also some "user defined" combinations of pins for fast input).


  • For easier editing the knowledge base is stored as plain text and then compiled to program code (actually just some filled arrays), that is then compiled with the main program to utilise flash memory (PROGMEM). Text values are compacted, objects are split over arrays and indexes and some othe magic is also done to archive smaller memory footprint.
  • The text file is structured as following:
/* this is an example file
for defining IC tests */

// empty lines are ignored, anything after // is comment
/* more line comment starts with  /* and are NOT nested
and ends in reverse way. Multiline comments are considered new line itself */  /* block comments can be also used in line, rest of line is again code */   // spaces are collapsed to one space even in text so can be used to allign values

// File consist of separate definitions. Last line is supposed to contain line end.
// definition starts with 2 or more equals ==, maybe space, than name of unit [A-Za-z0-9_]+ then eventual description (which is probably ignored) and some (or none) equals for nice look, the rest of the line is considered comment and should be empty

=== 74HC00 4xNAND ===
// definition should have defined number of legs on chip, either 14 ot 16, otherwise the number is derived from first suitable statement. Once known, all data are bound to it, no partial definitions are allowed. So it serves as security check too.
:legs 14
// there is more ways, how to define pins
:inputs 110110G011011V // one string only, no quotes needed, empty string not enabled
:outputs --#--##--#-- // G - GND and V Vcc are not needed, as their position is defined by HW
// 7400 have only gates, so all pins are one of inputs and outputs, so it can be fully defined by this shortcut
/* other possible values:
        -0._ is zero (or not present here)
        GVP is GND, Vcc and POWER (either G or V) and MUST be on the righ place or omnited so GV------GV----- is error
        #1!+ is one or present
        I means Input present here
        O means Output present here
        3Z is 3-state
        %B is Both "sometimes input, sometimes output, probably 3-state or high-Z too"
        N is NOT CONNECTED
In test are used this:
        ^ is pullup on Input/Both (but can be used also #+!)
        v is pulldown on Input/Both (but can be used also -._)
        0 is hard LOW
        1 is hard HIGH
        C positive pulse (if not already down bringed down, then bringed up, then down, then measured the rest)
        c negative pulse (if not already up bringed up, then bringed down, then up, then measured the rest)
        E positive/rising  clock edge (if not already down bringed down, then bringed up,  then measured the rest)
        e negative/faling clock edge (if not already up bringed up, then bringed down,  then measured the rest)
        L is LOW on output/3/Both (pullup attached, must be driven down) (also  -0._)
        H is HIGH on output/3/Both (pulldown attached, must be driven up) (also  #1+!)
        3Z is 3-state in High-Z (up on pullup, down on pulldown)
        X is does not care (keeps last pull anyway, does not test it, ignore on all following lines)
        x is does not care ON THIS ONE line (keeps last pull anyway, does not test it, but require something here on following line)
        F is Fetch - character at this position is used at the same position for all following lines, until full line issued (yes, clocks, edges, counters, ... too)
        R as single char on line means repeat last line (including clocks, edges, counters ... ) - can do fancy efects :)
        N is NOT CONNECTED should be used only on first line, otherwise another pin is unconnected each time!!!
        @x @ is prefix for bank 3 (extra functions) and XOR value of next character with 0x80 (so fancy characters can be created)
                Bank 3: (XORed with 0x80 for readability):
                A B C D is binary counter (A is LSb), start counting from zero on first use of any reference, ends (and reset) when no reference is active
                a b c d are negated bits of this counter (also can be used as counter from 1111 downwards) (Fancy NAND: @A@B@c;R;R;R )
// after header goes tests (zero or more) (again spaces on begining of line are skipped)
// each test is exactly long as all legs (with NGVP eventually omnited)

__#001!..+-- // 0,0 => 1
-#L-#LL#-L#- // 0,1 => 0
^vL^vLLv^Lv^ // 1,0 => 0
^^L^^LL^^L^^ // 1,1 => 0

// if all test pass, the IC is this unit and OK a everything stops
// if any test fails, the rest of test is skipped and net unit is tried
== End ==
// when there is no more units to try, IC is bad or failed

// variant 2:
/* Insides:
total pins  (TP): 14/16
actual pins (AP): 12/14

PWR(TP): ------#------#
VCC(TP): ......0......1 (masked by PWR)
NC (AP): ............ (# are never read, default pulldown)
// set by HW now
pulls   (AP) : ^^v^^vv^^v^^
outputs (AP) : --#--##--#-- (output xor pulls, then test for pulls)
ignore  (AP) : ------------ ( # means ignore, X, usually left in any state or just pulled down, not read this pass)

Per line:
Clocks, edges: c/C e/E ( inputs, ensures to create pulse/edge, positive/rising for large, negative/falling for small, after THEN measurement is done)
highZ   (AP) : ------------ ( Z means 2 reads done, one for ^, other for v, both must return set value)

Power and NC can be set only on first line, then apply for the whole unit (afects AP), later can be either mentioned (but only) on the same position, or just left out (prefered)
Outputs,Inputs,3state and Both (O/I/3/%) can be set on any line, the line is then only declaration and none clocks/edges/measurement are done with it. Such lines MUST be prefixed with ':'
- such line overrides registers on respective bits, other values (^v.XcCeEZ) are just placeholders and serve for enhanced readability
:size 14/16 // sets TP for unit must be first line, before Power/NC and is not counted as input line. If missing, size is determined from first line

// after line containing some Xes those positions must be skipped (all) until there is any change in Xes on line (so all following lines can be shorter with the same size) - then ALL Xes must be mentioned again
// for ignoring it just now use small 'x'

=== 666Evil666 2 NANDs and mysterious circuit on other side ===
:size 14
:test_inputs II.II.N.XXXX  // NC is not input, drives nothing, could have used . as well, 3-state is not input, 4 other does not matter
:test_outputs ..O..ONXXXO  // NC is neither output, 3-state should show something at least one time, last pin is simple output. BothWays was not discovered probably and X is ignored too
// test_inputs, test_outputs are hints, that this variant should be tested only if this markers fits
:pins IIOIIOGN3%%X.V // also declares TP=14, AP=11 (pin8=NC), pins 3,6,9 are Outputs (until declared otherwise), pin 9 is 3-state (just saying), pins 10,11 may be In or Out (need set later), 12 is not important, 13 is input, 7 is GND and 14 is PWR (must be anyway)
^^L^^LGZCHX^V // clock in data on pin 10 (so it is Input from now), then do 2 reads with different pin 9 (and check it is flowing), and both checks reads H from 11 (N-NotConnected should be used only on first line, otherwise another pin is unconnected each time)
               // note, that clock is applied first, driven down, up, down, then two reads was done
^^L^^LZCH^     // the same, GN and V are not needed to state, as well as X (until it is set otherwise), note, that clock is already down, so it just go up and down
v^Hv^HLevX     // clock should be falling edge, but is already down, so must go up, then down again (effectively next clock happend), should the edge be rising (E), then the clock would just go up and nothing more.
               // now the 2 NANDS are tested for other values, (3state) Z became active output in L, Both Ways Clock is Input v, next Both is Input in v, ignored X is input in ^ and last ^ is now ignored
               // now lets just check the NANDs and no the other part..
++L__H        // NADs are ok, lets play with evil part
XXXXXXLC^X.   // clock in 1 to other BothWays  and 0 to 13, ignore NANDs and 12 again, 3-state will follow last pin from now
HC.^         // clock in 01 this time
HC11         // clock in 11 BY SETTING some Arduino pins HIGH (not just pullup, so much more current possible)
LC00         // clock in hard 00 BY SETTING Arduino pins LOW
LC^v         // ok, return to normal pullups and clock in 10
HCLH         // change last two pins, set pullups for Both to ^v (as Output from there is expected), clock and ensure the output is really 01 (even if driven other way)
xCxx         // just clock in, ignore other pins for this line (small x)
HCLH         // but do not ignore them on this line (and do not have to write full line for getting rid of Xes)

// if we are here, 666Evil666 probably works and we can stop testing. If something failed, lets try other 74HC now
// next part starts with === name (comment) ===

=== 74HC00 4xNAND ===
:size 14 // size can be derived from first test pattern and will be set in data anyway, but let make easier to read this data and enable lenght check of first line too
:test_inputs  II.II..II.II
:test_outputs ..O..OO..O.. // no headers are needed here, but if used, we can skip this if something in footprint does not fit - much faster, than real testing

^^L^^LL^^L^^    // nothing fancy here

=== 74HC00f 4xNAND fancy ===
:size 14 // size can be derived from first test pattern and will be set in data anyway, but let make easier to read this data and enable lenght check of first line too
:test_inputs  II.II..II.II
:test_outputs ..O..OO..O.. // no headers are needed here, but if used, we can skip this if something in footprint does not fit - much faster, than real testing

@AXXXXXXXXXXX   // start the clock, ignore result
@a@b@c@a@b@c@c@b@a@c@b@a        // inverted clock
R       // clock overflow to 0b100, inverted gets H & H => L
// use counters and Repeat step

=== 74HC86 4xXOR ===
:size 14 // size can be derived from first test pattern and will be set in data anyway, but let make easier to read this data and enable lenght check of first line too
:test_inputs  II.II..II.II
:test_outputs ..O..OO..O.. // no headers are needed here, but if used, we can skip this if something in footprint does not fit - much faster, than real testing

^^L^^LL^^L^^    // nothing fancy here

=== 74HC138 3-8 demultiplexer ===
:size 16
:test_inputs    XXXXXIXXXXXXXX
:test_outputs   XXXXXXOOOOOOOO

=== 74HC595 8bit Shift Register ===
//drivenOuts==0b010000011111111 && knownInputs==0b001110000000000
:size 16
:test_inputs  00000000001110
:test_outputs 11111111000001
// Q:15 1 2 3 4 5 6 7
// Qout: 9
 nSRCLR=10; // not Clear
 SRCLK=11; // Shift Register Edge Clock
 RCLK=12; // Register (to storage) Edge clock
 nOE=13; // not Output Enabled
 SER=14; // Serial INPUT
xxxxxxxxcvv^vx  // clear
xxxxxxxx^vvv^x  // !clear, OE, SERin // unknown values yet
LLLLLLLLFvCF^L  // !clear, OE, SERin // ouput registers - should be all zeroes
LLLLLLLLCC@a@a  // Clock in 1, clock to out, FETCH !clear,OE
@ALLLLLLLFFFF   // FETCH clocks, counters on SERin,Q1

=== 74HC283 4-bit binary adder ===
:size 16
:test_inputs    OIIOIIIOOIIOII
:test_outputs   OIIOIIIOOIIOII
/* popis
LvvLvvvLLvvLvv  // 0+0 = 0
H^^H^^^HH^^H^^  // cr+1111+1111=1111+cr
H^^L^^vHH^^H^^  // 0+1111+1111=0111+cr
Hv^Hv^vLHv^Hv^  // 0+1010+0101=1111+0
Lv^Lv^^HLv^Lv^  // cr+1010+0101=0000+cr

=== 74HC161 4-bit counter
:size 16
:test_inputs    XXIIIIIIIOOOOO
:test_outputs   XXIIIIIIIOOOOO
 C    C C
Ml    EPE    T
cxxxxxxxxxxxxx  // reset
^C^vv^^v^HLLHL  // count from 1001
FHLHHL  // 1011
HHLLL   // 1100
HHLHL   // 1101
HHHLL   // 1110
HHHHH   // 1111+TC
@D@C@B@AL       // 0000 +L
*/ // This piece is picky for good power/CLCK otherwise it can bounce more steps on one clock so need to try carefully, if it is what it should be, then use full power 0/1 to test details
cvxxxx^^^LLLLL  //reset
^C^^^^^v^HHHHH  // set 1111 -> 1111+TC
^vvvvv^^vHHHHL  // disable TC
^vvvvv^^^HHHHH  // enable TC
^Cvvvv^^^LLxxL  // one step to 0000-tc, but maybe 2 steps, don't be picky
^C^^^^^v^HHHHH  // set 1111 -> 1111+TC
^Cvvvvv^vHHHHL  // step, but disabled by both (tc too)
^Cvvvv^^vHHHHL  // step, but disabled (step disabled, so no change to TC)
^Cvvvvv^^HHHHH  // step, but disabled (tc get set anyway)
^Cvvvv^^^LLxxL  // one step to 0000-tc, but maybe 2 steps, don't be picky
^Cv^v^^v^HLHLL  // set 0101 -> 0101-tc
^C^v^v^v^LHLHL  // set 1010 -> 1010-tc
// now go hard
c0xxxx^^^LLLLL  //reset
cvxxxx^^^LLLLL  //reset

=== 74HC157 4-2 multiplexer ===
:size 16
:test_inputs    XIXOIXOOXIOXII
:test_outputs   XIXOIXOOXIOXII
^^^L^^LL^^L^^^  // disabled
v^vH^vHHv^Hv^v  // enabled, channel 0
^^vL^vLLv^Lv^F  // enabled, channel 1
vv^Lv^LL^vL^v   // enabled, channel 0
^v^Hv^HH^vH^v   // enabled, channel 1
v@A@a@A@B@b@B@C@c@C@D@d@Dv      // channel 0

^@A@a@a@B@b@b@c@c@C@d@d@Dv      // channel 1

=== 74HC273 8bit input latch ===
:pins 20 // ouch :(

=== End ===
// as there are no more valid lines "End" is not IC variant, just empty marker, that is not stored
// file ends here