# HG changeset patch # User Paul Boddie # Date 1540225546 -7200 # Node ID 16e608d51f1aac376fed803791a60393cc506416 # Parent fcdc2c211f570de16c2546dabe34c9dbf251628c Added a parallel mode variant of the VGA example. diff -r fcdc2c211f57 -r 16e608d51f1a examples/vga-pmp/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-pmp/Makefile Mon Oct 22 18:25:46 2018 +0200 @@ -0,0 +1,33 @@ +# Makefile - Build the PIC32 deployment payload +# +# Copyright (C) 2015, 2017, 2018 Paul Boddie +# Copyright (C) Xiangfu Liu +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +include ../../mk/common.mk + +TARGET = vga.elf +DUMP = $(TARGET:.elf=.dump) +MAP = $(TARGET:.elf=.map) + +HEX = $(TARGET:.elf=.hex) +SREC = $(TARGET:.elf=.srec) + +# Ordering of objects is important and cannot be left to replacement rules. + +SRC = $(START_SRC) main.c $(COMMON_SRC) +OBJ = $(START_OBJ) main.o $(COMMON_OBJ) + +include ../../mk/rules.mk diff -r fcdc2c211f57 -r 16e608d51f1a examples/vga-pmp/README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-pmp/README.txt Mon Oct 22 18:25:46 2018 +0200 @@ -0,0 +1,111 @@ +Introduction +------------ + +This example demonstrates the generation of an analogue VGA signal from a +PIC32 microcontroller using the parallel mode (parallel master port, PMP) +peripheral. The result is not entirely satisfactory: + + * Pixels are very narrow unless buffered using a flip-flop driven by the + peripheral, this being a characteristic of the way the peripheral works, it + normally being used to drive memory and display controllers. + + * Introducing a flip-flop means that the final pixel from the pixel data + remains asserted and must be reset using a second DMA channel. + + * Every fourth pixel is wider than the others, this apparently being an + artefact of the DMA transfer mechanism. + +It might be possible introduce some kind of delay to the write strobe (PMWR) +and even out the pixel widths, but this has not been investigated. + +It appears to be the case that the system and peripheral clock frequencies +need to be matched. In this example, a frequency of 48MHz has been chosen. + +Hardware Details +================ + +The pin usage of this solution is documented below. + +PIC32MX270F256B-50I/SP Pin Assignments +-------------------------------------- + +MCLR# 1 \/ 28 + D7/PMD7/RA0 2 27 + D6/PMD6/RA1 3 26 RB15/U1TX + D0/PMD0/RB0 4 25 RB14 + D1/PMD1/RB1 5 24 RB13/(PMRD)/U1RX + D2/PMD2/RB2 6 23 + PMWR/RB3 7 22 RB11/PGEC2 + 8 21 RB10/PGEC3 + RA2 9 20 + (PMA0)/RA3 10 19 +HSYNC/OC1/RB4 11 18 RB9/PMD3/D3 + 12 17 RB8/PMD4/D4 + 13 16 RB7/PMD5/D5 +VSYNC/OC2/RB5 14 15 + +Note that RB6 is not available on pin 15 on this device (it is needed for VBUS +unlike the MX170 variant). + +UART Connections +---------------- + +UART1 is exposed by the RB13 and RB15 pins. + +Data Signal Routing +------------------- + +A flip-flop is used to buffer the outputs: + +Dn -> 74HC273:Dn + 74HC273:Qn -> Qn +VCC -> 74HC273:MR# +PMWR -> 74HC273:CP + +For two bits of intensity, two bits per colour channel: + +Q7 -> 2200R -> I +Q6 -> 4700R -> I + +I -> diode -> R +I -> diode -> G +I -> diode -> B + +Q5 -> 470R -> R +Q4 -> 1000R -> R +Q3 -> 470R -> G +Q2 -> 1000R -> G +Q1 -> 470R -> B +Q0 -> 1000R -> B + +HSYNC -> HS +VSYNC -> VS + +Output Socket Pinout +-------------------- + + 5 (GND) 4 (NC) 3 (B) 2 (G) 1 (R) + + 10 (GND) 9 (NC) 8 (GND) 7 (GND) 6 (GND) + + 15 (NC) 14 (VS) 13 (HS) 12 (NC) 11 (NC) + +Output Cable Pinout +------------------- + + 1 (R) 2 (G) 3 (B) 4 (NC) 5 (GND) + + 6 (GND) 7 (GND) 8 (GND) 9 (NC) 10 (GND) + + 11 (NC) 12 (NC) 13 (HS) 14 (VS) 15 (NC) + +References +---------- + +https://en.wikipedia.org/wiki/VGA_connector + +http://papilio.cc/index.php?n=Papilio.VGAWing + +http://lucidscience.com/pro-vga%20video%20generator-2.aspx + +https://sites.google.com/site/h2obsession/CBM/C128/rgbi-to-vga diff -r fcdc2c211f57 -r 16e608d51f1a examples/vga-pmp/devconfig.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-pmp/devconfig.h Mon Oct 22 18:25:46 2018 +0200 @@ -0,0 +1,63 @@ +/* + * Device configuration. + * + * Copyright (C) 2018 Paul Boddie + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#include "pic32.h" + +/* +Set the oscillator to be the FRC oscillator with PLL, with peripheral clock +divided by 1 (FPBDIV), and FRCDIV+PLL selected (FNOSC). + +The watchdog timer (FWDTEN) is also disabled. + +The secondary oscillator pin (FSOSCEN) is disabled to avoid pin conflicts with +RPB4. +*/ + +#define DEVCFG1_CONFIG (DEVCFG1_FWDTEN_OFF | DEVCFG1_FPBDIV_1 | \ + DEVCFG1_OSCIOFNC_OFF | DEVCFG1_FSOSCEN_OFF | \ + DEVCFG1_FNOSC_FRCDIV_PLL) + +/* +Set the FRC oscillator PLL function with an input division of 2, an output +division of 2, a multiplication of 24, yielding a multiplication of 6. + +The FRC is apparently at 8MHz but enforces input division of 2 to produce a +frequency in the acceptable range from 4MHz to 5MHz for the PLL: + +8MHz / 2 = 4MHz + +Multiplication and output division should produce a system clock of 48MHz: + +4MHz * 24 / 2 = 48MHz +*/ + +#define DEVCFG2_CONFIG (DEVCFG2_FPLLODIV_2 | DEVCFG2_FPLLMUL_24 | \ + DEVCFG2_FPLLIDIV_2) + +/* +The peripheral clock frequency (FPB) will be 48MHz given the above DEVCFG1 and +DEVCFG2 settings. +*/ + +#define FPB 48000000 + +#endif /* __CONFIG_H__ */ diff -r fcdc2c211f57 -r 16e608d51f1a examples/vga-pmp/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-pmp/main.c Mon Oct 22 18:25:46 2018 +0200 @@ -0,0 +1,303 @@ +/* + * Generate a VGA signal using a PIC32 microcontroller. + * + * Copyright (C) 2017, 2018 Paul Boddie + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "pic32_c.h" +#include "init.h" +#include "debug.h" +#include "main.h" +#include "vga.h" + + + +/* Display state. */ + +static void (*state_handler)(void); +static uint32_t line; + +/* Pixel data. */ + +static uint8_t linedata[LINE_LENGTH]; +static const uint8_t zerodata[ZERO_LENGTH] = {0}; + + + +static void test_linedata(void) +{ + int i; + + for (i = 0; i < LINE_LENGTH; i++) + linedata[i] = (i % 2) ? 0xff : 0x00; +} + +/* Blink an attached LED with delays implemented using a loop. */ + +static void blink(uint32_t delay, uint32_t port, uint32_t pins) +{ + uint32_t counter; + + /* Clear outputs (LED). */ + + CLR_REG(port, pins); + + while (1) + { + counter = delay; + + while (counter--) __asm__(""); /* retain loop */ + + /* Invert outputs (LED). */ + + INV_REG(port, pins); + rbits(PM_REG(0, PMxCON)); uart_write_nl(); + rhex(PM_REG(0, PMxMODE)); uart_write_nl(); + } +} + + + +/* Main program. */ + +void main(void) +{ + line = 0; + state_handler = vbp_active; + test_linedata(); + + init_memory(); + init_pins(); + init_outputs(); + + unlock_config(); + config_oc(); + config_uart(); + lock_config(); + + init_dma(); + init_pm(); + + /* Configure parallel master mode. */ + + pm_init(0, 0b10); + pm_set_output(0, 1, 0); + pm_on(0); + + /* Initiate DMA on the Timer2 interrupt transferring line data to the first + byte of PORTB. Do not enable the channel for initiation until the visible + region is about to start. */ + + dma_init(0, 3); + dma_set_auto_enable(0, 1); + dma_set_interrupt(0, T2, 1); + dma_set_transfer(0, PHYSICAL((uint32_t) linedata), LINE_LENGTH, + HW_PHYSICAL(PM_REG(0, PMxDIN)), 1, + LINE_LENGTH); + dma_init_interrupt(0, 0b1000, 1, 3); + + /* Enable DMA on the preceding channel's completion, with this also + initiating transfers. This "reset" or "zero" transfer is employed to set + the pixel level to black in a connected flip-flop. Without the flip-flop + it is superfluous. */ + + dma_init(1, 3); + dma_set_chaining(1, dma_chain_previous); + dma_set_interrupt(1, DMA0, 1); + dma_set_transfer(1, PHYSICAL((uint32_t) zerodata), ZERO_LENGTH, + HW_PHYSICAL(PM_REG(0, PMxDIN)), 1, + ZERO_LENGTH); + dma_set_receive_events(1, 1); + + /* Configure a timer for the horizontal sync. The timer has no prescaling + (0). */ + + timer_init(2, 0, HFREQ_LIMIT); + timer_on(2); + + /* Horizontal sync. */ + + /* Configure output compare in dual compare (continuous output) mode using + Timer2 as time base. The interrupt condition drives the first DMA channel + and is handled to drive the display state machine. */ + + oc_init(1, 0b101, 2); + oc_set_pulse(1, HSYNC_END); + oc_set_pulse_end(1, HSYNC_START); + oc_init_interrupt(1, 7, 3); + oc_on(1); + + /* Vertical sync. */ + + /* Configure output compare in single compare (output driven low) mode using + Timer2 as time base. The unit is enabled later. It is only really used to + achieve precisely-timed level transitions in hardware. */ + + oc_init(2, 0b010, 2); + oc_set_pulse(2, 0); + + uart_init(1, 115200); + uart_on(1); + + interrupts_on(); + + blink(3 << 24, PORTA, 1 << 2); +} + + + +/* Exception and interrupt handlers. */ + +void exception_handler(void) +{ + blink(3 << 12, PORTA, 1 << 2); +} + +void interrupt_handler(void) +{ + uint32_t ifs; + + /* Check for a OC1 interrupt condition. */ + + ifs = REG(OCIFS) & OC_INT_FLAGS(1, OCxIF); + + if (ifs) + { + line += 1; + state_handler(); + CLR_REG(OCIFS, ifs); + } +} + + + +/* Vertical back porch region. */ + +void vbp_active(void) +{ + if (line < VISIBLE_START) + return; + + /* Enter the visible region. */ + + state_handler = visible_active; + + /* NOTE: Set the line address. */ + + /* Enable the channel for the next line. */ + + dma_on(0); +} + +/* Visible region. */ + +void visible_active(void) +{ + uint32_t ifs; + + /* Remove any DMA interrupt condition (CHBCIF). */ + + ifs = REG(DMAIFS) & DMA_INT_FLAGS(0, DCHxIF); + + if (ifs) + { + CLR_REG(DMA_REG(0, DCHxINT), 0b11111111); + CLR_REG(DMAIFS, ifs); + } + + if (line < VFP_START) + { + /* NOTE: Update the line address and handle wraparound. */ + + return; + } + + /* End the visible region. */ + + state_handler = vfp_active; + + /* Disable the channel for the next line. */ + + dma_off(0); +} + +/* Vertical front porch region. */ + +void vfp_active(void) +{ + if (line < VSYNC_START) + return; + + /* Enter the vertical sync region. */ + + state_handler = vsync_active; + + /* Bring vsync low (single compare, output driven low) when the next line + starts. */ + + oc_init(2, 0b010, 2); + oc_on(2); +} + +/* Vertical sync region. */ + +void vsync_active(void) +{ + if (line < VSYNC_END) + return; + + /* Start again at the top of the display. */ + + line = 0; + state_handler = vbp_active; + + /* Bring vsync high (single compare, output driven high) when the next line + starts. */ + + oc_init(2, 0b001, 2); + oc_on(2); +} + + + +/* Peripheral pin configuration. */ + +void config_oc(void) +{ + /* Map OC1 to RPB4. */ + + REG(RPB4R) = 0b0101; /* RPB4R<3:0> = 0101 (OC1) */ + + /* Map OC2 to RPB5. */ + + REG(RPB5R) = 0b0101; /* RPB5R<3:0> = 0101 (OC2) */ +} + +void config_uart(void) +{ + /* Map U1RX to RPB13. */ + + REG(U1RXR) = 0b0011; /* U1RXR<3:0> = 0011 (RPB13) */ + + /* Map U1TX to RPB15. */ + + REG(RPB15R) = 0b0001; /* RPB15R<3:0> = 0001 (U1TX) */ + + /* Set RPB13 to input. */ + + SET_REG(TRISB, 1 << 13); +} diff -r fcdc2c211f57 -r 16e608d51f1a examples/vga-pmp/main.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-pmp/main.h Mon Oct 22 18:25:46 2018 +0200 @@ -0,0 +1,35 @@ +/* + * Generate a VGA signal using a PIC32 microcontroller. + * + * Copyright (C) 2017, 2018 Paul Boddie + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __MAIN_H__ +#define __MAIN_H__ + +/* Peripheral pin configuration. */ + +void config_oc(void); +void config_uart(void); + +/* Display state handlers. */ + +void vbp_active(void); +void visible_active(void); +void vfp_active(void); +void vsync_active(void); + +#endif /* __MAIN_H__ */ diff -r fcdc2c211f57 -r 16e608d51f1a examples/vga-pmp/vga.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-pmp/vga.h Mon Oct 22 18:25:46 2018 +0200 @@ -0,0 +1,55 @@ +/* + * Generate a VGA signal using a PIC32 microcontroller. + * + * Copyright (C) 2017, 2018 Paul Boddie + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __VGA_H__ +#define __VGA_H__ + +#define LINE_LENGTH 160 /* pixels */ +#define LINE_COUNT 256 /* distinct display lines */ + +#define ZERO_LENGTH 1 /* pixels */ + +/* 48MHz cycle measurements. */ + +#define HFREQ_LIMIT 1286 +#define HSYNC_START 920 +#define HSYNC_LIMIT 128 +#define HSYNC_END (HSYNC_START + HSYNC_LIMIT) + +/* Horizontal lines, back porch end. */ + +#define VISIBLE_START 70 +#define VFP_START (VISIBLE_START + 2 * LINE_COUNT) + +/* Horizontal lines, front porch end. */ + +#define VSYNC_START 620 + +/* Horizontal lines, back porch start. */ + +#define VSYNC_END 622 + +#define SCREEN_BASE 256 +#define SCREEN_SIZE (40 * 1024) +#define SCREEN_LIMIT (SCREEN_BASE + SCREEN_SIZE) + +#define SCREEN_BASE_KSEG0 (KSEG0_BASE + SCREEN_BASE) +#define SCREEN_LIMIT_KSEG0 (KSEG0_BASE + SCREEN_LIMIT) + +#endif /* __VGA_H__ */