Three-pin 640x400 VGA Console Mode

Dependents:   projet_AWA_testVGA2

Revision:
0:78fa88bb24cb
Child:
1:30d7a3e8e7a3
--- /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);
+}