NUCLEO F401RE 和使用 HAL_I2C_Mem_Read_DMA 进行多次读取

问题描述 投票:0回答:2

我正在做一个涉及STM32和加速度传感器(LIS2DE)的项目 我必须每秒从传感器读取寄存器并通过 UART 将其发送到 arduino IDE,但是,不知道为什么,其他两个 HAL_I2C_Mem_Read_DMA 不起作用

我尝试删除第一个 Mem_Read,但只有第二个可以工作,所以我检查了第二个调用的返回值,它给了我 HAL_BUSY。 使用 HAL_I2C_Master_Transmit_DMA 和 HAL_I2C_Master_Receive_DMA 工作正常,但我想尝试这种方式,因为教授建议我们使用这个函数来自动执行发送+接收。 您认为单独使用“发送”和“接收”更好还是这一个更好?为什么?

另外,有没有一个函数可以获取所有3轴值?我检查了数据表,看到了自动增量功能,但我没有找到任何示例

我会留下我的代码,显然我设置了一个1秒的定时器,并为UART_TX、I2C_TX和I2C_RX设置了DMA,启用了来自UART、TIM和I2C事件的中断

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
    HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_X_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)&acc[0], 1);
    HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_Y_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)&acc[1], 1);
    HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_Z_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)&acc[2], 1);
}

void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef* hi2c){
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
        if(count==2){    //because I'll receive a single interrupt for each Mem_Read, so after the third MemRxCpltCallback will transmit thanks UART values
        count = 0;

        len = snprintf(string, sizeof(string), "X: %+.3fg   Y: %+.3fg   Z: %+.3fg\n",
                acc[0] / 256.0 * 4.0,
                acc[1] / 256.0 * 4.0,
                acc[2] / 256.0 * 4.0);
        HAL_UART_Transmit_DMA(&huart2, (unsigned char*)string, len);
         }
         else
                count++;
}

感谢您的帮助,如果您需要更多信息或者问题中有什么不好的地方,请告诉我,我会更新问题。如果有人需要的话,我也可以发布我的代码的 zip 文件的链接

--- 更新 ---

现在我只是用 TIM IT 启动第一个,然后其他的由前一个触发,最后一个触发打印,仅使用全局变量来跟踪执行的调用。您认为这是一个相当不错的解决方案吗?它可以工作,但对我来说似乎很肮脏,但必须将 I2C 与 DMA 一起使用 顺便说一句,再次感谢您的每一份贡献

    #if VERSION == 2
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
    HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_X_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(acc+count), 1);
    count++;
}

void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef* hi2c){
    switch(count){
        case 1:
            HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_Y_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(acc+count), 1);
            count++;
            break;
        case 2:
            HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_Z_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(acc+count), 1);
            count++;
            break;
        case 3:
            count=0;

            len = snprintf(string, sizeof(string), "X: %+.3fg   Y: %+.3fg   Z: %+.3fg, (Tot:%+.3fg | %+.3fm/s²)\n",
                    acc[0] / 256.0 * 4.0,
                    acc[1] / 256.0 * 4.0,
                    acc[2] / 256.0 * 4.0,
                    sqrt(pow(acc[0],2)+pow(acc[1],2)+pow(acc[2],2))/64.0,
                    sqrt(pow(acc[0],2)+pow(acc[1],2)+pow(acc[2],2))/64.0 * 9.80665);
            HAL_UART_Transmit_DMA(&huart2, (unsigned char*)string, len);
    }
}
#endif

--- 更新2 --- 这是我的主要代码,其他代码是自动生成的,加上@pmacfarlane 建议的两个回调

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  MX_I2C1_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */

  if (HAL_I2C_Master_Transmit_DMA(&hi2c1, ACCEL_ADD, (uint8_t*)CTRL_REG1, sizeof(CTRL_REG1)) == HAL_OK)
      len = snprintf(string, sizeof(string), "LIS2DE found!\n");
  else{
      ACCEL_ADD = LIS2DE12_ADD;
      if (HAL_I2C_Master_Transmit_DMA(&hi2c1, ACCEL_ADD, (uint8_t*)CTRL_REG1, sizeof(CTRL_REG1)) == HAL_OK)
          len = snprintf(string, sizeof(string), "LIS2DE12 found!\n");
      else
          len = snprintf(string, sizeof(string), "Accelerator error!\n");
  }
  HAL_UART_Transmit_DMA(&huart2, (unsigned char*)string, len);

  HAL_I2C_Master_Transmit_DMA(&hi2c1, ACCEL_ADD, (uint8_t*)CTRL_REG2, sizeof(CTRL_REG2));
  HAL_I2C_Master_Transmit_DMA(&hi2c1, ACCEL_ADD, (uint8_t*)CTRL_REG4, sizeof(CTRL_REG4));

  TIM2->SR &= ~TIM_SR_UIF;          //or __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); or HAL_Delay(500);
  HAL_TIM_Base_Start_IT(&htim2);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
#if VERSION == 3
      while (!timer_elapsed){/*do nothing*/}

      for (int i=0; i<3; i++){
          dma_complete = false;
          HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, regs[i], I2C_MEMADD_SIZE_8BIT, (uint8_t*)(acc+i), 1);

          while (!dma_complete){/*do nothing*/}
      }

      len = snprintf(string, sizeof(string), "X: %+.3fg   Y: %+.3fg   Z: %+.3fg (Tot:%+.3fg | %+.3fm/s²)\n",
              acc[0] / 256.0 * 4.0,
              acc[1] / 256.0 * 4.0,
              acc[2] / 256.0 * 4.0,
              sqrt(pow(acc[0],2)+pow(acc[1],2)+pow(acc[2],2))/64.0,
              sqrt(pow(acc[0],2)+pow(acc[1],2)+pow(acc[2],2))/64.0 * 9.80665);
      HAL_UART_Transmit_DMA(&huart2, (unsigned char*)string, len);

      timer_elapsed = false;
#endif
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
callback stm32 sensors i2c dma
2个回答
0
投票
  1. 使用 DMA 读取一个字节没有任何意义
  2. 您需要从回调开始第二次和第三次传输。

0
投票

几点:

  • 如果您希望在 CPU 继续运行并进行其他处理的同时发生某些事务,通常会使用 DMA。如果您的代码启动 DMA 事务,然后基本上只是等待它完成,您并没有真正获得太多好处。
  • 中断服务例程(ISR)(例如您的两个回调)理想情况下应该执行最低限度的要求,并且应该尽快执行。如果使用操作系统,它们可能会提供信号量,或将某些内容添加到消息队列中。如果没有操作系统,他们通常可能只是设置一个标志。所有“繁重”处理都将在您的主(非 ISR)代码中进行。
  • 在 ISR 中运行时,较低优先级的中断将被阻止。因此,如果您的系统时钟中断的优先级低于计时器已用回调中断的优先级,则
    HAL_Delay()
    之类的函数将无法在计时器中断中工作,因为 sysTick 不会递增。

考虑到这些要点,您可以进行类似这样的设计。 (未经测试,不完整,但希望能表明意图。)

#include <stdbool.h>

static volatile bool timer_elapsed = false;
static volatile bool dma_complete = false;

void main(void)
{
    // Configure all peripherals etc.
    
    
    for (;;)
    {
        // Wait for timer to elapse
        while (!timer_elapsed)
        {
            // You could be doing other things while you wait...
        }
        
        // Read the three registers
        for (int i = 0; i < 3; ++i)
        {
            static const uint8_t regs[] = {OUT_X_ADD, OUT_Y_ADD, OUT_Z_ADD};
            
            dma_complete = false;
            do_i2c_dma(regs[i]); // Call the HAL DMA function

            // Wait for DMA to complete
            while (!dma_complete)
            {
                // You could be doing things here too...
            }
        }
        
        print_results(); // To be implemented
        
        timer_elapsed = false;
    }
}

void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef* hi2c)
{
    dma_complete = true;
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
{
    timer_elapsed = true;
}

标志是易失性的,因为它们在 ISR 中被修改,否则主代码可能会认为它们永远不会改变。

© www.soinside.com 2019 - 2024. All rights reserved.