[应用相关] stm32 modbus RTU程序

[复制链接]
12971|51
 楼主| chenqiang10 发表于 2018-7-29 20:55 | 显示全部楼层 |阅读模式
#include "RS485.h"
#include "CRC.h"

u32 RS485_Baudrate=9600;//通讯波特率
u8 RS485_Parity=0;//0无校验;1奇校验;2偶校验
u8 RS485_Addr=1;//从机地址
u16 RS485_Frame_Distance=4;//数据帧最小间隔(ms),超过此时间则认为是下一帧

u8 RS485_RX_BUFF[2048];//接收缓冲区2048字节
u16 RS485_RX_CNT=0;//接收计数器
u8 RS485_FrameFlag=0;//帧结束标记
u8 RS485_TX_BUFF[2048];//发送缓冲区
u16 RS485_TX_CNT=0;//发送计数器

//Modbus寄存器和单片机寄存器的映射关系
vu32 *Modbus_InputIO[100];//输入开关量寄存器指针(这里使用的是位带操作)
vu32 *Modbus_OutputIO[100];//输出开关量寄存器指针(这里使用的是位带操作)
u16 *Modbus_HoldReg[1000];//保持寄存器指针
u32 testData1=256,testData2=512;


 楼主| chenqiang10 发表于 2018-7-29 20:56 | 显示全部楼层
modbus寄存器映射
  1. void Modbus_RegMap(void)
  2. {
  3.         //输入开关量寄存器指针指向
  4.         Modbus_InputIO[0]=(vu32*)&PEin(4);//KEY0
  5.         Modbus_InputIO[1]=(vu32*)&PEin(3);//KEY1
  6.         Modbus_InputIO[2]=(vu32*)&PEin(2);//KEY2
  7.         Modbus_InputIO[3]=(vu32*)&PAin(0);//KEY3
  8.         
  9.         //输出开关量寄存器指针指向
  10.         Modbus_OutputIO[0]=(vu32*)&PBout(5);//LED0
  11.         Modbus_OutputIO[1]=(vu32*)&PEout(5);//LED1
  12.         
  13.         //保持寄存器指针指向
  14.         Modbus_HoldReg[0]=(u16*)&testData1;//测试数据1
  15.         Modbus_HoldReg[1]=((u16*)&testData1)+1;//测试数据1
  16.         Modbus_HoldReg[2]=(u16*)&testData2;//测试数据2
  17.         Modbus_HoldReg[3]=((u16*)&testData2)+1;//测试数据2
  18.         
  19. }


 楼主| chenqiang10 发表于 2018-7-29 20:56 | 显示全部楼层
本帖最后由 chenqiang10 于 2018-7-29 20:57 编辑

初始化串口2
  1. void RS485_Init(void)
  2. {
  3.         GPIO_InitTypeDef GPIO_InitStructure;
  4.         USART_InitTypeDef USART_InitStructure;
  5.         NVIC_InitTypeDef NVIC_InitStructure;
  6.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOG,ENABLE);
  7.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
  8.         
  9.         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;//PA2(TX)复用推挽输出
  10.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
  11.         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  12.         GPIO_Init(GPIOA,&GPIO_InitStructure);
  13.         GPIO_SetBits(GPIOA,GPIO_Pin_2);//默认高电平
  14.         
  15.         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;//PA3(RX)输入上拉
  16.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
  17.         GPIO_Init(GPIOA,&GPIO_InitStructure);
  18.         
  19.         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//PG9(RE/DE)通用推挽输出
  20.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
  21.         GPIO_Init(GPIOG,&GPIO_InitStructure);
  22.         GPIO_ResetBits(GPIOG,GPIO_Pin_9);//默认接收状态
  23.         
  24.         USART_DeInit(USART2);//复位串口2
  25.         USART_InitStructure.USART_BaudRate=RS485_Baudrate;
  26.         USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
  27.         USART_InitStructure.USART_WordLength=USART_WordLength_8b;
  28.         USART_InitStructure.USART_StopBits=USART_StopBits_1;
  29.         USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//收发模式
  30.         switch(RS485_Parity)
  31.         {
  32.                 case 0:USART_InitStructure.USART_Parity=USART_Parity_No;break;//无校验
  33.                 case 1:USART_InitStructure.USART_Parity=USART_Parity_Odd;break;//奇校验
  34.                 case 2:USART_InitStructure.USART_Parity=USART_Parity_Even;break;//偶校验
  35.         }
  36.         USART_Init(USART2,&USART_InitStructure);
  37.         
  38.         USART_ClearITPendingBit(USART2,USART_IT_RXNE);
  39.         USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);//使能串口2接收中断
  40.         
  41.         NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;
  42.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
  43.         NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
  44.         NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
  45.         NVIC_Init(&NVIC_InitStructure);
  46.         
  47.         USART_Cmd(USART2,ENABLE);//使能串口2
  48.         RS485_TX_EN=0;//默认为接收模式
  49.         
  50.         Timer7_Init();//定时器7初始化,用于监视空闲时间
  51.         Modbus_RegMap();//Modbus寄存器映射
  52. }


 楼主| chenqiang10 发表于 2018-7-29 20:59 | 显示全部楼层

定时器初始化
  1. void Timer7_Init(void)
  2. {
  3.   TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  4.         NVIC_InitTypeDef NVIC_InitStructure;

  5.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); //TIM7时钟使能

  6.         //TIM7初始化设置
  7.         TIM_TimeBaseStructure.TIM_Period = RS485_Frame_Distance*10; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  8.         TIM_TimeBaseStructure.TIM_Prescaler =7200; //设置用来作为TIMx时钟频率除数的预分频值 设置计数频率为10kHz
  9.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  10.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  11.         TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

  12.         TIM_ITConfig( TIM7, TIM_IT_Update, ENABLE );//TIM7 允许更新中断

  13.         //TIM7中断分组配置
  14.         NVIC_InitStructure.NVIC_IRQChannel =TIM7_IRQn;  //TIM7中断
  15.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
  16.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
  17.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
  18.         NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器                                                                  
  19. }



 楼主| chenqiang10 发表于 2018-7-29 20:59 | 显示全部楼层

发送N个字节数据
  1. void RS485_SendData(u8 *buff,u8 len)
  2. {
  3.         RS485_TX_EN=1;//切换为发送模式
  4.         while(len--)
  5.         {
  6.                 while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);//等待发送区为空
  7.                 USART_SendData(USART2,*(buff++));
  8.         }
  9.         while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);//等待发送完成
  10. }


 楼主| chenqiang10 发表于 2018-7-29 21:00 | 显示全部楼层
串口2中断服务程序
  1. void USART2_IRQHandler(void)
  2. {
  3.         u8 res;
  4.         u8 err;
  5.         if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
  6.         {
  7.                 if(USART_GetFlagStatus(USART2,USART_FLAG_NE|USART_FLAG_FE|USART_FLAG_PE)) err=1;//检测到噪音、帧错误或校验错误
  8.                 else err=0;
  9.                 res=USART_ReceiveData(USART2); //读接收到的字节,同时相关标志自动清除
  10.                
  11.                 if((RS485_RX_CNT<2047)&&(err==0))
  12.                 {
  13.                         RS485_RX_BUFF[RS485_RX_CNT]=res;
  14.                         RS485_RX_CNT++;
  15.                         
  16.                         TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除定时器溢出中断
  17.                         TIM_SetCounter(TIM7,0);//当接收到一个新的字节,将定时器7复位为0,重新计时(相当于喂狗)
  18.                         TIM_Cmd(TIM7,ENABLE);//开始计时
  19.                 }
  20.         }
  21. }


 楼主| chenqiang10 发表于 2018-7-29 21:01 | 显示全部楼层
定时器中断服务函数,用于判断接收是否完成。
  1. void TIM7_IRQHandler(void)
  2. {                                                                  
  3.         if(TIM_GetITStatus(TIM7,TIM_IT_Update)!=RESET)
  4.         {
  5.                 TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除中断标志
  6.                 TIM_Cmd(TIM7,DISABLE);//停止定时器
  7.                 RS485_TX_EN=1;//停止接收,切换为发送状态
  8.                 RS485_FrameFlag=1;//置位帧结束标记
  9.         }
  10. }


 楼主| chenqiang10 发表于 2018-7-29 21:13 | 显示全部楼层
//RS485服务程序,用于处理接收到的数据(请在主函数中循环调用)
  1. u16 startRegAddr;
  2. u16 RegNum;
  3. u16 calCRC;
  4. void RS485_Service(void)
  5. {
  6.         u16 recCRC;
  7.         if(RS485_FrameFlag==1)
  8.         {
  9.                 if(RS485_RX_BUFF[0]==RS485_Addr)//地址正确
  10.                 {
  11.                         if((RS485_RX_BUFF[1]==01)||(RS485_RX_BUFF[1]==02)||(RS485_RX_BUFF[1]==03)||(RS485_RX_BUFF[1]==05)||(RS485_RX_BUFF[1]==06)||(RS485_RX_BUFF[1]==15)||(RS485_RX_BUFF[1]==16))//功能码正确
  12.                   {
  13.                                 startRegAddr=(((u16)RS485_RX_BUFF[2])<<8)|RS485_RX_BUFF[3];//获取寄存器起始地址
  14.                                 if(startRegAddr<1000)//寄存器地址在范围内
  15.                                 {
  16.                                         calCRC=CRC_Compute(RS485_RX_BUFF,RS485_RX_CNT-2);//计算所接收数据的CRC
  17.                                         recCRC=RS485_RX_BUFF[RS485_RX_CNT-2]|(((u16)RS485_RX_BUFF[RS485_RX_CNT-1])<<8);//接收到的CRC(低字节在前,高字节在后)
  18.                                         if(calCRC==recCRC)//CRC校验正确
  19.                                         {
  20.                                                 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  21.                                                 switch(RS485_RX_BUFF[1])//根据不同的功能码进行处理
  22.                                                 {
  23.                                                         case 2://读输入开关量
  24.                                                         {
  25.                                                                 Modbus_02_Solve();
  26.                                                                 break;
  27.                                                         }
  28.                                                         
  29.                                                         case 1://读输出开关量
  30.                                                         {
  31.                                                                 Modbus_01_Solve();
  32.                                                                 break;
  33.                                                         }
  34.                                                                
  35.                                                         case 5://写单个输出开关量
  36.                                                         {
  37.                                                                 Modbus_05_Solve();
  38.                                                                 break;
  39.                                                         }
  40.                                                                
  41.                                                         case 15://写多个输出开关量
  42.                                                         {
  43.                                                                 Modbus_15_Solve();
  44.                                                                 break;
  45.                                                         }
  46.                                                                
  47.                                                         case 03: //读多个寄存器
  48.                                                         {
  49.                                                                 Modbus_03_Solve();
  50.                                                                 break;
  51.                                                         }
  52.                                                                
  53.                                                         case 06: //写单个寄存器
  54.                                                         {
  55.                                                                 Modbus_06_Solve();
  56.                                                                 break;
  57.                                                         }
  58.                                                                
  59.                                                         case 16: //写多个寄存器
  60.                                                         {
  61.                                                                 Modbus_16_Solve();
  62.                                                                 break;
  63.                                                         }
  64.                                                                                        
  65.                                                 }
  66.                                                 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  67.                                         }
  68.                                         else//CRC校验错误
  69.                                         {
  70.                                                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  71.                                                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
  72.                                                 RS485_TX_BUFF[2]=0x04; //异常码
  73.                                                 RS485_SendData(RS485_TX_BUFF,3);
  74.                                         }        
  75.                                 }
  76.                                 else//寄存器地址超出范围
  77.                                 {
  78.                                         RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  79.                                         RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
  80.                                         RS485_TX_BUFF[2]=0x02; //异常码
  81.                                         RS485_SendData(RS485_TX_BUFF,3);
  82.                                 }                                                
  83.                         }
  84.                         else//功能码错误
  85.                         {
  86.                                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  87.                                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
  88.                                 RS485_TX_BUFF[2]=0x01; //异常码
  89.                                 RS485_SendData(RS485_TX_BUFF,3);
  90.                         }
  91.           }
  92.                                 
  93.                 RS485_FrameFlag=0;//复位帧结束标志
  94.                 RS485_RX_CNT=0;//接收计数器清零
  95.                 RS485_TX_EN=0;//开启接收模式               
  96.         }               
  97. }


 楼主| chenqiang10 发表于 2018-7-29 21:13 | 显示全部楼层
//Modbus功能码02处理程序
//读输入开关量
  1. void Modbus_02_Solve(void)
  2. {
  3.         u16 ByteNum;
  4.         u16 i;
  5.         RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
  6.         if((startRegAddr+RegNum)<100)//寄存器地址+数量在范围内
  7.         {
  8.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  9.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
  10.                 ByteNum=RegNum/8;//字节数
  11.                 if(RegNum%8) ByteNum+=1;//如果位数还有余数,则字节数+1
  12.                 RS485_TX_BUFF[2]=ByteNum;//返回要读取的字节数
  13.                 for(i=0;i<RegNum;i++)
  14.                 {
  15.                         if(i%8==0) RS485_TX_BUFF[3+i/8]=0x00;
  16.                         RS485_TX_BUFF[3+i/8]>>=1;//低位先发送
  17.                         RS485_TX_BUFF[3+i/8]|=((*Modbus_InputIO[startRegAddr+i])<<7)&0x80;
  18.                         if(i==RegNum-1)//发送到最后一个位了
  19.                         {
  20.                                 if(RegNum%8) RS485_TX_BUFF[3+i/8]>>=8-(RegNum%8);//如果最后一个字节还有余数,则剩余MSB填充0
  21.                         }
  22.                 }
  23.                 calCRC=CRC_Compute(RS485_TX_BUFF,ByteNum+3);
  24.                 RS485_TX_BUFF[ByteNum+3]=calCRC&0xFF;
  25.                 RS485_TX_BUFF[ByteNum+4]=(calCRC>>8)&0xFF;
  26.                 RS485_SendData(RS485_TX_BUFF,ByteNum+5);
  27.         }
  28.         else//寄存器地址+数量超出范围
  29.         {
  30.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  31.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
  32.                 RS485_TX_BUFF[2]=0x02; //异常码
  33.                 RS485_SendData(RS485_TX_BUFF,3);
  34.         }
  35. }


 楼主| chenqiang10 发表于 2018-7-29 21:15 | 显示全部楼层
//Modbus功能码01处理程序
//读输出开关量
  1. void Modbus_01_Solve(void)
  2. {
  3.         u16 ByteNum;
  4.         u16 i;
  5.         RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
  6.         if((startRegAddr+RegNum)<100)//寄存器地址+数量在范围内
  7.         {
  8.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  9.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
  10.                 ByteNum=RegNum/8;//字节数
  11.                 if(RegNum%8) ByteNum+=1;//如果位数还有余数,则字节数+1
  12.                 RS485_TX_BUFF[2]=ByteNum;//返回要读取的字节数
  13.                 for(i=0;i<RegNum;i++)
  14.                 {
  15.                         if(i%8==0) RS485_TX_BUFF[3+i/8]=0x00;
  16.                         RS485_TX_BUFF[3+i/8]>>=1;//低位先发送
  17.                         RS485_TX_BUFF[3+i/8]|=((*Modbus_OutputIO[startRegAddr+i])<<7)&0x80;
  18.                         if(i==RegNum-1)//发送到最后一个位了
  19.                         {
  20.                                 if(RegNum%8) RS485_TX_BUFF[3+i/8]>>=8-(RegNum%8);//如果最后一个字节还有余数,则剩余MSB填充0
  21.                         }
  22.                 }
  23.                 calCRC=CRC_Compute(RS485_TX_BUFF,ByteNum+3);
  24.                 RS485_TX_BUFF[ByteNum+3]=calCRC&0xFF;
  25.                 RS485_TX_BUFF[ByteNum+4]=(calCRC>>8)&0xFF;
  26.                 RS485_SendData(RS485_TX_BUFF,ByteNum+5);
  27.         }
  28.         else//寄存器地址+数量超出范围
  29.         {
  30.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  31.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
  32.                 RS485_TX_BUFF[2]=0x02; //异常码
  33.                 RS485_SendData(RS485_TX_BUFF,3);
  34.         }
  35. }


 楼主| chenqiang10 发表于 2018-7-30 21:27 | 显示全部楼层
//Modbus功能码05处理程序
//写单个输出开关量
  1. void Modbus_05_Solve(void)
  2. {
  3.         if(startRegAddr<100)//寄存器地址在范围内
  4.         {
  5.                 if((RS485_RX_BUFF[4]==0xFF)||(RS485_RX_BUFF[5]==0xFF)) *Modbus_OutputIO[startRegAddr]=0x01;
  6.                 else *Modbus_OutputIO[startRegAddr]=0x00;
  7.                
  8.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  9.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
  10.                 RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
  11.                 RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
  12.                 RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
  13.                 RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
  14.                
  15.                 calCRC=CRC_Compute(RS485_TX_BUFF,6);
  16.                 RS485_TX_BUFF[6]=calCRC&0xFF;
  17.                 RS485_TX_BUFF[7]=(calCRC>>8)&0xFF;
  18.                 RS485_SendData(RS485_TX_BUFF,8);
  19.         }
  20.         else//寄存器地址超出范围
  21.         {
  22.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  23.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
  24.                 RS485_TX_BUFF[2]=0x02; //异常码
  25.                 RS485_SendData(RS485_TX_BUFF,3);
  26.         }
  27. }


 楼主| chenqiang10 发表于 2018-7-30 21:28 | 显示全部楼层
//Modbus功能码15处理程序
//写多个输出开关量
  1. void Modbus_15_Solve(void)
  2. {
  3.         u16 i;
  4.         RegNum=(((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
  5.         if((startRegAddr+RegNum)<100)//寄存器地址+数量在范围内
  6.         {        
  7.                 for(i=0;i<RegNum;i++)
  8.                 {
  9.                         if(RS485_RX_BUFF[7+i/8]&0x01) *Modbus_OutputIO[startRegAddr+i]=0x01;
  10.                         else *Modbus_OutputIO[startRegAddr+i]=0x00;
  11.                         RS485_RX_BUFF[7+i/8]>>=1;//从低位开始
  12.                 }
  13.                
  14.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  15.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
  16.                 RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
  17.                 RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
  18.                 RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
  19.                 RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
  20.                 calCRC=CRC_Compute(RS485_TX_BUFF,6);
  21.                 RS485_TX_BUFF[6]=calCRC&0xFF;
  22.                 RS485_TX_BUFF[7]=(calCRC>>8)&0xFF;
  23.                 RS485_SendData(RS485_TX_BUFF,8);
  24.         }
  25.         else//寄存器地址+数量超出范围
  26.         {
  27.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  28.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
  29.                 RS485_TX_BUFF[2]=0x02; //异常码
  30.                 RS485_SendData(RS485_TX_BUFF,3);
  31.         }
  32. }


 楼主| chenqiang10 发表于 2018-7-30 21:30 | 显示全部楼层
//Modbus功能码03处理程序
//读保持寄存器
  1. void Modbus_03_Solve(void)
  2. {
  3.         u8 i;
  4.         RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
  5.         if((startRegAddr+RegNum)<1000)//寄存器地址+数量在范围内
  6.         {
  7.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  8.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
  9.                 RS485_TX_BUFF[2]=RegNum*2;
  10.                 for(i=0;i<RegNum;i++)
  11.                 {
  12.                         RS485_TX_BUFF[3+i*2]=*Modbus_HoldReg[startRegAddr+i]&0xFF;//先发送低字节
  13.                         RS485_TX_BUFF[4+i*2]=(*Modbus_HoldReg[startRegAddr+i]>>8)&0xFF; //后发送高字节
  14.                 }
  15.                 calCRC=CRC_Compute(RS485_TX_BUFF,RegNum*2+3);
  16.                 RS485_TX_BUFF[RegNum*2+3]=calCRC&0xFF;
  17.                 RS485_TX_BUFF[RegNum*2+4]=(calCRC>>8)&0xFF;
  18.                 RS485_SendData(RS485_TX_BUFF,RegNum*2+5);
  19.         }
  20.         else//寄存器地址+数量超出范围
  21.         {
  22.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  23.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
  24.                 RS485_TX_BUFF[2]=0x02; //异常码
  25.                 RS485_SendData(RS485_TX_BUFF,3);
  26.         }
  27. }


 楼主| chenqiang10 发表于 2018-7-30 21:35 | 显示全部楼层
//Modbus功能码06处理程序
//写单个保持寄存器
  1. void Modbus_06_Solve(void)
  2. {
  3.         *Modbus_HoldReg[startRegAddr]=RS485_RX_BUFF[4];//低字节在前
  4.         *Modbus_HoldReg[startRegAddr]|=((u16)RS485_RX_BUFF[5])<<8;//高字节在后
  5.         
  6.         RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  7.         RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
  8.         RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
  9.         RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
  10.         RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
  11.         RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
  12.         
  13.         calCRC=CRC_Compute(RS485_TX_BUFF,6);
  14.         RS485_TX_BUFF[6]=calCRC&0xFF;
  15.         RS485_TX_BUFF[7]=(calCRC>>8)&0xFF;
  16.         RS485_SendData(RS485_TX_BUFF,8);
  17. }


 楼主| chenqiang10 发表于 2018-7-30 21:36 | 显示全部楼层
//Modbus功能码16处理程序
//写多个保持寄存器


  1. void Modbus_16_Solve(void)
  2. {
  3.         u8 i;
  4.         RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
  5.         if((startRegAddr+RegNum)<1000)//寄存器地址+数量在范围内
  6.         {
  7.                 for(i=0;i<RegNum;i++)
  8.                 {
  9.                         *Modbus_HoldReg[startRegAddr+i]=RS485_RX_BUFF[7+i*2]; //低字节在前
  10.                         *Modbus_HoldReg[startRegAddr+i]|=((u16)RS485_RX_BUFF[8+i*2])<<8; //高字节在后
  11.                 }
  12.                
  13.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  14.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
  15.                 RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
  16.                 RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
  17.                 RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
  18.                 RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
  19.                
  20.                 calCRC=CRC_Compute(RS485_TX_BUFF,6);
  21.                 RS485_TX_BUFF[6]=calCRC&0xFF;
  22.                 RS485_TX_BUFF[7]=(calCRC>>8)&0xFF;
  23.                 RS485_SendData(RS485_TX_BUFF,8);
  24.         }
  25.         else//寄存器地址+数量超出范围
  26.         {
  27.                 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
  28.                 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
  29.                 RS485_TX_BUFF[2]=0x02; //异常码
  30.                 RS485_SendData(RS485_TX_BUFF,3);
  31.         }
  32. }


740071911 发表于 2018-7-31 08:41 | 显示全部楼层
赞一个
lvyunzeng 发表于 2018-7-31 08:55 | 显示全部楼层
很长,很强大。
 楼主| chenqiang10 发表于 2018-8-19 19:36 | 显示全部楼层

我是觉得会用就行

评论

确实不错,特别是通讯寄存器映射方法很实用!  发表于 2018-8-21 12:56
 楼主| chenqiang10 发表于 2018-8-19 19:36 | 显示全部楼层

自己写出来是有点难度
 楼主| chenqiang10 发表于 2018-8-19 19:36 | 显示全部楼层

长不可怕,能正产通信慢慢看代码。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

39

主题

940

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部