/* PROP CONTROL
   ATmega8535 12 Мгц
   Andrey Vorobyov
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h> 
#include "usbdrv.h"
#include <avr/eeprom.h>

////////////////////////////////////////////////////////////
// RN

/*RN_WRITE  
- загрузить в RAM нормальный канал или реверсивный
    0b = 6
    1b = от 0 до 3 номер канала
    2b = 0 - нормальный 1-реверсивный
*/                      
#define RN_WRITE 6

/*RN_READ 
- прочитать нормальный или ревресивный (помещается в 0 байт, 0 - нормальный, 1-реверивный)
 0b = 7
 1b = от 0 до 3 номер канала
 2b = 0 (не используется)
*/
#define RN_READ  7

#define RN_INDEX 0
///////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
//LEFT_SHIFT

/*LEFT_SHIFT_WRITE 
- загрузить в RAM смещение влево
  0b = 8
  1b = от 0 до 3 номер канала
  2b = значение смещения
*/
#define LEFT_SHIFT_WRITE  8

/*LEFT_SHIFT_READ 
- прочитать смещение влево (помещается в 0 байт)
  0b = 9
  1b = от 0 до 3 номер канала
  2b = 0 (не используется)
*/
#define LEFT_SHIFT_READ  9

#define LSH_INDEX 1
////////////////////////////////////////////////////////

////////////////////////////////////////////////////////
//RIGHT_SHIFT

/*RIGHT_SHIFT_WRITE
 - загрузить в RAM смещение вправо
   0b = 10
  1b = от 0 до 3 номер канала
  2b = 0 (не используется)
*/
#define RIGHT_SHIFT_WRITE  10

/*RIGHT_SHIFT_READ
 - прочитеть смещеие вправо(помещается в 0 байт)
 0b = 11
 1b = от 0 до 3 номер канала
 2b = 0 (не используется)
*/
#define RIGHT_SHIFT_READ  11
		
#define RSH_INDEX 2
//////////////////////////////////////////////////////

//////////////////////////////////////////////////////
//CENTER

/*CENTER_WRITE
 - загрузить в RAM смещение по центру
 0b = 12
 1b = от 0 до 3 номер канала
 2b = значение смещения 
*/
#define CENTER_WRITE  12

/*CENTER_READ
- прочитать смещение по центру (помещается 0 байт)
 0b = 13
1b = от 0 до 3 номер канала
2b = 0 (не используется)
*/
#define CENTER_READ  13

#define C_INDEX 3
/////////////////////////////////////////////////////

/////////////////////////////////////////////////////
//STK	
	
/*STK_WRITE 
- загрузить в RAM номер ручки управления для канала
 0b = 15
 1b = от 0 до 3 номер канала
 2b = от 0 до 3 номер ручки
*/
#define STK_WRITE  15

/*STK_READ 
- прочитать номер ручки для канала
 0b = 16
 1b = от 0 до 3 номер канала
 2b = 0 (не используется)
*/
#define STK_READ  16

#define STK_INDEX 4
////////////////////////////////////////////////////

////////////////////////////////////////////////////
//TIMING

/*TIMING_READ
- прочитать расчитанный тайминг (помещается в 0 байт, от 0 до 40
0b = 17
1b = от 0 до 3 номер канала
2b = 0 (не используется)
*/
#define TIMING_READ 17

#define TIMING_INDEX 5
////////////////////////////////////////////////////

/////////////////////////////////////////////////////
//ADC

/*ADC_READ
-прочитать значение снятое с АЦП (помещается в 0 байт, от 0 до 255)
0b = 18
1b = от 0 до 3 номер канала
2b = 0 (не используется)
*/
#define ADC_READ 18

#define ADC_INDEX 6
//////////////////////////////////////////////////////

//////////////////////////////////////////////////////
//EEPROM

/*EEPROM_WRITE
- записывает все настройки в EEPROM
0b = 19
1b = 0 (не используется)
2b = 0 (не используется)
*/
#define EEPROM_WRITE 19
/////////////////////////////////////////////////////

//настройки канала RAM
unsigned char ChanelSetting[4][7];
//настройки канала EEPROM
EEMEM unsigned char ChanelSetting_EEMEM[4][5]; 
/*

     RN      LSH     RSH    CSH      STK  TIMING   ADC
     0        0       0      0        0     0       0
     0        0       0      0        0     0       0 
     0        0       0      0        0     0       0
     0        0       0      0        0     0       0 
         
   RN -реверсивный или нормальный. 0 - нормальный . 1 - реверсивный. поумолчанию 0 - нормальный
   LSH - смешение влево
   RSH - смешение вправо
   CSH - смешение по центру
   STK - номер ручки управления
   TIMING - расчитанное время
   ADC - значение снятое с АЦП
 */
 
/*Выччисление TIMING*/
unsigned char mass[41];
void MoveTiming(unsigned char start_index, 
                unsigned char count, 
				unsigned char max_cell, 
				unsigned char reverse_index,
				unsigned char minus_or_plus);
				
void GetTiming();

/*Снятие значение с АЦП*/
unsigned char ChanelNumber = 0;
/*Запуск АЦП*/
#define StartConvAdc()  ADCSRA |= (1<<ADSC) 
//------------------

unsigned char dataexchange_bufer[] = {0,0,0};

struct dataexchange_t       
{
   uchar b1;              
   uchar b2;              
   uchar b3;        
};                  
                    


struct dataexchange_t pdata = {0, 0, 0};


PROGMEM char usbHidReportDescriptor[22] = { // USB report descriptor         // Дескриптор описывает структуру пакета данных для обмена
    0x06, 0x00, 0xff,                       // USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                             // USAGE (Vendor Usage 1)
    0xa1, 0x01,                             // COLLECTION (Application)
    0x15, 0x00,                             //    LOGICAL_MINIMUM (0)        // min. значение для данных
    0x26, 0xff, 0x00,                       //    LOGICAL_MAXIMUM (255)      // max. значение для данных, 255 тут не случайно, а чтобы уложиться в 1 байт
    0x75, 0x08,                             //    REPORT_SIZE (8)            // информация передается порциями, это размер одного "репорта" 8 бит
    0x95, sizeof(struct dataexchange_t),    //    REPORT_COUNT               // количество порций (у нашем примере = 3, описанная выше структура передастся за три репорта)
    0x09, 0x00,                             //    USAGE (Undefined)
    0xb2, 0x02, 0x01,                       //    FEATURE (Data,Var,Abs,Buf)
    0xc0                                    // END_COLLECTION
};



static uchar    currentAddress;
static uchar    bytesRemaining;


/* usbFunctionRead() вызывается когда хост запрашивает порцию данных от устройства
 * 
 */
uchar   usbFunctionRead(uchar *data, uchar len)
{
    if(len > bytesRemaining)
        len = bytesRemaining;

    uchar *buffer = (uchar*)&pdata;

    if(!currentAddress)        
    {   
         //заполнение данных для отправки из буфера	
         pdata.b1 = dataexchange_bufer[0];
		 pdata.b2 = dataexchange_bufer[1];
		 pdata.b3 = dataexchange_bufer[2];
    }

    uchar j;
    for(j=0; j<len; j++)
        data[j] = buffer[j+currentAddress];

    currentAddress += len;
    bytesRemaining -= len;
    return len;
}


/* usbFunctionWrite() вызывается когда хост отправляет порцию данных к устройству
 * 
 */
uchar   usbFunctionWrite(uchar *data, uchar len)
{
    if(bytesRemaining == 0)
        return 1;               

    if(len > bytesRemaining)
        len = bytesRemaining;

    uchar *buffer = (uchar*)&pdata;
    
    uchar j;
    for(j=0; j<len; j++)
        buffer[j+currentAddress] = data[j];

    currentAddress += len;
    bytesRemaining -= len;

    if(bytesRemaining == 0)     
    {     
         dataexchange_bufer[1] = 0;
         dataexchange_bufer[2] = 0;    
         
		 switch(pdata.b1)
		 {
		     case RN_WRITE:
			 {
			     ChanelSetting[pdata.b2][RN_INDEX] = pdata.b3;
			     break;
			 } 
             case RN_READ :
			 {
			     dataexchange_bufer[0] = ChanelSetting[pdata.b2][RN_INDEX];
                 
				 break;
			 }
             case LEFT_SHIFT_WRITE :
			 {
			     ChanelSetting[pdata.b2][LSH_INDEX] = pdata.b3;
			     break;
			 }
             case LEFT_SHIFT_READ :
			 {
			     dataexchange_bufer[0] = ChanelSetting[pdata.b2][LSH_INDEX];
       	         break;
			 }
             case RIGHT_SHIFT_WRITE :
			 {
			     ChanelSetting[pdata.b2][RSH_INDEX] = pdata.b3;
			     break;
			 }
             case RIGHT_SHIFT_READ :
			 {
			     dataexchange_bufer[0] = ChanelSetting[pdata.b2][RSH_INDEX];
			     break;
			 }
             case CENTER_WRITE  :
			 {
			     ChanelSetting[pdata.b2][C_INDEX] = pdata.b3;
			     break;
			 }
             case CENTER_READ  :
			 {
			     dataexchange_bufer[0] = ChanelSetting[pdata.b2][C_INDEX];
			     break;
			 }	
             case STK_WRITE  :
			 {
			     ChanelSetting[pdata.b2][STK_INDEX] = pdata.b3;
			     break;
			 }
             case STK_READ  :
			 {
			     dataexchange_bufer[0] = ChanelSetting[pdata.b2][STK_INDEX];
			     break;
			 }  
			 case TIMING_READ:
			 {
			     dataexchange_bufer[0] = ChanelSetting[pdata.b2][TIMING_INDEX];
				 break;
			 }
			 case ADC_READ:
			 {
			     dataexchange_bufer[0] = ChanelSetting[pdata.b2][ADC_INDEX];
				 break;
			 }
			 case EEPROM_WRITE:
			 {
			    unsigned char i = 0;
	            unsigned char j = 0;
	            for(i =0;i<=3;i++)
	            {
	                 for(j=0;j<=4;j++)
		             {
					     eeprom_update_byte(&ChanelSetting_EEMEM[i][j],ChanelSetting[i][j]);
		             }	 
	            }
				break;
			 }
		 }
		 
    }

    return bytesRemaining == 0; 
}



usbMsgLen_t usbFunctionSetup(uchar data[8])
{
usbRequest_t    *rq = (void *)data;

    if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){   
        if(rq->bRequest == USBRQ_HID_GET_REPORT){  
           
            bytesRemaining = sizeof(struct dataexchange_t);
            currentAddress = 0;
            return USB_NO_MSG;  
        }else if(rq->bRequest == USBRQ_HID_SET_REPORT){
            
            bytesRemaining = sizeof(struct dataexchange_t);
            currentAddress = 0;
            return USB_NO_MSG;  
        }
    }else{
       
    }
    return 0;
}


//Выччисление TIMING
/*
void MoveTiming(unsigned char start_index, 
                unsigned char count, 
				unsigned char max_cell, 
				unsigned char reverse_index,
				unsigned char minus_or_plus)
{
    unsigned char counting = 0;
    if (start_index > max_cell)
	{
        return;
	}	
    while (counting != count)
    {
        unsigned char i = 0;
        for (i = start_index; i <= max_cell - 1; i++)
        {
            if (reverse_index == 0)
            {
                if (minus_or_plus == 1)
				{
                    mass[i] = mass[i] + 1;
				}
                if (minus_or_plus == 0)
				{
                    mass[i] = mass[i] - 1;
				}	
            }
            if (reverse_index == 1)
            {
                if (minus_or_plus == 1)
				{
                    mass[40 - i] = mass[40 - i] + 1;
				}
                if (minus_or_plus == 0)
				{
                    mass[40 - i] = mass[40 - i] - 1;
			    }
            }
            counting++;
            if (counting == count)
			{
                 return;
			}	 
        }
    }
}

void GetTiming()
{
    unsigned char lsh = 0;  
    unsigned char csh = 0;            
    unsigned char rsh = 0;
    unsigned char ADCvalue = 0;

    lsh =  ChanelSetting[ChanelNumber][LSH_INDEX];
    csh =  ChanelSetting[ChanelNumber][C_INDEX];            
    rsh = 40 - ChanelSetting[ChanelNumber][RSH_INDEX];
    ADCvalue = ChanelSetting[ChanelNumber][ADC_INDEX];


    unsigned char i =0;
    for ( i =0;i<=40;i++)
	{
         mass[i]=4;
    }
    if (csh<20)
    {
        csh = 20 - csh;  
        MoveTiming(lsh,6*csh,20-csh,0,1);
        MoveTiming(rsh,6*csh,20+csh,1,0);
        MoveTiming(lsh,6*lsh,20-csh,0,1);
        MoveTiming(rsh,6*rsh,20+csh,1,1);
    }
    if (csh==20)
    {
        MoveTiming(lsh,6*lsh,20,0,1);          	
        MoveTiming(rsh,6*rsh,20,1,1);         
    }
    if(csh>20)
    {
        MoveTiming(lsh,6*(csh-20),20+(csh-20),0,0);
        MoveTiming(rsh,6*(csh-20),20-(csh-20),1,1); 
        MoveTiming(lsh,6*lsh,20+(csh-20),0,1);
        MoveTiming(rsh,6*rsh,20-(csh-20),1,1);
    }
    for (i =0;i<= rsh-1;i++ )
	{
         mass[40-i] =0;
	}	 
    for (i =0;i<= lsh-1;i++ )
	{
        mass[i] =0;
	}	
    unsigned char num =0;
    unsigned char v1  =0;
    unsigned char v2  =0;
    for (i =0; i<=40;i++)
    {
        v1=num;
        v2 = num + mass[i];
        if (v2 > 244)
        {
            v1 = 0;
            v2 = 0;
        }
        if (mass[i] != 0)
        {
            num = num + mass[i] + 2;
        }
        if (v2!=0)
        {
            if (ADCvalue >= v1 && ADCvalue <= v2)
            {
                ChanelSetting[ChanelNumber][TIMING_INDEX] = i; 
                return;
            }
        }
    }	 
         
}
*/


//Обработчик АЦП
ISR(ADC_vect)
{
     unsigned char ADCbufer =0;
     ADCbufer =  ADCH;
     ChanelSetting[ChanelNumber][ADC_INDEX] = ADCbufer;
     //GetTiming();//расчет тайминга
	 //=====================================================================
	        unsigned char lsh = 0; 
            unsigned char csh = 0;             
            unsigned char rsh = 0;
            unsigned char ADCvalue = 0;
            unsigned char i =0;
            unsigned char counting = 0;
            unsigned char _break = 0;
            
			 lsh =  ChanelSetting[ChanelNumber][LSH_INDEX];
             csh =  ChanelSetting[ChanelNumber][C_INDEX];            
             rsh = 40 - ChanelSetting[ChanelNumber][RSH_INDEX];
			
			//если канал реверсивный
			if(ChanelSetting[ChanelNumber][RN_INDEX]==1)
			 {
             ADCvalue = 255 - ChanelSetting[ChanelNumber][ADC_INDEX];
			}
			else
			{
			 ADCvalue =ChanelSetting[ChanelNumber][ADC_INDEX];
			}
			//проверка на допустимость значений
			/*
			 lsh - должен быть меньше чем csh
			 rsh - должен быть больше csh
			 csh - может быть от 1 до до 39
			*/
			
			if(   ChanelSetting[ChanelNumber][LSH_INDEX] < ChanelSetting[ChanelNumber][C_INDEX] 
			   && ChanelSetting[ChanelNumber][RSH_INDEX] > ChanelSetting[ChanelNumber][C_INDEX]
			   && ChanelSetting[ChanelNumber][C_INDEX] >=1 
			   && ChanelSetting[ChanelNumber][C_INDEX] <= 39)
			{
			
            for (i = 0; i <= 40; i++)
            {
                mass[i] = 4;
            }
            if (csh<20)
            {
                 csh = 20 - csh;
                   
                //----------------------------------------------------- 
                  counting = 0;
                  _break = 0;
                  while (counting != 6*csh)
                  {
                      if (_break == 1)
                      {
                          break;
                      }
                    for (i = lsh; i <= 20-csh- 1; i++)
                    {
                           mass[i] = mass[i] + 1;//0,1
                     
                           counting++;
                           if (counting == 6 * csh)
                           {
                               _break = 1;
                               break;
                           }//if
                     }//for
                  }//while
      
                //------------------------------------------------------
                
                  counting = 0;
                  _break = 0;
                  while (counting != 6 * csh)
                  {
                      if (_break == 1)
                      {
                          break;
                      }
                      for (i = rsh; i <= 20 + csh - 1; i++)
                      {
                          mass[40 - i] = mass[40 - i] - 1; //1,0
                          counting++;
                          if (counting == 6 * csh)
                          {
                              _break = 1;
                              break;
                          }//if
                      }//for
                  }//while



                //------------------------------------------------------
                  counting = 0;
                  _break = 0;
                  while (counting != 6 * lsh)
                  {
                      if (_break == 1)
                      {
                          break;
                      }
                      for (i = lsh; i <= 20 - csh - 1; i++)
                      {
                          mass[i] = mass[i] + 1;//0,1

                          counting++;
                          if (counting == 6 * lsh)
                          {
                              _break = 1;
                              break;
                          }//if
                      }//for
                  }//while



                //-------------------------------------------------------

                  counting = 0;
                  _break = 0;
                  while (counting != 6 * rsh)
                  {
                      if (_break == 1)
                      {
                          break;
                      }
                      for (i = rsh; i <= 20 + csh - 1; i++)
                      {
                          mass[40 - i] = mass[40 - i] + 1; //1,1

                          counting++;
                          if (counting == 6 * rsh)
                          {
                              _break = 1;
                              break;
                          }//if
                      }//for
                  }//while


                //-------------------------------------------------------
            }

            if (csh==20)
            {
                //----------------------------------------------------
                counting = 0;
                _break = 0;
                while (counting != 6 * lsh)
                {
                    if (_break == 1)
                    {
                        break;
                    }
                    for (i = lsh; i <= 20 - 1; i++)
                    {
                        mass[i] = mass[i] + 1;//0,1

                        counting++;
                        if (counting == 6 * lsh)
                        {
                            _break = 1;
                            break;
                        }//if
                    }//for
                }//while 

                //----------------------------------------------------
                counting = 0;
                _break = 0;
                while (counting != 6 * rsh)
                {
                    if (_break == 1)
                    {
                        break;
                    }
                    for (i = rsh; i <= 20 - 1; i++)
                    {
                        mass[40 - i] = mass[40 - i] + 1; //1,1

                        counting++;
                        if (counting == 6 * rsh)
                        {
                            _break = 1;
                            break;
                        }//if
                    }//for
                }//while
             //------------------------------------------------------------------
            }
            if(csh>20)
            {
                //------------------------------------------------------------
                counting = 0;
                _break = 0;
                while (counting != 6 * (csh -20))
                {
                    if (_break == 1)
                    {
                        break;
                    }
                    for (i = lsh; i <= 20+(csh-20) - 1; i++)
                    {
                        mass[i] = mass[i] - 1;  //0,0

                        counting++;
                        if (counting == 6 * (csh-20))
                        {
                            _break = 1;
                            break;
                        }//if
                    }//for
                }//while




                //------------------------------------------------------------

                counting = 0;
                _break = 0;
                while (counting != 6 * (csh - 20))
                {
                    if (_break == 1)
                    {
                        break;
                    }
                    for (i = rsh; i <= 20 - (csh - 20) - 1; i++)
                    {
                        mass[40 - i] = mass[40 - i] + 1; //1,1

                        counting++;
                        if (counting == 6 * (csh - 20))
                        {
                            _break = 1;
                            break;
                        }//if
                    }//for
                }//while

                //-------------------------------------------------------------

                counting = 0;
                _break = 0;
                while (counting != 6 * lsh)
                {
                    if (_break == 1)
                    {
                        break;
                    }
                    for (i = lsh; i <= 20 + (csh - 20) - 1; i++)
                    {
                        mass[i] = mass[i] + 1;//0,1

                        counting++;
                        if (counting == 6 * lsh)
                        {
                            _break = 1;
                            break;
                        }//if
                    }//for
                }//while



                //-------------------------------------------------------------

                counting = 0;
                _break = 0;
                while (counting != 6 * rsh)
                {
                    if (_break == 1)
                    {
                        break;
                    }
                    for (i = rsh; i <= 20 - (csh - 20) - 1; i++)
                    {
                        mass[40 - i] = mass[40 - i] + 1; //1,1

                        counting++;
                        if (counting == 6 * rsh)
                        {
                            _break = 1;
                            break;
                        }//if
                    }//for
                }//while


                //--------------------------------------------------------------
            }
            for (i = 0; i <= rsh - 1; i++)
            {
                mass[40 - i] = 0;
            }
            for (i = 0; i <= lsh - 1; i++)
            {
                mass[i] = 0;
            }
            
            
               unsigned char num =0;
               unsigned char v1  =0;
               unsigned char v2  =0;
			   
               for (i =0; i<=40;i++)
               {
                   v1=num;
                   v2 = num + mass[i];

                   if (v2 > 244)
                   {
                       v1 = 0;
                       v2 = 0;
                   }
                   if (mass[i] != 0)
                   {
                       num = num + mass[i] + 2;
                   }
                     if (v2!=0)
                     {
                         if (ADCvalue >= v1 && ADCvalue <= v2)
                         {
                             ChanelSetting[ChanelNumber][TIMING_INDEX] = i; 
                             break;//return i ;
                         }
                     }
                            	  	   
       
                }	 
             
	        }
	 //=====================================================================
    //переключение на следующий канал 	 
     if(ChanelNumber+1<=3)
	 {
	    ChanelNumber++;
		
	 }
	 else
	 {
	    ChanelNumber =0;		
	 }  
    //подключение к АЦП ручки управления 	 
    switch(ChanelSetting[ChanelNumber][STK_INDEX])
	{
	    case 0:
		{
		     ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
             break;			
		}
		case 1:
		{
		    ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(1<<MUX0);
		    break;
		}
		case 2:
		{
		    ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(1<<MUX1)|(0<<MUX0);
		    break;
		}
		case 3:
		{
		    ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(1<<MUX1)|(1<<MUX0);
		    break;
		}
	}
	 	
	
}

int main(void)
{
    //инициализация АЦП
	ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
	ADCSRA = (1<<ADEN)|(0<<ADSC)|(0<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
    //загрузка данных из EEPROM в RAM [4][5]
	unsigned char a = 0;
	unsigned char b = 0;
	for(a =0;a<=3;a++)
	{
	     for(b=0;b<=4;b++)
		{
	         ChanelSetting[a][b]	= eeprom_read_byte(&ChanelSetting_EEMEM[a][b]);
		}	 
	}
	
    usbInit();
    usbDeviceDisconnect();  
    
    uchar i = 0;
    while(--i){             
        _delay_ms(1);
    }
    
    usbDeviceConnect();    

    sei();                  

    for(;;){                
        usbPoll(); 
		StartConvAdc();         
    }
    return 0;
}

