Three-pin 640x400 VGA Console Mode

Dependents:   projet_AWA_testVGA2

Committer:
Ivop
Date:
Wed Aug 10 11:24:28 2011 +0000
Revision:
3:ab761080df98
Parent:
2:70daa29e01bd
Child:
4:3f0bd68a4dda
documentation, first draft

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 3:ab761080df98 38 unsigned char text_buffer[80*25]; ///< 80x25 screen buffer, i.e. 25 lines of 80 characters.
Ivop 3:ab761080df98 39 unsigned char *font; ///< Pointer to the user supplied font.
Ivop 3:ab761080df98 40 ///< Each character is 8 pixels wide and 16 pixels high.
Ivop 3:ab761080df98 41 ///< The font consists of 16 groups of 256 bytes.
Ivop 3:ab761080df98 42 ///< i.e. 256 times the first byte of each character, then
Ivop 3:ab761080df98 43 ///< 256 times second byte of each character, and so on.
Ivop 3:ab761080df98 44 ///< This differs from the most common way bitmap fonts are
Ivop 3:ab761080df98 45 ///< ordered, so you have to preprocess your font data first.
Ivop 3:ab761080df98 46
Ivop 0:78fa88bb24cb 47 static unsigned line_counter;
Ivop 0:78fa88bb24cb 48 static unsigned char scanline0[100], scanline1[100]; // 100*8=800 color clocks
Ivop 0:78fa88bb24cb 49 static unsigned char *curline = scanline1+20;
Ivop 0:78fa88bb24cb 50
Ivop 0:78fa88bb24cb 51 #define NCHARS 256
Ivop 0:78fa88bb24cb 52
Ivop 0:78fa88bb24cb 53 struct dma_lli {
Ivop 0:78fa88bb24cb 54 const void *source;
Ivop 0:78fa88bb24cb 55 const void *dest;
Ivop 0:78fa88bb24cb 56 const struct dma_lli *next;
Ivop 0:78fa88bb24cb 57 unsigned control_word;
Ivop 0:78fa88bb24cb 58 };
Ivop 0:78fa88bb24cb 59
Ivop 0:78fa88bb24cb 60 extern const struct dma_lli dma_lli1;
Ivop 0:78fa88bb24cb 61
Ivop 0:78fa88bb24cb 62 static const struct dma_lli dma_lli0 = {
Ivop 0:78fa88bb24cb 63 scanline0, (void*)FL_I2STXFIFO, &dma_lli1, 0x84489019 // control word is explained below
Ivop 0:78fa88bb24cb 64 };
Ivop 0:78fa88bb24cb 65 static const struct dma_lli dma_lli1 = {
Ivop 0:78fa88bb24cb 66 scanline1, (void*)FL_I2STXFIFO, &dma_lli0, 0x84489019
Ivop 0:78fa88bb24cb 67 };
Ivop 0:78fa88bb24cb 68
Ivop 0:78fa88bb24cb 69 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 70
Ivop 0:78fa88bb24cb 71 #define FEED0_AND_WAIT(x,y) fl_pll0_feed(); while(y fl_pll0_get_##x())
Ivop 0:78fa88bb24cb 72
Ivop 0:78fa88bb24cb 73 static void init_pll0(unsigned int clock_source, int N, int M, int cpu_divider) {
Ivop 0:78fa88bb24cb 74 fl_select_pll0_clock_source(clock_source);
Ivop 0:78fa88bb24cb 75
Ivop 0:78fa88bb24cb 76 fl_pll0_control(FL_ENABLE, FL_DISCONNECT); FEED0_AND_WAIT(connect,);
Ivop 0:78fa88bb24cb 77 fl_pll0_control(FL_DISABLE, FL_DISCONNECT); FEED0_AND_WAIT(enable,);
Ivop 0:78fa88bb24cb 78
Ivop 0:78fa88bb24cb 79 fl_pll0_config(N, M);
Ivop 0:78fa88bb24cb 80 fl_pll0_feed();
Ivop 0:78fa88bb24cb 81
Ivop 0:78fa88bb24cb 82 fl_pll0_control(FL_ENABLE, FL_DISCONNECT); FEED0_AND_WAIT(enable,!);
Ivop 0:78fa88bb24cb 83
Ivop 0:78fa88bb24cb 84 fl_set_cpu_clock_divider(cpu_divider);
Ivop 0:78fa88bb24cb 85 while(!fl_pll0_get_lock()) ;
Ivop 0:78fa88bb24cb 86
Ivop 0:78fa88bb24cb 87 fl_pll0_control(FL_ENABLE, FL_CONNECT); FEED0_AND_WAIT(connect,!);
Ivop 0:78fa88bb24cb 88 }
Ivop 0:78fa88bb24cb 89
Ivop 0:78fa88bb24cb 90 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 91
Ivop 1:30d7a3e8e7a3 92 #if DEBUG_VGA
Ivop 0:78fa88bb24cb 93 static void init_uart0(unsigned divaddval, unsigned mulval, unsigned divisor) {
Ivop 0:78fa88bb24cb 94 fl_power_uart0(FL_ON);
Ivop 0:78fa88bb24cb 95 fl_select_clock_uart0(FL_CLOCK_DIV1);
Ivop 0:78fa88bb24cb 96 fl_uart_set_fractional_divider(0, divaddval, mulval);
Ivop 0:78fa88bb24cb 97 fl_uart_set_divisor_latch(0, divisor);
Ivop 0:78fa88bb24cb 98 }
Ivop 1:30d7a3e8e7a3 99 #endif
Ivop 0:78fa88bb24cb 100
Ivop 0:78fa88bb24cb 101 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 102
Ivop 0:78fa88bb24cb 103 static void init_vsync(unsigned port, unsigned pin) {
Ivop 0:78fa88bb24cb 104 fl_power_gpio(FL_ON);
Ivop 0:78fa88bb24cb 105 fl_pinsel(port, pin, FL_FUNC_DEFAULT, FL_IGNORE, FL_IGNORE);
Ivop 0:78fa88bb24cb 106 fl_gpio_set_direction(port, 1<<pin, FL_OUTPUT);
Ivop 0:78fa88bb24cb 107 fl_gpio_clear_value (port, 1<<pin);
Ivop 0:78fa88bb24cb 108 }
Ivop 0:78fa88bb24cb 109
Ivop 0:78fa88bb24cb 110 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 111
Ivop 0:78fa88bb24cb 112 static void init_dma_controller(void) {
Ivop 0:78fa88bb24cb 113 fl_power_gpdma(FL_ON);
Ivop 0:78fa88bb24cb 114 fl_dma_enable(FL_ENABLE);
Ivop 0:78fa88bb24cb 115 while (!fl_dma_is_enabled()) ;
Ivop 0:78fa88bb24cb 116
Ivop 0:78fa88bb24cb 117 fl_dma_set_srcaddr (0, dma_lli0.source);
Ivop 0:78fa88bb24cb 118 fl_dma_set_destaddr(0, dma_lli0.dest);
Ivop 0:78fa88bb24cb 119 fl_dma_set_next_lli(0, dma_lli0.next);
Ivop 0:78fa88bb24cb 120 fl_dma_channel_control(0, // control word
Ivop 0:78fa88bb24cb 121 25,
Ivop 0:78fa88bb24cb 122 4, 4, // src and dest burst size
Ivop 0:78fa88bb24cb 123 32, 32, // src and dest width
Ivop 0:78fa88bb24cb 124 FL_SRC_INCREMENT,
Ivop 0:78fa88bb24cb 125 FL_NO_DEST_INCREMENT,
Ivop 0:78fa88bb24cb 126 FL_ON // terminal count interrupt
Ivop 0:78fa88bb24cb 127 );
Ivop 0:78fa88bb24cb 128 // if ((fl_dma_channel_get_control_mask(0) | 25) != dma_lli0.control_word) {
Ivop 0:78fa88bb24cb 129 // dbprintf("%08x and %08x\r\n", fl_dma_channel_get_control_mask(0) | 25, dma_lli0.control_word);
Ivop 0:78fa88bb24cb 130 // dbprintf("control_word mismatch\r\n");
Ivop 0:78fa88bb24cb 131 // while(1);
Ivop 0:78fa88bb24cb 132 // }
Ivop 0:78fa88bb24cb 133
Ivop 0:78fa88bb24cb 134 fl_dma_channel_config(0, FL_ENABLE,
Ivop 0:78fa88bb24cb 135 FL_DMA_PERIPHERAL_IS_MEMORY, FL_DMA_BURST_REQUEST_I2S_CH0,
Ivop 0:78fa88bb24cb 136 FL_DMA_MEMORY_TO_PERIPHERAL,
Ivop 0:78fa88bb24cb 137 FL_ON, FL_ON
Ivop 0:78fa88bb24cb 138 );
Ivop 0:78fa88bb24cb 139
Ivop 0:78fa88bb24cb 140 fl_nvic_interrupt_set_enable(FL_NVIC_INT_DMA);
Ivop 0:78fa88bb24cb 141 }
Ivop 0:78fa88bb24cb 142
Ivop 0:78fa88bb24cb 143 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 144
Ivop 0:78fa88bb24cb 145 static void init_i2s(void) {
Ivop 0:78fa88bb24cb 146 // I2S on P0.9 (DIP5)
Ivop 0:78fa88bb24cb 147 fl_power_i2s(FL_ON);
Ivop 0:78fa88bb24cb 148 fl_select_clock_i2s(FL_CLOCK_DIV1); // assume 100MHz
Ivop 2:70daa29e01bd 149 // fl_pinsel(0, 7, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE); // I2STX_CLK
Ivop 2:70daa29e01bd 150 // fl_pinsel(0, 8, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE); // I2STX_WS
Ivop 0:78fa88bb24cb 151 fl_pinsel(0, 9, FL_FUNC_ALT1, FL_IGNORE, FL_IGNORE); // I2STX_SDA
Ivop 0:78fa88bb24cb 152 fl_i2s_set_tx_rate(1, 4);
Ivop 0:78fa88bb24cb 153 fl_i2s_output_set_config(FL_8BITS, FL_STEREO, 8, 0, 0, 0, 0);
Ivop 0:78fa88bb24cb 154 }
Ivop 0:78fa88bb24cb 155
Ivop 0:78fa88bb24cb 156 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 157
Ivop 0:78fa88bb24cb 158 static void init_hsync(void) {
Ivop 0:78fa88bb24cb 159 // PWM1.2 on P2.1 (DIP25)
Ivop 0:78fa88bb24cb 160 fl_power_pwm1(FL_ON);
Ivop 0:78fa88bb24cb 161 fl_select_clock_pwm1(FL_CLOCK_DIV1);
Ivop 0:78fa88bb24cb 162 fl_pinsel(2, 1, FL_FUNC_ALT1, FL_FLOATING, FL_FLOATING); // PWM1.2, no pullup/down
Ivop 0:78fa88bb24cb 163 fl_pwm_set_prescale(4); // 100/25 = 4
Ivop 0:78fa88bb24cb 164 fl_pwm_config_match(0, FL_ON, FL_ON, FL_OFF); // interrupt, reset, !stop
Ivop 0:78fa88bb24cb 165 fl_pwm_set_match(0, 800); // 800 color clocks
Ivop 0:78fa88bb24cb 166
Ivop 0:78fa88bb24cb 167 #define HSHIFT 48
Ivop 0:78fa88bb24cb 168
Ivop 0:78fa88bb24cb 169 fl_pwm_set_match(1, 97+HSHIFT); // go high at 97
Ivop 0:78fa88bb24cb 170 fl_pwm_set_match(2, 1+HSHIFT); // go low at 1
Ivop 0:78fa88bb24cb 171 fl_pwm_config_edges(2, FL_DOUBLE_EDGE);
Ivop 0:78fa88bb24cb 172 fl_pwm_output_enable(2, FL_ENABLE);
Ivop 0:78fa88bb24cb 173 }
Ivop 0:78fa88bb24cb 174
Ivop 0:78fa88bb24cb 175 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 176
Ivop 0:78fa88bb24cb 177 static void state_before_vsync(void);
Ivop 0:78fa88bb24cb 178
Ivop 0:78fa88bb24cb 179 static void (*state)(void) = state_before_vsync;
Ivop 0:78fa88bb24cb 180
Ivop 0:78fa88bb24cb 181 static void state_blank_area(void) {
Ivop 0:78fa88bb24cb 182 if (line_counter != 449) return;
Ivop 0:78fa88bb24cb 183
Ivop 0:78fa88bb24cb 184 line_counter = 0;
Ivop 0:78fa88bb24cb 185 state = state_before_vsync;
Ivop 0:78fa88bb24cb 186 }
Ivop 0:78fa88bb24cb 187
Ivop 0:78fa88bb24cb 188 static void state_clearing_buffers(void) {
Ivop 0:78fa88bb24cb 189 int i;
Ivop 0:78fa88bb24cb 190
Ivop 0:78fa88bb24cb 191 if (line_counter < 441) return;
Ivop 0:78fa88bb24cb 192
Ivop 0:78fa88bb24cb 193 for (i=0; i<20; i++) {
Ivop 0:78fa88bb24cb 194 *(curline+i ) = 0;
Ivop 0:78fa88bb24cb 195 *(curline+i+20) = 0;
Ivop 0:78fa88bb24cb 196 *(curline+i+40) = 0;
Ivop 0:78fa88bb24cb 197 *(curline+i+60) = 0;
Ivop 0:78fa88bb24cb 198 }
Ivop 0:78fa88bb24cb 199
Ivop 0:78fa88bb24cb 200 if (line_counter == 442) {
Ivop 0:78fa88bb24cb 201 state = state_blank_area;
Ivop 0:78fa88bb24cb 202 }
Ivop 0:78fa88bb24cb 203 }
Ivop 0:78fa88bb24cb 204
Ivop 0:78fa88bb24cb 205 static unsigned char *fp, *tb;
Ivop 0:78fa88bb24cb 206
Ivop 0:78fa88bb24cb 207 static void state_visible_area(void) {
Ivop 0:78fa88bb24cb 208 int x;
Ivop 0:78fa88bb24cb 209 unsigned char *sp = curline, *tbp = tb;
Ivop 0:78fa88bb24cb 210
Ivop 0:78fa88bb24cb 211 for (x=0; x<20; x++) {
Ivop 0:78fa88bb24cb 212 *sp++ = *(fp + *tbp++);
Ivop 0:78fa88bb24cb 213 *sp++ = *(fp + *tbp++);
Ivop 0:78fa88bb24cb 214 *sp++ = *(fp + *tbp++);
Ivop 0:78fa88bb24cb 215 *sp++ = *(fp + *tbp++);
Ivop 0:78fa88bb24cb 216 }
Ivop 0:78fa88bb24cb 217
Ivop 0:78fa88bb24cb 218 fp += NCHARS;
Ivop 0:78fa88bb24cb 219 if (fp == font+NCHARS*16) {
Ivop 0:78fa88bb24cb 220 fp = font;
Ivop 0:78fa88bb24cb 221 tb += 80;
Ivop 0:78fa88bb24cb 222 }
Ivop 0:78fa88bb24cb 223
Ivop 0:78fa88bb24cb 224 if (line_counter == 438) {
Ivop 0:78fa88bb24cb 225 state = state_clearing_buffers;
Ivop 0:78fa88bb24cb 226 }
Ivop 0:78fa88bb24cb 227 }
Ivop 0:78fa88bb24cb 228
Ivop 0:78fa88bb24cb 229 static void state_after_vsync(void) {
Ivop 0:78fa88bb24cb 230 if (line_counter != 38) return;
Ivop 0:78fa88bb24cb 231
Ivop 0:78fa88bb24cb 232 fp = font;
Ivop 0:78fa88bb24cb 233 tb = text_buffer;
Ivop 0:78fa88bb24cb 234 state = state_visible_area;
Ivop 0:78fa88bb24cb 235 }
Ivop 0:78fa88bb24cb 236
Ivop 0:78fa88bb24cb 237 static void state_during_vsync(void) {
Ivop 0:78fa88bb24cb 238 if (line_counter != 4) return;
Ivop 0:78fa88bb24cb 239
Ivop 0:78fa88bb24cb 240 fl_gpio_clear_value(0, 1<<6);
Ivop 0:78fa88bb24cb 241 state = state_after_vsync;
Ivop 0:78fa88bb24cb 242 }
Ivop 0:78fa88bb24cb 243
Ivop 0:78fa88bb24cb 244 static void state_before_vsync(void) {
Ivop 0:78fa88bb24cb 245 if (line_counter != 2) return;
Ivop 0:78fa88bb24cb 246
Ivop 0:78fa88bb24cb 247 fl_gpio_set_value(0, 1<<6);
Ivop 0:78fa88bb24cb 248 state = state_during_vsync;
Ivop 0:78fa88bb24cb 249 }
Ivop 0:78fa88bb24cb 250
Ivop 0:78fa88bb24cb 251 extern "C" void DMA_IRQHandler(void) __irq {
Ivop 0:78fa88bb24cb 252 fl_dma_clear_terminal_count_interrupt_request(0);
Ivop 0:78fa88bb24cb 253 line_counter++;
Ivop 0:78fa88bb24cb 254 curline = curline == scanline0+20 ? scanline1+20 : scanline0+20;
Ivop 0:78fa88bb24cb 255 state();
Ivop 0:78fa88bb24cb 256 }
Ivop 0:78fa88bb24cb 257
Ivop 0:78fa88bb24cb 258 // -----------------------------------------------------------------------------------
Ivop 0:78fa88bb24cb 259
Ivop 3:ab761080df98 260 /** Initialize the VGA signal
Ivop 3:ab761080df98 261 *
Ivop 3:ab761080df98 262 * This function should be the very first thing you call, before doing anything else.
Ivop 3:ab761080df98 263 * It turns off all peripherals, sets the main clock to 100MHz (instead of the 96MHz default of the mbed)
Ivop 3:ab761080df98 264 * and switches on PWM, I2S and DMA.
Ivop 3:ab761080df98 265 */
Ivop 3:ab761080df98 266
Ivop 0:78fa88bb24cb 267 void init_vga(void) {
Ivop 0:78fa88bb24cb 268 fl_power_off_all_peripherals();
Ivop 0:78fa88bb24cb 269
Ivop 0:78fa88bb24cb 270 init_pll0(FL_PLL0_CLOCK_SOURCE_MAIN, 2, 25, 3); // 100MHz
Ivop 1:30d7a3e8e7a3 271
Ivop 1:30d7a3e8e7a3 272 #if DEBUG_VGA
Ivop 0:78fa88bb24cb 273 init_uart0(0, 0, 651); // 100MHz/651/16=9600.6 (default 8N1)
Ivop 1:30d7a3e8e7a3 274 #endif
Ivop 1:30d7a3e8e7a3 275
Ivop 0:78fa88bb24cb 276 init_vsync(0, 6); // VSYNC on P0.6 (DIP8)
Ivop 0:78fa88bb24cb 277 init_i2s();
Ivop 0:78fa88bb24cb 278 init_hsync();
Ivop 0:78fa88bb24cb 279 init_dma_controller();
Ivop 0:78fa88bb24cb 280
Ivop 0:78fa88bb24cb 281 fl_pwm_timer_counter_enable(FL_ENABLE);
Ivop 0:78fa88bb24cb 282 fl_pwm_enable(FL_ENABLE);
Ivop 0:78fa88bb24cb 283 fl_i2s_config_dma1(FL_OFF, FL_ON, 0, 2);
Ivop 0:78fa88bb24cb 284 }