A33之485串口通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/YYGY731793898/article/details/79359148

                                                    A33之485串口通信

1.   485通信参考电路


接收发送控制端口为PH5。 电路电平已反向。

2.   bsp_modbus.h

#ifndef __BSP_MODBUS_H__

#define __BSP_MODBUS_H__

#include "../config.h"

#include "bsp_uart.h"

#include<pthread.h>

#ifndef MODBUS_MODULE

#define MODBUS_EXT     extern

#else

#define MODBUS_EXT

#endif//MODBUS_MODULE

#define CODE_READ_MUL_REGISTER                                  3

#define CODE_READ_ONLYREAD_REGISTER                         4

#define CODE_WRITE_ONE_REGISTER                                6

#define CODE_WRITE_MUL_REGISTER                                16

#define CODE_READ_MUL_ERR_REGISTER                  (0X80+CODE_READ_MUL_REGISTER)

#define CODE_WRITE_MUL_ERR_REGISTER                 (0X80+CODE_WRITE_MUL_REGISTER)

#defineCODE_READ_ONLYREAD_ERR_REGISTER            (0X80+CODE_READ_ONLYREAD_REGISTER)

#define CODE_WRITE_ONE_ERR_REGISTER                 (0X80+CODE_WRITE_ONE_REGISTER)

//以下为功能码

//03:读一个或多个保持寄存器请求数据包:地址-功能码-起始地址-寄存器个数 校验码  先高后低

//                                                                      应答数据包:地址-功能码-字节数-寄存器值 校验码

//                                                                      错误数据包:地址-错误码-异常码 校验码

//04:读输入寄存器(只读)        请求数据包:地址-功能码-起始地址-寄存器个数 校验码

//                                                                      应答数据包:地址-功能码-字节数-寄存器值 校验码

//                                                                      错误数据包:地址-错误码-异常码 校验码

//06:写单个寄存器                               请求数据包:地址-功能码-寄存器地址-寄存器值 校验码

//                                                                      应答数据包:地址-功能码-寄存器地址-寄存器值 校验码

//                                                                      错误数据包:地址-错误码-异常码 校验码

//16:写多个寄存器                               请求数据包:地址-功能码-寄存器起始地址-寄存器数量-字节数-寄存器值 校验码

//                                                                      应答数据包:地址-功能码-寄存器起始地址-寄存器数量  校验码

//        

#define IS_MODBUS_CODE(code)          ((code == CODE_READ_MUL_REGISTER)\

                                                               || (code == CODE_READ_ONLYREAD_REGISTER)\

                                                               || (code == CODE_WRITE_ONE_REGISTER) \

                                                               || (code == CODE_WRITE_MUL_REGISTER))

#define IS_MODBUS_CODE_AND_ERR(code)  ((code == CODE_READ_MUL_REGISTER)\

                                                               || (code == CODE_READ_ONLYREAD_REGISTER)\

                                                               || (code == CODE_WRITE_ONE_REGISTER) \

                                                                || (code ==CODE_WRITE_MUL_REGISTER) \

                                                                                          ||(code == CODE_READ_MUL_ERR_REGISTER)\

                                                                                          ||(code == CODE_READ_ONLYREAD_ERR_REGISTER)\

                                                               || (code == CODE_WRITE_ONE_ERR_REGISTER) \

                                                               || (code == CODE_WRITE_MUL_ERR_REGISTER))

typedef struct {

      intfd;

      intsec;

      intusec;

      charF_rx_ok;

      unsignedint speed;

}Uart_info;

Uart_info modbus_info;

MODBUS_EXT  char BackRxBuffer1[100];

MODBUS_EXT  unsigned char RxBufferSize1;

MODBUS_EXT pthread_mutex_t modbus_mutx;

3.   bsp_modbus.c

#include "bsp_modbus.h"

#include <string.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include<pthread.h>

#include<sys/types.h> 

#include<sys/stat.h> 

#include<sys/signal.h> 

#include<fcntl.h> 

#include<termios.h> 

#include<errno.h> 

A> 校验 CRC16查表

/************************CRC高位和地位字节值表***************************/

const unsigned char chCRCHTalbe[] =                                 // CRC 高位字节值表

{

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x00, 0xC1, 0x81, 0x40,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x00, 0xC1, 0x81, 0x40,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x00, 0xC1, 0x81, 0x40,

 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80,0x41, 0x00, 0xC1, 0x81, 0x40,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x00, 0xC1, 0x81, 0x40,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41,

 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x00, 0xC1, 0x81, 0x40,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,0x41, 0x01, 0xC0, 0x80, 0x41,

 0x00, 0xC1, 0x81, 0x40

};

const unsigned char chCRCLTalbe[] =                                 // CRC 低位字节值表

{

0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03,0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,

 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D,0xCD, 0x0F, 0xCF, 0xCE, 0x0E,

 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08,0xC8, 0xD8, 0x18, 0x19, 0xD9,

 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF,0x1F, 0xDD, 0x1D, 0x1C, 0xDC,

 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16,0xD6, 0xD2, 0x12, 0x13, 0xD3,

 0x11,0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,

 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34,0xF4, 0x3C, 0xFC, 0xFD, 0x3D,

 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B,0xFB, 0x39, 0xF9, 0xF8, 0x38,

 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A,0xEA, 0xEE, 0x2E, 0x2F, 0xEF,

 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25,0xE5, 0x27, 0xE7, 0xE6, 0x26,

 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20,0xE0, 0xA0, 0x60, 0x61, 0xA1,

 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7,0x67, 0xA5, 0x65, 0x64, 0xA4,

 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E,0xAE, 0xAA, 0x6A, 0x6B, 0xAB,

 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9,0x79, 0xBB, 0x7B, 0x7A, 0xBA,

 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC,0x7C, 0xB4, 0x74, 0x75, 0xB5,

 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3,0x73, 0xB1, 0x71, 0x70, 0xB0,

 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52,0x92, 0x96, 0x56, 0x57, 0x97,

 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D,0x9D, 0x5F, 0x9F, 0x9E, 0x5E,

 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58,0x98, 0x88, 0x48, 0x49, 0x89,

 0x4B,0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,

 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46,0x86, 0x82, 0x42, 0x43, 0x83,

 0x41, 0x81, 0x80, 0x40

};

/***********************************************************

函 数 名: CRC16

* 功能说明: CRC校验码生成函数

* 本代码中使用查表法,以提高运算速度

* 参    数:void

* 返 回 值: void

* 使用说明:

* 调用方法:CRC16 ();

**********************************************************/

u16 CRC16(u8 *puchMsg, u16 DataLen)

{

      u8uchCRCHi = 0xFF ; /* 高CRC字节初始化 */

      u8uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */

      u16Index ; /* CRC循环中的索引 */

      while(DataLen--) /* 传输消息缓冲区 */

      {

           Index= uchCRCLo ^ *puchMsg++ ; /* 计算CRC */

           uchCRCLo= (u8)(uchCRCHi ^ chCRCHTalbe[Index]) ;

           uchCRCHi= (u8)(chCRCLTalbe[Index]) ;

      }

      return((u16)uchCRCHi << 8) | uchCRCLo ;                 

}

/***********************************************************

* 函 数 名: code_crc_check

* 功能说明: CRC校验

* 参    数:u8* rxbuffer, u8 rxsize

* 返 回 值: u8   1:正确       0:错误

* 使用说明:

* 调用方法:code_crc_check();

**********************************************************/

u8 code_crc_check(u8* rxbuffer, u8rxsize)

{

      u16crcdata = 0;

      if(rxbuffer== NULL || rxsize < 2) return 0;

      crcdata= CRC16(rxbuffer, rxsize-2);

      if(crcdata== (((u16)rxbuffer[rxsize-1]<<8)+rxbuffer[rxsize-2]))         //如果校验正确

           return1;

      else

           return0;    

}

B>  协议相关

void GetRegisterVal(u16 addr, u16* data);     //获取寄存器的值

void SetRegisterVal(u16 addr, u16 data);   //设置寄存器的值

void code_read_mul_register(u8* rxbuffer,u8 rxsize, u8* txbuffer, u8* txsize);                                //读取多个寄存器的值

void code_read_onlyread_register(u8*rxbuffer, u8 rxsize, u8* txbuffer, u8* txsize);                          //读取单个寄存器的值

void code_write_one_register(u8* rxbuffer,u8 rxsize, u8* txbuffer, u8* txsize);                                //写单个寄存器的值

void code_write_mul_register(u8* rxbuffer,u8 rxsize, u8* txbuffer, u8* txsize);                                //写多个寄存器

C> 485发送接收模式选择

#define MODE_FILEPATH     "/sys/class/gpio_sw/PH5/data"

#define TX_MODE                    1

#define RX_MODE                    0

/**********************************************************

* 函 数 名:Modbus_TxRx_Mode

* 功能说明: 485模式选择

* 参    数:unsigned char

* 返 回 值: void

* 使用说明:

* 调用方法:Modbus_TxRx_Mode(mode);

***********************************************************/

void Modbus_TxRx_Mode(unsigned charmode)

{

      FILE*fp;

      if((fp= fopen(MODE_FILEPATH, "rb+")) == NULL)

      {

           perror("Cannotopen value file!\n");

           exit(1);

      }

      if(mode== RX_MODE)

           fprintf(fp,"1");

      else

           fprintf(fp,"0");

      fclose(fp);

}

D>串口相关

int Set_Option(int fd,int nSpeed,intnBits,char nEvent,int nStop)

{

   struct termios newtio,oldtio;

   if(tcgetattr(fd,&oldtio)!=0)                     //获取当前配置选项 存储到oldstdio结构体中

   {

       perror("error:SetupSerial!\n");

       return -1;

   }

   bzero(&newtio,sizeof(newtio));

   //使能串口接收

   newtio.c_cflag |= CLOCAL | CREAD;        //忽略调制解调器线路状态,使用接收器

   newtio.c_cflag &= ~CSIZE;                               //字符长度

   newtio.c_lflag &=~ICANON;//原始模式

   //newtio.c_lflag |=ICANON; //标准模式

   //设置串口数据位

   switch(nBits)

   {

       case 7:

            newtio.c_cflag |= CS7;

            break;

       case 8:

            newtio.c_cflag |=CS8;

            break;

   }

   //设置奇偶校验位

   switch(nEvent)

   {

       case 'O':

            newtio.c_cflag |= PARENB;         //使用奇偶校验

            newtio.c_cflag |= PARODD;        //对输入使用奇偶校验,对输出使用偶校验

            newtio.c_iflag |= (INPCK |ISTRIP);//允许输入奇偶校验,去除字符的第8个比特

            break;

       case 'E':

               newtio.c_cflag |= PARENB;      //使用奇偶校验

            newtio.c_cflag &= ~PARODD;    

            newtio.c_iflag |= (INPCK |ISTRIP);   

            break;

       case 'N':

            newtio.c_cflag &=~PARENB;       //不使用奇偶校验

            break;

   }

   //设置串口波特率

   switch(nSpeed)

   {

       case 2400:

            cfsetispeed(&newtio,B2400);      //设置输入速度

            cfsetospeed(&newtio,B2400);           //设置输出速度

            break;

       case 4800:

            cfsetispeed(&newtio,B4800);

            cfsetospeed(&newtio,B4800);

            break;

       case 9600:

            cfsetispeed(&newtio,B9600);

            cfsetospeed(&newtio,B9600);

            break;

       case 19200:

            cfsetispeed(&newtio,B19200);

            cfsetospeed(&newtio,B19200);

           break;

       case 38400:

                   cfsetispeed(&newtio,B38400);

                   cfsetospeed(&newtio,B38400);

                   break;

       case 115200:

            cfsetispeed(&newtio,B115200);

            cfsetospeed(&newtio,B115200);

            break;

       case 230400:

            cfsetispeed(&newtio,B230400);

            cfsetospeed(&newtio,B230400);

            break;

       default:

            cfsetispeed(&newtio,B19200);

            cfsetospeed(&newtio,B19200);

            break;

   }

   //设置停止位

   if(nStop == 1)

       newtio.c_cflag &= ~CSTOPB;      //设置一个停止位

   else if(nStop == 2)

       newtio.c_cflag |= CSTOPB;         //设置两个停止位

   newtio.c_cc[VTIME] = 1;

    newtio.c_cc[VMIN] = 100;                          //读满100个或者计时器超时1*0.1S的时候立即返回

   tcflush(fd,TCIFLUSH);

   if(tcsetattr(fd,TCSANOW,&newtio)!=0)

   {

       perror("com set error\n");

       return -1;

   }

   return 0;

}

/**********************************************************

* 函 数 名: Modbus_Send

* 功能说明: 485发送数据

* 参    数:char *Data, unsigned int lenth

* 返 回 值: int

* 使用说明:

* 调用方法:Modbus_Send(Data, lenth);

***********************************************************/

int Modbus_Send(char *Data, unsigned intlenth)

{

      size_tnleft;

      intnwrite;

      constchar *ptr;

      intfd = modbus_info.fd;

      if(lenth== 0)

           lenth= strlen(Data);

      Modbus_TxRx_Mode(TX_MODE);

      usleep(10);

      ptr= Data;

      nleft= lenth;

      while(nleft > 0)

      {

           if((nwrite = write(fd, ptr, nleft))<= 0)

           {

                 if(errno == EINTR)

                 {

                      nwrite= 0;

                      DEBUG("nwrite=0!\n\r");

                 }

                 else

                 {

                      perror("nwrite=-1!\n\r");

                      return(-1);

                 }

           }

           nleft-= nwrite;

           ptr+= nwrite;

      }

      if(modbus_info.speed== 115200)

     usleep((lenth/10+1)*1000);

 else

     usleep((lenth/2+1)*1000);

 Modbus_TxRx_Mode(RX_MODE);  

}

/**********************************************************

* 函 数 名:Modbus_Pthread_Rx

* 功能说明: 485接收数据

* 参    数:void *arg

* 返 回 值: void*

* 使用说明:

* 调用方法:Modbus_Pthread_Rx(arg);

***********************************************************/

void* Modbus_Pthread_Rx(void *arg)

{

      intfd;

      intretval;

      intlen;

      inti;

      structtimeval tv;

 fd_set rfds;

     Uart_info *uinfo = (Uart_info*)arg;

     char rx_data[100]={0};

 

      fd =uinfo->fd;

      tv.tv_sec = uinfo->sec;

     tv.tv_usec = uinfo->usec;

 

     while(1)

     {

           FD_ZERO(&rfds);                //把可读文件描述符的集合清空

              FD_SET(fd,&rfds );   //把文件描述符加入到集合中

              retval = select(fd+1,&rfds, NULL, NULL, &tv);

              if(retval == -1)

              {

                    perror("error\r\n");

                   break;

              }

              else if(retval == 0)

              {

                   //DEBUG("no data,waiting...\n");

                   continue;

              }

              else

              {

                    if(!FD_ISSET(fd,&rfds))

                         continue;

                    memset(rx_data, 0, 100);

                    len = read(fd, rx_data, 100);

        

                    if(retval == 1 && len == 0)

                {

                     if(errno== EINTR)

                           continue;

                     else

                           break;                   //断开连接

                }

                if(len== 0)

                     continue;

         

         

                     

                if(!Mod_Rev_process(rx_data, len))   //数据格式不正确,跳过不处理

                {

                     DEBUG("datafomart is failed!\r\n");

                     continue;

                }

                  

                     

                    Modbus_Send (BackRxBuffer1,RxBufferSize1);     //        

               }         

         }

 }

 pthread_exit(NULL);

}

void* Modbus_Pthread(void *arg)

{

      intfd;

     

      fd= open(Modbus_Dev, O_RDWR | O_NOCTTY);

      if(fd== -1)

      {

           perror("openttys2 failed!\r\n");

           pthread_exit(NULL);

      }

      //modbus_info.speed= 115200;

      modbus_info.speed= 19200;

      if(Set_Option(fd,modbus_info.speed, 8, 'N', 1) == -1)

      {

           perror("setttys2 failed!\r\n");

           pthread_exit(NULL);

      }

     

      DEBUG("init  ttys2 over!\r\n");

      modbus_info.fd= fd;

      modbus_info.sec= 0;

      modbus_info.usec= 10000;

     

      pthread_create(&pth_modbus_rx,NULL, Modbus_Pthread_Rx, ( void *)&modbus_info);

      pthread_detach(pth_modbus_rx);           //线程分离,线程结束后自动释放资源

     

      while(0==pthread_kill(pth_modbus_rx,0)  )  //发送空信号0给子线程,看是否存在

     {         

              sleep(1);

      }

     DEBUG("receive thread isquited\r\n");

     pthread_exit(NULL);

}

int Modbus_Thread_Create(void)

{

      returnpthread_create(&pth_modbus, NULL, Modbus_Pthread, NULL);

}

int Modbus_Thread_Destroy(void)

{   

      pthread_join(pth_modbus,NULL);

      return0;

}

/********************************************************************

* 函 数 名: Mod_Rev_process

* 功能说明: 485从机接收数据的处理函数

* 参    数:char *recv, int len

* 返 回 值: int 是否获取正确格式数据 0=NO  1=YES

* 使用说明:用于串口及网络接收数据的处理

* 调用方法:Mod_Rev_process();

******************************************************************/

int Mod_Rev_process(char *recv, int len)

{

      unsignedchar RxCounter1 = 0;

     char* rx_ptr = recv;

      if(len < 5)

           return0;

     while(rx_ptr < recv+len )            //上次接收到的数据已处理才保存接收数据

     {

              RxCounter1++;

              switch(RxCounter1)

              {

              case 1:

                        {

                        if(*rx_ptr == MAIN_485_IP_ADDR || (*rx_ptr>= MIN_485_ADDR && *rx_ptr <= MAX_485_ADDR ))   //是中控主机地址或从设备地址

                        {

                                  BackRxBuffer1[0] = *rx_ptr;

                        }

                            else

                        {

                                  RxCounter1 = 0;                 //不是发给本机的不处理

                            }

                            break;

                        }

                  case 2:

                        {

                             if(IS_MODBUS_CODE(*rx_ptr) == 0) //无效的功能码,重新开始接收

                       {

                                  RxCounter1 = 0;

                       }

                             else

                             {

                                  BackRxBuffer1[1] = *rx_ptr;                  //保存功能码

                       }

                         break;

                        }

                  case 3:

                        {

                             BackRxBuffer1[2] = *rx_ptr;

                             switch(BackRxBuffer1[1])

                       {

                                 caseCODE_READ_MUL_REGISTER:

                                     RxBufferSize1= BackRxBuffer1[2]+5;

                                     break;

                             case CODE_WRITE_MUL_REGISTER:

                                 case CODE_WRITE_ONE_REGISTER:

                                     RxBufferSize1 = 8;

                                     break;

                                 case CODE_READ_MUL_ERR_REGISTER:

                                 case CODE_READ_ONLYREAD_ERR_REGISTER:

                                 case CODE_WRITE_ONE_ERR_REGISTER:

                                 case CODE_WRITE_MUL_ERR_REGISTER:

                                          RxBufferSize1 = 5;

                                      break;

                                 default:

                                break;

                             }

                    }

                  default:

                        {

                        BackRxBuffer1[RxCounter1- 1] = *rx_ptr;

                              if(RxCounter1 >= RxBufferSize1)

                        {

                                 RxCounter1= 0;

                                 return1;

                              }

                        break;

                    }

              }

              rx_ptr++;

      }

      return0;

}

以上内容本人已经过实测验证无误。

猜你喜欢

转载自blog.csdn.net/YYGY731793898/article/details/79359148