Three-pin 640x400 VGA Console Mode

Dependents:   projet_AWA_testVGA2

Committer:
Ivop
Date:
Wed Aug 10 11:47:24 2011 +0000
Revision:
7:746c1bf00d40
Parent:
4:3f0bd68a4dda
added documentation

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Ivop 0:78fa88bb24cb 1 /*
Ivop 0:78fa88bb24cb 2 * 640x400 70Hz VGA Driver
Ivop 0:78fa88bb24cb 3 *
Ivop 0:78fa88bb24cb 4 * Copyright (C) 2011 by Ivo van Poorten <ivop@euronet.nl>
Ivop 0:78fa88bb24cb 5 * This file is licensed under the terms of the GNU Lesser
Ivop 0:78fa88bb24cb 6 * General Public License, version 3.
Ivop 0:78fa88bb24cb 7 *
Ivop 0:78fa88bb24cb 8 * Inspired by Simon's (not Ford) Cookbook entry and Cliff Biffle's
Ivop 0:78fa88bb24cb 9 * assembly code.
Ivop 0:78fa88bb24cb 10 */
Ivop 0:78fa88bb24cb 11
Ivop 0:78fa88bb24cb 12 #include "fastlib/common.h"
Ivop 0:78fa88bb24cb 13 #include "fastlib/pinsel.h"
Ivop 0:78fa88bb24cb 14 #include "fastlib/gpio.h"
Ivop 0:78fa88bb24cb 15 #include "fastlib/clock.h"
Ivop 0:78fa88bb24cb 16 #include "fastlib/power.h"
Ivop 0:78fa88bb24cb 17 #include "fastlib/pwm.h"
Ivop 0:78fa88bb24cb 18 #include "fastlib/i2s.h"
Ivop 0:78fa88bb24cb 19 #include "fastlib/uart.h"
Ivop 0:78fa88bb24cb 20 #include "fastlib/dma.h"
Ivop 0:78fa88bb24cb 21 #include "fastlib/nvic.h"
Ivop 0:78fa88bb24cb 22
Ivop 0:78fa88bb24cb 23 //#include "mbed.h"
Ivop 0:78fa88bb24cb 24
Ivop 0:78fa88bb24cb 25 #include <string.h>
Ivop 0:78fa88bb24cb 26
Ivop 1:30d7a3e8e7a3 27 #define DEBUG_VGA 0
Ivop 1:30d7a3e8e7a3 28
Ivop 1:30d7a3e8e7a3 29 #if DEBUG_VGA // DEBUG messages on UART0
Ivop 0:78fa88bb24cb 30 #include <stdio.h>
Ivop 0:78fa88bb24cb 31 #define dbprintf(...) printf(__VA_ARGS__)
Ivop 0:78fa88bb24cb 32 #else
Ivop 0:78fa88bb24cb 33 #define dbprintf(...)
Ivop 0:78fa88bb24cb 34 #endif
Ivop 0:78fa88bb24cb 35
Ivop 0:78fa88bb24cb 36 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 37
Ivop 4:3f0bd68a4dda 38 unsigned char text_buffer[80*25];
Ivop 4:3f0bd68a4dda 39 unsigned char *font;
Ivop 4:3f0bd68a4dda 40
Ivop 0:78fa88bb24cb 41 static unsigned line_counter;
Ivop 0:78fa88bb24cb 42 static unsigned char scanline0[100], scanline1[100]; // 100*8=800 color clocks
Ivop 0:78fa88bb24cb 43 static unsigned char *curline = scanline1+20;
Ivop 0:78fa88bb24cb 44
Ivop 0:78fa88bb24cb 45 #define NCHARS 256
Ivop 0:78fa88bb24cb 46
Ivop 0:78fa88bb24cb 47 struct dma_lli {
Ivop 0:78fa88bb24cb 48 const void *source;
Ivop 0:78fa88bb24cb 49 const void *dest;
Ivop 0:78fa88bb24cb 50 const struct dma_lli *next;
Ivop 0:78fa88bb24cb 51 unsigned control_word;
Ivop 0:78fa88bb24cb 52 };
Ivop 0:78fa88bb24cb 53
Ivop 0:78fa88bb24cb 54 extern const struct dma_lli dma_lli1;
Ivop 0:78fa88bb24cb 55
Ivop 0:78fa88bb24cb 56 static const struct dma_lli dma_lli0 = {
Ivop 0:78fa88bb24cb 57 scanline0, (void*)FL_I2STXFIFO, &dma_lli1, 0x84489019 // control word is explained below
Ivop 0:78fa88bb24cb 58 };
Ivop 0:78fa88bb24cb 59 static const struct dma_lli dma_lli1 = {
Ivop 0:78fa88bb24cb 60 scanline1, (void*)FL_I2STXFIFO, &dma_lli0, 0x84489019
Ivop 0:78fa88bb24cb 61 };
Ivop 0:78fa88bb24cb 62
Ivop 0:78fa88bb24cb 63 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 64
Ivop 0:78fa88bb24cb 65 #define FEED0_AND_WAIT(x,y) fl_pll0_feed(); while(y fl_pll0_get_##x())
Ivop 0:78fa88bb24cb 66
Ivop 0:78fa88bb24cb 67 static void init_pll0(unsigned int clock_source, int N, int M, int cpu_divider) {
Ivop 0:78fa88bb24cb 68 fl_select_pll0_clock_source(clock_source);
Ivop 0:78fa88bb24cb 69
Ivop 0:78fa88bb24cb 70 fl_pll0_control(FL_ENABLE, FL_DISCONNECT); FEED0_AND_WAIT(connect,);
Ivop 0:78fa88bb24cb 71 fl_pll0_control(FL_DISABLE, FL_DISCONNECT); FEED0_AND_WAIT(enable,);
Ivop 0:78fa88bb24cb 72
Ivop 0:78fa88bb24cb 73 fl_pll0_config(N, M);
Ivop 0:78fa88bb24cb 74 fl_pll0_feed();
Ivop 0:78fa88bb24cb 75
Ivop 0:78fa88bb24cb 76 fl_pll0_control(FL_ENABLE, FL_DISCONNECT); FEED0_AND_WAIT(enable,!);
Ivop 0:78fa88bb24cb 77
Ivop 0:78fa88bb24cb 78 fl_set_cpu_clock_divider(cpu_divider);
Ivop 0:78fa88bb24cb 79 while(!fl_pll0_get_lock()) ;
Ivop 0:78fa88bb24cb 80
Ivop 0:78fa88bb24cb 81 fl_pll0_control(FL_ENABLE, FL_CONNECT); FEED0_AND_WAIT(connect,!);
Ivop 0:78fa88bb24cb 82 }
Ivop 0:78fa88bb24cb 83
Ivop 0:78fa88bb24cb 84 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 85
Ivop 1:30d7a3e8e7a3 86 #if DEBUG_VGA
Ivop 0:78fa88bb24cb 87 static void init_uart0(unsigned divaddval, unsigned mulval, unsigned divisor) {
Ivop 0:78fa88bb24cb 88 fl_power_uart0(FL_ON);
Ivop 0:78fa88bb24cb 89 fl_select_clock_uart0(FL_CLOCK_DIV1);
Ivop 0:78fa88bb24cb 90 fl_uart_set_fractional_divider(0, divaddval, mulval);
Ivop 0:78fa88bb24cb 91 fl_uart_set_divisor_latch(0, divisor);
Ivop 0:78fa88bb24cb 92 }
Ivop 1:30d7a3e8e7a3 93 #endif
Ivop 0:78fa88bb24cb 94
Ivop 0:78fa88bb24cb 95 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 96
Ivop 0:78fa88bb24cb 97 static void init_vsync(unsigned port, unsigned pin) {
Ivop 0:78fa88bb24cb 98 fl_power_gpio(FL_ON);
Ivop 0:78fa88bb24cb 99 fl_pinsel(port, pin, FL_FUNC_DEFAULT, FL_IGNORE, FL_IGNORE);
Ivop 0:78fa88bb24cb 100 fl_gpio_set_direction(port, 1<<pin, FL_OUTPUT);
Ivop 0:78fa88bb24cb 101 fl_gpio_clear_value (port, 1<<pin);
Ivop 0:78fa88bb24cb 102 }
Ivop 0:78fa88bb24cb 103
Ivop 0:78fa88bb24cb 104 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 105
Ivop 0:78fa88bb24cb 106 static void init_dma_controller(void) {
Ivop 0:78fa88bb24cb 107 fl_power_gpdma(FL_ON);
Ivop 0:78fa88bb24cb 108 fl_dma_enable(FL_ENABLE);
Ivop 0:78fa88bb24cb 109 while (!fl_dma_is_enabled()) ;
Ivop 0:78fa88bb24cb 110
Ivop 0:78fa88bb24cb 111 fl_dma_set_srcaddr (0, dma_lli0.source);
Ivop 0:78fa88bb24cb 112 fl_dma_set_destaddr(0, dma_lli0.dest);
Ivop 0:78fa88bb24cb 113 fl_dma_set_next_lli(0, dma_lli0.next);
Ivop 0:78fa88bb24cb 114 fl_dma_channel_control(0, // control word
Ivop 0:78fa88bb24cb 115 25,
Ivop 0:78fa88bb24cb 116 4, 4, // src and dest burst size
Ivop 0:78fa88bb24cb 117 32, 32, // src and dest width
Ivop 0:78fa88bb24cb 118 FL_SRC_INCREMENT,
Ivop 0:78fa88bb24cb 119 FL_NO_DEST_INCREMENT,
Ivop 0:78fa88bb24cb 120 FL_ON // terminal count interrupt
Ivop 0:78fa88bb24cb 121 );
Ivop 0:78fa88bb24cb 122 // if ((fl_dma_channel_get_control_mask(0) | 25) != dma_lli0.control_word) {
Ivop 0:78fa88bb24cb 123 // dbprintf("%08x and %08x\r\n", fl_dma_channel_get_control_mask(0) | 25, dma_lli0.control_word);
Ivop 0:78fa88bb24cb 124 // dbprintf("control_word mismatch\r\n");
Ivop 0:78fa88bb24cb 125 // while(1);
Ivop 0:78fa88bb24cb 126 // }
Ivop 0:78fa88bb24cb 127
Ivop 0:78fa88bb24cb 128 fl_dma_channel_config(0, FL_ENABLE,
Ivop 0:78fa88bb24cb 129 FL_DMA_PERIPHERAL_IS_MEMORY, FL_DMA_BURST_REQUEST_I2S_CH0,
Ivop 0:78fa88bb24cb 130 FL_DMA_MEMORY_TO_PERIPHERAL,
Ivop 0:78fa88bb24cb 131 FL_ON, FL_ON
Ivop 0:78fa88bb24cb 132 );
Ivop 0:78fa88bb24cb 133
Ivop 0:78fa88bb24cb 134 fl_nvic_interrupt_set_enable(FL_NVIC_INT_DMA);
Ivop 0:78fa88bb24cb 135 }
Ivop 0:78fa88bb24cb 136
Ivop 0:78fa88bb24cb 137 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 138
Ivop 0:78fa88bb24cb 139 static void init_i2s(void) {
Ivop 0:78fa88bb24cb 140 // I2S on P0.9 (DIP5)
Ivop 0:78fa88bb24cb 141 fl_power_i2s(FL_ON);
Ivop 0:78fa88bb24cb 142 fl_select_clock_i2s(FL_CLOCK_DIV1); // assume 100MHz
Ivop 2:70daa29e01bd 143 // fl_pinsel(0, 7, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE); // I2STX_CLK
Ivop 2:70daa29e01bd 144 // fl_pinsel(0, 8, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE); // I2STX_WS
Ivop 0:78fa88bb24cb 145 fl_pinsel(0, 9, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE); // I2STX_SDA
Ivop 0:78fa88bb24cb 146 fl_i2s_set_tx_rate(1, 4);
Ivop 0:78fa88bb24cb 147 fl_i2s_output_set_config(FL_8BITS, FL_STEREO, 8, 0, 0, 0, 0);
Ivop 0:78fa88bb24cb 148 }
Ivop 0:78fa88bb24cb 149
Ivop 0:78fa88bb24cb 150 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 151
Ivop 0:78fa88bb24cb 152 static void init_hsync(void) {
Ivop 0:78fa88bb24cb 153 // PWM1.2 on P2.1 (DIP25)
Ivop 0:78fa88bb24cb 154 fl_power_pwm1(FL_ON);
Ivop 0:78fa88bb24cb 155 fl_select_clock_pwm1(FL_CLOCK_DIV1);
Ivop 0:78fa88bb24cb 156 fl_pinsel(2, 1, FL_FUNC_ALT1, FL_FLOATING, FL_FLOATING); // PWM1.2, no pullup/down
Ivop 0:78fa88bb24cb 157 fl_pwm_set_prescale(4); // 100/25 = 4
Ivop 0:78fa88bb24cb 158 fl_pwm_config_match(0, FL_ON, FL_ON, FL_OFF); // interrupt, reset, !stop
Ivop 0:78fa88bb24cb 159 fl_pwm_set_match(0, 800); // 800 color clocks
Ivop 0:78fa88bb24cb 160
Ivop 0:78fa88bb24cb 161 #define HSHIFT 48
Ivop 0:78fa88bb24cb 162
Ivop 0:78fa88bb24cb 163 fl_pwm_set_match(1, 97+HSHIFT); // go high at 97
Ivop 0:78fa88bb24cb 164 fl_pwm_set_match(2, 1+HSHIFT); // go low at 1
Ivop 0:78fa88bb24cb 165 fl_pwm_config_edges(2, FL_DOUBLE_EDGE);
Ivop 0:78fa88bb24cb 166 fl_pwm_output_enable(2, FL_ENABLE);
Ivop 0:78fa88bb24cb 167 }
Ivop 0:78fa88bb24cb 168
Ivop 0:78fa88bb24cb 169 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 170
Ivop 0:78fa88bb24cb 171 static void state_before_vsync(void);
Ivop 0:78fa88bb24cb 172
Ivop 0:78fa88bb24cb 173 static void (*state)(void) = state_before_vsync;
Ivop 0:78fa88bb24cb 174
Ivop 0:78fa88bb24cb 175 static void state_blank_area(void) {
Ivop 0:78fa88bb24cb 176 if (line_counter != 449) return;
Ivop 0:78fa88bb24cb 177
Ivop 0:78fa88bb24cb 178 line_counter = 0;
Ivop 0:78fa88bb24cb 179 state = state_before_vsync;
Ivop 0:78fa88bb24cb 180 }
Ivop 0:78fa88bb24cb 181
Ivop 0:78fa88bb24cb 182 static void state_clearing_buffers(void) {
Ivop 0:78fa88bb24cb 183 int i;
Ivop 0:78fa88bb24cb 184
Ivop 0:78fa88bb24cb 185 if (line_counter < 441) return;
Ivop 0:78fa88bb24cb 186
Ivop 0:78fa88bb24cb 187 for (i=0; i<20; i++) {
Ivop 0:78fa88bb24cb 188 *(curline+i ) = 0;
Ivop 0:78fa88bb24cb 189 *(curline+i+20) = 0;
Ivop 0:78fa88bb24cb 190 *(curline+i+40) = 0;
Ivop 0:78fa88bb24cb 191 *(curline+i+60) = 0;
Ivop 0:78fa88bb24cb 192 }
Ivop 0:78fa88bb24cb 193
Ivop 0:78fa88bb24cb 194 if (line_counter == 442) {
Ivop 0:78fa88bb24cb 195 state = state_blank_area;
Ivop 0:78fa88bb24cb 196 }
Ivop 0:78fa88bb24cb 197 }
Ivop 0:78fa88bb24cb 198
Ivop 0:78fa88bb24cb 199 static unsigned char *fp, *tb;
Ivop 0:78fa88bb24cb 200
Ivop 0:78fa88bb24cb 201 static void state_visible_area(void) {
Ivop 0:78fa88bb24cb 202 int x;
Ivop 0:78fa88bb24cb 203 unsigned char *sp = curline, *tbp = tb;
Ivop 0:78fa88bb24cb 204
Ivop 0:78fa88bb24cb 205 for (x=0; x<20; x++) {
Ivop 0:78fa88bb24cb 206 *sp++ = *(fp + *tbp++);
Ivop 0:78fa88bb24cb 207 *sp++ = *(fp + *tbp++);
Ivop 0:78fa88bb24cb 208 *sp++ = *(fp + *tbp++);
Ivop 0:78fa88bb24cb 209 *sp++ = *(fp + *tbp++);
Ivop 0:78fa88bb24cb 210 }
Ivop 0:78fa88bb24cb 211
Ivop 0:78fa88bb24cb 212 fp += NCHARS;
Ivop 0:78fa88bb24cb 213 if (fp == font+NCHARS*16) {
Ivop 0:78fa88bb24cb 214 fp = font;
Ivop 0:78fa88bb24cb 215 tb += 80;
Ivop 0:78fa88bb24cb 216 }
Ivop 0:78fa88bb24cb 217
Ivop 0:78fa88bb24cb 218 if (line_counter == 438) {
Ivop 0:78fa88bb24cb 219 state = state_clearing_buffers;
Ivop 0:78fa88bb24cb 220 }
Ivop 0:78fa88bb24cb 221 }
Ivop 0:78fa88bb24cb 222
Ivop 0:78fa88bb24cb 223 static void state_after_vsync(void) {
Ivop 0:78fa88bb24cb 224 if (line_counter != 38) return;
Ivop 0:78fa88bb24cb 225
Ivop 0:78fa88bb24cb 226 fp = font;
Ivop 0:78fa88bb24cb 227 tb = text_buffer;
Ivop 0:78fa88bb24cb 228 state = state_visible_area;
Ivop 0:78fa88bb24cb 229 }
Ivop 0:78fa88bb24cb 230
Ivop 0:78fa88bb24cb 231 static void state_during_vsync(void) {
Ivop 0:78fa88bb24cb 232 if (line_counter != 4) return;
Ivop 0:78fa88bb24cb 233
Ivop 0:78fa88bb24cb 234 fl_gpio_clear_value(0, 1<<6);
Ivop 0:78fa88bb24cb 235 state = state_after_vsync;
Ivop 0:78fa88bb24cb 236 }
Ivop 0:78fa88bb24cb 237
Ivop 0:78fa88bb24cb 238 static void state_before_vsync(void) {
Ivop 0:78fa88bb24cb 239 if (line_counter != 2) return;
Ivop 0:78fa88bb24cb 240
Ivop 0:78fa88bb24cb 241 fl_gpio_set_value(0, 1<<6);
Ivop 0:78fa88bb24cb 242 state = state_during_vsync;
Ivop 0:78fa88bb24cb 243 }
Ivop 0:78fa88bb24cb 244
Ivop 0:78fa88bb24cb 245 extern "C" void DMA_IRQHandler(void) __irq {
Ivop 0:78fa88bb24cb 246 fl_dma_clear_terminal_count_interrupt_request(0);
Ivop 0:78fa88bb24cb 247 line_counter++;
Ivop 0:78fa88bb24cb 248 curline = curline == scanline0+20 ? scanline1+20 : scanline0+20;
Ivop 0:78fa88bb24cb 249 state();
Ivop 0:78fa88bb24cb 250 }
Ivop 0:78fa88bb24cb 251
Ivop 0:78fa88bb24cb 252 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 253
Ivop 0:78fa88bb24cb 254 void init_vga(void) {
Ivop 0:78fa88bb24cb 255 fl_power_off_all_peripherals();
Ivop 0:78fa88bb24cb 256
Ivop 0:78fa88bb24cb 257 init_pll0(FL_PLL0_CLOCK_SOURCE_MAIN, 2, 25, 3); // 100MHz
Ivop 1:30d7a3e8e7a3 258
Ivop 1:30d7a3e8e7a3 259 #if DEBUG_VGA
Ivop 0:78fa88bb24cb 260 init_uart0(0, 0, 651); // 100MHz/651/16=9600.6 (default 8N1)
Ivop 1:30d7a3e8e7a3 261 #endif
Ivop 1:30d7a3e8e7a3 262
Ivop 0:78fa88bb24cb 263 init_vsync(0, 6); // VSYNC on P0.6 (DIP8)
Ivop 0:78fa88bb24cb 264 init_i2s();
Ivop 0:78fa88bb24cb 265 init_hsync();
Ivop 0:78fa88bb24cb 266 init_dma_controller();
Ivop 0:78fa88bb24cb 267
Ivop 0:78fa88bb24cb 268 fl_pwm_timer_counter_enable(FL_ENABLE);
Ivop 0:78fa88bb24cb 269 fl_pwm_enable(FL_ENABLE);
Ivop 0:78fa88bb24cb 270 fl_i2s_config_dma1(FL_OFF, FL_ON, 0, 2);
Ivop 0:78fa88bb24cb 271 }