/* Чтение данных с параллельной 8-битной шины Raspberry Pi 4 и вывод в stdout Сборка (внутри WiringPi/examples): make really-all Требования: Используется модифицированный WiringPi с функцией digitalReadPins() Режимы: fill - заполняет паузы последним байтом (высокий CPU, непрерывный поток) sleep - спит при отсутствии данных (низкий CPU, только реальные данные) */ #include #include #include #include #include #include #include #include #include #include /* Подключение физические контакты на Raspberry pi4: 29 - D0 7 - D1 22 - D2 18 - D3 37 - D4 35 - D5 33 - D6 31 - D7 36 - Запись 14 - GND */ unsigned int wr_pin = 27; // Пин сигнала записи (WR) unsigned int pins[] = { 21, 7, 6, 5, 25, 24, 23, 22 }; // 8-битная шина данных (wPi) const size_t out_buffer_len = 32U; // Размер логируемого пакета const size_t fifo_buffer_len = 4096U; // Размер ПППУ в байтах // Режимы работы typedef enum { MODE_FILL, // Заполнение пауз последним байтом (оригинальное поведение) MODE_SLEEP // Сон при отсутствии данных (энергоэффективный) } run_mode_t; // Глобальные настройки static run_mode_t run_mode = MODE_SLEEP; // По умолчанию энергоэффективный режим static bool debug_mode = false; struct fifo { char *buffer; size_t len; size_t wri; size_t rdi; }; void fifo_reset(struct fifo* ff) { ff->wri = 0; ff->rdi = 0; } bool fifo_init(struct fifo* ff, size_t len) { ff->buffer = malloc(len); ff->len = len; fifo_reset(ff); return ff->buffer != NULL; } void fifo_uninit(struct fifo* ff) { free(ff->buffer); ff->buffer = NULL; fifo_reset(ff); } size_t fifo_available(struct fifo *ff) { if (ff->wri >= ff->rdi) return ff->wri - ff->rdi; else return ff->wri + ff->len - ff->rdi; } bool fifo_can_write(struct fifo *ff, size_t len) { return (ff->len - fifo_available(ff)) > len; } bool fifo_can_read(struct fifo *ff, size_t len) { return fifo_available(ff) >= len; } bool fifo_try_read(struct fifo *ff, char* buffer, size_t len) { if (!fifo_can_read(ff, len)) return false; memcpy(buffer, &ff->buffer[ff->rdi], len); ff->rdi = (ff->rdi + len) % ff->len; return true; } bool fifo_try_write(struct fifo *ff, char* buffer, size_t len) { if (!fifo_can_write(ff, len)) return false; memcpy(&ff->buffer[ff->wri], buffer, len); ff->wri = (ff->wri + len) % ff->len; return true; } static struct fifo isr_ff; // Антидребезг static unsigned long long last_time_us = 0; static const long debounce_us = 5000; // 5 мс // Диагностика static unsigned long total_bytes = 0; static unsigned long interrupts_processed = 0; static unsigned long loop_iterations = 0; // Обработчик прерывания void isr_handle(void) { unsigned long long now_us; struct timeval now; gettimeofday(&now, NULL); now_us = now.tv_sec * 1000000LL + now.tv_usec; if (now_us - last_time_us < debounce_us) return; last_time_us = now_us; interrupts_processed++; char byte_read = 0xff & digitalReadPins(pins, sizeof pins / sizeof pins[0]); fifo_try_write(&isr_ff, &byte_read, 1); } // Режим 1: Заполнение пауз последним байтом void run_mode_fill(void) { char byte; char byte_prev = 0; while (true) { if (fifo_try_read(&isr_ff, &byte, 1)) { fwrite(&byte, 1, 1, stdout); byte_prev = byte; total_bytes++; } else { fwrite(&byte_prev, 1, 1, stdout); } fflush(stdout); loop_iterations++; if (debug_mode && (loop_iterations % 1000000 == 0)) { fprintf(stderr, "[DEBUG] Mode=fill, iterations=%lu, bytes=%lu\n", loop_iterations, total_bytes); } } } // Режим 2: Сон при отсутствии данных (с минимальной диагностикой) void run_mode_sleep(void) { char byte; int first_bytes_shown = 0; // Минимальная диагностика при запуске fprintf(stderr, "GPIO Monitor: waiting for data on WR pin %d...\n", wr_pin); fflush(stderr); while (true) { if (fifo_try_read(&isr_ff, &byte, 1)) { fwrite(&byte, 1, 1, stdout); fflush(stdout); total_bytes++; loop_iterations = 0; // Показать первые 10 байт в stderr if (first_bytes_shown < 10) { if (first_bytes_shown == 0) { fprintf(stderr, "First bytes: "); } fprintf(stderr, "0x%02x ", (unsigned char)byte); first_bytes_shown++; if (first_bytes_shown == 10) { fprintf(stderr, "\n"); fflush(stderr); } } } else { usleep(1000); loop_iterations++; } if (debug_mode && (loop_iterations > 0) && (loop_iterations % 10000 == 0)) { fprintf(stderr, "[DEBUG] Mode=sleep, idle loops=%lu, bytes=%lu\n", loop_iterations, total_bytes); } } } // Парсинг аргументов командной строки void parse_arguments(int argc, char *argv[]) { static struct option long_options[] = { {"mode", required_argument, 0, 'm'}, {"debug", no_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; int opt; int option_index = 0; while ((opt = getopt_long(argc, argv, "m:dh", long_options, &option_index)) != -1) { switch (opt) { case 'm': if (strcmp(optarg, "fill") == 0) { run_mode = MODE_FILL; } else if (strcmp(optarg, "sleep") == 0) { run_mode = MODE_SLEEP; } else { fprintf(stderr, "Неизвестный режим: %s\n", optarg); fprintf(stderr, "Доступные режимы: fill, sleep\n"); exit(EXIT_FAILURE); } break; case 'd': debug_mode = true; break; case 'h': printf("Использование: %s [OPTIONS]\n\n", argv[0]); printf("Опции:\n"); printf(" -m, --mode=MODE Режим работы: fill или sleep\n"); printf(" fill - заполнение пауз последним байтом\n"); printf(" sleep - сон при отсутствии данных (по умолчанию)\n"); printf(" -d, --debug Включить диагностику в stderr\n"); printf(" -h, --help Показать эту справку\n"); exit(EXIT_SUCCESS); break; default: fprintf(stderr, "Используйте --help для справки\n"); exit(EXIT_FAILURE); } } } int main(int argc, char *argv[]) { parse_arguments(argc, argv); if (!fifo_init(&isr_ff, fifo_buffer_len)) { perror("fifo_init()"); exit(EXIT_FAILURE); } if (wiringPiSetup() == -1) { perror("wiringPiSetup()"); exit(EXIT_FAILURE); } pinMode(wr_pin, INPUT); pullUpDnControl(wr_pin, PUD_UP); for (unsigned int i = 0; i < (sizeof pins / sizeof pins[0]); i++) { unsigned int pin = pins[i]; pullUpDnControl(pin, PUD_DOWN); pinMode(pin, INPUT); } if (wiringPiISR(wr_pin, INT_EDGE_RISING, &isr_handle) != 0) { perror("wiringPiISR()"); exit(EXIT_FAILURE); } if (run_mode == MODE_FILL) { run_mode_fill(); } else { run_mode_sleep(); } fifo_uninit(&isr_ff); return EXIT_SUCCESS; }