STM32 Master Reads Coils and inputs

This is second tutorial in the Modbus series and today we will see how to use the STM32 as the master, which will read the coils and Discrete inputs. I have already covered reading holding and input Registers in the previous tutorial.

We will continue with where we left in the previous tutorial. You must see it first as I am going to skip few explanation in this tutorial.

Let’s start with the cubeMX configuration.

The Connection

Below is the connection diagram of the module with the STM32F103C8 controller.

As shown above, The RO pin is connected to the PA10 (UART1 RX) and DI pin is connected to the PA9 (UART1 TX).

The RE and DE are connected together with the pin PA8, which we have set as the output in the MCU.

The CubeMX configuration is shown below

The UART1 is configured to it’s default setup with the baud rate of 115200 and 8-N-1. Also I have enabled the interrupt to receive the data from the second controller.

The pin PA8 is set as output and I have renamed it to the TX_EN pin.

In this tutorial the STM32 will act as a master and since I don’t actually have a slave device, I am going to use a software in the computer that will make the computer to act as a slave device.

The communication will take place using the RS485 standard. STM32 is connected to the RS485 to TTL converter and the computer is connected via the RS485 to USB converter.

This is it for the connection, let’s see the code now.



The Slave Software

As I mentioned above I am going to use a software so the computer can act as a slave device.

The software can be downloaded from https://www.simplymodbus.ca/RTUslave.htm

Some components of the software are explained below

  • We have the Slave ID, which we can set manually.
  • The window in the Green box shows the request sent by the slave (in hex format)
  • The Blue box is the Port configuration. make sure that the master and slave are configured similarly
  • The yellow box shows the data sent by the slave (in hex format)

Since this is a slave device, it already have the memory areas (coils and registers). We can access them by clicking the DATA button (in Black box)

Below is the image of the registers and coils available

Here we are only concerned about the Red box and Green box

  • I have enabled the function code 1 and 2 i.e Reading coils and Inputs.
  • The memory size is 1 bit
  • The first coil address is 1 with the offset same as the address.
  • The first input address is 10001 with the same offset.
  • I have configured the random bit values at these address locations and the master is supposed to read these values.


Some insight into the code

First We will create a function to send the data via the UART. The modbus module need to be put in the transmit mode before sending the data.

uint8_t RxData[10];
uint8_t TxData[8];
int Data[16];
int indx = 0;

void sendData (uint8_t *data)
{
  HAL_GPIO_WritePin(TX_EN_GPIO_Port, TX_EN_Pin, GPIO_PIN_SET);
  HAL_UART_Transmit(&huart2, data, 8, 1000);
  HAL_GPIO_WritePin(TX_EN_GPIO_Port,TX_EN_Pin , GPIO_PIN_RESET);
}
  • Here I have first defined the RX and TX buffers.
  • The Data buffer is where we will store our final converted values from the slave device.
  • The sendData function will be used by the master to send the request to the slave device.
  • Here we will first pull the TX_EN pin (RE and DE Pins) High. This will put the module in the transmitter mode.
  • Then we will send the data via the UART.
  • After sending the data, we will again pull the TX_EN pin LOW, so to put the module in the receive mode.

Master makes the Request

Before we actually send the request to the slave device, we need to prepare our TxData buffer.

The request pattern sent by the master is shown in the picture below

As you can see above, the master required to send 8 bytes in total. These contains:

  • 1 Byte for the slave address
  • 1 byte for the function code
  • 2 bytes for the Starting Register Address
  • 2 Bytes for the number of Registers it wants to read
  • 2 bytes for the CRC
  TxData[0] = 0x05;  // slave address
  TxData[1] = 0x01;  // Function code for Read coils

  /*
   * The function code 0x01 means we are reading coils
   * The coil address ranges from 1 - 10000
   * The address we input can range from 0-9999 (0x00-0x270F)
   * Here 0 corresponds to the Address 1 and 9999 corresponds to 10000
   * Although we can only read 2000 coils sequentially at once
   */

  TxData[2] = 0;
  TxData[3] = 0x01;
  //The coil address will be 00000000 00000000 = 1 + 1 = 2

  TxData[4] = 0;
  TxData[5] = 10;
  // no of coils/inputs to read will be 00000000 00001010 = 10 coils = 2 Byte

  uint16_t crc = crc16(TxData, 6);
  TxData[6] = crc&0xFF;   // CRC LOW
  TxData[7] = (crc>>8)&0xFF;  // CRC HIGH

  sendData(TxData);

I have assigned the following data to TxData buffer:

  1. The Slave ID, which is set to 0x05.
  2. The function code, which is 1 for reading the coils
  3. The higher byte of the starting coil address, which is 0
  4. The Lower byte of the starting coil address, which is 1
    • This will make the starting coil address as 1, which combines with the offset 1 and the actual start address would be 2.
  1. The higher byte of the number of points, which is 0
  2. The Lower byte of the number of points, which is 10.
    • This will make the total coils that master wants to read = 10. which will be equal to 2 bytes of data.
  3. The Lower byte of the CRC
  4. The Higher byte of the CRC

These 8 bytes will be sent by the master using the function sendData.


Slave Responds to the Request

The master have transmitted the request. Now let’s see what is happening on the slave side.

Below is the picture of the slave device after receiving the request

 The Red box contains the request made by the master. You can see the data is same as what we sent from the STM32.

The Blue box contains the coils, whose values was sent by the slave device.

  • Notice that the coil address starts from 2.
  • Since the coils are 1 bit in size, they are sent in a group of 8.
  • The coils from 2 to 9 are grouped together and their data is sent as a single byte
  • The coils 10 and 11 are sent as another byte

The “Latest Response Given” window represents the actual response sent by the slave device. It contains:

  • The Slave ID of the the slave (05)
  • The function code (01)
  • The number of data bytes the slave is sending, 2 in this case.
  • The next 2 bytes are the data bytes.
  • The last 2 bytes are the CRC, with lower byte sent first.

Now let’s see how the master should handle this incoming data.


Master Receives the data

Before transmitting the request we will enable the Receive interrupt for the master. Here I am using the IDLE Line Interrupt function so that the interrupt will be triggered whenever the line goes IDLE during the receive. This would mean that one set of requested data has been received.

When the interrupt is triggered, the RxEventCallback function gets called. Here we can write the rest of the code to handle the incoming data.

HAL_UARTEx_ReceiveToIdle_IT(&huart2, RxData, 10);

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	for (int j=0; j<RxData[2]; j++)
	{
		for (int i=0; i<8; i++)
		{
			Data[indx++] = ((RxData[3+j]>>i)&1);
		}
	}
}

Inside the callback function I have written a small function to separate a byte data into the bits. Which will be then stored in the integer Data buffer.

  • The RxData[2] contains the number of actual information bytes sent by the slave.
  • These information bytes starts from the RxData[3].
  • In order to extract the bits from a byte data, we will simply shift the byte to the right and perform the & operation with 1.
  • This will extract the Least Significant Bit from the byte and save it in the integer Data buffer.
  • Then shift the Byte again and perform the & operation.
  • We will repeat this until all the bits are extracted from the byte.
  • Then the j will increment, and the byte data will change to RxData[4].
  • The same operations will be performed to extract the bits from the RxData[4].

Reading Inputs

The process of reading inputs is similar to reading coils.

The only change is the function code for reading inputs is 2.

Below is the TxData buffer sent by the master

  TxData[0] = 0x05;  // slave address
  TxData[1] = 0x02;  // Function code for Reading inputs


  /*
   * The function code 0x02 means we are reading inputs
   * The input address ranges from 10001 - 20000
   * The address master provides can range from 0-9999 (0x00-0x270F)
   * Here 0 corresponds to the Address 10001 and 9999 corresponds to 20000
   * Although we can only read 2000 inputs sequentially at once
   */

  TxData[2] = 0;
  TxData[3] = 0x01;
  //The input address will be 00000000 00000000 = 1 + 10001 = 10002

  TxData[4] = 0;
  TxData[5] = 10;
  // no of inputs to be read will be 00000000 00001010 = 10 bits = 2 Bytes

  uint16_t crc = crc16(TxData, 6);
  TxData[6] = crc&0xFF;   // CRC LOW
  TxData[7] = (crc>>8)&0xFF;  // CRC HIGH

  sendData(TxData);

I have assigned the following data to TxData buffer:

  1. The Slave ID, which is set to 0x05.
  2. The function code, which is 1 for reading the coils
  3. The higher byte of the starting coil address, which is 0
  4. The Lower byte of the starting coil address, which is 1
    • This will make the starting coil address as 1, which combines with the offset 1 and the actual start address would be 2.
  1. The higher byte of the number of points, which is 0
  2. The Lower byte of the number of points, which is 10.
    • This will make the total coils that master wants to read = 10. which will be equal to 2 bytes of data.
  3. The Lower byte of the CRC
  4. The Higher byte of the CRC

These 8 bytes will be sent by the master using the function sendData.

The slave device will send the response in the similar manner and the master (STM32) will extract the bits in the similar manner.

You can check out the video to see the working.



RESULT

Below are the images showing the Query sent by the master and the Response received by the master.

Below is the image of the final extracted bits from the received bytes


Check out the Video Below




Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

Subscribe
Notify of

0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
keyboard_arrow_up