SysInfo for Arduino

nik182
Offline
Зарегистрирован: 04.05.2015

Это не порты, это регистры таймера. Цитата: 14.4.1 TIM1&TIM8 control register 1 (TIMx_CR1) . . . . . . . . . . . . . . . . . . . . . 333

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

В данном случае порты - это назначение, регистры - это реализация.

Ничто не мешает делать порты в виде регистров.

В то же время, например, 74HC595 - регистр, но не порт. А TIMx_... - именно порты, т.к.:

1. Расположены в адресном пространстве процессора.

2. Служат для управления периферийным устройством.

nik182
Offline
Зарегистрирован: 04.05.2015

Порт это что то такое, что позволяет пропустить через себя наружу или внутрь. Например, кроме непосредственно портов ножек процессора, собранных по 8 - 16 штук, последовательный порт, SPI порт, в ББ это LPT и.т.д. Но всё равно это вывод из корпуса процессора, который передаёт информацию, в виде физического изменения состояния - напряжения, отбирая ноги у портов ввода-вывода.  Никогда не рассматривал порт как логическое устройство.  Как раз регистры для меня не могут быть портами, потому что через них не проходит ничего. Это ключи управления. 74HC595 - регистр по функциональному назначению. МС имеет несколько ячеек памяти, объединённых в регистр. Вывод на ноги МС идет через порт. Этот порт простой - защёлка с выходом на ноги. Найдите мне пожалуйста хоть в одном мануале на микроконтроллер слово порт в значении логического устройства. Я ни разу не встречал.  

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

1. Порт - устройство, расположенное в адресном пространстве процессора и служащее для взаимодействия с периферией.

2. Регистр - устройство, способное запоминать и хранить информацию.

3. Из сравнения 1 и 2 делаем вывод, что термины "регистр" и "порт" - ортогональные: как регистр может либо быть, либо не быть портом, так и порт - быть либо не быть регистром.

4. В современных МК много периферии собрано внутри одного корпуса (и даже кристалла). Соответственно, устройства, служащие для взаимодействия процессора с периферией могут не иметь никаких выводов за пределы корпуса (кристалла). И, соответственно, не иметь подключенных к ним "ножек".

5. В русском языке слово "мануал" употребляется только в значении органной клавиатуры. Те же, кто слово "manual" переводят как "мануал", а не "руководство пользователя", говорят не по-русски, а потому ориентироваться на употребляемую ими терминологию вряд ли разумно. Чтобы владеть правильно русскоязычной терминологией, нужно читать не "мануалы", а учебники. Да, собственно, Вы и сами только что приводили примеры, как авторы "мануалов" пишут кто в лес, кто - по дрова.

nik182
Offline
Зарегистрирован: 04.05.2015

И тут мы приходим к любимому первому вопросу одного из наших профессоров - давайте определимся с терминами. Как я вижу наши определения сильно расходятся. В таких условиях я не вижу возможности продолжать дискуссию до момента согласования определений. Мануал - побуквенная калька с английского слова входящего в названия документов - руководств по эксплуатации. Это не учебники. Русский язык он такой. Вбирает в себя многое и развивается. Что то остаётся, что то отбрасывается. Очень люблю читать книги Никитина. Главное не содержание. Очень нравиться как он играет со словами. Иногда просто удовольствие увидеть новые словоформы и их применение. Для меня регистр и порт разные устройства. Регистр это совокупность ячеек памяти имеющих один адрес в адресном пространстве и одинаковое назначение по функционалу. Порт это совокупность сущностей, обеспечивающаяя обмен информацией между МК и внешним миром. В порт ввода-вывода входят в том числе несколько регистров, каждый из которых имеет свой собственный адрес. Таким образом порт это более широкое понятие.

P.S. Пример от STM - RM0383 Reference manual STM32F411xC/E advanced ARM®-based 32-bit MCUs - в данном случае справочное руководство по МК. Предлагаю ознакомиться и найти как используется слово port. 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

nik182 пишет:

давайте определимся с терминами.

Логично.

От себя хочу заметить, что, коль скоро, мы говорим об устоявшихся терминах, их свойства не должны быть противоречивы. Что дает основание полагать, что в таком случае нам на помощь может прийти логика.

Цитата:

Регистр это совокупность ячеек памяти имеющих один адрес в адресном пространстве и одинаковое назначение по функционалу.

Поправка: регистр не обязан иметь адрес. Пример - уже упоминавшийся 74HC595. 

И уточнение: коль скоро регистр - совокупность ячеек памяти, значение, прочитанное из регистра, должно соответствовать значению, записанному в регистр. При этом не обязательно, чтобы регистр одновременнор поддерживал операции как чтение, так и записи. Чтение и запись может происходить "с разных сторон", как в том же 74HC595.

Цитата:

Порт это совокупность сущностей, обеспечивающаяя обмен информацией между МК и внешним миром.

Не с "МК и внешним миром", а "процессором и периферийным устройством". Последние, напомню, могут быть расположены и на одном кристалле, поэтому до "внешнего мира" дело может и не дойти.

Цитата:

В порт ввода-вывода входят в том числе несколько регистров, каждый из которых имеет свой собственный адрес. Таким образом порт это более широкое понятие.

Давайте сначала, опираясь на определения, сделаем выводы, и только потом на их основе будем формулировать заключение.

МК - частный случай ЭВМ (компьютера). Соответственно, к нему применимы все архитектурные особенности ЭВМ.

По классике ЭВМ состоит из:

1. Центрального процессора.

2. Оперативной памяти.

3. Периферийных устройств. При этом среди ПУ должно быть минимум одно устройство ввода и одно - вывода.

Процессор имеет ограниченное количество возможностей, в частности, он имеет функции чтения и записи. К памяти и периферийным устройствам он может обращаться единообразно - только по адресу.

Нас сейчас интересует только обмен между процессором и периферийными устройствами, который и осуществляется через порты (по определению порта). Отсюда свойства портов:

1. Назначение - обмен информацией между процессором и периферийным устройством.

2. Обязан иметь адрес.

3. Должен поддерживать функции чтения и/или записи. 

4. Не обязан запоминать информацию (т.е. считанная информация не обязана повторять записанную).

Отсюда и следует "танцевать".

Может ли порт содержать несколько регистров? - может, но при этом порт имеет единственный адрес, значит, в этом случае и все составляющие порт регистры должны быть доступны по одному адресу. Такое тоже бывает.

Является ли порт более широким понятием? Вряд ли: каждый имеет собственные свойства, которые мало пересекаются у порта и регистра (собственно, пересекаются только возможности операций чтения и записи, все остальное - отличается), поэтому как порт может либо быть, либо не быть регистром ("быть" - когда запоминает информацию и прочитанное равно записанному, "не быть" - в противном случае), так и регистр может либо быть, либо не быть портом ("быть" - когда имеет адрес и служит для связи с периферией, "не быть" - в противном случае).

Один из примеров: TIMx_SR - "регистр статуса" STM32 на самом деле регистром не является, т.к. не сохраняет записанную величину.

nik182
Offline
Зарегистрирован: 04.05.2015

Я работаю с МК уже более 20 лет. За это время понятие порт претерпело несколько изменений. В самом начале портом могли назвать единственную ногу МК. Потом это отошло в сторону и портом стали называть устройство позволяющее обменяться информацией с МК - если брать IO pins то это несколько регистров и схема их соединения с ногами МК размером 8 бит. Когда начал знакомиться с STM32 в одной из статей вычитал ( к сожалению не нашёл с ходу сейчас), что из за того, что IO port получил дальнейшее развитие, новые функции, то от слова порт оказались в пользу  входы-выходы общего назначения. 

Примеров всё так же Вы не приводите. Только Ваши соображения, основанные на Ваших представлениях об устройстве мира. Просто пройдите поиском по предлагаемому Reference manual STM32F411xC/E и сделайте вывод о том, в каком контексте используется слово port по отношению к микроконтроллеру. Внутри документа имеется блоксхема системной архитектуры МК с описанием взаимодействия отдельных блоков - ядра, памяти, периферии. При описании блок схемы слово порт не встречается.     

DeGlucker
Offline
Зарегистрирован: 23.07.2014

Простая программа для проверки портов. В режиме OUTPUT по команде "s" или пробел записывает "1" по очереди во все используемые биты портов B, C, D и читает сосьояние пинов.

1-я колонка - то, что записано, 2, 3, 4 - то что прочиталось.

В режиме AUTO биты выводятся через 50 иксек.

#define	Version	"Port test V 3.10"
#define byte uint8_t

const byte MaskB = 0x3F;
const byte MaskC = 0x3F;
const byte MaskD = 0xFC;

byte smpl = 0;
byte mode = 0;

void printcode(byte);

void setup() {

  Serial.begin(19200);
  DDRB |= MaskB;
  DDRC |= MaskC;
  DDRD |= MaskD;
  Serial.println(Version);
  Serial.println("  s - step");
  Serial.println("  i - input pullup");
  Serial.println("  o - output");
  Serial.println("  a - auto output");
}
// ----------------------------
void loop() {

  if (Serial.available() > 0) {
    char cmd = Serial.read();
    switch (cmd) {
      case 'o':
	mode = 0;
	smpl = 0;
        DDRB |= MaskB;
        DDRC |= MaskC;
        DDRD |= MaskD;
	Serial.println("OUTPUT");
	break;

      case 'a':
	mode = 1;
	Serial.println("OUTPUT_AUTO");
	break;

      case ' ':
      case 's':
	if (mode == 0) {
	  SetPorts();
	  printcode(smpl);
	  printcode(PINB&MaskB);
	  printcode(PINC&MaskC);
	  printcode(PIND&MaskD);
	  Serial.println("");
	}
	else if (mode == 2)
	  PrintInput();
	break;

      case 'i':
	mode = 2;
	DDRB |= MaskB;
	PORTB = MaskB;
	DDRB &= ~MaskB;
	DDRC |= MaskC;
	PORTC = MaskC;
	DDRC &= ~MaskC;
	DDRD |= MaskD;
	PORTD = MaskD;
	DDRD &= ~MaskD;
	Serial.print("INPUT_PULLUP");
	PrintInput();
	break;
    }
  }

  if (mode == 1)
    SetPorts();

  byte i = 199;         // 50 usec
  do { asm volatile("nop"); } while (i--);
}

// ----------------------------
void SetPorts() {

  smpl = smpl<<1;
  if (smpl == 0)
    smpl = 1;

  PORTB &= ~MaskB;
  PORTB |= (smpl & MaskB);
  PORTC &= ~MaskC;
  PORTC |= (smpl & MaskC);
  PORTD &= ~MaskD;
  PORTD |= (smpl & MaskD);
}
// ----------------------------
void printcode(byte code) {

  if (code < 0x10)
    Serial.print(" ");
  Serial.print(code, HEX);
  Serial.print(" ");
}

// ----------------------------
void PrintInput() {

  Serial.println("");
  printcode(MaskB);
  printcode(MaskC);
  printcode(MaskD);
  Serial.println("");
  printcode(PINB&MaskB);
  printcode(PINC&MaskC);
  printcode(PIND&MaskD);
  Serial.println("");
}

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

nik182, Вы все пытаетесь увести разговор в какие-то частности. Ну при чем здесь stm32f411? Какое он имеет отношение к основным архитектурным принципам построения ЭВМ? Этим принципам уже более 70 лет, тогда как МК появились совсем недавно.

Вот смотрите: периферийное устройство может быть расположено на одном кристалле с центральным процессором, может быть на одной плате, а может быть - вообще в другом помещении. Неужели Вы считаете, что этот факт должен как-то сказываться на названии устройства, посредством которого центральный процессор обменивается информацией с периферийным устройством?

Вот скажите, как по-Вашему следует называть устройство, посредством которого центральный процессор, который умеет только читать и писать по выбранному адресу, общается с произвольным периферийным устройством? Как называется это устройство (возможно, логическое), которое имеет уникальный адрес и функции чтения и/или записи?

nik182
Offline
Зарегистрирован: 04.05.2015

Это Вы уводите в сторону. Вопрос вырос из Вашего замечания, понимает ли человек разницу между GPIO и портом. Как я вижу, у Вас есть собственное понимание слова порт. Оно ни как не коррелирует с практикой употребления слова порт в справочной литературе по микроконтроллерам. Все мои посты относились исключительно к МК. Никакой другой комнаты с периферией. Если быть совсем конкретным, то я не вижу разницы между названием porta, portb для АВР и GPIO для STM. Большее я обсуждать не хотел.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Ну не хотел, так не хотел.

Похоже, мы действительно говорили о совершенно разных вещах, ибо по моим представлениям porta и portb применительно к AVR так же соотносятся с термином port, как "милостивый государь" и "Государь Император".

nik182
Offline
Зарегистрирован: 04.05.2015

Но тема в общем интересная с методологической точки зрения. Предлагаю переместиться а отвлечённые. Надеюсь там можно будет и мнения других услышать. Только нужно как то сформулировать тему, что бы было понятно в чём корень вопроса. Я с ходу ни чего не придумал, что бы отразить мою и Вашу позицию одновременно.  

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

arduinec пишет:

Нашёл кучку сигнатур AVR-контроллеров: http://www.gammon.com.au/forum/?id=11633

В скетче заложено измерение напряжения питания и внутренней температуры ATmega32U4 (Леонардо) и ATtiny85, но проверить пока не могу из-за их отсутствия.

а attiny85 на каком ядре?

SYSINFO 1.08 (добавлен ID)
 

#define Version 1.08

#define SIGRD 5

#include <avr/boot.h>
#include <ArduinoUniqueID.h>


typedef struct {
  byte sig[3];
  const char *desc;
} signatureType;

int foundSig = -1;
unsigned int tik = 0;
byte freq, count = 0;

volatile unsigned int int_tic;
volatile unsigned long tic;
bool readwdt = 0;
unsigned int n = 0;

void setWDT() {
  TCCR1B = 0; TCCR1A = 0; TCNT1 = 0;
  TIMSK1 = 1 << TOIE1;
  TCCR1B = 1 << CS10;
  WDTCSR = (1 << WDCE) | (1 << WDE); //установить биты WDCE WDE (что б разрешить запись в другие биты
  WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1); // разрешение прерывания + выдержка 1 секунда(55 страница даташита)
}

ISR (TIMER1_OVF_vect) { //прерывания счёта по переполнению uint
  int_tic++; //считать переполнения через 65536 тактов
}

void setup()
{
  Serial.begin(115200);
  Serial.print(F("\nSysInfo for Arduino version "));
  Serial.println(Version);
  Serial.println();
  
  //UniqueIDdump(Serial);
  Serial.print("UniqueID: ");
  for (size_t i = 0; i < UniqueIDsize; i++)
  {
    if (UniqueID[i] < 0x10)
      Serial.print("0");
    Serial.print(UniqueID[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
  
  delay(1000);

  Frequency();
  Serial.println();
  Serial.print(F("F_CPU = "));
  Serial.println(F_CPU);

  Serial.print(F("Frequency = "));
  Serial.print(freq);
  Serial.println(F(" MHz"));
  Serial.println();

  CPU();
  Memory();
  Signature();
  VCC();
  Temperature();
  TestPins();
  int_tic = 0;
  readwdt = true;
  setWDT();
}

void loop() {}

void Frequency()
{
  byte fcpu;

  TCCR1A = 0;
  TCCR1B = 1 << CS10 | 1 << CS12; // clk/1024
  TCNT1 = 0;

  WDTCSR = (1 << WDCE) | (1 << WDE);
  WDTCSR = (1 << WDIE); // enable WDT delay 16ms

  while (count < 3) delay(1);

  WDTCSR = (1 << WDCE); // disable WDT

  if (tik <= 30) freq = 1;
  if ((tik > 30)  && (tik <= 100)) freq = 4;
  if ((tik > 100) && (tik <= 170)) freq = 8;
  if ((tik > 170) && (tik <= 240)) freq = 12;
  if ((tik > 240) && (tik <= 320)) freq = 16;
  if (tik > 320) freq = 20;

  fcpu = F_CPU / 1000000L;

  if ((fcpu == 16) && (freq == 8)) Serial.begin(19200);
}

ISR (WDT_vect) {
  if (!readwdt) {
    tik = TCNT1;
    count++;
    TCNT1 = 0;
  } else {
    if (n < 10) {
      n++;
      tic = ((uint32_t)int_tic << 16) | TCNT1 ; //подсчёт тиков
      Serial.print(" 1 Sec WDT= ");
      Serial.print( (float) tic * 625E-10 , 6 );
      ICR1 = 0; int_tic = 0; TCNT1 = 0;
      Serial.print(' ');
      Serial.println("Seconds");
    }
  }
}

void CPU()
{
  Serial.print(F("CPU_IDE = "));

#if   defined(__AVR_ATmega328P__)
  Serial.println(F("ATmega328P"));
#elif defined(__AVR_ATmega48__)
  Serial.println(F("ATmega48"));
#elif defined(__AVR_ATmega48P__)
  Serial.println(F("ATmega48P"));
#elif defined(__AVR_ATmega88__)
  Serial.println(F("ATmega88"));
#elif defined(__AVR_ATmega88P__)
  Serial.println(F("ATmega88P"));
#elif defined(__AVR_ATmega168__)
  Serial.println(F("ATmega168"));
#elif defined(__AVR_ATmega168P__)
  Serial.println(F("ATmega168P"));
#elif defined(__AVR_ATmega328__)
  Serial.println(F("ATmega328"));
#elif defined(__AVR_ATmega164__)
  Serial.println(F("ATmega164"));
#elif defined(__AVR_ATmega164P__)
  Serial.println(F("ATmega164P"));
#elif defined(__AVR_ATmega324__)
  Serial.println(F("ATmega324"));
#elif defined(__AVR_ATmega324P__)
  Serial.println(F("ATmega324P"));
#elif defined(__AVR_ATmega644__)
  Serial.println(F("ATmega644"));
#elif defined(__AVR_ATmega644P__)
  Serial.println(F("ATmega644P"));
#elif defined(__AVR_ATmega1284__)
  Serial.println(F("ATmega1284"));
#elif defined(__AVR_ATmega1284P__)
  Serial.println(F("ATmega1284P"));
#elif defined(__AVR_ATmega640__)
  Serial.println(F("ATmega640"));
#elif defined(__AVR_ATmega1280__)
  Serial.println(F("ATmega1280"));
#elif defined(__AVR_ATmega1281__)
  Serial.println(F("ATmega1281"));
#elif defined(__AVR_ATmega2560__)
  Serial.println(F("ATmega2560"));
#elif defined(__AVR_ATmega2561__)
  Serial.println(F("ATmega2561"));
#elif defined(__AVR_ATmega8U2__)
  Serial.println(F("ATmega8U2"));
#elif defined(__AVR_ATmega16U2__)
  Serial.println(F("ATmega16U2"));
#elif defined(__AVR_ATmega32U2__)
  Serial.println(F("ATmega32U2"));
#elif defined(__AVR_ATmega16U4__)
  Serial.println(F("ATmega16U4"));
#elif defined(__AVR_ATmega32U4__)
  Serial.println(F("ATmega32U4"));
#elif defined(__AVR_AT90USB82__)
  Serial.println(F("AT90USB82"));
#elif defined(__AVR_AT90USB162__)
  Serial.println(F("AT90USB162"));
#elif defined(__AVR_ATtiny24__)
  Serial.println(F("ATtiny24"));
#elif defined(__AVR_ATtiny44__)
  Serial.println(F("ATtiny44"));
#elif defined(__AVR_ATtiny84__)
  Serial.println(F("ATtiny84"));
#elif defined(__AVR_ATtiny25__)
  Serial.println(F("ATtiny25"));
#elif defined(__AVR_ATtiny45__)
  Serial.println(F("ATtiny45"));
#elif defined(__AVR_ATtiny85__)
  Serial.println(F("ATtiny85"));
#elif defined(__AVR_ATtiny13__)
  Serial.println(F("ATtiny13"));
#elif defined(__AVR_ATtiny13A__)
  Serial.println(F("ATtiny13A"));
#elif defined(__AVR_ATtiny2313__)
  Serial.println(F("ATtiny2313"));
#elif defined(__AVR_ATtiny2313A__)
  Serial.println(F("ATtiny2313A"));
#elif defined(__AVR_ATtiny4313__)
  Serial.println(F("ATtiny4313"));
#elif defined(__AVR_ATmega8__)
  Serial.println(F("ATmega8"));
#elif defined(__AVR_ATmega8A__)
  Serial.println(F("ATmega8A"));
#else
  Serial.println(F("Unknown"));
#endif

  Serial.println();
}

void Memory()
{
  extern int __bss_end, *__brkval;
  int freeRam;

  Serial.print(F("Flash Memory = "));
  Serial.print(FLASHEND);
  Serial.println(F(" bytes"));

  if ((int)__brkval == 0)
    freeRam = ((int)&freeRam) - ((int)&__bss_end);
  else
    freeRam = ((int)&freeRam) - ((int)__brkval);

  Serial.print(F("Free RAM memory = "));
  Serial.print(freeRam);
  Serial.println(F(" bytes"));

  Serial.println();
}

void Signature()
{
  const signatureType signatures [] = {
    { { 0x1E, 0x95, 0x0F }, "ATmega328P",  },    //  0
    { { 0x1E, 0x92, 0x05 }, "ATmega48A",   },    //  1
    { { 0x1E, 0x92, 0x0A }, "ATmega48PA",  },    //  2
    { { 0x1E, 0x93, 0x0A }, "ATmega88A",   },    //  3
    { { 0x1E, 0x93, 0x0F }, "ATmega88PA",  },    //  4
    { { 0x1E, 0x94, 0x06 }, "ATmega168A",  },    //  5
    { { 0x1E, 0x94, 0x0B }, "ATmega168PA", },    //  6
    { { 0x1E, 0x95, 0x14 }, "ATmega328",   },    //  7
    { { 0x1E, 0x95, 0x16 }, "ATmega328PB", },    //  8
    { { 0x1E, 0x94, 0x0A }, "ATmega164P",  },    //  9
    { { 0x1E, 0x95, 0x08 }, "ATmega324P",  },    // 10
    { { 0x1E, 0x96, 0x0A }, "ATmega644P",  },    // 11
    { { 0x1E, 0x97, 0x05 }, "ATmega1284P", },    // 12
    { { 0x1E, 0x97, 0x06 }, "ATmega1284",  },    // 13
    { { 0x1E, 0x96, 0x08 }, "ATmega640",   },    // 14
    { { 0x1E, 0x97, 0x03 }, "ATmega1280",  },    // 15
    { { 0x1E, 0x97, 0x04 }, "ATmega1281",  },    // 16
    { { 0x1E, 0x98, 0x01 }, "ATmega2560",  },    // 17
    { { 0x1E, 0x98, 0x02 }, "ATmega2561",  },    // 18
    { { 0x1E, 0x93, 0x89 }, "ATmega8U2",   },    // 19
    { { 0x1E, 0x94, 0x89 }, "ATmega16U2",  },    // 20
    { { 0x1E, 0x95, 0x8A }, "ATmega32U2",  },    // 21
    { { 0x1E, 0x94, 0x88 }, "ATmega16U4",  },    // 22
    { { 0x1E, 0x95, 0x87 }, "ATmega32U4",  },    // 23
    { { 0x1E, 0x93, 0x82 }, "At90USB82",   },    // 24
    { { 0x1E, 0x94, 0x82 }, "At90USB162",  },    // 25
    { { 0x1E, 0x91, 0x0B }, "ATtiny24",    },    // 26
    { { 0x1E, 0x92, 0x07 }, "ATtiny44",    },    // 27
    { { 0x1E, 0x93, 0x0C }, "ATtiny84",    },    // 28
    { { 0x1E, 0x91, 0x08 }, "ATtiny25",    },    // 29
    { { 0x1E, 0x92, 0x06 }, "ATtiny45",    },    // 30
    { { 0x1E, 0x93, 0x0B }, "ATtiny85",    },    // 31
    { { 0x1E, 0x91, 0x0A }, "ATtiny2313A", },    // 32
    { { 0x1E, 0x92, 0x0D }, "ATtiny4313",  },    // 33
    { { 0x1E, 0x90, 0x07 }, "ATtiny13A",   },    // 34
    { { 0x1E, 0x93, 0x07 }, "ATmega8A",    }     // 35
  };

  int flashSize, NumSig = 36;
  byte sig[3], fuse;

  Serial.print(F("Signature = "));

  sig[0] = boot_signature_byte_get(0);
  if (sig[0] < 16) Serial.print("0");
  Serial.print(sig[0], HEX);
  Serial.print(" ");

  sig[1] = boot_signature_byte_get(2);
  if (sig[1] < 16) Serial.print("0");
  Serial.print(sig[1], HEX);
  Serial.print(" ");

  sig[2] = boot_signature_byte_get(4);
  if (sig[2] < 16) Serial.print("0");
  Serial.println(sig[2], HEX);

  Serial.print(F("Fuses (Low/High/Ext/Lock) = "));

  fuse = boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS);
  if (fuse < 16) Serial.print("0");
  Serial.print(fuse, HEX);
  Serial.print(" ");

  fuse = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
  if (fuse < 16) Serial.print("0");
  Serial.print(fuse, HEX);
  Serial.print(" ");

  fuse = boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS);
  if (fuse < 16) Serial.print("0");
  Serial.print(fuse, HEX);
  Serial.print(" ");

  fuse = boot_lock_fuse_bits_get(GET_LOCK_BITS);
  if (fuse < 16) Serial.print("0");
  Serial.println(fuse, HEX);

  flashSize = 1 << (sig[1] & 0x0F);

  for (int j = 0; j < NumSig; j++)
  {
    if (memcmp(sig, signatures[j].sig, 3) == 0)
    {
      foundSig = j;
      Serial.print(F("Processor = "));
      Serial.println(signatures[j].desc);

      Serial.print(F("Flash memory size = "));
      Serial.print(flashSize, DEC);
      Serial.println(F(" kB"));
      break;
    }
  }

  if (foundSig < 0) Serial.println(F("Unrecogized signature"));

  Serial.println();
}

void VCC()
{
#define Vref 1100
  int mvVcc;

  if ((foundSig >= 0) && (foundSig <= 8)) {

    ADMUX = (1 << REFS0) | 0x0E;
    ADCSRB = 0;
    ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADSC) | 0x05;
    delay(1);
    mvVcc = (1023L * Vref) / ADC;

    Serial.print(F("VCC = "));
    Serial.print(mvVcc);
    Serial.println(F(" mV"));
    Serial.println();
  }

  if ((foundSig >= 14) && (foundSig <= 18)) {

    ADMUX = (1 << REFS0) | 0x1E;
    ADCSRB = 0;
    ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADSC) | 0x05;
    delay(1);
    mvVcc = (1023L * Vref) / ADC;

    Serial.print(F("VCC = "));
    Serial.print(mvVcc);
    Serial.println(F(" mV"));
    Serial.println();
  }

  if ((foundSig >= 22) && (foundSig <= 23)) {

    ADMUX = (1 << REFS0) | 0x1E;
    ADCSRB = 0;
    ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADSC) | 0x05;
    delay(1);
    mvVcc = (1023L * Vref) / ADC;

    Serial.print(F("VCC = "));
    Serial.print(mvVcc);
    Serial.println(F(" mV"));
    Serial.println();
  }

  if ((foundSig >= 29) && (foundSig <= 31)) {

    ADMUX = 0x0E;
    ADCSRB = 0;
    ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADSC) | 0x05;
    delay(1);
    mvVcc = (1023L * Vref) / ADC;

    Serial.print(F("VCC = "));
    Serial.print(mvVcc);
    Serial.println(F(" mV"));
    Serial.println();
  }
}

void Temperature()
{
  float temperature;

  if ((foundSig >= 0) && (foundSig <= 8)) {

    ADMUX = (1 << REFS1) | (1 << REFS0) | (1 << MUX3);
    ADCSRA |= (1 << ADEN);
    delay(20);
    ADCSRA |= (1 << ADSC);
    delay(1);

    temperature = (ADC - 324.31) / 1.22;

    Serial.print(F("Internal Temperature = "));
    Serial.print(temperature, 1);
    Serial.println(F(" C"));
    Serial.println();
  }

  if ((foundSig >= 22) && (foundSig <= 23)) {

    ADMUX = (1 << REFS1) | (1 << REFS0) | 0x07;
    ADCSRB = 0x20;
    ADCSRA |= (1 << ADEN);
    delay(20);
    ADCSRA |= (1 << ADSC);
    delay(1);

    temperature = (ADC - 324.31) / 1.22;

    Serial.print(F("Internal Temperature = "));
    Serial.print(temperature, 1);
    Serial.println(F(" C"));
    Serial.println();
  }

  if ((foundSig >= 29) && (foundSig <= 31)) {

    ADMUX = (1 << REFS1) | 0x0F;
    ADCSRA |= (1 << ADEN);
    delay(20);
    ADCSRA |= (1 << ADSC);
    delay(1);

    temperature = (ADC - 324.31) / 1.22;

    Serial.print(F("Internal Temperature = "));
    Serial.print(temperature, 1);
    Serial.println(F(" C"));
    Serial.println();
  }
}

void TestPins()
{
#define FIRST_PIN 0
#define LAST_PIN 19

  Serial.println(F("Test of short circuit on GND or VCC:"));

  for (byte pin = FIRST_PIN; pin <= LAST_PIN; pin++)
  {
    if (pin < 10) Serial.print(F("Pin:  "));
    else Serial.print(F("Pin: "));
    Serial.print(pin);

    pinMode(pin, OUTPUT);
    digitalWrite(pin, 0);
    Serial.print(F("    Low: "));
    if (!digitalRead(pin)) Serial.print(F("Ok  "));
    else Serial.print(F("Fail"));

    digitalWrite(pin, 1);
    Serial.print(F("  High: "));
    if (digitalRead(pin)) Serial.print(F("Ok  "));
    else Serial.print(F("Fail"));

    pinMode(pin, INPUT_PULLUP);
    Serial.print(F("  Pull Up: "));
    if (digitalRead(pin)) Serial.print(F("Ok  "));
    else Serial.print(F("Fail"));

    Serial.println();
    pinMode(pin, INPUT);
  }

  Serial.println();
}