//---------------------------------------------------------------------------
#pragma hdrstop

#include "AVRFunctions.h"       // переменные для функций работы с чипами AVR
#include "PortFunctions.h"      // переменные для функций работы с портом
#include "LogFunctions.h"       // переменные для отправки сообщений в лог
#include "ChipFunctions.h"      // переменные для работы с чипом
#include "Main.h"               // переменные для работы с главной формой
//---------------------------------------------------------------------------
#pragma package(smart_init)
////////////////////////////////////////////////////////////
// В ЭТОМ МОДУЛЕ У НАС ФУНКЦИИ, ОДИНАКОВЫЕ ДЛЯ ВСЕХ ЧИПОВ //
////////////////////////////////////////////////////////////
//***************************************************************************
//*** Первичные функции (фазы) **********************************************
//***************************************************************************
//---------------------------------------------------------------------------
//--- Ожидание высокого уровня на RDY ---------------------------------------
//---------------------------------------------------------------------------
bool WaitRDYHigh()
{ WRITE_BUFFER wr_buf;          // буфер для отправки данных
  READ_BUFFER rd_buf;           // буфер для приёма данных

  for(unsigned char i=0;i<CYCLES;i++)   // CYCLES - глобальная переменная из AVRFunctions
  {     //--- запрашиваем состояние PINC
        wr_buf.Size=1;
        wr_buf.Data[0]=0x18;                                  // PINCRead
        if(!WriteTo(wr_buf)) { SendMsgToLog(6," PINCRead");   // ошибка отправки команды
                               CloseHandle(Port);
                               Port=INVALID_HANDLE_VALUE;
                               return 0;
                             }
        //--- принимаем байт, содержащий PINC
        rd_buf=ReadFrom(1);                                   // читаем
        if(!rd_buf.Size) { SendMsgToLog(7," (PINC)");         // ошибка приёма пакета
                           CloseHandle(Port);
                           Port=INVALID_HANDLE_VALUE;
                           return 0;
                         }
        if((rd_buf.Data[0]&0x01))     return true;            // если младший бит (RDY = 1) - дождались, выходим
        Sleep(PAUSE);                   // PAUSE - глобальная переменная из AVRFunctions
  }
  return false;         // так и не дождались высокого уровня на RDY
}
//---------------------------------------------------------------------------
//--- PHASE A (Load Command) ------------------------------------------------
//---------------------------------------------------------------------------
bool PhaseA(unsigned char Command)
{ WRITE_BUFFER wr_buf;          // буфер для отправки данных

  //--- set XA1 (PC4), XA0 (PD6) to 10; BS1 (PC1), BS2 (PD7) to 00 ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x11;                                    // PORTCSetup
  wr_buf.Data[1]=PC_WORK | (1 << XA1);  // 0001 1000      // поднимаем XA1, сбрасываем BS1
  wr_buf.Data[2]=0x15;                                    // PORTDSetup
  wr_buf.Data[3]=PD_WORK;               // 0001 0100      // сбрасываем XA0, сбрасываем BS2
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," set XA1, XA0 = 10, BS1, BS2 = 00");        // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- загружаем команду Command ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x0B;                                          // DDRBSetup
  wr_buf.Data[1]=0xFF;          // 1111 1111                    // все линии на выход
  wr_buf.Data[2]=0x0D;                                          // PORTBSetup
  wr_buf.Data[3]=Command;                                       // Command
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," set Command");        // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- выдаём импульс на линии XTAL1 ---
  wr_buf.Size=2;
  wr_buf.Data[0]=0x1D;                                          // Do_Extended_Command
  wr_buf.Data[1]=0x02;                                          // GiveXtalPosPulse
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," GiveXtalPosPulse");   // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  return true;
}
//---------------------------------------------------------------------------
//--- PHASE B (Load Address Low Byte) ---------------------------------------
//---------------------------------------------------------------------------
bool PhaseB(unsigned char Address)
{ WRITE_BUFFER wr_buf;          // буфер для отправки данных

  //--- set XA1 (PC4), XA0 (PD6) to 00; BS1 (PC1), BS2 (PD7) to 00 ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x11;                                                          // PORTCSetup
  wr_buf.Data[1]=PC_WORK; // 0000 1000                                          // сбрасываем XA1, сбрасываем BS1
  wr_buf.Data[2]=0x15;                                                          // PORTDSetup
  wr_buf.Data[3]=PD_WORK; // 0001 0100                                          // сбрасываем XA0, сбрасываем BS2
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," set XA1, XA0 = 00, BS1, BS2 = 00");   // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- загружаем младший байт адреса (i) ---
  wr_buf.Size=4;                                                        // линии порта B на выход
  wr_buf.Data[0]=0x0B;                                                  // DDRBSetup
  wr_buf.Data[1]=0xFF;    // 1111 1111                                  // все линии на выход
  wr_buf.Data[2]=0x0D;                                                  // PORTBSetup
  wr_buf.Data[3]=Address;                                               // младший байт адреса
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," Load Address Low Byte");      // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- выдаём импульс на линии XTAL1 ---
  wr_buf.Size=2;
  wr_buf.Data[0]=0x1D;                                  // Do_Extended_Command
  wr_buf.Data[1]=0x02;                                  // GiveXtalPosPulse
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x1D 0x02");  // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  return true;
}
//---------------------------------------------------------------------------
//--- PHASE C (Load Address High Byte) --------------------------------------
//---------------------------------------------------------------------------
bool PhaseC(unsigned char Address)
{ WRITE_BUFFER wr_buf;          // буфер для отправки данных

  //--- set XA1 (PC4), XA0 (PD6) to 00; BS1 (PC1), BS2 (PD7) to 10 ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x11;                                                          // PORTCSetup
  wr_buf.Data[1]=PC_WORK | (1 << BS1);  // 0000 1010                            // сбрасываем XA1, поднимаем BS1
  wr_buf.Data[2]=0x15;                                                          // PORTDSetup
  wr_buf.Data[3]=PD_WORK;               // 0001 0100                            // сбрасываем XA0, сбрасываем BS2
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," set XA1, XA0 = 00, BS1, BS2 = 10");   // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- загружаем старший байт адреса слова ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x0B;                                    // DDRBSetup (уже настроено как нужно)
  wr_buf.Data[1]=0xFF;  // 1111 1111                      // все линии на выход
  wr_buf.Data[2]=0x0D;                                    // PORTBSetup
  wr_buf.Data[3]=Address;                                 // старший байт адреса слова
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x0D a");       // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- выдаём импульс на линии XTAL1 ---
  wr_buf.Size=2;
  wr_buf.Data[0]=0x1D;                                  // Do_Extended_Command
  wr_buf.Data[1]=0x02;                                  // GiveXtalPosPulse
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x1D 0x02");  // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  return true;
}
//---------------------------------------------------------------------------
//--- PHASE D (Load Data Low Byte) ------------------------------------------
//---------------------------------------------------------------------------
bool PhaseD(unsigned char Data)
{ WRITE_BUFFER wr_buf;          // буфер для отправки данных

  //--- set XA1 (PC4), XA0 (PD6) to 01; BS1 (PC1), BS2 (PD7) to 00 ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x11;                                                          // PORTCSetup
  wr_buf.Data[1]=PC_WORK;               // 0000 1000                            // сбрасываем XA1, сбрасываем BS1
  wr_buf.Data[2]=0x15;                                                          // PORTDSetup
  wr_buf.Data[3]=PD_WORK | (1<<XA0);    // 0101 0100                            // поднимаем XA0, сбрасываем BS2
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," set XA1, XA0 = 01, BS1, BS2 = 00");   // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- загружаем младший байт слова данных ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x0B;                                                  // DDRBSetup
  wr_buf.Data[1]=0xFF;  // 1111 1111                                    // все линии на выход
  wr_buf.Data[2]=0x0D;                                                  // PORTBSetup
  wr_buf.Data[3]=Data;                                                  // младший байт слова данных
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," Load Data Low Byte");         // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- выдаём импульс на линии XTAL1 ---
  wr_buf.Size=2;
  wr_buf.Data[0]=0x1D;                                  // Do_Extended_Command
  wr_buf.Data[1]=0x02;                                  // GiveXtalPosPulse
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x1D 0x02");  // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  return true;
}
//---------------------------------------------------------------------------
//--- PHASE E (Load Data High Byte) -----------------------------------------
//---------------------------------------------------------------------------
bool PhaseE(unsigned char Data)
{ WRITE_BUFFER wr_buf;          // буфер для отправки данных

  //--- set XA1 (PC4), XA0 (PD6) to 01; BS1 (PC1), BS2 (PD7) to 10 ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x11;                                                          // PORTCSetup
  wr_buf.Data[1]=PC_WORK | (1<<BS1);    // 0000 1010                            // сбрасываем XA1, поднимаем BS1
  wr_buf.Data[2]=0x15;                                                          // PORTDSetup
  wr_buf.Data[3]=PD_WORK | (1<<XA0);    // 0101 0100                            // поднимаем XA0
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," set XA1, XA0 = 01, BS1, BS2 = 10");   // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- загружаем старший байт слова данных ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x0B;                                                  // DDRBSetup (уже настроено как надо)
  wr_buf.Data[1]=0xFF;          // 1111 1111                            // все линии на выход
  wr_buf.Data[2]=0x0D;                                                  // PORTBSetup
  wr_buf.Data[3]=Data;                                                  // старший байт слова данных
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," Load Data High Byte");        // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- выдаём импульс на линии XTAL1 ---
  wr_buf.Size=2;
  wr_buf.Data[0]=0x1D;                                  // Do_Extended_Command
  wr_buf.Data[1]=0x02;                                  // GiveXtalPosPulse
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x1D 0x02");  // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  return true;
}
//---------------------------------------------------------------------------
//--- PHASE F (Load Address Extended Byte) ----------------------------------
//---------------------------------------------------------------------------
bool PhaseF(unsigned char Address)
{ WRITE_BUFFER wr_buf;          // буфер для отправки данных

  //--- set XA1 (PC4), XA0 (PD6) to 00; BS1 (PC1), BS2 (PD7) to 01 ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x11;                                                          // PORTCSetup
  wr_buf.Data[1]=PC_WORK;               // 0000 1000                            // сбрасываем XA1, сбрасываем BS1
  wr_buf.Data[2]=0x15;                                                          // PORTDSetup
  wr_buf.Data[3]=PD_WORK | (1 << BS2);  // 1001 0100                            // сбрасываем XA0, поднимаем BS2
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," set XA1, XA0 = 00, BS1, BS2 = 01");   // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- загружаем расширенный байт адреса слова ---
  wr_buf.Size=4;
  wr_buf.Data[0]=0x0B;                                    // DDRBSetup (уже настроено как нужно)
  wr_buf.Data[1]=0xFF;  // 1111 1111                      // все линии на выход
  wr_buf.Data[2]=0x0D;                                    // PORTBSetup
  wr_buf.Data[3]=Address;                                 // расширенный байт адреса слова
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x0D a");       // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  //--- выдаём импульс на линии XTAL1 ---
  wr_buf.Size=2;
  wr_buf.Data[0]=0x1D;                                  // Do_Extended_Command
  wr_buf.Data[1]=0x02;                                  // GiveXtalPosPulse
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x1D 0x02");  // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return 0;
                       }
  return true;
}
//***************************************************************************
//*** Вторичные функции (составленные из фаз) *******************************
//***************************************************************************
//---------------------------------------------------------------------------
//--- ПЕРЕВОД ЧИПА В РЕЖИМ ПРОГРАММИРОВАНИЯ ---------------------------------
//---------------------------------------------------------------------------
bool EnterProgrammingMode()
{ WRITE_BUFFER wr_buf;          // буфер для отправки данных
  READ_BUFFER rd_buf;           // буфер для приёма данных

  SendMsgToLog(18," enter Programming Mode");    // сообщаем о попытке перейти в режим параллельного программирования
  //--- устанавливаем направления работы линий и начальные уровни на них ---
  //--- линию RDY настраиваем на вход и отключаем подтягивающий резистор
  //--- линиии управления питанием пока тоже оставляем входами (их переключит команда, выполняющая алгоритм перехода)
  //--- все остальные линии настраиваем как выходы

  //--- настраиваем порт B
  wr_buf.Size=4;
  wr_buf.Data[0]=0x0B;                                    // DDRBSetup
  wr_buf.Data[1]=0x0;                                     // все линии на вход
  wr_buf.Data[2]=0x0D;                                    // PORTBSetup
  wr_buf.Data[3]=0x0;                                     // на всех линиях нули
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x0B 0x0 0x0D 0x0");        // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return false;
                       }
  //--- настраиваем порт C
  wr_buf.Size=4;
  wr_buf.Data[0]=0x0F;                                    // DDRCSetup
  wr_buf.Data[1]=0x32;  // 0011 0010                      // все используемые линии на выход, кроме RDY и управления питанием
  wr_buf.Data[2]=0x11;                                    // PORTCSetup
  wr_buf.Data[3]=0x0;                                     // на всех линиях нули (подтягивающие резисторы выключены)
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x0F 0x32 0x11 0x0");         // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return false;
                       }
  //--- настраиваем порт D (нули на всех линиях группы Prog_enable)
  //--- о сигнале PG можно не беспокоиться, он защищён маской и обычными командами не меняется
  wr_buf.Size=4;
  wr_buf.Data[0]=0x13;                                    // DDRDSetup
  wr_buf.Data[1]=0xFC;  // 1111 1100                      // все используемые линии на выход
  wr_buf.Data[2]=0x15;                                    // PORTDSetup
  wr_buf.Data[3]=0x0;                                     // на всех линиях нули (перед подачей питания нули нужны даже на WR, OE)
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x13 0xFC 0x15 0x0");         // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return false;
                       }
  Sleep(1);
  Sleep(Delays);//////////
  //--- выполняем алгоритм перехода в режим высоковольтного параллельного программирования для AVR ---
  wr_buf.Size=2;
  wr_buf.Data[0]=0x1D;                                    // выполнить расширенную команду
  wr_buf.Data[1]=0x0;                                     // команда Enter_HVP_ProgMode_AVR
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x1D 0x0");     // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return false;
                       }
  Sleep(1);
  Sleep(Delays);//////////
  //--- донастраиваем порт D - выставляем единицы на линиях WR, OE (выше мы уже настроили их не выход)
  wr_buf.Size=2;
  wr_buf.Data[0]=0x15;                                    // PORTDSetup
  wr_buf.Data[1]=PD_WORK;       // 0001 0100              // переключаем WR, OE в 1
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x15 0x14(PD_WORK)"); // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return false;
                       }
  SendMsgToLog(19," with success.");                    // сообщаем, что команда выполнена успешно
  return true;
}
//---------------------------------------------------------------------------
//--- ВЫХОД ИЗ РЕЖИМА ПРОГРАММИРОВАНИЯ --------------------------------------
//---------------------------------------------------------------------------
bool ExitProgrammingMode()
{ WRITE_BUFFER wr_buf;          // буфер для отправки данных
  READ_BUFFER rd_buf;           // буфер для приёма данных

  SendMsgToLog(18," exit Programming Mode");    // сообщаем о попытке выйти из режима параллельного программирования
  //--- для выхода достаточно просто отключить +5 и +12 Вольт ---
  //--- +12 Вольт отключается переключением выхода в ноль, а +5 Вольт - переключением выхода на вход
  wr_buf.Size=4;
  wr_buf.Data[0]=0x11;                                    // PORTCSetup
  wr_buf.Data[1]=0x0;                                     // на всех линиях нули (выключаем +12В, отключаем подтягивающие резисторы)
  wr_buf.Data[2]=0x0F;                                    // DDRCSetup
  wr_buf.Data[3]=0x32;  // 0011 0010                      // переключаем управление питанием на вход (остальные рабочие линии оставляем на выход, кроме RDY)
  if(!WriteTo(wr_buf)) { SendMsgToLog(6," 0x11 0x0 0x0F 0x32");         // ошибка отправки команды
                         CloseHandle(Port);
                         Port=INVALID_HANDLE_VALUE;
                         return false;
                       }
  Sleep(1);
  Sleep(Delays);//////////
  SendMsgToLog(19," with success.");                    // сообщаем, что команда выполнена успешно
  return true;
}
//---------------------------------------------------------------------------
//--- ЧТЕНИЕ БАЙТОВ СИГНАТУРЫ -----------------------------------------------
//---------------------------------------------------------------------------
unsigned int ReadSignatureBytes()
{ WRITE_BUFFER wr_buf;                  // буфер для отправки данных
  READ_BUFFER rd_buf;                   // буфер для приёма данных
  unsigned int ChipID, Temp;            // ответ функции

  SendMsgToLog(18," read signature bytes");     // сообщаем о попытке прочитать байты сигнатуры
  ChipID = 0;                                   // обнуляем ответ

  if(!PhaseA(0x08))        return 0;            // Load Command 0x08 - Read Signaure Byte
  //--- вычитываем последовательно 3 байта сигнатуры ---
  for(unsigned char i=0;i<3;i++)
  {     if(!PhaseB(i))  return 0;               // Load Address Low Byte
        //--- READ DATA FROM THE CHIP ---
        //--- перенастраиваем линии порта B на вход, устанавливаем BS=0, включаем OE, читаем состояние порта B, выключаем OE
        wr_buf.Size=7;
        wr_buf.Data[0]=0x0B;                                    // DDRBSetup
        wr_buf.Data[1]=0x0;                                     // все линии на вход
        // BS is already 0 after PhaseB
        wr_buf.Data[2]=0x15;                                    // PORTDSetup
        wr_buf.Data[3]=PD_WORK & ~(1<<OE);      // 0001 0000    // сбрасываем OE
        wr_buf.Data[4]=0x17;                                    // PINBRead
        wr_buf.Data[5]=0x15;                                    // PORTDSetup
        wr_buf.Data[6]=PD_WORK;                 // 0001 0100    // поднимаем OE
        if(!WriteTo(wr_buf)) { SendMsgToLog(6," Read Signature Bytes");  // ошибка отправки команды
                               CloseHandle(Port);
                               Port=INVALID_HANDLE_VALUE;
                               return 0;
                             }
        // принимаем байт, содержащий PINB
        rd_buf=ReadFrom(1);                              // читаем
        if(!rd_buf.Size) { SendMsgToLog(7," (PINB)");    // ошибка приёма пакета
                           CloseHandle(Port);
                           Port=INVALID_HANDLE_VALUE;
                           return 0;
                         }
        //--- вычисляем ID чипа
        Temp = rd_buf.Data[0];
        ChipID += (Temp << (8*i));
  }
  SendMsgToLog(19,"");            // чтение байтов сигнатуры завершено
  return ChipID;
}
//---------------------------------------------------------------------------
