/*****************************************************
This program was produced by the
CodeWizardAVR V2.03.4 Standard
Automatic Program Generator
© Copyright 1998-2008 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com

Project : 
Version : 
Date    : 12.11.2008
Author  : 
Company : 
Comments: 


Chip type           : ATmega8535
Program type        : Application
Clock frequency     : 11,059200 MHz
Memory model        : Small
External RAM size   : 0
Data Stack size     : 128
*****************************************************/

#include <mega8535.h>
#include <stdio.h>
#include <string.h>
#include <delay.h>

#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7

#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)

// Текст ответа в порт USART
#define TEXT_UNKNOWN_COMMAND "UNKNOWN COMMAND"
#define TEXT_ERROR "ERROR"
#define TEXT_OK "OK"

// USART Receiver buffer
#define RX_BUFFER_SIZE 100 // Размер массива приема
char rx_buffer[RX_BUFFER_SIZE]; // Массив приема

#if RX_BUFFER_SIZE<256
unsigned char rx_counter; // Счетчик массива
#else
unsigned int rx_counter; // Счетчик массива
#endif

// This flag is set on USART Receiver buffer overflow
bit rx_buffer_overflow; // Индекс перегрузки массива. В Коде не используеться.

eeprom struct config_structure {  // Структура настроек.
             unsigned char type;            // Тип порта. 0 - выход, 1 - вход. 
             unsigned char def;         // Если выход, какой логический уровень начальный.
             } eeprom_config[8]={{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}};




void SendCharUSART(unsigned char c); // Функция отправки по UART байта(буква)
void SendCharsUSART(unsigned char* c); // Функция отправки по UART массива байт(слово)
void SendTextUSART(flash unsigned char* c); // Функция отправки по UART константного массива байт(слова)
void ReadComand(); // Читает из массива приема команду
void DeleteBufferRX(); // Очищает массив. Не нашел в компиляторе фукнцию delete[]
static void avr_init(); // Функция начальной инициализации контроллера
void reset(); // Програмный резет

// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void) // Прерывание на прием данных
{
    char status,data;
    while ( !(UCSRA & (1<<RXC)) ); // Ждем, чтоб прошлый прием удался
    status=UCSRA; // читаем статус
    data=UDR; // заносим байт в временную переменную
    if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) // Что принятый байт был успешным
    {
        rx_buffer[rx_counter]=data; // Добовляем в буфер новый байт
        if (++rx_counter == RX_BUFFER_SIZE) // Увеличиваем порядковый индекс буферного массива и сравниваем с максимумом.
        {   // Если максимум достигнут, чтоб не записывать за пределы масива, обнуляем индекс и маркируем перегрузку. 
            rx_counter=0; // обнуляем индекс
            rx_buffer_overflow=1; // маркируем перегрузку. Нужно повешаться сразу.
        };
        if(data==0x0D) // Если конец строки команды(команда закончилась)
        {
            ReadComand(); // запускаем чтение команды и выполнение ее.
        }  
    };
}

//Отправка одного символа в USART
void SendCharUSART(unsigned char c)
{
    #asm("cli") // Отключаем возможные прерывания
    while ( !( UCSRA & (1<<UDRE)) ); // Ждем, чтоб прошлая отправка удалась
    UDR=c; // Отправляем символ
    #asm("sei") // Включаем возможные прерывани
}

//Отправка слова в USART
void SendCharsUSART(unsigned char* c)
{
    unsigned char i;
    #asm("cli") // Отключаем возможные прерывания
    for(i=0; i<strlen(c); i++) // Запускаем цикл отправки посимвольно
    {
        while ( !( UCSRA & (1<<UDRE)) ); // Ждем, чтоб прошлая отправка удалась
        UDR=c[i]; // Отправляем символ
    }
    #asm("sei") // Включаем возможные прерывания
}

// Отправка текста в порт USART
void SendTextUSART(flash unsigned char* c)
{
    unsigned char i;
    #asm("cli") // Отключаем возможные прерывания
    for(i=0; i<strlenf(c); i++) // Запускаем цикл отправки посимвольно
    {
        while ( !( UCSRA & (1<<UDRE)) ); // Ждем, чтоб прошлая отправка удалась
        UDR=c[i]; // Отправляем символ
    }
    while ( !( UCSRA & (1<<UDRE)) ); // Ждем, чтоб прошлая отправка удалась
    UDR=0x0D;
    #asm("sei") // Включаем возможные прерывания
}

// Читаем команы с буфера
void ReadComand()
{
    if(rx_buffer[0]=='A' && rx_buffer[1]=='T' && rx_buffer[2]=='+') // если команда начинаеться на AT+
    {
        if(rx_buffer[3]=='E') // если команда AT+E
        {
            if(rx_buffer[4]=='I' && rx_buffer[5]=='P' && rx_buffer[6]=='I' && rx_buffer[7]=='N' && rx_buffer[9]==0x0D) // если команда AT+EIPINx, где х - номер пина.
            {
                unsigned char num = 0;
                if(rx_buffer[8]>0x31) // Если ASCII код пина больше единицы(1)
                {
                    num = (rx_buffer[8]-0x31); // преобразуем ASCII число в десятичное(int) и отнимаем один. Сделано, чтоб команде указывать номер порта с единицы, а не с нуля.
                }
                if(num<=7) // если десятичное число меньше восьми
                {
                    eeprom_config[num].type = 0; // заносим в EEPROM настройки по данному пину
                    eeprom_config[num].def = 0; // заносим в EEPROM настройки по данному пину
                    SendTextUSART(TEXT_OK); // отвечаем в порт ОК
                    delay_ms(10); // ждем 10мсек на запись в EEPROM, по даташину требуеться 6,4мсек. взял с запасом.
                    reset(); // Делаем програмный резет
                }
                else
                {
                    SendTextUSART(TEXT_ERROR); // Иначе посылаем в порт ошибку, чтоб юзер протер глаза.
                } 
            }
            else if(rx_buffer[4]=='O' && rx_buffer[5]=='P' && rx_buffer[6]=='I' && rx_buffer[7]=='N' && rx_buffer[9]=='=' && rx_buffer[11]==0x0D) // если команда AT+EOPINx=y, где х - номер пина, y - состояние
            {
                unsigned char num1 = 0;
                unsigned char num2 = 0;
                if(rx_buffer[8]>0x31) // Если ASCII код пина больше единицы(1)
                {
                    num1 = (rx_buffer[8]-0x31); // преобразуем ASCII число в десятичное(int) и отнимаем один. Сделано, чтоб команде указывать номер порта с единицы, а не с нуля.
                }
                if(rx_buffer[10]!=0x30) // если ASCII число не равно нулю
                {
                    num2 = 1; // в переменную заносим десятичную единицу, иначе по умолчанию 1. Например юзер укажет состояние 4, это будет всеравно единица.
                }
                if(num1<=7)// если десятичное число меньше восьми
                {
                    eeprom_config[num1].type = 1; // заносим в EEPROM настройки по данному пину
                    eeprom_config[num1].def = num2; // заносим в EEPROM настройки по данному пину
                    SendTextUSART(TEXT_OK); // отвечаем в порт ОК
                    delay_ms(10); // ждем 10мсек на запись в EEPROM, по даташину требуеться 6,4мсек. взял с запасом.
                    reset(); // Делаем програмный резет
                }
                else
                {
                    SendTextUSART(TEXT_ERROR);SendTextUSART(TEXT_ERROR); // Иначе посылаем в порт ошибку, чтоб юзер протер глаза.
                } 
            }
            else if(rx_buffer[4]=='R' && rx_buffer[5]=='E' && rx_buffer[6]=='A' && rx_buffer[7]=='D' && rx_buffer[8]==0x0D) // если команда AT+EREAD, где х - номер пина, y - состояние
            {
                unsigned char i = 0;
                for(i=0; i<8; i++) // цмкл перебора настроек EEPROM
                {
                    SendCharUSART((char)(eeprom_config[i].type+0x30)); // выводим в UART настройки пина. 0 - вход, 1 - выход
                    SendCharUSART(':'); // выводим в UART разделитель
                    SendCharUSART((char)(eeprom_config[i].def+0x30)); // выводим в UART состояние по умолчанию пина
                    SendCharUSART(0x0D); // конец строки, переход на новую строку
                }
            }
            else if(rx_buffer[4]=='R' && rx_buffer[5]=='E' && rx_buffer[6]=='A' && rx_buffer[7]=='D' && rx_buffer[8]=='H' && rx_buffer[9]==0x0D) // если команда AT+EREADH, где х - номер пина, y - состояние
            {
                unsigned char i = 0;
                for(i=0; i<8; i++) // Тоже самое, что в предыдущей команде, только цифры не портируються в ASCII.
                {
                    SendCharUSART(eeprom_config[i].type);
                    SendCharUSART(':');
                    SendCharUSART(eeprom_config[i].def);
                    SendCharUSART(0x0D);
                }
            }
            else
            {
                SendTextUSART(TEXT_UNKNOWN_COMMAND); // Иначе посылаем в порт ошибку, чтоб юзер протер глаза.
            }
        }
        else if(rx_buffer[3]=='W' && rx_buffer[4]=='P' && rx_buffer[5]=='I' && rx_buffer[6]=='N' && rx_buffer[8]=='=' && rx_buffer[10]==0x0D) // если команда AT+WPINx=y, где x - номер порта, y - требуемое состояние
        {
            unsigned char num = 0;
            if(rx_buffer[7]>0x31)
            {
                num = (rx_buffer[7]-0x31);
            }
            if(num<8 && eeprom_config[num].type==1) // Проверяем, что данный порт настроен у нас как выход
            {
                if(rx_buffer[9] == '0')
                {
                    PORTA &= ~(1<<num); // если y равен нулю, подаем логическую единицу на порт
                }
                else
                {
                    PORTA |= 1<<num; // иначе подаем ноль
                }
                SendTextUSART(TEXT_OK);
            }
            else
            {
                SendTextUSART(TEXT_ERROR);
            }
        }
        else if(rx_buffer[3]=='R' && rx_buffer[4]=='P' && rx_buffer[5]=='I' && rx_buffer[6]=='N' && rx_buffer[8]==0x0D) // если команда AT+RPINx, где x - номер порта 
        {
            unsigned char num = 0;
            if(rx_buffer[7]>0x31)
            {
                num = (rx_buffer[7]-0x31);
            }
            if(num<8)
            {
                unsigned char res_p = 0;
                res_p = ((PINA >> num)&0b00000001)+0x30; // парсим битовую маску, чтоб узнать состояние входного/выходного порта
                SendCharUSART(res_p); // выводим результат. либо 1 либо 0.
                SendCharUSART(0x0D);
            }
            else
            {
                SendTextUSART(TEXT_ERROR);
            }
        }
        else
        {
            SendTextUSART(TEXT_UNKNOWN_COMMAND);
        }
    }
    else
    {
        SendTextUSART(TEXT_UNKNOWN_COMMAND);
    }
    DeleteBufferRX();
} 

// Очистка Буфера приема(не нашел функцию стандартную)
void DeleteBufferRX()
{
    #if RX_BUFFER_SIZE<256
    unsigned char i; // Счетчик массива
    #else
    unsigned int i; // Счетчик массива
    #endif
    #asm("cli") // Отключаем возможные прерывания
    for(i=0; i<RX_BUFFER_SIZE; i++) // цикл на всю длину буфера
    {
        rx_buffer[i] = 0; // очищаем ячейку буфера
    }
    rx_counter = 0; // обнуляем счетчик кол-ва символов в буфере
    #asm("sei") // Включаем возможные прерывания
}

void main(void)
{
    avr_init();
    while (1)
    {
        delay_ms(15000); // Вообще не знал, что сюда запихнуть. первое что пришло в голову =)
    };
}

static void avr_init()
{
    // Input/Output Ports initialization
    // Port A initialization
    unsigned char* run_p = "Run Program";
    unsigned char i = 0;
    unsigned char dd = 0;
    unsigned char po = 0;
    for(i=0; i<8; i++)
    {
        if(eeprom_config[i].type == 1) // Если пин выход
        {
            dd |= 1<<i; // составляем битовую маску для DDRA
        }
        if(eeprom_config[i].def == 1) // Если пин выход логический уровень стартовый на пине. Для входа он всегда 0.
        {
            po |= 1<<i; //  составляем битовую маску для PORTA
        }
    }
    PORTA=po;
    DDRA=dd;
    
    // Port B initialization
    // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
    // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
    PORTB=0x00;
    DDRB=0x00;
    
    // Port C initialization
    // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
    // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
    PORTC=0x00;
    DDRC=0x00;
    
    // Port D initialization
    // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
    // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
    PORTD=0x00;
    DDRD=0x00;
    
    // Timer/Counter 0 initialization
    // Clock source: System Clock
    // Clock value: Timer 0 Stopped
    // Mode: Normal top=FFh
    // OC0 output: Disconnected
    TCCR0=0x00;
    TCNT0=0x00;
    OCR0=0x00;

    // Timer/Counter 1 initialization
    // Clock source: System Clock
    // Clock value: Timer 1 Stopped
    // Mode: Normal top=FFFFh
    // OC1A output: Discon.
    // OC1B output: Discon.
    // Noise Canceler: Off
    // Input Capture on Falling Edge
    // Timer 1 Overflow Interrupt: Off
    // Input Capture Interrupt: Off
    // Compare A Match Interrupt: Off
    // Compare B Match Interrupt: Off
    TCCR1A=0x00;
    TCCR1B=0x00;
    TCNT1H=0x00;
    TCNT1L=0x00;
    ICR1H=0x00;
    ICR1L=0x00;
    OCR1AH=0x00;
    OCR1AL=0x00;
    OCR1BH=0x00;
    OCR1BL=0x00;

    // Timer/Counter 2 initialization
    // Clock source: System Clock
    // Clock value: Timer 2 Stopped
    // Mode: Normal top=FFh
    // OC2 output: Disconnected
    ASSR=0x00;
    TCCR2=0x00;
    TCNT2=0x00;
    OCR2=0x00;

    // External Interrupt(s) initialization
    // INT0: Off
    // INT1: Off
    // INT2: Off
    MCUCR=0x00;
    MCUCSR=0x00;

    // Timer(s)/Counter(s) Interrupt(s) initialization
    TIMSK=0x00;

    // USART initialization
    // Communication Parameters: 8 Data, 1 Stop, No Parity
    // USART Receiver: On
    // USART Transmitter: On
    // USART Mode: Asynchronous
    // USART Baud Rate: 115200
    UCSRA=0x00;
    UCSRB=0x98;
    UCSRC=0x86;
    UBRRH=0x00;
    UBRRL=0x05;

    // Analog Comparator initialization
    // Analog Comparator: Off
    // Analog Comparator Input Capture by Timer/Counter 1: Off
    ACSR=0x80;
    SFIOR=0x00;

    #asm("sei")
    
    SendCharsUSART(run_p); // отправляем в порт извещение, что МК нормально запустился
    SendCharUSART(0x0D);
}

// Програмный ресет
void reset()
{
    #asm("cli")
    WDTCR = (1<<4)|(1<<3);
    WDTCR = (1<<3);
    while(1){};
}