nrf 52xxx RS485 MODBUS-RTU

今天开始攒代码了,,, nrf 52xxx MODBUS
注意一点:
MODBUS数据域是大端的但是CRC校验码是小端的。

#define SYS_BUSY_MS (g_run2MsTick<<1)

2毫秒定时器判断一帧数据接收完成

void uart_chk_rcv_over_timer_handler(void *p_text)
{
    ++g_run2MsTick;
    //如果正在接收 且 空闲时间大于1个tick 认为接收一帧结束
    if((g_uartCrl.is_rcv_busy == UART_RCV_BUSY) \
    && (SYS_BUSY_MS > (2 + g_uartCrl.rcvTime)))
    {
        g_RS485BuffCtrl.rxLen = g_uartCrl.now_rcv_tail;
        g_RS485BuffCtrl.rxFlag = 1;
        g_uartCrl.now_rcv_tail = 0;
        g_uartCrl.is_rcv_busy = UART_RCV_IDLE;
    }
}

串口中断事件处理:
a、接收数据事件:接收数据并设置接收忙碌状态标志
b、发送完成事件: 切换 485方向为输入

/**@snippet [Handling the data received over UART] */
static void uart_event_handle(app_uart_evt_t * p_event)
{
    switch (p_event->evt_type)
    {
        case APP_UART_DATA_READY:
        	g_uartCrl.is_rcv_busy = UART_RCV_BUSY;
        	if(g_uartCrl.now_rcv_tail >= RS485_RX_MAX)
           		g_uartCrl.now_rcv_tail = 0;
        	UNUSED_VARIABLE(app_uart_get((uint8_t *)&g_RS485BuffCtrl.rxBuff[g_uartCrl.now_rcv_tail++]));
        	g_uartCrl.rcvTime = SYS_BUSY_MS;
            break;

        case APP_UART_COMMUNICATION_ERROR:
//            APP_ERROR_HANDLER(p_event->data.error_communication);
            break;

        case APP_UART_FIFO_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_code);
            break;

        case APP_UART_TX_EMPTY:
		  	DIR_485_IN();
            break;
        default:
            break;
    }
}

485 硬件驱动层

#include "app_inc.h"
#include "app_uart.h"
#include "nordic_common.h"
#include "nrf.h"
#include "boards.h"

#if defined (UART_PRESENT)
#include "nrf_uart.h"
#endif
#if defined (UARTE_PRESENT)
#include "nrf_uarte.h"
#endif

UartCtrol g_uartCrl = UartCtrolDefault;
/**@brief 将串口引脚设为普通IO口工作模式
 */
void SetUartPinDefaultMode()
{
    if(g_uartCrl.pinMode == UART_PIN_MODE_UART)
    {
        app_uart_close();
        g_uartCrl.pinMode = UART_PIN_MODE_DEFAULT;
    }
}
/**@brief 将串口引脚设为串口工作模式
 */
void SetUartPinUartMode(void)
{
    if(g_uartCrl.pinMode == UART_PIN_MODE_DEFAULT)
    {
        uart_init();
        g_uartCrl.pinMode = UART_PIN_MODE_UART;
    }
}
/**@brief 485初始化
 */
void Rs485Init(void)
{
    SetUartPinUartMode();
	DIR_485_INIT();
	DIR_485_IN();
}
/**@brief 串口发送函数
 */
void UartSend(uint8_t *p_data,uint8_t len)
{
    uint8_t i ;
    SetUartPinUartMode();DIR_485_OUT();
    for(i = 0;i < len;i++)
    {
        app_uart_put(p_data[i]);
    }
}

#ifndef __UART_H_
#define __UART_H_
#include <stdint.h>
#include <stdbool.h>

#define UART_RCV_BUSY    true 	///< 接收忙碌,接收中断置1
#define UART_RCV_IDLE    false	///< 接收空闲,超时检测置0

#define UART_PIN_MODE_UART     true
#define UART_PIN_MODE_DEFAULT  false

//485 收发控制
#define DIR_485_INIT()      do{nrf_gpio_cfg_output(RS485_DIR_PIN);}while(0)
#define DIR_485_IN()		do{nrf_gpio_pin_write(RS485_DIR_PIN,0);}while(0)
#define DIR_485_OUT()		do{nrf_gpio_pin_write(RS485_DIR_PIN,1);}while(0)

/*串口接收和发送控制*/
typedef struct {
    bool pinMode;     		///< 串口引脚工作模式
    bool is_rcv_busy; 		///< 没有数据在接收 1:有数据正在接收
    uint8_t now_rcv_tail;	///< 现在这一帧的接收字节数
    uint64_t rcvTime; 		///< 接受最近一个字节的时刻
}UartCtrol;

#define UartCtrolDefault                \
{                                       \
    .pinMode = UART_PIN_MODE_DEFAULT,   \
    .is_rcv_busy = UART_RCV_IDLE,       \
    .now_rcv_tail = 0,                  \
    .rcvTime = 0,                       \
};

/*var declare................................*/
extern UartCtrol g_uartCrl;
/*fun declare................................*/
void Rs485Init(void);
void UartSend(uint8_t *p_data,uint8_t len);
#endif

modbus.c

#include "modbus.h"
#include "app_inc.h"
#include "app_uart.h"

typedef MBErrType ModbusParseRet;   ///<  Modbus 解析结果
    
typedef struct {
    const uint8_t funCode;                              ///<  功能码
    ModbusParseRet (*parse)(uint8_t *rcv, uint8_t rcvlen, uint8_t *pack);  ///<  处理函数
}ModbusRtuNode;

#define MB_DEV_ADDR  1  ///< @todo: how to set addr?
#define MbParse(name)  static ModbusParseRet name (uint8_t *rcv, uint8_t rcvlen, uint8_t *pack)

static uint8_t s_packLen = 0;  ///< 回复数据中功能码和crc之间的byte数

/** @brief 根据ModBus规则计算CRC16
 *  @para _pBuf:待计算数据缓冲区,计算得到的结果存入_pBuf的最后两字节
 *        _usLen:待计算数据长度(字节数)
 *  @retval 16位校验值
 */
static uint16_t getModbusCRC16(uint8_t *_pBuf,uint16_t _usLen)
{
    unsigned short CRCValue = 0xFFFF;               ///< 初始化CRC变量各位为1
    unsigned char i,j;

    for(i=0;i<_usLen;++i){
        CRCValue ^= *(_pBuf+i);                 	///< 当前数据异或CRC低字节
        for(j=0;j<8;++j){                           ///< 一个字节重复右移8次
            if((CRCValue & 0x01) == 0x01)           ///< 判断右移前最低位是否为1
                 CRCValue = (CRCValue >> 1)^0xA001; ///< 如果为1则右移并异或表达式
            else
                CRCValue >>= 1;                     ///< 否则直接右移一位
        }
    } 
    return CRCValue;            
}

/** @brief 判断通信接收的寄存器地址是否属于正常范围
 *  @retval true 非法地址    false 合法地址
 */
static inline bool MbRegAddrIllegal(uint16_t addr)
{
    if((addr >= MB_INPUT_REG_START) && (addr <= MB_INPUT_REG_END))
        return false;
    if((addr >= MB_HOLD_REG_START) && (addr <= MB_HOLD_REG_END))
        return false;
    return true;
}

/** @brief  打包 MODBUS 响应帧
 *  @para  pdu-包括功能码和数据域  pduLen-功能码和数据域的长度
 */
static void PackMbRsp(uint8_t *pdu, uint8_t pduLen)
{
    uint8_t *pTx = g_RS485TxBuff;
    uint16_t crc;
    
    if((NULL == pdu) || (1 >= pduLen))
        return;
    
    pTx[0] = MB_DEV_ADDR;
    
    memcpy(&pTx[1], pdu, pduLen);
    
    crc = getModbusCRC16(pTx, pduLen + 1);
    pTx[pduLen + 1] = crc & 0xFF;
    pTx[pduLen + 2] = crc >> 8;
    //g_RS485BuffCtrl.txLen = pduLen + 3; g_RS485BuffCtrl.txFlag = true;
    UartSend(pTx, pduLen + 3);
}

/** @brief  打包 MODBUS 异常响应帧
 *  @para  funcode-功能码  errCode-错误码
 */
static void PackMbErrRsp(uint8_t funcode, uint8_t errCode)
{
    uint8_t *pTx = g_RS485TxBuff;
    uint16_t crc;
    
    if(MODBUS_NO_ERR == errCode)
	    return;
	    
    pTx[0] = MB_DEV_ADDR;
    pTx[1] = funcode | 0x80;
    pTx[2] = errCode;
    crc = getModbusCRC16(pTx, 3);
    pTx[3] = crc & 0xFF;
    pTx[4] = crc >> 8;
    UartSend(pTx, 5);
}

MbParse(GetInputReg)
{
	///< Parse the data and package the reply
    return MODBUS_NO_ERR;
}

MbParse(GetHoldReg)
{
	///< Parse the data and package the reply
    return MODBUS_NO_ERR;
}

MbParse(SetSingleHoldReg)
{
	///< Parse the data and package the reply
    return MODBUS_NO_ERR;
}

MbParse(SetMultiHoldReg)
{
    ///< Parse the data and package the reply
    return MODBUS_NO_ERR;
}

ModbusRtuNode s_modbusList[] = 
{
    {MODBUS_GET_HOLD_REG,        GetHoldReg         },
    {MODBUS_GET_INPUT_REG,       GetInputReg        },
    {MODBUS_SET_SINGLE_HOLD_REG, SetSingleHoldReg   },
    {MODBUS_SET_MULTI_HOLD_REG,  SetMultiHoldReg    },
};

void ModbusParseTask(void)
{
    uint8_t i, len;
    uint8_t mbErr = MODBUS_NO_ERR;
    uint16_t rcvCrc;
    uint8_t *pRx = g_RS485BuffCtrl.parseBuff;
    uint8_t *packData = g_RS485PackBuff;
    uint8_t rcvDevAddr, rcvFuncode;
    uint16_t rcvRegAddr;
    
    if((g_RS485BuffCtrl.rxFlag == false) || (g_RS485BuffCtrl.txFlag == true))
        return ;
    len = g_RS485BuffCtrl.rxLen;
    memcpy(pRx, g_RS485BuffCtrl.rxBuff, len);
    g_RS485BuffCtrl.rxFlag = false;
    
    rcvDevAddr = pRx[0];
    rcvFuncode = pRx[1];
    rcvRegAddr = (pRx[2] << 8) + pRx[3];
    rcvCrc     = (pRx[len-1] << 8) + pRx[len-2];
    
/*  modbus rtu link layer :
  ||==============================================||
  ||  dev addr  |  Fun code  |   Data   |   Crc   ||
  ||------------+------------+----------+---------||
  ||   1 byte   |   1 byte   |  n byte  |  2 byte ||
  ||==============================================||*/
    if(rcvDevAddr != MB_DEV_ADDR)                       ///<  dev addr err
        return;

    if(rcvCrc != getModbusCRC16(pRx, len - 2))          ///<  crc err
        return;

    for(i = 0; i < ARRAY_SIZE(s_modbusList); i++)
        if(rcvFuncode == s_modbusList[i].funCode)
            break;
    
    if(i >= ARRAY_SIZE(s_modbusList))                   ///<  funcode err
    {
        PackMbErrRsp(rcvFuncode, MODBUS_ILLEGAL_FUN);
        return;
    }
    
    if(MbRegAddrIllegal(rcvRegAddr))                    ///<  reg addr err
    {
        PackMbErrRsp(rcvFuncode, MODBUS_ILLEGAL_ADDR);
        return;
    }
    
    if(NULL != s_modbusList[i].parse)                    ///<  normal parse
        mbErr = s_modbusList[i].parse(&pRx[2], len-4, &packData[1]);
    
    if(MODBUS_NO_ERR != mbErr)
    {
        PackMbErrRsp(rcvFuncode, mbErr);                  ///<  response err ret
    }
    else
    {
        packData[0] = rcvFuncode;
        PackMbRsp(packData, s_packLen+1);                 ///<  response normal data
    }
}

modbus.h

#ifndef __MODBUS_H_
#define __MODBUS_H_
#include <stdint.h>

enum ModbusFuncode{
    MODBUS_GET_HOLD_REG = 0x03,         ///<  读取保持寄存器
    MODBUS_GET_INPUT_REG = 0x04,        ///<  读取输入寄存器
    MODBUS_SET_SINGLE_HOLD_REG = 0x06,  ///<  写入单个保持寄存器
    MODBUS_SET_MULTI_HOLD_REG = 0x10,   ///<  写入多个保持寄存器
};

typedef enum ModbusErrCode {
    MODBUS_NO_ERR,                  ///<  没有异常
    MODBUS_ILLEGAL_FUN,             ///<  不支持的功能码
    MODBUS_ILLEGAL_ADDR,            ///<  不支持的地址
    MODBUS_ILLEGAL_VAL,             ///<  不支持的数据
    MODUBS_SLAVE_TROUBLE,           ///<  从机故障
    MODBUS_CONFIRM,                 ///<  正在确认,防止主站发生接收超时错误
    MODBUS_SLAVE_BUSY,              ///<  从属设备忙
} MBErrType;

enum ModbusReg{
    ///< .............. 输入寄存器 (30001 ~ )
    MB_INPUT_REG_START = 30001,
    MB_INPUT_REG_END   = 31000,
    ///< .............. 保持寄存器 (40001 ~ )
    MB_HOLD_REG_START = 40001,
    MB_HOLD_REG_END   = 41000,
};

void ModbusParseTask(void);  ///<  MODUBS 通信任务

#endif
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值