Adicionar primeiro
This commit is contained in:
213
managed_components/espressif__esp-modbus/freemodbus/port/port.c
Normal file
213
managed_components/espressif__esp-modbus/freemodbus/port/port.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2013 Armink
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port
|
||||
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: port.c,v 1.60 2015/02/01 9:18:05 Armink $
|
||||
*/
|
||||
|
||||
/* ----------------------- System includes --------------------------------*/
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "port.h"
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static _lock_t s_port_lock;
|
||||
static UCHAR ucPortMode = 0;
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
INLINE int lock_obj(_lock_t *plock)
|
||||
{
|
||||
_lock_acquire(plock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
INLINE void unlock_obj(_lock_t *plock)
|
||||
{
|
||||
_lock_release(plock);
|
||||
}
|
||||
|
||||
INLINE void
|
||||
vMBPortEnterCritical(void)
|
||||
{
|
||||
_lock_acquire(&s_port_lock);
|
||||
}
|
||||
|
||||
INLINE void
|
||||
vMBPortExitCritical(void)
|
||||
{
|
||||
_lock_release(&s_port_lock);
|
||||
}
|
||||
|
||||
UCHAR
|
||||
ucMBPortGetMode( void )
|
||||
{
|
||||
return ucPortMode;
|
||||
}
|
||||
|
||||
void
|
||||
vMBPortSetMode( UCHAR ucMode )
|
||||
{
|
||||
ENTER_CRITICAL_SECTION();
|
||||
ucPortMode = ucMode;
|
||||
EXIT_CRITICAL_SECTION();
|
||||
}
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
|
||||
|
||||
BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout)
|
||||
{
|
||||
BOOL xResult = (BaseType_t)xQueueReceive(xMbUartQueue, (void*)pxEvent, (TickType_t) xTimeout);
|
||||
ESP_LOGD(MB_PORT_TAG, "%s, UART event: %u ", __func__, (unsigned)pxEvent->type);
|
||||
return xResult;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
|
||||
/*
|
||||
* The function is called from ASCII/RTU module to get processed data buffer. Sets the
|
||||
* received buffer and its length using parameters.
|
||||
*/
|
||||
__attribute__ ((weak))
|
||||
BOOL xMBMasterPortSerialGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength )
|
||||
{
|
||||
ESP_LOGD(MB_PORT_TAG, " %s default", __func__);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* The function is called from ASCII/RTU module to set processed data buffer
|
||||
* to be sent in transmitter state machine.
|
||||
*/
|
||||
__attribute__ ((weak))
|
||||
BOOL xMBMasterPortSerialSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength )
|
||||
{
|
||||
ESP_LOGD(MB_PORT_TAG, "%s default", __func__);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
|
||||
|
||||
__attribute__ ((weak))
|
||||
BOOL xMBPortSerialGetRequest( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength )
|
||||
{
|
||||
ESP_LOGD(MB_PORT_TAG, "%s default", __func__);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
__attribute__ ((weak))
|
||||
BOOL xMBPortSerialSendResponse( UCHAR *pucMBSerialFrame, USHORT usSerialLength )
|
||||
{
|
||||
ESP_LOGD(MB_PORT_TAG, "%s default", __func__);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MB_TCP_DEBUG
|
||||
|
||||
// This function is kept to realize legacy freemodbus frame logging functionality
|
||||
void
|
||||
prvvMBTCPLogFrame( const CHAR * pucMsg, UCHAR * pucFrame, USHORT usFrameLen )
|
||||
{
|
||||
int i;
|
||||
int res = 0;
|
||||
int iBufPos = 0;
|
||||
size_t iBufLeft = MB_TCP_FRAME_LOG_BUFSIZE;
|
||||
static CHAR arcBuffer[MB_TCP_FRAME_LOG_BUFSIZE];
|
||||
|
||||
assert( pucFrame != NULL );
|
||||
|
||||
for ( i = 0; i < usFrameLen; i++ ) {
|
||||
// Print some additional frame information.
|
||||
switch ( i )
|
||||
{
|
||||
case 0:
|
||||
// TID = Transaction Identifier.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, "| TID = " );
|
||||
break;
|
||||
case 2:
|
||||
// PID = Protocol Identifier.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | PID = " );
|
||||
break;
|
||||
case 4:
|
||||
// Length
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | LEN = " );
|
||||
break;
|
||||
case 6:
|
||||
// UID = Unit Identifier.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | UID = " );
|
||||
break;
|
||||
case 7:
|
||||
// MB Function Code.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | FUNC = " );
|
||||
break;
|
||||
case 8:
|
||||
// MB PDU rest.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | DATA = " );
|
||||
break;
|
||||
default:
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
if( res == -1 ) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
iBufPos += res;
|
||||
iBufLeft -= res;
|
||||
}
|
||||
|
||||
// Print the data.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, "%02X", pucFrame[i] );
|
||||
if( res == -1 ) {
|
||||
break;
|
||||
} else {
|
||||
iBufPos += res;
|
||||
iBufLeft -= res;
|
||||
}
|
||||
}
|
||||
|
||||
if( res != -1 ) {
|
||||
// Append an end of frame string.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " |" );
|
||||
if( res != -1 ) {
|
||||
ESP_LOGD(pucMsg, "%s", arcBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
254
managed_components/espressif__esp-modbus/freemodbus/port/port.h
Normal file
254
managed_components/espressif__esp-modbus/freemodbus/port/port.h
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010 Christian Walter
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port
|
||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: port.h,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||
*/
|
||||
|
||||
#ifndef PORT_COMMON_H_
|
||||
#define PORT_COMMON_H_
|
||||
|
||||
#include "sys/lock.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h" // for queue
|
||||
|
||||
#include "esp_log.h" // for ESP_LOGE macro
|
||||
#include "esp_timer.h"
|
||||
#include "driver/uart.h" // for uart_event_t
|
||||
|
||||
#if __has_include("driver/gptimer.h")
|
||||
#include "driver/gptimer.h"
|
||||
#else
|
||||
#include "driver/timer.h"
|
||||
#endif
|
||||
|
||||
#include "mbconfig.h"
|
||||
|
||||
#define INLINE inline __attribute__((always_inline))
|
||||
#define PR_BEGIN_EXTERN_C extern "C" {
|
||||
#define PR_END_EXTERN_C }
|
||||
|
||||
#define MB_PORT_TAG "MB_PORT_COMMON"
|
||||
|
||||
#define MB_BAUD_RATE_DEFAULT (115200)
|
||||
#define MB_QUEUE_LENGTH (CONFIG_FMB_QUEUE_LENGTH)
|
||||
|
||||
#define MB_SERIAL_TASK_PRIO (CONFIG_FMB_PORT_TASK_PRIO)
|
||||
#define MB_SERIAL_TASK_STACK_SIZE (CONFIG_FMB_PORT_TASK_STACK_SIZE)
|
||||
#define MB_SERIAL_TOUT (3) // 3.5*8 = 28 ticks, TOUT=3 -> ~24..33 ticks
|
||||
|
||||
// Set buffer size for transmission
|
||||
#define MB_SERIAL_BUF_SIZE (CONFIG_FMB_SERIAL_BUF_SIZE)
|
||||
|
||||
// common definitions for serial port implementations
|
||||
#define MB_SERIAL_TX_TOUT_MS (2200) // maximum time for transmission of longest allowed frame buffer
|
||||
#define MB_SERIAL_TX_TOUT_TICKS (pdMS_TO_TICKS(MB_SERIAL_TX_TOUT_MS)) // timeout for transmission
|
||||
#define MB_SERIAL_RX_TOUT_MS (1)
|
||||
#define MB_SERIAL_RX_TOUT_TICKS (pdMS_TO_TICKS(MB_SERIAL_RX_TOUT_MS)) // timeout for receive
|
||||
|
||||
#define MB_SERIAL_RESP_LEN_MIN (4)
|
||||
|
||||
// Common definitions for TCP port
|
||||
#define MB_TCP_BUF_SIZE (256 + 7) // Must hold a complete Modbus TCP frame.
|
||||
#define MB_TCP_DEFAULT_PORT (CONFIG_FMB_TCP_PORT_DEFAULT)
|
||||
#define MB_TCP_STACK_SIZE (CONFIG_FMB_PORT_TASK_STACK_SIZE)
|
||||
#define MB_TCP_TASK_PRIO (CONFIG_FMB_PORT_TASK_PRIO)
|
||||
|
||||
// The task affinity for Modbus stack tasks
|
||||
#define MB_PORT_TASK_AFFINITY (CONFIG_FMB_PORT_TASK_AFFINITY)
|
||||
|
||||
#define MB_TCP_READ_TIMEOUT_MS (100) // read timeout in mS
|
||||
#define MB_TCP_READ_TIMEOUT (pdMS_TO_TICKS(MB_TCP_READ_TIMEOUT_MS))
|
||||
#define MB_TCP_SEND_TIMEOUT_MS (500) // send event timeout in mS
|
||||
#define MB_TCP_SEND_TIMEOUT (pdMS_TO_TICKS(MB_TCP_SEND_TIMEOUT_MS))
|
||||
#define MB_TCP_PORT_MAX_CONN (CONFIG_FMB_TCP_PORT_MAX_CONN)
|
||||
|
||||
// Set the API unlock time to maximum response time
|
||||
// The actual release time will be dependent on the timer time
|
||||
#define MB_MAX_RESPONSE_TIME_MS (5000)
|
||||
|
||||
#define MB_TCP_FRAME_LOG_BUFSIZE (256)
|
||||
|
||||
#define MB_PORT_HAS_CLOSE (1) // Define to explicitly close port on destroy
|
||||
|
||||
// Define number of timer reloads per 1 mS
|
||||
#define MB_TIMER_TICS_PER_MS (20UL)
|
||||
#define MB_TIMER_TICK_TIME_US (1000 / MB_TIMER_TICS_PER_MS) // 50uS = one discreet for timer
|
||||
|
||||
#define MB_TCP_DEBUG (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) // Enable legacy debug output in TCP module.
|
||||
|
||||
#define MB_ATTR_WEAK __attribute__ ((weak))
|
||||
|
||||
#define MB_TCP_GET_FIELD(buffer, field) ((USHORT)((buffer[field] << 8U) | buffer[field + 1]))
|
||||
|
||||
#define MB_PORT_CHECK(a, ret_val, str, ...) \
|
||||
if (!(a)) { \
|
||||
ESP_LOGE(MB_PORT_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
return ret_val; \
|
||||
}
|
||||
|
||||
// Macro to check if stack shutdown event is active
|
||||
#define TCP_PORT_CHECK_SHDN(sema_ptr, callback_func) do { \
|
||||
if (sema_ptr) { \
|
||||
ESP_LOGD(TAG, "Shutdown stack from %s(%d)", __func__, __LINE__); \
|
||||
callback_func(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
int lock_obj(_lock_t *plock);
|
||||
void unlock_obj(_lock_t *plock);
|
||||
|
||||
#define CRITICAL_SECTION_INIT(lock) \
|
||||
do \
|
||||
{ \
|
||||
_lock_init((_lock_t *)&lock); \
|
||||
} while (0)
|
||||
|
||||
#define CRITICAL_SECTION_CLOSE(lock) \
|
||||
do \
|
||||
{ \
|
||||
_lock_close((_lock_t *)&lock); \
|
||||
} while (0)
|
||||
|
||||
#define CRITICAL_SECTION_LOCK(lock) \
|
||||
do \
|
||||
{ \
|
||||
lock_obj((_lock_t *)&lock); \
|
||||
} while (0)
|
||||
|
||||
#define CRITICAL_SECTION_UNLOCK(lock) \
|
||||
do \
|
||||
{ \
|
||||
unlock_obj((_lock_t *)&lock); \
|
||||
} while (0)
|
||||
|
||||
#define CRITICAL_SECTION(lock) for (int st = lock_obj((_lock_t *)&lock); (st > 0); unlock_obj((_lock_t *)&lock), st = -1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_BEGIN_EXTERN_C
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
typedef char BOOL;
|
||||
|
||||
typedef unsigned char UCHAR;
|
||||
typedef char CHAR;
|
||||
|
||||
typedef unsigned short USHORT;
|
||||
typedef short SHORT;
|
||||
|
||||
typedef unsigned long ULONG;
|
||||
typedef long LONG;
|
||||
|
||||
#if MB_TCP_DEBUG
|
||||
typedef enum
|
||||
{
|
||||
MB_LOG_DEBUG,
|
||||
MB_LOG_INFO,
|
||||
MB_LOG_WARN,
|
||||
MB_LOG_ERROR
|
||||
} eMBPortLogLevel;
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MB_PROTO_TCP,
|
||||
MB_PROTO_UDP,
|
||||
} eMBPortProto;
|
||||
|
||||
typedef enum {
|
||||
MB_PORT_IPV4 = 0, /*!< TCP IPV4 addressing */
|
||||
MB_PORT_IPV6 = 1 /*!< TCP IPV6 addressing */
|
||||
} eMBPortIpVer;
|
||||
|
||||
typedef struct {
|
||||
esp_timer_handle_t xTimerIntHandle;
|
||||
USHORT usT35Ticks;
|
||||
BOOL xTimerState;
|
||||
} xTimerContext_t;
|
||||
|
||||
void vMBPortEnterCritical(void);
|
||||
void vMBPortExitCritical(void);
|
||||
|
||||
#define ENTER_CRITICAL_SECTION( ) { ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port enter critical.", __func__); \
|
||||
vMBPortEnterCritical(); }
|
||||
|
||||
#define EXIT_CRITICAL_SECTION( ) { vMBPortExitCritical(); \
|
||||
ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port exit critical", __func__); }
|
||||
|
||||
#define MB_PORT_CHECK_EVENT( event, mask ) ( event & mask )
|
||||
#define MB_PORT_CLEAR_EVENT( event, mask ) do { event &= ~mask; } while(0)
|
||||
|
||||
#define MB_PORT_PARITY_GET(parity) ((parity != UART_PARITY_DISABLE) ? \
|
||||
((parity == UART_PARITY_ODD) ? MB_PAR_ODD : MB_PAR_EVEN) : MB_PAR_NONE)
|
||||
|
||||
// Legacy Modbus logging function
|
||||
#if MB_TCP_DEBUG
|
||||
void vMBPortLog( eMBPortLogLevel eLevel, const CHAR * szModule,
|
||||
const CHAR * szFmt, ... );
|
||||
void prvvMBTCPLogFrame( const CHAR * pucMsg, UCHAR * pucFrame, USHORT usFrameLen );
|
||||
#endif
|
||||
|
||||
void vMBPortSetMode( UCHAR ucMode );
|
||||
UCHAR ucMBPortGetMode( void );
|
||||
|
||||
BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout);
|
||||
|
||||
/**
|
||||
* This is modbus master user error handling funcion.
|
||||
* If it is defined in the user application, then helps to handle the errors
|
||||
* and received/sent buffers to transfer as well as handle the slave exception codes.
|
||||
*
|
||||
* @param xTransId - the identification of the trasaction
|
||||
* @param ucDestAddress destination salve address
|
||||
* @param usError - the error code, see the enumeration eMBMasterErrorEventType
|
||||
* @param pucRecvData current receive data pointer
|
||||
* @param ucRecvLength current length of receive buffer
|
||||
* @param pucSendData Send buffer data
|
||||
* @param ucSendLength Send buffer length
|
||||
*/
|
||||
void vMBMasterErrorCBUserHandler( uint64_t xTransId, USHORT usError, UCHAR ucDestAddress, const UCHAR* pucRecvData, USHORT ucRecvLength,
|
||||
const UCHAR* pucSendData, USHORT ucSendLength ) MB_ATTR_WEAK;
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_END_EXTERN_C
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* PORT_COMMON_H_ */
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010 Christian Walter
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port Demo Application
|
||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
*
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portevent.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||
*/
|
||||
|
||||
/* ----------------------- System includes ----------------------------------*/
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb.h"
|
||||
#include "mbport.h"
|
||||
#include "port.h"
|
||||
#include "mbconfig.h"
|
||||
#include "port_serial_slave.h"
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static QueueHandle_t xQueueHdl;
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
BOOL
|
||||
xMBPortEventInit( void )
|
||||
{
|
||||
BOOL bStatus = FALSE;
|
||||
if((xQueueHdl = xQueueCreate(MB_EVENT_QUEUE_SIZE, sizeof(eMBEventType))) != NULL)
|
||||
{
|
||||
vQueueAddToRegistry(xQueueHdl, "MbPortEventQueue");
|
||||
bStatus = TRUE;
|
||||
}
|
||||
return bStatus;
|
||||
}
|
||||
|
||||
void
|
||||
vMBPortEventClose( void )
|
||||
{
|
||||
if(xQueueHdl != NULL)
|
||||
{
|
||||
vQueueDelete(xQueueHdl);
|
||||
xQueueHdl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL MB_PORT_ISR_ATTR
|
||||
xMBPortEventPost( eMBEventType eEvent )
|
||||
{
|
||||
BaseType_t xStatus, xHigherPriorityTaskWoken = pdFALSE;
|
||||
assert(xQueueHdl != NULL);
|
||||
|
||||
if( (BOOL)xPortInIsrContext() == TRUE )
|
||||
{
|
||||
xStatus = xQueueSendFromISR(xQueueHdl, (const void*)&eEvent, &xHigherPriorityTaskWoken);
|
||||
if ( xHigherPriorityTaskWoken )
|
||||
{
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
if (xStatus != pdTRUE) {
|
||||
ESP_EARLY_LOGV(MB_PORT_TAG, "%s: Post message failure = %u.", __func__, (unsigned)xStatus);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xStatus = xQueueSend(xQueueHdl, (const void*)&eEvent, MB_EVENT_QUEUE_TIMEOUT);
|
||||
MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "%s: Post message failure.", __func__);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL
|
||||
xMBPortEventGet(eMBEventType * peEvent)
|
||||
{
|
||||
assert(xQueueHdl != NULL);
|
||||
BOOL xEventHappened = FALSE;
|
||||
|
||||
if (xQueueReceive(xQueueHdl, peEvent, portMAX_DELAY) == pdTRUE) {
|
||||
xEventHappened = TRUE;
|
||||
}
|
||||
return xEventHappened;
|
||||
}
|
||||
|
||||
QueueHandle_t
|
||||
xMBPortEventGetHandle(void)
|
||||
{
|
||||
if(xQueueHdl != NULL)
|
||||
{
|
||||
return xQueueHdl;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2013 Armink
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port
|
||||
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portevent.c v 1.60 2013/08/13 15:07:05 Armink add Master Functions$
|
||||
*/
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "mb_m.h"
|
||||
#include "mbport.h"
|
||||
#include "mbconfig.h"
|
||||
|
||||
#include "port.h"
|
||||
#include "mbport.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
|
||||
// Event bit mask for eMBMasterWaitRequestFinish()
|
||||
#define MB_EVENT_REQ_MASK (EventBits_t)( EV_MASTER_PROCESS_SUCCESS | \
|
||||
EV_MASTER_ERROR_RESPOND_TIMEOUT | \
|
||||
EV_MASTER_ERROR_RECEIVE_DATA | \
|
||||
EV_MASTER_ERROR_EXECUTE_FUNCTION )
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static SemaphoreHandle_t xResourceMasterHdl;
|
||||
static EventGroupHandle_t xEventGroupMasterHdl;
|
||||
static EventGroupHandle_t xEventGroupMasterConfirmHdl;
|
||||
static QueueHandle_t xQueueMasterHdl;
|
||||
|
||||
static _Atomic uint64_t xTransactionID = 0;
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
|
||||
BOOL
|
||||
xMBMasterPortEventInit( void )
|
||||
{
|
||||
xEventGroupMasterHdl = xEventGroupCreate();
|
||||
xEventGroupMasterConfirmHdl = xEventGroupCreate();
|
||||
MB_PORT_CHECK((xEventGroupMasterHdl != NULL) && (xEventGroupMasterConfirmHdl != NULL),
|
||||
FALSE, "mb stack event group creation error.");
|
||||
xQueueMasterHdl = xQueueCreate(MB_EVENT_QUEUE_SIZE, sizeof(xMBMasterEventType));
|
||||
MB_PORT_CHECK(xQueueMasterHdl, FALSE, "mb stack event group creation error.");
|
||||
vQueueAddToRegistry(xQueueMasterHdl, "MbMasterPortEventQueue");
|
||||
atomic_init(&xTransactionID, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL MB_PORT_ISR_ATTR
|
||||
xMBMasterPortEventPost( eMBMasterEventEnum eEvent)
|
||||
{
|
||||
BaseType_t xStatus, xHigherPriorityTaskWoken = pdFALSE;
|
||||
assert(xQueueMasterHdl != NULL);
|
||||
xMBMasterEventType xEvent;
|
||||
xEvent.xPostTimestamp = esp_timer_get_time();
|
||||
|
||||
if (eEvent & EV_MASTER_TRANS_START) {
|
||||
atomic_store(&(xTransactionID), xEvent.xPostTimestamp);
|
||||
}
|
||||
xEvent.eEvent = (eEvent & ~EV_MASTER_TRANS_START);
|
||||
|
||||
if( (BOOL)xPortInIsrContext() == TRUE ) {
|
||||
xStatus = xQueueSendFromISR(xQueueMasterHdl, (const void*)&xEvent, &xHigherPriorityTaskWoken);
|
||||
if ( xHigherPriorityTaskWoken ) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
if (xStatus != pdTRUE) {
|
||||
ESP_EARLY_LOGV(MB_PORT_TAG, "%s: Post message failure = %d.", __func__, xStatus);
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
xStatus = xQueueSend(xQueueMasterHdl, (const void*)&xEvent, MB_EVENT_QUEUE_TIMEOUT);
|
||||
MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "%s: Post message failure.", __func__);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL
|
||||
xMBMasterPortEventGet(xMBMasterEventType *peEvent)
|
||||
{
|
||||
assert(xQueueMasterHdl != NULL);
|
||||
BOOL xEventHappened = FALSE;
|
||||
|
||||
if (xQueueReceive(xQueueMasterHdl, peEvent, portMAX_DELAY) == pdTRUE) {
|
||||
peEvent->xTransactionId = atomic_load(&xTransactionID);
|
||||
// Set event bits in confirmation group (for synchronization with port task)
|
||||
xEventGroupSetBits(xEventGroupMasterConfirmHdl, peEvent->eEvent);
|
||||
peEvent->xGetTimestamp = esp_timer_get_time();
|
||||
xEventHappened = TRUE;
|
||||
}
|
||||
return xEventHappened;
|
||||
}
|
||||
|
||||
eMBMasterEventEnum
|
||||
xMBMasterPortFsmWaitConfirmation( eMBMasterEventEnum eEventMask, ULONG ulTimeout)
|
||||
{
|
||||
EventBits_t uxBits;
|
||||
uxBits = xEventGroupWaitBits( xEventGroupMasterConfirmHdl, // The event group being tested.
|
||||
eEventMask, // The bits within the event group to wait for.
|
||||
pdFALSE, // Keep masked bits.
|
||||
pdFALSE, // Don't wait for both bits, either bit will do.
|
||||
ulTimeout); // Wait timeout for either bit to be set.
|
||||
if (ulTimeout && uxBits) {
|
||||
// Clear confirmation events that where set in the mask
|
||||
xEventGroupClearBits( xEventGroupMasterConfirmHdl, (uxBits & eEventMask) );
|
||||
}
|
||||
return (eMBMasterEventEnum)(uxBits & eEventMask);
|
||||
}
|
||||
|
||||
uint64_t xMBMasterPortGetTransactionId( )
|
||||
{
|
||||
return atomic_load(&xTransactionID);
|
||||
}
|
||||
|
||||
// This function is initialize the OS resource for modbus master.
|
||||
void vMBMasterOsResInit( void )
|
||||
{
|
||||
xResourceMasterHdl = xSemaphoreCreateBinary();
|
||||
MB_PORT_CHECK((xResourceMasterHdl != NULL), ; , "%s: Resource create error.", __func__);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is take Mobus Master running resource.
|
||||
* Note:The resource is define by Operating System.
|
||||
*
|
||||
* @param lTimeOut the waiting time.
|
||||
*
|
||||
* @return resource take result
|
||||
*/
|
||||
BOOL xMBMasterRunResTake( LONG lTimeOut )
|
||||
{
|
||||
BaseType_t xStatus = pdTRUE;
|
||||
xStatus = xSemaphoreTake( xResourceMasterHdl, lTimeOut );
|
||||
MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s: Resource take failure.", __func__);
|
||||
ESP_LOGD(MB_PORT_TAG,"%s:Take MB resource (%lu ticks).", __func__, lTimeOut);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is release Modbus Master running resource.
|
||||
* Note:The resource is define by Operating System. If you not use OS this function can be empty.
|
||||
*/
|
||||
void vMBMasterRunResRelease( void )
|
||||
{
|
||||
BaseType_t xStatus = pdFALSE;
|
||||
xStatus = xSemaphoreGive( xResourceMasterHdl );
|
||||
if (xStatus != pdTRUE) {
|
||||
ESP_LOGD(MB_PORT_TAG,"%s: Release resource fail.", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is modbus master respond timeout error process callback function.
|
||||
* @note There functions will block modbus master poll while execute OS waiting.
|
||||
*
|
||||
* @param xTransId - the identification of the trasaction
|
||||
* @param ucDestAddress destination salve address
|
||||
* @param pucRecvData current receive data pointer
|
||||
* @param ucRecvLength current length of receive buffer
|
||||
* @param pucSendData Send buffer data
|
||||
* @param ucSendLength Send buffer length
|
||||
*
|
||||
*/
|
||||
void vMBMasterErrorCBRespondTimeout(uint64_t xTransId, UCHAR ucDestAddress, const UCHAR* pucSendData, USHORT ucSendLength)
|
||||
{
|
||||
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_RESPOND_TIMEOUT );
|
||||
ESP_LOGD(MB_PORT_TAG,"%s:Callback respond timeout.", __func__);
|
||||
if (vMBMasterErrorCBUserHandler) {
|
||||
vMBMasterErrorCBUserHandler( xTransId,
|
||||
(USHORT)EV_ERROR_RESPOND_TIMEOUT, ucDestAddress,
|
||||
NULL, 0,
|
||||
pucSendData, ucSendLength );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is modbus master receive data error process callback function.
|
||||
* @note There functions will block modbus master poll while execute OS waiting.
|
||||
*
|
||||
* @param xTransId - the identification of the trasaction
|
||||
* @param ucDestAddress destination salve address
|
||||
* @param pucRecvData current receive data pointer
|
||||
* @param ucRecvLength current length of receive buffer
|
||||
* @param pucSendData Send buffer data
|
||||
* @param ucSendLength Send buffer length
|
||||
*/
|
||||
void vMBMasterErrorCBReceiveData(uint64_t xTransId, UCHAR ucDestAddress,
|
||||
const UCHAR* pucRecvData, USHORT ucRecvLength,
|
||||
const UCHAR* pucSendData, USHORT ucSendLength)
|
||||
{
|
||||
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_RECEIVE_DATA );
|
||||
ESP_LOGD(MB_PORT_TAG,"%s:Callback receive data failure.", __func__);
|
||||
if (vMBMasterErrorCBUserHandler) {
|
||||
vMBMasterErrorCBUserHandler( xTransId,
|
||||
(USHORT)EV_ERROR_RECEIVE_DATA, ucDestAddress,
|
||||
pucRecvData, ucRecvLength,
|
||||
pucSendData, ucSendLength );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is modbus master execute function error process callback function.
|
||||
* @note There functions will block modbus master poll while execute OS waiting.
|
||||
* So,for real-time of system.Do not execute too much waiting process.
|
||||
*
|
||||
* @param xTransId - the identification of the trasaction
|
||||
* @param ucDestAddress destination salve address
|
||||
* @param pucRecvData current receive data pointer
|
||||
* @param ucRecvLength current length of receive buffer
|
||||
* @param pucSendData Send buffer data
|
||||
* @param ucSendLength Send buffer length
|
||||
*
|
||||
*/
|
||||
void vMBMasterErrorCBExecuteFunction(uint64_t xTransId, UCHAR ucDestAddress,
|
||||
const UCHAR* pucRecvData, USHORT ucRecvLength,
|
||||
const UCHAR* pucSendData, USHORT ucSendLength)
|
||||
{
|
||||
xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_EXECUTE_FUNCTION );
|
||||
ESP_LOGD(MB_PORT_TAG,"%s:Callback execute data handler failure.", __func__);
|
||||
if (vMBMasterErrorCBUserHandler) {
|
||||
vMBMasterErrorCBUserHandler( xTransId,
|
||||
(USHORT)EV_ERROR_EXECUTE_FUNCTION, ucDestAddress,
|
||||
pucRecvData, ucRecvLength,
|
||||
pucSendData, ucSendLength );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is modbus master request process success callback function.
|
||||
* @note There functions will block modbus master poll while execute OS waiting.
|
||||
* So,for real-time of system. Do not execute too much waiting process.
|
||||
*
|
||||
* @param xTransId - the identification of the trasaction
|
||||
* @param ucDestAddress destination salve address
|
||||
* @param pucRecvData current receive data pointer
|
||||
* @param ucRecvLength current length of receive buffer
|
||||
* @param pucSendData Send buffer data
|
||||
* @param ucSendLength Send buffer length
|
||||
*/
|
||||
void vMBMasterCBRequestSuccess(uint64_t xTransId, UCHAR ucDestAddress,
|
||||
const UCHAR* pucRecvData, USHORT ucRecvLength,
|
||||
const UCHAR* pucSendData, USHORT ucSendLength)
|
||||
{
|
||||
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_PROCESS_SUCCESS );
|
||||
ESP_LOGD(MB_PORT_TAG,"%s: Callback request success.", __func__);
|
||||
if (vMBMasterErrorCBUserHandler) {
|
||||
vMBMasterErrorCBUserHandler( xTransId,
|
||||
(USHORT)EV_ERROR_OK, ucDestAddress,
|
||||
pucRecvData, ucRecvLength,
|
||||
pucSendData, ucSendLength );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is wait for modbus master request finish and return result.
|
||||
* Waiting result include request process success, request respond timeout,
|
||||
* receive data error and execute function error.You can use the above callback function.
|
||||
* @note If you are use OS, you can use OS's event mechanism. Otherwise you have to run
|
||||
* much user custom delay for waiting.
|
||||
*
|
||||
* @return request error code
|
||||
*/
|
||||
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
|
||||
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||
eMBMasterEventEnum xRecvedEvent;
|
||||
|
||||
EventBits_t uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
|
||||
MB_EVENT_REQ_MASK, // The bits within the event group to wait for.
|
||||
pdTRUE, // Masked bits should be cleared before returning.
|
||||
pdFALSE, // Don't wait for both bits, either bit will do.
|
||||
portMAX_DELAY ); // Wait forever for either bit to be set.
|
||||
xRecvedEvent = (eMBMasterEventEnum)(uxBits);
|
||||
if (xRecvedEvent) {
|
||||
ESP_LOGD(MB_PORT_TAG,"%s: returned event = 0x%x", __func__, (int)xRecvedEvent);
|
||||
if (!(xRecvedEvent & MB_EVENT_REQ_MASK)) {
|
||||
// if we wait for certain event bits but get from poll subset
|
||||
ESP_LOGE(MB_PORT_TAG,"%s: incorrect event set = 0x%x", __func__, (int)xRecvedEvent);
|
||||
}
|
||||
xEventGroupSetBits( xEventGroupMasterConfirmHdl, (xRecvedEvent & MB_EVENT_REQ_MASK) );
|
||||
if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_PROCESS_SUCCESS)) {
|
||||
eErrStatus = MB_MRE_NO_ERR;
|
||||
} else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RESPOND_TIMEOUT)) {
|
||||
eErrStatus = MB_MRE_TIMEDOUT;
|
||||
} else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RECEIVE_DATA)) {
|
||||
eErrStatus = MB_MRE_REV_DATA;
|
||||
} else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_EXECUTE_FUNCTION)) {
|
||||
eErrStatus = MB_MRE_EXE_FUN;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event or timeout xRecvedEvent = 0x%x", __func__, (int)uxBits);
|
||||
// https://github.com/espressif/esp-idf/issues/5275
|
||||
// if a no event is received, that means vMBMasterPortEventClose()
|
||||
// has been closed, so event group has been deleted by FreeRTOS, which
|
||||
// triggers the send of 0 value to the event group to unlock this task
|
||||
// waiting on it. For this patch, handles it as a time out without assert.
|
||||
eErrStatus = MB_MRE_TIMEDOUT;
|
||||
}
|
||||
return eErrStatus;
|
||||
}
|
||||
|
||||
void vMBMasterPortEventClose(void)
|
||||
{
|
||||
if (xEventGroupMasterHdl) {
|
||||
vEventGroupDelete(xEventGroupMasterHdl);
|
||||
xEventGroupMasterHdl = NULL;
|
||||
}
|
||||
if (xQueueMasterHdl) {
|
||||
vQueueDelete(xQueueMasterHdl);
|
||||
xQueueMasterHdl = NULL;
|
||||
}
|
||||
if (xEventGroupMasterConfirmHdl) {
|
||||
vEventGroupDelete(xEventGroupMasterConfirmHdl);
|
||||
xEventGroupMasterConfirmHdl = NULL;
|
||||
}
|
||||
if (xResourceMasterHdl) {
|
||||
vSemaphoreDelete(xResourceMasterHdl);
|
||||
xResourceMasterHdl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010 Christian Walter
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Demo Application
|
||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||
*/
|
||||
|
||||
/* ----------------------- System includes ----------------------------------*/
|
||||
#include <stdlib.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb.h"
|
||||
#include "mbport.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
|
||||
BOOL
|
||||
bMBPortIsWithinException( void )
|
||||
{
|
||||
BOOL bIsWithinException = xPortInIsrContext();
|
||||
return bIsWithinException;
|
||||
}
|
||||
|
||||
void
|
||||
vMBPortClose( void )
|
||||
{
|
||||
extern void vMBPortSerialClose( void );
|
||||
extern void vMBPortTimerClose( void );
|
||||
extern void vMBPortEventClose( void );
|
||||
vMBPortSerialClose( );
|
||||
vMBPortTimerClose( );
|
||||
vMBPortEventClose( );
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2006 Christian Walter
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Demo Application
|
||||
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||
*/
|
||||
|
||||
/* ----------------------- System includes ----------------------------------*/
|
||||
#include <stdlib.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb_m.h"
|
||||
#include "mbport.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
|
||||
void
|
||||
vMBMasterPortClose( void )
|
||||
{
|
||||
extern void vMBMasterPortSerialClose( void );
|
||||
extern void vMBMasterPortTimerClose( void );
|
||||
extern void vMBMasterPortEventClose( void );
|
||||
vMBMasterPortSerialClose( );
|
||||
vMBMasterPortTimerClose( );
|
||||
vMBMasterPortEventClose( );
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010 Christian Walter
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port
|
||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||
*/
|
||||
|
||||
#include "driver/uart.h"
|
||||
#include "port.h"
|
||||
#include "driver/uart.h"
|
||||
#include "freertos/queue.h" // for queue support
|
||||
#include "soc/uart_periph.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h" // for esp_log
|
||||
#include "esp_err.h" // for ESP_ERROR_CHECK macro
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb.h"
|
||||
#include "mbport.h"
|
||||
#include "sdkconfig.h" // for KConfig options
|
||||
#include "port_serial_slave.h"
|
||||
|
||||
// Note: This code uses mixed coding standard from legacy IDF code and used freemodbus stack
|
||||
|
||||
// A queue to handle UART event.
|
||||
static QueueHandle_t xMbUartQueue;
|
||||
static TaskHandle_t xMbTaskHandle;
|
||||
static const CHAR *TAG = "MB_SERIAL";
|
||||
|
||||
// The UART hardware port number
|
||||
static UCHAR ucUartNumber = UART_NUM_MAX - 1;
|
||||
|
||||
static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
|
||||
static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
|
||||
|
||||
void vMBPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
|
||||
{
|
||||
// This function can be called from xMBRTUTransmitFSM() of different task
|
||||
if (bTxEnable) {
|
||||
bTxStateEnabled = TRUE;
|
||||
} else {
|
||||
bTxStateEnabled = FALSE;
|
||||
}
|
||||
if (bRxEnable) {
|
||||
//uart_enable_rx_intr(ucUartNumber);
|
||||
bRxStateEnabled = TRUE;
|
||||
vTaskResume(xMbTaskHandle); // Resume receiver task
|
||||
} else {
|
||||
vTaskSuspend(xMbTaskHandle); // Block receiver task
|
||||
bRxStateEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static USHORT usMBPortSerialRxPoll(size_t xEventSize)
|
||||
{
|
||||
BOOL xReadStatus = TRUE;
|
||||
USHORT usCnt = 0;
|
||||
|
||||
if (bRxStateEnabled) {
|
||||
// Get received packet into Rx buffer
|
||||
while(xReadStatus && (usCnt++ <= xEventSize)) {
|
||||
// Call the Modbus stack callback function and let it fill the buffers.
|
||||
xReadStatus = pxMBFrameCBByteReceived(); // callback to execute receive FSM
|
||||
}
|
||||
uart_flush_input(ucUartNumber);
|
||||
// Send event EV_FRAME_RECEIVED to allow stack process packet
|
||||
#if !CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
pxMBPortCBTimerExpired();
|
||||
#endif
|
||||
ESP_LOGD(TAG, "RX: %u bytes\n", (unsigned)usCnt);
|
||||
}
|
||||
return usCnt;
|
||||
}
|
||||
|
||||
BOOL xMBPortSerialTxPoll(void)
|
||||
{
|
||||
USHORT usCount = 0;
|
||||
BOOL bNeedPoll = TRUE;
|
||||
|
||||
if( bTxStateEnabled ) {
|
||||
// Continue while all response bytes put in buffer or out of buffer
|
||||
while((bNeedPoll) && (usCount++ < MB_SERIAL_BUF_SIZE)) {
|
||||
// Calls the modbus stack callback function to let it fill the UART transmit buffer.
|
||||
bNeedPoll = pxMBFrameCBTransmitterEmpty( ); // callback to transmit FSM
|
||||
}
|
||||
ESP_LOGD(TAG, "MB_TX_buffer send: (%u) bytes\n", (unsigned)usCount);
|
||||
// Waits while UART sending the packet
|
||||
esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
|
||||
vMBPortSerialEnable(TRUE, FALSE);
|
||||
MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void vUartTask(void *pvParameters)
|
||||
{
|
||||
uart_event_t xEvent;
|
||||
USHORT usResult = 0;
|
||||
for(;;) {
|
||||
if (xMBPortSerialWaitEvent(xMbUartQueue, (void*)&xEvent, portMAX_DELAY)) {
|
||||
ESP_LOGD(TAG, "MB_uart[%u] event:", (unsigned)ucUartNumber);
|
||||
switch(xEvent.type) {
|
||||
//Event of UART receving data
|
||||
case UART_DATA:
|
||||
ESP_LOGD(TAG,"Data event, length: %u", (unsigned)xEvent.size);
|
||||
// This flag set in the event means that no more
|
||||
// data received during configured timeout and UART TOUT feature is triggered
|
||||
if (xEvent.timeout_flag) {
|
||||
// Get buffered data length
|
||||
ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size));
|
||||
// Read received data and send it to modbus stack
|
||||
usResult = usMBPortSerialRxPoll(xEvent.size);
|
||||
ESP_LOGD(TAG,"Timeout occured, processed: %u bytes", (unsigned)usResult);
|
||||
}
|
||||
break;
|
||||
//Event of HW FIFO overflow detected
|
||||
case UART_FIFO_OVF:
|
||||
ESP_LOGD(TAG, "hw fifo overflow");
|
||||
xQueueReset(xMbUartQueue);
|
||||
break;
|
||||
//Event of UART ring buffer full
|
||||
case UART_BUFFER_FULL:
|
||||
ESP_LOGD(TAG, "ring buffer full");
|
||||
xQueueReset(xMbUartQueue);
|
||||
uart_flush_input(ucUartNumber);
|
||||
break;
|
||||
//Event of UART RX break detected
|
||||
case UART_BREAK:
|
||||
ESP_LOGD(TAG, "uart rx break");
|
||||
break;
|
||||
//Event of UART parity check error
|
||||
case UART_PARITY_ERR:
|
||||
ESP_LOGD(TAG, "uart parity error");
|
||||
break;
|
||||
//Event of UART frame error
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGD(TAG, "uart frame error");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGD(TAG, "uart event type: %u", (unsigned)xEvent.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate,
|
||||
UCHAR ucDataBits, eMBParity eParity)
|
||||
{
|
||||
esp_err_t xErr = ESP_OK;
|
||||
// Set communication port number
|
||||
ucUartNumber = ucPORT;
|
||||
// Configure serial communication parameters
|
||||
UCHAR ucParity = UART_PARITY_DISABLE;
|
||||
UCHAR ucData = UART_DATA_8_BITS;
|
||||
switch(eParity){
|
||||
case MB_PAR_NONE:
|
||||
ucParity = UART_PARITY_DISABLE;
|
||||
break;
|
||||
case MB_PAR_ODD:
|
||||
ucParity = UART_PARITY_ODD;
|
||||
break;
|
||||
case MB_PAR_EVEN:
|
||||
ucParity = UART_PARITY_EVEN;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Incorrect parity option: %u", (unsigned)eParity);
|
||||
return FALSE;
|
||||
}
|
||||
switch(ucDataBits){
|
||||
case 5:
|
||||
ucData = UART_DATA_5_BITS;
|
||||
break;
|
||||
case 6:
|
||||
ucData = UART_DATA_6_BITS;
|
||||
break;
|
||||
case 7:
|
||||
ucData = UART_DATA_7_BITS;
|
||||
break;
|
||||
case 8:
|
||||
ucData = UART_DATA_8_BITS;
|
||||
break;
|
||||
default:
|
||||
ucData = UART_DATA_8_BITS;
|
||||
break;
|
||||
}
|
||||
uart_config_t xUartConfig = {
|
||||
.baud_rate = ulBaudRate,
|
||||
.data_bits = ucData,
|
||||
.parity = ucParity,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.rx_flow_ctrl_thresh = 2,
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
#else
|
||||
.source_clk = UART_SCLK_APB,
|
||||
#endif
|
||||
};
|
||||
// Set UART config
|
||||
xErr = uart_param_config(ucUartNumber, &xUartConfig);
|
||||
MB_PORT_CHECK((xErr == ESP_OK),
|
||||
FALSE, "mb config failure, uart_param_config() returned (0x%x).", (int)xErr);
|
||||
// Install UART driver, and get the queue.
|
||||
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
|
||||
MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"mb serial driver failure, uart_driver_install() returned (0x%x).", (int)xErr);
|
||||
#if !CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
// Set timeout for TOUT interrupt (T3.5 modbus time)
|
||||
xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (int)xErr);
|
||||
#endif
|
||||
|
||||
// Set always timeout flag to trigger timeout interrupt even after rx fifo full
|
||||
uart_set_always_rx_timeout(ucUartNumber, true);
|
||||
|
||||
// Create a task to handle UART events
|
||||
BaseType_t xStatus = xTaskCreatePinnedToCore(vUartTask, "uart_queue_task",
|
||||
MB_SERIAL_TASK_STACK_SIZE,
|
||||
NULL, MB_SERIAL_TASK_PRIO,
|
||||
&xMbTaskHandle, MB_PORT_TASK_AFFINITY);
|
||||
if (xStatus != pdPASS) {
|
||||
vTaskDelete(xMbTaskHandle);
|
||||
// Force exit from function with failure
|
||||
MB_PORT_CHECK(FALSE, FALSE,
|
||||
"mb stack serial task creation error. xTaskCreate() returned (0x%x).",
|
||||
(int)xStatus);
|
||||
} else {
|
||||
vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void vMBPortSerialClose(void)
|
||||
{
|
||||
(void)vTaskSuspend(xMbTaskHandle);
|
||||
(void)vTaskDelete(xMbTaskHandle);
|
||||
ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
|
||||
}
|
||||
|
||||
BOOL xMBPortSerialPutByte(CHAR ucByte)
|
||||
{
|
||||
// Send one byte to UART transmission buffer
|
||||
// This function is called by Modbus stack
|
||||
UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
|
||||
return (ucLength == 1);
|
||||
}
|
||||
|
||||
// Get one byte from intermediate RX buffer
|
||||
BOOL xMBPortSerialGetByte(CHAR* pucByte)
|
||||
{
|
||||
assert(pucByte != NULL);
|
||||
USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS);
|
||||
return (usLength == 1);
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2013 Armink
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port
|
||||
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portserial.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions $
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "driver/uart.h"
|
||||
#include "soc/dport_access.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "port.h"
|
||||
#include "mbport.h"
|
||||
#include "mb_m.h"
|
||||
#include "mbrtu.h"
|
||||
#include "mbconfig.h"
|
||||
#include "port_serial_master.h"
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MB_SERIAL_RX_SEMA_TOUT_MS (1000)
|
||||
#define MB_SERIAL_RX_SEMA_TOUT (pdMS_TO_TICKS(MB_SERIAL_RX_SEMA_TOUT_MS))
|
||||
#define MB_SERIAL_RX_FLUSH_RETRY (2)
|
||||
|
||||
/* ----------------------- Static variables ---------------------------------*/
|
||||
static const CHAR *TAG = "MB_MASTER_SERIAL";
|
||||
|
||||
// A queue to handle UART event.
|
||||
static QueueHandle_t xMbUartQueue;
|
||||
static TaskHandle_t xMbTaskHandle;
|
||||
|
||||
// The UART hardware port number
|
||||
static UCHAR ucUartNumber = UART_NUM_MAX - 1;
|
||||
|
||||
static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
|
||||
static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
|
||||
|
||||
static SemaphoreHandle_t xMasterSemaRxHandle; // Rx blocking semaphore handle
|
||||
|
||||
static BOOL xMBMasterPortRxSemaInit( void )
|
||||
{
|
||||
xMasterSemaRxHandle = xSemaphoreCreateBinary();
|
||||
MB_PORT_CHECK((xMasterSemaRxHandle != NULL), FALSE , "%s: RX semaphore create failure.", __func__);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void vMBMasterPortRxSemaClose( void )
|
||||
{
|
||||
if (xMasterSemaRxHandle) {
|
||||
vSemaphoreDelete(xMasterSemaRxHandle);
|
||||
xMasterSemaRxHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL xMBMasterPortRxSemaTake( LONG lTimeOut )
|
||||
{
|
||||
BaseType_t xStatus = pdTRUE;
|
||||
xStatus = xSemaphoreTake(xMasterSemaRxHandle, lTimeOut );
|
||||
MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s: RX semaphore take failure.", __func__);
|
||||
ESP_LOGV(MB_PORT_TAG,"%s:Take RX semaphore (%" PRIu64 " ticks).", __func__, (uint64_t)lTimeOut);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void vMBMasterRxSemaRelease( void )
|
||||
{
|
||||
BaseType_t xStatus = pdFALSE;
|
||||
xStatus = xSemaphoreGive(xMasterSemaRxHandle);
|
||||
if (xStatus != pdTRUE) {
|
||||
ESP_LOGD(MB_PORT_TAG,"%s:RX semaphore is free.", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL vMBMasterRxSemaIsBusy( void )
|
||||
{
|
||||
BaseType_t xStatus = pdFALSE;
|
||||
xStatus = (uxSemaphoreGetCount(xMasterSemaRxHandle) == 0) ? TRUE : FALSE;
|
||||
return xStatus;
|
||||
}
|
||||
|
||||
void vMBMasterRxFlush( void )
|
||||
{
|
||||
size_t xSize = 1;
|
||||
esp_err_t xErr = ESP_OK;
|
||||
for (int xCount = 0; (xCount < MB_SERIAL_RX_FLUSH_RETRY) && xSize; xCount++) {
|
||||
xErr = uart_get_buffered_data_len(ucUartNumber, &xSize);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), ; , "mb flush serial fail, error = 0x%x.", (int)xErr);
|
||||
BaseType_t xStatus = xQueueReset(xMbUartQueue);
|
||||
if (xStatus) {
|
||||
xErr = uart_flush_input(ucUartNumber);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), ; , "mb flush serial fail, error = 0x%x.", (int)xErr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
|
||||
{
|
||||
// This function can be called from xMBRTUTransmitFSM() of different task
|
||||
if (bTxEnable) {
|
||||
vMBMasterRxFlush();
|
||||
bTxStateEnabled = TRUE;
|
||||
} else {
|
||||
bTxStateEnabled = FALSE;
|
||||
}
|
||||
if (bRxEnable) {
|
||||
bRxStateEnabled = TRUE;
|
||||
vMBMasterRxSemaRelease();
|
||||
vTaskResume(xMbTaskHandle); // Resume receiver task
|
||||
} else {
|
||||
vTaskSuspend(xMbTaskHandle); // Block receiver task
|
||||
bRxStateEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize)
|
||||
{
|
||||
BOOL xStatus = TRUE;
|
||||
USHORT usCnt = 0;
|
||||
|
||||
xStatus = xMBMasterPortRxSemaTake(MB_SERIAL_RX_SEMA_TOUT);
|
||||
if (xStatus) {
|
||||
while(xStatus && (usCnt++ <= xEventSize)) {
|
||||
// Call the Modbus stack callback function and let it fill the stack buffers.
|
||||
xStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM
|
||||
}
|
||||
// The buffer is transferred into Modbus stack and is not needed here any more
|
||||
uart_flush_input(ucUartNumber);
|
||||
ESP_LOGD(TAG, "Received data: %u(bytes in buffer)", (unsigned)usCnt);
|
||||
#if !CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
vMBMasterSetCurTimerMode(MB_TMODE_T35);
|
||||
xStatus = pxMBMasterPortCBTimerExpired();
|
||||
if (!xStatus) {
|
||||
xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
|
||||
ESP_LOGD(TAG, "Send additional RX ready event.");
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%u bytes) received. ",
|
||||
__func__, (unsigned)xEventSize);
|
||||
}
|
||||
return usCnt;
|
||||
}
|
||||
|
||||
BOOL xMBMasterPortSerialTxPoll(void)
|
||||
{
|
||||
USHORT usCount = 0;
|
||||
BOOL bNeedPoll = TRUE;
|
||||
|
||||
if( bTxStateEnabled ) {
|
||||
// Continue while all response bytes put in buffer or out of buffer
|
||||
while(bNeedPoll && (usCount++ < MB_SERIAL_BUF_SIZE)) {
|
||||
// Calls the modbus stack callback function to let it fill the UART transmit buffer.
|
||||
bNeedPoll = pxMBMasterFrameCBTransmitterEmpty( ); // callback to transmit FSM
|
||||
}
|
||||
ESP_LOGD(TAG, "MB_TX_buffer sent: (%u) bytes.", (unsigned)(usCount - 1));
|
||||
// Waits while UART sending the packet
|
||||
esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
|
||||
vMBMasterPortSerialEnable(TRUE, FALSE);
|
||||
MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// UART receive event task
|
||||
static void vUartTask(void* pvParameters)
|
||||
{
|
||||
uart_event_t xEvent;
|
||||
USHORT usResult = 0;
|
||||
for(;;) {
|
||||
if (xMBPortSerialWaitEvent(xMbUartQueue, (void*)&xEvent, portMAX_DELAY)) {
|
||||
ESP_LOGD(TAG, "MB_uart[%u] event:", (unsigned)ucUartNumber);
|
||||
switch(xEvent.type) {
|
||||
//Event of UART receiving data
|
||||
case UART_DATA:
|
||||
ESP_LOGD(TAG,"Data event, len: %u.", (unsigned)xEvent.size);
|
||||
// This flag set in the event means that no more
|
||||
// data received during configured timeout and UART TOUT feature is triggered
|
||||
if (xEvent.timeout_flag) {
|
||||
// Response is received but previous packet processing is pending
|
||||
// Do not wait completion of processing and just discard received data as incorrect
|
||||
if (vMBMasterRxSemaIsBusy()) {
|
||||
vMBMasterRxFlush();
|
||||
break;
|
||||
}
|
||||
// Get buffered data length
|
||||
ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size));
|
||||
// Read received data and send it to modbus stack
|
||||
usResult = usMBMasterPortSerialRxPoll(xEvent.size);
|
||||
ESP_LOGD(TAG,"Timeout occured, processed: %u bytes", (unsigned)usResult);
|
||||
}
|
||||
break;
|
||||
//Event of HW FIFO overflow detected
|
||||
case UART_FIFO_OVF:
|
||||
ESP_LOGD(TAG, "hw fifo overflow.");
|
||||
xQueueReset(xMbUartQueue);
|
||||
break;
|
||||
//Event of UART ring buffer full
|
||||
case UART_BUFFER_FULL:
|
||||
ESP_LOGD(TAG, "ring buffer full.");
|
||||
xQueueReset(xMbUartQueue);
|
||||
uart_flush_input(ucUartNumber);
|
||||
break;
|
||||
//Event of UART RX break detected
|
||||
case UART_BREAK:
|
||||
ESP_LOGD(TAG, "uart rx break.");
|
||||
break;
|
||||
//Event of UART parity check error
|
||||
case UART_PARITY_ERR:
|
||||
ESP_LOGD(TAG, "uart parity error.");
|
||||
xQueueReset(xMbUartQueue);
|
||||
uart_flush_input(ucUartNumber);
|
||||
break;
|
||||
//Event of UART frame error
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGD(TAG, "uart frame error.");
|
||||
xQueueReset(xMbUartQueue);
|
||||
uart_flush_input(ucUartNumber);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGD(TAG, "uart event type: %u.", (unsigned)xEvent.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
|
||||
{
|
||||
esp_err_t xErr = ESP_OK;
|
||||
// Set communication port number
|
||||
ucUartNumber = ucPORT;
|
||||
// Configure serial communication parameters
|
||||
UCHAR ucParity = UART_PARITY_DISABLE;
|
||||
UCHAR ucData = UART_DATA_8_BITS;
|
||||
switch(eParity){
|
||||
case MB_PAR_NONE:
|
||||
ucParity = UART_PARITY_DISABLE;
|
||||
break;
|
||||
case MB_PAR_ODD:
|
||||
ucParity = UART_PARITY_ODD;
|
||||
break;
|
||||
case MB_PAR_EVEN:
|
||||
ucParity = UART_PARITY_EVEN;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Incorrect parity option: %u", (unsigned)eParity);
|
||||
return FALSE;
|
||||
}
|
||||
switch(ucDataBits){
|
||||
case 5:
|
||||
ucData = UART_DATA_5_BITS;
|
||||
break;
|
||||
case 6:
|
||||
ucData = UART_DATA_6_BITS;
|
||||
break;
|
||||
case 7:
|
||||
ucData = UART_DATA_7_BITS;
|
||||
break;
|
||||
case 8:
|
||||
ucData = UART_DATA_8_BITS;
|
||||
break;
|
||||
default:
|
||||
ucData = UART_DATA_8_BITS;
|
||||
break;
|
||||
}
|
||||
uart_config_t xUartConfig = {
|
||||
.baud_rate = ulBaudRate,
|
||||
.data_bits = ucData,
|
||||
.parity = ucParity,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.rx_flow_ctrl_thresh = 2,
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
#else
|
||||
.source_clk = UART_SCLK_APB,
|
||||
#endif
|
||||
};
|
||||
// Set UART config
|
||||
xErr = uart_param_config(ucUartNumber, &xUartConfig);
|
||||
MB_PORT_CHECK((xErr == ESP_OK),
|
||||
FALSE, "mb config failure, uart_param_config() returned (0x%x).", (int)xErr);
|
||||
// Install UART driver, and get the queue.
|
||||
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
|
||||
MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"mb serial driver failure, uart_driver_install() returned (0x%x).", (int)xErr);
|
||||
// Set timeout for TOUT interrupt (T3.5 modbus time)
|
||||
xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (int)xErr);
|
||||
|
||||
// Set always timeout flag to trigger timeout interrupt even after rx fifo full
|
||||
uart_set_always_rx_timeout(ucUartNumber, true);
|
||||
MB_PORT_CHECK((xMBMasterPortRxSemaInit()), FALSE,
|
||||
"mb serial RX semaphore create fail.");
|
||||
// Create a task to handle UART events
|
||||
BaseType_t xStatus = xTaskCreatePinnedToCore(vUartTask, "uart_queue_task",
|
||||
MB_SERIAL_TASK_STACK_SIZE,
|
||||
NULL, MB_SERIAL_TASK_PRIO,
|
||||
&xMbTaskHandle, MB_PORT_TASK_AFFINITY);
|
||||
if (xStatus != pdPASS) {
|
||||
vTaskDelete(xMbTaskHandle);
|
||||
// Force exit from function with failure
|
||||
MB_PORT_CHECK(FALSE, FALSE,
|
||||
"mb stack serial task creation error. xTaskCreate() returned (0x%x).", (int)xStatus);
|
||||
} else {
|
||||
vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
|
||||
}
|
||||
ESP_LOGD(MB_PORT_TAG,"%s Init serial.", __func__);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void vMBMasterPortSerialClose(void)
|
||||
{
|
||||
vMBMasterPortRxSemaClose();
|
||||
(void)vTaskDelete(xMbTaskHandle);
|
||||
ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
|
||||
}
|
||||
|
||||
BOOL xMBMasterPortSerialPutByte(CHAR ucByte)
|
||||
{
|
||||
// Send one byte to UART transmission buffer
|
||||
// This function is called by Modbus stack
|
||||
UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
|
||||
return (ucLength == 1);
|
||||
}
|
||||
|
||||
// Get one byte from intermediate RX buffer
|
||||
BOOL xMBMasterPortSerialGetByte(CHAR* pucByte)
|
||||
{
|
||||
assert(pucByte != NULL);
|
||||
USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS);
|
||||
return (usLength == 1);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010 Christian Walter
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port Demo Application
|
||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||
*/
|
||||
/* ----------------------- Platform includes --------------------------------*/
|
||||
#include "port.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb.h"
|
||||
#include "mbport.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
|
||||
static const char *TAG = "MBS_TIMER";
|
||||
|
||||
static xTimerContext_t* pxTimerContext = NULL;
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
static void IRAM_ATTR vTimerAlarmCBHandler(void *param)
|
||||
{
|
||||
pxMBPortCBTimerExpired(); // Timer expired callback function
|
||||
pxTimerContext->xTimerState = TRUE;
|
||||
ESP_EARLY_LOGD(TAG, "Slave timeout triggered.");
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOL xMBPortTimersInit(USHORT usTimeOut50us)
|
||||
{
|
||||
#if CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
MB_PORT_CHECK((usTimeOut50us > 0), FALSE,
|
||||
"Modbus timeout discreet is incorrect.");
|
||||
MB_PORT_CHECK(!pxTimerContext, FALSE,
|
||||
"Modbus timer is already created.");
|
||||
pxTimerContext = calloc(1, sizeof(xTimerContext_t));
|
||||
if (!pxTimerContext) {
|
||||
return FALSE;
|
||||
}
|
||||
pxTimerContext->xTimerIntHandle = NULL;
|
||||
// Save timer reload value for Modbus T35 period
|
||||
pxTimerContext->usT35Ticks = usTimeOut50us;
|
||||
esp_timer_create_args_t xTimerConf = {
|
||||
.callback = vTimerAlarmCBHandler,
|
||||
.arg = NULL,
|
||||
#if (MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD && CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD)
|
||||
.dispatch_method = ESP_TIMER_ISR,
|
||||
#else
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
#endif
|
||||
.name = "MBS_T35timer"
|
||||
};
|
||||
// Create Modbus timer
|
||||
esp_err_t xErr = esp_timer_create(&xTimerConf, &(pxTimerContext->xTimerIntHandle));
|
||||
if (xErr) {
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void vMBPortTimersEnable(void)
|
||||
{
|
||||
#if CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
MB_PORT_CHECK((pxTimerContext && pxTimerContext->xTimerIntHandle), ; ,
|
||||
"timer is not initialized.");
|
||||
uint64_t xToutUs = (pxTimerContext->usT35Ticks * MB_TIMER_TICK_TIME_US);
|
||||
esp_timer_stop(pxTimerContext->xTimerIntHandle);
|
||||
esp_timer_start_once(pxTimerContext->xTimerIntHandle, xToutUs);
|
||||
pxTimerContext->xTimerState = FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MB_PORT_ISR_ATTR
|
||||
vMBPortTimersDisable(void)
|
||||
{
|
||||
#if CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
// Disable timer alarm
|
||||
esp_timer_stop(pxTimerContext->xTimerIntHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
void vMBPortTimerClose(void)
|
||||
{
|
||||
#if CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
// Delete active timer
|
||||
if (pxTimerContext) {
|
||||
if (pxTimerContext->xTimerIntHandle) {
|
||||
esp_timer_stop(pxTimerContext->xTimerIntHandle);
|
||||
esp_timer_delete(pxTimerContext->xTimerIntHandle);
|
||||
}
|
||||
free(pxTimerContext);
|
||||
pxTimerContext = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void vMBPortTimersDelay(USHORT usTimeOutMS)
|
||||
{
|
||||
vTaskDelay(usTimeOutMS / portTICK_PERIOD_MS);
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2013 Armink
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port
|
||||
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: porttimer_m.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions$
|
||||
*/
|
||||
|
||||
/* ----------------------- Platform includes --------------------------------*/
|
||||
#include "port.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb_m.h"
|
||||
#include "mbport.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "MBM_TIMER";
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static xTimerContext_t* pxTimerContext = NULL;
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
static void IRAM_ATTR vTimerAlarmCBHandler(void *param)
|
||||
{
|
||||
pxMBMasterPortCBTimerExpired(); // Timer expired callback function
|
||||
pxTimerContext->xTimerState = TRUE;
|
||||
ESP_EARLY_LOGD(TAG, "Timer mode: (%u) triggered", (unsigned)xMBMasterGetCurTimerMode());
|
||||
}
|
||||
|
||||
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
|
||||
{
|
||||
MB_PORT_CHECK((usTimeOut50us > 0), FALSE,
|
||||
"Modbus timeout discreet is incorrect.");
|
||||
MB_PORT_CHECK(!pxTimerContext, FALSE,
|
||||
"Modbus timer is already created.");
|
||||
pxTimerContext = calloc(1, sizeof(xTimerContext_t));
|
||||
if (!pxTimerContext) {
|
||||
return FALSE;
|
||||
}
|
||||
pxTimerContext->xTimerIntHandle = NULL;
|
||||
// Save timer reload value for Modbus T35 period
|
||||
pxTimerContext->usT35Ticks = usTimeOut50us;
|
||||
esp_timer_create_args_t xTimerConf = {
|
||||
.callback = vTimerAlarmCBHandler,
|
||||
.arg = NULL,
|
||||
#if (MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD && CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD)
|
||||
.dispatch_method = ESP_TIMER_ISR,
|
||||
#else
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
#endif
|
||||
.name = "MBM_T35timer"
|
||||
};
|
||||
// Create Modbus timer
|
||||
esp_err_t xErr = esp_timer_create(&xTimerConf, &(pxTimerContext->xTimerIntHandle));
|
||||
if (xErr) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Set timer alarm value
|
||||
static BOOL xMBMasterPortTimersEnable(uint64_t xToutUs)
|
||||
{
|
||||
MB_PORT_CHECK(pxTimerContext && (pxTimerContext->xTimerIntHandle), FALSE,
|
||||
"timer is not initialized.");
|
||||
MB_PORT_CHECK((xToutUs > 0), FALSE,
|
||||
"incorrect tick value for timer = (0x%llu).", xToutUs);
|
||||
esp_timer_stop(pxTimerContext->xTimerIntHandle);
|
||||
esp_timer_start_once(pxTimerContext->xTimerIntHandle, xToutUs);
|
||||
pxTimerContext->xTimerState = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void vMBMasterPortTimersT35Enable(void)
|
||||
{
|
||||
uint64_t xToutUs = (pxTimerContext->usT35Ticks * MB_TIMER_TICK_TIME_US);
|
||||
|
||||
// Set current timer mode, don't change it.
|
||||
vMBMasterSetCurTimerMode(MB_TMODE_T35);
|
||||
// Set timer alarm
|
||||
(void)xMBMasterPortTimersEnable(xToutUs);
|
||||
}
|
||||
|
||||
void vMBMasterPortTimersConvertDelayEnable(void)
|
||||
{
|
||||
// Covert time in milliseconds into ticks
|
||||
uint64_t xToutUs = (MB_MASTER_DELAY_MS_CONVERT * 1000);
|
||||
|
||||
// Set current timer mode
|
||||
vMBMasterSetCurTimerMode(MB_TMODE_CONVERT_DELAY);
|
||||
ESP_LOGD(MB_PORT_TAG,"%s Convert delay enable.", __func__);
|
||||
(void)xMBMasterPortTimersEnable(xToutUs);
|
||||
}
|
||||
|
||||
void vMBMasterPortTimersRespondTimeoutEnable(void)
|
||||
{
|
||||
uint64_t xToutUs = (MB_MASTER_TIMEOUT_MS_RESPOND * 1000);
|
||||
|
||||
vMBMasterSetCurTimerMode(MB_TMODE_RESPOND_TIMEOUT);
|
||||
ESP_LOGD(MB_PORT_TAG,"%s Respond enable timeout.", __func__);
|
||||
(void)xMBMasterPortTimersEnable(xToutUs);
|
||||
}
|
||||
|
||||
void MB_PORT_ISR_ATTR
|
||||
vMBMasterPortTimersDisable()
|
||||
{
|
||||
// Disable timer alarm
|
||||
esp_timer_stop(pxTimerContext->xTimerIntHandle);
|
||||
}
|
||||
|
||||
void vMBMasterPortTimerClose(void)
|
||||
{
|
||||
// Delete active timer
|
||||
if (pxTimerContext) {
|
||||
if (pxTimerContext->xTimerIntHandle) {
|
||||
esp_timer_stop(pxTimerContext->xTimerIntHandle);
|
||||
esp_timer_delete(pxTimerContext->xTimerIntHandle);
|
||||
}
|
||||
free(pxTimerContext);
|
||||
pxTimerContext = NULL;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user