MODBUS migrates STM32 and configures STM32 as slave and master respectively

MODBUS migrates STM32 and configures STM32 as slave and master respectively

Recently, I have self-taught the MODBUS communication protocol and found a lot of information from the Internet. I have also made the configuration of the slave and the host respectively, and now I am cooperating with them

  1. MCU adopts STM32F103C8T6
  2. The host can read and write to the slave
  3. The host needs to use an external interrupt to realize the operation of sending data

1, Configure slave

1.1 configure the system to realize the function of 1MS timing

Initialize the system clock to 72MHZ

/******************************************************************************
  * @brief  Select external clock or internal clock and multiply
  * @param  
	RCC_PLLSource: PLL Clock source:
						Options: RCC pllsource HSE div2, RCC pllsource HSE div1
	PLLMUL: PLL Octave factor of input clock 
	          Range: RCC? Cfgr? Pllmull2 ~ 16
						PLL The clock is determined according to the clock and frequency multiplication, and the maximum internal clock is 64M
  * @retval 
 ******************************************************************************/
void SysClock_Configuration(uint32_t RCC_PLLSource, uint32_t PLLMUL)
{
	__IO uint32_t HSEStatus = 0;
	
	RCC_ClocksTypeDef get_rcc_clock; 
	
  RCC_DeInit();     // Resets the RCC clock configuration to the default reset state.
 
	if(RCC_PLLSource_HSI_Div2 != RCC_PLLSource)   //Select external clock
		{	      
		RCC_HSEConfig(RCC_HSE_ON);   			 //Turn on the external clock
		if(RCC_WaitForHSEStartUp() == SUCCESS)    //Wait for the external clock to turn on
			{
				HSEStatus = 1;			
			}
		else
			{                                           //External clock open failed
				RCC_PLLSource = RCC_PLLSource_HSI_Div2;	//Auto select internal clock
				PLLMUL = RCC_CFGR_PLLMULL16;		   //Frequency distribution to 64MHZ
				RCC_HSEConfig(RCC_HSE_OFF);	            //Turn off the external clock
				RCC_HSICmd(ENABLE);	                    //Turn on internal clock
			}
	}
	else
		{	                                       //Internal clock
		   RCC_PLLSource = RCC_PLLSource_HSI_Div2; //Auto select internal clock
		   PLLMUL = RCC_CFGR_PLLMULL16;            //Octave to 64MHZ
		   RCC_HSEConfig(RCC_HSE_OFF);	           //Turn off the external clock
		   RCC_HSICmd(ENABLE);	                   //Turn on internal clock
		
	  }
	
	RCC_HCLKConfig(RCC_SYSCLK_Div1);             //HCLK (AHB) clock is the system clock 1 Frequency Division			
	RCC_PCLK1Config(RCC_HCLK_Div2);              //PCLK (APB1) clock is HCLK clock 2 frequency division 
	RCC_PCLK2Config(RCC_HCLK_Div1);              //PCLK (APB2) clock is HCLK clock 1 Frequency Division	
 
	//When 0-24MHz, flash "latency" is used;
	//24-48MHz, take flash ﹣ latency ﹣ 1;
	//For 48-72MHz, flash "latency" is used.
	FLASH_SetLatency(FLASH_Latency_2);          //No need to configure
	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); 
 
	RCC_PLLConfig(RCC_PLLSource, PLLMUL);     	//PLL clock configuration, clock source * PLLMUL	
	 
	RCC_PLLCmd(ENABLE);                         //Turn on PLL clock and wait for PLL clock to be ready
	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);  //Select PLL clock as system clock
   
	while(RCC_GetSYSCLKSource() != 0x08);  //Wait till PLL is used as system clock source
	RCC_ClockSecuritySystemCmd(ENABLE);	   //Turn on the clock security system
  
	RCC_GetClocksFreq(&get_rcc_clock);     //During the simulation, you can see the clock of each peripheral in the structure get RCC clock
}

Configure TIM3 clock

NVIC include function

#include "USART.h"
#include "TIMER.h"

// Using TIM3, timing the MODBUS Protocol
#define MODBUS_TIM                   TIM3             
#define MODBUS_TIM_APBxClock_FUN     RCC_APB1PeriphClockCmd
#define MODBUS_TIM_CLK               RCC_APB1Periph_TIM3
#define MODBUS_TIM_IRQ               TIM3_IRQn
#define MODBUS_TIM_IRQHandler        TIM3_IRQHandler
#define MODBUS_TIM_Period            (1000-1)
#define MODBUS_TIM_Prescaler         (72-1)
/******************************************************************************
  * @brief  MODBUS_TIM_Config: TIM3 Initialization
  * @param  
  * @retval 
 ******************************************************************************/
void MODBUS_TIM_Config(void)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  MODBUS_TIM_APBxClock_FUN(MODBUS_TIM_CLK, ENABLE);      //Turn on timer clock, i.e. internal clock CK "int = 72m
  TIM_TimeBaseStructure.TIM_Period=MODBUS_TIM_Period;        //Auto reload register week value (count value)
  // Generate an update or interrupt after accumulating Tim period frequencies
  // If the prescaled frequency of clock is 71, then the clock CK ﹣ CNT = CK ﹣ int / (71 + 1) = 1m of driving counter
  TIM_TimeBaseStructure.TIM_Prescaler= MODBUS_TIM_Prescaler;
  TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;     // Clock division factor, no basic timer, no need to worry
  TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // Counter counting mode, basic timer can only count up, no setting of counting mode
  TIM_TimeBaseStructure.TIM_RepetitionCounter=0;            // The value of the repetition counter. The basic timer is not available, regardless
  TIM_TimeBaseInit(MODBUS_TIM,&TIM_TimeBaseStructure);      // Initialize timer
  TIM_ClearFlag(MODBUS_TIM,TIM_FLAG_Update);                // Clear Counter interrupt flag bit
  TIM_ITConfig(MODBUS_TIM,TIM_IT_Update,ENABLE);            // Open Counter interrupt
  TIM_Cmd(MODBUS_TIM, ENABLE);                              // Enable counter
 }
/******************************************************************************
  * @brief  ALL_NVIC_Init: Configure interrupt priority
  * @param  
  * @retval 
 ******************************************************************************/


void ALL_NVIC_Init(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);             // Set interrupt group to 1	
	NVIC_InitStructure.NVIC_IRQChannel = MODBUS_TIM_IRQ ;       // Set interrupt source
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   // Set primary priority to 1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;          // Set preemption priority to 3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

Configure the interrupt function. The timing interrupt function is placed in MODBUS? USART. C

/******************************************************************************
  * @brief  MODBUS_TIM_IRQHandler: MODBUS Timer interrupt function
  * @param  
  * @retval 
 ******************************************************************************/
void MODBUS_TIM_IRQHandler (void)       //Timer interrupt function
{
	if ( TIM_GetITStatus( MODBUS_TIM, TIM_IT_Update) != RESET )
		{
	   TIM_ClearITPendingBit(MODBUS_TIM , TIM_FLAG_Update);//Clear interrupt flag bit
		}
}

Main function

int main(void)
{ 

	SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_CFGR_PLLMULL9);//Set the system clock to 72MHZ externally and 64MHZ internally
    MODBUS_TIM_Config();
     ALL_NVIC_Init();
}

Whether the running program reaches the breakpoint is as follows:

1.2 configure the system to realize the function of serial port receiving interrupt

Use USART2: PA2 and PA3 to configure the serial port GPIO port

// Serial port 2-USART2
#define MODBUS_USART                            USART2
#define MODBUS_USART_CLK                        RCC_APB1Periph_USART2
#define MODBUS_USART_APBxClkCmd                 RCC_APB1PeriphClockCmd
#define MODBUS_USART_BAUDRATE                   9600
// USART GPIO pin macro definition
#define MODBUS_USART_GPIO_CLK                   RCC_APB2Periph_GPIOA
#define MODBUS_USART_GPIO_APBxClkCmd            RCC_APB2PeriphClockCmd

#define MODBUS_USART_TX_GPIO_PORT               GPIOA
#define MODBUS_USART_TX_GPIO_PIN                GPIO_Pin_2
#define MODBUS_USART_RX_GPIO_PORT               GPIOA
#define MODBUS_USART_RX_GPIO_PIN                GPIO_Pin_3
// USART GPIO interrupt
#define MODBUS_USART_IRQ                        USART2_IRQn
#define MODBUS_USART_IRQHandler                 USART2_IRQHandler
/******************************************************************************
* @brief  MODBUS_USART_Config:MODBUS Configure serial mode
  * @param  nothing
  * @retval nothing
 ******************************************************************************/

void MODBUS_USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	
	MODBUS_USART_GPIO_APBxClkCmd(MODBUS_USART_GPIO_CLK, ENABLE);	  // Turn on the clock of serial port GPIO	
	MODBUS_USART_APBxClkCmd(MODBUS_USART_CLK, ENABLE);	            // Turn on the clock of serial peripheral	
	// Configure GPIO of USART1 Tx as push pull multiplexing mode
  GPIO_InitStructure.GPIO_Pin = MODBUS_USART_TX_GPIO_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(MODBUS_USART_TX_GPIO_PORT, &GPIO_InitStructure);		
	// Configure GPIO of USART Rx as floating input mode
  GPIO_InitStructure.GPIO_Pin = MODBUS_USART_RX_GPIO_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(MODBUS_USART_RX_GPIO_PORT, &GPIO_InitStructure);	
	// Configure the working parameters of the serial port
	USART_InitStructure.USART_BaudRate = MODBUS_USART_BAUDRATE;	  // Configure baud rate
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	  // Configure pin data word length
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	      // Configure stop bits
	USART_InitStructure.USART_Parity = USART_Parity_No ;	        // Configure check bit
	USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;	// Configure hardware flow control
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	         // Configure working mode, send and receive together
	USART_Init(MODBUS_USART, &USART_InitStructure);	                         // Complete initialization configuration of serial port	
	USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);	                 // Enable serial port receive interrupt
	USART_Cmd(MODBUS_USART, ENABLE);	                                     // Enable serial port
}
/******************************************************************************
  * @brief  ALL_NVIC_Init: Configure interrupt priority
  * @param  
  * @retval 
 ******************************************************************************/
void ALL_NVIC_Init(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);             // Set interrupt group to 1

	NVIC_InitStructure.NVIC_IRQChannel = MODBUS_USART_IRQ ;       // Set interrupt source
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    // Set primary priority to 1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;           // Set preemption priority to 0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);	
}

Configure the serial port sending character function to prepare for subsequent data sending

/******************************************************************************
  * @brief  Usart_SendByte: Send a character 
  * @param  
  * @retval 
 ******************************************************************************/

void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
  USART_SendData(pUSARTx,ch);                                    // Send a byte of data to USART 
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);   // Waiting to send data register is empty 
}

Configure serial port receive interrupt function

/******************************************************************************
  * @brief  MODBUS_USART_IRQHandler: MODBUS Serial interrupt function
  * @param  
  * @retval 
 ******************************************************************************/

 void MODBUS_USART_IRQHandler(void)
{
  uint8_t ucTemp;
  if (USART_GetITStatus(MODBUS_USART,USART_IT_RXNE)!=RESET)  //Judge whether there is data reception
  {} 
}

Main function

int main(void)
{ 

	SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_CFGR_PLLMULL9);//Set the system clock to 72MHZ externally and 64MHZ internally
	MODBUS_USART_Config();
    ALL_NVIC_Init();

}

Configured as 9600BT/s with serial port assistant to send data

1.3 configure the system to realize the function of 3.5T delay after receiving data

At the same time of configuring the delay 3.5T function, configure the Modbus structure to save the relevant data, and establish the MODBUS.c and MODBUS.h

Nested Models

#include "stm32f10x.h"
#include "modbusCRC.h"
#include "USART.h"

typedef struct
{
 unsigned char   myadd;          //Address of the device
 unsigned char   rcbuf[100];     //MODBUS receive buffer
 unsigned int    timout;         //Data intermittent time of MODbus	
 unsigned char   recount;        //Number of data received by MODbus port
 unsigned char   timrun;         //Flag of whether the MODbus timer is timed
 unsigned char   reflag;         //Flag to receive one frame of data
 unsigned char   Sendbuf[100];   //MODbus transmit buffer	
}MODBUS;

Configure serial receive function

/******************************************************************************
  * @brief  MODBUS_USART_IRQHandler: MODBUS Serial interrupt function
  * @param  
  * @retval 
 ******************************************************************************/

 void MODBUS_USART_IRQHandler(void)
{
  uint8_t ucTemp;
  if (USART_GetITStatus(MODBUS_USART,USART_IT_RXNE)!=RESET)  //Judge whether there is data reception
	{
		  ucTemp = USART_ReceiveData( MODBUS_USART ); //Save one byte received		
		  modbus.rcbuf[modbus.recount++]=ucTemp;     //Save to the receiving buffer of MODBUS		
		  modbus.timout=0;			  //In the process of serial port receiving data, the timer does not time		
		  if(modbus.recount==1)   //Receive the first byte of a frame of data sent by the host
			  {
			    modbus.timrun=1;   	//Start timing
			  }
  }
}

Configure timer functions

/******************************************************************************
  * @brief  MODBUS_TIM_IRQHandler: MODBUS Timer interrupt function
  * @param  
  * @retval 
 ******************************************************************************/
void MODBUS_TIM_IRQHandler (void)   //Timer interrupt function
{
	if ( TIM_GetITStatus( MODBUS_TIM, TIM_IT_Update) != RESET )
		{
	    if(modbus.timrun!=0)        //Whether the data sent by the serial port is over? Let the timer time when it is over
		  {
	     modbus.timout++;           //Timer timing 1 ms and start time recording
	   TIM_ClearITPendingBit(MODBUS_TIM , TIM_FLAG_Update);//Clear interrupt flag bit
		}
}

Combine the serial port and timer to run the program, send the assistant with the serial port assistant, and check whether the debugging program enters the breakpoint

1.4. Configure the system to process data

After configuration, we have basically configured the timing of modbus, but the data has not been processed. Next, we have processed the data and verified it with MODBUS debugging assistant

Where MODBUS includes

#include "modbusCRC.h"
#include "USART.h"

Where MODBUS? USART contains

#include "USART.h"
#include "MODBUS.h"
#include "TIMER.h"

Define register in MODBUS

unsigned int Reg[]={0x0000,   //Value in the device register
           0x1111,
           0x2222,
           0x3333,
           0x4444,
           0x5555,
           0x0006,
           0x0007,
           0x0008,
           0x0009,
           0x000A,	
          };	

The following functions are included in MODBUS. They are not explained one by one. You can read them in detail. The processing function does not send error codes. In fact, the function codes are enough

/******************************************************************************
  * @brief  Modbud_fun3: 3 Function code processing - the host should read the registers of the slave
  * @param  
  * @retval 
 ******************************************************************************/
void Modbud_fun3(void)                           
{
  u16 Regadd;
	u16 Reglen;
	u16 byte;
	u16 i,j;
	u16 crc;
	Regadd=modbus.rcbuf[2]*256+modbus.rcbuf[3];  //Get the first address of the register to read
	Reglen=modbus.rcbuf[4]*256+modbus.rcbuf[5];  //Get the number of registers to read
	i=0;
	
	modbus.Sendbuf[i++]=modbus.myadd;           //Send this device address
  modbus.Sendbuf[i++]=0x03;                   //Send function code      
  byte=Reglen*2;                              //Number of data bytes to return
  //modbus.Sendbuf[i++]=byte/256;  
	modbus.Sendbuf[i++]=byte%256;               //Number of bytes of data sent to return         
	
	for(j=0;j<Reglen;j++)
	{
	  modbus.Sendbuf[i++]=Reg[Regadd+j]/256;    //Send high order of bytes of read data 
		modbus.Sendbuf[i++]=Reg[Regadd+j]%256;  //Send the low order of bytes of read data 
	}
	crc=crc16(modbus.Sendbuf,i);                //CRC check
	modbus.Sendbuf[i++]=crc/256;                //Send high value of CRC
	modbus.Sendbuf[i++]=crc%256;                //Send low value of CRC
	
	for(j=0;j<i;j++)                            //Send one by one through serial port
   Usart_SendByte( MODBUS_USART,modbus.Sendbuf[j]);
}
/******************************************************************************
  * @brief  Modbud_fun6: 6 Function code processing, write register
  * @param  
  * @retval 
 ******************************************************************************/

void Modbud_fun6()                             
{
  unsigned int Regadd;
	unsigned int val;
	unsigned int i,crc,j;
	i=0;
  Regadd=modbus.rcbuf[2]*256+modbus.rcbuf[3];  //Get the address to be modified 
	val=modbus.rcbuf[4]*256+modbus.rcbuf[5];   //Modified value
	Reg[Regadd]=val;                           //Modify the corresponding registers of the device
	//Here is the response host
	modbus.Sendbuf[i++]=modbus.myadd;          //Send this device address
  modbus.Sendbuf[i++]=0x06;                    //Send function code 
  modbus.Sendbuf[i++]=Regadd/256;              //Send modify address high
	modbus.Sendbuf[i++]=Regadd%256;            //Send modify address low
	modbus.Sendbuf[i++]=val/256;               //Send modified value high
	modbus.Sendbuf[i++]=val%256;               //Send modified value low
	crc=crc16(modbus.Sendbuf,i);               //Check address, function code, address and data
	modbus.Sendbuf[i++]=crc/256;               //Send high value of CRC
	modbus.Sendbuf[i++]=crc%256;               //Send low value of CRC
	
	for(j=0;j<i;j++)                             //Send one by one through serial port
   Usart_SendByte( MODBUS_USART,modbus.Sendbuf[j]);
}
/******************************************************************************
  * @brief  Mosbus_Event: MODBUS Data processing program
  * @param  
  * @retval 
 ******************************************************************************/

void Mosbus_Event(void)
{
	unsigned int crc;
	unsigned int rccrc;
	unsigned char i=0;
  if(modbus.reflag==0)      //No MODbus packets received
	{
	  return ;                //No processing instructions received, continue to wait for the next data
	}
	while(modbus.rcbuf[i++]!=modbus.myadd);
		
	crc= crc16(&modbus.rcbuf[0], modbus.recount-2);                             //Calculation check code
  rccrc=modbus.rcbuf[modbus.recount-2]*256 + modbus.rcbuf[modbus.recount-1];  //Received check code
  if(crc ==  rccrc)                                                           //Packet complies with CRC verification rules
	{ 
	  if(modbus.rcbuf[0] == modbus.myadd)         //Confirm whether the data package is sent to the device 
		{
		  switch(modbus.rcbuf[1])                   //Analysis function code
			{
			  case 0:     break;
			  case 1:     break;
		    case 2:     break;
		    case 3:     Modbud_fun3();    break;   //Function code 3 processing
		    case 4:     break;
		    case 5:     break;
		    case 6:     Modbud_fun6();     break;  //6 function code processing
	      case 7:     break;			
        //....				
			}
		}
		else if(modbus.rcbuf[0] == 0)             //Broadcast address, do not process
		{
		}
	}                                          //Packet does not conform to CRC verification rules
	modbus.recount=0;                          //Clear cache count
  modbus.reflag=0;	                         //Restart execution of processing functions
}

The following functions are included in MODBUS? USART

/******************************************************************************
  * @brief  MODBUS_Init: MODBUS Initialization
  * @param  
  * @retval 
 ******************************************************************************/
void MODBUS_Init(void)
{
	MODBUS_TIM_Config();
	MODBUS_USART_Config();
	modbus.myadd=4;        //Initialize the address of the slave device
	modbus.timrun=0;       //Initialize MODbus timer stop timing
}
/******************************************************************************
  * @brief  MODBUS_USART_IRQHandler: MODBUS Serial interrupt function
  * @param  
  * @retval 
 ******************************************************************************/
 void MODBUS_USART_IRQHandler(void)
{
  uint8_t ucTemp;
  if (USART_GetITStatus(MODBUS_USART,USART_IT_RXNE)!=RESET)  //Judge whether there is data reception
	{
			ucTemp = USART_ReceiveData( MODBUS_USART ); //Save one byte received
		
		  modbus.rcbuf[modbus.recount++]=ucTemp;     //Save to the receiving buffer of MODBUS
		
		  modbus.timout=0;			  //In the process of serial port receiving data, the timer does not time
		
		  if(modbus.recount==1)   //Receive the first byte of a frame of data sent by the host
			  {
			    modbus.timrun=1;   	//Start timing
			  }
  }
}
/******************************************************************************
  * @brief  MODBUS_TIM_IRQHandler: MODBUS Timer interrupt function
  * @param  
  * @retval 
 ******************************************************************************/
void MODBUS_TIM_IRQHandler (void)   //Timer interrupt function
{
	if ( TIM_GetITStatus( MODBUS_TIM, TIM_IT_Update) != RESET )
		{
	    if(modbus.timrun!=0)        //Whether the data sent by the serial port is over? Let the timer time when it is over
		  {
	     modbus.timout++;           //Timer timing 1 ms and start time recording
			 if(modbus.timout>=8)       //The interval time has reached the time, assuming 8T, the actual 3.5T is enough
				{
					modbus.timrun=0;        //Off timer -- stop timer
					modbus.reflag=1;        //Receive a frame of data and start processing the data
				}
			}
	   TIM_ClearITPendingBit(MODBUS_TIM , TIM_FLAG_Update);//Clear interrupt flag bit
		}
}

Main function

int main(void)
{ 
	SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_CFGR_PLLMULL9);//Set the system clock to 72MHZ externally and 64MHZ internally
	MODBUS_Init();
    ALL_NVIC_Init();
	while(1)
	Mosbus_Event();
}

Run the MODBUS debugging assistant, configure 9600TB/s, and set the address to 4

Send: 04 06 00 00 01 48 5F

Write 1 to register 0

Back: 04 06 00 00 01 48 5F

Sent: 04 03 00 00 01 84 5F

Indicates the value of the register after reading address 0

Back: 04 03 02 00 01 B5 84

2, Configure host

2.1 configure the system to realize the function of 1MS timing

Above configurationClick jump

2.2 configure the system to realize the function of serial port receiving interrupt

Above configurationClick jump

2.3 configure a USART1 and external interrupt function

USART1 configuration is used to view data. This serial port is only used to view data without configuring receive interrupt

// Serial port 1-USART1
#define DEBUG1_USART                            USART1
#define DEBUG1_USART_CLK                        RCC_APB2Periph_USART1
#define DEBUG1_USART_APBxClkCmd                 RCC_APB2PeriphClockCmd
#define DEBUG1_USART_BAUDRATE                   9600
// USART GPIO pin macro definition
#define DEBUG1_USART_GPIO_CLK                   RCC_APB2Periph_GPIOA
#define DEBUG1_USART_GPIO_APBxClkCmd            RCC_APB2PeriphClockCmd
#define DEBUG1_USART_TX_GPIO_PORT               GPIOA
#define DEBUG1_USART_TX_GPIO_PIN                GPIO_Pin_9
#define DEBUG1_USART_RX_GPIO_PORT               GPIOA
#define DEBUG1_USART_RX_GPIO_PIN                GPIO_Pin_10
/******************************************************************************
* @brief  DEBUG_USART_Init:Configure serial port debugging
  * @param  nothing
  * @retval nothing
 ******************************************************************************/
void DEBUG_USART_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	DEBUG1_USART_GPIO_APBxClkCmd(DEBUG1_USART_GPIO_CLK, ENABLE);	  // Turn on the clock of serial port GPIO	
	DEBUG1_USART_APBxClkCmd(DEBUG1_USART_CLK, ENABLE);	            // Turn on the clock of serial peripheral	
	// Configure GPIO of USART1 Tx as push pull multiplexing mode
  GPIO_InitStructure.GPIO_Pin = DEBUG1_USART_TX_GPIO_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(DEBUG1_USART_TX_GPIO_PORT, &GPIO_InitStructure);		
	// Configure GPIO of USART Rx as floating input mode
  GPIO_InitStructure.GPIO_Pin = DEBUG1_USART_RX_GPIO_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(DEBUG1_USART_RX_GPIO_PORT, &GPIO_InitStructure);	
	// Configure the working parameters of the serial port
	USART_InitStructure.USART_BaudRate = DEBUG1_USART_BAUDRATE;	  // Configure baud rate
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	  // Configure pin data word length
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	      // Configure stop bits
	USART_InitStructure.USART_Parity = USART_Parity_No ;	      // Configure check bit
	USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;	// Configure hardware flow control
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	       // Configure working mode, send and receive together
	USART_Init(DEBUG1_USART, &USART_InitStructure);	                       // Complete initialization configuration of serial port	
	USART_ITConfig(DEBUG1_USART, USART_IT_RXNE, ENABLE);	               // Enable serial port receive interrupt
	USART_Cmd(DEBUG1_USART, ENABLE);	                                   // Enable serial port
}

Configure external interrupt and use the function of PA0 key

//Pin definition
#define KEY_UP_INT_GPIO_PORT           GPIOA
#define KEY_UP_INT_GPIO_CLK           (RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO)
#define KEY_UP_INT_GPIO_PIN            GPIO_Pin_0
#define KEY_UP_INT_EXTI_PORTSOURCE     GPIO_PortSourceGPIOA
#define KEY_UP_INT_EXTI_PINSOURCE      GPIO_PinSource0

#define KEY_UP_INT_EXTI_LINE           EXTI_Line0
#define KEY_UP_INT_EXTI_IRQ            EXTI0_IRQn
#define KEY_UP_IRQHandler              EXTI0_IRQHandler
void EXTI_Key_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;

	RCC_APB2PeriphClockCmd(KEY_UP_INT_GPIO_CLK,ENABLE);    //Turn on the clock of key GPIO port
 /*--------------------------KEY1 Configuration---------------------*/
	GPIO_InitStructure.GPIO_Pin = KEY_UP_INT_GPIO_PIN;     // Select the GPIO for the key 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  // It is configured as pull-down input, because other pins of floating input interfere with it greatly 
	GPIO_Init(KEY_UP_INT_GPIO_PORT, &GPIO_InitStructure);
	GPIO_EXTILineConfig(KEY_UP_INT_EXTI_PORTSOURCE,KEY_UP_INT_EXTI_PINSOURCE);// Select EXTI's signal source 	
	EXTI_InitStructure.EXTI_Line = KEY_UP_INT_EXTI_LINE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;   // EXTI is interrupt mode 
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//Rising edge interrupt
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;             //Enable interruption 
	EXTI_Init(&EXTI_InitStructure);
}

The main point here is to use the pull-down method instead of the floating input method

Configure external interrupt function in MODBUS? USART

/******************************************************************************
  * @brief  MODBUS_TIM_IRQHandler: MODBUS Timer interrupt function
  * @param  
  * @retval 
 ******************************************************************************/
void MODBUS_TIM_IRQHandler (void)   //Timer interrupt function
{
	if ( TIM_GetITStatus( MODBUS_TIM, TIM_IT_Update) != RESET )
		{
	    if(modbus.timrun!=0)        //Whether the data sent by the serial port is over? Let the timer time when it is over
		  {
	     modbus.timout++;           //Timer timing 1 ms and start time recording
			 if(modbus.timout>=8)       //The interval time has reached the time, assuming 8T, the actual 3.5T is enough
				{
					modbus.timrun=0;        //Off timer -- stop timer
					modbus.reflag=1;        //Receive a frame of data and start processing the data
				}
			}
	   TIM_ClearITPendingBit(MODBUS_TIM , TIM_FLAG_Update);//Clear interrupt flag bit
		}
}

 /******************************************************************************
  * @brief  KEY_UP_IRQHandler: External data sending method
  * @param  
  * @retval 
 ******************************************************************************/
void KEY_UP_IRQHandler(void)
{

	if (EXTI_GetITStatus(KEY_UP_INT_EXTI_LINE) != RESET)   //Make sure the EXTI Line interrupt is generated
		{
		
	  Usart_SendByte( DEBUG1_USART,modbus.myadd);
		Usart_SendByte( DEBUG1_USART,0x03);
		Usart_SendByte( DEBUG1_USART,0x00);
		Usart_SendByte( DEBUG1_USART,0x00);
		Usart_SendByte( DEBUG1_USART,0x00);
		Usart_SendByte( DEBUG1_USART,0x02);
	  Usart_SendByte( DEBUG1_USART,0xC4);
		Usart_SendByte( DEBUG1_USART,0x5E);	
			
		Usart_SendByte( MODBUS_USART,modbus.myadd);
		Usart_SendByte( MODBUS_USART,0x03);
		Usart_SendByte( MODBUS_USART,0x00);
		Usart_SendByte( MODBUS_USART,0x00);
		Usart_SendByte( MODBUS_USART,0x00);
		Usart_SendByte( MODBUS_USART,0x02);
	  Usart_SendByte( MODBUS_USART,0xC4);
		Usart_SendByte( MODBUS_USART,0x5E);	

		EXTI_ClearITPendingBit(KEY_UP_INT_EXTI_LINE);        //Clear interrupt flag bit
		}
}

Open serial port USART2 to send data and use USART1 to receive data

Data processing of write instruction returned from slave

Send: 04 06 00 00 01 48 5F

Write 1 to the address of register 00 00

Back: 00 01

Data processing of read instruction returned from slave

Sent: 04 03 02 00 01 B5 84

Read 1 to address d of register 00 00

Back: 00 01

2.4 configure the system to realize the function of 3.5T delay after receiving data

Above configurationClick jump

2.5. Configure the system to process data

This place is different from slave

/******************************************************************************
  * @brief  Modbud_fun3: 3 Function code processing No. -- after the host reads the register of the slave, it saves the information to the register of the specified length of the host
  * @param  
  * @retval 
 ******************************************************************************/
void Modbud_fun3(void)                           
{
  unsigned int Regadd=0,i=0,j,Reglen;                                   
	Reglen=modbus.rcbuf[2];                         //Get the number of registers read
	for(i=0;i<Reglen;i++)                           //Process the read data and save it to the corresponding registers of the host
	{                                               //Data is saved from the first register to the specified number
	Reg[Regadd]=modbus.rcbuf[3+i]*256;              //Save data high register
	Usart_SendByte( DEBUG1_USART,Reg[Regadd]/256);  //Send to another serial port for display	
  i++;		                                        //Data increase, processing low order
    Reg[Regadd]=Reg[Regadd]+modbus.rcbuf[i+3];      //Send to another serial port for display	
	Usart_SendByte( DEBUG1_USART,Reg[Regadd]%256);  //Save data low register
	Regadd++;		                               //After processing the high and low data, proceed to the next data
	}
}
/******************************************************************************
  * @brief  Modbud_fun6: 6 No. 2 function code processing, the host writes data to the register specified by the slave, and returns the modified data after writing
  * @param  
  * @retval 
 ******************************************************************************/
void Modbud_fun6()                             
{
  unsigned int Regadd,i=0,crc,j,val;             
	Regadd=modbus.rcbuf[2]*256+modbus.rcbuf[3];        //Get the address of the register to change
	Reg[Regadd]=modbus.rcbuf[4]*256+modbus.rcbuf[5];   //Save the modified data from the slave to the master register
  Usart_SendByte( DEBUG1_USART,Reg[Regadd]/256);     //Another serial port displays the modified data
	Usart_SendByte( DEBUG1_USART,Reg[Regadd]%256);     //Another serial port displays the modified data
}

The main function is as follows

int main(void)
{ 
	SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_CFGR_PLLMULL9);//Set the system clock to 72MHZ externally and 64MHZ internally
	MODBUS_Init();
  ALL_NVIC_Init();
	Usart_SendByte( DEBUG1_USART,0x32);
	CODE_End();
	while(1)
	Mosbus_Event();
}

3, Master sends command to slave

3.1. The host sends the write command to the slave

Connect the serial port of the host to the serial port of the slave, press the key of the host, and check whether there is data reception at another serial port of the host

Key interrupt configuration function

		Usart_SendByte( DEBUG1_USART,modbus.myadd);
		Usart_SendByte( DEBUG1_USART,0x06);
		Usart_SendByte( DEBUG1_USART,0x00);
		Usart_SendByte( DEBUG1_USART,0x00);
		Usart_SendByte( DEBUG1_USART,0xFF);
		Usart_SendByte( DEBUG1_USART,0xFF);
	    Usart_SendByte( DEBUG1_USART,0x88);
		Usart_SendByte( DEBUG1_USART,0x2F);			

		Usart_SendByte( MODBUS_USART,modbus.myadd);
		Usart_SendByte( MODBUS_USART,0x06);
		Usart_SendByte( MODBUS_USART,0x00);
		Usart_SendByte( MODBUS_USART,0x00);
		Usart_SendByte( MODBUS_USART,0xFF);
		Usart_SendByte( MODBUS_USART,0xFF);
	    Usart_SendByte( MODBUS_USART,0x88);
	    Usart_SendByte( MODBUS_USART,0x2F);	

Equivalent to sending data from the host to the slave: 0x04 0x06 0x00 0x00 0xFF 0xFF 0x88

Return: 0xFF 0xFF

3.2. The host sends the read command to the slave

Connect the serial port of the host to the serial port of the slave, press the key of the host, and check whether there is data reception at another serial port of the host

	    Usart_SendByte( MODBUS_USART,modbus.myadd);
		Usart_SendByte( MODBUS_USART,0x03);
		Usart_SendByte( MODBUS_USART,0x00);
		Usart_SendByte( MODBUS_USART,0x00);
		Usart_SendByte( MODBUS_USART,0x00);
		Usart_SendByte( MODBUS_USART,0x01);
	    Usart_SendByte( MODBUS_USART,0x84);
		Usart_SendByte( MODBUS_USART,0x5F);	
		
	    Usart_SendByte( DEBUG1_USART,modbus.myadd);
		Usart_SendByte( DEBUG1_USART,0x03);
		Usart_SendByte( DEBUG1_USART,0x00);
		Usart_SendByte( DEBUG1_USART,0x00);
		Usart_SendByte( DEBUG1_USART,0x00);
	 	Usart_SendByte( DEBUG1_USART,0x01);
	    Usart_SendByte( DEBUG1_USART,0x84);
		Usart_SendByte( DEBUG1_USART,0x5F);	

Equivalent to sending data from the host to the slave: 0x04 0x06 0x00 0x00 0x01 0x84 0x5F

Return: 0x00 0x00

The 00 return is due to the reduction of the original value after the 3.1 step of the debugging process.

Posted on Wed, 08 Apr 2020 00:10:18 -0700 by MrPen