⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 modbus.c

📁 富士通单片机MB90F387上实现MODBUS
💻 C
📖 第 1 页 / 共 5 页
字号:
  SSR1_RIE = 0;        	// 关闭接收中断,查询时要开中断
      
  ICR13 = 5;           	// 设定中断优先级
} 

#ifdef USE_UART1RX_INT
__interrupt void OnUART1RX(void)
{	
	if(Rx_Flag)
	{
			Rx_Buffer[Rx_Ptr] = SIDR1;// 接收数据存缓存     	  
			Rx_Ptr++;		    //接收计数器,又名接收指针,可以延时判断其变化情况,判断是否已经接收完毕		     		  
	}
 	SCR1_REC = 0;   //清除错误标志 	
}
#endif

#ifdef USE_UART1TX_INT
__interrupt void OnUART1TX(void)
{    
	  ++Tx_Ptr;//在send_rec()中,已将Tx_Buffer[0]发出
    if (Tx_Ptr < TxBytes) 
    {
        SODR1 = Tx_Buffer[Tx_Ptr]; // 发送数据    
    }
    else
    {
        Tx_Flag = 0;       // 发送结束标志
        SSR1_TIE = 0;      // 关闭发送中断
    }
}
#endif
/***************************************************************************************
* 作者  :冯子龙
* 日期  :200709
* 名称  :wordHByte()
* 功能  :取字的高字节
* 返回  :字的高字节
* 备注  :Tested.
		      正数的原码、反码、补码相同;负数的补码为负数绝对值的反码加1
          移位:一端的位被"挤掉" 而另一端空出的位以0 填补;如果是对有符号类型数据进行右移操作,
          则在其左端补入原来数据的符号位,即保持原来的符号不变其右端的移出位被丢弃
****************************************************************************************/
uchar wordHByte(uint uintdata)
{	
	return((uchar) (uintdata>>8));
}
/***************************************************************************************
* 作者  :冯子龙
* 日期  :200709
* 名称  :wordLByte()
* 功能  :取字的低字节
* 返回  :字的低字节
* 备注  :Tested
****************************************************************************************/
uchar wordLByte(uint uintdata)
{
	return((uchar)uintdata);
}
     			
/***************************************************************************************
* 作者  :冯子龙
* 日期  :200709
* 名称  :ASCII2RTU()
* 功能  :将Modbus中ASCII模式串,去除起始和结束符后,转换为二进制串
* 返回  :RTU串的长度(字节数)
* 备注  :
****************************************************************************************/
uint  ASCII2RTU( uchar *dest, uchar *source)
{
    uchar i;
        
    if( *source !=':') return 0;//非ASCII串
    source ++;//忽略起始符“:”
    i = 0; //初始化RTU串的长度计数器
    while ( *source != 0x0d)
    {
    	*dest = char_tab[*source];//asctohex函数每次合并两个ASCII字符成一个二进制的字节 
   		source++;
   		*dest = *dest*16 + char_tab[*source]; 		
      
      dest ++;
      source ++;
      i++;
    }
    source ++;
    if ( *source != 0x0a) return 0;//判断0x0D/0x0A结束符
    else  return i;//返回RTU串的长度(字节数)
}

/***************************************************************************************
* 作者  :冯子龙
* 日期  :200709
* 名称  :getLRC()
* 功能  :ASCII模式下的LRC校验计算
* 返回  :LRC校验和(十六进制值),在构成ASCII串时,需要拆分转换hextoasc()以添加在串尾
* 备注  :Tested
          *source 是初始的二进制串,所以可以直接LRC计算;
          LRC计算方法:把每一个需要传输的数据(按照十六进制表示与计算)按字节叠加后取反加1即可;
          计算中,不包括起始位、停止位、奇偶位(如果有的话);
          此外,无符数相加,类似循环计数的形式,自动丢弃进位; 
****************************************************************************************/
uchar getLRC(uchar *source,uint lenth)
{
    uchar tmp;
    tmp = 0;
    
    while (lenth-- )
    {
      tmp += *source++;  
    }
    return ((uchar)(-((char)tmp))); //LRC结果,比较新颖;也可以这样计算:0xFF-tmp+1
}

/***************************************************************************************
* 作者  :冯子龙
* 日期  :200709
* 名称  :RTU2ASCII()
* 功能  :将RTU模式串去除两字节的getCRC16校验值后,转换为ASCII模式串
* 返回  :无
* 备注  :注意,该算法中*source不包含getCRC16校验域(两个字节);
          转换后,增加起始位、停止位、LRC校验后才构成ASCII串
****************************************************************************************/
void RTU2ASCII( uchar *dest, uchar *source, uint lenth)
{
		uchar tmp;
    dest++; //预留起始符空间
    //下面向字符0~9以及A~F转换,不包括两个字节的getCRC16校验值
    for ( ; lenth>0; lenth--)
    { 
      tmp = *source; 
      tmp = *source & 0xf0; //取高字节
      tmp >>= 4;
      *dest = tab_char[tmp];//高字节转换成ASCII码
      dest++;
      *dest = tab_char[*source & 0x0f];//低字节转换成ASCII码
      dest++;
      source++;    
    }  
}
/***************************************************************************************
* 作者  :冯子龙
* 日期  :200709
* 名称  :construct_ascii_frm()
* 功能  :构建ASCII模式帧
* 返回  :无
* 备注  :
****************************************************************************************/
void construct_ascii_frm ( uchar *dst_buf, uchar *src_buf, uchar lenth)
{
    uchar lrc_tmp;
    
    lrc_tmp = getLRC( src_buf, lenth);//对二进制串,计算LRC校验和
    *(src_buf+lenth) = lrc_tmp;//添加LRC校验到二进制串
    lenth++;//二进制串长度增1
    *dst_buf = ':'; //添加起始符
    RTU2ASCII( dst_buf,src_buf, lenth);//由二进制串转换为可视字符串
    *(dst_buf + 2 * lenth+1) = 0x0d;    //可视字符串尾添加结束符
    *(dst_buf + 2 * lenth+2) = 0x0a;    //可视字符串尾添加结束符,最终构成了ASCII码串
}
/***************************************************************************************
* 作者  :冯子龙
* 日期  :200709
* 名称  :construct_rtu_frm()
* 功能  :构建RTU模式帧
* 返回  :无
* 备注  :
****************************************************************************************/
void construct_rtu_frm ( uchar *dst_buf,uchar *src_buf,uchar lenth)
{
    uint crc_tmp;
    
    crc_tmp = getCRC16(src_buf, lenth);    //对二进制串,计算getCRC16校验    
    *(src_buf+lenth) = crc_tmp & 0xff;  //CRC 低字节在前
    *(src_buf+lenth+1) = crc_tmp >> 8;  //CRC 高字节在后    
    lenth++;
    lenth++;

    while ( lenth--) //形成Modbus通信的RTU串
    {
      *dst_buf = *src_buf;
      dst_buf++;
      src_buf++;    
    }
}
void FaultProcess(void)
{
  	switch(Rx_Buffer[2])
		{
			 case 0x01://从设备接收到的功能码是不允许的
			 {       
			 	  //在显示终端上报警该故障(测试用) 
          display_str(0,0,"                "); 
          display_str(0,2,"                ");  
          display_str(0,4,"  Cmd Invalid   "); 
          display_str(0,6,"                ");
          wait_ms(1000);  		 	  			 	          			 	  
					break;        			 	
			 }
			 case 0x02://对从设备来说,数据地址无效
			 {			 	
			 	  //在显示终端上报警该故障(测试用)
          display_str(0,0,"                "); 
          display_str(0,2,"                ");  
          display_str(0,4,"Address Invalid "); 
          display_str(0,6,"                "); 	
          wait_ms(1000);		 	  
					break;         			 	
			 }
			 case 0x03://对从设备来说,数据值无效
			 {
					//在显示终端上报警该故障(测试用)
          display_str(0,0,"                "); 
          display_str(0,2,"                ");  
          display_str(0,4,"  Data Invalid  "); 
          display_str(0,6,"                "); 	
          wait_ms(1000);				
					break;         			 	
			 }
			 case 0x04://
			 {
			 }	
			 case 0x05://
			 {
			 }	
			 case 0x06://
			 {
			 }	
			 case 0x07://
			 {
			 }	
			 case 0x08://
			 {
			 }				 			 			 			 		 
			 case 0x09://CRC校验错误
			 {
					//在显示终端上报警该故障(测试用)
          display_str(0,0,"                "); 
          display_str(0,2,"                ");  
          display_str(0,4,"   CRC Error    "); 
          display_str(0,6,"                "); 
          wait_ms(1000);					
					break;         			 	
			 }			 			 
			 default:
			 {
			 	  //在显示终端上报警该故障(测试用)
          display_str(0,0,"                "); 
          display_str(0,2,"                ");  
          display_str(0,4,"  COMM ERROR    "); 
          display_str(0,6,"                "); 	
          wait_ms(1000); //1s	used for test	 	  
			 	  break;        			 
			 }
		}	
}
/********************************************************************************************************
* 作者  :冯子龙
* 日期  :200709
* 名称  :Data_anlysis_RTU()
* 功能  :RTU模式  接收分析
* 返回  :1,CMD_ERR
* 备注  :
*********************************************************************************************************/
char Data_anlysis_RTU( int  *dest, uchar *src,uint start_address, uint fr_lenth)
{
    uint crc_result, crc_tmp;
    uchar i, j, shift;
  
    crc_tmp = *(src + fr_lenth-2); // crc  第一字节
    crc_tmp = crc_tmp * 256 + *( src+fr_lenth-1); // CRC 值
    crc_result = getCRC16(src, fr_lenth-2); // 计算CRC 值  
    if ( crc_tmp != crc_result ) 
    {
        #ifdef operatorDEBUG
        		Rx_Buffer[2] = 0x09;//显示 crc 错误
        		FaultProcess();   
        #endif       	    	
        return CRC_ERR;// CRC 校验错误
    }    
    switch ( *(src+1) ) // 功能码
    {
      case READ_COIL://读取继电器状态 CMD01
      {
          for ( i=0; i<*( src+2); i++)
          {
                shift = 1;
                for ( j=0; j<8; j++)
                { 
                    *(dest+start_address+i*8+j) = shift & *( src+3);
                    *(src+3) >>= 1;              
                }
          }
          break;
      }
      case READ_DI://读取开关量输入 CMD02
      {
          for ( i=0; i<*( src+2); i++)
          {
                shift = 1;
                for (j=0; j<8; j ++)
                { 
                     *(dest+start_address+i*8+j) = shift & *( src+3);
                    *( src+3)>>=1;                  
                }
          }
          break;
      }
      case READ_HLD_REGs://读取多个保持寄存器 CMD03
      {
          for ( i=0; i<*( src+2); i+=2)
          {
                *(dest + start_address+ i/2)= *(src+i+3)*256 +  *(src+i+4) ;            
          }
          #ifdef operatorDEBUG
          if(*( src+2)==2)//手持操作器显示用,因为手持仅进行每次读一个字的操作。连接ABB测试用
          {
          	 para_val = *(src+3)*256 +  *(src+4);
          }  
          #endif   
          break ;
      }
      case READ_AI://读取模拟量输入 CMD04
      {
          for ( i=0; i<*( src+2); i+=2)
          {
                *(dest + start_address+ i/2) = *( src+i+3)*256 +  *( src+i+4) ;            
          } 
          break;
      }
			case SET_COIL://强制单个线圈进行 CMD05
			{
					break;	
			}
			case SET_HLD_REG://写单个寄存器 CMD06
			{
          #ifdef operatorDEBUG
				  		display_str(0,4, "    Changed!   ");//液晶提示更改完成
          #endif 				
					break;	
			}	
			case SET_COILs://强制多个线圈进行 CMD15
			{
					break;	
			}	
			case SET_HLD_REGs: //写多个寄存器 CMD16
			{
					break;	
			}	
			case READ_SET_HLD_REGs: //读写多个寄存器 CMD23
			{					

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -