CommonPIC32

examples/vga/main.c

154:c8a37eb47211
2021-12-14 Paul Boddie Fixed pin labels: RB10/PGEC3 should be RB10/PGED2.
     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     21 #include "debug.h"    22 #include "display.h"    23 #include "font.h"    24 #include "image.h"    25 #include "init.h"    26 #include "pic32_c.h"    27 #include "utils.h"    28 #include "viewport.h"    29     30 /* Specific functionality. */    31     32 #include "devconfig.h"    33 #include "main.h"    34 #include "vga_display.h"    35     36     37     38 /* Define the scroll increment depending on how many DMA channels are used.    39    CPU-driven transfers permit an increment of 1. */    40     41 #ifdef TRANSFER_CPU    42 #define SCROLL_XSTEP    1    43 #else    44 #define SCROLL_XSTEP    LINE_CHANNELS    45 #endif    46     47     48     49 /* Define timers if not indicated in the build configuration. */    50     51 #ifndef LINE_TIMER    52 #define LINE_TIMER      2    53 #endif    54     55 #ifndef TRANSFER_TIMER    56 #define TRANSFER_TIMER  0    57 #endif    58     59 /* Define different output ports and pins for parallel mode. */    60     61 #ifndef PARALLEL_MODE    62 #define VGA_OUTPUT      PORTB    63 #define OC1_PIN         RPA0R    64 #define OC2_PIN         RPA1R    65 #define LED_PIN         (1 << 3)    66 #else    67 #define VGA_OUTPUT      PM_REG(0, PMxDIN)    68 #define OC1_PIN         RPB4R    69 #define OC2_PIN         RPB5R    70 #define LED_PIN         (1 << 2)    71 #endif    72     73     74     75 /* Initialise memory for a multi-frame display. */    76     77 static Display(banner_config, LINE_LENGTH, 10, 1);    78 static Display(display_config, LINE_LENGTH, LINE_COUNT - 10, FRAME_COUNT);    79     80     81     82 /* Bundled image and font data. */    83     84 extern image_t screendata;    85 extern image_t sprite;    86     87 static SpriteOverwriting(scr, &screendata, &display_config, FRAME_COUNT, -1);    88     89 extern uint8_t fontchars[];    90 extern font_range_t fontbase;    91     92 static Font(font_config, fontchars, &fontbase);    93     94     95     96 /* Plot the revealed region at the edge of the screen after scrolling. */    97     98 static void plot_screen_edge(int xorigin, int yorigin, int xstep, int ystep)    99 {   100     image_update_scrolled_tiled_image(&scr, xorigin, yorigin, xstep, ystep);   101 }   102    103    104    105 /* Update the sprite position. */   106    107 static void update_sprite_position(sprite_t *s, int *xdir, int *ydir)   108 {   109     position_t *last = image_get_stored_position(s, wrap_value(display_config.frame - 1, display_config.frames));   110     position_t *next = image_get_sprite_position(s);   111    112     if (((*xdir > 0) && (last->x < display_config.line_length - s->image->width - *xdir)) ||   113         ((*xdir < 0) && (last->x > -*xdir)))   114     {   115         next->x = last->x + *xdir;   116     }   117     else   118     {   119         next->x = last->x;   120         if (*xdir)   121             *xdir = -*xdir;   122     }   123    124     if (((*ydir > 0) && (last->y < display_config.line_count - s->image->height - *ydir)) ||   125         ((*ydir < 0) && (last->y > -*ydir)))   126     {   127         next->y = last->y + *ydir;   128     }   129     else   130     {   131         next->y = last->y;   132         if (*ydir)   133             *ydir = -*ydir;   134     }   135 }   136    137 /* Move a sprite around on the framebuffer. */   138    139 static void animate(uint32_t delay)   140 {   141     /* Stores of background details, replotted when moving the sprite. */   142    143     Sprite(s1, &sprite, &display_config, FRAME_COUNT, 0x0c);   144     Sprite(s2, &sprite, &display_config, FRAME_COUNT, 0x0c);   145     Sprite(s3, &sprite, &display_config, FRAME_COUNT, 0x0c);   146    147     /* Scrolling viewport. */   148    149     Viewport(v, &display_config, SCROLL_XSTEP, 1, plot_screen_edge);   150    151     /* Reset the viewport to a well-defined state. */   152    153     viewport_set_origin(&v, 0, 0);   154    155     /* Sprite and scrolling directions. */   156    157     int xdir[] = {1, -1, 0}, ydir[] = {1, 0, -1};   158    159     /* Sprite positions. */   160    161     image_set_sprite_position(&s1, 0, 0);   162     image_set_sprite_position(&s2, display_config.line_length - s2.image->width, 0);   163     image_set_sprite_position(&s3, 0, display_config.line_count - s3.image->height);   164    165     /* Animation loop. */   166    167     while (1)   168     {   169         image_plot_sprite(&s1);   170         image_plot_sprite(&s2);   171         image_plot_sprite(&s3);   172    173         /* Update the display with the frame details. */   174    175         vga_set_frame(&display_config, 1);   176         wait(delay);   177    178         /* Select the next frame to plot to. */   179    180         display_select_next_frame(&display_config);   181    182         /* Prepare the frame for updates. */   183    184         viewport_unplot_sprite_from_image(&v, &s1, &scr);   185         viewport_unplot_sprite_from_image(&v, &s2, &scr);   186         viewport_unplot_sprite_from_image(&v, &s3, &scr);   187    188         /* Update the origin to scroll the screen. */   189    190         /* Due to the effect of a simple screen start increment in the   191            dual channel configuration, horizontal scrolling involves two   192            pixel increments and thus requires a two-pixel column to be   193            plotted per scrolling increment. */   194    195         /* For vertically-scaled backgrounds, the full resolution image   196            is traversed by multiples of the scrolling increment. */   197    198         viewport_update_origin(&v, xdir[0] * SCROLL_XSTEP,   199                                    ydir[0]);   200    201         /* Update the position of the sprite being plotted. */   202    203         update_sprite_position(&s1, &xdir[0], &ydir[0]);   204         update_sprite_position(&s2, &xdir[1], &ydir[1]);   205         update_sprite_position(&s3, &xdir[2], &ydir[2]);   206     }   207 }   208    209 /* Set up a banner. */   210    211 static void setup_banner(void)   212 {   213     int frame, x = 0, y = 0;   214    215     for (frame = 0; frame < banner_config.frames; frame++)   216     {   217         /* Obtain the frame. */   218    219         display_select_frame(&banner_config, frame);   220    221         write_string(&banner_config, &font_config, "VGA display example", &x, &y, 0xff);   222     }   223    224     display_select_frame(&banner_config, 0);   225 }   226    227 /* Set up a background. */   228    229 static void setup_background(void)   230 {   231     int frame;   232    233     for (frame = 0; frame < display_config.frames; frame++)   234     {   235         /* Obtain the frame. */   236    237         display_select_frame(&display_config, frame);   238    239         /* Tile the image on the screen. */   240    241         image_tile_sprite(&scr, 0, 0, display_config.line_length,   242                                       display_config.line_count,   243                                       0, 0);   244     }   245    246     display_select_frame(&display_config, 0);   247 }   248    249 static void setup(void)   250 {   251     setup_banner();   252     setup_background();   253 }   254    255    256    257 /* Main program. */   258    259 void main(void)   260 {   261     init_memory();   262     init_pins();   263     init_outputs();   264    265     unlock_config();   266     config_oc();   267     config_uart();   268     lock_config();   269    270 #ifndef TRANSFER_CPU   271     init_dma();   272 #endif   273    274 #ifdef PARALLEL_MODE   275     init_pm();   276    277     /* Configure parallel master mode. */   278    279     pm_init(0, 0b10);   280     pm_set_output(0, 1, 0);   281     pm_on(0);   282 #endif   283    284     uart_init(1, FPB, 115200);   285     uart_on(1);   286    287     /* Set the display to a well-defined state. */   288    289     init_display(&banner_config);   290     init_display(&display_config);   291    292     /* Initialise VGA output with one or two line channels, configuring a line   293        timer and any transfer timer, with an initiating channel being introduced   294        if a transfer timer is specified. */   295    296     init_vga_with_timers(&banner_config, LINE_CHANNELS, LINE_TIMER, TRANSFER_TIMER);   297     vga_add_window(&display_config);   298    299     /* Configure VGA output transfer to the output register, also configuring   300        output compare units for horizontal and vertical sync. */   301    302     vga_configure_transfer(VGA_OUTPUT);   303     vga_configure_sync(1, 2);   304    305     interrupts_on();   306    307     /* Move a sprite around on the screen with a delay between each movement. */   308    309     setup();   310     animate(1);   311 }   312    313    314    315 /* Exception and interrupt handlers. */   316    317 void exception_handler(void)   318 {   319     blink(1 << 22, PORTA, LED_PIN);   320 }   321    322 void interrupt_handler(void)   323 {   324     vga_interrupt_handler();   325 }   326    327    328    329 /* Peripheral pin configuration. */   330    331 void config_oc(void)   332 {   333     /* Map OC1. */   334    335     REG(OC1_PIN) = 0b0101;   336    337     /* Map OC2. */   338    339     REG(OC2_PIN) = 0b0101;   340 }   341    342 void config_uart(void)   343 {   344     /* Map U1RX to RPB13. */   345    346     REG(U1RXR) = 0b0011;        /* U1RXR<3:0> = 0011 (RPB13) */   347    348     /* Map U1TX to RPB15. */   349    350     REG(RPB15R) = 0b0001;       /* RPB15R<3:0> = 0001 (U1TX) */   351    352     /* Set RPB13 to input. */   353    354     SET_REG(TRISB, 1 << 13);   355 }