Приложение 3. Текст программы.

;***********************************************************************
;  Программа GSM шлюза by RA9FTMike
;
;  частота кварца 4,608 Мгц
;
;порт D:
; PD0 - RXD (вход UART)
; PD1 - TXD (выход UART)
; PD2 - вход внешнего прерывния INT0 (получаем строб от DTMF приёмника)
;
; PD4 - выход, звуковой сигнал
; PD5 - вход, 1-труба лежитт, 0-труба поднята
; PD6 - выход, 1-посылается вызов на PABX, 0-вызова нет
;
;порт B:
;PB0,PB1,PB2,PB3 биты для считывания тетрады с DTMF приёмника:
; 1 0001
; 2 0010
; 3 0011
; 4 0100
; 5 0101
; 6 0110
; 7 0111
; 8 1000
; 9 1001
; 0 1010
; * 1011
; # 1100
; A 1101
; B 1110
; C 1111
; D 0000
;
; PB5 - Frequency RING 560 Hz
; PB7 - вкл./выкл. внешней гарнитуры (аудио сигналы)

;
;***********************************************************************
.include "2313def.inc"       ;подключить файл описаний имен 
                             ;регистов ввода/вывода 

.DEVICE AT90S2313


;------------ Задание символических имен

;флаг о состоянии:
.def flags =r18
; бит 0 - была принята первая цифра

.def    tmp1       =r19    ;Оперативный регистр 1
.def    tmp2       =r20    ;Оперативный регистр 2
.def    tmp3       =r21    ;Оперативный регистр 3
.def    tmp4       =r22    ;Оперативный регистр 4
.def    tmp5       =r23    ;Оперативный регистр 5

.def tcnt0_add1=r24 ;дополнение к счётчику 0

;-------------- Установка векторов прерывания
.org    0                    ;В эту точку процессор попадает после сброса
rjmp start

; обработка внешнего прерывния INT0
.org INT0addr ;
rjmp EXT_INT0   ; Обработка внешнего IRQ0

; прерывание по переполнению счётчика 1
.org OVF1addr
rjmp TIM_OVF1

;-------------- Инициализация:
start:


ldi tmp1,RAMEND   ;Установка стека
out spl,tmp1

; Выключить питание аналогового компаратора
; по умолчанию - вкл.
; temp:
ldi tmp1,0b10000000
out ACSR,tmp1

;----------- инициализация UART -----------
;прерывания выключены, TXEN=1,RXEN=1, 
        ldi tmp1,0b00011000

;ldi tmp1,0b00011101  ;! попробуем так

        out UCR,tmp1            
; скорость нужно подсчитать для нужной частоты...
; (зависит от частоты кварца)

        ldi tmp1,4 
        out UBRR,tmp1


;------------------ инициализация порта D
; PD0 - RXD (вход UART)
; PD1 - TXD (выход UART)
; PD2 - вход внешнего прерывния INT0 (получаем строп от DTMF приёмника)
;
; PD4 - выход, звуковой сигнал
; PD5 - вход, 1-труба лежитт, 0-труба поднята
; PD6 - выход, 1-идет звонок в телефон, 0-звонка нет

; направление данных вход - 0, выход - 1
ldi tmp1,0b01010010
out ddrd,tmp1

; подтягивающие резисторы
ldi tmp1,0b00100000
out portd,tmp1


;------------------- инициализация порта B
;PB0,PB1,PB2,PB3 биты для считывания тетрады с DTMF приёмника:
; PB5 - выход, Frequency RING 560 Hz
; PB7 - выход, вкл./выкл. внешней гарнитуры (аудио сигналы)

; направление данных вход - 0, выход - 1
ldi tmp1,0b11100000
out ddrb,tmp1

; включим подтягивающие резисторы
ldi tmp1,0b11011111
out portb,tmp1 ;включили подтягивающие резисторы


;------------------ irq0 init
; регистр MCUCR
; запрос на прерывание по нарастающему фронту на входе INT0
ldi tmp1,0b00000011
out MCUCR,tmp1

; регистр GIMSK
; Бит 6 - INT0: Запрос внешнего прерывания 0 разрешен
ldi tmp1,0b01000000
out GIMSK,tmp1


;------------------- Регистр X
; адрес ОЗУ, куда будем записывать номер телефона
ldi XL,0x60
clr XH

;------------------- инициализвция таймера 1

; если таймер был запущен, остановить его
; действует, когда начинаем сначала...
ldi tmp1,0b00000000
out TCCR1B,tmp1

; регистр TIMSK - 
ldi tmp1,0b00000000
out timsk,tmp1

;--------------------------
; регистр флагов прерываний, очистим, записав единицу
; чтоб цифра, если была, не принималась
ldi tmp1,0b01000000
out GIFR,tmp1


clr tmp1
clr tmp2
clr tmp3
clr tmp4
clr flags


; установить флаг I регистра SREG процессора
; тем самым разрешим обработку прерываний
sei 


;------------------- главная программа
loop:

cli

sbis pind,5
rjmp truba_wzjata

sbic pind,5
rjmp truba_levit
loop_1:


rcall RING_REC
cpi tmp1,'R'
BREQ DO_RING
sei

rjmp loop
;------------------- конец главной программы

truba_levit:
ldi XL,0x60
clr XH
; остановить счетчик
ldi tmp1,0b00000000
out TCCR1B,tmp1
; выключить прерывания по переполнению таймеров 
ldi tmp1,0b00000000
out timsk,tmp1

cbr flags,0b00000001 //флаг, принята 1-а цифра, сбросить бит

rjmp loop_1



truba_wzjata:
sbrs flags,0    //пропуск, если набрана одна или более цифр
rjmp switch_1	//обработка взятия трубки
rjmp loop_1


DO_RING:

sbis pind,5	//пропуск, если бит в регистре установлен, труба лежит
rjmp switch_1	//
sbi portd,6	//включить звонок

clr tmp4
ldi tmp5,2

DO_RING_WAIT:


//генерация
sbi portb,5

ldi tmp1,0x55
ldi tmp2,0x06
do_ring_tone_wait_1:
dec tmp1
brne do_ring_tone_wait_1
dec tmp2
brne do_ring_tone_wait_1

cbi portb,5

ldi tmp1,0x55
ldi tmp2,0x06
do_ring_tone_wait_2:
dec tmp1
brne do_ring_tone_wait_2
dec tmp2
brne do_ring_tone_wait_2
//генерация


dec tmp4
brne DO_RING_WAIT
dec tmp5
brne DO_RING_WAIT


cbi portd,6    //выключить звонок

clr tmp2
clr tmp3
ldi tmp4,17
DO_RING_WAIT_2:
sbis pind,5   //пропуск, если бит в регистре установлен, труба лежит

rjmp switch_1

rcall RING_REC
cpi tmp1,'R'
breq DO_RING   // если пришел новый 'RING 0x0d' снова ухоим на подачу звонка

dec tmp2
brne DO_RING_WAIT_2
dec tmp3
brne DO_RING_WAIT_2
dec tmp4
brne DO_RING_WAIT_2


rjmp start  //таймаут закончился, звонки прекратились, сброс


// ответ на вызов
SEND_ATA:
ldi tmp1,0x06
rcall uart_tx
ldi tmp1,0x04
rcall uart_tx
ldi tmp1,0x02
rcall uart_tx
ldi tmp1,'A'
rcall uart_tx
ldi tmp1,'T'
rcall uart_tx
ldi tmp1,'A'
rcall uart_tx
ldi tmp1,0x0d
rcall uart_tx


rjmp WAIT_ATH

;------------ посмотреть порт и если что вдруг пришло, принять
; Если пришел RING, оставить в регистре tmp1 байт 'R'
; иначе очистить

RING_REC:
sbis usr,rxc
rjmp RING_REC_END
in tmp1,udr
cpi tmp1,'R'
brne RING_REC_END

ldi tmp4,1

clr tmp2
clr tmp3
RING_REC_WAIT:

sbic usr,rxc
rjmp RING_REC_1

dec tmp2
brne RING_REC_WAIT
dec tmp3
brne RING_REC_WAIT
rjmp RING_REC_END

RING_REC_1:
cpi tmp4,1
brne RING_REC_2
in tmp1,udr
cpi tmp1,'I'
brne RING_REC_END
ldi tmp4,2
rjmp RING_REC_WAIT
RING_REC_2:
cpi tmp4,2
brne RING_REC_3
in tmp1,udr
cpi tmp1,'N'
brne RING_REC_END
ldi tmp4,3
rjmp RING_REC_WAIT
RING_REC_3:
cpi tmp4,3
brne RING_REC_4
in tmp1,udr
cpi tmp1,'G'
brne RING_REC_END
ldi tmp4,4
rjmp RING_REC_WAIT
RING_REC_4:
cpi tmp4,4
brne RING_REC_END
in tmp1,udr
cpi tmp1,0x0d
brne RING_REC_END

ldi tmp1,'R'
ret


RING_REC_END:
clr tmp1
ret



;------------------- переполнение таймера/счетчика 1
; выдать команду ATD <номер>;0x0d

TIM_OVF1:

cli ; запретить все прерывания

; остановить счетчик
ldi tmp1,0b00000000
out TCCR1B,tmp1

;запретить оба внешних прерывания
ldi tmp1,0b00000000
out GIMSK,tmp1

; выключить прерывания по счетчикам
ldi tmp1,0b00000000
out timsk,tmp1

ldi tmp1,0x3b ;   ";"
st X+,tmp1
ldi tmp1,0x0d
st X,tmp1

clr tmp2
; адрес ОЗУ, откуда будем читать номер телефона
ldi XL,0x60
clr XH
TIM_OVF1_1:
inc tmp2
ld tmp1,X+
cpi tmp1,0x0d
brne TIM_OVF1_1
ldi tmp1,4
add tmp2,tmp1 ; в tmp2 получили кол-во знаков в команде

ldi tmp1,0x06
rcall uart_tx
mov tmp1,tmp2
rcall uart_tx

ldi tmp1,0x06
eor tmp1,tmp2
rcall uart_tx

; "ATD "
ldi tmp1,'A'
rcall uart_tx
ldi tmp1,'T'
rcall uart_tx
ldi tmp1,'D'
rcall uart_tx
ldi tmp1,0x20
rcall uart_tx

ldi XL,0x60
clr XH
rjmp TIM_OVF1_3
TIM_OVF1_2:
rcall uart_tx
TIM_OVF1_3:
ld tmp1,X+
cpi tmp1,0x0d
brne TIM_OVF1_2
rcall uart_tx
rjmp WAIT_ATH
reti


WAIT_ATH:
// задержка, в случае просечек
clr tmp2
clr tmp3
SEND_ATA_WAIT:
dec tmp2
brne SEND_ATA_WAIT
dec tmp3
brne SEND_ATA_WAIT

WAIT_ATH_1:
rcall no_carrier_check
cpi tmp1,'N'
breq busy
sbis pind,5  //пропуск если бит в порту установлен, если трубка лежит
rjmp WAIT_ATH_1

ath_now:

ldi tmp1,0x06

rcall uart_tx
ldi tmp1,0x04
rcall uart_tx
ldi tmp1,0x02
rcall uart_tx
ldi tmp1,'A'
rcall uart_tx
ldi tmp1,'T'
rcall uart_tx
ldi tmp1,'H'
rcall uart_tx
ldi tmp1,0x0d
rcall uart_tx


rjmp start ;начнем сначала...


;------------------------- busy_tx
busy:
cli

ldi tmp3,0xaa

busy_tone:

sbi portd,4
ldi tmp1,0x7F
ldi tmp2,0x08
busy_tone_wait_1:
dec tmp1
brne busy_tone_wait_1
dec tmp2
brne busy_tone_wait_1

cbi portd,4
ldi tmp1,0x7F
ldi tmp2,0x08
busy_tone_wait_2:
dec tmp1
brne busy_tone_wait_2
dec tmp2
brne busy_tone_wait_2

sbic pind,5
rjmp ath_now

dec tmp3
brne busy_tone

clr tmp3
clr tmp4
ldi tmp5,5
busy_tone_wait_3:
sbic pind,5
rjmp ath_now
dec tmp3
brne busy_tone_wait_3
dec tmp4
brne busy_tone_wait_3
dec tmp5
brne busy_tone_wait_3

rjmp busy




;------------------- INT0 чтение даных из dtmf приёмника
EXT_INT0:
cli ; запретить все прерывания
in tmp1, pinb
cbr tmp1,0b11110000 ;сбросить все ненужные флаги (обнулить)

cpi tmp1,0b00000001 ;сравнить с "1"
brne EXT_INT0_2 ;перейти, если не равно
ldi tmp2,0x31
rjmp EXT_INT0_OK

EXT_INT0_2:
cpi tmp1,0b00000010 ;  2
brne EXT_INT0_3
ldi tmp2,0x32
rjmp EXT_INT0_OK

EXT_INT0_3:
cpi tmp1,0b00000011 ;  3
brne EXT_INT0_4
ldi tmp2,0x33
rjmp EXT_INT0_OK

EXT_INT0_4:
cpi tmp1,0b00000100 ;  4
brne EXT_INT0_5
ldi tmp2,0x34
rjmp EXT_INT0_OK

EXT_INT0_5:
cpi tmp1,0b00000101 ;  5
brne EXT_INT0_6
ldi tmp2,0x35
rjmp EXT_INT0_OK

EXT_INT0_6:
cpi tmp1,0b00000110 ;  6
brne EXT_INT0_7
ldi tmp2,0x36
rjmp EXT_INT0_OK

EXT_INT0_7:
cpi tmp1,0b00000111 ;  7
brne EXT_INT0_8
ldi tmp2,0x37
rjmp EXT_INT0_OK

EXT_INT0_8:
cpi tmp1,0b00001000 ;  8
brne EXT_INT0_9
ldi tmp2,0x38
rjmp EXT_INT0_OK

EXT_INT0_9:
cpi tmp1,0b00001001 ;  9
brne EXT_INT0_0
ldi tmp2,0x39
rjmp EXT_INT0_OK

EXT_INT0_0:
cpi tmp1,0b00001010 ;  0
brne EXT_INT0_Z
ldi tmp2,0x30
rjmp EXT_INT0_OK

EXT_INT0_Z:
cpi tmp1,0b00001011 ;  * = '+'
brne EXT_INT0_R
ldi tmp2,'+'
rjmp EXT_INT0_OK

EXT_INT0_R:
cpi tmp1,0b00001100 ;  # = соединяться прямо после нажатия
brne EXT_INT0_BAD
sbrs flags,0
rjmp EXT_INT0_BAD
rjmp EXT_INT0_OK_NOW

EXT_INT0_BAD:
rjmp busy

EXT_INT0_OK_NOW:
ser tmp1
out tcnt1h,tmp1
out tcnt1l,tmp1
rjmp EXT_INT0_OK_NOW_CONTINUE

EXT_INT0_OK:

cpi XL,0x73         ; если => 20 цифр
brge EXT_INT0_END

st X+,tmp2 ; сохранить цифру в ОЗУ

; установить таймер/счётчик 1 на 5 секунд до переполнения
; (зависит от частоты кварца)

ldi tmp1,0xa8
out tcnt1h,tmp1
ldi tmp1,0x1c
out tcnt1l,tmp1

EXT_INT0_OK_NOW_CONTINUE:
; регистр TIMSK - разрешение прерывания по переполнению таймера 1
ldi tmp1,0b10000000
out timsk,tmp1

; определяется делитель СК/1024, стартовать счетчик
ldi tmp1,0b00000101
out TCCR1B,tmp1


sbr flags,0b00000001 ;принята первая цифра и больше


EXT_INT0_END:
sei ; разрешить прерывания
reti 
;---------------- завершение обработки прерывания INT0


;---------------- передача байта в uart из tmp1
uart_tx:

;Если бит UDRE в USR установлен, то пропустить cледующую команду
sbis USR, UDRE
;Вернуться на метку trans
rjmp uart_tx
out UDR, tmp1    ;Вывести в регистр данных передатчика UART
                    ;содержимое tmp1
ret


;---------------- ПП обработки взятия трубки
; после взятия отправляет комаду, спрашивает состояние
; либо ответ на вызов(ring), либо набор номера...

switch_1:
cli

ldi tmp1,0x06
rcall uart_tx
ldi tmp1,0x08
rcall uart_tx
ldi tmp1,0x0E
rcall uart_tx
ldi tmp1,'A'
rcall uart_tx
ldi tmp1,'T'
rcall uart_tx
ldi tmp1,'+'
rcall uart_tx
ldi tmp1,'C'
rcall uart_tx
ldi tmp1,'P'
rcall uart_tx
ldi tmp1,'A'
rcall uart_tx
ldi tmp1,'S'
rcall uart_tx
ldi tmp1,0x0d
rcall uart_tx

clr tmp2
clr tmp3
clr tmp4

switch_1_wait_1:
sbic usr,rxc  //пропуск если бит сброшен
rjmp switch_1_1
dec tmp2
brne switch_1_wait_1
dec tmp3
brne switch_1_wait_1
rjmp switch_1_bad

switch_1_1:
cpi tmp4,0
brne switch_1_2
in tmp1,udr
cpi tmp1,'+'
brne switch_1_wait_1
ldi tmp4,1
rjmp switch_1_wait_1


switch_1_2:
cpi tmp4,1
brne switch_1_3
in tmp1,udr
cpi tmp1,'C'
brne switch_1_bad
ldi tmp4,2
rjmp switch_1_wait_1


switch_1_3:
cpi tmp4,2
brne switch_1_4
in tmp1,udr
cpi tmp1,'P'
brne switch_1_bad
ldi tmp4,3
rjmp switch_1_wait_1


switch_1_4:
cpi tmp4,3
brne switch_1_5
in tmp1,udr
cpi tmp1,'A'
brne switch_1_bad
ldi tmp4,4
rjmp switch_1_wait_1

switch_1_5:
cpi tmp4,4
brne switch_1_6
in tmp1,udr
cpi tmp1,'S'
brne switch_1_bad
ldi tmp4,5
rjmp switch_1_wait_1


switch_1_6:
cpi tmp4,5
brne switch_1_7
in tmp1,udr
cpi tmp1,':'
brne switch_1_bad
ldi tmp4,6
rjmp switch_1_wait_1

switch_1_7:
cpi tmp4,6
brne switch_1_8
in tmp1,udr
cpi tmp1,' '
brne switch_1_bad
ldi tmp4,7
rjmp switch_1_wait_1

switch_1_8:
cpi tmp4,7
brne switch_1_bad
in tmp1,udr
cpi tmp1,0x30
brne switch_1_ok_1
rjmp switch_1_ok_2

switch_1_bad:
rjmp busy

switch_1_ok_1:
rjmp send_ata

switch_1_ok_2:  //посылать ответ станции

; регистр GIMSK
; Бит 6 - INT0: Запрос внешнего прерывания 0 разрешен
ldi tmp1,0b01000000
out GIMSK,tmp1


send_tone:
cli
sbi portd,4

ldi tmp1,0x7F
ldi tmp2,0x08
send_tone_wait_1:
dec tmp1
brne send_tone_wait_1
dec tmp2
brne send_tone_wait_1

cbi portd,4

ldi tmp1,0x7F
ldi tmp2,0x08
send_tone_wait_2:
dec tmp1
brne send_tone_wait_2
dec tmp2
brne send_tone_wait_2

sbic pind,5
rjmp start

sbrc flags,0
rjmp loop

sei
rjmp send_tone


;------------------------ "NO CARRIER" receiving
; если приняло, в tmp1 осталяет 'N'
; иначе очищает
no_carrier_check:

sbis usr,rxc
rjmp no_carrier_check_END
in tmp1,udr
cpi tmp1,'N'
brne no_carrier_check_END

ldi tmp4,1
clr tmp2
clr tmp3

no_carrier_check_WAIT:
sbic usr,rxc
rjmp no_carrier_check_1
dec tmp2
brne no_carrier_check_WAIT
dec tmp3
brne no_carrier_check_WAIT
rjmp no_carrier_check_END

no_carrier_check_1:
cpi tmp4,1
brne no_carrier_check_2
in tmp1,udr
cpi tmp1,'O'
brne no_carrier_check_END
ldi tmp4,2
rjmp no_carrier_check_WAIT
no_carrier_check_2:
cpi tmp4,2
brne no_carrier_check_3
in tmp1,udr
cpi tmp1,' '
brne no_carrier_check_END
ldi tmp4,3
rjmp no_carrier_check_WAIT
no_carrier_check_3:
cpi tmp4,3
brne no_carrier_check_4
in tmp1,udr
cpi tmp1,'C'
brne no_carrier_check_END
ldi tmp4,4
rjmp no_carrier_check_WAIT
no_carrier_check_4:
cpi tmp4,4
brne no_carrier_check_5
in tmp1,udr
cpi tmp1,'A'
brne no_carrier_check_END
ldi tmp4,5
rjmp no_carrier_check_WAIT

no_carrier_check_5:
cpi tmp4,5
brne no_carrier_check_END
in tmp1,udr
cpi tmp1,'R'
brne no_carrier_check_END


ldi tmp1,'N'
ret

no_carrier_check_END:
clr tmp1
ret


.exit



Результат компиляции:

AVRASM: AVR macro assembler 2.1.2 (build 99 Nov  4 2005 09:35:05)
Copyright (C) 1995-2005 ATMEL Corporation

D:\gsm_gate\gsm_gateway.asm(40): Including file 'd:\Program Files\Atmel\AVR Tools\AvrAssembler2\Appnotes\2313def.inc'
D:\ gsm_gate \gsm_gateway.asm(902): No EEPROM data, deleting D:\ gsm_gate\gsm_gateway.eep

AT90S2313 memory use summary [bytes]:
Segment   Begin    End      Code   Data   Used    Size   Use%
---------------------------------------------------------------
[.cseg] 0x000000 0x0003ee   1000      0   1000    2048  48.8%
[.dseg] 0x000060 0x000060      0      0      0     128   0.0%
[.eseg] 0x000000 0x000000      0      0      0     128   0.0%

Assembly complete, 0 errors. 0 warnings