Uses the RP2040's Programmable IO to create a PAL colour video signal. It consists of the PIO program, some "driver" and tools code, a test program and a program for creating colour Look-up Tables (LUTs).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
rppico-pio-pal/pal_colour_table_gen.c

232 lines
6.5 KiB

/*
* This program generates Look-up Tables for RGB to PAL colour conversion.
* It can be used to generate tables with independent numbers of bits for
* R, G and B and four to eight bits per sample.
* The number of samples per colour carrier period is fixed to eight, but that
* can easily be changed by adding another command line parameter..
* The program creates four tables which are to be used round robin in four
* consecutive lines, reflecting the colour carrier's phase shift of -PI/2
* per video line. The LUTs also account for the PAL swing, i.e., the V
* component carrier alternating between 0 and PI phase.
* Output is either written to STDOUT or a supplied file. In both cases, a
* C header is written.
*
* I must note that it hasn't been tested very much, I only used
* 2 bits for each colour component and 4 bits per sample.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdint.h>
#include <stdbool.h>
int main(int argc, char **argv) {
bool writeFile = false;
int rBits, gBits, bBits, bps;
uint32_t *lut[4], tblIdx = 0;
uint32_t nColours, nEntriesPerColour, nSamples = 8, bitsPerColour, mask, bitsLeft;
float dR, dG, dB, r, g, b;
float Ey, Eu, Ev;
float i, q, dRad, Vf, phaseShift;
float Vmax = 1.3, dV, Vblack = 0.3, Vswing = 0.7, Vburst = 0.15;
float *sinTbl[4], *cosTbl[4];
uint32_t nBlack, nV;
FILE* f;
if(argc < 5) {
printf("usage: %s <rBits> <gBits> <bBits> <bitsPerSample> [output file]\n", argv[0]);
return -1;
}
if(argc > 5) {
f = fopen(argv[5], "w+");
if(NULL == f) {
printf("failed to open file %s\n", argv[5]);
return -1;
}
} else {
f = stdout;
}
rBits = atoi(argv[1]);
gBits = atoi(argv[2]);
bBits = atoi(argv[3]);
bps = atoi(argv[4]);
if((rBits < 1) || (gBits < 1) || (bBits < 1) || (bps < 4) || (bps > 8)) {
printf("invalid parameters: %s %d %d %d %d\nBits per Colour >= 1, 4 <= Bits per Sample <= 8\n", argv[0], rBits, gBits, bBits, bps);
return -1;
}
nColours = 1 << (rBits + gBits + bBits);
bitsPerColour = (bps * nSamples);
nEntriesPerColour = bitsPerColour / 32;
nEntriesPerColour += ((bitsPerColour % 32) == 0) ? 0 : 1;
dV = Vmax / (1 << bps);
dRad = 2 * M_PI / nSamples;
mask = (1 << bps) - 1;
nBlack = (uint32_t)roundf(Vblack / dV);
printf("Generating %u colours, each using %u uint32\n", nColours, nEntriesPerColour);
for(int i = 0; i < 4; ++i) {
lut[i] = (uint32_t*)malloc(nColours * nEntriesPerColour * sizeof(uint32_t));
sinTbl[i] = (float*)malloc(nSamples * sizeof(float));
cosTbl[i] = (float*)malloc(nSamples * sizeof(float));
}
if((lut[0] == NULL) || (lut[1] == NULL) || (lut[2] == NULL) || (lut[3] == NULL) || (sinTbl[0] == NULL) || (cosTbl[0] == NULL) || (sinTbl[1] == NULL) || (cosTbl[1] == NULL) || (sinTbl[2] == NULL) || (cosTbl[2] == NULL) || (sinTbl[3] == NULL) || (cosTbl[3] == NULL)) {
printf("malloc(%u) failed\n", nColours * nEntriesPerColour * sizeof(uint32_t));
return -1;
}
// prepare sine and cosine lookup tables
for(int i = 0; i < 4; ++i) {
for(uint sample = 0; sample < nSamples; ++sample) {
phaseShift = (4 - i) * M_PI_2;
sinTbl[i][sample] = sin(sample * dRad + phaseShift);
cosTbl[i][sample] = cos(sample * dRad + phaseShift);
}
}
printf("Sine and Cosine lookup tables created\n");
dR = 1.0 / (float)((1 << rBits) - 1);
dG = 1.0 / (float)((1 << gBits) - 1);
dB = 1.0 / (float)((1 << bBits) - 1);
r = 0.0;
g = 0.0;
b = 0.0;
tblIdx = 0;
for(uint rl = 0; rl < (1 << rBits); ++rl, r += dR) {
g = 0.0;
for(uint gl = 0; gl < (1 << gBits); ++gl, g += dG) {
b = 0.0;
for(uint bl = 0; bl < (1 << bBits); ++bl, b += dB) {
Ey = 0.299 * r + 0.587 * g + 0.114 * b;
Eu = 0.492 * (b - Ey);
Ev = 0.877 * (r - Ey);
bitsLeft = 32;
for(uint sample = 0; sample < nSamples; ++sample) {
if(bitsLeft < bps) {
// if we use less than 32 bits, shift each finished word to the
// left such that the most significant nibble is in bits 31:28.
// That's important as the PIO SM is configured to shift left,
// i.e., it uses the most significant bits.
lut[0][tblIdx] <<= bitsLeft;
lut[1][tblIdx] <<= bitsLeft;
lut[2][tblIdx] <<= bitsLeft;
lut[3][tblIdx] <<= bitsLeft;
++tblIdx;
bitsLeft = 32;
}
for(int idx = 0; idx < 4; ++idx) {
i = Eu * sinTbl[idx][sample];
q = Ev * cosTbl[idx][sample];
if(idx & 1) {
Vf = Vswing * (Ey + i - q);
nV = (uint32_t)roundf(Vf / dV);
} else {
Vf = Vswing * (Ey + i + q);
nV = (uint32_t)roundf(Vf / dV);
}
lut[idx][tblIdx] = (lut[idx][tblIdx] << bps) | ((nV + nBlack) & mask);
}
bitsLeft -= bps;
}
if(bitsLeft > 0) {
lut[0][tblIdx] <<= bitsLeft;
lut[1][tblIdx] <<= bitsLeft;
lut[2][tblIdx] <<= bitsLeft;
lut[3][tblIdx] <<= bitsLeft;
}
++tblIdx;
}
}
}
printf("Writing output\n\n");
fprintf(f, "#ifndef PAL_COLOUR_LUT_H__\n#define PAL_COLOUR_LUT_H__\n\n#define N_BITS_PER_SAMPLE %u\n#define N_SAMPLES %u\n#define N_COLOUR_ENTRIES %u\n\n", bps, nSamples, nColours);
fprintf(f, "#define WORDS_PER_COLOUR %u\n\n#define R_BITS %u\n#define G_BITS %u\n#define B_BITS %u\n#define PAL_LUT_MASK 0x%08x\n\n", nEntriesPerColour, rBits, gBits, bBits, (1 << (rBits + gBits + bBits)) - 1);
tblIdx = 0;
fprintf(f, "uint palLut[4][%u] = {{\n", nColours * nEntriesPerColour);
for(int i = 0; i < 3; ++i) {
while(tblIdx < (nColours - 1) * nEntriesPerColour) {
if(nEntriesPerColour > 1) {
for(uint32_t ent = 0; ent < nEntriesPerColour - 1; ++ent) {
fprintf(f, "0x%08x, ", lut[i][tblIdx++]);
}
fprintf(f, "0x%08x,\n", lut[i][tblIdx++]);
} else {
fprintf(f, "\t0x%08x,\n", lut[i][tblIdx++]);
}
}
if(nEntriesPerColour > 1) {
for(uint32_t ent = 0; ent < nEntriesPerColour - 1; ++ent) {
fprintf(f, "0x%08x, ", lut[i][tblIdx++]);
}
fprintf(f, "0x%08x\n", lut[i][tblIdx++]);
} else {
fprintf(f, "\t0x%08x\n", lut[i][tblIdx++]);
}
fprintf(f, "},\n{\n", nColours * nEntriesPerColour);
tblIdx = 0;
}
while(tblIdx < (nColours - 1) * nEntriesPerColour) {
if(nEntriesPerColour > 1) {
for(uint32_t ent = 0; ent < nEntriesPerColour - 1; ++ent) {
fprintf(f, "0x%08x, ", lut[3][tblIdx++]);
}
fprintf(f, "0x%08x,\n", lut[3][tblIdx++]);
} else {
fprintf(f, "\t0x%08x,\n", lut[3][tblIdx++]);
}
}
if(nEntriesPerColour > 1) {
for(uint32_t ent = 0; ent < nEntriesPerColour - 1; ++ent) {
fprintf(f, "0x%08x, ", lut[3][tblIdx++]);
}
fprintf(f, "0x%08x\n", lut[3][tblIdx++]);
} else {
fprintf(f, "\t0x%08x\n", lut[3][tblIdx++]);
}
fprintf(f, "}};\n\n#endif");
fclose(f);
return 0;
}