基于MODBUS协议,通过485通信发送指令实现上位机控制继电器(公司项目实践)


1、解析通信协议

1.物理接口

串行通信接口RS485,采用半双工异步串行通讯。

2.通信波特率

上级监控与电池巡检单元之间采用9600bps通信波特率。

3.数据格式

1位起始位,8位数据位,1位停止位,校验位:无校验。

4.通信方式

上级监控与该产品之间以交换数据包的方式进行通信。上级监控向该产品轮询数据,该产品一直处于被动状态,只有上级监控要求该产品上报数据,该产品才能发送数据

5.数据帧格式

上级监控(HOST)与该产品(SLAVE)之间交换的数据信息分为两种类型:

(1)命令信息:由HOST发出到SLAVE的命令;

(2)返回信息:由SLAVE返回到HOST的响应。

数据帧格式:

发送顺序号

1

2

3

4

地址

功能码

数据

CRC校验码

长度

8 Bit

 8 Bit

N * 8Bit

16 Bit

地址:

地址ADR为该产品的识别标志,一个数据总线RS485上不可挂相同地址的该产品。编址从C0H—EFH,

共80个。

功能码

该产品支持03(读数据) ,06(写单个寄存器) ,10(写多个寄存器)。

数据

上报或下设的数据,按寄存器(数据地址)进行发送,每一个寄存器由两个字节组成。

CRC校验码

CRC(Cyclical Redundancy Check)对地址、功能码和数据进行校验,由两字节组成,CRC由传输设备生成,附加在数据帧中,如果由接收到数据计算出来的校验和与附加在数据后的校验和不一致,则有错误发生。采用CRC16(ModbusRTU)格式,发送时低位在前,高位在后。

本篇只介绍其中10功能码

设置多个数据-功能码0X10

设置命令帧格式

字段值

字段说明

C0H

地址 (拨码开关的值+0X70)

10H

功能码10

00H

设置数据地址高字节

设置数据地址低字节

02H

00H

数据个数高字节

数据个数低字节,

02H

04H

字节个数。等于数据个数的2倍

第1个数据高字节

第1个数据低字节

第2个数据高字节

第2个数据低字节

第n个数据高字节

第n个数据低字节

CRCHi

CRC高字节

CRC低字节

CRCLo

响应帧格式

字段值

字段说明

C0H

地址 (拨码开关的值+0XC0)

10H

功能码10

00H

设置数据地址高字节

设置数据地址低字节

02H

00H

数据个数高字节

数据个数低字节,

02H

CRCHi

CRC高字节

CRC低字节

CRCLo

每次最多可以设置16个数据

寄存地址

数据

0x20

第1个开关合闸信号

信号时长2S

0x21

第2个开关合闸信号

信号时长2S

0x22

第3个开关合闸信号

信号时长2S

0x23

第4个开关合闸信号

信号时长2S

0x24

第5个开关合闸信号

信号时长2S

0x25

第6个开关合闸信号

信号时长2S

0x26

第7个开关合闸信号

信号时长2S

0x27

第8个开关合闸信号

信号时长2S

.

NONE

NONE

.

NONE

NONE

0x30

第1个开关分闸信号

信号时长2S

0x31

第2个开关分闸信号

信号时长2S

0x32

第3个开关分闸信号

信号时长2S

0x33

第4个开关分闸信号

信号时长2S

0x34

第5个开关分闸信号

信号时长2S

0x35

第6个开关分闸信号

信号时长2S

0x36

第7个开关分闸信号

信号时长2S

0x37

第8个开关分闸信号

信号时长2S

2、配置485通信的接收与发送

串口初始化及接收和发送函数的编写如下

#include "rs485.h"	 
#include "SysTick.h"


//接收缓存区 	
u8 RS485_RX_BUF[64];  	//接收缓冲,最大64个字节.
//接收到的数据长度
u8 RS485_RX_CNT=0,Sendflag;  

  
									 
//初始化IO 串口2
//pclk1:PCLK1时钟频率(Mhz)
//bound:波特率	  
void RS485_Init(u32 bound)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
 	NVIC_InitTypeDef NVIC_InitStructure;
 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟
	
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;	//PA2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);  

//	RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,ENABLE);//复位串口2
//	RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,DISABLE);//停止复位
 
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据长度
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;///奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
	USART_Init(USART2, &USART_InitStructure); ; //初始化串口
  
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //使能串口2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级2级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
	NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
 
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断
//	USART_ClearFlag(USART2,USART_IT_RXNE);
	USART_Cmd(USART2, ENABLE);                    //使能串口 

}

void USART2_IRQHandler(void)
{
	u8 res;	    
 
 	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
	{	 
	 			 
		res =USART_ReceiveData(USART2); 	//读取接收到的数据
		if(RS485_RX_CNT<64)
		{
			RS485_RX_BUF[RS485_RX_CNT]=res;		//记录接收到的值
			RS485_RX_CNT++;						//接收数据增加1 
		} 
		Sendflag=1;
	}  											 
} 


void RS485_Send_Data(u8 *buf,u8 len)
{
	u8 t;
  	for(t=0;t<len;t++)		
	{		   
		while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);	  
		USART_SendData(USART2,buf[t]);
	}	 
 
	while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);		
	RS485_RX_CNT=0;
	Sendflag=0;	
}
//RS485查询接收到的数据
//buf:接收缓存首地址
//len:读到的数据长度
void RS485_Receive_Data(u8 *buf,u8 *len)
{
	u8 rxlen=RS485_RX_CNT;
	u8 i=0;
	*len=0;				//默认为0
	delay_ms(2);		//等待2ms,连续超过2ms没有接收到一个数据,则认为接收结束
	if(rxlen==RS485_RX_CNT&&rxlen)//接收到了数据,且接收完成了
	{
		for(i=0;i<rxlen;i++)
		{
			buf[i]=RS485_RX_BUF[i];	
		}		
		*len=RS485_RX_CNT;	//记录本次数据长度
		RS485_RX_CNT=0;		//清零
	}
}

3、用宏定义对位带操作及引脚进行封装,便于编写控制继电器相关函数

#ifndef _COMMUNICATION_H
#define _COMMUNICATION_H

#define RLYA_PIN	GPIO_Pin_12|GPIO_Pin_11|GPIO_Pin_10|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_6|GPIO_Pin_4|GPIO_Pin_5
#define RLYB_PIN	GPIO_Pin_15|GPIO_Pin_14|GPIO_Pin_12|GPIO_Pin_11|GPIO_Pin_10|GPIO_Pin_1|GPIO_Pin_0


#define RLY1ON  PAout(12)=1
#define RLY1OFF PAout(12)=0
#define RLY2ON  PAout(11)=1
#define RLY2OFF PAout(11)=0
#define RLY3ON  PAout(10)=1
#define RLY3OFF PAout(10)=0
#define RLY4ON  PAout(9)=1
#define RLY4OFF PAout(9)=0
#define RLY5ON  PAout(8)=1
#define RLY5OFF PAout(8)=0
#define RLY6ON  PBout(15)=1
#define RLY6OFF PBout(15)=0
#define RLY7ON  PBout(14)=1
#define RLY7OFF PBout(14)=0
#define RLY8ON  PBout(12)=1
#define RLY8OFF PBout(12)=0
#define RLY9ON  PBout(11)=1
#define RLY9OFF PBout(11)=0
#define RLY10ON  PBout(10)=1
#define RLY10OFF PBout(10)=0
#define RLY11ON  PBout(1)=1
#define RLY11OFF PBout(1)=0
#define RLY12ON  PBout(0)=1
#define RLY12OFF PBout(0)=0
#define RLY13ON  PAout(7)=1
#define RLY13OFF PAout(7)=0
#define RLY14ON  PAout(6)=1
#define RLY14OFF PAout(6)=0
#define RLY15ON  PAout(4)=1
#define RLY15OFF PAout(4)=0
#define RLY16ON  PAout(5)=1
#define RLY16OFF PAout(5)=0
extern unsigned char ACT_FLAG;
void RLY_Init(void);
void IN_state_Init(void);
//extern void IN_state(char num1,char sta1);
extern void RLY_action(char num,char sta);

extern unsigned short currlevel[100];

void readcurrlevel(void);

#endif

4、结合产品电路框架图,对需要控制的继电器所对应的端口及管脚进行初始化

代码如下(示例):

#include "communication.h"
#include "system.h"

unsigned short currlevel[100];

void RLY_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);

	GPIO_InitStructure.GPIO_Pin=RLYA_PIN;	   //选择你要设置的IO口
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//下拉输入  
	GPIO_Init(GPIOA,&GPIO_InitStructure);		  /* 初始化GPIO */
	
	GPIO_InitStructure.GPIO_Pin=RLYB_PIN;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//下拉输入  
	GPIO_Init(GPIOB,&GPIO_InitStructure);		  /* 初始化GPIO */
	GPIO_ResetBits(GPIOA,RLYA_PIN);
	GPIO_ResetBits(GPIOB,RLYB_PIN);
}


void RLY_action(char num,char sta)
{
		
	switch(num)
			{
				case 0:
				{
				if(sta==1) RLY1ON;else RLY1OFF;break;		
				}
				case 1:
				{
				if(sta==1) RLY3ON;else RLY3OFF;break;		
				}
				case 2:
				{
				if(sta==1) RLY5ON;else RLY5OFF;break;		
				}
				case 3:
				{
				if(sta==1) RLY7ON;else RLY7OFF;break;
				}
				case 4:
				{
				if(sta==1) RLY9ON;else RLY9OFF;break;		
				}
				case 5:
				{
				if(sta==1) RLY11ON;else RLY11OFF;break;		
				}
				case 6:
				{
				if(sta==1) RLY13ON;else RLY13OFF;break;		
				}
				case 7:
				{
				if(sta==1) RLY15ON;else RLY15OFF;break;		
				}
				case 8:
				{
				if(sta==1) RLY2ON;else RLY2OFF;break;		
				}
				case 9:
				{
				if(sta==1) RLY4ON;else RLY4OFF;break;		
				}			
				case 10:
				{
				if(sta==1) RLY6ON;else RLY6OFF;break;		
				}		
				case 11:
				{
				if(sta==1) RLY8ON;else RLY8OFF;break;		
				}
				case 12:
				{
				if(sta==1) RLY10ON;else RLY10OFF;break;		
				}	
				case 13:
				{
				if(sta==1) RLY12ON;else RLY12OFF;break;		
				}	
				case 14:
				{
				if(sta==1) RLY14ON;else RLY14OFF;break;		
				}	
				case 15:
				{
				if(sta==1) RLY16ON;else RLY16OFF;break;		
				}				
				
			}
}

5、使用定时器中断控制指示灯闪烁(减少cpu的占用)并对继电器打开动作做延时处理

#include "time.h"
#include "led.h"
#include "SysTick.h"



unsigned char FLAG_BUF[16]={2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},ACT_FLAG2=0;


/*******************************************************************************
* 函 数 名         : TIM4_Init
* 函数功能		   : TIM4初始化函数
* 输    入         : per:重装载值
					 psc:分频系数
* 输    出         : 无
*******************************************************************************/
void TIM4_Init(u16 per,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//使能TIM4时钟
	
	TIM_TimeBaseInitStructure.TIM_Period=per;   //自动装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
	
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //开启定时器中断
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//定时器中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		//子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	
	
	TIM_Cmd(TIM4,ENABLE); //使能定时器	
}






/*******************************************************************************
* 函 数 名         : TIM4_IRQHandler
* 函数功能		   : TIM4中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void TIM4_IRQHandler(void)
{
	static u32 time1=0,time2=0,time3=0,time4=0,time5=0,time6=0,time7=0,time8=0,time9=0,time10=0,time11=0,time12=0,time13=0,time14=0,time15=0,time16=0,time17=0;
	if(TIM_GetITStatus(TIM4,TIM_IT_Update))
	{
		time1++;
		if(time1%10==0)
		LED1=!LED1;
		if(FLAG_BUF[0]==1)
		{
			ACT_FLAG2=1;
			time2++;
			if(time2%40==0)
				FLAG_BUF[0]=0;
		}
		if(FLAG_BUF[1]==1)
		{
			ACT_FLAG2=1;
			time3++;
			if(time3%40==0)
				FLAG_BUF[1]=0;
		}
		if(FLAG_BUF[2]==1)
		{
			ACT_FLAG2=1;
			time4++;
			if(time4%40==0)
				FLAG_BUF[2]=0;
		}
		if(FLAG_BUF[3]==1)
		{
			ACT_FLAG2=1;
			time5++;
			if(time5%40==0)
				FLAG_BUF[3]=0;
		}
		if(FLAG_BUF[4]==1)
		{
			ACT_FLAG2=1;
			time6++;
			if(time6%40==0)
				FLAG_BUF[4]=0;
		}
		if(FLAG_BUF[5]==1)
		{
			ACT_FLAG2=1;
			time7++;
			if(time7%40==0)
				FLAG_BUF[5]=0;
		}
		if(FLAG_BUF[6]==1)
		{
			ACT_FLAG2=1;
			time8++;
			if(time8%40==0)
				FLAG_BUF[6]=0;
		}
		if(FLAG_BUF[7]==1)
		{
			ACT_FLAG2=1;
			time9++;
			if(time9%40==0)
				FLAG_BUF[7]=0;
		}
		if(FLAG_BUF[8]==1)
		{
			ACT_FLAG2=1;
			time10++;
			if(time10%40==0)
				FLAG_BUF[8]=0;
		}
		if(FLAG_BUF[9]==1)
		{
			ACT_FLAG2=1;
			time11++;
			if(time11%40==0)
				FLAG_BUF[9]=0;
		}
		if(FLAG_BUF[10]==1)
		{
			ACT_FLAG2=1;
			time12++;
			if(time12%40==0)
				FLAG_BUF[10]=0;
		}
		if(FLAG_BUF[11]==1)
		{
			ACT_FLAG2=1;
			time13++;
			if(time13%40==0)
				FLAG_BUF[11]=0;
		}
		if(FLAG_BUF[12]==1)
		{
			ACT_FLAG2=1;
			time14++;
			if(time14%40==0)
				FLAG_BUF[12]=0;
		}
		if(FLAG_BUF[13]==1)
		{
			ACT_FLAG2=1;
			time15++;
			if(time15%40==0)
				FLAG_BUF[13]=0;
		}
		if(FLAG_BUF[14]==1)
		{
			ACT_FLAG2=1;
			time16++;
			if(time16%40==0)
				FLAG_BUF[14]=0;
		}
		if(FLAG_BUF[15]==1)
		{
			ACT_FLAG2=1;
			time17++;
			if(time17%40==0)
				FLAG_BUF[15]=0;
		}
	}
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update);	
}

6、主函数的逻辑结构设计与编写

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "rs485.h"
#include "crc16.h"
#include "communication.h"
#include "time.h"


int main()
{
	u8 m=0,n=0,p=0;
	u8 rs485buf[100];   //接收缓存数组
	unsigned char TX_BUFF[100],inputdata[64],on_flagbuf[64];   //发送缓存数组
	unsigned char ACT_FLAG=0;
	unsigned short currlevelchange[100];  //存放改变开关状态后的电位情况
	u8 len=0;
	unsigned int CRCcode;
	unsigned short j=0,k,i=0;
	unsigned short RegNum,RegStart,NumH,NumL;
//	unsigned char	Addr=0;
	
	SysTick_Init(64);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
	LED_Init();
	RLY_Init();
	IN_state_Init();
	RS485_Init(9600);
	TIM4_Init(10,36000-1);  
//	RLY_action(0,1);      //打开继电器1
	
	while(1)
	{	
		if(ACT_FLAG2==0)
		RS485_Receive_Data(rs485buf,&len);
		readcurrlevel();
		if(rs485buf[0]==0XC0)//KMT协议  rs485buf[0]==0XC0
		{
			NumH = rs485buf[2];
			NumL = rs485buf[3];
			RegStart = NumH*256+NumL;//起始地址
						
			NumH = rs485buf[4];
			NumL = rs485buf[5];
			RegNum = NumH*256+NumL;//请求数量

			for(m=0;m<2;m++)
			TX_BUFF[m]=rs485buf[m];
        if(rs485buf[1]==0x10)
			{
				CRCcode = GetCRC16(&rs485buf[0],(RegNum*2+7));
				NumH = CRCcode>>8;
				NumL = CRCcode&0xff;
				if(rs485buf[RegNum*2+7]==NumH&&rs485buf[RegNum*2+8]==NumL)
				{
//					if(ACT_FLAG2==0)
//					{
					if((RegStart+RegNum)<0x29)
					{
						for(k=0;k<16;k++)
						{
							if(FLAG_BUF[k]!=2)
								break;
						}
						if(k==16)
						{	
						for(k=RegStart-32;k<(RegStart-32+RegNum);k++)
						{
							j+=2;
							inputdata[j+6]=rs485buf[j+6]&0xff;
							if(inputdata[j+6]==1)
							{
								
								RLY_action(k,1); 
//								on_flagbuf[p]=k;
//								p++;
//								FLAG_BUF[k]=1;
					
							}
						}
						}
						for(k=0;k<16;k++)
						{
							if(FLAG_BUF[k]!=2)
								break;
						}
						if(k==16)
						{
						for(k=RegStart-32;k<(RegStart-32+RegNum);k++)
						{
							i+=2;
							inputdata[i+6]=rs485buf[i+6]&0xff;
							if(inputdata[i+6]==1)
							{
								on_flagbuf[p]=k;
								p++;
								FLAG_BUF[k]=1;
							}
						
						}
						}
						readcurrlevel();
						for(n=0;n<8;n++)
						{
							currlevelchange[n]=currlevel[n];
						}
						if(FLAG_BUF[on_flagbuf[0]]==0)
						for(n=0;n<p;n++)
						{
							if(FLAG_BUF[on_flagbuf[n]]==0)         	
								{
								RLY_action(on_flagbuf[n],0);      
								FLAG_BUF[on_flagbuf[n]]=2;
								}	
						}
					}
						
					
					if((RegStart+RegNum)>0x30)        
//					if(0)
					{	
						for(k=0;k<16;k++)
						{
							if(FLAG_BUF[k]!=2)
								break;
						}
						if(k==16)
						{	
						for(k=RegStart-40;k<(RegStart-40+RegNum);k++)
						{
							j+=2;
							inputdata[j+6]=rs485buf[j+6]&0xff;
							if(inputdata[j+6]==1)
							{
								
								RLY_action(k,1); 
//								on_flagbuf[p]=k;
//								p++;
//								FLAG_BUF[k]=1;
					
							}
						}
						}
						for(k=0;k<16;k++)
						{
							if(FLAG_BUF[k]!=2)
								break;
						}
						if(k==16)
						{
						for(k=RegStart-40;k<(RegStart-40+RegNum);k++)
						{
							i+=2;
							inputdata[i+6]=rs485buf[i+6]&0xff;
							if(inputdata[i+6]==1)
							{
								on_flagbuf[p]=k;
								p++;
								FLAG_BUF[k]=1;
							}
						
						}
						
						}
						readcurrlevel();
						for(n=0;n<8;n++)
						{
							currlevelchange[n]=currlevel[n];
						}
						if(FLAG_BUF[on_flagbuf[0]]==0)
						for(n=0;n<p;n++)
						{
							if(FLAG_BUF[on_flagbuf[n]]==0)         	
								{
								RLY_action(on_flagbuf[n],0);      
								FLAG_BUF[on_flagbuf[n]]=2;                                 
								}	
						}
					}
					if(n==p)				              
					{
						ACT_FLAG=1;                    
						p=0;
						j=0;
						i=0;
						ACT_FLAG2=0;
					}
					for(m=0;m<6;m++)
						TX_BUFF[m]=rs485buf[m];
					CRCcode = GetCRC16(&TX_BUFF[0] ,6);
					NumH = CRCcode >> 8;
					NumL = CRCcode & 0xff;
					TX_BUFF[6] = NumH;
					TX_BUFF[7] = NumL;
					if(Sendflag)
						RS485_Send_Data(TX_BUFF,8);
					if(ACT_FLAG==1)
					{
						for(k=0;k<(RegNum*2+8);k++)
						{
							rs485buf[k]=0;
						}
						ACT_FLAG=0;
					}
				                          

		}
		}


总结

以上就是今天要讲的内容,本文仅仅介绍了10功能码的程序设计。为保证产品能多次执行相同或不同的上位机指令,主函数中设置了多个闭环,用来保证后来指令不对当前指令造成干扰。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值