1 /*
2 * SPDX-FileCopyrightText: 2013 Armink
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
7 */
8 /*
9 * FreeModbus Libary: ESP32 Port
10 * Copyright (C) 2013 Armink <armink.ztl@gmail.com>
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 * File: $Id: portevent.c v 1.60 2013/08/13 15:07:05 Armink add Master Functions$
35 */
36
37 /* ----------------------- Modbus includes ----------------------------------*/
38 #include "mb_m.h"
39 #include "mbport.h"
40 #include "mbconfig.h"
41
42 #include "freertos/FreeRTOS.h"
43 #include "freertos/task.h"
44 #include "freertos/event_groups.h"
45 #include "port.h"
46 #include "mbport.h"
47 #include "freertos/semphr.h"
48 #include "port_serial_master.h"
49
50 #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
51 /* ----------------------- Defines ------------------------------------------*/
52 // Event bit mask for xMBMasterPortEventGet()
53 #define MB_EVENT_POLL_MASK (EventBits_t)( EV_MASTER_READY | \
54 EV_MASTER_FRAME_RECEIVED | \
55 EV_MASTER_EXECUTE | \
56 EV_MASTER_FRAME_SENT | \
57 EV_MASTER_FRAME_TRANSMIT | \
58 EV_MASTER_ERROR_PROCESS )
59
60 // Event bit mask for eMBMasterWaitRequestFinish()
61 #define MB_EVENT_REQ_MASK (EventBits_t)( EV_MASTER_PROCESS_SUCCESS | \
62 EV_MASTER_ERROR_RESPOND_TIMEOUT | \
63 EV_MASTER_ERROR_RECEIVE_DATA | \
64 EV_MASTER_ERROR_EXECUTE_FUNCTION )
65
66 #define MB_EVENT_RESOURCE (EventBits_t)( 0x0080 )
67
68 /* ----------------------- Variables ----------------------------------------*/
69 static EventGroupHandle_t xResourceMasterHdl;
70 static EventGroupHandle_t xEventGroupMasterHdl;
71 static EventGroupHandle_t xEventGroupMasterConfirmHdl;
72
73 /* ----------------------- Start implementation -----------------------------*/
74
75 BOOL
xMBMasterPortEventInit(void)76 xMBMasterPortEventInit( void )
77 {
78 xEventGroupMasterHdl = xEventGroupCreate();
79 xEventGroupMasterConfirmHdl = xEventGroupCreate();
80 MB_PORT_CHECK((xEventGroupMasterHdl != NULL) && (xEventGroupMasterConfirmHdl != NULL),
81 FALSE, "mb stack event group creation error.");
82 return TRUE;
83 }
84
85 BOOL MB_PORT_ISR_ATTR
xMBMasterPortEventPost(eMBMasterEventType eEvent)86 xMBMasterPortEventPost( eMBMasterEventType eEvent )
87 {
88 BOOL bStatus = FALSE;
89 eMBMasterEventType eTempEvent = eEvent;
90
91 if( (BOOL)xPortInIsrContext() == TRUE )
92 {
93 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
94 BaseType_t xResult = xEventGroupSetBitsFromISR( xEventGroupMasterHdl,
95 (EventBits_t) eTempEvent,
96 &xHigherPriorityTaskWoken );
97 // Was the message posted successfully?
98 if( xResult == pdPASS ) {
99 // If xHigherPriorityTaskWoken is now set to pdTRUE
100 // then a context switch should be requested.
101 if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR();
102 bStatus = TRUE;
103 } else {
104 bStatus = FALSE;
105 }
106 }
107 else
108 {
109 // Set event bits if the function is called from task
110 // The return result is not checked here because
111 // It might be that event bit was cleared automatically as a
112 // task that was waiting for the bit was removed from the Blocked state.
113 (void) xEventGroupSetBits( xEventGroupMasterHdl, (EventBits_t)eTempEvent );
114 bStatus = TRUE;
115 }
116 return bStatus;
117 }
118
119 eMBMasterEventType
xMBMasterPortFsmWaitConfirmation(eMBMasterEventType eEventMask,ULONG ulTimeout)120 xMBMasterPortFsmWaitConfirmation( eMBMasterEventType eEventMask, ULONG ulTimeout)
121 {
122 EventBits_t uxBits;
123 uxBits = xEventGroupWaitBits( xEventGroupMasterConfirmHdl, // The event group being tested.
124 eEventMask, // The bits within the event group to wait for.
125 pdFALSE, // Keep masked bits.
126 pdFALSE, // Don't wait for both bits, either bit will do.
127 ulTimeout); // Wait timeout for either bit to be set.
128 if (ulTimeout && uxBits) {
129 // Clear confirmation events that where set in the mask
130 xEventGroupClearBits( xEventGroupMasterConfirmHdl, (uxBits & eEventMask) );
131 }
132 return (eMBMasterEventType)(uxBits & eEventMask);
133 }
134
135 BOOL
xMBMasterPortEventGet(eMBMasterEventType * eEvent)136 xMBMasterPortEventGet( eMBMasterEventType* eEvent )
137 {
138 EventBits_t uxBits;
139 BOOL xEventHappened = FALSE;
140 uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
141 MB_EVENT_POLL_MASK, // The bits within the event group to wait for.
142 pdTRUE, // Masked bits should be cleared before returning.
143 pdFALSE, // Don't wait for both bits, either bit will do.
144 portMAX_DELAY); // Wait forever for either bit to be set.
145 // Check if poll event is correct
146 if (MB_PORT_CHECK_EVENT(uxBits, MB_EVENT_POLL_MASK)) {
147 *eEvent = (eMBMasterEventType)(uxBits & MB_EVENT_POLL_MASK);
148 // Set event bits in confirmation group (for synchronization with port task)
149 xEventGroupSetBits( xEventGroupMasterConfirmHdl, *eEvent );
150 xEventHappened = TRUE;
151 } else {
152 ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event triggered = %d.", __func__, uxBits);
153 *eEvent = (eMBMasterEventType)uxBits;
154 xEventHappened = FALSE;
155 }
156 return xEventHappened;
157 }
158
159 // This function is initialize the OS resource for modbus master.
vMBMasterOsResInit(void)160 void vMBMasterOsResInit( void )
161 {
162 xResourceMasterHdl = xEventGroupCreate();
163 xEventGroupSetBits(xResourceMasterHdl, MB_EVENT_RESOURCE);
164 MB_PORT_CHECK((xResourceMasterHdl != NULL), ; , "Resource create error.");
165 }
166
167 /**
168 * This function is take Mobus Master running resource.
169 * Note:The resource is define by Operating System.
170 *
171 * @param lTimeOut the waiting time.
172 *
173 * @return resource take result
174 */
xMBMasterRunResTake(LONG lTimeOut)175 BOOL xMBMasterRunResTake( LONG lTimeOut )
176 {
177 EventBits_t uxBits;
178 uxBits = xEventGroupWaitBits( xResourceMasterHdl, // The event group being tested.
179 MB_EVENT_RESOURCE, // The bits within the event group to wait for.
180 pdTRUE, // Masked bits should be cleared before returning.
181 pdFALSE, // Don't wait for both bits, either bit will do.
182 lTimeOut); // Resource wait timeout.
183 MB_PORT_CHECK((uxBits == MB_EVENT_RESOURCE), FALSE , "Take resource failure.");
184 ESP_LOGD(MB_PORT_TAG,"%s:Take resource (%x) (%lu ticks).", __func__, uxBits, lTimeOut);
185 return TRUE;
186 }
187
188 /**
189 * This function is release Modbus Master running resource.
190 * Note:The resource is define by Operating System.If you not use OS this function can be empty.
191 */
vMBMasterRunResRelease(void)192 void vMBMasterRunResRelease( void )
193 {
194 EventBits_t uxBits = xEventGroupSetBits( xResourceMasterHdl, MB_EVENT_RESOURCE );
195 MB_PORT_CHECK((uxBits == MB_EVENT_RESOURCE), ; , "Resource release failure.");
196 ESP_LOGD(MB_PORT_TAG,"%s: Release resource (%x).", __func__, uxBits);
197 }
198
199 /**
200 * This is modbus master respond timeout error process callback function.
201 * @note There functions will block modbus master poll while execute OS waiting.
202 *
203 * @param ucDestAddress destination salve address
204 * @param pucPDUData PDU buffer data
205 * @param ucPDULength PDU buffer length
206 *
207 */
vMBMasterErrorCBRespondTimeout(UCHAR ucDestAddress,const UCHAR * pucPDUData,USHORT ucPDULength)208 void vMBMasterErrorCBRespondTimeout(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
209 {
210 BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RESPOND_TIMEOUT);
211 MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_RESPOND_TIMEOUT' failed!", __func__);
212 ESP_LOGD(MB_PORT_TAG,"%s:Callback respond timeout.", __func__);
213 }
214
215 /**
216 * This is modbus master receive data error process callback function.
217 * @note There functions will block modbus master poll while execute OS waiting.
218 *
219 * @param ucDestAddress destination salve address
220 * @param pucPDUData PDU buffer data
221 * @param ucPDULength PDU buffer length
222 */
vMBMasterErrorCBReceiveData(UCHAR ucDestAddress,const UCHAR * pucPDUData,USHORT ucPDULength)223 void vMBMasterErrorCBReceiveData(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
224 {
225 BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RECEIVE_DATA);
226 MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_RECEIVE_DATA' failed!", __func__);
227 ESP_LOGD(MB_PORT_TAG,"%s:Callback receive data timeout failure.", __func__);
228 ESP_LOG_BUFFER_HEX_LEVEL("Err rcv buf", (void*)pucPDUData, (uint16_t)ucPDULength, ESP_LOG_DEBUG);
229 }
230
231 /**
232 * This is modbus master execute function error process callback function.
233 * @note There functions will block modbus master poll while execute OS waiting.
234 * So,for real-time of system.Do not execute too much waiting process.
235 *
236 * @param ucDestAddress destination salve address
237 * @param pucPDUData PDU buffer data
238 * @param ucPDULength PDU buffer length
239 *
240 */
vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress,const UCHAR * pucPDUData,USHORT ucPDULength)241 void vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
242 {
243 BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_EXECUTE_FUNCTION);
244 MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_EXECUTE_FUNCTION' failed!", __func__);
245 ESP_LOGD(MB_PORT_TAG,"%s:Callback execute data handler failure.", __func__);
246 ESP_LOG_BUFFER_HEX_LEVEL("Exec func buf", (void*)pucPDUData, (uint16_t)ucPDULength, ESP_LOG_DEBUG);
247 }
248
249 /**
250 * This is modbus master request process success callback function.
251 * @note There functions will block modbus master poll while execute OS waiting.
252 * So,for real-time of system. Do not execute too much waiting process.
253 */
vMBMasterCBRequestSuccess(void)254 void vMBMasterCBRequestSuccess( void ) {
255 /**
256 * @note This code is use OS's event mechanism for modbus master protocol stack.
257 * If you don't use OS, you can change it.
258 */
259 BOOL ret = xMBMasterPortEventPost(EV_MASTER_PROCESS_SUCCESS);
260 MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_PROCESS_SUCCESS' failed!", __func__);
261 ESP_LOGD(MB_PORT_TAG,"%s: Callback request success.", __func__);
262 }
263
264 /**
265 * This function is wait for modbus master request finish and return result.
266 * Waiting result include request process success, request respond timeout,
267 * receive data error and execute function error.You can use the above callback function.
268 * @note If you are use OS, you can use OS's event mechanism. Otherwise you have to run
269 * much user custom delay for waiting.
270 *
271 * @return request error code
272 */
eMBMasterWaitRequestFinish(void)273 eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
274 eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
275 eMBMasterEventType xRecvedEvent;
276
277 EventBits_t uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
278 MB_EVENT_REQ_MASK, // The bits within the event group to wait for.
279 pdTRUE, // Masked bits should be cleared before returning.
280 pdFALSE, // Don't wait for both bits, either bit will do.
281 portMAX_DELAY ); // Wait forever for either bit to be set.
282 xRecvedEvent = (eMBMasterEventType)(uxBits);
283 if (xRecvedEvent) {
284 ESP_LOGD(MB_PORT_TAG,"%s: returned event = 0x%x", __func__, xRecvedEvent);
285 if (!(xRecvedEvent & MB_EVENT_REQ_MASK)) {
286 // if we wait for certain event bits but get from poll subset
287 ESP_LOGE(MB_PORT_TAG,"%s: incorrect event set = 0x%x", __func__, xRecvedEvent);
288 }
289 xEventGroupSetBits( xEventGroupMasterConfirmHdl, (xRecvedEvent & MB_EVENT_REQ_MASK) );
290 if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_PROCESS_SUCCESS)) {
291 eErrStatus = MB_MRE_NO_ERR;
292 } else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RESPOND_TIMEOUT)) {
293 eErrStatus = MB_MRE_TIMEDOUT;
294 } else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RECEIVE_DATA)) {
295 eErrStatus = MB_MRE_REV_DATA;
296 } else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_EXECUTE_FUNCTION)) {
297 eErrStatus = MB_MRE_EXE_FUN;
298 }
299 } else {
300 ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event or timeout xRecvedEvent = 0x%x", __func__, uxBits);
301 // https://github.com/espressif/esp-idf/issues/5275
302 // if a no event is received, that means vMBMasterPortEventClose()
303 // has been closed, so event group has been deleted by FreeRTOS, which
304 // triggers the send of 0 value to the event group to unlock this task
305 // waiting on it. For this patch, handles it as a time out without assert.
306 eErrStatus = MB_MRE_TIMEDOUT;
307 }
308 return eErrStatus;
309 }
310
vMBMasterPortEventClose(void)311 void vMBMasterPortEventClose(void)
312 {
313 vEventGroupDelete(xEventGroupMasterHdl);
314 vEventGroupDelete(xEventGroupMasterConfirmHdl);
315 vEventGroupDelete(xResourceMasterHdl);
316 }
317
318 #endif
319