After a little probing with the scope, I figured out how to program this thing. So far, just big, ugly boring yellow and magenta. I've used 364 bytes of my 1 kB - hopefully there is enough room left to do something cool. I guess I could always move to assembly, too - if I have to...
The vertical back porch is 80ns longer than it should be due to the way the counter reset logic works - there's essentially a "branch delay slot" at the end of the frame when the counter needs to be reset. Nobody cares if the 1+ ms back porch is off by this much. Otherwise, the 640x480 output should be exactly by-the-book. With some extra code, I could compensate and make the back porch exact, too, but it probably is not worth the effort (and precious instruction space).
I made a few subtle changes to the design as I built it; I'll post a full updated schematic once I put this thing through a few more tests.
Here's the code so far. Mostly bitbanging the SRAM to jam the waveforms in. Now, I have to make something flashy. Maybe I'll play ouside the box a little and use all my 28 kB code space for something cool but not contest-related :-)
//
// vga_test.c - create first VGA frame
//
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pic16f1718.h>
// CONFIG1
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config FCMEN = ON
// CONFIG2
#pragma config WRT = ALL
#pragma config PPS1WAY = OFF
#pragma config ZCDDIS = ON
#pragma config PLLEN = ON
#pragma config STVREN = OFF
#pragma config BORV = LO
#pragma config LPBOR = OFF
#pragma config LVP = ON
//
// h/w interface definition
//
#define REG_OE_bar 0b00010000
#define WE_bar 0b00000001
#define OE_bar 0b00000010
#define CP_en 0b00001000
#define MR_en 0b00100000
#define CP_bar 0b00000100
#define MR_bar 0b00010000
#define VSYNC 0b10000000
#define HSYNC 0b01000000
#define RGB(r, g, b) (((r & 0x3) << 4) | ((g & 0x3) << 2) | (b & 0x3))
void SetupPeripherals() {
// intosc 32 MHz
OSCCON = 0b11110000;
// select digital I/O
ANSELA = 0;
ANSELB = 0;
ANSELC = 0;
// set TRIS bits: all outputs
PORTA = 0x00;
TRISA = 0x00;
PORTB = 0x00;
TRISB = 0x00;
PORTC = 0x00;
TRISC = 0x80;
}
//
// set control lines for free-running VGA signal generation
//
void RunMode()
{
TRISC = 0xff; // data lines all inputs
// reset address counter, then let it rip
LATB = WE_bar | OE_bar&0 | CP_en&0 | CP_bar&0 | MR_en | MR_bar ;
LATB = WE_bar | OE_bar&0 | CP_en&0 | CP_bar&0 | MR_en&0 | MR_bar&0 ;
LATA = REG_OE_bar&0;
}
//
// set control lines for bitbanging waveforms into SRAM, and
// reset SRAM address counter to 0
//
void LoadMode()
{
LATA = REG_OE_bar;
// toggle CP with MR low to reset address counter
LATB = WE_bar | OE_bar | CP_en | CP_bar | MR_en | MR_bar ;
LATB = WE_bar | OE_bar | CP_en | CP_bar&0 | MR_en | MR_bar ;
LATB = WE_bar | OE_bar | CP_en | CP_bar | MR_en | MR_bar ;
// bring out of reset
LATB = WE_bar | OE_bar | CP_en | CP_bar | MR_en | MR_bar&0 ;
TRISC = 0x00; // data lines all outputs
}
//
// bitbang a number of identical bytes into sequential SRAM addresses
//
void write_SRAM_bytes(uint8_t value, uint8_t count)
{
PORTC = value;
LATB = WE_bar | OE_bar | CP_en | CP_bar | MR_en | MR_bar&0 ;
do {
// toggle WE to write data
LATB = WE_bar&0 | OE_bar | CP_en | CP_bar | MR_en | MR_bar&0 ;
LATB = WE_bar | OE_bar | CP_en | CP_bar | MR_en | MR_bar&0 ;
// toggle CP to advance address
LATB = WE_bar | OE_bar | CP_en | CP_bar&0 | MR_en | MR_bar&0 ;
LATB = WE_bar | OE_bar | CP_en | CP_bar | MR_en | MR_bar&0 ;
} while (--count);
}
void GenerateLine(uint8_t vsync, uint8_t rgb, uint8_t count)
{
do {
write_SRAM_bytes( vsync | HSYNC | rgb&0 , 16); // front porch
write_SRAM_bytes( vsync | HSYNC&0 | rgb&0 , 96); // sync pulse
write_SRAM_bytes( vsync | HSYNC | rgb&0 , 48); // back porch
write_SRAM_bytes( vsync | HSYNC | rgb , 200); // video
write_SRAM_bytes( vsync | HSYNC | rgb , 200); // video
write_SRAM_bytes( vsync | HSYNC | rgb , 240); // video
} while (--count);
}
void GenerateFrame()
{
GenerateLine( VSYNC , RGB(0, 0, 0), 33); // back porch
GenerateLine( VSYNC , RGB(3, 3, 0), 240); // video
GenerateLine( VSYNC , RGB(3, 0, 3), 240); // video
GenerateLine( VSYNC , RGB(0, 0, 0), 10); // front porch
GenerateLine( VSYNC&0 , RGB(0, 0, 0), 2); // sync pulse
write_SRAM_bytes( VSYNC | HSYNC | 0, 2); // end of vsync; resets counter
}
int main() {
SetupPeripherals();
LoadMode();
GenerateFrame();
RunMode();
while(1){
continue;
}
return 0;
}
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.