Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Reading and Writing to EEPROM using pic16f684

Status
Not open for further replies.

Steve311

Member
Hi All, I am stuck with communicating between a pic16f684 and 24LC16B EEPROM. I am simply trying to write one byte to one Word Address of the EEPROM, and simply read it back. I then output it to a hyperterminal on my PC. I know my hyperterminal works fine, I have verified that.

Below is my code; I am using software rather than an MSSP module. I have previously been able to communicate with a different i2c device using the same code (a Kionix accelerometer). So I think my i2c routines are OK. I keep getting a result of 0x20 regardless of what I input into the EEPROM Word Address.

Any help is greatly appreciated!

Thx

Code:
;.......................................................................................................................
;.......................................................................................................................
;Define Global Deifnitions:	
I2C_PORT	equ		PORTC
I2C_TRIS	equ		TRISC
#define		SDA	 	2
#define		SCL 	3
	
;.......................................................................................................................
;.......................................................................................................................
START_PROGRAM
;Initialize Inputs/Outputs, and Registers on the PIC16F684.
;Initialize TRISA and TRISB registers. 
 	bsf 	STATUS, RP0			;Bank 1
 	movlw 	b'00001100'		 
 	movwf 	TRISC				;Assigns SDA and SCL Inputs.
 	movlw	b'00000000'		
	movwf	TRISA				;Assigns all TRISA as outputs. 
	movlw	0x03
	movwf	OSCCON				;Loads internal 31.25kHz clock
	bcf		STATUS, RP0			;Bank 0
	clrf	CCP1CON
;......................................................................................................................
;.......................................................................................................................



	
	
Start_Loop
	clrf	TEST1

	movlw	0x0A
	movwf	Write_Delay
Write_Delay_Loop
	call	Clock_Pulse_Delay
	decfsz	Write_Delay
	goto	Write_Delay_Loop

	call	Start_Command
	movlw	0xA0
	call	Send_i2c_Byte


	call	ACK

	movlw	b'00001111'
	call	Send_i2c_Byte
	call	ACK
	movlw	0x4D
	call	Send_i2c_Byte
	call	ACK
	call	Stop_Command


;;TEST TO WRITE TO JUST ONE REGISTER

	goto	Read_EEPROM


;Read from Bank 0, Word 0 (Control Byte: 10100001)
Read_EEPROM
	movlw	0x0f
	movwf	Read_Delay
Read_Delay_Loop1
	call	Clock_Pulse_Delay
	decfsz	Read_Delay
	goto	Read_Delay_Loop1
	
	call	Start_Command
	movlw	b'10100000'		;A0
	call	Send_i2c_Byte
	call	ACK

	movlw	b'00001111'		;0x01
	call	Send_i2c_Byte
	call	ACK

;	call	Clock_Pulse_Delay
	call	Start_Command
	movlw	b'10100001'
	call	Send_i2c_Byte	
	call	ACK
	call	Receive_i2c_Byte



	movf	Received_I2C_Byte, w
	call	Transmit_RS_232
 

	call	NACK
	call	Stop_Command



	goto	Send_Output



Send_Output
;	movlw	0x55
;	call	Transmit_RS_232
;	movf	TEST1, w
;	call	Transmit_RS_232
;;	movf	TEST2, w
;	call	Transmit_RS_232
;	movf	TEST3, w
;	call	Transmit_RS_232


	goto	Start_Loop







;---------------------------------------------------------------------------------------
;Sub Routines for PIC
;---------------------------------------------------------------------------------------
;i2c Routines
;----------------------------------
Start_Command	
	call	Set_SCL_High				;Release SCL
	call	Set_SDA_High				;Release SDA
	call	Clock_Pulse_Delay			;Delay
	call	Set_SDA_Low					;Set SDA Low
	call	Clock_Pulse_Delay			;Delay
	return
;----------------------------------
SAD_W
	movlw	0x1E
	call	Send_i2c_Byte				;(SAD+W)Slave Address plus write.
	return
;----------------------------------
SAD_R
	movlw	0x1F
	call	Send_i2c_Byte				;(SAD+R)Slave Address plus read.
	return
;----------------------------------
ACK
	call	Set_SDA_High				;Release SCL
	call	Set_SCL_High				;Release SDA
	call	Clock_Pulse_Delay			;Delay
Clock_Stretch
	btfss	I2C_PORT, SCL				
	goto	Clock_Stretch				;Wait for slave to release clock line (if "stretching")
	call	Clock_Pulse_Delay			;Delay
	bcf		STATUS, C					;Initally clears the carry bit
	btfsc	I2C_PORT, SDA				;Checks if slave is pulling SDA low (giving its ACK)
	goto	_Error						;Clear carry for NACK
	bsf		STATUS, C					;Set carry for ACK
	btfss	STATUS, C					;Check carry bit to ensure ACK
	goto	START_PROGRAM					;Start over if NACK
	call	Set_SCL_Low
	return								;If ACK received, continue 
;----------------------------------

SEND_ACK
	call	Clock_Pulse_Delay
	call	Set_SCL_Low
Get_ACK_SDA
	btfss	I2C_PORT, SDA				;Wait to see if the SDA line is released.
	goto	Get_ACK_SDA					;Loop
	call	Set_SDA_Low
;	call	Set_SDA_High				;Ensure SDA is released.
	call	Set_SCL_High				;Trigger SCL
	call	Clock_Pulse_Delay			;Delay
	call	Set_SCL_Low
;	call	Set_SCL_High				;Trigger Clock
	call	Set_SDA_High				;Release SDA
	call	Clock_Pulse_Delay			;Delay
;	call	Clock_Pulse_Delay			;Delay
;	call	Set_SDA_High				;Release SDA
;	call	Set_SCL_Low
	return								

NACK
	call	Set_SDA_High				;Release SCL
	call	Clock_Pulse_Delay			;Delay
	call	Set_SCL_High				;Set SCL Low
;	call	Set_SDA_High				;Release SDA
	call	Clock_Pulse_Delay			;Delay
	call	Set_SCL_Low
	return
;----------------------------------
Stop_Command
	call	Set_SCL_High				;Release SCL
	call	Clock_Pulse_Delay			;Delay
	call	Set_SDA_High				;Release SDA
	call	Clock_Pulse_Delay			;Delay
	call	Clock_Pulse_Delay			;Delay
	return
;----------------------------------
Send_i2c_Byte
	movwf	i2c_transmit_byte			;Moves whatever value is in the working register into i2c_transmit_byte
	movlw	0x08	
	movwf	i2c_bit_counter				;Sets 8 bits to be sent out
Send_i2c_Bit
	call	Set_SCL_Low					;Ensure SCL is low initially
	call	Clock_Pulse_Delay			;Delay
	rlf		i2c_transmit_byte, f		;MSB is now in carry (MSB is sent first)
	btfsc	STATUS, C					
	call	Set_SDA_High
	btfss	STATUS, C
	call	Set_SDA_Low					;Sets either 0 or 1 to be sent out. (Checks carry bit)
	call	Clock_Pulse_Delay			;Delay
	call	Set_SCL_High				;Trigger Clock; set SCL high
 	call	Clock_Pulse_Delay			;Delay
	decfsz	i2c_bit_counter, f			;Checks if 8 bits have been sent out
	goto	Send_i2c_Bit				;No
	call	Set_SCL_Low					;Yes; Set SCL Low
	call	Clock_Pulse_Delay			;Delay
	return
;-------------------------------------	
Receive_i2c_Byte
	clrf	Received_I2C_Byte			;Clears previous received byte
	bsf		Received_I2C_Byte, 0
;	call	Set_SDA_High
	call	Clock_Pulse_Delay
Get_i2c_Bit
	call	Set_SCL_Low
	call	Set_SDA_High				;Set SCL Low
	call	Clock_Pulse_Delay			;Delay
	bcf		STATUS, C					;Defaults to incoming bit = 0 (sets carry = 0)
	call	Set_SCL_High				;Release SCL
	call	Clock_Pulse_Delay
	btfsc	I2C_PORT, SDA				;Read port, skip if clear
	bsf		STATUS, C					;If incoming bit = 1, set carry flag to 1
	rlf		Received_I2C_Byte, f		;move carry into data (MSB first)
	call	Clock_Pulse_Delay
	btfss	STATUS, C
	goto	Get_i2c_Bit					;No

;Test to ensure hypertermianl works correctly. 
;	movlw	0x55
;	movwf	Received_I2C_Byte

;	call	Set_SCL_Low


	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay
	return								;Yes, return

	
	

;Routines to Set SDA and SCL High/Low

Set_SCL_High
	bsf		STATUS, RP0				;Bank 1
	bsf		I2C_TRIS, SCL			;Sets SCL as an input
	clrf	ANSEL
	bcf		STATUS, RP0				;Bank 0	
	movlw	0x07
	movwf	CMCON0
	return

Set_SCL_Low
	bcf		I2C_PORT, SCL			;Sets SCL port pin low
	bsf		STATUS, RP0				;Bank 1
	bcf		I2C_TRIS, SCL			;Sets SCL as an output
	clrf	ANSEL
	bcf		STATUS, RP0				;Bank 0
	movlw	0x07
	movwf	CMCON0
	return	

Set_SDA_High
	bsf		STATUS, RP0				;Bank 1
	bsf		I2C_TRIS, SDA			;Sets SDA as an input (high impedance)
	clrf	ANSEL
	bcf		STATUS, RP0				;Bank 0
	movlw	0x07
	movwf	CMCON0
	return

Set_SDA_Low
	bcf		I2C_PORT, SDA			;Sets SDA port pin low
	bsf		STATUS, RP0				;Bank 1
	bcf		I2C_TRIS, SDA			;Sets SDA as an output
	clrf	ANSEL
	bcf		STATUS, RP0				;Bank 0
	movlw	0x07
	movwf	CMCON0
	return	

;---------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------
;Delays
Clock_Pulse_Delay
	movlw	0x03
	movwf	pw_counter
Clock_Pulse_Delay_Wait
	nop
	movlw	0x02
	movwf	pw_counter1
Clock_Pulse_Delay_Wait1
	decfsz	pw_counter1, f
	goto	Clock_Pulse_Delay_Wait1
	decfsz	pw_counter, f
	goto	Clock_Pulse_Delay_Wait
	return

bit_delay
	movlw	0x16
	movwf	delay_counter
bit_wait
	nop
	decfsz  delay_counter, f
	goto	bit_wait
	return

Osc_Stablize
	movlw	0x0F
	movwf	osc_counter
Osc_Wait
	nop
	nop
	nop
	decfsz	osc_counter, f
	goto	Osc_Wait
	return

_Error
	call	Clock_Pulse_Delay
	btfsc	I2C_PORT, SDA
	goto	Next
	return

Next	
	call	Clock_Pulse_Delay
	btfsc	I2C_PORT, SDA
	goto	Next1
	return
Next1
	call	Clock_Pulse_Delay
	btfss	I2C_PORT, SDA
	return
	
;Error
	movlw	0x45
	call	Transmit_RS_232
	movlw	0x52
	call	Transmit_RS_232
	movlw	0x52
	call	Transmit_RS_232
	movlw	0x4F
	call	Transmit_RS_232
	movlw	0x52
	call	Transmit_RS_232
	goto	START_PROGRAM

I2cInit	
	call	Set_SCL_Low
	call	Set_SDA_Low
	call	Set_SCL_High
	call	Set_SDA_High
ClockLoop
	btfsc	PORTC,SDA
;	goto	BusFixed
	bcf		PORTC,SCL
	bsf		STATUS,RP0
	bcf		TRISC,SCL
	goto	$+1
	bsf		TRISC,SCL
	bcf		STATUS,RP0
	goto	ClockLoop

;---------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------
;Sub routines for PIC to Hyperterminal	
;****SENDS INVERSE LOGIC TO PROLIFIC RS-232 TO USE CONVERTER
Transmit_RS_232	
	movwf	transmit_byte_rs232		;moves W to transmit_byte, 

	bsf		STATUS, RP0
	movlw	0x65
	movwf	OSCCON
	movlw	0x00
	movwf	TRISC
	movlw	0x00
	movwf	TRISA
	bcf		STATUS, RP0

;	movwf	transmit_byte_rs232		;moves W to transmit_byte

	call	Osc_Stablize

;	movlw	0x55	
;	movwf	transmit_byte_rs232



	bcf		PORTC, 5
;	movlw	0x55
;	movwf	Received_I2C_Byte
;	movf	Received_I2C_Byte, w
	movlw	0x08		
	movwf	bit_counter_rs232		;sets 8 bits to send out
	bsf		PORTC, 5
	call	bit_delay

Send_Serial_Loop_RS232
	rrf		transmit_byte_rs232, f		;Send one bit
	btfss 	STATUS,	 C
	bsf		PORTC, 5
	btfsc	STATUS,  C
	bcf		PORTC, 5
	call	bit_delay
	decfsz	bit_counter_rs232, f		;Tests if all done
	goto	Send_Serial_Loop_RS232
	bcf		PORTC, 5
	call	bit_delay
	call	bit_delay

	clrf	transmit_byte_rs232
	clrf	bit_counter_rs232

	bsf		STATUS, RP0
	movlw	0x03
	movwf	OSCCON
;	movlw	0x00
;	movwf	TRISC
;	movlw	0x00
;	movwf	TRISA
	bcf		STATUS, RP0
	return
;--------------------------



Sleep_Routine
	movlw	0x07	
	movwf	WDTCON
	sleep
	movlw	0x00	
	movwf	WDTCON
	return


Control_Byte_Write_000
	movlw	0xA0
	call	Send_i2c_Byte
	return
Control_Byte_Write_010
	movlw	0xA4
	call	Send_i2c_Byte
	return
Control_Byte_Write_111
	movlw	0xAE
	call	Send_i2c_Byte
	return
Word_Address_0
	movlw	0x00
	call	Send_i2c_Byte
	return
Word_Address_55
	movlw	0x55
	call	Send_i2c_Byte
	return
Word_Address_ff
	movlw	0xff
	call	Send_i2c_Byte
	return
 
Hi Steve. Some coding practice tips here...

1) There is absolutely no reason to modify the TRIS bits to pull port lines high/low. A simple "bsf <PORT>,<BIT>" to pull high and a "bcf <PORT>,<BIT>" to pull low will do. The ONLY time a port needs to be tristated is if you need to use the port pins as inputs.

2) There is no reason to define I2C_PORT and I2C_TRIS. All that needs to be defined for bit banging I2C is SDA and SCL, and you define them as such -

Code:
#define          SDA              PORTC,2
#define          SCL              PORTC,3

Now we can eliminate the subroutines "Set_SDA_High", "Set_SDA_Low", "Set_SCL_High" and "Set_SCL_Low" (it should NEVER require a subroutine for a task as simple as setting a single pin high or low). You can just simply do -

Code:
;these 4 lines are just code examples to illustrate how to go about setting SDA/SCL high and low

 	bsf		SDA		;set SDA high
	bcf		SDA		;set SDA low
	bsf		SCL		;set SCL high
	bsf		SCL		;set SCL low
 
Last edited:
Hi Jon, Thanks for the input. With using bcf to set the line low, won't it end up floating high as the i2c bus requires pull-up resistors? I tried it in my code and I set a stop point right after the start command, and I notice there is NO high to low transition on the Data line..?
 
Hi Steve.

HOW PIC I/O PINS WORK

PIC I/O pins have internal active strong pull up devices. The bits in a port's TRIS registers set whether these active pull up/pull downs are active or inactive. They are controlled by the port latch.

Writing a 0 to a pin's TRIS bit makes that pin's active pull up/pull down devices (known as output drivers) active.

When the output driver is active, either the pull up or the pull down is on. They are never both off when the output driver is active. A bsf instruction activates the active pull up/deactivates the active pull down while bcf activates the active pull down/deactivates the active pull up.

Knowing this, we can conclude that the pin can never float when the internal output drivers are active.

When we write a 1 to a pin's TRIS bit, this turns both active pull up/pull down devices off. This leaves the pin in a floating state internally, and we now must use an external component to pull the pin up or down. With a pull up resistor on the pin, this will take the pin out of the floating state and pull it high unless some external component is pulling the pin low. If nothing externally pulls it low, the pull up resistor will pull it high.

Having an external pull up on a pin that is set to output will just make the pin sink the resistor current when set low. However, bsf and bcf instructions will still pull the pin high/low respectively.
 
Last edited:
Thanks for the great explanation. That clarify's quite a bit for me. No pun intended...

So it seems that bcf and bsf are a better option. I do know my i2c routines work with a Kionix accelerometer, but I am always open for doing things more efficiently (or the right way, haha)....

So I definitely want to write 1's to the SDA and SCL TRIS bits for i2c. So If I go to clear the SDA line for a start command, it will go low momentarly? Or do I want to drive it low by writing a 0 to its TRIS bit?
 
Thanks for the great explanation. That clarify's quite a bit for me. No pun intended...

So it seems that bcf and bsf are a better option. I do know my i2c routines work with a Kionix accelerometer, but I am always open for doing things more efficiently (or the right way, haha)....

So I definitely want to write 1's to the SDA and SCL TRIS bits for i2c. So If I go to clear the SDA line for a start command, it will go low momentarly? Or do I want to drive it low by writing a 0 to its TRIS bit?

Actually....

You want to write 0's to SDA and SCL's TRIS bits. This configures them as OUTPUTS, which allows them to send I2C data out.

Then you write the bits you're sending to SDA and SCL's PORT bits.

Think about it like this. The pins can function as EITHER an input OR an output. Not both at the same time (there is actually a way to do this on ports that have weak internal pull ups but that's a technique that lies beyond the scope of this discussion). The TRIS register bits are the bits that set up each pin on a port as input or output. Writing a 1 to a TRIS bit configures the TRIS bit's pin as an input while writing a 0 to a TRIS bit configures the TRIS bit's pin as an output. The ONLY time you mess with TRIS bits is when you're changing an I/O pin from an input to an output or vice versa. In this case you want SDA and SCL to function as outputs, so you would write 0's to their TRIS bits.

When you want to set an output high or low, you write to the output pin's PORT bit. You can either modify the entire port value by first writing a value to W, then writing the value from W to the PORT register, OR you can write to the pin directly with bsf and bcf. With input pins, you READ the pin's PORT bits with btfss and btfsc instructions.
 
Last edited:
Steve...I got some stuff I'm typing up for you...more to come, but I'll leave you with this little bit.

I2C requires open collector (or open drain in this case) drive. This is why the pull ups are needed. You can simulate an open drain pin by first writing 0's to the pin's port latch bits in the PORT register. These bits always remain 0's and never get changed. Then, you would toggle the pin's TRIS bits to switch the pin between input and output, which makes the pin switch between 0 (provided by the port latch bits) and floating. The resistor provides the high when in the floating state.

Writing a 1 to the pin's TRIS bit would make the pin float internally, which allows the pull up to source a high signal on the SDA/SCL lines.
Writing a 0 to the pin's TRIS bit would make the pin output a 0, provided by the 0's in the pin's port latch bits, which pulls the SDA/SCL lines low.

More to come...with coding examples. Stay tuned.
 
Last edited:
Great thanks alot Jon. I should be able to spend more time on this later today/tonight. I believe my first issue is that I am writing 1's to the TRIS pins.... I will start there!
 
Great thanks alot Jon. I should be able to spend more time on this later today/tonight. I believe my first issue is that I am writing 1's to the TRIS pins.... I will start there!

When sending I2C data, it is the TRIS bits that need to be toggled as you shift out I2C data. But in order for it to work right, you have to first write 0's to those pins in the right PORT register.

Since you're using PORTC pins 2 and 3, ensure that you write 0's to those pins in your initialization routine -

Code:
                     bcf                   PORTC,2
                     bcf                   PORTC,3

Then, when you switch TRISC pins 2 and 3 to a 1, the pin floats, but the line it's connected to is pulled high by the pull up resistors. When you switch TRISC bits 2 and 3 to a 0, the 0's written to the pins in the PORTC register are output on PORTC pins 2 and 3, which pulls the lines low.

Make sense?
 
Last edited:
Here is some sample code I wrote for a 16F886. Not sure if it works in hardware, but MPLAB SIM runs it fine and on the watch window the appropriate bits get driven float/low. Also, I used PORTB 2 and 3 just for kicks instead.

Notice my use of indirect addressing. My I2C routine loads the address of TRISB into the FSR, then bits 2 and 3 of INDF (which are equated to the labels SDA and SCL respectively) are being toggled between 0 and 1. This allows me to access the TRISB register without having to bank select.

My code first loads a byte counter to count how many bytes will be sent prior to sending a stop bit. I loaded it with the value of decimal 18 to count 16 data bytes + the control and word bytes. This allows me to page write 16 bytes to the memory, then come to a halt. This code, in theory, should write alternating values of 0xAA and 0x55 to the first 16 locations of memory block 0.

The subroutine first sends the start bit, burns 4 clock cycles, then drops SCL low to start the clock. Then, 8 bits are clocked out as SCL is toggled. Then, SCL is dropped low and SDA is brought high. We then clear timer 0 and the timer 0 interrupt flag, then poll the SDA line for an ACK bit. Timer 0 is providing a time out to jump out of the routine in the event that an ACK bit is never received from the slave.

After receiving the ACK bit from the slave, it decrements the byte counter. If the byte counter does not equal zero, it leaves SCL low, jumps back to the SendI2C Subroutine (or the main code, depending on whether we're sending control and word byte or just data bytes), fetches the next byte to send, then calls the SWI2CWrite routine again. However, if the byte counter = 0, it sends the stop bit, then jumps back to main code where it stops on the "goto $" instruction.

I also included an interrupt handler template with context save routines in case you decide to use interrupts.

Hope this helps.

Code:
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**					Header Information					**
;**												**
;*************************************************************************************************
;*************************************************************************************************

		list		p=16F886, r=dec, w=-302
		include		<P16F886.INC>
		__config	_CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC
		
;**************************************************************************************************
;**************************************************************************************************

		cblock		0x20
				I2CREG
				I2CFLAGS
				BITCOUNT
				BYTECOUNT
				CONTBYTE
				WORDBYTE
				DATABYTE
		endc		
				
		cblock		0x70
				W_TEMP
				STATUS_TEMP
				PCLATH_TEMP
				FSR_TEMP
		endc
		
;*************************************************************************************************
;*************************************************************************************************

#define		SDA		INDF,2
#define		SCL		INDF,3
#define		NOACK		I2CFLAGS,0

;*************************************************************************************************
;*************************************************************************************************
;**												**
;**					Code Organization					**
;**												**
;*************************************************************************************************
;*************************************************************************************************

		org		0x0000			;reset vector
		goto		START
		
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**					Interrupt Handler					**
;**												**
;*************************************************************************************************
;*************************************************************************************************
		
		org		0x0004			;interrupt vector
		movwf		W_TEMP			;save W
		swapf		STATUS,W		;save status
		movwf		STATUS_TEMP
		banksel		0			;bank 0
		movfw		PCLATH			;save PCLATH
		movwf		PCLATH_TEMP
		movfw		FSR			;save FSR
		movwf		FSR_TEMP
		
;space reserved for interrupt handler (if applicable)

ISRExit		movfw		FSR_TEMP		;restore FSR
		movwf		FSR
		movfw		PCLATH_TEMP		;restore PCLATH
		movwf		PCLATH
		swapf		STATUS_TEMP,W		;restore STATUS
		movwf		STATUS
		swapf		W_TEMP,F		;restore W
		swapf		W_TEMP,W
		retfie
		
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**					Initialization Routine					**
;**												**
;*************************************************************************************************
;*************************************************************************************************		
		
START		clrf		PORTA			;clear all port latches
		clrf		PORTB
		clrf		PORTC
		clrf		PORTE
		
		banksel		ANSEL			;bank 3
		clrf		ANSEL			;all port pins digital I/O mode
		clrf		ANSELH
		
		banksel		TRISA			;bank 1
		clrf		TRISA			;RA0-RA5 output
		movlw		b'00001100'		;RB0-RB1, RB4-RB7 output, RB2-RB3 default float for I2C
		movwf		TRISB
		clrf		TRISC			;RC0-RC7 output
		
		movlw		0xC7			;init timer 0 as ack time out timer
		movwf		OPTION_REG

		banksel		0			;bank 0
		
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**					Begin Main Code						**
;**												**
;*************************************************************************************************
;*************************************************************************************************
		
;Example 1 - Sending Control/Word/Data
		
MAIN		movlw		18			;init byte counter
		movwf		BYTECOUNT		;to send 18 bytes
		movlw		0x50			;load control byte buffer - write to block 0
		movwf		CONTBYTE
		movlw		0x00			;load word byte buffer - start at address 0x00
		movwf		WORDBYTE
		movlw		0xAA			;load data byte buffer
		movwf		DATABYTE
		call		SendI2CCont		;send

;Example 2 - Continuous data after Control/Word/Data have already been sent

		movlw		0x55
		movwf		DATABYTE
		call		SendI2CData
		movlw		0xAA
		movwf		DATABYTE
		call		SendI2CData
		movlw		0x55
		movwf		DATABYTE
		call		SendI2CData
		movlw		0xAA
		movwf		DATABYTE
		call		SendI2CData
		movlw		0x55
		movwf		DATABYTE
		call		SendI2CData
		movlw		0xAA
		movwf		DATABYTE
		call		SendI2CData
		movlw		0x55
		movwf		DATABYTE
		call		SendI2CData
		movlw		0xAA
		movwf		DATABYTE
		call		SendI2CData
		movlw		0x55
		movwf		DATABYTE
		call		SendI2CData
		movlw		0xAA
		movwf		DATABYTE
		call		SendI2CData
		movlw		0x55
		movwf		DATABYTE
		call		SendI2CData
		movlw		0xAA
		movwf		DATABYTE
		call		SendI2CData
		movlw		0x55
		movwf		DATABYTE
		call		SendI2CData
		movlw		0xAA
		movwf		DATABYTE
		call		SendI2CData
		movlw		0x55
		movwf		DATABYTE
		call		SendI2CData
		
		goto		$
		
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**					I2C Subroutines						**
;**												**
;*************************************************************************************************
;*************************************************************************************************
		
SendI2CCont	movfw		CONTBYTE
		call		SWI2CWrite
SendI2CWord	movfw		WORDBYTE
		call		SWI2CWrite
SendI2CData	movfw		DATABYTE
		call		SWI2CWrite
		return
		
;*************************************************************************************************
		
SWI2CWrite	movwf		I2CREG		;drop send data into send register
		bcf		NOACK		;clear NOACK flag
		movlw		0x86		;move TRISC address to FSR pointer
		movwf		FSR		;to avoid bank selecting
		movlw		8		;init bit counter to send 8 bytes
		movwf		BITCOUNT

StartBit	bcf		SDA		;send start bit
		call		SWI2CReturn	;burn 4 clock cycles
		bcf		SCL		;clock low

TestBit		rlf		I2CREG,F	;shift bits
		bcf		SDA		;default send = 0
		btfsc		STATUS,C	;MSB=0?
		bsf		SDA		;no, send 1
		bsf		SCL		;clock high
		decfsz		BITCOUNT,F	;send 8 bits
		goto		$-8
		
AckByte		call		WaitForAck	;wait for ACK bit
		bcf		SCL		;clock low
		btfsc		NOACK		;ack received?
		goto		SWI2CReturn	;no, exit

		decfsz		BYTECOUNT,F	;decrement byte counter
		goto		SWI2CReturn

StopBit		bsf		SCL		;clock high
		call		SWI2CReturn	;burn 4 clock cycles
		bsf		SDA		;send stop bit
		goto		SWI2CReturn	;exit		

WaitForAck	bcf		SCL		;clock low
		bsf		SDA		;data line float
		clrf		TMR0		;clear timer 0
		bcf		INTCON,T0IF	;clear timer 0 interrupt flag
		bsf		SCL		;clock high
		btfss		PORTB,2		;ack received?
		goto		SWI2CReturn	;yes, return back
		btfss		INTCON,T0IF	;no, time out expired?
		goto		$-3		;no, wait for ack
		bsf		NOACK		;no ack, set NOACK flag

SWI2CReturn	return

;*************************************************************************************************

		end
 
Now for receiving I2C data, the TRIS bits for your SDA/SCL pins should remain 1's. But...you will be READING the SDA/SCL bits in the PORT register instead as TRIS bits cannot be externally modified. Just thought I'd add that little tid bit.

To summarize -

When sending I2C data, write 0's to the SDA/SCL bits in the PORT register, then toggle the SDA/SCL pin's TRIS bits.

When receiving I2C data, write 1's to the SDA/SCL bits in the TRIS register, then read the SDA/SCL pin's PORT bits.
 
Hi jon thanks for the response, im a little tied up with family things si i wont be able to get back to programming until tomorrow night.... just wanted to give you the heads up and that im not blowing you off

Thanks
Steve
 
Jon, This is perfect I can definately make lee -way with your notes. I guess I never really understood TRIS vs PORT bits.

With knowing all of this, Do you think it is more efficient to use a pic with the MSSP module? I did buy some pic16f1824's which have the hardware. I guess I'm going back and forth right now trying to figure which method is best...

I do like how the 1824 has 4096 of pogram memory....

What do you think?
 
I say definitely go with a PIC that has the on-chip MSSP module. I use the PIC 16F886 or the PIC 16F887, depending on whether the application calls for a 28-pin or a 40-pin PIC. Both of these chips feature 8K code/256 byte data memories on chip.

However, if you need less pins than this, the 1824 would be the better option.
 
Yes the package size is what I need to focus on. I will be using a 4x4mm DFN package in the final application.

So thank you again for all of your help, but I think I will forget about the software and wait for a pickit3 to come in so I can program the 1824 in hardware.

Steve
 
Yes the package size is what I need to focus on. I will be using a 4x4mm DFN package in the final application.

So thank you again for all of your help, but I think I will forget about the software and wait for a pickit3 to come in so I can program the 1824 in hardware.

Steve

Here's the PICkit 2 Supported Device List

**broken link removed**

About 1/2 way down the page you will see that with the latest PICkit 2 software (v2.61) and the latest device file (v1.62.14), the PICkit 2 is also compatible with the enhanced mid-range core, including the 16F1824. You install the software on your PC (if you don't already have it) and the device file goes in the directory "C:\Program Files\Microchip\PICkit 2 v2". When prompted to overwrite, click "Yes".

So no PICkit 3 needed. ;)

Hope this helps.
 
Last edited:
I did try that and it did not seem to work, so I did order a pickit3. It just did arrive. I can write successfully to the pic6lf1824.

I already did run into an issue with the program. I simply am writing a ASCII character into a routine to send out via hyperterminal. The program does in fact do so (You will see in my code I first write "T", then I write "V".)..and it outputs correctly to my PC. However, it seems like the pic is getting reset somehow and it continuously sends out "T" "V".....Any thoughts? Maybe something with the config bits? I did clear the WDT

Below is my code: I did turn quite a few lines into text with a ; for things I don't think I need...Any help would be great!

Code:
;Call proper header, include and source files to initialize PIC
 ;	LIST  		p=16F1824    ; Tells Assembler what PIC is used
 	#INCLUDE	<p16lf1824.inc>    ; Includes factory defaults for the PIC
;.......................................................................................................................
;Configure PIC optional properties
 ;__CONFIG	__CONFIG1 _MCLRE_OFF ;_WDTE_OFF ;& BOREN_OFF & _MCLRE_OFF ; Enables CLKOUT, WDT, and Digital I/O on MCLR/RA3 
;.......................................................................................................................
 errorlevel -302, -207, -305	
;.......................................................................................................................
;DISREGARD THIS
;Pins of interest:			PDIP			LP4
;				Pin#		 1				16		Vdd (+2.8V)
;				Pin#		 14				13		Vss (GND) *(Tie ground to Accelerometer and RS232-USB converter, and PICKit2 when programming)
;				Pin#		 5				4		RC5 (RS_232 Data Output) 
;				Pin#		 11				8		Serial Data I/O (SDA)
;				Pin#		 10				9		Serial Clock I/O (SCL)
;				Pin#		 13				12		ICSP Data (ICSP DAT) *(Used for programming only)
;				Pin#		 12				11		ICSP Clock(ICSP CLK) *(Used for programming only)						
;.......................................................................................................................
;Define Constant Working Register Variables 
	cblock 0x20
	i2c_Transmit_Byte
	i2c_Received_Byte
	transmit_byte_rs232
	bit_counter_rs232
	delay_counter
	osc_counter
	pw_counter
	pw_counter1
	datao
	poll_counter

	endc
;.......................................................................................................................
;.......................................................................................................................
;Define Global Deifnitions:	
I2C_PORT	equ		PORTC
I2C_TRIS	equ		TRISC
Write_eeprom equ	0xA0
Read_eeprom	 equ	0xA1
#define		SDA	 	2
#define		SCL 	3
	
;.......................................................................................................................
;.......................................................................................................................
START_PROGRAM
;Initialize Inputs/Outputs, and Registers on the PIC16F684.
;Initialize TRISA and TRISB registers. 
 ;	bsf 	STATUS, RP0			;Bank 1
 ;	movlw 	b'00001100'		 
 ;	movwf 	TRISC				;Assigns SDA and SCL Inputs.
 ;	movlw	b'00000000'		
;	movwf	TRISA				;Assigns all TRISA as outputs. 
;	movlw	0x03
;	movwf	OSCCON				;Loads internal 31.25kHz clock
;	bcf		STATUS, RP0			;Bank 0
;	clrf	CCP1CON
;......................................................................................................................
;.......................................................................................................................

	clrwdt
;done1
;	bsf		PORTC, 2
;	bcf		PORTC, 3
;	goto	done1

	movlw	0x54
	call	Transmit_RS_232
	
	
Start_Loop
	movlw	0x56
	call	Transmit_RS_232
done4
	goto	done4
 
The program should terminate where I "goto - done4" and I should only see one output of "TV"... Any idea on where this might be re-setting itself? MPLab SIM did not show anything in the build file...
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top