1
\$\begingroup\$

I am working on an application running on STM32, which aims to read data from the digimatic spc protocol (mitutoyo device) and then send the data to the raspberry pi via the I2C bus. The Raspberry pi is the master on the I2C bus and the STM32 is the slave.

STM32 reads the data from the digimatic protocol in a cycle. I2C in STM32 operates over an interrupt. So when a request comes from raspberry in STM32, an interrupt is triggered and serviced immediately. The problem is that reading data from the digimatic is interrupted, so bad data is sent via I2C. As soon as the program returns to the place where it ended before the interruption, the reading is already in another phase and the reading of the data is completed with an error.

Has anyone solved a similar problem?

Here is the code to read the data

int main(void)
{
  HAL_Init();
  SystemClock_Config();

  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_TIM2_Init();

  HAL_I2C_EnableListen_IT(&hi2c1);

  while (1)
  {
      //reading data
      RQST_LOW();
      HAL_Delay(5);
      RQST_HIGH();
      uint32_t timeout=200;

      time=0;
      TIM2->CNT=0;
      HAL_TIM_Base_Start_IT(&htim2);

      for(uint8_t i = 0; i < 13; i++ )
      {
          uint8_t k=0;
        for (uint8_t j = 0; j < 4; j++)
        {
          while(HAL_GPIO_ReadPin(DG_CLK_GPIO_Port,DG_CLK_Pin) == false && timeout>time)
          {
              time=(uint32_t)TIM2->CNT;
          }
          while( HAL_GPIO_ReadPin(DG_CLK_GPIO_Port,DG_CLK_Pin) == true && timeout>time)
          {
              time=(uint32_t)TIM2->CNT;
          }
          bitWrite(k, j, (HAL_GPIO_ReadPin(DG_DATA_GPIO_Port,DG_DATA_Pin) & 0x1));
        }
        rawdata[i] = k;
      }

      if(!(timeout>time))
      {
          aTxBuffer[0]=' ';
          aTxBuffer[1]=' ';
          aTxBuffer[2]=' ';
          aTxBuffer[3]=' ';
          aTxBuffer[4]=' ';
          aTxBuffer[5]=' ';
          aTxBuffer[6]=' ';
          aTxBuffer[7]=' ';
      }
      else
      {
          HAL_TIM_Base_Stop_IT(&htim2);
          char buf[7];
          for(int lp=0;lp<6;lp++)
          {
            buf[lp]=rawdata[lp+5]+'0';
          }
          uint64_t cur_value_int =atoi(buf);
          char buf_dec[2];
          buf_dec[0] = rawdata[11]+'0';
          decimal = atoi(buf_dec);
          float cur_value = (float)cur_value_int*(float)pow(10,-decimal);
          char buf_sign[2];
          buf_sign[0] = rawdata[4]+'0';
          int32_t temp = atoi(buf_sign);
          if(rawdata[4]==8)
          {
            cur_value = -cur_value;
          }
          char buf_unit[2];
          buf_unit[0] = rawdata[12]+'0';
          temp = atol(buf_unit);
          units = (bool)temp;
          RQST_LOW();
          HAL_Delay(50);
          float nearest = roundf(cur_value * 100) / 100;
          char array[8] = {0};
          sprintf(aTxBuffer, "%.2f", nearest);
      }
      HAL_Delay(300);
  }
}

Here is the I2C interrupt handler code

void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
  UNUSED(AddrMatchCode);

  if(hi2c->Instance==I2C1)
  {
      if(TransferDirection == TRANSFER_DIR_READ)
      {
          if(HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, (uint8_t *)aRxBuffer, RXBUFFERSIZE, I2C_NEXT_FRAME)!=HAL_OK)
          {
              Error_Handler();
          }
      }
      else
      {
          HAL_I2C_Slave_Seq_Transmit_IT(&hi2c1, (uint8_t*)aTxBuffer, 8, I2C_LAST_FRAME);
      }
  }
}

void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
    HAL_I2C_EnableListen_IT(&hi2c1); // Restart
}

void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *I2cHandle)
{
  if (HAL_I2C_GetError(I2cHandle) != HAL_I2C_ERROR_AF)
  {
      Error_Handler();
  }
}

I will be happy for any advice

\$\endgroup\$
2
  • \$\begingroup\$ Well what is a "digimatic spc" and what protocol it uses? \$\endgroup\$
    – Justme
    Jan 26, 2021 at 8:57
  • \$\begingroup\$ Why don't you just defer servicing that interrupt until the SPC read is complete? \$\endgroup\$ Nov 14 at 23:13

1 Answer 1

0
\$\begingroup\$

This approach can be work.

In the IC2 interrupt;

void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
  UNUSED(AddrMatchCode);

  if(hi2c->Instance==I2C1)
  {
      if(TransferDirection == TRANSFER_DIR_READ)
      {
          .
          .
          .
      }
      else
      {
         sendData = 1;
      }
  }
}

After reading data from the digimatic;

// After reading data
if(sendData)
{
  HAL_I2C_Slave_Seq_Transmit_IT(&hi2c1, (uint8_t*)aTxBuffer, 8, I2C_LAST_FRAME);
  sendData = 0;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.