blabla
vga640x400.c@2:70daa29e01bd, 2011-07-24 (annotated)
- Committer:
- Ivop
- Date:
- Sun Jul 24 16:34:25 2011 +0000
- Revision:
- 2:70daa29e01bd
- Parent:
- 1:30d7a3e8e7a3
- Child:
- 3:ab761080df98
Do not export I2S CLK and WS signals (not needed)
Who changed what in which revision?
User | Revision | Line number | New 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 | 0:78fa88bb24cb | 38 | unsigned char text_buffer[80*25]; |
Ivop | 0:78fa88bb24cb | 39 | unsigned char *font; |
Ivop | 0:78fa88bb24cb | 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 | } |