FreeModBusRtu移植至stm32L431RCT6(小熊派开发板)实战指南

文章目录

  • 前言
  • 一、移植前需要的工作
  • 二、修改点讲解
  • 1.串口中断
  • 2.定时器
  • 3.保持寄存器
  • 4.测试
  • 总结

  • 前言

    最近需要做一个modbus485的传感器,主要是用来做从机。之前做过主机的是stm标准库,那这次做一个HAL的从机协议栈,方便大家直接获取数据。
    移植成功的代码仓库如下:Freemodbus从机仓库代码


    一、移植前需要的工作

    1.了解modbus485协议(协议帧内容含义等等)
    2.选一块stm32的开发板(随便选,能用stm32cubemx就行)
    3.移植这个协议,主要关注3点,串口、定时器以及数据存储的寄存器缓冲区

    二、修改点讲解

    1.串口中断

    portserial.c :

    
    extern UART_HandleTypeDef huart1;
    
    
    /* ----------------------- Start implementation -----------------------------*/
    BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
                           eMBParity eParity)
    {
        //这个是用stm32cubemx生成的函数名称,我增加了参数
    	MX_USART1_UART_Init(ulBaudRate);
        return TRUE;
    }
    
    void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
    {
       ENTER_CRITICAL_SECTION();
    		  
      if (xRxEnable)
      {
      	  //使能串口接收中断
    	 __HAL_UART_ENABLE_IT(&huart1, USART_IT_RXNE);
      }
      else
      {
         //关闭串口接收中断
    	 __HAL_UART_DISABLE_IT(&huart1, USART_IT_RXNE);
      }
    
    
      if (xTxEnable)
      {
         //使能串口发送中断
    	 __HAL_UART_ENABLE_IT(&huart1, USART_IT_TXE);
      }
      else
      {
     	 //关闭串口发送中断
    	 __HAL_UART_DISABLE_IT(&huart1, USART_IT_TXE);
      }
    	  
      EXIT_CRITICAL_SECTION();
    
    }
    
    void vMBPortClose(void)
    {
    	//不做函数处理
    }
    
    BOOL xMBPortSerialPutByte(CHAR ucByte)
    { 
    	// 发送串口数据 一次发送一个数据
    	HAL_UART_Transmit(&huart1, &ucByte,1,5);
        return TRUE;
    }
    
    BOOL xMBPortSerialGetByte(CHAR *pucByte)
    {
    	// 获取串口数据 一次拿一个数据
    	HAL_UART_Receive(&huart1,pucByte,1, 0);	
        return TRUE;
    }
    void prvvUARTTxReadyISR(void)
    {
        pxMBFrameCBTransmitterEmpty();
    }
    
    void prvvUARTRxISR(void)
    {
        pxMBFrameCBByteReceived();
    
    }
    
    void USART1_IRQHandler(void)
    {
      HAL_UART_IRQHandler(&huart1);
      if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
      {    //获取接收RXNE标志位是否被置位
    	 prvvUARTRxISR();
    	 __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
    	
      } 
      
      if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE) == SET)
      {
    	prvvUARTTxReadyISR();
    	__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);
      }
     
    
    }
    
    

    2.定时器

    代码如下:

    BOOL xMBPortTimersInit(USHORT usTim1Timerout50us)
    {
    	//这个是用stm32cubemx生成的函数名称,我增加了参数
    	MX_TIM2_Init(usTim1Timerout50us);
        return TRUE;
    }
    
    void vMBPortTimersEnable()
    {
        // 开启定时器,计算是否超时
    	__HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);  // 清除中断标志位
        __HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE); //开启定时器自动更新中断
    	__HAL_TIM_SET_COUNTER(&htim2, 0x0000);  // 设置计数器的值为0
    	__HAL_TIM_ENABLE(&htim2);   // 开启定时器中断
    }
    
    void vMBPortTimersDisable()
    {   
    	__HAL_TIM_DISABLE(&htim2);    // 关闭定时器中断
    	__HAL_TIM_SET_COUNTER(&htim2, 0x0000);  // 设置计数器的值为0
    	__HAL_TIM_DISABLE_IT(&htim2, TIM_IT_UPDATE); //关闭定时器自动更新中断
    	__HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);  // 清除中断标志位
    }
    
    void prvvTIMERExpiredISR(void)
    {
        (void)pxMBPortCBTimerExpired();
    }
    
    void TIM2_IRQHandler(void)
    {
      HAL_TIM_IRQHandler(&htim2);
      if(__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET)
      {
        prvvTIMERExpiredISR();
      	__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);  // 清除中断标志位
      }
    }
    

    3.保持寄存器

    在寄存器中,我们需要划分我们自己的寄存器的含义,从而达到数据传输的含义

    #define S_REG_HOLDING_START           0
    #这是一个倾角传感器的保持寄存器数据划分
    /*----------------------------------------
    *         划分保持寄存器的类型区域            
    *========================================
    * 0 |从机地址
    *-----------------------------------------
    * 1 |X轴角度 
    *-----------------------------------------
    * 2 |Y轴角度
    *-----------------------------------------
    * 3 |Z轴角度
    *-----------------------------------------
    */
    #define S_SLAVEID_REG_START S_REG_HOLDING_START
    #define S_SLAVEID_REG_NUM  1
    #define S_SLAVEID_REG_END S_SLAVEID_REG_START+S_SLAVEID_REG_NUM
    
    #define S_XANGLE_REG_START S_SLAVEID_REG_END
    #define S_XANGLE_REG_NUM 1
    #define S_XANGLE_REG_END S_XANGLE_REG_START+S_XANGLE_REG_NUM
    
    #define S_YANGLE_REG_START S_XANGLE_REG_END
    #define S_YANGLE_REG_NUM 1
    #define S_YANGLE_REG_END S_YANGLE_REG_START+S_YANGLE_REG_NUM
    
    #define S_ZANGLE_REG_START S_YANGLE_REG_END
    #define S_ZANGLE_REG_NUM 1
    #define S_ZANGLE_REG_END S_ZANGLE_REG_START+S_ZANGLE_REG_NUM
    
    
    //当你发送指令过来时,最终的解析在这里。这是一个关键的函数,要想理解这个函数,你要多参考前面的接口。
    //usAddress 要查询的寄存器地址 usNRegs 寄存器数量  eMode 模式  pucRegBuffer 缓冲区
    //这个函数就是根据usAddress ,从你的保持寄存器usSRegHoldBuf中读出想要的数据返回给主机。
    eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
            USHORT usNRegs, eMBRegisterMode eMode)
    {
        eMBErrorCode    eStatus = MB_ENOERR;
        USHORT          iRegIndex;
        USHORT 			*pusRegHoldingBuf;
        USHORT          reg_holding_start;
        USHORT          reg_holding_nregs;
        USHORT          usRegHoldStart;
    
        pusRegHoldingBuf = usSRegHoldBuf;
        reg_holding_start = S_REG_HOLDING_START;
        reg_holding_nregs = S_REG_HOLDING_NREGS;
        usRegHoldStart = usSRegHoldStart;
    
        /* it already plus one in modbus function method. */
        usAddress--;
    
        if ((usAddress >= reg_holding_start)
                && (usAddress + usNRegs <= reg_holding_start + reg_holding_nregs))
        {
            iRegIndex = usAddress - usRegHoldStart;
            switch (eMode)
            {
            /* read current register values from the protocol stack. */
            case MB_REG_READ:
                while (usNRegs > 0)
                {
                    *pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] >> 8);
                    *pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] & 0xFF);
                    iRegIndex++;
                    usNRegs--;
                }
                break;
    
            /* write current register values with new values from the protocol stack. */
            case MB_REG_WRITE:
                while (usNRegs > 0)
                {
                    pusRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
                    pusRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
                    iRegIndex++;
                    usNRegs--;
                }
                break;
            }
        }
        else
        {
            eStatus = MB_ENOREG;
        }
        return eStatus;
    }
    
    

    4.测试


    注意烧写的时候要擦除整片芯片


    总结

    多多指教

    作者:&向上

    物联沃分享整理
    物联沃-IOTWORD物联网 » FreeModBusRtu移植至stm32L431RCT6(小熊派开发板)实战指南

    发表回复