kopia lustrzana https://github.com/hoglet67/RGBtoHDMI
282 wiersze
7.2 KiB
C
282 wiersze
7.2 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#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
|
|
};
|