CommonPIC32

lib/vga_display.c

54:908bd0dbb6b2
2018-10-24 Paul Boddie Moved horizontal/vertical sync configuration and functions into library code.
     1 /*     2  * Generate a VGA signal using a PIC32 microcontroller.     3  *     4  * Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk>     5  *     6  * This program is free software: you can redistribute it and/or modify     7  * it under the terms of the GNU General Public License as published by     8  * the Free Software Foundation, either version 3 of the License, or     9  * (at your option) any later version.    10  *    11  * This program is distributed in the hope that it will be useful,    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    14  * GNU General Public License for more details.    15  *    16  * You should have received a copy of the GNU General Public License    17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.    18  */    19     20 #include "pic32_c.h"    21 #include "init.h"    22 #include "vga_display.h"    23     24     25     26 /* Display state. */    27     28 vga_display_t vga_display;    29     30     31     32 /* Initialise the state machine. */    33     34 void init_vga(display_config_t *display_config,    35               void (*start_visible)(),    36               void (*update_visible)(),    37               void (*stop_visible)())    38 {    39     /* Display parameters. */    40     41     vga_display.display_config = display_config;    42     43     /* Display state handlers. */    44     45     vga_display.start_visible = start_visible;    46     vga_display.update_visible = update_visible;    47     vga_display.stop_visible = stop_visible;    48     49     /* Initial state. */    50     51     vga_display.state_handler = vbp_active;    52     vga_display.line = 0;    53 }    54     55 /* Configure a timer and output compare units for horizontal and vertical    56    sync. */    57     58 void vga_configure_sync(int hsync_unit, int vsync_unit, int timer)    59 {    60     /* Record the peripherals in use. */    61     62     vga_display.hsync_unit = hsync_unit;    63     vga_display.vsync_unit = vsync_unit;    64     vga_display.timer = timer;    65     66     /* Configure a timer for the horizontal sync. The timer has no prescaling    67        (0). */    68         69     timer_init(timer, 0, vga_display.display_config->hfreq_limit);    70     timer_on(timer);    71         72     /* Horizontal sync. */    73         74     /* Configure output compare in dual compare (continuous output) mode using    75        the timer as time base. The interrupt condition drives the first DMA    76        channel and is handled to drive the display state machine. */    77     78     oc_init(hsync_unit, 0b101, timer);    79     oc_set_pulse(hsync_unit, vga_display.display_config->hsync_end);    80     oc_set_pulse_end(hsync_unit, vga_display.display_config->hsync_start);    81     oc_init_interrupt(hsync_unit, 7, 3);    82     oc_on(hsync_unit);    83     84     /* Vertical sync. */    85     86     /* Configure output compare in single compare (output driven low) mode using    87        the timer as time base. The unit is enabled later. It is only really used    88        to achieve precisely-timed level transitions in hardware. */    89                             90     oc_init(vsync_unit, 0b010, timer);    91     oc_set_pulse(vsync_unit, 0);    92 }    93     94     95     96 /* Interrupt handlers. */    97     98 void vga_interrupt_handler(void)    99 {   100     vga_display.line += 1;   101     vga_display.state_handler();   102 }   103    104    105    106 /* Vertical back porch region. */   107    108 void vbp_active(void)   109 {   110     if (vga_display.line < vga_display.display_config->visible_start)   111         return;   112    113     /* Enter the visible region. */   114    115     vga_display.state_handler = visible_active;   116    117     /* Set the line address. */   118    119     vga_display.linedata = vga_display.display_config->screen_start;   120     vga_display.start_visible(&vga_display);   121 }   122    123 /* Visible region. */   124    125 void visible_active(void)   126 {   127     if (vga_display.line < vga_display.display_config->vfp_start)   128     {   129         /* Update the line address and handle wraparound. */   130    131         if (!(vga_display.line % vga_display.display_config->line_multiplier))   132         {   133             vga_display.linedata += vga_display.display_config->line_length;   134    135             if (vga_display.linedata >= vga_display.display_config->screen_limit)   136                 vga_display.linedata -= vga_display.display_config->screen_size;   137         }   138    139         vga_display.update_visible(&vga_display);   140         return;   141     }   142    143     /* End the visible region. */   144    145     vga_display.state_handler = vfp_active;   146    147     /* Disable the channel for the next line. */   148    149     vga_display.stop_visible(&vga_display);   150 }   151    152 /* Vertical front porch region. */   153    154 void vfp_active(void)   155 {   156     if (vga_display.line < vga_display.display_config->vsync_start)   157         return;   158    159     /* Enter the vertical sync region. */   160    161     vga_display.state_handler = vsync_active;   162    163     /* Bring vsync low when the next line starts. */   164    165     vsync_low();   166 }   167    168 /* Vertical sync region. */   169    170 void vsync_active(void)   171 {   172     if (vga_display.line < vga_display.display_config->vsync_end)   173         return;   174    175     /* Start again at the top of the display. */   176    177     vga_display.line = 0;   178     vga_display.state_handler = vbp_active;   179    180     /* Bring vsync high when the next line starts. */   181    182     vsync_high();   183 }   184    185    186    187 /* Bring vsync low (single compare, output driven low) when the next line   188    starts. */   189    190 void vsync_low(void)   191 {   192     oc_init(vga_display.vsync_unit, 0b010, vga_display.timer);   193     oc_on(vga_display.vsync_unit);   194 }   195    196 /* Bring vsync high (single compare, output driven high) when the next line   197    starts. */   198    199 void vsync_high(void)   200 {   201     oc_init(vga_display.vsync_unit, 0b001, vga_display.timer);   202     oc_on(vga_display.vsync_unit);   203 }