diff --git a/README.md b/README.md index 93f04e4..5227ba6 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,30 @@ This is a library for generating composite video on an ATmega microcontroller. -The library can create NTSC or PAL at a resolution of 128x96. The library currently works on ATmega168, ATmega328, ATmega1280, ATmega2560, ATmega644P, ATmega1284P, ATmega90USB1286 and more can be added by editing one file. +This branch of the TVout library has been patched to allow use with the Arduino Leonardo. The goal of this project is to create a simple interupt driven library for generating composite video on a single AVR chip. +Currently the output is NTSC or PAL at a resolution of 128x96 by default. The library currently works on ATmega168,328,1280,2560,644p,1284p,32U4,AT90USB1286 and more can be added by editing spec/hardware_setup.h. +There are some timing issues with the m1284p, may be related to sanguino core. + +``` +MCU SYNC VIDEO AUDIO Arduino SYNC VIDEO AUDIO +m168,m328 B 1 D 7 B 3 NG,Decimila,UNO 9 7 11 +m1280,m2560 B 5 A 7 B 4 Mega 11 A7(D29) 10 +m644,m1284p D 5 A 7 D 7 sanguino 13 A7(D24) 8 +m32u4 B 5 B 4 B 7 Leonardo 9 8 11 +AT90USB1286 B 5 F 7 B 4 -- -- -- -- +``` ##Connections -SYNC is on OCR1A and AUDIO is on OC2A +SYNC is on OCR1A and AUDIO is on OCR2A (except on the Arduino Leonardo, where AUDIO is on OCR0A) There are some timing issues with the m1284p, may be related to sanguino core. On NG, Decimila, UNO and Nano the sync is pin 9, video on 7 and audio on 11. On Mega2560 sync is pin 11, video is on A7(D29) and audio is on pin 10. - - ##Examples https://youtu.be/MEg_V4YZDh0 diff --git a/TVout.cpp b/TVout.cpp index 751eb54..578e9af 100644 --- a/TVout.cpp +++ b/TVout.cpp @@ -298,6 +298,7 @@ unsigned char TVout::get_pixel(uint8_t x, uint8_t y) { * The color of the line. * (see color note at the top of this file) */ +/* Patched to allow support for the Arduino Leonardo */ void TVout::draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, char c) { if (x0 > display.hres*8 || y0 > display.vres || x1 > display.hres*8 || y1 > display.vres) @@ -809,7 +810,11 @@ void TVout::tone(unsigned int frequency, unsigned long duration_ms) { if (frequency == 0) return; +#if defined(__AVR_ATmega32U4__) +#define TIMER 0 +#else #define TIMER 2 +#endif //this is init code TCCR2A = 0; TCCR2B = 0; @@ -874,4 +879,4 @@ void TVout::tone(unsigned int frequency, unsigned long duration_ms) { void TVout::noTone() { TCCR2B = 0; PORT_SND &= ~(_BV(SND_PIN)); //set pin 11 to 0 -} // end of noTone \ No newline at end of file +} // end of noTone diff --git a/TVout/TVout.cpp b/TVout/TVout.cpp index 1c07d61..578e9af 100644 --- a/TVout/TVout.cpp +++ b/TVout/TVout.cpp @@ -91,6 +91,14 @@ char TVout::begin(uint8_t mode, uint8_t x, uint8_t y) { } // end of begin +/* Stop video render and free the used memory. + */ + void TVout::end() { + TIMSK1 = 0; + free(screen); +} + + /* Fill the screen with some color. * * Arguments: @@ -122,7 +130,8 @@ void TVout::fill(uint8_t color) { /* Gets the Horizontal resolution of the screen * - * Returns the horizonal resolution. + * Returns: + * The horizonal resolution. */ unsigned char TVout::hres() { return display.hres*8; @@ -289,69 +298,76 @@ unsigned char TVout::get_pixel(uint8_t x, uint8_t y) { * The color of the line. * (see color note at the top of this file) */ +/* Patched to allow support for the Arduino Leonardo */ void TVout::draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, char c) { if (x0 > display.hres*8 || y0 > display.vres || x1 > display.hres*8 || y1 > display.vres) return; - int e; - signed int dx,dy,j, temp; - signed char s1,s2, xchange; - signed int x,y; + if (x0 == x1) + draw_column(x0,y0,y1,c); + else if (y0 == y1) + draw_row(y0,x0,x1,c); + else { + int e; + signed int dx,dy,j, temp; + signed char s1,s2, xchange; + signed int x,y; - x = x0; - y = y0; + x = x0; + y = y0; - //take absolute value - if (x1 < x0) { - dx = x0 - x1; - s1 = -1; - } - else if (x1 == x0) { - dx = 0; - s1 = 0; - } - else { - dx = x1 - x0; - s1 = 1; - } + //take absolute value + if (x1 < x0) { + dx = x0 - x1; + s1 = -1; + } + else if (x1 == x0) { + dx = 0; + s1 = 0; + } + else { + dx = x1 - x0; + s1 = 1; + } - if (y1 < y0) { - dy = y0 - y1; - s2 = -1; - } - else if (y1 == y0) { - dy = 0; - s2 = 0; - } - else { - dy = y1 - y0; - s2 = 1; - } + if (y1 < y0) { + dy = y0 - y1; + s2 = -1; + } + else if (y1 == y0) { + dy = 0; + s2 = 0; + } + else { + dy = y1 - y0; + s2 = 1; + } - xchange = 0; + xchange = 0; - if (dy>dx) { - temp = dx; - dx = dy; - dy = temp; - xchange = 1; - } + if (dy>dx) { + temp = dx; + dx = dy; + dy = temp; + xchange = 1; + } - e = ((int)dy<<1) - dx; + e = ((int)dy<<1) - dx; - for (j=0; j<=dx; j++) { - sp(x,y,c); + for (j=0; j<=dx; j++) { + sp(x,y,c); - if (e>=0) { - if (xchange==1) x = x + s1; - else y = y + s2; - e = e - ((int)dx<<1); + if (e>=0) { + if (xchange==1) x = x + s1; + else y = y + s2; + e = e - ((int)dx<<1); + } + if (xchange==1) + y = y + s2; + else + x = x + s1; + e = e + ((int)dy<<1); } - if (xchange==1) - y = y + s2; - else - x = x + s1; - e = e + ((int)dy<<1); } } // end of draw_line @@ -369,7 +385,7 @@ void TVout::draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, char c) { * the color of the fill. * (see color note at the top of this file) */ -void TVout::fill_line(uint8_t line, uint16_t x0, uint16_t x1, uint8_t c) { +void TVout::draw_row(uint8_t line, uint16_t x0, uint16_t x1, uint8_t c) { uint8_t lbit, rbit; if (x0 == x1) @@ -407,7 +423,60 @@ void TVout::fill_line(uint8_t line, uint16_t x0, uint16_t x1, uint8_t c) { screen[x0] ^= rbit; } } -} // end of fill_line +} // end of draw_row + + +/* Fill a column from one point to another + * + * Argument: + * row: + * The row that fill will be performed on. + * y0: + * edge 0 of the fill. + * y1: + * edge 1 of the fill. + * c: + * the color of the fill. + * (see color note at the top of this file) +*/ +void TVout::draw_column(uint8_t row, uint16_t y0, uint16_t y1, uint8_t c) { + + unsigned char bit; + int byte; + + if (y0 == y1) + set_pixel(row,y0,c); + else { + if (y1 < y0) { + bit = y0; + y0 = y1; + y1 = bit; + } + bit = 0x80 >> (row&7); + byte = row/8 + y0*display.hres; + if (c == WHITE) { + while ( y0 <= y1) { + screen[byte] |= bit; + byte += display.hres; + y0++; + } + } + else if (c == BLACK) { + while ( y0 <= y1) { + screen[byte] &= ~bit; + byte += display.hres; + y0++; + } + } + else if (c == INVERT) { + while ( y0 <= y1) { + screen[byte] ^= bit; + byte += display.hres; + y0++; + } + } + } +} /* draw a rectangle at x,y with a specified width and height @@ -433,7 +502,7 @@ void TVout::draw_rect(uint8_t x0, uint8_t y0, uint8_t w, uint8_t h, char c, char if (fc != -1) { for (unsigned char i = y0; i < y0+h; i++) - fill_line(i,x0,x0+w,fc); + draw_row(i,x0,x0+w,fc); } draw_line(x0,y0,x0+w,y0,c); draw_line(x0,y0,x0,y0+h,c); @@ -466,10 +535,12 @@ void TVout::draw_circle(uint8_t x0, uint8_t y0, uint8_t radius, char c, char fc) int ddF_y = -2 * radius; int x = 0; int y = radius; + uint8_t pyy = y,pyx = x; + //there is a fill color if (fc != -1) - fill_line(y0,x0-radius,x0+radius,fc); + draw_row(y0,x0-radius,x0+radius,fc); sp(x0, y0 + radius,c); sp(x0, y0 - radius,c); @@ -485,11 +556,20 @@ void TVout::draw_circle(uint8_t x0, uint8_t y0, uint8_t radius, char c, char fc) x++; ddF_x += 2; f += ddF_x; - if (fc != -1) { //there is a fill color - fill_line(y0+y,x0-x,x0+x,fc); - fill_line(y0-y,x0-x,x0+x,fc); - fill_line(y0+x,x0-y,x0+y,fc); - fill_line(y0-x,x0-y,x0+y,fc); + + //there is a fill color + if (fc != -1) { + //prevent double draws on the same rows + if (pyy != y) { + draw_row(y0+y,x0-x,x0+x,fc); + draw_row(y0-y,x0-x,x0+x,fc); + } + if (pyx != x && x != y) { + draw_row(y0+x,x0-y,x0+y,fc); + draw_row(y0-x,x0-y,x0+y,fc); + } + pyy = y; + pyx = x; } sp(x0 + x, y0 + y,c); sp(x0 - x, y0 + y,c); @@ -730,7 +810,11 @@ void TVout::tone(unsigned int frequency, unsigned long duration_ms) { if (frequency == 0) return; +#if defined(__AVR_ATmega32U4__) +#define TIMER 0 +#else #define TIMER 2 +#endif //this is init code TCCR2A = 0; TCCR2B = 0; @@ -795,4 +879,4 @@ void TVout::tone(unsigned int frequency, unsigned long duration_ms) { void TVout::noTone() { TCCR2B = 0; PORT_SND &= ~(_BV(SND_PIN)); //set pin 11 to 0 -} // end of noTone \ No newline at end of file +} // end of noTone diff --git a/TVout/spec/hardware_setup.h b/TVout/spec/hardware_setup.h index c11e18c..615ae4b 100644 --- a/TVout/spec/hardware_setup.h +++ b/TVout/spec/hardware_setup.h @@ -107,6 +107,27 @@ #define PORT_SND PORTB #define DDR_SND DDRB #define SND_PIN 4 +#elif defined(__AVR_ATmega32U4__) // Modified for Arduino Leonardo +//video +#define PORT_VID PORTB +#define DDR_VID DDRB +#define VID_PIN 4 // 8 +//sync +#define PORT_SYNC PORTB +#define DDR_SYNC DDRB +#define SYNC_PIN 5 // 9 +//sound +#define PORT_SND PORTB +#define DDR_SND DDRB +#define SND_PIN 7 // 11 +#define TCCR2A TCCR0A +#define TCCR2B TCCR0B +#define OCR2A OCR0A +#define OCR2B OCR0B +#define COM2A0 COM0A0 +#define COM2A1 COM0A1 +#define CS20 CS00 +#define WGM21 WGM01 #endif //automatic BST/BLD/ANDI macro definition @@ -143,4 +164,4 @@ #define BST_HWS "bst r16,7\n\t" #define ANDI_HWS "andi r16,0x7F\n" #endif -#endif \ No newline at end of file +#endif diff --git a/TVoutfonts/README.md b/TVoutfonts/README.md new file mode 100644 index 0000000..7590cb8 --- /dev/null +++ b/TVoutfonts/README.md @@ -0,0 +1 @@ +Copy this folder to your Arduino libraries folder diff --git a/spec/hardware_setup.h b/spec/hardware_setup.h index c11e18c..615ae4b 100644 --- a/spec/hardware_setup.h +++ b/spec/hardware_setup.h @@ -107,6 +107,27 @@ #define PORT_SND PORTB #define DDR_SND DDRB #define SND_PIN 4 +#elif defined(__AVR_ATmega32U4__) // Modified for Arduino Leonardo +//video +#define PORT_VID PORTB +#define DDR_VID DDRB +#define VID_PIN 4 // 8 +//sync +#define PORT_SYNC PORTB +#define DDR_SYNC DDRB +#define SYNC_PIN 5 // 9 +//sound +#define PORT_SND PORTB +#define DDR_SND DDRB +#define SND_PIN 7 // 11 +#define TCCR2A TCCR0A +#define TCCR2B TCCR0B +#define OCR2A OCR0A +#define OCR2B OCR0B +#define COM2A0 COM0A0 +#define COM2A1 COM0A1 +#define CS20 CS00 +#define WGM21 WGM01 #endif //automatic BST/BLD/ANDI macro definition @@ -143,4 +164,4 @@ #define BST_HWS "bst r16,7\n\t" #define ANDI_HWS "andi r16,0x7F\n" #endif -#endif \ No newline at end of file +#endif