我正在使用Atmega2560和24c16A EEPROM来测试I2C代码。对于每个状态转换,控制器通过更改状态寄存器 TWSR 来响应。发送启动条件后,EEPROM响应,然后对器件地址和写入指令(SLA+R/W)进行响应,状态寄存器更新正常。但是当我传输接下来的 8 位时,而不是 ACK(发送 ACK 的数据字节),状态更改为重复启动。代码如下。我永远找不到可能的解决方案来使其工作。
.include "./m2560def.inc"
.list
.cseg
.org 0x00
jmp inicio ; PC = 0x0000 RESET
inicio:
LDI R21, HIGH(RAMEND) ;Set Up Stack
OUT SPH, R21
LDI R21, LOW(RAMEND)
OUT SPL, R21
CALL I2C_INIT ;Initialize TWI(I2C)
CALL I2C_START ;Transmit START condition
LDI R27, 0b11010000 ;SLA(0b1001100) + W(0)
CALL I2C_WRITE ;Write R27 ato the I2C bus
LDI R27, 0b11110000 ;Data to be transmitted
CALL I2C_WRITE ;Write R27 ato the I2C bus
CALL I2C_STOP ;Transmit STOP condition
HERE: RJMP HERE
;----------------------------I2C_INIT-----------------------------
I2C_INIT:
LDI R21, 0
OUT TWSR, R21 ;Set prescaler bits to 0
LDI R21, 0x47 ;R21 = 0x47
OUT TWBR, R21 ;Fclk = 50 KHz (8 MHz Xtal)
LDI R21, (1<<TWEN) ;R21 = 0x04
OUT TWCR, R21 ;HEnable TWI (I2C)
RET
;----------------------------I2C_START-----------------------------
I2C_START:
LDI R21, (1<<TWINT)|1<<(TWSTA)|(1<<TWEN)
OUT TWCR, R21 ;Transmit START condition
WAIT1:
IN R21, TWCR ;Read Control Register TWCR into R21
SBRS R21, TWINT ;Skip the next line if TWINT is 1
RJMP WAIT1 ;Jump a WAIT1 if TWINT is 1
RET
;----------------------------I2C_WRITE -----------------------------
I2C_WRITE:
OUT TWDR, R27 ;Move the byte into TWRD
LDI R21, (1<<TWINT)|(1<<TWEN)
OUT TWCR, R21 ;Configure TWCR to send TWDR
WAIT3:
IN R21, TWCR ;Read Control Register TWCR into R21
SBRS R21, TWINT ;Skip the next line if TWINT is 1
RJMP WAIT3 ;Jump a WAIT3 if TWINT is 1
RET
;----------------------------I2C_STOP------------------------------
I2C_STOP:
LDI R21, (1<<TWINT)|1<<(TWSTO)|(1<<TWEN)
OUT TWCR, R21 ;Transmit STOP condition
RET
;----------------------------I2C_READ------------------------------
I2C_READ:
LDI R21, (1<<TWINT)|(1<<TWEN)
OUT TWCR, R21
WAIT2:
IN R21, TWCR ;Read Control Register TWCR into R21
SBRS R21, TWINT ;Skip the next line if TWINT is 1
RJMP WAIT2 ;Jump a WAIT2 if TWINT is 1
IN R27, TWCR ;Read received data into R21
RET
输出为
D:ATMEL_AVRI2C_TWII2C_TWI.asm(41,0): error: Operand 1 out of range: 0xb9
D:ATMEL_AVRI2C_TWII2C_TWI.asm(43,0): error: Operand 1 out of range: 0xb8
D:ATMEL_AVRI2C_TWII2C_TWI.asm(45,0): error: Operand 1 out of range: 0xbc
D:ATMEL_AVRI2C_TWII2C_TWI.asm(51,0): error: Operand 1 out of range: 0xbc
D:ATMEL_AVRI2C_TWII2C_TWI.asm(53,0): error: Operand 2 out of range: 0xbc
D:ATMEL_AVRI2C_TWII2C_TWI.asm(60,0): error: Operand 1 out of range: 0xbb
D:ATMEL_AVRI2C_TWII2C_TWI.asm(62,0): error: Operand 1 out of range: 0xbc
D:ATMEL_AVRI2C_TWII2C_TWI.asm(64,0): error: Operand 2 out of range: 0xbc
D:ATMEL_AVRI2C_TWII2C_TWI.asm(72,0): error: Operand 1 out of range: 0xbc
D:ATMEL_AVRI2C_TWII2C_TWI.asm(78,0): error: Operand 1 out of range: 0xbc
D:ATMEL_AVRI2C_TWII2C_TWI.asm(80,0): error: Operand 2 out of range: 0xbc
D:ATMEL_AVRI2C_TWII2C_TWI.asm(83,0): error: Operand 2 out of range: 0xbc
Assembly failed, 12 errors, 0 warnings
这些错误中涉及的代码行包括:
OUT TWSR, R21
OUT TWBR, R21
OUT TWCR, R21
OUT TWCR, R21
IN R21, TWCR
OUT TWDR, R27
OUT TWCR, R21
IN R21, TWCR
OUT TWCR, R21
OUT TWCR, R21
IN R21, TWCR
IN R27, TWCR
在 AVR 中,通常所有 I/O 寄存器都映射到内存空间,从地址0x0020
开始
in
和out
指令只能与其中的前 64 条一起使用(0x20...0x5F内存地址)sbi
、cbi
、sbis
和sbic
有进一步的限制,只能与前32个I/O寄存器一起使用(内存地址0x20...0x3F).请参考 AVR 指令集手册
在 ATmega2560 的数据表第 33 节"寄存器摘要"第 401 页中,您可以看到 TWI 寄存器具有0xB8 0xBD的地址,即无法使用in
和out
指令访问它们。你必须使用STS
和LDS
. 即LDS R21, TWCR
而不是IN R21, TWCR
等。
此外,访问前 64 个寄存器时始终要小心。确保相应的名称定义其 I/O 而不是 RAM 地址。即 PORTA 应等于 0x02(in
/out
指令的 IO 地址)而不是0x22(RAM 地址或 PORTA,当使用in
或out
访问时,等于 EEARH 寄存器)。