Full graphic 640x400 monochrome VGA driver library

Dependents:   vga640x400graphic

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers vga640x400g.c Source File

vga640x400g.c

00001 /*
00002  * 640x400 70Hz 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 "vga640x400g_functions.h"
00023 
00024 
00025 // -----------------------------------------------------------------------------------
00026 
00027 // force the framebuffer in ABSHRAM0 and ABSHRAM1 (16k + 16k)
00028 // warning!! this disables the use of the USB, Ethernet and CAN bus functions!
00029 unsigned char *framebuffer = (unsigned char *)(0x2007C000);
00030 
00031 // the framepointer
00032 volatile unsigned char *pointer=framebuffer;
00033 
00034 // active line counter
00035 static unsigned line_counter;
00036 
00037 // -----------------------------------------------------------------------------------
00038 // setup CPU PLL
00039 #define FEED0_AND_WAIT(x,y)   fl_pll0_feed(); while(y fl_pll0_get_##x())
00040 
00041 static void init_pll0(unsigned int clock_source, int N, int M, int cpu_divider) {
00042     fl_select_pll0_clock_source(clock_source);
00043 
00044     fl_pll0_control(FL_ENABLE,  FL_DISCONNECT);
00045     FEED0_AND_WAIT(connect,);
00046     fl_pll0_control(FL_DISABLE, FL_DISCONNECT);
00047     FEED0_AND_WAIT(enable,);
00048 
00049     fl_pll0_config(N, M);
00050     fl_pll0_feed();
00051 
00052     fl_pll0_control(FL_ENABLE,  FL_DISCONNECT);
00053     FEED0_AND_WAIT(enable,!);
00054 
00055     fl_set_cpu_clock_divider(cpu_divider);
00056     while (!fl_pll0_get_lock()) ;
00057 
00058     fl_pll0_control(FL_ENABLE,  FL_CONNECT);
00059     FEED0_AND_WAIT(connect,!);
00060 }
00061 
00062 // -----------------------------------------------------------------------------------
00063 // setup UART0
00064 static void init_uart0(unsigned divaddval, unsigned mulval, unsigned divisor) {
00065     fl_power_uart0(FL_ON);
00066     fl_select_clock_uart0(FL_CLOCK_DIV1);
00067     fl_uart_set_fractional_divider(0, divaddval, mulval);
00068     fl_uart_set_divisor_latch(0, divisor);
00069 }
00070 
00071 // -----------------------------------------------------------------------------------
00072 // setup VSYNC output on designated pin
00073 static void init_vsync(unsigned port, unsigned pin) {
00074     fl_power_gpio(FL_ON);
00075     fl_pinsel(port, pin, FL_FUNC_DEFAULT, FL_IGNORE, FL_IGNORE);
00076     fl_gpio_set_direction(port, 1<<pin, FL_OUTPUT);
00077     fl_gpio_clear_value    (port, 1<<pin);
00078 }
00079 
00080 // -----------------------------------------------------------------------------------
00081 // define structure for DMA linked lists
00082 struct dma_lli {
00083     void *source;
00084     void *dest;
00085     struct dma_lli *next;
00086     unsigned control_word;
00087 };
00088 
00089 // some arbitrary blank data for I2S used for blanking
00090 // even after DMA the I2S output will keep on emitting zeroes (= blank)
00091 static unsigned char blanking[32]={0,0,0,0,0,0,0,0,
00092                                    0,0,0,0,0,0,0,0,
00093                                    0,0,0,0,0,0,0,0,
00094                                    0,0,0,0,0,0,0,0
00095                                   };
00096 
00097 // preset our blanking DMA linked list
00098 extern const struct dma_lli blank_lli;
00099 
00100 // blank linked lists ends the DMA cycle (lli=0)
00101 static const struct dma_lli blank_lli = {
00102     blanking, (void*)FL_I2STXFIFO, 0, 4
00103     | (1 << 12)
00104     | (1 << 15)
00105     | (2 << 18)
00106     | (2 << 21)
00107     | (0 << 26)
00108     | (0 << 27)
00109     | (0 << 31)
00110 };
00111 
00112 // setup the DMA controller
00113 static void init_dma_controller(void) {
00114     fl_power_gpdma(FL_ON);
00115     fl_dma_enable(FL_ENABLE);
00116     while (!fl_dma_is_enabled()) ;
00117 
00118     // do some initial DMA setup but no need to start the DMA
00119     fl_dma_set_srcaddr (0, framebuffer);         // initial source pointer
00120     fl_dma_set_destaddr(0, (void*)FL_I2STXFIFO); // destination is I2S
00121     fl_dma_set_next_lli(0, &blank_lli);          // link active list to blank list
00122 
00123     fl_dma_channel_control(0,                    // control word
00124                            20,                   // count (20*4 = 80)
00125                            4, 4,                 // src and dest burst size
00126                            32, 32,               // src and dest width
00127                            FL_SRC_INCREMENT,
00128                            FL_NO_DEST_INCREMENT,
00129                            FL_OFF                // no interrupts
00130                           );
00131 
00132 }
00133 
00134 // -----------------------------------------------------------------------------------
00135 // setup I2S for 25 MHz dot/pixel clock
00136 static void init_i2s(void) {
00137     // I2S on P0.9 (DIP5)
00138     fl_power_i2s(FL_ON);
00139     fl_select_clock_i2s(FL_CLOCK_DIV1);                     // assume 100MHz
00140     fl_pinsel(0, 7, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE);    // I2STX_CLK
00141     fl_pinsel(0, 8, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE);    // I2STX_WS
00142     fl_pinsel(0, 9, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE);    // I2STX_SDA
00143     fl_i2s_set_tx_rate(1, 4);
00144     fl_i2s_output_set_config(FL_8BITS, FL_STEREO, 8, 0, 0, 0, 0);
00145 
00146 }
00147 
00148 // -----------------------------------------------------------------------------------
00149 // create HSYNC output with PWM
00150 static void init_hsync(void) {
00151     // PWM1.2 on P2.1 (DIP25)
00152     fl_power_pwm1(FL_ON);
00153     fl_select_clock_pwm1(FL_CLOCK_DIV1);
00154     fl_pinsel(2, 1, FL_FUNC_ALT1, FL_FLOATING, FL_FLOATING);    // PWM1.2, no pullup/down
00155     fl_pwm_set_prescale(4);         // 100/25 = 4
00156 
00157 #define HSHIFT 0
00158 
00159     // main PWM
00160     fl_pwm_set_match(0, 800);   // 800 color clocks
00161 
00162     // generate line interrupts from PWM MR0
00163     fl_pwm_config_match(0, FL_ON, FL_ON, FL_OFF);   // interrupt, reset, !stop
00164 
00165     // this PWM generates the HSYNC pulse
00166     fl_pwm_set_match(2, 16+HSHIFT);         // go low at 16
00167     fl_pwm_set_match(1, 48+HSHIFT);         // go high at 48
00168     fl_pwm_config_edges(2, FL_DOUBLE_EDGE); // need this for negative sync
00169     fl_pwm_output_enable(2, FL_ENABLE);     // enable this output
00170 
00171 }
00172 
00173 // -----------------------------------------------------------------------------------
00174 // state machine list for the complete screen output
00175 static void state_before_vsync(void);
00176 
00177 static void (*state)(void) = state_before_vsync;
00178 
00179 static void state_blank_area(void) {
00180     if (line_counter != 449) return;
00181 
00182     line_counter = 0;
00183     pointer=framebuffer;
00184     state = state_before_vsync;
00185 }
00186 
00187 
00188 // emit a line from the visible area (framebuffer)
00189 static void state_visible_area(void) {
00190     extern const struct dma_lli blank_lli;
00191 
00192     if (line_counter != 438) {
00193         // reset DMA parameters for active line
00194         fl_dma_set_srcaddr (0,(unsigned char *)pointer);  // source is our current framebuffer pointer
00195         fl_dma_set_destaddr(0, (void*)FL_I2STXFIFO);      // destination is I2S
00196         fl_dma_set_next_lli(0, &blank_lli);               // connect to blanking list
00197         fl_dma_channel_control(0,                  // control word
00198                                20,                 // count (20*4 = 80 bytes active)
00199                                4, 4,               // src and dest burst size
00200                                32, 32,             // src and dest width
00201                                FL_SRC_INCREMENT,
00202                                FL_NO_DEST_INCREMENT,
00203                                FL_OFF              // no interrupt on first 640 pixel
00204                               );
00205         // restart DMA sequence
00206         fl_dma_channel_config(0, FL_ENABLE,
00207                               FL_DMA_PERIPHERAL_IS_MEMORY, FL_DMA_BURST_REQUEST_I2S_CH0,
00208                               FL_DMA_MEMORY_TO_PERIPHERAL,
00209                               FL_ON, FL_ON
00210                              );
00211         // increment framebuffer pointer
00212         pointer+=80;
00213 
00214     } else   state = state_blank_area;
00215 }
00216 
00217 
00218 static void state_after_vsync(void) {
00219     if (line_counter != 38) return;
00220     state = state_visible_area;
00221 }
00222 
00223 static void state_during_vsync(void) {
00224     if (line_counter != 4) return;
00225 
00226     fl_gpio_clear_value(0, 1<<6);
00227     state = state_after_vsync;
00228 }
00229 
00230 static void state_before_vsync(void) {
00231     if (line_counter != 2) return;
00232 
00233     fl_gpio_set_value(0, 1<<6);
00234     state = state_during_vsync;
00235 }
00236 
00237 // inactive
00238 extern "C" void DMA_IRQHandler(void) __irq {
00239     fl_dma_clear_terminal_count_interrupt_request(0);
00240 
00241 }
00242 
00243 // active
00244 extern "C" void PWM1_IRQHandler(void) __irq {
00245     int regval=*FL_PWM1IR;
00246     // clear interrupt flag
00247     state();
00248     line_counter++;
00249     *FL_PWM1IR=regval;
00250 }
00251 
00252 
00253 // -----------------------------------------------------------------------------------
00254 
00255 void init_vga(void) {
00256     fl_power_off_all_peripherals();
00257 
00258     init_pll0(FL_PLL0_CLOCK_SOURCE_MAIN, 2, 25, 3); // 100MHz
00259     init_uart0(0, 0, 651);                          // 100MHz/651/16=9600.6 (default 8N1)
00260 
00261     init_vsync(0, 6);                               // VSYNC on P0.6 (DIP8)
00262     init_i2s();
00263     init_hsync();
00264     init_dma_controller();
00265 
00266     fl_pwm_timer_counter_enable(FL_ENABLE);
00267     fl_pwm_enable(FL_ENABLE);
00268     fl_i2s_config_dma1(FL_OFF, FL_ON, 0, 2);
00269     fl_nvic_interrupt_set_enable(FL_NVIC_INT_PWM);  // start the PWM interrupts
00270 }