24x01 I2C на ATTINY13 без TWI

?v=1
/*
	InDRIVE v4 Application (chip like: FT24C01, Selfprg Must Be Enabled!!! )

	Fuse BYTES
	Low(0x7A) : 		High(0xEE) :
	SPIEN = 1		SELFPRGEN = 1
	EESAVE = 0		DWEN = 0
	WDTON = 0		BODLEVEL1 = 0
	CKDIV8 = 0		BODLEVEL0 = 0
	SUT1 = 0		RSTDISBL = 1
	SUT0 = 1
	CKSEL1 = 0
	CKSEL0 = 1

*/

.include "tn13def.inc"

/* On/Off Defines */
.equ	OFF			=0
.equ	ON			=1
/* Chip Pinouts */
.equ	FT24Cxx		=0
.equ	InDRIVE_v4	=1

/* Reset Controller Enable/Disable */
.equ	RESET_CNT	=OFF
/* Chip Pinouts Define: InDrivev4/Regular I2C Chip */
.equ	CHIP_PINOUTS	=FT24Cxx

.equ	DEBUG		=OFF
/* I2C Mode Define MODE 1/MODE 2 */
.equ	MODE_1		=1
.equ	MODE_2		=2
.EQU	I2C_MODE	=MODE_1

/* Define I2C Parameters */
/* Page Size for Write in BYTES 4/8/16 */
/* 128/256 Size in MODE 1 Protocol can't receive more than 128 Bytes */
.IF I2C_MODE == MODE_2
	.MESSAGE "Compile for I2C Mode 2"
.equ	I2C_PAGE_SIZE	=8
.equ	I2C_EEP_SIZE	=256
.ELSE
	.MESSAGE "Compile for I2C Mode 1"
.equ	I2C_PAGE_SIZE	=4
.equ	I2C_EEP_SIZE	=128
.ENDIF

/* 
Modes Define

	A - Address
	iD - Data From Master
	oD - Data To Master

	MODE 1
		Write - [START][AAAAAAA0][iD + 0]...[iD + I2C_PAGE_SIZE - 1][STOP]
		Read -  [START][AAAAAAA1][oD + 0]...[oD + I2C_EEP_SIZE - 1][STOP]
	MODE 2 
		Write - [START][10100000][AAAAAAAA][iD + 0]...[iD + I2C_PAGE_SIZE - 1][STOP]
		Read -  [START][10100001][oD + 0]...[oD + I2C_EEP_SIZE - 1][STOP]
*/

/*
	I2C slave, fSCL = 400kHz
	FULLY Implemented I2C Protocol for 24xx01/24xx02
	And RESET Controller FOR InDRIVE(v4)

**************************************************************   
	ATTINY13A I2C Configuration
**************************************************************   
   	pin configuration InDRIVE v4:

                       ,---_---.
    (RESET/PB5)     nc |1     8| VCC
          (PB3)  inRST |2     7| SCL  	(PB2)
          (PB4) outRST |3     6| EMU24X (PB1) SDA pin from CORE
                   GND |4     5| nc 	(PB0) unused SDA pin
                       `-------'

   	pin configuration FT24C01:

                       ,---_---.
    (RESET/PB5)     nc |1     8| VCC
        	  (PB3)  	nc |2     7| nc  	(PB2)
      	    (PB4) 	nc |3     6| SCL 	(PB1) SCL pin I2C
                   GND |4     5| SDA 	(PB0) SDA pin I2C
                       `-------'

   	pin configuration FT24C02:

                       ,---_---.
    (RESET/PB5)     A0 |1     8| VCC
       	   (PB3)  	A1 |2     7| nc  	(PB2)
      	    (PB4) 	A2 |3     6| SCL 	(PB1) SCL pin I2C
                   GND |4     5| SDA 	(PB0) SDA pin I2C
                       `-------'

*/
/* Pins Define */
.IF CHIP_PINOUTS == InDRIVE_v4
	.MESSAGE "Chip Pinouts: InDRIVE v4"
.equ	EMU24X	= 1
.equ 	SCL 	= 2
.ELSE
.equ	EMU24X	= 0
.equ 	SCL 	= 1
	.MESSAGE "Chip Pinouts: Regular 24Cxx"
.ENDIF
.equ 	SDA 	= EMU24X
.equ 	ACK 	= SDA
.equ	inRST	= 3
.equ	outRST	= 4


.def	TMPnoINT		=R19
.def	Counter			=R3
.def	CounterInWrite	=R4
.def	SREGST			=R5

/* Real ATTINY13A FLASH Page SIZE In Bytes */
.equ	PAGESIZEB		=(PAGESIZE*2)

/* SRAM Mapping */
.DSEG
.IF I2C_MODE == MODE_2
	_I2c_device_inaddr:  .BYTE 1	/* MODE 2 Region */
	_I2c_device_myaddr:  .BYTE 1
.ENDIF
	_valSPMCSR: .BYTE 1
	_I2c_data_buffer:	.BYTE I2C_PAGE_SIZE
	_I2c_FLASH_buffer:	.BYTE PAGESIZEB
.cseg

/* Read/Write Pointers */
.def	I2c_start_addr	=R16
.def	I2c_wr_counter	=R17
.def	I2c_wr_pointer	=R18
.def	I2c_rd_pointer	=R21

.def	_PINB			=R20

.IF I2C_MODE != MODE_2
	.IF I2C_MODE != MODE_1
		.error "Invalid mode for I2C Selected, please correct I2C_MODE define"
	.ENDIF
.ENDIF

.cseg

.org 0//Reset
.IF RESET_CNT == ON
		rjmp	WaitinRSTHI		;RESET
		reti					;INT0addr	= 0x0001	; External Interrupt 0
		rjmp	ResetInProcess	;PCI0addr	= 0x0002	; External Interrupt Request 0
.ELSE
		rjmp	main			;RESET
		reti					;INT0addr	= 0x0001	; External Interrupt 0
		reti					;PCI0addr	= 0x0002	; External Interrupt Request 0
.ENDIF
		reti					;OVF0addr	= 0x0003	; Timer/Counter0 Overflow
		reti					;ERDYaddr	= 0x0004	; EEPROM Ready
		reti					;ACIaddr	= 0x0005	; Analog Comparator
		reti					;OC0Aaddr	= 0x0006	; Timer/Counter Compare Match A
		reti					;OC0Baddr	= 0x0007	; Timer/Counter Compare Match B
		reti					;WDTaddr	= 0x0008	; Watchdog Time-out
		reti					;ADCCaddr	= 0x0009	; ADC Conversion Complete

// *******************************************************************************************
// **   Reset Processor                                                       				**
// *******************************************************************************************
.IF RESET_CNT == ON
	.MESSAGE "Reset Controller Function is: ON"
ResetInProcess:	;Proccess reset
		sbic	pinb,inRST	;Process reset on falling edge
		reti

WaitinRSTHI:
		cli

		;Wait ~80 mS 0x04E360 on 9.6MHz
		ldi		R16,0x10
		ldi		R17,0xE3
		ldi		R18,0x60
		;Set RESET Enable
		cbi		PORTB,outRST

_CntLO:
		dec		R18
		BRNE	_CntLO
_CntME:
		dec		R17
		BRNE	_CntLO
_CntHI:
		dec		R16
		BRNE	_CntLO

		; And Clear Iterrupt flag for normal exit
		ldi		R16,(1 << PCIF)
		out		GIFR,R16
.ELSE
	.MESSAGE "Reset Controller Function is: OFF"
.ENDIF	// RESET_CNT == ON
		
// *******************************************************************************************************
// **   Main Programm                                                         				**
// *******************************************************************************************************
main:
		;init STACK
		ldi		TMPnoINT,low(RAMEND)
		out		SPL,TMPnoINT

		;init IO
		ldi		TMPnoINT,0x00
		out		MCUCR,TMPnoINT
		;Outputs to HI (Pull UP)
		ldi		TMPnoINT,0xFF
		out		PORTB,TMPnoINT
.IF RESET_CNT == ON
		;Output enable for outRST, all other for input
		sbi		PORTB, outRST
		sbi		PINB, inRST
		sbi		DDRB,outRST	; Output Enable
		;Init Interrupt		
    	ldi 	TMPnoINT, (1 << inRST)	; set pin change interrupt for inRST
    	out 	PCMSK, TMPnoINT		
		ldi		TMPnoINT, (1<R23
.ENDIF
lI2c_11:
	in	_PINB, pinb
	sbrs _PINB,scl                                            ;wait for SCL=1
	rjmp lI2c_11
	sbrc _PINB,sda
	rjmp lI2c_13
lI2c_12:                                                     ;if SDA=0
	sbic pinb,sda
	rjmp lI2c_wait_for_start                                  ;  SDA 0->1? I2CSTOP! (unexpected: wait for next start)
	sbic pinb,scl
	rjmp lI2c_12                                              ;  loop while SCL=1
	clc                                                      ;  SDA=0->C
	rjmp lI2c_15
lI2c_13:                                                     ;if SDA=1
	sbis pinb,sda
	rjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)
	sbic pinb,scl
	rjmp lI2c_13                                              ;  loop while SCL=1
	sec                                                      ;  SDA=1->C
lI2c_15:
	rol r24
	dec r22
	brne lI2c_11                                              ;loop to next bit

.IF I2C_MODE == MODE_2
	sts _I2c_device_inaddr,r24
	SUB r24,r23                                              ;my address?
	cpi r24,2
	brlo lI2c_ack_1
	rjmp lI2c_exit                                            ;  no: exit and wait for next start
.ENDIF

lI2c_ack_1:                                                  ;  yes: generate ack

	clr		I2c_wr_counter								; Clear Write Buffer Pointers
	clr		I2c_wr_pointer
	clr		I2c_start_addr
	cbi portb,ack
	sbi ddrb,ack                                             ;pinb.ack = output (ACK)
lI2c_ack_10:
	sbis pinb,scl                                            ;wait for SCL=1
	rjmp lI2c_ack_10
lI2c_ack_11:
	sbic pinb,scl                                            ;wait for SCL=0
	rjmp lI2c_ack_11
/*************************************************************************
*		Select Read/Write
**************************************************************************/
.IF I2C_MODE == MODE_2
	cpi r24,0
	breq lI2c_get_2
.ELSE
	mov	I2c_start_addr,r24				; Extract Received Address
	lsr I2c_start_addr					; And Read/Write BIT
	mov I2c_rd_pointer,I2c_start_addr	; Upadate Read Address
	brcs lI2c_send_new_byte 
	rjmp	lI2c_ack_25
.ENDIF
/*************************************************************************
*		Sending Data to Master
**************************************************************************/
lI2c_send_new_byte:                                             ;read address received
	sbi ddrb,sda                                             ;pinb.sda = output (will send data)
/* Read From Flash */
	andi I2c_rd_pointer, (I2C_EEP_SIZE - 1)
	LDI	ZH,high(MemoryBlockFLASH<<1)
	LDI	ZL,low(MemoryBlockFLASH<<1)
	add ZL,I2c_rd_pointer
	inc I2c_rd_pointer
	lpm	R24, Z
	ldi r22,8

.IF DEBUG == 1
	rjmp lI2c_s1	
/* Read From SRAM */
ReadFromSram:
	andi I2c_rd_pointer, (I2C_EEP_SIZE - 1)
	LDI	ZH,high(_I2c_device_inaddr)
	LDI	ZL,low(_I2c_device_inaddr)
	add ZL,I2c_rd_pointer
	inc I2c_rd_pointer
	ld	R24, Z
	ldi r22,8
.ENDIF

lI2c_s1:
	cbi portb,sda
	sbrc r24,7
	sbi portb,sda
lI2c_s2:
	sbis pinb,scl                                            ;wait for SCL=1
	rjmp lI2c_s2
	lsl r24
lI2c_s3:
	sbic pinb,scl                                            ;wait for SCL=0
	rjmp lI2c_s3
	dec r22
	brne lI2c_s1
	sbi portb,sda                                            ;pinb.sda = 0 (will generate ACK)
	cbi ddrb,sda                                             ;pinb.sda = input (will receive data)

lI2c_s4:                                            			;wait acknowloge receive
	sbis pinb,scl                                            ;wait for SCL=1
	rjmp lI2c_s4
lI2c_s5:
	sbic pinb,scl                                            ;wait for SCL=0
	rjmp lI2c_s5
	
	sbis pinb,sda											;if answer received, - continue
	rjmp	lI2c_send_new_byte

	rjmp lI2c_wait_for_start_stop                             ;wait for next start/stop

/*************************************************************************
*		Receiving Data from Master
**************************************************************************/
.IF I2C_MODE == MODE_2
lI2c_get_2:                                                  ;write address received: get 2nd byte
	cbi ddrb,ack                                             ;pinb.ack = output (ACK)
lI2c_20:                                                     ;wait for SCL=0
	sbic pinb,scl
	rjmp lI2c_20
	ldi r22,8                                                ;bits to receive=8
lI2c_21:
	sbis pinb,scl                                            ;wait for SCL=1
	rjmp lI2c_21
	sbic pinb,sda
	rjmp lI2c_23
lI2c_22:                                                     ;if SDA=0
	sbic pinb,sda
	rjmp lI2c_stop                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)
	sbic pinb,scl
	rjmp lI2c_22                                              ;  loop while SCL=1
	clc                                                      ;  SDA=0->C
	rjmp lI2c_25
lI2c_23:                                                     ;if SDA=1
	sbis pinb,sda
	rjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)
	sbic pinb,scl
	rjmp lI2c_23                                              ;  loop while SCL=1
	sec                                                      ;  SDA=1->C
lI2c_25:
	rol r24
	dec r22
	brne lI2c_21                                              ;loop to next bit
	mov I2c_start_addr,r24                                         ;store received I2C address
	mov I2c_rd_pointer,I2c_start_addr						; Upadate Read Address
.ENDIF

ReceiveAcknowloge:
	cbi	portb,sda
	sbi	ddrb,sda
lI2c_ack_21:
	sbis pinb,scl                                            ;wait for SCL=1
	rjmp lI2c_ack_21
	sbic pinb,sda
	rjmp lI2c_ack_23
lI2c_ack_22:                                                     ;if SDA=0
	sbic pinb,sda
	rjmp lI2c_stop                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)
	sbic pinb,scl
	rjmp lI2c_ack_22                                              ;  loop while SCL=1
	rjmp lI2c_ack_25
lI2c_ack_23:                                                     ;if SDA=1
	sbis pinb,sda
	rjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)
	sbic pinb,scl
	rjmp lI2c_ack_23                                              ;  loop while SCL=1
lI2c_ack_25:

	cbi ddrb,ack                                             ;pinb.ack = input
	LDI	ZH,high(_I2c_data_buffer)
	LDI	ZL,low(_I2c_data_buffer)
lI2c_get_3:                                                  ;get 3rd byte
lI2c_30:                                                     ;wait for SCL=0
	sbic pinb,scl
	rjmp lI2c_30
	ldi r22,8                                                ;bits to receive=8
	mov	TMPnoINT, I2c_wr_pointer
	andi TMPnoINT, (I2C_PAGE_SIZE - 1)
	add ZL,TMPnoINT
lI2c_31:
	sbis pinb,scl                                            ;wait for SCL=1
	rjmp lI2c_31
	sbic pinb,sda
	rjmp lI2c_33_
lI2c_32:                                                     ;if SDA=0
	sbic pinb,sda
	rjmp lI2c_stop_                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)
	sbic pinb,scl
	rjmp lI2c_32                                              ;  loop while SCL=1
	clc                                                      ;  SDA=0->C
	rjmp lI2c_35
lI2c_33_:
lI2c_33:                                                     ;if SDA=1
	sbis pinb,sda
	rjmp lI2c_start_                                           ;  SDA 1->0? I2CSTART! (repeated start)
	sbic pinb,scl
	rjmp lI2c_33                                              ;  loop while SCL=1
	sec                                                      ;  SDA=1->C
lI2c_35:
	rol r24
	dec r22
	brne lI2c_31                                              ;loop to next bit

	inc I2c_wr_counter
	inc I2c_wr_pointer
	inc I2c_rd_pointer
	st	Z,R24

	rjmp ReceiveAcknowloge

lI2c_wait_for_start_stop:                                    ;wait for start/stop
lI2c_ss_1:
	sbis pinb,scl                                            ;wait for SCL=1
	rjmp lI2c_ss_1
	sbic pinb,sda
	rjmp lI2c_ss_3
lI2c_ss_2:                                                   ;if SDA=0
	sbic pinb,sda
	rjmp lI2c_stop                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)
	sbic pinb,scl
	rjmp lI2c_ss_2                                            ;  loop while SCL=1
	rjmp lI2c_ss_1
lI2c_ss_3:                                                   ;if SDA=1
	sbis pinb,sda
	rjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)
	sbic pinb,scl
	rjmp lI2c_ss_3                                            ;  loop while SCL=1
	rjmp lI2c_ss_1                                            ;  SDA=1->C

lI2c_stop_:
lI2c_stop:                                                   ;if stop,
	cbi	ddrb, sda
	cpi	I2c_wr_counter,0x00
	brne WriteFlashRom
lI2c_exit:
	rjmp lI2c_get

lI2c_start_:
	rjmp lI2c_get_1

WriteFlashRom:
	cli							; Disable INTERRUPTS
	rcall WriteReceivedData
	clr		I2c_wr_counter								; Clear Write Buffer Pointers
	clr		I2c_wr_pointer
	sei							; Enable INTERRUPTS
	rjmp lI2c_get
/***********************************************************
* 			Write internal FLASH by page
************************************************************/
//--------- Erase/Program Page FROM FLASH to SRAM
BackupFlashPage:
	ldi		YH,high(_I2c_FLASH_buffer) ; Load SRAM Buffer 
	ldi		YL,low(_I2c_FLASH_buffer)

	mov		TMPnoINT, I2c_start_addr
	andi	TMPnoINT, ((I2C_EEP_SIZE - 1) & ~(PAGESIZEB - 1))

	ldi		ZH,high(MemoryBlockFLASH << 1)
	ldi		ZL,low(MemoryBlockFLASH << 1)

	adc		ZL,TMPnoINT
	brcc	NoIncBackupPage
	inc		ZH
NoIncBackupPage:

	ldi		R22, PAGESIZEB
BackupPageLoop:
	lpm		TMPnoINT,Z+
	st		Y+,TMPnoINT
	dec		R22
	brne	BackupPageLoop
	ret
//--------- Add received data to SRAM Page
WriteReceivedData:
	/* Have Data For Write ? */
	cpi		I2c_wr_counter,0x00
	brne	DataForWritePresent
	ret
DataForWritePresent:

	rcall BackupFlashPage

	ldi		YH,high(_I2c_FLASH_buffer) ; Load SRAM Backup Buffer Address
	ldi		YL,low(_I2c_FLASH_buffer)

	ldi		XH,high(_I2c_data_buffer) ; Load SRAM Receive Buffer Address
	ldi		XL,low(_I2c_data_buffer)

	mov		TMPnoINT, I2c_start_addr
	andi	TMPnoINT, (PAGESIZEB - 1)

	adc		YL,TMPnoINT
	brcc	NoIncPreparePage
	inc		YH
NoIncPreparePage:

	mov		CounterInWrite,TMPnoINT
	/* Make PAGE SIZE Window */
	cpi		I2c_wr_counter, I2C_PAGE_SIZE
	brlo	PreparePageLoop
	ldi		I2c_wr_counter, I2C_PAGE_SIZE
PreparePageLoop:
	ld		R22,X+
	st		Y+,R22

	dec		I2c_wr_counter
	breq	NormalPageWrite

	mov		TMPnoINT,CounterInWrite
	cpi		TMPnoINT, (PAGESIZEB - 1)
	brne	NoWritePageSizeExceeded

	rcall	Erase_page_by_SPM
	rcall	Write_Current_Page

	inc		CounterInWrite
	inc		I2c_start_addr
	dec		I2c_wr_pointer

	rjmp	WriteReceivedData

NoWritePageSizeExceeded:

	inc		CounterInWrite
	inc		I2c_start_addr
	dec		I2c_wr_pointer

	rjmp	PreparePageLoop

NormalPageWrite:
	rcall	Erase_page_by_SPM
	rcall	Write_Current_Page

	inc		I2c_start_addr
	dec		I2c_wr_pointer
	ret
//--------- Erase page
Erase_page_by_SPM:
	ldi		ZH,high(MemoryBlockFLASH << 1)
	ldi		ZL,low(MemoryBlockFLASH << 1)

	mov		TMPnoINT,I2c_start_addr
	andi 	TMPnoINT, ((I2C_EEP_SIZE - 1) & ~(PAGESIZEB - 1)) /* Mask Maximum Data Size and Mask Out Real Flash Page Size */
	adc		ZL,TMPnoINT
	brcc	NoIncErasePage
	inc		ZH
NoIncErasePage:
	/* Load Erase Instruction */
	ldi		TMPnoINT, (1<

© Habrahabr.ru