华大HC32L176与三相四线计量模块JSY_333通讯例程以及对三相三线认识误区

在某宝购买这个产品后,需要编写程序读取数据,这款产品可以使用TTL和RS485进行通讯。
在这里插入图片描述
在这里插入图片描述
我用的是用华大单片机HC32L176,首先对串口进行初始化。程序可以自行下载。
链接:https://pan.baidu.com/s/1FD2VecV64ZHFxJ8v5H3ZNA
提取码:wy6d

目录

  • 串口程序编写
  • MODBUS CRC16检验
  • MODBUS 03H功能码
  • 读取到数据后解析数据
  • 测量与标准台数据对比
  • 三相三线认识误区

串口程序编写

结构体定义:
typedef struct
{u8 Busy : 1;u8 SendFlag : 1;u8 : 6;u16 Len;u16 Cnt;u8 Buff[100]; //
} UART_TxDef;
typedef struct
{u8 led : 1;u8 Finish : 1; // 串口接收完成u8 IRQ : 1;    // 串口接收中u8 Flag : 2;u8 WaitRx : 4;u16 TimeOut;u16 nRxLen;u8 ucData[550]; // 
} UART_RxDef;
typedef struct
{UART_TxDef TX;UART_RxDef RX;
} UART_Def;
extern UART_Def RS485_UART;
extern UART_Def SLAVE_UART;
extern UART_Def BLE_UART;
extern UART_Def USB_UART;
#define RS485_BUFF_LEN sizeof(RS485_UART.RX.ucData) - 10
#define SLAVE_BUFF_LEN sizeof(SLAVE_UART.RX.ucData) - 10
#define BLE_BUFF_LEN sizeof(BLE_UART.RX.ucData) - 10
#define USB_BUFF_LEN sizeof(USB_UART.RX.ucData) - 10
void HC32_UART_Init(void)
{App_UartPortCfg();///< 外设模块时钟使能Sysctrl_SetPeripheralGate(SysctrlPeripheralLpUart0, TRUE);Sysctrl_SetPeripheralGate(SysctrlPeripheralLpUart1, TRUE);Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0, TRUE); ///<Sysctrl_SetPeripheralGate(SysctrlPeripheralUart1, TRUE);RS485_SLAVE_UartCfg(RS485_COM, 0x06);RS485_SLAVE_UartCfg(SLAVE_COM, 0x06);BLE_USB_UartCfg(BLE_COM, 9600);BLE_USB_UartCfg(USB_COM, 115200);EnableNvic(UART0_2_IRQn, IrqLevel2, TRUE);EnableNvic(UART1_3_IRQn, IrqLevel2, TRUE);EnableNvic(LPUART0_IRQn, IrqLevel2, TRUE); ///< 系统中断使能EnableNvic(LPUART1_IRQn, IrqLevel2, TRUE); ///< 系统中断使能
}

串口数据发送接收

u16 GET_UARTx_RECEIVE_PACKET(UART_Def *UARTx_MAKE, u8 *m_pData)
{if (UARTx_MAKE->RX.nRxLen == 0 && UARTx_MAKE->RX.Finish == SET)return 0;memcpy(m_pData, UARTx_MAKE->RX.ucData, UARTx_MAKE->RX.nRxLen);return UARTx_MAKE->RX.nRxLen;
}void UART_CLEAR_RECEIVE_DATA(UART_Def *UARTx_MAKE)
{DDL_ZERO_STRUCT(UARTx_MAKE->RX);
}
void RS485_SLAVE_SEND_PACKET(u8 Flag, M0P_UART_TypeDef *UARTx, UART_Def *UARTx_MAKE, u8 *pData, u8 len)
{static u32 TimeulValue=0;TimeulValue= GetSysTick();BLE_USB_SEND_PACKET(bUSB,USB_COM,&USB_UART,pData,len);while (UARTx_MAKE->TX.Busy == SET) // 先查忙,按照次方法两帧需有间隔时间,使用需注意{if (CompareSysTick(TimeulValue, 100)){break;}}if (Flag == bRS485){RS485_Tx();}StrTruncate(pData, UARTx_MAKE->TX.Buff, 0, 0, len);UARTx_MAKE->TX.Len = len;UARTx_MAKE->TX.Cnt = 0;Uart_EnableIrq(UARTx, UartTxIrq); // 打开发送中断Uart_SendDataIt(UARTx, UARTx_MAKE->TX.Buff[UARTx_MAKE->TX.Cnt]);
}
void BLE_USB_SEND_PACKET(u8 Flag, M0P_LPUART_TypeDef *UARTx, UART_Def *UARTx_MAKE, u8 *pData, u8 len)
{static u32 TimeulValue=0;TimeulValue= GetSysTick();while (UARTx_MAKE->TX.Busy == SET) // 先查忙,按照次方法两帧需有间隔时间,使用需注意{if (CompareSysTick(TimeulValue, 100)){break;}}LPUart_EnableIrq(UARTx, LPUartTxIrq); // 打开发送中断StrTruncate(pData, UARTx_MAKE->TX.Buff, 0, 0, len);UARTx_MAKE->TX.Len = len;UARTx_MAKE->TX.Cnt = 0;LPUart_SendDataIt(UARTx, UARTx_MAKE->TX.Buff[UARTx_MAKE->TX.Cnt]);
}

MODBUS CRC16检验

uint16_t GetModBusCRC16(uint8_t *aucData, u16 iBytesCount)
{uint8_t wHi = 0;uint8_t wLo = 0;uint16_t wCRC = 0xFFFF;u16 i, j;uint8_t wCheck = 0;for (i = 0; i < iBytesCount; i++){Wdt_Feed();wCRC ^= aucData[i];for (j = 0; j < 8; j++){wCheck = wCRC & 1;wCRC = wCRC >> 1;wCRC = wCRC & 0x7fff;if (wCheck == 1)wCRC = wCRC ^ 0xa001;wCRC = wCRC & 0xffff;}}wHi = wCRC / 256;wLo = wCRC % 256;wCRC = (wHi << 8) | wLo;return wCRC;
}

MODBUS 03H功能码

typedef struct
{u8 Addr;           // 设备地址u8 FunCmd;         // 功能码u8 RegAdd[2];      // 寄存器地址u8 RegNum[2];      // 寄存器长度u8 CRC[2];         // CRC 校验
} MODBUS_03M_FrameDef; // 主发格式
typedef struct
{u8 Addr;           // 设备地址u8 FunCmd;         // 功能码u8 ByteNum;        // 返回字节数u8 DATA[260];      // 寄存器数据+CRC
} MODBUS_03S_FrameDef; // 设备应答格式
// 设备只读
/*** @description:* @param {u8} Addr* @param {u16} Reg* @param {u16} len* @param {u8} *SendPacket* @return {*}* @author: Hcc*/
u16 Modbus_Read_03_Tx(u8 Addr, u16 Reg, u16 len, u8 *SendPacket)
{MODBUS_03M_FrameDef *M_Send = (MODBUS_03M_FrameDef *)SendPacket;u16 CRC_16 = 0;M_Send->Addr = Addr;M_Send->FunCmd = 0x03;U16T0Array(&Reg, M_Send->RegAdd); // 0x0048转为0x00 0x48U16T0Array(&len, M_Send->RegNum);CRC_16 = GetModBusCRC16(&M_Send->Addr, 6);M_Send->CRC[0] = (CRC_16)&0xFF;M_Send->CRC[1] = (CRC_16 >> 8) & 0xFF;return 8; // 需要发送8个字节
}
/*** @description: 收到从设备返回数据,需要校验* @param {u8} *RxAckData* @param {u16} Len* @return {*}* @author: Hcc*/
u16 Modbus_Read_03_Rx(u8 *RxAckData, u16 Len)
{MODBUS_03S_FrameDef *M_Recv = (MODBUS_03S_FrameDef *)RxAckData;u16 CRC_16 = 0;if (Len < 5){return 0;}if (M_Recv->FunCmd != 0x03){return 0;}CRC_16 = GetModBusCRC16(&M_Recv->Addr, Len - 2);if (CRC_16 == (M_Recv->DATA[M_Recv->ByteNum + 1] << 8 | M_Recv->DATA[M_Recv->ByteNum])){return Len; // 验证正确}return 0; //
}

串口发送数据后等待应答

/*** @description: 读取设备型号* @return {*}0 获取到设备型号 1正在发送 2等待接收 3接收超时或者数据错误* @author: Hcc*/
u8 Modbus_Read_ModelNumber_TTL(void)
{u8 result = 0xFF;static u8 Setp = 0;switch (Setp){case 0:strMODBUS.DeviceT = 0;strMODBUS.TxLen = Modbus_Read_03_Tx(0x00, 0x0000, 5, strMODBUS.TxBuff);RS485_SLAVE_SEND_PACKET(bSLAVE, SLAVE_COM, &SLAVE_UART, strMODBUS.TxBuff, strMODBUS.TxLen);UART_CLEAR_RECEIVE_DATA( &SLAVE_UART);strMODBUS.TimeOut = GetSysTick();strMODBUS.TX = SET;Setp = 1;result = 1; // 正在发送break;case 1:if (CompareSysTick(strMODBUS.TimeOut, 500)) // 500ms超时,未接收到数据{Setp = 0;result = 3;break;}strMODBUS.RxLen = GET_UARTx_RECEIVE_PACKET(&SLAVE_UART, strMODBUS.RxBuff);if (strMODBUS.RxLen != 0) // 有数据返回{if (Modbus_Read_03_Rx(strMODBUS.RxBuff, strMODBUS.RxLen) != 0) // 校验数据{UART_CLEAR_RECEIVE_DATA(&SLAVE_UART);ArrayToU16(&strMODBUS.RxBuff[3], &strMODBUS.DeviceT); // 收到设备型号strMODBUS.Addr = strMODBUS.RxBuff[11];strMODBUS.Baud = strMODBUS.RxBuff[12];Setp = 0;result = 0;}}break;default:Setp = 0;break;}return result;
}

读取到数据后解析数据

typedef struct
{float U[3];//电压float I[3];//电流float P[4];//有功功率float Q[4];//无功功率float S[4];//视在功率float UFrq;//频率float Pf[4];//功率因素float pE[4];//有功电能float qE[4];//无功电能float sE[4];//视在电能u16 DIR;//功率方向u16 ALM;//报警状态
} JSY333_P_Def;
static LCD_MENU_Def LCD_MENU;
static JSY333_P_Def JSY333_P; // 333电气参数
static void GetJSY_333_Parameter(void)
{static u32 TickTime = 0;int Index = 0;MODBUS_03S_FrameDef *RecvData;if (CompareSysTick(TickTime, 1000) == 0) // 500ms读一次数据{return;}LCD_MENU.LED = RESET;RecvData = (MODBUS_03S_FrameDef *)strMODBUS.RxBuff;if (Modbus_Read_ModelParameter(bSLAVE, SLAVE_COM, &SLAVE_UART, strMODBUS.Addr, 0x0100, 0x34) == 0) // 获取到数据{TickTime = GetSysTick();LCD_MENU.LED = SET;for (Index = 0; Index < 3; Index++) //{ArrayToU16(&RecvData->DATA[0 + Index * 2], &strSystem.usValue);  // 电压JSY333_P.U[Index] = strSystem.usValue / 100.0;                   // 整数转为浮点数必须要加.0ArrayToU16(&RecvData->DATA[6 + Index * 2], &strSystem.usValue);  // 电流JSY333_P.I[Index] = strSystem.usValue / 100.0;                   // 整数转为浮点数必须要加.0ArrayToU16(&RecvData->DATA[12 + Index * 2], &strSystem.usValue); // 有功功率JSY333_P.P[Index] = strSystem.usValue;                           // 整数转为浮点数必须要加.0ArrayToU16(&RecvData->DATA[22 + Index * 2], &strSystem.usValue); // 无功功率JSY333_P.Q[Index] = strSystem.usValue;                           // 整数转为浮点数必须要加.0ArrayToU16(&RecvData->DATA[32 + Index * 2], &strSystem.usValue); // 视在功率JSY333_P.S[Index] = strSystem.usValue;                           // 整数转为浮点数必须要加.0}ArrayToU32(&RecvData->DATA[18], &strSystem.ulValue); // 有功功率JSY333_P.P[3] = strSystem.ulValue;                   // 整数转为浮点数必须要加.0ArrayToU32(&RecvData->DATA[28], &strSystem.ulValue); // 无功功率JSY333_P.Q[3] = strSystem.ulValue;                   // 整数转为浮点数必须要加.0ArrayToU32(&RecvData->DATA[38], &strSystem.ulValue); // 视在功率JSY333_P.S[Index] = strSystem.ulValue;               // 整数转为浮点数必须要加.0ArrayToU16(&RecvData->DATA[42], &strSystem.usValue); // 频率JSY333_P.UFrq = strSystem.usValue / 100.0;           // 整数转为浮点数必须要加.0for (Index = 0; Index < 4; Index++){                                                                    //ArrayToU16(&RecvData->DATA[44 + Index * 2], &strSystem.usValue); // 功率因素JSY333_P.Pf[Index] = strSystem.usValue / 1000.0;                 // 整数转为浮点数必须要加.0ArrayToU32(&RecvData->DATA[52 + Index * 4], &strSystem.ulValue); // 有功电能JSY333_P.pE[Index] = strSystem.ulValue / 100.0;                  // 整数转为浮点数必须要加.0ArrayToU32(&RecvData->DATA[68 + Index * 4], &strSystem.ulValue); // 有功电能JSY333_P.qE[Index] = strSystem.ulValue / 100.0;                  // 整数转为浮点数必须要加.0ArrayToU32(&RecvData->DATA[84 + Index * 4], &strSystem.ulValue); // 有功电能JSY333_P.sE[Index] = strSystem.ulValue / 100.0;                  // 整数转为浮点数必须要加.0}ArrayToU16(&RecvData->DATA[100], &JSY333_P.DIR); // 功率方向ArrayToU16(&RecvData->DATA[102], &JSY333_P.ALM); // 报警状态}
}

根据产品说明书,这款产品测量的电气参数太多,大家可以举一反三读取线电压、相位角、谐波等数据。

测量与标准台数据对比

在这里插入图片描述

在这里插入图片描述

三相三线认识误区

很多人以为三相三线和三相四线一样,A B C三相间隔120°,其实不然
三相三线实际测量图
在这里插入图片描述
接线方式,R相电流I1和R-T电压U13接到一个功率测量单元,计算的功率记为P1=I1•U13;S相电流I2和S-T电压U23接到一个功率测量单元,功率记为P2=I2•U23;T相电流I3和R-S电压U12接到一个功率测量单元,计算的功率记为P3=I3•U12。同前述的推导方式:
在这里插入图片描述
由此可见3V3A接线本质就是经典的两瓦特计法,多了P3=I3•U12,但计算总功率并不用P3。两瓦特计法是电工测量的基本常识,选取其中一相(T)作为参考点,根据基尔霍夫电流定律另外两线的电流(I1、I2)全部回流到参考点,总功率为另外两线分别对参考点电压(URT、UST)产生的功率之和。
结论和推论:
• 3V3A实质为两瓦特计法,三相总功率为P1+P2。
• 单个功率值没有物理意义,仪器测量的单个功率是线电压和线电流相乘,实际各相的电流不是单个线电压产生的结果,所以单个功率表的计算结果P1或者P2不代表某一相的功率,第三个测量单元是RS线电压和T相线电流的乘积,这个功率也是没有意义的。

• 一些特殊的情况:结合下图,在三相平衡系统中阻性负载时,P1>0,P2>0,P3=0;感性平衡负载时,相比阻性电压超前电流,电压将逆时针旋转一定角度,P3>0,当大于60°时加上纯阻性时UST超前I2的 30°共大于90°,则P2<0;容性平衡负载时,相比阻性电压落后电流,电压将顺时针旋转一定角度,P3<0,当大于60°时加上纯阻性时URT落后I1的 30°共大于90°,则P1<0。

电压向量图
在这里插入图片描述
电压电流向量图
在这里插入图片描述
大家有其他问题可以QQ交流:953091376


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部