Говорят же "не шутите с макросами"... У ПИКовых есть хитра ловушка для работающих с подпрограммами/условными переходами - содержимое "по умолчанию" в PCLATH и/или (у некоторых) прямая оговорка на условие - "все подпрограммы/обслуживание прерываний только на нулевой странице памяти программ". Как только размещение точки обращения выходит за пределы нулевой страницы (объем прожки более примитива) так сей подвох и всплывает. Для 0х0000-0х0700 проблемы незаметно. Стоит метке попасть в 0х0800 и выше - имеем "фокус".
Сперва в нем управление ногой ТХ было сделано через bsf/bcf, но так как это неправильно, я решил переделать через битовые маски......
А чем маски отличаются от команд установки бит? Если Вы намекаете на RMW, то маски на порт никак от этого не спасают, потому что команды bcf/bsf делают ровно тоже самое. Только аппаратно. За один машинный цикл. Для защиты от перезаписи в порт физического состояния ног необходимо организовать переменную-буфер порта, которая заменит защелку выходных портов в этом МК отсутствующую в нем аппаратно. И выводить в порт нужно именно эту переменную.
BOB51 писал(а):
Говорят же "не шутите с макросами"... Стоит метке попасть в 0х0800 и выше - имеем "фокус".
Какая связь макросов с объемом и расположением кода? 0х800 - это примерно 2000-ная строка кода(с учетом 2-х словных call и goto). Из слов автора вопроса не следует, что он располагает свой код так далеко. Особенно если учесть, что 0x3FF- это ПОСЛЕДНИЙ адрес программной памяти...
... Для защиты от перезаписи в порт физического состояния ног необходимо организовать переменную-буфер порта, которая заменит защелку выходных портов в этом МК отсутствующую в нем аппаратно. И выводить в порт нужно именно эту переменную.
Спасибо, очень полезная для меня информация. Буду гуглить реализацию, потому что в микрочиповских документах я такой рекомендации не вычитывал. Насчет вылезания за 0 страницу памяти, так это невозможно, в этом ПИКе всего 1 Кб памяти, а страницы по 2 Кб идут. В качестве доказательства - код из дизасcеммблера с адресами: Спойлер
Код:
--- D:\Docs\Dropbox\Embed\PIC\Projects\Smart_Home\STP_1b\PIC16F84_master_control\Matrix_keyboard_Libs.asm 1: include p16f84.inc 2: include Matrix_keyboard.h 3: 4: Matrix_ketboard_udata udata_shr 5: key_scan res 1 6: key_addr res 1 7: key_char res 1 8: debounce_counter res 1 9: 10: #define Yes_Press flags,0 11: 12: global key_scan,key_char 13: global Matrix_Keyboard_ISR,Init_Matrix_Keyboard,Key_Read,Key_Convert,Release_Key,Anti_Debounce 14: extern flags 15: extern End_Interrupt_Heandler,flags 16: 17: Matrix_keyboard code 18: 19: ;========== Обработчик прерывания по изминению состояния RB7-RB4 20: Matrix_Keyboard_ISR 04D 205D CALL 0x5d 21: call Key_Read ; Было нажатие, считывам кнопку 04E 206E CALL 0x6e 22: call Key_Convert 04F 1410 BSF 0x10, 0 23: bsf Yes_Press ; Установим флаг нажатой кнокпи 050 100B BCF 0xb, 0 24: bcf INTCON,RBIF ; Сбросим флаг вызваного прерывания 051 280F GOTO 0xf 25: goto End_Interrupt_Heandler 26: 27: ;========== Инициализация матричной клавитуры 28: Init_Matrix_Keyboard ; Begin of program 052 0186 CLRF 0x6 29: clrf PORTB ; Предварительно установим разряды столбцов в 0 053 1683 BSF 0x3, 0x5 30: banksel TRISB ; Select Bank 1 054 307F MOVLW 0x7f 31: movlw b'01111111' ; (0<<RBPU) - Enable pull-ups resistors on PortB 055 0581 ANDWF 0x1, F 32: andwf OPTION_REG 33: if (USE_3x4_KEYBOARD) 056 30F1 MOVLW 0xf1 34: movlw b'11110001' 35: else 36: movlw b'11110000' 37: endif 057 0086 MOVWF 0x6 38: movwf TRISB ; Разряды столбцов становятся выходами и опускаються в 0 39: 058 1283 BCF 0x3, 0x5 40: banksel PORTB ; Return Bank 0 059 100B BCF 0xb, 0 41: bcf INTCON,RBIF 05A 3008 MOVLW 0x8 42: movlw (1<<RBIE) ; Разрешам прерывания по изминению состояния порта RB 05B 048B IORWF 0xb, F 43: iorwf INTCON 05C 0008 RETURN 44: return 45: 46: ;========== Опрос клавиатуры. Помещение кода нажалой клавиши в key_scan 47: Key_Read 05D 0806 MOVF 0x6, W 48: movfw PORTB ; Считываем значение PORTB - это код строки 05E 39F0 ANDLW 0xf0 49: andlw b'11110000' ; Зануляем столбцы 05F 0095 MOVWF 0x15 50: movwf key_scan 060 1683 BSF 0x3, 0x5 51: banksel TRISB 52: if (USE_3x4_KEYBOARD) 061 300E MOVLW 0xe 53: movlw b'00001110' 54: else 55: movlw b'00001111' 56: endif 062 0086 MOVWF 0x6 57: movwf TRISB ; Устанавливаем строки выходами, а столбцы - входами 58: 063 1283 BCF 0x3, 0x5 59: banksel PORTB 064 0186 CLRF 0x6 60: clrf PORTB ; Опускаем все строки в 0 065 0806 MOVF 0x6, W 61: movfw PORTB ; Считываем значение PORTB - это код столбца 62: if (USE_3x4_KEYBOARD) 066 390E ANDLW 0xe 63: andlw b'00001110' ; Зануляем строки 64: else 65: andlw b'00001111' ; Зануляем строки 66: endif 067 0495 IORWF 0x15, F 67: iorwf key_scan ; Логическое сложение результата 68: ;---------- Reset keypad interface 068 1683 BSF 0x3, 0x5 69: banksel TRISB 70: if (USE_3x4_KEYBOARD) 069 30F1 MOVLW 0xf1 71: movlw b'11110001' 72: else 73: movlw b'11110000' 74: endif 06A 0086 MOVWF 0x6 75: movwf TRISB ; Возвращаем строки - как входы, а столбцы - как выходы 76: 06B 1283 BCF 0x3, 0x5 77: banksel PORTB 06C 0186 CLRF 0x6 78: clrf PORTB ; Гарантируем 0 выходные значения 06D 0008 RETURN 79: return 80: 81: ;========== Преобразование кода клавиши, содержащегося в key_scan, в ASCII код, и запись этого кода в key_char 82: Key_Convert 06E 1003 BCF 0x3, 0 83: clrc 06F 0196 CLRF 0x16 84: clrf key_addr 85: ;---------- Сперва определяем строку 070 1B95 BTFSC 0x15, 0x7 86: btfsc key_scan,7 071 2873 GOTO 0x73 87: goto kp1 072 2884 GOTO 0x84 88: goto col_find ; Если строка 1, key_scan без изминений 073 1B15 BTFSC 0x15, 0x6 89: kp1 btfsc key_scan,6 074 2878 GOTO 0x78 90: goto kp2 075 3004 MOVLW 0x4 91: movlw .4 076 0496 IORWF 0x16, F 92: iorwf key_addr ; Если строка 2, порядковый номер нажатой кнопки как минимум 3(4) 077 2884 GOTO 0x84 93: goto col_find 078 1A95 BTFSC 0x15, 0x5 94: kp2 btfsc key_scan,5 079 287D GOTO 0x7d 95: goto kp3 07A 3008 MOVLW 0x8 96: movlw .8 07B 0496 IORWF 0x16, F 97: iorwf key_addr ; Если строка 3, порядковый номер нажатой кнопки как минимум 6(8) 07C 2884 GOTO 0x84 98: goto col_find 07D 1A15 BTFSC 0x15, 0x4 99: kp3 btfsc key_scan,4 07E 2882 GOTO 0x82 100: goto kp4 07F 300C MOVLW 0xc 101: movlw .12 080 0496 IORWF 0x16, F 102: iorwf key_addr ; Если строка 4, порядковый номер нажатой кнопки как минимум 9(12) 081 2884 GOTO 0x84 103: goto col_find 104: kp4 082 3010 MOVLW 0x10 105: movlw .16 083 288E GOTO 0x8e 106: goto keypad_op ; Строка не распознана, возврат с кодом ошибки (E) через таблицу 107: col_find ; Теперь определяем столбец 084 1995 BTFSC 0x15, 0x3 108: btfsc key_scan,3 085 2887 GOTO 0x87 109: goto cf1 086 288E GOTO 0x8e 110: goto keypad_op ; Если столбец 1, key_scan без изминений 087 1915 BTFSC 0x15, 0x2 111: cf1 btfsc key_scan,2 088 288C GOTO 0x8c 112: goto cf2 089 3001 MOVLW 0x1 113: movlw .1 08A 0496 IORWF 0x16, F 114: iorwf key_addr ; Если столбец 2, порядковый номер нажатой кнопки надо увеличить на 1 08B 288E GOTO 0x8e 115: goto keypad_op 116: cf2 117: if (!USE_3x4_KEYBOARD) 118: btfsc key_scan,1 119: goto cf3 120: endif 08C 3002 MOVLW 0x2 121: movlw .2 08D 0496 IORWF 0x16, F 122: iorwf key_addr ; Если столбец 3, порядковый номер нажатой кнопки надо увеличить на 2 123: if (!USE_3x4_KEYBOARD) 124: goto keypad_op 125: cf3 movlw .3 126: iorwf key_addr ; Если столбец 3, порядковый номер нажатой кнопки надо увеличить на 3 127: endif 128: keypad_op 08E 0816 MOVF 0x16, W 129: movfw key_addr 08F 2092 CALL 0x92 130: call Key_Table 090 0097 MOVWF 0x17 131: movwf key_char ; сохраняем ASCII код нажатой кнопки 091 0008 RETURN 132: return 133: 134: ;========== Таблица преобразования кода клавиши в ASCII код символа 135: Key_Table 092 0782 ADDWF 0x2, F 136: addwf PCL 093 3431 RETLW 0x31 137: dt "123A456B789C*0#DE" 094 3432 RETLW 0x32 095 3433 RETLW 0x33 096 3441 RETLW 0x41 097 3434 RETLW 0x34 098 3435 RETLW 0x35 099 3436 RETLW 0x36 09A 3442 RETLW 0x42 09B 3437 RETLW 0x37 09C 3438 RETLW 0x38 09D 3439 RETLW 0x39 09E 3443 RETLW 0x43 09F 342A RETLW 0x2a 0A0 3430 RETLW 0x30 0A1 3423 RETLW 0x23 0A2 3444 RETLW 0x44 0A3 3445 RETLW 0x45 138: 139: ;========== Подпрограмма ожидания отпускания кнопки 140: Release_Key ; Крутимся в цикле и опрашиваем состояние кнопок, пока все не будут отпущены 0A4 205D CALL 0x5d 141: call Key_Read 0A5 0815 MOVF 0x15, W 142: movfw key_scan 143: if (USE_3x4_KEYBOARD) 0A6 39FE ANDLW 0xfe 144: andlw b'11111110' ; Подавляем RB0 0A7 3CFE SUBLW 0xfe 145: sublw b'11111110' ; Проверка на неактивность 146: else 147: sublw b'11111111' ; Проверка на неактивность 148: endif 0A8 1D03 BTFSS 0x3, 0x2 149: btfss STATUS,Z 0A9 28A4 GOTO 0xa4 150: goto Release_Key 0AA 0008 RETURN 151: return 152: 153: ;========== Подпрограмма антидребезговой задержки 154: Anti_Debounce 0AB 0198 CLRF 0x18 155: clrf debounce_counter 156: Loop_Anti_Debounce 0AC 0B98 DECFSZ 0x18, F 157: decfsz debounce_counter 0AD 28AC GOTO 0xac 158: goto Loop_Anti_Debounce 0AE 0008 RETURN 159: return --- D:\Docs\Dropbox\Embed\PIC\Projects\Smart_Home\STP_1b\PIC16F84_master_control\PIC16F84_master_control.asm 1: list p=16f84 2: __config _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC 3: include p16f84.inc 4: include Soft_UART_Lib_use_Timer0.h 5: include Matrix_keyboard.h 6: include Macro.h 7: 8: PIC16F84_Master_Control udata_shr 9: w_temp res 1 10: status_temp res 1 11: command res 1 12: num_of_module res 1 13: flags res 1 14: 15: #define Yes_Press flags,0 16: 17: extern TX_byte,RX_byte,UART_counter,delay_counter,key_scan,key_char 18: extern Ini_UART,Send_TX_byte,Delay_1bit,Start_bit,Receive_bit,Get_RX_byte,Init_Matrix_Keyboard,Key_Read,Key_Convert,Release_Key,Anti_Debounce,Matrix_Keyboard_ISR 19: global flags 20: global End_Interrupt_Heandler 21: ;====================== 22: RESET_VECTOR code 0h 000 2815 GOTO 0x15 23: goto Ini 24: ;====================== 25: ISR code 4h ; Вектор прерывания 26: Interrupt_Heandler 004 008C MOVWF 0xc 27: push ; Сохраним контекст 005 0E03 SWAPF 0x3, W 006 008D MOVWF 0xd 007 180B BTFSC 0xb, 0 28: btfsc INTCON,RBIF 008 284D GOTO 0x4d 29: goto Matrix_Keyboard_ISR 009 280B GOTO 0xb 30: goto UART_Receive ; Проверяем в последнюю очередь, потому что UART программный, и спец флага прерыванию у него нет 00A 280F GOTO 0xf 31: goto End_Interrupt_Heandler 32: 33: UART_Receive 34: if (USE_T0CKI) 00B 1683 BSF 0x3, 0x5 35: banksel OPTION_REG ; Bank 1 00C 1E81 BTFSS 0x1, 0x5 36: btfss OPTION_REG,T0CS ; Проверим источник тактирования таймера 00D 28C1 GOTO 0xc1 37: goto Receive_bit ; Если внутренний - это очередной бит 00E 28B9 GOTO 0xb9 38: goto Start_bit ; Если внешний - мы ждали старт-бит 39: else 40: btfss INTCON,INTE 41: goto Receive_bit ; Если запрещено внешнее прерывание - это очередной бит 42: goto Start_bit ; Если разрешено внешнее прерывание - мы ждали старт-бит 43: endif 44: 45: End_Interrupt_Heandler 46: if (!USE_T0CKI) 47: bcf INTCON,INTF ; Сбросим флаг внешнего прерывания 48: endif 00F 110B BCF 0xb, 0x2 49: bcf INTCON,T0IF ; Сбросим флаг переполнения Таймера 0 010 0E0D SWAPF 0xd, W 50: pop ; Восстановим контекст 011 0083 MOVWF 0x3 012 0E8C SWAPF 0xc, F 013 0E0C SWAPF 0xc, W 014 0009 RETFIE 51: retfie 52: 53: Ini 015 0190 CLRF 0x10 54: clrf flags 016 2052 CALL 0x52 55: call Init_Matrix_Keyboard 017 20CF CALL 0xcf 56: call Ini_UART ; Инициализация программного UART после инициализации матричной клавиатуры, потому что можем использовать RB0 57: 58: Main 59: ; call Get_RX_byte 60: ; incf RX_byte 61: ; movfw RX_byte 018 3055 MOVLW 0x55 62: movlw b'01010101' 019 20DC CALL 0xdc 63: call Send_TX_byte 01A 20AB CALL 0xab 64: call Anti_Debounce 01B 2818 GOTO 0x18 65: goto Main 66: 01C 1C10 BTFSS 0x10, 0 67: btfss Yes_Press ; Если не было нажатия, крутимся в цикле 01D 2818 GOTO 0x18 68: goto Main 01E 138B BCF 0xb, 0x7 69: bcf INTCON,GIE ; Будем передавать символ, запрещаем прерывания 01F 0817 MOVF 0x17, W 70: movfw key_char 020 20DC CALL 0xdc 71: call Send_TX_byte ; Отправим символ 72: ;---------- Антидребезговый комплекс из задержек и опросов состояния кнопок 021 20A4 CALL 0xa4 73: call Release_Key ; Ждем отпускания всех кнопок 022 20AB CALL 0xab 74: call Anti_Debounce ; Антидребезговая задержка 023 20A4 CALL 0xa4 75: call Release_Key ; Ждем отпускания всех кнопок 024 20AB CALL 0xab 76: call Anti_Debounce ; Антидребезговая задержка 025 20A4 CALL 0xa4 77: call Release_Key ; Ждем отпускания всех кнопок 78: ;---------- Конец антидребезгового комплекса 026 1010 BCF 0x10, 0 79: bcf Yes_Press ; Обработали нажатие, сбросим флаг 027 100B BCF 0xb, 0 80: bcf INTCON,RBIF ; Сбросим флаг возможно прошедшего прерывания 028 178B BSF 0xb, 0x7 81: bsf INTCON,GIE ; Закончили передачу символа, разрешаем прерывания 029 2818 GOTO 0x18 82: goto Main 83: 84: Wait_Num_of_Module ; Первая нажатая кнопка - это номер модуля 02A 1C10 BTFSS 0x10, 0 85: btfss flags,0 ; Если не было нажатия, крутимся в цикле 02B 282A GOTO 0x2a 86: goto Wait_Num_of_Module 02C 138B BCF 0xb, 0x7 87: bcf INTCON,GIE ; Запрещаем повторные прерывания по дребезгу 02D 0817 MOVF 0x17, W 88: movfw key_char 02E 390F ANDLW 0xf 89: andlw b'00001111' 02F 008F MOVWF 0xf 90: movwf num_of_module 91: ;---------- Антидребезговый комплекс из задержек и опросов состояния кнопок 030 20A4 CALL 0xa4 92: call Release_Key ; Ждем отпускания всех кнопок 031 20AB CALL 0xab 93: call Anti_Debounce ; Антидребезговая задержка 032 20A4 CALL 0xa4 94: call Release_Key ; Ждем отпускания всех кнопок 033 20AB CALL 0xab 95: call Anti_Debounce ; Антидребезговая задержка 034 20A4 CALL 0xa4 96: call Release_Key ; Ждем отпускания всех кнопок 97: ;---------- Конец антидребезгового комплекса 035 1010 BCF 0x10, 0 98: bcf flags,0 ; Обработали нажатие, сбросим флаг 036 100B BCF 0xb, 0 99: bcf INTCON,RBIF ; Сбросим флаг возможно прошедшего прерывания 037 178B BSF 0xb, 0x7 100: bsf INTCON,GIE ; Закончили передачу символа, разрешаем прерывания 101: Wait_Command ; Вторая нажатая кнопка - это команда 038 1C10 BTFSS 0x10, 0 102: btfss flags,0 ; Если не было нажатия, крутимся в цикле 039 2838 GOTO 0x38 103: goto Wait_Command 03A 138B BCF 0xb, 0x7 104: bcf INTCON,GIE ; Будем передавать символ, запрещаем прерывания 03B 0817 MOVF 0x17, W 105: movfw key_char 03C 390F ANDLW 0xf 106: andlw b'00001111' 03D 008E MOVWF 0xe 107: movwf command ; Сформируем команду 03E 0E0E SWAPF 0xe, W 108: swapf command,w ; Пепреместим команду в старшую тетраду 03F 048F IORWF 0xf, F 109: iorwf num_of_module ; И наложим маской на номер модуля 110: ; Получаем на выходе номер модуля (младшая тетрада) и переключаемый выход (старшая тетрада) 040 080F MOVF 0xf, W 111: movfw num_of_module ; Берем полученую составную команду 041 20DC CALL 0xdc 112: call Send_TX_byte ; И отправляем в сеть 113: ;---------- Антидребезговый комплекс из задержек и опросов состояния кнопок 042 20A4 CALL 0xa4 114: call Release_Key ; Ждем отпускания всех кнопок 043 20AB CALL 0xab 115: call Anti_Debounce ; Антидребезговая задержка 044 20A4 CALL 0xa4 116: call Release_Key ; Ждем отпускания всех кнопок 045 20AB CALL 0xab 117: call Anti_Debounce ; Антидребезговая задержка 046 20A4 CALL 0xa4 118: call Release_Key ; Ждем отпускания всех кнопок 119: ;---------- Конец антидребезгового комплекса 047 1010 BCF 0x10, 0 120: bcf flags,0 048 100B BCF 0xb, 0 121: bcf INTCON,RBIF ; Сбросим флаг возможно прошедшего прерывания 049 178B BSF 0xb, 0x7 122: bsf INTCON,GIE ; Закончили передачу символа, разрешаем прерывания 123: ; goto Wait_Num_of_Module 124: 04A 20AF CALL 0xaf 125: call Get_RX_byte ; Принимаем ответ (зациклимся в ожидании) 126: ; movfw RX_byte ; Засунем принятый байт в рабочий регистр 127: ; И анализируем ответ 04B 080F MOVF 0xf, W 128: movfw num_of_module 04C 0212 SUBWF 0x12, W 129: subwf RX_byte,w 130: 2007 2BFF GOTO 0x3ff 131: end --- D:\Docs\Dropbox\Embed\PIC\Projects\Smart_Home\STP_1b\PIC16F84_master_control\Soft_UART_Lib_use_Timer0.asm 1: include Soft_UART_Lib_use_Timer0.h 2: 3: ; В прерывании только прием байта (побитно), и выставление флага окончания приема, который уже анализируется в основном цикле 4: ;====================== 5: Soft_UART udata_shr 6: TX_byte res 1 7: RX_byte res 1 8: UART_counter res 1 9: delay_counter res 1 10: 11: global TX_byte,RX_byte,UART_counter,delay_counter 12: global Ini_UART,Send_TX_byte,Delay_1bit,Start_bit,Receive_bit,Get_RX_byte 13: 14: extern End_Interrupt_Heandler 15: ;====================== 16: SOFT_UART code 17: ;====================== 18: Get_RX_byte 19: if (!USE_T0CKI) ; Если используем INT, есть возможность поспать 20: sleep 21: nop 22: endif 0AF 3000 MOVLW 0 23: movlw 0 0B0 0213 SUBWF 0x13, W 24: subwf UART_counter,w 0B1 1903 BTFSC 0x3, 0x2 25: btfsc STATUS,Z ; Дожидаемся начала приема 0B2 28AF GOTO 0xaf 26: goto Get_RX_byte 27: Loop_Get_RX_byte 0B3 3000 MOVLW 0 28: movlw 0 0B4 0213 SUBWF 0x13, W 29: subwf UART_counter,w 0B5 1D03 BTFSS 0x3, 0x2 30: btfss STATUS,Z ; Дожидаемся окончания приема 0B6 28B3 GOTO 0xb3 31: goto Loop_Get_RX_byte 32: if (USE_ONE_PIN) 0B7 2101 CALL 0x101 33: call Delay_1bit ; Ожидаем завершения приема 34: endif 35: Wait_Receive_complete 36: ; btfss RX ; Ожидаем стоп бит 37: ; goto Wait_Receive_complete 0B8 0008 RETURN 38: return ; Байт принят 39: ;====================== 40: Start_bit 41: if (USE_T0CKI) 0B9 1281 BCF 0x1, 0x5 42: bcf OPTION_REG,T0CS ; Устанавливаем тактирование Таймера 0 от внутреннего генератора 0BA 1283 BCF 0x3, 0x5 43: banksel TMR0 ; Bank 0 0BB 3080 MOVLW 0x80 44: movlw .128 ; Перезапишем таймер для периода переполнения в 156 мкс (середина 1 бита данных) 45: else 46: movlw .123 ; Перезапишем таймер для периода переполнения в 156 мкс (середина 1 бита данных) 47: endif 0BC 0081 MOVWF 0x1 48: movwf TMR0 0BD 3008 MOVLW 0x8 49: movlw .8 ; Загрузим количество принимаемых битов 0BE 0093 MOVWF 0x13 50: movwf UART_counter 0BF 0192 CLRF 0x12 51: clrf RX_byte ; Очистим приемник 52: if (!USE_T0CKI) 53: bcf INTCON,INTE ; Запрещаем внешнее прерывание (4 бит) 54: bsf INTCON,T0IE ; Разрешаем прерывание по переполнению Таймера 0 (5 бит) 55: endif 0C0 280F GOTO 0xf 56: goto End_Interrupt_Heandler 57: ;====================== 58: Receive_bit 59: if (USE_T0CKI) 0C1 1283 BCF 0x3, 0x5 60: banksel TMR0 ; Bank 0 0C2 30A6 MOVLW 0xa6 61: movlw .166 ; Перезапишем таймер для периода переполнения в 104 мкс 62: else 63: movlw .164 ; Перезапишем таймер для периода переполнения в 104 мкс 64: endif 0C3 0081 MOVWF 0x1 65: movwf TMR0 0C4 1003 BCF 0x3, 0 66: bcf STATUS,C ; Сбросим бит переноса 0C5 0C92 RRF 0x12, F 67: rrf RX_byte ; Задвигаем сброшеный бит переноса в приемный регистр 0C6 1A05 BTFSC 0x5, 0x4 68: btfsc RX ; А теперь проверяем, что на самом деле надо было туда задвинуть 0C7 1792 BSF 0x12, 0x7 69: bsf RX_byte,7 ; Если 1, надо срочно подкорректировать, ведь мы задвинули 0 0C8 0B93 DECFSZ 0x13, F 70: decfsz UART_counter ; Весь байт принят ? 0C9 280F GOTO 0xf 71: goto End_Interrupt_Heandler ; Нет, идем принимать следующий бит 72: if (USE_T0CKI) 0CA 30FF MOVLW 0xff 73: movlw .255 ; Перезапишем таймер для переполнения при приходе старт-бита 0CB 0081 MOVWF 0x1 74: movwf TMR0 0CC 1683 BSF 0x3, 0x5 75: banksel OPTION_REG ; Bank 1 0CD 1681 BSF 0x1, 0x5 76: bsf OPTION_REG,T0CS ; Устанавливаем тактирование Таймера 0 от внешнего источника, чтобы словить следующий старт-бит 77: else 78: bcf INTCON,T0IE ; Запрещаем прерывание по переполнению Таймера 0 (5 бит) 79: bsf INTCON,INTE ; Разрешаем внешнее прерывание (4 бит) для приема следующего старт-бита 80: endif 0CE 280F GOTO 0xf 81: goto End_Interrupt_Heandler ; На выход 82: ;====================== 83: Ini_UART 0CF 3010 MOVLW 0x10 84: bsf_TX ; Заранее установим TX 0D0 0485 IORWF 0x5, F 0D1 1683 BSF 0x3, 0x5 85: banksel OPTION_REG ; Bank 1 86: if (USE_PIC12) 87: call 3FFh 88: movwf OSCCAL 89: endif 90: 91: if (!USE_ONE_PIN) 92: bcf_TX ; Настроим TX на выход 93: endif 94: 95: if (USE_T0CKI) 0D2 3038 MOVLW 0x38 96: movlw b'00111000' ; Подтягивающие резисторы включены (для матричной клавиатуры), тактирование Таймера 0 от внешнего источника, инкремент по заднему фронту, пределитель для WD 97: else 98: movlw b'00001000' ; Подтягивающие резисторы включены (для матричной клавиатуры), внешнее прерывание по заднему фронту, тактирование Таймера 0 от внутреннего генератора, пределитель для WD 99: endif 0D3 0081 MOVWF 0x1 100: movwf OPTION_REG 0D4 1283 BCF 0x3, 0x5 101: banksel INTCON ; Bank 0 102: 103: if (USE_PIC12) 104: movlw b'00000111' 105: movwf CMCON ; Выключим аналоговый компаратор 106: endif 107: 0D5 3000 MOVLW 0 108: movlw .0 ; Инициализируем счетчик принятых битов для определения момена начала и конца приема байта в подпрограмме Get_RX_byte 0D6 0093 MOVWF 0x13 109: movwf UART_counter 110: 111: if (USE_T0CKI) 0D7 30FF MOVLW 0xff 112: movlw .255 ; Перезапишем таймер для переполнения при приходе старт-бита 0D8 0081 MOVWF 0x1 113: movwf TMR0 0D9 30A0 MOVLW 0xa0 114: movlw (1<<GIE)|(1<<T0IE) ; Разрешаем прерывания вообще (7 бит) и прерывание по переполнению таймера (5 бит) 115: else 116: movlw (1<<GIE)|(1<<INTE) ; Разрешаем прерывания вообще (7 бит) и внешнее прерывание (4 бит) 117: endif 0DA 048B IORWF 0xb, F 118: iorwf INTCON 0DB 0008 RETURN 119: return 120: ;====================== 121: Send_TX_byte ; Подпрограмма передачи 1 байта 0DC 138B BCF 0xb, 0x7 122: bcf INTCON,GIE ; Запрещаем прерывания 123: ; bsf_TX ; Предварительно установим TX 0DD 1605 BSF 0x5, 0x4 124: bsf TX ; Предварительно установим TX 125: 126: if (USE_ONE_PIN) 0DE 1683 BSF 0x3, 0x5 127: banksel OPTION_REG ; Bank 1 128: ; bcf_TX ; Настроим TX на выход 0DF 1205 BCF 0x5, 0x4 129: bcf TX 0E0 1283 BCF 0x3, 0x5 130: banksel INTCON ; Bank 0 131: endif 132: 133: ;---------- 134: ; bcf_TX ; Шлем старт бит 0E1 1205 BCF 0x5, 0x4 135: bcf TX ; Шлем старт бит 0E2 0091 MOVWF 0x11 136: movwf TX_byte ; Загрузим передавемый байт 0E3 3008 MOVLW 0x8 137: movlw .8 ; Загрузим количество передаваемых битов 0E4 0093 MOVWF 0x13 138: movwf UART_counter 0E5 0000 NOP 139: nop 140: Transmit_Next_Bit 0E6 0000 NOP 141: nop 0E7 2101 CALL 0x101 142: call Delay_1bit ; Длинной 1 бит 0E8 0C91 RRF 0x11, F 143: rrf TX_byte ; Продвигаем в бит переноса все биты, начиная с младшего 0E9 1803 BTFSC 0x3, 0 144: btfsc STATUS,C ; Проверяем, что выдавилось в бит переноса 145: ; bsf_TX ; Если 1, выставляем 1 на ТХ 0EA 1605 BSF 0x5, 0x4 146: bsf TX ; Если 1, выставляем 1 на ТХ 0EB 1C03 BTFSS 0x3, 0 147: btfss STATUS,C 148: ; bcf_TX ; Если 0, выставляем 0 на ТХ 0EC 1205 BCF 0x5, 0x4 149: bcf TX ; Если 0, выставляем 0 на ТХ 0ED 0B93 DECFSZ 0x13, F 150: decfsz UART_counter ; Весь байт отослан ? 0EE 28E6 GOTO 0xe6 151: goto Transmit_Next_Bit ; Нет, идем слать следующий бит 0EF 2101 CALL 0x101 152: call Delay_1bit ; Задержка длинной 1 бит 0F0 0000 NOP 153: nop 0F1 0000 NOP 154: nop 0F2 0000 NOP 155: nop 0F3 0000 NOP 156: nop 0F4 0000 NOP 157: nop 0F5 3010 MOVLW 0x10 158: bsf_TX ; Шлем стоп-бит 0F6 0485 IORWF 0x5, F 0F7 2101 CALL 0x101 159: call Delay_1bit ; Задержка длинной 1 бит 160: ;---------- 161: if (USE_ONE_PIN) 0F8 1683 BSF 0x3, 0x5 162: banksel OPTION_REG ; Bank 1 0F9 3010 MOVLW 0x10 163: bsf_TX ; Настроим ножку на вход 0FA 0485 IORWF 0x5, F 0FB 1283 BCF 0x3, 0x5 164: banksel INTCON ; Вернем Bank 0 165: endif 166: 167: if (USE_T0CKI) 0FC 30FF MOVLW 0xff 168: movlw .255 ; Перезапишем таймер для переполнения при приходе старт-бита 0FD 0081 MOVWF 0x1 169: movwf TMR0 170: else 171: bcf INTCON,INTF ; Сбросим флаг внешнего прерывания 172: endif 0FE 110B BCF 0xb, 0x2 173: bcf INTCON,T0IF ; Сбросим флаг переполнения Таймера 0 0FF 178B BSF 0xb, 0x7 174: bsf INTCON,GIE ; Разрешаем прерывания 100 0008 RETURN 175: return 176: ;====================== 177: Delay_1bit 101 301E MOVLW 0x1e 178: movlw BITS_DELAY ; 104 мкс для 9600 бод 102 0094 MOVWF 0x14 179: movwf delay_counter 180: Loop_Delay 103 0B94 DECFSZ 0x14, F 181: decfsz delay_counter 104 2903 GOTO 0x103 182: goto Loop_Delay 105 0008 RETURN 183: return
Качественное и безопасное устройство, работающее от аккумулятора, должно учитывать его физические и химические свойства, профили заряда и разряда, их изменение во времени и под влиянием различных условий, таких как температура и ток нагрузки. Мы расскажем о литий-ионных аккумуляторных батареях EVE и нескольких решениях от различных китайских компаний, рекомендуемых для разработок приложений с использованием этих АКБ. Представленные в статье китайские аналоги помогут заменить продукцию западных брендов с оптимизацией цены без потери качества.
Это юмор такой ? INTCON отображается на все банки.
Не совсем. Я придерживаюсь тактики основное время работать с 0 банком, и только в случае необходимости включать нужный, и после завершения нужной операции опять возвращать 0. Мне так проще, я так всегда знаю в каком я банке, и не ищу по коду, какой я до этого сделал активным
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
Ну он (INTCON) же вроде как "отбражен" на другие банки, но физически то размещен в 0, поэтому я листинге всегда наблюдаю "BCF 0x3, 0x5" , что мне как бы и требуется. Поначалу я писал "banksel PORTB" после установки направления работы выходов. Но потом у меня появились общие программные модули для 12 и 16 ПИКов (тот же программный UART), и в таких модулях я, чтобы не забивать код директивами условной компиляции под каждый МК, стал поступать проще. Я вибираю банк "банкселом" регистра из того же банка, в котором находиться требуемый для работы регистр (карта памяти у меня перед глазами) и при этом этот регистр одинаково называется у 12 и 16 пиков, и размещен он у обох этих пиков в одном и том же банке. Для выбора 0 банка я стал использовать INTCON, а для выбора 1 банка OPTION_REG. И пока этот способ меня не подводил, компилятор (или правильнее транслятор?) всегда все правильно "думал". Если этот способ имеет подводные камни, прошу, укажите их
...Если этот способ имеет подводные камни, прошу, укажите их
Я принципиально не использую в своих программах "неоднозначности" (и другим не советую), поэтому на тему "подводных камней" у вас сказать не могу. Если уж вы решили сделать "универсальную" библиотеку - к примеру, для PIC10F322T вам придётся достаточно много переделывать в вашей программе.
Ну он (INTCON) же вроде как "отбражен" на другие банки, но физически то размещен в 0, поэтому я листинге всегда наблюдаю "BCF 0x3, 0x5" , что мне как бы и требуется.
Ассемблер не имеет к физическому размещению регистров никакого отношения. И его действия определяются его внутренней логикой. Что до размещения INTCON, то физически он размещен В КОНТРОЛЛЕРЕ ПРЕРЫВАНИЙ, которым он и управляет, а адресный дешифратор при этом регистре имеет в качестве входа только семь разрядов адреса и не имеет бит выборки банков из регистра состояний. Что АВТОМАТИЧЕСКИ приводит к выборке этого регистра В ЛЮБОМ банке. Такшта говорить о конкретном банке его размещения - бессмысленная задача.
Добавлено after 3 minutes 28 seconds:
sdn_ писал(а):
Если TMR0, у разных пиков, будет не в банке 0, то будет попа.
TMR0 во всех пиках 10..16 семейств размещен в нулевом банке. Потому что этот таймер относится к системным и даже не может быть остановлен. Подразумевается, что этот таймер используется RTOS для системных клоков.
Спасибо люди добрые. Аргумент про неоднозначность принял как весомый. Посмотрел еще раз внимательно на карту памяти обоих подсемейств (12 и 16), теперь буду везде вместо "banksel INTCON" использовать "banksel TMR0", это не будет создавать неоднозначности (во всяком случае для применяемого мной старья 16F84 и 12F629, у которых всего по 2 банка памяти данных, там где 4 банка опять будет неоднозначность, но я уже осведомлен, а значит вооружен), и позволит писать код как раньше. А насчет универсальности программных модулей, так она довольно условна, и главное требование написанного кода - чтобы работало на том, что есть у меня. А 10 пиков у меня нет, и пока не предвидится А насчет невозможности остановить TMR0, я уже ВОВ51 в личку сокрушался. А теперь ясна картина стала, зачем оно так сделано, спасибо.
Последний раз редактировалось Пока_без_кота Вт янв 03, 2017 10:35:53, всего редактировалось 1 раз.
таймер относится к системным и даже не может быть остановлен.
В спящем режиме таймер0 остановлен.
В спящем режиме и ядро остановлено. То есть код не исполняется. Поэтому обсуждать таймеры (или иную периферию) нет необходимости. Слип - внепрограммный режим. Рассматривался вопрос расположения нулевого таймера в поле адресов данных и причины такого расположения.
Добавлено after 6 minutes 34 seconds:
Пока_без_кота писал(а):
буду везде вместо "banksel INTCON" использовать "banksel TMR0"
А почему бы не использовать прямую установку STATUS <RP1:RP0>? Можно и свой макрос для сброса банка в ноль написать. Без упоминания регистров. А вообще, в новых 12...16 ПИКах банки переключаются через BSR регистр командой movlb <номер банка>.
... А почему бы не использовать прямую установку STATUS <RP1:RP0>? Можно и свой макрос для сброса банка в ноль написать. Без упоминания регистров. А вообще, в новых 12...16 ПИКах банки переключаются через BSR регистр командой movlb <номер банка>.
Ну собственно я поначалу так и делал, просто по мере чтения разных книг узнал, что это вроде как некрасиво, и не универсально. Вот может начну когда-то 18 Пики осваивать, там эта привычка и аукнулась бы. Я сторонник учиться медленно, неторопливо, но "академически правильно", чтобы потом не переучиваться. Потому что переучиваться всегда тяжелее чем учиться с 0. А насчет новых пиков, так мне тут уже намекнули что существуют 12 пики с аппаратным UART (12F1822), и я уже даже нашел где подешевле. Просто дороже они, чем я 629 брал (в 4 раза), вот и жду зарплаты, чтобы купить парочку на пробу.
вибираю банк "банкселом" регистра ... Если этот способ имеет подводные камни, прошу, укажите их
Имеет. Подводные камни в том, что banksel - это не реальная инструкция, а псевдоинструкция препроцессора, раскрывающаяся в две процессорные инструкции, если банков 4 штуки. При вычисляемых переходах, инструкциях вида btfss и попадании на границы страниц может весьма подвести. А в новых сериях типа 12(16)F1xxx банков уже гораздо больше и ими управляет специальный регистр.
Добавлено after 5 minutes 26 seconds:
BOB51 писал(а):
прямая оговорка на условие - "все подпрограммы/обслуживание прерываний только на нулевой странице памяти программ".
Ну нет, почему же. Получив прерывание и сохранив текущую страницу, выставляем нужную страницу битами регистра PCLATH и переходим. Псевдоинструкция lgoto.
_________________ Подпись убрал вместе с автором. aen
TMR0 во всех пиках 10..16 семейств размещен в нулевом банке
Дело не в конкретном регистре, а в примере плохого стиля программирования.
КРАМ писал(а):
Потому что этот таймер относится к системным
Да не поэтому, а потому, что у микрочипа так исторически сложилось наверное.
Добавлено after 9 minutes 23 seconds:
Пока_без_кота писал(а):
теперь буду везде вместо "banksel INTCON" использовать "banksel TMR0"
Использовать banksel нужно тогда, когда требуется перейти в другой банк и и с текущим регистром, а не с TMR0.
Добавлено after 4 minutes 22 seconds:
Пока_без_кота писал(а):
..Ну собственно я поначалу так и делал, просто по мере чтения разных книг узнал, что это вроде как некрасиво, и не универсально. Вот может начну когда-то 18 Пики осваивать, там эта привычка и аукнулась бы.
В 18 пиках, вообще - то, системные регистры доступны в банке быстрого доступа всем скопом. Пейсать на ASM под 18 пики - это просто сказка. К этому ещё и нет деления страниц памяти программ по 2кБ.
Пока_без_кота писал(а):
А насчет новых пиков, так мне тут уже намекнули что существуют 12 пики с аппаратным UART (12F1822), и я уже даже нашел где подешевле.
Там 32 - банка. Вы вообще потоните в них. Тяготеете к переносимости и универсальности видимо. Так ша осваивайте СИ.
Я сторонник учиться медленно, неторопливо, но "академически правильно", чтобы потом не переучиваться. Потому что переучиваться всегда тяжелее чем учиться с 0.
А как быть вообще с другими платформами? Скажем, PIC24/dsPIC33 или ARM? Нужно учиться, а не зазубривать шаблоны. Тогда и не будет проблем с "переучиванием". Когда Вам советуют применять всяческие макросы и условную компиляцию, то дело тут совсем не в стиле программирования, а в переносимости кода. Читабельность кода с условной компиляцией близка к нулю. Поэтому есть разного рода приблуды, исключающие видимость конструкций ifdef.
Добавлено after 16 minutes 14 seconds:
sdn_ писал(а):
КРАМ писал(а):
TMR0 во всех пиках 10..16 семейств размещен в нулевом банке
Дело не в конкретном регистре, а в примере плохого стиля программирования.
Плохой стиль - это что? Это когда не на АСМе? Или когда без поллитра невозможно прочесть исходник написанный этим самым "хорошим" стилем. Переносимость кода на МК - фикция. Хоть на Си пиши, хоть на Шарпе. Потому что переносимый код не использует фичи контроллера, а значит не оптимален ни по архитектуре, ни по цене реализации. переносимость кода - отговорка ленивых программистов, готовых многократно впаривать работодателю один и тот же код.
sdn_ писал(а):
КРАМ писал(а):
Потому что этот таймер относится к системным
Да не поэтому, а потому, что у микрочипа так исторически сложилось наверное.
Так "наверное" или Вы знаете?
Почему то "исторически" расположение нулевого таймера не перекочевало на иные платформы Микрочипа... В чем же состоит такая "историчность"?
sdn_ писал(а):
Пока_без_кота писал(а):
А насчет новых пиков, так мне тут уже намекнули что существуют 12 пики с аппаратным UART (12F1822), и я уже даже нашел где подешевле.
Там 32 - банка. Вы вообще потоните в них.
Не выдумывайте сложности. Найти расположение регистра в банках совершенно не сложно. Гораздо проще, чем найти нужный регистр в структуре периферийного модуля на STM32.
Это когда банк переключается через обращение к INTCON.
КРАМ писал(а):
Так "наверное" или Вы знаете?
А что есть какая - то неднозначность в трактовке слова "наверное" ? Ессесно, что если бы знал, то так бы не выразился. Ничего не мешало микрочипу выбрать любой другой банк для TMR0. У микрочипа нужно спросить почему именно банк 0 .
КРАМ писал(а):
Не выдумывайте сложности. Найти расположение регистра в банках совершенно не сложно.
Вам не сложно, а ему будет сложно, если в четырех банках путается.
Вообще-то... Регистры спецфункций/аппаратные модули изучать приходится "по мере потребления", а вот систему команд/особенности ядра - в любом случае. Да и без кучи ерраташитов никак не обойтись... Как и в каком стиле писать исходник также зависит от конкретной задачи и набранного опыта автора. Относительно "стереотипа размещения" аппаратных устройств расширения - понятно, что некоторая преемственность внутри семейств имеется - но абсолютно не факт, что в соседней подгруппе будет то же самое соблюдаться. А менять кристаллы приходится довольно часто (и не только внутри текущего семейства). Гораздо проще использовать готовую заготовку из подключаемого файла описания РСФ - там все имена уже заявлены (и вроде в большинстве случаев без существенных ошибок).
Касательно программного 232-го... Я б прерывания по таймеру использовать не стал - уж и так времени "в обрез" при 4800 всего 208 циклов на тайм-слот, а при 9600 и того меньше - всего 104. Да еще от условных переходов с неравнозначной длительностью исполнения желательно избавится (базовый цикл у 629-го по умолчанию 0,000001 секунда). Тем более, что малолапке в принципе нет смысла в одновременном выполнении нескольких задач.
sdn_ освоивший mcs51 вполне может со среднемладшими разобраться
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения