首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >用DMA和HAL不工作的I2C传输

用DMA和HAL不工作的I2C传输
EN

Stack Overflow用户
提问于 2021-03-26 07:58:28
回答 2查看 4.1K关注 0票数 1

这似乎是一个有点常见的问题,但我在网上找到的任何解决方案都没有成功。具体来说,我试图通过I2C/DMA和在cubeIDE中生成的HAL将1024字节的缓冲区(完整的128x64PX映像)传输到cubeIDE显示器。我用的是STML432核子板。如果不使用DMA使用HAL_I2C_Mem_Write传输缓冲区,我没有问题。

基于我看到的其他问题,问题在于DMA完成时I2C总线仍然在传输。我只是不知道如何纠正这一点,并且给出的例子通常不使用HAL (不幸的是,尽管我做了很多努力,我还是不太有能力将它们正确地应用到HAL中,我猜)。我尝试过对I2c和DMA使用中断,但没有成功,只传输了前254个字节(在屏幕上只显示了两行)。

下面是我发送缓冲区的代码:

代码语言:javascript
运行
复制
static void ssd1306_WriteMData_DMA(const uint8_t *data, uint16_t size)
{
    while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY);
    HAL_I2C_Mem_Write_DMA(&hi2c1, I2C_ADDR, SSD1306_REG_MDAT, 1, (uint8_t*)data, size);
}

以及每个中断处理程序的代码:

代码语言:javascript
运行
复制
void I2C1_EV_IRQHandler(void)
{
  /* USER CODE BEGIN I2C1_EV_IRQn 0 */
    if(I2C1->ISR & I2C_ISR_TCR){
        
    I2C1->CR2 |=  (I2C_CR2_STOP);// stop i2c
    I2C1->ICR |=  (I2C_ICR_STOPCF);// Reset the ICR  flag.

    // stop DMA
    DMA1->IFCR |= DMA_IFCR_CTCIF6;
    // clear flag
    DMA1_Channel6->CCR &= ~DMA_CCR_EN;
}
  /* USER CODE END I2C1_EV_IRQn 0 */
  //HAL_I2C_EV_IRQHandler(&hi2c1);
  /* USER CODE BEGIN I2C1_EV_IRQn 1 */
 
  /* USER CODE END I2C1_EV_IRQn 1 */
}


void DMA1_Channel6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel6_IRQn 0 */

    // stop DMA
    DMA1->IFCR |= DMA_IFCR_CTCIF6;
    // clear flag
    DMA1_Channel6->CCR &= ~DMA_CCR_EN;

  /* USER CODE END DMA1_Channel6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_i2c1_tx);
  /* USER CODE BEGIN DMA1_Channel6_IRQn 1 */

  /* USER CODE END DMA1_Channel6_IRQn 1 */
}

我认为这是所有相关的代码,让我知道,如果有其他的东西,我错过了。外设的所有初始化代码都是通过cubeMX完成的,但如果需要的话,我可以发布这些代码或设置。我觉得这是一件很简单的事情,我错过了,但老实说,这实在是太过分了,所以我不太明白到底发生了什么……

谢谢你的帮助!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-07-30 05:28:36

问题在于您的自定义DMA1_Channel6_IRQHandlerI2C1_EV_IRQHandler。这些函数将在I2C传输255个字节之后立即调用,这是NBYTES的MAX_NBYTE_SIZE。HAL已经在stm32l4xx_hal_i2c.c中拥有所有必需的中断例程。

如果数据大小大于255个字节,则I2C_Master_ISR_DMA;

  • Checks将I2C_DMAMasterTransmitCplt;

  • Starts

  • 处理程序设置为
  1. ,并使用重新加载模式。
  2. 使用HAL_DMA_Start_IT()
  3. Configures I2C寄存器使用I2C_TransferConfig()

将I2C DMA完全回调设置为

HAL驱动程序将使用I2C_Master_ISR_DMAI2C_DMAMasterTransmitCplt处理所有的I2C_Master_ISR_DMA中断。

  1. I2C_DMAMasterTransmitCplt将为255 (MAX_NBYTE_SIZE)或更少的块重新启动DMA,bytes.
  2. I2C_Master_ISR_DMA将使用I2C_TransferConfig重置RELOAD/NBYTES寄存器。对于最后一个数据块,使用了I2C_AUTOEND_MODE .

所以你所需要的就是

在functions

  • enable设备配置Tool

  • configure DMA中删除DMA1_Channel6_IRQHandlerI2C1_EV_IRQHandler I2C1事件中断中的“用户代码”,数据宽度为byte/byte
  1. perform,一次调用HAL_I2C_Mem_Write_DMA(...)启动transfer
  2. check HAL_I2C_STATE_READY,然后再传输

请参阅HAL_I2C_Mem_Write_DMAI2C_Master_ISR_DMAstm32l4xx_hal_i2c.c中的I2C_DMAMasterTransmitCplt源代码,以了解其工作原理。

关于DMA为何在I2C仍在工作时结束:哈尔驱动程序使用255个字节块通过DMA发送I2C数据,停止DMA,启动DMA,清除I2C_CR2 NBYTES/重新加载,启用DMA。DMA可以使用DMA_CIRCULAR模式连续运行,但目前还没有在HAL I2C驱动程序中实现。

代码语言:javascript
运行
复制
// DMA enabled single time
hi2c1.hdmatx->XferCpltCallback =  MY_I2C_DMAMasterTransmitCplt;
HAL_DMA_Start_IT(hi2c1.hdmatx, (uint32_t)&i2cBuffer, (uint32_t)&hi2c1.Instance->TXDR, I2C_BUFFER_SIZE);
MY_I2C_TransferConfig(&hi2c1,  (uint16_t)DAC_ADDR, 254, I2C_RELOAD_MODE, I2C_GENERATE_START_WRITE); // in first call using I2C_GENERATE_START_WRITE
uint32_t tmpisr = I2C_IT_TCI; 
__HAL_I2C_ENABLE_IT(&hi2c1, tmpisr);
hi2c1.Instance->CR1 |= I2C_CR1_TXDMAEN;

仍然需要使用I2C_CR2每254个字节清除MY_I2C_TransferConfig NBYTES/RELOAD (我不使用255将中断触发与数组中的偶数索引对齐):

代码语言:javascript
运行
复制
static HAL_StatusTypeDef MY_I2C_Master_ISR_DMA(struct __I2C_HandleTypeDef *hi2c, uint32_t ITFlags, uint32_t ITSources)
{
    if (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_TCR) == SET)
    {
        MY_I2C_TransferConfig(&hi2c1,  (uint16_t)DAC_ADDR, 254, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); // in repeated calls using I2C_NO_STARTSTOP
    
    }
    return HAL_OK;
}

使用这种方法,DMA循环缓冲区大小不限于255个字节:

代码语言:javascript
运行
复制
#define I2C_BUFFER_SIZE 1024
uint8_t i2cBuffer[I2C_BUFFER_SIZE];

Main.c应该具有MY_I2C_TransferConfig()函数,它是从stm32l4xx_hal_i2c.c复制的私有函数HAL_I2C_TransferConfig()的粘贴版本。在早期的STM32微控制器上,不存在NBYTES/RELOAD字段,也不需要以这种方式更新I2C_CR2。在循环模式下使用DMA可以实现最高的帧速率,您只需要使用XferHalfCpltCallbackXferCpltCallback回调及时填充DMA缓冲区。可以使用memcpy()或DMA MEMTOMEM传输从较大的缓冲区复制帧。

票数 2
EN

Stack Overflow用户

发布于 2021-03-26 08:30:19

你还没说你用的是哪一种STM32。它们有不同的位定义(因为早期发布的部分中的I2C外围设备是垃圾),但是看起来您使用的是后面的一个。

基本上,您可以在参考手册中的I2C寄存器的位定义中找到所需的内容。如果您正在设置停止之前,它已经完成,您需要寻找一个繁忙的位,被清除,或BTF (字节传输完成)位,被设置时,你是时候发送停止。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66812977

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档