feat: add STM32 four-channel weighing firmware

This commit is contained in:
FinleyHsu
2026-07-28 11:01:55 +08:00
commit 1282b9b77e
205 changed files with 80376 additions and 0 deletions
@@ -0,0 +1,672 @@
/*************************** (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*******************/