This is a version of vga640x480g customized to work well with MbedConsole
Fork of vga640x480g by
vga640x480g.cpp
- Committer:
- earlz
- Date:
- 2012-09-17
- Revision:
- 1:89bf087ed9cd
- Parent:
- vga640x480g.c@ 0:821e34a87609
- Child:
- 2:c9543fb59830
File content as of revision 1:89bf087ed9cd:
/* * 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 }