I2C 在内存读取时返回忙或错误
I2C returning Busy or Error on memory reading
我开始使用以下代码来处理 Bosch BME280 sensor 使用 Nucleo-F446ZE 和 Nucleo-F411RE 板的
with STM32.Device; use STM32.Device;
with STM32.GPIO; use STM32.GPIO;
with STM32; use STM32;
with STM32.I2C;
with HAL.I2C; use HAL.I2C;
use HAL;
procedure Simple_I2C_Demo is
-- I2C Bus selected
Selected_I2C_Port : constant access STM32.I2C.I2C_Port := I2C_1'Access;
Selected_I2C_Port_AF : constant GPIO_Alternate_Function := GPIO_AF_I2C1_4;
Selected_I2C_Clock_Pin : GPIO_Point renames PB8;
Selected_I2C_Data_Pin : GPIO_Point renames PB9;
Port : constant HAL.I2C.Any_I2C_Port := Selected_I2C_Port;
-- Shift one because of 7-bit addressing
I2C_Address : constant HAL.I2C.I2C_Address := 16#76# * 2;
procedure SetupHardware is
GPIO_Conf_AF : GPIO_Port_Configuration (Mode_AF);
Selected_Clock_Speed : constant := 10_000;
begin
Enable_Clock (Selected_I2C_Clock_Pin);
Enable_Clock (Selected_I2C_Data_Pin);
Enable_Clock (Selected_I2C_Port.all);
STM32.Device.Reset (Selected_I2C_Port.all);
Configure_Alternate_Function (Selected_I2C_Clock_Pin, Selected_I2C_Port_AF);
Configure_Alternate_Function (Selected_I2C_Data_Pin, Selected_I2C_Port_AF);
GPIO_Conf_AF.AF_Speed := Speed_100MHz;
GPIO_Conf_AF.AF_Output_Type := Open_Drain;
GPIO_Conf_AF.Resistors := Pull_Up;
Configure_IO (Selected_I2C_Clock_Pin, GPIO_Conf_AF);
Configure_IO (Selected_I2C_Data_Pin, GPIO_Conf_AF);
STM32.I2C.Configure
(Selected_I2C_Port.all,
(Clock_Speed => Selected_Clock_Speed,
Addressing_Mode => STM32.I2C.Addressing_Mode_7bit,
Own_Address => 16#00#, others => <>));
STM32.I2C.Set_State (Selected_I2C_Port.all, Enabled => True);
end SetupHardware;
ID : HAL.I2C.I2C_Data (1 .. 1);
Status : HAL.I2C.I2C_Status;
begin
SetupHardware;
HAL.I2C.Mem_Read (This => Port.all,
Addr => I2C_Address,
Mem_Addr => 16#D0#,
Mem_Addr_Size => HAL.I2C.Memory_Size_8b,
Data => ID,
Status => Status,
Timeout => 15000);
if Status /= Ok then
raise Program_Error with "I2C read error:" & Status'Img;
end if;
end Simple_I2C_Demo;
在这个简单的例子中,我总是在阅读结束时得到一个错误状态。在更完整的代码的上下文中,我总是在等待 15 秒后得到忙状态。
我真的不明白发生了什么,因为我的代码很大程度上受到了 the code I found on Github I2C 传感器的启发。
也许我忘记了 I2C init 的具体代码,但由于我不是专家,所以我更愿意向专家请教:)
不,在 HAL_I2C_Mem_Read
和 HAL_I2C_Master_Transmit
、wait
、HAL_I2C_Master_Receive
过程之间只是一个细微差别 cf 。如果您知道要接收的数据大小,可以使用 HAL_I2C_Master_Transmit
、等等、HAL_I2C_Master_Receive
过程。
C++ HAL I2C 示例在 https://letanphuc.net/2017/05/stm32f0-i2c-tutorial-7/
//Trigger Temperature measurement
buffer[0]=0x00;
HAL_I2C_Master_Transmit(&hi2c1,0x40<<1,buffer,1,100);
HAL_Delay(20);
HAL_I2C_Master_Receive(&hi2c1,0x40<<1,buffer,2,100);
//buffer[0] : MSB data
//buffer[1] : LSB data
rawT = buffer[0]<<8 | buffer[1]; //combine 2 8-bit into 1 16bit
Temperature = ((float)rawT/65536)*165.0 -40.0;
//Trigger Humidity measurement buffer[0]=0x01;
HAL_I2C_Master_Transmit(&hi2c1,0x40<<1,buffer,1,100);
HAL_Delay(20);
HAL_I2C_Master_Receive(&hi2c1,0x40<<1,buffer,2,100);
//buffer[0] : MSB data
//buffer[1] : LSB data
rawH = buffer[0]<<8 | buffer[1]; //combine 2 8-bit into 1 16bit
Humidity = ((float)rawH/65536)*100.0; HAL_Delay(100); }
注意它使用HAL_I2C_Master_Transmit
,等待20ms直到slave把数据放到总线上,然后用HAL_I2C_Master_Receive
接收。此代码有效,我自己测试过。
可能的问题是 BME280 支持单字节读取和多字节读取(直到它发送 NOACK 并停止) . HAL_I2C_Mem_Read
等待 ACK 或停止但由于某些原因它没有得到它导致 Busy 然后 Timeout 行为的原因,cf数据表第 33 页 http://www.embeddedadventures.com/datasheets/BME280.pdf 用于多字节读取。您将超时指定为 15 秒,并在 15 秒后获得超时。 所以看起来 BME280 根本没有停止发送,或者它什么也没发送,包括不发送 NOACK 和停止条件 ...
HAL_I2C_Mem_Read
有时候会出问题,这个要看slave https://community.arm.com/developer/ip-products/system/f/embedded-forum/7989/trouble-getting-values-with-i2c-using-hal_library
顺便说一下
HAL.I2C.Mem_Read (This => Port.all,
Addr => I2C_Address,
Mem_Addr => 16#D0#,
Mem_Addr_Size => HAL.I2C.Memory_Size_8b,
Data => ID,
Status => Status,
Timeout => 15000);
你尝试从寄存器中读取 1 个字节的芯片标识号 D0
cf http://www.embeddedadventures.com/datasheets/BME280.pdf 第 26 页
终于找到问题所在了。在使用STM HAL用C测试并调查Ada配置代码后,我发现缺少一行:
GPIO_Conf_AF.AF_Speed := Speed_100MHz;
GPIO_Conf_AF.AF_Output_Type := Open_Drain;
GPIO_Conf_AF.Resistors := Pull_Up;
-- Missing configuration part of the record
GPIO_Conf_AF.AF := Selected_I2C_Port_AF;
-- That should be present even though there was a call to configure
-- each pin few lines above
Configure_IO (Selected_I2C_Clock_Pin, GPIO_Conf_AF);
Configure_IO (Selected_I2C_Data_Pin, GPIO_Conf_AF);
在Configure_Alternate_Function之后使用Configure_IO破坏了配置,因为有一部分记录未初始化,GPIO 配置不正确。
更准确地说,在查看 GPIO 处理内部的代码后,Configure_IO 调用 Configure_Alternate_Function 使用 GPIO_Port_Configuration 记录的 AF 部分。就我而言,它正在重置它。
由于缺少行,代码现在可以使用 Mem_Read 和 Master_Transmit/[= 正确运行22=]Master_Receive.
非常感谢 ralf htp 建议我深入研究生成的 C 代码。
我开始使用以下代码来处理 Bosch BME280 sensor 使用 Nucleo-F446ZE 和 Nucleo-F411RE 板的
with STM32.Device; use STM32.Device;
with STM32.GPIO; use STM32.GPIO;
with STM32; use STM32;
with STM32.I2C;
with HAL.I2C; use HAL.I2C;
use HAL;
procedure Simple_I2C_Demo is
-- I2C Bus selected
Selected_I2C_Port : constant access STM32.I2C.I2C_Port := I2C_1'Access;
Selected_I2C_Port_AF : constant GPIO_Alternate_Function := GPIO_AF_I2C1_4;
Selected_I2C_Clock_Pin : GPIO_Point renames PB8;
Selected_I2C_Data_Pin : GPIO_Point renames PB9;
Port : constant HAL.I2C.Any_I2C_Port := Selected_I2C_Port;
-- Shift one because of 7-bit addressing
I2C_Address : constant HAL.I2C.I2C_Address := 16#76# * 2;
procedure SetupHardware is
GPIO_Conf_AF : GPIO_Port_Configuration (Mode_AF);
Selected_Clock_Speed : constant := 10_000;
begin
Enable_Clock (Selected_I2C_Clock_Pin);
Enable_Clock (Selected_I2C_Data_Pin);
Enable_Clock (Selected_I2C_Port.all);
STM32.Device.Reset (Selected_I2C_Port.all);
Configure_Alternate_Function (Selected_I2C_Clock_Pin, Selected_I2C_Port_AF);
Configure_Alternate_Function (Selected_I2C_Data_Pin, Selected_I2C_Port_AF);
GPIO_Conf_AF.AF_Speed := Speed_100MHz;
GPIO_Conf_AF.AF_Output_Type := Open_Drain;
GPIO_Conf_AF.Resistors := Pull_Up;
Configure_IO (Selected_I2C_Clock_Pin, GPIO_Conf_AF);
Configure_IO (Selected_I2C_Data_Pin, GPIO_Conf_AF);
STM32.I2C.Configure
(Selected_I2C_Port.all,
(Clock_Speed => Selected_Clock_Speed,
Addressing_Mode => STM32.I2C.Addressing_Mode_7bit,
Own_Address => 16#00#, others => <>));
STM32.I2C.Set_State (Selected_I2C_Port.all, Enabled => True);
end SetupHardware;
ID : HAL.I2C.I2C_Data (1 .. 1);
Status : HAL.I2C.I2C_Status;
begin
SetupHardware;
HAL.I2C.Mem_Read (This => Port.all,
Addr => I2C_Address,
Mem_Addr => 16#D0#,
Mem_Addr_Size => HAL.I2C.Memory_Size_8b,
Data => ID,
Status => Status,
Timeout => 15000);
if Status /= Ok then
raise Program_Error with "I2C read error:" & Status'Img;
end if;
end Simple_I2C_Demo;
在这个简单的例子中,我总是在阅读结束时得到一个错误状态。在更完整的代码的上下文中,我总是在等待 15 秒后得到忙状态。
我真的不明白发生了什么,因为我的代码很大程度上受到了 the code I found on Github I2C 传感器的启发。
也许我忘记了 I2C init 的具体代码,但由于我不是专家,所以我更愿意向专家请教:)
不,在 HAL_I2C_Mem_Read
和 HAL_I2C_Master_Transmit
、wait
、HAL_I2C_Master_Receive
过程之间只是一个细微差别 cf HAL_I2C_Master_Transmit
、等等、HAL_I2C_Master_Receive
过程。
C++ HAL I2C 示例在 https://letanphuc.net/2017/05/stm32f0-i2c-tutorial-7/
//Trigger Temperature measurement
buffer[0]=0x00;
HAL_I2C_Master_Transmit(&hi2c1,0x40<<1,buffer,1,100);
HAL_Delay(20);
HAL_I2C_Master_Receive(&hi2c1,0x40<<1,buffer,2,100);
//buffer[0] : MSB data
//buffer[1] : LSB data
rawT = buffer[0]<<8 | buffer[1]; //combine 2 8-bit into 1 16bit
Temperature = ((float)rawT/65536)*165.0 -40.0;
//Trigger Humidity measurement buffer[0]=0x01;
HAL_I2C_Master_Transmit(&hi2c1,0x40<<1,buffer,1,100);
HAL_Delay(20);
HAL_I2C_Master_Receive(&hi2c1,0x40<<1,buffer,2,100);
//buffer[0] : MSB data
//buffer[1] : LSB data
rawH = buffer[0]<<8 | buffer[1]; //combine 2 8-bit into 1 16bit
Humidity = ((float)rawH/65536)*100.0; HAL_Delay(100); }
注意它使用HAL_I2C_Master_Transmit
,等待20ms直到slave把数据放到总线上,然后用HAL_I2C_Master_Receive
接收。此代码有效,我自己测试过。
可能的问题是 BME280 支持单字节读取和多字节读取(直到它发送 NOACK 并停止) . HAL_I2C_Mem_Read
等待 ACK 或停止但由于某些原因它没有得到它导致 Busy 然后 Timeout 行为的原因,cf数据表第 33 页 http://www.embeddedadventures.com/datasheets/BME280.pdf 用于多字节读取。您将超时指定为 15 秒,并在 15 秒后获得超时。 所以看起来 BME280 根本没有停止发送,或者它什么也没发送,包括不发送 NOACK 和停止条件 ...
HAL_I2C_Mem_Read
有时候会出问题,这个要看slave https://community.arm.com/developer/ip-products/system/f/embedded-forum/7989/trouble-getting-values-with-i2c-using-hal_library
顺便说一下
HAL.I2C.Mem_Read (This => Port.all,
Addr => I2C_Address,
Mem_Addr => 16#D0#,
Mem_Addr_Size => HAL.I2C.Memory_Size_8b,
Data => ID,
Status => Status,
Timeout => 15000);
你尝试从寄存器中读取 1 个字节的芯片标识号 D0
cf http://www.embeddedadventures.com/datasheets/BME280.pdf 第 26 页
终于找到问题所在了。在使用STM HAL用C测试并调查Ada配置代码后,我发现缺少一行:
GPIO_Conf_AF.AF_Speed := Speed_100MHz;
GPIO_Conf_AF.AF_Output_Type := Open_Drain;
GPIO_Conf_AF.Resistors := Pull_Up;
-- Missing configuration part of the record
GPIO_Conf_AF.AF := Selected_I2C_Port_AF;
-- That should be present even though there was a call to configure
-- each pin few lines above
Configure_IO (Selected_I2C_Clock_Pin, GPIO_Conf_AF);
Configure_IO (Selected_I2C_Data_Pin, GPIO_Conf_AF);
在Configure_Alternate_Function之后使用Configure_IO破坏了配置,因为有一部分记录未初始化,GPIO 配置不正确。
更准确地说,在查看 GPIO 处理内部的代码后,Configure_IO 调用 Configure_Alternate_Function 使用 GPIO_Port_Configuration 记录的 AF 部分。就我而言,它正在重置它。
由于缺少行,代码现在可以使用 Mem_Read 和 Master_Transmit/[= 正确运行22=]Master_Receive.
非常感谢 ralf htp 建议我深入研究生成的 C 代码。