Материал из EPIRB Wiki
Перейти к: навигация, поиск

Контроллер маяка на PIC16F630

Контроллер маяка на PIC16F630
Переключатель для управления маяком
Схема контроллера

Версия прошивки 3.0, циклы 30с/30с; 60с/60с; 1м/4м

Файл:Mf3.HEX.zip


;-Передает в режиме лисы код Морзе (MO, MOE, MOI, MOS, MOH, MO5, MOT, T). Вариант кода задается переключателем.
;-Может работать непрерывно или циклом: 30с/30с; 60с/60с; 1м/4м.
;-Есть выход 1000 Гц для модуляции маяка на 144 МГц.
;-Автоотключение через 8 часов.
;-Потребление в выключенном состоянии 0.08 ма, во время работы, при передаче позывного, 0.5 ма.
;-Уход часов определяется кварцем.
;-Код с переключателя считывается один раз после сброса.
;-Режим отложенного старта (ОС), задержка 120 минут
;-Ускорение передачи позывного за 10 сек до конца цикла (только для 5 мин цикла)
;-Питание от 3 до 5 вольт 
;
; PIC16F630
; WDT off, intLC, MCLR on, BOR on, PWRTE on
;
	list	P=16F630
	radix	HEX

;====================================================================
; именование пользовательских регистров
;	
 	CBLOCK	0X20	; начальный адрес пользовательских регистров
        TMPW		; временный регистр для хранения W во время прерывания
	TMPS		; временный регистр для хранения STATUS                                                    
	SEC_CNT		; счетчик пар секунд
	MIN_CNT 	; счетчик минут (пятиминуток, автовыключение)
	FLAG		; регистр флагов:
			; FLAG,0 разрешает передачу позывного в свое время
			; FLAG,2 флаг задержки включения (1-можно работать)
                        ; FLAG,3 флаг взводится через 5 мин после начала работы
			; и отменяет задержку включения при сбросе
			; FLAG,4 флаг устанавливается за 10 сек до конца цикла


	DELAY		; счетчик пятиминуток, задержка включения
	TX_ALL_CNT	; счетчик пар секунд в цикле
	TX_OFF		; с какой секунды пауза
	TX_ALL		; сколько пар секунд в цикле

	SWITCH		; состояние переключателя
	CHAR_INDEX	; номер передаваемого символа

	CW_SPEED	; скорость передачи

; процедура SEND_TONE
	NBR_DIT		; NBR_DIT кол-во пропускаемых точек
	CTR_NBR		; счетчик пропускаемых точек
	TONE		; счетчик частоты
	SET_TONE	; установка тона передаваемого сигнала
; процедура SEND_SPACE
	NBR_SPACE	; колво пропускаемых точек
	CTR_DIT		; счетчик пропускаемых точек
; процедура SEND_ONE_CHAR
	CW_CHAR		; передаваемый символ
	ENDC


;
; Именование регистров микроконтроллера
;
CMCON		EQU	0X19	; регистр контроля компаратора
TMR0		EQU	0X01	; timer0
TMR1L		EQU	0X0E	; timer1 младший байт
TMR1H		EQU	0X0F	; timer1 старший байт     
T1CON		EQU	0X10	; timer1 control register
PCL		EQU	0X02	; программный счетчик
STATUS		EQU	0X03	; регистр status
FSR		EQU	0X04	; указатель адреса в памяти данных
PORTA		EQU	0X05	; порт A
PORTC		EQU	0X07	; порт C
PCLATH		EQU	0X0A	; верхние 5 бит программного счетчика
INTCON		EQU	0X0B	; регистр контроля прерываний
TRISA		EQU	0X85	; управляющий регистр порта A
TRISC		EQU	0X87	; управляющий регистр порта C 
OPTION_REG	EQU	0X81	; регистр option
EEADR		EQU	0X9B	; адресный регистр EEPROM
EECON1		EQU	0X9C	; содержит бит RD
EEDAT		EQU	0X9A	; прочитанные из EEPROM данные
PCON		EQU	0X8E	; регистр power control
PIE1		EQU	0X8C	; регистр контроля периферийных прерываний
PIR1		EQU	0X0C	; регистр флагов прерываний от 
				; периферийных модулей

;
; присвоение значений символьным константам
;
RP0		EQU	0X05	; бит-указатель страницы данных в STATUS
W		EQU	0X00	; file register designator = W
F		EQU	0X01	; file register designator = F 
Z		EQU	0X02	; бит нуля в STATUS 
C		EQU	0X00	; бит преноса в STATUS
RD		EQU	0X00	; инициализация чтения EEPROM
POR		EQU	0X01	; бит POR регистра PCON обнуляется, если был
				; сброс по включению питания
BOR		EQU	0X00	; сброс по снижению питания
TMR1IF		EQU	0X00	; бит переполнения TMR1 in register PIR1
TMR1ON		EQU	0X00	;
GIE		EQU	0X07	;
TMR1IE		EQU	0X00	; бит разрешения прерываний от TMR1
PON		EQU	0X00	; POWER ON IN FLAG
;
;=================================================================
; vector table
;
	ORG 0X000		; стартовый адрес	
	GOTO START		; 

;==================================================================
; TIMER1 INTERRUPT 
;
	ORG 0X004		; адрес	прерывания
	MOVWF TMPW		; сохраняем W
	SWAPF STATUS,W
	MOVWF TMPS		; сохраняем STATUS
	
	BTFSC PIR1,0		; прерывание от TMR1?
	GOTO T1_INT		; да
        NOP			; обработчик левого прерывания
	GOTO END_ISR		; 

T1_INT
	MOVF TX_ALL_CNT,W	; вычитание
	SUBWF TX_OFF,W		; TX_OFF-TX_ALL_CNT
	
	BCF FLAG,0		;
	SKPC			; пропустить, если результат >=0
	BSF FLAG,0		;


	MOVF TX_ALL_CNT,W	; ускоряемся за 10 сек до конца цикла
	SUBLW D'125'		; ставим флаг, по которому во время передачи
	SKPNZ			; меняем скорость
	BSF FLAG,4		; только при 5 мин цикле

				; использовать либо верхнее, либо нижнее

	;SUBLW D'250'		; а это костыль для ускорения за 10 сек
	;SKPNZ			; до конца любого цикла, даже если
	;BSF FLAG,4		; работаем непрерывно

	DECFSZ TX_ALL_CNT,F	; TX_ALL_CNT-1, пропустить, если 0
	GOTO POP_TX
	MOVF TX_ALL,W		; восстанавливаем
	MOVWF TX_ALL_CNT	;

	BCF FLAG,4		; в конце цикла сбросить флаг ускорения
POP_TX

       	DECFSZ SEC_CNT, F	; SEC_CNT-1
	GOTO POP		; 
	MOVLW D'150'		; 300 секунд = 5 мин
	MOVWF SEC_CNT
	
	BTFSS FLAG,2		; если задержка активирована, то
	BSF FLAG,3		; через 5 мин после запуска поднимаем флаг
				; отмены задержки включения

	DECF MIN_CNT,F		; MIN_CNT-1
	DECFSZ DELAY,F		; DELAY-1, пропустить след, если 0
	GOTO POP
	BSF FLAG,2		; устанавливаем флаг разрешения работы	

POP	
	BCF PIR1,0		; сброс флага переполнения

END_ISR
	SWAPF TMPS,W		; восстанавливаем регистры W and SATUS
	MOVWF STATUS		;
	SWAPF TMPW,F		;
	SWAPF TMPW,W		;
	RETFIE			; возврат из прерывания

;==================================================================
;  Начало основной программы 
;==================================================================
START	
	MOVLW B'10001110'       ;
	BSF STATUS, RP0		; bank 1
	MOVWF OPTION_REG	; инициализация регистра option
	BCF STATUS, RP0		; bank 0	
			
	CLRF PORTA		; очистка порта A 
	CLRF PORTC		; очистка порта C

	MOVLW B'00000111'	; 
	MOVWF CMCON		; запрещаем работу компаратора

	BSF STATUS, RP0		; bank 1
	MOVLW B'00000000'	; 
	MOVWF TRISA		; порт A - все выходы
	MOVLW B'11111111'	; 
	MOVWF TRISC		; порт C - все входы
	BCF STATUS, RP0		; bank 0	

	CLRF TMR0		; на всякий случай. Говорят, надо...
	CLRWDT			; сбрасываем WDT

	BSF PORTA,2		; на 11 ноге висят подтягивающие резисторы,
				; подключенные к переключателю
				; после считывания напряжение с переключателя
				; снимается для экономии энергии

;==================================================================
; инициализация TMR1
;
	CLRF T1CON		; очистить TIMER1 control register
	CLRF TMR1H		; очистить верхний байт TMR1
	CLRF TMR1L		; очистить нижний байт TMR1
	CLRF INTCON		; запретить прерывания
	BSF STATUS,RP0		; bank 1
	CLRF PIE1		; очистить рег контроля периферийных прерываний
	BCF STATUS, RP0		; bank 0	
	CLRF PIR1		; очистить рег флагов периферийных прерываний
	MOVLW B'00001110'	; инициализация TIMER1 control register
	MOVWF T1CON		; с этого момента генератор 32.768 включен


;==================================================================

	MOVLW D'150'		; инициализация счетчика пар секунд
	MOVWF SEC_CNT		; 150 пар = 300 сек = 5 мин
        DECF SEC_CNT,F		; имитируем прерывание

	;MOVLW D'2'		; сколько пятиминуток будем работать	
	MOVLW D'96'		; сколько пятиминуток будем работать
	MOVWF MIN_CNT		; 96х5=8 час

;	MOVLW D'2'		; сколько пятиминуток задержка включения
	MOVLW D'24'		; сколько пятиминуток задержка включения
	MOVWF DELAY		; 24х5=120 мин

	MOVLW D'178'		; тон звука, 178 ~ 1000 Гц
	MOVWF SET_TONE		; чем больше, тем ниже частота

	BSF FLAG,0		; поднимаем флаг

	BCF FLAG,4


;==================================================================
; если это включение питания, то выполняется задержка для
; гарантированного запуска генератора 32768 и его инициализация
; если другой сброс, то инициализация и задержка пропускаются
;
	BSF STATUS, RP0		; bank 1
	MOVF PCON,W		; PCON -> W
	BCF STATUS, RP0		; bank 0

	MOVWF TMPW		; W -> TMPW
	BTFSS TMPW,POR		; проверяем состояние POR, если 0, то был сброс
	GOTO DELAY_START	; по включению - задержка однозначно нужна
	BTFSC TMPW,BOR          ; а это был сброс по снижению напр питания
        GOTO SKIP_DELAY         ; или не был...
	
DELAY_START

	BCF FLAG,3		; при первом включении очистить FLAG,3

	; задержка 
	MOVLW D'255'		; скорость передачи 
	MOVWF CW_SPEED		; чем больше, тем медленней
	 
 	MOVLW 0X80	
	MOVWF CW_CHAR		; 
        CALL SEND_ONE_CHAR	; в качестве задержки предаем 7 точек

	BSF STATUS, RP0		; bank 1
	BSF PCON,POR		; устанавливаем POR=1
	BSF PCON,BOR		; устанавливаем BOR=1
	BCF STATUS, RP0		; bank 0

	SLEEP			; ожидаем запуск

SKIP_DELAY


;==================================================================
; читаем переключатель
;
        MOVF PORTC,W		; читаем порт C
	ANDLW B'00011100'	; маска 
	MOVWF SWITCH		; сохраняем состояние переключателя

        MOVF PORTC,W		; читаем порт C
	ANDLW B'00000011'	; маска двух младших бит

	BCF FLAG,2		; обнуляем флаг задержки
	BTFSC PORTC,5		; если перекл. on (на землю, 0) пропуск. след.
	BSF FLAG,2		; устанавливаем флаг, можно работать

	BCF PORTA,2		; отключаем подтягивающие резисторы

	ADDWF PCL,F		; таблица
	GOTO SW00
	GOTO SW01
	GOTO SW10
	GOTO SW11
SW00			; непрерывно
	MOVLW D'0'		; молчим последние 0 с
	MOVWF TX_OFF		;
	MOVLW D'30'		; цикл 60 сек
	GOTO END_SW
SW01			; цикл 30сек/30сек
	MOVLW D'15'		; молчим последние 30 с
	MOVWF TX_OFF		;
	MOVLW D'30'		; цикл 60 сек
	GOTO END_SW
SW10			; цикл 1 мин/1 мин
	MOVLW D'30'		; молчим последние 60 с
	MOVWF TX_OFF		;
	MOVLW D'60'		; цикл 120 сек
	GOTO END_SW
SW11			; цикл 1 мин/4 мин
	MOVLW D'120'		; молчим последние 4 m
	MOVWF TX_OFF		;
	MOVLW D'150'		; цикл 5 m

END_SW
	MOVWF TX_ALL		;
	MOVWF TX_ALL_CNT	; инициализация	
        DECF TX_ALL_CNT,F	; считаем, что 1 прерывание уже случилось

;==================================================================
; Окончание инициализации TIMER1
;

	MOVLW B'11000000'	; разрешить прерывания от периферийных модулей
	MOVWF INTCON

	BSF STATUS,RP0		; bank 1
	BSF PIE1,TMR1IE		; разрешить прерывания от TMR1
	BCF STATUS, RP0		; bank 0	
	BSF T1CON,TMR1ON        ; TMR1 ON


;==================================================================
; Если с момента запуска прошло 5 мин, то при сбросе задержку 
; включения игнорируем

	BTFSC FLAG,3		; FLAG,3 поднимается через 5 м после начала
				; работы
	BSF FLAG,2		; задержка включения не нужна

;==================================================================
; MAIN
; передача последовательности символов
; можно предавать любое количество, 0XFF означает конец последовательности,
; после чего формируется пауза и передача циклически повторяется
;
SEND_MSG
                                ; если автоотключение не нужно, то три 
				; нижеследующих строчки надо закомментировать
	MOVF MIN_CNT,F		; проверяем, не обнулился ли 
	SKPNZ			; счетчик пятиминуток
	GOTO POWER_OFF		; если да, то выключаемся

	BTFSS FLAG,2		; пропустить след, если задержка включения
				; кончилась (1-разрешение работы)
	GOTO FLASH		; мигнуть и sleep

        BTFSS FLAG,0		; если флаг сброшен, то идем спать
	GOTO TO_SLEEP
	
	MOVF SWITCH,W
	MOVWF CHAR_INDEX	; SWITCH -> CHAR_INDEX

GET_MSG
	MOVF CHAR_INDEX,W		
	BCF INTCON,GIE		; запрещаем прерывания
	BSF STATUS, RP0		; bank 1
	MOVWF EEADR		; значение CHAR_INDEX = адресу кода в EEPROM
	BSF EECON1,RD		; разрешаем чтение EEPROM
	MOVF EEDAT,W		; значение ячейки -> регистр W
	BCF STATUS, RP0		; bank 0	 
	BSF INTCON,GIE		; разрешаем прерывания
	;MOVLW 0x06
	MOVWF CW_CHAR		; W -> CW_CHAR	

	MOVLW 0XFF		; конец сообщения?
	SUBWF CW_CHAR,W		;
	SKPNZ
	GOTO END_MSG		; да, перейти к END_MSG

	MOVLW D'60'		; скорость передачи быстро
	BTFSS FLAG,4		; устанавливается за 10 с до конца цикла
	MOVLW D'160'		; скорость передачи медленно
	MOVWF CW_SPEED		; чем больше, тем медленней

	CALL SEND_CHAR		; передаем букву и паузу в 3 точки
                                ; или паузу 6 точек, если SPACE
	INCF CHAR_INDEX		; CHAR_INDEX+1	
	GOTO GET_MSG

END_MSG
	CALL WORD_SPACE         ; пауза 6 точек 
	GOTO SEND_MSG		;	

				; время работы закончилось
POWER_OFF			; отсюда выход только через RESET
	CLRF T1CON		; очистить TIMER1 control register, выключаем
				; генератор 32768
	;BCF T1CON,TMR1ON       ; TMR1 OFF
	CLRF INTCON		; запретить прерывания

				; после сброса считаем, что это сброс по питанию
	BSF STATUS, RP0		; bank 1
	BCF PCON,POR		; устанавливаем POR=0
	BCF PCON,BOR		; устанавливаем BOR=0
	BCF STATUS, RP0		; bank 0

TO_SLEEP			;
	CLRF PORTA
	SLEEP
	NOP
	GOTO SEND_MSG
;========================================================================
; индикатор включенной задержки
; мигаем порт A.1
FLASH
	MOVLW D'60'		; скорость передачи 
	MOVWF CW_SPEED		; чем больше, тем медленней
	BSF PORTA,1
	MOVLW 0X01		;
	MOVWF NBR_SPACE		; пауза в 1 точку
	CALL SEND_SPACE		;
	BCF PORTA,1
	GOTO TO_SLEEP
;========================================================================
; SEND_CHAR
; Подпрограмма получает код символа в регистре CW_CHAR, побитно перебирает 
; его, и, в зависимости от состояния бита, передает: тире, если бит = 1;
; точку, если бит = 0.
; Знак передается, пока в слове есть хоть одна единица. Последняя единица
; "съедается", она означает конец слова. 
;
SEND_CHAR
	MOVLW 0X00		; пауза?
	SUBWF CW_CHAR,W		;
	SKPNZ			; 
	GOTO WORD_SPACE

SEND_ONE_CHAR 
        BTFSS FLAG,0		; обрываем передачу, если флаг сброшен
	RETURN

	CLRC			; очистить бит переноса
	RRF CW_CHAR, F		; сдвиг вправо через перенос
				; 
	MOVF CW_CHAR, F         ; проверяем содержимое CW_CHAR, если все "0"
	SKPNZ                   ; то Z=1, выполняем RETURN, иначе пропускаем
	GOTO PAUSE2		; конец буквы
	SKPC			; проверить бит переноса, пропустить, если=1
	GOTO SEND_A_DIT		; перейти к SEND_A_DIT

SEND_A_DAH			; передаем тире 
	MOVLW 0X03		; 1 тире = 3 точки
	MOVWF NBR_DIT
	MOVLW 0X01		;
	MOVWF NBR_SPACE		; после тире - пауза в 1 точку
	BSF PORTA,0		; включаем PORT A.2
	CALL SEND_TONE		; генериуем звук, оно же - пауза
	BCF PORTA,0		; выключаем PORT A.2
	CALL SEND_SPACE
	GOTO SEND_ONE_CHAR

SEND_A_DIT 
	MOVLW 0X01		; передаем точку
	MOVWF NBR_DIT
	MOVWF NBR_SPACE		; после точки - пауза в 1 точку
	BSF PORTA,0		; включаем PORT A.2
	CALL SEND_TONE          ; генерируем звук, оно же - пауза
	BCF PORTA,0		; выключаем PORT A.2
	CALL SEND_SPACE
	GOTO SEND_ONE_CHAR

PAUSE2
	MOVLW D'2'		; пауза в 2 точки после буквы	
	MOVWF NBR_SPACE		; 
	CALL SEND_SPACE         ;
	RETURN

WORD_SPACE
	MOVLW D'6'		; пауза в 6 точек между словами	
	MOVWF NBR_SPACE		; 
	CALL SEND_SPACE         ;
	RETURN

;==================================================================
; SEND_TONE
; Подпрограмма осуществляет задержку и одновременно формирует сигнал
; звуковой частоты (порт A.1)
;

SEND_TONE
 
	MOVF NBR_DIT, W		; NBR_DIT кол-во пропускаемых точек 
	MOVWF CTR_NBR		; 

DIT_LENGTH 
	MOVF CW_SPEED, W	; CW_SPEED скорость передачи
	MOVWF CTR_DIT		;

START_CYCLE 
	BSF PORTA,1		; A.1=1 в начале цикла
	MOVF SET_TONE,W		; 
	MOVWF TONE		; заносим значение в счетчик цикла задержки

HIGH_HALF 
	DECFSZ TONE, F		; считаем до нуля
	GOTO HIGH_HALF
	BCF PORTA,1		; A.1=0 в конце цикла

	MOVF SET_TONE,W		;
	MOVWF TONE

LOW_HALF 
	DECFSZ TONE, F		; 
	GOTO LOW_HALF
	DECFSZ CTR_DIT, F	;
	GOTO START_CYCLE
	DECFSZ CTR_NBR, F
	GOTO DIT_LENGTH		; 
	RETURN			; 

;================================================================
; SEND_SPACE
; Делает тоже самое, что и SEND_TONE, только вместо команд управления
; портами стоит команда NOP
;

SEND_SPACE
 
	MOVF NBR_SPACE, W	; 
	MOVWF CTR_NBR

DIT_LENGTH_SP 
	MOVF CW_SPEED, W	; 
	MOVWF CTR_DIT

START_CYCLE_SP
	NOP
	MOVF SET_TONE,W
	MOVWF TONE		; 

HIGH_HALF_SP 
	DECFSZ TONE, F		; 
	GOTO HIGH_HALF_SP
	NOP
	MOVF SET_TONE,W
	MOVWF TONE

LOW_HALF_SP 
	DECFSZ TONE, F		; 
	GOTO LOW_HALF_SP
	DECFSZ CTR_DIT, F	;
	GOTO START_CYCLE_SP
	DECFSZ CTR_NBR, F
	GOTO DIT_LENGTH_SP	; 
	RETURN			; 

;==================================================================
;==================================================================
; Идентификатор
	ORG  0x2000	; стартовый адрес

        DATA 0x0000
        DATA 0x0000
        DATA 0x0000
        DATA 0x0000

;==================================================================
; Слово конфигурации
	ORG  0x2007	; стартовый адрес

	DATA 0x31E4

;==================================================================
; Запись в EEPROM при программировании
; Переключателем адресуется номер ячейки, кратный 4. Соответственно, маяк
; может передавать последовательно три символа Морзе, четвертым обязательно
; должен быть EOM
; В последнюю адресуемую ячейку можно записать большее количество символов

	ORG  0X2100	; стартовый адрес

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
        DATA 0xFF	; EOM     11111111
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
        DATA 0x02	; E .     00000010  	
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x04	; I ..    00000100
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x08	; S ...   00001000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x10	; H ....  00010000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x20	; 5 ..... 00100000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x03	; T -     00000011
        DATA 0xFF	; EOM     11111111

	DATA 0x03	; T -     00000011
        DATA 0xFF	; EOM     11111111
        DATA 0xFF	; EOM     11111111
        DATA 0xFF	; EOM     11111111

	END

Версия прошивки 3.1, циклы 30с/120с; 60с/60с; 1м/4м

Файл:Mf31.HEX.zip

;-Передает в режиме лисы код Морзе (MO, MOE, MOI, MOS, MOH, MO5, MOT, T). Вариант кода задается переключателем.
;-Может работать непрерывно или циклом: 30с/120с; 60с/60с; 1м/4м.
;-Есть выход 1000 Гц для модуляции маяка на 144 МГц.
;-Автоотключение через 8 часов.
;-Потребление в выключенном состоянии 0.08 ма, во время работы, при передаче позывного, 0.5 ма.
;-Уход часов определяется кварцем.
;-Код с переключателя считывается один раз после сброса.
;-Режим отложенного старта (ОС), задержка 60 минут
;-Ускорение передачи позывного за 10 сек до конца цикла (только для 5 мин цикла)
;-Питание от 3 до 5 вольт 
;
; PIC16F630
; WDT off, intLC, MCLR on, BOR on, PWRTE on
;
	list	P=16F630
	radix	HEX

;====================================================================
; именование пользовательских регистров
;	
 	CBLOCK	0X20	; начальный адрес пользовательских регистров
        TMPW		; временный регистр для хранения W во время прерывания
	TMPS		; временный регистр для хранения STATUS                                                    
	SEC_CNT		; счетчик пар секунд
	MIN_CNT 	; счетчик минут (пятиминуток, автовыключение)
	FLAG		; регистр флагов:
			; FLAG,0 разрешает передачу позывного в свое время
			; FLAG,2 флаг задержки включения (1-можно работать)
                        ; FLAG,3 флаг взводится через 5 мин после начала работы
			; и отменяет задержку включения при сбросе
			; FLAG,4 флаг устанавливается за 10 сек до конца цикла


	DELAY		; счетчик пятиминуток, задержка включения
	TX_ALL_CNT	; счетчик пар секунд в цикле
	TX_OFF		; с какой секунды пауза
	TX_ALL		; сколько пар секунд в цикле

	SWITCH		; состояние переключателя
	CHAR_INDEX	; номер передаваемого символа

	CW_SPEED	; скорость передачи

; процедура SEND_TONE
	NBR_DIT		; NBR_DIT кол-во пропускаемых точек
	CTR_NBR		; счетчик пропускаемых точек
	TONE		; счетчик частоты
	SET_TONE	; установка тона передаваемого сигнала
; процедура SEND_SPACE
	NBR_SPACE	; колво пропускаемых точек
	CTR_DIT		; счетчик пропускаемых точек
; процедура SEND_ONE_CHAR
	CW_CHAR		; передаваемый символ
	ENDC


;
; Именование регистров микроконтроллера
;
CMCON		EQU	0X19	; регистр контроля компаратора
TMR0		EQU	0X01	; timer0
TMR1L		EQU	0X0E	; timer1 младший байт
TMR1H		EQU	0X0F	; timer1 старший байт     
T1CON		EQU	0X10	; timer1 control register
PCL		EQU	0X02	; программный счетчик
STATUS		EQU	0X03	; регистр status
FSR		EQU	0X04	; указатель адреса в памяти данных
PORTA		EQU	0X05	; порт A
PORTC		EQU	0X07	; порт C
PCLATH		EQU	0X0A	; верхние 5 бит программного счетчика
INTCON		EQU	0X0B	; регистр контроля прерываний
TRISA		EQU	0X85	; управляющий регистр порта A
TRISC		EQU	0X87	; управляющий регистр порта C 
OPTION_REG	EQU	0X81	; регистр option
EEADR		EQU	0X9B	; адресный регистр EEPROM
EECON1		EQU	0X9C	; содержит бит RD
EEDAT		EQU	0X9A	; прочитанные из EEPROM данные
PCON		EQU	0X8E	; регистр power control
PIE1		EQU	0X8C	; регистр контроля периферийных прерываний
PIR1		EQU	0X0C	; регистр флагов прерываний от 
				; периферийных модулей

;
; присвоение значений символьным константам
;
RP0		EQU	0X05	; бит-указатель страницы данных в STATUS
W		EQU	0X00	; file register designator = W
F		EQU	0X01	; file register designator = F 
Z		EQU	0X02	; бит нуля в STATUS 
C		EQU	0X00	; бит преноса в STATUS
RD		EQU	0X00	; инициализация чтения EEPROM
POR		EQU	0X01	; бит POR регистра PCON обнуляется, если был
				; сброс по включению питания
BOR		EQU	0X00	; сброс по снижению питания
TMR1IF		EQU	0X00	; бит переполнения TMR1 in register PIR1
TMR1ON		EQU	0X00	;
GIE		EQU	0X07	;
TMR1IE		EQU	0X00	; бит разрешения прерываний от TMR1
PON		EQU	0X00	; POWER ON IN FLAG
;
;=================================================================
; vector table
;
	ORG 0X000		; стартовый адрес	
	GOTO START		; 

;==================================================================
; TIMER1 INTERRUPT 
;
	ORG 0X004		; адрес	прерывания
	MOVWF TMPW		; сохраняем W
	SWAPF STATUS,W
	MOVWF TMPS		; сохраняем STATUS
	
	BTFSC PIR1,0		; прерывание от TMR1?
	GOTO T1_INT		; да
        NOP			; обработчик левого прерывания
	GOTO END_ISR		; 

T1_INT
	MOVF TX_ALL_CNT,W	; вычитание
	SUBWF TX_OFF,W		; TX_OFF-TX_ALL_CNT
	
	BCF FLAG,0		;
	SKPC			; пропустить, если результат >=0
	BSF FLAG,0		;


	MOVF TX_ALL_CNT,W	; ускоряемся за 10 сек до конца цикла
	SUBLW D'125'		; ставим флаг, по которому во время передачи
	SKPNZ			; меняем скорость
	BSF FLAG,4		; только при 5 мин цикле

				; использовать либо верхнее, либо нижнее

	;SUBLW D'250'		; а это костыль для ускорения за 10 сек
	;SKPNZ			; до конца любого цикла, даже если
	;BSF FLAG,4		; работаем непрерывно

	DECFSZ TX_ALL_CNT,F	; TX_ALL_CNT-1, пропустить, если 0
	GOTO POP_TX
	MOVF TX_ALL,W		; восстанавливаем
	MOVWF TX_ALL_CNT	;

	BCF FLAG,4		; в конце цикла сбросить флаг ускорения
POP_TX

       	DECFSZ SEC_CNT, F	; SEC_CNT-1
	GOTO POP		; 
	MOVLW D'150'		; 300 секунд = 5 мин
	MOVWF SEC_CNT
	
	BTFSS FLAG,2		; если задержка активирована, то
	BSF FLAG,3		; через 5 мин после запуска поднимаем флаг
				; отмены задержки включения

	DECF MIN_CNT,F		; MIN_CNT-1
	DECFSZ DELAY,F		; DELAY-1, пропустить след, если 0
	GOTO POP
	BSF FLAG,2		; устанавливаем флаг разрешения работы	

POP	
	BCF PIR1,0		; сброс флага переполнения

END_ISR
	SWAPF TMPS,W		; восстанавливаем регистры W and SATUS
	MOVWF STATUS		;
	SWAPF TMPW,F		;
	SWAPF TMPW,W		;
	RETFIE			; возврат из прерывания

;==================================================================
;  Начало основной программы 
;==================================================================
START	
	MOVLW B'10001110'       ;
	BSF STATUS, RP0		; bank 1
	MOVWF OPTION_REG	; инициализация регистра option
	BCF STATUS, RP0		; bank 0	
			
	CLRF PORTA		; очистка порта A 
	CLRF PORTC		; очистка порта C

	MOVLW B'00000111'	; 
	MOVWF CMCON		; запрещаем работу компаратора

	BSF STATUS, RP0		; bank 1
	MOVLW B'00000000'	; 
	MOVWF TRISA		; порт A - все выходы
	MOVLW B'11111111'	; 
	MOVWF TRISC		; порт C - все входы
	BCF STATUS, RP0		; bank 0	

	CLRF TMR0		; на всякий случай. Говорят, надо...
	CLRWDT			; сбрасываем WDT

	BSF PORTA,2		; на 11 ноге висят подтягивающие резисторы,
				; подключенные к переключателю
				; после считывания напряжение с переключателя
				; снимается для экономии энергии

;==================================================================
; инициализация TMR1
;
	CLRF T1CON		; очистить TIMER1 control register
	CLRF TMR1H		; очистить верхний байт TMR1
	CLRF TMR1L		; очистить нижний байт TMR1
	CLRF INTCON		; запретить прерывания
	BSF STATUS,RP0		; bank 1
	CLRF PIE1		; очистить рег контроля периферийных прерываний
	BCF STATUS, RP0		; bank 0	
	CLRF PIR1		; очистить рег флагов периферийных прерываний
	MOVLW B'00001110'	; инициализация TIMER1 control register
	MOVWF T1CON		; с этого момента генератор 32.768 включен


;==================================================================

	MOVLW D'150'		; инициализация счетчика пар секунд
	MOVWF SEC_CNT		; 150 пар = 300 сек = 5 мин
        DECF SEC_CNT,F		; имитируем прерывание

	;MOVLW D'2'		; сколько пятиминуток будем работать	
	MOVLW D'96'		; сколько пятиминуток будем работать
	MOVWF MIN_CNT		; 96х5=8 час

;	MOVLW D'2'		; сколько пятиминуток задержка включения
	MOVLW D'12'		; сколько пятиминуток задержка включения
	MOVWF DELAY		; 12х5=60 мин

	MOVLW D'178'		; тон звука, 178 ~ 1000 Гц
	MOVWF SET_TONE		; чем больше, тем ниже частота

	BSF FLAG,0		; поднимаем флаг

	BCF FLAG,4


;==================================================================
; если это включение питания, то выполняется задержка для
; гарантированного запуска генератора 32768 и его инициализация
; если другой сброс, то инициализация и задержка пропускаются
;
	BSF STATUS, RP0		; bank 1
	MOVF PCON,W		; PCON -> W
	BCF STATUS, RP0		; bank 0

	MOVWF TMPW		; W -> TMPW
	BTFSS TMPW,POR		; проверяем состояние POR, если 0, то был сброс
	GOTO DELAY_START	; по включению - задержка однозначно нужна
	BTFSC TMPW,BOR          ; а это был сброс по снижению напр питания
        GOTO SKIP_DELAY         ; или не был...
	
DELAY_START

	BCF FLAG,3		; при первом включении очистить FLAG,3

	; задержка 
	MOVLW D'255'		; скорость передачи 
	MOVWF CW_SPEED		; чем больше, тем медленней
	 
 	MOVLW 0X80	
	MOVWF CW_CHAR		; 
        CALL SEND_ONE_CHAR	; в качестве задержки предаем 7 точек

	BSF STATUS, RP0		; bank 1
	BSF PCON,POR		; устанавливаем POR=1
	BSF PCON,BOR		; устанавливаем BOR=1
	BCF STATUS, RP0		; bank 0

	SLEEP			; ожидаем запуск

SKIP_DELAY


;==================================================================
; читаем переключатель
;
        MOVF PORTC,W		; читаем порт C
	ANDLW B'00011100'	; маска 
	MOVWF SWITCH		; сохраняем состояние переключателя

        MOVF PORTC,W		; читаем порт C
	ANDLW B'00000011'	; маска двух младших бит

	BCF FLAG,2		; обнуляем флаг задержки
	BTFSC PORTC,5		; если перекл. on (на землю, 0) пропуск. след.
	BSF FLAG,2		; устанавливаем флаг, можно работать

	BCF PORTA,2		; отключаем подтягивающие резисторы

	ADDWF PCL,F		; таблица
	GOTO SW00
	GOTO SW01
	GOTO SW10
	GOTO SW11
SW00			; непрерывно
	MOVLW D'0'		; молчим последние 0 с
	MOVWF TX_OFF		;
	MOVLW D'30'		; цикл 60 сек
	GOTO END_SW
SW01			; цикл 30сек/120сек
	MOVLW D'60'		; молчим последние 120 с
	MOVWF TX_OFF		;
	MOVLW D'75'		; цикл 150 сек
	GOTO END_SW
SW10			; цикл 1 мин/1 мин
	MOVLW D'30'		; молчим последние 60 с
	MOVWF TX_OFF		;
	MOVLW D'60'		; цикл 120 сек
	GOTO END_SW
SW11			; цикл 1 мин/4 мин
	MOVLW D'120'		; молчим последние 4 m
	MOVWF TX_OFF		;
	MOVLW D'150'		; цикл 5 m

END_SW
	MOVWF TX_ALL		;
	MOVWF TX_ALL_CNT	; инициализация	
        DECF TX_ALL_CNT,F	; считаем, что 1 прерывание уже случилось

;==================================================================
; Окончание инициализации TIMER1
;

	MOVLW B'11000000'	; разрешить прерывания от периферийных модулей
	MOVWF INTCON

	BSF STATUS,RP0		; bank 1
	BSF PIE1,TMR1IE		; разрешить прерывания от TMR1
	BCF STATUS, RP0		; bank 0	
	BSF T1CON,TMR1ON        ; TMR1 ON


;==================================================================
; Если с момента запуска прошло 5 мин, то при сбросе задержку 
; включения игнорируем

	BTFSC FLAG,3		; FLAG,3 поднимается через 5 м после начала
				; работы
	BSF FLAG,2		; задержка включения не нужна

;==================================================================
; MAIN
; передача последовательности символов
; можно предавать любое количество, 0XFF означает конец последовательности,
; после чего формируется пауза и передача циклически повторяется
;
SEND_MSG
                                ; если автоотключение не нужно, то три 
				; нижеследующих строчки надо закомментировать
	MOVF MIN_CNT,F		; проверяем, не обнулился ли 
	SKPNZ			; счетчик пятиминуток
	GOTO POWER_OFF		; если да, то выключаемся

	BTFSS FLAG,2		; пропустить след, если задержка включения
				; кончилась (1-разрешение работы)
	GOTO FLASH		; мигнуть и sleep

        BTFSS FLAG,0		; если флаг сброшен, то идем спать
	GOTO TO_SLEEP
	
	MOVF SWITCH,W
	MOVWF CHAR_INDEX	; SWITCH -> CHAR_INDEX

GET_MSG
	MOVF CHAR_INDEX,W		
	BCF INTCON,GIE		; запрещаем прерывания
	BSF STATUS, RP0		; bank 1
	MOVWF EEADR		; значение CHAR_INDEX = адресу кода в EEPROM
	BSF EECON1,RD		; разрешаем чтение EEPROM
	MOVF EEDAT,W		; значение ячейки -> регистр W
	BCF STATUS, RP0		; bank 0	 
	BSF INTCON,GIE		; разрешаем прерывания
	;MOVLW 0x06
	MOVWF CW_CHAR		; W -> CW_CHAR	

	MOVLW 0XFF		; конец сообщения?
	SUBWF CW_CHAR,W		;
	SKPNZ
	GOTO END_MSG		; да, перейти к END_MSG

	MOVLW D'60'		; скорость передачи быстро
	BTFSS FLAG,4		; устанавливается за 10 с до конца цикла
	MOVLW D'160'		; скорость передачи медленно
	MOVWF CW_SPEED		; чем больше, тем медленней

	CALL SEND_CHAR		; передаем букву и паузу в 3 точки
                                ; или паузу 6 точек, если SPACE
	INCF CHAR_INDEX		; CHAR_INDEX+1	
	GOTO GET_MSG

END_MSG
	CALL WORD_SPACE         ; пауза 6 точек 
	GOTO SEND_MSG		;	

				; время работы закончилось
POWER_OFF			; отсюда выход только через RESET
	CLRF T1CON		; очистить TIMER1 control register, выключаем
				; генератор 32768
	;BCF T1CON,TMR1ON       ; TMR1 OFF
	CLRF INTCON		; запретить прерывания

				; после сброса считаем, что это сброс по питанию
	BSF STATUS, RP0		; bank 1
	BCF PCON,POR		; устанавливаем POR=0
	BCF PCON,BOR		; устанавливаем BOR=0
	BCF STATUS, RP0		; bank 0

TO_SLEEP			;
	CLRF PORTA
	SLEEP
	NOP
	GOTO SEND_MSG
;========================================================================
; индикатор включенной задержки
; мигаем порт A.1
FLASH
	MOVLW D'60'		; скорость передачи 
	MOVWF CW_SPEED		; чем больше, тем медленней
	BSF PORTA,1
	MOVLW 0X01		;
	MOVWF NBR_SPACE		; пауза в 1 точку
	CALL SEND_SPACE		;
	BCF PORTA,1
	GOTO TO_SLEEP
;========================================================================
; SEND_CHAR
; Подпрограмма получает код символа в регистре CW_CHAR, побитно перебирает 
; его, и, в зависимости от состояния бита, передает: тире, если бит = 1;
; точку, если бит = 0.
; Знак передается, пока в слове есть хоть одна единица. Последняя единица
; "съедается", она означает конец слова. 
;
SEND_CHAR
	MOVLW 0X00		; пауза?
	SUBWF CW_CHAR,W		;
	SKPNZ			; 
	GOTO WORD_SPACE

SEND_ONE_CHAR 
        BTFSS FLAG,0		; обрываем передачу, если флаг сброшен
	RETURN

	CLRC			; очистить бит переноса
	RRF CW_CHAR, F		; сдвиг вправо через перенос
				; 
	MOVF CW_CHAR, F         ; проверяем содержимое CW_CHAR, если все "0"
	SKPNZ                   ; то Z=1, выполняем RETURN, иначе пропускаем
	GOTO PAUSE2		; конец буквы
	SKPC			; проверить бит переноса, пропустить, если=1
	GOTO SEND_A_DIT		; перейти к SEND_A_DIT

SEND_A_DAH			; передаем тире 
	MOVLW 0X03		; 1 тире = 3 точки
	MOVWF NBR_DIT
	MOVLW 0X01		;
	MOVWF NBR_SPACE		; после тире - пауза в 1 точку
	BSF PORTA,0		; включаем PORT A.2
	CALL SEND_TONE		; генериуем звук, оно же - пауза
	BCF PORTA,0		; выключаем PORT A.2
	CALL SEND_SPACE
	GOTO SEND_ONE_CHAR

SEND_A_DIT 
	MOVLW 0X01		; передаем точку
	MOVWF NBR_DIT
	MOVWF NBR_SPACE		; после точки - пауза в 1 точку
	BSF PORTA,0		; включаем PORT A.2
	CALL SEND_TONE          ; генерируем звук, оно же - пауза
	BCF PORTA,0		; выключаем PORT A.2
	CALL SEND_SPACE
	GOTO SEND_ONE_CHAR

PAUSE2
	MOVLW D'2'		; пауза в 2 точки после буквы	
	MOVWF NBR_SPACE		; 
	CALL SEND_SPACE         ;
	RETURN

WORD_SPACE
	MOVLW D'6'		; пауза в 6 точек между словами	
	MOVWF NBR_SPACE		; 
	CALL SEND_SPACE         ;
	RETURN

;==================================================================
; SEND_TONE
; Подпрограмма осуществляет задержку и одновременно формирует сигнал
; звуковой частоты (порт A.1)
;

SEND_TONE
 
	MOVF NBR_DIT, W		; NBR_DIT кол-во пропускаемых точек 
	MOVWF CTR_NBR		; 

DIT_LENGTH 
	MOVF CW_SPEED, W	; CW_SPEED скорость передачи
	MOVWF CTR_DIT		;

START_CYCLE 
	BSF PORTA,1		; A.1=1 в начале цикла
	MOVF SET_TONE,W		; 
	MOVWF TONE		; заносим значение в счетчик цикла задержки

HIGH_HALF 
	DECFSZ TONE, F		; считаем до нуля
	GOTO HIGH_HALF
	BCF PORTA,1		; A.1=0 в конце цикла

	MOVF SET_TONE,W		;
	MOVWF TONE

LOW_HALF 
	DECFSZ TONE, F		; 
	GOTO LOW_HALF
	DECFSZ CTR_DIT, F	;
	GOTO START_CYCLE
	DECFSZ CTR_NBR, F
	GOTO DIT_LENGTH		; 
	RETURN			; 

;================================================================
; SEND_SPACE
; Делает тоже самое, что и SEND_TONE, только вместо команд управления
; портами стоит команда NOP
;

SEND_SPACE
 
	MOVF NBR_SPACE, W	; 
	MOVWF CTR_NBR

DIT_LENGTH_SP 
	MOVF CW_SPEED, W	; 
	MOVWF CTR_DIT

START_CYCLE_SP
	NOP
	MOVF SET_TONE,W
	MOVWF TONE		; 

HIGH_HALF_SP 
	DECFSZ TONE, F		; 
	GOTO HIGH_HALF_SP
	NOP
	MOVF SET_TONE,W
	MOVWF TONE

LOW_HALF_SP 
	DECFSZ TONE, F		; 
	GOTO LOW_HALF_SP
	DECFSZ CTR_DIT, F	;
	GOTO START_CYCLE_SP
	DECFSZ CTR_NBR, F
	GOTO DIT_LENGTH_SP	; 
	RETURN			; 

;==================================================================
;==================================================================
; Идентификатор
	ORG  0x2000	; стартовый адрес

        DATA 0x0000
        DATA 0x0000
        DATA 0x0000
        DATA 0x0000

;==================================================================
; Слово конфигурации
	ORG  0x2007	; стартовый адрес

	DATA 0x31E4

;==================================================================
; Запись в EEPROM при программировании
; Переключателем адресуется номер ячейки, кратный 4. Соответственно, маяк
; может передавать последовательно три символа Морзе, четвертым обязательно
; должен быть EOM
; В последнюю адресуемую ячейку можно записать большее количество символов

	ORG  0X2100	; стартовый адрес

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
        DATA 0xFF	; EOM     11111111
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
        DATA 0x02	; E .     00000010  	
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x04	; I ..    00000100
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x08	; S ...   00001000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x10	; H ....  00010000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x20	; 5 ..... 00100000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x03	; T -     00000011
        DATA 0xFF	; EOM     11111111

	DATA 0x03	; T -     00000011
        DATA 0xFF	; EOM     11111111
        DATA 0xFF	; EOM     11111111
        DATA 0xFF	; EOM     11111111

	END

Версия прошивки 3.2, циклы 30с/120с; 60с/60с; 1м/4м


;-Передает в режиме лисы код Морзе (MO, MOE, MOI, MOS, MOH, MO5, RA1AMO). Вариант кода задается переключателем.
;-Может работать непрерывно или циклом: 30с/120с; 60с/60с; 1м/4м.
;-Есть выход 1000 Гц для модуляции маяка на 144 МГц.
;-Автоотключение через 8 часов, без учета ОС.
;-Потребление в выключенном состоянии менее 0.08 ма, во время работы, при передаче позывного, 0.5 ма.
;-Уход часов определяется кварцем.
;-Код с переключателя считывается один раз после сброса.
;-Режим отложенного старта (ОС), задержка 120 минут
;-Питание от 3 до 5 вольт 
;
; PIC16F630
; WDT off, intLC, MCLR on, BOR on, PWRTE on
;
	list	P=16F630
	radix	HEX

;====================================================================
; именование пользовательских регистров
;	
 	CBLOCK	0X20	; начальный адрес пользовательских регистров
        TMPW		; временный регистр для хранения W во время прерывания
	TMPS		; временный регистр для хранения STATUS                                                    
	SEC_CNT		; счетчик пар секунд
	MIN_CNT 	; счетчик минут (пятиминуток, автовыключение)
	FLAG		; регистр флагов:
			; FLAG,0 разрешает передачу позывного в свое время
			; FLAG,2 флаг задержки включения (1-можно работать)
                        ; FLAG,3 флаг взводится через 5 мин после начала работы
			; и отменяет задержку включения при сбросе
			; FLAG,4 флаг устанавливается за 10 сек до конца цикла


	DELAY		; счетчик пятиминуток, задержка включения
	TX_ALL_CNT	; счетчик пар секунд в цикле
	TX_OFF		; с какой секунды пауза
	TX_ALL		; сколько пар секунд в цикле

	SWITCH		; состояние переключателя
	CHAR_INDEX	; номер передаваемого символа

	CW_SPEED	; скорость передачи

; процедура SEND_TONE
	NBR_DIT		; NBR_DIT кол-во пропускаемых точек
	CTR_NBR		; счетчик пропускаемых точек
	TONE		; счетчик частоты
	SET_TONE	; установка тона передаваемого сигнала
; процедура SEND_SPACE
	NBR_SPACE	; колво пропускаемых точек
	CTR_DIT		; счетчик пропускаемых точек
; процедура SEND_ONE_CHAR
	CW_CHAR		; передаваемый символ
	ENDC


;
; Именование регистров микроконтроллера
;
CMCON		EQU	0X19	; регистр контроля компаратора
TMR0		EQU	0X01	; timer0
TMR1L		EQU	0X0E	; timer1 младший байт
TMR1H		EQU	0X0F	; timer1 старший байт     
T1CON		EQU	0X10	; timer1 control register
PCL		EQU	0X02	; программный счетчик
STATUS		EQU	0X03	; регистр status
FSR		EQU	0X04	; указатель адреса в памяти данных
PORTA		EQU	0X05	; порт A
PORTC		EQU	0X07	; порт C
PCLATH		EQU	0X0A	; верхние 5 бит программного счетчика
INTCON		EQU	0X0B	; регистр контроля прерываний
TRISA		EQU	0X85	; управляющий регистр порта A
TRISC		EQU	0X87	; управляющий регистр порта C 
OPTION_REG	EQU	0X81	; регистр option
EEADR		EQU	0X9B	; адресный регистр EEPROM
EECON1		EQU	0X9C	; содержит бит RD
EEDAT		EQU	0X9A	; прочитанные из EEPROM данные
PCON		EQU	0X8E	; регистр power control
PIE1		EQU	0X8C	; регистр контроля периферийных прерываний
PIR1		EQU	0X0C	; регистр флагов прерываний от 
				; периферийных модулей

;
; присвоение значений символьным константам
;
RP0		EQU	0X05	; бит-указатель страницы данных в STATUS
W		EQU	0X00	; file register designator = W
F		EQU	0X01	; file register designator = F 
Z		EQU	0X02	; бит нуля в STATUS 
C		EQU	0X00	; бит преноса в STATUS
RD		EQU	0X00	; инициализация чтения EEPROM
POR		EQU	0X01	; бит POR регистра PCON обнуляется, если был
				; сброс по включению питания
BOR		EQU	0X00	; сброс по снижению питания
TMR1IF		EQU	0X00	; бит переполнения TMR1 in register PIR1
TMR1ON		EQU	0X00	;
GIE		EQU	0X07	;
TMR1IE		EQU	0X00	; бит разрешения прерываний от TMR1
PON		EQU	0X00	; POWER ON IN FLAG
;
;=================================================================
; vector table
;
	ORG 0X000		; стартовый адрес	
	GOTO START		; 

;==================================================================
; TIMER1 INTERRUPT 
;
	ORG 0X004		; адрес	прерывания
	MOVWF TMPW		; сохраняем W
	SWAPF STATUS,W
	MOVWF TMPS		; сохраняем STATUS
	
	BTFSC PIR1,0		; прерывание от TMR1?
	GOTO T1_INT		; да
        NOP			; обработчик левого прерывания
	GOTO END_ISR		; 

T1_INT
	MOVF TX_ALL_CNT,W	; вычитание
	SUBWF TX_OFF,W		; TX_OFF-TX_ALL_CNT
	
	BCF FLAG,0		;
	SKPC			; пропустить, если результат >=0
	BSF FLAG,0		;


;	MOVF TX_ALL_CNT,W	; ускоряемся за 10 сек до конца цикла
;	SUBLW D'125'		; ставим флаг, по которому во время передачи
;	SKPNZ			; меняем скорость
;	BSF FLAG,4		; только при 5 мин цикле

				; использовать либо верхнее, либо нижнее

	;SUBLW D'250'		; а это костыль для ускорения за 10 сек
	;SKPNZ			; до конца любого цикла, даже если
	;BSF FLAG,4		; работаем непрерывно

	DECFSZ TX_ALL_CNT,F	; TX_ALL_CNT-1, пропустить, если 0
	GOTO POP_TX
	MOVF TX_ALL,W		; восстанавливаем
	MOVWF TX_ALL_CNT	;

;	BCF FLAG,4		; в конце цикла сбросить флаг ускорения
POP_TX

       	DECFSZ SEC_CNT, F	; SEC_CNT-1
	GOTO POP		; 
	MOVLW D'150'		; 300 секунд = 5 мин
	MOVWF SEC_CNT
	
	BTFSS FLAG,2		; если задержка активирована, то
	BSF FLAG,3		; через 5 мин после запуска поднимаем флаг
				; отмены задержки включения

	BTFSC FLAG,2		; если флаг разрешения работы установлен, начинаем отсчет 
				; задержки отключения
	DECF MIN_CNT,F		; MIN_CNT-1 минуты работы считаем после 
				; окончании задержки включения


	DECFSZ DELAY,F		; DELAY-1, пропустить след, если 0
	GOTO POP		; задержка окончилась
	BSF FLAG,2		; устанавливаем флаг разрешения работы	


POP	
	BCF PIR1,0		; сброс флага переполнения

END_ISR
	SWAPF TMPS,W		; восстанавливаем регистры W and SATUS
	MOVWF STATUS		;
	SWAPF TMPW,F		;
	SWAPF TMPW,W		;
	RETFIE			; возврат из прерывания

;==================================================================
;  Начало основной программы 
;==================================================================
START	
	MOVLW B'10001110'       ;
	BSF STATUS, RP0		; bank 1
	MOVWF OPTION_REG	; инициализация регистра option
	BCF STATUS, RP0		; bank 0	
			
	CLRF PORTA		; очистка порта A 
	CLRF PORTC		; очистка порта C

	MOVLW B'00000111'	; 
	MOVWF CMCON		; запрещаем работу компаратора

	BSF STATUS, RP0		; bank 1
	MOVLW B'00000000'	; 
	MOVWF TRISA		; порт A - все выходы
	MOVLW B'11111111'	; 
	MOVWF TRISC		; порт C - все входы
	BCF STATUS, RP0		; bank 0	

	CLRF TMR0		; на всякий случай. Говорят, надо...
	CLRWDT			; сбрасываем WDT

	BSF PORTA,2		; на 11 ноге висят подтягивающие резисторы,
				; подключенные к переключателю
				; после считывания напряжение с переключателя
				; снимается для экономии энергии

;==================================================================
; инициализация TMR1
;
	CLRF T1CON		; очистить TIMER1 control register
	CLRF TMR1H		; очистить верхний байт TMR1
	CLRF TMR1L		; очистить нижний байт TMR1
	CLRF INTCON		; запретить прерывания
	BSF STATUS,RP0		; bank 1
	CLRF PIE1		; очистить рег контроля периферийных прерываний
	BCF STATUS, RP0		; bank 0	
	CLRF PIR1		; очистить рег флагов периферийных прерываний
	MOVLW B'00001110'	; инициализация TIMER1 control register
	MOVWF T1CON		; с этого момента генератор 32.768 включен


;==================================================================

	MOVLW D'150'		; инициализация счетчика пар секунд
	MOVWF SEC_CNT		; 150 пар = 300 сек = 5 мин
        DECF SEC_CNT,F		; имитируем прерывание

	;MOVLW D'2'		; сколько пятиминуток будем работать	
	MOVLW D'96'		; сколько пятиминуток будем работать
	MOVWF MIN_CNT		; 96х5=8 час

;	MOVLW D'2'		; сколько пятиминуток задержка включения
	MOVLW D'24'		; сколько пятиминуток задержка включения
	MOVWF DELAY		; 24х5=120 мин

	MOVLW D'178'		; тон звука, 178 ~ 1000 Гц
	MOVWF SET_TONE		; чем больше, тем ниже частота

	BSF FLAG,0		; поднимаем флаг

;	BCF FLAG,4


;==================================================================
; если это включение питания, то выполняется задержка для
; гарантированного запуска генератора 32768 и его инициализация
; если другой сброс, то инициализация и задержка пропускаются
;
	BSF STATUS, RP0		; bank 1
	MOVF PCON,W		; PCON -> W
	BCF STATUS, RP0		; bank 0

	MOVWF TMPW		; W -> TMPW
	BTFSS TMPW,POR		; проверяем состояние POR, если 0, то был сброс
	GOTO DELAY_START	; по включению - задержка однозначно нужна
	BTFSC TMPW,BOR          ; а это был сброс по снижению напр питания
        GOTO SKIP_DELAY         ; или не был...
	
DELAY_START

	BCF FLAG,3		; при первом включении очистить FLAG,3

	; задержка 
	MOVLW D'255'		; скорость передачи 
	MOVWF CW_SPEED		; чем больше, тем медленней
	 
 	MOVLW 0X80	
	MOVWF CW_CHAR		; 
        CALL SEND_ONE_CHAR	; в качестве задержки предаем 7 точек

	BSF STATUS, RP0		; bank 1
	BSF PCON,POR		; устанавливаем POR=1
	BSF PCON,BOR		; устанавливаем BOR=1
	BCF STATUS, RP0		; bank 0

	SLEEP			; ожидаем запуск

SKIP_DELAY


;==================================================================
; читаем переключатель
;
        MOVF PORTC,W		; читаем порт C
	ANDLW B'00011100'	; маска 
	MOVWF SWITCH		; сохраняем состояние переключателя

        MOVF PORTC,W		; читаем порт C
	ANDLW B'00000011'	; маска двух младших бит

	BCF FLAG,2		; обнуляем флаг задержки
	BTFSC PORTC,5		; если перекл. on (на землю, 0) пропуск. след.
	BSF FLAG,2		; устанавливаем флаг, можно работать

	BCF PORTA,2		; отключаем подтягивающие резисторы

	ADDWF PCL,F		; таблица
	GOTO SW00
	GOTO SW01
	GOTO SW10
	GOTO SW11
SW00			; непрерывно
	MOVLW D'0'		; молчим последние 0 с
	MOVWF TX_OFF		;
	MOVLW D'30'		; цикл 60 сек
	GOTO END_SW
SW01			; цикл 30сек/120сек
	MOVLW D'60'		; молчим последние 120 с
	MOVWF TX_OFF		;
	MOVLW D'75'		; цикл 150 сек
	GOTO END_SW
SW10			; цикл 1 мин/1 мин
	MOVLW D'30'		; молчим последние 60 с
	MOVWF TX_OFF		;
	MOVLW D'60'		; цикл 120 сек
	GOTO END_SW
SW11			; цикл 1 мин/4 мин
	MOVLW D'120'		; молчим последние 4 m
	MOVWF TX_OFF		;
	MOVLW D'150'		; цикл 5 m

END_SW
	MOVWF TX_ALL		;
	MOVWF TX_ALL_CNT	; инициализация	
        DECF TX_ALL_CNT,F	; считаем, что 1 прерывание уже случилось

;==================================================================
; Окончание инициализации TIMER1
;

	MOVLW B'11000000'	; разрешить прерывания от периферийных модулей
	MOVWF INTCON

	BSF STATUS,RP0		; bank 1
	BSF PIE1,TMR1IE		; разрешить прерывания от TMR1
	BCF STATUS, RP0		; bank 0	
	BSF T1CON,TMR1ON        ; TMR1 ON


;==================================================================
; Если с момента запуска прошло 5 мин, то при сбросе задержку 
; включения игнорируем

	BTFSC FLAG,3		; FLAG,3 поднимается через 5 м после начала
				; работы
	BSF FLAG,2		; задержка включения не нужна

;==================================================================
; MAIN
; передача последовательности символов
; можно предавать любое количество, 0XFF означает конец последовательности,
; после чего формируется пауза и передача циклически повторяется
;
SEND_MSG
                                ; если автоотключение не нужно, то три 
				; нижеследующих строчки надо закомментировать
	MOVF MIN_CNT,F		; проверяем, не обнулился ли 
	SKPNZ			; счетчик пятиминуток
	GOTO POWER_OFF		; если да, то выключаемся

	BTFSS FLAG,2		; пропустить след, если задержка включения
				; кончилась (1-разрешение работы)
	GOTO FLASH		; мигнуть и sleep

        BTFSS FLAG,0		; если флаг сброшен, то идем спать
	GOTO TO_SLEEP
	
	MOVF SWITCH,W
	MOVWF CHAR_INDEX	; SWITCH -> CHAR_INDEX

GET_MSG
	MOVF CHAR_INDEX,W		
	BCF INTCON,GIE		; запрещаем прерывания
	BSF STATUS, RP0		; bank 1
	MOVWF EEADR		; значение CHAR_INDEX = адресу кода в EEPROM
	BSF EECON1,RD		; разрешаем чтение EEPROM
	MOVF EEDAT,W		; значение ячейки -> регистр W
	BCF STATUS, RP0		; bank 0	 
	BSF INTCON,GIE		; разрешаем прерывания
	;MOVLW 0x06
	MOVWF CW_CHAR		; W -> CW_CHAR	

	MOVLW 0XFF		; конец сообщения?
	SUBWF CW_CHAR,W		;
	SKPNZ
	GOTO END_MSG		; да, перейти к END_MSG

;	MOVLW D'60'		; скорость передачи быстро
;	BTFSS FLAG,4		; устанавливается за 10 с до конца цикла
	MOVLW D'160'		; скорость передачи медленно
	MOVWF CW_SPEED		; чем больше, тем медленней

	CALL SEND_CHAR		; передаем букву и паузу в 3 точки
                                ; или паузу 6 точек, если SPACE
	INCF CHAR_INDEX		; CHAR_INDEX+1	
	GOTO GET_MSG

END_MSG
	CALL WORD_SPACE         ; пауза 6 точек 
	GOTO SEND_MSG		;	

				; время работы закончилось
POWER_OFF			; отсюда выход только через RESET

	CLRF FLAG		; очистка регистра флагов
	CLRF T1CON		; очистить TIMER1 control register, выключаем
				; генератор 32768
	;BCF T1CON,TMR1ON       ; TMR1 OFF
	CLRF INTCON		; запретить прерывания

				; после сброса считаем, что это сброс по питанию
	BSF STATUS, RP0		; bank 1
	BCF PCON,POR		; устанавливаем POR=0
	BCF PCON,BOR		; устанавливаем BOR=0
	BCF STATUS, RP0		; bank 0

TO_SLEEP			;
	CLRF PORTA
	SLEEP
	NOP
	GOTO SEND_MSG
;========================================================================
; индикатор включенной задержки
; мигаем порт A.1
FLASH
	MOVLW D'60'		; скорость передачи 
	MOVWF CW_SPEED		; чем больше, тем медленней
	BSF PORTA,1
	MOVLW 0X01		;
	MOVWF NBR_SPACE		; пауза в 1 точку
	CALL SEND_SPACE		;
	BCF PORTA,1
	GOTO TO_SLEEP
;========================================================================
; SEND_CHAR
; Подпрограмма получает код символа в регистре CW_CHAR, побитно перебирает 
; его, и, в зависимости от состояния бита, передает: тире, если бит = 1;
; точку, если бит = 0.
; Знак передается, пока в слове есть хоть одна единица. Последняя единица
; "съедается", она означает конец слова. 
;
SEND_CHAR
	MOVLW 0X00		; пауза?
	SUBWF CW_CHAR,W		;
	SKPNZ			; 
	GOTO WORD_SPACE

SEND_ONE_CHAR 
        BTFSS FLAG,0		; обрываем передачу, если флаг сброшен
	RETURN

	CLRC			; очистить бит переноса
	RRF CW_CHAR, F		; сдвиг вправо через перенос
				; 
	MOVF CW_CHAR, F         ; проверяем содержимое CW_CHAR, если все "0"
	SKPNZ                   ; то Z=1, выполняем RETURN, иначе пропускаем
	GOTO PAUSE2		; конец буквы
	SKPC			; проверить бит переноса, пропустить, если=1
	GOTO SEND_A_DIT		; перейти к SEND_A_DIT

SEND_A_DAH			; передаем тире 
	MOVLW 0X03		; 1 тире = 3 точки
	MOVWF NBR_DIT
	MOVLW 0X01		;
	MOVWF NBR_SPACE		; после тире - пауза в 1 точку
	BSF PORTA,0		; включаем PORT A.2
	CALL SEND_TONE		; генериуем звук, оно же - пауза
	BCF PORTA,0		; выключаем PORT A.2
	CALL SEND_SPACE
	GOTO SEND_ONE_CHAR

SEND_A_DIT 
	MOVLW 0X01		; передаем точку
	MOVWF NBR_DIT
	MOVWF NBR_SPACE		; после точки - пауза в 1 точку
	BSF PORTA,0		; включаем PORT A.2
	CALL SEND_TONE          ; генерируем звук, оно же - пауза
	BCF PORTA,0		; выключаем PORT A.2
	CALL SEND_SPACE
	GOTO SEND_ONE_CHAR

PAUSE2
	MOVLW D'2'		; пауза в 2 точки после буквы	
	MOVWF NBR_SPACE		; 
	CALL SEND_SPACE         ;
	RETURN

WORD_SPACE
	MOVLW D'6'		; пауза в 6 точек между словами	
	MOVWF NBR_SPACE		; 
	CALL SEND_SPACE         ;
	RETURN

;==================================================================
; SEND_TONE
; Подпрограмма осуществляет задержку и одновременно формирует сигнал
; звуковой частоты (порт A.1)
;

SEND_TONE
 
	MOVF NBR_DIT, W		; NBR_DIT кол-во пропускаемых точек 
	MOVWF CTR_NBR		; 

DIT_LENGTH 
	MOVF CW_SPEED, W	; CW_SPEED скорость передачи
	MOVWF CTR_DIT		;

START_CYCLE 
	BSF PORTA,1		; A.1=1 в начале цикла
	MOVF SET_TONE,W		; 
	MOVWF TONE		; заносим значение в счетчик цикла задержки

HIGH_HALF 
	DECFSZ TONE, F		; считаем до нуля
	GOTO HIGH_HALF
	BCF PORTA,1		; A.1=0 в конце цикла

	MOVF SET_TONE,W		;
	MOVWF TONE

LOW_HALF 
	DECFSZ TONE, F		; 
	GOTO LOW_HALF
	DECFSZ CTR_DIT, F	;
	GOTO START_CYCLE
	DECFSZ CTR_NBR, F
	GOTO DIT_LENGTH		; 
	RETURN			; 

;================================================================
; SEND_SPACE
; Делает тоже самое, что и SEND_TONE, только вместо команд управления
; портами стоит команда NOP
;

SEND_SPACE
 
	MOVF NBR_SPACE, W	; 
	MOVWF CTR_NBR

DIT_LENGTH_SP 
	MOVF CW_SPEED, W	; 
	MOVWF CTR_DIT

START_CYCLE_SP
	NOP
	MOVF SET_TONE,W
	MOVWF TONE		; 

HIGH_HALF_SP 
	DECFSZ TONE, F		; 
	GOTO HIGH_HALF_SP
	NOP
	MOVF SET_TONE,W
	MOVWF TONE

LOW_HALF_SP 
	DECFSZ TONE, F		; 
	GOTO LOW_HALF_SP
	DECFSZ CTR_DIT, F	;
	GOTO START_CYCLE_SP
	DECFSZ CTR_NBR, F
	GOTO DIT_LENGTH_SP	; 
	RETURN			; 

;==================================================================
;==================================================================
; Идентификатор
	ORG  0x2000	; стартовый адрес

        DATA 0x0132
        DATA 0xF321
        DATA 0x0000
        DATA 0x0000

;==================================================================
; Слово конфигурации
	ORG  0x2007	; стартовый адрес

;	DATA 0x31E4
	DATA 0x1024	; вкл защиту данных
;	DATA 0x11A4
;==================================================================
; Запись в EEPROM при программировании
; Переключателем адресуется номер ячейки, кратный 4. Соответственно, маяк
; может передавать последовательно три символа Морзе, четвертым обязательно
; должен быть EOM
; В последнюю адресуемую ячейку можно записать большее количество символов

	ORG  0X2100	; стартовый адрес

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
        DATA 0xFF	; EOM     11111111
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
        DATA 0x02	; E .     00000010  	
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x04	; I ..    00000100
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x08	; S ...   00001000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x10	; H ....  00010000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x20	; 5 ..... 00100000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x03	; T -     00000011
        DATA 0xFF	; EOM     11111111

	DATA 0x0A	; R .-.   
        DATA 0x06	; A .-
        DATA 0x3E	; 1 .----
        DATA 0x06	; A .-
        DATA 0x07	; M --
        DATA 0x0F	; O ---
        DATA 0xFF	; EOM     11111111

	END

Вариант для управления радиостанцией, 30с/120с; 60с/60с; 1м/4м, передача позывного

Контроллер маяка на PIC16F630 в корпусе
Схема для управления маяком для городской охоты, возможно подключение обычной радиостанции
; на 11 ногу можно повесить управление TX (высокий уровень при передаче)

; PIC16F630
; WDT off, intLC, MCLR on, BOR on, PWRTE on
;
	list	P=16F630
	radix	HEX

;====================================================================
; именование пользовательских регистров
;	
 	CBLOCK	0X20	; начальный адрес пользовательских регистров
        TMPW		; временный регистр для хранения W во время прерывания
	TMPS		; временный регистр для хранения STATUS                                                    
	SEC_CNT		; счетчик пар секунд
	MIN_CNT 	; счетчик минут (пятиминуток, автовыключение)
	FLAG		; регистр флагов:
			; FLAG,0 разрешает передачу позывного в свое время
			; FLAG,2 флаг задержки включения (1-можно работать)
                        ; FLAG,3 флаг взводится через 5 мин после начала работы
			; и отменяет задержку включения при сбросе
			; FLAG,4 флаг устанавливается за 10 сек до конца цикла


	DELAY		; счетчик пятиминуток, задержка включения
	TX_ALL_CNT	; счетчик пар секунд в цикле
	TX_OFF		; с какой секунды пауза
	TX_ALL		; сколько пар секунд в цикле

	SWITCH		; состояние переключателя
	CHAR_INDEX	; номер передаваемого символа

	CW_SPEED	; скорость передачи

; процедура SEND_TONE
	NBR_DIT		; NBR_DIT кол-во пропускаемых точек
	CTR_NBR		; счетчик пропускаемых точек
	TONE		; счетчик частоты
	SET_TONE	; установка тона передаваемого сигнала
; процедура SEND_SPACE
	NBR_SPACE	; колво пропускаемых точек
	CTR_DIT		; счетчик пропускаемых точек
; процедура SEND_ONE_CHAR
	CW_CHAR		; передаваемый символ
	ENDC


;
; Именование регистров микроконтроллера
;
CMCON		EQU	0X19	; регистр контроля компаратора
TMR0		EQU	0X01	; timer0
TMR1L		EQU	0X0E	; timer1 младший байт
TMR1H		EQU	0X0F	; timer1 старший байт     
T1CON		EQU	0X10	; timer1 control register
PCL		EQU	0X02	; программный счетчик
STATUS		EQU	0X03	; регистр status
FSR		EQU	0X04	; указатель адреса в памяти данных
PORTA		EQU	0X05	; порт A
PORTC		EQU	0X07	; порт C
PCLATH		EQU	0X0A	; верхние 5 бит программного счетчика
INTCON		EQU	0X0B	; регистр контроля прерываний
TRISA		EQU	0X85	; управляющий регистр порта A
TRISC		EQU	0X87	; управляющий регистр порта C 
OPTION_REG	EQU	0X81	; регистр option
EEADR		EQU	0X9B	; адресный регистр EEPROM
EECON1		EQU	0X9C	; содержит бит RD
EEDAT		EQU	0X9A	; прочитанные из EEPROM данные
PCON		EQU	0X8E	; регистр power control
PIE1		EQU	0X8C	; регистр контроля периферийных прерываний
PIR1		EQU	0X0C	; регистр флагов прерываний от 
				; периферийных модулей

;
; присвоение значений символьным константам
;
RP0		EQU	0X05	; бит-указатель страницы данных в STATUS
W		EQU	0X00	; file register designator = W
F		EQU	0X01	; file register designator = F 
Z		EQU	0X02	; бит нуля в STATUS 
C		EQU	0X00	; бит преноса в STATUS
RD		EQU	0X00	; инициализация чтения EEPROM
POR		EQU	0X01	; бит POR регистра PCON обнуляется, если был
				; сброс по включению питания
BOR		EQU	0X00	; сброс по снижению питания
TMR1IF		EQU	0X00	; бит переполнения TMR1 in register PIR1
TMR1ON		EQU	0X00	;
GIE		EQU	0X07	;
TMR1IE		EQU	0X00	; бит разрешения прерываний от TMR1
PON		EQU	0X00	; POWER ON IN FLAG
;
;=================================================================
; vector table
;
	ORG 0X000		; стартовый адрес	
	GOTO START		; 

;==================================================================
; TIMER1 INTERRUPT 
;
	ORG 0X004		; адрес	прерывания
	MOVWF TMPW		; сохраняем W
	SWAPF STATUS,W
	MOVWF TMPS		; сохраняем STATUS
	
	BTFSC PIR1,0		; прерывание от TMR1?
	GOTO T1_INT		; да
        NOP			; обработчик левого прерывания
	GOTO END_ISR		; 

T1_INT
	MOVF TX_ALL_CNT,W	; вычитание
	SUBWF TX_OFF,W		; TX_OFF-TX_ALL_CNT
	
	BCF FLAG,0		;
	SKPC			; пропустить, если результат >=0
	BSF FLAG,0		;


	





;	MOVF TX_ALL_CNT,W	; ускоряемся за 10 сек до конца цикла
;	SUBLW D'125'		; ставим флаг, по которому во время передачи
;	SKPNZ			; меняем скорость
;	BSF FLAG,4		; только при 5 мин цикле

				; использовать либо верхнее, либо нижнее

	;SUBLW D'250'		; а это костыль для ускорения за 10 сек
	;SKPNZ			; до конца любого цикла, даже если
	;BSF FLAG,4		; работаем непрерывно

	DECFSZ TX_ALL_CNT,F	; TX_ALL_CNT-1, пропустить, если 0
	GOTO POP_TX
	MOVF TX_ALL,W		; восстанавливаем
	MOVWF TX_ALL_CNT	;

;	BCF FLAG,4		; в конце цикла сбросить флаг ускорения
POP_TX

       	DECFSZ SEC_CNT, F	; SEC_CNT-1
	GOTO POP		; 
	MOVLW D'150'		; 300 секунд = 5 мин
	MOVWF SEC_CNT
	
	BTFSS FLAG,2		; если задержка активирована, то
	BSF FLAG,3		; через 5 мин после запуска поднимаем флаг
				; отмены задержки включения

	BTFSC FLAG,2		; если флаг разрешения работы установлен, начинаем отсчет 
				; задержки отключения
	DECF MIN_CNT,F		; MIN_CNT-1 минуты работы считаем после 
				; окончании задержки включения


	DECFSZ DELAY,F		; DELAY-1, пропустить след, если 0
	GOTO POP		; задержка окончилась
	BSF FLAG,2		; устанавливаем флаг разрешения работы	


POP	
	BCF PIR1,0		; сброс флага переполнения

END_ISR
	SWAPF TMPS,W		; восстанавливаем регистры W and SATUS
	MOVWF STATUS		;
	SWAPF TMPW,F		;
	SWAPF TMPW,W		;
	RETFIE			; возврат из прерывания

;==================================================================
;  Начало основной программы 
;==================================================================
START	
	MOVLW B'10001110'       ;
	BSF STATUS, RP0		; bank 1
	MOVWF OPTION_REG	; инициализация регистра option
	BCF STATUS, RP0		; bank 0	
			
	CLRF PORTA		; очистка порта A 
	CLRF PORTC		; очистка порта C

	MOVLW B'00000111'	; 
	MOVWF CMCON		; запрещаем работу компаратора

	BSF STATUS, RP0		; bank 1
	MOVLW B'00000000'	; 
	MOVWF TRISA		; порт A - все выходы
	MOVLW B'11111111'	; 
	MOVWF TRISC		; порт C - все входы
	BCF STATUS, RP0		; bank 0	

	CLRF TMR0		; на всякий случай. Говорят, надо...
	CLRWDT			; сбрасываем WDT


;==================================================================
; инициализация TMR1
;
	CLRF T1CON		; очистить TIMER1 control register
	CLRF TMR1H		; очистить верхний байт TMR1
	CLRF TMR1L		; очистить нижний байт TMR1
	CLRF INTCON		; запретить прерывания
	BSF STATUS,RP0		; bank 1
	CLRF PIE1		; очистить рег контроля периферийных прерываний
	BCF STATUS, RP0		; bank 0	
	CLRF PIR1		; очистить рег флагов периферийных прерываний
	MOVLW B'00001110'	; инициализация TIMER1 control register
	MOVWF T1CON		; с этого момента генератор 32.768 включен


;==================================================================

	MOVLW D'150'		; инициализация счетчика пар секунд
	MOVWF SEC_CNT		; 150 пар = 300 сек = 5 мин
        DECF SEC_CNT,F		; имитируем прерывание

	;MOVLW D'2'		; сколько пятиминуток будем работать	
	MOVLW D'96'		; сколько пятиминуток будем работать
	MOVWF MIN_CNT		; 96х5=8 час

;	MOVLW D'2'		; сколько пятиминуток задержка включения
	MOVLW D'24'		; сколько пятиминуток задержка включения
	MOVWF DELAY		; 24х5=120 мин

	MOVLW D'178'		; тон звука, 178 ~ 1000 Гц
	MOVWF SET_TONE		; чем больше, тем ниже частота

	BSF FLAG,0		; поднимаем флаг

;	BCF FLAG,4


;==================================================================
; если это включение питания, то выполняется задержка для
; гарантированного запуска генератора 32768 и его инициализация
; если другой сброс, то инициализация и задержка пропускаются
;
	BSF STATUS, RP0		; bank 1
	MOVF PCON,W		; PCON -> W
	BCF STATUS, RP0		; bank 0

	MOVWF TMPW		; W -> TMPW
	BTFSS TMPW,POR		; проверяем состояние POR, если 0, то был сброс
	GOTO DELAY_START	; по включению - задержка однозначно нужна
	BTFSC TMPW,BOR          ; а это был сброс по снижению напр питания
        GOTO SKIP_DELAY         ; или не был...
	
DELAY_START

	BCF FLAG,3		; при первом включении очистить FLAG,3

	; задержка 
	MOVLW D'255'		; скорость передачи 
	MOVWF CW_SPEED		; чем больше, тем медленней
	 
 	MOVLW 0X80	
	MOVWF CW_CHAR		; 
        CALL SEND_ONE_CHAR	; в качестве задержки предаем 7 точек

	BSF STATUS, RP0		; bank 1
	BSF PCON,POR		; устанавливаем POR=1
	BSF PCON,BOR		; устанавливаем BOR=1
	BCF STATUS, RP0		; bank 0

	SLEEP			; ожидаем запуск

SKIP_DELAY


;==================================================================
; читаем переключатель
;

	BSF PORTA,2		; на 11 ноге висят подтягивающие резисторы,
				; подключенные к переключателю
				; после считывания напряжение с переключателя
				; снимается для экономии энергии


        MOVF PORTC,W		; читаем порт C
	ANDLW B'00011100'	; маска 
	MOVWF SWITCH		; сохраняем состояние переключателя

        MOVF PORTC,W		; читаем порт C
	ANDLW B'00000011'	; маска двух младших бит

	BCF FLAG,2		; обнуляем флаг задержки
	BTFSC PORTC,5		; если перекл. on (на землю, 0) пропуск. след.
	BSF FLAG,2		; устанавливаем флаг, можно работать

	BCF PORTA,2		; отключаем подтягивающие резисторы

	ADDWF PCL,F		; таблица
	GOTO SW00
	GOTO SW01
	GOTO SW10
	GOTO SW11
SW00			; непрерывно
	MOVLW D'0'		; молчим последние 0 с
	MOVWF TX_OFF		;
	MOVLW D'30'		; цикл 60 сек
	GOTO END_SW
SW01			; цикл 30сек/120сек
	MOVLW D'60'		; молчим последние 120 с
	MOVWF TX_OFF		;
	MOVLW D'75'		; цикл 150 сек
	GOTO END_SW
SW10			; цикл 1 мин/1 мин
	MOVLW D'30'		; молчим последние 60 с
	MOVWF TX_OFF		;
	MOVLW D'60'		; цикл 120 сек
	GOTO END_SW
SW11			; цикл 1 мин/4 мин
	MOVLW D'120'		; молчим последние 4 m
	MOVWF TX_OFF		;
	MOVLW D'150'		; цикл 5 m

END_SW
	MOVWF TX_ALL		;
	MOVWF TX_ALL_CNT	; инициализация	
        DECF TX_ALL_CNT,F	; считаем, что 1 прерывание уже случилось

;==================================================================
; Окончание инициализации TIMER1
;

	MOVLW B'11000000'	; разрешить прерывания от периферийных модулей
	MOVWF INTCON

	BSF STATUS,RP0		; bank 1
	BSF PIE1,TMR1IE		; разрешить прерывания от TMR1
	BCF STATUS, RP0		; bank 0	
	BSF T1CON,TMR1ON        ; TMR1 ON


;==================================================================
; Если с момента запуска прошло 5 мин, то при сбросе задержку 
; включения игнорируем

	BTFSC FLAG,3		; FLAG,3 поднимается через 5 м после начала
				; работы
	BSF FLAG,2		; задержка включения не нужна

;==================================================================
; MAIN
; передача последовательности символов
; можно предавать любое количество, 0XFF означает конец последовательности,
; после чего формируется пауза и передача циклически повторяется
;
SEND_MSG
                                ; если автоотключение не нужно, то три 
				; нижеследующих строчки надо закомментировать
	MOVF MIN_CNT,F		; проверяем, не обнулился ли 
	SKPNZ			; счетчик пятиминуток
	GOTO POWER_OFF		; если да, то выключаемся

	BTFSS FLAG,2		; пропустить след, если задержка включения
				; кончилась (1-разрешение работы)
	GOTO FLASH		; мигнуть и sleep

        BTFSS FLAG,0		; если флаг сброшен
	BSF PORTA,2		; TX выключаем

        BTFSS FLAG,0		; если флаг сброшен, то идем спать
	GOTO TO_SLEEP
	

	BSF PORTA,2		; на 11 ногу можно повесить управление TX 


	MOVF SWITCH,W
	MOVWF CHAR_INDEX	; SWITCH -> CHAR_INDEX

GET_MSG
	MOVF CHAR_INDEX,W		
	BCF INTCON,GIE		; запрещаем прерывания
	BSF STATUS, RP0		; bank 1
	MOVWF EEADR		; значение CHAR_INDEX = адресу кода в EEPROM
	BSF EECON1,RD		; разрешаем чтение EEPROM
	MOVF EEDAT,W		; значение ячейки -> регистр W
	BCF STATUS, RP0		; bank 0	 
	BSF INTCON,GIE		; разрешаем прерывания
	;MOVLW 0x06
	MOVWF CW_CHAR		; W -> CW_CHAR	

	MOVLW 0XFF		; конец сообщения?
	SUBWF CW_CHAR,W		;
	SKPNZ
	GOTO END_MSG		; да, перейти к END_MSG

;	MOVLW D'60'		; скорость передачи быстро
;	BTFSS FLAG,4		; устанавливается за 10 с до конца цикла
;
	MOVLW D'80'		; скорость передачи медленно
	MOVWF CW_SPEED		; чем больше, тем медленней

	CALL SEND_CHAR		; передаем букву и паузу в 3 точки
                                ; или паузу 6 точек, если SPACE
	INCF CHAR_INDEX		; CHAR_INDEX+1	
	GOTO GET_MSG

END_MSG
	CALL WORD_SPACE         ; пауза 6 точек 
	GOTO SEND_MSG		;	

				; время работы закончилось
POWER_OFF			; отсюда выход только через RESET

	CLRF FLAG		; очистка регистра флагов
	CLRF T1CON		; очистить TIMER1 control register, выключаем
				; генератор 32768
	;BCF T1CON,TMR1ON       ; TMR1 OFF
	CLRF INTCON		; запретить прерывания

				; после сброса считаем, что это сброс по питанию
	BSF STATUS, RP0		; bank 1
	BCF PCON,POR		; устанавливаем POR=0
	BCF PCON,BOR		; устанавливаем BOR=0
	BCF STATUS, RP0		; bank 0

TO_SLEEP			;
	CLRF PORTA
	SLEEP
	NOP
	GOTO SEND_MSG
;========================================================================
; индикатор включенной задержки
; мигаем порт A.1
FLASH
	MOVLW D'60'		; скорость передачи 
	MOVWF CW_SPEED		; чем больше, тем медленней
	BSF PORTA,1
	MOVLW 0X01		;
	MOVWF NBR_SPACE		; пауза в 1 точку
	CALL SEND_SPACE		;
	BCF PORTA,1
	GOTO TO_SLEEP
;========================================================================
; SEND_CHAR
; Подпрограмма получает код символа в регистре CW_CHAR, побитно перебирает 
; его, и, в зависимости от состояния бита, передает: тире, если бит = 1;
; точку, если бит = 0.
; Знак передается, пока в слове есть хоть одна единица. Последняя единица
; "съедается", она означает конец слова. 
;
SEND_CHAR
	MOVLW 0X00		; пауза?
	SUBWF CW_CHAR,W		;
	SKPNZ			; 
	GOTO WORD_SPACE

SEND_ONE_CHAR 
        BTFSS FLAG,0		; обрываем передачу, если флаг сброшен
	RETURN

	CLRC			; очистить бит переноса
	RRF CW_CHAR, F		; сдвиг вправо через перенос
				; 
	MOVF CW_CHAR, F         ; проверяем содержимое CW_CHAR, если все "0"
	SKPNZ                   ; то Z=1, выполняем RETURN, иначе пропускаем
	GOTO PAUSE2		; конец буквы
	SKPC			; проверить бит переноса, пропустить, если=1
	GOTO SEND_A_DIT		; перейти к SEND_A_DIT

SEND_A_DAH			; передаем тире 
	MOVLW 0X03		; 1 тире = 3 точки
	MOVWF NBR_DIT
	MOVLW 0X01		;
	MOVWF NBR_SPACE		; после тире - пауза в 1 точку
	BSF PORTA,0		; включаем PORT A.2
	CALL SEND_TONE		; генериуем звук, оно же - пауза
	BCF PORTA,0		; выключаем PORT A.2
	CALL SEND_SPACE
	GOTO SEND_ONE_CHAR

SEND_A_DIT 
	MOVLW 0X01		; передаем точку
	MOVWF NBR_DIT
	MOVWF NBR_SPACE		; после точки - пауза в 1 точку
	BSF PORTA,0		; включаем PORT A.2
	CALL SEND_TONE          ; генерируем звук, оно же - пауза
	BCF PORTA,0		; выключаем PORT A.2
	CALL SEND_SPACE
	GOTO SEND_ONE_CHAR

PAUSE2
	MOVLW D'2'		; пауза в 2 точки после буквы	
	MOVWF NBR_SPACE		; 
	CALL SEND_SPACE         ;
	RETURN

WORD_SPACE
	MOVLW D'6'		; пауза в 6 точек между словами	
	MOVWF NBR_SPACE		; 
	CALL SEND_SPACE         ;
	RETURN

;==================================================================
; SEND_TONE
; Подпрограмма осуществляет задержку и одновременно формирует сигнал
; звуковой частоты (порт A.1)
;

SEND_TONE
 
	MOVF NBR_DIT, W		; NBR_DIT кол-во пропускаемых точек 
	MOVWF CTR_NBR		; 

DIT_LENGTH 
	MOVF CW_SPEED, W	; CW_SPEED скорость передачи
	MOVWF CTR_DIT		;

START_CYCLE 
	BSF PORTA,1		; A.1=1 в начале цикла
	MOVF SET_TONE,W		; 
	MOVWF TONE		; заносим значение в счетчик цикла задержки

HIGH_HALF 
	DECFSZ TONE, F		; считаем до нуля
	GOTO HIGH_HALF
	BCF PORTA,1		; A.1=0 в конце цикла

	MOVF SET_TONE,W		;
	MOVWF TONE

LOW_HALF 
	DECFSZ TONE, F		; 
	GOTO LOW_HALF
	DECFSZ CTR_DIT, F	;
	GOTO START_CYCLE
	DECFSZ CTR_NBR, F
	GOTO DIT_LENGTH		; 
	RETURN			; 

;================================================================
; SEND_SPACE
; Делает тоже самое, что и SEND_TONE, только вместо команд управления
; портами стоит команда NOP
;

SEND_SPACE
 
	MOVF NBR_SPACE, W	; 
	MOVWF CTR_NBR

DIT_LENGTH_SP 
	MOVF CW_SPEED, W	; 
	MOVWF CTR_DIT

START_CYCLE_SP
	NOP
	MOVF SET_TONE,W
	MOVWF TONE		; 

HIGH_HALF_SP 
	DECFSZ TONE, F		; 
	GOTO HIGH_HALF_SP
	NOP
	MOVF SET_TONE,W
	MOVWF TONE

LOW_HALF_SP 
	DECFSZ TONE, F		; 
	GOTO LOW_HALF_SP
	DECFSZ CTR_DIT, F	;
	GOTO START_CYCLE_SP
	DECFSZ CTR_NBR, F
	GOTO DIT_LENGTH_SP	; 
	RETURN			; 

;==================================================================
;==================================================================
; Идентификатор
	ORG  0x2000	; стартовый адрес

        DATA 0x0132
        DATA 0xF321
        DATA 0x0000
        DATA 0x0000

;==================================================================
; Слово конфигурации
	ORG  0x2007	; стартовый адрес

;	DATA 0x31E4
	DATA 0x1024	; вкл защиту данных
;	DATA 0x11A4
;==================================================================
; Запись в EEPROM при программировании
; Переключателем адресуется номер ячейки, кратный 4. Соответственно, маяк
; может передавать последовательно три символа Морзе, четвертым обязательно
; должен быть EOM
; В последнюю адресуемую ячейку можно записать большее количество символов

	ORG  0X2100	; стартовый адрес

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
        DATA 0xFF	; EOM     11111111
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
        DATA 0x02	; E .     00000010  	
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x04	; I ..    00000100
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x08	; S ...   00001000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x10	; H ....  00010000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x20	; 5 ..... 00100000
        DATA 0xFF	; EOM     11111111

	DATA 0x07	; M --    00000111
	DATA 0x0F	; O ---   00001111
	DATA 0x03	; T -     00000011
        DATA 0xFF	; EOM     11111111

	DATA 0x09	; D
	DATA 0x02	; E
	DATA 0x00	; PAUSE
	DATA 0x0A	; R .-.   
        DATA 0x06	; A .-
        DATA 0x3E	; 1 .----
        DATA 0x06	; A .-
        DATA 0x07	; M --
        DATA 0x0E	; W ---
	DATA 0x00	; PAUSE
	DATA 0x11	; B	
	DATA 0x02	; E
	DATA 0x06	; A
	DATA 0x15	; C	
	DATA 0x0F	; O ---   00001111
	DATA 0x05	; N	
        DATA 0xFF	; EOM     11111111

	END

Программирование кодов Морзе

DATA 0x3F	; 0 ----- 00111111
DATA 0x3E	; 1 .---- 00111110  	
DATA 0x3C	; 2 ..--- 00111100
DATA 0x38	; 3 ...-- 00111000	
DATA 0x30	; 4 ....- 00110000
DATA 0x20	; 5 ..... 00100000
DATA 0x21	; 6 -.... 00100001
DATA 0x23	; 7 --... 00100011
DATA 0x27	; 8 ---.. 00100111
DATA 0x2F	; 9 ----. 00101111

DATA 0x06	; A .-    00000110	
DATA 0x11	; B -...  00010001
DATA 0x15	; C -.-.  00010101
DATA 0x09	; D -..   00001001
DATA 0x02	; E .     00000010	
DATA 0X14	; F ..-.  00010100
DATA 0X0B	; G --.   00001011
DATA 0x10	; H ....  00010000
DATA 0x04	; I ..    00000100
DATA 0x1E	; J .---  00011110
DATA 0x0D	; K -.-   00001101	
DATA 0x12	; L .-..  00010010
DATA 0x07	; M --    00000111
DATA 0x05	; N -.    00000101
DATA 0x0F	; O ---   00001111
DATA 0x16	; P .--.  00010110
DATA 0x1B	; Q --.-  00011011
DATA 0x0A	; R .-.   00001010
DATA 0x08	; S ...   00001000
DATA 0x03	; T -     00000011
DATA 0x0C	; U ..-   00001100	
DATA 0x18	; V ...-  00011000
DATA 0x0E	; W .--   00001110
DATA 0X19	; X -..-  00011001
DATA 0X1D	; Y -.--  00011101
DATA 0X13	; Z --..  00010011
DATA 0x29	; / -..-. 00101001	
DATA 0x00	; PAUSE   00000000
DATA 0xFF	; EOM     11111111