请选择 进入手机版 | 继续访问电脑版

你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【NUCLEO-H533RE评测】+学习及测试I3C热拔插

[复制链接]
晒太阳的懒猫 发布时间:2024-7-7 18:24
I3C是基于I2C协议改进而来,诞生的目的是为了统一传感器通讯协议。I3C与I2C一样使用两根线,并且支持IBI中断和热拔插,同时频率可以达到较高的频率,因为它与I2C不同,I2C两根引脚使用的是开漏输出,开漏内部接地是无法输出高电平的,如果需要输出高电平必须外接上拉电阻才能实现高电平输出,但是电阻会阻碍电流导通,所以会比较耗时,I2C最高也才达到5MB,但是I3C可达12.5MB,并且功能比I2C强大许多且它支持I2C,向前兼容。I3C的SCL和SDA线两根线分别使用了推挽与开漏:SCL(推挽输出时钟线),SDA(开漏输出数据线)。I3C只有在推挽输出模式下才可以达到12.5MB,并且特定模式下的频率可以达20MB。
I3C为了安全与可靠,第九位采用的是奇偶校验位,原始的I2C第九位为ACK信号,接收方收到这个信号时需要把SDA拉低已表示收到ACK信号并返回确认ACK信号告知主机已经接收完成,I3C第九位使用的是奇偶校验位,XOR (Data [7:0], 1),即所有位与1进行异或的结果,接收方接收到以后需要自行异或并与第九位做比较,如果相同则回复ACK确认,否则NACK要求重发。I3C总体下来每次传输数据可以说是传输十位。
I3C在应答方式上,发送方将SCL拉低,随后SDA拉高,从机需要将SDA拉低来确认ACK,如果不将SDA拉低则意味着NACK。

在通讯上,I3C有255个指令,其中有些是保留命令,有15个是要求必须被遵循的。通讯帧格式如下:

领域 作用
S或Sr(重复) CCC通常以起始条件或重复条件开始
7’h7E/W/ACK 三部分:7‘h7E:以全局广播地址开始,所有I3C从机都可以看到CCC代码。W:写位清零(值1‘b0),表明主机正在向从机写一条消息。此消息时钟包含CCC代码,并且视CCC代码的值而定,可以选择包含其它数据。ACK:1个或多个I3C从期间的集体ACK(SDA驱动为低电平)
Command Code/T 一个8位的值指示正在发送哪个命令,后面跟一个T位。
Defining Byte(Optional)/T 根据需要,此可选字段与广播和直接读取写入CCC消息一起使用。它后面是一个T位。
Data(Optional)/T 根据需要,此可选字段与广播和直接读取/写入CCC消息一起使用。它后面是一个T位。
Sr/Broadcast Addressor P CCC总是以STOP或重复的START和广播地址结尾。

更详细的I3C通讯协议,这里不在继续罗列。

STM32H533里集成了2个I3C外设,鉴于手里没有支持的I3C设备,考虑做以下实验:

1、使用I3C连接I2C设备,测试I3C的向下兼容性。

2、使用STM32H533自身的两个I3C设备互联,学习I3C通讯。

实验在例程的基础上,结合CubeMX,生成I3C的初始化代码。根据设置,使用PB6、PB7作为第一组,PC6、PC7作为第二组。

图片1.png

整个程序调了一天才调通。调通之前犯了很多错误,比如没有设置中断、插错引脚。幸运的是因为使用CubeMX处理生成的基础代码,很多设置不用自己手动追加。

主程序如下:

``

/ USER CODE BEGIN Header /

/**


  • @file : main.c
  • @brief : Main program body

  • @attention
  • Copyright (c) 2024 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 "main.h"

#include "gui.h"

#include "oled.h"

#define I3C_IDX_FRAME_1 0U / Index of Frame 1 /

#define I3C_IDX_FRAME_2 1U / Index of Frame 2 /

I3C_HandleTypeDef hi3c1;

I3C_HandleTypeDef hi3c2;

// 与帧上下文相关的上下文缓冲区包含通信的不同缓冲值

I3C_XferTypeDef aContextBuffers[2];

// DAA过程中检测到的目标数量

__IO uint32_t uwTargetCount = 0;

// I3C发送用的缓冲区

uint8_t aTxBuffer[0x0F];

// I3C接收用的缓冲区

uint8_t aRxBuffer[RXBUFFERSIZE];

// HAL用来计算通信的控制数据的缓冲区

uint32_t aControlBuffer[0xF];

/****/

/ Target Descriptor /

/****/

TargetDesc_TypeDef TargetDesc1 = {

"TARGET_ID1",

DEVICE_ID1,

0x0000000000000000,

0x00,

TARGET1_DYN_ADDR,

};

/****/

/ Target Descriptor /

/****/

TargetDesc_TypeDef TargetDesc2 = {

"TARGET_ID2",

DEVICE_ID2,

0x0000000000000000,

0x00,

TARGET2_DYN_ADDR,

};

// 目标描述符数组

TargetDesc_TypeDef *aTargetDesc[2] = {

&TargetDesc1, / DEVICE_ID1 /

&TargetDesc2 / DEVICE_ID2 /

};

/ Variable to catch HotJoin event /

__IO uint32_t uwHotJoinRequested = 0;

/ Buffer that contain payload data, mean PID, BCR, DCR /

uint8_t aPayloadBuffer[64*COUNTOF(aTargetDesc)];

// 设置CCC关联数据的数组

uint8_t aDISEC_data[1] = {0x08};

/ Variable to display reading data /

uint32_t uwDisplayDelay = 0U;

int16_t Temperature = 0;

int16_t aGyroscope[3] = {0};

int16_t aAccelerometer[3] = {0};

// 广播用CCC的描述符

I3C_CCCTypeDef aBroadcast_CCC[] = {

// 目标地址 CCC Value CCC data + defbyte pointer CCC size + defbyte Direction */

{0, Broadcast_DISEC, {aDISEC_data, 1}, LL_I3C_DIRECTION_WRITE},

{0, Broadcast_RSTDAA, {NULL, 0}, LL_I3C_DIRECTION_WRITE},

};

UART_HandleTypeDef huart1;

void SystemClock_Config (void );

static void MX_GPIO_Init (void );

static void MX_I3C1_Init (void );

static void MX_I3C2_Init (void );

static void MX_USART1_UART_Init (void );

/**

  • @brief The application entry point.
  • @retval int

*/

int main (void ) {

uint8_t flag=0;

// 复位所有外设,初始化Flash接口和Systick。

HAL_Init();

// 设置系统时钟

SystemClock_Config();

// 初始化相关外设

MX_GPIO_Init();

MX_I3C1_Init();

MX_I3C2_Init();

MX_USART1_UART_Init();

// 初始化OLED并显示信息

OLED_Init();

OLED_Clear(0);

GUI_ShowString(0, 0, (uint8_t*)"Test STM32H533 I3C", 8, 1);

HAL_Delay(100);

if (HAL_I3C_ActivateNotification(&hi3c1, NULL, HAL_I3C_IT_HJIE) != HAL_OK ) {

/ Error_Handler() function is called when error occurs. /

Error_Handler();

}

while (1) {

// 等待目标连接上

while (uwHotJoinRequested == 0U) {

// 为热连接启动监听

if (flag == 0) {

// I3C2发出连接请求

if (HAL_I3C_Tgt_HotJoinReq_IT(&hi3c2) != HAL_OK ) {

Error_Handler();

} else {

flag = 1;

}

}

}

// 分配动态地址

if (HAL_I3C_Ctrl_DynAddrAssign_IT(&hi3c1, I3C_ONLY_ENTDAA) != HAL_OK ) {

Error_Handler();

}

// 获取状态

while (HAL_I3C_GetState(&hi3c1) != HAL_I3C_STATE_LISTEN ) {

}

// 复位,等待捕捉其它I3C设备

uwHotJoinRequested = 0;

}

}

/**

  • @brief System Clock Configuration
  • @retval None

*/

void SystemClock_Config (void ) {

RCC_OscInitTypeDef RCC_OscInitStruct = {0};

RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

/** Configure the main internal regulator output voltage

*/

__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);

while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

/** Initializes the RCC Oscillators according to the specified parameters

  • in the RCC_OscInitTypeDef structure.

*/

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;

RCC_OscInitStruct.HSIState = RCC_HSI_ON;

RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV2;

RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;

RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK ) {

Error_Handler();

}

/** Initializes the CPU, AHB and APB buses clocks

*/

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2

|RCC_CLOCKTYPE_PCLK3;

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;

RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK ) {

Error_Handler();

}

}

/**

  • @brief I3C1 Initialization Function
  • @param None
  • @retval None

*/

static void MX_I3C1_Init (void ) {

I3C_FifoConfTypeDef sFifoConfig = {0};

I3C_CtrlConfTypeDef sCtrlConfig = {0};

hi3c1.Instance = I3C1;

hi3c1.Mode = HAL_I3C_MODE_CONTROLLER ;

hi3c1.Init.CtrlBusCharacteristic.SDAHoldTime = HAL_I3C_SDA_HOLD_TIME_1_5;

hi3c1.Init.CtrlBusCharacteristic.WaitTime = HAL_I3C_OWN_ACTIVITY_STATE_0;

hi3c1.Init.CtrlBusCharacteristic.SCLPPLowDuration = 0x09;

hi3c1.Init.CtrlBusCharacteristic.SCLI3CHighDuration = 0x09;

hi3c1.Init.CtrlBusCharacteristic.SCLODLowDuration = 0x59;

hi3c1.Init.CtrlBusCharacteristic.SCLI2CHighDuration = 0x00;

hi3c1.Init.CtrlBusCharacteristic.BusFreeDuration = 0x32;

hi3c1.Init.CtrlBusCharacteristic.BusIdleDuration = 0xf8;

if (HAL_I3C_Init(&hi3c1) != HAL_OK )

{

Error_Handler();

}

/** Configure FIFO

*/

sFifoConfig.RxFifoThreshold = HAL_I3C_RXFIFO_THRESHOLD_1_4;

sFifoConfig.TxFifoThreshold = HAL_I3C_TXFIFO_THRESHOLD_1_4;

sFifoConfig.ControlFifo = HAL_I3C_CONTROLFIFO_DISABLE;

sFifoConfig.StatusFifo = HAL_I3C_STATUSFIFO_DISABLE;

if (HAL_I3C_SetConfigFifo(&hi3c1, &sFifoConfig) != HAL_OK )

{

Error_Handler();

}

/** Configure controller

*/

sCtrlConfig.DynamicAddr = 0;

sCtrlConfig.StallTime = 0x00;

sCtrlConfig.HotJoinAllowed = ENABLE ;

sCtrlConfig.ACKStallState = DISABLE ;

sCtrlConfig.CCCStallState = DISABLE ;

sCtrlConfig.TxStallState = DISABLE ;

sCtrlConfig.RxStallState = DISABLE ;

sCtrlConfig.HighKeeperSDA = DISABLE ;

if (HAL_I3C_Ctrl_Config(&hi3c1, &sCtrlConfig) != HAL_OK )

{

Error_Handler();

}

}

/**

  • @brief I3C2 Initialization Function
  • @param None
  • @retval None

*/

static void MX_I3C2_Init (void ) {

I3C_FifoConfTypeDef sFifoConfig = {0};

I3C_TgtConfTypeDef sTgtConfig = {0};

hi3c2.Instance = I3C2;

hi3c2.Mode = HAL_I3C_MODE_TARGET ;

hi3c2.Init.TgtBusCharacteristic.BusAvailableDuration = 0xf8;

if (HAL_I3C_Init(&hi3c2) != HAL_OK ) {

Error_Handler();

}

/** Configure FIFO

*/

sFifoConfig.RxFifoThreshold = HAL_I3C_RXFIFO_THRESHOLD_1_4;

sFifoConfig.TxFifoThreshold = HAL_I3C_TXFIFO_THRESHOLD_1_4;

sFifoConfig.ControlFifo = HAL_I3C_CONTROLFIFO_DISABLE;

sFifoConfig.StatusFifo = HAL_I3C_STATUSFIFO_DISABLE;

if (HAL_I3C_SetConfigFifo(&hi3c2, &sFifoConfig) != HAL_OK ) {

Error_Handler();

}

/** Configure Target

*/

sTgtConfig.Identifier = 0xC7;

sTgtConfig.MIPIIdentifier = DEVICE_ID2;

sTgtConfig.CtrlRoleRequest = DISABLE ;

sTgtConfig.HotJoinRequest = ENABLE ;

sTgtConfig.IBIRequest = DISABLE ;

sTgtConfig.IBIPayload = DISABLE ;

sTgtConfig.IBIPayloadSize = HAL_I3C_PAYLOAD_EMPTY;

sTgtConfig.MaxReadDataSize = 0xFF;

sTgtConfig.MaxWriteDataSize = 0xFF;

sTgtConfig.CtrlCapability = DISABLE ;

sTgtConfig.GroupAddrCapability = DISABLE ;

sTgtConfig.DataTurnAroundDuration = HAL_I3C_TURNAROUND_TIME_TSCO_LESS_12NS;

sTgtConfig.MaxReadTurnAround = 0;

sTgtConfig.MaxDataSpeed = HAL_I3C_GETMXDS_FORMAT_1;

sTgtConfig.MaxSpeedLimitation = DISABLE ;

sTgtConfig.HandOffActivityState = HAL_I3C_HANDOFF_ACTIVITY_STATE_0;

sTgtConfig.HandOffDelay = DISABLE ;

sTgtConfig.PendingReadMDB = DISABLE ;

if (HAL_I3C_Tgt_Config(&hi3c2, &sTgtConfig) != HAL_OK )

{

Error_Handler();

}

}

/**

  • @brief USART1 Initialization Function
  • @param None
  • @retval None

*/

static void MX_USART1_UART_Init (void ) {

huart1.Instance = USART1;

huart1.Init.BaudRate = 115200;

huart1.Init.WordLength = UART_WORDLENGTH_8B;

huart1.Init.StopBits = UART_STOPBITS_1;

huart1.Init.Parity = UART_PARITY_NONE;

huart1.Init.Mode = UART_MODE_TX_RX;

huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;

huart1.Init.OverSampling = UART_OVERSAMPLING_16;

huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;

huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;

huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

if (HAL_UART_Init(&huart1) != HAL_OK ) {

Error_Handler();

}

if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK ) {

Error_Handler();

}

if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK ) {

Error_Handler();

}

if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK ) {

Error_Handler();

}

}

/**

  • @brief GPIO Initialization Function
  • @param None
  • @retval None

*/

static void MX_GPIO_Init (void ) {

GPIO_InitTypeDef GPIO_InitStruct = {0};

/ GPIO Ports Clock Enable /

__HAL_RCC_GPIOC_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();

__HAL_RCC_GPIOB_CLK_ENABLE();

/Configure GPIO pin Output Level /

HAL_GPIO_WritePin(USER_LED_GPIO_Port, USER_LED_Pin, GPIO_PIN_RESET );

/Configure GPIO pin Output Level /

HAL_GPIO_WritePin(GPIOA, OLED_SCL_Pin|OLED_SDA_Pin, GPIO_PIN_SET );

// 设置用户按钮使用的GPIO口

GPIO_InitStruct.Pin = USER_BUTTON_Pin;

GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

GPIO_InitStruct.Pull = GPIO_NOPULL;

HAL_GPIO_Init(USER_BUTTON_GPIO_Port, &GPIO_InitStruct);

// 设置用户LED按钮使用的GPIO口

GPIO_InitStruct.Pin = USER_LED_Pin;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

HAL_GPIO_Init(USER_LED_GPIO_Port, &GPIO_InitStruct);

// 设置OLED使用的接口(模拟方式)

GPIO_InitStruct.Pin = OLED_SCL_Pin|OLED_SDA_Pin;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_PULLUP;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

/**

  • @brief 从机请求地址分配的回调函数
  • @par Called functions
    • HAL_I3C_TgtReqDynamicAddrCallback()
    • HAL_I3C_Ctrl_SetDynamicAddress()
  • @retval None

*/

void HAL_I3C_TgtReqDynamicAddrCallback (I3C_HandleTypeDef *hi3c, uint64_t targetPayload) {

printf ("从机请求地址分配。");

GUI_ShowString(0, 16, (uint8_t*)"ReqAddr", 8, 1);

/ Update Payload on aTargetDesc /

aTargetDesc[uwTargetCount]->TARGET_BCR_DCR_PID = targetPayload;

/ Send associated dynamic address /

HAL_I3C_Ctrl_SetDynAddr(hi3c, aTargetDesc[uwTargetCount++]->DYNAMIC_ADDR);

}

/**

  • @brief 控制器完成动态地址的分配的回调函数
  • @param hi3c : [IN] 包含配置信息的结构体.
  • @retval None

*/

void HAL_I3C_CtrlDAACpltCallback (I3C_HandleTypeDef *hi3c) {

printf ("完成动态地址的分配。");

GUI_ShowString(60, 16, (uint8_t*)"Addr OK", 8, 1);

}

/**

  • @brief 收到连接请求后代额回调函数.
  • @par Called functions
    • HAL_I3C_NotifyCallback()
  • @retval None

*/

void HAL_I3C_NotifyCallback (I3C_HandleTypeDef *hi3c, uint32_t eventId) {

if ((eventId & EVENT_ID_HJ) == EVENT_ID_HJ) {

HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

// 建立收到连接请求的标志

printf ("收到连接请求!");

GUI_ShowString(0, 8, (uint8_t*)"Notify", 8, 1);

uwHotJoinRequested = 1;

}

}

/**

  • @brief 从机(I3C2)连接成功的回调函数.
  • 函数目的是为了检查热连接过程是否完成
  • @par Called functions
    • HAL_I3C_TgtHotJoinCallback()
  • @retval None

*/

void HAL_I3C_TgtHotJoinCallback (I3C_HandleTypeDef *hi3c, uint8_t dynamicAddress) {

// 从机连上

GUI_ShowString(0, 24, (uint8_t*)"Target Addr=", 8, 1);

GUI_ShowNum(100, 24, dynamicAddress, 2, 8, 1);

printf ("Slave is OK! Address=%d", dynamicAddress);

}

/**

  • @brief This function is executed in case of error occurrence.
  • @retval None

*/

void Error_Handler (void ) {

/ USER CODE BEGIN Error_Handler_Debug /

/ User can add his own implementation to report the HAL error return state /

__disable_irq();

while (1) {

}

/ USER CODE END Error_Handler_Debug /

}

#ifdef USE_FULL_ASSERT

/**

  • @brief Reports the name of the source file and the source line number
  • where the assert_param error has occurred.
  • @param file: pointer to the source file name
  • @param line: assert_param error line source number
  • @retval None

*/

void assert_failed(uint8_t *file, uint32_t line)

{

/ USER CODE BEGIN 6 /

/* User can add his own implementation to report the file name and line number,

ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

/ USER CODE END 6 /

}

#endif / USE_FULL_ASSERT /

这个程序测试的是动态热插拔I3C的设备,实际上因为使用的是单片机的两个I3C外设之间的连接,链路是一开始就接好了的。只是在程序中,由代码控制从属设备主动发起连接请求。主设备收到连接请求后,分配地址,建立连接。程序中关于I3C的设置,没完全搞懂,后面有时间慢慢学习、理解。

运行结果如图所示:

图片2.png

收藏 评论1 发布时间:2024-7-7 18:24

举报

1个回答
yang9397 回答时间:2024-8-6 08:56:06

謝謝分享,I3C適合做什麽

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版