Subversion Repositories Projects

Rev

Blame | Last modification | View Log | RSS feed

/*****************************************************************************
 *   Copyright (C) 2009 Peter "woggle" Mack, mac@denich.net                  *
 *   - original LCD control by Thomas "thkais" Kaiser                        *
 *   - special number formating routines taken from C-OSD                    *
 *      from Claas Anders "CaScAdE" Rathje                                   *
 *   - some extension, ellipse and circ_line by Peter "woggle" Mack          *
 *                                                                           *
 *   This program is free software; you can redistribute it and/or modify    *
 *   it under the terms of the GNU General Public License as published by    *
 *   the Free Software Foundation; either version 2 of the License.          *
 *                                                                           *
 *   This program is distributed in the hope that it will be useful,         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
 *   GNU General Public License for more details.                            *
 *                                                                           *
 *   You should have received a copy of the GNU General Public License       *
 *   along with this program; if not, write to the                           *
 *   Free Software Foundation, Inc.,                                         *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.               *
 *                                                                           *
 *****************************************************************************/


#include "cpu.h"
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "font8x6.h"
#include "Font8x8.h"
#include "eeprom.h"
#include "lcd.h"
#include "HAL_HW3_9.h"


#define DISP_W 128
#define DISP_H 64

#define DISP_BUFFER ((DISP_H * DISP_W) / 8)
#define LINE_BUFFER (((DISP_H/8) * DISP_W) / 8)

#define Jeti 1  // Jeti Routinen

volatile uint8_t display_buffer[DISP_BUFFER];   // Display-Puffer, weil nicht zurückgelesen werden kann
volatile uint8_t line_buffer[LINE_BUFFER];              // Zeilen-Puffer, weil nicht zurückgelesen werden kann

volatile uint16_t display_buffer_pointer;               // Pointer auf das aktuell übertragene Byte
volatile uint8_t display_buffer_counter;                // Hilfszähler zur Selektierung der Page
volatile uint8_t display_page_counter;                  // aktuelle Page-Nummer
volatile uint8_t display_mode;                                  // Modus für State-Machine
volatile uint8_t LCD_ORIENTATION;

// DOG: 128 x 64 with 6x8 Font => 21 x 8
// MAX7456: 30 x 16

uint8_t lcd_xpos;
uint8_t lcd_ypos;


//-----------------------------------------------------------
void send_byte (uint8_t data)
{
        clr_cs ();
        SPDR = data;
        while (!(SPSR & (1<<SPIF)));
        //SPSR = SPSR;
        set_cs ();
}


//-----------------------------------------------------------
// * Writes one command byte
// * cmd           - the command byte
//
void lcd_command(uint8_t cmd)
{
//      LCD_SELECT();
//      LCD_CMD();
//      spi_write(cmd);
//      LCD_UNSELECT();
        clr_cs ();
        SPDR = cmd;
        while (!(SPSR & (1<<SPIF)));
        //SPSR = SPSR;
        set_cs ();
}


//-----------------------------------------------------------
void lcd_cls (void)
{
        uint16_t i, j;

//      memset (display_buffer, 0, 1024);
        for (i = 0; i < DISP_BUFFER; i++)
                display_buffer[i] = 0x00;
        for (i = 0; i < 8; i++)
        {
                clr_A0 ();
                send_byte (0xB0 + i);                   //1011xxxx
                send_byte (0x10);                               //00010000
//              send_byte(0x04);                                //00000100 gedreht plus 4 Byte
//              send_byte(0x00);                                //00000000
                send_byte (LCD_ORIENTATION);    //00000000

                set_A0 ();
                for (j = 0; j < 128; j++)
                        send_byte (0x00);
        }

        lcd_xpos = 0;
        lcd_ypos = 0;
}


//-----------------------------------------------------------
void lcd_cls_line (uint8_t x, uint8_t y, uint8_t w)
{
        uint8_t lcd_width;
        uint8_t lcd_zpos;
        uint8_t i;
        uint8_t max = 21;
        lcd_width = w;
        lcd_xpos = x;
        lcd_ypos = y;

        if ((lcd_xpos + lcd_width) > max)
                lcd_width = max - lcd_xpos;

        lcd_zpos = lcd_xpos + lcd_width;

        for (i = lcd_xpos; i < lcd_zpos; i++)
                lcd_putc (i, lcd_ypos, 0x20, 0);
}


//-----------------------------------------------------------
void wait_1ms (void)
{
        _delay_ms (1);
}


//-----------------------------------------------------------
void wait_ms (uint16_t time)
{
        uint16_t i;

        for (i = 0; i < time; i++)
                wait_1ms ();
}


//-----------------------------------------------------------
void LCD_Init (uint8_t LCD_Mode)        // LCD_Mode 0= Default Mode 1= EEPROM-Parameter)
{
        lcd_xpos = 0;
        lcd_ypos = 0;

//      DDRB = 0xFF;

        // SPI max. speed
        // the DOGM128 lcd controller can work at 20 MHz
        SPCR = (1 << SPE) | (1 << MSTR) | (1 << CPHA) | (1 << CPOL);
        SPSR = (1 << SPI2X);

        set_cs ();
        clr_reset ();
        wait_ms (10);
        set_reset ();

        clr_cs ();
        clr_A0 ();

        send_byte (0x40);               //Display start line = 0
        if (LCD_Mode == 1)
        {
                if (LCD_ORIENTATION == 0)
                {
                        send_byte (0xA1); // A1 normal A0 reverse(original)
                        send_byte (0xC0); // C0 normal C8 reverse(original)
                }
                else
                {
                        send_byte (0xA0); // A1 normal A0 reverse(original)
                        send_byte (0xC8); // C0 normal C8 reverse(original)
                }
        }
        else
        {
                send_byte (0xA1); // A1 normal A0 reverse(original)
                send_byte (0xC0); // C0 normal C8 reverse(original)
        }
        if (LCD_Mode == 1)
        {
                if (LCD_DisplayMode == 0)
                        send_byte (0xA6);               //Display normal, not mirrored
                else
                        send_byte (0xA7);               //Display reverse, not mirrored
        }
        else
                send_byte (0xA6);


        send_byte (0xA2);               //Set bias 1/9 (Duty 1/65)
        send_byte (0x2F);               //Booster, regulator and follower on
        send_byte (0xF8);               //Set internal booster to 4x
        send_byte (0x00);               //Set internal booster to 4x
        send_byte (0x27);               //resistor ratio set

        if (LCD_Mode == 1)
        {
                send_byte (0x81);               //Electronic volume     register set
                send_byte (LCD_Kontrast);               //Electronic volume     register set
        }
        else
        {
                send_byte (0x81);
                send_byte (0x16);
        }

        send_byte (0xAC);               //Cursor
        send_byte (0x00);               //No Cursor
        send_byte (0xAF);               //No indicator

        if (LCD_Mode == 1)
        {
                // Helligkeit setzen
                OCR2A = LCD_Helligkeit * 2.55;
        }
        else
        {
                OCR2A = 255;
        }

        lcd_cls ();
}


//-----------------------------------------------------------
void set_adress (uint16_t adress, uint8_t data)
{
        uint8_t page;
        uint8_t column;

        page = adress >> 7;

        clr_A0 ();
        send_byte (0xB0 + page);

        column = (adress & 0x7F) + LCD_ORIENTATION;

        send_byte (0x10 + (column >> 4));
        send_byte (column & 0x0F);

        set_A0 ();
        send_byte (data);
}


//-----------------------------------------------------------
void scroll (void)
{
        uint16_t adress;

        for (adress = 0; adress < 896; adress++)
        {
                display_buffer[adress] = display_buffer[adress + 128];
                set_adress (adress, display_buffer[adress]);
        }

        for (adress = 896; adress < 1024; adress++)
        {
                display_buffer[adress] = 0;
                set_adress (adress, 0);
        }
}


//-----------------------------------------------------------
// sicher eine Zeile für die Statusanzeige
void copy_line (uint8_t y)
{
        uint8_t i;
        uint16_t adress;

        adress = y * 128 + 0 * 6;
        adress &= 0x3FF;

        for (i = 0; i < 6*21; i++)
        {
                line_buffer[i] = display_buffer[adress+i];
                set_adress (adress + i, display_buffer[adress + i]);
        }
}


//-----------------------------------------------------------
// holt gesicherte Zeile wieder zurück
void paste_line (uint8_t y)
{
        uint8_t i;
        uint16_t adress;

        adress = y * 128 + 0 * 6;
        adress &= 0x3FF;

        for (i = 0; i < 6*21; i++)
        {
                display_buffer[adress+i] =line_buffer[i];
                set_adress (adress + i, display_buffer[adress + i]);
        }
}


//-----------------------------------------------------------
void lcd_puts_at(uint8_t x, uint8_t y,const char *s, uint8_t mode )
{
        while (*s)
        {
                lcd_putc(x, y, *s++, mode);
                x++;
        }

}/* lcd_puts */


//-----------------------------------------------------------
void lcd_putc (uint8_t x, uint8_t y, uint8_t c, uint8_t mode)
{
        uint8_t ch;
        uint8_t i;
        uint16_t adress;

        if (mode == 2)
                lcd_frect ((x*6),(y*8),5,7,1); // invertierte Darstellung

        if (mode == 3)     lcd_putc_jeti (x, y, c,0);
        else
          if (mode == 4)     lcd_putc_jeti (x, y, c,2);
        else
         {

            switch (c)
            {   // ISO 8859-1

            case 0xc4:  // Ä
                    c = 0x01;
                    break;

            case 0xe4:  // ä
                    c = 0x02;
                    break;

            case 0xd6:  // Ö
                    c = 0x03;
                    break;

            case 0xf6:  // ö
                    c = 0x04;
                    break;

            case 0xdc:  // Ü
                    c = 0x05;
                    break;

            case 0xfc:  // ü
                    c = 0x06;
                    break;

            case 0xdf:  // ß
                    //c = 0x07;
                    c = 0x1e; // ° (used by Jeti)
                    break;
            }

            c &= 0x7f;

            adress = y * 128 + x * 6;
            adress &= 0x3FF;

            for (i = 0; i < 6; i++)
            {
                    ch = pgm_read_byte (&font8x6[0][0] + i + c * 6);

                    switch (mode)
                    {

                    case 0:
                            display_buffer[adress+i] = ch;
                            break;

                    case 1:
                            display_buffer[adress+i] |= ch;
                            break;

                    case 2:
                            display_buffer[adress+i] ^= ch;
                            break;

                    case 3:
                            display_buffer[adress+i] &= ch;
                            break;

                    case 4:
                            display_buffer[adress+i] &= ~ch;
                            break;
                    }

                    set_adress (adress + i, display_buffer[adress + i]);
              }
       }
}


#if Jeti
//-----------------------------------------------------------
void lcd_putc_jeti (uint8_t x, uint8_t y, uint8_t c, uint8_t mode)
{
        uint8_t ch;
        uint8_t i;
        uint16_t adress;
        if (mode == 2)
                lcd_frect ((x*8),(y*8),8,8,1); // invertierte Darstellung
        switch (c)
        {

        case 0x7e:
                c = 0x1a; // ->
                break;

        case 0x7f:
                c = 0x1b; // <-
                break;

        case 0xdf:
                c = 0xf8; // °
                break;
        }

        adress = y * 128 + x * 8;
        adress &= 0x3FF;

        for (i = 0; i < 8; i++)
        {
                ch = pgm_read_byte (&Font8x8[0][0] + i + c * 8);

                switch (mode)
                {

                case 0:
                        display_buffer[adress+i] = ch;
                        break;

                case 1:
                        display_buffer[adress+i] |= ch;
                        break;

                case 2:
                        display_buffer[adress+i] ^= ch;
                        break;

                case 3:
                        display_buffer[adress+i] &= ch;
                        break;

                case 4:
                        display_buffer[adress+i] &= ~ch;
                        break;
                }

                set_adress (adress + i, display_buffer[adress + i]);
        }
}


//-----------------------------------------------------------
void lcd_printpj (const char *text, uint8_t mode)
{
        while (pgm_read_byte(text))
        {
                switch (pgm_read_byte(text))
                {

                case 0x0D:
                        lcd_xpos = 0;
                        break;

                case 0x0A:
                        new_line();
                        break;

                default:
                        lcd_putc_jeti (lcd_xpos, lcd_ypos, pgm_read_byte(text), mode);
                        lcd_xpos++;
                        if (lcd_xpos > 20)
                        {
                                lcd_xpos = 0;
                                new_line ();
                        }
                        break;
                }
                text++;
        }
}


//-----------------------------------------------------------
void lcd_printpj_at (uint8_t x, uint8_t y, const char *text, uint8_t mode)
{
        lcd_xpos = x;
        lcd_ypos = y;
        lcd_printpj (text, mode);
}
#endif


//-----------------------------------------------------------
void new_line (void)
{
        lcd_ypos++;

        if (lcd_ypos > 7)
        {
                scroll ();
                lcd_ypos = 7;
        }
}


//-----------------------------------------------------------
void lcd_printpns (const char *text, uint8_t mode)
{
        while (pgm_read_byte(text))
        {
                switch (pgm_read_byte(text))
                {

                case 0x0D:
                        lcd_xpos = 0;
                        break;

                case 0x0A:
                        new_line();
                        break;

                default:
                        lcd_putc (lcd_xpos, lcd_ypos, pgm_read_byte(text), mode);
                        lcd_xpos++;
                        if (lcd_xpos > 21)
                        {
                                lcd_xpos = 0;
//                              new_line ();
                        }
                        break;
                }
                text++;
        }
}


//-----------------------------------------------------------
void lcd_printpns_at (uint8_t x, uint8_t y, const char *text, uint8_t mode)
{
        lcd_xpos = x;
        lcd_ypos = y;
        lcd_printpns (text, mode);
}


//-----------------------------------------------------------
void lcd_printp (const char *text, uint8_t mode)
{
        while (pgm_read_byte(text))
        {
                switch (pgm_read_byte(text))
                {

                case 0x0D:
                        lcd_xpos = 0;
                        break;

                case 0x0A:
                        new_line();
                        break;

                default:
                        lcd_putc (lcd_xpos, lcd_ypos, pgm_read_byte(text), mode);
                        lcd_xpos++;
                        if (lcd_xpos > 21)
                        {
                                lcd_xpos = 0;
                                new_line ();
                        }
                        break;
                }
                text++;
        }
}


//-----------------------------------------------------------
void lcd_printp_at (uint8_t x, uint8_t y, const char *text, uint8_t mode)
{
        lcd_xpos = x;
        lcd_ypos = y;
        lcd_printp (text, mode);
}


//-----------------------------------------------------------
void lcd_print (uint8_t *text, uint8_t mode)
{
        while (*text)
        {
                switch (*text)
                {

                case 0x0D:
                        lcd_xpos = 0;
                        break;

                case 0x0A:
                        new_line();
                        break;

                default:
                        lcd_putc (lcd_xpos, lcd_ypos, *text, mode);
                        lcd_xpos++;
                        if (lcd_xpos > 21)
                        {
                                lcd_xpos = 0;
                                new_line ();
                        }
                        break;
                }
                text++;
        }
}


//-----------------------------------------------------------
void lcd_print_at (uint8_t x, uint8_t y, uint8_t *text, uint8_t mode)
{
        lcd_xpos = x;
        lcd_ypos = y;
        lcd_print (text, mode);
}


//-----------------------------------------------------------
void print_display (uint8_t *text)
{
        while (*text)
        {
                lcd_putc (lcd_xpos, lcd_ypos, *text, 0);
                lcd_xpos++;
                if (lcd_xpos >= 20)
                {
                        lcd_xpos = 0;
                        new_line ();
                }
                text++;
        }
}


//-----------------------------------------------------------
void print_display_at (uint8_t x, uint8_t y, uint8_t *text)
{
        lcd_xpos = x;
        lcd_ypos = y;
        print_display (text);
}


//-----------------------------------------------------------
// + Plot (set one Pixel)
//-----------------------------------------------------------
// mode:
// 0=Clear, 1=Set, 2=XOR
void lcd_plot (uint8_t xpos, uint8_t ypos, uint8_t mode)
{
        uint16_t adress;
        uint8_t mask;

        if ((xpos < DISP_W) && (ypos < DISP_H))
        {
                adress = (ypos / 8) * DISP_W + xpos;            // adress = 0/8 * 128 + 0   = 0
                mask = 1 << (ypos & 0x07);                                      // mask = 1<<0 = 1
                adress &= DISP_BUFFER - 1;
                switch (mode)
                {

                case 0:
                        display_buffer[adress] &= ~mask;
                        break;

                case 1:
                        display_buffer[adress] |= mask;
                        break;

                case 2:
                        display_buffer[adress] ^= mask;
                        break;
                }
                set_adress (adress, display_buffer[adress]);
        }
}


//-----------------------------------------------------------
// + Line (draws a line from x1,y1 to x2,y2
// + Based on Bresenham line-Algorithm
// + found in the internet, modified by thkais 2007
//-----------------------------------------------------------

void lcd_line (unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, uint8_t mode)
{
        int x, y, count, xs, ys, xm, ym;

        x = (int) x1;
        y = (int) y1;
        xs = (int) x2 - (int) x1;
        ys = (int) y2 - (int) y1;
        if (xs < 0)
                xm = -1;
        else
                if (xs > 0)
                        xm = 1;
                else
                        xm = 0;
        if (ys < 0)
                ym = -1;
        else
                if (ys > 0)
                        ym = 1;
                else
                        ym = 0;
        if (xs < 0)
                xs = -xs;

        if (ys < 0)
                ys = -ys;

        lcd_plot ((unsigned char) x, (unsigned char) y, mode);

        if (xs > ys) // Flat Line <45 degrees
        {
                count = -(xs / 2);
                while (x != x2)
                {
                        count = count + ys;
                        x = x + xm;
                        if (count > 0)
                        {
                                y = y + ym;
                                count = count - xs;
                        }
                        lcd_plot ((unsigned char) x, (unsigned char) y, mode);
                }
        }
        else // Line >=45 degrees
        {
                count =- (ys / 2);
                while (y != y2)
                {
                        count = count + xs;
                        y = y + ym;
                        if (count > 0)
                        {
                                x = x + xm;
                                count = count - ys;
                        }
                        lcd_plot ((unsigned char) x, (unsigned char) y, mode);
                }
        }
}


//-----------------------------------------------------------
// + Filled rectangle
// + x1, y1 = upper left corner
//-----------------------------------------------------------

void lcd_frect (uint8_t x1, uint8_t y1, uint8_t widthx, uint8_t widthy, uint8_t mode)
{
        uint16_t x2, y2;
        uint16_t i;

        if (x1 >= DISP_W)
                x1 = DISP_W - 1;

        if (y1 >= DISP_H)
                y1 = DISP_H - 1;

        x2 = x1 + widthx;
        y2 = y1 + widthy;

        if (x2 > DISP_W)
                x2 = DISP_W;

        if (y2 > DISP_H)
                y2 = DISP_H;

        for (i = y1; i <= y2; i++)
        {
                lcd_line (x1, i, x2, i, mode);
        }
}


//-----------------------------------------------------------
// + outline of rectangle
// + x1, y1 = upper left corner
//-----------------------------------------------------------

void lcd_rect (uint8_t x1, uint8_t y1, uint8_t widthx, uint8_t widthy, uint8_t mode)
{
        uint16_t x2, y2;

        if (x1 >= DISP_W)
                x1 = DISP_W - 1;
        if (y1 >= DISP_H)
                y1 = DISP_H - 1;
        x2 = x1 + widthx;
        y2 = y1 + widthy;

        if (x2 > DISP_W)
                x2 = DISP_W;

        if (y2 > DISP_H)
                y2 = DISP_H;

        lcd_line (x1, y1, x2, y1, mode);
        lcd_line (x2, y1, x2, y2, mode);
        lcd_line (x2, y2, x1, y2, mode);
        lcd_line (x1, y2, x1, y1, mode);
}


//-----------------------------------------------------------
// + outline of a circle
// + Based on Bresenham-algorithm found in wikipedia
// + modified by thkais (2007)
//-----------------------------------------------------------

void lcd_circle (int16_t x0, int16_t y0, int16_t radius, uint8_t mode)
{
        int16_t f = 1 - radius;
        int16_t ddF_x = 0;
        int16_t ddF_y = -2 * radius;
        int16_t x = 0;
        int16_t y = radius;

        lcd_plot (x0, y0 + radius, mode);
        lcd_plot (x0, y0 - radius, mode);
        lcd_plot (x0 + radius, y0, mode);
        lcd_plot (x0 - radius, y0, mode);

        while (x < y)
        {
                if (f >= 0)
                {
                        y --;
                        ddF_y += 2;
                        f += ddF_y;
                }
                x ++;
                ddF_x += 2;
                f += ddF_x + 1;

                lcd_plot (x0 + x, y0 + y, mode);
                lcd_plot (x0 - x, y0 + y, mode);

                lcd_plot (x0 + x, y0 - y, mode);
                lcd_plot (x0 - x, y0 - y, mode);

                lcd_plot (x0 + y, y0 + x, mode);
                lcd_plot (x0 - y, y0 + x, mode);

                lcd_plot (x0 + y, y0 - x, mode);
                lcd_plot (x0 - y, y0 - x, mode);
        }
}


//-----------------------------------------------------------
// + filled Circle
// + modified circle-algorithm thkais (2007)
//-----------------------------------------------------------

void lcd_fcircle (int16_t x0, int16_t y0, int16_t radius,uint8_t mode)
{
        int16_t f = 1 - radius;
        int16_t ddF_x = 0;
        int16_t ddF_y = -2 * radius;
        int16_t x = 0;
        int16_t y = radius;

        lcd_line (x0, y0 + radius, x0, y0 - radius, mode);

        lcd_line (x0 + radius, y0, x0 - radius, y0, mode);

        while (x < y)
        {
                if (f >= 0)
                {
                        y--;
                        ddF_y += 2;
                        f += ddF_y;
                }
                x++;
                ddF_x += 2;
                f += ddF_x + 1;

                lcd_line (x0 + x, y0 + y, x0 - x, y0 + y, mode);
                lcd_line (x0 + x, y0 - y, x0 - x, y0 - y, mode);
                lcd_line (x0 + y, y0 + x, x0 - y, y0 + x, mode);
                lcd_line (x0 + y, y0 - x, x0 - y, y0 - x, mode);
        }
}


//-----------------------------------------------------------
//
void lcd_circ_line (uint8_t x, uint8_t y, uint8_t r, uint16_t deg, uint8_t mode)
{
        uint8_t xc, yc;
        double deg_rad;

        deg_rad = (deg * M_PI) / 180.0;

        yc = y - (uint8_t) round (cos (deg_rad) * (double) r);
        xc = x + (uint8_t) round (sin (deg_rad) * (double) r);
        lcd_line (x, y, xc, yc, mode);
}


//-----------------------------------------------------------
//
void lcd_ellipse_line (uint8_t x, uint8_t y, uint8_t rx, uint8_t ry, uint16_t deg, uint8_t mode)
{
        uint8_t xc, yc;
        double deg_rad;

        deg_rad = (deg * M_PI) / 180.0;

        yc = y - (uint8_t) round (cos (deg_rad) * (double) ry);
        xc = x + (uint8_t) round (sin (deg_rad) * (double) rx);
        lcd_line (x, y, xc, yc, mode);
}


//-----------------------------------------------------------
//
void lcd_ellipse (int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint8_t mode)
{
        const int16_t rx2 = rx * rx;
        const int16_t ry2 = ry * ry;
        int16_t F = round (ry2 - rx2 * ry + 0.25 * rx2);
        int16_t ddF_x = 0;
        int16_t ddF_y = 2 * rx2 * ry;
        int16_t x = 0;
        int16_t y = ry;

        lcd_plot (x0, y0 + ry, mode);
        lcd_plot (x0, y0 - ry, mode);
        lcd_plot (x0 + rx, y0, mode);
        lcd_plot (x0 - rx, y0, mode);
        // while ( 2*ry2*x < 2*rx2*y ) {  we can use ddF_x and ddF_y
        while (ddF_x < ddF_y)
        {
                if(F >= 0)
                {
                        y     -= 1;             // south
                        ddF_y -= 2 * rx2;
                        F     -= ddF_y;
                }
                x     += 1;                     // east
                ddF_x += 2 * ry2;
                F     += ddF_x + ry2;
                lcd_plot (x0 + x, y0 + y, mode);
                lcd_plot (x0 + x, y0 - y, mode);
                lcd_plot (x0 - x, y0 + y, mode);
                lcd_plot (x0 - x, y0 - y, mode);
        }
        F = round (ry2 * (x + 0.5) * (x + 0.5) + rx2 * (y - 1) * (y - 1) - rx2 * ry2);
        while(y > 0)
        {
                if(F <= 0)
                {
                        x     += 1;             // east
                        ddF_x += 2 * ry2;
                        F     += ddF_x;
                }
                y     -= 1;                     // south
                ddF_y -= 2 * rx2;
                F     += rx2 - ddF_y;
                lcd_plot (x0 + x, y0 + y, mode);
                lcd_plot (x0 + x, y0 - y, mode);
                lcd_plot (x0 - x, y0 + y, mode);
                lcd_plot (x0 - x, y0 - y, mode);
        }
}


//-----------------------------------------------------------
//
void lcd_ecircle (int16_t x0, int16_t y0, int16_t radius, uint8_t mode)
{
        lcd_ellipse (x0, y0, radius + 3, radius, mode);
}


//-----------------------------------------------------------
//
void lcd_ecirc_line (uint8_t x, uint8_t y, uint8_t r, uint16_t deg, uint8_t mode)
{
        lcd_ellipse_line(x, y, r + 3, r, deg, mode);
}


//-----------------------------------------------------------
//
void lcd_view_font (uint8_t page)
{
        int x;
        int y;

        lcd_cls ();
        lcd_printp (PSTR("  0123456789ABCDEF\r\n"), 0);
        lcd_printpns_at (0, 7, PSTR(" \x1a    \x1b     Exit"), 0);

        lcd_ypos = 2;
        for (y = page * 4 ; y < (page * 4 + 4); y++)
        {
                if (y < 10)
                {
                        lcd_putc (0, lcd_ypos, '0' + y, 0);
                }
                else
                {
                        lcd_putc (0, lcd_ypos, 'A' + y - 10, 0);
                }
                lcd_xpos = 2;
                for (x = 0; x < 16; x++)
                {
                        lcd_putc (lcd_xpos, lcd_ypos, y * 16 + x, 0);
                        lcd_xpos++;
                }
                lcd_ypos++;
        }
}


//-----------------------------------------------------------
uint8_t hdigit (uint8_t d)
{
        if (d < 10)
        {
                return '0' + d;
        }
        else
        {
                return 'A' + d - 10;
        }
}


//-----------------------------------------------------------
void lcd_print_hex_at (uint8_t x, uint8_t y, uint8_t h, uint8_t mode)
{
        lcd_xpos = x;
        lcd_ypos = y;

        lcd_putc (lcd_xpos++, lcd_ypos, hdigit (h >> 4), mode);
        lcd_putc (lcd_xpos, lcd_ypos, hdigit (h & 0x0f), mode);
}


//-----------------------------------------------------------
void lcd_print_hex (uint8_t h, uint8_t mode)
{
//      lcd_xpos = x;
//      lcd_ypos = y;

        lcd_putc (lcd_xpos++, lcd_ypos, hdigit (h >> 4), mode);
        lcd_putc (lcd_xpos++, lcd_ypos, hdigit (h & 0x0f), mode);
        lcd_putc (lcd_xpos++, lcd_ypos, ' ', mode);
}


//-----------------------------------------------------------
void lcd_write_number_u (uint8_t number)
{
        uint8_t num = 100;
        uint8_t started = 0;

        while (num > 0)
        {
                uint8_t b = number / num;
                if (b > 0 || started || num == 1)
                {
                        lcd_putc (lcd_xpos++, lcd_ypos, '0' + b, 0);
                        started = 1;
                }
                number -= b * num;

                num /= 10;
        }
}


//-----------------------------------------------------------
void lcd_write_number_u_at (uint8_t x, uint8_t y, uint8_t number)
{
        lcd_xpos = x;
        lcd_ypos = y;
        lcd_write_number_u (number);
}


//-----------------------------------------------------------
// Write only some digits of a unsigned <number> at <x>/<y> to MAX7456 display memory
// <num> represents the largest multiple of 10 that will still be displayable as
// the first digit, so num = 10 will be 0-99 and so on
// <pad> = 1 will cause blank spaced to be filled up with zeros e.g. 007 instead of   7
//
void write_ndigit_number_u (uint8_t x, uint8_t y, uint16_t number, int16_t length, uint8_t pad, uint8_t mode)
{
        char s[7];

        utoa(number, s, 10 );

        uint8_t len = strlen(s);

        if (length < len)
        {
                for (uint8_t i = 0; i < length; i++)
                {
                        lcd_putc (x++, y, '*', mode);
                }
                return;
        }

        for (uint8_t i = 0; i < length - len; i++)
        {
                if (pad==1)
                {
                        lcd_putc (x++, y, '0', mode);
                }
                else
                {
                        lcd_putc (x++, y, ' ', mode);
                }
        }
        lcd_print_at(x, y, (uint8_t*)s, mode);
}

//-----------------------------------------------------------
// Write only some digits of a signed <number> at <x>/<y> to MAX7456 display memory
// <num> represents the largest multiple of 10 that will still be displayable as
// the first digit, so num = 10 will be 0-99 and so on
// <pad> = 1 will cause blank spaced to be filled up with zeros e.g. 007 instead of   7
//
void write_ndigit_number_s (uint8_t x, uint8_t y, int16_t number, int16_t length, uint8_t pad, uint8_t mode)
{
        char s[7];

        itoa(number, s, 10 );

        uint8_t len = strlen(s);

        if (length < len)
        {
                for (uint8_t i = 0; i < length; i++)
                {
                        lcd_putc (x++, y, '*', mode);
                }
                return;
        }

        for (uint8_t i = 0; i < length - len; i++)
        {
                if (pad)
                {
                        lcd_putc (x++, y, '0', mode);
                }
                else
                {
                        lcd_putc (x++, y, ' ', mode);
                }
        }
        lcd_print_at(x, y, (uint8_t*)s, mode);
}


//-----------------------------------------------------------
// Write only some digits of a unsigned <number> at <x>/<y> to MAX7456 display memory
// as /10th of the value
// <num> represents the largest multiple of 10 that will still be displayable as
// the first digit, so num = 10 will be 0-99 and so on
// <pad> = 1 will cause blank spaced to be filled up with zeros e.g. 007 instead of   7
//
void write_ndigit_number_u_10th (uint8_t x, uint8_t y, uint16_t number, int16_t length, uint8_t pad, uint8_t mode)
{
        char s[7];

        itoa(number, s, 10 );

        uint8_t len = strlen(s);

        if (length < len)
        {
                for (uint8_t i = 0; i < length; i++)
                {
                        lcd_putc (x++, y, '*', mode);
                }
                return;
        }

        for (uint8_t i = 0; i < length - len; i++)
        {
                if (pad)
                {
                        lcd_putc (x++, y, '0', mode);
                }
                else
                {
                        lcd_putc (x++, y, ' ', mode);
                }
        }

        char rest = s[len - 1];

        s[len - 1] = 0;

        if (len == 1)
        {
                lcd_putc (x-1, y, '0', mode);
        }
        else if (len == 2 && s[0] == '-')
        {
                lcd_putc (x-1, y, '-', mode);
                lcd_putc (x, y, '0', mode);
        }
        else
        {
                lcd_print_at(x, y, (uint8_t*)s, mode);
        }
        x += len - 1;
        lcd_putc (x++, y, '.', mode);
        lcd_putc (x++, y, rest, mode);
}


//-----------------------------------------------------------
void write_ndigit_number_u_100th (uint8_t x, uint8_t y, uint16_t number, int16_t length, uint8_t pad)
{
        uint8_t num = 100;

        while (num > 0)
        {
                uint8_t b = number / num;

                if ((num / 10) == 1)
                {
                        lcd_putc (x++, y, '.', 0);
                }
                lcd_putc (x++, y, '0' + b, 0);
                number -= b * num;

                num /= 10;
        }
}


//-----------------------------------------------------------
// Write only some digits of a signed <number> at <x>/<y> to MAX7456 display memory
// as /10th of the value
// <num> represents the largest multiple of 10 that will still be displayable as
// the first digit, so num = 10 will be 0-99 and so on
// <pad> = 1 will cause blank spaced to be filled up with zeros e.g. 007 instead of   7
//
void write_ndigit_number_s_10th (uint8_t x, uint8_t y, int16_t number, int16_t length, uint8_t pad, uint8_t mode)
{
        char s[7];

        itoa (number, s, 10 );

        uint8_t len = strlen(s);

        if (length < len)
        {
                for (uint8_t i = 0; i < length; i++)
                {
                        lcd_putc (x++, y, '*', mode);
                }
                return;
        }

        for (uint8_t i = 0; i < length - len; i++)
        {
                if (pad)
                {
                        lcd_putc (x++, y, '0', mode);
                }
                else
                {
                        lcd_putc (x++, y, ' ', mode);
                }
        }

        char rest = s[len - 1];

        s[len - 1] = 0;

        if (len == 1)
        {
                lcd_putc (x-1, y, '0', mode);
        }
        else if (len == 2 && s[0] == '-')
        {
                lcd_putc (x-1, y, '-', mode);
                lcd_putc (x, y, '0', mode);
        }
        else
        {
                lcd_print_at(x, y, (uint8_t*)s, mode);
        }
        x += len - 1;
        lcd_putc (x++, y, '.', mode);
        lcd_putc (x++, y, rest, mode);
}


//-----------------------------------------------------------
// write <seconds> as human readable time at <x>/<y> to MAX7456 display mem
//
void write_time (uint8_t x, uint8_t y, uint16_t seconds)
{
        uint16_t min = seconds / 60;
        seconds -= min * 60;
        write_ndigit_number_u (x, y, min, 2, 0,0);
        lcd_putc (x + 2, y, ':', 0);
        write_ndigit_number_u (x + 3, y, seconds, 2, 1,0);
}


//-----------------------------------------------------------
// wirte a <position> at <x>/<y> assuming it is a gps position for long-/latitude
//
void write_gps_pos (uint8_t x, uint8_t y, int32_t position)
{
        if (position < 0)
        {
                position ^= ~0;
                position++;
                lcd_putc (x++, y, '-', 0);
        }
        else
        {
                lcd_putc (x++, y, ' ', 0);
        }
        write_ndigit_number_u (x, y, (uint16_t) (position / (int32_t) 10000000), 3, 1,0);
        lcd_putc (x + 3, y, '.', 0);
        position = position - ((position / (int32_t) 10000000) * (int32_t) 10000000);
        write_ndigit_number_u (x + 4, y, (uint16_t) (position / (int32_t) 1000), 4, 1,0);
        position = position - ((uint16_t) (position / (int32_t) 1000) * (int32_t) 1000);
        write_ndigit_number_u (x + 8, y, (uint16_t) position, 3, 1,0);
        lcd_putc (x + 11, y, 0x1e, 0);  // degree symbol
}