#include #include #include #include #include #include "defs.h" #include "cpld.h" #include "osd.h" #include "logging.h" #include "rgb_to_fb.h" #include "rpi-gpio.h" #define RANGE 16 // The number of frames to compute differences over #define NUM_CAL_FRAMES 10 typedef struct { int sp_offset; int filter_c; int filter_l; } config_t; // Current calibration state for mode 0..6 static config_t default_config; // Current configuration static config_t *config = &default_config; // OSD message buffer static char message[80]; // Aggregate calibration metrics (i.e. errors summed across all channels) static int sum_metrics[RANGE]; // Error count for final calibration values for mode 0..6 static int errors; // The CPLD version number static int cpld_version; // ============================================================= // Param definitions for OSD // ============================================================= enum { OFFSET, FILTER_C, FILTER_L }; static param_t params[] = { { OFFSET, "Offset", 0, 15, 1 }, { FILTER_C, "C Filter", 0, 1, 1 }, { FILTER_L, "L Filter", 0, 1, 1 }, { -1, NULL, 0, 0, 0 } }; // ============================================================= // Private methods // ============================================================= static void write_config(config_t *config) { int sp = 0; int scan_len = 6; sp |= config->sp_offset & 15; sp |= config->filter_c << 4; sp |= config->filter_l << 5; for (int i = 0; i < scan_len; i++) { RPI_SetGpioValue(SP_DATA_PIN, sp & 1); for (int j = 0; j < 1000; j++); RPI_SetGpioValue(SP_CLKEN_PIN, 1); for (int j = 0; j < 100; j++); RPI_SetGpioValue(SP_CLK_PIN, 0); RPI_SetGpioValue(SP_CLK_PIN, 1); for (int j = 0; j < 100; j++); RPI_SetGpioValue(SP_CLKEN_PIN, 0); for (int j = 0; j < 1000; j++); sp >>= 1; } RPI_SetGpioValue(SP_DATA_PIN, 0); } static void osd_sp(config_t *config, int line, int metric) { // Line ------ sprintf(message, "Offset: %d", config->sp_offset); osd_set(line, 0, message); line++; // Line ------ if (metric < 0) { sprintf(message, "Errors: unknown"); } else { sprintf(message, "Errors: %d", metric); } osd_set(line, 0, message); } static void log_sp(config_t *config) { log_info("sp_offset = %d", config->sp_offset); } // ============================================================= // Public methods // ============================================================= static void cpld_init(int version) { cpld_version = version; config->sp_offset = 0; config->filter_c = 1; config->filter_l = 1; for (int i = 0; i < RANGE; i++) { sum_metrics[i] = -1; } errors = -1; } static int cpld_get_version() { return cpld_version; } static void cpld_calibrate(capture_info_t *capinfo, int elk) { int min_i = 0; int metric; // this is a point value (at one sample offset) int min_metric; int win_metric; // this is a windowed value (over three sample offsets) int min_win_metric; int range = RANGE; // 0..15 for the Atom log_info("Calibrating..."); // Measure the error metrics at all possible offset values min_metric = INT_MAX; printf("INFO: "); for (int i = 0; i < NUM_OFFSETS; i++) { printf("%7c", 'A' + i); } printf(" total\r\n"); for (int value = 0; value < range; value++) { config->sp_offset = value; write_config(config); metric = diff_N_frames(capinfo, NUM_CAL_FRAMES, 0, elk); log_info("INFO: value = %d: metric = ", metric); sum_metrics[value] = metric; osd_sp(config, 1, metric); if (metric < min_metric) { min_metric = metric; } } // Use a 3 sample window to find the minimum and maximum min_win_metric = INT_MAX; for (int i = 0; i < range; i++) { int left = (i - 1 + range) % range; int right = (i + 1 + range) % range; win_metric = sum_metrics[left] + sum_metrics[i] + sum_metrics[right]; if (sum_metrics[i] == min_metric) { if (win_metric < min_win_metric) { min_win_metric = win_metric; min_i = i; } } } // In all modes, start with the min metric config->sp_offset = min_i; log_sp(config); write_config(config); // Perform a final test of errors log_info("Performing final test"); errors = diff_N_frames(capinfo, NUM_CAL_FRAMES, 0, elk); osd_sp(config, 1, errors); log_sp(config); log_info("Calibration complete, errors = %d", errors); } static void cpld_set_mode(int mode) { write_config(config); } static int cpld_analyse(int manual_setting) { return (SYNC_BIT_COMPOSITE_SYNC); } static void cpld_update_capture_info(capture_info_t *capinfo) { // Update the capture info stucture, if one was passed in if (capinfo) { // Update the sample width capinfo->sample_width = 1; // 1 = 6 bits // Update the line capture function capinfo->capture_line = capture_line_normal_6bpp_table; } } static param_t *cpld_get_params() { return params; } static int cpld_get_value(int num) { switch (num) { case OFFSET: return config->sp_offset; case FILTER_C: return config->filter_c; case FILTER_L: return config->filter_l; } return 0; } static const char *cpld_get_value_string(int num) { return NULL; } static void cpld_set_value(int num, int value) { if (value < params[num].min) { value = params[num].min; } if (value > params[num].max) { value = params[num].max; } switch (num) { case OFFSET: config->sp_offset = value; break; case FILTER_C: config->filter_c = value; break; case FILTER_L: config->filter_l = value; break; } write_config(config); } static void cpld_show_cal_summary(int line) { osd_sp(config, line, errors); } static void cpld_show_cal_details(int line) { int range = (*sum_metrics < 0) ? 0 : RANGE; if (range == 0) { sprintf(message, "No calibration data for this mode"); osd_set(line, 0, message); } else { int num = range >> 1; for (int value = 0; value < num; value++) { sprintf(message, "Offset %d: %6d; Offset %2d: %6d", value, sum_metrics[value], value + num, sum_metrics[value + num]); osd_set(line + value, 0, message); } } } static int cpld_old_firmware_support() { return 0; } static int cpld_get_divider() { return 8; // not sure of value for atom cpld } static int cpld_get_delay() { return 0; } cpld_t cpld_atom = { .name = "Atom", .init = cpld_init, .get_version = cpld_get_version, .calibrate = cpld_calibrate, .set_mode = cpld_set_mode, .analyse = cpld_analyse, .old_firmware_support = cpld_old_firmware_support, .get_divider = cpld_get_divider, .get_delay = cpld_get_delay, .update_capture_info = cpld_update_capture_info, .get_params = cpld_get_params, .get_value = cpld_get_value, .get_value_string = cpld_get_value_string, .set_value = cpld_set_value, .show_cal_summary = cpld_show_cal_summary, .show_cal_details = cpld_show_cal_details };