ST意法半导体
直播中

杨杰

8年用户 1212经验值
私信 关注
[问答]

请问HAL_I2C_Mem_Write_DMA函数为什么只发送一个字节呢?

阻塞版本 (HAL_I2C_Mem_Write) 运行良好,现在我正在尝试使用 HAL_I2C_Mem_Write_DMA。以下是配置工具中给出的设置。



配置器生成的结果代码(为简洁起见,我删除了一些注释):
  • static void MX_DMA_Init(void)
  • {
  •   __HAL_RCC_DMA1_CLK_ENABLE();
  •   HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
  •   HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
  • }
  • static void MX_I2C2_Init(void)
  • {
  •   hi2c2.Instance = I2C2;
  •   hi2c2.Init.ClockSpeed = 400000;
  •   hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
  •   hi2c2.Init.OwnAddress1 = 0;
  •   hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  •   hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  •   hi2c2.Init.OwnAddress2 = 0;
  •   hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  •   hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  •   if (HAL_I2C_Init(&hi2c2) != HAL_OK)
  •   {
  •     Error_Handler();
  •   }
  • }
  • void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
  • {
  •   GPIO_InitTypeDef GPIO_InitStruct = {0};
  •   if(hi2c->Instance==I2C2)
  •   {
  •     __HAL_RCC_GPIOB_CLK_ENABLE();
  •     /**I2C2 GPIO Configuration
  •     PB10     ------> I2C2_SCL
  •     PB11     ------> I2C2_SDA
  •     */
  •     GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
  •     GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  •     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  •     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  •     /* Peripheral clock enable */
  •     __HAL_RCC_I2C2_CLK_ENABLE();
  •     /* I2C2 DMA Init */
  •     /* I2C2_TX Init */
  •     hdma_i2c2_tx.Instance = DMA1_Channel4;
  •     hdma_i2c2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  •     hdma_i2c2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  •     hdma_i2c2_tx.Init.MemInc = DMA_MINC_ENABLE;
  •     hdma_i2c2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  •     hdma_i2c2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  •     hdma_i2c2_tx.Init.Mode = DMA_NORMAL;
  •     hdma_i2c2_tx.Init.Priority = DMA_PRIORITY_LOW;
  •     if (HAL_DMA_Init(&hdma_i2c2_tx) != HAL_OK)
  •     {
  •       Error_Handler();
  •     }
  •     __HAL_LINKDMA(hi2c,hdmatx,hdma_i2c2_tx);
  •     /* I2C2 interrupt Init */
  •     HAL_NVIC_SetPriority(I2C2_EV_IRQn, 0, 0);
  •     HAL_NVIC_EnableIRQ(I2C2_EV_IRQn);
  •   }
  • }
我从中调用它的代码:
  • void SSD1306_BlitLineBuffer(uint8_t row) {
  •     if ( row >= (SSD1306_SCREEN_HEIGHT / SSD1306_LINEBUFFER_HEIGHT) )
  •         return;
  •     SSD1306_WriteCommand(0xB0 + row);
  •     SSD1306_WriteCommand(0x00);
  •     SSD1306_WriteCommand(0x10);
  •     //HAL_I2C_Mem_Write(&SSD1306_I2C_BUS, SSD1306_ADDRESS, 0x40, 1, ssd1306Bytes, SSD1306_BUFFER_SIZE, HAL_MAX_DELAY);
  •     if ( HAL_OK != HAL_I2C_Mem_Write_DMA(&SSD1306_I2C_BUS, SSD1306_ADDRESS, 0x40, 1, ssd1306Bytes, SSD1306_BUFFER_SIZE) ) {
  •         Error_Handler();
  •         }
  • }
  • void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c) {
  • }
注释掉的非 DMA 调用工作正常。
缓冲区大小为 128 字节。
当我在回调中放置断点时,断点被成功触发。
HAL_I2C_Mem_Write_DMA 返回 HAL_OK。当我走进它时,这是它返回前的状态:

请注意,XferCount 以 128 开始。
这是我的逻辑分析器的视图,首先是阻塞(非 DMA)调用的结果:

现在 DMA 版本的结果:

仅发送 MemAddress (0x40)。数据缓冲区似乎被完全忽略了。
当我在回调中放置断点时,XferCount 变为零,State 和 Mode 分别为“ready”和“none”。据我所知,这是正常操作应该发生的情况。

唯一运行的是一个非常简单的计时器(未启用通道),用于每 20 毫秒触发一次 I2C 调用。当用非阻塞版本测量时,数据传输需要 3.2ms,所以应该有足够的空闲时间。
所以我不知道为什么没有发送缓冲区本身。任何的意见都将会有帮助。

回帖(1)

孙勳努

2023-1-17 10:30:53
好的,我终于想通了。配置工具正在生成这样的调用:


  •   MX_I2C2_Init();
  •   MX_DMA_Init();

....但这些需要以相反的顺序进行,首先是 DMA。所以我所要做的就是扭转它们。
我认为问题在于我首先配置了没有任何 DMA 的 I2C,然后又回来启用 DMA 设置。当您启用更多功能时,该工具只会将任何新需要的调用附加到生成列表的末尾。
不幸的是,每次我在此之后生成代码时,配置工具也会继续以错误的顺序持续重新安排这些调用。解决方案似乎是这样的:


  • 完全禁用 I2C 外围设备(我想其他人也使用 DMA...)
  • 生成代码(两个调用都将从生成的代码中删除)
  • 通过 DMA 请求、中断等再次启用 I2C
  • 再次生成代码

当 I2C 和 DMA 一起启用和生成,这两个调用将以正确的顺序排列,并且在未来的代码生成中将保持不变。
所以我在这里浪费了 6 个小时,但仍然可能比没有配置工具的时间少。很高兴看到这个问题在某个时候得到解决。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分