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