Приложение 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