Files
SmartBin_STM_Woer/STM32_4路称重_Git提交精简工程/src/HARDWARE/RS485/RS485.c
T

673 lines
20 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*************************** (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<TX_Size; i++) //发送数据
{
RS485_SendData(*(s+i+3));
CS += (*(s+i+3));
}
RS485_SendData(CS); //发送校验字节
RS485_SendData(0x16); //发送帧尾
delay_ms(1);
CTRL485 = 0; //一帧发送完毕,恢复信道切换功能
}
/*
***********************************************************************
* 函 数 名: Serial_AddrxDatas
* 功能说明: 通讯地址判断
* 形 参: s 指向从通讯地址
* 返 回 值: 0x00 错误,0x01 正确,0x02 广播地址
***********************************************************************
*/
static uint8_t Serial_Address(uint8_t *s)
{
// printf("\r\n地址:%x\r\n", *s);
if(*s == 0x99)
{
// printf("\r\n收到广播指令\r\n");
return(0x02); //是广播地址
}
if(*s == Address)
{
// printf("\r\n地址匹配正确\r\n");
return(0x01); //地址匹配正确
}
// printf("\r\n地址匹配不正确\r\n");
return(0x00); //地址匹配不正确
}
/*
***********************************************************************
* 函 数 名: RS485_CommandDecoding
* 功能说明: 执行通讯命令
* 形 参: s 发送数据帧地址指针
* 返 回 值: 0 错误,1 正确
***********************************************************************
*/
uint8_t RS485_CommandDecoding(uint8_t *pRxBuf, uint8_t *pTxBuf)
{
printf("\r\nRS485_%x <-- %x\r\n", *(pRxBuf+3), *(pRxBuf+1));
rxCmd = *(pRxBuf+1);
switch(Serial_Address(pRxBuf)) //判断地址是否正确
{
case 0x02: //广播地址0x99
switch(*(pRxBuf+1)) //通讯命令译码
{
case 0x20: //主机广播开门命令
printf("\r\n收到广播开门指令\r\n");
radioCom = 1;
isOpenDoor = 1;
break;
default:
break;
}
break;
case 0x01: //正常地址
slaveAddress = *(pRxBuf+3); //取出应答从机的地址
switch(*(pRxBuf+1)) //通讯命令译码
{
case 0x00: //应答命令
break;
case 0x01: //读取超声波测量值
break;
case 0x10: //开门指令
// switch(slaveAddress)
// {
// case 0x11: //从机1的分机1返回数据
// devRunningStatusNO_1 = 1;
// break;
//
// case 0x12: //从机1的分机2返回数据
// devRunningStatusNO_2 = 1;
// break;
//
// case 0x21: //从机2的分机1返回数据
// devRunningStatusNO_3 = 1;
// break;
//
// case 0x22: //从机2的分机2返回数据
// devRunningStatusNO_4 = 1;
// break;
//
// default:
// printf("\r\nError_0x10指令出错\r\n");
// break;
// }
break;
case 0x11: //获取开门控制运行状态指令
// switch(slaveAddress)
// {
// case 0x11: //从机1的分机1返回数据
// devRunningStatusNO_1 = *(pRxBuf+4);
// printf("\r\nRX_Data = 0x%02X\r\n", *(pRxBuf+4));
// break;
//
// case 0x12: //从机1的分机2返回数据
// devRunningStatusNO_2 = *(pRxBuf+4);
// printf("\r\nRX_Data = 0x%02X\r\n", *(pRxBuf+4));
// break;
//
// case 0x21: //从机2的分机1返回数据
// devRunningStatusNO_3 = *(pRxBuf+4);
// printf("\r\nRX_Data = 0x%02X\r\n", *(pRxBuf+4));
// break;
//
// case 0x22: //从机2的分机2返回数据
// devRunningStatusNO_4 = *(pRxBuf+4);
// printf("\r\nRX_Data = 0x%02X\r\n", *(pRxBuf+4));
// break;
//
// default:
// printf("\r\nError_0x10指令出错\r\n");
// break;
// }
break;
case 0x12: //获取从机测距数据指令
// switch(slaveAddress)
// {
// case 0x10:
// case 0x11:
// case 0x12:
// getDistance1 = *(pRxBuf+4); //玻璃
// getDistance2 = *(pRxBuf+5); //金属
// break;
//
// case 0x20:
// case 0x21:
// case 0x22:
// getDistance3 = *(pRxBuf+4); //瓶子
// getDistance4 = *(pRxBuf+5); //塑料
// break;
//
// }
// printf("\r\nRX_Data = %d, %d\r\n", *(pRxBuf+4), *(pRxBuf+5));
break;
case 0x13: //获取从机投放数据指令
// switch(slaveAddress)
// {
// case 0x11: //从机1的分机1返回数据(注意:此称重由第2路秤返回)
// getWeight4 = (uint16_t)(*(pRxBuf+4)) << 8; //取出重量高8位数据
// getWeight4 |= *(pRxBuf+5); //取出重量低8位数据
//
// getWeightBefore4 = (uint16_t)(*(pRxBuf+6)) << 8; //取出重量高8位数据
// getWeightBefore4 |= *(pRxBuf+7); //取出重量低8位数据
//
// getWeightAfter4 = (uint16_t)(*(pRxBuf+8)) << 8; //取出重量高8位数据
// getWeightAfter4 |= *(pRxBuf+9); //取出重量低8位数据
//
// getDistance1 = *(pRxBuf+10); //取出测距数据
//
// faultCodeNO_1 = *(pRxBuf+11); //取出设备故障码
//
// devRunningStatusNO_1 = 3;
// printf("\r\nRX_Data1 = %d, %d, %d, %d, %d\r\n", getWeight4, getWeightBefore4, getWeightAfter4, getDistance1, faultCodeNO_1);
// break;
//
// case 0x12: //从机1的分机2返回数据(注意:此称重由第3路秤返回)
// getWeight3 = (uint16_t)(*(pRxBuf+4)) << 8; //取出重量高8位数据
// getWeight3 |= *(pRxBuf+5); //取出重量低8位数据
//
// getWeightBefore3 = (uint16_t)(*(pRxBuf+6)) << 8; //取出重量高8位数据
// getWeightBefore3 |= *(pRxBuf+7); //取出重量低8位数据
//
// getWeightAfter3 = (uint16_t)(*(pRxBuf+8)) << 8; //取出重量高8位数据
// getWeightAfter3 |= *(pRxBuf+9); //取出重量低8位数据
//
// getDistance2 = *(pRxBuf+10); //取出测距数据
//
// faultCodeNO_2 = *(pRxBuf+11); //取出设备故障码
//
// devRunningStatusNO_2 = 3;
// printf("\r\nRX_Data2 = %d, %d, %d, %d, %d\r\n", getWeight3, getWeightBefore3, getWeightAfter3, getDistance2, faultCodeNO_2);
// break;
//
// case 0x21: //从机2的分机1返回数据(注意:此称重由第3路秤返回)
// getWeight2 = (uint16_t)(*(pRxBuf+4)) << 8; //取出重量高8位数据
// getWeight2 |= *(pRxBuf+5); //取出重量低8位数据
//
// getWeightBefore2 = (uint16_t)(*(pRxBuf+6)) << 8; //取出重量高8位数据
// getWeightBefore2 |= *(pRxBuf+7); //取出重量低8位数据
//
// getWeightAfter2 = (uint16_t)(*(pRxBuf+8)) << 8; //取出重量高8位数据
// getWeightAfter2 |= *(pRxBuf+9); //取出重量低8位数据
//
// getDistance3 = *(pRxBuf+10); //取出测距数据
//
// faultCodeNO_3 = *(pRxBuf+11); //取出设备故障码
//
// devRunningStatusNO_3 = 3;
// printf("\r\nRX_Data3 = %d, %d, %d, %d, %d\r\n", getWeight2, getWeightBefore2, getWeightAfter2, getDistance3, faultCodeNO_3);
// break;
//
// case 0x22: //从机2的分机2返回数据
// getWeight1 = (uint16_t)(*(pRxBuf+4)) << 8; //取出重量高8位数据
// getWeight1 |= *(pRxBuf+5); //取出重量低8位数据
//
// getWeightBefore1 = (uint16_t)(*(pRxBuf+6)) << 8; //取出重量高8位数据
// getWeightBefore1 |= *(pRxBuf+7); //取出重量低8位数据
//
// getWeightAfter1 = (uint16_t)(*(pRxBuf+8)) << 8; //取出重量高8位数据
// getWeightAfter1 |= *(pRxBuf+9); //取出重量低8位数据
//
// getDistance4 = *(pRxBuf+10); //取出测距数据
//
// faultCodeNO_4 = *(pRxBuf+11); //取出设备故障码
//
// devRunningStatusNO_4 = 3;
// printf("\r\nRX_Data4 = %d, %d, %d, %d, %d\r\n", getWeight1, getWeightBefore1, getWeightAfter1, getDistance4, faultCodeNO_4);
// break;
//
// default:
// printf("\r\nError_0x10指令出错\r\n");
// break;
// }
break;
case 0x14: //获取从机投放数据数据指令(这里只获取瓶子+塑料箱的数据)
// getWeight2 = (uint16_t)(*(pRxBuf+4)) << 8; //取出重量高8位数据
// getWeight2 |= *(pRxBuf+5); //取出重量低8位数据
//
// getWeightBefore2 = (uint16_t)(*(pRxBuf+6)) << 8; //取出重量高8位数据
// getWeightBefore2 |= *(pRxBuf+7); //取出重量低8位数据
//
// getWeightAfter2 = (uint16_t)(*(pRxBuf+8)) << 8; //取出重量高8位数据
// getWeightAfter2 |= *(pRxBuf+9); //取出重量低8位数据
//
// getDistance3 = *(pRxBuf+10); //取出测距数据
// faultCodeNO_3 = *(pRxBuf+11); //取出设备故障码
//
// devRunningStatusNO_3 = 3;
//
// getSmallBottleCount = (uint16_t)(*(pRxBuf+12)) << 8; //取出瓶子高8位数据
// getSmallBottleCount |= *(pRxBuf+13); //取出瓶子低8位数据
// getDistance5 = *(pRxBuf+14); //取出测距数据
// printf("\r\nRX_Data3 = %d, %d, %d, %d, %d\r\n", getWeight2, getWeightBefore2, getWeightAfter2, getDistance3, faultCodeNO_3);
//
break;
case 0x20: //获取从机设备的当前状态指令
// switch(slaveAddress)
// {
// case 0x11: //从机1的分机1返回数据
// deviceStatusNO_1 = *(pRxBuf+4);
// break;
//
// case 0x12: //从机1的分机2返回数据
// deviceStatusNO_2 = *(pRxBuf+4);
// break;
//
// case 0x21: //从机2的分机1返回数据
// deviceStatusNO_3 = *(pRxBuf+4);
// break;
//
// case 0x22: //从机2的分机2返回数据
// deviceStatusNO_4 = *(pRxBuf+4);
// break;
//
// default:
// printf("\r\nError_0x20指令出错\r\n");
// break;
// }
break;
case 0x30: //主机发送用户信息应答
break;
case 0x31: //主机发送用户投放奖励信息应答
break;
case 0x32: //主机发送退出显示界面应答
break;
case 0x39: //主机发送退出操作应答
break;
default: //其他命令
break;
}
break;
case 0x00: //地址错误
return 0;
}
return 1; //帧执行正确,返回
}
/*
***********************************************************************
* 函 数 名: RS485_PackSendProtocol
* 功能说明: 根据RS485消息打包发送命令帧
* 形 参: msg -> 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*******************/