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 ind
x
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