/*************************** (C) COPYRIGHT 2017 CXHY **************************** * 文件名 :RS485.c * 描 述 :RS485通信处理函数,包含3部分:接收帧程序、发送帧程序及执行命令程序。 * 版 本 :V1.0 * 作 者 :XuZhongPing * 日 期 :2017/02/10 *********************************************************************************/ #include "delay.h" #include "sys.h" #include "RS485.h" // #include "usart5.h" #include "usart.h" #include "timer.h" #include "message.h" #include "device.h" #include "data_typedef.h" /*通讯变量定义*/ #define BaudRate 9600 //波特率 #define DATA_SIZE 32 //通讯缓存区长度 uint8_t Address = 0x00; //设备地址 uint8_t Serial_buf[DATA_SIZE]; //定义通讯缓存区 uint8_t RS485_RX_BUF[64]; //接收缓冲,最大64个字节 uint8_t RS485_TX_BUF[64]; //发送缓冲,最大64个字节 //主程序变量定义 uint8_t RS485_RX_STA = 0x00; //RS485接收状态标记 uint8_t RS485_CommunicationState; //RS485通讯成功与否标志 uint8_t radioCom = 0; //广播命令标志 uint8_t responseMark = 0; //主机应答标志 uint16_t RS485_serialNumber; //RS485通信流水号 uint8_t rxCmd; //从机返回的指令码 uint8_t slaveAddress = 0; //从机返回的地址 uint8_t getMCU2DataStatus; //取得MCU2的数据状态 uint8_t getMCU2DistanceStatus; //取得MCU2的测距数据状态 /* *********************************************************************** * 函 数 名: RS485_Init * 功能说明: 初始化RS485接口 * 形 参: 无 * 返 回 值: 无 *********************************************************************** */ void RS485_Init(void) { //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能GPIOA时钟 //RS485_TX_EN RS485发送接收控制脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_ResetBits(GPIOC, GPIO_Pin_2); //输出0,默认接收状态 USART5_Init(BaudRate); //RS485对应的串口初始化 } /* *********************************************************************** * 函 数 名: RS485_ReceiveData * 功能说明: RS485接收数据 * 形 参: rxData -> 接收到的数据 * 返 回 值: 无 *********************************************************************** */ void RS485_ReceiveData(uint8_t rxData) { static uint8_t RX_State = 0; //接收状态 static uint8_t CS = 0; //接收核验和 static uint8_t RX_Size = 0; //接收通讯数据长度 static uint8_t Byte_Count = 0; //接收数据字节计数 // printf("R = %x\r\n", rxData); switch(RX_State) { case 0: //第一前导字节 if(rxData == 0xfe){ RX_State = 1; }else{ RX_State = 0; } break; case 1: //第二前导字节 if(rxData == 0xfe){ RX_State = 1; //转到接收帧头 }else{ if(rxData == 0x68) { RX_State = 3; //转到接收设备通讯地址 CS = 0x68; }else{ RX_State = 0; } } break; case 2: //接收帧头 if(rxData == 0x68){ RX_State = 3; //转到接收设备通讯地址 CS = 0x68; }else{ RX_State = 0; } break; case 3: //接收设备通讯地址 RS485_RX_BUF[0] = rxData; CS += rxData; RX_State = 4; break; case 4: //接收帧头 if(rxData == 0x68){ RX_State = 5; //转到接收通讯命令 CS += 0x68; }else{ RX_State = 0; } break; case 5: //接收通讯命令 RS485_RX_BUF[1] = rxData; CS += rxData; RX_State = 6; //转到接收数据长度 break; case 6: //接收通讯数据长度 RS485_RX_BUF[2] = rxData; CS += rxData; RX_Size = rxData; //接收的数据长度 if(RX_Size == 0x00){ //如果数据长度为0,则直接转到接收校验码 RX_State = 8; }else{ RX_State = 7; //转到接收数据 } break; case 7: //接收N个字节数据 if(Byte_Count < RX_Size-1){ RS485_RX_BUF[Byte_Count + 3] = rxData; CS += rxData; Byte_Count++; }else{ RS485_RX_BUF[Byte_Count + 3] = rxData; CS += rxData; Byte_Count = 0; //已完成N个字节的数据接收计数归0 RX_State = 8; //转到接收核验码 } break; case 8: //接收校验 if(CS != rxData){ RX_State = 0; }else{ RX_State = 9; //转到接收帧尾码0x16 } break; case 9: //接收帧尾 if(rxData == 0x16){ //是帧尾吗? RS485_RX_STA = 0xfd; //一帧接收成功,标记USART_RX_STA=0xfd通知主程序进行通讯处理 } RX_State = 0; break; default: //其它 break; } } /* *********************************************************************** * 函 数 名: Receive_One * 功能说明: 接收一帧通讯函数 * 形 参: 接收存取地址指针 * 返 回 值: 接收正确标志,1 为接收到一帧数据,0 未接收到数据 *********************************************************************** */ // uint8_t Receive_One(uint8_t *s) // { // if(RS485_RX_STA == 0xfd) // { // return 1; //一帧接收成功,关闭接收通讯,处理一帧完后再打开 // }else{ // return 0; // } // } /* *********************************************************************** * 函 数 名: RS485_SendData * 功能说明: 发送一个字节数据 * 形 参: 发送数据 * 返 回 值: 无 *********************************************************************** */ void RS485_SendData(uint8_t Serial_data) { // CTRL485 = 1; //设置为发送模式 UART5_Send_Data(Serial_data); // CTRL485 = 0; //恢复为接收模式 } /* *********************************************************************** * 函 数 名: RS485_SendOne * 功能说明: 通过RS485发送一帧数据 * 形 参: 发送数据帧地址指针 * 返 回 值: 无 *********************************************************************** */ void RS485_SendOne(uint8_t *s) { uint8_t i; uint8_t CS = 0x68; //接收核验和 uint8_t TX_Size = 0; //接收通讯数据长度 printf("\r\nRS485 --> %x\r\n", *(s+1)); CTRL485 = 1; //RS-485 通讯,切换为发送状态 delay_ms(1); RS485_SendData(0xfe); //发送前导字节 RS485_SendData(0xfe); RS485_SendData(0xfe); RS485_SendData(0xfe); RS485_SendData(0x68); //发送帧头 RS485_SendData(*s); //发送地址 CS += *s; RS485_SendData(0x68); //发送帧头 CS += 0x68; RS485_SendData(*(s+1)); //发送帧命令 CS += *(s+1); TX_Size = *(s+2); //取出通讯数据长度 RS485_SendData(TX_Size); //发送数据长度 CS += *(s+2); for(i=0; i RS485发送消息 * (其中msg->msgType为指令,msg->msgData为要发送的从机地址) * 0x01->读取超声波测量值; * 0x02->读取重量值 * 0x03->读取温度值 * 0x10->读取温度、测距、水位、重量、身份信息的组合 * 0x20->主机广播开门指令 * 返 回 值: 无 *********************************************************************** */ void RS485_PackSendProtocol(Com_ProtocolMessage *msg) { RS485_TX_BUF[0] = msg->msgData; //设置从机通讯地址 RS485_TX_BUF[1] = msg->msgType; //存入指令码 RS485_TX_BUF[2] = 0; //存入数据长度 // switch(msg->msgType) // { // case 0x30: // RS485_TX_BUF[2] = 27; //存入数据长度 // // RS485_TX_BUF[3] = isAuth; //存入数据 // RS485_TX_BUF[4] = putInType; // memcpy(&RS485_TX_BUF[5], icCardNumberBit, 17); // RS485_TX_BUF[5+17] = (uint8_t)(accountNum >> 24); // RS485_TX_BUF[6+17] = (uint8_t)(accountNum >> 16); // RS485_TX_BUF[7+17] = (uint8_t)(accountNum >> 8); // RS485_TX_BUF[8+17] = (uint8_t)accountNum; // // RS485_TX_BUF[9+17] = (uint8_t)(capital >> 24); // RS485_TX_BUF[10+17] = (uint8_t)(capital >> 16); // RS485_TX_BUF[11+17] = (uint8_t)(capital >> 8); // RS485_TX_BUF[12+17] = (uint8_t)capital; // break; // // case 0x31: // RS485_TX_BUF[2] = 8; //存入数据长度 // // RS485_TX_BUF[3] = (uint8_t)(capital >> 24); // RS485_TX_BUF[4] = (uint8_t)(capital >> 16); // RS485_TX_BUF[5] = (uint8_t)(capital >> 8); // RS485_TX_BUF[6] = (uint8_t)capital; // // RS485_TX_BUF[7] = (uint8_t)(capitalAfter >> 24); // RS485_TX_BUF[8] = (uint8_t)(capitalAfter >> 16); // RS485_TX_BUF[9] = (uint8_t)(capitalAfter >> 8); // RS485_TX_BUF[10] = (uint8_t)capitalAfter; // break; // // default: // break; // } } /* *********************************************************************** * 函 数 名: RS485_Task * 功能说明: RS485通讯任务 * 形 参: 无 * 返 回 值: 无 *********************************************************************** */ void RS485_Task(void) { uint8_t rs485succeedFlag = 0; uint8_t timerOut = 0; static Com_ProtocolMessage *pMsg; static timer timer_01; static uint8_t timer_ok = 0; if(timer_ok == 0) { timer_ok = 1; timer_set(&timer_01, 4); //创建1个定时器 } pMsg = MessageGet(MSG_FIFO_RS485_PROTOCOL); //从消息对列中取出RS485通讯协议消息 if(pMsg) { // printf("\r\n----->RS485有数据要发送正在加载数据......\r\n"); // printf("\r\n----->RS485msgType = %x\r\n", pMsg->msgType); // printf("\r\n----->RS485msgData = %x\r\n", pMsg->msgData); RS485_PackSendProtocol(pMsg); RS485_SendOne(RS485_TX_BUF); //给从机发送一帧数据 if(pMsg->msgData == 0x99) //广播地址无需等待应答 { RS485_RX_STA = 0; }else{ timer_restart(&timer_01); while(!timerOut && !rs485succeedFlag) { if(timer_expired(&timer_01)) //超时未接收到数据 { timerOut = 1; //超时返回初始状态 printf("\r\n----->RS485TimerOut\r\n"); RS485_RX_STA = 0; } if(RS485_RX_STA == 0xfd) //有接收到从机返回的数据,进行地址和数据验证 { RS485_RX_STA = 0; if(RS485_CommandDecoding(RS485_RX_BUF, RS485_TX_BUF)) //解释返回的数据 { // printf("\r\nmsgType = %x, rxCmd = %x\r\n", pMsg->msgType, rxCmd); if(pMsg->msgType == rxCmd) { rs485succeedFlag = 1; RS485_CommunicationState = 1; //设置本次发送的命令帧成功 printf("\r\n----->RS485<--OK\r\n"); } } } } } } } /******************** (C) COPYRIGHT 2017 YNHB *****END OF FILE*******************/