Мне понадобился тестер логических микросхем и решил сделать на базе Arduino. Он не только полезен в хозяйстве электронщика, но и помогает глубоко понять принцип работы цифровой логики (серий К155, К176, К561 и их зарубежные аналоги).
Принцип работы прост: Arduino подает комбинации сигналов (0 и 1) на входные пины микросхемы и считывает результат с выходных пинов, сверяя их с «таблицей истинности».
Внимание! Этот проект в стадии разработки и я думаю, что мы этот проект сделаем до конца!
1. Что понадобится для сборки
- Arduino Nano
- OLED - дисплей (I2C)
- Энкодер со встроенной кнопкой
- Макетная плата
- Соединительные провода
2. Схема подключения
Большинство логических микросхем имеют 14 или 16 ног.
На пине D13 у нас стоит на плате светодиод, его нужно выпаять, чтобы он нам через резистор на землю помехи не давал.
--- РАСПИНОВКА ЭНКОДЕРА ---
ENC_CLK - 13
ENC_DT - A1
ENC_SW - A6
--- РАСПИНОВКА OLED ---
Пины (SDA) - A4
Пины (SCL) - A5
Подавать питание будем программно, вы сможете проверять любые микросхемы (даже те, у которых питание посередине), а также защитить чип: ток будет подаваться только в момент теста.
Чтобы Arduino могла сама подавать «питание» и «землю» на любые ножки, мы будем использовать цифровые пины в режимах OUTPUT HIGH (для VCC) и LOW (для GND).
Схема подключения контактов ZIF-панели
Теперь нам нужно задействовать все 16 контактов ZIF-панели. Поскольку у Nano не хватает цифровых пинов для 16 линий + OLED + энкодер, мы используем аналоговые входы как цифровые.
Распиновка:
Важно по A6 и A7: они не могут работать на выход. Поэтому на ZIF-панель их подавать нельзя. Мы используем их только для кнопок.
При установке 14-пинового чипа в 16-пиновую панель получается следующее соответствие:
- Левая сторона: 1-7 пины чипа попадают точно в 1-7 пины ZIF.
- Правая сторона: Происходит смещение. 8-я ножка чипа попадает в 10-й пин ZIF, а 14-я ножка чипа (питание) — в 16-й пин ZIF.
- Свободные пины: 8 и 9 пины на 16-пиновой панели остаются пустыми.
То есть 1-я ножка любого чипа вставляется в первую (начало) панельки.
Режим Меню
Главное меню
Отображение версии тестера, выбор автосканирования микросхемы, или ручной режим, выбираете какую серию микросхемы выбрать.
Далее количество ножек 14 или 16
Крутим энкодер и выбираем из отфильтрованного списка чип который нужен (советский и зарубежный аналог), чуть ниже экрана высвечивается количество логических элементов
Прокрутка меню
На экране высвечивается название чипа, чертёж микросхемы с распиновкой ножек с наружи, а внутри отображение питания +- , отображение входов и выходов ( Input, Output ), а также отображение неисправной ножки знаком (Х), и снизу надпись Fail PIN: 3
А тут отображение исправного чипа Status OK
Код в пробном периоде
Вы просто копируйте этот код и вставляйте в свою среду и тестируйте
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <avr/pgmspace.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
// --- РАСПИНОВКА ЭНКОДЕРА ---
#define ENC_CLK 13
#define ENC_DT A1
#define ENC_SW A6
// Типы пинов
#define P_VCC 0
#define P_GND 1
#define P_IN 2
#define P_OUT 3
#define P_NC 255
// Порядок пинов Arduino для 1-16 ножек ZIF панели
const uint8_t zif[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, A0, A2, A3, 1, 0};
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
struct Chip {
char name[19];
char desc[13];
uint8_t family;
uint8_t pins;
uint8_t algo;
uint8_t map[16];
};
// --- СПРАВОЧНИК МИКРОСХЕМ ---
const Chip database[] PROGMEM = {
// КАТЕГОРИЯ: TTL (K155 / 74xx)
{"K155LA1 / 7420", "2x 4I-NE", 0, 14, 0, {2,2,255,2,2,3, 1,255, 255, 3,2,2,255,2,2, 0}},
{"K155LA2 / 7430", "1x 8I-NE", 0, 14, 0, {2,2,2,2,2,2, 1,255, 255, 3,255,255,2,2,255, 0}},
{"K155LA3 / 7400", "4x 2I-NE", 0, 14, 0, {2,2,3, 2,2,3, 1,255, 255, 3,2,2, 3,2,2, 0}},
{"K155LA4 / 7410", "3x 3I-NE", 0, 14, 0, {2,2,2,2,2,3, 1,255, 255, 3,2,3,2,2,2, 0}},
{"K155LA6 / 7440", "2x 4I-NE DRV", 0, 14, 0, {2,2,255,2,2,3, 1,255, 255, 3,2,2,255,2,2, 0}},
{"K155LA7 / 7406", "2x 4I-NE OK", 0, 14, 0, {2,2,255,2,2,3, 1,255, 255, 3,2,2,255,2,2, 0}},
{"K155LA8 / 7401", "4x 2I-NE OK", 0, 14, 0, {3,2,2, 3,2,2, 1,255, 255, 2,2,3, 2,2,3, 0}},
{"K155LA10/ 7412", "3x 3I-NE OK", 0, 14, 0, {2,2,2,2,2,3, 1,255, 255, 3,2,3,2,2,2, 0}},
{"K155LA11/ 7426", "4x 2I-NE HV", 0, 14, 0, {2,2,3, 2,2,3, 1,255, 255, 3,2,2, 3,2,2, 0}},
{"K155LA12/ 7437", "4x 2I-NE PWR", 0, 14, 0, {2,2,3, 2,2,3, 1,255, 255, 3,2,2, 3,2,2, 0}},
{"K155LA13/ 7438", "4x 2I-NE SCH", 0, 14, 0, {2,2,3, 2,2,3, 1,255, 255, 3,2,2, 3,2,2, 0}},
{"K155LI1 / 7408", "4x 2I", 0, 14, 1, {2,2,3, 2,2,3, 1,255, 255, 3,2,2, 3,2,2, 0}},
{"K155LN1 / 7404", "6x NOT", 0, 14, 3, {2,3,2,3,2,3, 1, 255, 255, 3,2,3,2,3,2, 0}},
{"K155TV1 / 7472", "JK-TRIG + AND", 0, 14, 7, {2,2,2,3,3,2, 1,255, 255, 2,2,2,2,0,1, 0}},
{"K155TV15 / 74107", "2x JK-TRIG CLR", 0, 14, 7, {2,3,3,2,0,2, 1,255, 255, 3,3,2,2,2,1, 0}},
{"K155LP5 / 7486", "4x XOR", 0, 14, 10, {2,2,3, 2,2,3, 1,255, 255, 3,2,2, 3,2,2, 0}},
{"K155LP9 / 7407", "6x BUFF. OK", 0, 14, 3, {2,3,2,3,2,3, 1,255, 255, 3,2,3,2,3,2, 0}},
{"K155LP10 / 74125","4x 3-ST BUFF", 0, 14, 11, {2,2,3, 2,2,3, 1,255, 255, 3,2,2, 3,2,2, 0}},
{"K155LP11 / 74126","4x 3-ST BUFF", 0, 14, 11, {2,2,3, 2,2,3, 1,255, 255, 3,2,2, 3,2,2, 0}},
{"K155TM2 / 7474", "2x D-TRIG", 0, 14, 6, {2,2,3,3,2,2, 1, 255, 255, 2,2,3,3,2,2, 0}},
{"K155TM5 / 7475", "4x D-LATCH", 0, 16, 6, {3,3,2,2,0,0,2, 1, 2,0,0,2,2,3,3, 0}},
{"K155TM7 / 7477", "4x D-LATCH", 0, 14, 6, {3,2,2,0,0,2, 1,255, 255, 2,0,0,2,2,3, 0}},
{"K155TM8 / 74175", "4x D-TRIG CLR", 0, 16, 6, {1,3,3,2,2,3,3, 1, 2,3,3,2,2,3,3, 0}},
{"K155TM9 / 74174", "6x D-TRIG CLR", 0, 16, 6, {1,3,2,2,3,2,2, 1, 3,2,2,3,2,3,3, 0}},
{"K155IE2 / 7490", "DEC COUNTER", 0, 14, 20, {2,3,3,255,0,2,2, 255, 255, 1,2,2,3,2,2, 255}},
{"K155IE4 / 7492", "DIV12 COUNT", 0, 14, 20, {2,255,255,255,0,2,2,255, 255, 3,3,3,1,3,2,2}},
{"K155IE5 / 7493", "BIN COUNTER", 0, 14, 20, {2,2,2,255,0,2,2,255, 255, 3,3,3,1,3,2,2}},
{"K155IE6 / 74192", "UD DEC COUNT", 0, 16, 20, {2,3,3,2,2,3,3, 1, 1,2,2,2,3,3,2, 0}},
{"K155IE7 / 74193", "UD BIN COUNT", 0, 16, 20, {2,3,3,2,2,3,3, 1, 1,2,2,2,3,3,2, 0}},
{"K155IE8 / 7497", "FREQ DIVIDER", 0, 16, 20, {2,3,3,3,3,3,2, 1, 2,2,2,2,2,2,2, 0}},
{"K155IE9 / 74160", "SYNC DEC COUNT", 0, 16, 20, {2,2,3,3,3,3,2, 1, 2,2,3,3,2,2,2, 0}},
{"K155ID1 / 7441", "NIXIE DRIVER", 0, 16, 5, {3,3,3,3,0,3,3,3, 3,3,3,1,2,2,2,2}},
{"K155ID4 / 74155", "2x 2-4 DECODER", 0, 16, 5, {2,2,2,3,3,3,3, 1, 3,3,3,3,2,2,2, 0}},
{"K155ID7 / 74138", "3-8 DECODER", 0, 16, 5, {2,2,2,2,2,2,3, 1, 3,3,3,3,3,3,3, 0}},
{"K155ID10 / 74145", "4-10 BCD DEC", 0, 16, 5, {3,3,3,3,3,3,3, 1, 3,3,3, 2,2,2,2, 0}},
{"K155KP2 / 74153", "2x MUX 4-1", 0, 16, 12, {2,2,3,2,2,2,3, 1, 3,2,2,2,3,2,2, 0}},
{"K155KP5 / 74152", "MUX 8-1", 0, 14, 12, {2,2,2,2,2,2, 1,255, 255, 3,2,2,2,2,2, 0}},
{"K155KP7 / 74151", "MUX 8-1", 0, 16, 12, {3,3,2,2,2,2,2, 1, 2,2,2,2,2,2,2, 0}},
{"K155KP11/ 74257", "4x MUX 2-1 3S", 0, 16, 12, {2,2,2,3,2,2,3, 1, 3,2,2,3,2,2,2, 0}},
{"K155PR6 / 74184", "BCD-BIN CNV", 0, 16, 52, {2,2,2,2,2,3,3, 1, 3,3,3,2,2,2,3, 0}},
{"K155PR7 / 74185", "BIN-BCD CNV", 0, 16, 52, {2,2,2,2,2,3,3, 1, 3,3,3,2,2,2,3, 0}},
// КАТЕГОРИЯ: K561 (40xx)
{"K561LA7 / 4011", "4x 2I-NE", 1, 14, 0, {2,2,3, 3,2,2, 1,255, 255, 2,2,3, 3,2,2, 0}},
{"K561LA8 / 4012", "2x 4I-NE", 1, 14, 0, {3,2,2,2,2,255,1,255, 255, 255,2,2,2,2,3, 0}},
{"K561LA9 / 4023", "3x 3I-NE", 1, 14, 0, {2,2,2,2,2,3, 1,255, 255, 3,2,255,2,2,3, 0}},
{"K561LA10 / 4068", "1x 8I-NE", 1, 14, 0, {255,2,2,2,2,255,1,255, 255, 255,2,2,2,2,3, 0}},
{"K561LE5 / 4001", "4x 2ILI-NE", 1, 14, 4, {2,2,3, 3,2,2, 1,255, 255, 2,2,3, 3,2,2, 0}},
{"K561LE6 / 4002", "2x 4ILI-NE", 1, 14, 4, {3,2,2,2,2,255,1,255, 255, 255,2,2,2,2,3, 0}},
{"K561LE10 / 4025", "3x 3ILI-NE", 1, 14, 4, {2,2,3, 2,2,3, 1,255, 255, 3,2,2,2,2,2, 0}},
{"K561LN1 / 4009", "6x NOT BUFF", 1, 16, 3, {0,3,2,3,2,3,2, 1, 2,3,2,3,2,3,2, 0}},
{"K561LN2 / 4049", "6x NOT BUFF", 1, 16, 3, {0,3,2,3,2,3,2, 1, 2,3,2,3,2,3,2, 255}},
{"K561LN3 / 4050", "6x BUFF", 1, 16, 15, {0,3,2,3,2,3,2, 1, 2,3,2,3,2,3,2, 255}},
{"K561TM2 / 4013", "2x D-TRIG", 1, 14, 6, {3,3,2,2,2,2, 1,255, 255, 2,2,2,2,3,3, 0}},
{"K561TM3 / 4042", "4x D-LATCH", 1, 16, 6, {3,2,3,2,0,2,2, 1, 2,3,2,3,2,2,2, 0}},
{"K561IE8 / 4017", "DEC COUNTER", 1, 16, 21, {3,3,3,3,3,3,3, 1, 3,3,3,3,2,2,2, 0}},
{"K561IE9 / 4022", "OCT COUNTER", 1, 16, 21, {3,3,3,3,3,255,3, 1, 255,3,3,2,2,2,2, 0}},
{"K561IE10 / 4520", "2x BIN COUNT", 1, 16, 20, {2,2,3,3,3,3,2, 1, 2,2,3,3,3,3,2, 0}},
{"K561IE11 / 4510", "UD DEC COUNT", 1, 16, 20, {2,3,2,2,2,3,3, 1, 2,2,3,3,2,3,3, 0}},
{"K561IE14 / 4029", "UD BIN/DEC", 1, 16, 20, {2,3,2,2,2,3,3, 1, 2,2,3,3,2,3,3, 0}},
{"K561IE16 / 4020", "14-BIT DIV", 1, 16, 20, {3,3,3,3,3,3,3, 1, 3,3,3,3,2,2,3, 0}},
{"K561KT1 / 4016", "4x ANALOG SW", 1, 14, 13, {2,2,3, 3,2,2, 1,255, 255, 2,2,3, 3,2,2, 0}},
{"K561KT3 / 4066", "4x ANALOG SW", 1, 14, 13, {2,2,3, 3,2,2, 1,255, 255, 2,2,3, 3,2,2, 0}},
{"K561KP1 / 4052", "2x MUX 4-1", 1, 16, 12, {3,3,3,3,3,0,3, 1, 3,3,3,3,3,2,2, 0}},
{"K561KP2 / 4051", "MUX 8-1", 1, 16, 12, {3,3,3,3,3,3,0, 1, 3,3,3,2,2,2,3, 0}},
{"K561KP3 / 4053", "3x MUX 2-1", 1, 16, 12, {3,3,3,3,3,3,0, 1, 3,3,2,2,2,3,3, 0}},
{"K561IV1 / 4532", "8-3 ENCODER", 1, 16, 51, {2,2,2,2,2,3,3, 1, 2,2,2,2,2,3,3, 0}},
{"K561TL1 / 4093", "4x 2I-NE SCHM", 1, 14, 0, {2,2,3, 3,2,2, 1,255, 255, 2,2,3, 3,2,2, 0}},
{"K561TL2 / 40106", "6x NOT SCHMITT", 1, 14, 3, {2,3,2,3,2,3, 1,255, 255, 3,2,3,2,3,2, 0}},
{"K561IP2 / 4008", "4-BIT ADDER", 1, 16, 25, {2,2,2,3,2,3,2, 1, 3,3,3,2,2,2,2, 0}},
{"K561IP3 / 4063", "4-BIT COMP", 1, 16, 50, {2,2,2,2,3,3,3, 1, 2,2,2,2,2,2,2, 0}},
{"K561IP5 / 40101", "9-BIT PARITY", 1, 14, 26, {2,2,2,2,2,2, 1,255, 255, 3,3,2,2,2,3, 0}},
{"K561IR2 / 4015", "2x 4BIT SHIFT", 1, 16, 30, {2,3,3,3,2,2,3, 1, 2,3,3,3,3,2,2, 0}},
{"K561IR9 / 4035", "4-BIT UNIV REG", 1, 16, 30, {2,2,2,3,2,1,2, 1, 3,2,3,2,3,2,2, 0}},
{"K561IR11 / 4014", "8-BIT P-S REG", 1, 16, 30, {2,3,2,2,2,3,2, 1, 2,2,2,2,2,2,2, 0}},
{"K561IR12 / 4094", "8-BIT SHIFT-L", 1, 16, 30, {3,2,2,3,3,3,3, 1, 3,3,3,3,2,2,2, 0}},
{"K561LS2 / 4019", "4x MUX/OR", 1, 16, 2, {2,2,3,2,2,3,2, 1, 2,3,2,2,3,2,2, 0}},
// КАТЕГОРИЯ: K176
{"K176LA7 / 4011", "4x 2I-NE", 2, 14, 0, {2,2,3, 3,2,2, 1,255, 255,2,2,3, 3,2,2, 0}},
{"K176LA8 / 4012", "2x 4I-NE", 2, 14, 0, {3,2,2,2,2,255,1,255, 255, 255,2,2,2,2,3, 0}},
{"K176LA9 / 4023", "3x 3I-NE", 2, 14, 0, {2,2,2,2,2,3, 1,255, 255, 3,2,255,2,2,3, 0}},
{"K176IE1", "6-BIT BIN COUNT", 2, 14, 20, {2,3,3,3,3,3, 1,255, 255, 2,2,2,2,2,2, 0}},
{"K176IE4", "COUNT TO 10/7SEG", 2, 14, 41, {3,3,3,3,3,2, 1,255, 255, 2,3,3,3,3,3, 0}},
{"K176IE5", "15-BIT CLOCK DIV", 2, 14, 40, {2,2,3,2,2,255,1,255, 255, 255,2,2,2,2,2, 0}},
{"K176IE12", "CLOCK DIV/COUNT", 2, 16, 40, {3,3,3,2,2,2,2, 1, 3,3,3,3,2,2,2, 0}},
{"K176IE13", "CLOCK W/ ALARM", 2, 16, 40, {2,2,3,3,2,2,2, 1, 2,2,2,2,3,3,2, 0}},
{"K176IE18", "CLOCK DIV/SOUND", 2, 16, 40, {3,3,3,2,2,2,2, 1, 3,3,3,3,2,2,2, 0}},
{"K176ID2", "BIN-7SEG DECODER", 2, 16, 45, {2,2,2,2,2,2,2, 1, 3,3,3,3,3,3,3, 0}},
{"K176ID3", "BIN-7SEG HIGH V", 2, 16, 45, {2,2,2,2,2,2,2, 1, 3,3,3,3,3,3,3, 0}},
{"K176LE5 / 4001", "4x 2ILI-NE", 2, 14, 4, {2,2,3, 3,2,2, 1,255, 255, 2,2,3, 3,2,2, 0}},
{"K176LE6 / 4002", "2x 4ILI-NE", 2, 14, 4, {3,2,2,2,2,255,1,255, 255, 255,2,2,2,2,3, 0}},
{"K176LE10 / 4025", "3x 3ILI-NE", 2, 14, 4, {2,2,3, 2,2,3, 1,255, 255, 3,2,2,2,2,2, 0}},
{"K176TM1 / 4013", "2x D-TRIG", 2, 14, 6, {3,3,2,2,2,2, 1,255, 255, 2,2,2,2,3,3, 0}},
{"K176TM2 / 4013", "2x D-TRIG", 2, 14, 6, {3,3,2,2,2,2, 1,255, 255, 2,2,2,2,3,3, 0}},
{"K176TL1", "4x 2I-NE SCHMITT", 2, 14, 0, {2,2,3, 3,2,2, 1,255, 255, 2,2,3, 3,2,2, 0}},
};
const int chipCount = sizeof(database) / sizeof(Chip);
enum State { MAIN_MENU, PIN_MENU, LIST_MENU, TEST_RESULT };
State currentState = MAIN_MENU;
int selMode = 0, selPin = 14, fCount = 0, fPos = 0, selectedIdx = 0;
int filteredIdx[80];
int failedPin = -1;
int lastClk;
bool buttonPressed = false;
// --- ФУНКЦИИ УПРАВЛЕНИЯ ---
void pulse(int pin) {
digitalWrite(zif[pin], HIGH); delayMicroseconds(10);
digitalWrite(zif[pin], LOW); delayMicroseconds(10);
}
int runLogicTest(Chip& c) {
for (int i = 0; i < 16; i++) {
if (c.map[i] == P_OUT) {
// --- ТЕСТ ДЛЯ ПРОСТОЙ ЛОГИКИ (Algo 0, 1, 3, 4, 10) ---
if (c.algo == 0 || c.algo == 1 || c.algo == 3 || c.algo == 4 || c.algo == 10) {
for (byte t = 0; t < 4; t++) {
bool inA = t & 1;
bool inB = t & 2;
for (int j = 0; j < 16; j++) {
if (c.map[j] == P_IN) digitalWrite(zif[j], (j % 2 == 0) ? inA : inB);
}
delayMicroseconds(50);
bool result = digitalRead(zif[i]);
bool expected;
switch (c.algo) {
case 0: expected = !(inA && inB); break; // И-НЕ
case 1: expected = (inA && inB); break; // И
case 3: expected = !inA; break; // НЕ
case 4: expected = !(inA || inB); break; // ИЛИ-НЕ
case 10: expected = (inA ^ inB); break; // XOR
}
if (result != expected) return i;
}
}
// --- ТЕСТ ДЛЯ ДЕШИФРАТОРОВ (Algo 5: ИД1, ИД3, ИД4, ИД7) ---
else if (c.algo == 5) {
for (byte val = 0; val < 10; val++) { // Проверяем комбинации 0-9
// 1. Подаем двоичный код на все входы (P_IN)
for (int j = 0; j < 16; j++) {
if (c.map[j] == P_IN) {
// Находим, какой это бит (упрощенно: первый встреченный IN - бит 0 и т.д.)
// Для точного теста лучше подавать val на входы согласно весам A,B,C,D
digitalWrite(zif[j], (val >> (j % 4)) & 1);
}
}
delayMicroseconds(50);
// 2. Проверяем выходы
// В дешифраторах обычно активный уровень - LOW (0)
// Мы не знаем точно, какой выход должен гореть для 'val' без сложной карты,
// но мы можем проверить, что хотя бы один выход изменился.
bool anyLow = false;
for (int outIdx = 0; outIdx < 16; outIdx++) {
if (c.map[outIdx] == P_OUT && digitalRead(zif[outIdx]) == LOW) anyLow = true;
}
if (!anyLow && val < 10) return i; // Если при поданном коде ни один выход не "прижался"
}
}
// --- ТЕСТ ДЛЯ D-ТРИГГЕРОВ (Algo 6: ТМ2, ТМ5, ТМ7) ---
else if (c.algo == 6) {
// Сначала пробуем записать "1"
for (int j = 0; j < 16; j++) if (c.map[j] == P_IN) digitalWrite(zif[j], HIGH);
delayMicroseconds(10);
// Если это ТМ2, нужно дернуть CLK. Для простоты дергаем все входы.
for (int j = 0; j < 16; j++) if (c.map[j] == P_IN) { digitalWrite(zif[j], LOW); delayMicroseconds(5); digitalWrite(zif[j], HIGH); }
if (digitalRead(zif[i]) != HIGH) return i; // Не записалась единица
// Теперь пробуем записать "0"
for (int j = 0; j < 16; j++) if (c.map[j] == P_IN) digitalWrite(zif[j], LOW);
for (int j = 0; j < 16; j++) if (c.map[j] == P_IN) { digitalWrite(zif[j], HIGH); delayMicroseconds(5); digitalWrite(zif[j], LOW); }
if (digitalRead(zif[i]) != LOW) return i; // Не записался ноль
}
// --- ТЕСТ ДЛЯ СЧЕТЧИКОВ (Algo 20: ИЕ2, ИЕ5) ---
else if (c.algo == 20) {
// Проверяем, что выход вообще может менять состояние при пульсациях на входе
bool initial = digitalRead(zif[i]);
bool changed = false;
for (int p = 0; p < 20; p++) {
for (int j = 0; j < 16; j++) if (c.map[j] == P_IN) { digitalWrite(zif[j], !digitalRead(zif[j])); }
delayMicroseconds(10);
if (digitalRead(zif[i]) != initial) { changed = true; break; }
}
if (!changed) return i; // Счётчик не считает (выход "залип")
}
}
}
return -1;
}
int autoScan() {
display.clearDisplay();
display.setCursor(20, 30);
display.print(F("SCANNING..."));
display.display();
for (int i = 0; i < chipCount; i++) {
Chip c;
memcpy_P(&c, &database[i], sizeof(Chip));
// Сканируем только те чипы, которые подходят под текущий выбор ног (14 или 16)
if (c.pins != selPin) continue;
if (performTest(i) == -1) return i; // Ура, нашли!
resetZif();
}
return -1; // Не нашли
}
void resetZif() {
for (uint8_t i = 0; i < 16; i++) {
pinMode(zif[i], INPUT); // Переводим все пины в безопасный режим
}
}
int performTest(int idx) {
selectedIdx = idx;
Chip c;
memcpy_P(&c, &database[idx], sizeof(Chip));
resetZif(); // Шаг 1: Сброс
delay(50);
// Шаг 2: Подача питания
for (uint8_t i = 0; i < 16; i++) {
uint8_t mode = c.map[i];
if (mode == P_GND) {
pinMode(zif[i], OUTPUT);
digitalWrite(zif[i], LOW);
} else if (mode == P_VCC) {
pinMode(zif[i], OUTPUT);
digitalWrite(zif[i], HIGH);
}
}
delay(20); // Даем чипу "проснуться"
// Шаг 3: Настройка входов и выходов
for (uint8_t i = 0; i < 16; i++) {
uint8_t mode = c.map[i];
if (mode == P_IN) {
pinMode(zif[i], OUTPUT);
digitalWrite(zif[i], LOW);
} else if (mode == P_OUT) {
pinMode(zif[i], INPUT_PULLUP);
}
}
// Шаг 4: Запуск логики (ваша функция runLogicTest)
int result = runLogicTest(c);
// Шаг 5: Не сбрасываем пины сразу, чтобы увидеть результат на OLED
return result;
}
// --- ГРАФИКА ---
void drawResult(Chip& c, int err) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
// Заголовок
display.setCursor(0, 0);
display.print(c.name);
// Рисуем корпус (прямоугольник)
int x = 12, y = 22, w = 104, h = 20;
display.drawRect(x, y, w, h, WHITE);
display.drawCircle(x, y + h/2, 3, WHITE); // Ключ микросхемы
int pinsHalf = c.pins / 2;
float pinStep = (float)w / (pinsHalf + 1);
for (int i = 0; i < pinsHalf; i++) {
// --- НИЖНИЙ РЯД ПИНОВ (1..pins/2) ---
int px = x + (int)(pinStep * (i + 1));
display.drawLine(px, y + h, px, y + h + 2, WHITE);
// Определяем тип пина для нижней стороны
uint8_t type = c.map[i];
display.setCursor(px - 3, y + h - 10); // Внутри рамки снизу
if (type == P_IN) display.print('I');
else if (type == P_OUT) display.print('O');
else if (type == P_VCC) display.print('+');
else if (type == P_GND) display.print('-');
// Отрисовка ошибки или номера пина
if (i == err) {
display.fillRect(px - 4, y + h + 4, 9, 9, WHITE);
display.setTextColor(BLACK);
display.setCursor(px - 3, y + h + 5); display.print('X');
display.setTextColor(WHITE);
} else {
display.setCursor(px - 3, y + h + 5); display.print(i + 1);
}
// --- ВЕРХНИЙ РЯД ПИНОВ (pins..pins/2+1) ---
int idxR = c.pins - 1 - i;
int pinMapIdx = (c.pins == 14 && idxR >= 7) ? idxR + 2 : idxR; // Коррекция для 14-пин в 16-панели
display.drawLine(px, y - 2, px, y, WHITE);
// Определяем тип пина для верхней стороны
uint8_t typeT = c.map[pinMapIdx];
display.setCursor(px - 3, y + 2); // Внутри рамки сверху
if (typeT == P_IN) display.print('I');
else if (typeT == P_OUT) display.print('O');
else if (typeT == P_VCC) display.print('+');
else if (typeT == P_GND) display.print('-');
if (pinMapIdx == err) {
display.fillRect(px - 4, y - 12, 9, 9, WHITE);
display.setTextColor(BLACK);
display.setCursor(px - 3, y - 11); display.print('X');
display.setTextColor(WHITE);
} else {
display.setCursor(px - (idxR >= 9 ? 6 : 3), y - 11); display.print(idxR + 1);
}
}
// Статус внизу
display.drawLine(0, 54, 128, 54, WHITE);
display.setCursor(0, 57);
if (err == -1) display.print(F("STATUS: OK"));
else { display.print(F("FAIL PIN: ")); display.print(err + 1); }
display.display();
}
void drawMenu() {
display.clearDisplay();
display.setTextColor(WHITE);
if (currentState == MAIN_MENU) {
display.setCursor(20, 0); display.print(F("IC TESTER v1.5"));
const char* m[] = {"AUTO SCAN", "TTL K155", "CMOS K561", "CMOS K176"};
for(int i=0; i<4; i++) {
display.setCursor(10, 15+i*11);
if(selMode == i) display.print(F("> "));
display.print(m[i]);
}
} else if (currentState == PIN_MENU) {
display.setCursor(30, 15); display.print(F("SELECT PINS:"));
display.setCursor(50, 35); display.print(selPin == 14 ? F("> 14") : F(" 14"));
display.setCursor(50, 45); display.print(selPin == 16 ? F("> 16") : F(" 16"));
} else if (currentState == LIST_MENU) {
Chip c; memcpy_P(&c, &database[filteredIdx[fPos]], sizeof(Chip));
display.setCursor(0, 0); display.print(F("SELECT CHIP:"));
display.drawRect(0, 15, 128, 25, WHITE);
display.setCursor(5, 23); display.print(c.name);
display.setCursor(0, 45); display.print(c.desc);
} else if (currentState == TEST_RESULT) {
Chip c; memcpy_P(&c, &database[selectedIdx], sizeof(Chip));
drawResult(c, failedPin);
}
display.display();
}
void setup() {
resetZif(); // Сразу переводим панель в безопасный режим
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
pinMode(ENC_CLK, INPUT_PULLUP);
pinMode(ENC_DT, INPUT_PULLUP);
lastClk = digitalRead(ENC_CLK);
drawMenu();
}
void loop() {
bool up = false, dn = false, ok = false;
int currentClk = digitalRead(ENC_CLK);
if (currentClk != lastClk && currentClk == LOW) {
if (digitalRead(ENC_DT) != currentClk) dn = true; else up = true;
}
lastClk = currentClk;
int swVal = analogRead(ENC_SW);
if (swVal < 100) {
if (!buttonPressed) { delay(50); ok = true; buttonPressed = true; }
} else { buttonPressed = false; }
if (up || dn || ok) {
if (up) {
if (currentState == MAIN_MENU) selMode = (selMode > 0) ? selMode - 1 : 3;
else if (currentState == PIN_MENU) selPin = (selPin == 14) ? 16 : 14;
else if (currentState == LIST_MENU && fPos > 0) fPos--;
}
if (dn) {
if (currentState == MAIN_MENU) selMode = (selMode < 3) ? selMode + 1 : 0;
else if (currentState == PIN_MENU) selPin = (selPin == 16) ? 14 : 16;
else if (currentState == LIST_MENU && fPos < fCount - 1) fPos++;
}
if (ok) {
delay(200);
if (currentState == MAIN_MENU) {
if (selMode == 0) { // Нажали AUTO SCAN
int foundIdx = autoScan();
if (foundIdx != -1) {
selectedIdx = foundIdx;
failedPin = -1;
currentState = TEST_RESULT;
} else {
display.clearDisplay();
display.setCursor(15, 30);
display.print(F("UNKNOWN CHIP"));
display.display();
delay(1500);
}
} else currentState = PIN_MENU;
} else if (currentState == PIN_MENU) {
fCount = 0;
for(int i=0; i<chipCount; i++) {
Chip c; memcpy_P(&c, &database[i], sizeof(Chip));
if(c.family == (selMode-1) && c.pins == selPin) filteredIdx[fCount++] = i;
}
fPos = 0; currentState = LIST_MENU;
} else if (currentState == LIST_MENU) {
failedPin = performTest(filteredIdx[fPos]);
currentState = TEST_RESULT;
} else if (currentState == TEST_RESULT) currentState = MAIN_MENU;
}
drawMenu();
}
}