A communication protocol commonly used in industry , A communication protocol .Modbus The agreement includes RTU,ASCII,TCP. among MODBUS-RTU Most commonly used , It's simple , It is very easy to realize on the single chip microcomputer .modbus The simple analysis of the protocol is as follows :
1, The master writes data to the slave
If the MCU receives a message, it will analyze the message and execute the corresponding processing , As the message above :
01 06 00 01 00 17 98 04
Slave address Function number Data address data CRC check

If the local address is 1 , Then the MCU receives the data and calculates it according to the data CRC Check and judge whether the data is correct , If the data is correct , The result is :
HoldDataReg[1] = 0x0017;
MODBUS The host completes a write operation to the slave data , Communication is realized .

2, Master to slave read data operation
Host read HoldDataReg[1] operation , Then the message is :
01 03 00 01 00 01 D5 CA
Slave address Function number Data address Number of data read CRC check
Then the MCU receives the data and calculates it according to the data CRC Check and judge whether the data is correct , If the data is correct , The result is : Return information to host , The returned information is also formatted :
Return to content :
01 03 02 0017 F8 4A
Slave address Function number Number of data bytes Two byte data CRC check
MODBUS The host completes a read operation of the slave data , Communication is realized .

The uploaded program teaches you by hand 51 The routine of MCU is modified , Can read / Write operation (03,06 code ), Successfully passed modbus Debug wizard test , Read write communication is normal !
Due to the new contact modbus Communication protocol , There has been a problem of communication timeout before , I found a lot of information on the Internet , There is no solution , So take out the program and share it with you . Hope to learn from each other , Common progress !

#include “rs485.h”
#include “SysTick.h”
#include “crc16.h”
#include “led.h”


* letter number name : RS485_Init
* Function function : USART2 Initialization function
* transport enter : bound: Baud rate
* transport Out : nothing

u8 USART2_RX_BUF[64]; // Receive buffer , maximum 64 byte
u8 USART2_RX_CNT=0; // Receive byte counter
u8 flagFrame=0; // Frame receiving completion flag , That is, a new frame of data is received
unsigned char regGroup[5]; //Modbus Register group , The address is 0x00~0x04
void RS485_Init(u32 bound)
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// Enable GPIOA\G Clock RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);// Enable USART2 Clock
/* to configure GPIO The mode and function of IO mouth */ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2; //TX-485 // Serial output PA2
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; // Multiplex push pull output
GPIO_Init(GPIOA,&GPIO_InitStructure); /* Initialize serial port input IO */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3; //RX-485 // serial ports PA3
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; // Analog input
GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;
//CS-485 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // Push pull output
GPIO_Init(GPIOG,&GPIO_InitStructure); //USART2 Initialization settings
USART_InitStructure.USART_BaudRate = bound;// Baud rate setting
USART_InitStructure.USART_WordLength = USART_WordLength_8b;// The length is 8 Bit data format
USART_InitStructure.USART_StopBits = USART_StopBits_1;// A stop bit
USART_InitStructure.USART_Parity = USART_Parity_No;// No parity bit
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;// Hardware free data flow control USART_InitStructure.USART_Mode =
USART_Mode_Rx | USART_Mode_Tx; // Transceiver mode USART_Init(USART2, &USART_InitStructure);
// Initialize serial port 2 USART_Cmd(USART2, ENABLE); // Enable serial port 2 USART_ClearFlag(USART2,
USART_FLAG_TC); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);// Turn on accept interrupt //Usart2
NVIC to configure NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;// Preemption Priority 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; // Subpriority 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ Channel enable
NVIC_Init(&NVIC_InitStructure); // Class according to the specified parameters VIC register , RS485_TX_EN=0; // The default is receive mode

//1ms timing

void TIM2_Init()
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);// Enable TIM4 Clock
TIM_TimeBaseInitStructure.TIM_Period=1000; // Auto load value
TIM_TimeBaseInitStructure.TIM_Prescaler=72-1; // division factor
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; // Set up count up mode
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); // Turn on timer interrupt
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); NVIC_InitStructure.NVIC_IRQChannel =
TIM2_IRQn;// Timer interrupt channel
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;// Preemption Priority
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; // Subpriority
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ Channel enable
NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM2,ENABLE); // Enable timer

// Calculate the length of data sent , And put the data in the *buf Array
u8 UartRead(u8 *buf, u8 len)
u8 i;
if(len>USART2_RX_CNT) // When the specified read length is greater than the actual received data length
len=USART2_RX_CNT; // The read length is set to the actual received data length
for(i=0;i<len;i++) // Copy the received data to the receive pointer
*buf=USART2_RX_BUF[i]; // Copy data to buf in
USART2_RX_CNT=0; // Receive counter clear
return len; // Returns the actual read length

u8 rs485_UartWrite(u8 *buf ,u8 len) // send out
u8 i=0;
GPIO_SetBits(GPIOG,GPIO_Pin_3); // Sending mode
delay_ms(3); //3MS delayed
USART_SendData(USART2,buf[i]); // adopt USARTx Peripheral sends single data
// Check the specified USART Flag setting or not , Send data vacancy flag
GPIO_ResetBits(GPIOG,GPIO_Pin_3); // Set to receive mode


// Serial port driver function , Detection of data frame reception , Scheduling function , Need to be invoked in the main loop
void UartDriver()
unsigned char i=0,cnt;
unsigned int crc;
unsigned char crch,crcl;
static u8 len;
static u8 buf[60];
if(flagFrame) // Frame receiving completion flag , That is, a new frame of data is received
flagFrame=0; // Frame receive completion flag clear
len = UartRead(buf,sizeof(buf)); // Read the received command into the buffer
if(buf[0]==0x01) // Judge whether the address is correct 0x01
crc=GetCRC16(buf,len-2); // calculation CRC Check value , get out CRC Check value
crch=crc>>8; //crc high position
crcl=crc&0xFF; //crc Low position
if((buf[len-2]==crch)&&(buf[len-1]==crcl)) // judge CRC Check whether it is correct
switch (buf[1]) // Perform the operation according to the function code
case 0x03: // Read data
if((buf[2]==0x00)&&(buf[3]<=0x05)) // Register address support 0x0000~0x0005
if(buf[3]<=0x04) { i=buf[3];// Extract register address cnt=buf[5]; // Extract the number of registers to be read buf[2]=cnt*2;
// The number of bytes of data read , Is a register *2, because modbus The registers defined are 16 position len=3; while(cnt--) { buf[len++]=0x00;
// Register high byte complement 0 buf[len++]=regGroup[i++]; // Low byte } } break; } else // When register address is not supported , Return error code
{ buf[1]=0x83; // Highest position of function code 1 buf[2]=0x02; // Set exception code to 02- Invalid address len=3; break; } case
0x06: // Write to a single register if((buf[2]==0x00)&&(buf[3]<=0x05)) // Register address support 0x0000-0x0005 {
if(buf[3]<=0x04) { i=buf[3]; // Extract register address regGroup[i]=buf[5]; // Save register data led3=0; }
len -=2; // length -2 To recalculate CRC And return to the original frame break; } else { // The register address is not supported , Return error code buf[1]=0x86;
// Highest position of function code 1 buf[2]=0x02; // Set exception code to 02- Invalid address len=3; break; } default: // Other unsupported function codes
buf[1]=0x80; // Highest position of function code 1 buf[2]=0x01; // Set exception code to 01— invalid function len=3; break; }
crc=GetCRC16(buf,len); // calculation CRC Check value buf[len++]=crc>>8; //CRC High byte
buf[len++]=crc&0xff; //CRC Low byte rs485_UartWrite(buf,len); // Send response frame } } }

void UartRxMonitor(u8 ms) // Serial port receiving monitoring
static u8 USART2_RX_BKP=0; // definition USART2_RC_BKP Comparison between the length of temporarily stored verses and the actual length
static u8 idletmr=0; // Define monitoring time
if(USART2_RX_CNT>0)// When the receive counter is greater than zero , Monitor bus idle time
if(USART2_RX_BKP!=USART2_RX_CNT) // Receive counter change , When the data is just received , Clear idle time
USART2_RX_BKP=USART2_RX_CNT; // Assignment operation , Give the actual length to USART2_RX_BKP
idletmr=0; // Reset the monitoring time
else Receive counter not changed , When the bus is idle , Accumulated idle time
// If there are more than 3.5 A pause of two bytes , The receiving device will refresh the current message and assume that the next byte is the beginning of a new data frame
if(idletmr<5) // Idle time less than 1ms Time , Continuous accumulation
idletmr +=ms;
if(idletmr>=5) // Free time up to 1ms Time , It is determined that 1 Frame received
flagFrame=1;// Set command arrival flag , Frame receiving completion flag


* letter number name : USART2_IRQHandler
* Function function : USART2 Interrupt function
* transport enter : nothing
* transport Out : nothing

void USART2_IRQHandler(void)