Modbus #6. STM32 as Slave || Write Registers

This is the 6th Tutorial in the Modbus Series, and we will continue to use the STM32 as a slave Device. This tutorial will cover how the STM32 as a slave device will send a response to the queries regarding writing a single and multiple Registers.

We will cover the function codes FC06 and FC16 in this tutorial, but from the slave prospective.

I have already covered the connection parameters in the previous tutorial, and we will continue with the same connection. in fact this is going to be the same project with a few additions in the modbusslave.c file.

I have just added the functions for writing single Register and multiple Registers in the modbusslave.c file. The rest of the project is pretty much the same as the previous one.

The Master Sotware

I am using the simply modbus master software, which can be downloaded from https://www.simplymodbus.ca/download.htm

So far we have used the master software for reading the Registers and coils from the slave device. Now we will use it for writing them.

In order to write the data into the slave device, we have to first open the “write” window in the software as shown below

Here inside the RED box if we click the write button, a new window will open. This is shown below

You might be already familiar with all the options shown in this window. We will see the use of the software later in the results section of this tutorial.



Some insight into the CODE

This is the same project in continuation, so I will only explain the parts that have been added in this particular tutorial.

Writing single Register

uint8_t writeSingleReg (void)
{
	uint16_t startAddr = ((RxData[2]<<8)|RxData[3]);  // start Register Address

	if (startAddr>49)  // The Register Address can not be more than 49 as we only have record of 50 Registers in total
	{
		modbusException(ILLEGAL_DATA_ADDRESS);   // send an exception
		return 0;
	}

	Holding_Registers_Database[startAddr] = (RxData[4]<<8)|RxData[5];

	// Prepare Response

	TxData[0] = SLAVE_ID;    // slave ID
	TxData[1] = RxData[1];   // function code
	TxData[2] = RxData[2];   // Start Addr HIGH Byte
	TxData[3] = RxData[3];   // Start Addr LOW Byte
	TxData[4] = RxData[4];   // Reg Data HIGH Byte
	TxData[5] = RxData[5];   // Reg Data LOW  Byte

	sendData(TxData, 6);  // send data... CRC will be calculated in the function itself
	return 1;   // success
}

In order to write a single register, the master simply send the Register address followed by the Data to write

  • Here we first extract the Register address by using the RxData[2] and [3].
  • If the address is more than 49, the slave will send an exception.
    • This is because I have defined the database for only 50 Registers (0-49).
  • Next we will combine the 2 bytes in RxData[4] and [5] into a single 16 bit value, and update the database with this value.
  • The response sent by the slave in this case is exactly same as the query sent by the master.
  • So we will copy the individual bytes from RxData into the TxData and send the Response.

Below you can see the query sent by the master software.

Below is the image showing the Respective register before and after the query was sent. You can see the value has been updated as per the data sent by the master.


Writing Multiple Registers

uint8_t writeHoldingRegs (void)
{
	uint16_t startAddr = ((RxData[2]<<8)|RxData[3]);  // start Register Address

	uint16_t numRegs = ((RxData[4]<<8)|RxData[5]);   // number to registers master has requested
	if ((numRegs<1)||(numRegs>123))  // maximum no. of Registers as per the PDF
	{
		modbusException (ILLEGAL_DATA_VALUE);  // send an exception
		return 0;
	}

	uint16_t endAddr = startAddr+numRegs-1;  // end Register
	if (endAddr>49)  // end Register can not be more than 49 as we only have record of 50 Registers in total
	{
		modbusException(ILLEGAL_DATA_ADDRESS);   // send an exception
		return 0;
	}

The initial part of this function is same as the other function we saw in the slave programming

  • We find the Address of the start Register by using the RxData[2] and [3].
  • Then we find the number of Registers requested by the master. This information arrives in RxData[4] and [5].
  • As per the standards, the master can write a maximum of 123 Registers at once. So if the master requests more than that, the slave will send an exception regarding ILLEGAL_DTATA_VALUE
  • Next we will calculate the the address of the last Register (endAddr). Since I have defined the database for only 50 Registers (0-49), if this end Register address exceeds 49th Register, the slave will send an exception regarding ILLEGAL_DATA_ADDRESS.

If there are no exceptions so far, we will write the data into the database and send the response.

	int indx = 7;  // we need to keep track of index in RxData
	for (int i=0; i<numRegs; i++)
	{
		Holding_Registers_Database[startAddr++] = (RxData[indx++]<<8)|RxData[indx++];
	}

	// Prepare Response

	//| SLAVE_ID | FUNCTION_CODE | Start Addr | num of Regs    | CRC     |
	//| 1 BYTE   |  1 BYTE       |  2 BYTE    | 2 BYTES      | 2 BYTES |

	TxData[0] = SLAVE_ID;    // slave ID
	TxData[1] = RxData[1];   // function code
	TxData[2] = RxData[2];   // Start Addr HIGH Byte
	TxData[3] = RxData[3];   // Start Addr LOW Byte
	TxData[4] = RxData[4];   // num of Regs HIGH Byte
	TxData[5] = RxData[5];   // num of Regs LOW Byte

	sendData(TxData, 6);  // send data... CRC will be calculated in the function itself
	return 1;   // success
}

I am using the indx variable to keep tract of how many bytes of the RxData has been read. Since the actual data starts from the RxData[7], I have defined the indx = 7.

  • Here we will combine the 2 bytes of the RxData buffer and store the 16 bit value in the database.
  • After each value gets stored, the start address will increment so that the next value can be stored in the next position in the database.
  • the indx variable keeps updating, so that we always read the next byte of the RxData buffer.
  • The for loop repeats as many times as the number of Registers requested by the master.

Once the data has been stored, the slave will send a response. The format of the response is shown below.

These are basically the first 6 bytes of the RxData buffer.

So we will copy the individual bytes from RxData into the TxData and send the Response.

Below you can see the query sent by the master software. The master wants to write 4 Registers starting from the 21.

Below is the image showing the Respective registers before and after the query was sent. You can see the value has been updated as per the data sent by the master.



Result

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

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