Three-pin 640x400 VGA Console Mode

Dependents:   projet_AWA_testVGA2

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers vga640x400.c Source File

vga640x400.c

00001 /*
00002  * 640x400 70Hz VGA Driver
00003  *
00004  * Copyright (C) 2011 by Ivo van Poorten <ivop@euronet.nl>
00005  * This file is licensed under the terms of the GNU Lesser
00006  * General Public License, version 3.
00007  *
00008  * Inspired by Simon's (not Ford) Cookbook entry and Cliff Biffle's
00009  * assembly code.
00010  */
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 
00023 //#include "mbed.h"
00024 
00025 #include <string.h>
00026 
00027 #define DEBUG_VGA 0
00028 
00029 #if DEBUG_VGA       // DEBUG messages on UART0
00030 #include <stdio.h>
00031 #define dbprintf(...) printf(__VA_ARGS__)
00032 #else
00033 #define dbprintf(...)
00034 #endif
00035  
00036 // -----------------------------------------------------------------------------------
00037 
00038 unsigned char text_buffer[80*25];
00039 unsigned char *font;
00040 
00041 static unsigned line_counter;
00042 static unsigned char scanline0[100], scanline1[100];    // 100*8=800 color clocks
00043 static unsigned char *curline = scanline1+20;
00044 
00045 #define NCHARS 256
00046 
00047 struct dma_lli {
00048     const void *source;
00049     const void *dest;
00050     const struct dma_lli *next;
00051     unsigned control_word;
00052 };
00053 
00054 extern const struct dma_lli dma_lli1;
00055 
00056 static const struct dma_lli dma_lli0 = {
00057     scanline0, (void*)FL_I2STXFIFO, &dma_lli1, 0x84489019 // control word is explained below
00058 };
00059 static const struct dma_lli dma_lli1 = {
00060     scanline1, (void*)FL_I2STXFIFO, &dma_lli0, 0x84489019
00061 };
00062 
00063 // -----------------------------------------------------------------------------------
00064 
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); FEED0_AND_WAIT(connect,);
00071     fl_pll0_control(FL_DISABLE, FL_DISCONNECT); FEED0_AND_WAIT(enable,);
00072     
00073     fl_pll0_config(N, M);
00074     fl_pll0_feed();
00075     
00076     fl_pll0_control(FL_ENABLE,  FL_DISCONNECT); FEED0_AND_WAIT(enable,!);
00077     
00078     fl_set_cpu_clock_divider(cpu_divider);
00079     while(!fl_pll0_get_lock()) ;
00080     
00081     fl_pll0_control(FL_ENABLE,  FL_CONNECT);    FEED0_AND_WAIT(connect,!);
00082 }   
00083 
00084 // -----------------------------------------------------------------------------------
00085 
00086 #if DEBUG_VGA
00087 static void init_uart0(unsigned divaddval, unsigned mulval, unsigned divisor) {
00088     fl_power_uart0(FL_ON);
00089     fl_select_clock_uart0(FL_CLOCK_DIV1);
00090     fl_uart_set_fractional_divider(0, divaddval, mulval);
00091     fl_uart_set_divisor_latch(0, divisor);
00092 }
00093 #endif
00094 
00095 // -----------------------------------------------------------------------------------
00096 
00097 static void init_vsync(unsigned port, unsigned pin) {
00098     fl_power_gpio(FL_ON);
00099     fl_pinsel(port, pin, FL_FUNC_DEFAULT, FL_IGNORE, FL_IGNORE);
00100     fl_gpio_set_direction(port, 1<<pin, FL_OUTPUT);
00101     fl_gpio_clear_value    (port, 1<<pin);
00102 }
00103 
00104 // -----------------------------------------------------------------------------------
00105 
00106 static void init_dma_controller(void) {
00107     fl_power_gpdma(FL_ON);
00108     fl_dma_enable(FL_ENABLE);
00109     while (!fl_dma_is_enabled()) ;
00110 
00111     fl_dma_set_srcaddr (0, dma_lli0.source);
00112     fl_dma_set_destaddr(0, dma_lli0.dest);
00113     fl_dma_set_next_lli(0, dma_lli0.next);
00114     fl_dma_channel_control(0,       // control word
00115         25,
00116         4, 4,               // src and dest burst size
00117         32, 32,             // src and dest width
00118         FL_SRC_INCREMENT,
00119         FL_NO_DEST_INCREMENT,
00120         FL_ON               // terminal count interrupt
00121     );
00122 //    if ((fl_dma_channel_get_control_mask(0) | 25) != dma_lli0.control_word) {
00123 //        dbprintf("%08x and %08x\r\n", fl_dma_channel_get_control_mask(0) | 25, dma_lli0.control_word);
00124 //        dbprintf("control_word mismatch\r\n");
00125 //        while(1);
00126 //    }
00127 
00128     fl_dma_channel_config(0, FL_ENABLE,
00129         FL_DMA_PERIPHERAL_IS_MEMORY, FL_DMA_BURST_REQUEST_I2S_CH0,
00130         FL_DMA_MEMORY_TO_PERIPHERAL,
00131         FL_ON, FL_ON
00132     );
00133     
00134     fl_nvic_interrupt_set_enable(FL_NVIC_INT_DMA);
00135 }
00136 
00137 // -----------------------------------------------------------------------------------
00138 
00139 static void init_i2s(void) {
00140     // I2S on P0.9 (DIP5)
00141     fl_power_i2s(FL_ON);
00142     fl_select_clock_i2s(FL_CLOCK_DIV1);                     // assume 100MHz
00143  //   fl_pinsel(0, 7, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE);    // I2STX_CLK
00144  //   fl_pinsel(0, 8, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE);    // I2STX_WS
00145     fl_pinsel(0, 9, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE);    // I2STX_SDA
00146     fl_i2s_set_tx_rate(1, 4);
00147     fl_i2s_output_set_config(FL_8BITS, FL_STEREO, 8, 0, 0, 0, 0);
00148 }
00149 
00150 // -----------------------------------------------------------------------------------
00151 
00152 static void init_hsync(void) {
00153     // PWM1.2 on P2.1 (DIP25)
00154     fl_power_pwm1(FL_ON);
00155     fl_select_clock_pwm1(FL_CLOCK_DIV1);
00156     fl_pinsel(2, 1, FL_FUNC_ALT1, FL_FLOATING, FL_FLOATING);    // PWM1.2, no pullup/down
00157     fl_pwm_set_prescale(4);         // 100/25 = 4
00158     fl_pwm_config_match(0, FL_ON, FL_ON, FL_OFF);   // interrupt, reset, !stop
00159     fl_pwm_set_match(0, 800);   // 800 color clocks
00160   
00161 #define HSHIFT 48
00162  
00163     fl_pwm_set_match(1, 97+HSHIFT);    // go high at 97
00164     fl_pwm_set_match(2, 1+HSHIFT);     // go low at 1
00165     fl_pwm_config_edges(2, FL_DOUBLE_EDGE);
00166     fl_pwm_output_enable(2, FL_ENABLE);
00167 }
00168 
00169 // -----------------------------------------------------------------------------------
00170 
00171 static void state_before_vsync(void);
00172 
00173 static void (*state)(void) = state_before_vsync;
00174 
00175 static void state_blank_area(void) {
00176     if (line_counter != 449) return;
00177     
00178     line_counter = 0;
00179     state = state_before_vsync;
00180 }
00181 
00182 static void state_clearing_buffers(void) {
00183     int i;
00184     
00185     if (line_counter < 441) return;
00186 
00187     for (i=0; i<20; i++) {
00188         *(curline+i   ) = 0;
00189         *(curline+i+20) = 0;
00190         *(curline+i+40) = 0;
00191         *(curline+i+60) = 0;
00192     }
00193         
00194     if (line_counter == 442) {
00195         state = state_blank_area;
00196     }
00197 }
00198 
00199 static unsigned char *fp, *tb;
00200 
00201 static void state_visible_area(void) {
00202     int x;
00203     unsigned char *sp = curline, *tbp = tb;
00204 
00205     for (x=0; x<20; x++) {
00206         *sp++ = *(fp + *tbp++);
00207         *sp++ = *(fp + *tbp++);
00208         *sp++ = *(fp + *tbp++);
00209         *sp++ = *(fp + *tbp++);
00210     }
00211 
00212     fp += NCHARS;
00213     if (fp == font+NCHARS*16) {
00214         fp = font;
00215         tb += 80;
00216     }
00217 
00218     if (line_counter == 438) {
00219         state = state_clearing_buffers;
00220     }
00221 }
00222 
00223 static void state_after_vsync(void) {
00224     if (line_counter != 38) return;
00225     
00226     fp = font;
00227     tb = text_buffer;
00228     state = state_visible_area;
00229 }
00230 
00231 static void state_during_vsync(void) {
00232     if (line_counter != 4) return;
00233 
00234     fl_gpio_clear_value(0, 1<<6);    
00235     state = state_after_vsync;
00236 }
00237 
00238 static void state_before_vsync(void) {
00239     if (line_counter != 2) return;
00240 
00241     fl_gpio_set_value(0, 1<<6);    
00242     state = state_during_vsync;
00243 }
00244 
00245 extern "C" void DMA_IRQHandler(void) __irq {
00246     fl_dma_clear_terminal_count_interrupt_request(0);
00247     line_counter++;
00248     curline = curline == scanline0+20 ? scanline1+20 : scanline0+20;
00249     state();
00250 }
00251 
00252 // -----------------------------------------------------------------------------------
00253 
00254 void init_vga(void) {
00255     fl_power_off_all_peripherals();
00256 
00257     init_pll0(FL_PLL0_CLOCK_SOURCE_MAIN, 2, 25, 3); // 100MHz
00258 
00259 #if DEBUG_VGA
00260     init_uart0(0, 0, 651);                          // 100MHz/651/16=9600.6 (default 8N1)
00261 #endif
00262 
00263     init_vsync(0, 6);                               // VSYNC on P0.6 (DIP8)
00264     init_i2s();
00265     init_hsync();
00266     init_dma_controller();
00267  
00268     fl_pwm_timer_counter_enable(FL_ENABLE);
00269     fl_pwm_enable(FL_ENABLE);
00270     fl_i2s_config_dma1(FL_OFF, FL_ON, 0, 2);
00271 }