#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
static void usage(const char * progname)
{
fprintf(stderr, "Usage: %s [options]\n", progname);
fprintf(stderr, "\t-h Assume HSync is active low (default)\n");
fprintf(stderr, "\t+h Assume HSync is active high\n");
fprintf(stderr, "\t-v Assume VSync is active low (default)\n");
fprintf(stderr, "\t+v Assume VSync is active high\n");
fprintf(stderr, "\t-c CSync output will be active low (default)\n");
fprintf(stderr, "\t+c CSync output will be active high\n");
fprintf(stderr, "\t-o <goio> Select output GPIO pin number. Default 1\n");
fprintf(stderr, "\t-s <us> Set HSync width in microseconds. Default 4.7\n");
fprintf(stderr, "\t-t <us> Set line period in microseconds. Default 64.0\n");
fprintf(stderr, "\t-e <us> Set EQ pulse width when interlaced. Default 0.0\n");
fprintf(stderr, "\t-w <halflines> Set VSW in half-lines when interlaced. Default 6\n");
fprintf(stderr, "\t-i | --interlace Generate serrated CSync for an interlaced mode\n");
fprintf(stderr, "\t-p | --pal Equivalent to -i -s 4.7 -t 64.0 -e 2.35 -w 5\n");
fprintf(stderr, "\t-n | --ntsc Equivalent to -i -s 4.7 -t 63.56 -e 2.3 -w 6\n");
}
unsigned int opt_hpos = 0;
unsigned int opt_vpos = 0;
unsigned int opt_cpos = 0;
unsigned int opt_gpio = 1;
unsigned int opt_ilace = 0;
unsigned int opt_vsw = 6;
double opt_hsw = 4.7;
double opt_period = 64.0;
double opt_eqp = 0.0;
static void setpal()
{
opt_ilace = 1;
opt_hsw = 4.7;
opt_period = 64.0;
opt_vsw = 5;
opt_eqp = 2.35;
}
static void setntsc()
{
opt_ilace = 1;
opt_hsw = 4.7;
opt_period = 63.56;
opt_vsw = 6;
opt_eqp = 2.3;
}
static int getopts(int argc, const char **argv)
{
for (; argc > 1; argc--, argv++) {
if (argv[1][0] != '+' && argv[1][0] != '-')
return -1;
if (argv[1][1] == '-') {
if (!strcmp(argv[1], "--interlace"))
opt_ilace = 1;
else if (!strcasecmp(argv[1], "--pal"))
setpal();
else if (!strcasecmp(argv[1], "--ntsc"))
setntsc();
else
return -1;
continue;
}
switch (argv[1][1]) {
case 'h':
opt_hpos = (argv[1][0]=='+');
break;
case 'v':
opt_vpos = (argv[1][0]=='+');
break;
case 'c':
opt_cpos = (argv[1][0]=='+');
break;
case 'o':
if (--argc < 2) return -1;
argv++;
opt_gpio = atoi(argv[1]);
break;
case 's':
if (--argc < 2) return -1;
argv++;
opt_hsw = atof(argv[1]);
break;
case 't':
if (--argc < 2) return -1;
argv++;
opt_period = atof(argv[1]);
break;
case 'e':
if (--argc < 2) return -1;
argv++;
opt_eqp = atof(argv[1]);
break;
case 'w':
if (--argc < 2) return -1;
argv++;
opt_vsw = atoi(argv[1]);
break;
case 'i':
opt_ilace = 1;
break;
case 'p':
setpal();
break;
case 'n':
setntsc();
break;
default:
return -1;
}
}
return 0;
}
static int setup_pio_for_csync_prog(PIO pio)
{
double tc;
unsigned int i, offset;
unsigned short instructions[] = {
0x90a0, 0x7040, 0xb322, 0x3083, 0xa422, 0x2003, 0x00c7, 0x0047, 0x1002, };
struct pio_program prog = {
.instructions = instructions,
.length = sizeof(instructions) / sizeof(instructions[0]),
.origin = -1
};
pio_sm_config cfg = pio_get_default_sm_config();
int sm = pio_claim_unused_sm(pio, true);
if (sm < 0)
return -1;
pio_sm_set_enabled(pio, sm, false);
if (!opt_vpos)
instructions[6] = 0x00c2;
if (!opt_hpos) {
instructions[3] ^= 0x80;
instructions[5] ^= 0x80;
}
if (opt_cpos) {
for (i = 0; i < prog.length; i++)
instructions[i] ^= 0x1000;
}
offset = pio_add_program(pio, &prog);
if (offset == PIO_ORIGIN_ANY)
return -1;
sm_config_set_wrap(&cfg, offset + 2,
offset + (opt_vpos ? 6 : 7));
sm_config_set_sideset(&cfg, 1, false, false);
sm_config_set_sideset_pins(&cfg, opt_gpio);
pio_gpio_init(pio, opt_gpio);
sm_config_set_jmp_pin(&cfg, 2);
pio_sm_init(pio, sm, offset, &cfg);
pio_sm_set_consecutive_pindirs(pio, sm, opt_gpio, 1, true);
tc = 1.0e-6 * ((opt_period - 2.0 * opt_hsw) * (double) clock_get_hz(clk_sys));
pio_sm_put(pio, sm, (unsigned int)(tc - 2.0));
pio_sm_set_enabled(pio, sm, true);
return 0;
}
static int start_timers(PIO pio, int hedge, const unsigned int tc[3])
{
static const unsigned short instructions[2][4] = {
{ 0xa022, 0x2083, 0x0042, 0xc010 },
{ 0xa022, 0x2003, 0x0042, 0xc010 },
};
const struct pio_program prog = {
.instructions = instructions[hedge],
.length = 4,
.origin = -1
};
unsigned int offset, i;
pio_claim_sm_mask(pio, 0xE);
offset = pio_add_program(pio, &prog);
if (offset == PIO_ORIGIN_ANY)
return -1;
for (i = 0; i < 3; i++) {
pio_sm_config cfg = pio_get_default_sm_config();
pio_sm_set_enabled(pio, i + 1, false);
sm_config_set_wrap(&cfg, offset, offset + 3);
pio_sm_init(pio, i + 1, offset, &cfg);
pio_sm_put(pio, i + 1, tc[i] - 4);
pio_sm_exec(pio, i + 1, pio_encode_pull(false, false));
pio_sm_exec(pio, i + 1, pio_encode_out(pio_y, 32));
pio_sm_set_enabled(pio, i + 1, true);
}
return 0;
}
static int setup_pio_for_csync_ilace(PIO pio)
{
static const int wrap_target = 2;
static const int wrap = 23;
unsigned short instructions[] = {
0x90a0, 0x7040, 0x3083, 0xa422, 0x2003, 0x12c2, 0x3083, 0xc442, 0x2003, 0x30c2, 0x10d4, 0x3083, 0xa442, 0x2003, 0xd042, 0xd043, 0x30c2, 0x20c3, 0x1054, 0x1002, 0xd041, 0x3083, 0x20c1, 0x104e, };
struct pio_program prog = {
.instructions = instructions,
.length = sizeof(instructions)/sizeof(instructions[0]),
.origin = -1
};
pio_sm_config cfg = pio_get_default_sm_config();
unsigned int i, offset;
unsigned int tc[3];
unsigned int sm = 0;
pio_claim_sm_mask(pio, 1);
tc[1] = 5.0e-7 * opt_period * (double)clock_get_hz(clk_sys);
tc[0] = tc[1] - 1.0e-6 * opt_hsw * (double)clock_get_hz(clk_sys);
tc[2] = tc[1] + tc[0];
if (start_timers(pio, opt_hpos ? 0 : 1, tc) < 0) {
pio_sm_unclaim(pio, sm);
return -1;
}
pio_sm_set_enabled(pio, sm, false);
for (i = 0; i < prog.length; i++) {
if (opt_cpos)
instructions[i] ^= 0x1000;
if (!opt_hpos && (instructions[i] & 0xe07f) == 0x2003)
instructions[i] ^= 0x0080;
}
offset = pio_add_program(pio, &prog);
if (offset == PIO_ORIGIN_ANY)
return -1;
sm_config_set_wrap(&cfg, offset + wrap_target, offset + wrap);
sm_config_set_sideset(&cfg, 1, false, false);
sm_config_set_sideset_pins(&cfg, opt_gpio);
pio_gpio_init(pio, opt_gpio);
sm_config_set_jmp_pin(&cfg, 2);
pio_sm_init(pio, sm, offset, &cfg);
pio_sm_set_consecutive_pindirs(pio, sm, opt_gpio, 1, true);
pio_sm_put(pio, sm, opt_vsw - 1);
pio_sm_set_enabled(pio, sm, true);
return 0;
}
static int setup_pio_for_csync_tv(PIO pio)
{
static const int wrap_target = 6;
static const int wrap = 27;
unsigned short instructions[] = {
0x3703, 0x3083, 0xa7e6, 0x2003, 0xb7e6, 0x10c1, 0xd042, 0xd043, 0xb022, 0x30c2, 0x004a, 0x6021, 0x002e, 0x20c3, 0x7021, 0x1020, 0xd041, 0xb022, 0x3083, 0x0053, 0x6021, 0x0037, 0x20c1, 0x7021, 0x1020, 0x10c6, 0xb0e6, 0x7022, };
struct pio_program prog = {
.instructions = instructions,
.length = sizeof(instructions)/sizeof(instructions[0]),
.origin = -1
};
pio_sm_config cfg = pio_get_default_sm_config();
unsigned int i, offset;
unsigned int tc[3];
unsigned int sm = 0;
pio_claim_sm_mask(pio, 1);
tc[1] = 5.0e-7 * opt_period * (double)clock_get_hz(clk_sys);
tc[0] = tc[1] - 1.0e-6 * opt_hsw * (double)clock_get_hz(clk_sys);
tc[2] = tc[1] + tc[0];
if (start_timers(pio, opt_hpos ? 0 : 1, tc) < 0) {
pio_sm_unclaim(pio, sm);
return -1;
}
pio_sm_set_enabled(pio, sm, false);
for (i = 0; i < prog.length; i++) {
if (opt_cpos)
instructions[i] ^= 0x1000;
if (!opt_hpos && (instructions[i] & 0xe07f) == 0x2003)
instructions[i] ^= 0x0080;
}
offset = pio_add_program(pio, &prog);
if (offset == PIO_ORIGIN_ANY)
return -1;
sm_config_set_wrap(&cfg, offset + wrap_target, offset + wrap);
sm_config_set_sideset(&cfg, 1, false, false);
sm_config_set_sideset_pins(&cfg, opt_gpio);
pio_gpio_init(pio, opt_gpio);
sm_config_set_jmp_pin(&cfg, 2);
pio_sm_init(pio, sm, offset, &cfg);
pio_sm_set_consecutive_pindirs(pio, sm, opt_gpio, 1, true);
tc[0] = (unsigned)(1.0e-6 * opt_eqp * (double) clock_get_hz(clk_sys));
pio_sm_put(pio, sm, (opt_vsw <= 5) ? 0x02ABFFAA : 0xAABFFEAA);
pio_sm_put(pio, sm, tc[0] - 3);
pio_sm_exec(pio, sm, pio_encode_pull(false, false));
pio_sm_exec(pio, sm, pio_encode_out(pio_y, 32));
pio_sm_exec(pio, sm, pio_encode_in(pio_y, 32));
pio_sm_exec(pio, sm, pio_encode_pull(false, false));
pio_sm_exec(pio, sm, pio_encode_out(pio_y, 32));
pio_sm_set_enabled(pio, sm, true);
pio_sm_set_enabled(pio, sm, true);
return 0;
}
int main(int argc, const char **argv)
{
int r = 0;
if (getopts(argc, argv)) {
const char * progname = (argc > 0 && argv[0]) ? argv[0] : "dpi_csync";
usage(progname);
return 1;
}
if (!opt_ilace)
r = setup_pio_for_csync_prog(pio0);
else if (opt_eqp <= 0 || opt_vsw < 5 || opt_vsw > 6)
r = setup_pio_for_csync_ilace(pio0);
else
r = setup_pio_for_csync_tv(pio0);
if (r) {
fprintf(stderr, "PIO setup failed\n");
return 1;
}
while (true)
sleep_ms(1000);
return 0;
}