Full graphic 640x400 monochrome VGA driver library
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Tue Jul 19 2022 20:06:41 by 1.7.2