阻塞版本 (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,所以应该有足够的空闲时间。
所以我不知道为什么没有发送缓冲区本身。任何的意见都将会有帮助。