1 /* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * FreeModbus Libary: ESP32 Port
17  * Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are permitted provided that the following conditions
21  * are met:
22  * 1. Redistributions of source code must retain the above copyright
23  *   notice, this list of conditions and the following disclaimer.
24  * 2. Redistributions in binary form must reproduce the above copyright
25  *   notice, this list of conditions and the following disclaimer in the
26  *   documentation and/or other materials provided with the distribution.
27  * 3. The name of the author may not be used to endorse or promote products
28  *   derived from this software without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
31  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32  * IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
33  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
34  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
35  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
39  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
42  */
43 #include "port.h"
44 #include "driver/uart.h"
45 #include "freertos/queue.h" // for queue support
46 #include "soc/uart_periph.h"
47 #include "driver/gpio.h"
48 #include "esp_log.h"        // for esp_log
49 #include "esp_err.h"        // for ESP_ERROR_CHECK macro
50 
51 /* ----------------------- Modbus includes ----------------------------------*/
52 #include "mb.h"
53 #include "mbport.h"
54 #include "sdkconfig.h"              // for KConfig options
55 #include "port_serial_slave.h"
56 
57 // Note: This code uses mixed coding standard from legacy IDF code and used freemodbus stack
58 
59 // A queue to handle UART event.
60 static QueueHandle_t xMbUartQueue;
61 static TaskHandle_t  xMbTaskHandle;
62 static const CHAR *TAG = "MB_SERIAL";
63 
64 // The UART hardware port number
65 static UCHAR ucUartNumber = UART_NUM_MAX - 1;
66 
67 static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
68 static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
69 
vMBPortSerialEnable(BOOL bRxEnable,BOOL bTxEnable)70 void vMBPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
71 {
72     // This function can be called from xMBRTUTransmitFSM() of different task
73     if (bTxEnable) {
74         bTxStateEnabled = TRUE;
75     } else {
76         bTxStateEnabled = FALSE;
77     }
78     if (bRxEnable) {
79         //uart_enable_rx_intr(ucUartNumber);
80         bRxStateEnabled = TRUE;
81         vTaskResume(xMbTaskHandle); // Resume receiver task
82     } else {
83         vTaskSuspend(xMbTaskHandle); // Block receiver task
84         bRxStateEnabled = FALSE;
85     }
86 }
87 
usMBPortSerialRxPoll(size_t xEventSize)88 static USHORT usMBPortSerialRxPoll(size_t xEventSize)
89 {
90     BOOL xReadStatus = TRUE;
91     USHORT usCnt = 0;
92 
93     if (bRxStateEnabled) {
94         // Get received packet into Rx buffer
95         while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) {
96             // Call the Modbus stack callback function and let it fill the buffers.
97             xReadStatus = pxMBFrameCBByteReceived(); // callback to execute receive FSM
98         }
99         uart_flush_input(ucUartNumber);
100         // Send event EV_FRAME_RECEIVED to allow stack process packet
101 #if !CONFIG_FMB_TIMER_PORT_ENABLED
102         // Let the stack know that T3.5 time is expired and data is received
103         (void)pxMBPortCBTimerExpired(); // calls callback xMBRTUTimerT35Expired();
104 #endif
105         ESP_LOGD(TAG, "RX: %d bytes\n", usCnt);
106     }
107     return usCnt;
108 }
109 
xMBPortSerialTxPoll(void)110 BOOL xMBPortSerialTxPoll(void)
111 {
112     USHORT usCount = 0;
113     BOOL bNeedPoll = TRUE;
114 
115     if( bTxStateEnabled ) {
116         // Continue while all response bytes put in buffer or out of buffer
117         while((bNeedPoll) && (usCount++ < MB_SERIAL_BUF_SIZE)) {
118             // Calls the modbus stack callback function to let it fill the UART transmit buffer.
119             bNeedPoll = pxMBFrameCBTransmitterEmpty( ); // callback to transmit FSM
120         }
121         ESP_LOGD(TAG, "MB_TX_buffer send: (%d) bytes\n", (uint16_t)usCount);
122         // Waits while UART sending the packet
123         esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
124         vMBPortSerialEnable(TRUE, FALSE);
125         MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
126         return TRUE;
127     }
128     return FALSE;
129 }
130 
vUartTask(void * pvParameters)131 static void vUartTask(void *pvParameters)
132 {
133     uart_event_t xEvent;
134     USHORT usResult = 0;
135     for(;;) {
136         if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) {
137             ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber);
138             switch(xEvent.type) {
139                 //Event of UART receving data
140                 case UART_DATA:
141                     ESP_LOGD(TAG,"Data event, length: %d", xEvent.size);
142                     // This flag set in the event means that no more
143                     // data received during configured timeout and UART TOUT feature is triggered
144                     if (xEvent.timeout_flag) {
145                         // Read received data and send it to modbus stack
146                         usResult = usMBPortSerialRxPoll(xEvent.size);
147                         ESP_LOGD(TAG,"Timeout occured, processed: %d bytes", usResult);
148                     }
149                     break;
150                 //Event of HW FIFO overflow detected
151                 case UART_FIFO_OVF:
152                     ESP_LOGD(TAG, "hw fifo overflow\n");
153                     xQueueReset(xMbUartQueue);
154                     break;
155                 //Event of UART ring buffer full
156                 case UART_BUFFER_FULL:
157                     ESP_LOGD(TAG, "ring buffer full\n");
158                     xQueueReset(xMbUartQueue);
159                     uart_flush_input(ucUartNumber);
160                     break;
161                 //Event of UART RX break detected
162                 case UART_BREAK:
163                     ESP_LOGD(TAG, "uart rx break\n");
164                     break;
165                 //Event of UART parity check error
166                 case UART_PARITY_ERR:
167                     ESP_LOGD(TAG, "uart parity error\n");
168                     break;
169                 //Event of UART frame error
170                 case UART_FRAME_ERR:
171                     ESP_LOGD(TAG, "uart frame error\n");
172                     break;
173                 default:
174                     ESP_LOGD(TAG, "uart event type: %d\n", xEvent.type);
175                     break;
176             }
177         }
178     }
179     vTaskDelete(NULL);
180 }
181 
xMBPortSerialInit(UCHAR ucPORT,ULONG ulBaudRate,UCHAR ucDataBits,eMBParity eParity)182 BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate,
183                         UCHAR ucDataBits, eMBParity eParity)
184 {
185     esp_err_t xErr = ESP_OK;
186     // Set communication port number
187     ucUartNumber = ucPORT;
188     // Configure serial communication parameters
189     UCHAR ucParity = UART_PARITY_DISABLE;
190     UCHAR ucData = UART_DATA_8_BITS;
191     switch(eParity){
192         case MB_PAR_NONE:
193             ucParity = UART_PARITY_DISABLE;
194             break;
195         case MB_PAR_ODD:
196             ucParity = UART_PARITY_ODD;
197             break;
198         case MB_PAR_EVEN:
199             ucParity = UART_PARITY_EVEN;
200             break;
201         default:
202             ESP_LOGE(TAG, "Incorrect parity option: %d", eParity);
203             return FALSE;
204     }
205     switch(ucDataBits){
206         case 5:
207             ucData = UART_DATA_5_BITS;
208             break;
209         case 6:
210             ucData = UART_DATA_6_BITS;
211             break;
212         case 7:
213             ucData = UART_DATA_7_BITS;
214             break;
215         case 8:
216             ucData = UART_DATA_8_BITS;
217             break;
218         default:
219             ucData = UART_DATA_8_BITS;
220             break;
221     }
222     uart_config_t xUartConfig = {
223         .baud_rate = ulBaudRate,
224         .data_bits = ucData,
225         .parity = ucParity,
226         .stop_bits = UART_STOP_BITS_1,
227         .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
228         .rx_flow_ctrl_thresh = 2,
229         .use_ref_tick = UART_SCLK_APB,
230     };
231     // Set UART config
232     xErr = uart_param_config(ucUartNumber, &xUartConfig);
233     MB_PORT_CHECK((xErr == ESP_OK),
234             FALSE, "mb config failure, uart_param_config() returned (0x%x).", xErr);
235     // Install UART driver, and get the queue.
236     xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
237                                     MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
238     MB_PORT_CHECK((xErr == ESP_OK), FALSE,
239             "mb serial driver failure, uart_driver_install() returned (0x%x).", xErr);
240 #if !CONFIG_FMB_TIMER_PORT_ENABLED
241     // Set timeout for TOUT interrupt (T3.5 modbus time)
242     xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
243     MB_PORT_CHECK((xErr == ESP_OK), FALSE,
244             "mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr);
245 #endif
246 
247     // Set always timeout flag to trigger timeout interrupt even after rx fifo full
248     uart_set_always_rx_timeout(ucUartNumber, true);
249 
250     // Create a task to handle UART events
251     BaseType_t xStatus = xTaskCreatePinnedToCore(vUartTask, "uart_queue_task",
252                                                     MB_SERIAL_TASK_STACK_SIZE,
253                                                     NULL, MB_SERIAL_TASK_PRIO,
254                                                     &xMbTaskHandle, MB_PORT_TASK_AFFINITY);
255     if (xStatus != pdPASS) {
256         vTaskDelete(xMbTaskHandle);
257         // Force exit from function with failure
258         MB_PORT_CHECK(FALSE, FALSE,
259                 "mb stack serial task creation error. xTaskCreate() returned (0x%x).",
260                 xStatus);
261     } else {
262         vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
263     }
264     return TRUE;
265 }
266 
vMBPortSerialClose(void)267 void vMBPortSerialClose(void)
268 {
269     (void)vTaskSuspend(xMbTaskHandle);
270     (void)vTaskDelete(xMbTaskHandle);
271     ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
272 }
273 
xMBPortSerialPutByte(CHAR ucByte)274 BOOL xMBPortSerialPutByte(CHAR ucByte)
275 {
276     // Send one byte to UART transmission buffer
277     // This function is called by Modbus stack
278     UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
279     return (ucLength == 1);
280 }
281 
282 // Get one byte from intermediate RX buffer
xMBPortSerialGetByte(CHAR * pucByte)283 BOOL xMBPortSerialGetByte(CHAR* pucByte)
284 {
285     assert(pucByte != NULL);
286     USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS);
287     return (usLength == 1);
288 }
289