Help understanding extra .inc files within program

Status
Not open for further replies.

Steve311

Member
Hi All,

I am a bit confused with how .inc files work using MPLab. I understand how the individual pic's .inc file works as it defines the processor directives..

I need to create a 703x3 look-up table (total of 2109 values). I have 3 inputs (hence the "x3") which are X, Y and Z. There can be a total of 709 different possible variations of these inputs (hence the "703"). I need to evaluate each and every one of these 2109 values and compare them to my sampled data taken (of X, Y and Z at any given time within the program).

My question is would a .inc file work for creating such a look-up table? What are the benefits and/or limitations of these .inc files? How many of these can I call out within my program?

I am using a pic16f684.

Any advice would help. Thanks in advance!

SL
 
Firstly... The chip you selected only has 2048 bytes of memory... 128bytes of ram and 256 bytes of EEPROM, so a lookup table of that size would need to be external....

If you attach a 24c16 you would be able to access the table externally quite easily..

You can include many inc files, The reason I use them is to give me better readability... You can group together routines and hold them within one single file ie.. LCD routines in one, I2C routines in another etc....

Hope this helps
 
Thanks ian. i was thinking about using external memory. From the 24c16 datasheet, it doesnt really show how to input and retreive bytes of data, unless im missing something here? Im currently using i2c in my application so that wont be a problem.... do you know of any good app notes for using this type of memory with lookup tables?

Thanks again
 
Reading and writing to I2C eeprom is the same as most I2C transactions. The 24C16 is strange as it has 2048 locations in 128 byte pages, therefore is accessed by a byte address and pages are set by the device address.... I think I should have pointed out, the 24LC64 or 256 would probably be a far better option... There are TONS of code snippets on the web for I2C eeprom read / writes.. I use C (primarily) so I don't have any... It wouldn't take me much time to convert them though... But as I said.. I don't need to.
 
.INC files are used for assembling in "common" code routines that you use in every piece of code you write. Things like delay loops, EEPROM read/write, serial interrupts, etc etc....code segments that get used in most pieces of code you write but never change. This keeps you from having to type the same routines over and over and over everytime you write a new program. Think of it as having libraries available, but for assembly language writers (hey...why should the C cats have all the fun? ).

What you do is you write the function routine out, then save it in a "FILENAME.INC" file. Once you have all of your function include files written, you save them into an "include" directory of your choice, then direct the assembler to this directory by changing the "include path" setting in MPLAB. Then when you want to use that function in a given program, all you have to do is type out -

Code:
                include                "FILENAME.INC"

wherever you want that function routine to appear in your main program.

Example, I have an include file for my main delay functions titled 16F88xEEPROM.INC and in this file resides this code -

Code:
;To use these routines -
;1) Ensure that DATA_EE_ADDR and DATA_EE_DATA are defined in the common RAM space (addresses 0x70-0x7F)
;   using either the cblock directive or EQU directive statements
;2) To use the write routine, load the write address into DATA_EE_ADDR and the write data to DATA_EE_DATA
:3) To use the read routine, all that needs to be loaded is the read address in DATA_EE_ADDR. The read
;   function returns the read data to DATA_EE_DATA.
;4) If using interrupts, be sure to define the variable INTERRUPTS before the start of the main program
;   using the #define directive as shown below -

;#define	INTERRUPTS	1			;DO NOT COMMENT OUT THIS LINE!!!


DataEEWrite	bcf		INTCON,GIE		;disable interrupts
		banksel		EEADR			;bank 2
		movfw		DATA_EE_ADDR		;write address to W
		movwf		EEADR			;write address to address pointer
		clrf		EEADRH			;8 bit addressing
		movfw		DATA_EE_DATA		;write data to W
		movwf		EEDAT			;write data to data buffer
		clrf		EEDATH			;8 bit data
		banksel		EECON1			;bank 3
		bcf		EECON1,EEPGD		;access data EEPROM
		bsf		EECON1,WREN		;enable EEPROM write
		movlw		0x55			;write unlock codes
		movwf		EECON2
		movlw		0xAA
		movwf		EECON2
		bsf		EECON1,WR		;initiate write cycle
		btfsc		EECON1,WR		;write complete?
		goto		$-1			;no, check again
		bcf		EECON1,WREN		;yes, disable EEPROM write
		banksel		0			;bank 0
		bcf		PIR2,EEIF		;clear EEPROM write interrupt flag

ifdef		INTERRUPTS

		bsf		INTCON,GIE		;enable interrupts

endif
		
		return					;done

DataEERead	bcf		INTCON,GIE		;disable interrupts
		banksel		EEADR			;bank 2
		movfw		DATA_EE_ADDR		;read address to W
		movwf		EEADR			;read address to EEADR
		clrf		EEADRH			;8 bit addressing
		banksel		EECON1			;bank 3
		bcf		EECON1,EEPGD		;access data EEPROM
		bsf		EECON1,RD		;initiate read
		banksel		EEADR			;bank 2
		movfw		EEDAT			;read data to W
		movwf		DATA_EE_DATA		;read data to data buffer
		banksel		0			;bank 0
		
ifdef		INTERRUPTS

		bsf		INTCON,GIE		;enable interrupts

endif

		return					;done

Then at the very bottom of my main code where I would normally type those routines out, I type -

Code:
		include		"16F88xEEPROM.INC"

This keeps my common functions I use in every program in separate files that are easy to assemble in and save me loads of typing everytime. Also, having common functions in separate files makes them super easy to find without having to sift through the main program to find them since they're in separate files.
 
Last edited:
Thanks Ian - I actually did end up buying the 64k bit just in for potential future expansion anyways. I guess I am still a bit confused with the architecture of the EEPROM (the datasheet doesn't explain it very well). I'm confused with the "pages". I understand the locations (ex. the 16bit has 8 "banks" with 256 bytes per bank, 8 bits per byte, giving the 16k). I don't understand how the datasheet references a data "page" as only 16 bytes of 8 bit data. So does that mean there are Could you help clarify this?

Does this mean I can access one page from 0x00 to 0x0f, then the next page from 0x10 to 0x1f? and so on... until 0xf0 to 0xff?

Thanks Again
 
Jon - Great explanation of the .inc files... I do see the great benefits to using these. What are the limitations of these and how it relates to the pic's program memory, RAM, and EEPROM? Are they at all related?

Also, I notice your posted code is for our main delay with the pic and EEPROM in i2c.... What is the purpose of this main delay and how is it used? I am having trouble talking to the 16LC16 (I dont seem to be getting the first ACK while trying to write to a EEPROM location). I wonder if it is something to do with that? I can talk to a Kionix accelerometer perfectly fine using the same i2c sub routines so I don't really question those...

Below is my code; Basically, I am not getting the first "ACK" you see, and the following two lines are a test point that I put in just to see where the program is getting hung up..

Any help would be great! Thanks

Here is my code:
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
	clrf	TEST2
	clrf	TEST3
;Write to Bank 0, Word 0 (Control Byte: 10100000)


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

	call	Clock_Pulse_Delay
	call	Start_Command
	movlw	0xA0
	call	Send_i2c_Byte
	call	ACK


	movlw	0x58
	call	Transmit_RS_232

	movlw	0x01
	call	Send_i2c_Byte
	call	ACK
	movlw	0x56
	call	Send_i2c_Byte
	call	ACK
	call	Stop_Command
;;TEST TO WRITE TO JUST ONE REGISTER

	goto	Read_EEPROM

	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay
	call	Start_Command
	movlw	0xA4
	call	Send_i2c_Byte
	call	ACK
	movlw	0x55
	call	Send_i2c_Byte
	call	ACK
	movlw	0x57
	call	Send_i2c_Byte
	call	ACK
	call	Stop_Command

	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay
	call	Start_Command
	movlw	b'10101110'
	call	Send_i2c_Byte
	call	ACK
	movlw	0xff
	call	Send_i2c_Byte
	call	ACK
	movlw	0x58
	call	Send_i2c_Byte
	call	ACK
	call	Stop_Command

;Read from Bank 0, Word 0 (Control Byte: 10100001)
Read_EEPROM
	movlw	0x0A
	movwf	Read_Delay
Read_Delay_Loop1
	call	Clock_Pulse_Delay
	decfsz	Read_Delay
	goto	Read_Delay_Loop1
	
	call	Start_Command
	movlw	b'10100000'
	call	Send_i2c_Byte
	call	ACK
	movlw	b'00000001'
	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
	movwf	TEST1
	call	NACK
	call	Stop_Command


	goto	Send_Output

	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay	
	call	Start_Command
	movlw	b'10100100'
	call	Send_i2c_Byte
	call	ACK
	movlw	b'01010101'
	call	Send_i2c_Byte
	call	ACK
	call	Clock_Pulse_Delay
	call	Start_Command
	movlw	b'10100101'
	call	Send_i2c_Byte
	call	ACK
	call	Receive_i2c_Byte
	movf	Received_I2C_Byte, w
	movwf	TEST2
	call	NACK
	call	Stop_Command

	call	Clock_Pulse_Delay
	call	Clock_Pulse_Delay	
	call	Start_Command		;Start
	movlw	b'10101110'
	call	Send_i2c_Byte		;
	call	ACK
	movlw	b'11111111'
	call	Send_i2c_Byte
	call	ACK
	call	Clock_Pulse_Delay
	call	Start_Command
	movlw	b'10101111'
	call	Send_i2c_Byte
	call	ACK
	call	Receive_i2c_Byte
	movf	Received_I2C_Byte, w
	movwf	TEST1
	call	NACK
	call	Stop_Command

Send_Output
	movf	TEST1, w
	call	Transmit_RS_232
;;	movf	TEST2, w
;	call	Transmit_RS_232
;	movf	TEST3, w
;	call	Transmit_RS_232

done
	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_SCL_High				;Release SCL
	call	Set_SDA_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	0x01
	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
 
Actually I mistyped my post. Those code routines are for reading from/writing to the on chip EEPROM on 16F88x series PICs.

The limitation is that you cannot assemble in more code than can physically fit onto the PIC's internal code space. So the code present in your main code + code present in .inc files cannot exceed the available code space in the PIC.

Also, the include statements in main code that include the routines must be placed in line with the main program where you want the code in the .inc files to be assembled in at. Other than that, no real limitations.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…