
	   ; TrackIR для геймпада на ATmega 8
	   ; тактовая частота 14,318 МГц (кварц от системного блока компьютера)           
	   ; фьюзы: CKSEL0=1,CKSEL1=1,CKSEL2=1,CKSEL3=1,SUT0=0,SUT1=1,SKOPT=0   
					   
					   ; Псевдокоманды управления 


.include "m8def.inc"            ; присоединение файла описаний

.list                           ; включение листинга

.def temp =        R16          ; 1 регистр для инициализации оборудования и обработки видео-импульсов сигнала изображения в основной программе       
.def ogran_29 =    R17          ; Регистр - ограничитель для пропуска 29 срочных импульсов в начале каждого полукадра (записана константа 29)       
.def max =         R18          ; Регистр - для константы - 255      
.def line_stroki = R19          ; Регистр - для записи обновленного результата подсчитанных импульсов от счетчика/таймера Т0       
.def step =        R10          ; 1 вспомогательный регистр (с его помощью производим арифмтические и логические операции с подсчитанными импульсами от Т0)
.def dobavka =     R20          ; Регистр - для константы - 14 
.def min =         R21		; Регистр - для константы - 0
.def kolvo_strok = R22          ; Регистр - счетчик для записи обновленного результата подсчитанных строчных импульсов каждого полукадра видеосигнала               
.def temp2    =    R23          ; 2й вспомогательный регистр                   
.def kadr_Y =      R24          ; Регистр для сохранения старого результата подсчитанных строчных импульсов в полукадре
.def kadr_X =      R25 		; Регистр для сохранения старого результата подсчитанных импульсов счетчиком Т0 в строках 			  
.def step_wrate	=  R26 	        ; Регистр - счетчик для слежения за четными и нечетными полукадровыми полями видеосигнала			  
.def loop1  =      R27          ; Вспомогательный регистр для формирования задержки 
.def step2  =      R28          ; Регистр - счетчик для формирования сигнала стоп во время отсутствия импульсов изображения 


                                ; Начало программного кода


     
.cseg                   ; выбор сегмента программного кода (Flesh память программ)

      
                    ; Таблица векторов прерываний для ATmega8

start:   .org $000        
         rjmp   init      ; (RESET) 
         .org $001
         rjmp   kadr      ; (INT0) External Interrupt Request 0
         .org $002
         rjmp   stroki    ; (INT1) External Interrupt Request 1
         .org $003
         reti		  ; (TIMER2 COMP) Timer/Counter2 Compare Match
         .org $004
         reti             ; (TIMER2 OVF) Timer/Counter2 Overflow
         .org $005
         reti		  ; (TIMER1 CAPT) Timer/Counter1 Capture Event
         .org $006
         reti             ; (TIMER1 COMPA) Timer/Counter1 Compare Match A
         .org $007
         reti             ; (TIMER1 COMPB) Timer/Counter1 Compare Match B
         .org $008
         reti             ; (TIMER1 OVF) Timer/Counter1 Overflow
         .org $009
         reti             ; (TIMER0 OVF) Timer/Counter0 Overflow
         .org $00A
         reti             ; (SPI,STC) Serial Transfer Complete
         .org $00B
         reti             ; (USART,RXC) USART, Rx Complete
         .org $00C
         reti             ; (USART,UDRE) USART Data Register Empty
         .org $00D
         reti             ; (USART,TXC) USART, Tx Complete
         .org $00E
         reti		  ; (ADC) ADC Conversion Complete
         .org $00F
         reti             ; (EE_RDY) EEPROM Ready
         .org $010
         reti             ; (ANA_COMP) Analog Comparator 
         .org $011
         reti             ; (TWI) 2-wire Serial Interface
         .org $012        
         reti             ; (SPM_RDY) Store Program Memory Ready

.org   INT_VECTORS_SIZE   ; Конец таблицы прерываний


                               ; Инициализация вершины стека

 init:ldi    YH, high(RAMEND)    ; запись в cтарший байт Y-регистра старшего байта адреса памяти ОЗУ "RAMEND" (т.е из $045F число 04)
      ldi    YL, low(RAMEND)     ; запись в младший байт Y-регистра младшего байта адреса памяти ОЗУ "RAMEND" (т.е из $045F число 5F)
      out    SPH, YH             ; запись старшего байта адреса памяти в старший байт регистра стека
      out    SPL, YL             ; запись младшего байта адреса памяти в младший байт регистра стека

      
	  
	                         ; Инициализация портов ВВ
      ldi     temp, 0
      out     DDRD, temp          ; настройка порта D на вход
      out     PORTD, temp         ; отключение подтягивающих резисторов на входах порта D
      ldi     temp,  0xFF      
      out     DDRC, temp          ; настройка порта С на выход
      ldi     temp, 0
      out     SFIOR,temp          ; на всякий случай обнуляем регистр SFIOR

                           	   
				 ;Инициализация счетчика/таймера Т1

      ldi      temp, 0b11000001 
      out      TCCR1A,temp        ; установка конфигурации таймера Т1(включен режим Fast PWM 8 разрядов)
      ldi      temp, 0b00001010  
      out      TCCR1B,temp        ; включен предварительный делитель на /8 
	  
      sbi      DDRB, 0 	          ; подкючение младшего пина порта РВ0 на выход (контроль срабатывания компаратора) 
      sbi      DDRB, 1		  ; подкючение ОС1А к выходу порта РВ1 
      sbi      DDRB, 3            ; подкючение ОС2 к выходу порта РВ3            
      cbi      DDRB, 2            ; подключение пина РВ2  на вход (переключает режим работы TrackIR)         
      ldi      temp, 0b00000100						
      out      PORTB, temp        ; подключеие подтягивающего резистора ко входу РВ2				 
							 
				;Инициализация счетчика/таймера Т0

      ldi      temp, 0b00000010   
      out      TCCR0,temp         ; установка конфигурации таймера Т0 (предварительный делитель на /8 )

				
			        ;Инициализация счетчика/таймера Т2
      
      ldi      temp, 0b01111010  ; включен режим Fast PWM 
      out      TCCR2,temp        ; включен предварительный делитель на /8 
	 
				
		        ;Настройка внешних прерываний по входам INTO и INT1
        
      ldi      temp, 0b11000000  ; запись числа для регистра GICR 
      out      GICR,temp         ; включение внешнего прерывания по входу INT0 и INT1 
      ldi      temp, 0b00001111  ; запись числа для регистра MCUCR 
      out      MCUCR, temp       ; настройка регистра MCUCR - срабатывание по передним фронтам  сигналов INT0 и INT1 
	 
                       
				 ; Включение компаратора

      ldi      temp, 0           ; прерывание компаратора отключено,
      out      ACSR, temp        ; будем следить за состоянием  5 бита(АСО), когда там 1 (т.е AIN0>AIN1)      

      
	                    ; Предустановка необходимых регистров

     clr      min                ; запись 0 в регистр  min 
     ldi      max, 0xFF          ; запись 255 в регистр max
     ldi      dobavka, 14        ; запись добавочного числа 14 в регистр dobavka для арифметических операций с результатом счетчика Т0
     clr      step_wrate         ; обнуление регистра  step_wrate
     out      OCR1AH, min        ; запись 0 в старший байт счетчика/таймера Т1 канал А	  
     
     sei                         ; разрешаем прерывания
					 
					 
		      ;Начало основной программы
                      ;Запись координат положения маркера при появлении видеоимпульсов изображения в каждом полукадре  

 
main: cbi      PORTB, 0          ; тушим светодиод компаратора на PB0 (когда остутствуют видеоимпульсы изображения)
      in       temp, ACSR        ; читаем содержимое регистра компаратора ACSR и пишем в temp 
      sbrs     temp, 5           ; проверяем состояние 5 бита(АСО) регистра ACSR 
      rjmp     main              ; если АСО =0 переходим к началу программы  
      inc      step2             ; если АСО =1 делаем инкремент счетчика видеоимпульсов изображения step2
      cpi      step2, 1          ; сравниваем содержимое счетчика step2 с единицей
      brne     main	         ; если счетчик step2 подсчитал больше одного импульса, то их не учитываем и переходим в начало (работаем только с первым видеоимпульсом изображения в каждом полукадре)                    
      sbi      PORTB, 0          ; если зарегистрирован 1 видеоимпульс изображения, то зажигаем светодиод компаратора на PB0 и продолжаем выполнять программу далее...
 
                           
				; Выбор режима работы TrackIR                              

      in       temp, PINB        ; проверка пина порта PB2.Если 0, то пропускаем команду и выполняем программу формирования координат в виде ШИМ  
      sbrc     temp, 2           ; если 1 то переходим к метке d4 и выполняем другую программу - формирование сигналов управления (0В; 1,5В; 3В) для геймпада
      rjmp     d4
	                          
                     ; Ветка основной программы - определение координат ИК маркера по двум осям координат и выдача сигналов в виде ШИМ   

      in       line_stroki, TCNT0  ; читаем текущее значение счетчика Т0 и пишем в line_stroki 
      lsl      line_stroki         ; умножаем содержимое  line_stroki на 2
      mov      step,line_stroki    ; копируем содержимое  line_stroki в регистр step
      add      step, dobavka       ; производим арифметические и логические операции с числом подсчитанным счетчиком Т0lsr      step               
      lsr      step               
      add      line_stroki, step   ; записываем преобразованное значение step обратно в  line_stroki

      out      OCR1AL, kolvo_strok ; записываем  подсчитанное количество строк в регистр сравнения OCR1AL счетчика Т0
      out      OCR2, line_stroki   ; записываем в регистр сравнения OCR2 Т2 текущее значение подсчитанных счетчиком импульсов в строке 
	  
      rjmp     main                ; переходим в начало основной программы
       
                              
			     ; Ветка основной программы - формирование сигналов управления (0В; 1,5В; 3В) для геймпада
   
 d4: in       line_stroki, TCNT0  ; читаем текущее значение счетчика Т0 и пишем в line_stroki	   	   	   
     lsl      line_stroki         ; умножаем содержимое  line_stroki на 2
     mov      step,line_stroki    ; копируем содержимое  line_stroki в регистр step
     add      step, dobavka       ; производим арифметические и логические операции с числом подсчитанным счетчиком Т0
     lsr      step               
     lsr      step               
     add      line_stroki, step   ; записываем преобразованное значение step обратно в  line_stroki
	   
     cpi      step_wrate, 1       ; проверка счетчика полукадров на 1, благодаря ему чередуются циклы сохранения предыдущих координат и определение текущих
     breq     d3                  ; если  step_wrate равен 1, переходим к вызову подпрограммы для определения текущих координат положения маркера и формирования сигналов управления
                                  ; если  step_wrate равен 0, переходим к вызову подпрограммы для сохранения предыдущих координат
	   
     rcall    sohranenie          ; вызов подпрограммы для сохранения предыдущих координат 
     nop
     rjmp     main                ; переходим в начало основной программы
 
  
 d3: rcall    koordinaty          ; вызов подпрограммы для определения текущих координат положения маркера и формирования сигналов управления
     nop
     rjmp     main	          ; переходим в начало основной программы
 
 
                              ; Подпрограмма сохранения предыдущих координат маркера
	   			
sohranenie: 
     cli                          ; запрещаем прерывания, что-бы исключить сбой в работе подпрограммы
           	  		  
     mov     kadr_Y, kolvo_strok  ; копируем значения регистров - счетчиков в буферы kadr_X и kadr_Y				
     mov     kadr_X, line_stroki			
		
     ldi     step_wrate, 1        ; записываем в счетчик полукадров единицу
                                 			   		  	
 e1: in      loop1, PIND          ; синхронизируемся с кадровыми импульсами (не выходим из подпрограммы 
     sbrc    loop1, 2             ; пока не появится очередной кадровый синхроимпульс)
     rjmp    e1
 		   
     sei                          ; разрешаем прерывания 
     ret                          ; выход из подпрограммы 
				
                  
                ; Подпрограмма определения текущих координат маркера и формирование сигналов управления
														  											
koordinaty:
     cli                         ; запрещаем прерывания чтобы исключить сбой в работе подпрограммы
     
     cp      kadr_Y, kolvo_strok ; сравниваем текущее значение регистра-счетчика kolvo_strok с предыдущим (координата по оси Y)
     brsh    j2                  ; если  значение kadr_Y больше или равно kolvo_strok, то переходим на J2
     brlo    j3                  ; если  значение kadr_Y меньше kolvo_strok, то переходим на J3
			                             
                                 ; определяем: "На сколько изменилось количество импульсов при сравнении с предыдущим результатом по оси Y?"
 j2: sub     kadr_Y, kolvo_strok ; для этого вычитаем из предыдущего значения текущее	
     cpi     kadr_Y, 4           ; устраняем сигнал ошибки для этого сравниваем разность чисел с константой - 4
     brlo    j4                  ; если разность  чисел меньше 4, значит считаем что маркер не изменял положение по оси Y и осуществляем переход по метке  j4
		    
     cbi     PORTC, 2            ; если разность чисел больше или равна 4, значит маркер переместился вниз 
     cbi     PORTC, 3            ; при этом на РС2, РС3 формируем сигнал управления при котором напряжение на входе - Y геймпада будет равно 0 В
     rjmp    j6			 ; переходим к определению текущих координат маркера по оси Х
       
 j4: cbi     PORTC, 2            ; при разности чисел меньше 4 считаем, что  маркер не перемещался по оси Y
     sbi     PORTC, 3            ; при этом на РС2, РС3 формируем сигнал управления при котором напряжение на входе - Y геймпада будет равно 1,5 В
     rjmp    j6                  ; переходим к определению текущих координат маркера по оси Х
            
 j3: sub     kolvo_strok,  kadr_Y; вычитаем из текущего предыдущее значение счетчика 	
     cpi     kolvo_strok, 4      ; устраняем сигнал ошибки для этого сравниваем разность чисел с константой - 4
     brlo    j4                  ; если разность  чисел меньше 4, значит считаем что маркер не изменял положение по оси Y и осуществляем переход по метке  j4
           	
     sbi     PORTC, 2            ; если разность чисел больше или равна 4, значит маркер переместился вверх  
     sbi     PORTC, 3            ; при этом на РС2, РС3 формируем сигнал управления при котором напряжение на входе - Y геймпада будет равно 3 В
	   			                        ; переходим к определению текущих координат маркера по оси Х
	  
 j6: cp      kadr_X, line_stroki ; сравниваем текущее значение счетчика Т0 с предыдущим (координата по оси Х)
     brsh    j10                 ; если  значение kadr_Х больше или равно line_stroki, то переходим на J10
     brlo    j11                 ; если  значение kadr_Х меньше line_stroki, то переходим на J11
	                                    
										; определяем: "На сколько изменилось количество импульсов подсчитанных счетчиком Т0 при сравнении с предыдущим результатом по оси Х?"
j10: sub     kadr_X, line_stroki ; для этого вычитаем из предыдущего значения текущее
     cpi     kadr_X, 4           ; устраняем сигнал ошибки для этого сравниваем разность чисел с константой - 4
     brlo    j8                  ; если разность  чисел меньше 4, значит считаем что маркер не изменял положение по оси Х и осуществляем переход по метке  j8
	     
     cbi     PORTC, 4            ; если разность чисел больше или равна 4, значит маркер переместился влево 
     cbi     PORTC, 5            ; при этом на РС4, РС5 формируем сигнал управления при котором напряжение на входе - Х геймпада будет равно 0 В
     rjmp    j12                 ; готовимся к выходу из подпрограммы и переходим к метке j12
	   
	   
j11: sub     line_stroki, kadr_X ; вычитаем из текущего предыдущее значение счетчика 
     cpi     line_stroki, 4      ; устраняем сигнал ошибки для этого сравниваем разность чисел с константой - 4
     brlo    j8                  ; если разность  чисел меньше 4, значит считаем что маркер не изменял положение по оси Х и осуществляем переход по метке  j8
			
     sbi     PORTC, 4            ; если разность чисел больше или равна 4, значит маркер переместился вправо 
     sbi     PORTC, 5            ; при этом на РС4, РС5 формируем сигнал управления при котором напряжение на входе - Х геймпада будет равно 3 В
     rjmp    j12                 ; готовимся к выходу из подпрограммы и переходим к метке j12
	   	    
 j8: cbi     PORTC, 4            ; при разности чисел меньше 4 считаем, что  маркер не перемещался по оси Х
     sbi     PORTC, 5            ; при этом на РС4, РС5 формируем сигнал управления при котором напряжение на входе - Х геймпада будет равно 1,5 В
	                         ; готовимся к выходу из подпрограммы и переходим к метке j12

J12: dec     step_wrate          ; уменьшаем значение счетчика полукадров на 1
	       
j14:  in     loop1, PIND         ; синхронизируемся с кадровыми импульсами (не выходим из подпрограммы 
     sbrc    loop1, 3            ; пока не появится очередной кадровый синхроимпульс)
     rjmp    J14
		
sei                              ; разрешаем прерывания       

ret                              ; выход из подпрограммы 						
						
						
		      ; Подпрограмма прерывания по приходу кадрового синхроимпульса

kadr:  cpi      step2, 1                 ; проверка счетчика видеоимпульсов изображения step2 на наличие хотя-бы одного зарег. видеоимпульса
       brsh     m1                       ; если зарегистрирован один или более импульс, переходим к метке  m1
       cbi      PORTC, 2                 ; если счетчик обнулен значит маркер исчез из зоны видимости камеры поэтому  
       sbi      PORTC, 3                 ; устанавливаем пины порта С при которых
       cbi      PORTC, 4                 ; сигналы управления эквивалентны сигналу в гейпаде "стоп"
       sbi      PORTC, 5        

  m1:  ldi      ogran_29, 29             ; запись числа 29 в регистр ogran_29
       clr      kolvo_strok              ; обнуляем счетчик-регистр для подсчета количества строк в полукадре	   		   
       clr      step2                    ; обнуляем  регистр-счетчик step2
       reti                              ; выход из подпрограммы прерывания
                     
                      ; Подпрограмма прерывания по приходу строчного синхроимпульса 

stroki:out      TCNT0, min               ; обнуляем счетчик Т0 
       cpse     ogran_29, min            ; проверяем содержимое регистра счетчика ogran_29 на 0. Если ноль, то пропускам команду    
       rjmp     m2 		         ; проверяем содержимое регистра ogran_29 на 0. Если ноль, то переходим к метке m2  
       cpse     kolvo_strok, max         ; проверяем: "не достиг ли счетчик-регистр количества строк в полукадре числа 255?" Если достиг, то пропускаем следующую команду и выходим из подпрограммы         
       inc      kolvo_strok              ; если не достиг 255, то прибавляем к содержимому счетчика-регистра количества строк в полукадре единицу
       reti                              ; выход из подпрограммы прерывания
  m2:  dec      ogran_29                 ; вычитаем из содержимого регистра ogran_29 единицу
       reti                              ; выход из подпрограммы прерывания

      
		 
