VGA 640x480 full graphic driver
Dependents: vga640x480graphic gps_fastlib Chua-VGA Wolfram-1D-VGA ... more
Diff: vga640x480g.c
- Revision:
- 0:821e34a87609
diff -r 000000000000 -r 821e34a87609 vga640x480g.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vga640x480g.c Wed Jul 27 19:43:07 2011 +0000 @@ -0,0 +1,298 @@ +/* + * 640x480 60Hz full graphic VGA Driver + * + * Copyright (C) 2011 by Ivo van Poorten <ivop(at)euronet.nl> + * and Gert van der Knokke <gertk(at)xs4all.nl> + * This file is licensed under the terms of the GNU Lesser + * General Public License, version 3. + * + * Inspired by Simon's (not Ford) Cookbook entry and Cliff Biffle's + * assembly code. + */ +#include "fastlib/common.h" +#include "fastlib/pinsel.h" +#include "fastlib/gpio.h" +#include "fastlib/clock.h" +#include "fastlib/power.h" +#include "fastlib/pwm.h" +#include "fastlib/i2s.h" +#include "fastlib/uart.h" +#include "fastlib/dma.h" +#include "fastlib/nvic.h" +#include "vga640x480g_functions.h" + +// the default mode +#define mode640x480 +#undef mode640x400 + +// 640 x 400 @ 70 Hz +#ifdef mode640x400 +#define VTOTAL 445 +#define VISIBLE 400 +#define VSYNCSTART 401 +#define VSYNCEND 404 +#define VPOLARITY positive +#endif + +// 640 x 480 @ 60 Hz +#ifdef mode640x480 +#define VTOTAL 525 +#define VISIBLE 480 +#define VSYNCSTART 490 +#define VSYNCEND 492 +#define VPOLARITY negative +#endif + +// ----------------------------------------------------------------------------------- + +// force the framebuffer in ABSHRAM0 and ABSHRAM1 (16k + 16k) +// warning!! this disables the use of the USB, Ethernet and CAN bus functions! +unsigned char *framebuffer = (unsigned char *)(0x2007C000); + +// we need some extra space for 80 more lines +#define EXTENSIONMAX 6400 +unsigned char framebufferextension[EXTENSIONMAX]; +unsigned char *framebuffer2 = framebufferextension; + +// the framepointer +volatile unsigned char *pointer=framebuffer; + +// active line counter +static unsigned line_counter; + +// ----------------------------------------------------------------------------------- +// setup CPU PLL +#define FEED0_AND_WAIT(x,y) fl_pll0_feed(); while(y fl_pll0_get_##x()) + +static void init_pll0(unsigned int clock_source, int N, int M, int cpu_divider) { + fl_select_pll0_clock_source(clock_source); + + fl_pll0_control(FL_ENABLE, FL_DISCONNECT); + FEED0_AND_WAIT(connect,); + fl_pll0_control(FL_DISABLE, FL_DISCONNECT); + FEED0_AND_WAIT(enable,); + + fl_pll0_config(N, M); + fl_pll0_feed(); + + fl_pll0_control(FL_ENABLE, FL_DISCONNECT); + FEED0_AND_WAIT(enable,!); + + fl_set_cpu_clock_divider(cpu_divider); + while (!fl_pll0_get_lock()) ; + + fl_pll0_control(FL_ENABLE, FL_CONNECT); + FEED0_AND_WAIT(connect,!); +} + +// ----------------------------------------------------------------------------------- +// setup UART0 +static void init_uart0(unsigned divaddval, unsigned mulval, unsigned divisor) { + fl_power_uart0(FL_ON); + fl_select_clock_uart0(FL_CLOCK_DIV1); + fl_uart_set_fractional_divider(0, divaddval, mulval); + fl_uart_set_divisor_latch(0, divisor); +} + +// ----------------------------------------------------------------------------------- +// setup VSYNC output on designated pin +static void init_vsync(unsigned port, unsigned pin) { + fl_power_gpio(FL_ON); + fl_pinsel(port, pin, FL_FUNC_DEFAULT, FL_IGNORE, FL_IGNORE); + fl_gpio_set_direction(port, 1<<pin, FL_OUTPUT); + fl_gpio_clear_value (port, 1<<pin); +} + +// ----------------------------------------------------------------------------------- +// define structure for DMA linked lists +struct dma_lli { + void *source; + void *dest; + struct dma_lli *next; + unsigned control_word; +}; + +// some arbitrary blank data for I2S used for blanking +// even after DMA the I2S output will keep on emitting zeroes (= blank) +static unsigned char blanking[32]={0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 + }; + +// preset our blanking DMA linked list +extern const struct dma_lli blank_lli; + +// blank linked lists ends the DMA cycle (lli=0) +static const struct dma_lli blank_lli = { + blanking, (void*)FL_I2STXFIFO, 0, 4 + | (1 << 12) + | (1 << 15) + | (2 << 18) + | (2 << 21) + | (0 << 26) + | (0 << 27) + | (0 << 31) +}; + +// setup the DMA controller +static void init_dma_controller(void) { + fl_power_gpdma(FL_ON); + fl_dma_enable(FL_ENABLE); + while (!fl_dma_is_enabled()) ; + + // do some initial DMA setup but no need to start the DMA + fl_dma_set_srcaddr (0, framebuffer); // initial source pointer + fl_dma_set_destaddr(0, (void*)FL_I2STXFIFO); // destination is I2S + fl_dma_set_next_lli(0, &blank_lli); // link active list to blank list + + fl_dma_channel_control(0, // control word + 20, // count (20*4 = 80) + 4, 4, // src and dest burst size + 32, 32, // src and dest width + FL_SRC_INCREMENT, + FL_NO_DEST_INCREMENT, + FL_OFF // no interrupts + ); + +} + +// ----------------------------------------------------------------------------------- +// setup I2S for 25 MHz dot/pixel clock +static void init_i2s(void) { + // I2S on P0.9 (DIP5) + fl_power_i2s(FL_ON); + fl_select_clock_i2s(FL_CLOCK_DIV1); // assume 100MHz + fl_pinsel(0, 7, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE); // I2STX_CLK + fl_pinsel(0, 8, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE); // I2STX_WS + fl_pinsel(0, 9, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE); // I2STX_SDA + fl_i2s_set_tx_rate(1, 4); + fl_i2s_output_set_config(FL_8BITS, FL_STEREO, 8, 0, 0, 0, 0); + +} + +// ----------------------------------------------------------------------------------- +// create HSYNC output with PWM +static void init_hsync(void) { + // PWM1.2 on P2.1 (DIP25) + fl_power_pwm1(FL_ON); + fl_select_clock_pwm1(FL_CLOCK_DIV1); + fl_pinsel(2, 1, FL_FUNC_ALT1, FL_FLOATING, FL_FLOATING); // PWM1.2, no pullup/down + fl_pwm_set_prescale(4); // 100/25 = 4 + +#define HSHIFT 0 + + // main PWM + fl_pwm_set_match(0, 800); // 800 color clocks + + // generate line interrupts from PWM MR0 + fl_pwm_config_match(0, FL_ON, FL_ON, FL_OFF); // interrupt, reset, !stop + + // this PWM generates the HSYNC pulse + fl_pwm_set_match(2, 16+HSHIFT); // go low at 16 + fl_pwm_set_match(1, 48+HSHIFT); // go high at 48 + fl_pwm_config_edges(2, FL_DOUBLE_EDGE); // need this for negative sync + fl_pwm_output_enable(2, FL_ENABLE); // enable this output + +} + +// ----------------------------------------------------------------------------------- +// state machine list for the complete screen output +static void state_before_vsync(void); + +static void (*state)(void) = state_before_vsync; + +// emit a line from the visible area (framebuffer) +static void state_visible_area(void) { + extern const struct dma_lli blank_lli; + + // limit visible area to the size of the framebuffer + if (line_counter != (VISIBLE+1)) { + // reset DMA parameters for active line + fl_dma_set_srcaddr (0,(unsigned char *)pointer); // source is our current framebuffer pointer + fl_dma_set_destaddr(0, (void*)FL_I2STXFIFO); // destination is I2S + fl_dma_set_next_lli(0, &blank_lli); // connect to blanking list + fl_dma_channel_control(0, // control word + 20, // count (20*4 = 80 bytes active) + 4, 4, // src and dest burst size + 32, 32, // src and dest width + FL_SRC_INCREMENT, + FL_NO_DEST_INCREMENT, + FL_OFF // no interrupt on first 640 pixel + ); + // increment framebuffer pointer + pointer+=80; + + // restart DMA sequence + fl_dma_channel_config(0, FL_ENABLE, + FL_DMA_PERIPHERAL_IS_MEMORY, FL_DMA_BURST_REQUEST_I2S_CH0, + FL_DMA_MEMORY_TO_PERIPHERAL, + FL_ON, FL_ON + ); + + if (line_counter==400) pointer=framebuffer2; // add 80 lines extra + } else state = state_before_vsync; +} + + +static void state_after_vsync(void) { // VSYNC is over, now ait for end of frame + if (line_counter != VTOTAL) return; // if not end of frame + line_counter = 0; // reset line counter + pointer=framebuffer; // and framebuffer + state = state_visible_area; // we're in visible mode +} + +static void state_during_vsync(void) { // VSYNC is active + if (line_counter != VSYNCEND) return; // if not end of vsync +#if VPOLARITY==positive // check polarity + fl_gpio_clear_value(0, 1<<6); // vsync low +#else // or + fl_gpio_set_value(0, 1<<6); // vsync high +#endif + state = state_after_vsync; // VSYNC is done +} + +static void state_before_vsync(void) { // wait for VSYNC start + if (line_counter != VSYNCSTART) return; // if not +#if VPOLARITY==positive // check polarity + fl_gpio_set_value(0, 1<<6); // vsync high +#else // or + fl_gpio_clear_value(0, 1<<6); // vsync low +#endif + state = state_during_vsync; // VSYNC has started +} + +// inactive +extern "C" void DMA_IRQHandler(void) __irq { + fl_dma_clear_terminal_count_interrupt_request(0); + +} + +// active +extern "C" void PWM1_IRQHandler(void) __irq { + int regval=*FL_PWM1IR; + // clear interrupt flag + state(); + line_counter++; + *FL_PWM1IR=regval; +} + + +// ----------------------------------------------------------------------------------- + +void init_vga(void) { + fl_power_off_all_peripherals(); + + init_pll0(FL_PLL0_CLOCK_SOURCE_MAIN, 2, 25, 3); // 100MHz + init_uart0(0, 0, 651); // 100MHz/651/16=9600.6 (default 8N1) + + init_vsync(0, 6); // VSYNC on P0.6 (DIP8) + init_i2s(); + init_hsync(); + init_dma_controller(); + + fl_pwm_timer_counter_enable(FL_ENABLE); + fl_pwm_enable(FL_ENABLE); + fl_i2s_config_dma1(FL_OFF, FL_ON, 0, 2); + fl_nvic_interrupt_set_enable(FL_NVIC_INT_PWM); // start the PWM interrupts +}