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