;************************************************************************ ;* * ;* 以 PIC16F877A 為I2C Master 模式存取一串列式 EEPROM 的範例程式 * ;* * ;* Written by: Richard Yang * ;* Sr. Corporate Application Engineer * ;* Microchip Technology Inc. * ;* Date: Oct. 2nd '2002 * ;* Revision: 1.00 * ;************************************************************************ ;***************************************************************************************** ; This source code provides a demonstration of the MSSP peripheral ; on the PIC16F87x MCU. ; ; ;*********** The subroutines for EEPROM ***************** ; ; EE_Random_Read ; EEPROM Random address Read from EEPROM ; EE_SEQU_Read ; EEPROM Sequential Read form EEPROM ; EE_Ack_Check ; Polling current status of EEPROM ; EE_Page_Write ; Page Write function for EEPROM ; EE_Byte_Write ; Write a byte to EEPROM with address setting ; ;*********** The subroutines for I2C ***************** ; ; Init_I2C_Master ; Initial I2C Module for Master Mode , 7-bit address ; StartI2C ; Send a START Condition !! ; StopI2C ; Send s STOP Condition ; RstartI2C ; Send a Repeat Start conditional to I2C ; Non_Ack ; Send a Non-acknowledge signal to I2C ; An_Ack ; Send a acknowledge signal to I2C ; Sebd_Byte ; Send a byte to I2C bus ; RecI2C ; Enable Read a byte form slave device ; I2C_Done : Wait the I2C completed the currect process ; ;***************************************************************************************** list p=16f877a #include ; ;*************************************** ; 定義變數在RAM的位址 ; CBLOCK 0x20 I2C_Ctrl I2C_Addr I2C_Data I2C_Page_Length D_Count ENDC cblock 0x40 I2C_Page_Buffer:8 I2C_SEQU_Buffer:8 endc ; ;*************************************** ; 定義中斷的暫存器位址 w_temp EQU 0x72 status_temp EQU 0x73 pclath_temp EQU 0x74 ; ;*************************************** ; 定義 I2C 的位址及腳位 ; EEPROM_CMD equ 0xA0 ; Device adress of Slave Point EE_Read equ .1 EE_Write equ .0 ; #define SCL PORTC,3 ; I2C SCL pin #define SDA PORTC,4 ; I2C SDA pin ; ;******************************************** ; Locates startup code @ the reset vector ;******************************************** Reset_Addr org 0x00 nop goto Main_Init ; org 0x04 goto ISR ; ;*************************************************************************** ;**** The Start Address of ISR is 0x004 ;**** "PUSH" & "POP" 的使用範例 : 適用於像 PIC16F877 有SHARE BANK 的PIC ;*************************************************************************** ISR Push movwf w_temp ; save off current W register contents movf STATUS,w ; move status register into W register movwf status_temp ; save off contents of STATUS register movf PCLATH,W movwf pclath_temp ; ; Put your interrupt code here ; Pop movf pclath_temp,W movwf PCLATH movf status_temp,w ; retrieve copy of STATUS register movwf STATUS ; restore pre-isr STATUS register contents swapf w_temp,f swapf w_temp,w ; restore pre-isr W register contents ; retfie ; return from interrupt ;---------------------------------------------------------------------- Main_Init pagesel Init_I2C_Master ; Set PAGE to PCLATH Register call Init_I2C_Master ; Init the MSSP for I2C Master ; banksel I2C_Ctrl movlw EEPROM_CMD ; Load EEPROM command address @ 0xA0 movwf I2C_Ctrl ; ; Main ; ;---------------------------------------------------------------------- ; ;* 以下的測試程式會先將一組數字(0x40-0x47)寫入到RMA0x40-0x47的位置 ;* 再用Page Write的方式將8個位元組的資料寫到 EEPROM 後 ;* 進行狀態的偵測EEPROM是否已完成寫入的動作,最後將剛寫 ;* 入的資料以Sequential Read的方式從EEPROM讀出來放在I2C_SEQU_Buffer中 ; Test_Page_RW movlw 0x08 movwf I2C_Page_Length movlw I2C_Page_Buffer _Fill_RAM movwf FSR movwf INDF incf FSR,W decfsz I2C_Page_Length,F goto _Fill_RAM ; Test_Page_Write ; banksel I2C_Ctrl movlw EEPROM_CMD ; Load EEPROM command address @ 0xA0 movwf I2C_Ctrl movlw 0x10 ; Select EEPROM location at 0x00 movwf I2C_Addr movlw .8 movwf I2C_Page_Length call EE_Page_Write ; call EE_Ack_Check ; banksel I2C_Ctrl movlw EEPROM_CMD ; Load EEPROM command address @ 0xA0 movwf I2C_Ctrl movlw 0x10 movwf I2C_Addr movlw .8 movwf I2C_Page_Length call EE_SEQU_Read ; goto $ ; ;--------------------------------------------------------- ; ;* 以下的測試程式會先寫入一個位元組的資料寫到 EEPROM 中 ;* 再進行狀態的偵測是否已完成寫入的動作,最後將剛寫 ;* 入的資料再從EEPROM讀出來放在I2C_Data暫存器中 ; Test_1byte_RW banksel I2C_Ctrl movlw EEPROM_CMD ; Load EEPROM command address @ 0xA0 movwf I2C_Ctrl movlw 0x00 ; Select EEPROM location at 0x00 movwf I2C_Addr movlw 0xAA ; Write data 0x5A to location 0x00 of EEPROM movwf I2C_Data call EE_Byte_Write ; Test2 call EE_Ack_Check ; Polling Acknowledge for next access ; banksel I2C_Data movlw 0x00 ; Clear I2C data buffer movwf I2C_Data ; Test3 banksel I2C_Ctrl movlw EEPROM_CMD ; Load EEPROM command address @ 0xA0 movwf I2C_Ctrl movlw 0x00 movwf I2C_Addr call EE_Random_Read ; goto $ ; ; ;****************************************************** ;* Random Read a Byte from EEPROM ;* ;* Input: ;* - I2C_Ctrl : Control Byte of EEPROM ;* - I2C_Addr : Location of EEPROM ;* Output: ;* - I2C_Data : Read Data from EEPROM ;****************************************************** ; ; Send Command for RANDOM READ : ; " Start+ 0xA0 + EE_Address + ReStart + 0xA1 + Read_Data + NAck + Stop " ; EE_Random_Read call StartI2C ; Set SSPCON2.SEN ; bcf I2C_Ctrl,0 ; set for write Command movf I2C_Ctrl,W ; Send Slave Address to I2C Bus call Send_Byte ; movf I2C_Addr,W ; Send out the Rendom address of EEPROM call Send_Byte ; call RstartI2C ; Send a Repeat Start to I2C ; bsf I2C_Ctrl,0 ; set for Read Command movf I2C_Ctrl,W ; Send Slave Address to I2C Bus call Send_Byte ; call RecI2C ; Enable I2C Receive ; BANKSEL SSPBUF movf SSPBUF,W ; Save to I2C_Data First !! movwf I2C_Data call Non_Ack ; Initial NACK Response !! call StopI2C ; Initial STOP Condition return ; ;*************************************************************** ;* Sequential Read from EEPROM ;* ;* Input: ;* - I2C_Ctrl : Control Byte of EEPROM ;* - I2C_Addr : Start Location of EEPROM ;* - I2C_Page_Length : How many byte need to read ;* Output: ;* - I2C_SEQU_Buffer : Sequential Read Data buffer ;* ;*************************************************************** ; ; Send Command for RANDOM READ : ; " Start+ 0xA0 + EE_Address + ReStart + 0xA1 + Read_Data + NAck + Stop " ; EE_SEQU_Read call StartI2C ; Set SSPCON2.SEN ; bcf I2C_Ctrl,0 ; set for write Command movf I2C_Ctrl,W ; Send Slave Address to I2C Bus call Send_Byte ; movf I2C_Addr,W ; Send out the Rendom address of EEPROM call Send_Byte ; call RstartI2C ; Send a Repeat Start to I2C ; bsf I2C_Ctrl,0 ; set for Read Command movf I2C_Ctrl,W ; Send Slave Address to I2C Bus call Send_Byte ; movlw I2C_SEQU_Buffer movwf FSR ; _Sequ_Loop call RecI2C ; Enable I2C Receive BANKSEL SSPBUF movf SSPBUF,W ; Save to I2C_Data First !! movwf INDF incf FSR,F decfsz I2C_Page_Length,F goto _Cont_Read goto _End_Read _Cont_Read call An_Ack goto _Sequ_Loop ; _End_Read call Non_Ack ; Initial NACK Response !! call StopI2C ; Initial STOP Condition return ; ;****************************************************** ;* EEPROM Acknowledge Polling ;* ;* -- The routine will polling the ACK ;* response from EEPROM ;* -- ACK=0 return ;* -- ACK=1 send Restart & loop check ;* ;****************************************************** ; EE_Ack_Check call StartI2C ; Set SSPCON2.SEN bcf I2C_Ctrl,0 ; Clear for Write Command movf I2C_Ctrl,W ; Send Slave Address to I2C Bus call Send_Byte _Ack_Polling BANKSEL SSPCON2 btfss SSPCON2,ACKSTAT ; Check ACKSTAT bit , 0 = ACK , 1 = NACK goto ACK_Return ; Ack = 0 ; EEPROM is Ready _Ack_Hi ; Ack = 1 ; EEPROM is Busy for Write call Delay_mS ; Delay 500uS for next Ack polling call RstartI2C ; Send a Repeat Start to I2C bcf I2C_Ctrl,0 ; Clear for Write Command movf I2C_Ctrl,W ; Send Slave Address to I2C Bus call Send_Byte goto _Ack_Polling ACK_Return call StopI2C ; Initial STOP Condition return ; ;********************************************************** ;* Page Write 1 to 8 Bytes to EEPROM ;* ;* Input: ;* - I2C_Ctrl : Control Byte of EEPROM ;* - I2C_Addr : Location of EEPROM ;* - I2C_Page_Buffer: RAM location of Data ;* - I2C_Page_Length : Data length count ;* ;********************************************************** ; ; Send Command for PAGE WRITE : ; " Start+ 0xA0 + EE_Address + W_Data 0+ .. + W_Data N + Stop " ; EE_Page_Write movf I2C_Page_Length,W btfsc STATUS,Z return ; call StartI2C ; Set SSPCON2.SEN ; bcf I2C_Ctrl,0 ; Clear for Write Command movf I2C_Ctrl,W ; Send Slave Address to I2C Bus call Send_Byte ; movf I2C_Addr,W ; Send out the Command call Send_Byte ; movlw I2C_Page_Buffer movwf FSR _W1 movf INDF,W call Send_Byte incf FSR,F decfsz I2C_Page_Length,F goto _W1 ; call StopI2C ; Initial STOP Condition return ; ; ;******************************************************* ;* Write a Byte to EEPROM ;* ;* Input: ;* - I2C_Ctrl : Control Byte of EEPROM ;* - I2C_Addr : Location of EEPROM ;* - I2C_Data : Data to EEPROM ;* ;******************************************************* ; ; Send Command for BYTE WRITE : ; " Start+ 0xA0 + EE_Address + Write_Data + Stop " ; EE_Byte_Write call StartI2C ; Set SSPCON2.SEN ; bcf I2C_Ctrl,0 ; Clear for Write Command movf I2C_Ctrl,W ; Send Slave Address to I2C Bus call Send_Byte ; movf I2C_Addr,W ; Send out the Command call Send_Byte ; movf I2C_Data,W ; Send out the Data call Send_Byte ; call StopI2C ; Initial STOP Condition return ; ;********************************************************************** ; ; As following subroutines perform commonly used I2C functions. ; You can use these subroutines for your I2C access ; ;********************************************************************** ; ;*** Initial I2Cmodule fot 7-bit I2C Master Mode with 100Kcps @4MHz ; Init_I2C_Master BANKSEL TRISC ; Initial PortC,bit 3 & 4 as Input bsf SCL ; RC3 = SCL , RC4 = SDA bsf SDA ; BANKSEL PORTC bsf SCL ; SCL = Hi bsf SDA ; SDA = Hi ; movlw b'00101000' ; I2C Master Mode, Clock Rate: FOSC/(4*SSPADD+1) movwf SSPCON ; ; banksel SSPADD movlw .9 ; This gives 100KHz I2C clock @ 4MHz movwf SSPADD ; (4MHz/4) / (9+1)= 100KHz ; movlw b'10000000' ; Disable slew rate control, movwf SSPSTAT ; and clear status bits ; movlw b'00000000' ; Set SCL,SDA into Ready status movwf SSPCON2 ; return ; ;****** Send START condition to bus ******* ; StartI2C ; Initiate the I2C START condition. banksel SSPCON2 bsf SSPCON2,SEN goto I2C_Done ; ;****** Send STOP condition to bus ******* ; StopI2C banksel SSPCON2 bsf SSPCON2,PEN goto I2C_Done ; ;****** Send RESTART condition to bus ******* ; RstartI2C banksel SSPCON2 bsf SSPCON2,RSEN goto I2C_Done ; ;****** Send a Non-Acknowledge status to bus (ACK=1) ******* ; Non_Ack banksel SSPCON2 bsf SSPCON2,ACKDT ; Set the ACK bit bsf SSPCON2,ACKEN ; Initiate the NACK sequence. goto I2C_Done ; ;****** Send an Acknowledge status to bus (ACK=0) ******* ; An_Ack banksel SSPCON2 bcf SSPCON2,ACKDT ; Clear the ACK bit bsf SSPCON2,ACKEN ; Initiate the NACK sequence. goto I2C_Done ; ;****** Send data to I2C bus from Wreg. ******* ; Send_Byte banksel SSPBUF movwf SSPBUF goto I2C_Done ; ;****** Enable I2C Receive for Master Mode ******* ; RecI2C banksel SSPCON2 bsf SSPCON2,RCEN ; Set the receive enable bit. goto I2C_Done ; ;****** Check the I2C stage is completed ******** ; I2C_Done banksel PIR1 btfss PIR1,SSPIF ; Poll for SSPIF goto $-1 bcf PIR1,SSPIF return ; Delay_mS banksel D_Count movlw .124 ; Load .5mS Dealy Value movwf D_Count _D_mS nop decfsz D_Count,F goto _D_mS return ; ; *********** End Of Program !!!!! ; end