用户工具

站点工具


icore4t_45
银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
V1.0 2020-9-26 zgf 初次建立

实验四十五:Modbus TCP通信实验——电源监控

一、 实验目的与意义

  1. 了解Modbus TCP通讯协议;
  2. 了解Modbus TCP与TCP协议的关系;
  3. 掌握Modbus Poll的使用方法;
  4. 掌握KEIL MDK 集成开发环境使用方法。

二、 实验设备及平台

  1. iCore4T 双核心板及底板;
  2. JLINK(或相同功能)仿真器;
  3. 以太网通讯线缆;
  4. Keil MDK 开发平台;
  5. 装有WIN XP(及更高版本)系统的计算机。

三、 实验原理

1.Modbus _TCP简介

  • Modbus通信协议由Modicon公司于1979年发明的,是全球最早用于工业现场的总线规约。它是一种主从通讯方式,采用服务器和客户机的模式记性通讯。Modbus通信协议具有多个变种,其具有支持串口(主要是RS-485总线),以太网多个版本,其中最著名的是Modbus RTU,Modbus ASCII和Modbus TCP三种。本实验主要针对Modbus TCP进行的一个数据通讯实验。
  • Modbus TCP协议基于TCP协议实现,其数据帧包含于TCP数据之中,本文以数据读取及其应答为例对Modbus TCP进行读取。数据读取请求报文格式如表1所示。
  • 表1 Modbus TCP数据读取请求报文
事务处理标识 协议标识 报文长度 设备标识 功能码 起始地址 寄存器数量
2byte 2byte 2byte 1byte 1byte 2 byte 2 byte
  • 事务处理标识符:两个字节,一般每次通讯后将加1,从而区别不同的通讯数据报文。
  • 协议标识符:两个字节,Modbus TCP标识符为0x0000。
  • 数据长度:两个字节,标识后面的数据量的大小,数据长度以字节为单位。
  • 设备标识:用以标识连接在串行线或者网络上的远程服务端的地址。
  • 功能码:一般为读取的寄存器的类型。
  • 起始地址:寄存器的起始地址。
  • 寄存器数量:要读取寄存器的数量。
  • 数据读取应答报文格式如表2所示。
  • 表2 Modbus TCP数据读取应答报文
事务处理标识 协议标识 报文长度 设备标识 功能码 数据长度 数据
2byte 2byte 2byte 1byte 1byte 1byte n byte
  • 事务处理标识符:两个字节,与请求标识一致。
  • 协议标识符:两个字节,与请求标识一致。
  • 数据长度:两个字节,指示后面的数据量的大小,数据长度以字节为单位。
  • 设备标识:用以标识连接在串行线或者网络上的远程服务端的地址。
  • 功能码:一般为读取的寄存器的地址,与请求标识一致。
  • 数据长度:传输的数据长度。
  • 数据:寄存器的数据。

四、 实验程序

  • 本实验主要基于TCP通讯和ADC电源监控两部分实现,本文对这两个方面不做过多描述。

1.主函数

int main(void)
{
  /* USER CODE BEGIN 1 */
	int cnt;
	short data;
  /* USER CODE END 1 */
  /*MCU Configuration------------------------------------*/ 
 	HAL_Init();
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
  /* Configure the system clock */
 	SystemClock_Config();
  /* USER CODE BEGIN SysInit */
	i2c.initialize();
	axp152.initialize();
	axp152.set_dcdc1(3500);//[ARM & FPGA BK1/2/6 &OTHER]
	axp152.set_dcdc2(1200);//[FPGA INT & PLL D]
	axp152.set_aldo1(2500);//[FPGA PLL A]
	axp152.set_dcdc4(3300);//[POWER_OUTPUT]
	axp152.set_dcdc3(3300);//[FPGA BK4][Adjustable]
	axp152.set_aldo2(3300);//[FPGA BK3][Adjustable]
	axp152.set_dldo1(3300);//[FPGA BK7][Adjustable]
	axp152.set_dldo2(3300);//[FPGA BK5][Adjustable]
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
MX_ADC1_Init();
  MX_ADC3_Init();
  MX_ETH_Init();
 
  /* USER CODE BEGIN 2 */
//LWIP初始化
NETMPU_Config();
lwip.initialize();
eth_tcps.initialize();
 /* USER CODE END 2 */
 
 /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
		if((cnt ++ / 800000) % 2){
			LED_RED_ON;
		}else{
			LED_RED_OFF;
		}
		lwip.periodic_handle();
	  //根据定时器计数标志,每隔一秒,读取一次ADC的值
		if(adc_read_flag ==1)
		{
			adc_read_flag = 0;
			my_adc.read(0);
                my_adc.read_mux();
		}
 
		//将ADC采集的计算值,放大1000倍便于观察数据的正确性。计算公式请参考adc部分例程。
		data = my_adc.value[0]*6000;
		hold_reg[0] = data >> 8;
		hold_reg[1] = data & 0xff;
 
		data = my_adc.value[1] * 2000;
		hold_reg[2] = data >> 8;
		hold_reg[3] = data & 0xff;
 
		data = my_adc.value[2]*2000;
		hold_reg[4] = data >> 8;
		hold_reg[5] = data & 0xff;
 
		data = my_adc.value[3]*2000;
		hold_reg[6] = data >> 8;
		hold_reg[7] = data & 0xff;
 
		data = my_adc.value[4]*2000;
		hold_reg[8] = data >> 8;
		hold_reg[9] = data & 0xff;
 
		data = my_adc.value[5]*500;··
		hold_reg[10] = data >> 8;
		hold_reg[11] = data & 0xff;
 
		data = my_adc.value[6]*1000;
		hold_reg[12] = data >> 8;
		hold_reg[13] = data & 0xff;
 
		data = my_adc.value[7]*2000;
		hold_reg[14] = data >> 8;
		hold_reg[15] = data & 0xff;
 
		data = my_adc.value[8]*2000;
		hold_reg[16] = data >> 8;
		hold_reg[17] = data & 0xff;
 
		if(eth_tcps.receive_ok_flag == 1){
			eth_tcps.receive_ok_flag = 0;
			modbus_tcp.process(eth_tcps.receive_buffer);
		}
  }
  /* USER CODE END 3 */
}

2.TCP数据发送函数

  • 基于TCP中的服务器代码数据发送函数进行函数封装,设计一个发送指定数量数据的函数。
int send(struct tcp_pcb *tpcb,unsigned char *data,int len)
{
	int ret_err;
	struct tcp_server_struct *es; 
	memcpy(eth_tcps.send_buffer,data,len);
	es = tpcb->callback_arg;
	if(es != NULL){  				 //连接处于空闲可以发送数据
	es->p = pbuf_alloc(PBUF_TRANSPORT,len,PBUF_POOL);//申请内存 
		pbuf_take(es->p,(char*)eth_tcps.send_buffer,len);//将eth_tcps.send_buffer[]中的数据拷贝到es->p_tx中
		tcp_server_senddata(tpcb,es);	        //将eth_tcps.send_buffer[]里面复制给pbuf的数据发送出去
		if(es->p)pbuf_free(es->p);		//释放内存
		ret_err=ERR_OK;
	}else{
		tcp_abort(tpcb);			//终止连接,删除pcb控制块
		ret_err=ERR_ABRT;
	}
	return ret_err;
}

3.Modbus TCP协议处理

  • Modbus TCP的协议处理由多个子函数组成,函数代码如下。
int err;
char discrete_input[32] = {0x55,0x55,0x55};         
char coil[32] = {0x55,0x55,0x55};                   
char input_reg[20] = {0,1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10};  
char hold_reg[512] = {0x01,0x02,0x03};
 
//--------------------------- Function --------------------------//
static int process(unsigned char *modbus_recvbuf)
{	
	unsigned char receive_buffer_temp[100];
	memcpy(receive_buffer_temp,modbus_recvbuf,12);
     memset(modbus_recvbuf,0,12);
	mb_rsq_pdu(receive_buffer_temp,12);
 
     return 0;
}
static int mb_rsq_pdu(unsigned char *receive_buffer_temp,int counter_temp)
 {
   if(receive_buffer_temp[0 + 6] == 0x01){
     switch(receive_buffer_temp[1 + 6]){
       case 1:
               function_1(receive_buffer_temp);
               break;
       case 2:
               function_2(receive_buffer_temp);
               break;
       case 3:
               function_3(receive_buffer_temp);
               break;
       case 4:
               function_4(receive_buffer_temp);
               break;
       case 5:
               function_5(receive_buffer_temp,counter_temp);
               break;
       case 6:
               function_6(receive_buffer_temp,counter_temp);
               break;
       default :
               mb_excep_rsq_pdu(receive_buffer_temp,1);
               break;
     }
  }else if(receive_buffer_temp[0 + 6] == 0){
      broadcast(receive_buffer_temp);
    }
  return 0;
 }
static int function_1(unsigned char *receive_buffer_temp)
{
  int i;
  unsigned short cnt;
  unsigned short coil_num;
  unsigned short start_address;
int temp = 0;
  start_address = (receive_buffer_temp[2 + 6] << 8) | receive_buffer_temp[3 + 6];
  coil_num = receive_buffer_temp[4 + 6] << 8| receive_buffer_temp[5 + 6];
  if((start_address + coil_num) > 255){
    mb_excep_rsq_pdu(receive_buffer_temp,2);
    return 1;
  }
  receive_buffer_temp[2 + 6] = ((coil_num % 8 )? (coil_num / 8 + 1) : (coil_num / 8));
  cnt = receive_buffer_temp[2 + 6] + 5 - 2;
  if(coil_num % 8){
    if(coil_num < 8){
      for(i = 0;i < coil_num;i ++)temp |= 1 << i;
      receive_buffer_temp[3 + 6] = ((coil[start_address / 8]) >> (start_address % 8) | (coil[start_address / 8 + 1]) << (8 - (start_address % 
8))) & temp;
    }else {
      for(i = 0;i < receive_buffer_temp[2 + 6] - 1;i++)receive_buffer_temp[3 + i + 6] = (coil[i + start_address / 8]) >> (start_address % 8) | (coil[i + start_address / 8 + 1]) << (8 - (start_address % 8));
      receive_buffer_temp[3 + i + 6] = (coil[i + start_address / 8] << ((8 - (coil_num % 8 - start_address % 8) % 8)) & 0xff) >> (8 - (coil_num % 8));
    }
  }else {
      for(i = 0;i < receive_buffer_temp[2 + 6];i++)receive_buffer_temp[3 + i + 6] = (coil[i + start_address / 8]) >> (start_address % 8) | (coil[i + start_address / 8 + 1]) << (8 - (start_address % 8));
  }
	receive_buffer_temp[4] = (cnt & 0xff00) >> 8;
	receive_buffer_temp[5] = (cnt & 0x00ff);
	cnt = cnt + 6;
	eth_tcps.send(eth_tcps.tcppcbnew,receive_buffer_temp,cnt);
  return 0;
}
static int function_2(unsigned char *receive_buffer_temp)
{
  int i;
  unsigned short cnt;
  unsigned short discrete_num;
  unsigned short start_address;
int temp = 0;
 
  start_address = (receive_buffer_temp[2 + 6] << 8) | receive_buffer_temp[3 + 6];
 
  discrete_num = receive_buffer_temp[4 + 6] << 8| receive_buffer_temp[5 + 6];
 
  if((start_address + discrete_num) > 255){
    mb_excep_rsq_pdu(receive_buffer_temp,2);
    return 1;
  }
  receive_buffer_temp[2 + 6] = ((discrete_num % 8 )? (discrete_num / 8 + 1) : (discrete_num / 8));
  cnt = receive_buffer_temp[2 + 6] + 5 - 2;
  if(discrete_num % 8){
    if(discrete_num < 8){
      for(i = 0;i < discrete_num;i ++)temp |= 1 << i;
      receive_buffer_temp[3 + 6] = ((discrete_input[start_address / 8]) >> (start_address % 8) | (discrete_input[start_address / 8 + 1]) << (8 - (start_address % 8))) & temp;
    }else {
      for(i = 0;i < receive_buffer_temp[2 + 6] - 1;i++)receive_buffer_temp[3 + i + 6] = (discrete_input[i + start_address / 8]) >> (start_address % 8) | (discrete_input[i + start_address / 8 + 1]) << (8 - (start_address % 8));
      receive_buffer_temp[3 + i + 6] = (discrete_input[i + start_address / 8] << ((8 - (discrete_num % 8 - start_address % 8) % 8)) & 0xff) >> (8 - (discrete_num % 8));
    }
  }else {
      for(i = 0;i < receive_buffer_temp[2 + 6];i++)receive_buffer_temp[3 + i + 6] = (discrete_input[i + start_address / 8]) >> (start_address % 8) | (discrete_input[i + start_address / 8 + 1]) << (8 - (start_address % 8));
  }
 
	receive_buffer_temp[4] = (cnt & 0xff00) >> 8;
	receive_buffer_temp[5] = (cnt & 0x00ff);
	cnt = cnt + 6;
 
	eth_tcps.send(eth_tcps.tcppcbnew,receive_buffer_temp,cnt);
  return 0;
}
static int function_3(unsigned char *receive_buffer_temp)
{  
  int i;
  int cnt = 0;
  unsigned short int start_address;
 
  start_address = (receive_buffer_temp[2 + 6] << 8) | receive_buffer_temp[3 + 6];
 
  receive_buffer_temp[2 + 6] = receive_buffer_temp[5 + 6] * 2;
 
  if(receive_buffer_temp[2 + 6] > 48){ 
    mb_excep_rsq_pdu(receive_buffer_temp,2);
    return 1;
  }
 
  if((start_address * 2 + receive_buffer_temp[2]) > 512){
    mb_excep_rsq_pdu(receive_buffer_temp,2);
    return 1;
  }
  cnt = receive_buffer_temp[2 + 6] + 5 - 2;
  for(i = 0;i < receive_buffer_temp[2 + 6];i++){
		receive_buffer_temp[i + 3 + 6] = hold_reg[start_address * 2 + i];
	}
 
	receive_buffer_temp[4] = (cnt & 0xff00) >> 8;
	receive_buffer_temp[5] = (cnt & 0x00ff);
	cnt = cnt + 6;
 
	eth_tcps.send(eth_tcps.tcppcbnew,receive_buffer_temp,cnt);
  return 0;
}
static int function_4(unsigned char *receive_buffer_temp)
{
  int i;
  int cnt;
  unsigned short int start_address;
 
  start_address = (receive_buffer_temp[2 + 6] << 8) | receive_buffer_temp[3 + 6];
 
  receive_buffer_temp[2 + 6] = receive_buffer_temp[5 + 6] * 2;
 
  if((start_address * 2 + receive_buffer_temp[2 + 6]) > 20){
    mb_excep_rsq_pdu(receive_buffer_temp,2);
    return 1;
  }
 
  cnt = receive_buffer_temp[2 + 6] + 5 - 2;
  for(i = 0;i < receive_buffer_temp[2 + 6];i++)receive_buffer_temp[i + 3 + 6] = input_reg[start_address * 2 + i];
 
	receive_buffer_temp[4] = (cnt & 0xff00) >> 8;
	receive_buffer_temp[5] = (cnt & 0x00ff);
	cnt = cnt + 6;
 
	eth_tcps.send(eth_tcps.tcppcbnew,receive_buffer_temp,cnt);
  return 0;
}
static int function_5(unsigned char *receive_buffer_temp,int counter_temp)
{
  unsigned short start_address;
  start_address = (receive_buffer_temp[2 + 6] << 8) | receive_buffer_temp[3 + 6];
 
  if(start_address > 255){
    mb_excep_rsq_pdu(receive_buffer_temp,2);
    return 1;
  }
 
  if((receive_buffer_temp[4 + 6] == 0xff) && (receive_buffer_temp[5 + 6] == 0x00)){
     coil[(start_address / 8)] |= 1 << start_address % 8;
  }else if((receive_buffer_temp[4 + 6] == 0x00) && (receive_buffer_temp[5 + 6] == 0x00)){
     coil[(start_address / 8)] &= ~(1 << start_address % 8);
  }else {
     mb_excep_rsq_pdu(receive_buffer_temp,3);
  }
 
	receive_buffer_temp[4] = (6 & 0xff00) >> 8;
	receive_buffer_temp[5] = (6 & 0x00ff);
 
	eth_tcps.send(eth_tcps.tcppcbnew,receive_buffer_temp,counter_temp);
  return 0;
}
static int function_6(unsigned char *receive_buffer_temp,int counter_temp)
{
  unsigned short start_address;
 
  start_address = (receive_buffer_temp[2 + 6] << 8) | receive_buffer_temp[3 + 6];
 
  if(start_address > 255){
    mb_excep_rsq_pdu(receive_buffer_temp,2);
    return 1;
  }
 
  hold_reg[start_address * 2] = receive_buffer_temp[4 + 6];
  hold_reg[start_address * 2 + 1] = receive_buffer_temp[5 + 6];
 
receive_buffer_temp[4] = (6 & 0xff00) >> 8;
receive_buffer_temp[5] = (6 & 0x00ff);
	eth_tcps.send(eth_tcps.tcppcbnew,receive_buffer_temp,counter_temp);
  return 0;
}
static int mb_excep_rsq_pdu(unsigned char *receive_buffer_temp,int error_code)
 {
   receive_buffer_temp[1 + 6] |= 0x80; 
   switch(error_code) {
     case 1:
             receive_buffer_temp[2 + 6] = 1;
             break;
     case 2:
             receive_buffer_temp[2 + 6] = 2;
             break;
     case 3:
             receive_buffer_temp[2 + 6] = 3;
             break;
     case 4:
             receive_buffer_temp[2 + 6] = 4;
             break;
     default :
             break;
   }
	receive_buffer_temp[4] = (3 & 0xff00) >> 8;
	receive_buffer_temp[5] = (3 & 0x00ff);
 
	 eth_tcps.send(eth_tcps.tcppcbnew,receive_buffer_temp,9);
	return 0;
 }
static int broadcast(unsigned char *receive_buffer_temp)
 {
   int start_address;
   switch(receive_buffer_temp[1 + 6]){
     case 5: 
             start_address = (receive_buffer_temp[2 + 6] << 8) | receive_buffer_temp[3 + 6];
             if(start_address > 255){
                return 1;
             }
             if((receive_buffer_temp[4 + 6] == 0xff) && (receive_buffer_temp[5 + 6] == 0x00)){
                coil[(start_address / 8)] |= 1 << start_address % 8;
             }else if((receive_buffer_temp[4 + 6] == 0x00) && 	
(receive_buffer_temp[5 + 6] == 0x00)){
                coil[(start_address / 8)] &= ~(1 << start_address % 8);
             }
             break;
    case 6:	
            start_address = (receive_buffer_temp[2 + 6] << 8) | receive_buffer_temp[3 + 6];
            if(start_address > 255){
               return 1;
            }
            hold_reg[start_address * 2] = receive_buffer_temp[4 + 6];
            hold_reg[start_address * 2 + 1] = receive_buffer_temp[5 + 6]; 
            break;
    } 
   return 0;
 }

五、 实验步骤

  1. 把仿真器与iCore4T的SWD调试口相连(直接相连或者通过转接器相连);
  2. 把iCore4T通过以太网线与计算机相连,为iCore4T供电;
  3. 设置电脑IP地址(见附录1);
  4. 打开Keil MDK 开发环境,并打开本实验工程;
  5. 烧写程序到iCore4T上;
  6. 打开Modbus Poll软件建立连接(见附录2),读取hold寄存器中的数据值,观察核心板返回的数据值。

六、 实验现象

  • Modbus Poll软件可看到核心板电源数据的检测值。

附录1:电脑IP设置

  • 打开控制面板→网络和 Internet→网络连接,选择对应的网卡,右键点击属性,设置IPV4的IP地址,如图所示:

附录2:Modbus Poll软件建立连接

  • 打开Modbus Poll,点击Connet,设置协议类型、IP地址和端口号,点击OK开始进行连接。

icore4t_45.txt · 最后更改: 2022/04/01 10:54 由 sean