我正在尝试将STM32作为I2C从设备编写,具有以下简单接口:
因此,主设备每次都会发送一个注册地址,然后从该寄存器地址中读取或写入数据。
从设备需要始终接收一个字节的注册地址,然后,如果下一个操作是读取,则将该寄存器中的信息发送回主设备,如果主设备的下一个操作是另一个写入,则覆盖该寄存器。
但是,当我运行我的代码时,有些NACKS,其中应该是ACKS。
当主设备请求缓冲区时,以下是响应: 您可以在从设备完成发送最后一个字节后看到NACK 这有点麻烦,但主设备可以正常接收数据,所以我可以接受这种情况。
然而,当我尝试向从设备的寄存器写入数据时,出现以下问题:从设备接收到寄存器地址,然后接收到1个字节和ack,但在接收到第二个字节后,由于某种原因它就一直卡住了(我需要在这里使用时钟拉伸)。这不好,从设备不仅没有接收到所有数据,还锁定了线路以阻止进一步通信。为什么会出现这种情况?我已经思考了几个月了。
这是主设备代码,仅供参考(在简单的Arduino上运行),因为重点真正在于STM32从设备代码。
以下是I2C从设备的cubeMX配置:
非常感谢您提供的任何见解。
谢谢!
![enter image description here](https://istack.dev59.com/5jQya.webp)
从设备需要始终接收一个字节的注册地址,然后,如果下一个操作是读取,则将该寄存器中的信息发送回主设备,如果主设备的下一个操作是另一个写入,则覆盖该寄存器。
但是,当我运行我的代码时,有些NACKS,其中应该是ACKS。
当主设备请求缓冲区时,以下是响应: 您可以在从设备完成发送最后一个字节后看到NACK 这有点麻烦,但主设备可以正常接收数据,所以我可以接受这种情况。
![enter image description here](https://istack.dev59.com/cr18c.webp)
![enter image description here](https://istack.dev59.com/yuxlg.webp)
#include <Wire.h>
uint16_t read_register(int devAddr, unsigned char regAddr, unsigned char bytes, unsigned char * buffer){
unsigned char i = 0;
Wire.beginTransmission(devAddr);
Wire.write(regAddr);
Wire.endTransmission(false);
Wire.requestFrom(devAddr, bytes , true);
while(Wire.available()){
buffer[i] = Wire.read();
i++;
}
return true;
}
uint16_t write_register(int devAddr, unsigned char regAddr, unsigned char bytes, unsigned char * buffer){
unsigned char i = 0;
Wire.beginTransmission(devAddr);
Wire.write(regAddr); // Reg to write
for(i = 0; i < bytes; i++){
Wire.write(buffer[i]);
}
Wire.endTransmission(true);
return true;
}
void setup()
{
Wire.begin();
Wire.setClock(400);
Serial.begin(9600);
while (!Serial); // Leonardo: wait for serial monitor
Serial.println("Starting");
}
void loop()
{
unsigned char buffSize = 4;
unsigned char readBuff[buffSize];
unsigned char writeBuff[5] = {0xFB, 0xE3, 0XE2, 0xE1, 0xE0};
for (int i = 0; i < buffSize; i++) readBuff[i] = 0;
read_register(0x1F, 251, buffSize, readBuff);
Serial.print(readBuff[3], HEX);
Serial.print(readBuff[2], HEX);
Serial.print(readBuff[1], HEX);
Serial.println(readBuff[0], HEX);
write_register(0x1F, 0xFB, 5, writeBuff);
delay(2000);
}
以下是STM32从设备的I2C代码部分:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file i2c.c
* @brief This file provides code for the configuration
* of the I2C instances.
******************************************************************************
* @attention
*
* Copyright (c) 2022 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "i2c.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
I2C_HandleTypeDef hi2c1;
/* I2C1 init function */
void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
// Get I2C address code from hardware jumpers
// Address starts at I2C_ADDRESS_BASE and is offset by value read on jumpers array
uint8_t I2C_Address = 0x0;
I2C_Address = (I2C_ADDRESS_BASE + (
(HAL_GPIO_ReadPin(AD0_GPIO_Port, AD0_Pin) << 0)|
(HAL_GPIO_ReadPin(AD1_GPIO_Port, AD1_Pin) << 1)|
(HAL_GPIO_ReadPin(AD2_GPIO_Port, AD2_Pin) << 2)|
(HAL_GPIO_ReadPin(AD3_GPIO_Port, AD3_Pin) << 3)
)) << 1;
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x0000020B;
hi2c1.Init.OwnAddress1 = I2C_Address;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_ENABLE;
hi2c1.Init.OwnAddress2 = (I2C_ADDRESS_BASE + 16) << 1;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}
/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(i2cHandle->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspInit 0 */
/* USER CODE END I2C1_MspInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/**I2C1 GPIO Configuration
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* I2C1 clock enable */
__HAL_RCC_I2C1_CLK_ENABLE();
/* I2C1 interrupt Init */
HAL_NVIC_SetPriority(I2C1_EV_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
HAL_NVIC_SetPriority(I2C1_ER_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
/* USER CODE BEGIN I2C1_MspInit 1 */
/* USER CODE END I2C1_MspInit 1 */
}
}
void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle)
{
if(i2cHandle->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspDeInit 0 */
/* USER CODE END I2C1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_I2C1_CLK_DISABLE();
/**I2C1 GPIO Configuration
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_7);
/* I2C1 interrupt Deinit */
HAL_NVIC_DisableIRQ(I2C1_EV_IRQn);
HAL_NVIC_DisableIRQ(I2C1_ER_IRQn);
/* USER CODE BEGIN I2C1_MspDeInit 1 */
/* USER CODE END I2C1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
#define I2C_BUFFER_SIZE 8
uint8_t i2c_buffer[I2C_BUFFER_SIZE];
uint8_t reg_addr_rcvd = 0;
#define I2C_REG_ADD_SIZE 1
#define I2C_PAYLOAD_SIZE 4
extern void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode){
UNUSED(AddrMatchCode);
// If is master write, listen to necessary amount of bytes
if(TransferDirection == I2C_DIRECTION_TRANSMIT){
// First write request is always 1 byte of the requested reg address
// Will saved it on the first position of I2C_buffer
if(!reg_addr_rcvd){
HAL_I2C_Slave_Sequential_Receive_IT(hi2c, (void*)i2c_buffer, I2C_REG_ADD_SIZE, I2C_FIRST_FRAME);
} else {
// If a subsequent write request is sent, will receve 4 bytes from master
// Save it on the rest of the buffer
HAL_I2C_Slave_Sequential_Receive_IT(hi2c, (void*)i2c_buffer, I2C_PAYLOAD_SIZE, I2C_NEXT_FRAME);
}
}
else {
// If a read request is sent by the master, return the value of the data in the requested register that was saved on 1st
// position of the I2C buffer
HAL_I2C_Slave_Sequential_Transmit_IT(hi2c, data_register[i2c_buffer[0]].mem_addr, data_register[i2c_buffer[0]].len, I2C_LAST_FRAME);
}
// Read address + data size. If it is a read command, data size will be zero
}
extern void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c){
// This is called after a master 'write' request. first time around it will be a register.
// Second time if its a write to register request, it will be a payload
if(!reg_addr_rcvd){
// If reg_addr_rcvd is false, means that it received a register
reg_addr_rcvd = 1;
} else {
// If reg_addr_rcvd is set, means that this callback was returned after the payload data has been received
reg_addr_rcvd = 0;
}
HAL_I2C_EnableListen_IT(hi2c);
HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin);
}
extern void HAL_I2C_ListenCpltCallback (I2C_HandleTypeDef *hi2c){
HAL_I2C_EnableListen_IT(hi2c);
HAL_GPIO_TogglePin(LED_B_GPIO_Port, LED_B_Pin);
}
extern void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c){
// Reset reg_addr_rcvd after finish sending requested register
reg_addr_rcvd = 0;
HAL_I2C_EnableListen_IT(hi2c);
}
extern void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{
HAL_GPIO_TogglePin(LED_R_GPIO_Port, LED_R_Pin);
//HAL_I2C_ERROR_NONE 0x00000000U /*!< No error */
//HAL_I2C_ERROR_BERR 0x00000001U /*!< BERR error */
//HAL_I2C_ERROR_ARLO 0x00000002U /*!< ARLO error */
//HAL_I2C_ERROR_AF 0x00000004U /*!< Ack Failure error */
//HAL_I2C_ERROR_OVR 0x00000008U /*!< OVR error */
//HAL_I2C_ERROR_DMA 0x00000010U /*!< DMA transfer error */
//HAL_I2C_ERROR_TIMEOUT 0x00000020U /*!< Timeout Error */
uint32_t error_code = HAL_I2C_GetError(hi2c);
if (error_code != HAL_I2C_ERROR_AF){}
HAL_I2C_EnableListen_IT(hi2c);
}
/* USER CODE END 1 */
以下是I2C从设备的cubeMX配置:
![在此输入图像描述](https://istack.dev59.com/lY5ex.webp)
![在此输入图像描述](https://istack.dev59.com/7MEgI.webp)
![在此输入图像描述](https://istack.dev59.com/6eVlN.webp)