В начало | Зарегистрироваться | Заказать наши киты почтой
 
 
 
 

GPS-модуль "NEO-6M", получение и вывод данных на дисплей SSD1283A

📆1 июля 2024   ✒️ayan   🔎134   💬0  

Здравствуйте, уважаемые жители и гости Датагора! Представляю проект обмена данными между микроконтроллером ATmega328P и GPS-модулем NEO-6M.
Четкий прокомментированный исходный код на Си прилагается.

Видео-демонстрация работы проекта




Краткое описание работы модуля

Примерно раз в секунду модуль по протоколу UART со скоростью 9600 bps выдаёт на пин TX пакет символьных данных из представленных на Рисунке 1 шести предложений (sentence), подробнее о которых можно узнать здесь.
GPS-модуль "NEO-6M", получение и вывод данных на дисплей SSD1283A
Рисунок 1. Пакет предложений модуля NEO-6M

Каждое предложение начинается символом «$» и завершается символом «*», после которого следуют контрольная сумма (КС), а также символы возврата каретки «CR» и новой строки «LF», не отображённые на рисунке.
Нас будут интересовать данные трёх предложений: GPRMC (время, дата, широта и долгота), GPVTG (скорость) и GPGGA (высота над уровнем моря). До соединения со спутником данные, кроме времени и даты, — не валидные, что подтверждается символом «V», следующим за значением времени в GPRMC. После соединения, сопровождаемого блинком имеющегося на борту модуля синего светодиода, начинают поступать валидные данные, о чём свидетельствует смена упомянутого символа на «А»

Код проекта

Программа написана для ATmega328P на Си в Visual Studio Code. Скачать архив с исходниками можно внизу, в разделе файлов.

Управление GPS-модулем

Процесс заключается в:
• настройке протокола UART,
• считыванию и сортировке интересующих данных,
• подсчёте КС предложения (являющейся результатом последовательной операции ИСКЛЮЧАЮЩЕЕ ИЛИ для символов между «$» и «*») и её сверкой с контрольной суммой, полученной от модуля.

При этом, необходимо учитывать следующее:
1. Время между битами, а так же предложениями – чуть более 100 мкс.
2. Из не валидных пакетов следует хранить только значения времени и даты.
3. Желательно обойтись без буферизации.

Учитывая вышеизложенное, структура библиотеки NEO6 включает следующие функции:
а) gps_init():
• устанавливает скорость 9600 bps,
• настраивает пины RX и TX микроконтроллера,
• глобально и локально разрешает прерывание по приёму байта данных.

б) gps_parse_data() в зависимости от имени текущего предложения и порядкового номера принятого символа сохраняет последний в соответствующем поле структуры gps.

в) Обработчик прерывания:
• определяет имя текущего предложения и порядковый номер полученного символа, а затем вызывает функцию gps_parse_data(),
• подсчитывает контрольную сумму предложения, сравнивает её с принятой и сохраняет результат сравнения (false или true) в поле check_sum структуры.
• по завершению пакета устанавливает в 1 флаг packet_end_flag.

#ifndef NEO6_H_
#define NEO6_H_

#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>
#include <avr/interrupt.h>
#include "display.h"

#define UART_BAUDRATE       9600
#define FREQUENCY           16000000UL
#define UBRR_VALUE          FREQUENCY / 16 / UART_BAUDRATE - 1

#define	SENTENCE_IS_GPRMC   gps.header[0] == 'M' && gps.header[1] == 'C'
#define SENTENCE_IS_GPVTG   gps.header[0] == 'T' && gps.header[1] == 'G'
#define SENTENCE_IS_GPGGA   gps.header[0] == 'G' && gps.header[1] == 'A'
#define SENTENCE_IS_GPGSA   gps.header[0] == 'S' && gps.header[1] == 'A'
#define SENTENCE_IS_GPGSV   gps.header[0] == 'S' && gps.header[1] == 'V'
#define SENTENCE_IS_GPGLL   gps.header[0] == 'L' && gps.header[1] == 'L'

typedef struct
{
  uint8_t counter;
  bool do_check_sum;
  volatile uint8_t sentence  :4;
  bool packet_end_flag;	
  uint8_t status             :2;
  uint8_t header[2];
	
  uint8_t hours_tens;
  uint8_t hours_units;
  uint8_t minutes_tens;
  uint8_t minutes_units;
  uint8_t seconds_tens;
  uint8_t seconds_units;
  uint8_t date_tens;
  uint8_t date_units;
  uint8_t month_tens;
  uint8_t month_units;
  uint8_t year_tens;
  uint8_t year_units;
  uint8_t latitude_degree_1;
  uint8_t latitude_degree_2;
  uint8_t latitude_minutes_1;
  uint8_t latitude_minutes_2;
  uint8_t latitude_minutes_3;
  uint8_t latitude_minutes_4;
  uint8_t latitude_minutes_5;
  uint8_t latitude_minutes_6;
  uint8_t latitude_minutes_7;
  uint8_t longitude_degree_1;
  uint8_t longitude_degree_2;
  uint8_t longitude_minutes_1;
  uint8_t longitude_minutes_2;
  uint8_t longitude_minutes_3;
  uint8_t longitude_minutes_4;
  uint8_t longitude_minutes_5;
  uint8_t longitude_minutes_6;
  uint8_t longitude_minutes_7;
  uint8_t altitude_hundreds;	
  uint8_t altitude_tens;	
  uint8_t altitude_units;	
  uint8_t altitude_fraction;	
  uint8_t altitude_unit;
  uint8_t check_sum_high_received;
  uint8_t check_sum_low_received;
  uint8_t speed_integer;
  uint8_t speed_fraction_1;
  uint8_t speed_fraction_2;
  uint8_t speed_fraction_3;
  uint8_t speed_indicator;
  bool check_sum[7];
} NEO6_t;

extern NEO6_t gps;

enum {NO_STATUS, STATUS_INVALID, STATUS_VALID};
enum {NO_SENTENCE, GPRMC, GPVTG, GPGGA, GPGSA,  GPGSV,  GPGLL};
enum {CHECK_SUM_RECEIVED, CHECK_SUM_CALCULATED};
	
/* gprmc */
enum 
{
  GPRMC_HOURS_TENS = 8, GPRMC_HOURS_UNITS, GPRMC_MINUTES_TENS, GPRMC_MINUTES_UNITS, GPRMC_SECONDS_TENS, GPRMC_SECONDS_UNITS, GPRMC_STATUS = 18, \
  GPRMC_LATITUDE_DEGREE_1 = 20, GPRMC_LATITUDE_DEGREE_2, GPRMC_LATITUDE_MINUTES_1, GPRMC_LATITUDE_MINUTES_2, GPRMC_LATITUDE_MINUTES_3 = 25, GPRMC_LATITUDE_MINUTES_4, GPRMC_LATITUDE_MINUTES_5, GPRMC_LATITUDE_MINUTES_6, GPRMC_LATITUDE_MINUTES_7, \
  GPRMC_LONGITUDE_DEGREE_1 = 34, GPRMC_LONGITUDE_DEGREE_2, GPRMC_LONGITUDE_MINUTES_1, GPRMC_LONGITUDE_MINUTES_2, GPRMC_LONGITUDE_MINUTES_3 = 39, GPRMC_LONGITUDE_MINUTES_4, GPRMC_LONGITUDE_MINUTES_5, GPRMC_LONGITUDE_MINUTES_6, GPRMC_LONGITUDE_MINUTES_7, \
  GPRMC_DATE_TENS = 54, GPRMC_DATE_UNITS, GPRMC_MONTH_TENS, GPRMC_MONTH_UNITS, GPRMC_YEAR_TENS, GPRMC_YEAR_UNITS, \
  GPRMC_CHECK_SUM_HIGH = 65, GPRMC_CHECK_SUM_LOW
};
enum {GPRMC_DATE_TENS_INVALID = 26, GPRMC_DATE_UNITS_INVALID, GPRMC_MONTH_TENS_INVALID, GPRMC_MONTH_UNITS_INVALID, GPRMC_YEAR_TENS_INVALID, GPRMC_YEAR_UNITS_INVALID, GPRMC_CHECK_SUM_HIGH_INVALID = 37, GPRMC_CHECK_SUM_LOW_INVALID};

/* gpvtg */
enum {GPVTG_SPEED_INTEGER = 22, GPVTG_SPEED_FRACTION_1 = 24, GPVTG_SPEED_FRACTION_2, GPVTG_SPEED_FRACTION_3, GPVTG_SPEED_INDICATOR = 28, GPVTG_CHECK_SUM_HIGH = 32, GPVTG_CHECK_SUM_LOW};
enum {GPVTG_CHECK_SUM_HIGH_INVALID = 18, GPVTG_CHECK_SUM_LOW_INVALID};
	
/* gpgga */
enum {GPGGA_ALTITUDE_HUNDREDS = 55, GPGGA_ALTITUDE_TENS, GPGGA_ALTITUDE_UNITS, GPGGA_ALTITUDE_FRACTION = 59, GPGGA_ALTITUDE_UNIT = 61, GPGGA_CHECK_SUM_HIGH = 73, GPGGA_CHECK_SUM_LOW};
enum {GPGGA_CHECK_SUM_HIGH_INVALID = 39, GPGGA_CHECK_SUM_LOW_INVALID};
	
/* gpgsa */
enum {GPGSA_CHECK_SUM_HIGH = 47, GPGSA_CHECK_SUM_LOW};
enum {GPGSA_CHECK_SUM_HIGH_INVALID = 42, GPGSA_CHECK_SUM_LOW_INVALID};
	
/* gpgsv */
enum {GPGSV_CHECK_SUM_HIGH = 67, GPGSV_CHECK_SUM_LOW};
enum {GPGSV_CHECK_SUM_HIGH_INVALID = 31, GPGSV_CHECK_SUM_LOW_INVALID};	
	
/* gpgll */
enum {GPGLL_CHECK_SUM_HIGH = 49, GPGLL_CHECK_SUM_LOW};
enum {GPGLL_CHECK_SUM_HIGH_INVALID = 26, GPGLL_CHECK_SUM_LOW_INVALID};	
	
void gps_init();
void gps_parse_data(uint8_t data);
void serialSendByte(char symbol);
void serialPrintln(const char* buff);
void serialPrint(const char* buff);

#endif /* NEO6_H_ */

#include "NEO6.h"

NEO6_t gps = {.do_check_sum = false, .status = STATUS_INVALID, .sentence = NO_SENTENCE, .packet_end_flag = false};

ISR(USART_RX_vect)
{
  static uint8_t check_sum = 0;
	
  uint8_t data = UDR0;		
	
  gps.counter++;		
  if(gps.counter == 5)	
    gps.header[0] = data;
  else if(gps.counter == 6)
  {	
    gps.header[1] = data;
    if(SENTENCE_IS_GPRMC)
      gps.sentence = GPRMC;
    else if(SENTENCE_IS_GPVTG)
      gps.sentence = GPVTG;
    else if(SENTENCE_IS_GPGGA)
      gps.sentence = GPGGA;
    else if(SENTENCE_IS_GPGSA)
      gps.sentence = GPGSA;
    else if(SENTENCE_IS_GPGSV)
      gps.sentence = GPGSV;
    else if(SENTENCE_IS_GPGLL)
      gps.sentence = GPGLL;
    else 
      gps.sentence = NO_SENTENCE;			
  }		
  else if(gps.counter > 6 && gps.sentence != NO_SENTENCE)
    gps_parse_data(data);		
		
  switch(data)
  {
    case '$':
      gps.do_check_sum = true;
    break;
		
    case '*':
    case 13:
      gps.do_check_sum = false;
    break;
		
    case 10:
    {
      uint8_t check_sum_high_calculated = check_sum >> 4;
      uint8_t check_sum_low_calculated = check_sum & 0x0F;	
			
      if(gps.check_sum_high_received >= '0' && gps.check_sum_high_received <= '9')
        gps.check_sum_high_received -= 48;
      else if(gps.check_sum_high_received >= 'A' && gps.check_sum_high_received <= 'F')
        gps.check_sum_high_received -= 55;
		
      if(gps.check_sum_low_received >= '0' && gps.check_sum_low_received <= '9')
        gps.check_sum_low_received -= 48;
      else if(gps.check_sum_low_received >= 'A' && gps.check_sum_low_received <= 'F')
        gps.check_sum_low_received -= 55;
				
      if(check_sum_high_calculated == gps.check_sum_high_received && check_sum_low_calculated == gps.check_sum_low_received)	
        gps.check_sum[gps.sentence] = true;
      else 
        gps.check_sum[gps.sentence] = false;	
       
      gps.do_check_sum = false;
      gps.counter = 0;							
      check_sum = 0;	
      if(gps.sentence == GPGLL)
        gps.packet_end_flag = true;
    }
    break;
		
    default:
      if(gps.do_check_sum == true)
        check_sum ^= (uint8_t)data;			
    break;
  }
}

void gps_init()
{
  unsigned int ubrr_value = UBRR_VALUE;

  UBRR0H = (unsigned char)(ubrr_value >> 8);
  UBRR0L = (unsigned char)(ubrr_value);
  UCSR0B |= (1 << RXCIE0) | (1 << TXEN0) | (1 << RXEN0);
  UCSR0C |= (3 << UCSZ00);
  sei();
}

void gps_parse_data(uint8_t data)
{	
  if(gps.sentence == GPRMC)
  {
    if(gps.counter == GPRMC_STATUS && data == 'V')
      gps.status = STATUS_INVALID;
    if(gps.counter == GPRMC_STATUS && data == 'A')
      gps.status = STATUS_VALID;
		
    switch(gps.status)
    {
      case STATUS_INVALID:
        if(gps.counter == GPRMC_HOURS_TENS)
          gps.hours_tens = data;
        else if(gps.counter == GPRMC_HOURS_UNITS)
          gps.hours_units = data;
        else if(gps.counter == GPRMC_MINUTES_TENS)
          gps.minutes_tens = data;
        else if(gps.counter == GPRMC_MINUTES_UNITS)
          gps.minutes_units = data;
        else if(gps.counter == GPRMC_SECONDS_TENS)
          gps.seconds_tens = data;
        else if(gps.counter == GPRMC_SECONDS_UNITS)
          gps.seconds_units = data;
        else if(gps.counter == GPRMC_DATE_TENS_INVALID)
          gps.date_tens = data;
        else if(gps.counter == GPRMC_DATE_UNITS_INVALID) 
          gps.date_units = data;
        else if(gps.counter == GPRMC_MONTH_TENS_INVALID)
          gps.month_tens = data;
        else if(gps.counter == GPRMC_MONTH_UNITS_INVALID)
          gps.month_units = data; 
        else if(gps.counter == GPRMC_YEAR_TENS_INVALID)
          gps.year_tens = data;
        else if(gps.counter == GPRMC_YEAR_UNITS_INVALID)
          gps.year_units = data;
        else if(gps.counter == GPRMC_YEAR_UNITS_INVALID)
          gps.year_units = data; 
        else if(gps.counter == GPRMC_CHECK_SUM_HIGH_INVALID) 
          gps.check_sum_high_received = data;					
        else if(gps.counter == GPRMC_CHECK_SUM_LOW_INVALID)
          gps.check_sum_low_received = data;					
      break;
			
      case STATUS_VALID:
        /* time */
        if(gps.counter == GPRMC_HOURS_TENS)
          gps.hours_tens = data;
        else if(gps.counter == GPRMC_HOURS_UNITS)
          gps.hours_units = data;
        else if(gps.counter == GPRMC_MINUTES_TENS)
          gps.minutes_tens = data;
        else if(gps.counter == GPRMC_MINUTES_UNITS)
          gps.minutes_units = data;
        else if(gps.counter == GPRMC_SECONDS_TENS)
          gps.seconds_tens = data;
        else if(gps.counter == GPRMC_SECONDS_UNITS)
          gps.seconds_units = data;
        /* latitude */
        if(gps.counter == GPRMC_LATITUDE_DEGREE_1)
          gps.latitude_degree_1= data;
        else if(gps.counter == GPRMC_LATITUDE_DEGREE_2)
          gps.latitude_degree_2 = data;
        else if(gps.counter == GPRMC_LATITUDE_MINUTES_1)
          gps.latitude_minutes_1 = data;
        else if(gps.counter == GPRMC_LATITUDE_MINUTES_2)
          gps.latitude_minutes_2 = data;
        else if(gps.counter == GPRMC_LATITUDE_MINUTES_3)
          gps.latitude_minutes_3 = data;
        else if(gps.counter == GPRMC_LATITUDE_MINUTES_4)
          gps.latitude_minutes_4 = data;
        else if(gps.counter == GPRMC_LATITUDE_MINUTES_5)
          gps.latitude_minutes_5 = data;
        else if(gps.counter == GPRMC_LATITUDE_MINUTES_6)
          gps.latitude_minutes_6 = data;
        else if(gps.counter == GPRMC_LATITUDE_MINUTES_7)
          gps.latitude_minutes_7 = data;
        /* longitude */
        else if(gps.counter == GPRMC_LONGITUDE_DEGREE_1)
          gps.longitude_degree_1 = data;
        else if(gps.counter == GPRMC_LONGITUDE_DEGREE_2)
          gps.longitude_degree_2 = data;
        else if(gps.counter == GPRMC_LONGITUDE_MINUTES_1)
          gps.longitude_minutes_1 = data;
        else if(gps.counter == GPRMC_LONGITUDE_MINUTES_2)
          gps.longitude_minutes_2 = data;
        else if(gps.counter == GPRMC_LONGITUDE_MINUTES_3)
          gps.longitude_minutes_3 = data;
        else if(gps.counter == GPRMC_LONGITUDE_MINUTES_4)
          gps.longitude_minutes_4 = data;
        else if(gps.counter == GPRMC_LONGITUDE_MINUTES_5)
          gps.longitude_minutes_5 = data;
        else if(gps.counter == GPRMC_LONGITUDE_MINUTES_6)
          gps.longitude_minutes_6 = data;
        else if(gps.counter == GPRMC_LONGITUDE_MINUTES_7)
          gps.longitude_minutes_7 = data;
        /* date */
        else if(gps.counter == GPRMC_DATE_TENS)
          gps.date_tens = data;
        else if(gps.counter == GPRMC_DATE_UNITS)
          gps.date_units = data;
        else if(gps.counter == GPRMC_MONTH_TENS)
          gps.month_tens = data;
        else if(gps.counter == GPRMC_MONTH_UNITS)
          gps.month_units = data;
        else if(gps.counter == GPRMC_YEAR_TENS)
          gps.year_tens = data;
        else if(gps.counter == GPRMC_YEAR_UNITS)
          gps.year_units = data;
        /* check sum */
        else if(gps.counter == GPRMC_CHECK_SUM_HIGH)
          gps.check_sum_high_received = data;
        else if(gps.counter == GPRMC_CHECK_SUM_LOW)
          gps.check_sum_low_received = data;					
      break;		
			
      default:
      break;				
    }
  }			
  else if(gps.sentence == GPVTG)
  {
    if(gps.status == STATUS_INVALID)
    {
      if(gps.counter == GPVTG_CHECK_SUM_HIGH_INVALID)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPVTG_CHECK_SUM_LOW_INVALID)
        gps.check_sum_low_received = data;
    }
    else if(gps.status == STATUS_VALID)
    {
      if(gps.counter == GPVTG_SPEED_INTEGER)
        gps.speed_integer = data;
      else if(gps.counter == GPVTG_SPEED_FRACTION_1)
        gps.speed_fraction_1 = data;				
      else if(gps.counter == GPVTG_SPEED_FRACTION_2)
        gps.speed_fraction_2 = data;				
      else if(gps.counter == GPVTG_SPEED_FRACTION_3)
        gps.speed_fraction_3 = data;				
      else if(gps.counter == GPVTG_SPEED_INDICATOR)
        gps.speed_indicator = data;				
      else if(gps.counter == GPVTG_CHECK_SUM_HIGH)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPVTG_CHECK_SUM_LOW)
        gps.check_sum_low_received = data;
    }	
  }	
  else if(gps.sentence == GPGGA)
  {
    if(gps.status == STATUS_INVALID)
    {
      if(gps.counter == GPGGA_CHECK_SUM_HIGH_INVALID)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPGGA_CHECK_SUM_LOW_INVALID)
        gps.check_sum_low_received = data;			
    }
    else if(gps.status == STATUS_VALID)
    {
      if(gps.counter == GPGGA_ALTITUDE_HUNDREDS)
        gps.altitude_hundreds = data;
      else if(gps.counter == GPGGA_ALTITUDE_TENS)
        gps.altitude_tens = data;
      else if(gps.counter == GPGGA_ALTITUDE_UNITS)
        gps.altitude_units = data;
      else if(gps.counter == GPGGA_ALTITUDE_FRACTION)
        gps.altitude_fraction = data;
      else if(gps.counter == GPGGA_ALTITUDE_UNIT)
        gps.altitude_unit = data;	
      else if(gps.counter == GPGGA_CHECK_SUM_HIGH)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPGGA_CHECK_SUM_LOW)
        gps.check_sum_low_received = data;					
    }
  }
  else if(gps.sentence == GPGSA)
  {
    if(gps.status == STATUS_INVALID)
    {
      if(gps.counter == GPGSA_CHECK_SUM_HIGH_INVALID)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPGSA_CHECK_SUM_LOW_INVALID)
        gps.check_sum_low_received = data;			
    }
    else if(gps.status == STATUS_VALID)
    {
      if(gps.counter == GPGSA_CHECK_SUM_HIGH)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPGSA_CHECK_SUM_LOW)
        gps.check_sum_low_received = data;			
    }
  }	
  else if(gps.sentence == GPGSV)
  {
    if(gps.status == STATUS_INVALID)
    {
      if(gps.counter == GPGSV_CHECK_SUM_HIGH_INVALID)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPGSV_CHECK_SUM_LOW_INVALID)
        gps.check_sum_low_received = data;
    }
    else if(gps.status == STATUS_VALID)
    {
      if(gps.counter == GPGSV_CHECK_SUM_HIGH)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPGSV_CHECK_SUM_LOW)
        gps.check_sum_low_received = data;
    }
  }	
  else if(gps.sentence == GPGLL)
  {
    if(gps.status == STATUS_INVALID)
    {
      if(gps.counter == GPGLL_CHECK_SUM_HIGH_INVALID)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPGLL_CHECK_SUM_LOW_INVALID)
        gps.check_sum_low_received = data;
    }
    else if(gps.status == STATUS_VALID)
    {
      if(gps.counter == GPGLL_CHECK_SUM_HIGH)
        gps.check_sum_high_received = data;
      else if(gps.counter == GPGLL_CHECK_SUM_LOW)
        gps.check_sum_low_received = data;
    }
  }	
}

void serialSendByte(char data)
{
  while (!(UCSR0A & (1 << UDRE0)));
  UDR0 = data;
}

void serialPrintln(const char* buff)
{
  while(*buff != '\0')
  {
    serialSendByte(*buff);
    buff++;
  }
  serialSendByte('\n');
}

void serialPrint(const char* buff)
{
  while(*buff != '\0')
  {
    serialSendByte(*buff);
    buff++;
  }
}

Вывод данных на дисплей

Для визуализации принимаемых GPS-данных выбран дисплей SSD1283A, за работу которого отвечают пять библиотек:

SPI содержит код одноимённого программного протокола.
#ifndef SPI_H_
#define SPI_H_

#define F_CPU          16000000UL

#include <util/delay.h>
#include <avr/io.h>
#include <stdint.h>

#define SCK             PD2
#define SCK_DDR         DDRD
#define SCK_PORT        PORTD
#define SCK_HIGH        SCK_PORT |= (1 << SCK)
#define SCK_LOW         SCK_PORT &= ~(1 << SCK)

#define MOSI            PD3
#define MOSI_DDR        DDRD
#define MOSI_PORT       PORTD
#define MOSI_HIGH       MOSI_PORT |= (1 << MOSI)
#define MOSI_LOW        MOSI_PORT &= ~(1 << MOSI)

#define MISO            PC0
#define MISO_DDR        DDRC
#define MISO_PORT       PORTC
#define MISO_PIN        PINC
#define MISO_IS_HIGH    (MISO_PIN & (1 << MISO))

#define SPI_PINS_INIT   SCK_DDR |= (1 << SCK);   \
                        MOSI_DDR |= (1 << MOSI); \
                        MISO_DDR &= ~(1 << MISO)

#define MSBit           0x80
#define LSBit           0x01

void SPI_init();
uint8_t SPI_byte(uint8_t _byte);

#endif /* SPI_H_ */

#include "SPI.h"

void SPI_init()
{
  SPI_PINS_INIT;
}

uint8_t SPI_byte(uint8_t _byte)
{
  for(uint8_t bitNum = 8; bitNum; bitNum--)
  {
    if (_byte & MSBit)
      MOSI_HIGH;
    else
      MOSI_LOW;
    _byte <<= 1;
    SCK_HIGH;
    if(MISO_IS_HIGH)
      _byte |= LSBit;
    SCK_LOW;
  }
  return _byte;
}

SSD1283A обеспечивает инициализацию дисплея, очистку экрана и прорисовку пикселя с заданными координатами и цветом.
#ifndef SSD1283A_H_
#define SSD1283A_H_

#include "SPI.h"

#define SSD1283A_DC                           PD4
#define SSD1283A_DC_DDR			      DDRD
#define SSD1283A_DC_PORT		      PORTD
#define SSD1283A_DC_COMMAND		      SSD1283A_DC_PORT &= ~(1 << SSD1283A_DC)
#define SSD1283A_DC_DATA                      SSD1283A_DC_PORT |= (1 << SSD1283A_DC)

#define SSD1283A_CS                           PD5
#define SSD1283A_CS_DDR			      DDRD
#define SSD1283A_CS_PORT		      PORTD
#define SSD1283A_CS_HIGH		      SSD1283A_CS_PORT |= (1 << SSD1283A_CS)
#define SSD1283A_CS_LOW			      SSD1283A_CS_PORT &= ~(1 << SSD1283A_CS)

#define SSD1283A_RESET			      PD6
#define SSD1283A_RESET_DDR		      DDRD
#define SSD1283A_RESET_PORT		      PORTD
#define SSD1283A_RESET_HIGH		      SSD1283A_RESET_PORT |= (1 << SSD1283A_RESET)
#define SSD1283A_RESET_LOW                    SSD1283A_RESET_PORT &= ~(1 << SSD1283A_RESET)

#define SSD1283_PINS_INIT                     SSD1283A_DC_DDR |= 1 << SSD1283A_DC;        \
                                              SSD1283A_CS_DDR |= 1 << SSD1283A_CS;        \
                                              SSD1283A_RESET_DDR |= 1 << SSD1283A_RESET;  \
                                              SSD1283A_CS_HIGH; SSD1283A_RESET_HIGH

#define SSD1283A_OSCILLATION_START            0x00
#define SSD1283A_OSCILLATION_START_VALUE      1 << SSD1283A_OSCEN
#define SSD1283A_OSCEN                        0
#define SSD1283A_DRIVER_OUTPUT_CONTROL        0x01
#define SSD1283A_DRIVER_OUTPUT_CONTROL_VALUE  (1 << SSD1283A_REV) | (1 << SSD1283A_MUX_7) | (1 << SSD1283A_MUX_1) | (1 << SSD1283A_MUX_0)
#define SSD1283A_MUX_0                        0
#define SSD1283A_MUX_1                        1
#define SSD1283A_MUX_7                        7
#define SSD1283A_REV                          13
#define SSD1283A_LCD_DRIVE_AC_CONTROL         0x02
#define SSD1283A_LCD_DRIVE_AC_CONTROL_VALUE   (1 << SSD1283A_BC) | (1 << SSD1283A_EOR)
#define SSD1283A_EOR                          8
#define SSD1283A_BC                           9
#define SSD1283A_ENTRY_MODE                   0x03
#define SSD1283A_ENTRY_MODE_VALUE             (1 << SSD1283A_DFM1) | (1 << SSD1283A_DFM0) | (1 << SSD1283A_OEDEF) | (1 << SSD1283A_ID1) | (1 << SSD1283A_ID0)
#define SSD1283A_ID0                          4
#define SSD1283A_ID1                          5
#define SSD1283A_OEDEF                        11
#define SSD1283A_DFM0                         13
#define SSD1283A_DFM1                         14
#define SSD1283A_DISPLAY_CONTROL              0x07
#define SSD1283A_DISPLAY_CONTROL_VALUE        (1 << SSD1283A_GON) | (1 << SSD1283A_DTE) | (1 << SSD1283A_D1) | (1 << SSD1283A_D0)
#define SSD1283A_D0                           0
#define SSD1283A_D1                           1
#define SSD1283A_DTE                          4
#define SSD1283A_GON                          5
#define SSD1283A_FRAME_CYCLE_CONTROL          0x0B
#define SSD1283A_FRAME_CYCLE_CONTROL_VALUE    (1 << SSD1283A_NO0) | (1 << SSD1283A_SDT0) | (1 << SSD1283A_EQ1) | (1 << SSD1283A_RTN3) | (1 << SSD1283A_RTN2)
#define SSD1283A_RTN2                         2
#define SSD1283A_RTN3                         3
#define SSD1283A_EQ1                          11
#define SSD1283A_SDT0                         12
#define SSD1283A_NO0                          14
#define SSD1283A_POWER_CONTROL1               0x10
#define SSD1283A_POWER_CONTROL1_VALUE         (1 << SSD1283A_DCY1) | (1 << SSD1283A_BTH2) | (1 << SSD1283A_BTH1) | (1 << SSD1283A_BTH0) | (1 << 8) | (1 << 7) | (1 << 6) | (1 << SSD1283A_AP2) | (1 << SSD1283A_AP1) | (1 << SSD1283A_AP0)
#define SSD1283A_AP0                          1
#define SSD1283A_AP1                          2
#define SSD1283A_AP2                          3
#define SSD1283A_BTH0                         9
#define SSD1283A_BTH1                         10
#define SSD1283A_BTH2                         11
#define SSD1283A_DCY1                         13
#define SSD1283A_POWER_CONTROL2               0x11
#define SSD1283A_POWER_CONTROL2_VALUE         (1 << SSD1283A_PU0) | (1 << 2)
#define SSD1283A_PU0                          3
#define SSD1283A_POWER_CONTROL3               0x12
#define SSD1283A_POWER_CONTROL3_VALUE         (1 << SSD1283A_VRH3) | (1 << SSD1283A_VRH0)
#define SSD1283A_VRH0                         0
#define SSD1283A_VRH3                         3
#define SSD1283A_POWER_CONTROL4               0x13
#define SSD1283A_POWER_CONTROL4_VALUE         (1 << SSD1283A_VCOMG) | (1 << SSD1283A_VDV4) | (1 << SSD1283A_VDV0)
#define SSD1283A_VDV0                         8
#define SSD1283A_VDV4                         12
#define SSD1283A_VCOMG                        13
#define SSD1283A_OSCILLATOR_FREQUENCY         0x2C
#define SSD1283A_OSCILLATOR_FREQUENCY_VALUE   (1 << SSD1283A_OSCR3)
#define SSD1283A_OSCR3                        15

#define BLACK                                 0x0000
#define BLUE                                  0x001F
#define RED                                   0xF800
#define GREEN                                 0x07E0
#define CYAN                                  0x07FF
#define MAGENTA                               0xF81F
#define YELLOW                                0xFFE0
#define WHITE                                 0xFFFF

#define BACKGROUND_COLOR                      BLACK

#define SSD1283A_WIDTH                        128
#define SSD1283A_HIGHT			      160
#define PIXEL_NUMBER			      130

#define SSD1283A_DELAY(value)                 _delay_ms(value)

void SSD1283A_command(uint8_t command);
void SSD1283A_data(uint8_t data);
void SSD1283A_data_16(uint16_t data);
void SSD1283A_write_reg(uint16_t command, uint16_t data);
void SSD1283A_init();
void SSD1283A_set_rotation(uint8_t rotation);
void SSD1283A_set_window_address(int16_t x1, int16_t y1, int16_t x2, int16_t y2);
void SSD1283A_clear_display(int16_t color);

void drawPixel(uint16_t x, uint16_t y, uint16_t color);

extern uint8_t rotation;

#endif /* SSD1283A_H_ */

#include "SSD1283A.h"

uint8_t rotation;

void SSD1283A_command(uint8_t command)
{
  SSD1283A_DC_COMMAND;
  SSD1283A_CS_LOW;
  SPI_byte(command);
  SSD1283A_CS_HIGH;
}

void SSD1283A_data(uint8_t data)
{
  SSD1283A_DC_DATA;
  SSD1283A_CS_LOW;
  SPI_byte(data);
  SSD1283A_CS_HIGH;
}

void SSD1283A_data_16(uint16_t data)
{
  SSD1283A_DC_DATA;
  SSD1283A_CS_LOW;
  SPI_byte(data >> 8);
  SPI_byte(data);
  SSD1283A_CS_HIGH;
}

void SSD1283A_write_reg(uint16_t command, uint16_t data)
{
  SSD1283A_DC_COMMAND;
  SSD1283A_CS_LOW;
  SPI_byte(command);
  SSD1283A_DC_DATA;
  SPI_byte(data >> 8);
  SPI_byte(data);
  SSD1283A_CS_HIGH;
}

void SSD1283A_init()
{
  SPI_init();
  SSD1283_PINS_INIT;

  SSD1283A_RESET_LOW;
  SSD1283A_DELAY(10);
  SSD1283A_RESET_HIGH;
  SSD1283A_DELAY(120);

  SSD1283A_write_reg(SSD1283A_OSCILLATION_START, SSD1283A_OSCILLATION_START_VALUE);
  SSD1283A_write_reg(SSD1283A_DRIVER_OUTPUT_CONTROL, SSD1283A_DRIVER_OUTPUT_CONTROL_VALUE);
  SSD1283A_write_reg(SSD1283A_LCD_DRIVE_AC_CONTROL, SSD1283A_LCD_DRIVE_AC_CONTROL_VALUE);
  SSD1283A_write_reg(SSD1283A_ENTRY_MODE, SSD1283A_ENTRY_MODE_VALUE);
  SSD1283A_write_reg(SSD1283A_DISPLAY_CONTROL, SSD1283A_DISPLAY_CONTROL_VALUE);
  SSD1283A_write_reg(SSD1283A_FRAME_CYCLE_CONTROL, SSD1283A_FRAME_CYCLE_CONTROL_VALUE);
  SSD1283A_write_reg(SSD1283A_POWER_CONTROL1, SSD1283A_POWER_CONTROL1_VALUE);
  SSD1283A_write_reg(SSD1283A_POWER_CONTROL2, SSD1283A_POWER_CONTROL2_VALUE);
  SSD1283A_write_reg(SSD1283A_POWER_CONTROL3, SSD1283A_POWER_CONTROL3_VALUE);
  SSD1283A_write_reg(SSD1283A_POWER_CONTROL4, SSD1283A_POWER_CONTROL4_VALUE);
  SSD1283A_write_reg(SSD1283A_OSCILLATOR_FREQUENCY, SSD1283A_OSCILLATOR_FREQUENCY_VALUE);

  SSD1283A_set_rotation(0);
  SSD1283A_clear_display(BACKGROUND_COLOR);
}

void SSD1283A_set_rotation(uint8_t _rotation)
{
  rotation = _rotation & 0x03;
  switch(rotation)
  {
    case 0:
      SSD1283A_write_reg(0x01, 0x2183);
      SSD1283A_write_reg(0x03, 0x6830);
    break;

    case 1:
      SSD1283A_write_reg(0x01, 0x2283);
      SSD1283A_write_reg(0x03, 0x6808);
    break;

    case 2:
      SSD1283A_write_reg(0x01, 0x2183);
      SSD1283A_write_reg(0x03, 0x6800);
    break;

    case 3:
      SSD1283A_write_reg(0x01, 0x2283);
      SSD1283A_write_reg(0x03, 0x6838);
    break;
  }
  SSD1283A_set_window_address(0, 0, PIXEL_NUMBER - 1, PIXEL_NUMBER - 1);
}

void SSD1283A_set_window_address(int16_t x1, int16_t y1, int16_t x2, int16_t y2)
{
  switch(rotation)
  {
    case 0:
      SSD1283A_command(0x44); SSD1283A_data(x2 + 2); SSD1283A_data(x1 + 2);
      SSD1283A_command(0x45); SSD1283A_data(y2 + 2); SSD1283A_data(y1 + 2);
      SSD1283A_command(0x21); SSD1283A_data(y1 + 2); SSD1283A_data(x1 + 2);
    break;
		
    case 1:
      SSD1283A_command(0x44); SSD1283A_data(PIXEL_NUMBER - y1 + 1); SSD1283A_data(PIXEL_NUMBER - y2 + 1);
      SSD1283A_command(0x45); SSD1283A_data(PIXEL_NUMBER - x1 - 1); SSD1283A_data(PIXEL_NUMBER - x2 - 1);
      SSD1283A_command(0x21); SSD1283A_data(PIXEL_NUMBER - x1 - 1); SSD1283A_data(PIXEL_NUMBER - y1 + 1);
    break;

    case 2:
      SSD1283A_command(0x44); SSD1283A_data(PIXEL_NUMBER - x1 + 1); SSD1283A_data(PIXEL_NUMBER - x2 + 1);
      SSD1283A_command(0x45); SSD1283A_data(PIXEL_NUMBER - y1 + 1); SSD1283A_data(PIXEL_NUMBER - y2 + 1);
      SSD1283A_command(0x21); SSD1283A_data(PIXEL_NUMBER - y1 + 1); SSD1283A_data(PIXEL_NUMBER - x1 + 1);
    break;

    case 3:
      SSD1283A_command(0x44); SSD1283A_data(y2 + 2); SSD1283A_data(y1 + 2);
      SSD1283A_command(0x45); SSD1283A_data(x2); SSD1283A_data(x1);
      SSD1283A_command(0x21); SSD1283A_data(x1); SSD1283A_data(y1 + 2);
    break;
  }
}

void SSD1283A_clear_display(int16_t color)
{
  SSD1283A_set_window_address(0, 0, PIXEL_NUMBER - 1, PIXEL_NUMBER - 1);
  SSD1283A_command(0x22);
  for(unsigned int coordinateX = 0; coordinateX < PIXEL_NUMBER; coordinateX++)
    for(unsigned int coordinateY = 0; coordinateY < PIXEL_NUMBER; coordinateY++)
      SSD1283A_data_16(color);
}

void drawPixel(uint16_t x, uint16_t y, uint16_t color)
{
  SSD1283A_set_window_address(x, y, x, y);
  SSD1283A_command(0x22);
  SSD1283A_data_16(color);
}

GFX предназначена для вывода на экран горизонтальных и вертикальных линий, рамок, символьных и строковых переменных.
#ifndef GFX_H_
#define GFX_H_

#include "images.h"

#define CHAR_CODE_SHIFT   0x20
#define MSBit             0x80

void setColor(uint16_t color);
void setCursor(uint16_t x, uint16_t y);
void drawVerticalLine(uint16_t height);
void drawHorizontalLine(uint16_t length);
void drawRectangle(uint16_t width, uint16_t height);
void fillRectangle(uint16_t width, uint16_t height);
void drawByte(uint8_t data);
void setFont(font_t* fontPtr);
void printChar(const char data);
void printString(char* _string);
extern void drawPixel(uint16_t x, uint16_t y, uint16_t color);

extern uint16_t currentX, currentY, currentColor;

#endif /* GFX_H_ */

#include "GFX.h"

uint16_t currentX, currentY, currentColor;

void setColor(uint16_t color)
{
  currentColor = color;
}

void setCursor(uint16_t x, uint16_t y)
{
  currentX = x;
  currentY = y;
}

void drawVerticalLine(uint16_t height)
{
  while(height--)
  {
    drawPixel(currentX, currentY, currentColor);
    currentY++;
  }
}

void drawHorizontalLine(uint16_t length)
{
  while(length--)
  {
    drawPixel(currentX, currentY, currentColor);
    currentX++;
  }
}

void drawRectangle(uint16_t width, uint16_t height)
{
  uint16_t startX = currentX, startY = currentY;

  drawVerticalLine(height);
  currentY--;
  drawHorizontalLine(width);
  currentX = startX; currentY = startY;
  drawHorizontalLine(width);
  currentX--;
  drawVerticalLine(height);
}

void fillRectangle(uint16_t width, uint16_t height)
{
  uint16_t startX = currentX;

  for(uint16_t coordinateY = 0; coordinateY < height; coordinateY++)
  {
    drawHorizontalLine(width);
    currentY++;
    currentX = startX;
  }
}

void drawByte(uint8_t data)
{
  for(int bitNum = 0; bitNum < 8; bitNum++)
  {
    if(data & MSBit)
      drawPixel(currentX, currentY, currentColor);
    data <<= 1;
    currentY++;
  }
}

void setFont(font_t* fontPtr)
{
  currentFont = fontPtr;
}

void printChar(const char data)
{
  uint16_t startY = currentY;
  uint16_t startX = currentX;
  uint8_t currentByteNum = 0;

  uint8_t charNum = data - CHAR_CODE_SHIFT;

  uint8_t height = pgm_read_byte(currentFont->height);
  uint8_t width = pgm_read_byte(currentFont->width + charNum);
	
  uint8_t strips_per_height = height / 8;
  if(strips_per_height * 8 != height)
    strips_per_height++;

  for(uint8_t stripNum = 0; stripNum < strips_per_height; stripNum++)
  {
    for(uint8_t byteNum = 0; byteNum < width; byteNum++)
    {
      drawByte(pgm_read_byte(pgm_read_word(currentFont->array + charNum) + currentByteNum));
      currentByteNum++;
      currentX++;
      currentY = startY;
    }
    startY += 8;
    currentY = startY;
    currentX = startX;
  }
}

void printString(char* _string)
{
  uint16_t stringStartY = currentY;

  while (*_string != '\0')
  {
    uint8_t charNum = *_string - CHAR_CODE_SHIFT;
    uint16_t width = pgm_read_byte(currentFont->width + charNum);
		
    printChar(*_string);
    _string++;
    currentY = stringStartY;
    currentX += width;
  }
}

images хранит шрифт Arial 14, сгенерированный программой lcd-image-converter.
#ifndef IMAGES_H_
#define IMAGES_H_

#include <stdint.h>
#include <avr/pgmspace.h>
#include <stddef.h>

typedef struct
{
  const char* const* array;
  const char* width;
  const char* height;
} font_t;
extern font_t* currentFont;

/* FONTS */
/* arial_14 */
extern font_t arial_14;

#endif /* IMAGES_H_ */

#include "images.h"

/* FONTS */
font_t* currentFont = NULL;

/* arial_14 */
const char arial_14_0x20[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};
const char arial_14_0x21[] PROGMEM = {0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00,};
const char arial_14_0x22[] PROGMEM = {0x00, 0x1e, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};
const char arial_14_0x23[] PROGMEM = {0x02, 0x02, 0x07, 0x1a, 0x02, 0x07, 0x1a, 0x02, 0x40, 0x78, 0xc0, 0x40, 0x78, 0xc0, 0x40, 0x40,};
const char arial_14_0x24[] PROGMEM = {0x06, 0x09, 0x11, 0x3f, 0x10, 0x10, 0x08, 0x00, 0x10, 0x08, 0x08, 0xfc, 0x88, 0x90, 0x60, 0x00,};
const char arial_14_0x25[] PROGMEM = {0x00, 0x0e, 0x11, 0x11, 0x0e, 0x00, 0x03, 0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x30, 0xc0, 0x00, 0x70, 0x88, 0x88, 0x70, 0x00,};
const char arial_14_0x26[] PROGMEM = {0x00, 0x00, 0x0e, 0x11, 0x11, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x70, 0x88, 0x08, 0x88, 0x48, 0x30, 0x50, 0x08,};
const char arial_14_0x27[] PROGMEM = {0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,};
const char arial_14_0x28[] PROGMEM = {0x00, 0x03, 0x0c, 0x10, 0x00, 0x00, 0xf8, 0x06, 0x01, 0x00,};
const char arial_14_0x29[] PROGMEM = {0x00, 0x10, 0x0c, 0x03, 0x00, 0x00, 0x01, 0x06, 0xf8, 0x00,};
const char arial_14_0x2a[] PROGMEM = {0x08, 0x0a, 0x1c, 0x0a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,};
const char arial_14_0x2b[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xf0, 0x80, 0x80, 0x80,};
const char arial_14_0x2c[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00,};
const char arial_14_0x2d[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x00,};
const char arial_14_0x2e[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,};
const char arial_14_0x2f[] PROGMEM = {0x00, 0x00, 0x07, 0x18, 0x18, 0xe0, 0x00, 0x00,};
const char arial_14_0x30[] PROGMEM = {0x00, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x00, 0x00, 0xf0, 0x08, 0x08, 0x08, 0x08, 0xf0, 0x00,};
const char arial_14_0x31[] PROGMEM = {0x00, 0x00, 0x04, 0x08, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,};
const char arial_14_0x32[] PROGMEM = {0x00, 0x0c, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x00, 0x00, 0x08, 0x18, 0x28, 0x48, 0x88, 0x08, 0x00,};
const char arial_14_0x33[] PROGMEM = {0x00, 0x0c, 0x10, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0xf0, 0x00,};
const char arial_14_0x34[] PROGMEM = {0x00, 0x00, 0x03, 0x04, 0x08, 0x1f, 0x00, 0x00, 0x60, 0xa0, 0x20, 0x20, 0x20, 0xf8, 0x20, 0x00,};
const char arial_14_0x35[] PROGMEM = {0x00, 0x07, 0x1a, 0x12, 0x12, 0x12, 0x11, 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x18, 0xe0, 0x00,};
const char arial_14_0x36[] PROGMEM = {0x00, 0x07, 0x08, 0x11, 0x11, 0x11, 0x08, 0x00, 0x00, 0xf0, 0x88, 0x08, 0x08, 0x08, 0xf0, 0x00,};
const char arial_14_0x37[] PROGMEM = {0x00, 0x10, 0x10, 0x10, 0x11, 0x16, 0x18, 0x00, 0x00, 0x00, 0x00, 0x38, 0xc0, 0x00, 0x00, 0x00,};
const char arial_14_0x38[] PROGMEM = {0x00, 0x0e, 0x11, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x00, 0xf0, 0x08, 0x08, 0x08, 0x08, 0xf0, 0x00,};
const char arial_14_0x39[] PROGMEM = {0x00, 0x0f, 0x10, 0x10, 0x10, 0x11, 0x0f, 0x00, 0x00, 0x10, 0x88, 0x88, 0x88, 0x10, 0xe0, 0x00,};
const char arial_14_0x3a[] PROGMEM = {0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,};
const char arial_14_0x3b[] PROGMEM = {0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,};
const char arial_14_0x3c[] PROGMEM = {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x04, 0x00, 0x00, 0x80, 0x40, 0x40, 0x20, 0x20, 0x10, 0x00,};
const char arial_14_0x3d[] PROGMEM = {0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,};
const char arial_14_0x3e[] PROGMEM = {0x00, 0x04, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x00,};
const char arial_14_0x3f[] PROGMEM = {0x00, 0x0c, 0x10, 0x10, 0x10, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x68, 0x80, 0x00, 0x00, 0x00,};
const char arial_14_0x40[] PROGMEM = {0x01, 0x06, 0x08, 0x08, 0x11, 0x12, 0x12, 0x12, 0x11, 0x13, 0x08, 0x04, 0x03, 0x00, 0xf0, 0x0c, 0x02, 0xf2, 0x09, 0x09, 0x09, 0x11, 0x79, 0x89, 0x0a, 0x32, 0xc4, 0x00,};
const char arial_14_0x41[] PROGMEM = {0x00, 0x00, 0x01, 0x0e, 0x10, 0x0e, 0x01, 0x00, 0x00, 0x18, 0x60, 0xc0, 0x40, 0x40, 0x40, 0xc0, 0x60, 0x18,};
const char arial_14_0x42[] PROGMEM = {0x00, 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf0, 0x00,};
const char arial_14_0x43[] PROGMEM = {0x00, 0x07, 0x08, 0x10, 0x10, 0x10, 0x10, 0x08, 0x04, 0x00, 0x00, 0xe0, 0x10, 0x08, 0x08, 0x08, 0x08, 0x10, 0x20, 0x00,};
const char arial_14_0x44[] PROGMEM = {0x00, 0x1f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x07, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0xe0, 0x00,};
const char arial_14_0x45[] PROGMEM = {0x00, 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00,};
const char arial_14_0x46[] PROGMEM = {0x00, 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};
const char arial_14_0x47[] PROGMEM = {0x00, 0x07, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x04, 0x00, 0x00, 0xe0, 0x10, 0x08, 0x08, 0x08, 0x88, 0x88, 0x90, 0xe0, 0x00,};
const char arial_14_0x48[] PROGMEM = {0x00, 0x1f, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1f, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00,};
const char arial_14_0x49[] PROGMEM = {0x00, 0x1f, 0x00, 0x00, 0xf8, 0x00,};
const char arial_14_0x4a[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x30, 0x08, 0x08, 0x08, 0xf0, 0x00,};
const char arial_14_0x4b[] PROGMEM = {0x00, 0x1f, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00, 0xf8, 0x40, 0x80, 0x00, 0x80, 0x60, 0x10, 0x08,};
const char arial_14_0x4c[] PROGMEM = {0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,};
const char arial_14_0x4d[] PROGMEM = {0x00, 0x1f, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x1f, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xe0, 0x18, 0xe0, 0x00, 0x00, 0xf8, 0x00,};
const char arial_14_0x4e[] PROGMEM = {0x00, 0x1f, 0x08, 0x06, 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x80, 0x60, 0x10, 0xf8, 0x00,};
const char arial_14_0x4f[] PROGMEM = {0x00, 0x07, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x07, 0x00, 0x00, 0xe0, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0xe0, 0x00,};
const char arial_14_0x50[] PROGMEM = {0x00, 0x1f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x00, 0x00, 0xf8, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,};
const char arial_14_0x51[] PROGMEM = {0x00, 0x07, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x07, 0x00, 0x00, 0xe0, 0x10, 0x08, 0x08, 0x08, 0x28, 0x10, 0x38, 0xc8, 0x00,};
const char arial_14_0x52[] PROGMEM = {0x00, 0x1f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x00, 0x00, 0xf8, 0x80, 0x80, 0x80, 0xc0, 0xa0, 0x90, 0x08, 0x00,};
const char arial_14_0x53[] PROGMEM = {0x00, 0x0e, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0c, 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x88, 0x88, 0xf0, 0x00,};
const char arial_14_0x54[] PROGMEM = {0x00, 0x10, 0x10, 0x10, 0x1f, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00,};
const char arial_14_0x55[] PROGMEM = {0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0xe0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xe0, 0x00,};
const char arial_14_0x56[] PROGMEM = {0x18, 0x06, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x18, 0x00, 0x00, 0x80, 0x60, 0x18, 0x60, 0x80, 0x00, 0x00,};
const char arial_14_0x57[] PROGMEM = {0x18, 0x07, 0x00, 0x00, 0x00, 0x0f, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x07, 0x18, 0x00, 0x00, 0xe0, 0x18, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x18, 0xe0, 0x00, 0x00,};
const char arial_14_0x58[] PROGMEM = {0x10, 0x0c, 0x02, 0x01, 0x01, 0x02, 0x0c, 0x10, 0x08, 0x30, 0x40, 0x80, 0x80, 0x40, 0x30, 0x08,};
const char arial_14_0x59[] PROGMEM = {0x10, 0x08, 0x06, 0x01, 0x00, 0x01, 0x06, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00,};
const char arial_14_0x5a[] PROGMEM = {0x00, 0x10, 0x10, 0x10, 0x11, 0x16, 0x18, 0x10, 0x08, 0x18, 0x68, 0x88, 0x08, 0x08, 0x08, 0x08,};
const char arial_14_0x5b[] PROGMEM = {0x00, 0x1f, 0x10, 0x00, 0x00, 0xff, 0x01, 0x00,};
const char arial_14_0x5c[] PROGMEM = {0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x18,};
const char arial_14_0x5d[] PROGMEM = {0x00, 0x10, 0x1f, 0x00, 0x00, 0x01, 0xff, 0x00,};
const char arial_14_0x5e[] PROGMEM = {0x01, 0x0e, 0x10, 0x0e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,};
const char arial_14_0x5f[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,};
const char arial_14_0x60[] PROGMEM = {0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};
const char arial_14_0x61[] PROGMEM = {0x00, 0x02, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0x30, 0x48, 0x88, 0x88, 0x90, 0xf8, 0x00,};
const char arial_14_0x62[] PROGMEM = {0x00, 0x1f, 0x02, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0xf8, 0x10, 0x08, 0x08, 0x08, 0xf0, 0x00,};
const char arial_14_0x63[] PROGMEM = {0x00, 0x03, 0x04, 0x04, 0x04, 0x02, 0x00, 0x00, 0xf0, 0x08, 0x08, 0x08, 0x10, 0x00,};
const char arial_14_0x64[] PROGMEM = {0x00, 0x03, 0x04, 0x04, 0x04, 0x02, 0x1f, 0x00, 0x00, 0xf0, 0x08, 0x08, 0x08, 0x10, 0xf8, 0x00,};
const char arial_14_0x65[] PROGMEM = {0x00, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0xf0, 0x88, 0x88, 0x88, 0x88, 0x90, 0x00,};
const char arial_14_0x66[] PROGMEM = {0x04, 0x0f, 0x14, 0x14, 0x00, 0xf8, 0x00, 0x00,};
const char arial_14_0x67[] PROGMEM = {0x00, 0x03, 0x04, 0x04, 0x04, 0x02, 0x07, 0x00, 0x00, 0xf2, 0x09, 0x09, 0x09, 0x11, 0xfe, 0x00,};
const char arial_14_0x68[] PROGMEM = {0x00, 0x1f, 0x02, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00,};
const char arial_14_0x69[] PROGMEM = {0x00, 0x17, 0x00, 0x00, 0xf8, 0x00,};
const char arial_14_0x6a[] PROGMEM = {0x00, 0x17, 0x00, 0x01, 0xfe, 0x00,};
const char arial_14_0x6b[] PROGMEM = {0x00, 0x1f, 0x00, 0x00, 0x01, 0x02, 0x04, 0x00, 0xf8, 0x40, 0x80, 0x40, 0x30, 0x08,};
const char arial_14_0x6c[] PROGMEM = {0x00, 0x1f, 0x00, 0x00, 0xf8, 0x00,};
const char arial_14_0x6d[] PROGMEM = {0x00, 0x07, 0x02, 0x04, 0x04, 0x03, 0x06, 0x04, 0x04, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00,};
const char arial_14_0x6e[] PROGMEM = {0x00, 0x07, 0x02, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00,};
const char arial_14_0x6f[] PROGMEM = {0x00, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0xf0, 0x08, 0x08, 0x08, 0x08, 0xf0, 0x00,};
const char arial_14_0x70[] PROGMEM = {0x00, 0x07, 0x02, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0xff, 0x10, 0x08, 0x08, 0x08, 0xf0, 0x00,};
const char arial_14_0x71[] PROGMEM = {0x00, 0x03, 0x04, 0x04, 0x04, 0x02, 0x07, 0x00, 0x00, 0xf0, 0x08, 0x08, 0x08, 0x10, 0xff, 0x00,};
const char arial_14_0x72[] PROGMEM = {0x00, 0x07, 0x02, 0x04, 0x04, 0x00, 0xf8, 0x00, 0x00, 0x00,};
const char arial_14_0x73[] PROGMEM = {0x00, 0x03, 0x04, 0x04, 0x04, 0x02, 0x00, 0x00, 0x10, 0x88, 0x88, 0x88, 0x70, 0x00,};
const char arial_14_0x74[] PROGMEM = {0x04, 0x1f, 0x04, 0x04, 0x00, 0xf8, 0x08, 0x08,};
const char arial_14_0x75[] PROGMEM = {0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xf0, 0x08, 0x08, 0x08, 0x10, 0xf8, 0x00,};
const char arial_14_0x76[] PROGMEM = {0x06, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0x80, 0x60, 0x18, 0x60, 0x80, 0x00,};
const char arial_14_0x77[] PROGMEM = {0x06, 0x01, 0x00, 0x01, 0x06, 0x01, 0x00, 0x01, 0x06, 0x00, 0xe0, 0x18, 0xe0, 0x00, 0xe0, 0x18, 0xe0, 0x00,};
const char arial_14_0x78[] PROGMEM = {0x04, 0x03, 0x00, 0x00, 0x03, 0x04, 0x08, 0x30, 0xc0, 0xc0, 0x30, 0x08,};
const char arial_14_0x79[] PROGMEM = {0x06, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0x81, 0x71, 0x0e, 0x70, 0x80, 0x00,};
const char arial_14_0x7a[] PROGMEM = {0x04, 0x04, 0x04, 0x05, 0x06, 0x04, 0x08, 0x18, 0x68, 0x88, 0x08, 0x08,};
const char arial_14_0x7b[] PROGMEM = {0x00, 0x00, 0x0f, 0x10, 0x00, 0x00, 0x40, 0xbe, 0x01, 0x00,};
const char arial_14_0x7c[] PROGMEM = {0x00, 0x1f, 0x00, 0x00, 0xff, 0x00,};
const char arial_14_0x7d[] PROGMEM = {0x00, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x01, 0xbe, 0x40, 0x00,};
const char arial_14_0x7e[] PROGMEM = {0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00,};
const char arial_14_0x7f[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

const char* const arial_14_array[] PROGMEM = {arial_14_0x20,arial_14_0x21,arial_14_0x22,arial_14_0x23,arial_14_0x24,arial_14_0x25,arial_14_0x26,arial_14_0x27,arial_14_0x28,arial_14_0x29,arial_14_0x2a,arial_14_0x2b,arial_14_0x2c,arial_14_0x2d,arial_14_0x2e,arial_14_0x2f,arial_14_0x30,arial_14_0x31,arial_14_0x32,arial_14_0x33,arial_14_0x34,arial_14_0x35,arial_14_0x36,arial_14_0x37,arial_14_0x38,arial_14_0x39,arial_14_0x3a,arial_14_0x3b,arial_14_0x3c,arial_14_0x3d,arial_14_0x3e,arial_14_0x3f,arial_14_0x40,arial_14_0x41,arial_14_0x42,arial_14_0x43,arial_14_0x44,arial_14_0x45,arial_14_0x46,arial_14_0x47,arial_14_0x48,arial_14_0x49,arial_14_0x4a,arial_14_0x4b,arial_14_0x4c,arial_14_0x4d,arial_14_0x4e,arial_14_0x4f,arial_14_0x50,arial_14_0x51,arial_14_0x52,arial_14_0x53,arial_14_0x54,arial_14_0x55,arial_14_0x56,arial_14_0x57,arial_14_0x58,arial_14_0x59,arial_14_0x5a,arial_14_0x5b,arial_14_0x5c,arial_14_0x5d,arial_14_0x5e,arial_14_0x5f,arial_14_0x60,arial_14_0x61,arial_14_0x62,arial_14_0x63,arial_14_0x64,arial_14_0x65,arial_14_0x66,arial_14_0x67,arial_14_0x68,arial_14_0x69,arial_14_0x6a,arial_14_0x6b,arial_14_0x6c,arial_14_0x6d,arial_14_0x6e,arial_14_0x6f,arial_14_0x70,arial_14_0x71,arial_14_0x72,arial_14_0x73,arial_14_0x74,arial_14_0x75,arial_14_0x76,arial_14_0x77,arial_14_0x78,arial_14_0x79,arial_14_0x7a,arial_14_0x7b,arial_14_0x7c,arial_14_0x7d,arial_14_0x7e,arial_14_0x7f};
const char arial_14_width[] PROGMEM = {4,5,5,8,8,12,9,3,5,5,5,8,4,5,4,4,8,8,8,8,8,8,8,8,8,8,4,4,8,8,8,8,14,9,9,10,10,9,9,11,9,3,6,9,8,11,9,11,9,11,10,9,9,9,9,13,8,9,8,4,4,4,5,8,5,8,8,7,8,8,4,8,8,3,3,7,3,11,8,8,8,8,5,7,4,8,7,9,6,7,6,5,3,5,8,7};
const char arial_14_height PROGMEM = 16;
font_t arial_14 = {.array = arial_14_array, .width = arial_14_width, .height = &arial_14_height};

display ответственна за прорисовку времени, даты, широты/долготы, высоты над уровнем моря и скорости.
#ifndef DISPLAY_H_
#define DISPLAY_H_

#include "GFX.h"
#include "SSD1283A.h"
#include "NEO6.h"

#define ARIAL_14_RECTANGLE_WIDHT    8
#define ARIAL_14_RECTANGLE_HEIGHT   16

/* time */
#define TIME_COLOR                  YELLOW
#define TIME_X                      5
#define TIME_Y                      5
#define HOURS_TENS_X                40
#define HOURS_UNITS_X               HOURS_TENS_X + 8
#define TIME_DOT_1_X                HOURS_UNITS_X + 10
#define MINUTES_TENS_X              TIME_DOT_1_X + 5
#define MINUTES_UNITS_X             MINUTES_TENS_X + 8
#define TIME_DOT_2_X                MINUTES_UNITS_X + 10
#define SECONDS_TENS_X              TIME_DOT_2_X + 5
#define SECONDS_UNITS_X             SECONDS_TENS_X + 8
/* date */
#define DATE_COLOR                  RED
#define DATE_X                      5
#define DATE_Y                      25
#define DAY_TENS_X                  40
#define DAY_UNITS_X                 DAY_TENS_X + 8
#define DATE_SLASH_1_X              DAY_UNITS_X + 8
#define MONTH_TENS_X                DATE_SLASH_1_X + 5
#define MONTH_UNITS_X               MONTH_TENS_X + 8
#define DATE_SLASH_2_X              MONTH_UNITS_X + 8
#define YEAR_TENS_X                 DATE_SLASH_2_X + 5
#define YEAR_UNITS_X                YEAR_TENS_X + 8
/* latitude */
#define LATITUDE_COLOR              WHITE
#define LATITUDE_X                  5
#define LATITUDE_Y                  45
#define LATITUDE_DEGREE_1_X         30
#define LATITUDE_DEGREE_2_X         LATITUDE_DEGREE_1_X + 8
#define LATITUDE_DOT_X              LATITUDE_DEGREE_2_X + 8
#define LATITUDE_MINUTES_1_X        LATITUDE_DOT_X + 5
#define LATITUDE_MINUTES_2_X        LATITUDE_MINUTES_1_X + 8
#define LATITUDE_MINUTES_3_X        LATITUDE_MINUTES_2_X + 8
#define LATITUDE_MINUTES_4_X        LATITUDE_MINUTES_3_X + 8
#define LATITUDE_MINUTES_5_X        LATITUDE_MINUTES_4_X + 8
#define LATITUDE_MINUTES_6_X        LATITUDE_MINUTES_5_X + 8
#define LATITUDE_MINUTES_7_X        LATITUDE_MINUTES_6_X + 8
/* longitude */
#define LONGITUDE_COLOR             WHITE
#define LONGITUDE_X                 5
#define LONGITUDE_Y                 65
#define LONGITUDE_DEGREE_1_X        30
#define LONGITUDE_DEGREE_2_X        LONGITUDE_DEGREE_1_X + 8
#define LONGITUDE_DOT_X             LONGITUDE_DEGREE_2_X + 8
#define LONGITUDE_MINUTES_1_X       LONGITUDE_DOT_X + 5
#define LONGITUDE_MINUTES_2_X       LONGITUDE_MINUTES_1_X + 8
#define LONGITUDE_MINUTES_3_X       LONGITUDE_MINUTES_2_X + 8
#define LONGITUDE_MINUTES_4_X       LONGITUDE_MINUTES_3_X + 8
#define LONGITUDE_MINUTES_5_X       LONGITUDE_MINUTES_4_X + 8
#define LONGITUDE_MINUTES_6_X       LONGITUDE_MINUTES_5_X + 8
#define LONGITUDE_MINUTES_7_X       LONGITUDE_MINUTES_6_X + 8
/* altitude */
#define ALTITUDE_COLOR              WHITE
#define ALTITUDE_X                  5
#define ALTITUDE_Y                  85
#define ALTITUDE_HUNDREDS_X         30
#define ALTITUDE_TENS_X	            ALTITUDE_HUNDREDS_X + 8
#define ALTITUDE_UNITS_X            ALTITUDE_TENS_X + 8
#define ALTITUDE_DOT_X              ALTITUDE_UNITS_X + 8
#define ALTITUDE_FRACTION_X         ALTITUDE_DOT_X + 5
#define ALTITUDE_UNIT_X	            ALTITUDE_FRACTION_X + 10
/* speed */
#define SPEED_COLOR                 GREEN
#define SPEED_X                     5
#define SPEED_Y                     105
#define SPEED_INTEGER_X             50
#define SPEED_DOT_X                 SPEED_INTEGER_X + 8
#define SPEED_FRACTION_1_X          SPEED_DOT_X + 5
#define SPEED_FRACTION_2_X          SPEED_FRACTION_1_X + 8
#define SPEED_FRACTION_3_X          SPEED_FRACTION_2_X + 8
#define SPEED_UNIT_X                SPEED_FRACTION_3_X + 10

void display_init();
void draw_time();
void draw_date();
void draw_location();
void draw_altitude();
void draw_speed();

#endif /* DISPLAY_H_ */

#include "display.h"
#include <stdio.h>

void display_init()
{
  SSD1283A_init();

  setFont(&arial_14);
  /* time */
  setColor(TIME_COLOR);
  setCursor(TIME_X, TIME_Y);
  printString("time:");
  setCursor(HOURS_TENS_X, TIME_Y);
  printChar('0');
  setCursor(HOURS_UNITS_X, TIME_Y);
  printChar('0');	
  setCursor(TIME_DOT_1_X, TIME_Y);
  printChar(':');
  setCursor(MINUTES_TENS_X, TIME_Y);
  printChar('0');
  setCursor(MINUTES_UNITS_X, TIME_Y);
  printChar('0');	
  setCursor(TIME_DOT_2_X, TIME_Y);
  printChar(':');
  setCursor(SECONDS_TENS_X, TIME_Y);
  printChar('0');
  setCursor(SECONDS_UNITS_X, TIME_Y);
  printChar('0');
  /* date */
  setColor(DATE_COLOR);
  setCursor(DATE_X, DATE_Y);
  printString("date:");
  setCursor(DAY_TENS_X, DATE_Y);
  printChar('0');
  setCursor(DAY_UNITS_X, DATE_Y);
  printChar('0');	
  setCursor(DATE_SLASH_1_X, DATE_Y);
  printChar('/');
  setCursor(MONTH_TENS_X, DATE_Y);
  printChar('0');
  setCursor(MONTH_UNITS_X, DATE_Y);
  printChar('0');	
  setCursor(DATE_SLASH_2_X, DATE_Y);
  printChar('/');
  setCursor(YEAR_TENS_X, DATE_Y);
  printChar('0');
  setCursor(YEAR_UNITS_X, DATE_Y);
  printChar('0');	
  /* latitude */
  setColor(LATITUDE_COLOR);
  setCursor(LATITUDE_X, LATITUDE_Y);
  printString("lat:");
  setCursor(LATITUDE_DEGREE_1_X, LATITUDE_Y);
  printChar('0');
  setCursor(LATITUDE_DEGREE_2_X, LATITUDE_Y);
  printChar('0');	
  setCursor(LATITUDE_DOT_X, LATITUDE_Y);
  printChar('.');
  setCursor(LATITUDE_MINUTES_1_X, LATITUDE_Y);
  printChar('0');	
  setCursor(LATITUDE_MINUTES_2_X, LATITUDE_Y);
  printChar('0');	
  setCursor(LATITUDE_MINUTES_3_X, LATITUDE_Y);
  printChar('0');	
  setCursor(LATITUDE_MINUTES_4_X, LATITUDE_Y);
  printChar('0');	
  setCursor(LATITUDE_MINUTES_5_X, LATITUDE_Y);
  printChar('0');	
  setCursor(LATITUDE_MINUTES_6_X, LATITUDE_Y);
  printChar('0');	
  setCursor(LATITUDE_MINUTES_7_X, LATITUDE_Y);
  printChar('0');	
  /* longitude */
  setColor(LONGITUDE_COLOR);
  setCursor(LONGITUDE_X, LONGITUDE_Y);
  printString("lng:");
  setCursor(LONGITUDE_DEGREE_1_X, LONGITUDE_Y);
  printChar('0');
  setCursor(LONGITUDE_DEGREE_2_X, LONGITUDE_Y);
  printChar('0');	
  setCursor(LONGITUDE_DOT_X, LONGITUDE_Y);
  printChar('.');
  setCursor(LONGITUDE_MINUTES_1_X, LONGITUDE_Y);
  printChar('0');
  setCursor(LONGITUDE_MINUTES_2_X, LONGITUDE_Y);
  printChar('0');
  setCursor(LONGITUDE_MINUTES_3_X, LONGITUDE_Y);
  printChar('0');
  setCursor(LONGITUDE_MINUTES_4_X, LONGITUDE_Y);
  printChar('0');
  setCursor(LONGITUDE_MINUTES_5_X, LONGITUDE_Y);
  printChar('0');
  setCursor(LONGITUDE_MINUTES_6_X, LONGITUDE_Y);
  printChar('0');
  setCursor(LONGITUDE_MINUTES_7_X, LONGITUDE_Y);
  printChar('0');	
  /* altitude */
  setColor(ALTITUDE_COLOR);
  setCursor(ALTITUDE_X, ALTITUDE_Y);
  printString("alt:");
  setCursor(ALTITUDE_HUNDREDS_X, ALTITUDE_Y);
  printChar('0');
  setCursor(ALTITUDE_TENS_X, ALTITUDE_Y);
  printChar('0');	
  setCursor(ALTITUDE_UNITS_X, ALTITUDE_Y);
  printChar('0');	
  setCursor(ALTITUDE_DOT_X, ALTITUDE_Y);
  printChar('.');
  setCursor(ALTITUDE_FRACTION_X, ALTITUDE_Y);
  printChar('0');
  setCursor(ALTITUDE_UNIT_X, ALTITUDE_Y);
  printChar('m');	
  /* speed */
  setColor(SPEED_COLOR);
  setCursor(SPEED_X, SPEED_Y);
  printString("speed:");
  setCursor(SPEED_INTEGER_X, SPEED_Y);
  printChar('0');	
  setCursor(SPEED_DOT_X, SPEED_Y);
  printChar('.');	
  setCursor(SPEED_FRACTION_1_X, SPEED_Y);
  printChar('0');	
  setCursor(SPEED_FRACTION_2_X, SPEED_Y);
  printChar('0');
  setCursor(SPEED_FRACTION_3_X, SPEED_Y);
  printChar('0');	
  setCursor(SPEED_UNIT_X, SPEED_Y);
  printString("km/h");		
}

void draw_time()
{
  static uint8_t previous_hours_tens = '0';
  static uint8_t previous_hours_units = '0';
  static uint8_t previous_minutes_tens = '0';
  static uint8_t previous_minutes_units = '0';
  static uint8_t previous_seconds_tens = '0';
  static uint8_t previous_seconds_units = '0';

  if(previous_hours_tens != gps.hours_tens)
  {
    previous_hours_tens = gps.hours_tens;
    setCursor(HOURS_TENS_X, TIME_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(HOURS_TENS_X, TIME_Y);
    setColor(TIME_COLOR);
    printChar(gps.hours_tens);
  }

  if(previous_hours_units != gps.hours_units)
  {
    previous_hours_units = gps.hours_units;
    setCursor(HOURS_UNITS_X, TIME_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(HOURS_UNITS_X, TIME_Y);
    setColor(TIME_COLOR);
    printChar(gps.hours_units);
  }

  if(previous_minutes_tens != gps.minutes_tens)
  {
    previous_minutes_tens = gps.minutes_tens;
    setCursor(MINUTES_TENS_X , TIME_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(MINUTES_TENS_X , TIME_Y);
    setColor(TIME_COLOR);
    printChar(gps.minutes_tens);
  }

  if(previous_minutes_units != gps.minutes_units)
  {
    previous_minutes_units = gps.minutes_units;
    setCursor(MINUTES_UNITS_X , TIME_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(MINUTES_UNITS_X , TIME_Y);
    setColor(TIME_COLOR);
    printChar(gps.minutes_units);
  }

  if(previous_seconds_tens != gps.seconds_tens)
  {
    previous_seconds_tens = gps.seconds_tens;
    setCursor(SECONDS_TENS_X, TIME_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(SECONDS_TENS_X, TIME_Y);
    setColor(TIME_COLOR);
    printChar(gps.seconds_tens);
  }

  if(previous_seconds_units != gps.seconds_units)
  {
    previous_seconds_units = gps.seconds_units;
    setCursor(SECONDS_UNITS_X, TIME_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(SECONDS_UNITS_X, TIME_Y);
    setColor(TIME_COLOR);
    printChar(gps.seconds_units);
  }
}

void draw_date()
{
  static uint8_t previous_day_tens = '0';
  static uint8_t previous_day_units = '0';
  static uint8_t previous_month_tens = '0';
  static uint8_t previous_month_units = '0';
  static uint8_t previous_year_tens = '0';
  static uint8_t previous_year_units = '0';

  if(previous_day_tens != gps.date_tens)
  {
    previous_day_tens = gps.date_tens;
    setCursor(DAY_TENS_X, DATE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(DAY_TENS_X, DATE_Y);
    setColor(RED);
    printChar(gps.date_tens);
  }

  if(previous_day_units != gps.date_units)
  {
    previous_day_units = gps.date_units;
    setCursor(DAY_UNITS_X, DATE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(DAY_UNITS_X, DATE_Y);
    setColor(RED);
    printChar(gps.date_units);
  }

  if(previous_month_tens != gps.month_tens)
  {
    previous_month_tens = gps.month_tens;
    setCursor(MONTH_TENS_X, DATE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(MONTH_TENS_X, DATE_Y);
    setColor(RED);
    printChar(gps.month_tens);
  }

  if(previous_month_units != gps.month_units)
  {
    previous_month_units = gps.month_units;
    setCursor(MONTH_UNITS_X, DATE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(MONTH_UNITS_X, DATE_Y);
    setColor(RED);
    printChar(gps.month_units);
  }

  if(previous_year_tens != gps.year_tens)
  {
    previous_year_tens = gps.year_tens;
    setCursor(YEAR_TENS_X, DATE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(YEAR_TENS_X, DATE_Y);
    setColor(RED);
    printChar(gps.year_tens);
  }

  if(previous_year_units != gps.year_units)
  {
    previous_year_units = gps.year_units;
    setCursor(YEAR_UNITS_X, DATE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(YEAR_UNITS_X, DATE_Y);
    setColor(RED);
    printChar(gps.year_units);
  }
}

void draw_location()
{
  static uint8_t previous_latitude_degree_1 = '0';
  static uint8_t previous_latitude_degree_2 = '0';
  static uint8_t previous_latitude_minutes_1 = '0';
  static uint8_t previous_latitude_minutes_2 = '0';
  static uint8_t previous_latitude_minutes_3 = '0';
  static uint8_t previous_latitude_minutes_4 = '0';
  static uint8_t previous_latitude_minutes_5 = '0';
  static uint8_t previous_latitude_minutes_6 = '0';
  static uint8_t previous_latitude_minutes_7 = '0';	

  static uint8_t previous_longitude_degree_1 = '0';
  static uint8_t previous_longitude_degree_2 = '0';
  static uint8_t previous_longitude_minutes_1 = '0';
  static uint8_t previous_longitude_minutes_2 = '0';
  static uint8_t previous_longitude_minutes_3 = '0';
  static uint8_t previous_longitude_minutes_4 = '0';
  static uint8_t previous_longitude_minutes_5 = '0';
  static uint8_t previous_longitude_minutes_6 = '0';
  static uint8_t previous_longitude_minutes_7 = '0';

  if(previous_latitude_degree_1 != gps.latitude_degree_1)
  {
    previous_latitude_degree_1 = gps.latitude_degree_1;
    setCursor(LATITUDE_DEGREE_1_X, LATITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LATITUDE_DEGREE_1_X, LATITUDE_Y);
    setColor(LATITUDE_COLOR);
    printChar(gps.latitude_degree_1);
  }

  if(previous_latitude_degree_2 != gps.latitude_degree_2)
  {
    previous_latitude_degree_2 = gps.latitude_degree_2;
    setCursor(LATITUDE_DEGREE_2_X, LATITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LATITUDE_DEGREE_2_X, LATITUDE_Y);
    setColor(LATITUDE_COLOR);
    printChar(gps.latitude_degree_2);
  }

  if(previous_latitude_minutes_1 != gps.latitude_minutes_1)
  {
    previous_latitude_minutes_1 = gps.latitude_minutes_1;
    setCursor(LATITUDE_MINUTES_1_X, LATITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LATITUDE_MINUTES_1_X, LATITUDE_Y);
    setColor(LATITUDE_COLOR);
    printChar(gps.latitude_minutes_1);
  }

  if(previous_latitude_minutes_2 != gps.latitude_minutes_2)
  {
    previous_latitude_minutes_2 = gps.latitude_minutes_2;
    setCursor(LATITUDE_MINUTES_2_X, LATITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LATITUDE_MINUTES_2_X, LATITUDE_Y);
    setColor(LATITUDE_COLOR);
    printChar(gps.latitude_minutes_2);
  }

  if(previous_latitude_minutes_3 != gps.latitude_minutes_3)
  {
    previous_latitude_minutes_3 = gps.latitude_minutes_3;
    setCursor(LATITUDE_MINUTES_3_X, LATITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LATITUDE_MINUTES_3_X, LATITUDE_Y);
    setColor(LATITUDE_COLOR);
    printChar(gps.latitude_minutes_3);
  }

  if(previous_latitude_minutes_4 != gps.latitude_minutes_4)
  {
    previous_latitude_minutes_4 = gps.latitude_minutes_4;
    setCursor(LATITUDE_MINUTES_4_X, LATITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LATITUDE_MINUTES_4_X, LATITUDE_Y);
    setColor(LATITUDE_COLOR);
    printChar(gps.latitude_minutes_4);
  }

  if(previous_latitude_minutes_5 != gps.latitude_minutes_5)
  {
    previous_latitude_minutes_5 = gps.latitude_minutes_5;
    setCursor(LATITUDE_MINUTES_5_X, LATITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LATITUDE_MINUTES_5_X, LATITUDE_Y);
    setColor(LATITUDE_COLOR);
    printChar(gps.latitude_minutes_5);
  }

  if(previous_latitude_minutes_6 != gps.latitude_minutes_6)
  {
    previous_latitude_minutes_6 = gps.latitude_minutes_6;
    setCursor(LATITUDE_MINUTES_6_X, LATITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LATITUDE_MINUTES_6_X, LATITUDE_Y);
    setColor(LATITUDE_COLOR);
    printChar(gps.latitude_minutes_6);
  }

  if(previous_latitude_minutes_7 != gps.latitude_minutes_7)
  {
    previous_latitude_minutes_7 = gps.latitude_minutes_7;
    setCursor(LATITUDE_MINUTES_7_X, LATITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LATITUDE_MINUTES_7_X, LATITUDE_Y);
    setColor(LATITUDE_COLOR);
    printChar(gps.latitude_minutes_7);
  }

  if(previous_longitude_degree_1 != gps.longitude_degree_1)
  {
    previous_longitude_degree_1 = gps.longitude_degree_1;
    setCursor(LONGITUDE_DEGREE_1_X, LONGITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LONGITUDE_DEGREE_1_X, LONGITUDE_Y);
    setColor(LONGITUDE_COLOR);
    printChar(gps.longitude_degree_1);
  }

  if(previous_longitude_degree_2 != gps.longitude_degree_2)
  {
    previous_longitude_degree_2 = gps.longitude_degree_2;
    setCursor(LONGITUDE_DEGREE_2_X, LONGITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LONGITUDE_DEGREE_2_X, LONGITUDE_Y);
    setColor(LONGITUDE_COLOR);
    printChar(gps.longitude_degree_2);
  }

  if(previous_longitude_minutes_1 != gps.longitude_minutes_1)
  {
    previous_longitude_minutes_1 = gps.longitude_minutes_1;
    setCursor(LONGITUDE_MINUTES_1_X, LONGITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LONGITUDE_MINUTES_1_X, LONGITUDE_Y);
    setColor(LONGITUDE_COLOR);
    printChar(gps.longitude_minutes_1);
  }

  if(previous_longitude_minutes_2 != gps.longitude_minutes_2)
  {
    previous_longitude_minutes_2 = gps.longitude_minutes_2;
    setCursor(LONGITUDE_MINUTES_2_X, LONGITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LONGITUDE_MINUTES_2_X, LONGITUDE_Y);
    setColor(LONGITUDE_COLOR);
    printChar(gps.longitude_minutes_2);
  }

  if(previous_longitude_minutes_3 != gps.longitude_minutes_3)
  {
    previous_longitude_minutes_3 = gps.longitude_minutes_3;
    setCursor(LONGITUDE_MINUTES_3_X, LONGITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LONGITUDE_MINUTES_3_X, LONGITUDE_Y);
    setColor(LONGITUDE_COLOR);
    printChar(gps.longitude_minutes_3);
  }

  if(previous_longitude_minutes_4 != gps.longitude_minutes_4)
  {
    previous_longitude_minutes_4 = gps.longitude_minutes_4;
    setCursor(LONGITUDE_MINUTES_4_X, LONGITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LONGITUDE_MINUTES_4_X, LONGITUDE_Y);
    setColor(LONGITUDE_COLOR);
    printChar(gps.longitude_minutes_4);
  }

  if(previous_longitude_minutes_5 != gps.longitude_minutes_5)
  {
    previous_longitude_minutes_5 = gps.longitude_minutes_5;
    setCursor(LONGITUDE_MINUTES_5_X, LONGITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LONGITUDE_MINUTES_5_X, LONGITUDE_Y);
    setColor(LONGITUDE_COLOR);
    printChar(gps.longitude_minutes_5);
  }

  if(previous_longitude_minutes_6 != gps.longitude_minutes_6)
  {
    previous_longitude_minutes_6 = gps.longitude_minutes_6;
    setCursor(LONGITUDE_MINUTES_6_X, LONGITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LONGITUDE_MINUTES_6_X, LONGITUDE_Y);
    setColor(LONGITUDE_COLOR);
    printChar(gps.longitude_minutes_6);
  }	

  if(previous_longitude_minutes_7 != gps.longitude_minutes_7)
  {
    previous_longitude_minutes_7 = gps.longitude_minutes_7;
    setCursor(LONGITUDE_MINUTES_7_X, LONGITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(LONGITUDE_MINUTES_7_X, LONGITUDE_Y);
    setColor(LONGITUDE_COLOR);
    printChar(gps.longitude_minutes_7);
  }	
}

void draw_altitude()
{
  static uint8_t previous_altitude_hundreds = '0';
  static uint8_t previous_altitude_tens = '0';
  static uint8_t previous_altitude_units = '0';
  static uint8_t previous_altitude_fraction = '0';
	
  if(previous_altitude_hundreds != gps.altitude_hundreds)
  {
    previous_altitude_hundreds = gps.altitude_hundreds;
    setCursor(ALTITUDE_HUNDREDS_X, ALTITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(ALTITUDE_HUNDREDS_X, ALTITUDE_Y);
    setColor(ALTITUDE_COLOR);
    printChar(gps.altitude_hundreds);		
  }

  if(previous_altitude_tens != gps.altitude_tens)
  {
    previous_altitude_tens = gps.altitude_tens;
    setCursor(ALTITUDE_TENS_X, ALTITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(ALTITUDE_TENS_X, ALTITUDE_Y);
    setColor(ALTITUDE_COLOR);
    printChar(gps.altitude_tens);
  }

  if(previous_altitude_units != gps.altitude_unit)
  {
    previous_altitude_units = gps.altitude_unit;
    setCursor(ALTITUDE_UNITS_X, ALTITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(ALTITUDE_UNITS_X, ALTITUDE_Y);
    setColor(ALTITUDE_COLOR);
    printChar(gps.altitude_units);
  }

  if(previous_altitude_fraction != gps.altitude_fraction)
  {
    previous_altitude_fraction = gps.altitude_fraction;
    setCursor(ALTITUDE_FRACTION_X, ALTITUDE_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(ALTITUDE_FRACTION_X, ALTITUDE_Y);
    setColor(ALTITUDE_COLOR);
    printChar(gps.altitude_fraction);
  }

  setCursor(ALTITUDE_UNIT_X, ALTITUDE_Y);
  setColor(BACKGROUND_COLOR);
  fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
  setCursor(ALTITUDE_UNIT_X, ALTITUDE_Y);
  setColor(ALTITUDE_COLOR);
  printChar(gps.altitude_unit);		
}

void draw_speed()
{
  static uint8_t previous_speed_integer = '0';
  static uint8_t previous_speed_fraction_1 = '0';
  static uint8_t previous_speed_fraction_2 = '0';
  static uint8_t previous_speed_fraction_3 = '0';

  if(previous_speed_integer != gps.speed_integer)
  {
    previous_speed_integer = gps.speed_integer;
    setCursor(SPEED_INTEGER_X, SPEED_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(SPEED_INTEGER_X, SPEED_Y);
    setColor(SPEED_COLOR);
    printChar(gps.speed_integer);
  }

  if(previous_speed_fraction_1 != gps.speed_fraction_1)
  {
    previous_speed_fraction_1 = gps.speed_fraction_1;
    setCursor(SPEED_FRACTION_1_X, SPEED_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(SPEED_FRACTION_1_X, SPEED_Y);
    setColor(SPEED_COLOR);
    printChar(gps.speed_fraction_1);
  }

  if(previous_speed_fraction_2 != gps.speed_fraction_2)
  {
    previous_speed_fraction_2 = gps.speed_fraction_2;
    setCursor(SPEED_FRACTION_2_X, SPEED_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(SPEED_FRACTION_2_X, SPEED_Y);
    setColor(SPEED_COLOR);
    printChar(gps.speed_fraction_2);
  }	

  if(previous_speed_fraction_3 != gps.speed_fraction_3)
  {
    previous_speed_fraction_3 = gps.speed_fraction_3;
    setCursor(SPEED_FRACTION_3_X, SPEED_Y);
    setColor(BACKGROUND_COLOR);
    fillRectangle(ARIAL_14_RECTANGLE_WIDHT, ARIAL_14_RECTANGLE_HEIGHT);
    setCursor(SPEED_FRACTION_3_X, SPEED_Y);
    setColor(SPEED_COLOR);
    printChar(gps.speed_fraction_3);
  }	
}

Основная функция

В main() по завершению пакета на дисплей выводятся:
1. Время и дата.
2. Широта, долгота, высота и скорость, если соответствующие данные — валидные.

Файлы

Программа выложена в архив. Кроме того, там же размещён код эмулятора, который передаёт в последовательный порт такие же данные, что и реальный модуль.
🎁kod.zip  71.2 Kb ⇣ 2

Спасибо за внимание!

Камрад, рассмотри датагорские рекомендации

🌼 Полезные и проверенные железяки, можно брать

Опробовано в лаборатории редакции или читателями.




 

Читательское голосование

Нравится

Статью одобрили 11 читателей.

Для участия в голосовании зарегистрируйтесь и войдите на сайт с вашими логином и паролем.
 

Поделись с друзьями!

 

 

Связанные материалы

 

Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 6. Протоколы обмена данными I2C и SPI... В проекте из предыдущей части нашей ассемблерной эпопеи мы подключали к микроконтроллеру светодиод...
Схема на Датагоре. Новая статья Электронные часы-термометр с беспроводным датчиком через радиомодуль nRF24L01... Здравствуйте, уважаемые Датагорцы! Представляю вашему вниманию электронные часы с функцией...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 3. TFT дисплей 2.8" (240х320) на ILI9341... Битва за урожай закончена, можно продолжить повествование. Полноцветный TFT-дисплей 240×320 ILI9341...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 2. Шаблонные файлы и инструкции МК... В предыдущей части статьи мы провели подготовительную работу и вкратце разобрали принципы работы...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 7. Компиляция, отладка, загрузка... Привет датагорцам и гостям нашего кибер-города! В предыдущих частях материала по Ассемблеру...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 5. Периферия МК.... Сегодня мы рассмотрим работу следующих модулей периферии: • порта ввода-вывода, • таймера •...
Схема на Датагоре. Новая статья Программирование микроконтроллеров на языке C. Часть 2... Добрый день, уважаемые камрады-датагорцы! Сегодня, рассмотрев некоторые общие моменты, мы займёмся...
Схема на Датагоре. Новая статья Обмен данными между Java-приложением и МК. Часть 1. По проводу, USB-UART... Приветствую всех жителей и гостей кибер-города Датагор! Работа устройства на базе микроконтроллера...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 3. Макросы и функции... Привет, датагорцы — любители Ассемблера! В пункте 2.5.2 «Инструкции условного перехода» предыдущей...
Схема на Датагоре. Новая статья Программирование микроконтроллеров на языке C. Часть 3... Всем датагорцам привет! Продолжим изучение микроконтроллеров и языка Си. Эффективность программы...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 5. Графика... В предыдущих частях статьи нами более или менее подробно были рассмотрены основные принципы работы...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 4. Система адресации памяти, назначение выводов, тактирование и прерывания МК... Привет датагорцам! Сегодня мы остановимся на следующих вопросах касательно рассматриваемых нами...
 

Комментарии, вопросы, ответы, дополнения, отзывы

 

Добавить комментарий, вопрос, отзыв 💬

Камрады, будьте дружелюбны, соблюдайте правила!

  • Смайлы и люди
    Животные и природа
    Еда и напитки
    Активность
    Путешествия и места
    Предметы
    Символы
    Флаги
 
 
В начало | Зарегистрироваться | Заказать наши киты почтой