// наиболее часто используемые команды для VAG

#define TX_H    PORTA |= (1<<6)
#define TX_L    PORTA &= (1<<6)^255
#define TX_IN   (PORTA & (1<<5))

proc CopyBlock()
{
    var a,i;

    a = Inbuffer[0]+1;
    for(i = 0;i < a;i++)
    {
        Combuffer[i] = Inbuffer[i];
    }    
}

// процедура задержки
// используется при начальном коннекте
// Timer увеличивается на 1 каждую миллисекунду, т.е. частота счета - 1000Гц
proc wait(d)
{
    Timer = 0;
    while(Timer < d) ;
}

// чтение байта из UART с тайм-аутом
proc ReadUARTTimeOut(t)
{
    var data;
    Timer= 0;
    for(;;)
    {
        data = UART_DATA;
        if(data != 0x8000) break;
        if(Timer > t) break;
    }
    return(data);
}

proc TXByteAndECO(b)
{
    UART_DATA = b;
    ReadUARTTimeOut(20);
}

// процедура ожидания и проверки синхроответа в начале коннекта
// возвращает код ошибки
// 254 - нет ответа вообще
// 253 - что-то принято, но не синхро
// 252 - синхро принято, но код панели не VAG
// 0 - получена правильный ответ

proc CheckSync()
{
    var data;
    Timer = 0;      // это для тайм-аута
    for(;;)
    {
        data = UART_DATA;
        if(data != 0x8000)  break;          // какой-то ответ есть
        if(Timer > 1000)    return(254);    // вообще нет ответа
    }
    if(data = 0x55)
    {
        // принят правильный первый байт 
        // ждем еще двух байт - 0x01 и 0x8A
        data = ReadUARTTimeOut(200) << 8;
        data |= ReadUARTTimeOut(200);
        PanelCode = data;
        if(data = 0x018A) return(0);
        else return(252);           
    }
    else return(253);       
}

// процедура приема блока данных от панели
proc GetBlock()
{
    var i,data,size;
    size = ReadUARTTimeOut(250);        // первый байт пакета - размер
    if(size = 0x8000) 
    {
        // тайм-аут, ничего не принято, устанавливаем нулевой размер блока
        Inbuffer[0] = 0;
        return;
    }
    TXByteAndECO(size ^ 255);
    if(size > 48) size = 48;
    InBuffer[0] = size;
    if(size = 0) return;
    for(i=1;i < size; i++)
    {
        data = ReadUARTTimeOut(20);     // очередной байт пакета
        InBuffer[i] = data;             // в буфер
        TXByteAndECO(data ^ 255);       // инверсный ответ
    }
    // последний байт пакета - без инверсного ответа
    InBuffer[i] = ReadUARTTimeOut(20);
}

// пустая команда - для поддержания коннекта
// если панель отвечает, то возвращается 0, иначе 255
proc EmptyCmd()
{
    var result,data;
    result = 0;
    BlockNumber += 2;   
    TXByteAndECO(0x03);
    data = ReadUARTTimeOut(50);                     // ответ
    if(data != (0x03 ^ 255))    result = 255;
    TXByteAndECO(BlockNumber);
    data = ReadUARTTimeOut(50);                     // ответ
    if(data != (BlockNumber ^ 255)) result = 255;
    TXByteAndECO(0x09);
    data = ReadUARTTimeOut(50);                     // ответ
    if(data != (0x09 ^ 255))    result = 255;
    TXByteAndECO(0x03);
    return(result);
}


// передача команды в панель
// данные для передачи должны находится в массиве InBuffer
// нулевой байт массива - число байт в команде (включая байты номера блока и последний байт) 
// далее стразу байты команды (номер блока не нужен)
// номер блока и последний байт 0х03 вставляет сама процедура

proc CMD()
{
    var size,i;

    BlockNumber += 2;
    size = InBuffer[0];                

    TXByteAndECO(Size);                 // первый байт команды - размер
    ReadUARTTimeOut(50);                // ответ
    TXByteAndECO(BlockNumber);          // второй байт команды - номер блока
    ReadUARTTimeOut(50);                // ответ

    size -= 1;                          // вычитаем байт размера
    for(i=1;i < size;i++)
    {
        // остальные байты команды
        TXByteAndECO(Inbuffer[i]);
        ReadUARTTimeOut(50);            // инверсный ответ
    }   
    TXByteAndECO(0x03);                 // конец команды
}


// процедура начального коннекта
// устанавливает начальный коннект и считывает строки идентификации
// Входной параметр 
//   mode = 0 - строки не передаются в комьютер (используется при входе в сервисный режим)
//   mode != 0 - строки передаются в компьютер
//
// возвращаемое значение
//      255 - не подключен адаптер
//      254 - не принята синхропосылка в ответ
//      253 - что-то принято, но не синхро (нет байта 0x55)
//      252 - синхро принято, но код панели не 0x018A  ( код панели во внешней переменной Ack)
//        0 - все OK

proc InitConnect()
{
    var i,data,BackData;
    UART_CR = 0;        // отключаем аппаратный UART
    IDLE_ENABLE=0;      // запрещаем фоновую работу
    Connected = 0;
    Safelogin = 0;
    // вручную на скорости 5 бод передаем байт 0x17 - панель приборов
    // байт передается, начиная с младшего бита
    // старт-бит = 0
    // передается 7 бит, затем бит нечетного паритета (для числа 0x17 будет 1 (в числе 0x17 четыре единицы))
    // бит паритета добавим восьмым в байт, т.е будем передавать 8 бит = 0x97
    // стоп-бит = 1 
    // при скорости 5 бод длина одного бита = 200 миллисекунд

    TX_H; wait(1700);           // пауза перед коннектом
    
    TX_L; wait(200);            //TX_H;      // старт-бит

    data = 0x97;
    BackData = 0;               // для контроля наличия адаптера    
    for(i = 0; i < 8; i++)
    {
        if((data & 0x01) = 0)   TX_L;
        else                    TX_H; 
        wait(200);
        
        BackData = BackData >> 1;
        if(TX_IN != 0) BackData |= 0x80;
        data = data >> 1;
    } 
    TX_H; 
    
    if(BackData != 0x97)    
    {
        // адаптер неисправен
        State = 255;
        return;        
    }
    // включаем аппаратный UART
    UART_CR = UART_ON | UART_NORMAL | PAR_NO | CHL_8BIT | STOP_BIT1 | 10470;
    BackData = UART_DATA;
    BackData = CheckSync();     // ждем ответа от панели
    if(BackData != 0)
    {
        // коннект неудачный
        State = BackData;
        return;
    }
    State = 1;                          // инициализация OK

    BlockNumber = 0;                    // номер блока в начальное значение
    UART_DATA = 0x75;                   // принят правильный ответ, отвечаем байтом 0x75
    ReadUARTTimeOut(50);                // принимаем собственное эхо
    GetBlock();                         // теперь панель выдает VAGNumber
    CopyBlock();
    State = 2;

    EmptyCMD();                         // пустая команда
    GetBlock();                         // теперь панель выдает общее название компонента
    CopyBlock();
    State = 3;

    EmptyCMD();                         // пустая команда
    GetBlock();                         // теперь панель выдает код программы
    CopyBlock();
    State = 4;

    EmptyCMD();                         // пустая команда
    GetBlock();                         // теперь панель выдает дилерский номер
    CopyBlock();
    State = 5;

    ReadUARTTimeOut(50);
    // коннект состоялся, разрешаем обработчик Idle
    Timer = 0;
    IDLE_ENABLE = 1;
    Connected = 1;
}    

proc VDOFreeze()
{
    // 08 xx 1B 80 01 02 03 04 xx - freeze 

    InBuffer[0] = 0x08; 
    InBuffer[1] = 0x1B; 
    InBuffer[2] = 0x80; 
    InBuffer[3] = 0x01; 
    InBuffer[4] = 0x02; 
    InBuffer[5] = 0x03; 
    InBuffer[6] = 0x04; 
    CMD();
    GetBlock();             // ответ от панели  
    return(InBuffer[2]);
}

proc VDOID()
{
    // 04 xx 1B 84 xx - get VDO ID
    Inbuffer[0] = 0x04;
    Inbuffer[1] = 0x1B;
    Inbuffer[2] = 0x84;
    CMD();
    GetBlock();             // ответ от панели
    return(InBuffer[2]);
}

proc SoftReset()
{
    // 04 xx 1B 82 xx - програмный сброс 

    InBuffer[0] = 0x04; 
    InBuffer[1] = 0x1B; 
    InBuffer[2] = 0x82; 
    CMD();
    GetBlock();             // ответ от панели
}
