| CODE |
// ########################################################################### // ## AVR Video Game - Example on how to generate video signals // ## with an Atmel AVR at90s8535 at 8 MHz. // ## By: Robin Stridh 2002 // ## // ## Software: See sample program at end of file. // ## // ## Hardware: The external hardware consists of only 5 resistors, // ## 4 switches and a scart connector. // ## // ## // ## AVR: SCART CONNECTOR: // ## ==== ================ // ## ___ // ## B.0 -- UP ------- -----+ // ## ___ | // ## B.1 -- RIGHT ---- -----+ // ## ___ | // ## B.2 -- LEFT ----- -----+ // ## ___ | // ## B.3 -- DOWN ----- -----+-- GND // ## ___ // ## +- 2 |- _| // ## | |- _| // ## B.7 -- SOUND --[10k ]-----+-- AUDIO SIGNAL -+- 6 |- _| // ## | |- _| // ## GND --[1k ]-----+ |- _| // ## |- _| // ## |- _| // ## C.0 -- SYNC --[1.2k]-----+ |- _| // ## | |- _| 17 // ## D.7 -- VIDEO --[560 ]-----+-- VIDEO SIGNAL -- 20 |- _| // ## | \| // ## GND --[100 ]-----+ // ## // ## PINS C.1-C.7 and D.0-D.6 cannot be used by other i/o. // ## PINS A.0-A.7 and B.4-B.6 are free to use however. // ########################################################################### #define CLOCK_FREQUENCY 8 #include <io.h> #include <interrupt.h> #include <sig-avr.h> #include <delay.h> // ########################################################################### // ## Screen settings // ## Constraints: // ## LVL - FVL = HEIGHT * (8 >> LINE_REPEAT) // ## W * H = SCREEN_SIZE * 8 // ########################################################################### // ## An almost full screen image of 64x56 pixels with time for sound at // ## end of each line. #define WIDTH 64 // Width of screen in pixels #define HEIGHT 56 // Height of screen in pixels #define SCREEN_SIZE 448 // Size of screen in bytes #define LINE_DELAY delay_us(8) // Defines left edge #define FIRST_VISIBLE_LINE 40 // Defines upper edge #define LAST_VISIBLE_LINE 264 // Defines lower edge #define PIXEL_DELAY { __asm__("nop"); __asm__("nop"); } // Defines pixel width #define LINE_REPEAT 1 // Defines pixel height (1 means 4 lines, // 2 means 2 lines and 3 means 1 line) // ## A nice full screen image of 64x56 pixels. Leaves very little time at // ## end of line. /* #define WIDTH 64 #define HEIGHT 56 #define SCREEN_SIZE 448 #define LINE_DELAY delay_1us_8 #define FIRST_VISIBLE_LINE 40 #define LAST_VISIBLE_LINE 264 #define PIXEL_DELAY { __asm__("nop"); __asm__("nop"); __asm__("nop"); } #define LINE_REPEAT 1 */ // ## A small window with highest possible resolution with 8 MHz. /* #define WIDTH 64 #define HEIGHT 56 #define SCREEN_SIZE 448 #define LINE_DELAY delay_us(12); #define FIRST_VISIBLE_LINE 120 #define LAST_VISIBLE_LINE 176 #define PIXEL_DELAY #define LINE_REPEAT 3 */ // ########################################################################### // ## Video settings // ## These settings are adapted to PAL. For NTSC use the following: // ## BEGIN_VSYNC = 248, END_VSYNC = 251, NEW_FRAME = 263 (Not tested!) // ########################################################################### #define BEGIN_VSYNC 298 #define END_VSYNC 301 #define NEW_FRAME 313 // Brute force macro for outputting a line of pixel data as // fast as possible. If that is too fast a delay is used. #define OUTPUT_LINE(r) { \ outp((r), PORTD); (r) = (r) << 1; PIXEL_DELAY; \ outp((r), PORTD); (r) = (r) << 1; PIXEL_DELAY; \ outp((r), PORTD); (r) = (r) << 1; PIXEL_DELAY; \ outp((r), PORTD); (r) = (r) << 1; PIXEL_DELAY; \ outp((r), PORTD); (r) = (r) << 1; PIXEL_DELAY; \ outp((r), PORTD); (r) = (r) << 1; PIXEL_DELAY; \ outp((r), PORTD); (r) = (r) << 1; PIXEL_DELAY; \ outp((r), PORTD); (r) = (r) << 1; PIXEL_DELAY; \ } // Help macros #define CLEAR_PIXEL(x, y) screen[((y)<<3)+((x)>>3)] &= ~(128 >> ((x)&0x07)) #define PUT_PIXEL(x, y) screen[((y)<<3)+((x)>>3)] |= 128 >> ((x)&0x07) #define GET_PIXEL(x, y) (screen[((y)<<3)+((x)>>3)] & 128 >> ((x)&0x07)) #define KEYPAD_UP (bit_is_clear(PINB, 0)) #define KEYPAD_RIGHT (bit_is_clear(PINB, 1)) #define KEYPAD_LEFT (bit_is_clear(PINB, 2)) #define KEYPAD_DOWN (bit_is_clear(PINB, 3)) // ########################################################################### // ## Globals // ########################################################################### // Video unsigned char sync, data; volatile int scanline; #define black 0x00 #define white 0xff unsigned char screen[SCREEN_SIZE]; // Sound #define SOUND_FREQ 4 unsigned char sound_active; #define SOUND sound_active = 200 // Duration in ms unsigned char sound = 1; // high // Extern functions extern void VID_PostLineRoutines(void); extern void VID_PostFrameRoutines(void); // ########################################################################### // ## Interrupt routine called on every line to generate the horisontal // ## sync pulse. Also genareates the vertical sync on every frame by // ## inverting the horisontal sync. // ########################################################################### SIGNAL(SIG_OUTPUT_COMPARE1A) { outp(sync, PORTC); outp(black, PORTD); scanline ++; if (scanline == BEGIN_VSYNC) { sync = 0xff; data = 0x00; } if (scanline == END_VSYNC) { sync = 0x00; data = 0xff; } if (scanline == NEW_FRAME) { scanline = 0; } outp(data, PORTC); } void VID_Init(void) { //init timer 1 to generate sync outp(0x02, OCR1AH); outp(0x00, OCR1AL); outp(0x09, TCCR1B); //full speed; clear-on-match outp(0x00, TCCR1A); //turn off pwm and oc lines outp(0x10, TIMSK); //enable interrupt //init ports outp(0xf0, DDRB); //outputs/inputs outp(0x0f, PORTB); //zeros/activate pull-up outp(0xff, DDRD); //video out and switches outp(0xff, DDRC); //initialize synch constants scanline = 0; sync = 0x00; data = 0xff; outp(0x40, MCUCR); // enable sleep } void VID_MainLoop(void) { register unsigned char v1, v2, v3, v4, v5, v6, v7, v8; int i; sei(); // enable interrupts while(1) { // Performed in visible part of screen if (scanline < LAST_VISIBLE_LINE && scanline >= FIRST_VISIBLE_LINE) { i = (scanline - FIRST_VISIBLE_LINE) << LINE_REPEAT & 0xfff8; // Wait until next line starts __asm__("sleep"); // Load the pixels into registers v1 = screen[i]; v2 = screen[i+1]; v3 = screen[i+2]; v4 = screen[i+3]; v5 = screen[i+4]; v6 = screen[i+5]; v7 = screen[i+6]; v8 = screen[i+7]; // Wait until right position LINE_DELAY; // Output pixels OUTPUT_LINE(v1); OUTPUT_LINE(v2); OUTPUT_LINE(v3); OUTPUT_LINE(v4); OUTPUT_LINE(v5); OUTPUT_LINE(v6); OUTPUT_LINE(v7); OUTPUT_LINE(v8); // Return to black level outp(black, PORTD); } // Performed outside visible part of screen (about 5.6 ms) else { // Performed once every frame if (scanline == LAST_VISIBLE_LINE + 1) { VID_PostFrameRoutines(); } // Sleep until next interrupt __asm__("sleep"); } // Performed after each line VID_PostLineRoutines(); // Generate sound if active if ((scanline & 0x07) != 0 && sound_active) { if (sound == 1) { sbi(PORTB, 7); sound = 0; } else { cbi(PORTB, 7); sound = 1; } sound_active--; } } } // ################################################################## // ## Bouncing Ball - Sample program to demonstrate the AVR Video // ## Game console. Copy into a separate file and compile for // ## AT90S8535. // ## By: Robin Stridh 2002 // ################################################################## /* #include <video.h> void init_screen(void); int main(void) { init_screen(); VID_Init(); VID_MainLoop(); return 0; } void init_screen() { int i; for (i = 0; i < SCREEN_SIZE; i++) screen[i] = 0x00; //top line for (i = 0; i < 8; i++) screen[i] = 0xff; //bottom line for (i = SCREEN_SIZE - 9; i < SCREEN_SIZE; i++) screen[i] = 0xff; //left and right line for (i = 8; i < SCREEN_SIZE - 8; i += 8) { screen[i] = 0x80; screen[i+7] = 0x01; } } void VID_PostLineRoutines(void) {} void VID_PostFrameRoutines(void) { static unsigned char x = WIDTH / 2, y = HEIGHT / 2, dx = 2, dy = 1; CLEAR_PIXEL(x, y); x += dx; if (x < 1 || x >= WIDTH) { x -= dx; dx = -dx; SOUND; } y += dy; if (y < 1 || y >= HEIGHT - 1) { y -= dy; dy = -dy; SOUND; } PUT_PIXEL(x, y); } */ ######################################### #include <video.h> // Functions void init_screen(void); void move_ball(void); // Globals int i, j; char x, y, dx, dy; char px, py, pw, pdx; unsigned char n_blocks; unsigned char demo, game_over; #define BALL_DELAY 1 // no of empty loops between ball movements unsigned char delay_loops = BALL_DELAY; int main(void) { VID_Init(); init_screen(); VID_MainLoop(); return 0; } void VID_PostLineRoutines(void) {} void VID_PostFrameRoutines(void) { if (KEYPAD_DOWN) demo = 1; if (KEYPAD_UP) demo = 0; if (!game_over) { // Move paddle for (i = 0; i < pw; i++) CLEAR_PIXEL(px + i, py); if (!demo) { if (KEYPAD_RIGHT && px < WIDTH - pw - pdx) px += pdx; if (KEYPAD_LEFT && px > pdx) px -= pdx; } else { px = x - pw / 2; if (px < 1) px = 1; if (px > WIDTH - pw - 1) px = WIDTH - pw - 1; } for (i = 0; i < pw; i++) PUT_PIXEL(px + i, py); // Move ball if (delay_loops == 0) { move_ball(); delay_loops = BALL_DELAY; } else delay_loops--; if (n_blocks == 0) game_over = 1; // hit all blocks if (y > HEIGHT - 2) game_over = 1; // dropped ball } else { // Game over for (i = 0; i < SCREEN_SIZE; i++) screen[i] = white; // restart on up if (KEYPAD_UP) init_screen(); } } void init_screen() { int i; unsigned char r, c; x = WIDTH / 2; y = HEIGHT - 2; dx = 2; dy = -1; px = WIDTH / 2 - 3; py = HEIGHT - 2; pw = 12; pdx = 2; n_blocks = 36; demo = 1; game_over = 0; for (i = 0; i < SCREEN_SIZE; i++) { screen[i] = black; } //top line for (i = 0; i < 8; i++) { screen[i] = white; } //bottom line // for (i = 247; i < 256; i++) { // for (i = SCREEN_SIZE - 9; i < SCREEN_SIZE; i++) { // screen[i] = white; // } //left and right line // for (i = 8; i < 248; i += 8) { for (i = 8; i < SCREEN_SIZE - 8; i += 8) { screen[i] = 0x80; screen[i+7] = 0x01; } //blocks for (r = 5; r < 23; r += 3) { for (c = 1; c < 7; c++) { screen[(r<<3) + c] = 0x7e; screen[((r+1)<<3) + c] = 0x7e; } } } void move_ball() { // Move ball CLEAR_PIXEL(x, y); // x += dx; if (x < 1 || x >= WIDTH) { x -= dx; dx = -dx; } // y += dy; if (y < 1 || y >= HEIGHT - 1) { y -= dy; dy = -dy; } x += dx; if (GET_PIXEL(x, y)) { if (x > 0 && x < WIDTH && y > 0 && y < HEIGHT - 2) { // clear block screen[((y-1)<<3) + (x>>3)] = black; screen[(y<<3) + (x>>3)] = black; screen[((y+1)<<3) + (x>>3)] = black; n_blocks--; SOUND; } x -= dx; dx = -dx; } y += dy; if (GET_PIXEL(x, y)) { if (x > 0 && x < WIDTH && y > 0 && y < HEIGHT - 2) { // clear block screen[((y-1)<<3) + (x>>3)] = black; screen[(y<<3) + (x>>3)] = black; screen[((y+1)<<3) + (x>>3)] = black; n_blocks--; SOUND; } y -= dy; dy = -dy; } PUT_PIXEL(x, y); } // ########################################################################### // ## delay.h - Exact delay routines for Atmel AVR with 4 and 8 MHz crystals. // ## If an 8 MHz crystal is used, define the clock frequency as: // ## #define CLOCK_FREQUENCY 8 // ## Observe that delay_us only is exact for times > 2 us with 8 Mhz and // ## times > 4 us with 4 MHz. Use delay_1us for shorter delays. // ## // ## By: Robin Stridh 2002 // ########################################################################### #ifndef __DELAY_H__ #define __DELAY_H__ // With an 8 MHz crystal his delay will be exact for any us > 2. // (us == 1 takes 1.63 us and us == 2 takes 2.25 us.) void delay_us_8(unsigned int us) { if (us > 1) { // The minimum overhead for us == 1 us--; __asm__ ("nop"); __asm__ ("nop"); // This while loop will take exactly 1 us per turn. while ( --us ) { __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); } } } // With a 4 MHz crystal his delay will be exact for any us > 4. // (us == 1, 2, 3 or 4 all takes 3.25 us.) void delay_us_4(unsigned int us) { if (us > 4) { // The minimum overhead for us == 1 us-=4; __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); // This while loop will take exactly 1 us per turn. while (--us); } } // Macro to use for delays less than 3 us. #define delay_1us_8 { \ __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); \ __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); } // Macro to use for delays less than 5 us. #define delay_1us_4 { \ __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); } // With an 8 MHz crystal his delay will be exact for any ms. void delay_ms_8(unsigned int ms) { delay_us_8(997); // The function call and setup will take exactly 3 us. // This while loop will take exactly 1 ms per turn. Run ms - 1 times. while (--ms) { delay_us_8(999); __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); __asm__ ("nop"); } } // With an 4 MHz crystal his delay will be exact for any ms. void delay_ms_4(unsigned int ms) { delay_us_4(994); // The function call and setup will take exactly 3 us. // This while loop will take exactly 1 ms per turn. Run ms - 1 times. while (--ms) delay_us_4(999); } #if CLOCK_FREQUENCY == 8 #define delay_us delay_us_8 #define delay_ms delay_ms_8 #define delay_1us delay_1us_8 #else #define delay_us delay_us_4 #define delay_ms delay_ms_4 #define delay_1us delay_1us_4 #endif #endif // __DELAY_H__ |