# HG changeset patch # User Paul Boddie # Date 1540323294 -7200 # Node ID 18aa2a60b1ab1195c4a79c59b71afb06928157c2 # Parent 24065fd1609fd665ea5dbfa97cb96cd755586cb2 Introduced dual pixel transfer channels to the timer-based VGA example, also introducing a new example modifying the original VGA example similarly. Changed the framebuffer pattern generator function to observe the necessary pixel data arrangement where the channels divide each line into two, contributing pixels that are interleaved. diff -r 24065fd1609f -r 18aa2a60b1ab examples/vga-dual/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-dual/Makefile Tue Oct 23 21:34:54 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) $(DISPLAY_SRC) +OBJ = $(START_OBJ) main.o $(COMMON_OBJ) $(DISPLAY_OBJ) + +include ../../mk/rules.mk diff -r 24065fd1609f -r 18aa2a60b1ab examples/vga-dual/README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-dual/README.txt Tue Oct 23 21:34:54 2018 +0200 @@ -0,0 +1,105 @@ +Introduction +------------ + +This example demonstrates the generation of an analogue VGA signal from a +PIC32 microcontroller using general output pins. It follows on from the work +done in the VGAPIC32 project. The result is not entirely satisfactory: + + * Every fourth pixel is wider than the others, this apparently being an + artefact of the DMA transfer mechanism. + +It might be possible to introduce some kind of delay and even out the pixel +widths, but this has not been investigated with hardware. However, unlike the +vga-pmp example, there is no accompanying signal to potentially orchestrate +the staging of individual pixels at a slightly delayed rate. Potentially, the +peripheral clock signal might be generated and processed to make such a +signal. + +Unlike the vga example, this example employs two DMA channels for pixel data +which are interleaved to investigate a potential remedy for the wide pixel +effect. Unfortunately, despite each channel contributing every other word (or +group of four pixels), the effect persists. However, the picture is perhaps +more stable than in the vga example. + +Hardware Details +================ + +The pin usage of this solution is documented below. + +PIC32MX270F256B-50I/SP Pin Assignments +-------------------------------------- + +MCLR# 1 \/ 28 +HSYNC/OC1/RA0 2 27 +VSYNC/OC2/RA1 3 26 RB15/U1TX + D0/RB0 4 25 RB14 + D1/RB1 5 24 RB13/U1RX + D2/RB2 6 23 + D3/RB3 7 22 RB11/PGEC2 + 8 21 RB10/PGEC3 + RA2 9 20 + RA3 10 19 + D4/RB4 11 18 RB9 + 12 17 RB8 + 13 16 RB7/D7 + D5/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 +------------------- + +For one bit of intensity, two bits per colour channel: + +D7 -> 2200R -> I + +I -> diode -> R +I -> diode -> G +I -> diode -> B + +D6 (not connected) + +D5 -> 470R -> R +D4 -> 1000R -> R +D3 -> 470R -> G +D2 -> 1000R -> G +D1 -> 470R -> B +D0 -> 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 24065fd1609f -r 18aa2a60b1ab examples/vga-dual/devconfig.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-dual/devconfig.h Tue Oct 23 21:34:54 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 2 (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_2 | \ + 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 24MHz given the above DEVCFG1 and +DEVCFG2 settings. +*/ + +#define FPB 24000000 + +#endif /* __CONFIG_H__ */ diff -r 24065fd1609f -r 18aa2a60b1ab examples/vga-dual/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-dual/main.c Tue Oct 23 21:34:54 2018 +0200 @@ -0,0 +1,311 @@ +/* + * 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" + +/* Specific functionality. */ + +#include "main.h" +#include "vga.h" +#include "display.h" + + + +/* Display state. */ + +static void (*state_handler)(void); +static uint32_t line; + +/* Pointers to pixel lines. */ + +static uint8_t *linedata, *linedatalimit, *screenstart; + +/* Pixel data. */ + +static const uint8_t zerodata[ZERO_LENGTH] = {0}; +static uint8_t framebuffer[SCREEN_SIZE]; + + + +/* 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); + } +} + + + +/* Main program. */ + +void main(void) +{ + line = 0; + state_handler = vbp_active; + test_linedata(framebuffer); + + /* Initialise the current display line pointer and display limit. */ + + linedatalimit = framebuffer + SCREEN_SIZE; + screenstart = framebuffer; + + init_memory(); + init_pins(); + init_outputs(); + + unlock_config(); + config_oc(); + config_uart(); + lock_config(); + + init_dma(); + + /* Initiate DMA on the Timer2 interrupt transferring line data to the first + byte of PORTB. Do not enable the channels for initiation until the visible + region is about to start. + + Here, two DMA channels are interleaved. This appears to provide a more + stable picture. */ + + dma_init(0, 3); + dma_set_auto_enable(0, 1); + dma_set_interrupt(0, T2, 1); + dma_set_transfer(0, PHYSICAL((uint32_t) screenstart), LINE_LENGTH / 2, + HW_PHYSICAL(PORTB), 1, + TRANSFER_CELL_SIZE); + + dma_init(1, 3); + dma_set_auto_enable(1, 1); + dma_set_interrupt(1, T2, 1); + dma_set_transfer(1, PHYSICAL((uint32_t) screenstart + LINE_LENGTH / 2), LINE_LENGTH / 2, + HW_PHYSICAL(PORTB), 1, + TRANSFER_CELL_SIZE); + + /* Enable DMA on the preceding channel's completion, with the timer event + initiating the transfer. */ + + dma_init(2, 3); + dma_set_chaining(2, dma_chain_previous); + dma_set_interrupt(2, T2, 1); + dma_set_transfer(2, PHYSICAL((uint32_t) zerodata), ZERO_LENGTH, + HW_PHYSICAL(PORTB), 1, + ZERO_LENGTH); + dma_set_receive_events(2, 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 << 3); +} + + + +/* Exception and interrupt handlers. */ + +void exception_handler(void) +{ + blink(3 << 12, PORTA, 1 << 3); +} + +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; + + /* Set the line address. */ + + linedata = screenstart; + dma_set_source(0, PHYSICAL((uint32_t) linedata), LINE_LENGTH / 2); + dma_set_source(1, PHYSICAL((uint32_t) linedata + LINE_LENGTH / 2), LINE_LENGTH / 2); + + /* Enable the channels for the next line. */ + + dma_on(0); + dma_on(1); +} + +/* Visible region. */ + +void visible_active(void) +{ + INV_REG(PORTA, 1 << 2); + + if (line < VFP_START) + { + /* Update the line address and handle wraparound. */ + + if (!(line % LINE_MULTIPLIER)) + { + linedata += LINE_LENGTH; + if (linedata >= linedatalimit) + linedata -= SCREEN_SIZE; + } + + dma_set_source(0, PHYSICAL((uint32_t) linedata), LINE_LENGTH / 2); + dma_set_source(1, PHYSICAL((uint32_t) linedata + LINE_LENGTH / 2), LINE_LENGTH / 2); + return; + } + + /* End the visible region. */ + + state_handler = vfp_active; + + /* Disable the channels for the next line. */ + + dma_off(0); + dma_off(1); +} + +/* 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 RPA0. */ + + REG(RPA0R) = 0b0101; /* RPA0R<3:0> = 0101 (OC1) */ + + /* Map OC2 to RPA1. */ + + REG(RPA1R) = 0b0101; /* RPA1R<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 24065fd1609f -r 18aa2a60b1ab examples/vga-dual/main.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-dual/main.h Tue Oct 23 21:34:54 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 24065fd1609f -r 18aa2a60b1ab examples/vga-dual/vga.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/vga-dual/vga.h Tue Oct 23 21:34:54 2018 +0200 @@ -0,0 +1,58 @@ +/* + * 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 LINE_MULTIPLIER 2 + +#define ZERO_LENGTH 1 /* pixels */ + +/* 24MHz cycle measurements. */ + +#define HFREQ_LIMIT 643 +#define HSYNC_START 460 +#define HSYNC_LIMIT 64 +#define HSYNC_END (HSYNC_START + HSYNC_LIMIT) + +/* Horizontal lines, back porch end. */ + +#define VISIBLE_START 70 +#define VFP_START (VISIBLE_START + LINE_MULTIPLIER * LINE_COUNT) + +/* Horizontal lines, front porch end. */ + +#define VSYNC_START 620 + +/* Horizontal lines, back porch start. */ + +#define VSYNC_END 622 + +/* Framebuffer properties. */ + +#define SCREEN_SIZE (LINE_LENGTH * LINE_COUNT) + +/* Transfer and pixel allocation properties. */ + +#define TRANSFER_CELL_SIZE (LINE_LENGTH / 2) +#define CELL_SIZE 4 + +#endif /* __VGA_H__ */ diff -r 24065fd1609f -r 18aa2a60b1ab examples/vga-timer/README.txt --- a/examples/vga-timer/README.txt Tue Oct 23 19:29:55 2018 +0200 +++ b/examples/vga-timer/README.txt Tue Oct 23 21:34:54 2018 +0200 @@ -23,33 +23,43 @@ more readily delayed by other activity in the system, and with instability of the picture being the result. +Unlike the vga example, but in common with the vga-dual example, this example +employs two DMA channels for pixel data which are interleaved to investigate a +potential remedy for the wide pixel effect. This seems to preserve consistent +pixel widths only with a transfer cell size of 1: other cell sizes suffer from +the wide pixel problem. Despite not offering the greater throughput of larger +cell sizes, merely employing dual channels increases throughput for a cell +size of 1, making the technique worth using. + In contrast to the vga and vga-pmp examples, a special DMA channel is employed to initiate the pixel transfer process without actually transferring any pixel data itself. The channel arrangement is as follows: Transfer Initiator DMA Channel Transfer Activity ------------------ ----------- ----------------- - Timer2 DMA0 zerodata -> PORTB - Timer3 DMA1 linedata -> PORTB - DMA1 (completion) DMA2 zerodata -> PORTB + Timer2 DMA1 zerodata -> PORTB + Timer3 DMA0 linedata -> PORTB + Timer3 DMA2 linedata -> PORTB + Timer3 DMA3 zerodata -> PORTB -The real purpose of this channel (DMA0) is to capture the Timer2 interrupt -condition and to enable the following channel (DMA1) through channel chaining. -Having been enabled, DMA1 is then able to conduct transfers at a tempo -dictated by Timer3. Finally, DMA2 acts as the "reset" or "zero" channel to -ensure that the pixel level is set to black at the end of each display line. +The real purpose of this channel (DMA1) is to capture the Timer2 interrupt +condition and to enable the following channels (DMA0, DMA2) through channel +chaining. Having been enabled, DMA0 and DMA2 are then able to conduct +transfers at a tempo dictated by Timer3. Finally, DMA3 acts as the "reset" or +"zero" channel to ensure that the pixel level is set to black at the end of +each display line. In principle, other initiating conditions can be used instead of Timer3, which is configured to produce such conditions as frequently as possible: * A persistent interrupt condition can be employed instead. For example, configuring UART2 and setting the UART2 transfer interrupt, employing this - interrupt condition for DMA1, produces the same effect. + interrupt condition for DMA0 and DMA2, produces the same effect. * An external interrupt such as INT2 can be configured, and the peripheral clock can be routed through the CLKO pin and back into the microcontroller via an appropriate pin. With INT2 being employed as the interrupt - condition for DMA1, the same effect is produced. + condition for DMA0 and DMA2, the same effect is produced. Hardware Details ================ diff -r 24065fd1609f -r 18aa2a60b1ab examples/vga-timer/main.c --- a/examples/vga-timer/main.c Tue Oct 23 19:29:55 2018 +0200 +++ b/examples/vga-timer/main.c Tue Oct 23 21:34:54 2018 +0200 @@ -98,44 +98,55 @@ Timer2 -> OC1 -> OC2 (vertical sync region) - -> DMA0: zerodata -> PORTB (visible region) + -> DMA1: zerodata -> PORTB (visible region) | - Timer3 -> DMA1: linedata -> PORTB + Timer3 -> DMA0: linedata -> PORTB + Timer3 -> DMA2: linedata -> PORTB | - Timer3 -> DMA2: zerodata -> PORTB + Timer3 -> DMA3: zerodata -> PORTB */ /* Initiate DMA on the Timer2 interrupt condition, 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) zerodata), ZERO_LENGTH, + dma_init(1, 3); + dma_set_auto_enable(1, 1); + dma_set_interrupt(1, T2, 1); + dma_set_transfer(1, PHYSICAL((uint32_t) zerodata), ZERO_LENGTH, HW_PHYSICAL(PORTB), 1, ZERO_LENGTH); - /* Enable DMA on the preceding channel's completion, with the Timer3 + /* Enable DMA on the zero channel's completion, with the Timer3 interrupt condition initiating transfers. */ - dma_init(1, 3); - dma_set_chaining(1, dma_chain_previous); - dma_set_interrupt(1, T3, 1); - dma_set_transfer(1, PHYSICAL((uint32_t) screenstart), LINE_LENGTH, + dma_init(0, 3); + dma_set_chaining(0, dma_chain_next); + dma_set_interrupt(0, T3, 1); + dma_set_transfer(0, PHYSICAL((uint32_t) screenstart), LINE_LENGTH / 2, HW_PHYSICAL(PORTB), 1, - 1); + TRANSFER_CELL_SIZE); + + /* Enable DMA on the zero channel's completion, with the Timer3 + interrupt condition initiating transfers. */ + + dma_init(2, 3); + dma_set_chaining(2, dma_chain_previous); + dma_set_interrupt(2, T3, 1); + dma_set_transfer(2, PHYSICAL((uint32_t) screenstart + LINE_LENGTH / 2), LINE_LENGTH / 2, + HW_PHYSICAL(PORTB), 1, + TRANSFER_CELL_SIZE); /* Enable DMA on the preceding channel's completion, with this also initiating transfers. */ - dma_init(2, 3); - dma_set_chaining(2, dma_chain_previous); - dma_set_interrupt(2, T3, 1); - dma_set_transfer(2, PHYSICAL((uint32_t) zerodata), ZERO_LENGTH, + dma_init(3, 3); + dma_set_chaining(3, dma_chain_previous); + dma_set_interrupt(3, T3, 1); + dma_set_transfer(3, PHYSICAL((uint32_t) zerodata), ZERO_LENGTH, HW_PHYSICAL(PORTB), 1, ZERO_LENGTH); - dma_set_receive_events(2, 1); + dma_set_receive_events(3, 1); /* Configure a timer for the horizontal sync. The timer has no prescaling (0). */ @@ -218,11 +229,12 @@ /* Set the line address. */ linedata = screenstart; - dma_set_source(1, PHYSICAL((uint32_t) linedata), LINE_LENGTH); + dma_set_source(0, PHYSICAL((uint32_t) linedata), LINE_LENGTH / 2); + dma_set_source(2, PHYSICAL((uint32_t) linedata + LINE_LENGTH / 2), LINE_LENGTH / 2); - /* Enable the channel for the next line. */ + /* Enable the channels for the next line. */ - dma_on(0); + dma_on(1); } /* Visible region. */ @@ -242,7 +254,8 @@ linedata -= SCREEN_SIZE; } - dma_set_source(1, PHYSICAL((uint32_t) linedata), LINE_LENGTH); + dma_set_source(0, PHYSICAL((uint32_t) linedata), LINE_LENGTH / 2); + dma_set_source(2, PHYSICAL((uint32_t) linedata + LINE_LENGTH / 2), LINE_LENGTH / 2); return; } @@ -250,9 +263,9 @@ state_handler = vfp_active; - /* Disable the channel for the next line. */ + /* Disable the channels for the next line. */ - dma_off(0); + dma_off(1); } /* Vertical front porch region. */ diff -r 24065fd1609f -r 18aa2a60b1ab examples/vga-timer/vga.h --- a/examples/vga-timer/vga.h Tue Oct 23 19:29:55 2018 +0200 +++ b/examples/vga-timer/vga.h Tue Oct 23 21:34:54 2018 +0200 @@ -20,22 +20,23 @@ #ifndef __VGA_H__ #define __VGA_H__ -#define LINE_LENGTH 80 /* pixels */ -#define LINE_COUNT 256 /* distinct display lines */ +#define LINE_LENGTH 92 /* pixels */ +#define LINE_COUNT 128 /* distinct display lines */ +#define LINE_MULTIPLIER 4 #define ZERO_LENGTH 1 /* pixels */ /* 24MHz cycle measurements. */ #define HFREQ_LIMIT 643 -#define HSYNC_START 523 +#define HSYNC_START 504 #define HSYNC_LIMIT 40 #define HSYNC_END (HSYNC_START + HSYNC_LIMIT) /* Horizontal lines, back porch end. */ #define VISIBLE_START 70 -#define VFP_START (VISIBLE_START + 2 * LINE_COUNT) +#define VFP_START (VISIBLE_START + LINE_MULTIPLIER * LINE_COUNT) /* Horizontal lines, front porch end. */ @@ -48,6 +49,10 @@ /* Framebuffer properties. */ #define SCREEN_SIZE (LINE_LENGTH * LINE_COUNT) -#define LINE_MULTIPLIER 2 + +/* Transfer and pixel allocation properties. */ + +#define TRANSFER_CELL_SIZE 1 +#define CELL_SIZE 1 #endif /* __VGA_H__ */ diff -r 24065fd1609f -r 18aa2a60b1ab examples/vga/README.txt --- a/examples/vga/README.txt Tue Oct 23 19:29:55 2018 +0200 +++ b/examples/vga/README.txt Tue Oct 23 21:34:54 2018 +0200 @@ -8,10 +8,13 @@ * 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 and even out the pixel -widths, but this has not been investigated. However, unlike the vga-pmp -example, there is no accompanying signal to potentially orchestrate the -staging of individual pixels at a slightly delayed rate. +It might be possible to introduce some kind of delay and even out the pixel +widths, but this has not been investigated with hardware. However, unlike the +vga-pmp example, there is no accompanying signal to potentially orchestrate +the staging of individual pixels at a slightly delayed rate. + +Attempts to introduce other remedies to the wide pixel problem have been made +with the vga-dual and vga-timer examples. Hardware Details ================ diff -r 24065fd1609f -r 18aa2a60b1ab include/display.h --- a/include/display.h Tue Oct 23 19:29:55 2018 +0200 +++ b/include/display.h Tue Oct 23 21:34:54 2018 +0200 @@ -22,6 +22,7 @@ #include +int get_position(int x); void test_linedata(uint8_t *framebuffer); #endif /* __DISPLAY_H__ */ diff -r 24065fd1609f -r 18aa2a60b1ab lib/display.c --- a/lib/display.c Tue Oct 23 19:29:55 2018 +0200 +++ b/lib/display.c Tue Oct 23 21:34:54 2018 +0200 @@ -25,6 +25,16 @@ +/* Return the line data position for the given pixel. */ + +int get_position(int x) +{ + int cell = x / CELL_SIZE, offset = x % CELL_SIZE; + int pos = (cell / 2) * CELL_SIZE + offset; + + return cell % 2 ? pos + LINE_LENGTH / 2 : pos; +} + /* Provide a pattern to test the line data. */ void test_linedata(uint8_t *framebuffer) @@ -38,11 +48,11 @@ { /* Pixel: I0RRGGBB = Y0YYYYXX */ - linedata[x] = (x % 2) ? - (((y / (LINE_COUNT / 32)) & 0b1) << 7) | - (((y / (LINE_COUNT / 16)) & 0b1111) << 2) | - ((x / (LINE_LENGTH / 4)) & 0b11) : - 0x00; + linedata[get_position(x)] = (x % 2) ? + (((y / (LINE_COUNT / 32)) & 0b1) << 7) | + (((y / (LINE_COUNT / 16)) & 0b1111) << 2) | + ((x / (LINE_LENGTH / 4)) & 0b11) : + 0x00; } linedata += LINE_LENGTH;