blabla
Diff: vga640x400.c
- Revision:
- 0:78fa88bb24cb
- Child:
- 1:30d7a3e8e7a3
- Child:
- 8:6e362b7be26b
diff -r 000000000000 -r 78fa88bb24cb vga640x400.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vga640x400.c Sun Jul 03 18:13:20 2011 +0000 @@ -0,0 +1,264 @@ +/* + * 640x400 70Hz VGA Driver + * + * Copyright (C) 2011 by Ivo van Poorten <ivop@euronet.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 "mbed.h" + +#include <string.h> + +#if 0 // DEBUG messages on UART0 +#include <stdio.h> +#define dbprintf(...) printf(__VA_ARGS__) +#else +#define dbprintf(...) +#endif + +// ----------------------------------------------------------------------------------- + +unsigned char text_buffer[80*25]; +unsigned char *font; + +static unsigned line_counter; +static unsigned char scanline0[100], scanline1[100]; // 100*8=800 color clocks +static unsigned char *curline = scanline1+20; + +#define NCHARS 256 + +struct dma_lli { + const void *source; + const void *dest; + const struct dma_lli *next; + unsigned control_word; +}; + +extern const struct dma_lli dma_lli1; + +static const struct dma_lli dma_lli0 = { + scanline0, (void*)FL_I2STXFIFO, &dma_lli1, 0x84489019 // control word is explained below +}; +static const struct dma_lli dma_lli1 = { + scanline1, (void*)FL_I2STXFIFO, &dma_lli0, 0x84489019 +}; + +// ----------------------------------------------------------------------------------- + +#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,!); +} + +// ----------------------------------------------------------------------------------- + +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); +} + +// ----------------------------------------------------------------------------------- + +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); +} + +// ----------------------------------------------------------------------------------- + +static void init_dma_controller(void) { + fl_power_gpdma(FL_ON); + fl_dma_enable(FL_ENABLE); + while (!fl_dma_is_enabled()) ; + + fl_dma_set_srcaddr (0, dma_lli0.source); + fl_dma_set_destaddr(0, dma_lli0.dest); + fl_dma_set_next_lli(0, dma_lli0.next); + fl_dma_channel_control(0, // control word + 25, + 4, 4, // src and dest burst size + 32, 32, // src and dest width + FL_SRC_INCREMENT, + FL_NO_DEST_INCREMENT, + FL_ON // terminal count interrupt + ); +// if ((fl_dma_channel_get_control_mask(0) | 25) != dma_lli0.control_word) { +// dbprintf("%08x and %08x\r\n", fl_dma_channel_get_control_mask(0) | 25, dma_lli0.control_word); +// dbprintf("control_word mismatch\r\n"); +// while(1); +// } + + 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 + ); + + fl_nvic_interrupt_set_enable(FL_NVIC_INT_DMA); +} + +// ----------------------------------------------------------------------------------- + +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); +} + +// ----------------------------------------------------------------------------------- + +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 + fl_pwm_config_match(0, FL_ON, FL_ON, FL_OFF); // interrupt, reset, !stop + fl_pwm_set_match(0, 800); // 800 color clocks + +#define HSHIFT 48 + + fl_pwm_set_match(1, 97+HSHIFT); // go high at 97 + fl_pwm_set_match(2, 1+HSHIFT); // go low at 1 + fl_pwm_config_edges(2, FL_DOUBLE_EDGE); + fl_pwm_output_enable(2, FL_ENABLE); +} + +// ----------------------------------------------------------------------------------- + +static void state_before_vsync(void); + +static void (*state)(void) = state_before_vsync; + +static void state_blank_area(void) { + if (line_counter != 449) return; + + line_counter = 0; + state = state_before_vsync; +} + +static void state_clearing_buffers(void) { + int i; + + if (line_counter < 441) return; + + for (i=0; i<20; i++) { + *(curline+i ) = 0; + *(curline+i+20) = 0; + *(curline+i+40) = 0; + *(curline+i+60) = 0; + } + + if (line_counter == 442) { + state = state_blank_area; + } +} + +static unsigned char *fp, *tb; + +static void state_visible_area(void) { + int x; + unsigned char *sp = curline, *tbp = tb; + + for (x=0; x<20; x++) { + *sp++ = *(fp + *tbp++); + *sp++ = *(fp + *tbp++); + *sp++ = *(fp + *tbp++); + *sp++ = *(fp + *tbp++); + } + + fp += NCHARS; + if (fp == font+NCHARS*16) { + fp = font; + tb += 80; + } + + if (line_counter == 438) { + state = state_clearing_buffers; + } +} + +static void state_after_vsync(void) { + if (line_counter != 38) return; + + fp = font; + tb = text_buffer; + state = state_visible_area; +} + +static void state_during_vsync(void) { + if (line_counter != 4) return; + + fl_gpio_clear_value(0, 1<<6); + state = state_after_vsync; +} + +static void state_before_vsync(void) { + if (line_counter != 2) return; + + fl_gpio_set_value(0, 1<<6); + state = state_during_vsync; +} + +extern "C" void DMA_IRQHandler(void) __irq { + fl_dma_clear_terminal_count_interrupt_request(0); + line_counter++; + curline = curline == scanline0+20 ? scanline1+20 : scanline0+20; + state(); +} + +// ----------------------------------------------------------------------------------- + +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); +}