在通讯上,I3C有255个指令,其中有些是保留命令,有15个是要求必须被遵循的。通讯帧格式如下:
更详细的I3C通讯协议,这里不在继续罗列。 STM32H533里集成了2个I3C外设,鉴于手里没有支持的I3C设备,考虑做以下实验: 1、使用I3C连接I2C设备,测试I3C的向下兼容性。 2、使用STM32H533自身的两个I3C设备互联,学习I3C通讯。 实验在例程的基础上,结合CubeMX,生成I3C的初始化代码。根据设置,使用PB6、PB7作为第一组,PC6、PC7作为第二组。 整个程序调了一天才调通。调通之前犯了很多错误,比如没有设置中断、插错引脚。幸运的是因为使用CubeMX处理生成的基础代码,很多设置不用自己手动追加。 主程序如下: `` / USER CODE BEGIN Header / /**
*/ / 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 ); /**
*/ 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; } } /**
*/ 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
*/ 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(); } } /**
*/ 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(); } } /**
*/ 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(); } } /**
*/ 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(); } } /**
*/ 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); } /**
*/ 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); } /**
*/ void HAL_I3C_CtrlDAACpltCallback (I3C_HandleTypeDef *hi3c) { printf ("完成动态地址的分配。"); GUI_ShowString(60, 16, (uint8_t*)"Addr OK", 8, 1); } /**
*/ 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; } } /**
*/ 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); } /**
*/ 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 /**
*/ 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的设置,没完全搞懂,后面有时间慢慢学习、理解。 运行结果如图所示: |
实战经验 | STM32H5 USBD Classic驱动 CDC移植
NUCLEO-H563ZI刷入Micropython固件并点亮LED灯
基于STM32H5的DA之初体验经验分享(带 Trust Zone)
【免费申请】高性能和低成本双Buff加持的NUCLEO H533RE,等你来!
【NUCLEO-H533RE评测】使用双存储区Flash 在不关闭系统的状态下,实现OTA
【NUCLEO-H533RE评测】高性能-全频,硬件加速在电机控制相关应用的速度对比。
【NUCLEO-H533RE评测】HASH对比测试
【NUCLEO H533RE评测分享】高性能和低成本双Buff加持的NUCLEO H533RE
【NUCLEO-H533RE评测】+加载OLED显示部件
【NUCLEO-H533RE评测】+Coremark跑分测试
謝謝分享,I3C適合做什麽