;********************************************************************** ; This file is a basic code template for assembly code generation * ; on the PICmicro PIC16F876A. This file contains the basic code * ; building blocks to build upon. * ; * ; If interrupts are not used all code presented between the ORG * ; 0x004 directive and the label main can be removed. In addition * ; the variable assignments for 'w_temp' and 'status_temp' can * ; be removed. * ; * ; Refer to the MPASM User's Guide for additional information on * ; features of the assembler (Document DS33014). * ; * ; Refer to the respective PICmicro data sheet for additional * ; information on the instruction set. * ; * ;********************************************************************** ; * ; Filename: va-08.asm * ; Date: 26.02.2006 * ; File Version: 8 * ; * ; Author: Alexey Kudryavtsev * ; Company: iXBT.COM * ; * ; * ;********************************************************************** ; * ; Files required: * ; * ; * ; * ;********************************************************************** ; * ; Notes: * ; PORTA bit0 RA0 - концевой выключатель низ, 0, если включен * ; PORTA bit0 RA1 - концевой выключатель верх, 0, если включен * ; PORTA bit0 RA2 - направление, 0 - вниз, 1 - вверх * ; PORTA bit0 RA3 - движение, 0 - запрещено, 1 - разрешено * ; * ; * ; * ;********************************************************************** list p=16f876A ; list directive to define processor #include ; processor specific variable definitions ; __CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _RC_OSC & _WRT_OFF & _LVP_ON & _CPD_OFF __CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _HS_OSC & _WRT_OFF & _LVP_ON & _CPD_OFF ; '__CONFIG' directive is used to embed configuration data within .asm file. ; The lables following the directive are located in the respective .inc file. ; See respective data sheet for additional information on configuration word. ;***** VARIABLE DEFINITIONS w_temp EQU 0x71 ; variable used for context saving status_temp EQU 0x72 ; variable used for context saving pclath_temp EQU 0x73 ; variable used for context saving ;***** КОНСТАНТЫ n_step EQU 4 ; число шагов в цикле bottom_sw EQU 1 ; концевой выключатель низ, 1, если включен top_sw EQU 0 ; концевой выключатель верх, 1, если включен dir_bit EQU 2 ; направление, 0 - вниз, 1 - вверх move_bit EQU 3 ; движение, 0 - запрещено, 1 - разрешено btn_plus EQU 5 ; кнопка плюс, RB5 btn_minus EQU 4 ; кнопка минус, RB4 btn_cycle EQU 64 ; кол-во циклов в устранении дребезга контактов tmp_scal EQU B'00000000' ; таймер 0 предварительный делитель 2:1 tmr1_scal EQU B'00100001' ; включен таймер 1 предварительный делитель 4:1, длительность звука tmr1_sc_er EQU B'00110001' ; включен таймер 1 предварительный делитель 8:1, длительность звука переполнения start_scal EQU 0x80 ; начальное значение делителя шагов tmr2_scal EQU B'00000111' ; включен таймер 2 предварительный делитель 16:1, высота звука tmr2_sc_er EQU B'00000101' ; включен таймер 2 предварительный делитель 4:1, высота звука переполнения pwm_per EQU 0xFF ; период ШИМ scal_adr EQU 0 ; адрес, куда сохранять делитель cblock 0x20 ; блок переменных step ; текущий шаг ; dir ; направление ncbtn ; счетчик цикла в устранении дребезга контактов btn_code ; нажатая кнопка step_scal ; делитель шагов cur_step ; счетчик шагов is_sound ; флаг звука, >= 1 вкл., = 0 выкл. endc ; ORG 0x000 ; processor reset vector RESET_VECTOR CODE 0x000 nop ; nop required for icd goto main ; go to beginning of program ; ORG 0x004 ; interrupt vector location INT_VECTOR CODE 0x004 ; movwf w_temp ; save off current W register contents ; movf STATUS,w ; move status register into W register ; movwf status_temp ; save off contents of STATUS register ; movf PCLATH,w ; move pclath register into w register ; movwf pclath_temp ; save off contents of PCLATH register ; isr code can go here or be located as a call subroutine elsewhere ;процедура по таймеру ;1. выбрать байт из таблицы по шагу и направлению. ;2. проверять концевые выключатели, биты направления ;3. вывести в порт ; banksel INTCON btfss INTCON, T0IF ; если не установлен флаг прерывания от таймера 0 goto det_int ; то определяем какое это прерывание bcf INTCON, T0IF ; очистить флаг прерывания decfsz cur_step, f ; уменьшаем счетчик шагов retfie ; выходим, если он не 0 goto timer_int ; то перейдем к обработке прерывания от таймера det_int btfss PIR1, TMR1IF ; если не установлен флаг прерывания от таймера 1 goto button_int ; то перейдем к обработке прерывания от порта B ; **** обработка прер. от таймера 1, звук надо выключить call sound_off goto return_from_int ; **** обработка прер. от порта B (нажата кнопка) button_int movf is_sound, w btfss STATUS,Z ; проверяем на 0 goto return_from_i_b ; выходим, так как is_sound не 0, звук включен ; *** фильтр дребезга контактов movf PORTB, w movwf btn_code ; сохраняем нажатые кнопки call btn_ftl ; вызываем фильтр дребезга контактов iorlw 0 btfsc STATUS,Z ; проверяем на 0 goto return_from_i_b ; выходим, так как состояние кнопок изменилось btfsc PORTA, move_bit goto check_btn ; установлен move_bit, можно изменять делитель шагов btfsc btn_code, btn_plus ; если не нажата кнопка плюс, RB5 goto return_from_i_b ; то выходим btfsc btn_code, btn_minus; если не нажата кнопка минус, RB4 goto return_from_i_b ; то выходим из обработки прерывания call write_scl ; если нажаты обе кнопки во время остановки, то запомним делитель шагов call sound_wr ; звук записи делителя goto return_from_i_b ; выходим check_btn ; banksel PORTB btfsc btn_code, btn_plus ; если не нажата кнопка плюс, RB5 goto check_minus_btn ; проверим не нажата ли кнопка минус ; нажата кнопка плюс decf step_scal, f ; уменьшаем делитель btfss STATUS,Z ; проверяем на 0 goto return_from_i_sc; выходим из обработки прерывания от порта B, значение step_scal изменилось incf step_scal, f ; увеличиваем делитель, так как он переполнился call sound_err ; звук переполнения делителя goto return_from_i_b ; то выходим из обработки прерывания, значение step_scal не изменилось check_minus_btn btfsc btn_code, btn_minus; если не нажата кнопка минус, RB4 goto return_from_i_b ; то выходим из обработки прерывания ; нажата кнопка минус incf step_scal, f ; увеличиваем делитель и выходим btfss STATUS,Z ; проверяем на 0 goto return_from_i_sc; если не 0, то выходим из обработки прерывания от порта B, значение step_scal изменилось decf step_scal, f ; уменьшаем делитель, так как он переполнился call sound_err ; звук переполнения делителя goto return_from_i_b ; то выходим из обработки прерывания, значение step_scal не изменилось return_from_i_sc call sound_on ; нормальный звук нажатой кнопки movf step_scal, w ; текущее значение делителя шагов movwf cur_step ; текущее значение делителя шагов в счетчик шагов return_from_i_b ; banksel INTCON bcf INTCON, RBIF ;очистить флаг прерывания от порта B goto return_from_int timer_int movf step_scal, w ; текущее значение делителя шагов movwf cur_step ; текущее значение делителя шагов в счетчик шагов btfss PORTA, move_bit goto stop_move ; не установлен move_bit, останавливаемся btfss PORTA, dir_bit goto move_down ;dir = 0, движение вниз, btfsc PORTA, top_sw ;dir = 1, движение вверх, проверяем верхний выключатель goto stop_move_top ; при движении вверх верхний выключатель включен = 1, останавливаем goto get_bits ; все нормально, можно двигаться вверх move_down btfsc PORTA, bottom_sw ; dir = 0, движение вниз, проверяем нижний выключатель goto stop_move_bottom ; при движении вниз нижний выключатель включен = 1, останавливаем get_bits decfsz step, f ; уменьшим шаг goto next_step ; если не 0, то след. шаг movlw n_step ; иначе начнем шагать сначала (вернее с конца) последовательности movwf step ; step = n_step next_step clrw btfsc PORTA, dir_bit ; проверяем dir movlw n_step ; dir = 1, вверх, грузим из второй половины таблицы (+ n_step) addwf step, w ; вычисляем индекс в таблице call pin_table ; загружаем биты movwf PORTC ; display w on PORTC goto return_from_i_t stop_move ; не установлен move_bit, останавливаемся stop_move_top ; при движении вверх верхний выключатель включен = 0 stop_move_bottom ; при движении вниз нижний выключатель включен = 0 return_from_i_t ; banksel INTCON ; bcf INTCON,T0IF ;очистить флаг прерывания return_from_int ; movf pclath_temp,w ; retrieve copy of PCLATH register ; movwf PCLATH ; restore pre-isr PCLATH register contents ; movf status_temp,w ; retrieve copy of STATUS register ; movwf STATUS ; restore pre-isr STATUS register contents ; swapf w_temp,f ; swapf w_temp,w ; restore pre-isr W register contents retfie ; return from interrupt main ;1. запустить таймер ;2. ждать clrf PORTC ; clear PORTC clrf PORTA ; clear PORTA clrf is_sound ; звук выключен banksel TRISC CLRF TRISC^80H ; configure PORTC as all outputs MOVLW B'00000110' ; Configure all pins MOVWF ADCON1^80H ; as digital inputs movlw B'00111111' ; configure PORTA as all inputs bit0 - bit3 movwf TRISA^80H movlw B'11111111' ; configure PORTB as inputs bit5 - bit4 movwf TRISB^80H banksel n_step movlw n_step movwf step ; первый шаг = n_step, начало (вернее конец) последовательности call read_scl ; читаем делитель шагов из памяти btfsc STATUS, Z ; проверяем на 0 movlw start_scal ; если 0, то загружаем по умолчанию movwf step_scal ; начальное значение делителя шагов movwf cur_step ; начальное значение делителя шагов в счетчик шагов movlw B'00000000' | tmp_scal banksel OPTION_REG movwf OPTION_REG^80H ; таймер 0 предварительный делитель 256:1 bcf OPTION_REG^80H, NOT_RBPU ; включить слабую подтяжку на порту B banksel INTCON bsf INTCON, T0IE ; разрешить прерывания от таймера bsf INTCON, RBIE ; разрешить прерывания от порта B bsf INTCON, GIE ; разрешить все прерывания bsf INTCON, PEIE ; разрешить все другие прерывания goto $ ; infinite loop ; просмотр таблицы шагов pin_table addwf PCL,f ;добавить W к PCL для получ. адреса эл-та таблицы dt B'00' dt B'00', B'10', B'11', B'01' ;вперед dt B'00', B'01', B'11', B'10' ;назад ; *** фильтр дребезга контактов btn_ftl movlw btn_cycle ; кол-во циклов в устранении дребезга контактов movwf ncbtn ; счетчик цикла в в устранении дребезга контактов next_test movf PORTB, w ; читаем состояние кнопок subwf btn_code, w btfss STATUS, Z ; если состояние кнопок сохранилось, то продолжаем цикл retlw 0 ; иначе возвращаем 0 decfsz ncbtn, f goto next_test retlw 1 ; иначе возвращаем 1, так как во время цикла состояние кнопок не изменилось ; **** включение звука sound_on movlw tmr2_scal movwf T2CON ; включен таймер 2, управляет частотой звука clrf TMR1L clrf TMR1H movlw tmr1_scal movwf T1CON ; включен таймер 1, с делителем, управляет временем писка movlw 1 goto sound1 sound_err ; **** включение звука переполнения делителя movlw tmr2_sc_er movwf T2CON ; включен таймер 2, управляет частотой звука clrf TMR1L clrf TMR1H movlw tmr1_sc_er movwf T1CON ; включен таймер 1, с делителем, управляет временем писка movlw 1 goto sound1 sound_wr ; **** включение звука записи делителя movlw tmr2_sc_er movwf T2CON ; включен таймер 2, управляет частотой звука clrf TMR1L clrf TMR1H movlw tmr1_sc_er movwf T1CON ; включен таймер 1, с делителем, управляет временем писка movlw 2 ; в два раза длиннее ; goto sound1 sound1 movwf is_sound ; выставляем флаг включенного звука movlw pwm_per ; период ШИМ, управляет частотой звука banksel PR2 movwf PR2^80H bcf STATUS, C rrf PR2^80H, w ; делим цикл на 2 banksel CCPR1L movwf CCPR1L ; длительность импульса ШИМ movlw B'00001100' movwf CCP1CON ; включен режим ШИМ banksel PIE1 bsf PIE1^80H, TMR1IE ; разрешить прерывания от таймера 1 banksel is_sound return ; **** выключение звука sound_off bcf PIR1, TMR1IF ; очистить флаг прерывания decfsz is_sound, f return clrf CCP1CON ; выключен режим ШИМ bcf T2CON, TMR2ON ; выключить Timer2 bcf T1CON, TMR1ON ; выключить Timer1 return ; **** запомним делитель шагов write_scl banksel EECON1 bsf EECON1, WREN ; разрешить запись в EEPROM movlw scal_adr ; адрес, куда сохранять делитель banksel EEADR ; ???? movwf EEADR ; адрес, куда сохранять делитель banksel step_scal movf step_scal, w ; делитель banksel EEDATA movwf EEDATA ; делитель banksel EECON2 ; ????? movlw 55H movwf EECON2 movlw 0AAH movwf EECON2 ; banksel EECON1 bsf EECON1, WR ; начать запись ee_wr1 btfsc EECON1, WR ; запись завершена? goto ee_wr1 ; нет bcf EECON1,WREN ; запретить запись в EEPROM banksel step_scal return read_scl banksel scal_adr movlw scal_adr ; адрес, откуда брать делитель banksel EEADR ; movwf EEADR ; Address to read banksel EECON1 ; bsf EECON1, RD ; EE Read banksel EEDATA movf EEDATA, w ; W = EEDATA banksel scal_adr return END ; directive 'end of program'