gpt4 book ai didi

c - 为什么 STM32 I2C 从机返回不需要的 NACK 或无限期延长时钟?

转载 作者:行者123 更新时间:2023-12-05 01:03:32 25 4
gpt4 key购买 nike

我正在尝试将 STM32 编写为具有简单接口(interface)的 I2C 从设备,其工作原理如下: enter image description here

所以master每次都会发送一个注册地址,然后从该注册地址写入或读取。

然后从机需要始终接收 1 个字节的注册地址,然后如果下一个操作是读取,则它将该寄存器中的信息发送回主机,或者如果主机的下一个操作是另一个写入,则覆盖该寄存器.

然而,当我运行我的代码时,我得到了一些应该是 ACKS 的 NACKS

这是主请求缓冲区时的响应:在从机完成发送最后一个字节后,您可以在末尾看到 NACK这有点痛苦,但主人收到数据没问题,所以我可以忍受这个 enter image description here

但是,当我尝试写入从属设备上的寄存器时,结果如下:从机接收寄存器地址,然后接收 1 个字节和 ack,然后在接收到第二个字节后由于某种原因它只是保持排队(我需要在这里使用时钟拉伸(stretch))这是不行的,不仅从站没有收到所有数据,而且还锁定了线路以进行任何进一步的通信。为什么我有这个?在这一点上,我为此挠了好几个月 enter image description here这里的主代码仅供引用(在简单的 Arduino 上运行),因为重点是 STM32 从代码:

#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 配置 enter image description here enter image description here enter image description here

感谢你们的任何见解。谢谢!

最佳答案

你问了两个问题。这是对第一个问题的回答,这就是为什么你在读取 4 个数据字节后得到 nack?

这是绝对正确和预期的行为,无论如何它是由 Arduino 而不是 STM32 完成的。

解释一下:每个字节后的确认始终是未发送字节的一方的责任。当主机向从机写入地址或数据时,如果从机收到该字节并准备好开始发送下一个字节,则从机生成确认。

当主机从从机读取数据时,从机(STM32)发送数据字节,由主机(Arduino)负责选择发送ack或nack。如果 master 发送一个 ack 表示“我已经收到这个字节,准备再给我发送一个字节”。如果 master 发送 nack 表示“我已经完成了从你那里获取数据”。

在 I2C 中,在不知道需要多少字节的情况下开始读取是完全合法的,在这种情况下,您将确认每个字节,然后在您读取足够多的内容时发送停止条件。但是,在您的情况下,您预先告诉 Arduino 它应该读取 4 个字节,因此它会继续确认前三个字节并确认第四个字节。

在某些情况下,这种行为可以节省从端的资源,因为从端立即知道它不需要准备好第五个字节。

关于c - 为什么 STM32 I2C 从机返回不需要的 NACK 或无限期延长时钟?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74172080/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com