打印
[应用相关]

Freemodbus通信,串口接收模式改为DMA接收模式

[复制链接]
楼主: 東南博士
手机看帖
扫描二维码
随时随地手机跟帖
41
東南博士|  楼主 | 2020-2-11 10:59 | 只看该作者 回帖奖励 |倒序浏览
void
eMBRTUStart( void )
{
   OS_CPU_SR                  cpu_sr;
   // ENTER_CRITICAL_SECTION(  );
        OS_ENTER_CRITICAL(        );         
       
    /* Initially the receiver is in the state STATE_RX_INIT. we start
     * the timer and if no character is received within t3.5 we change
     * to STATE_RX_IDLE. This makes sure that we delay startup of the
     * modbus protocol stack until the bus is free.
     */
    eRcvState = STATE_RX_INIT;
    vMBPortSerialEnable( TRUE, FALSE );
    vMBPortTimersEnable(  );
       
        OS_EXIT_CRITICAL(        );

  // EXIT_CRITICAL_SECTION(  );
}

使用特权

评论回复
42
東南博士|  楼主 | 2020-2-11 10:59 | 只看该作者
void
eMBRTUStop( void )
{   
        OS_CPU_SR                  cpu_sr;
    //ENTER_CRITICAL_SECTION(  );
        OS_ENTER_CRITICAL(        );                
    vMBPortSerialEnable( FALSE, FALSE );
    vMBPortTimersDisable(  );
        OS_EXIT_CRITICAL(        );
    //EXIT_CRITICAL_SECTION(  );
}

使用特权

评论回复
43
東南博士|  楼主 | 2020-2-11 10:59 | 只看该作者
eMBErrorCode
eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
    OS_CPU_SR                  cpu_sr;
    BOOL            xFrameReceived = FALSE;
    eMBErrorCode    eStatus = MB_ENOERR;
       
        OS_ENTER_CRITICAL(        );         
  //  ENTER_CRITICAL_SECTION(  );
    assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );

    /* Length and CRC check */
    if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
        && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) )
    {
        /* Save the address field. All frames are passed to the upper layed
         * and the decision if a frame is used is done there.
         */
        *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF];

        /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
         * size of address field and CRC checksum.
         */
        *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );

        /* Return the start of the Modbus PDU to the caller. */
        *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF];
        xFrameReceived = TRUE;
    }
    else
    {
        eStatus = MB_EIO;
    }
        OS_EXIT_CRITICAL(        );
//   EXIT_CRITICAL_SECTION(  );
    return eStatus;
}

使用特权

评论回复
44
東南博士|  楼主 | 2020-2-11 11:01 | 只看该作者
eMBErrorCode
eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          usCRC16;
    OS_CPU_SR                  cpu_sr;

  // ENTER_CRITICAL_SECTION(  );
        OS_ENTER_CRITICAL(        );         

    /* Check if the receiver is still in idle state. If not we where to
     * slow with processing the received frame and the master sent another
     * frame on the network. We have to abort sending the frame.
     */
    if( eRcvState == STATE_RX_IDLE )
    {
        /* First byte before the Modbus-PDU is the slave address. */
        pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
        usSndBufferCount = 1;

        /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
        pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
        usSndBufferCount += usLength;

        /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
        usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
        ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
        ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );

        /* Activate the transmitter. */
        eSndState = STATE_TX_XMIT;
        vMBPortSerialEnable( FALSE, TRUE );
    }
    else
    {
        eStatus = MB_EIO;
    }
   // EXIT_CRITICAL_SECTION(  );
   OS_EXIT_CRITICAL(        );         

    return eStatus;
}

使用特权

评论回复
45
東南博士|  楼主 | 2020-2-11 11:01 | 只看该作者
BOOL
xMBRTUReceiveFSM( void )
{
    BOOL            xTaskNeedSwitch = FALSE;
    UCHAR           ucByte;

    assert( eSndState == STATE_TX_IDLE );

    /* Always read the character. */
    ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte );

    switch ( eRcvState )
    {
        /* If we have received a character in the init state we have to
         * wait until the frame is finished.
         */
    case STATE_RX_INIT:
        vMBPortTimersEnable(  );
        break;

        /* In the error state we wait until all characters in the
         * damaged frame are transmitted.
         */
    case STATE_RX_ERROR:
        vMBPortTimersEnable(  );
        break;

        /* In the idle state we wait for a new character. If a character
         * is received the t1.5 and t3.5 timers are started and the
         * receiver is in the state STATE_RX_RECEIVCE.
         */
    case STATE_RX_IDLE:
        usRcvBufferPos = 0;
        ucRTUBuf[usRcvBufferPos++] = ucByte;
        eRcvState = STATE_RX_RCV;

        /* Enable t3.5 timers. */
        vMBPortTimersEnable(  );
        break;

        /* We are currently receiving a frame. Reset the timer after
         * every character received. If more than the maximum possible
         * number of bytes in a modbus frame is received the frame is
         * ignored.
         */
    case STATE_RX_RCV:
        if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
        {
            ucRTUBuf[usRcvBufferPos++] = ucByte;
        }
        else
        {
            eRcvState = STATE_RX_ERROR;
        }
        vMBPortTimersEnable(  );
        break;
    }
    return xTaskNeedSwitch;
}

使用特权

评论回复
46
東南博士|  楼主 | 2020-2-11 11:01 | 只看该作者

BOOL
xMBRTUTransmitFSM( void )
{
    BOOL            xNeedPoll = FALSE;

    assert( eRcvState == STATE_RX_IDLE );

    switch ( eSndState )
    {
        /* We should not get a transmitter event if the transmitter is in
         * idle state.  */
    case STATE_TX_IDLE:
        /* enable receiver/disable transmitter. */
        vMBPortSerialEnable( TRUE, FALSE );
        break;

    case STATE_TX_XMIT:
        /* check if we are finished. */
        if( usSndBufferCount != 0 )
        {
            xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
            pucSndBufferCur++;  /* next byte in sendbuffer. */
            usSndBufferCount--;
        }
        else
        {
            xNeedPoll = xMBPortEventPost( EV_FRAME_SENT );
            /* Disable transmitter. This prevents another transmit buffer
             * empty interrupt. */
                       
            vMBPortSerialEnable( TRUE, FALSE );
            eSndState = STATE_TX_IDLE;
        }
        break;
    }

    return xNeedPoll;
}

使用特权

评论回复
47
東南博士|  楼主 | 2020-2-11 11:01 | 只看该作者
BOOL
xMBRTUTimerT35Expired( void )
{
    BOOL                   xNeedPoll = FALSE;
        static unsigned char   xtimeoutCount = 0;
        static unsigned short  xPreviousLength = 0;
               unsigned short  xNowLength = 0;
       
        /* 超时计数. */
        xtimeoutCount ++;

        /* 获取dma通道剩余长度. */
        xNowLength = DMA_GetCurrDataCounter(DMA1_Channel5);

        /*- 如果DMA通道仍然在接收数据,则清空超时计数. */
        if (xNowLength != xPreviousLength)  
        {
                xtimeoutCount   = 0;
                xPreviousLength = xNowLength;
        }
        else
        {
                /*- 10ms内没有接收到数据,表示通道处于空闲状态. */
                if (xtimeoutCount > 2)
                {
                        if (xNowLength < MB_SER_PDU_SIZE_MAX )
                        {
                                usRcvBufferPos = MB_SER_PDU_SIZE_MAX - xNowLength;
                                DMA_ClearFlag(DMA1_FLAG_TC5);
                                DMA_Cmd(DMA1_Channel5,DISABLE);  
                MB_DMAchannelInit();
                               
                                /*- 修改状态机的状态,表示接收完毕. */
                            xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );
                            eRcvState = STATE_RX_IDLE;
                        }
                   xtimeoutCount = 0;
                }
       
        }
    return xNeedPoll;
}

使用特权

评论回复
48
東南博士|  楼主 | 2020-2-11 11:02 | 只看该作者
//导出指针
unsigned char * exportPoint(unsigned short * length)
{
    *length = MB_SER_PDU_SIZE_MAX;
        return (unsigned char *)&ucRTUBuf[0];
}

使用特权

评论回复
49
東南博士|  楼主 | 2020-2-11 11:02 | 只看该作者
port.h:串口的相关配置宏

使用特权

评论回复
50
東南博士|  楼主 | 2020-2-11 11:03 | 只看该作者
#ifndef _PORT_H
#define _PORT_H

#include <assert.h>
#include <inttypes.h>
#include "includes.h"
#include <stdio.h>
#include <stdint.h>

/* ---------------------------------STM32 includes ----------------------------*/
#include "stm32f10x.h"


#define        INLINE                      inline
#define PR_BEGIN_EXTERN_C           extern "C" {
#define        PR_END_EXTERN_C             }



/* --------------------------  Modbus macros -----------------------------------*/
#define MODBUS_PORT_SERIAL_RCC       RCC_APB2Periph_USART1
#define MODBUS_PORT_SERIAL_TX_GPIO   GPIOA
#define MODBUS_PORT_SERIAL_RX_GPIO   GPIOA
#define MODBUS_PORT_SERIAL_TX_PIN    GPIO_Pin_9
#define MODBUS_PORT_SERIAL_RX_PIN    GPIO_Pin_10

#define MODBUS_RS485_CONTRL_GPIO     GPIOA     
#define MODBUS_RS485_CONTRL_PIN      GPIO_Pin_11
#define MODBUS_RS485_CONTRL_RCC      RCC_APB2Periph_GPIOA
#define MODBUS_RS485_SERIAL_PORT     USART1

#define MB_IRQn                      USART1_IRQn
#define MB_IRQHandler                USART1_IRQHandler


//TODO  暂时先写B2引脚,等组网测试时再确认
#define SLAVE_RS485_SEND_MODE       GPIO_SetBits(MODBUS_RS485_CONTRL_GPIO,MODBUS_RS485_CONTRL_PIN)
#define SLAVE_RS485_RECEIVE_MODE    GPIO_ResetBits(MODBUS_RS485_CONTRL_GPIO,MODBUS_RS485_CONTRL_PIN)

#define ENTER_CRITICAL_SECTION( )                   
#define EXIT_CRITICAL_SECTION( )        


/*位带操作,实现51类似的GPIO控制功能
*具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).IO口操作宏定义*/

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+ 0x2000000 \
                                                        +((addr &0xFFFFF)<<5)+(bitnum<<2))
                                                       
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
       
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

/* ------------------------IO口地址映射 ----------------------------------------*/
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) /*!> 0x4001080C */
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) /*!> 0x40010C0C */
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) /*!> 0x4001100C */
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) /*!> 0x4001140C */
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) /*!> 0x4001180C */
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) /*!> 0x40011A0C */  
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) /*!> 0x40011E0C */   
#define GPIOA_IDR_Addr    (GPIOA_BASE+8)  /*!> 0x40010808 */
#define GPIOB_IDR_Addr    (GPIOB_BASE+8)  /*!> 0x40010C08 */
#define GPIOC_IDR_Addr    (GPIOC_BASE+8)  /*!> 0x40011008 */
#define GPIOD_IDR_Addr    (GPIOD_BASE+8)  /*!> 0x40011408 */
#define GPIOE_IDR_Addr    (GPIOE_BASE+8)  /*!> 0x40011808 */
#define GPIOF_IDR_Addr    (GPIOF_BASE+8)  /*!> 0x40011A08 */
#define GPIOG_IDR_Addr    (GPIOG_BASE+8)  /*!> 0x40011E08 */

/* ----------------------- IO口操作,只对单一的IO口 ----------------------------*/

#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)   

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)   

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)   

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  




#define LED1    PCout(6)

#define GREEN_LED_BLINK() do \
                              {\
                                                                        LED1 = ~LED1 ;\
                                                  }while(0)
       
typedef uint8_t BOOL;

typedef unsigned char UCHAR;
typedef char CHAR;

typedef uint16_t USHORT;
typedef int16_t SHORT;

typedef uint32_t ULONG;
typedef int32_t LONG;

#ifndef TRUE
#define TRUE            1
#endif

#ifndef FALSE
#define FALSE           0
#endif

#endif

使用特权

评论回复
51
東南博士|  楼主 | 2020-2-11 11:03 | 只看该作者
DMA进行发送&接收,可以减少自身MCU的浪费。

使用特权

评论回复
52
東南博士|  楼主 | 2020-2-11 11:05 | 只看该作者
这个暂时还没有实验!
找时间测试测试!
希望DMA的效果好一些!

使用特权

评论回复
53
renzheshengui| | 2020-3-3 16:37 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
54
wakayi| | 2020-3-3 16:43 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
55
wowu| | 2020-3-3 16:50 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
56
xiaoqizi| | 2020-3-3 16:58 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
57
磨砂| | 2020-3-3 17:03 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
58
ckjjj| | 2022-7-19 18:06 | 只看该作者
up,你这个功能上已经实现了么?

使用特权

评论回复
59
zhanghqi| | 2022-7-19 22:11 | 只看该作者
实验完记得来分享

使用特权

评论回复
60
foxsbig| | 2022-10-2 14:00 | 只看该作者
这是标准协议么

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则