- PVSM.RU - https://www.pvsm.ru -

24×01 I2C на ATTINY13 без TWI

Казалось бы что тут такого сложного, ну I2C ну без TWI.

Моя реальная задача обстояла чуть шире, в устройстве устанавливался чип в режиме "Reset" контроллера, для экономии места в программируемой логике, задача этого чипа состояла в том чтобы получив на входе короткий импульс как сигнал к началу генерации "длинного" сброса, чип удерживал одну из линий в течении довольно длительного промежутка времени в низком уровне (в программируемой логике можно было сделать цепочку из триггеров в качестве счетчика, но линейка триггеров занимала почти 80 ячеек в CPLD EPM240T100 и место там ой как нужно) к тому же этот чип должен был хранить данные полученный от внешнего устройства по шине I2C в режиме 1 (mode1).

Вообще существует три режима I2C, 2-ой и 3-ий режимы рассчитаны на то что на шине I2C висит гирлянда из устройств, таким образом прежде чем начать работать с устройством на шине в режиме 2 и 3 нужно сначала выбрать устройство, затем уже отправлять в него адреса и данные. Режим 1 это простейший режим работы I2C, пользователь сразу отправляет в чип адрес по которому необходимо записать или прочитать данные и работает с данными.

Вот собственно о чем я говорю (MODE1):

24x01 I2C на ATTINY13 без TWI - 1

А это MODE2:

24x01 I2C на ATTINY13 без TWI - 2

В общем-то тут всё итак понятно, но! Здесь есть три задачи:

  1. В ATTINY13 нет аппаратного I2C.

  2. Чип должен выполнять ещё и сброс (хотя для данной статьи это не особо важно).

  3. И финальная проблема 24x01 предполагает объем EEP 128 байт, а в данном чипе EEPROM объемом только 64 байта.

Первая проблема ставит задачу реализации протокола, в том числе отслеживание ошибок на шине, кодом, - тут для нас ассемблер, вторая задача это не особо сложная реализация алгоритма сброса, а вот третья задача выглядит чуть сложнее. Теоретически мы можем задействовать часть памяти FLASH не занятой нашей реализацией, но EEPROM можно писать побайтно, а FLASH только страницами, поэтому прежде чем записать во FLASH полученные данные я обязан сохранить состав всей страницы в памяти микроконтроллера, "подменить" полученные данные внутри страничного буфера, стереть страницу, записать новый состав страницы. При этом запись может попасться в смежные страницы, то есть на стыках. Таким образом я должен произвести запись в два этапа:

24x01 I2C на ATTINY13 без TWI - 3

В общем я так понимаю зачастую лирика мало кого интересует, тут CTRC+C и CTRL+V чаще применяется, поэтому выкладываю уже код.

Только сперва Коментарии:

  1. CHIP_PINOUTS - Как расположить выводы на чипе (FT24Cxx - стандартная раскладка чипа I2C, InDRIVE_v4 - раскладка под реализацию с контроллером сброса).

  2. I2C_MODE - Режим работы I2C (MODE_1 и MODE_2).

  3. I2CPAGESIZE - размер страницы I2C при страничном режиме записи.

  4. I2CEEPSIZE - общий объем памяти чипа (реализация в коде сначала использует 64 байта EEPROM, затем растягивает "остатки" во FLASH).

  5. RESET_CNT - наличие контроллера сброса (ON - присутствует, OFF - отсутствует).

Собственно перейдем к проверке, достаем TLL866

24x01 I2C на ATTINY13 без TWI - 4

Загружаем откомпилированную программу (AVR Studio v4), я пролистал в конец чтобы показать состав "псевдо EEP":

24x01 I2C на ATTINY13 без TWI - 5

Ставим правильные конфигурационные биты и записываем микропрограмму в нашу ATTINY13:

24x01 I2C на ATTINY13 без TWI - 6

Ага, а теперь не извлекая чип пишем его в режиме I2C EEPROM Mode1 с проверкой:

24x01 I2C на ATTINY13 без TWI - 7

Работает. Что и требовалось.

Код I2C EEP для ATTINY13
/*
	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<<PCIE)		; unmask interrupt PCIE
		out		GIMSK,TMPnoINT
		SEI					;Enable PCIE Int Processing
.ELSE
		CLI		; Disable Reset Controller
.ENDIF

.IF I2C_MODE == MODE_2
		/* Set I2C Device Address */
		ldi		TMPnoINT, 0xA0
		sts		_I2c_device_myaddr,TMPnoINT
.ENDIF

		/* Clear Buffer Pointers */
		clr		I2c_wr_counter
		clr		I2c_wr_pointer
		clr		I2c_start_addr
		clr		I2c_rd_pointer


main_loop:

lI2c_get:
	sbi portb,ack
	sbis pinb,scl                                            ;wait for SCL&SDA=1
	rjmp lI2c_get
	sbis pinb,sda
	rjmp lI2c_get
lI2c_wait_for_start:
	sbis pinb,scl                                            ;wait for SCL=1,SDA=0 (START)
	rjmp lI2c_get
	sbic pinb,sda
	rjmp lI2c_wait_for_start
lI2c_get_0:                                                  ;clear receive area
.IF I2C_MODE == MODE_2
	clr r22
	sts _I2c_device_inaddr,r22
.ENDIF
lI2c_get_1:                                                  ;get 1st byte
	sbi portb,ack
lI2c_10:                                                     ;wait for SCL=0
	sbic pinb,scl
	rjmp lI2c_10
	ldi r22,8                                                ;bits to receive=8
.IF I2C_MODE == MODE_2
	lds r23,_I2c_device_myaddr                               ;I2C address->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<<PGERS) | (1<<SPMEN)
	sts		_valSPMCSR, TMPnoINT
	rcall	Wait_spm	;CPU Halted while erase
	ret

//Wite page
Write_Current_Page:
	ldi		XH,high(_I2c_FLASH_buffer) ; Load SRAM Backup Buffer Address
	ldi		XL,low(_I2c_FLASH_buffer)

	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	NoIncWritePage
	inc		ZH
NoIncWritePage:

	ldi		TMPnoINT, (PAGESIZE)
	mov		Counter, TMPnoINT

	movw	Y,Z			; Store Z in Y reg
	clr		ZL			;Clear Z
	clr		ZH
Fill_Page_Buffer_loop:
	ld		R0, X+
	ld		R1, X+

	ldi		TMPnoINT, (1<<SPMEN)	;Write word into page buffer
	sts		_valSPMCSR, TMPnoINT
	rcall	Wait_spm	;CPU Halted while erase

	adiw	Z, 2
	dec		Counter
	brne	Fill_Page_Buffer_loop	

	movw	Z,Y			; Restore Z from Y reg

	ldi 	TMPnoINT, (1<<PGWRT) | (1<<SPMEN)
	sts		_valSPMCSR, TMPnoINT
	rcall	Wait_spm	;CPU Halted while erase
	ret

Do_spm:
; check for previous SPM complete
Wait_spm:
	in	TMPnoINT, SPMCSR
	sbrc TMPnoINT, SPMEN
	rjmp Wait_spm
Wait_ee:
	sbic EECR, EEWE
	rjmp Wait_ee
; SPM timed sequence
	lds	TMPnoINT, _valSPMCSR
	out SPMCSR, TMPnoINT
	spm
	ret

.cseg
.org	(0x200 - (I2C_EEP_SIZE/2))
MemoryBlockFLASH:          ; Bytes in Flash
   .DB 0x55, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
   .DB 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
   .DB 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F
   .DB 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
   .DB 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F
   .DB 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F
   .DB 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F
   .DB 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D
   ; Mark End of EEP DATA
.org	(0x1FF)
	.db 0x7E,0x7F

.MESSAGE "I2C For Read/Write Processing SELFPRGEN FUSE Must Be Enabled!!!"

.eseg
MemoryBlockEEP:          ; Bytes in EEP
   .DB 0x55, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
   .DB 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
   .DB 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F
   .DB 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F

Всем добра и успехов!!!

Спасибо за внимание.

Автор: Михаил Шероносов

Источник [1]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/algoritmy/359900

Ссылки в тексте:

[1] Источник: https://habr.com/ru/post/533922/?utm_source=habrahabr&utm_medium=rss&utm_campaign=533922