PIC-программа для цифрового дозиметра

; Публикую здесь, ибо - поэма! На языке Ассемблер.
; Вам, братья-радиолюбители. Дарю!
; Можете сразу ассемблировать в MPASMWIN, ошибок 0,
; всё это прекрасно работает в маленькой коробочке, давно и надёжно.

; К примеру, едва повышенный фон внутри хрустальной вазы отмечается звуковым "бип" уже в первые 10... 15 секунд.
; При сколько-нибудь существенных уровнях время реакции - около секунды,
; это лучше, чем у опытного дозиметриста с аналоговым "щелкунчиком".

; Схема ясна из назначения выводов контроллера.

; #####################################
; ##  Программа цифрового дозиметра  ##
; ##  © Каёткин Евгений Борисович    ##
; ##  Снежинск, 27 Января 2016 г.    ##
; #####################################

; Вариант для СБТ-11 (ок. 50 и/мкР)
; Тактирование контроллера от встроенного кварцевого генератора, частота 6.0 МГц.

; Задача: обеспечить статистически-достаточное время интеграции,
; но при этом быструю реакцию прибора на внезапное повышение уровня

; Этого можно достичь постоянно включённой звуковой индикацией,
; как на аналоговых "щелкунчиках", либо применением большого датчика.
; То и другое крайне неудобно, желателен прибор миниатюрный, скромно
; лежащий в кармане и подающий голос только при необходимости.

; Решение: каждую секунду будем иметь минутную статистику, для этого
; минутный измерительный интервал ежесекундно сдвигаем,
; самый старый секундный отсчёт заменяя свежим.

; Подпрограммы:
; счёт импульсов
; вычисление текущей мощности дозы
; индикация текущей мощности дозы
; вычисление, хранение, индикация интегральной дозы
; циклический сдвиг статистического массива
; вывод данных на дисплей
; чтение с дисплея
; звуковые сигналы
; световые сигналы
; управление высоковольтным преобразователем
; клавиатура (кнопки)
; обработка аварийных ситуаций

;
errorlevel-302
LIST    p=16F84 ; 16F84A is the target processor
#INCLUDE P16F84.INC
;   Set configuration bits using definitions from the include file, p16f84.inc
__config _HS_OSC & _PWRTE_ON & _WDT_OFF & _CP_OFF
__idlocs 0xFFFF ; Set User ID Memory

;
;registers:

cblock  0CH ; MIN REG ADDR = 0CH, MAX REG ADDR = 4Fh
FSRSAVE ; здесь храним указатель для косвенной адресации РОНов
SNDSTAT ; состояние звукового сигнала: 0 - отключен, 1 - включен, 2 - режим поиска
TEMP_REG ; символ для вывода на дисплей, счёт цикла измерения, BIN2DEC etc

NIZLOG
VERHLOG ; 10H

LPULSCNT ; здесь храним микрорентгены в час до вывода на индикатор
MPULSCNT ; то же, средний байт
HPULSCNT ; то же, старший байт

DOSECNTL ; здесь храним все импульсы, зарегистрированные с момента включения
DOSECNTM ; средний
DOSECNTH ; высокий

DEC_L ; в двух тетрадах единицы и десятки
DEC_M ; в двух тетрадах сотни и тысячи
DEC_H ; в младшей тетраде десятки тысяч

P_COUNT ; здесь считаем прерывания, число циклов, скроллинг и др.
MILLI ; средний, до 255 х 255 циклов, период
MICRO ; малый, до 255 циклов, период

STATUS_TEMP ; два регистра хранения состояния процессора
W_TEMP ; для корректного возврата из прерывания

LPLSSAVE
MPLSSAVE ; 20H
HPLSSAVE

LREGSAVE
MREGSAVE
HREGSAVE

DATATOWR
CONF_ADDR ; REG26H

LEDSAV ; LED STATUS
MKR ; счёт пакетов импульсов (50 импульсов = 1 микрорентген для СБТ-11, грубо)

REG29H
REG2AH
REG2BH
REG2CH
REG2DH ; ниже 18 пар регистров

REG2EH
REG2FH

FIRSTFREE ; REG30H, ниже - 16 пар
REG31H ; FIRSTFREE+1
REG32H

REG33H
REG34H

REG35H
REG36H

REG37H ; а здесь осталось .24 (18H) = .12 пар

endc

;


#####

#DEFINE MAXRAM 4FH
#DEFINE LEDSTAT LEDSAV,0
#DEFINE CARRY STATUS,0 ; CARRY bit in STATUS register
#DEFINE ZERO STATUS,Z
#DEFINE SOUNDON SNDSTAT,0 ; звук включен, если 1
#DEFINE SEARCH SNDSTAT,1 ; поиск, если 1
#DEFINE BLINK SNDSTAT,2 ; используется в подпрограмме мерцания надписи о превышении МД
#DEFINE BLINKD SNDSTAT,3 ; используется в подпрограмме мерцания надписи  о превышении ИД
#DEFINE FONFLAG SNDSTAT,4 ; флаги, используемые для того,
#DEFINE TEHFLAG SNDSTAT,5 ; чтобы при превышении заданных уровней
#DEFINE MAXFLAG SNDSTAT,6 ; звуковой сигнал раздавался однократно и
#DEFINE SOSFLAG SNDSTAT,7 ; прибор не пищал постоянно, как идиот
#DEFINE RAZMER SNDSTAT,2 ; здесь храним код размерности - милли (1) или микро (0)

;входы:
#DEFINE GAYGER PORTA,2 ; - сигнал от счётчика Гейгера, "1", длительность 200 мкс, мёртвое время 400 мкс
#DEFINE KNOPMODE  PORTB,5 ; - кнопка режима, активный уровень - "0"
#DEFINE GAYGER2 PORTB,7 ; вход для счётчика Гейгера 2
#DEFINE BUSYFLAG PORTB,3 ; - флаг занятости ЖКИ
#DEFINE LED PORTB,4 ; выход для светодиода и(ли) управления в/вольтным питанием

;выходы:
; ; ## !!! ВНИМАНИЕ! старший полубайт ЖКИ соединён с МЛАДШИМ полубайтом PORTB !!!      ##
; ## нибблы аппаратно переставлены из-за конфликта LCD с on-board PIC-программатором ##
;
#DEFINE RS PORTA,0 ; - команда (1) / данные (0) для дисплея
#DEFINE EN PORTA,1 ; - CS дисплея (единицей! <= вот он и конфликт: дисплей захватывает шину данных)
#DEFINE RW PORTA,3 ; - сигнал чтение / запись дисплея
;#DEFINE VIBRO PORT?,? ; - управление виброзвонком
#DEFINE SOUND PORTA,4 ; - звуковой сигнал (SOS)
TECHFON EQU .60
MAXFON EQU .120 ; макс 1 р/год, 2732 мкр/сутки, 114/час, 1.9/мин, 0,0317/с - уровень реагирования
; в Зиверты можно перевести позже, поставив десятичную точку в нужном месте
IMPNAMKR EQU .50 ; паспортная чувствительность, имп. на микрорентген
PASPFON EQU .32 ; MAX паспортный фон
REGCOUNT EQU MAXRAM - (FIRSTFREE - 1) ; число свободных РОН
TUNMD EQU .60 ; тюнинг МД, время между скроллингами регистровых пар, if TUNMD>> then MD>>
IVPKT EQU .180 ; тюнинг ИД, импульсов в пакете учёта ИД, if IVPKT>>, then ID<<.

;


#####

ORG 0
BEGIN: CLRF INTCON
CLRWDT
  CLRF TMR0
GOTO MAIN
; ORG 4
GOTO EVERINT

; #########################
; ##  Инициализация PIC  ##
; #########################

TABLE: ADDWF PCL,F  ; W содержит смещение от начала таблицы.
VCHAS: RETLW 1H ; ----Ш ; нарисуем символ /h - код 0
RETLW 15H ; Ш-Ш-Ш
RETLW       11H ; Ш---Ш
RETLW 1CH ; ШШШ--
RETLW       12H ; Ш--Ш-
  RETLW 12H ; Ш--Ш-
  RETLW       12H ; Ш--Ш-
RETLW 0H ; -----
DYN: RETLW 1 ; ----Ш ; нарисуем символ "звук включен" - код 1
RETLW 3 ; ---ШШ
RETLW       1DH ; ШШШ-Ш
RETLW 15H ; Ш-Ш-Ш
RETLW       15H ; Ш-Ш-Ш
  RETLW 1DH ; ШШШ-Ш
  RETLW       3 ; ---ШШ
RETLW 1 ; ----Ш
oe: RETLW 0 ; ----- ; нарисуем символ "oe" - код 3
RETLW 0AH ; -Ш-Ш-
RETLW       0 ; -----
RETLW 0EH ; -ШШШ-
RETLW       11H ; Ш---Ш
  RETLW 11H ; Ш---Ш
  RETLW       0EH ; -ШШШ-
RETLW 0 ; -----
RETLW 'S'
RETLW     'E'
RETLW     'R'
RETLW     'G'
RETLW     'E'
RETLW     'A'
RETLW     'N'
RETLW 'T'
RETLW '2'
RETLW '5'
RETLW     '0'
RETLW     '8'
RETLW     '1'
RETLW     '9'
RETLW     '5'
RETLW     '2'
RETLW 0E0H
RETLW     6FH
RETLW     0B7H
RETLW     0B8H
RETLW     0BCH
RETLW     65H
RETLW     0BFH
RETLW     70H
RETLW       4BH
RETLW     61H
RETLW     0B5H
RETLW     0BFH
RETLW     0BAH
RETLW     0B8H
RETLW     0BDH
RETLW     61H
MAIN: BSF STATUS,RP0
CLRF OPTION_REG^80
MOVLW 4
MOVWF TRISA^80 ; PORTA = OUTPUT, кроме A2 - это вход сигнала от счётчика Гейгера
MOVLW 0A0H
MOVWF TRISB^80 ; PORTB: разряды 7 и 5 - входы, остальные - выходы
BCF STATUS,RP0
CALL LCDINIT

CLEARAM:MOVLW 50H - TEMP_REG ; обнулим все РОН, количество РОН для очистки
MOVWF FSRSAVE
CLR: MOVF FSRSAVE,W ; вычислить текущий РОН, прибавив к номеру адрес очищаемого 
ADDLW TEMP_REG
MOVWF FSR
CLRF INDF ; обнулим этот ИНДирект Файл - непрямым образом выбранный регистр
DECFSZ FSRSAVE,F
GOTO CLR

BLOWRAM:MOVLW .7 ; задать пока фоновые значения, чтобы было что считать.
MOVWF LREGSAVE
MOVLW FIRSTFREE+2
MOVWF FSR
MOVLW .248
BLR: INCF INDF,F
INCF FSR,F
INCF FSR,F
ADDLW 1
BTFSS ZERO
GOTO BLR

MOVLW 1
MOVWF SNDSTAT ; включим звук и очистим флаги звуковой сигнализации
ZGEN: MOVLW .24 ; 3 символа по 8 байт = 24 ячейки сейчас занимает собственный знакогенератор
MOVWF P_COUNT
MOVLW 40H ; нарисуем свои символы
CALL CMDTOLCD
CLRF FSRSAVE
CALL ZG
MOVLW .8
MOVWF P_COUNT
MOVLW       80H     ; Команда перехода на первую строку. Индикация начнётся с первой матрицы
CALL ZG-1
MOVLW .8
MOVWF P_COUNT
movlw 0C0H ; Команда перехода на вторую строку, в младшей тетраде - количество пропущенных знакомест
CALL ZG-1

SILEN: BTFSC KNOPMODE ; При нажатой в момент включения кнопке "Режим" обнулить интегральную дозу.
GOTO SILENCE
MOVLW .8
MOVWF P_COUNT
movlw       80H     ; Команда перехода на первую строку. Индикация начнётся с первой матрицы
CALL ZG-1
MOVLW .8
MOVWF P_COUNT
movlw 0C0H ; Команда перехода на вторую строку, в младшей тетраде - количество пропущенных знакомест
CALL ZG-1
CALL TIRE
CALL TCK ; "К" - как дела?
CALL TIRE
CALL CLEAROM

SILENCE:BTFSS KNOPMODE
GOTO SILENCE
CLRF CONF_ADDR ; прочитаем прошлую интегральную дозу
CALL READ
MOVWF DOSECNTL ; и поместим в текущие регистры её учёта
INCF CONF_ADDR,F
CALL READ
MOVWF DOSECNTM
INCF CONF_ADDR,F
CALL READ
MOVWF DOSECNTH
CALL INI_TMR ; инициализация режима прерываний от RTCC
GOTO CG3

; ##########################
; ##  Измерительный цикл  ##
; ##########################

; максимальное выходное число от прерывания до прерывания, за 2с, = 4000 (примерно 1000H)
; со счётчиком СБТ-11, д.быть 0.144 Р/ч = 1.44 мЗв/ч
; Выше - токовый режим, уровень "1" постоянно.
; Максимум для ЖКИ = 0FFFFH, 65.536 единиц, программа DIG5ONLCD индицирует три старших разряда
; Десятичная точка сдвигается автоматически; если она есть, то с ней четыре знакоместа, иначе три

CG1: BTFSS GAYGER ; Г ?
GOTO CG1 ; нет - ждать импульс
BSF LEDSTAT ; флаг "мигнуть светодиодом"
CG2: BTFSC GAYGER ; L ?
GOTO CG2 ; нет - ждать спад импульса
BTFSC SEARCH ; да - потратить мёртвое время счётчика на вычисления
CALL DZIN ; дзынькнуть, если задан режим поиска
DECFSZ MKR,F
GOTO ID_PLUS
MOVLW IVPKT ; импульсов в пакете учёта ИД, тюнинг ИД - этой константой
MOVWF MKR
CG3: INCFSZ DOSECNTL,F ; учесть пакет IVPKT импульсов (ок. 4 mkR)  в интегральную дозу
GOTO ID_PLUS
INCFSZ DOSECNTM,F
GOTO ID_PLUS
INCFSZ DOSECNTH,F ; уже инкрементируем самый главный байт учёта дозы;
GOTO ID_PLUS ; если даже он переполнился (67.108864 R = .67108864 Sv), то 'Overflow'
CALL CLEAROM
GOTO SOSLOOP

ID_PLUS:INCFSZ FIRSTFREE,F ; был импульс, учесть его в младший байт
GOTO CG1 ;
INCF FIRSTFREE+1,F ; при переполнении увеличить старший байт
GOTO CG1 ; из этого цикла выходим только по прерываниям


; #########################
; ##  Главная программа  ##
; #########################

EVERINT:MOVWF W_TEMP ; PUSH: Copy W to TEMP register, 
SWAPF STATUS, W ; Swap status to be saved into W
MOVWF STATUS_TEMP ; Save status to STATUS_TEMP register
BTFSS INTCON,T0IF ; проверим флаг срабатывания прерываний от RTCC
GOTO POP ; если нет - уходим

BTFSC LED ; светодиод горит?
BCF LED ; да, погасить его
BTFSC LEDSTAT ; флаг установлен?
BSF LED ; да, зажечь светодиод
BCF LEDSTAT ; погасить флаг

CALL KEY
CALL INTMR ; если да, то снова загружаем число в TMR0
BCF INTCON,T0IF ; сбрасываем флаг срабатывания
DECFSZ FSRSAVE,F ; считаем срабатывания таймера, если 2-с истекли, то
GOTO POP ; идём работать над результатами измерений, нет - уходим обратно
BCF INTCON,GIE ; запретим прерывания
CALL LGIND ; двоичный логарифмический индикатор выходного уровня

; сохраним в REG SAVE результат предыдущего скроллинга, он в PULS SAVE

BCF CARRY
RRF LPLSSAVE,W
MOVWF LREGSAVE ; ополовиним предыдущий результат ради коэффициента 1,5
BCF CARRY ; и возможности в том числе нечётных последних цифр

RRF MPLSSAVE,W
MOVWF MREGSAVE
BCF CARRY

RRF HPLSSAVE,W
MOVWF HREGSAVE
BCF CARRY

MOVF FIRSTFREE+1,W ; нет смысла делать скроллинг при >128 имп./с
BTFSS ZERO
GOTO NOSCROL
MOVF FIRSTFREE,W ; и даже при >= 16 имп./с (> 1.15 мР/ч)
ANDLW 0E0H
BTFSS ZERO
GOTO NOSCROL

MOVF FIRSTFREE,W
ANDLW 0FCH ; при > 3 имп./с (> 216 mkR/h) укороченный до 4-х пар скроллинг
BTFSC ZERO
GOTO SCROLL

MOVF FIRSTFREE,W
MOVWF FIRSTFREE+.24
MOVF FIRSTFREE+1,W
MOVWF FIRSTFREE+.25

MOVLW .4
MOVWF P_COUNT
CALL READRAM
GOTO NSK6

; прямой расчёт, в регистрах FIRSTFREE, FIRSTFREE+1 - счёт импульсов за 1 цикл (2с) умножить на 18, будет "18 пар".
; Если счёт импульсов (СИ) за 2-с фрейм умножить на 1800 фреймов в час, получим импульсы в час (3600 с).
; Теперь разделим на чувствительность (50 имп/мкр), получим микроРентгены в час.
; Итак, мощность дозы МД = СИ * 1800 / 50 = СИ * 36, или МД = СИ * 18 * 2.
; Умножение на 18 уже имеем: регистров в этом примере я специально взял 18 пар.
; Если уровень статистически достаточный, для ускорения реакции прибора применён укороченный скроллинг
; с последующим доумножением до 18-ти.
; Чувствительность счётчика может отличаться от 50 имп/мкР, произвольно взятых для пояснения идеи вычисления,
; как и число регистровых пар. На деле регистров 16 пар, а дальнейшее умножение на нужный коэффициент
; сделано за счёт сложения с половиной предыдущего счёта и прочей юридически обоснованной уголовщины.
; В частности, "успокоение" прибора после резкого всплеска не должно длиться целую минуту, это неприемлемо долго.
; Поэтому для естественного поведения цифр на дисплее применены некоторые вычислительные хитрости - см. далее.
; Для калибровки под конкретный счётчик следует подобрать константы TUNMD и IVPKT.

NOSCROL:MOVF FIRSTFREE,W
MOVWF LPULSCNT
MOVF FIRSTFREE+1,W
MOVWF MPULSCNT
CLRF HPULSCNT

CALL U4N
CALL NOCHPAAR ; КОМПЕНСИРОВАТЬ ПРОСЧЁТЫ НА ВЫСОКИХ УРОВНЯХ!!!
CALL NOCHPAAR

NSK6: CALL U4N ; 4пары * 4 = 16пар
CALL NOCHPAAR ; для NSK6 (4п*4)+1п+1п=18пар, для NOSCROL - 20пар, для SCROLL - 16пар.
CALL NOCHPAAR
GOTO DEAL

SCROLL: MOVLW .16 ; REGCOUNT / 2
MOVWF P_COUNT ; суммируем импульсы из 16-ти пар
CALL READRAM

DEAL: MOVF LPULSCNT,W ; сохраним новые вычисления для следующего прохода в PULS SAVE
MOVWF LPLSSAVE
MOVF MPULSCNT,W
MOVWF MPLSSAVE
MOVF HPULSCNT,W
MOVWF HPLSSAVE

MOVF LREGSAVE,W ; и прибавим (результат предыдущего цикла)/2 из REG SAVE
ADDWF LPULSCNT,F
BTFSC CARRY
INCF MPULSCNT,F
MOVF MREGSAVE,W
ADDWF MPULSCNT,F
BTFSC CARRY
INCF HPULSCNT,F
MOVF HREGSAVE,W
ADDWF HPULSCNT,F

CLRF CONF_ADDR ; сохраним ИД в ПЗУ
MOVF DOSECNTL,W
MOVWF DATATOWR
CALL WRITE
INCF CONF_ADDR,F
MOVF DOSECNTM,W
MOVWF DATATOWR
CALL WRITE
INCF CONF_ADDR,F
MOVF DOSECNTH,W
MOVWF DATATOWR
CALL WRITE

; проверим, есть ли импульсы от счётчика, не авария ли

MOVF LPULSCNT,W
IORWF MPULSCNT,W
IORWF HPULSCNT,W
BTFSS ZERO
GOTO DONE
BTFSC GAYGER
CALL LHIGH ; уровень "1" постоянно
BTFSS GAYGER
CALL LLOW ; уровень "0" постоянно

DONE: CALL LEVELMETER ; калькулятор размерности
CALL NEEDSND ; анализатор предустановленных порогов звуковой сигнализации
CALL DIFDOZ ; текущий уровень - на дисплей, первая строка
CALL INTDOZ ; индикатор интегральной дозы, вторая строка
CLRF FIRSTFREE ; обнулим счёт импульсов от счётчика
CLRF FIRSTFREE+1 ; то же, старший байт
CALL INI_TMR
POP: SWAPF STATUS_TEMP, W ; Swap nibbles in STATUS_TEMP register and place result into W
MOVWF STATUS ; Move W into STATUS register (sets bank to original state)
SWAPF W_TEMP, F ; Swap nibbles in W_TEMP and place result in W_TEMP
SWAPF W_TEMP, W ; Swap nibbles in W_TEMP and place result into W 
RETFIE

; ; ##                Подпрограммы                ##
;
; ##################
; ##  Init Timer  ##
; ##################

; инициализация прерываний от RTCC
; TUNMD = тюнинг показаний МД, определяет время скроллинга регистровых пар.
; За большее время будет больше импульсов при неизменной МД. Значит, если прибор "завышает", то уменьшаем TUNMD и наоборот.

INI_TMR:MOVLW TUNMD
MOVWF FSRSAVE
BSF STATUS,RP0
MOVLW b'00000111'
MOVWF OPTION_REG^80 ; предделитель для RTCC 1:256, 1500000 => T = 1,7066666666666666666666666666667e-4 c
BCF STATUS,RP0 ;
MOVLW b'10100000'
MOVWF INTCON ; разрешим прерывания от RTCC
INTMR: MOVLW .64 ; (при .64 до прерывания останется 192 тактов, это T*192=32768 мкс)
MOVWF TMR0 ; 32768 * (TUNMD = 61) = 1.998848 s = 2 s with 0.001% deviation
RETURN

; ##############
; # Умножители #
; ##############

U4N: CALL U2N ; *4
U2N: BCF CARRY ; *2
RLF LPULSCNT,F
RLF MPULSCNT,F
BTFSC CARRY
INCF HPULSCNT,F
RETURN

NOCHPAAR:
MOVF FIRSTFREE,W
ADDWF LPULSCNT,F
BTFSC CARRY
INCF MPULSCNT,F
MOVF FIRSTFREE+1,W
ADDWF MPULSCNT,F
BTFSC CARRY
INCF HPULSCNT,F
RETURN


; ###################
; ## TABLE => LCD  ##
; ###################

CALL CMDTOLCD
ZG: MOVF FSRSAVE,W ; перебираем адреса ОЗУ дисплея
CALL TABLE ; чтобы получить номер кода в таблице
CALL DATATOLCD ; и записать полученный код в эту ячейку
INCF FSRSAVE,F ; инкремент адреса ОЗУ дисплея задан автоматический
DECFSZ P_COUNT,F
GOTO ZG
RETURN

; ####################################################
; ##  Чтение, суммирование и сдвиг содержимого РОН  ##
; ####################################################

READRAM:CLRF LPULSCNT
CLRF MPULSCNT
CLRF HPULSCNT
MOVLW .32 ; = число РОН в "барабане"
MOVWF FSRSAVE
MOVF P_COUNT,W
MOVWF TEMP_REG ; P_COUNT - счёт регистровых пар = .16 или .4
RRAM: MOVF FSRSAVE,W ; при первом проходе это 22H ; 24H
ADDLW FIRSTFREE-2 ; 22H + 2EH - 2 = 4EH ;24H + 2CH - 2 = 4EH
MOVWF FSR
MOVF INDF,W ; в W - значение из РОН 4EH, это младший байт из пары
BCF CARRY
ADDWF LPULSCNT,F ; добавим к младшему байту счёта импульсов
BTFSS CARRY ; этот младший байт переполнился?
GOTO RRM ; нет, идём дальше
INCFSZ MPULSCNT,F ; да - увеличим средний байт счёта импульсов
GOTO RRM ; средний байт переполнился?
INCF HPULSCNT,F ; да - увеличим старший байт счёта импульсов
RRM: MOVF FSRSAVE,W ; повторно восстановим FSR, при первом проходе 24H
ADDLW FIRSTFREE-2 ; 22H + 2EH - 2 = 4EH ; 24H + 2CH - 2 = 4EH
MOVWF FSR
INCF FSR,F
MOVF INDF,W ; в W - значение из РОН 4FH
ADDWF MPULSCNT,F ; добавим к среднему байту счёта импульсов
BTFSC CARRY ; средний байт переполнился?
INCF HPULSCNT,F ; да - увеличим старший
DECF FSRSAVE,F
DECF FSRSAVE,F ; старшие адреса, которые уйдут первыми
DECFSZ TEMP_REG,F
GOTO RRAM

ROTALL: DECF P_COUNT,W ; Cдвиг содержимого РОН, P_COUNT - счёт регистровых пар для сдвига
MOVWF TEMP_REG
BCF CARRY
RLF TEMP_REG,F ; (P_COUNT-1)*2 = на 2 меньше количества регистров для сдвига
MOVLW MAXRAM-2 ; 4DH
MOVWF FSRSAVE
ROTR: MOVF FSRSAVE,W
MOVWF FSR
MOVF INDF,W ; прочитаем ячейку 4DH
INCF FSR,F ; 4EH
INCF FSR,F ; 4FH
MOVWF INDF ; и перепишем в 4FH
DECF FSRSAVE,F ; 4C => 4E etc., каждый регистр сверху вниз поочерёдно
DECFSZ TEMP_REG,F
GOTO ROTR
RETURN

; ########################################
; ##  Логарифмический индикатор уровня  ##
; ########################################

; динамический диапазон индикатора 2^16. 1 имп/2с это примерно 32 мкр/ч = 2^5,
; макс. для датчика 4000 имп/ 2с - 2^12, надо сдвигать влево 2 раза и делать пьедестал

LGIND: MOVLW 68H ; нарисуем свои собственные, в зависимости от уровня, 7-й и 8-й символы (адрес 6EH - 7FH,

автоинкремент),
CALL CMDTOLCD
MOVF FIRSTFREE,W ; двоичный логарифмический индикатор выходного уровня, низ
MOVWF NIZLOG
MOVF FIRSTFREE+1,W ; двоичный логарифмический индикатор выходного уровня, верх
MOVWF VERHLOG

BCF CARRY
RLF NIZLOG,F
RLF VERHLOG,F ; * 2
BCF CARRY
RLF NIZLOG,W
RLF VERHLOG,F ; * 2
BCF CARRY

IORLW .3 ; пьедестал, 2 ступени
MOVWF NIZLOG

CLRF TEMP_REG
CALL LOG2IND
MOVF NIZLOG,W
MOVWF VERHLOG
LOG2IND:MOVLW .8 ; полная полоска - 800H - соотв. 2048 * 2 имп/с = запас по уровню 1 полоска
MOVWF P_COUNT
BTFSC TEMP_REG,2 ; если при первом проходе был столбик, дальше рисуем только столбик
GOTO STOLBIK
LOGIND: RLF VERHLOG,F ; сверху вниз рисуем пусто, пока не встретится "1"
BTFSC CARRY
GOTO STOLBIK
CALL DATATOLCD+1 ; записать "пусто" из TEMP_REG в эту ячейку памяти ЖКИ
DECFSZ P_COUNT,F
GOTO LOGIND
RETURN
STOLBIK:MOVLW 0EH
CALL DATATOLCD ; записать столбик в эту ячейку памяти ЖКИ (и, значит, в TEMP_REG)
DECFSZ P_COUNT,F
GOTO STOLBIK
RETURN


; ###########################################
; ## Калькулятор размерности, корректор МД ##
; ###########################################

LEVELMETER:

; HPULSCNT, MPULSCNT и LPULSCNT содержат счёт импульсов за 2*2*18= 72 cекунды назад,
; если результат умножить на число 72-c фреймов в часе (50) и разделить на IMPNAMKR (50)
; получим мощность дозы в мкР/час: для СБТ-11 k= 50/50 =1.

BCF RAZMER ; сначала здесь '0'
MOVF HPULSCNT,W
BTFSC ZERO ; уровень выше 0FFFFH (65 мР/ч)?
RETURN ; да - вычислим миллиРентгены, нет - уходим, размерность - микроРентгены 
 
MRH: BSF RAZMER ; теперь здесь '1'
BTFSC SOSFLAG ; уровень 3 мР/ч ранее был превышен?
GOTO RDDN ; да, в память об этом установлен SOSFLAG, ничего не делаем
CALL SOS ; не было этого - сигнал SOS о превышении
BSF SOSFLAG ; после SOS запретим SOS (локально, ТУТ, SOSFLAG'ом), и установим,
BSF MAXFLAG ; что уровни 28000, 3000,
BSF TEHFLAG ; 120
BSF FONFLAG ; и 60 уже разок были превышены

RDDN: MOVF MPULSCNT,W ; уровень > 10000H = 65536 мкР/ч
MOVWF LPULSCNT
MOVF HPULSCNT,W
MOVWF MPULSCNT ; разделим на 256, сместив всё на байт = здесь 240H max 100H = 256 min
CLRF HPULSCNT
BCF CARRY ; и ещё поделим на 2, итого /512 = 120H max 80H min = 128
RRF MPULSCNT,F
RRF LPULSCNT,F
BCF CARRY ; и ещё на 2, итого /1024 = 90H max = .144 & 40H min = 64
RRF MPULSCNT,F
RRF LPULSCNT,F
INCF LPULSCNT,F ; коррекция ошибки индикации 2,4% = .145 max 65 min
INCF LPULSCNT,F ; коррекция ошибки индикации 2,4% = .146 max 66 min <= O!!! то, что надо
RETURN

; ######################################
; ##  Вывод мощности дозы на дисплей  ##
; ######################################

; число в регистрах MPULSCNT и LPULSCNT - mkR/h или mR/h за последний фрейм

DIFDOZ: CALL CLEARDISP
CALL BINTODEC ; результат преобразуем в десятичный вид
movlw       80H     ; Команда перехода на первую строку, первое знакоместо
CALL CMDTOLCD ;
CALL DIG5ONLCD ; выведем же уже текущую мощность дозы в первую строку индикатора

MOVLW 84H     ; В младшей тетраде - количество пропущенных знакомест
CALL CMDTOLCD        ;
MOVLW     5H ; логарифмический индикатор уровня, верх
CALL    DATATOLCD

MOVLW 'm' ; 'm'
CALL    DATATOLCD

MOVLW     'R' ; 'R'
CALL    DATATOLCD
MOVLW     0 ; '/h'
GOTO    DATATOLCD ; там и найдём свой RETURN

; #############################################
; ## Проверка уровней звуковой сигнализации  ##
; #############################################

NEEDSND:BTFSS RAZMER ; там 'm'?
GOTO MR2
BTFSC SOSFLAG ; уровень 3000 ранее был превышен?
RETURN ; да, в память об этом установлен SOSFLAG, ничего не делаем
CALL SOS ; нет - сигнал SOS о превышении
BSF SOSFLAG
RETURN
; SOSFLAG SNDSTAT,7
MR2: MOVF HPULSCNT,W
BTFSC ZERO
GOTO SRBYT
BTFSC SOSFLAG ; уровень 3000 ранее был превышен?
RETURN ; да, в память об этом установлен SOSFLAG, ничего не делаем
GOTO BPPS

SRBYT: MOVF MPULSCNT,W ; SUBLW: W = LITERAL - W, if W=0 then Z=1, if W<0 then CARRY=1
SUBLW .11 ; анализируем средний байт:
BTFSC CARRY ; уровень 3000 mkR/h, (0BB8H) превышен? 
GOTO MR1 ; если нет, идём на MR1
BTFSC SOSFLAG ; уровень 3000 ранее был превышен?
RETURN ; да, в память об этом установлен SOSFLAG, ничего не делаем
BPPS: CALL SOS ; нет - сигнал SOS о превышении
BSF SOSFLAG ; после SOS запретим SOS (локально, ТУТ, SOSFLAG'ом), и установим,
BSF MAXFLAG ; что уровни 28000, 3000,
BSF TEHFLAG ; 120
BSF FONFLAG ; и 60 уже были превышены
RETURN
MR1: BCF SOSFLAG ; уровень ниже 3000, o5 считаем, что превышения уровня 3000 не было
MOVF MPULSCNT,W ; если старший байт - не нуль, но и не тревога - пропустить обработку,
BTFSC ZERO ; чтобы не сигналила на младших разрядах (хотя они и так уже пройдены = заблокированы)
GOTO MFON ; если старший байт - ноль, анализируем младший
RETURN
; MAXFLAG SNDSTAT,6
MFON: MOVF LPULSCNT,W ; анализируем младший байт счёта импульсов
SUBLW MAXFON ; CONST - W = > W, if W = 0 then Z=1, if W < 0 then CARRY = 1. 
BTFSC CARRY ; максимально-допустимый уровень 120 превышен?
GOTO TFON ; нет, проверяем на техногенный уровень 60
BTFSC MAXFLAG ; уровень реагирования 120 ранее уже был превышен?
RETURN ; да - обработка окончена
CALL ATTEN ; нет, это впервые -  сигнал "Внимание!"
BSF MAXFLAG ; и установим, что уровни 120,
BSF TEHFLAG ; 60
BSF FONFLAG ; и 30 были превышены
RETURN
; TEHFLAG SNDSTAT,5
TFON: BCF MAXFLAG
MOVF LPULSCNT,W
SUBLW TECHFON ; W = LITERAL - W, if W=0 then Z=1, if W<0 then CARRY=1.
BTFSC CARRY
GOTO PFON
BTFSC TEHFLAG ; уже было > 60?
RETURN ; да, уходим
CALL TCK ; фон впервые превысил техногенный - би-бикнуть, если включено
CALL BEEP
BSF TEHFLAG
BSF FONFLAG
RETURN

PFON: BCF TEHFLAG ; но только раз
MOVF LPULSCNT,W
SUBLW PASPFON
BTFSC CARRY ; фон больше паспортного - пикнуть, если включено
GOTO BPS
BTFSC FONFLAG
RETURN
CALL BEEP
BSF FONFLAG
RETURN
BPS: BCF FONFLAG ; один раз
RETURN

; ###################################
; ##  Индикация интегральной дозы  ##
; ###################################

; регистры HPULSCNT, MPULSCNT, LPULSCNT освободились, поместим туда миллиРентгены
; резон: IVPKT импульсов - это 0.004мР. 1мР = (4/1,024)мкР) * 256 = 1000 мкР,
; итого каждый пренос в DOSECNTM = 1 миллиРентген.
; чтобы вместо миллиРентген вывести на дисплей миллиЗиверты, достаточно вставить десятичную точку.

INTDOZ: BCF RAZMER ; вывод на дисплей только с точкой
; MOVF DOSECNTH,W ; можно разобрать здесь дозовые превышения
; SUBLW 13H ; W = LITERAL - W, if W=0 then Z=1, if W<0 then CARRY=1. 
; SUBLW 4H ; if W > LITERAL then CARRY=1. 

MOVF DOSECNTM,W
MOVWF LPULSCNT
MOVF DOSECNTH,W
MOVWF MPULSCNT ; сдвиг вправо на байт, чтобы так и сделать: /256 * 4/1,024 mkR = mR
CLRF HPULSCNT
CALL BINTODEC ; из MPULSCNT и LPULSCNT 0FFFFH (65535) as max
movlw 0C0H     ; Команда перехода на вторую строку
CALL CMDTOLCD
CALL DIG5ONLCD ; выведем интегральную дозу во вторую строку индикатора и припишем - Roentgen
DISPMR: movlw 0C4H     ; Команда перехода на вторую строку
CALL CMDTOLCD ; В младшей тетраде - количество пропущенных знакомест
movlw     6H ; логарифмический индикатор уровня, низ
CALL    DATATOLCD
movlw     'R' ; 'R'
CALL    DATATOLCD
movlw     2 ; 'oe'
CALL    DATATOLCD
movlw     1 ; и символ 'звук включен'
BTFSC SOUNDON
GOTO     DATATOLCD
movlw     2AH ; или символ 'звук выключен'
BTFSC SEARCH
movlw     0EEH ; или символ режима 'поиск'
GOTO     DATATOLCD ; там и найдём свой RETURN

; ; ##  Вывод 5-значного числа на дисплей                ##
; ##  единицы - в младшей тетраде регистра DEC_L, десятки - в старшей,  ##
; ##  сотни в младшей тетраде DEC_M, тысячи - в старшей DEC_M, десятки  ##
; ##  тысяч мкРч - в младшей тетраде DEC_H, max 65.535 = 0FFFFH         ##
;
DIG5ONLCD:
MOVLW 3
MOVWF FSRSAVE ; кол-во символов для печати
CLRF P_COUNT
INCF P_COUNT,F ; взвели сторожок: 1 - не печатать цифру, как только обнулится - печатать
MOVF DEC_H,W ; 5-я цифра
BTFSS ZERO
CLRF P_COUNT ; ведущие нули пропускаем,
CALL DIGLCD ; на первой же значащей цифре сбросим сторожок - дальше выводить всё (и нули тоже)
SWAPF DEC_M,W ; 4-я цифра
ANDLW 0FH
BTFSS ZERO
CLRF P_COUNT
CALL DIGLCD
BTFSS RAZMER ; десятичная точка не нужна, если МД > 65 мР/ч
GOTO STOCH ; и разряды сдвинуты на 3 позиции (мкР > мР
MOVLW ' '
GOTO BEZTO
STOCH: MOVLW '.'
BEZTO: CALL DATATOLCD
MOVF DEC_M,W ; 3-я цифра
ANDLW 0FH
CLRF P_COUNT
CALL DIGLCD
SWAPF DEC_L,W ; 2-я цифра
ANDLW 0FH
CALL DIGLCD
MOVF DEC_L,W ; 1-я цифра
DIGLCD: MOVF P_COUNT,F
BTFSS ZERO ; 1 - не печатать символ, 0 - печатать
RETURN
MOVF FSRSAVE,F
BTFSC ZERO ; цифр больше трёх - не печатать
RETURN
DECF FSRSAVE,F
ANDLW 0FH
ADDLW 30H
GOTO    DATATOLCD
PROBEL: MOVLW ' '
GOTO    DATATOLCD

; #############################################
; ##  Тревога: предел возможностей превышен  ##
; #############################################

LHIGH: BTFSC BLINK ; да, мерцать
GOTO SOSCANT ; через раз
BSF BLINK
RETURN

SOSCANT:BCF BLINK
movlw 80H     ; Команда перехода на первую строку
CALL CMDTOLCD        ; В младшей тетраде - количество пропущенных знакомест
MOVLW '*'
CALL    DATATOLCD
MOVLW 'H'
CALL    DATATOLCD
MOVLW 'i'
CALL    DATATOLCD
MOVLW '*'
CALL    DATATOLCD
GOTO SOS

; ##########################################
; ##  Тревога: нет импульсов от счётчика  ##
; ##########################################

LLOW: BTFSC BLINK ; да, мерцать
GOTO SOSZERO ; через раз
BSF BLINK
RETURN
SOSZERO:BCF BLINK
movlw 80H     ; Команда перехода на первую строку
CALL CMDTOLCD        ; В младшей тетраде - количество пропущенных знакомест
MOVLW '*'
CALL    DATATOLCD
MOVLW 'L'
CALL    DATATOLCD
MOVLW 'o'
CALL    DATATOLCD
MOVLW '*'
CALL    DATATOLCD
GOTO ATTEN

; ###########################################
; ## Обнуление интегральной дозы в EEPROM  ##
; ###########################################

CLEAROM:CLRF CONF_ADDR
CLRF DATATOWR
CALL WRITE
INCF CONF_ADDR,F
CLRF DATATOWR
CALL WRITE
INCF CONF_ADDR,F
CLRF DATATOWR
CALL WRITE
RETURN

; ##############################################
; ##  Тревога: превышен дозовый порог прибора ##
; ##############################################

SOSLOOP:BCF INTCON,GIE ; запретим прерывания
BSF SOUNDON
CALL SOSD
BTFSC KNOPMODE
GOTO SSLP
CLRF DOSECNTH
CLRW
GOTO MAIN ; CG1
SSLP: CALL PAUSEM
CALL PAUSEM
GOTO SOSLOOP

; ##################################
; ##  Мигающая надпись "Overflow" ##
; ##################################

SOSD: BTFSS BLINKD
GOTO SMERT
BCF BLINKD
movlw 0C0H     ; Команда перехода на вторую строку
CALL CMDTOLCD        ; В младшей тетраде - количество пропущенных знакомест
MOVLW 8
MOVWF FSRSAVE
METLA: MOVLW '*'
CALL    DATATOLCD
DECFSZ FSRSAVE,F
GOTO METLA
CALL PAUSEM
CALL PAUSEM
GOTO BEEP

SMERT: BSF BLINKD
movlw 0C0H     ; Команда перехода на вторую строку
CALL CMDTOLCD        ; В младшей тетраде - количество пропущенных знакомест
movlw       'O' ;'O'
CALL    DATATOLCD
movlw     'v' ;'v'
CALL    DATATOLCD
movlw     'e' ;'e'
CALL    DATATOLCD
movlw     'r' ;'r'
CALL    DATATOLCD
movlw     'f' ;'f'
CALL    DATATOLCD
movlw       'l' ;'l'
CALL    DATATOLCD
movlw     'o' ;'o'
CALL    DATATOLCD
movlw     'v' ;'v'
CALL    DATATOLCD
CALL PAUSEM
CALL PAUSEM
GOTO BEEP

; #################################
; ##  звуковой сигнал "Тревога!" ##
; #################################

SOS: BTFSS SOUNDON
RETURN
CALL TCK
CALL TCK
CALL TCK
CALL TIRE
CALL TIRE
CALL TIRE
CALL TCK
CALL TCK
GOTO TCK

; ##################################
; ##  звуковой сигнал "Внимание!" ##
; ##################################

ATTEN: BTFSS SOUNDON
RETURN
CALL TIRE
CALL TCK
CALL TCK
CALL TCK

; #################################
; ##  Точка, тире и пауза Морзе  ##
; #################################

TIRE: CALL BEEP
CALL BEEP
TCK: CALL BEEP
PAUSEM: movlw .100
movwf MILLI ; здесь можно задать скорость передачи Морзе-кода
clrf MICRO
PM: decfsz MICRO,f
goto $+2
decfsz MILLI,f
GOTO PM
RETURN

; ###############################
; ##  Подпрограмма клавиатуры  ##
; ###############################

KEY: BTFSC KNOPMODE ; на выводе KNOPMODE цепочка из двух резисторов 5.1K на + 5 В.
RETURN ; Кнопка между их ср. точкой и "0", конденсатор 10000 пФ параллельно кнопке.
CALL BBP
CALL BBP
DREBEZG:BTFSS KNOPMODE ; ждём отпускания
GOTO DREBEZG ; если долго, то выключить? или прибор неотключаемый, как одичавшие ручные часы?
MOVF SNDSTAT,W ; надо же уже решить что-то тут
ANDLW 3
BTFSS ZERO
ADDLW 1
ADDLW 2
ANDLW 3
MOVWF SNDSTAT
RETURN


; ###########################################
; ##  ДЗЫНЬ - однократный звуковой сигнал  ##
; ###########################################

DZIN: MOVLW .20 ; звук, длительность
MOVWF MILLI
MNDR: GOTO MEANDR


; ##########################################
; ##  BEEP - однократный звуковой сигнал  ##
; ##########################################

BEEP: BTFSS SOUNDON
RETURN
BBP: MOVLW .80 ; звуковой сигнал, длительность
MOVWF MILLI
MEANDR: BCF SOUND
CALL IMPOLUP
BSF SOUND
CALL POLUPERIOD
DECFSZ MILLI,F
GOTO MEANDR
RETURN

IMPOLUP:MOVLW .37
GOTO PLPD

POLUPERIOD:
MOVLW .77
PLPD: MOVWF MICRO
DECFSZ MICRO,F
GOTO $-1
RETURN

; ####################################
; ## подпрограммы вывода на дисплей ##
; ####################################

CLEARDISP:
MOVLW 1
CMDTOLCD:
MOVWF TEMP_REG
CALL LCD_Busy?
BCF RS ; register select, 0 - instruction register IR; 1 - data register DR
GOTO DLC
DATATOLCD:
MOVWF TEMP_REG
CALL LCD_Busy?
BSF RS ; register select, 0 - instruction register IR; 1 - data register DR
DLC: BCF RW ; RW = 0 - write, 1 - read
SWAPF TEMP_REG,w ; запись в индикатор при 4-разрядной шине данных
bsf EN
    MOVWF    PORTB
NOP
    bcf EN
NOP
MOVF TEMP_REG,w ; затем младший ниббл
bsf EN
    MOVWF   PORTB
NOP
    bcf EN
RETURN
LCD_Busy?: ; Проверка Busy Flag LCD
    bsf STATUS, RP0
    movlw    0AFh
    MOVWF    TRISB^80 ; PORTB - входы
    BCF STATUS, RP0
BCF RS ; register select, 0 - instruction register IR; 1 - data register DR
BSF RW ; RW = 0 - write, 1 - read
BSF EN
BUSY: btfsc    BUSYFLAG  ; Проверка бита "Занят"
goto    BUSY
    BCF EN
    bsf STATUS, RP0
MOVLW 0A0H
MOVWF TRISB^80 ; PORTB: разряды 7, 5 - входы, остальные - выходы
    BCF STATUS, RP0
RETURN

CMLCD: MOVWF TEMP_REG ; ввод команд при начальной инициализации ЖКИ
; здесь SWAPF TEMP_REG,w - учтено перестановкой нибблов в байтах инициализации
BCF RS ; register select, 0 - instruction register IR; 1 - data register DR
BCF RW ; RW = 0 - write, 1 - read
BSF  EN
    MOVWF    PORTB
NOP
BCF  EN
NOP
SWAPF TEMP_REG,w
BSF EN
    MOVWF    PORTB
NOP
BCF EN

; ; ##  задержка на 40 мс                ##
; ##  Число циклов определяется по формуле: MILLI*256*6 (циклов программы). ##
; ##  в каждом цикле количество выполняемых команд одинаково.     ##
; ##  Используемые регистры: MILLI, MICRO.     ##
;
DELAY40:
MOVLW .127
MOVWF MILLI
clrf MICRO
DELAY: DECFSZ MICRO,f
goto $+2
DECFSZ MILLI,f
goto DELAY
RETURN

; ; ##  Перевод двоичного числа в двоично-десятичное, трёхбайтное. ##
; ##  Входные данные: двоичное число в регистрах MPULSCNT, LPULSCNT.         ##
; ##  Выходные: единицы в младшей тетраде DEC_L, десятки в старшей DEC_L,       ##
; ##  сотни в младшей тетраде DEC_M, тысячи в старшей DEC_M,                ##
; ##  десятки тысяч в младшей тетраде DEC_H.                ##
; ##  Используемые регистры: MPULSCNT, LPULSCNT, DEC_H, DEC_M, DEC_L, TEMP_REG. ##
;
BINTODEC:
MOVLW .16
MOVWF TEMP_REG
CLRF DEC_H
CLRF DEC_M
CLRF DEC_L
GOTO NEEDWORK
ADJDEC: MOVLW 33H
ADDWF DEC_H,F
ADDWF DEC_M,F
ADDWF DEC_L,F
MOVLW 3H
BTFSS DEC_H,3
SUBWF DEC_H,F
BTFSS DEC_M,3
SUBWF DEC_M,F
BTFSS DEC_L,3
SUBWF DEC_L,F
MOVLW 30H
BTFSS DEC_H,7
SUBWF DEC_H,F
BTFSS DEC_M,7
SUBWF DEC_M,F
BTFSS DEC_L,7
SUBWF DEC_L,F
NEEDWORK:
RLF LPULSCNT,F
RLF MPULSCNT,F
RLF DEC_L,F
RLF DEC_M,F
RLF DEC_H,F
DECFSZ TEMP_REG,F
GOTO ADJDEC
RETURN

; #############################
; ##  Инициализация дисплея  ##
; #############################

LCDINIT:CALL DELAY40 ; после вкл. питания обязательна задержка и инициализация дисплея
CALL DELAY40 ; надо свопить: нибблы аппаратно переставлены из-за конфликта LCD с программатором:
; иначе при EN = 1 LCD захватывает шину и не даёт (пере)программировать контроллер!
MOVLW 83h ; 38h
        call    CMLCD
MOVLW 82h ; 28h ; 4 разряда b0010
       call    CMLCD ; 2 строки, символ 5*8 = b10XX
MOVLW 82h ; 28h
        call    CMLCD ; да, по даташиту зачем-то дважды, туповат видать дисплейчик-от
MOVLW 0C0h ; 0Ch - включить дисплей, курсор выключен, не мерцает: 0001 D(ISPLAY) C(URSOR) B(LINK)
CALL CMLCD
MOVLW 10h ; 1H - очистить дисплей
CALL CMLCD
MOVLW 60h ; 6H - установка режима сдвига курсора и дисплея
GOTO    CMLCD

;DATA EEPROM READ/WRITE

READ: BCF STATUS, RP0 ; Bank 0
MOVF CONF_ADDR,W ;
MOVWF    EEADR ; Address to read
BSF STATUS, RP0 ; Bank 1
BSF EECON1, RD ; EE Read
BCF STATUS, RP0 ; Bank 0
MOVF EEDATA, W ; W = EEDATA
RETURN

WRITE: MOVF CONF_ADDR,W ;
MOVWF    EEADR ; Address to Write
MOVF DATATOWR,W
MOVWF    EEDATA ; begin write
BSF STATUS, RP0 ; Bank 1
BCF INTCON, GIE ; Disable INTs.
BSF EECON1, WREN ; Enable Write
MOVLW 55h
MOVWF EECON2 ; Write 55h
MOVLW 0AAh
MOVWF EECON2 ; Write AAh
BSF EECON1, WR ; Set WR bit
WRDONE: BTFSS EECON1, EEIF
GOTO WRDONE
BSF INTCON, GIE ; Enable INTs.
BCF EECON1, WREN ; Disable Write
BCF EECON1, EEIF
BCF    STATUS, RP0 ; Bank 0
RETURN

END


Рецензии