Берем число (например, двухбайтовое целое), пока оно больше 10000 - вычитаем из него 10000, считаем сколько раз вычли в отдельном регистре. Прибавляем к значению регистра счетчика код символа "0" - получаем код символа нужной цифры текущего десятичного разряда - шлем его по USART на комп где в любой терминальной программе он выводится как десятичная цифра. Далее - в исходных регистрах у нас остался остаток меньше 10000 - начинаем вычитать из него 1000 и действуем по тому уже алгоритму.
Код:
.include "Tn13def.inc"
; функции ножек
.equ _PIN_PC_OUT = PB2 ; 7 - й PIN - вывод данных на комп
.equ _PIN_RELE_OUT1 = PB3 ; 2 - й PIN - выход на обмотку 1
.equ _PIN_RELE_OUT2 = PB1 ; 6 - й PIN - выход на обмотку 2
.equ _PIN_RELE_IN1 = PB4 ; 3 - й PIN - вход контакта 1
.equ _PIN_RELE_IN2 = PB0 ; 5 - й PIN - вход контакта 2
; регистры общего назначения
.def _r_mom = r16 ; служебный регистр для записи в порты
.def _r_WAIT = r17
.def _r_count_measure_sets = r18
.def _r_transmit_data = r19
.def _r_t1_lb = r20
.def _r_t1_hb = r21
.def _r_t1_last_lb = r22
.def _r_t1_last_hb = r23
.def _r_t2_lb = r24
.def _r_t2_hb = r25
.def _r_t2_last_lb = r26
.def _r_t2_last_hb = r27
.def _r_count_measure_time_lb = r28
.def _r_count_measure_time_hb = r29
; переменные
.equ _one_measure_time = 96 ; тактов = 10us - дискретность измерения
.equ _max_measure_time = 3000 ; циклов по 10us = 30ms - интервал измерения
.equ _sets_of_measures = 10 ; количество измерений (1 измерение = туда + обратно)
;.equ _RS232_time = 1000 ; тактов бита RS232 для скорости 9600 бод
.equ _RS232_time = 250 ; тактов бита RS232 для скорости 38400 бод
; для десятичной системы исчисления
.equ _SI_order = 10 ; для десятичной системы исчисления при выводе результатов
;.equ _max_length_SI = 5 ; макс. разрядность чисел в _SI_order
.equ _SI_order_0 = 1
.equ _SI_order_1 = _SI_order
.equ _SI_order_2 = _SI_order_1*_SI_order_1
.equ _SI_order_3 = _SI_order_2*_SI_order_1
.equ _SI_order_4 = _SI_order_3*_SI_order_1
; нужные коды KOI_8
.equ KOI_8_comma = $2C ; код ","
.equ KOI_8_semicolon = $3B ; код ";"
.equ KOI_8_ps = $0A ; код "перевод строки"
.equ KOI_8_vk = $0D ; код "возврат каретки"
;--------------------------------------------------------------------
rjmp RESET ; Reset Handler
reti ;rjmp EXT_INT0 ; IRQ0 Handler
reti ;rjmp PIN_CHANGE ; PCINT0 Handler
reti ;rjmp TIM0_OVF ; Timer0 Overflow Handler
reti ;rjmp EE_RDY ; EEPROM Ready Handler
reti ;rjmp ANA_COMP ; Analog Comparator Handler
reti ;rjmp TIM0_COMPA ; Timer0 CompareA Handler
reti ;rjmp TIM0_COMPB ; Timer0 CompareB Handler
reti ;rjmp WATCHDOG ; Watchdog Interrupt Handler
reti ;rjmp ADCCONV ; ADC Conversion Handler
;--------------------------------------------------------------------
RESET:
ldi _r_mom, (1<<_PIN_RELE_OUT1) + (1<<_PIN_RELE_OUT2) + (1<<_PIN_PC_OUT)
out DDRB, _r_mom ; пины выходов
ldi _r_mom, (1<<_PIN_RELE_IN1) + (1<<_PIN_RELE_IN2)
out PORTB, _r_mom ; подтягиваюие резисторы на входы
; ldi _r_mom, (1<<_PIN_RELE_IN1) + (1<<_PIN_RELE_IN2) + (1<<_PIN_RELE_OUT1) + (1<<_PIN_RELE_OUT2)
; out PCMSK, _r_mom ; маска пинов для прерываний по pin change
rjmp MAIN
;--------------------------------------------------------------------
; прерывание по изменению состояния выводов
;PIN_CHANGE:
; clr _r_mom
; out GIMSK, _r_mom
; reti
;--------------------------------------------------------------------
WAIT_Idle:
; для относительно длинных пауз
; ждем в состоянии Idle ПРИМЕРНО _r_WAIT / 37.5 секунд
cli
; clr _r_mom
; out GIMSK, _r_mom
in _r_mom, MCUCR
cbr _r_mom, (1<<SM0) + (1<<SM1)
sbr _r_mom, (1<<SE)
;ldi _r_mom, (0<<ISC00) + (0<<ISC01) + (0<<SM0) + (0<<SM1) + (1<<SE) + (0<<PUD)
out MCUCR, _r_mom ; задаем режим sleep-а Idle и устанавливаем флаг его разрешения
ldi _r_mom, 250
out OCR0A, _r_mom ; значение счетчика таймера для прерывания
clr _r_mom
out TCNT0, _r_mom ; сброс счетчика таймера
ldi _r_mom, (1<<OCIE0A)
out TIMSK0, _r_mom ; разрешаем прерывание по счетчику таймера
ldi _r_mom, (1<<CS02) + (0<<CS01) + (1<<CS00)
out TCCR0B, _r_mom ; запускаем таймер с частотой СК/1024
sei
WAIT_Idle_sleep:
sleep ; Idle
clr _r_mom
out TCNT0, _r_mom ; сброс счетчика таймера
dec _r_WAIT
brne WAIT_Idle_sleep
cli
in _r_mom, MCUCR
cbr _r_mom, (1<<SE)
out MCUCR, _r_mom ; сбрасываем флаг разрешения sleep-а
clr _r_mom
out TIMSK0, _r_mom ; запрещаем прерывание по счетчику таймера
out TCCR0B, _r_mom ; останавливаем таймер
out OCR0A, _r_mom ; нулевое значение счетчика таймера для прерывания
out TCNT0, _r_mom ; сброс счетчика таймера
ret
;;--------------------------------------------------------------------
;
;EEPROM_write:
;
; sbic EECR, EEPE
; rjmp EEPROM_write
;
; ; Set Programming mode
; ldi _r_mom, (0<<EEPM1)|(0<<EEPM0)
; out EECR, _r_mom
;
; out EEARL, _r_EEPROM_address
; out EEDR, _r_EEPROM_data
; sbi EECR, EEMPE
; sbi EECR, EEPE
; ret
;;--------------------------------------------------------------------
;
;EEPROM_read:
;
; sbic EECR, EEPE
; rjmp EEPROM_read
; out EEARL, _r_EEPROM_address
; sbi EECR, EERE
; in _r_EEPROM_data, EEDR
; ret
;;--------------------------------------------------------------------
DECIMAL_value_KOI_8:
; _r_t1_hb:_r_t1_lb - остаток от числа, _SI_order-ичный разряд которого надо получить
; _r_count_measure_time_hb:_r_count_measure_time_lb - число _SI_order в нужной степени
; результат - в _r_transmit_data (и уже +$30 к цифре для получения её кода KOI-8)
clr _r_transmit_data
DECIMAL_value_KOI_8_1:
cp _r_t1_lb, _r_count_measure_time_lb
cpc _r_t1_hb, _r_count_measure_time_hb
brlo DECIMAL_value_KOI_8_END
sub _r_t1_lb, _r_count_measure_time_lb
sbc _r_t1_hb, _r_count_measure_time_hb
inc _r_transmit_data
rjmp DECIMAL_value_KOI_8_1
DECIMAL_value_KOI_8_END:
ldi _r_mom, $30 ; свиг для кода KOI-8 цифры
add _r_transmit_data, _r_mom
ret
;--------------------------------------------------------------------
PAUSE_RS232:
; _RS232_time - 13 (тактов на операции при передаче) - 5 (вызов rcall) - 2 (ret)
; итог / 5 (тактов каждого цикла) = в счетчик
ldi _r_mom, (_RS232_time - 13 - 5 - 2) / 5;
PAUSE_RS232_Count:
dec _r_mom
nop
nop
brne PAUSE_RS232_Count
ret
;--------------------------------------------------------------------
DATA_transmit_RS232:
; содержимое _r_transmit_data - на комп по RS232
cli
; отправляем посылку RS232
; 13 тактов операций после каждого бита + (_RS232_time - 9) тактов паузы
cbi PORTB, _PIN_PC_OUT ; старт-бит, до сего момента у нас была 1-ца и долго
rcall PAUSE_RS232
nop
nop
nop
nop
nop
nop
; 8 бит данных и 2 стоп бита = 10 бит
; для посылки их в одном цикле инвертируем байт даннных
; и будем посылать инвертированные биты - 2 стоп бита получатся
; как инвертированные нули при логическом сдвиге вправо :)
com _r_transmit_data
ldi _r_WAIT, 10
DATA_transmit_RS232_DATA_BITS:
sbrc _r_transmit_data, 0
cbi PORTB, _PIN_PC_OUT
sbrs _r_transmit_data, 0
sbi PORTB, _PIN_PC_OUT
rcall PAUSE_RS232
nop
nop
nop
nop
lsr _r_transmit_data
dec _r_WAIT
brne DATA_transmit_RS232_DATA_BITS
; по хорошему, надо было запомнить флаг прерывания в начале процедуры
; и вернуть его такой же в конце. Но мы не будем возвращать
; - по умолчанию живем без прерываний, если надо - выставим когда надо
;sei
ret
;--------------------------------------------------------------------
VALUE_transmit_RS232:
;_r_t1_hb:_r_t1_lb - на комп по RS232 в десятичной системе с запятой
;_r_count_measure_time_hb:_r_count_measure_time_lb - _SI_order
;_r_transmit_data - который потом шлем на комп по RS232
ldi _r_count_measure_time_lb, low(_SI_order_4)
ldi _r_count_measure_time_hb, high(_SI_order_4)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
ldi _r_count_measure_time_lb, low(_SI_order_3)
ldi _r_count_measure_time_hb, high(_SI_order_3)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
ldi _r_count_measure_time_lb, low(_SI_order_2)
ldi _r_count_measure_time_hb, high(_SI_order_2)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
; запятая
ldi _r_transmit_data, KOI_8_comma
rcall DATA_transmit_RS232
ldi _r_count_measure_time_lb, low(_SI_order_1)
ldi _r_count_measure_time_hb, high(_SI_order_1)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
ldi _r_count_measure_time_lb, low(_SI_order_0)
ldi _r_count_measure_time_hb, high(_SI_order_0)
rcall DECIMAL_value_KOI_8
rcall DATA_transmit_RS232
ret
;--------------------------------------------------------------------
MEASURE_transmit_RS232:
; вынес в процедуру ИСКЛЮЧИТЕЛЬНО из-за ограничения количества строк перехода!
;_r_t1_hb:_r_t1_lb - на комп по RS232 в десятичной системе с запятой
;_r_count_measure_time_hb:_r_count_measure_time_lb - _SI_order
;_r_transmit_data - который потом шлем на комп по RS232
; сначала рассчитаем и приготовим все нужные рагистры для передачи
; а потом уже передадим - при передаче они корежатся :)
; расчет и запись ti_last как разница ti_lastт - ti
cp _r_t1_last_lb, _r_t1_lb
cpc _r_t1_last_hb, _r_t1_hb
brlo _clr_r_t1_last
sub _r_t1_last_lb, _r_t1_lb
sbc _r_t1_last_hb, _r_t1_hb
rjmp _calc_r_t1_last_end
_clr_r_t1_last:
clr _r_t1_last_lb
clr _r_t1_last_hb
_calc_r_t1_last_end:
cp _r_t2_last_lb, _r_t2_lb
cpc _r_t2_last_hb, _r_t2_hb
brlo _clr_r_t2_last
sub _r_t2_last_lb, _r_t2_lb
sbc _r_t2_last_hb, _r_t2_hb
rjmp _calc_r_t2_last_end
_clr_r_t2_last:
clr _r_t2_last_lb
clr _r_t2_last_hb
_calc_r_t2_last_end:
; тут в _r_t1_hb:_r_t1_lb уже нужное число для передачи - ничего не делаем
rcall VALUE_transmit_RS232
ldi _r_transmit_data, KOI_8_semicolon
rcall DATA_transmit_RS232 ; символ ";"
mov _r_t1_lb, _r_t1_last_lb
mov _r_t1_hb, _r_t1_last_hb
rcall VALUE_transmit_RS232
ldi _r_transmit_data, KOI_8_semicolon
rcall DATA_transmit_RS232 ; символ ";"
mov _r_t1_lb, _r_t2_lb
mov _r_t1_hb, _r_t2_hb
rcall VALUE_transmit_RS232
ldi _r_transmit_data, KOI_8_semicolon
rcall DATA_transmit_RS232 ; символ ";"
mov _r_t1_lb, _r_t2_last_lb
mov _r_t1_hb, _r_t2_last_hb
rcall VALUE_transmit_RS232
; счетчик измерений считает от 0 вверх
; если он четный - в конце передаем ";", иначе переход на новую строку
sbrc _r_count_measure_sets, 0
rjmp MEASURE_transmit_RS232_SEND_TO_PC_new_line
ldi _r_transmit_data, KOI_8_semicolon
rcall DATA_transmit_RS232 ; символ ";"
rjmp MEASURE_transmit_RS232_END
MEASURE_transmit_RS232_SEND_TO_PC_new_line:
ldi _r_transmit_data, KOI_8_ps
rcall DATA_transmit_RS232
ldi _r_transmit_data, KOI_8_vk
rcall DATA_transmit_RS232 ; переход на новую строку
MEASURE_transmit_RS232_END:
ret
;--------------------------------------------------------------------
INITIALIZE_MEASURE:
; вынес в процедуру ИСКЛЮЧИТЕЛЬНО из-за ограничения количества строк перехода!
clr _r_count_measure_time_lb
clr _r_count_measure_time_hb
ser _r_t1_lb
ser _r_t1_hb
ser _r_t2_lb
ser _r_t2_hb
; используем тут _r_transmit_data для начального состояния входов
in _r_transmit_data, PINB
in _r_mom, MCUCR
cbr _r_mom, (1<<SM0) + (1<<SM1)
sbr _r_mom, (1<<SE)
out MCUCR, _r_mom ; задаем режим sleep-а Idle и устанавливаем флаг его разрешения
ldi _r_mom, _one_measure_time - 1 - 8 - 4 - 8
out OCR0A, _r_mom ; значение счетчика таймера для прерывания
; 1 - на установку флага прерывания
; 8 - на переход на адрес вектора прерывания
; 4 - на reti с адреса вектора прерывания
; 8 = 2 на сброс счетчика таймера + 6 на проверку условия цикла
clr _r_mom
out TCNT0, _r_mom ; сброс счетчика таймера
ldi _r_mom, (1<<OCIE0A)
out TIMSK0, _r_mom ; разрешаем прерывание по счетчику таймера
ldi _r_mom, (0<<CS02) + (0<<CS01) + (1<<CS00)
out TCCR0B, _r_mom ; запускаем таймер с частотой СК
sei
ret
;--------------------------------------------------------------------
MAIN:
cbi PORTB, _PIN_RELE_OUT1
cbi PORTB, _PIN_RELE_OUT2
sbi PORTB, _PIN_PC_OUT ; инициализируем передачу на комп
; ldi _r_mom, (1<<PCIE)
; out GIMSK, _r_mom ; разрешаем прерыванияпо изменению состояния выводов
; in _r_mom, MCUCR
; cbr _r_mom, (1<<SM0)
; sbr _r_mom, (1<<SM1) + (1<<SE)
; ;ldi _r_mom, (0<<ISC00) + (0<<ISC01) + (0<<SM0) + (0<<SM1) + (1<<SE) + (0<<PUD)
; out MCUCR, _r_mom ; задаем режим sleep-а Power-down и устанавливаем флаг его разрешения
; sei
; sleep ; Power-down
; тут проснемся от прерывания PIN_CHANGE
ldi _r_WAIT, 20 ; 37.5 Гц
rcall WAIT_Idle ; ждать 1s
; инициализировать контакты реле
sbi PORTB, _PIN_RELE_OUT2
ldi _r_WAIT, 6 ; 37.5 Гц
rcall WAIT_Idle ; ждать 250ms
cbi PORTB, _PIN_RELE_OUT2
ldi _r_WAIT, 6 ; 37.5 Гц
rcall WAIT_Idle ; ждать 250ms
; провести серию измерений, записывая результаты каждого в EEPROM
clr _r_count_measure_sets
SET_OF_MEASURES:
rcall INITIALIZE_MEASURE
; команда на переключение - инвертируем _PIN_RELE_OUT1
ldi _r_WAIT, (1<<_PIN_RELE_OUT1)
in _r_mom, PORTB
eor _r_mom, _r_WAIT
out PORTB, _r_mom
; измеряем t1 и t2
MEASURE: ; опрос пинов строго раз в 10us = 96 тактов
; таких опросов строго до достижения _max_measure_time
clr _r_mom
out TCNT0, _r_mom ; сброс счетчика таймера
; тут у нас есть на все наши операции максимум 96 - 1 - 8 - 4 - 8 = 75 тактов!
; ldi _r_mom, 25
;a3: dec _r_mom
; brne a3
; ; 75 тактов
; ;nop
; ;nop
; nop
ldi _r_mom, 1
clr _r_WAIT
add _r_count_measure_time_lb, _r_mom
adc _r_count_measure_time_hb, _r_WAIT ; инкремент счетчика
; манипуляции с _r_mom, после которых в битах _PIN_RELE_IN1 и _PIN_RELE_IN2
; будут 1-цы только если текущие значения не равны начальным из _r_transmit_data
in _r_mom, PINB
eor _r_mom, _r_transmit_data
; старшие байты _r_t1_hb и _r_t2_hb изначально забиты 1-цами
; а после занесения в них значений измерения - гарантированно меньше,
; т.к. максимальное значение у нас будет 3000 а возможное - 65536
cpi _r_t1_hb, 255
brne MEASURE_t1_END ; пропускаем если _r_t1_hb <> 255 - уже заполнили
sbrs _r_mom, _PIN_RELE_IN1
rjmp MEASURE_t1_END ; пропускаем если состояние не изменилось
mov _r_t1_lb, _r_count_measure_time_lb
mov _r_t1_hb, _r_count_measure_time_hb
MEASURE_t1_END:
cpi _r_t2_hb, 255
brne MEASURE_t2_END ; пропускаем если _r_t2_hb <> 255 - уже заполнили
sbrs _r_mom, _PIN_RELE_IN2
rjmp MEASURE_t2_END ; пропускаем если состояние не изменилось
mov _r_t2_lb, _r_count_measure_time_lb
mov _r_t2_hb, _r_count_measure_time_hb
MEASURE_t2_END:
; каждый цикл записываем тек. время если состояние вывода РАВНО исходному
; - так отследим время окончания дребезга
sbrc _r_mom, _PIN_RELE_IN1
rjmp MEASURE_t1_last_END ; пропускаем если состояние изменилось
mov _r_t1_last_lb, _r_count_measure_time_lb
mov _r_t1_last_hb, _r_count_measure_time_hb
MEASURE_t1_last_END:
sbrc _r_mom, _PIN_RELE_IN2
rjmp MEASURE_t2_last_END ; пропускаем если состояние изменилось
mov _r_t2_last_lb, _r_count_measure_time_lb
mov _r_t2_last_hb, _r_count_measure_time_hb
MEASURE_t2_last_END:
; заняли максимум 40 тактов
sleep
ldi _r_mom, low(_max_measure_time)
ldi _r_WAIT, high(_max_measure_time)
cp _r_count_measure_time_lb, _r_mom
cpc _r_count_measure_time_hb, _r_WAIT
brlo MEASURE
; прошли _max_measure_time - сделали очередное измерение
cli
in _r_mom, MCUCR
cbr _r_mom, (1<<SE)
out MCUCR, _r_mom ; сбрасываем флаг разрешения sleep-а
clr _r_mom
out TCCR0B, _r_mom ; останавливаем таймер
out TCNT0, _r_mom ; сброс счетчика таймера
ldi _r_WAIT, 6 ; 37.5 Гц
rcall WAIT_Idle ; ждать 250ms
; команда на исходное положение - инвертируем _PIN_RELE_OUT2
ldi _r_WAIT, (1<<_PIN_RELE_OUT2)
in _r_mom, PORTB
eor _r_mom, _r_WAIT
out PORTB, _r_mom
; передать результаты на комп
rcall MEASURE_transmit_RS232
ldi _r_WAIT, 6 ; 37.5 Гц
rcall WAIT_Idle ; ждать 250ms
; если ещё не все измерения сделаны (а одно измерение - это сначала туда а потом обратно)
; то сделаем ещё измерение
inc _r_count_measure_sets
cpi _r_count_measure_sets, 2*_sets_of_measures
brlo SET_OF_MEASURES
; провели серию измерений и передали результаты на комп -
; готовы к новой серии измерений (на старт и PowerDown)
;rjmp MAIN
; нет - тут падаем в Power-down без права прерывания и ждем RESET :)))
SLEEP_Power_down:
cli
in _r_mom, MCUCR
cbr _r_mom, (1<<SM0)
sbr _r_mom, (1<<SM1) + (1<<SE)
;ldi _r_mom, (0<<ISC00) + (0<<ISC01) + (0<<SM0) + (0<<SM1) + (1<<SE) + (0<<PUD)
out MCUCR, _r_mom ; задаем режим sleep-а Power-down и устанавливаем флаг его разрешения
sleep ; Power-down
rjmp SLEEP_Power_down