为什么我只收到第一个地址字节? (I2C 协议)
Why am I only receiving the first address byte? (I2C Protocol)
期望从机确认和return数据,但它没有。 This is my protocol. This is my Datasheet
数据表提到“从机将通过先发送 MSB 字节来应答。Byte0 和 byte1 包含
预测值。所有字节均由主机确认。"
编辑: Source Library
另外,仅供参考,我使用的是 Arduino Fio,但我没有继承 Arduino 库。
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <uart.h>
#include <i2c_master.h>
#define LED PB5
#define I2C_READ 0x5A
char buffer[1];
//char data[9];
uint16_t val = 0;
uint8_t status = 0;
void getVal()
{
if(i2c_start(I2C_READ))
{
uart_puts("Start ");
val = ((uint8_t)i2c_read_ack())<<8;
val |= i2c_read_ack();
status = ((uint8_t)i2c_read_nack());
i2c_stop();
} else
{
uart_puts("Error");
i2c_stop();
}
}
int main(void)
{
init_uart(57600);
i2c_init();
DDRB = _BV(5);
for(;;)
{
getVal();
itoa(status, buffer, 10); //convert decimal to string base 10
uart_puts(buffer);
PORTB = 0xFF;
_delay_ms(500);
PORTB = 0x00;
_delay_ms(500);
}
return 0; /* never reached */
}
编辑修订:
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <uart.h>
#include <i2c_master.h>
#define LED PB5
#define I2C_READ 0x5A
char buffer[10];
//char data[9];
uint16_t val = 0;
uint8_t status = 0;
{
if(!i2c_start(I2C_READ))
{
uart_puts("Error");
i2c_stop();
} else
{
uart_puts("Start ");
i2c_start((IAQ_ADDR << 1) + 1); //i2c_start(0xB5);
val = ((uint8_t)i2c_read_ack())<<8;
val |= i2c_read_ack();
status = ((uint8_t)i2c_read_nack());
i2c_stop();
}
}
int main(void)
{
init_uart(57600);
i2c_init();
DDRB = _BV(5);
for(;;)
{
getVal();
itoa(status, buffer, 10); //convert decimal to string base 10
uart_puts(buffer);
PORTB = 0xFF;
_delay_ms(500);
PORTB = 0x00;
_delay_ms(500);
}
return 0; /* never reached */
}
您的程序有未定义的行为。您已将 buffer
声明为:
char buffer[1];
这是一组单个字符。您可以存储的唯一以 null 结尾的字符串是 空字符串 (即 buffer[0] == '[=12=]'
的位置)。但是您正在使用它来将整数转换为字符串。
您需要使缓冲区足够大以容纳您希望在其中存储的最大字符串,包括终止符。
如果没有正在使用的 i2c 库的详细信息,很难判断,但我首先要检查的一件事是 i2c_start(I2C_READ)
。
数据表中提供的 i2c 地址是您在宏中输入的 0x5a
。但是第一个字节还包含 read/write 标志作为最低有效位。 i2c_start()
函数必须将 0xb5
放到总线上(即 (0x5a << 1) + 1
用于读取)
如果 i2c_start()
不是,那么您的从属设备实际上没有被寻址,因此不会确认。
我不确定您的根本问题是什么。你有什么症状?你的 UART 有什么样的输出?
正如其他人所说,您的 buffer
变量太小,无法将任何有价值的东西保存为 C 字符串。 itoa
(AVR libc entry) 通常提供一个以 null 结尾的字符串,因此您至少需要足够大以容纳所有字符以及 null 值。您可能遇到缓冲区溢出问题,在这种情况下,您应该有一个简单的解决方案。
您使用 uin8_t
意味着您应该分配足够的 space 来表示最多 255 个。您需要 3 个字符来表示整个范围,以及一个字符来表示空值特点。更改您的代码以阅读 char buffer[4];
并查看是否可以改善您遇到的问题。
如果 I2C 协议实现有误,如果没有关于错误位置的准确信息,将很难诊断。如果您可以使用逻辑分析仪,应该会有所帮助。
编辑:
我刚看了数据表,你的 i2c_start(0x5A)
与连续 i2c_read_ack()
的方法似乎是正确的。但是,根据您的链接协议,您可能只是在 UART 上看到 "Error" 作为 i2c_start()
returns 0
成功启动事务,然后检查 if(i2c_start(I2C_READ))
当你应该在 getVal()
.
中测试 if(! i2c_start(I2C_READ))
根据您链接的 i2c_master.c 要点,您似乎还有一些不必要的 uint8_t
转换。我建议只使用 Arduino IDE 如果这真的是一个 Arduino 兼容板,看看你是否可以获得一个简单的 I2C 示例来使用传感器来证明你的访问方法。
对于I2C,在SDA和SCL引脚上都有上拉电阻是非常重要的。如果这些不存在,总线将始终读取低电压或浮动电压。我对 AVR 硬件的了解不足以了解它的行为方式,但如果是这种情况,我希望“//检查启动条件是否已成功传输”失败。
一般像这样的硬件开发时,我还是建议有一个逻辑分析仪。当您对硬件总线的实际功能视而不见时,它可以提供帮助。
此类设备的一个示例是 https://www.saleae.com/,但存在多个供应商 and/or 开放项目。
期望从机确认和return数据,但它没有。 This is my protocol. This is my Datasheet
数据表提到“从机将通过先发送 MSB 字节来应答。Byte0 和 byte1 包含 预测值。所有字节均由主机确认。"
编辑: Source Library
另外,仅供参考,我使用的是 Arduino Fio,但我没有继承 Arduino 库。
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <uart.h>
#include <i2c_master.h>
#define LED PB5
#define I2C_READ 0x5A
char buffer[1];
//char data[9];
uint16_t val = 0;
uint8_t status = 0;
void getVal()
{
if(i2c_start(I2C_READ))
{
uart_puts("Start ");
val = ((uint8_t)i2c_read_ack())<<8;
val |= i2c_read_ack();
status = ((uint8_t)i2c_read_nack());
i2c_stop();
} else
{
uart_puts("Error");
i2c_stop();
}
}
int main(void)
{
init_uart(57600);
i2c_init();
DDRB = _BV(5);
for(;;)
{
getVal();
itoa(status, buffer, 10); //convert decimal to string base 10
uart_puts(buffer);
PORTB = 0xFF;
_delay_ms(500);
PORTB = 0x00;
_delay_ms(500);
}
return 0; /* never reached */
}
编辑修订:
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <uart.h>
#include <i2c_master.h>
#define LED PB5
#define I2C_READ 0x5A
char buffer[10];
//char data[9];
uint16_t val = 0;
uint8_t status = 0;
{
if(!i2c_start(I2C_READ))
{
uart_puts("Error");
i2c_stop();
} else
{
uart_puts("Start ");
i2c_start((IAQ_ADDR << 1) + 1); //i2c_start(0xB5);
val = ((uint8_t)i2c_read_ack())<<8;
val |= i2c_read_ack();
status = ((uint8_t)i2c_read_nack());
i2c_stop();
}
}
int main(void)
{
init_uart(57600);
i2c_init();
DDRB = _BV(5);
for(;;)
{
getVal();
itoa(status, buffer, 10); //convert decimal to string base 10
uart_puts(buffer);
PORTB = 0xFF;
_delay_ms(500);
PORTB = 0x00;
_delay_ms(500);
}
return 0; /* never reached */
}
您的程序有未定义的行为。您已将 buffer
声明为:
char buffer[1];
这是一组单个字符。您可以存储的唯一以 null 结尾的字符串是 空字符串 (即 buffer[0] == '[=12=]'
的位置)。但是您正在使用它来将整数转换为字符串。
您需要使缓冲区足够大以容纳您希望在其中存储的最大字符串,包括终止符。
如果没有正在使用的 i2c 库的详细信息,很难判断,但我首先要检查的一件事是 i2c_start(I2C_READ)
。
数据表中提供的 i2c 地址是您在宏中输入的 0x5a
。但是第一个字节还包含 read/write 标志作为最低有效位。 i2c_start()
函数必须将 0xb5
放到总线上(即 (0x5a << 1) + 1
用于读取)
如果 i2c_start()
不是,那么您的从属设备实际上没有被寻址,因此不会确认。
我不确定您的根本问题是什么。你有什么症状?你的 UART 有什么样的输出?
正如其他人所说,您的 buffer
变量太小,无法将任何有价值的东西保存为 C 字符串。 itoa
(AVR libc entry) 通常提供一个以 null 结尾的字符串,因此您至少需要足够大以容纳所有字符以及 null 值。您可能遇到缓冲区溢出问题,在这种情况下,您应该有一个简单的解决方案。
您使用 uin8_t
意味着您应该分配足够的 space 来表示最多 255 个。您需要 3 个字符来表示整个范围,以及一个字符来表示空值特点。更改您的代码以阅读 char buffer[4];
并查看是否可以改善您遇到的问题。
如果 I2C 协议实现有误,如果没有关于错误位置的准确信息,将很难诊断。如果您可以使用逻辑分析仪,应该会有所帮助。
编辑:
我刚看了数据表,你的 i2c_start(0x5A)
与连续 i2c_read_ack()
的方法似乎是正确的。但是,根据您的链接协议,您可能只是在 UART 上看到 "Error" 作为 i2c_start()
returns 0
成功启动事务,然后检查 if(i2c_start(I2C_READ))
当你应该在 getVal()
.
if(! i2c_start(I2C_READ))
根据您链接的 i2c_master.c 要点,您似乎还有一些不必要的 uint8_t
转换。我建议只使用 Arduino IDE 如果这真的是一个 Arduino 兼容板,看看你是否可以获得一个简单的 I2C 示例来使用传感器来证明你的访问方法。
对于I2C,在SDA和SCL引脚上都有上拉电阻是非常重要的。如果这些不存在,总线将始终读取低电压或浮动电压。我对 AVR 硬件的了解不足以了解它的行为方式,但如果是这种情况,我希望“//检查启动条件是否已成功传输”失败。
一般像这样的硬件开发时,我还是建议有一个逻辑分析仪。当您对硬件总线的实际功能视而不见时,它可以提供帮助。
此类设备的一个示例是 https://www.saleae.com/,但存在多个供应商 and/or 开放项目。