cancel
Showing results for 
Search instead for 
Did you mean: 

why does Modbus CRC calculation changes for each cycle, if called in while() loop!

TMuka.1
Associate II

The CRC_Accumulate() function has been called in while loop and the rx_data received as uint8_t and converted into uint32_t and then passed to CRC_accumulate() function in order to calculate the CRC value, but unfortunately the CRC value changes every cycle, what should I do to correct this behaviour?

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"
#include "usb_device.h"
 
/* Private define ------------------------------------------------------------*/
#define RX_BUFFER_SIZE   20
#define BUFFER_SIZE 5U
 
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc;
CRC_HandleTypeDef hcrc;
I2C_HandleTypeDef hi2c1;
RTC_HandleTypeDef hrtc;
SPI_HandleTypeDef hspi1;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim5;
TIM_HandleTypeDef htim9;
 
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
 
/* USER CODE BEGIN PV */
uint8_t rx_buffer[RX_BUFFER_SIZE], rx_flag, rx_index, rx_data, error_flag;
uint32_t temp[4];
//CRC Calculation
uint32_t CRCValue = 0;
uint32_t ExpectedCRCValue = 0;
uint32_t Data[BUFFER_SIZE]= {0x00000000, 0x00000011, 0x000000AA, 0x000000CC, 0x0AEC5781};
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_SPI1_Init(void);
static void MX_RTC_Init(void);
static void MX_ADC_Init(void);
static void MX_I2C1_Init(void);
static void MX_TIM3_Init(void);
static void MX_TIM5_Init(void);
static void MX_TIM9_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_TIM2_Init(void);
static void MX_CRC_Init(void);
 
int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USB_DEVICE_Init();
  MX_FATFS_Init();
  MX_SPI1_Init();
  MX_RTC_Init();
  MX_ADC_Init();
  MX_I2C1_Init();
  MX_TIM3_Init();
  MX_TIM5_Init();
  MX_TIM9_Init();
  MX_USART1_UART_Init();
  MX_TIM2_Init();
  MX_CRC_Init();
  
  HAL_UART_Receive_IT(&huart1,  &rx_data, sizeof(rx_data));
  
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if (rx_flag==1){
		  Data[0] =rx_buffer[0]<<24|rx_buffer[1]<<16|rx_buffer[2]<<8|rx_buffer[3];
		  Data[1] =rx_buffer[4]<<24|rx_buffer[5]<<16|rx_buffer[6]<<8|rx_buffer[7];
		  Data[2] =rx_buffer[8]<<24|rx_buffer[9]<<16|rx_buffer[10]<<8|rx_buffer[11];
		  Data[3] =rx_buffer[12]<<24|rx_buffer[13]<<16|rx_buffer[14]<<8|rx_buffer[15];
		  Data[4] =rx_buffer[16]<<24|rx_buffer[17]<<16|rx_buffer[18]<<8|rx_buffer[19];
		  ExpectedCRCValue = Data[4]; // 0x0AEC5781, which is fed from PC's serial terminal!
		  temp[0] = Data[0];
		  temp[1] = Data[1];
		  temp[2] = Data[2];
		  temp[3] = Data[3];
		  CRCValue = HAL_CRC_Accumulate(&hcrc, (uint32_t *)&temp, sizeof(temp));
		  if(CRCValue != ExpectedCRCValue)
		   {
			//Error_Handler();
			  error_flag=1;
		   }
	  }
	  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
	  HAL_Delay(500);  //250 ms delay
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
 
  }
}
 
void SystemClock_Config(void)
{
// removed for easy readability of main task
}
static void MX_CRC_Init(void)
{
// removed for easy readability of main task
}
static void MX_USART1_UART_Init(void)
{
// removed for easy readability of main task
}
static void MX_DMA_Init(void)
{
// removed for easy readability of main task
}
static void MX_GPIO_Init(void)
{
// removed for easy readability of main task
}
 
 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance==USART1)
	{
		//if the data is not being received, clear the buffer
		if(rx_index ==0)
		{
			for (int i=0; i<20; i++)
			{
				rx_buffer[i]=0;
			}
		}
		//if the character received is other than 'enter' ASCII13, save the data in buffer
		if(rx_data!=13)
		{
			rx_buffer[rx_index++]=rx_data;
		}
		else
		{
			rx_index=0;
			rx_flag=1;  // turn the rx_flag 'ON'
			HAL_UART_Transmit_IT(&huart1, rx_buffer, sizeof(rx_buffer)); // transmit the data via UART
		}
		HAL_UART_Receive_IT(&huart1, &rx_data, 1);  // restart the interrupt reception mode & receive only 1 char at a time!
	}
}
 
/*
Alternative while loop: (where the crc calculation is stable but wrong)
Moreover, I tried using flow control flag (i.e. rx_flag=0), but still no sucess, the CRC calculation result is wrong, probably the temp[4] variable was not filled or floating/instable!
 */
  while (1)
  {
	  if (rx_flag==1){
		  Data[0] =rx_buffer[0]<<24|rx_buffer[1]<<16|rx_buffer[2]<<8|rx_buffer[3];
		  Data[1] =rx_buffer[4]<<24|rx_buffer[5]<<16|rx_buffer[6]<<8|rx_buffer[7];
		  Data[2] =rx_buffer[8]<<24|rx_buffer[9]<<16|rx_buffer[10]<<8|rx_buffer[11];
		  Data[3] =rx_buffer[12]<<24|rx_buffer[13]<<16|rx_buffer[14]<<8|rx_buffer[15];
		  Data[4] =rx_buffer[16]<<24|rx_buffer[17]<<16|rx_buffer[18]<<8|rx_buffer[19];
		  temp[0] = Data[0];
		  temp[1] = Data[1];
		  temp[2] = Data[2];
		  temp[3] = Data[3];
		  ExpectedCRCValue = Data[4]; // 0x0AEC5781
		  HAL_Delay(500);
		  CRCValue = HAL_CRC_Accumulate(&hcrc, (uint32_t *)&temp, sizeof(temp));
		  if(CRCValue != ExpectedCRCValue)
		   {
			  error_flag=1;
		   }
		  rx_flag=0;  // set the rx_flag as false
	  }
	  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
	  HAL_Delay(500);  //250 ms delay
 
  }

Output:

  The uint32_t CRCValue changes every cycle, although the same rx_data is sent or Data[0-3] and temp[0-3] are same!

0693W00000HoRjVQAV.pngPFA Screenshot

What is wrong here? Please help!

 Regards,

Thangaraj

8 REPLIES 8

Doesn't seem to be an STM32 issue, the values in the array come from the bytes "0x00" you sent over the wire​

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Muhammed Güler
Senior III

You are trying to send the expression as ascii. 0 = 48 x=120 from the ascii table

TMuka.1
Associate II

@Community member​ @Muhammed G�ler​ 

that means HAL_UART_Receive_IT() can't receive more than 1 character at a time? How I should receive {0x00000000, 0x00000011, 0x000000AA, 0x000000CC, 0x0AEC5781} from a serial terminal? I need it for a modbus communication implementation!

If anybody has any suggestion please help!

MM..1
Chief II

Seems you create new ask, but still dont understand UART, Is serial N bit M parity protocol, In basic usage is N 8 , then try write received byte to 32 bit word have no effect

Data[rx_index++]=rx_data;

secondary you need define your UART data to BINARY or ASCI , Seems you send ASCI and try receive as binary next mistake.

Simply receive to string with good amount character around 255 buff is ok, Then convert it to Data[] ... but try it send is same mistake or if send need binary maybe ok ...

HAL_UART_Transmit(&huart1, Data, sizeof(Data), 100); // transmit the data via UART

Well clearly it can receive multiple characters, they are in ASCII, and you're going to have to parse the data you sent to get the numbers out into an array of uint32_t.

The logic in your call back looks incomplete.

Variables with similar names are used, inconsistently.

rx_data for example isn't 8 bytes wide.

There's no numeric conversion, using atoi() or sscanf()

The issue here is with C coding, and data representation, not the in ability of the STM32 to fill the array with data as requested. Although doing one byte at a time would allow for white-space, and resynchronization, to be handled properly.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
TMuka.1
Associate II

@MM..1​ 

There is no configuration settings available to set UART data as ASCII or Binary, it should be programmed in the receiver program how to interpret it! There is only option to choose between 8 or 9 bits! So first I have to receive the rx_data as string and then convert them into 32 bit integer (uint8_t to uint32_t)!

TMuka.1
Associate II

I fixed the mistake in the code and now it works perfect!

/* USER CODE BEGIN PV */
uint8_t aintialText[] = "\r\nUSART Example\r\n";
uint8_t rx_buffer[RX_BUFFER_SIZE], rx_flag, rx_index, rx_data, error_flag, data_fill;
//CRC Calculation
uint32_t CRCValue = 0;
uint32_t ExpectedCRCValue = 0;
uint32_t temp[4] = {0x00000000, 0x00000011, 0x000000AA, 0x000000CC};
uint32_t Data[BUFFER_SIZE] = {0x00000000, 0x00000011, 0x000000AA, 0x000000CC, 0x0AEC5781};
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_USB_DEVICE_Init();
  MX_FATFS_Init();
  MX_SPI1_Init();
  MX_RTC_Init();
  MX_ADC_Init();
  MX_I2C1_Init();
  MX_TIM3_Init();
  MX_TIM5_Init();
  MX_TIM9_Init();
  MX_USART1_UART_Init();
  MX_TIM2_Init();
  MX_CRC_Init();
  /* USER CODE BEGIN 2 */
  /* Initiate Continuous reception */
  HAL_UART_Receive_IT(&huart1,  &rx_data, sizeof(rx_data));
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
      if (rx_flag==1){
          Data[0] =rx_buffer[0]<<24|rx_buffer[1]<<16|rx_buffer[2]<<8|rx_buffer[3];
          Data[1] =rx_buffer[4]<<24|rx_buffer[5]<<16|rx_buffer[6]<<8|rx_buffer[7];
          Data[2] =rx_buffer[8]<<24|rx_buffer[9]<<16|rx_buffer[10]<<8|rx_buffer[11];
          Data[3] =rx_buffer[12]<<24|rx_buffer[13]<<16|rx_buffer[14]<<8|rx_buffer[15];
          Data[4] =rx_buffer[16]<<24|rx_buffer[17]<<16|rx_buffer[18]<<8|rx_buffer[19];
          temp[0] = Data[0];
          temp[1] = Data[1];
          temp[2] = Data[2];
          temp[3] = Data[3];
          ExpectedCRCValue = Data[4]; // 0x0AEC5781
          HAL_Delay(500);
          CRCValue = HAL_CRC_Calculate(&hcrc, (uint32_t *)&temp, 4U);
          if(CRCValue != ExpectedCRCValue)
           {
              error_flag=1;
           }
          rx_flag=0;  // set the rx_flag as false
      }
      HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
      HAL_Delay(500);  //250 ms delay
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
static void MX_CRC_Init(void)
{
  /* USER CODE BEGIN CRC_Init 0 */
  /* USER CODE END CRC_Init 0 */
  /* USER CODE BEGIN CRC_Init 1 */
  /* USER CODE END CRC_Init 1 */
  hcrc.Instance = CRC;
  if (HAL_CRC_Init(&hcrc) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN CRC_Init 2 */
  /* USER CODE END CRC_Init 2 */
}
.
.
.
 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance==USART1)
    {
        //if the data is not being received, clear the buffer
        if(rx_index ==0)
        {
            for (int i=0; i<20; i++)
            {
                rx_buffer[i]=0;
            }
        }
        //if the character received is other than 'enter' ASCII13, save the data in buffer
        if(rx_data!=13)
        {
            rx_buffer[rx_index++]=rx_data;
        }
        else
        {
            rx_index=0;
            rx_flag=1;  // turn the rx_flag 'ON'
            HAL_UART_Transmit_IT(&huart1, rx_buffer, sizeof(rx_buffer)); // transmit the data via UART
        }
        HAL_UART_Receive_IT(&huart1, &rx_data, 1);  // restart the interrupt reception mode & receive only 1 char at a time!
    }
}

The mistake was:

CRCValue = HAL_CRC_Accumulate(&hcrc, (uint32_t *)&temp, sizeof(temp));

This was the reason, the calculated CRC value was incorrect before!

Now, I modified as:

CRCValue = HAL_CRC_Calculate(&hcrc, (uint32_t *)&temp, sizeof(&temp));

Now, I got the expected CRC value (see the screenshot)!

0693W00000HoszkQAB.pngBut the problem now is my Modbus slave device can only generate 16 bit CRC codes and my STM32L1 series can only validate/process 32 bit CRC code! I don't know if there is any possibility where I can use the same STM32L1 series MCU to validate transmission from Modbus device with CRC 16bit validation(or polynomial)! As far as I know in STM32L1, there is no possibility to configure CRC settings (I tried in CubeMX and also in Code, both doesn't let me modify any CRC settings)!

Please help if anybody has any suggestion!

Thank you!

Regards,

Thangaraj

Yeah, I don't recall if the L1 does or does not. The F3 did.

I've posted software and STM32 programmable methods to do the MODBUS, other CRC16, and PKZIP type variations.

In all honesty the STM32 HW implementation is POOR, I built single cycle byte wide CRC and and REED-SOLOMON implementations in the 1990's that out ran the memory circuits and MCU's.

Personally I prefer tight table software methods which can be very efficient (speed/footprint), and are also context-switch/thread-safe..

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..