Modbus xMBUtilSetBits函数注释

该方法用来设置线圈的数据,贴上部分代码。
我们知道Modbus为数据为大端模式(高地址放在低位,低地址放在高位),这样可以按着我们的习惯来设置IO数据。如下为设置多个线圈帧。
在这里插入图片描述
今天就以这样的组合看modbus协议如何发送和解析的协议帧的。
下图是我的测试记录,可以看到差异差异就是倒数第3个字节

框选1:发送的报文Tx:008108-01 0F 0B B8 00 10 02 03 00 43 68
框选2:发送的报文Tx:008116-01 0F 0B B8 00 10 02 03 C0 43 38 在这里插入图片描述
下面先了解下 GD32单片机是大端还是小端模式

    {
    uint16_t data= 0x1122;
    uint8_t *usdat = (uint8_t*)&data;
    LOGE("0x%x,%x",(uint8_t*)&usdat[0],usdat[0]);
    LOGE("0x%x,%x",(uint8_t*)&usdat[1],usdat[1]);
    }

打印结果:
在这里插入图片描述
可以看到,针对GD32单片机来说,数据低位保存在低地址上,即“小弟弟”–>小低低模式。

接着上面的,目前我先把3000开始的2个线圈打开了,可以看到协议数据部分是03 00, 由于modbus是大端模式,单片机是小段模式,则低位的3000和3001寄存器对应的状态是0x03,即低位的2个灯亮了。

30003001~~~~3015
eMBErrorCode
eMBRegCoilsCB(MBObj_t *pMbObj, UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode)
{
    //return MB_ENOREG;
    eMBErrorCode    eStatus = MB_ENOERR;
    short           iNCoils = (short)usNCoils;
    unsigned short  usBitOffset;

    //这里主要是MODBUS上层代码把地址+1了,很想I2C的读地址(+1操作),为此这里把地址进行还原
    usAddress = usAddress -1;
    LOGD("codi");
    /* Check if we have registers mapped at this block. */
    if ((usAddress >= REG_COILS_START) &&
        (usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE))
    {
        usBitOffset = (unsigned short)(usAddress - REG_COILS_START);
        switch (eMode)
        {
            /* Read current values and pass to protocol stack. */
        case MB_REG_READ:
            while (iNCoils > 0)
            {
                *pucRegBuffer++ =
                    xMBUtilGetBits(ucRegCoilsBuf[pMbObj->portId], usBitOffset,
                    (unsigned char)(iNCoils >
                        8 ? 8 :
                        iNCoils));
                iNCoils -= 8;
                usBitOffset += 8;
            }
            break;

            /* Update current register values. */
        case MB_REG_WRITE:
            LOGD("ncoils:%d",iNCoils);
        {  
            UCHAR *Psrc = ucRegCoilsBuf[pMbObj->portId];
            while (iNCoils > 0)
            {//这个地方为线圈写操作
            //重点说的就是这里,这里我们设置16个bit,
            //usBitOffset:首个线圈的偏移。3000-3000 = 0、iNCoils =16.接下来请看下面这个函数的具体实现
                xMBUtilSetBits(Psrc++, usBitOffset,
                    (unsigned char)(iNCoils > 8 ? 8 : iNCoils),
                    *pucRegBuffer++);
                iNCoils -= 8;
            }
            {
            //add by armwind
            UCHAR *PpduBUf = NULL;
            master_ops->pvGetPduBuffer(&PpduBUf);
            if (MB_FUNC_WRITE_SINGLE_COIL == PpduBUf[MB_PDU_FUNC_OFF]) {
                //用户回调方法
            } else if (MB_FUNC_WRITE_MULTIPLE_COILS == PpduBUf[MB_PDU_FUNC_OFF]) {
                //用户回调方法
                }
            }
        }
            break;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}

//注意这里,我们我们线圈寄存器基地址为3000,假设bitoffset为6,ucNBits为3,ucValue=0x03,
//即3006开始的3个bit

//ucByteBuf:寄存器首地址
//usBitOffset:寄存器偏移地址
//ucNBits:线圈数量
//线圈状态值
void
xMBUtilSetBits( UCHAR * ucByteBuf, USHORT usBitOffset, UCHAR ucNBits,
                UCHAR ucValue )
{
    USHORT          usWordBuf;
    USHORT          usMask;
    USHORT          usByteOffset;
    USHORT          usNPreBits;
    USHORT          usValue = ucValue;

    assert( ucNBits <= 8 );
    assert( ( size_t )BITS_UCHAR == sizeof( UCHAR ) * 8 );

    /* Calculate byte offset for first byte containing the bit values starting
     * at usBitOffset. 偏移地址换算成几个字节,这里usByteOffset=0*/
    usByteOffset = ( USHORT )( ( usBitOffset ) / BITS_UCHAR );

    /* How many bits precede our bits to set. 多出来的bit数,usNPreBits=6*/
    usNPreBits = ( USHORT )( usBitOffset - usByteOffset * BITS_UCHAR );

    /* Move bit field into position over bits to set */
    usValue <<= usNPreBits;  //0x03<<6,数据对准窗口

    /* Prepare a mask for setting the new bits. ‬*/
    usMask = ( USHORT )( ( 1 << ( USHORT ) ucNBits ) - 1 );//计算数据bit掩码,1<<3-1,即0x07
    usMask <<= usBitOffset - usByteOffset * BITS_UCHAR; //数据bit掩码,滑动到对应的数据区域

    /* copy bits into temporary storage. */
    usWordBuf = ucByteBuf[usByteOffset]; //获取低地址数据
    usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UCHAR; //获取高位地址数据

    /* Zero out bit field bits and then or value bits into them. */
    // 这一步先将数据区域清0,然后再与上赋予的数据
    usWordBuf = ( USHORT )( ( usWordBuf & ( ~usMask ) ) | usValue );

    /* move bits back into storage */
    ucByteBuf[usByteOffset] = ( UCHAR )( usWordBuf & 0xFF );
    ucByteBuf[usByteOffset + 1] = ( UCHAR )( usWordBuf >> BITS_UCHAR );
}
  • 获取数据bit掩码
    在这里插入图片描述

  • 将数据滑动到指定偏移的窗口区
    在这里插入图片描述
    然后想根据掩码,与非后清空数据区,再按位或即可将数据更新进去

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值