/* Pixel Scribble Simple unbuffered pixel-plotting routines. Note: the TellyMate will need to have the last 64 characters in the chosen fontbank defined as the 64 teletext graphics characters. The 'pixel patterns' fontbank (downloadable from batsocks) has these characters. Details on how to reprogram a fontbank can be found in the UserGuide to the TellyMate. If you have a multi-fontbank TellyMate, you'll need to change the GRAPHICS_FONTBANK macro to match the fontbank that contains the graphics characters. */ #define BAUD_RATE 57600 #define CHAR_ESC "\x1B" #define CHAR_DLE "\x10" #define GRAPHICS_FONTBANK 0 void setup( void ) { Serial.begin( BAUD_RATE ) ; Serial.flush(); // The pixel plotting routine needs the TellyMate to send data back to the Arduino. tellymate_enable_transmit(); Serial.print( CHAR_ESC "E" ) ; // clear screen Serial.print( CHAR_ESC "f" ) ; // turn off cursor // initialise the screen so that every row is set to use the graphics fontbank. // (only needed if this isn't fontbank 0, which is the default.) if (GRAPHICS_FONTBANK != 0) { for ( byte row = 0 ; row < 25 ; row++ ) { tm_cursor_move( row , 0 ) ; // move the cursor the required row (any column would do) Serial.print( CHAR_ESC "_" ); Serial.print( ((byte)('6' + GRAPHICS_FONTBANK))) ; // use the specified fontbank } } } float g_head_pos = 0.0; // A binary-friendly increment for the tick variable. // If it's not binary friendly, then occasional rounding errors mean that the tail of the line // might not be cleared at the right pixel. #define TICK 1/128.0 void loop( void ) { g_head_pos += TICK ; float head = g_head_pos ; float tail = head - (256 * TICK) ; // set the pixels at the 'head' of the line... byte x = fn_x( head ) ; byte y = fn_y( head ) ; SetPixel( x , y ) ; // clear the pixel at the 'tail' of the line... x = fn_x( tail ) ; y = fn_y( tail ) ; ClearPixel( x , y ) ; } byte fn_x( float tick ) { return (byte)(38 + 37 * sin( tick * 1.8 ) * cos( tick * 3.2 )) ; } byte fn_y( float tick ) { return (byte)(37 + 36 * cos( tick * 1.2 ) * sin( tick * 3.1 )) ; } //================================= // Helper routines //================================= void SetPixel( byte x , byte y ) { // unbuffered version _UnbufferedPixelRender( x , y , 1 ) ; } void ClearPixel( byte x , byte y ) {// unbuffered version _UnbufferedPixelRender( x, y , 0 ) ; } void _UnbufferedPixelRender( byte x, byte y, boolean set ) { byte row ; byte col ; int screen ; static byte s_row = 255 ; static byte s_col = 255 ; static byte s_s = 0 ; row = y / 3 ; // 3 pixels down per character col = x / 2 ; // 2 pixels across per character if ((row == s_row) & (col == s_col)) { // we can use the 'cached' version - the pixel is in the same screenbyte as before. screen = s_s ; tm_cursor_move( row , col ) ; } else { tm_cursor_move( row , col ) ; screen = tellymate_cursor_read() ; } if (screen >= 0) { s_row = row ; s_col = col ; byte s = screen ; s = s & 0b00111111 ; byte yy = row * 3 ; byte xx = col * 2 ; byte shift = 0; if (x > xx) shift += 1 ; if (y > yy) shift += 2 ; if (y > (yy+1)) shift += 2 ; byte bitvalue = 1 << shift ; if (set) { s = s | bitvalue ; // set the bit } else { s = s & ~bitvalue ; // clear the bit } s = 0b11000000 | s ; s_s = s ; if (s != screen) { // only output the character if it's changed. Serial.print( s ) ; } } } void tm_cursor_move( uint8_t row , uint8_t col ) { // Yrc Serial.print( CHAR_ESC ) ; Serial.print( 'Y' ) ; Serial.print((unsigned char)(32 + row)) ; Serial.print((unsigned char)(32 + col)) ; } void tellymate_enable_transmit( void ) { // enable transmit sequence: Serial.print( CHAR_ESC ) ; for( byte i = 0 ; i < 4 ; i++ ){ Serial.print( '~' ) ; } } int tellymate_get_response() { // a command has been sent to the tellymate that should cause // a byte to be returned. // // I now need to wait *at least* 20 bit durations to receive the reply. // (the [remainder] of the send sequence to the TellyMate will take 10 bits, and the receive from the TellyMate will take 10 bits // It might be twice that (depending on when the character is read on the other end), and how long it takes to send the reply. // // each bit is 1/57600th of a second. // There are 1000000 microseconds in a second, so there are 1000000/57600 microseconds per bit int result = -1 ; // default result delayMicroseconds( 20 * (1000000 / BAUD_RATE )) ; for( uint8_t i = 0 ; i < 30 ; i++ ) { if (Serial.available() > 0) { result = Serial.read(); break ; } // delay a short while delayMicroseconds( 1000000 / BAUD_RATE ) ; // approximately one bit duration } // If no character is received after 50 bit durations, the default value of -1 is returned. return result ; } int tellymate_data_read( char param ) { // {n where n is the param passed. // As at tellymate firmware 1.0.10, n can be as follows: // '0' - read current row // '1' - read current column // '2' - read firmware major version (e.g. 1) // '3' - read firmware minor version (e.g. 0) // '4' - read firmware revision (e.g.10) // '5' - read output format 0 = NTSC, 1 = PAL Serial.print( CHAR_ESC "^" ) ; Serial.print( param ) ; return( tellymate_get_response() ) ; } void tellymate_get_cursor( int &row , int &col ) { Serial.print( CHAR_ESC "^6" ) ; row = tellymate_get_response() ; col = tellymate_get_response() ; } int tellymate_cursor_read() { // | Serial.print( CHAR_ESC "|" ); return( tellymate_get_response() ) ; } int tellymate_cursor_read( byte row, byte col ) { // }rc Serial.print( CHAR_ESC "`" ) ; Serial.print( (char)(32+row) ) ; Serial.print( (char)(32+col) ) ; return( tellymate_get_response() ) ; }