VGA 640x480 full graphic driver

Dependents:   vga640x480graphic gps_fastlib Chua-VGA Wolfram-1D-VGA ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers vga640x480g.c Source File

vga640x480g.c

00001 /*
00002  * 640x480 60Hz full graphic VGA Driver
00003  *
00004  * Copyright (C) 2011 by Ivo van Poorten <ivop(at)euronet.nl>
00005  * and Gert van der Knokke <gertk(at)xs4all.nl>
00006  * This file is licensed under the terms of the GNU Lesser
00007  * General Public License, version 3.
00008  *
00009  * Inspired by Simon's (not Ford) Cookbook entry and Cliff Biffle's
00010  * assembly code.
00011  */
00012 #include "fastlib/common.h"
00013 #include "fastlib/pinsel.h"
00014 #include "fastlib/gpio.h"
00015 #include "fastlib/clock.h"
00016 #include "fastlib/power.h"
00017 #include "fastlib/pwm.h"
00018 #include "fastlib/i2s.h"
00019 #include "fastlib/uart.h"
00020 #include "fastlib/dma.h"
00021 #include "fastlib/nvic.h"
00022 #include "vga640x480g_functions.h"
00023 
00024 // the default mode 
00025 #define mode640x480 
00026 #undef mode640x400 
00027 
00028 // 640 x 400 @ 70 Hz
00029 #ifdef mode640x400
00030 #define VTOTAL 445
00031 #define VISIBLE 400
00032 #define VSYNCSTART 401
00033 #define VSYNCEND 404
00034 #define VPOLARITY positive
00035 #endif
00036 
00037 // 640 x 480 @ 60 Hz
00038 #ifdef mode640x480
00039 #define VTOTAL 525
00040 #define VISIBLE 480
00041 #define VSYNCSTART 490
00042 #define VSYNCEND 492
00043 #define VPOLARITY negative
00044 #endif
00045 
00046 // -----------------------------------------------------------------------------------
00047 
00048 // force the framebuffer in ABSHRAM0 and ABSHRAM1 (16k + 16k)
00049 // warning!! this disables the use of the USB, Ethernet and CAN bus functions!
00050 unsigned char *framebuffer = (unsigned char *)(0x2007C000);
00051 
00052 // we need some extra space for 80 more lines
00053 #define EXTENSIONMAX 6400
00054 unsigned char framebufferextension[EXTENSIONMAX];
00055 unsigned char *framebuffer2 = framebufferextension;
00056 
00057 // the framepointer
00058 volatile unsigned char *pointer=framebuffer;
00059 
00060 // active line counter
00061 static unsigned line_counter;
00062 
00063 // -----------------------------------------------------------------------------------
00064 // setup CPU PLL
00065 #define FEED0_AND_WAIT(x,y)   fl_pll0_feed(); while(y fl_pll0_get_##x())
00066 
00067 static void init_pll0(unsigned int clock_source, int N, int M, int cpu_divider) {
00068     fl_select_pll0_clock_source(clock_source);
00069 
00070     fl_pll0_control(FL_ENABLE,  FL_DISCONNECT);
00071     FEED0_AND_WAIT(connect,);
00072     fl_pll0_control(FL_DISABLE, FL_DISCONNECT);
00073     FEED0_AND_WAIT(enable,);
00074 
00075     fl_pll0_config(N, M);
00076     fl_pll0_feed();
00077 
00078     fl_pll0_control(FL_ENABLE,  FL_DISCONNECT);
00079     FEED0_AND_WAIT(enable,!);
00080 
00081     fl_set_cpu_clock_divider(cpu_divider);
00082     while (!fl_pll0_get_lock()) ;
00083 
00084     fl_pll0_control(FL_ENABLE,  FL_CONNECT);
00085     FEED0_AND_WAIT(connect,!);
00086 }
00087 
00088 // -----------------------------------------------------------------------------------
00089 // setup UART0
00090 static void init_uart0(unsigned divaddval, unsigned mulval, unsigned divisor) {
00091     fl_power_uart0(FL_ON);
00092     fl_select_clock_uart0(FL_CLOCK_DIV1);
00093     fl_uart_set_fractional_divider(0, divaddval, mulval);
00094     fl_uart_set_divisor_latch(0, divisor);
00095 }
00096 
00097 // -----------------------------------------------------------------------------------
00098 // setup VSYNC output on designated pin
00099 static void init_vsync(unsigned port, unsigned pin) {
00100     fl_power_gpio(FL_ON);
00101     fl_pinsel(port, pin, FL_FUNC_DEFAULT, FL_IGNORE, FL_IGNORE);
00102     fl_gpio_set_direction(port, 1<<pin, FL_OUTPUT);
00103     fl_gpio_clear_value    (port, 1<<pin);
00104 }
00105 
00106 // -----------------------------------------------------------------------------------
00107 // define structure for DMA linked lists
00108 struct dma_lli {
00109     void *source;
00110     void *dest;
00111     struct dma_lli *next;
00112     unsigned control_word;
00113 };
00114 
00115 // some arbitrary blank data for I2S used for blanking
00116 // even after DMA the I2S output will keep on emitting zeroes (= blank)
00117 static unsigned char blanking[32]={0,0,0,0,0,0,0,0,
00118                                    0,0,0,0,0,0,0,0,
00119                                    0,0,0,0,0,0,0,0,
00120                                    0,0,0,0,0,0,0,0
00121                                   };
00122 
00123 // preset our blanking DMA linked list
00124 extern const struct dma_lli blank_lli;
00125 
00126 // blank linked lists ends the DMA cycle (lli=0)
00127 static const struct dma_lli blank_lli = {
00128     blanking, (void*)FL_I2STXFIFO, 0, 4
00129     | (1 << 12)
00130     | (1 << 15)
00131     | (2 << 18)
00132     | (2 << 21)
00133     | (0 << 26)
00134     | (0 << 27)
00135     | (0 << 31)
00136 };
00137 
00138 // setup the DMA controller
00139 static void init_dma_controller(void) {
00140     fl_power_gpdma(FL_ON);
00141     fl_dma_enable(FL_ENABLE);
00142     while (!fl_dma_is_enabled()) ;
00143 
00144     // do some initial DMA setup but no need to start the DMA
00145     fl_dma_set_srcaddr (0, framebuffer);         // initial source pointer
00146     fl_dma_set_destaddr(0, (void*)FL_I2STXFIFO); // destination is I2S
00147     fl_dma_set_next_lli(0, &blank_lli);          // link active list to blank list
00148 
00149     fl_dma_channel_control(0,                    // control word
00150                            20,                   // count (20*4 = 80)
00151                            4, 4,                 // src and dest burst size
00152                            32, 32,               // src and dest width
00153                            FL_SRC_INCREMENT,
00154                            FL_NO_DEST_INCREMENT,
00155                            FL_OFF                // no interrupts
00156                           );
00157 
00158 }
00159 
00160 // -----------------------------------------------------------------------------------
00161 // setup I2S for 25 MHz dot/pixel clock
00162 static void init_i2s(void) {
00163     // I2S on P0.9 (DIP5)
00164     fl_power_i2s(FL_ON);
00165     fl_select_clock_i2s(FL_CLOCK_DIV1);                     // assume 100MHz
00166     fl_pinsel(0, 7, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE);    // I2STX_CLK
00167     fl_pinsel(0, 8, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE);    // I2STX_WS
00168     fl_pinsel(0, 9, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE);    // I2STX_SDA
00169     fl_i2s_set_tx_rate(1, 4);
00170     fl_i2s_output_set_config(FL_8BITS, FL_STEREO, 8, 0, 0, 0, 0);
00171 
00172 }
00173 
00174 // -----------------------------------------------------------------------------------
00175 // create HSYNC output with PWM
00176 static void init_hsync(void) {
00177     // PWM1.2 on P2.1 (DIP25)
00178     fl_power_pwm1(FL_ON);
00179     fl_select_clock_pwm1(FL_CLOCK_DIV1);
00180     fl_pinsel(2, 1, FL_FUNC_ALT1, FL_FLOATING, FL_FLOATING);    // PWM1.2, no pullup/down
00181     fl_pwm_set_prescale(4);         // 100/25 = 4
00182 
00183 #define HSHIFT 0
00184 
00185     // main PWM
00186     fl_pwm_set_match(0, 800);   // 800 color clocks
00187 
00188     // generate line interrupts from PWM MR0
00189     fl_pwm_config_match(0, FL_ON, FL_ON, FL_OFF);   // interrupt, reset, !stop
00190 
00191     // this PWM generates the HSYNC pulse
00192     fl_pwm_set_match(2, 16+HSHIFT);         // go low at 16
00193     fl_pwm_set_match(1, 48+HSHIFT);         // go high at 48
00194     fl_pwm_config_edges(2, FL_DOUBLE_EDGE); // need this for negative sync
00195     fl_pwm_output_enable(2, FL_ENABLE);     // enable this output
00196 
00197 }
00198 
00199 // -----------------------------------------------------------------------------------
00200 // state machine list for the complete screen output
00201 static void state_before_vsync(void);
00202 
00203 static void (*state)(void) = state_before_vsync;
00204 
00205 // emit a line from the visible area (framebuffer)
00206 static void state_visible_area(void) {
00207     extern const struct dma_lli blank_lli;
00208 
00209     // limit visible area to the size of the framebuffer
00210     if (line_counter != (VISIBLE+1)) {
00211         // reset DMA parameters for active line
00212         fl_dma_set_srcaddr (0,(unsigned char *)pointer);  // source is our current framebuffer pointer
00213         fl_dma_set_destaddr(0, (void*)FL_I2STXFIFO);      // destination is I2S
00214         fl_dma_set_next_lli(0, &blank_lli);               // connect to blanking list
00215         fl_dma_channel_control(0,                  // control word
00216                                20,                 // count (20*4 = 80 bytes active)
00217                                4, 4,               // src and dest burst size
00218                                32, 32,             // src and dest width
00219                                FL_SRC_INCREMENT,
00220                                FL_NO_DEST_INCREMENT,
00221                                FL_OFF              // no interrupt on first 640 pixel
00222                               );
00223         // increment framebuffer pointer
00224         pointer+=80;
00225 
00226         // restart DMA sequence
00227         fl_dma_channel_config(0, FL_ENABLE,
00228                               FL_DMA_PERIPHERAL_IS_MEMORY, FL_DMA_BURST_REQUEST_I2S_CH0,
00229                               FL_DMA_MEMORY_TO_PERIPHERAL,
00230                               FL_ON, FL_ON
00231                              );
00232 
00233         if (line_counter==400) pointer=framebuffer2;    // add 80 lines extra
00234     } else   state = state_before_vsync;
00235 }
00236 
00237 
00238 static void state_after_vsync(void) {   // VSYNC is over, now ait for end of frame
00239     if (line_counter != VTOTAL) return; // if not end of frame
00240     line_counter = 0;                   // reset line counter
00241     pointer=framebuffer;                // and framebuffer
00242     state = state_visible_area;         // we're in visible mode
00243 }
00244 
00245 static void state_during_vsync(void) {  // VSYNC is active
00246     if (line_counter != VSYNCEND) return;   // if not end of vsync
00247 #if VPOLARITY==positive                  // check polarity
00248     fl_gpio_clear_value(0, 1<<6);       // vsync low
00249 #else                                   // or
00250     fl_gpio_set_value(0, 1<<6);         // vsync high
00251 #endif
00252     state = state_after_vsync;          // VSYNC is done
00253 }
00254 
00255 static void state_before_vsync(void) {  // wait for VSYNC start
00256     if (line_counter != VSYNCSTART) return; // if not
00257 #if VPOLARITY==positive              // check polarity
00258     fl_gpio_set_value(0, 1<<6);     // vsync high
00259 #else                               // or
00260     fl_gpio_clear_value(0, 1<<6);   // vsync low
00261 #endif
00262     state = state_during_vsync;     // VSYNC has started
00263 }
00264 
00265 // inactive
00266 extern "C" void DMA_IRQHandler(void) __irq {
00267     fl_dma_clear_terminal_count_interrupt_request(0);
00268 
00269 }
00270 
00271 // active
00272 extern "C" void PWM1_IRQHandler(void) __irq {
00273     int regval=*FL_PWM1IR;
00274     // clear interrupt flag
00275     state();
00276     line_counter++;
00277     *FL_PWM1IR=regval;
00278 }
00279 
00280 
00281 // -----------------------------------------------------------------------------------
00282 
00283 void init_vga(void) {
00284     fl_power_off_all_peripherals();
00285 
00286     init_pll0(FL_PLL0_CLOCK_SOURCE_MAIN, 2, 25, 3); // 100MHz
00287     init_uart0(0, 0, 651);                          // 100MHz/651/16=9600.6 (default 8N1)
00288 
00289     init_vsync(0, 6);                               // VSYNC on P0.6 (DIP8)
00290     init_i2s();
00291     init_hsync();
00292     init_dma_controller();
00293 
00294     fl_pwm_timer_counter_enable(FL_ENABLE);
00295     fl_pwm_enable(FL_ENABLE);
00296     fl_i2s_config_dma1(FL_OFF, FL_ON, 0, 2);
00297     fl_nvic_interrupt_set_enable(FL_NVIC_INT_PWM);  // start the PWM interrupts
00298 }