;При использовании данного материала просьба оставлять ссылку на автора: ;www.kulakov.ru ;engineer@jeo.ru ; ;Данная программа позволяет осуществлять связь между несколькими контроллерами по шине I2C. ;Каждый подключенный к шине узел может находиться в одном из двух режимов - ведущего или ведомого. ;Приведенные ниже подпрограммы позволяют функционировать контроллеру в любом из этих режимов, а так же ;переключаться из одного в другой в процессе работы. Это дает возможность организовать ;режим "мультимастер", в котором любое устройство по мере необходимости может захватить шину для ;передачи. ; ;Обмен осуществляется пакетами в который в первом байте передается число байт следующих за ним. ;В режиме ведомого прием и передача пакета происходит по прерыванию, в режиме ведущего непосредственно ;в процессе исполнения программы. ;По окончанию приема пакета в режиме ведомого устанавливается флаг HvCommI2C, который основная программа ;должна проверять в процессе работы для того чтобы обработать принятый пакет. Если обработка принятого ;пакета достаточно короткая, ее можно разместить непосредственно в обработчике прерывания. ; ;Принятый пакет размещается в регистрах с именами Byte_I2C_Rx_0, Byte_I2C_Rx_1 и т.д., ;Передаваемый должен записываться в регистры с именами Byte_I2C_Tx_0, Byte_I2C_Tx_1 и т.д. ;Адрес контроллера записывается в константу MY_ADDR. ; ;^^^^^^^^^^^^^Подпрограммы:^^^^^^^^^^^^ ; ;Init - общая инициализация ; ;Init_I2C_Slave - включение режима ведомого ; ;Init_I2C_Master - включение режима ведущего ; ;Slave_to_Master - переключение из режима ведомого в режим ведущего. Для обратно переключения ;необходимо вызвать Init_I2C_Slave. ; ;Transmitt_I2C_Master - передача в режиме ведущего. Перед вызовом этой п/п необходимо в ;Byte_I2C_Tx_0 записать адрес приемника. По выходу из этой п/п необходимо проверить флаг Err_I2C. ;Если он установлен, то при передаче произошла ошибка (например, шина занята другим контроллером) ;и пакет не был передан. ; ; ;Receive_I2C_Master - Прием пакета в режиме ведущего. Перед вызовом этой п/п необходимо в ;Byte_I2C_Tx_0 записать адрес вызываемого узла, а в Index_I2C_Rx 4. По выходу из этой п/п принятый пакет ;будет в регистрах Byte_I2C_Rx_0, Byte_I2C_Rx_1 и т.д. В этой п/п так же необходимо проверить флаг Err_I2C. ;Если он установлен, то произошла ошибка и пакет не был принят. ; ; ;Пример реализации режима "мультимастер" приведен в основном теле программы. ; ;www.kulakov.ru ;engineer@jeo.ru #include p16f877.inc ;Переменные, банк0 cblock 0x20 Index_I2C_Tx Byte_I2C_Tx_0 Byte_I2C_Tx_1 Byte_I2C_Tx_2 Byte_I2C_Tx_3 Byte_I2C_Tx_4 Byte_I2C_Tx_5 Byte_I2C_Tx_6 Index_I2C_Rx Byte_I2C_Rx_0 Byte_I2C_Rx_1 Byte_I2C_Rx_2 Byte_I2C_Rx_3 Byte_I2C_Rx_4 IndexRec_I2C Flag CntLo ;Регистры для пп задержки CntHi ; Temp ; Sample0 ;Регистры для примера Sample1 ; Sample2 ; endc ;Последние 16 байт: W_TEMP EQU 7Dh STATUS_TEMP EQU 7Eh PCLATH_TEMP EQU 7Fh ;Константы INIT_PORTA EQU b'00001111' INIT_PORTB EQU b'00001111' INIT_PORTC EQU b'00011110' INIT_PORTD EQU b'00000000' INIT_PORTE EQU b'00000000' INIT_OPTION EQU b'10001000' INIT_INTCON EQU b'11000000' INIT_PIE1 EQU b'00000000' INIT_T1CON EQU b'00110001' INIT_ADCON0 EQU b'10000001' INIT_ADCON1 EQU b'10001101' ;Адреса I2C MY_ADDR EQU .1;Адрес данного контроллера на I2C ;Команды I2C ;Биты Flag: #define RP0_Tmp Flag,0 #define Err_I2C Flag,1 #define NoStop Flag,2 #define ReStrt Flag,3 #define OnReception Flag,4 #define HvCommI2C Flag,5 #define Stop Flag,6 goto Begin Intrp org 0x4 bcf RP0_Tmp btfsc STATUS,RP0 bsf RP0_Tmp MOVWF W_TEMP SWAPF STATUS,W CLRF STATUS MOVWF STATUS_TEMP MOVF PCLATH, W MOVWF PCLATH_TEMP CLRF PCLATH ;прерывание от I2C_SLAVE btfss PIR1,3 goto rett bcf PIR1,3 bsf STATUS,RP0 btfsc SSPSTAT^80h,R_W goto rcv btfss SSPSTAT^80h,0 goto rett bcf STATUS,RP0 movf SSPBUF,W bsf STATUS,RP0 btfss SSPSTAT^80h,5 goto rett bcf STATUS,RP0 btfsc OnReception goto contrec movwf Index_I2C_Rx bsf OnReception clrf IndexRec_I2C goto fnrec contrec movlw Byte_I2C_Rx_0 movwf FSR movf IndexRec_I2C,0 addwf FSR,1 movf SSPBUF,W movwf INDF incf IndexRec_I2C,1 decfsz Index_I2C_Rx,1 goto fnrec bcf OnReception bsf HvCommI2C ;Здесь можно вставить обработку принятой команды fnrec bsf STATUS,RP0 bsf PIE1^80h,3 bcf STATUS,RP0 goto rett rcv btfsc SSPSTAT^80h,5 goto txagn ; Был принят адрес ;ПЕРЕДАЧА В РЕЖИМЕ ВЕДОМОГО ;Инициализировать передачу bcf STATUS,RP0 movlw Byte_I2C_Tx_0 movwf FSR txagn bcf STATUS,RP0 movf INDF,0 movwf SSPBUF bsf SSPCON,CKP ;Начать передачу incf FSR,1 goto rett rett bcf STATUS,RP0 btfsc RP0_Tmp bsf STATUS,RP0 MOVF PCLATH_TEMP, W MOVWF PCLATH SWAPF STATUS_TEMP,W MOVWF STATUS SWAPF W_TEMP,F SWAPF W_TEMP,W retfie ;Общая инициализация Init BSF STATUS,RP0 BCF STATUS,IRP BCF STATUS,RP1 MOVLW INIT_PORTA MOVWF TRISA^80h MOVLW INIT_PORTB MOVWF TRISB^80h MOVLW INIT_PORTC MOVWF TRISC^80h MOVLW INIT_PORTD MOVWF TRISD^80h MOVLW INIT_PORTE MOVWF TRISE^80h MOVLW INIT_OPTION MOVWF OPTION_REG^80h movlw INIT_PIE1 movwf PIE1^80h clrf PIE2^80h movlw INIT_ADCON1 movwf ADCON1^80h BCF STATUS,RP0 movlw INIT_INTCON movwf INTCON movlw INIT_T1CON movwf T1CON movlw INIT_ADCON0 movwf ADCON0 RETURN Slave_to_Master bsf STATUS,RP0 bcf SSPSTAT^80h,6 bsf SSPSTAT^80h,7 clrf SSPCON2^80h movlw .50 ;Задание скорости movwf SSPADD^80h ; bcf PIE1^80h,SSPIE ;Прерывание выкл. bcf PIE2^80h,BCLIE ;Прерывание при конфликте выкл. bcf STATUS,RP0 bcf PIR1,SSPIF ;на всякий случай обнулить флаг прерывания movlw b'00100000' andwf SSPCON,1 movlw b'00101000' ;Инициализировать и включить SSP в I2C iorwf SSPCON,1 return Init_I2C_Slave bcf STATUS,RP0 clrf SSPCON ;Выключить SSP clrf PORTC movlw B'00111110' movwf SSPCON bsf STATUS,RP0 movlw B'00001000' ;Разрешить прерывание при приеме movwf PIE1^80h movlw MY_ADDR ;Адрес movwf SSPADD^80h bcf STATUS,C rlf SSPADD^80h,1 bsf TRISC^80h, 3 bsf TRISC^80h, 4 bsf SSPCON2^80h,7 ;Разрешить общий вызов bcf STATUS,RP0 bcf PIR1,3 RETURN Init_I2C_Master clrf SSPCON ;Выключить SSP bsf STATUS,RP0 clrf SSPSTAT^80h bsf TRISC^80h,3 bsf TRISC^80h,4 bcf SSPSTAT^80h,6 bsf SSPSTAT^80h,7 clrf SSPCON2^80h movlw .50 ;Задание скорости movwf SSPADD^80h ; bcf PIE1^80h,SSPIE ;Прерывание bcf PIE2^80h,BCLIE ;Прерывание при конфликте bcf STATUS,RP0 bcf PIR1,SSPIF ;на всякий случай обнулить флаг прерывания movlw b'00101000' ;Инициализировать и включить SSP в I2C movwf SSPCON return Transmitt_I2C_Master incf Byte_I2C_Tx_1,0 movwf Index_I2C_Tx incf Index_I2C_Tx,1 bcf Err_I2C bcf PIR2,BCLIF bcf PIR1,SSPIF bsf STATUS,RP0 btfsc SSPSTAT^80h,P goto Nxx btfss SSPSTAT^80h,S goto Nxx bcf STATUS,RP0 ;Шина занята bsf Err_I2C return Nxx bsf SSPCON2^80h,SEN ;START Wt_strt btfsc SSPCON2^80h,SEN goto Wt_strt bcf STATUS,RP0 ;Выдача Start закончена btfss PIR2,BCLIF goto nxx bcf PIR2,BCLIF bsf Err_I2C ;Конфликт goto Stp nxx ;START выдан bcf STATUS,C ;Сдвинуть байт адреса и rlf Byte_I2C_Tx_0,1 ;сбросить бит R/W movlw Byte_I2C_Tx_0 movwf FSR TrAgn bcf PIR1,SSPIF movf INDF,0 ;Инициировать передачу movwf SSPBUF ; wt_finTr btfss PIR1,SSPIF goto wt_finTr bcf PIR1,SSPIF btfss PIR2,BCLIF goto nxx1 bcf PIR2,BCLIF bsf Err_I2C ;Конфликт goto Stp nxx1 bsf STATUS,RP0 btfss SSPCON2^80h,ACKSTAT goto nxx2 bcf STATUS,RP0 ;Прием байта не подтвержден bsf Err_I2C goto Stp nxx2 bcf STATUS,RP0 incf FSR,1 decfsz Index_I2C_Tx,1 goto jjkksdfg goto pqo jjkksdfg goto TrAgn pqo bcf PIR1,SSPIF btfsc NoStop return Stp bcf PIR1,SSPIF ;STOP bsf STATUS,RP0 bsf SSPCON2^80h,PEN bcf STATUS,RP0 wt_stp btfss PIR1,SSPIF goto wt_stp ;Конец передачи bcf PIR1,SSPIF btfsc PIR2,BCLIF bsf Err_I2C ;Конфликт return Receive_I2C_Master bcf Err_I2C bcf PIR2,BCLIF bcf PIR1,SSPIF bsf STATUS,RP0 btfsc SSPSTAT^80h,P goto Nxxx bcf STATUS,RP0 btfsc ReStrt goto Nxxx bsf STATUS,RP0 btfss SSPSTAT^80h,S goto Nxxx bcf STATUS,RP0 ;Шина занята bsf Err_I2C ; return ; Nxxx bcf STATUS,RP0 bcf PIR1,3 btfsc ReStrt goto rstrt bsf STATUS,RP0 bsf SSPCON2^80h,SEN ;START Wt_strtx btfsc SSPCON2^80h,SEN goto Wt_strtx bcf STATUS,RP0 ;Выдача Start закончена btfss PIR2,BCLIF goto nxxx bcf PIR2,BCLIF bsf Err_I2C ;Конфликт goto Stp rstrt bsf STATUS,RP0 bsf SSPCON2^80h,RSEN ;ReSTART bcf STATUS,RP0 Wt_strtx2 btfss PIR1,3 goto Wt_strtx2 bcf PIR1,3 ;Выдача ReStart закончена btfss PIR2,BCLIF goto nxxx bcf PIR2,BCLIF bsf Err_I2C ;Конфликт goto Stp nxxx ;START выдан bsf STATUS,C ;Сдвинуть байт адреса и rlf Byte_I2C_Tx_0,1 ;установить бит R/W movf Byte_I2C_Tx_0,0 ;Инициировать передачу movwf SSPBUF ; bcf PIR1,SSPIF wt_finTrx btfss PIR1,SSPIF goto wt_finTrx bcf PIR1,SSPIF btfss PIR2,BCLIF goto nxxx1 bcf PIR2,BCLIF bsf Err_I2C ;Конфликт goto Stp nxxx1 bsf STATUS,RP0 btfss SSPCON2^80h,ACKSTAT goto nxxx2 bcf STATUS,RP0 ;Прием байта не подтвержден bsf Err_I2C goto Stp nxxx2 movlw Byte_I2C_Rx_0 movwf FSR RecAgn bsf STATUS,RP0 bsf SSPCON2^80h,RCEN ;Прием кол-ва байтов, которое нужно принять fsadgda btfss SSPSTAT^80h,BF goto fsadgda bcf STATUS,RP0 movf SSPBUF,0 ;Считать принятый байт movwf INDF bcf PIR1,SSPIF decfsz Index_I2C_Rx,1 goto notlast ;Был принят последний байт bsf STATUS,RP0 ;подтверждение не выдавать bsf SSPCON2^80h,ACKDT ; bsf SSPCON2^80h,ACKEN ; adgads btfsc SSPCON2^80h,ACKEN goto adgads bcf STATUS,RP0 btfss PIR2,BCLIF goto ggdf bcf PIR2,BCLIF bsf Err_I2C ;Конфликт ggdf goto Stp notlast ;Принят не последний байт bsf STATUS,RP0 ;подтверждение bcf SSPCON2^80h,ACKDT ; bsf SSPCON2^80h,ACKEN ; adgads1 btfsc SSPCON2^80h,ACKEN goto adgads1 bcf STATUS,RP0 btfss PIR2,BCLIF goto ggdf1 bcf PIR2,BCLIF bsf Err_I2C ;Конфликт goto Stp ggdf1 incf FSR,1 goto RecAgn Del500mcs ;пп задержки на 500 мкс movlw .60 movwf CntLo movlw 4 movwf CntHi _Del500 decfsz CntLo,1 goto _Del500 decfsz CntHi,1 goto _Del500 return Del15 movlw .30 ;пп задержки на 15 мс movwf Temp ; _15ms call Del500mcs ; decfsz Temp,1 ; goto _15ms ; return ;************************************ Н А Ч А Л О ************************************ Begin call Init call Init_I2C_Slave ;По запуску во всех контроллерах включается режим ведомого clrf Flag StrtProg ;основной цикл программы btfss HvCommI2C ;Проверка флага приема пакета goto NoFrame ;Пакет принят не был bcf HvCommI2C ;Здесь обработка принятых команд I2C NoFrame ;////////// Далее пример передачи в режиме ведущего ;Допусти необходимо передать последовательность байт, находящихся в регистрах ;Sample0, Sample1, Sample2 в узел с адресом ADDR_RECEIVER ADDR_RECEIVER EQU .2 movlw 0x11 ;Запишем в них какие-нибудь значения movwf Sample0 ; movlw 0x22 ; movwf Sample1 ; movlw 0x33 ; movwf Sample2 ; Trnsmtr bsf STATUS,RP0 ; btfsc SSPSTAT^80h,P ;Проверка, свободна ли шина goto I2C_Busy_Trm ; btfss SSPSTAT^80h,S ; goto I2C_Busy_Trm ; btfss SSPSTAT^80h,P ; goto $-1 ; I2C_Busy_Trm ; bcf STATUS,RP0 ; call Slave_to_Master ;Переключиться в режим ведущего movlw ADDR_RECEIVER ;Адрес приемника movwf Byte_I2C_Tx_0 ; movlw .3 ;Число передаваемых байт movwf Byte_I2C_Tx_1 ; movf Sample0,0 ;Запись передаваемых значений movwf Byte_I2C_Tx_2 ; movf Sample1,0 ; movwf Byte_I2C_Tx_3 ; movf Sample2,0 ; movwf Byte_I2C_Tx_4 ; bcf NoStop bcf INTCON,7 ;Запретить прерывания call Transmitt_I2C_Master;Передать bsf INTCON,7 ;Резрешить прерывания call Init_I2C_Slave ;Переключиться в режим ведомого btfss Err_I2C ;Проверка флага ошибки goto Trm_OK ;Ошибки нет call Del15 ;Ошибка. Задержка на 15 мсек. на тот случай, если вызываемый контроллер пробует передать что либо в данный контроллер goto Trnsmtr ;повторить передачу заново Trm_OK ;////////// Пример приема в режиме ведущего. ;Пусть необходимо принять пакет из контроллера с адресом ADDR_TRANSMITTER. ADDR_TRANSMITTER EQU .3 Rcvr bsf STATUS,RP0 ;Проверка, свободна ли шина btfsc SSPSTAT^80h,P ; goto I2C_Busy_rcvr ; btfss SSPSTAT^80h,S ; goto I2C_Busy_rcvr ; btfss SSPSTAT^80h,P ; goto $-1 ; I2C_Busy_rcvr ; bcf STATUS,RP0 ; call Slave_to_Master ;Переключение в режим ведущего movlw 4 ;Записать 4 в Index_I2C_Rx movwf Index_I2C_Rx ; movlw ADDR_TRANSMITTER ;Записать адрес в Byte_I2C_Tx_0 movwf Byte_I2C_Tx_0 ; call Receive_I2C_Master ;Принять пакет btfss Err_I2C ;Проверка на ошибку goto Rcvr_OK ;Ошибки нет call Init_I2C_Slave ; call Del15 ; goto Rcvr ;Повторить прием Rcvr_OK goto StrtProg end