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: portserial.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions $
35  */
36 
37 #include <string.h>
38 #include "driver/uart.h"
39 #include "soc/dport_access.h"
40 #include "freertos/FreeRTOS.h"
41 #include "freertos/task.h"
42 #include "freertos/queue.h"
43 #include "esp_log.h"
44 #include "sdkconfig.h"
45 
46 /* ----------------------- Modbus includes ----------------------------------*/
47 #include "port.h"
48 #include "mbport.h"
49 #include "mb_m.h"
50 #include "mbrtu.h"
51 #include "mbconfig.h"
52 #include "port_serial_master.h"
53 
54 /* ----------------------- Defines ------------------------------------------*/
55 
56 /* ----------------------- Static variables ---------------------------------*/
57 static const CHAR *TAG = "MB_MASTER_SERIAL";
58 
59 // A queue to handle UART event.
60 static QueueHandle_t xMbUartQueue;
61 static TaskHandle_t  xMbTaskHandle;
62 
63 // The UART hardware port number
64 static UCHAR ucUartNumber = UART_NUM_MAX - 1;
65 
66 static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
67 static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
68 
vMBMasterPortSerialEnable(BOOL bRxEnable,BOOL bTxEnable)69 void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
70 {
71     // This function can be called from xMBRTUTransmitFSM() of different task
72     if (bTxEnable) {
73         bTxStateEnabled = TRUE;
74     } else {
75         bTxStateEnabled = FALSE;
76     }
77     if (bRxEnable) {
78         bRxStateEnabled = TRUE;
79         vTaskResume(xMbTaskHandle); // Resume receiver task
80     } else {
81         vTaskSuspend(xMbTaskHandle); // Block receiver task
82         bRxStateEnabled = FALSE;
83     }
84 }
85 
usMBMasterPortSerialRxPoll(size_t xEventSize)86 static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize)
87 {
88     BOOL xReadStatus = TRUE;
89     USHORT usCnt = 0;
90 
91     if (bRxStateEnabled) {
92         while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) {
93             // Call the Modbus stack callback function and let it fill the stack buffers.
94             xReadStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM
95         }
96         // The buffer is transferred into Modbus stack and is not needed here any more
97         uart_flush_input(ucUartNumber);
98         ESP_LOGD(TAG, "Received data: %d(bytes in buffer)\n", (uint32_t)usCnt);
99     } else {
100         ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%d bytes) received. ", __func__, xEventSize);
101     }
102     return usCnt;
103 }
104 
xMBMasterPortSerialTxPoll(void)105 BOOL xMBMasterPortSerialTxPoll(void)
106 {
107     USHORT usCount = 0;
108     BOOL bNeedPoll = TRUE;
109 
110     if( bTxStateEnabled ) {
111         // Continue while all response bytes put in buffer or out of buffer
112         while(bNeedPoll && (usCount++ < MB_SERIAL_BUF_SIZE)) {
113             // Calls the modbus stack callback function to let it fill the UART transmit buffer.
114             bNeedPoll = pxMBMasterFrameCBTransmitterEmpty( ); // callback to transmit FSM
115         }
116         ESP_LOGD(TAG, "MB_TX_buffer sent: (%d) bytes.", (uint16_t)(usCount - 1));
117         // Waits while UART sending the packet
118         esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
119         vMBMasterPortSerialEnable(TRUE, FALSE);
120         MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
121         return TRUE;
122     }
123     return FALSE;
124 }
125 
126 // UART receive event task
vUartTask(void * pvParameters)127 static void vUartTask(void* pvParameters)
128 {
129     uart_event_t xEvent;
130     USHORT usResult = 0;
131     for(;;) {
132         if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) {
133             ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber);
134             switch(xEvent.type) {
135                 //Event of UART receiving data
136                 case UART_DATA:
137                     ESP_LOGD(TAG,"Data event, len: %d.", xEvent.size);
138                     // This flag set in the event means that no more
139                     // data received during configured timeout and UART TOUT feature is triggered
140                     if (xEvent.timeout_flag) {
141                         // Read received data and send it to modbus stack
142                         usResult = usMBMasterPortSerialRxPoll(xEvent.size);
143                         ESP_LOGD(TAG,"Timeout occured, processed: %d bytes", usResult);
144                         // Block receiver task until data is not processed
145                         vTaskSuspend(NULL);
146                     }
147                     break;
148                 //Event of HW FIFO overflow detected
149                 case UART_FIFO_OVF:
150                     ESP_LOGD(TAG, "hw fifo overflow.");
151                     xQueueReset(xMbUartQueue);
152                     break;
153                 //Event of UART ring buffer full
154                 case UART_BUFFER_FULL:
155                     ESP_LOGD(TAG, "ring buffer full.");
156                     xQueueReset(xMbUartQueue);
157                     uart_flush_input(ucUartNumber);
158                     break;
159                 //Event of UART RX break detected
160                 case UART_BREAK:
161                     ESP_LOGD(TAG, "uart rx break.");
162                     break;
163                 //Event of UART parity check error
164                 case UART_PARITY_ERR:
165                     ESP_LOGD(TAG, "uart parity error.");
166                     break;
167                 //Event of UART frame error
168                 case UART_FRAME_ERR:
169                     ESP_LOGD(TAG, "uart frame error.");
170                     break;
171                 default:
172                     ESP_LOGD(TAG, "uart event type: %d.", xEvent.type);
173                     break;
174             }
175         }
176     }
177     vTaskDelete(NULL);
178 }
179 
180 /* ----------------------- Start implementation -----------------------------*/
xMBMasterPortSerialInit(UCHAR ucPORT,ULONG ulBaudRate,UCHAR ucDataBits,eMBParity eParity)181 BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
182 {
183     esp_err_t xErr = ESP_OK;
184     // Set communication port number
185     ucUartNumber = ucPORT;
186     // Configure serial communication parameters
187     UCHAR ucParity = UART_PARITY_DISABLE;
188     UCHAR ucData = UART_DATA_8_BITS;
189     switch(eParity){
190         case MB_PAR_NONE:
191             ucParity = UART_PARITY_DISABLE;
192             break;
193         case MB_PAR_ODD:
194             ucParity = UART_PARITY_ODD;
195             break;
196         case MB_PAR_EVEN:
197             ucParity = UART_PARITY_EVEN;
198             break;
199         default:
200             ESP_LOGE(TAG, "Incorrect parity option: %d", eParity);
201             return FALSE;
202     }
203     switch(ucDataBits){
204         case 5:
205             ucData = UART_DATA_5_BITS;
206             break;
207         case 6:
208             ucData = UART_DATA_6_BITS;
209             break;
210         case 7:
211             ucData = UART_DATA_7_BITS;
212             break;
213         case 8:
214             ucData = UART_DATA_8_BITS;
215             break;
216         default:
217             ucData = UART_DATA_8_BITS;
218             break;
219     }
220     uart_config_t xUartConfig = {
221         .baud_rate = ulBaudRate,
222         .data_bits = ucData,
223         .parity = ucParity,
224         .stop_bits = UART_STOP_BITS_1,
225         .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
226         .rx_flow_ctrl_thresh = 2,
227         .source_clk = UART_SCLK_APB,
228     };
229     // Set UART config
230     xErr = uart_param_config(ucUartNumber, &xUartConfig);
231     MB_PORT_CHECK((xErr == ESP_OK),
232             FALSE, "mb config failure, uart_param_config() returned (0x%x).", xErr);
233     // Install UART driver, and get the queue.
234     xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
235                                     MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
236     MB_PORT_CHECK((xErr == ESP_OK), FALSE,
237             "mb serial driver failure, uart_driver_install() returned (0x%x).", xErr);
238     // Set timeout for TOUT interrupt (T3.5 modbus time)
239     xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
240     MB_PORT_CHECK((xErr == ESP_OK), FALSE,
241             "mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr);
242 
243     // Set always timeout flag to trigger timeout interrupt even after rx fifo full
244     uart_set_always_rx_timeout(ucUartNumber, true);
245 
246     // Create a task to handle UART events
247     BaseType_t xStatus = xTaskCreatePinnedToCore(vUartTask, "uart_queue_task",
248                                                     MB_SERIAL_TASK_STACK_SIZE,
249                                                     NULL, MB_SERIAL_TASK_PRIO,
250                                                     &xMbTaskHandle, MB_PORT_TASK_AFFINITY);
251     if (xStatus != pdPASS) {
252         vTaskDelete(xMbTaskHandle);
253         // Force exit from function with failure
254         MB_PORT_CHECK(FALSE, FALSE,
255                 "mb stack serial task creation error. xTaskCreate() returned (0x%x).",
256                 xStatus);
257     } else {
258         vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
259     }
260     ESP_LOGD(MB_PORT_TAG,"%s Init serial.", __func__);
261     return TRUE;
262 }
263 
vMBMasterPortSerialClose(void)264 void vMBMasterPortSerialClose(void)
265 {
266     (void)vTaskDelete(xMbTaskHandle);
267     ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
268 }
269 
xMBMasterPortSerialPutByte(CHAR ucByte)270 BOOL xMBMasterPortSerialPutByte(CHAR ucByte)
271 {
272     // Send one byte to UART transmission buffer
273     // This function is called by Modbus stack
274     UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
275     return (ucLength == 1);
276 }
277 
278 // Get one byte from intermediate RX buffer
xMBMasterPortSerialGetByte(CHAR * pucByte)279 BOOL xMBMasterPortSerialGetByte(CHAR* pucByte)
280 {
281     assert(pucByte != NULL);
282     USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS);
283     return (usLength == 1);
284 }
285