Adicionar primeiro

This commit is contained in:
2025-06-06 21:17:25 +01:00
parent c188084ba4
commit 282e7f517b
841 changed files with 199592 additions and 1 deletions

View 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

View 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_ */

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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( );
}

View File

@@ -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( );
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}