
1. Introduction
HMI (Human-Machine Interface) is increasingly used in industrial automation systems and equipment due to its small size, high performance, and strong real-time capabilities. It features various displays such as letters, Chinese characters, graphics, and images, with a simple and user-friendly interface. Equipped with long-lasting membrane button keyboards, it is easy to operate. Typically, it uses a microcontroller with high integration, fast speed, high reliability, and low cost [1] as its core controller to achieve real-time rapid processing. The combination of PLC and microcontroller not only enhances the data processing capability of PLCs but also provides users with a friendly and straightforward interface. This article discusses in detail how to implement communication between a microcontroller and PLC using the Modbus communication protocol as an example.

2. Modbus Communication Protocol
The Modbus protocol is a universal language used in electronic controllers. Through this protocol, controllers can communicate with each other and with other devices via a network.
The Modbus protocol follows a master-slave principle, where only one device (the master) can initiate the transmission (query). Other devices (slaves) respond based on the data provided by the master device. The format of the master device’s query includes: device address (or broadcast, which does not require a response), function code, all data to be sent, and an error detection field. The response from the slave device includes the confirmation address, function code, any data to be returned, and an error detection field. If an error occurs during message reception or the slave device cannot execute its command, the slave will generate an error message and send it as a response.
Controllers can be set to two transmission modes: ASCII and RTU. At the same baud rate, RTU can transmit more data than ASCII, hence the use of RTU mode.
(1) Typical RTU Message Frame
The typical RTU message frame is shown in Table 1.

The address field of the RTU message frame contains 8 bits. Possible slave device addresses range from 0 to 127 (decimal). Address 0 is used as a broadcast address to allow all slave devices to recognize it. The master device selects the slave device by placing the address of the intended slave device in the address field of the message. When the slave device sends a response message, it places its own address in the response’s address field so that the master device knows which device is responding.
The function code field in the RTU message frame contains 8 bits. When the message is sent from the master device to the slave, the function code field informs the slave of the actions to be performed; when the slave responds, it uses the function code field to indicate whether the response is normal (no error) or if an error has occurred (known as an exception response, generally indicated by setting the highest bit of the function code to 1).
The data field of the message sent from the master to the slave contains additional information that the slave must use to perform the actions defined by the function code. This includes information such as the address of non-contiguous registers, the number of items to be processed, and the actual number of data bytes in the field. If no errors occur, the data field returned from the slave contains the requested data. If an error occurs, this field contains an exception code that the master device application can use to determine the next action.
When using RTU mode for character frames, the error detection field contains a 16-bit value (implemented using two 8-bit characters). The content of the error detection field is derived from the cyclic redundancy check (CRC) method applied to the message content. The CRC field is appended to the end of the message, with the low byte added first followed by the high byte.
(2) All Modbus Function Codes
The definitions of Modbus function codes are shown in Table 2.

3. Design of Common Function Communication Programs
This article introduces the design of several commonly used Modbus function programs. The author uses a microcontroller as the master device, writing a program on the microcontroller to achieve communication between the microcontroller and PLC. The microcontroller sends command information to the PLC, which automatically responds. The PLC communicates through the serial port of the microcontroller, with the program implemented in C51. The sub-functions of the program and their functionalities are as follows:
(1) Serial Port Initialization
void ProtocolInit(void)
Functionality: Sets the serial port to asynchronous communication mode 1 (1 start bit, 8 data bits, 1 stop bit); Timer/Counter 1 is set as the baud rate generator, with a communication speed of 9600 bps; enables serial interrupt and sets the serial interrupt to high priority. (2) Simple CRC Function
unsigned char Crc16(unsigned char *puchMsg, unsigned char usDataLen)
Functionality: Initializes a 16-bit register with all “1”s, then processes each consecutive 8-bit byte from the message with the current value in the register. Each 8-bit character is ORed with the register content, and the result is shifted towards the least significant bit, filling the most significant bit with 0. The LSB is extracted for detection; if LSB is 1, the register is ORed with a preset value; if LSB is 0, no action is taken. This process is repeated 8 times. After the last bit (the 8th), the next 8-bit byte is also ORed with the current value of the register. The final value in the register is the CRC value after processing all bytes in the message.
(3) Initialize Variables
void Initvar(void)
Functionality: Initializes all process variables.
(4) Serial Interrupt Service Routine
void ProtocolSerialProcess(void) interrupt 4 using 2
Functionality: Sends the command array generated by the master device upon interrupt, setting the flag after sending; receives the response array returned by the PLC, storing it in the receive array, setting the flag, and assuming the response is correct for the master to process.
(5) Read N Bit Variables (Coils)
void ProtocolRead_bit(unsigned char DeviceAddr/*PLC Address*/, unsigned char RegType/*Register Type*/, unsigned int BitAddr/*Starting Address*/, unsigned char SubAddr/*Sub Address*/, unsigned int BitNum/*Number of Bits*/)
Functionality: Forms a command array to read N bit variables based on function parameters and initiates sending. Waits for sending and receiving to complete (re-sends if not completed within timeout). Analyzes the receive array: if correct, saves the read data; if incorrect, re-sends.
(6) Write a Bit Variable
void ProtocolSetBit(unsigned char DeviceAddr/*PLC Address*/, unsigned char RegType/*Register Type*/, unsigned int BitAddr/*Address*/, unsigned char SubAddr/*Sub Address*/, unsigned int ClrSet/*Write Value “1” or “0”*/)
Functionality: Forms a command array to set a bit variable to “1” or “0” based on function parameters and initiates sending. Waits for sending and receiving to complete (re-sends if not completed within timeout). Analyzes the receive array: if correct, returns; if incorrect, re-sends.
(7) Read N Byte Variables
void ProtocolReadByte(unsigned char DeviceAddr/*PLC Address*/, unsigned char RegType/*Register Type*/, unsigned int RegAddr/*Starting Address*/, unsigned char SubAddr/*Sub Address*/, unsigned int RegNum/*Number of Variables*/)
Functionality: Forms a command array to read N byte variables based on function parameters and initiates sending. Waits for sending and receiving to complete (re-sends if not completed within timeout). Analyzes the receive array: if correct, saves the read data; if incorrect, re-sends.
(8) Write N Byte Variables
void ProtocolSetByte(unsigned char DeviceAddr/*PLC Address*/, unsigned char RegType/*Register Type*/, unsigned int RegAddr/*Starting Address*/, unsigned char SubAddr/*Sub Address*/, unsigned int RegNum/*Number of Variables*/)
Functionality: Forms a command array to write N byte variables (the values to write are read from a parameter array), initiates sending. Waits for sending and receiving to complete (re-sends if not completed within timeout). Analyzes the receive array: if correct, returns; if incorrect, re-sends.
4. Conclusion
The above program has been tested and applied in actual human-machine systems. By following similar methods, other function programs can be written to achieve different controls and operations on PLCs. Utilizing the strengths of both microcontrollers and PLCs can form a networked, intelligent industrial control system. Additionally, the entire microcontroller system program is written in C51 language, making it concise, readable, and easy to debug. The combination of microcontrollers and human-machine interfaces allows real-time display of PLC working conditions, real-time control, setting, and adjustment of PLC operation, enhancing the automation level and real-time performance of industrial control.
(Content sourced from the internet, copyright belongs to the original author)
Disclaimer: If there are copyright issues, please contact for deletion! No individual or organization assumes related legal responsibilities.
