From bd49261d8e95e116b327c68a64749dc1a000f987 Mon Sep 17 00:00:00 2001 From: digant73 Date: Fri, 22 Sep 2023 23:30:54 +0200 Subject: [PATCH 01/20] Added TX Interrupt/DMA based serial writing --- TFT/src/User/API/Printing.c | 12 +- TFT/src/User/API/SerialConnection.c | 134 ++++--- TFT/src/User/API/SerialConnection.h | 22 +- TFT/src/User/API/menu.c | 17 +- TFT/src/User/API/parseACK.c | 6 +- TFT/src/User/Hal/gd32f20x/Serial.c | 116 ++++-- TFT/src/User/Hal/gd32f20x/Serial.h | 31 +- TFT/src/User/Hal/gd32f20x/uart.c | 15 +- TFT/src/User/Hal/gd32f20x/uart.h | 3 +- TFT/src/User/Hal/stm32f10x/Serial.c | 103 ++++-- TFT/src/User/Hal/stm32f10x/Serial.h | 31 +- TFT/src/User/Hal/stm32f10x/uart.c | 17 +- TFT/src/User/Hal/stm32f10x/uart.h | 3 +- TFT/src/User/Hal/stm32f2_f4xx/Serial.c | 472 +++++++++++++++++++++---- TFT/src/User/Hal/stm32f2_f4xx/Serial.h | 31 +- TFT/src/User/Hal/stm32f2_f4xx/uart.c | 11 +- TFT/src/User/Hal/stm32f2_f4xx/uart.h | 3 +- TFT/src/User/main.c | 8 +- TFT/src/User/main.h | 6 +- 19 files changed, 794 insertions(+), 247 deletions(-) diff --git a/TFT/src/User/API/Printing.c b/TFT/src/User/API/Printing.c index 10d409c102..127cad4a22 100644 --- a/TFT/src/User/API/Printing.c +++ b/TFT/src/User/API/Printing.c @@ -105,7 +105,17 @@ void loopBreakToCondition(CONDITION_CALLBACK condCallback) // from that command. Than another "M108" will be sent to unlock a next possible blocking command. // This way enough "M108" will be sent to unlock all possible blocking command(s) (ongoing or enqueued) but not too much and // not too fast one after another to overload/overrun the serial communication - TASK_LOOP_WHILE(condCallback(), if (Serial_Available(SERIAL_PORT) != 0) sendEmergencyCmd("M108\n")); + + uint16_t rIndex_old = -1; // out of band value -1 will guarantee the beginning of M108 transmission loop + uint16_t rIndex; + + TASK_LOOP_WHILE(condCallback(), + if ((rIndex = Serial_GetReadingIndex(SERIAL_PORT)) != rIndex_old) + { + sendEmergencyCmd("M108\n"); + rIndex_old = rIndex; + } + ); // remove any enqueued command that could come from a supplementary serial port or TFT media // (if printing from remote host or TFT media) during the loop above diff --git a/TFT/src/User/API/SerialConnection.c b/TFT/src/User/API/SerialConnection.c index 6c3dd9dfb9..77c3ff2e63 100644 --- a/TFT/src/User/API/SerialConnection.c +++ b/TFT/src/User/API/SerialConnection.c @@ -1,21 +1,30 @@ #include "SerialConnection.h" #include "includes.h" -#define SERIAL_PORT_QUEUE_SIZE NOBEYOND(512, RAM_SIZE * 64, 4096) -#define SERIAL_PORT_2_QUEUE_SIZE 512 -#define SERIAL_PORT_3_QUEUE_SIZE 512 -#define SERIAL_PORT_4_QUEUE_SIZE 512 +// uncomment this line to use inline copy (fast code) instead of memcpy() (less code) +#define USE_INLINE_COPY + +#define SERIAL_PORT_RX_QUEUE_SIZE NOBEYOND(ACK_CACHE_SIZE, RAM_SIZE * 64, 4 * ACK_CACHE_SIZE) +#define SERIAL_PORT_2_RX_QUEUE_SIZE ACK_CACHE_SIZE +#define SERIAL_PORT_3_RX_QUEUE_SIZE ACK_CACHE_SIZE +#define SERIAL_PORT_4_RX_QUEUE_SIZE ACK_CACHE_SIZE + +// make TX queue size simmetric to ACK messages queue size +#define SERIAL_PORT_TX_QUEUE_SIZE ACK_CACHE_SIZE +#define SERIAL_PORT_2_TX_QUEUE_SIZE ACK_CACHE_SIZE +#define SERIAL_PORT_3_TX_QUEUE_SIZE ACK_CACHE_SIZE +#define SERIAL_PORT_4_TX_QUEUE_SIZE ACK_CACHE_SIZE const SERIAL_PORT_INFO serialPort[SERIAL_PORT_COUNT] = { - {SERIAL_PORT, SERIAL_PORT_QUEUE_SIZE, "", "1 - Printer"}, + {SERIAL_PORT , SERIAL_PORT_RX_QUEUE_SIZE , SERIAL_PORT_TX_QUEUE_SIZE , "" , "1 - Printer"}, #ifdef SERIAL_PORT_2 - {SERIAL_PORT_2, SERIAL_PORT_2_QUEUE_SIZE, "2", "2 - WIFI"}, + {SERIAL_PORT_2, SERIAL_PORT_2_RX_QUEUE_SIZE, SERIAL_PORT_2_TX_QUEUE_SIZE, "2", "2 - Wifi"}, #endif #ifdef SERIAL_PORT_3 - {SERIAL_PORT_3, SERIAL_PORT_3_QUEUE_SIZE, "3", "3 - UART3"}, + {SERIAL_PORT_3, SERIAL_PORT_3_RX_QUEUE_SIZE, SERIAL_PORT_3_TX_QUEUE_SIZE, "3", "3 - UART3"}, #endif #ifdef SERIAL_PORT_4 - {SERIAL_PORT_4, SERIAL_PORT_4_QUEUE_SIZE, "4", "4 - UART4"} + {SERIAL_PORT_4, SERIAL_PORT_4_RX_QUEUE_SIZE, SERIAL_PORT_4_TX_QUEUE_SIZE, "4", "4 - UART4"} #endif }; @@ -28,7 +37,7 @@ static inline void Serial_InitPrimary(void) infoHost.status = HOST_STATUS_IDLE; setReminderMsg(LABEL_UNCONNECTED, SYS_STATUS_DISCONNECTED); - Serial_Config(serialPort[PORT_1].port, serialPort[PORT_1].cacheSize, baudrateValues[infoSettings.serial_port[PORT_1]]); + Serial_Config(serialPort[PORT_1].port, serialPort[PORT_1].cacheSizeRX, serialPort[PORT_1].cacheSizeTX, baudrateValues[infoSettings.serial_port[PORT_1]]); } static inline void Serial_DeInitPrimary(void) @@ -55,7 +64,7 @@ void Serial_Init(SERIAL_PORT_INDEX portIndex) // Disable the serial port when it is not in use and/or not connected to a device (floating) to // avoid to receive and process wrong data due to possible electromagnetic interference (EMI). if (infoSettings.serial_port[portIndex] > 0) // if serial port is enabled - Serial_Config(serialPort[portIndex].port, serialPort[portIndex].cacheSize, + Serial_Config(serialPort[portIndex].port, serialPort[portIndex].cacheSizeRX, serialPort[portIndex].cacheSizeTX, baudrateValues[infoSettings.serial_port[portIndex]]); } } @@ -66,7 +75,7 @@ void Serial_Init(SERIAL_PORT_INDEX portIndex) { if (infoSettings.serial_port[portIndex] > 0) // if serial port is enabled { - Serial_Config(serialPort[portIndex].port, serialPort[portIndex].cacheSize, + Serial_Config(serialPort[portIndex].port, serialPort[portIndex].cacheSizeRX, serialPort[portIndex].cacheSizeTX, baudrateValues[infoSettings.serial_port[portIndex]]); } } @@ -132,58 +141,101 @@ void Serial_Forward(SERIAL_PORT_INDEX portIndex, const char * msg) #endif ) Serial_Put(serialPort[portIndex].port, msg); // pass on the message to the port - } } -uint16_t Serial_Available(SERIAL_PORT_INDEX portIndex) +uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize) { - if (!WITHIN(portIndex, PORT_1, SERIAL_PORT_COUNT - 1)) - return 0; + // NOTE: used 32 bit variables for performance reasons (in particular for data copy) - return (dmaL1Data[portIndex].cacheSize + dmaL1Data[portIndex].wIndex - dmaL1Data[portIndex].rIndex) % dmaL1Data[portIndex].cacheSize; -} + // wIndex: update L1 cache's writing index (dynamically changed (by L1 cache's interrupt handler) variables/attributes) + // and make a static access (32 bit) to it to speedup performance on this function + // + uint32_t wIndex = dmaL1DataRX[port].wIndex = Serial_GetWritingIndex(port); // get the latest wIndex + uint32_t flag = dmaL1DataRX[port].flag; // get the current flag position -uint16_t Serial_Get(SERIAL_PORT_INDEX portIndex, char * buf, uint16_t bufSize) -{ - // if port index is out of range or no data to read from L1 cache - if (!WITHIN(portIndex, PORT_1, SERIAL_PORT_COUNT - 1) || dmaL1Data[portIndex].flag == dmaL1Data[portIndex].wIndex) + if (flag == wIndex) // if no data to read from L1 cache, nothing to do return 0; - DMA_CIRCULAR_BUFFER * dmaL1Data_ptr = &dmaL1Data[portIndex]; - - // make a static access to dynamically changed (by L1 cache's interrupt handler) variables/attributes - uint16_t wIndex = dmaL1Data_ptr->wIndex; + uint32_t cacheSize = dmaL1DataRX[port].cacheSize; - // L1 cache's reading index (not dynamically changed (by L1 cache's interrupt handler) variables/attributes) - uint16_t rIndex = dmaL1Data_ptr->rIndex; + while (dmaL1DataRX[port].cache[flag] != '\n' && flag != wIndex) // check presence of "\n" in available data + { + flag = (flag + 1) % cacheSize; + } - while (dmaL1Data_ptr->cache[rIndex] == ' ' && rIndex != wIndex) // remove leading empty space, if any + if (flag == wIndex) // if "\n" was not found (message incomplete), update flag and exit { - rIndex = (rIndex + 1) % dmaL1Data_ptr->cacheSize; + dmaL1DataRX[port].flag = flag; // update queue's custom flag with flag (also equal to wIndex) + + return 0; } - for (uint16_t i = 0; i < (bufSize - 1) && rIndex != wIndex; ) // retrieve data until buf is full or L1 cache is empty + // rIndex: L1 cache's reading index (not dynamically changed (by L1 cache's interrupt handler) variables/attributes) + // + DMA_CIRCULAR_BUFFER * dmaL1Data_ptr = &dmaL1DataRX[port]; + char * cache = dmaL1Data_ptr->cache; + uint32_t rIndex = dmaL1Data_ptr->rIndex; + + while (cache[rIndex] == ' ' && rIndex != flag) // remove leading empty space, if any { - buf[i] = dmaL1Data_ptr->cache[rIndex]; - rIndex = (rIndex + 1) % dmaL1Data_ptr->cacheSize; + rIndex = (rIndex + 1) % cacheSize; + } + + // msgSize: message size (after updating rIndex removing leading empty spaces). Terminating null character '\0' not included + uint32_t msgSize = (cacheSize + flag - rIndex) % cacheSize + 1; - if (buf[i++] == '\n') // if data end marker is found + // if buf size is not enough to store the data plus the terminating null character "\0", skip the data copy + // + // NOTE: the following check should never be matched if buf has a proper size and there is no reading error. + // If so, the check could be commented out just to improve performance. Just keep it to make the code more robust + // + if (bufSize < (msgSize + 1)) // +1 is for the terminating null character '\0' + return 0; + + // if data is one chunk only, retrieve data from upper part of circular cache + if (rIndex <= flag) + { + #ifdef USE_INLINE_COPY + while (rIndex <= flag) { - buf[i] = '\0'; // end character - dmaL1Data_ptr->flag = dmaL1Data_ptr->rIndex = rIndex; // update queue's custom flag and reading index with rIndex + *(buf++) = cache[rIndex++]; + } + #else + memcpy(buf, &cache[rIndex], msgSize); + buf += msgSize; + #endif + } + else // data at end and beginning of cache + { + #ifdef USE_INLINE_COPY + while (rIndex <= cacheSize) + { + *(buf++) = cache[rIndex++]; + } + + rIndex = 0; + uint32_t maxIndex = bufSize - (cacheSize - dmaL1Data_ptr->rIndex); // used dmaL1Data_ptr->rIndex and not rIndex - return i; // return the number of bytes stored in buf + while (rIndex < maxIndex) + { + *(buf++) = cache[rIndex++]; } + #else + memcpy(buf, &cache[rIndex], cacheSize - rIndex); + buf += cacheSize - rIndex; + + memcpy(buf, cache, flag + 1); + buf += flag; + #endif } - // if here, a partial message is present on the L1 cache (message not terminated by "\n"). - // We temporary skip the message until it is fully received updating also dmaL1Data_ptr->flag to - // prevent to read again (multiple times) the same partial message on next function invokation + *buf = '\0'; // end character - dmaL1Data_ptr->flag = wIndex; // update queue's custom flag with wIndex + // update queue's custom flag and reading index with next index + dmaL1Data_ptr->flag = dmaL1Data_ptr->rIndex = (flag + 1) % cacheSize; - return 0; // return the number of bytes stored in buf + return msgSize; // return the number of bytes stored in buf } #ifdef SERIAL_PORT_2 diff --git a/TFT/src/User/API/SerialConnection.h b/TFT/src/User/API/SerialConnection.h index de1d055664..5e2ede44a2 100644 --- a/TFT/src/User/API/SerialConnection.h +++ b/TFT/src/User/API/SerialConnection.h @@ -10,6 +10,10 @@ extern "C" { #include "variants.h" // for SERIAL_PORT_2 etc. #include "uart.h" // for _UART_CNT etc. +// size of buffer where read ACK messages are stored (including terminating null character '\0'). +// Use a power of 2 for performance reasons! +#define ACK_CACHE_SIZE NOBEYOND(256, 512, 512) + #define BAUDRATE_COUNT 10 typedef enum @@ -27,12 +31,13 @@ typedef enum PORT_4, #endif SERIAL_PORT_COUNT -} SERIAL_PORT_INDEX; // serial port index for all enabled serial ports (This is not actual physical port number) +} SERIAL_PORT_INDEX; // serial port index for all enabled serial ports (this is not the actual physical serial port number) typedef struct { uint8_t port; // physical port (e.g. _USART1) related to serial port (e.g. 0 for SERIAL_PORT, 1 for SERIAL_PORT_2 etc...) - uint16_t cacheSize; // queue size for sending/receiving data to/from the port + uint16_t cacheSizeRX; // buffer size for receiving data from the serial port + uint16_t cacheSizeTX; // buffer size for sending data to the serial port const char * const id; // serial port ID (e.g. "" for SERIAL_PORT, "2" for SERIAL_PORT_2 etc...) const char * const desc; // serial port description (e.g. "1 - Printer" for SERIAL_PORT, "2 - WIFI" for SERIAL_PORT_2 etc...) } SERIAL_PORT_INFO; // serial port info @@ -60,21 +65,16 @@ void Serial_DeInit(SERIAL_PORT_INDEX portIndex); // - ALL_PORTS: all serial ports (primary and supplementary) // - SUP_PORTS: all supplementary serial ports // - specific port index: specific serial port +// - msg: message to send void Serial_Forward(SERIAL_PORT_INDEX portIndex, const char * msg); -// retrieve the number of bytes available on the provided serial port: -// - portIndex: index of serial port -// -// - return value: number of bytes available on serial port -uint16_t Serial_Available(SERIAL_PORT_INDEX portIndex); - -// retrieve a message from the provided serial port, if any: -// - portIndex: index of serial port where data are read from +// retrieve a message from the provided physical serial port: +// - port: physical serial port where data are read from // - buf: buffer where data are stored // - bufSize: size of buffer (max number of bytes that can be stored in buf) // // - return value: number of bytes stored in buf -uint16_t Serial_Get(SERIAL_PORT_INDEX portIndex, char * buf, uint16_t bufSize); +uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize); #ifdef SERIAL_PORT_2 // retrieve messages from all the enabled supplementary ports storing them diff --git a/TFT/src/User/API/menu.c b/TFT/src/User/API/menu.c index 253a8dd06b..f08b39a7f4 100644 --- a/TFT/src/User/API/menu.c +++ b/TFT/src/User/API/menu.c @@ -1220,6 +1220,14 @@ void loopBackEnd(void) Serial_GetFromUART(); #endif + // Handle USB communication + #ifdef USB_FLASH_DRIVE_SUPPORT + USB_LoopProcess(); + #endif + + if ((bePriorityCounter++ % BE_PRIORITY_DIVIDER) != 0) // a divider value of 16 -> run 6% of the time only + return; + // Temperature monitor loopCheckHeater(); @@ -1238,11 +1246,6 @@ void loopBackEnd(void) if (infoMachineSettings.onboardSD == ENABLED) loopPrintFromOnboard(); - // Handle USB communication - #ifdef USB_FLASH_DRIVE_SUPPORT - USB_LoopProcess(); - #endif - // Check filament runout status #ifdef FIL_RUNOUT_PIN FIL_BE_CheckRunout(); @@ -1316,6 +1319,10 @@ void loopFrontEnd(void) void loopProcess(void) { loopBackEnd(); + + if ((fePriorityCounter++ % FE_PRIORITY_DIVIDER) != 0) // a divider value of 16 -> run 6% of the time only + return; + loopFrontEnd(); } diff --git a/TFT/src/User/API/parseACK.c b/TFT/src/User/API/parseACK.c index d43a332caf..79b7944890 100644 --- a/TFT/src/User/API/parseACK.c +++ b/TFT/src/User/API/parseACK.c @@ -43,10 +43,8 @@ const char magic_echo[] = "echo:"; const char magic_warning[] = "Warning:"; // RRF warning const char magic_message[] = "message"; // RRF message in Json format -#define ACK_CACHE_SIZE 512 // including ending character '\0' - char ack_cache[ACK_CACHE_SIZE]; // buffer where read ACK messages are stored -uint16_t ack_len; // length of data currently present in ack_cache +uint16_t ack_len; // length of data present in ack_cache without the terminating null character '\0' uint16_t ack_index; SERIAL_PORT_INDEX ack_port_index = PORT_1; // index of target serial port for the ACK message (related to originating gcode) bool hostDialog = false; @@ -469,7 +467,7 @@ void parseACK(void) requestCommandInfo.inResponse = false; } } - else if (strlen(requestCommandInfo.cmd_rev_buf) + strlen(ack_cache) < CMD_MAX_REV) + else if (strlen(requestCommandInfo.cmd_rev_buf) + (ack_len + 1) < CMD_MAX_REV) // +1 is for the terminating null character '\0' { strcat(requestCommandInfo.cmd_rev_buf, ack_cache); diff --git a/TFT/src/User/Hal/gd32f20x/Serial.c b/TFT/src/User/Hal/gd32f20x/Serial.c index 2b9badedcb..673c1664d7 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.c +++ b/TFT/src/User/Hal/gd32f20x/Serial.c @@ -1,8 +1,24 @@ #include "Serial.h" #include "includes.h" // for infoHost -// dma rx buffer -DMA_CIRCULAR_BUFFER dmaL1Data[_UART_CNT] = {0}; +// set this line to "true" to enable serial IDLE interrupt. IDLE interrupt is no more needed, so always set this macro to "false" +#define RX_IDLE_INTERRUPT false + +// uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used +//#define TX_DMA_WRITE + +// uncomment this line to use compact code (less code) instead of fast code +#define USE_COMPACT_CODE + +#ifndef USE_COMPACT_CODE + // uncomment this line to use inline copy (fast code) instead of memcpy() (less code) + #define USE_INLINE_COPY +#endif + +//#define DEFAULT_WRITE // old unbuffered TX serial writing (just for comparison with new TX solutions) + +DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer +DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer // Config for USART Channel //USART1 RX DMA2 Channel4 Steam2/5 @@ -35,32 +51,33 @@ void Serial_DMAClearFlag(uint8_t port) switch(port) { case _USART1: DMA_INTC(DMA0) = (0x0F << 12); break; - case _USART2: DMA_INTC(DMA0) = (0x0F << 20); break; + case _USART2: DMA_INTC(DMA0) = (0x0F << 20); break; case _USART3: DMA_INTC(DMA0) = (0x0F << 8); break; - case _UART4: DMA_INTC(DMA1) = (0x0F << 8); break; + case _UART4: DMA_INTC(DMA1) = (0x0F << 8); break; case _UART5: DMA_INTC(DMA1) = (0x0F << 4); break; - case _USART6: DMA_INTC(DMA1) = (0x0F << 20); break; + case _USART6: DMA_INTC(DMA1) = (0x0F << 20); break; } } void Serial_DMA_Config(uint8_t port) { const SERIAL_CFG * cfg = &Serial[port]; + rcu_periph_clock_enable(cfg->dma_rcc); - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) &= ~(1<<0); // Disable DMA + DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) &= ~(1<<0); // Disable DMA Serial_DMAClearFlag(port); - USART_CTL2(cfg->uart) |= 1<<6; // DMA enable receiver + USART_CTL2(cfg->uart) |= 1<<6; // DMA enable receiver DMA_CHPADDR(cfg->dma_stream, cfg->dma_channel) = (uint32_t)(&USART_DATA(cfg->uart)); - DMA_CHMADDR(cfg->dma_stream, cfg->dma_channel) = (uint32_t)(dmaL1Data[port].cache); - DMA_CHCNT(cfg->dma_stream, cfg->dma_channel) = dmaL1Data[port].cacheSize; + DMA_CHMADDR(cfg->dma_stream, cfg->dma_channel) = (uint32_t)(dmaL1DataRX[port].cache); + DMA_CHCNT(cfg->dma_stream, cfg->dma_channel) = dmaL1DataRX[port].cacheSize; // DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) = cfg->dma_channel << 25; DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 3<<12; // Priority level: Very high DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 0<<10; // Memory data size: 8 - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 0<<8; // Peripheral data size: 8 - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 1<<7; // Memory increment mode + DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 0<<8; // Peripheral data size: 8 + DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 1<<7; // Memory increment mode DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 0<<6; // Peripheral not increment mode DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 1<<5; // Circular mode enabled DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 0<<4; // Data transfer direction: Peripheral-to-memory @@ -69,24 +86,36 @@ void Serial_DMA_Config(uint8_t port) void Serial_ClearData(uint8_t port) { - dmaL1Data[port].wIndex = dmaL1Data[port].rIndex = dmaL1Data[port].flag = dmaL1Data[port].cacheSize = 0; + dmaL1DataRX[port].wIndex = dmaL1DataRX[port].rIndex = dmaL1DataRX[port].flag = dmaL1DataRX[port].cacheSize = 0; - if (dmaL1Data[port].cache != NULL) + if (dmaL1DataRX[port].cache != NULL) { - free(dmaL1Data[port].cache); - dmaL1Data[port].cache = NULL; + free(dmaL1DataRX[port].cache); + dmaL1DataRX[port].cache = NULL; + } + + dmaL1DataTX[port].wIndex = dmaL1DataTX[port].rIndex = dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize = 0; + + if (dmaL1DataTX[port].cache != NULL) + { + free(dmaL1DataTX[port].cache); + dmaL1DataTX[port].cache = NULL; } } -void Serial_Config(uint8_t port, uint16_t cacheSize, uint32_t baudrate) +void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate) { Serial_ClearData(port); - dmaL1Data[port].cacheSize = cacheSize; - dmaL1Data[port].cache = malloc(cacheSize); - while (!dmaL1Data[port].cache); // malloc failed + dmaL1DataRX[port].cacheSize = cacheSizeRX; + dmaL1DataRX[port].cache = malloc(cacheSizeRX); + while (!dmaL1DataRX[port].cache); // malloc failed, blocking! + + dmaL1DataTX[port].cacheSize = cacheSizeTX; + dmaL1DataTX[port].cache = malloc(cacheSizeTX); + while (!dmaL1DataTX[port].cache); // malloc failed, blocking! - UART_Config(port, baudrate, USART_INT_IDLE); // IDLE interrupt + UART_Config(port, baudrate, USART_IT_IDLE, RX_IDLE_INTERRUPT); // configure serial line with or without IDLE interrupt Serial_DMA_Config(port); } @@ -99,15 +128,43 @@ void Serial_DeConfig(uint8_t port) UART_DeConfig(port); } +uint16_t Serial_GetReadingIndex(uint8_t port) +{ + return dmaL1DataRX[port].rIndex; +} + +uint16_t Serial_GetWritingIndex(uint8_t port) +{ + return dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channel); +} + +void Serial_PutChar(uint8_t port, const char ch) +{ + while ((USART_STAT0(Serial[port].uart) & (1 << USART_FLAG_TC)) == (uint16_t)RESET); + USART_DATA(Serial[port].uart) = (uint8_t) ch; +} + +void Serial_Put(uint8_t port, const char * msg) +{ + while (*msg) + { + while ((USART_STAT0(Serial[port].uart) & (1 << USART_FLAG_TC)) == (uint16_t)RESET); + USART_DATA(Serial[port].uart) = ((uint16_t)*msg++ & (uint16_t)0x01FF); + } +} + +// ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { +#if IDLE_INTERRUPT == true // RX serial IDLE interrupt if ((USART_STAT0(Serial[port].uart) & (1<<4)) != 0) { - USART_STAT0(Serial[port].uart); // Clear interrupt flag + USART_STAT0(Serial[port].uart); // clear interrupt flag USART_DATA(Serial[port].uart); - dmaL1Data[port].wIndex = dmaL1Data[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channel); + dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channel); } +#endif } void USART0_IRQHandler(void) @@ -139,18 +196,3 @@ void USART5_IRQHandler(void) { USART_IRQHandler(_USART6); } - -void Serial_Put(uint8_t port, const char *s) -{ - while (*s) - { - while ((USART_STAT0(Serial[port].uart) & (1 << USART_FLAG_TC)) == (uint16_t)RESET); - USART_DATA(Serial[port].uart) = ((uint16_t)*s++ & (uint16_t)0x01FF); - } -} - -void Serial_PutChar(uint8_t port, const char ch) -{ - while ((USART_STAT0(Serial[port].uart) & (1 << USART_FLAG_TC)) == (uint16_t)RESET); - USART_DATA(Serial[port].uart) = (uint8_t) ch; -} diff --git a/TFT/src/User/Hal/gd32f20x/Serial.h b/TFT/src/User/Hal/gd32f20x/Serial.h index ca48a22082..c2d1953372 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.h +++ b/TFT/src/User/Hal/gd32f20x/Serial.h @@ -5,20 +5,37 @@ #include "variants.h" // for uint32_t etc... #include "uart.h" -typedef volatile struct // precautionally declared as volatile due to access from interrupt handler and main thread +typedef struct { char *cache; - uint16_t wIndex; // writing index - uint16_t rIndex; // reading index - uint16_t flag; // custom flag (for custom usage by the application) + volatile uint16_t wIndex; // writing index. Precautionally declared as volatile due to access from interrupt handler and main thread + volatile uint16_t rIndex; // reading index. Precautionally declared as volatile due to access from interrupt handler and main thread + uint16_t flag; // custom flag (for custom usage by the application) uint16_t cacheSize; } DMA_CIRCULAR_BUFFER; -extern DMA_CIRCULAR_BUFFER dmaL1Data[_UART_CNT]; +extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; -void Serial_Config(uint8_t port, uint16_t cacheSize, uint32_t baudrate); +void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); -void Serial_Put(uint8_t port, const char *s); + +// retrieve the next reading index in the message queue of the provided serial port: +// - port: index of serial port +// +// - return value: next reading index +uint16_t Serial_GetReadingIndex(uint8_t port); + +// retrieve the next writing index in the message queue of the provided serial port +// based on Interrupt/DMA status while writing serial data in the background +// - port: index of serial port +// +// - return value: next writing index +uint16_t Serial_GetWritingIndex(uint8_t port); + +// send character to a uart port. TX interrupt based serial writing is always used even if TX DMA based serial writing is enabled void Serial_PutChar(uint8_t port, const char ch); +// send a zero terminated message to uart port. TX interrupt based serial writing is used if TX DMA based serial writing is disabled +void Serial_Put(uint8_t port, const char * msg); + #endif diff --git a/TFT/src/User/Hal/gd32f20x/uart.c b/TFT/src/User/Hal/gd32f20x/uart.c index bba1545d47..92b95a67eb 100644 --- a/TFT/src/User/Hal/gd32f20x/uart.c +++ b/TFT/src/User/Hal/gd32f20x/uart.c @@ -87,7 +87,7 @@ void UART_GPIO_DeInit(uint8_t port) GPIO_InitSet(uart_rx[port], MGPIO_MODE_IPN, 0); } -void UART_Protocol_Init(uint8_t port,uint32_t baud) +void UART_Protocol_Init(uint8_t port, uint32_t baud) { rcu_periph_clock_enable(rcu_uart_en[port]); // Enable clock @@ -103,19 +103,22 @@ void UART_Protocol_Init(uint8_t port,uint32_t baud) usart_enable(uart[port]); } -void UART_IRQ_Init(uint8_t port, uint16_t usart_it) +void UART_IRQ_Init(uint8_t port, uint16_t usart_it, bool idle_interrupt) { uint32_t IRQ_Channel[_UART_CNT] = {USART0_IRQn, USART1_IRQn, USART2_IRQn, UART3_IRQn, UART4_IRQn, USART5_IRQn}; nvic_irq_enable(IRQ_Channel[port], 0U, 0U); - usart_interrupt_enable(uart[port], usart_it); - usart_interrupt_flag_clear(uart[port], usart_it); + if (idle_interrupt) // enable or disable serial line IDLE interrupt + { + usart_interrupt_enable(uart[port], usart_it); + usart_interrupt_flag_clear(uart[port], usart_it); + } } -void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it) +void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it, bool idle_interrupt) { UART_Protocol_Init(port, baud); - UART_IRQ_Init(port, usart_it); + UART_IRQ_Init(port, usart_it, idle_interrupt); UART_GPIO_Init(port); // After all initialization is completed, enable IO, otherwise a 0xFF will be sent automatically after power-on } diff --git a/TFT/src/User/Hal/gd32f20x/uart.h b/TFT/src/User/Hal/gd32f20x/uart.h index 9f50557035..4230750ff6 100644 --- a/TFT/src/User/Hal/gd32f20x/uart.h +++ b/TFT/src/User/Hal/gd32f20x/uart.h @@ -1,6 +1,7 @@ #ifndef _UART_H_ #define _UART_H_ +#include #include #define _USART1 0 @@ -11,7 +12,7 @@ #define _USART6 5 #define _UART_CNT 6 -void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it); +void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it, bool idle_interrupt); void UART_DeConfig(uint8_t port); void UART_Puts(uint8_t port, uint8_t *str); void UART_Write(uint8_t port, uint8_t d); diff --git a/TFT/src/User/Hal/stm32f10x/Serial.c b/TFT/src/User/Hal/stm32f10x/Serial.c index bbe60e14c8..524f29d340 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.c +++ b/TFT/src/User/Hal/stm32f10x/Serial.c @@ -1,8 +1,24 @@ #include "Serial.h" #include "includes.h" // for infoHost -// dma rx buffer -DMA_CIRCULAR_BUFFER dmaL1Data[_UART_CNT] = {0}; +// set this line to "true" to enable serial IDLE interrupt. IDLE interrupt is no more needed, so always set this macro to "false" +#define RX_IDLE_INTERRUPT false + +// uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used +//#define TX_DMA_WRITE + +// uncomment this line to use compact code (less code) instead of fast code +#define USE_COMPACT_CODE + +#ifndef USE_COMPACT_CODE + // uncomment this line to use inline copy (fast code) instead of memcpy() (less code) + #define USE_INLINE_COPY +#endif + +//#define DEFAULT_WRITE // old unbuffered TX serial writing (just for comparison with new TX solutions) + +DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer +DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer // Config for USART Channel typedef struct @@ -30,8 +46,8 @@ void Serial_DMA_Config(uint8_t port) cfg->uart->CR3 |= 1<<6; // DMA enable receiver cfg->dma_chanel->CPAR = (uint32_t)(&cfg->uart->DR); - cfg->dma_chanel->CMAR = (uint32_t)(dmaL1Data[port].cache); - cfg->dma_chanel->CNDTR = dmaL1Data[port].cacheSize; + cfg->dma_chanel->CMAR = (uint32_t)(dmaL1DataRX[port].cache); + cfg->dma_chanel->CNDTR = dmaL1DataRX[port].cacheSize; cfg->dma_chanel->CCR = 0X00000000; cfg->dma_chanel->CCR |= 3<<12; // Channel priority level cfg->dma_chanel->CCR |= 1<<7; // Memory increment mode @@ -41,24 +57,36 @@ void Serial_DMA_Config(uint8_t port) void Serial_ClearData(uint8_t port) { - dmaL1Data[port].wIndex = dmaL1Data[port].rIndex = dmaL1Data[port].flag = dmaL1Data[port].cacheSize = 0; + dmaL1DataRX[port].wIndex = dmaL1DataRX[port].rIndex = dmaL1DataRX[port].flag = dmaL1DataRX[port].cacheSize = 0; - if (dmaL1Data[port].cache != NULL) + if (dmaL1DataRX[port].cache != NULL) { - free(dmaL1Data[port].cache); - dmaL1Data[port].cache = NULL; + free(dmaL1DataRX[port].cache); + dmaL1DataRX[port].cache = NULL; + } + + dmaL1DataTX[port].wIndex = dmaL1DataTX[port].rIndex = dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize = 0; + + if (dmaL1DataTX[port].cache != NULL) + { + free(dmaL1DataTX[port].cache); + dmaL1DataTX[port].cache = NULL; } } -void Serial_Config(uint8_t port, uint16_t cacheSize, uint32_t baudrate) +void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate) { Serial_ClearData(port); - dmaL1Data[port].cacheSize = cacheSize; - dmaL1Data[port].cache = malloc(cacheSize); - while (!dmaL1Data[port].cache); // malloc failed + dmaL1DataRX[port].cacheSize = cacheSizeRX; + dmaL1DataRX[port].cache = malloc(cacheSizeRX); + while (!dmaL1DataRX[port].cache); // malloc failed, blocking! + + dmaL1DataTX[port].cacheSize = cacheSizeTX; + dmaL1DataTX[port].cache = malloc(cacheSizeTX); + while (!dmaL1DataTX[port].cache); // malloc failed, blocking! - UART_Config(port, baudrate, USART_IT_IDLE); // IDLE interrupt + UART_Config(port, baudrate, USART_IT_IDLE, RX_IDLE_INTERRUPT); // configure serial line with or without IDLE interrupt Serial_DMA_Config(port); } @@ -70,15 +98,43 @@ void Serial_DeConfig(uint8_t port) UART_DeConfig(port); } +uint16_t Serial_GetReadingIndex(uint8_t port) +{ + return dmaL1DataRX[port].rIndex; +} + +uint16_t Serial_GetWritingIndex(uint8_t port) +{ + return dmaL1DataRX[port].cacheSize - Serial[port].dma_chanel->CNDTR; +} + +void Serial_PutChar(uint8_t port, const char ch) +{ + while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); + Serial[port].uart->DR = (uint8_t) ch; +} + +void Serial_Put(uint8_t port, const char * msg) +{ + while (*msg) + { + while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); + Serial[port].uart->DR = ((uint16_t)*msg++ & (uint16_t)0x01FF); + } +} + +// ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { - if ((Serial[port].uart->SR & (1<<4)) != 0) +#if IDLE_INTERRUPT == true // RX serial IDLE interrupt + if ((Serial[port].uart->SR & USART_FLAG_IDLE) != 0) // RX: check for serial IDLE interrupt { - Serial[port].uart->SR; + //Serial[port].uart->SR; // already done in the guard above Serial[port].uart->DR; - dmaL1Data[port].wIndex = dmaL1Data[port].cacheSize - Serial[port].dma_chanel->CNDTR; + dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - Serial[port].dma_chanel->CNDTR; } +#endif } void USART1_IRQHandler(void) @@ -105,18 +161,3 @@ void UART5_IRQHandler(void) { USART_IRQHandler(_UART5); } - -void Serial_Put(uint8_t port, const char *s) -{ - while (*s) - { - while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); - Serial[port].uart->DR = ((uint16_t)*s++ & (uint16_t)0x01FF); - } -} - -void Serial_PutChar(uint8_t port, const char ch) -{ - while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); - Serial[port].uart->DR = (uint8_t) ch; -} diff --git a/TFT/src/User/Hal/stm32f10x/Serial.h b/TFT/src/User/Hal/stm32f10x/Serial.h index ca48a22082..c2d1953372 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.h +++ b/TFT/src/User/Hal/stm32f10x/Serial.h @@ -5,20 +5,37 @@ #include "variants.h" // for uint32_t etc... #include "uart.h" -typedef volatile struct // precautionally declared as volatile due to access from interrupt handler and main thread +typedef struct { char *cache; - uint16_t wIndex; // writing index - uint16_t rIndex; // reading index - uint16_t flag; // custom flag (for custom usage by the application) + volatile uint16_t wIndex; // writing index. Precautionally declared as volatile due to access from interrupt handler and main thread + volatile uint16_t rIndex; // reading index. Precautionally declared as volatile due to access from interrupt handler and main thread + uint16_t flag; // custom flag (for custom usage by the application) uint16_t cacheSize; } DMA_CIRCULAR_BUFFER; -extern DMA_CIRCULAR_BUFFER dmaL1Data[_UART_CNT]; +extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; -void Serial_Config(uint8_t port, uint16_t cacheSize, uint32_t baudrate); +void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); -void Serial_Put(uint8_t port, const char *s); + +// retrieve the next reading index in the message queue of the provided serial port: +// - port: index of serial port +// +// - return value: next reading index +uint16_t Serial_GetReadingIndex(uint8_t port); + +// retrieve the next writing index in the message queue of the provided serial port +// based on Interrupt/DMA status while writing serial data in the background +// - port: index of serial port +// +// - return value: next writing index +uint16_t Serial_GetWritingIndex(uint8_t port); + +// send character to a uart port. TX interrupt based serial writing is always used even if TX DMA based serial writing is enabled void Serial_PutChar(uint8_t port, const char ch); +// send a zero terminated message to uart port. TX interrupt based serial writing is used if TX DMA based serial writing is disabled +void Serial_Put(uint8_t port, const char * msg); + #endif diff --git a/TFT/src/User/Hal/stm32f10x/uart.c b/TFT/src/User/Hal/stm32f10x/uart.c index 6d93476090..b17881d198 100644 --- a/TFT/src/User/Hal/stm32f10x/uart.c +++ b/TFT/src/User/Hal/stm32f10x/uart.c @@ -85,11 +85,11 @@ void UART_GPIO_DeInit(uint8_t port) GPIO_InitSet(uart_rx[port], MGPIO_MODE_IPN, 0); } -void UART_Protocol_Init(uint8_t port,uint32_t baud) +void UART_Protocol_Init(uint8_t port, uint32_t baud) { USART_InitTypeDef USART_InitStructure; - *rcc_uart_en[port] |= rcc_uart_bit[port]; + *rcc_uart_en[port] |= rcc_uart_bit[port]; // Enable clock USART_InitStructure.USART_BaudRate = baud; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; @@ -102,7 +102,7 @@ void UART_Protocol_Init(uint8_t port,uint32_t baud) USART_Cmd(uart[port],ENABLE); } -void UART_IRQ_Init(uint8_t port, uint16_t usart_it) +void UART_IRQ_Init(uint8_t port, uint16_t usart_it, FunctionalState idle_interrupt) { uint32_t IRQ_Channel[_UART_CNT] = {USART1_IRQn, USART2_IRQn, USART3_IRQn, UART4_IRQn, UART5_IRQn}; @@ -113,15 +113,15 @@ void UART_IRQ_Init(uint8_t port, uint16_t usart_it) NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_Init(&NVIC_InitStructure); - USART_ITConfig(uart[port], usart_it, ENABLE); + USART_ITConfig(uart[port], usart_it, idle_interrupt); // enable or disable serial line IDLE interrupt USART_ClearITPendingBit(uart[port], usart_it); } -void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it) +void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it, bool idle_interrupt) { UART_Protocol_Init(port, baud); - UART_IRQ_Init(port, usart_it); - UART_GPIO_Init(port); // Finally, initialize IO, else will send 0xFF. + UART_IRQ_Init(port, usart_it, idle_interrupt ? ENABLE : DISABLE); + UART_GPIO_Init(port); // After all initialization is completed, enable IO, otherwise a 0xFF will be sent automatically after power-on } void UART_DeConfig(uint8_t port) @@ -129,7 +129,7 @@ void UART_DeConfig(uint8_t port) UART_GPIO_DeInit(port); *rcc_uart_rst[port] |= rcc_uart_bit[port]; - *rcc_uart_rst[port] &= ~rcc_uart_bit[port]; + *rcc_uart_rst[port] &= ~rcc_uart_bit[port]; // Reset clock } void UART_Write(uint8_t port, uint8_t d) @@ -137,6 +137,7 @@ void UART_Write(uint8_t port, uint8_t d) while ((uart[port]->SR & USART_FLAG_TC) == (uint16_t)RESET); uart[port]->DR = ((uint16_t)d & (uint16_t)0x01FF); } + void UART_Puts(uint8_t port, uint8_t *str) { while (*str) diff --git a/TFT/src/User/Hal/stm32f10x/uart.h b/TFT/src/User/Hal/stm32f10x/uart.h index b51881a306..28bac32834 100644 --- a/TFT/src/User/Hal/stm32f10x/uart.h +++ b/TFT/src/User/Hal/stm32f10x/uart.h @@ -1,6 +1,7 @@ #ifndef _UART_H_ #define _UART_H_ +#include #include #define _USART1 0 @@ -10,7 +11,7 @@ #define _UART5 4 // UART5 don't support DMA #define _UART_CNT 6 -void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it); +void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it, bool idle_interrupt); void UART_DeConfig(uint8_t port); void UART_Puts(uint8_t port, uint8_t *str); void UART_Write(uint8_t port, uint8_t d); diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c index fe931f69c2..371fc440f5 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c @@ -1,16 +1,24 @@ #include "Serial.h" #include "includes.h" // for infoHost -// dma rx buffer -DMA_CIRCULAR_BUFFER dmaL1Data[_UART_CNT] = {0}; +// set this line to "true" to enable serial IDLE interrupt. IDLE interrupt is no more needed, so always set this macro to "false" +#define RX_IDLE_INTERRUPT false -// Config for USART Channel -//USART1 RX DMA2 Channel4 Steam2/5 -//USART2 RX DMA1 Channel4 Steam5 -//USART3 RX DMA1 Channel4 Steam1 -//UART4 RX DMA1 Channel4 Steam2 -//UART5 RX DMA1 Channel4 Steam0 -//USART6 RX DMA2 Channel5 Steam1/2 +// uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used +//#define TX_DMA_WRITE + +// uncomment this line to use compact code (less code) instead of fast code +//#define USE_COMPACT_CODE + +#ifndef USE_COMPACT_CODE + // uncomment this line to use inline copy (fast code) instead of memcpy() (less code) + #define USE_INLINE_COPY +#endif + +//#define DEFAULT_WRITE // old unbuffered TX serial writing (just for comparison with new TX solutions) + +DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer +DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer // Config for USART Channel typedef struct @@ -18,28 +26,90 @@ typedef struct USART_TypeDef *uart; uint32_t dma_rcc; uint8_t dma_channel; - DMA_Stream_TypeDef *dma_stream; + DMA_Stream_TypeDef *dma_streamRX; + DMA_Stream_TypeDef *dma_streamTX; + volatile uint32_t txBytes[_UART_CNT]; } SERIAL_CFG; -static const SERIAL_CFG Serial[_UART_CNT] = { - {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2}, - {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5}, - {USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1}, - {UART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2}, - {UART5, RCC_AHB1Periph_DMA1, 4, DMA1_Stream0}, - {USART6, RCC_AHB1Periph_DMA2, 5, DMA2_Stream1}, +static SERIAL_CFG Serial[_UART_CNT] = { +// USART DMA1 or DMA2 Channel RX_STREAM TX_STREAM + {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2, DMA2_Stream7 }, + {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5, DMA1_Stream6 }, + {USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1, DMA1_Stream3 }, + {UART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2, DMA1_Stream4 }, + {UART5, RCC_AHB1Periph_DMA1, 4, DMA1_Stream0, DMA1_Stream7 }, + {USART6, RCC_AHB1Periph_DMA2, 5, DMA2_Stream1, DMA2_Stream6 }, }; -void Serial_DMAClearFlag(uint8_t port) +// Clear all (RX and TX) DMA interrupts for a serial port +void Serial_DMAClearInterruptFlags(uint8_t port) +{ + // Channel to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 + // Channel to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 + + switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) + { + case _USART1: DMA2->LIFCR = (0x3F << 16); // DMA2_Stream2 low bits:16-21 Channel 4 + DMA2->HIFCR = (0x3F << 22); break; // DMA2_Stream7 high bits:22-27 Channel 4 + + case _USART2: DMA1->HIFCR = (0x3F << 6) | // DMA1_Stream5 high bits: 6-11 Channel 4 + (0x3F << 16); break; // DMA1_Stream6 high bits:16-21 Channel 4 + + case _USART3: DMA1->LIFCR = (0x3F << 6) | // DMA1_Stream1 low bits: 6-11 Channel 4 + (0x3F << 22); break; // DMA1_Stream3 low bits:22-27 Channel 4 + + case _UART4: DMA1->LIFCR = (0x3F << 16); // DMA1_Stream2 low bits:16-21 Channel 4 + DMA1->HIFCR = (0x3F << 0); break; // DMA1_Stream4 high bits: 0- 5 Channel 4 + + case _UART5: DMA1->LIFCR = (0x3F << 0); // DMA1_Stream0 low bits: 0- 5 Channel 4 + DMA1->HIFCR = (0x3F << 22); break; // DMA1_Stream7 high bits:22-27 Channel 4 + + case _USART6: DMA2->LIFCR = (0xFC << 4); // DMA2_Stream1 low bits: 6-11 Channel 5 + DMA2->HIFCR = (0x3F << 16); break; // DMA2_Stream6 high bits:16-21 Channel 5 + } +} + +// Clear all RX DMA interrupts for a serial port +void Serial_DMAClearInterruptFlagsRX(uint8_t port) +{ + // Channel to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 + // Channel to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 + + switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) + { + case _USART1: DMA2->LIFCR = (0x3F << 16); break; // DMA2_Stream7 high bits:22-27 Channel 4 + + case _USART2: DMA1->HIFCR = (0x3F << 6); break; // DMA1_Stream6 high bits:16-21 Channel 4 + + case _USART3: DMA1->LIFCR = (0x3F << 6); break; // DMA1_Stream3 low bits:22-27 Channel 4 + + case _UART4: DMA1->LIFCR = (0x3F << 16); break; // DMA1_Stream4 high bits: 0- 5 Channel 4 + + case _UART5: DMA1->LIFCR = (0x3F << 0); break; // DMA1_Stream7 high bits:22-27 Channel 4 + + case _USART6: DMA2->LIFCR = (0xFC << 4); break; // DMA2_Stream6 high bits:16-21 Channel 5 + } +} + +// Clear all TX DMA interrupts for a serial port +void Serial_DMAClearInterruptFlagsTX(uint8_t port) { - switch(port) + // Channel to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 + // Channel to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 + + switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) { - case _USART1: DMA2->LIFCR = (0x3F << 16); break; // DMA2_Stream2 low bits:16-21 - case _USART2: DMA1->HIFCR = (0xFC << 4); break; // DMA1_Stream5 high bits: 6-11 - case _USART3: DMA1->LIFCR = (0xFC << 4); break; // DMA1_Stream1 low bits: 6-11 - case _UART4: DMA1->LIFCR = (0x3F << 16); break; // DMA1_Stream2 low bits:16-21 - case _UART5: DMA1->LIFCR = (0x3F << 0); break; // DMA1_Stream0 low bits: 0-5 - case _USART6: DMA2->LIFCR = (0xFC << 4); break; // DMA2_Stream1 low bits: 6-11 + case _USART1: DMA2->HIFCR = (0x3F << 22); break; // DMA2_Stream7 high bits:22-27 Channel 4 + + case _USART2: DMA1->HIFCR = (0x3F << 16); break; // DMA1_Stream6 high bits:16-21 Channel 4 + + case _USART3: DMA1->LIFCR = (0x3F << 22); break; // DMA1_Stream3 low bits:22-27 Channel 4 + + case _UART4: DMA1->HIFCR = (0x3F << 0); break; // DMA1_Stream4 high bits: 0- 5 Channel 4 + + case _UART5: DMA1->HIFCR = (0x3F << 22); break; // DMA1_Stream7 high bits:22-27 Channel 4 + + case _USART6: DMA2->HIFCR = (0x3F << 16); break; // DMA2_Stream6 high bits:16-21 Channel 5 } } @@ -47,47 +117,92 @@ void Serial_DMA_Config(uint8_t port) { const SERIAL_CFG * cfg = &Serial[port]; - RCC_AHB1PeriphClockCmd(cfg->dma_rcc, ENABLE); // DMA RCC EN + RCC_AHB1PeriphClockCmd(cfg->dma_rcc, ENABLE); // DMA RCC enable + + cfg->dma_streamRX->CR &= ~(1<<0); // disable RX DMA + cfg->dma_streamTX->CR &= ~(1<<0); // disable TX DMA + + Serial_DMAClearInterruptFlagsRX(port); // clear DMA RX interrupt flags + + cfg->uart->CR3 |= 3<<6; // DMA enable transmitter(DMAT) and receiver(DMAR) + + cfg->dma_streamRX->PAR = (uint32_t)(&cfg->uart->DR); // peripheral address RX (usart) + cfg->dma_streamTX->PAR = (uint32_t)(&cfg->uart->DR); // peripheral address TX (usart) + + cfg->dma_streamRX->M0AR = (uint32_t)(dmaL1DataRX[port].cache); // destination RX data (sram) + cfg->dma_streamRX->NDTR = dmaL1DataRX[port].cacheSize; // buffer size RX - cfg->dma_stream->CR &= ~(1<<0); // Disable DMA - Serial_DMAClearFlag(port); - cfg->uart->CR3 |= 1<<6; // DMA enable receiver + cfg->dma_streamRX->CR = cfg->dma_channel << 25; // channel selection + cfg->dma_streamTX->CR = cfg->dma_channel << 25; // channel selection TX - cfg->dma_stream->PAR = (uint32_t)(&cfg->uart->DR); - cfg->dma_stream->M0AR = (uint32_t)(dmaL1Data[port].cache); - cfg->dma_stream->NDTR = dmaL1Data[port].cacheSize; + if (port == SERIAL_PORT) // main serial port has highest priority, writing before reading + { + cfg->dma_streamRX->CR |= 2<<16; // RX priority level: High + cfg->dma_streamTX->CR |= 3<<16; // TX priority level: Very high + } + else + { + cfg->dma_streamRX->CR |= 0<<16; // RX priority level: Low + cfg->dma_streamTX->CR |= 1<<16; // TX priority level: Medium + } + +//cfg->dma_streamRX->CR &= ~(3<<13); // memory data size: 8 +//cfg->dma_streamTX->CR &= ~(3<<13); // memory data size: 8 + +//cfg->dma_streamRX->CR &= ~(3<<11); // peripheral data size: 8 +//cfg->dma_streamTX->CR &= ~(3<<11); // peripheral data size: 8 + + cfg->dma_streamRX->CR |= 1<<10; // memory increment mode + cfg->dma_streamTX->CR |= 1<<10; // memory increment mode + +//cfg->dma_streamRX->CR &= ~(1<<9); // peripheral no increment mode +//cfg->dma_streamTX->CR &= ~(1<<9); // peripheral no increment mode + + cfg->dma_streamRX->CR |= 1<<8; // circular mode enabled RX +//cfg->dma_streamTX->CR &= ~(1<<8); // circular mode disabled + +//cfg->dma_streamRX->CR &= ~(1<<6); // data transfer direction: Peripheral-to-memory + cfg->dma_streamTX->CR |= 1<<6; // data transfer direction: Memory-to-Peripheral - cfg->dma_stream->CR = cfg->dma_channel << 25; - cfg->dma_stream->CR |= 3<<16; // Priority level: Very high - cfg->dma_stream->CR |= 0<<13; // Memory data size: 8 - cfg->dma_stream->CR |= 0<<11; // Peripheral data size: 8 - cfg->dma_stream->CR |= 1<<10; // Memory increment mode - cfg->dma_stream->CR |= 0<<9; // Peripheral not increment mode - cfg->dma_stream->CR |= 1<<8; // Circular mode enabled - cfg->dma_stream->CR |= 0<<6; // Data transfer direction: Peripheral-to-memory - cfg->dma_stream->CR |= 1<<0; // Enable DMA +//cfg->dma_streamTX->CR |= 1<<4; // enable TX DMA Transfer Complete interrupt, done later when needed + + cfg->dma_streamRX->CR |= 1<<0; // enable RX DMA } void Serial_ClearData(uint8_t port) { - dmaL1Data[port].wIndex = dmaL1Data[port].rIndex = dmaL1Data[port].flag = dmaL1Data[port].cacheSize = 0; + dmaL1DataRX[port].wIndex = dmaL1DataRX[port].rIndex = dmaL1DataRX[port].flag = dmaL1DataRX[port].cacheSize = 0; + + if (dmaL1DataRX[port].cache != NULL) + { + free(dmaL1DataRX[port].cache); + dmaL1DataRX[port].cache = NULL; + } + + dmaL1DataTX[port].wIndex = dmaL1DataTX[port].rIndex = dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize = 0; - if (dmaL1Data[port].cache != NULL) + if (dmaL1DataTX[port].cache != NULL) { - free(dmaL1Data[port].cache); - dmaL1Data[port].cache = NULL; + free(dmaL1DataTX[port].cache); + dmaL1DataTX[port].cache = NULL; } } -void Serial_Config(uint8_t port, uint16_t cacheSize, uint32_t baudrate) +void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate) { Serial_ClearData(port); - dmaL1Data[port].cacheSize = cacheSize; - dmaL1Data[port].cache = malloc(cacheSize); - while (!dmaL1Data[port].cache); // malloc failed + dmaL1DataRX[port].cacheSize = cacheSizeRX; + dmaL1DataRX[port].cache = malloc(cacheSizeRX); + while (!dmaL1DataRX[port].cache); // malloc failed, blocking! - UART_Config(port, baudrate, USART_IT_IDLE); // IDLE interrupt + dmaL1DataTX[port].cacheSize = cacheSizeTX; + dmaL1DataTX[port].cache = malloc(cacheSizeTX); + while (!dmaL1DataTX[port].cache); // malloc failed, blocking! + + Serial[port].txBytes[port] = 0; + + UART_Config(port, baudrate, USART_IT_IDLE, RX_IDLE_INTERRUPT); // configure serial line with or without IDLE interrupt Serial_DMA_Config(port); } @@ -95,20 +210,252 @@ void Serial_DeConfig(uint8_t port) { Serial_ClearData(port); - Serial[port].dma_stream->CR &= ~(1<<0); // Disable DMA - Serial_DMAClearFlag(port); + Serial[port].dma_streamRX->CR &= ~(1<<0); // disable RX DMA + Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA + + Serial_DMAClearInterruptFlagsRX(port); UART_DeConfig(port); } +uint16_t Serial_GetReadingIndex(uint8_t port) +{ + return dmaL1DataRX[port].rIndex; +} + +uint16_t Serial_GetWritingIndex(uint8_t port) +{ + return dmaL1DataRX[port].cacheSize - Serial[port].dma_streamRX->NDTR; +} + +#ifdef DEFAULT_WRITE // unbuffered TX serial writing + +void Serial_PutChar(uint8_t port, const char ch) +{ + while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); + Serial[port].uart->DR = (uint8_t) ch; +} + +#else // use TX interrupt based serial writing + +void Serial_PutChar(uint8_t port, const char ch) +{ + // wait for enough free buffer in TX cache to store all data + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) + { + } + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; + + Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer +} + +#endif + +#ifdef DEFAULT_WRITE // unbuffered TX serial writing + +void Serial_Put(uint8_t port, const char * msg) +{ + while (*msg) + { + while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); + Serial[port].uart->DR = ((uint16_t)*msg++ & (uint16_t)0x01FF); + } +} + +#elif !defined(TX_DMA_WRITE) // TX interrupt based serial writing + +void Serial_Put(uint8_t port, const char * msg) +{ +#ifdef USE_COMPACT_CODE // less code + while (*msg) + { + // blocking! wait for buffer to become available + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { }; + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg++; + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; + + Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer + } +#else // fast code + // NOTE: used 32 bit variables for performance reasons (in particular for data copy) + + DMA_CIRCULAR_BUFFER * dmaL1Data_ptr = &dmaL1DataTX[port]; + char * cache = dmaL1Data_ptr->cache; + uint32_t cacheSize = dmaL1Data_ptr->cacheSize; + uint32_t wIndex = dmaL1Data_ptr->wIndex; + uint32_t msgSize = strlen(msg); + + // if cache size is not enough to store the data, skip the data copy + // + // NOTE: the following check should never be matched if cache has a proper size. + // If so, the check could be commented out just to improve performance. Just keep it to make the code more robust + // + if ((cacheSize - 1) < msgSize) + return; + + // wait for enough free buffer in TX cache to store all data. + // Used dmaL1Data_ptr->rIndex dynamically changed by TX cache's interrupt handler + // + // NOTE: -1 is needed because full cache usage will introduce a conflict on rIndex and wIndex + // (wIndex == rIndex will indicate an empty cache or a full cache) + // + while ((((cacheSize - 1) - wIndex + dmaL1Data_ptr->rIndex) % cacheSize) < msgSize) + { + } + + uint32_t maxIndex; + + // if data is one chunk only, store data on upper part of circular cache + if ((cacheSize - wIndex) >= msgSize) + { + #ifdef USE_INLINE_COPY + maxIndex = wIndex + msgSize; + + while (wIndex < maxIndex) + { + cache[wIndex++] = *(msg++); + } + #else + memcpy(&cache[wIndex], msg, msgSize); + #endif + } + else // data at end and beginning of cache + { + #ifdef USE_INLINE_COPY + while (wIndex < cacheSize) + { + cache[wIndex++] = *(msg++); + } + + wIndex = 0; + maxIndex = msgSize - (cacheSize - dmaL1Data_ptr->wIndex); // used dmaL1Data_ptr->wIndex and not wIndex + + while (wIndex < maxIndex) + { + cache[wIndex++] = *(msg++); + } + #else + memcpy(&cache[wIndex], msg, cacheSize - wIndex); + memcpy(cache, &msg[cacheSize - wIndex], msgSize - (cacheSize - wIndex)); + #endif + } + + // update queue's writing index with next index + dmaL1Data_ptr->wIndex = (dmaL1Data_ptr->wIndex + msgSize) % cacheSize; + + Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer +#endif // USE_COMPACT_CODE +} + +#else // TX DMA based serial writing + +// DMA Serial Write support function +void Serial_Send_TX(uint8_t port) +{ + // setup DMA transfer, and wait for serial Transfer Complete (TX) interrupt in ISR + if (dmaL1DataTX[port].wIndex >= dmaL1DataTX[port].rIndex) + Serial[port].txBytes[port] = dmaL1DataTX[port].wIndex - dmaL1DataTX[port].rIndex; + else + Serial[port].txBytes[port] = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts + + Serial[port].dma_streamTX->M0AR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // start address TX data + Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags + Serial[port].uart->CR1 |= (1<<6); // enable Transfer Complete (TX) serial Interrupt + Serial[port].dma_streamTX->NDTR = Serial[port].txBytes[port]; // no. bytes to transfer + Serial[port].dma_streamTX->CR |= 1<<0; // enable TX DMA +} + +void Serial_Put(uint8_t port, const char * msg) +{ + while (*msg) + { + // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) + if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && + ((Serial[port].uart->CR1 & (1<<6)) == 0)) + Serial_Send_TX(port); + + // wait until space becomes available, blocking! + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { } + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg; // copy character to cache + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex + + if ((*msg == '\n') && ((Serial[port].uart->CR1 & (1<<6)) == 0)) + Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already + + msg++; // let the compiler optimize this, no need to do it manually! + } +} +/* +void Serial_PutChar(uint8_t port, const char ch) +{ + // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) + if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && + ((Serial[port].uart->CR1 & (1<<6)) == 0)) + Serial_Send_TX(port); + + // wait until space becomes available, blocking! + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { } + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; // copy character to cache + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex + + if ((ch == '\n') && ((Serial[port].uart->CR1 & (1<<6)) == 0)) + Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already +} +*/ +#endif + +// ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { - if ((Serial[port].uart->SR & (1<<4)) != 0) +#if IDLE_INTERRUPT == true // RX serial IDLE interrupt + if ((Serial[port].uart->SR & USART_FLAG_IDLE) != 0) // RX: check for serial IDLE interrupt { - Serial[port].uart->SR; + //Serial[port].uart->SR; // already done in the guard above Serial[port].uart->DR; - dmaL1Data[port].wIndex = dmaL1Data[port].cacheSize - Serial[port].dma_stream->NDTR; + dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - Serial[port].dma_streamRX->NDTR; } +#endif + + // TX interrupt based serial writing + // + if ((Serial[port].uart->SR & USART_FLAG_TXE) != 0) // TX: check for TXE interrupt + { + if (dmaL1DataTX[port].rIndex == dmaL1DataTX[port].wIndex) // no more data? + { + Serial[port].uart->CR1 &= ~USART_FLAG_TXE; // clear TXE interrupt bit + } + else + { + Serial[port].uart->DR = (uint8_t)dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]; // write next available character + + dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + 1) % dmaL1DataTX[port].cacheSize; // increase reading index + } + } + +#ifdef TX_DMA_WRITE // TX DMA based serial writing + if ((Serial[port].uart->SR & (1<<6)) != 0) // TX: check for Transfer Complete (TC) + { + Serial[port].uart->SR &= ~(1<<6); // clear Transfer Complete (TC) bit + + // NOTE: the ISR is sometimes called while DMA is still active, so check NDTR status! + if (Serial[port].dma_streamTX->NDTR == 0) // sending is complete + { + dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + Serial[port].txBytes[port]) % dmaL1DataTX[port].cacheSize; + Serial[port].txBytes[port] = 0; + + if (dmaL1DataTX[port].wIndex != dmaL1DataTX[port].rIndex) // is more data available? + Serial_Send_TX(port); // continue sending data + else // removed causes double line transfers + Serial[port].uart->CR1 &= ~(1<<6); // disable Transfer Complete (TC) interrupt, nothing more to do + } + // else: more data is coming, wait for next Transfer Complete (TC) interrupt + } +#endif } void USART1_IRQHandler(void) @@ -140,18 +487,3 @@ void USART6_IRQHandler(void) { USART_IRQHandler(_USART6); } - -void Serial_Put(uint8_t port, const char *s) -{ - while (*s) - { - while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); - Serial[port].uart->DR = ((uint16_t)*s++ & (uint16_t)0x01FF); - } -} - -void Serial_PutChar(uint8_t port, const char ch) -{ - while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); - Serial[port].uart->DR = (uint8_t) ch; -} diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h index ca48a22082..c2d1953372 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h @@ -5,20 +5,37 @@ #include "variants.h" // for uint32_t etc... #include "uart.h" -typedef volatile struct // precautionally declared as volatile due to access from interrupt handler and main thread +typedef struct { char *cache; - uint16_t wIndex; // writing index - uint16_t rIndex; // reading index - uint16_t flag; // custom flag (for custom usage by the application) + volatile uint16_t wIndex; // writing index. Precautionally declared as volatile due to access from interrupt handler and main thread + volatile uint16_t rIndex; // reading index. Precautionally declared as volatile due to access from interrupt handler and main thread + uint16_t flag; // custom flag (for custom usage by the application) uint16_t cacheSize; } DMA_CIRCULAR_BUFFER; -extern DMA_CIRCULAR_BUFFER dmaL1Data[_UART_CNT]; +extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; -void Serial_Config(uint8_t port, uint16_t cacheSize, uint32_t baudrate); +void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); -void Serial_Put(uint8_t port, const char *s); + +// retrieve the next reading index in the message queue of the provided serial port: +// - port: index of serial port +// +// - return value: next reading index +uint16_t Serial_GetReadingIndex(uint8_t port); + +// retrieve the next writing index in the message queue of the provided serial port +// based on Interrupt/DMA status while writing serial data in the background +// - port: index of serial port +// +// - return value: next writing index +uint16_t Serial_GetWritingIndex(uint8_t port); + +// send character to a uart port. TX interrupt based serial writing is always used even if TX DMA based serial writing is enabled void Serial_PutChar(uint8_t port, const char ch); +// send a zero terminated message to uart port. TX interrupt based serial writing is used if TX DMA based serial writing is disabled +void Serial_Put(uint8_t port, const char * msg); + #endif diff --git a/TFT/src/User/Hal/stm32f2_f4xx/uart.c b/TFT/src/User/Hal/stm32f2_f4xx/uart.c index 91cc6c22f0..b2878583ea 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/uart.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/uart.c @@ -98,7 +98,7 @@ void UART_GPIO_DeInit(uint8_t port) GPIO_InitSet(uart_rx[port], MGPIO_MODE_IPN, 0); } -void UART_Protocol_Init(uint8_t port,uint32_t baud) +void UART_Protocol_Init(uint8_t port, uint32_t baud) { USART_InitTypeDef USART_InitStructure; @@ -115,7 +115,7 @@ void UART_Protocol_Init(uint8_t port,uint32_t baud) USART_Cmd(uart[port],ENABLE); } -void UART_IRQ_Init(uint8_t port, uint16_t usart_it) +void UART_IRQ_Init(uint8_t port, uint16_t usart_it, FunctionalState idle_interrupt) { uint32_t IRQ_Channel[_UART_CNT] = {USART1_IRQn, USART2_IRQn, USART3_IRQn, UART4_IRQn, UART5_IRQn, USART6_IRQn}; @@ -126,14 +126,14 @@ void UART_IRQ_Init(uint8_t port, uint16_t usart_it) NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_Init(&NVIC_InitStructure); - USART_ITConfig(uart[port], usart_it, ENABLE); + USART_ITConfig(uart[port], usart_it, idle_interrupt); // enable or disable serial line IDLE interrupt USART_ClearITPendingBit(uart[port], usart_it); } -void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it) +void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it, bool idle_interrupt) { UART_Protocol_Init(port, baud); - UART_IRQ_Init(port, usart_it); + UART_IRQ_Init(port, usart_it, idle_interrupt ? ENABLE : DISABLE); UART_GPIO_Init(port); // After all initialization is completed, enable IO, otherwise a 0xFF will be sent automatically after power-on } @@ -150,6 +150,7 @@ void UART_Write(uint8_t port, uint8_t d) while ((uart[port]->SR & USART_FLAG_TC) == (uint16_t)RESET); uart[port]->DR = ((uint16_t)d & (uint16_t)0x01FF); } + void UART_Puts(uint8_t port, uint8_t *str) { while (*str) diff --git a/TFT/src/User/Hal/stm32f2_f4xx/uart.h b/TFT/src/User/Hal/stm32f2_f4xx/uart.h index 9f50557035..4230750ff6 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/uart.h +++ b/TFT/src/User/Hal/stm32f2_f4xx/uart.h @@ -1,6 +1,7 @@ #ifndef _UART_H_ #define _UART_H_ +#include #include #define _USART1 0 @@ -11,7 +12,7 @@ #define _USART6 5 #define _UART_CNT 6 -void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it); +void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it, bool idle_interrupt); void UART_DeConfig(uint8_t port); void UART_Puts(uint8_t port, uint8_t *str); void UART_Write(uint8_t port, uint8_t d); diff --git a/TFT/src/User/main.c b/TFT/src/User/main.c index fb2056a92e..527acb6c2d 100644 --- a/TFT/src/User/main.c +++ b/TFT/src/User/main.c @@ -1,9 +1,11 @@ #include "main.h" #include "includes.h" -MENU infoMenu; // Menu structure -HOST infoHost; // Information interaction with Marlin -CLOCKS mcuClocks; // System clocks: SYSCLK, AHB, APB1, APB2, APB1_Timer, APB2_Timer2 +MENU infoMenu; // Menu structure +HOST infoHost; // Information interaction with Marlin +CLOCKS mcuClocks; // System clocks: SYSCLK, AHB, APB1, APB2, APB1_Timer, APB2_Timer2 +uint32_t bePriorityCounter; // Back end priority counter +uint32_t fePriorityCounter; // Front end priority counter int main(void) { diff --git a/TFT/src/User/main.h b/TFT/src/User/main.h index 78744cf391..6e90c401b4 100644 --- a/TFT/src/User/main.h +++ b/TFT/src/User/main.h @@ -10,7 +10,9 @@ extern "C" { #include "variants.h" // for RCC_ClocksTypeDef #include "uart.h" // for _UART_CNT -#define MAX_MENU_DEPTH 10 // max sub menu depth +#define MAX_MENU_DEPTH 10 // max sub menu depth +#define BE_PRIORITY_DIVIDER 16 // a divider value of 16 -> run 6% of the time only. Use a power of 2 for performance reasons! +#define FE_PRIORITY_DIVIDER 16 // a divider value of 16 -> run 6% of the time only. Use a power of 2 for performance reasons! typedef void (* FP_MENU)(void); @@ -46,6 +48,8 @@ typedef struct extern MENU infoMenu; extern HOST infoHost; extern CLOCKS mcuClocks; +extern uint32_t bePriorityCounter; +extern uint32_t fePriorityCounter; #ifdef __cplusplus } From 8cbc81eb0b8d564917b4965b2ed913ff3f0d8624 Mon Sep 17 00:00:00 2001 From: digant73 Date: Sat, 23 Sep 2023 00:04:05 +0200 Subject: [PATCH 02/20] cleanup --- TFT/src/User/Hal/gd32f20x/Serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT/src/User/Hal/gd32f20x/Serial.c b/TFT/src/User/Hal/gd32f20x/Serial.c index 673c1664d7..bb74b0e64f 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.c +++ b/TFT/src/User/Hal/gd32f20x/Serial.c @@ -115,7 +115,7 @@ void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uin dmaL1DataTX[port].cache = malloc(cacheSizeTX); while (!dmaL1DataTX[port].cache); // malloc failed, blocking! - UART_Config(port, baudrate, USART_IT_IDLE, RX_IDLE_INTERRUPT); // configure serial line with or without IDLE interrupt + UART_Config(port, baudrate, USART_INT_IDLE, RX_IDLE_INTERRUPT); // configure serial line with or without IDLE interrupt Serial_DMA_Config(port); } From 1fad987979f8acf6e73c51d31b8badce871c31aa Mon Sep 17 00:00:00 2001 From: digant73 Date: Sat, 23 Sep 2023 19:02:03 +0200 Subject: [PATCH 03/20] minor cleanup --- TFT/src/User/API/Printing.c | 3 +++ TFT/src/User/API/SerialConnection.c | 12 +++++----- TFT/src/User/API/menu.c | 4 ---- TFT/src/User/Hal/gd32f20x/Serial.c | 30 ++---------------------- TFT/src/User/Hal/gd32f20x/Serial.h | 19 +++++++++++++-- TFT/src/User/Hal/stm32f10x/Serial.c | 21 ++--------------- TFT/src/User/Hal/stm32f10x/Serial.h | 18 +++++++++++++-- TFT/src/User/Hal/stm32f2_f4xx/Serial.c | 32 +++++++------------------- TFT/src/User/Hal/stm32f2_f4xx/Serial.h | 21 +++++++++++++++-- 9 files changed, 73 insertions(+), 87 deletions(-) diff --git a/TFT/src/User/API/Printing.c b/TFT/src/User/API/Printing.c index 127cad4a22..3bad224813 100644 --- a/TFT/src/User/API/Printing.c +++ b/TFT/src/User/API/Printing.c @@ -906,6 +906,9 @@ void loopPrintFromTFT(void) } } } + + if (comment_parsing) // parse comment from gcode file + parseComment(); } if (gcode_count == 0) diff --git a/TFT/src/User/API/SerialConnection.c b/TFT/src/User/API/SerialConnection.c index 77c3ff2e63..329fa94f56 100644 --- a/TFT/src/User/API/SerialConnection.c +++ b/TFT/src/User/API/SerialConnection.c @@ -4,13 +4,13 @@ // uncomment this line to use inline copy (fast code) instead of memcpy() (less code) #define USE_INLINE_COPY -#define SERIAL_PORT_RX_QUEUE_SIZE NOBEYOND(ACK_CACHE_SIZE, RAM_SIZE * 64, 4 * ACK_CACHE_SIZE) -#define SERIAL_PORT_2_RX_QUEUE_SIZE ACK_CACHE_SIZE -#define SERIAL_PORT_3_RX_QUEUE_SIZE ACK_CACHE_SIZE -#define SERIAL_PORT_4_RX_QUEUE_SIZE ACK_CACHE_SIZE +#define SERIAL_PORT_RX_QUEUE_SIZE NOBEYOND(ACK_CACHE_SIZE, RAM_SIZE * 64, 4096) // ACK messages reading queue from mainboard +#define SERIAL_PORT_2_RX_QUEUE_SIZE 512 +#define SERIAL_PORT_3_RX_QUEUE_SIZE 512 +#define SERIAL_PORT_4_RX_QUEUE_SIZE 512 -// make TX queue size simmetric to ACK messages queue size -#define SERIAL_PORT_TX_QUEUE_SIZE ACK_CACHE_SIZE +// make TX queue size simmetric to ACK messages queue size for all supplementary serial ports +#define SERIAL_PORT_TX_QUEUE_SIZE 256 // gcodes writing queue to mainboard #define SERIAL_PORT_2_TX_QUEUE_SIZE ACK_CACHE_SIZE #define SERIAL_PORT_3_TX_QUEUE_SIZE ACK_CACHE_SIZE #define SERIAL_PORT_4_TX_QUEUE_SIZE ACK_CACHE_SIZE diff --git a/TFT/src/User/API/menu.c b/TFT/src/User/API/menu.c index f08b39a7f4..ee77b96a51 100644 --- a/TFT/src/User/API/menu.c +++ b/TFT/src/User/API/menu.c @@ -1211,10 +1211,6 @@ void loopBackEnd(void) // Parse the received slave response information parseACK(); - // Parse comment from gcode file - if (GET_BIT(infoSettings.general_settings, INDEX_FILE_COMMENT_PARSING) == 1) // if file comment parsing is enabled - parseComment(); - // Retrieve and store (in command queue) the gcodes received from other UART, such as ESP3D etc... #ifdef SERIAL_PORT_2 Serial_GetFromUART(); diff --git a/TFT/src/User/Hal/gd32f20x/Serial.c b/TFT/src/User/Hal/gd32f20x/Serial.c index bb74b0e64f..097238f42a 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.c +++ b/TFT/src/User/Hal/gd32f20x/Serial.c @@ -21,23 +21,7 @@ DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer // Config for USART Channel -//USART1 RX DMA2 Channel4 Steam2/5 -//USART2 RX DMA1 Channel4 Steam5 -//USART3 RX DMA1 Channel4 Steam1 -//UART4 RX DMA1 Channel4 Steam2 -//UART5 RX DMA1 Channel4 Steam0 -//USART6 RX DMA2 Channel5 Steam1/2 - -// Config for USART Channel -typedef struct -{ - uint32_t uart; - rcu_periph_enum dma_rcc; - uint8_t dma_channel; - uint32_t dma_stream; -} SERIAL_CFG; - -static const SERIAL_CFG Serial[_UART_CNT] = { +const SERIAL_CFG Serial[_UART_CNT] = { {USART0, RCU_DMA0, 4, DMA0}, {USART1, RCU_DMA0, 5, DMA0}, {USART2, RCU_DMA0, 2, DMA0}, @@ -128,16 +112,6 @@ void Serial_DeConfig(uint8_t port) UART_DeConfig(port); } -uint16_t Serial_GetReadingIndex(uint8_t port) -{ - return dmaL1DataRX[port].rIndex; -} - -uint16_t Serial_GetWritingIndex(uint8_t port) -{ - return dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channel); -} - void Serial_PutChar(uint8_t port, const char ch) { while ((USART_STAT0(Serial[port].uart) & (1 << USART_FLAG_TC)) == (uint16_t)RESET); @@ -156,7 +130,7 @@ void Serial_Put(uint8_t port, const char * msg) // ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { -#if IDLE_INTERRUPT == true // RX serial IDLE interrupt +#if RX_IDLE_INTERRUPT == true // RX serial IDLE interrupt if ((USART_STAT0(Serial[port].uart) & (1<<4)) != 0) { USART_STAT0(Serial[port].uart); // clear interrupt flag diff --git a/TFT/src/User/Hal/gd32f20x/Serial.h b/TFT/src/User/Hal/gd32f20x/Serial.h index c2d1953372..a7c59b7d90 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.h +++ b/TFT/src/User/Hal/gd32f20x/Serial.h @@ -14,7 +14,16 @@ typedef struct uint16_t cacheSize; } DMA_CIRCULAR_BUFFER; +typedef struct +{ + uint32_t uart; + rcu_periph_enum dma_rcc; + uint8_t dma_channel; + uint32_t dma_stream; +} SERIAL_CFG; + extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; +extern const SERIAL_CFG Serial[_UART_CNT]; void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); @@ -23,14 +32,20 @@ void Serial_DeConfig(uint8_t port); // - port: index of serial port // // - return value: next reading index -uint16_t Serial_GetReadingIndex(uint8_t port); +static inline uint16_t Serial_GetReadingIndex(uint8_t port) +{ + return dmaL1DataRX[port].rIndex; +} // retrieve the next writing index in the message queue of the provided serial port // based on Interrupt/DMA status while writing serial data in the background // - port: index of serial port // // - return value: next writing index -uint16_t Serial_GetWritingIndex(uint8_t port); +static inline uint16_t Serial_GetWritingIndex(uint8_t port) +{ + return dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channel); +} // send character to a uart port. TX interrupt based serial writing is always used even if TX DMA based serial writing is enabled void Serial_PutChar(uint8_t port, const char ch); diff --git a/TFT/src/User/Hal/stm32f10x/Serial.c b/TFT/src/User/Hal/stm32f10x/Serial.c index 524f29d340..81756e88ff 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.c +++ b/TFT/src/User/Hal/stm32f10x/Serial.c @@ -21,14 +21,7 @@ DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer // Config for USART Channel -typedef struct -{ - USART_TypeDef *uart; - uint32_t dma_rcc; - DMA_Channel_TypeDef *dma_chanel; -} SERIAL_CFG; - -static const SERIAL_CFG Serial[_UART_CNT] = { +const SERIAL_CFG Serial[_UART_CNT] = { {USART1, RCC_AHBPeriph_DMA1, DMA1_Channel5}, {USART2, RCC_AHBPeriph_DMA1, DMA1_Channel6}, {USART3, RCC_AHBPeriph_DMA1, DMA1_Channel3}, @@ -98,16 +91,6 @@ void Serial_DeConfig(uint8_t port) UART_DeConfig(port); } -uint16_t Serial_GetReadingIndex(uint8_t port) -{ - return dmaL1DataRX[port].rIndex; -} - -uint16_t Serial_GetWritingIndex(uint8_t port) -{ - return dmaL1DataRX[port].cacheSize - Serial[port].dma_chanel->CNDTR; -} - void Serial_PutChar(uint8_t port, const char ch) { while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); @@ -126,7 +109,7 @@ void Serial_Put(uint8_t port, const char * msg) // ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { -#if IDLE_INTERRUPT == true // RX serial IDLE interrupt +#if RX_IDLE_INTERRUPT == true // RX serial IDLE interrupt if ((Serial[port].uart->SR & USART_FLAG_IDLE) != 0) // RX: check for serial IDLE interrupt { //Serial[port].uart->SR; // already done in the guard above diff --git a/TFT/src/User/Hal/stm32f10x/Serial.h b/TFT/src/User/Hal/stm32f10x/Serial.h index c2d1953372..9868f00645 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.h +++ b/TFT/src/User/Hal/stm32f10x/Serial.h @@ -14,7 +14,15 @@ typedef struct uint16_t cacheSize; } DMA_CIRCULAR_BUFFER; +typedef struct +{ + USART_TypeDef *uart; + uint32_t dma_rcc; + DMA_Channel_TypeDef *dma_chanel; +} SERIAL_CFG; + extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; +extern const SERIAL_CFG Serial[_UART_CNT]; void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); @@ -23,14 +31,20 @@ void Serial_DeConfig(uint8_t port); // - port: index of serial port // // - return value: next reading index -uint16_t Serial_GetReadingIndex(uint8_t port); +static inline uint16_t Serial_GetReadingIndex(uint8_t port) +{ + return dmaL1DataRX[port].rIndex; +} // retrieve the next writing index in the message queue of the provided serial port // based on Interrupt/DMA status while writing serial data in the background // - port: index of serial port // // - return value: next writing index -uint16_t Serial_GetWritingIndex(uint8_t port); +static inline uint16_t Serial_GetWritingIndex(uint8_t port) +{ + return dmaL1DataRX[port].cacheSize - Serial[port].dma_chanel->CNDTR; +} // send character to a uart port. TX interrupt based serial writing is always used even if TX DMA based serial writing is enabled void Serial_PutChar(uint8_t port, const char ch); diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c index 371fc440f5..1f3212a583 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c @@ -21,17 +21,7 @@ DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer // Config for USART Channel -typedef struct -{ - USART_TypeDef *uart; - uint32_t dma_rcc; - uint8_t dma_channel; - DMA_Stream_TypeDef *dma_streamRX; - DMA_Stream_TypeDef *dma_streamTX; - volatile uint32_t txBytes[_UART_CNT]; -} SERIAL_CFG; - -static SERIAL_CFG Serial[_UART_CNT] = { +SERIAL_CFG Serial[_UART_CNT] = { // USART DMA1 or DMA2 Channel RX_STREAM TX_STREAM {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2, DMA2_Stream7 }, {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5, DMA1_Stream6 }, @@ -186,6 +176,8 @@ void Serial_ClearData(uint8_t port) free(dmaL1DataTX[port].cache); dmaL1DataTX[port].cache = NULL; } + + Serial[port].txBytes[port] = 0; } void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate) @@ -200,8 +192,6 @@ void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uin dmaL1DataTX[port].cache = malloc(cacheSizeTX); while (!dmaL1DataTX[port].cache); // malloc failed, blocking! - Serial[port].txBytes[port] = 0; - UART_Config(port, baudrate, USART_IT_IDLE, RX_IDLE_INTERRUPT); // configure serial line with or without IDLE interrupt Serial_DMA_Config(port); } @@ -217,16 +207,6 @@ void Serial_DeConfig(uint8_t port) UART_DeConfig(port); } -uint16_t Serial_GetReadingIndex(uint8_t port) -{ - return dmaL1DataRX[port].rIndex; -} - -uint16_t Serial_GetWritingIndex(uint8_t port) -{ - return dmaL1DataRX[port].cacheSize - Serial[port].dma_streamRX->NDTR; -} - #ifdef DEFAULT_WRITE // unbuffered TX serial writing void Serial_PutChar(uint8_t port, const char ch) @@ -411,7 +391,7 @@ void Serial_PutChar(uint8_t port, const char ch) // ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { -#if IDLE_INTERRUPT == true // RX serial IDLE interrupt +#if RX_IDLE_INTERRUPT == true // RX serial IDLE interrupt if ((Serial[port].uart->SR & USART_FLAG_IDLE) != 0) // RX: check for serial IDLE interrupt { //Serial[port].uart->SR; // already done in the guard above @@ -421,6 +401,8 @@ void USART_IRQHandler(uint8_t port) } #endif +#ifndef DEFAULT_WRITE + // TX interrupt based serial writing // if ((Serial[port].uart->SR & USART_FLAG_TXE) != 0) // TX: check for TXE interrupt @@ -456,6 +438,8 @@ void USART_IRQHandler(uint8_t port) // else: more data is coming, wait for next Transfer Complete (TC) interrupt } #endif + +#endif } void USART1_IRQHandler(void) diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h index c2d1953372..5b05e405f6 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h @@ -14,7 +14,18 @@ typedef struct uint16_t cacheSize; } DMA_CIRCULAR_BUFFER; +typedef struct +{ + USART_TypeDef *uart; + uint32_t dma_rcc; + uint8_t dma_channel; + DMA_Stream_TypeDef *dma_streamRX; + DMA_Stream_TypeDef *dma_streamTX; + volatile uint32_t txBytes[_UART_CNT]; +} SERIAL_CFG; + extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; +extern SERIAL_CFG Serial[_UART_CNT]; void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); @@ -23,14 +34,20 @@ void Serial_DeConfig(uint8_t port); // - port: index of serial port // // - return value: next reading index -uint16_t Serial_GetReadingIndex(uint8_t port); +static inline uint16_t Serial_GetReadingIndex(uint8_t port) +{ + return dmaL1DataRX[port].rIndex; +} // retrieve the next writing index in the message queue of the provided serial port // based on Interrupt/DMA status while writing serial data in the background // - port: index of serial port // // - return value: next writing index -uint16_t Serial_GetWritingIndex(uint8_t port); +static inline uint16_t Serial_GetWritingIndex(uint8_t port) +{ + return dmaL1DataRX[port].cacheSize - Serial[port].dma_streamRX->NDTR; +} // send character to a uart port. TX interrupt based serial writing is always used even if TX DMA based serial writing is enabled void Serial_PutChar(uint8_t port, const char ch); From db3d56f8a0172b8b94f0ab5b7c804fd19db3273e Mon Sep 17 00:00:00 2001 From: digant73 Date: Sat, 23 Sep 2023 20:51:56 +0200 Subject: [PATCH 04/20] minor cleanup --- TFT/src/User/Hal/stm32f2_f4xx/Serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c index 1f3212a583..b02101cace 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c @@ -8,7 +8,7 @@ //#define TX_DMA_WRITE // uncomment this line to use compact code (less code) instead of fast code -//#define USE_COMPACT_CODE +#define USE_COMPACT_CODE #ifndef USE_COMPACT_CODE // uncomment this line to use inline copy (fast code) instead of memcpy() (less code) From aaf7bf36412d901b0ae3f87326f0546b28af0265 Mon Sep 17 00:00:00 2001 From: digant73 Date: Sun, 24 Sep 2023 23:24:23 +0200 Subject: [PATCH 05/20] added interrupt based writing for STM32F10x --- TFT/src/User/API/SerialConnection.c | 9 +- TFT/src/User/Hal/stm32f10x/Serial.c | 121 +++++++++++++++++++++++++ TFT/src/User/Hal/stm32f2_f4xx/Serial.c | 90 +++++++++--------- 3 files changed, 168 insertions(+), 52 deletions(-) diff --git a/TFT/src/User/API/SerialConnection.c b/TFT/src/User/API/SerialConnection.c index 329fa94f56..6ac35f6022 100644 --- a/TFT/src/User/API/SerialConnection.c +++ b/TFT/src/User/API/SerialConnection.c @@ -9,11 +9,10 @@ #define SERIAL_PORT_3_RX_QUEUE_SIZE 512 #define SERIAL_PORT_4_RX_QUEUE_SIZE 512 -// make TX queue size simmetric to ACK messages queue size for all supplementary serial ports -#define SERIAL_PORT_TX_QUEUE_SIZE 256 // gcodes writing queue to mainboard -#define SERIAL_PORT_2_TX_QUEUE_SIZE ACK_CACHE_SIZE -#define SERIAL_PORT_3_TX_QUEUE_SIZE ACK_CACHE_SIZE -#define SERIAL_PORT_4_TX_QUEUE_SIZE ACK_CACHE_SIZE +#define SERIAL_PORT_TX_QUEUE_SIZE 256 // gcodes writing queue to mainboard +#define SERIAL_PORT_2_TX_QUEUE_SIZE 128 +#define SERIAL_PORT_3_TX_QUEUE_SIZE 128 +#define SERIAL_PORT_4_TX_QUEUE_SIZE 128 const SERIAL_PORT_INFO serialPort[SERIAL_PORT_COUNT] = { {SERIAL_PORT , SERIAL_PORT_RX_QUEUE_SIZE , SERIAL_PORT_TX_QUEUE_SIZE , "" , "1 - Printer"}, diff --git a/TFT/src/User/Hal/stm32f10x/Serial.c b/TFT/src/User/Hal/stm32f10x/Serial.c index 81756e88ff..e951b0a4f3 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.c +++ b/TFT/src/User/Hal/stm32f10x/Serial.c @@ -91,6 +91,8 @@ void Serial_DeConfig(uint8_t port) UART_DeConfig(port); } +#ifdef DEFAULT_WRITE // unbuffered TX serial writing + void Serial_PutChar(uint8_t port, const char ch) { while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); @@ -106,6 +108,107 @@ void Serial_Put(uint8_t port, const char * msg) } } +#else // use TX interrupt based serial writing + +void Serial_PutChar(uint8_t port, const char ch) +{ + // wait for enough free buffer in TX cache to store all data + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) + { + } + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; + + Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer +} + +void Serial_Put(uint8_t port, const char * msg) +{ +#ifdef USE_COMPACT_CODE // less code + while (*msg) + { + // blocking! wait for buffer to become available + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { }; + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg++; + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; + + Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer + } +#else // fast code + // NOTE: used 32 bit variables for performance reasons (in particular for data copy) + + DMA_CIRCULAR_BUFFER * dmaL1Data_ptr = &dmaL1DataTX[port]; + char * cache = dmaL1Data_ptr->cache; + uint32_t cacheSize = dmaL1Data_ptr->cacheSize; + uint32_t wIndex = dmaL1Data_ptr->wIndex; + uint32_t msgSize = strlen(msg); + + // if cache size is not enough to store the data, skip the data copy + // + // NOTE: the following check should never be matched if cache has a proper size. + // If so, the check could be commented out just to improve performance. Just keep it to make the code more robust + // + if ((cacheSize - 1) < msgSize) + return; + + // wait for enough free buffer in TX cache to store all data. + // Used dmaL1Data_ptr->rIndex dynamically changed by TX cache's interrupt handler + // + // NOTE: -1 is needed because full cache usage will introduce a conflict on rIndex and wIndex + // (wIndex == rIndex will indicate an empty cache or a full cache) + // + while ((((cacheSize - 1) - wIndex + dmaL1Data_ptr->rIndex) % cacheSize) < msgSize) + { + } + + uint32_t maxIndex; + + // if data is one chunk only, store data on upper part of circular cache + if ((cacheSize - wIndex) >= msgSize) + { + #ifdef USE_INLINE_COPY + maxIndex = wIndex + msgSize; + + while (wIndex < maxIndex) + { + cache[wIndex++] = *(msg++); + } + #else + memcpy(&cache[wIndex], msg, msgSize); + #endif + } + else // data at end and beginning of cache + { + #ifdef USE_INLINE_COPY + while (wIndex < cacheSize) + { + cache[wIndex++] = *(msg++); + } + + wIndex = 0; + maxIndex = msgSize - (cacheSize - dmaL1Data_ptr->wIndex); // used dmaL1Data_ptr->wIndex and not wIndex + + while (wIndex < maxIndex) + { + cache[wIndex++] = *(msg++); + } + #else + memcpy(&cache[wIndex], msg, cacheSize - wIndex); + memcpy(cache, &msg[cacheSize - wIndex], msgSize - (cacheSize - wIndex)); + #endif + } + + // update queue's writing index with next index + dmaL1Data_ptr->wIndex = (dmaL1Data_ptr->wIndex + msgSize) % cacheSize; + + Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer +#endif // USE_COMPACT_CODE +} + +#endif + // ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { @@ -118,6 +221,24 @@ void USART_IRQHandler(uint8_t port) dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - Serial[port].dma_chanel->CNDTR; } #endif + +#ifndef DEFAULT_WRITE // TX interrupt based serial writing + + if ((Serial[port].uart->SR & USART_FLAG_TXE) != 0) // TX: check for TXE interrupt + { + if (dmaL1DataTX[port].rIndex == dmaL1DataTX[port].wIndex) // no more data? + { + Serial[port].uart->CR1 &= ~USART_FLAG_TXE; // disable TXE interrupt + } + else + { + Serial[port].uart->DR = (uint8_t)dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]; // write next available character + + dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + 1) % dmaL1DataTX[port].cacheSize; // increase reading index + } + } + +#endif // DEFAULT_WRITE } void USART1_IRQHandler(void) diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c index b02101cace..38f06ab8fb 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c @@ -5,7 +5,7 @@ #define RX_IDLE_INTERRUPT false // uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used -//#define TX_DMA_WRITE +#define TX_DMA_WRITE // uncomment this line to use compact code (less code) instead of fast code #define USE_COMPACT_CODE @@ -122,7 +122,7 @@ void Serial_DMA_Config(uint8_t port) cfg->dma_streamRX->M0AR = (uint32_t)(dmaL1DataRX[port].cache); // destination RX data (sram) cfg->dma_streamRX->NDTR = dmaL1DataRX[port].cacheSize; // buffer size RX - cfg->dma_streamRX->CR = cfg->dma_channel << 25; // channel selection + cfg->dma_streamRX->CR = cfg->dma_channel << 25; // channel selection RX cfg->dma_streamTX->CR = cfg->dma_channel << 25; // channel selection TX if (port == SERIAL_PORT) // main serial port has highest priority, writing before reading @@ -215,7 +215,16 @@ void Serial_PutChar(uint8_t port, const char ch) Serial[port].uart->DR = (uint8_t) ch; } -#else // use TX interrupt based serial writing +void Serial_Put(uint8_t port, const char * msg) +{ + while (*msg) + { + while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); + Serial[port].uart->DR = ((uint16_t)*msg++ & (uint16_t)0x01FF); + } +} + +#elif !defined(TX_DMA_WRITE) // TX interrupt based serial writing void Serial_PutChar(uint8_t port, const char ch) { @@ -230,21 +239,6 @@ void Serial_PutChar(uint8_t port, const char ch) Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer } -#endif - -#ifdef DEFAULT_WRITE // unbuffered TX serial writing - -void Serial_Put(uint8_t port, const char * msg) -{ - while (*msg) - { - while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); - Serial[port].uart->DR = ((uint16_t)*msg++ & (uint16_t)0x01FF); - } -} - -#elif !defined(TX_DMA_WRITE) // TX interrupt based serial writing - void Serial_Put(uint8_t port, const char * msg) { #ifdef USE_COMPACT_CODE // less code @@ -342,18 +336,35 @@ void Serial_Send_TX(uint8_t port) Serial[port].dma_streamTX->M0AR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // start address TX data Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags - Serial[port].uart->CR1 |= (1<<6); // enable Transfer Complete (TX) serial Interrupt + Serial[port].uart->CR1 |= USART_FLAG_TC; // enable Transfer Complete (TX) serial Interrupt Serial[port].dma_streamTX->NDTR = Serial[port].txBytes[port]; // no. bytes to transfer Serial[port].dma_streamTX->CR |= 1<<0; // enable TX DMA } +void Serial_PutChar(uint8_t port, const char ch) +{ + // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) + if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && + ((Serial[port].uart->CR1 & USART_FLAG_TC) == 0)) + Serial_Send_TX(port); + + // wait until space becomes available, blocking! + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { } + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; // copy character to cache + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex + + if ((ch == '\n') && ((Serial[port].uart->CR1 & USART_FLAG_TC) == 0)) + Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already +} + void Serial_Put(uint8_t port, const char * msg) { while (*msg) { // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && - ((Serial[port].uart->CR1 & (1<<6)) == 0)) + ((Serial[port].uart->CR1 & USART_FLAG_TC) == 0)) Serial_Send_TX(port); // wait until space becomes available, blocking! @@ -362,30 +373,13 @@ void Serial_Put(uint8_t port, const char * msg) dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg; // copy character to cache dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex - if ((*msg == '\n') && ((Serial[port].uart->CR1 & (1<<6)) == 0)) + if ((*msg == '\n') && ((Serial[port].uart->CR1 & USART_FLAG_TC) == 0)) Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already msg++; // let the compiler optimize this, no need to do it manually! } } -/* -void Serial_PutChar(uint8_t port, const char ch) -{ - // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) - if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && - ((Serial[port].uart->CR1 & (1<<6)) == 0)) - Serial_Send_TX(port); - // wait until space becomes available, blocking! - while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { } - - dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; // copy character to cache - dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex - - if ((ch == '\n') && ((Serial[port].uart->CR1 & (1<<6)) == 0)) - Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already -} -*/ #endif // ISR, serial interrupt handler @@ -403,13 +397,13 @@ void USART_IRQHandler(uint8_t port) #ifndef DEFAULT_WRITE - // TX interrupt based serial writing - // +#ifndef TX_DMA_WRITE // TX interrupt based serial writing + if ((Serial[port].uart->SR & USART_FLAG_TXE) != 0) // TX: check for TXE interrupt { if (dmaL1DataTX[port].rIndex == dmaL1DataTX[port].wIndex) // no more data? { - Serial[port].uart->CR1 &= ~USART_FLAG_TXE; // clear TXE interrupt bit + Serial[port].uart->CR1 &= ~USART_FLAG_TXE; // disable TXE interrupt } else { @@ -419,10 +413,11 @@ void USART_IRQHandler(uint8_t port) } } -#ifdef TX_DMA_WRITE // TX DMA based serial writing - if ((Serial[port].uart->SR & (1<<6)) != 0) // TX: check for Transfer Complete (TC) +#else // TX DMA based serial writing + + if ((Serial[port].uart->SR & USART_FLAG_TC) != 0) // TX: check for Transfer Complete (TC) { - Serial[port].uart->SR &= ~(1<<6); // clear Transfer Complete (TC) bit + Serial[port].uart->SR &= ~USART_FLAG_TC; // clear Transfer Complete (TC) bit // NOTE: the ISR is sometimes called while DMA is still active, so check NDTR status! if (Serial[port].dma_streamTX->NDTR == 0) // sending is complete @@ -433,13 +428,14 @@ void USART_IRQHandler(uint8_t port) if (dmaL1DataTX[port].wIndex != dmaL1DataTX[port].rIndex) // is more data available? Serial_Send_TX(port); // continue sending data else // removed causes double line transfers - Serial[port].uart->CR1 &= ~(1<<6); // disable Transfer Complete (TC) interrupt, nothing more to do + Serial[port].uart->CR1 &= ~USART_FLAG_TC; // disable Transfer Complete (TC) interrupt, nothing more to do } // else: more data is coming, wait for next Transfer Complete (TC) interrupt } -#endif -#endif +#endif // TX_DMA_WRITE + +#endif // DEFAULT_WRITE } void USART1_IRQHandler(void) From 3def09c68fa9b3192bfc2c48cad5efae857b0cef Mon Sep 17 00:00:00 2001 From: digant73 Date: Mon, 25 Sep 2023 21:45:28 +0200 Subject: [PATCH 06/20] cleanup --- TFT/src/User/API/Printing.c | 2 +- TFT/src/User/API/SerialConnection.c | 6 +- TFT/src/User/API/interfaceCmd.c | 1 - TFT/src/User/Hal/gd32f20x/Serial.c | 20 +- TFT/src/User/Hal/gd32f20x/Serial.h | 16 +- TFT/src/User/Hal/stm32f10x/Serial.c | 36 ++-- TFT/src/User/Hal/stm32f10x/Serial.h | 20 +- TFT/src/User/Hal/stm32f2_f4xx/Serial.c | 255 ++++++++++++++----------- TFT/src/User/Hal/stm32f2_f4xx/Serial.h | 24 +-- 9 files changed, 208 insertions(+), 172 deletions(-) diff --git a/TFT/src/User/API/Printing.c b/TFT/src/User/API/Printing.c index 3bad224813..2382170bbb 100644 --- a/TFT/src/User/API/Printing.c +++ b/TFT/src/User/API/Printing.c @@ -110,7 +110,7 @@ void loopBreakToCondition(CONDITION_CALLBACK condCallback) uint16_t rIndex; TASK_LOOP_WHILE(condCallback(), - if ((rIndex = Serial_GetReadingIndex(SERIAL_PORT)) != rIndex_old) + if ((rIndex = Serial_GetReadingIndexRX(SERIAL_PORT)) != rIndex_old) { sendEmergencyCmd("M108\n"); rIndex_old = rIndex; diff --git a/TFT/src/User/API/SerialConnection.c b/TFT/src/User/API/SerialConnection.c index 6ac35f6022..90dc6f53fd 100644 --- a/TFT/src/User/API/SerialConnection.c +++ b/TFT/src/User/API/SerialConnection.c @@ -17,7 +17,7 @@ const SERIAL_PORT_INFO serialPort[SERIAL_PORT_COUNT] = { {SERIAL_PORT , SERIAL_PORT_RX_QUEUE_SIZE , SERIAL_PORT_TX_QUEUE_SIZE , "" , "1 - Printer"}, #ifdef SERIAL_PORT_2 - {SERIAL_PORT_2, SERIAL_PORT_2_RX_QUEUE_SIZE, SERIAL_PORT_2_TX_QUEUE_SIZE, "2", "2 - Wifi"}, + {SERIAL_PORT_2, SERIAL_PORT_2_RX_QUEUE_SIZE, SERIAL_PORT_2_TX_QUEUE_SIZE, "2", "2 - WiFi"}, #endif #ifdef SERIAL_PORT_3 {SERIAL_PORT_3, SERIAL_PORT_3_RX_QUEUE_SIZE, SERIAL_PORT_3_TX_QUEUE_SIZE, "3", "3 - UART3"}, @@ -150,8 +150,8 @@ uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize) // wIndex: update L1 cache's writing index (dynamically changed (by L1 cache's interrupt handler) variables/attributes) // and make a static access (32 bit) to it to speedup performance on this function // - uint32_t wIndex = dmaL1DataRX[port].wIndex = Serial_GetWritingIndex(port); // get the latest wIndex - uint32_t flag = dmaL1DataRX[port].flag; // get the current flag position + uint32_t wIndex = dmaL1DataRX[port].wIndex = Serial_GetWritingIndexRX(port); // get the latest wIndex + uint32_t flag = dmaL1DataRX[port].flag; // get the current flag position if (flag == wIndex) // if no data to read from L1 cache, nothing to do return 0; diff --git a/TFT/src/User/API/interfaceCmd.c b/TFT/src/User/API/interfaceCmd.c index cfe3522a92..638ae249c4 100644 --- a/TFT/src/User/API/interfaceCmd.c +++ b/TFT/src/User/API/interfaceCmd.c @@ -203,7 +203,6 @@ bool moveCacheToCmd(void) void clearCmdQueue(void) { cmdQueue.count = cmdQueue.index_w = cmdQueue.index_r = 0; - cmdCache.count = cmdCache.index_w = cmdCache.index_r = 0; } // Strip out any leading space from the passed command. diff --git a/TFT/src/User/Hal/gd32f20x/Serial.c b/TFT/src/User/Hal/gd32f20x/Serial.c index 097238f42a..34fb8cc5ae 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.c +++ b/TFT/src/User/Hal/gd32f20x/Serial.c @@ -1,8 +1,8 @@ #include "Serial.h" -#include "includes.h" // for infoHost +#include "includes.h" -// set this line to "true" to enable serial IDLE interrupt. IDLE interrupt is no more needed, so always set this macro to "false" -#define RX_IDLE_INTERRUPT false +// set this line to "true" to enable IDLE Line interrupt. It is no more needed, so always set this macro to "false" +#define IDLE_LINE_IT false // uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used //#define TX_DMA_WRITE @@ -20,7 +20,7 @@ DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer -// Config for USART Channel +// config for USART channel const SERIAL_CFG Serial[_UART_CNT] = { {USART0, RCU_DMA0, 4, DMA0}, {USART1, RCU_DMA0, 5, DMA0}, @@ -93,13 +93,13 @@ void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uin dmaL1DataRX[port].cacheSize = cacheSizeRX; dmaL1DataRX[port].cache = malloc(cacheSizeRX); - while (!dmaL1DataRX[port].cache); // malloc failed, blocking! + while (!dmaL1DataRX[port].cache); // malloc failed, blocking! dmaL1DataTX[port].cacheSize = cacheSizeTX; dmaL1DataTX[port].cache = malloc(cacheSizeTX); - while (!dmaL1DataTX[port].cache); // malloc failed, blocking! + while (!dmaL1DataTX[port].cache); // malloc failed, blocking! - UART_Config(port, baudrate, USART_INT_IDLE, RX_IDLE_INTERRUPT); // configure serial line with or without IDLE interrupt + UART_Config(port, baudrate, USART_IT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt Serial_DMA_Config(port); } @@ -130,10 +130,10 @@ void Serial_Put(uint8_t port, const char * msg) // ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { -#if RX_IDLE_INTERRUPT == true // RX serial IDLE interrupt - if ((USART_STAT0(Serial[port].uart) & (1<<4)) != 0) +#if IDLE_LINE_IT == true // IDLE Line interrupt + if ((USART_STAT0(Serial[port].uart) & (1<<4)) != 0) // check for IDLE Line interrupt { - USART_STAT0(Serial[port].uart); // clear interrupt flag + USART_STAT0(Serial[port].uart); // clear IDLE Line bit USART_DATA(Serial[port].uart); dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channel); diff --git a/TFT/src/User/Hal/gd32f20x/Serial.h b/TFT/src/User/Hal/gd32f20x/Serial.h index a7c59b7d90..b84e281600 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.h +++ b/TFT/src/User/Hal/gd32f20x/Serial.h @@ -7,7 +7,7 @@ typedef struct { - char *cache; + char * cache; volatile uint16_t wIndex; // writing index. Precautionally declared as volatile due to access from interrupt handler and main thread volatile uint16_t rIndex; // reading index. Precautionally declared as volatile due to access from interrupt handler and main thread uint16_t flag; // custom flag (for custom usage by the application) @@ -28,29 +28,29 @@ extern const SERIAL_CFG Serial[_UART_CNT]; void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); -// retrieve the next reading index in the message queue of the provided serial port: +// retrieve the next reading index in the RX message queue of the provided serial port: // - port: index of serial port // // - return value: next reading index -static inline uint16_t Serial_GetReadingIndex(uint8_t port) +static inline uint16_t Serial_GetReadingIndexRX(uint8_t port) { return dmaL1DataRX[port].rIndex; } -// retrieve the next writing index in the message queue of the provided serial port -// based on Interrupt/DMA status while writing serial data in the background +// retrieve the next writing index in the RX message queue of the provided serial port +// based on Interrupt/DMA status while writing serial data in the background: // - port: index of serial port // // - return value: next writing index -static inline uint16_t Serial_GetWritingIndex(uint8_t port) +static inline uint16_t Serial_GetWritingIndexRX(uint8_t port) { return dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channel); } -// send character to a uart port. TX interrupt based serial writing is always used even if TX DMA based serial writing is enabled +// send character to a uart port void Serial_PutChar(uint8_t port, const char ch); -// send a zero terminated message to uart port. TX interrupt based serial writing is used if TX DMA based serial writing is disabled +// send a zero terminated message to uart port void Serial_Put(uint8_t port, const char * msg); #endif diff --git a/TFT/src/User/Hal/stm32f10x/Serial.c b/TFT/src/User/Hal/stm32f10x/Serial.c index e951b0a4f3..ef5a554dda 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.c +++ b/TFT/src/User/Hal/stm32f10x/Serial.c @@ -1,8 +1,8 @@ #include "Serial.h" -#include "includes.h" // for infoHost +#include "includes.h" -// set this line to "true" to enable serial IDLE interrupt. IDLE interrupt is no more needed, so always set this macro to "false" -#define RX_IDLE_INTERRUPT false +// set this line to "true" to enable IDLE Line interrupt. It is no more needed, so always set this macro to "false" +#define IDLE_LINE_IT false // uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used //#define TX_DMA_WRITE @@ -20,7 +20,7 @@ DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer -// Config for USART Channel +// config for USART channel const SERIAL_CFG Serial[_UART_CNT] = { {USART1, RCC_AHBPeriph_DMA1, DMA1_Channel5}, {USART2, RCC_AHBPeriph_DMA1, DMA1_Channel6}, @@ -73,13 +73,13 @@ void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uin dmaL1DataRX[port].cacheSize = cacheSizeRX; dmaL1DataRX[port].cache = malloc(cacheSizeRX); - while (!dmaL1DataRX[port].cache); // malloc failed, blocking! + while (!dmaL1DataRX[port].cache); // malloc failed, blocking! dmaL1DataTX[port].cacheSize = cacheSizeTX; dmaL1DataTX[port].cache = malloc(cacheSizeTX); - while (!dmaL1DataTX[port].cache); // malloc failed, blocking! + while (!dmaL1DataTX[port].cache); // malloc failed, blocking! - UART_Config(port, baudrate, USART_IT_IDLE, RX_IDLE_INTERRUPT); // configure serial line with or without IDLE interrupt + UART_Config(port, baudrate, USART_IT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt Serial_DMA_Config(port); } @@ -87,7 +87,7 @@ void Serial_DeConfig(uint8_t port) { Serial_ClearData(port); - Serial[port].dma_chanel->CCR &= ~(1<<0); // Disable DMA + Serial[port].dma_chanel->CCR &= ~(1<<0); // disable DMA UART_DeConfig(port); } @@ -95,7 +95,7 @@ void Serial_DeConfig(uint8_t port) void Serial_PutChar(uint8_t port, const char ch) { - while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); + while ((Serial[port].uart->SR & USART_SR_TC) == (uint16_t)RESET); Serial[port].uart->DR = (uint8_t) ch; } @@ -103,7 +103,7 @@ void Serial_Put(uint8_t port, const char * msg) { while (*msg) { - while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); + while ((Serial[port].uart->SR & USART_SR_TC) == (uint16_t)RESET); Serial[port].uart->DR = ((uint16_t)*msg++ & (uint16_t)0x01FF); } } @@ -120,7 +120,7 @@ void Serial_PutChar(uint8_t port, const char ch) dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; - Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer + Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer } void Serial_Put(uint8_t port, const char * msg) @@ -134,7 +134,7 @@ void Serial_Put(uint8_t port, const char * msg) dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg++; dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; - Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer + Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer } #else // fast code // NOTE: used 32 bit variables for performance reasons (in particular for data copy) @@ -203,7 +203,7 @@ void Serial_Put(uint8_t port, const char * msg) // update queue's writing index with next index dmaL1Data_ptr->wIndex = (dmaL1Data_ptr->wIndex + msgSize) % cacheSize; - Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer + Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer #endif // USE_COMPACT_CODE } @@ -212,10 +212,10 @@ void Serial_Put(uint8_t port, const char * msg) // ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { -#if RX_IDLE_INTERRUPT == true // RX serial IDLE interrupt - if ((Serial[port].uart->SR & USART_FLAG_IDLE) != 0) // RX: check for serial IDLE interrupt +#if IDLE_LINE_IT == true // IDLE Line interrupt + if ((Serial[port].uart->SR & USART_SR_IDLE) != RESET) // check for IDLE Line interrupt { - //Serial[port].uart->SR; // already done in the guard above + Serial[port].uart->SR; // clear IDLE Line bit Serial[port].uart->DR; dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - Serial[port].dma_chanel->CNDTR; @@ -224,11 +224,11 @@ void USART_IRQHandler(uint8_t port) #ifndef DEFAULT_WRITE // TX interrupt based serial writing - if ((Serial[port].uart->SR & USART_FLAG_TXE) != 0) // TX: check for TXE interrupt + if ((Serial[port].uart->SR & USART_SR_TXE) != 0) // check for TXE interrupt { if (dmaL1DataTX[port].rIndex == dmaL1DataTX[port].wIndex) // no more data? { - Serial[port].uart->CR1 &= ~USART_FLAG_TXE; // disable TXE interrupt + Serial[port].uart->CR1 &= ~USART_CR1_TXEIE; // disable TXE interrupt } else { diff --git a/TFT/src/User/Hal/stm32f10x/Serial.h b/TFT/src/User/Hal/stm32f10x/Serial.h index 9868f00645..7efd74e655 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.h +++ b/TFT/src/User/Hal/stm32f10x/Serial.h @@ -7,7 +7,7 @@ typedef struct { - char *cache; + char * cache; volatile uint16_t wIndex; // writing index. Precautionally declared as volatile due to access from interrupt handler and main thread volatile uint16_t rIndex; // reading index. Precautionally declared as volatile due to access from interrupt handler and main thread uint16_t flag; // custom flag (for custom usage by the application) @@ -16,9 +16,9 @@ typedef struct typedef struct { - USART_TypeDef *uart; + USART_TypeDef * uart; uint32_t dma_rcc; - DMA_Channel_TypeDef *dma_chanel; + DMA_Channel_TypeDef * dma_chanel; } SERIAL_CFG; extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; @@ -27,29 +27,29 @@ extern const SERIAL_CFG Serial[_UART_CNT]; void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); -// retrieve the next reading index in the message queue of the provided serial port: +// retrieve the next reading index in the RX message queue of the provided serial port: // - port: index of serial port // // - return value: next reading index -static inline uint16_t Serial_GetReadingIndex(uint8_t port) +static inline uint16_t Serial_GetReadingIndexRX(uint8_t port) { return dmaL1DataRX[port].rIndex; } -// retrieve the next writing index in the message queue of the provided serial port -// based on Interrupt/DMA status while writing serial data in the background +// retrieve the next writing index in the RX message queue of the provided serial port +// based on Interrupt/DMA status while writing serial data in the background: // - port: index of serial port // // - return value: next writing index -static inline uint16_t Serial_GetWritingIndex(uint8_t port) +static inline uint16_t Serial_GetWritingIndexRX(uint8_t port) { return dmaL1DataRX[port].cacheSize - Serial[port].dma_chanel->CNDTR; } -// send character to a uart port. TX interrupt based serial writing is always used even if TX DMA based serial writing is enabled +// send character to a uart port void Serial_PutChar(uint8_t port, const char ch); -// send a zero terminated message to uart port. TX interrupt based serial writing is used if TX DMA based serial writing is disabled +// send a zero terminated message to uart port void Serial_Put(uint8_t port, const char * msg); #endif diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c index 38f06ab8fb..0df4a27d6b 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c @@ -1,8 +1,8 @@ #include "Serial.h" -#include "includes.h" // for infoHost +#include "includes.h" -// set this line to "true" to enable serial IDLE interrupt. IDLE interrupt is no more needed, so always set this macro to "false" -#define RX_IDLE_INTERRUPT false +// set this line to "true" to enable IDLE Line interrupt. It is no more needed, so always set this macro to "false" +#define IDLE_LINE_IT false // uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used #define TX_DMA_WRITE @@ -20,86 +20,122 @@ DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer -// Config for USART Channel +// config for USART channel SERIAL_CFG Serial[_UART_CNT] = { -// USART DMA1 or DMA2 Channel RX_STREAM TX_STREAM - {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2, DMA2_Stream7 }, - {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5, DMA1_Stream6 }, - {USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1, DMA1_Stream3 }, - {UART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2, DMA1_Stream4 }, - {UART5, RCC_AHB1Periph_DMA1, 4, DMA1_Stream0, DMA1_Stream7 }, - {USART6, RCC_AHB1Periph_DMA2, 5, DMA2_Stream1, DMA2_Stream6 }, +// USART DMA1 or DMA2 Channel RX Stream TX Stream TX Bytes + {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2, DMA2_Stream7, 0}, + {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5, DMA1_Stream6, 0}, + {USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1, DMA1_Stream3, 0}, + {UART4 , RCC_AHB1Periph_DMA1, 4, DMA1_Stream2, DMA1_Stream4, 0}, + {UART5 , RCC_AHB1Periph_DMA1, 4, DMA1_Stream0, DMA1_Stream7, 0}, + {USART6, RCC_AHB1Periph_DMA2, 5, DMA2_Stream1, DMA2_Stream6, 0}, }; -// Clear all (RX and TX) DMA interrupts for a serial port -void Serial_DMAClearInterruptFlags(uint8_t port) +// clear all DMA RX and TX interrupt flags for a serial port +void Serial_DMAClearITflags(uint8_t port) { // Channel to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 // Channel to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) { - case _USART1: DMA2->LIFCR = (0x3F << 16); // DMA2_Stream2 low bits:16-21 Channel 4 - DMA2->HIFCR = (0x3F << 22); break; // DMA2_Stream7 high bits:22-27 Channel 4 - - case _USART2: DMA1->HIFCR = (0x3F << 6) | // DMA1_Stream5 high bits: 6-11 Channel 4 - (0x3F << 16); break; // DMA1_Stream6 high bits:16-21 Channel 4 - - case _USART3: DMA1->LIFCR = (0x3F << 6) | // DMA1_Stream1 low bits: 6-11 Channel 4 - (0x3F << 22); break; // DMA1_Stream3 low bits:22-27 Channel 4 - - case _UART4: DMA1->LIFCR = (0x3F << 16); // DMA1_Stream2 low bits:16-21 Channel 4 - DMA1->HIFCR = (0x3F << 0); break; // DMA1_Stream4 high bits: 0- 5 Channel 4 - - case _UART5: DMA1->LIFCR = (0x3F << 0); // DMA1_Stream0 low bits: 0- 5 Channel 4 - DMA1->HIFCR = (0x3F << 22); break; // DMA1_Stream7 high bits:22-27 Channel 4 - - case _USART6: DMA2->LIFCR = (0xFC << 4); // DMA2_Stream1 low bits: 6-11 Channel 5 - DMA2->HIFCR = (0x3F << 16); break; // DMA2_Stream6 high bits:16-21 Channel 5 + case _USART1: + DMA2->LIFCR = (0x3F << 16); // DMA2_Stream2 low bits:16-21 Channel 4 + DMA2->HIFCR = (0x3F << 22); // DMA2_Stream7 high bits:22-27 Channel 4 + break; + + case _USART2: + DMA1->HIFCR = (0x3F << 6) | // DMA1_Stream5 high bits: 6-11 Channel 4 + (0x3F << 16); // DMA1_Stream6 high bits:16-21 Channel 4 + break; + + case _USART3: + DMA1->LIFCR = (0x3F << 6) | // DMA1_Stream1 low bits: 6-11 Channel 4 + (0x3F << 22); // DMA1_Stream3 low bits:22-27 Channel 4 + break; + + case _UART4: + DMA1->LIFCR = (0x3F << 16); // DMA1_Stream2 low bits:16-21 Channel 4 + DMA1->HIFCR = (0x3F << 0); // DMA1_Stream4 high bits: 0- 5 Channel 4 + break; + + case _UART5: + DMA1->LIFCR = (0x3F << 0); // DMA1_Stream0 low bits: 0- 5 Channel 4 + DMA1->HIFCR = (0x3F << 22); // DMA1_Stream7 high bits:22-27 Channel 4 + break; + + case _USART6: + DMA2->LIFCR = (0x3F << 6); // DMA2_Stream1 low bits: 6-11 Channel 5 + DMA2->HIFCR = (0x3F << 16); // DMA2_Stream6 high bits:16-21 Channel 5 + break; } } -// Clear all RX DMA interrupts for a serial port -void Serial_DMAClearInterruptFlagsRX(uint8_t port) +// clear all DMA RX interrupt flags for a given serial port +void Serial_DMAClearITflagsRX(uint8_t port) { // Channel to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 // Channel to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) { - case _USART1: DMA2->LIFCR = (0x3F << 16); break; // DMA2_Stream7 high bits:22-27 Channel 4 + case _USART1: + DMA2->LIFCR = (0x3F << 16); // DMA2_Stream2 low bits:16-21 Channel 4 + break; - case _USART2: DMA1->HIFCR = (0x3F << 6); break; // DMA1_Stream6 high bits:16-21 Channel 4 + case _USART2: + DMA1->HIFCR = (0x3F << 6); // DMA1_Stream5 high bits: 6-11 Channel 4 + break; - case _USART3: DMA1->LIFCR = (0x3F << 6); break; // DMA1_Stream3 low bits:22-27 Channel 4 + case _USART3: + DMA1->LIFCR = (0x3F << 6); // DMA1_Stream1 low bits: 6-11 Channel 4 + break; - case _UART4: DMA1->LIFCR = (0x3F << 16); break; // DMA1_Stream4 high bits: 0- 5 Channel 4 + case _UART4: + DMA1->LIFCR = (0x3F << 16); // DMA1_Stream2 low bits:16-21 Channel 4 + break; - case _UART5: DMA1->LIFCR = (0x3F << 0); break; // DMA1_Stream7 high bits:22-27 Channel 4 + case _UART5: + DMA1->LIFCR = (0x3F << 0); // DMA1_Stream0 low bits: 0-5 Channel 4 + break; - case _USART6: DMA2->LIFCR = (0xFC << 4); break; // DMA2_Stream6 high bits:16-21 Channel 5 + case _USART6: + DMA2->LIFCR = (0x3F << 6); // DMA2_Stream1 low bits: 6-11 Channel 5 + break; } } -// Clear all TX DMA interrupts for a serial port -void Serial_DMAClearInterruptFlagsTX(uint8_t port) +// clear all DMA TX interrupt flags for a given serial port +void Serial_DMAClearITflagsTX(uint8_t port) { // Channel to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 // Channel to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) { - case _USART1: DMA2->HIFCR = (0x3F << 22); break; // DMA2_Stream7 high bits:22-27 Channel 4 + case _USART1: + DMA2->HIFCR = (0x3F << 22); // DMA2_Stream7 high bits:22-27 Channel 4 + break; - case _USART2: DMA1->HIFCR = (0x3F << 16); break; // DMA1_Stream6 high bits:16-21 Channel 4 + case _USART2: + DMA1->HIFCR = (0x3F << 16); // DMA1_Stream6 high bits:16-21 Channel 4 + break; - case _USART3: DMA1->LIFCR = (0x3F << 22); break; // DMA1_Stream3 low bits:22-27 Channel 4 + case _USART3: + DMA1->LIFCR = (0x3F << 22); // DMA1_Stream3 low bits:22-27 Channel 4 + break; - case _UART4: DMA1->HIFCR = (0x3F << 0); break; // DMA1_Stream4 high bits: 0- 5 Channel 4 + case _UART4: + DMA1->HIFCR = (0x3F << 0); // DMA1_Stream4 high bits: 0- 5 Channel 4 + break; - case _UART5: DMA1->HIFCR = (0x3F << 22); break; // DMA1_Stream7 high bits:22-27 Channel 4 + case _UART5: + DMA1->HIFCR = (0x3F << 22); // DMA1_Stream7 high bits:22-27 Channel 4 + break; - case _USART6: DMA2->HIFCR = (0x3F << 16); break; // DMA2_Stream6 high bits:16-21 Channel 5 + case _USART6: + DMA2->HIFCR = (0x3F << 16); // DMA2_Stream6 high bits:16-21 Channel 5 + break; } } @@ -107,56 +143,56 @@ void Serial_DMA_Config(uint8_t port) { const SERIAL_CFG * cfg = &Serial[port]; - RCC_AHB1PeriphClockCmd(cfg->dma_rcc, ENABLE); // DMA RCC enable + RCC_AHB1PeriphClockCmd(cfg->dma_rcc, ENABLE); // DMA RCC enable - cfg->dma_streamRX->CR &= ~(1<<0); // disable RX DMA - cfg->dma_streamTX->CR &= ~(1<<0); // disable TX DMA + cfg->dma_streamRX->CR &= ~(1<<0); // disable RX DMA + cfg->dma_streamTX->CR &= ~(1<<0); // disable TX DMA - Serial_DMAClearInterruptFlagsRX(port); // clear DMA RX interrupt flags + Serial_DMAClearITflags(port); // clear DMA RX-TX interrupt flags - cfg->uart->CR3 |= 3<<6; // DMA enable transmitter(DMAT) and receiver(DMAR) + cfg->uart->CR3 |= 3<<6; // DMA enable transmitter (DMAT) and receiver (DMAR) - cfg->dma_streamRX->PAR = (uint32_t)(&cfg->uart->DR); // peripheral address RX (usart) - cfg->dma_streamTX->PAR = (uint32_t)(&cfg->uart->DR); // peripheral address TX (usart) + cfg->dma_streamRX->PAR = (uint32_t)(&cfg->uart->DR); // RX peripheral address (usart) + cfg->dma_streamTX->PAR = (uint32_t)(&cfg->uart->DR); // TX peripheral address (usart) - cfg->dma_streamRX->M0AR = (uint32_t)(dmaL1DataRX[port].cache); // destination RX data (sram) - cfg->dma_streamRX->NDTR = dmaL1DataRX[port].cacheSize; // buffer size RX + cfg->dma_streamRX->M0AR = (uint32_t)(dmaL1DataRX[port].cache); // RX destination data (sram) + cfg->dma_streamRX->NDTR = dmaL1DataRX[port].cacheSize; // RX buffer size - cfg->dma_streamRX->CR = cfg->dma_channel << 25; // channel selection RX - cfg->dma_streamTX->CR = cfg->dma_channel << 25; // channel selection TX + cfg->dma_streamRX->CR = cfg->dma_channel << 25; // RX channel selection, set to 0 all the other CR bits + cfg->dma_streamTX->CR = cfg->dma_channel << 25; // TX channel selection, set to 0 all the other CR bits - if (port == SERIAL_PORT) // main serial port has highest priority, writing before reading - { - cfg->dma_streamRX->CR |= 2<<16; // RX priority level: High - cfg->dma_streamTX->CR |= 3<<16; // TX priority level: Very high + if (port == SERIAL_PORT) + { // primary serial port priority at highest level (TX higher than RX) + cfg->dma_streamRX->CR |= 2<<16; // RX priority level: High + cfg->dma_streamTX->CR |= 3<<16; // TX priority level: Very high } else - { - cfg->dma_streamRX->CR |= 0<<16; // RX priority level: Low - cfg->dma_streamTX->CR |= 1<<16; // TX priority level: Medium + { // secondary serial ports priority at medium level + cfg->dma_streamRX->CR |= 0<<16; // RX priority level: Low + cfg->dma_streamTX->CR |= 1<<16; // TX priority level: Medium } -//cfg->dma_streamRX->CR &= ~(3<<13); // memory data size: 8 -//cfg->dma_streamTX->CR &= ~(3<<13); // memory data size: 8 +//cfg->dma_streamRX->CR &= ~(3<<13); // RX memory data size: 8 bit +//cfg->dma_streamTX->CR &= ~(3<<13); // TX memory data size: 8 bit -//cfg->dma_streamRX->CR &= ~(3<<11); // peripheral data size: 8 -//cfg->dma_streamTX->CR &= ~(3<<11); // peripheral data size: 8 +//cfg->dma_streamRX->CR &= ~(3<<11); // RX peripheral data size: 8 bit +//cfg->dma_streamTX->CR &= ~(3<<11); // TX peripheral data size: 8 bit - cfg->dma_streamRX->CR |= 1<<10; // memory increment mode - cfg->dma_streamTX->CR |= 1<<10; // memory increment mode + cfg->dma_streamRX->CR |= 1<<10; // RX memory increment mode + cfg->dma_streamTX->CR |= 1<<10; // TX memory increment mode -//cfg->dma_streamRX->CR &= ~(1<<9); // peripheral no increment mode -//cfg->dma_streamTX->CR &= ~(1<<9); // peripheral no increment mode +//cfg->dma_streamRX->CR &= ~(1<<9); // peripheral no increment mode +//cfg->dma_streamTX->CR &= ~(1<<9); // peripheral no increment mode - cfg->dma_streamRX->CR |= 1<<8; // circular mode enabled RX -//cfg->dma_streamTX->CR &= ~(1<<8); // circular mode disabled + cfg->dma_streamRX->CR |= 1<<8; // RX circular mode enabled +//cfg->dma_streamTX->CR &= ~(1<<8); // TX circular mode disabled -//cfg->dma_streamRX->CR &= ~(1<<6); // data transfer direction: Peripheral-to-memory - cfg->dma_streamTX->CR |= 1<<6; // data transfer direction: Memory-to-Peripheral +//cfg->dma_streamRX->CR &= ~(1<<6); // RX data transfer direction: Peripheral-to-memory + cfg->dma_streamTX->CR |= 1<<6; // TX data transfer direction: Memory-to-Peripheral -//cfg->dma_streamTX->CR |= 1<<4; // enable TX DMA Transfer Complete interrupt, done later when needed +//cfg->dma_streamTX->CR |= 1<<4; // enable TX DMA Transfer Complete interrupt, done later when needed - cfg->dma_streamRX->CR |= 1<<0; // enable RX DMA + cfg->dma_streamRX->CR |= 1<<0; // re-enable RX DMA } void Serial_ClearData(uint8_t port) @@ -177,7 +213,7 @@ void Serial_ClearData(uint8_t port) dmaL1DataTX[port].cache = NULL; } - Serial[port].txBytes[port] = 0; + Serial[port].txBytes = 0; } void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate) @@ -186,13 +222,13 @@ void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uin dmaL1DataRX[port].cacheSize = cacheSizeRX; dmaL1DataRX[port].cache = malloc(cacheSizeRX); - while (!dmaL1DataRX[port].cache); // malloc failed, blocking! + while (!dmaL1DataRX[port].cache); // malloc failed, blocking! dmaL1DataTX[port].cacheSize = cacheSizeTX; dmaL1DataTX[port].cache = malloc(cacheSizeTX); - while (!dmaL1DataTX[port].cache); // malloc failed, blocking! + while (!dmaL1DataTX[port].cache); // malloc failed, blocking! - UART_Config(port, baudrate, USART_IT_IDLE, RX_IDLE_INTERRUPT); // configure serial line with or without IDLE interrupt + UART_Config(port, baudrate, USART_IT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt Serial_DMA_Config(port); } @@ -203,7 +239,7 @@ void Serial_DeConfig(uint8_t port) Serial[port].dma_streamRX->CR &= ~(1<<0); // disable RX DMA Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA - Serial_DMAClearInterruptFlagsRX(port); + Serial_DMAClearITflags(port); UART_DeConfig(port); } @@ -211,7 +247,7 @@ void Serial_DeConfig(uint8_t port) void Serial_PutChar(uint8_t port, const char ch) { - while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); + while ((Serial[port].uart->SR & USART_SR_TC) == (uint16_t)RESET); Serial[port].uart->DR = (uint8_t) ch; } @@ -219,7 +255,7 @@ void Serial_Put(uint8_t port, const char * msg) { while (*msg) { - while ((Serial[port].uart->SR & USART_FLAG_TC) == (uint16_t)RESET); + while ((Serial[port].uart->SR & USART_SR_TC) == (uint16_t)RESET); Serial[port].uart->DR = ((uint16_t)*msg++ & (uint16_t)0x01FF); } } @@ -236,7 +272,7 @@ void Serial_PutChar(uint8_t port, const char ch) dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; - Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer + Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer } void Serial_Put(uint8_t port, const char * msg) @@ -250,7 +286,7 @@ void Serial_Put(uint8_t port, const char * msg) dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg++; dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; - Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer + Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer } #else // fast code // NOTE: used 32 bit variables for performance reasons (in particular for data copy) @@ -319,7 +355,7 @@ void Serial_Put(uint8_t port, const char * msg) // update queue's writing index with next index dmaL1Data_ptr->wIndex = (dmaL1Data_ptr->wIndex + msgSize) % cacheSize; - Serial[port].uart->CR1 |= USART_FLAG_TXE; // set TXE interrupt bit to start the serial transfer + Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer #endif // USE_COMPACT_CODE } @@ -330,22 +366,22 @@ void Serial_Send_TX(uint8_t port) { // setup DMA transfer, and wait for serial Transfer Complete (TX) interrupt in ISR if (dmaL1DataTX[port].wIndex >= dmaL1DataTX[port].rIndex) - Serial[port].txBytes[port] = dmaL1DataTX[port].wIndex - dmaL1DataTX[port].rIndex; + Serial[port].txBytes = dmaL1DataTX[port].wIndex - dmaL1DataTX[port].rIndex; else - Serial[port].txBytes[port] = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts + Serial[port].txBytes = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts Serial[port].dma_streamTX->M0AR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // start address TX data - Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags - Serial[port].uart->CR1 |= USART_FLAG_TC; // enable Transfer Complete (TX) serial Interrupt - Serial[port].dma_streamTX->NDTR = Serial[port].txBytes[port]; // no. bytes to transfer - Serial[port].dma_streamTX->CR |= 1<<0; // enable TX DMA + Serial_DMAClearITflagsTX(port); // clear DMA TX interrupt flags + Serial[port].uart->CR1 |= USART_CR1_TCIE; // enable Transfer Complete (TX) serial Interrupt + Serial[port].dma_streamTX->NDTR = Serial[port].txBytes; // no. bytes to transfer + Serial[port].dma_streamTX->CR |= 1<<0; // enable TX DMA } void Serial_PutChar(uint8_t port, const char ch) { // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && - ((Serial[port].uart->CR1 & USART_FLAG_TC) == 0)) + ((Serial[port].uart->CR1 & USART_CR1_TCIE) == 0)) Serial_Send_TX(port); // wait until space becomes available, blocking! @@ -354,7 +390,7 @@ void Serial_PutChar(uint8_t port, const char ch) dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; // copy character to cache dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex - if ((ch == '\n') && ((Serial[port].uart->CR1 & USART_FLAG_TC) == 0)) + if ((ch == '\n') && ((Serial[port].uart->CR1 & USART_CR1_TCIE) == 0)) Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already } @@ -364,7 +400,7 @@ void Serial_Put(uint8_t port, const char * msg) { // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && - ((Serial[port].uart->CR1 & USART_FLAG_TC) == 0)) + ((Serial[port].uart->CR1 & USART_CR1_TCIE) == 0)) Serial_Send_TX(port); // wait until space becomes available, blocking! @@ -373,7 +409,7 @@ void Serial_Put(uint8_t port, const char * msg) dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg; // copy character to cache dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex - if ((*msg == '\n') && ((Serial[port].uart->CR1 & USART_FLAG_TC) == 0)) + if ((*msg == '\n') && ((Serial[port].uart->CR1 & USART_CR1_TCIE) == 0)) Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already msg++; // let the compiler optimize this, no need to do it manually! @@ -385,10 +421,10 @@ void Serial_Put(uint8_t port, const char * msg) // ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { -#if RX_IDLE_INTERRUPT == true // RX serial IDLE interrupt - if ((Serial[port].uart->SR & USART_FLAG_IDLE) != 0) // RX: check for serial IDLE interrupt +#if IDLE_LINE_IT == true // IDLE Line interrupt + if ((Serial[port].uart->SR & USART_SR_IDLE) != RESET) // check for IDLE Line interrupt { - //Serial[port].uart->SR; // already done in the guard above + Serial[port].uart->SR; // clear IDLE Line bit Serial[port].uart->DR; dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - Serial[port].dma_streamRX->NDTR; @@ -399,11 +435,11 @@ void USART_IRQHandler(uint8_t port) #ifndef TX_DMA_WRITE // TX interrupt based serial writing - if ((Serial[port].uart->SR & USART_FLAG_TXE) != 0) // TX: check for TXE interrupt + if ((Serial[port].uart->SR & USART_SR_TXE) != 0) // check for TXE interrupt { if (dmaL1DataTX[port].rIndex == dmaL1DataTX[port].wIndex) // no more data? { - Serial[port].uart->CR1 &= ~USART_FLAG_TXE; // disable TXE interrupt + Serial[port].uart->CR1 &= ~USART_CR1_TXEIE; // disable TXE interrupt } else { @@ -415,20 +451,21 @@ void USART_IRQHandler(uint8_t port) #else // TX DMA based serial writing - if ((Serial[port].uart->SR & USART_FLAG_TC) != 0) // TX: check for Transfer Complete (TC) + if ((Serial[port].uart->SR & USART_SR_TC) != 0) // check for Transfer Complete (TC) interrupt { - Serial[port].uart->SR &= ~USART_FLAG_TC; // clear Transfer Complete (TC) bit + Serial[port].uart->SR &= ~USART_SR_TC; // clear Transfer Complete (TC) bit // NOTE: the ISR is sometimes called while DMA is still active, so check NDTR status! - if (Serial[port].dma_streamTX->NDTR == 0) // sending is complete + // + if (Serial[port].dma_streamTX->NDTR == 0) // sending is complete { - dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + Serial[port].txBytes[port]) % dmaL1DataTX[port].cacheSize; - Serial[port].txBytes[port] = 0; + dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + Serial[port].txBytes) % dmaL1DataTX[port].cacheSize; + Serial[port].txBytes = 0; if (dmaL1DataTX[port].wIndex != dmaL1DataTX[port].rIndex) // is more data available? Serial_Send_TX(port); // continue sending data else // removed causes double line transfers - Serial[port].uart->CR1 &= ~USART_FLAG_TC; // disable Transfer Complete (TC) interrupt, nothing more to do + Serial[port].uart->CR1 &= ~USART_CR1_TCIE; // disable Transfer Complete (TC) interrupt, nothing more to do } // else: more data is coming, wait for next Transfer Complete (TC) interrupt } diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h index 5b05e405f6..36f6665d44 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h @@ -7,7 +7,7 @@ typedef struct { - char *cache; + char * cache; volatile uint16_t wIndex; // writing index. Precautionally declared as volatile due to access from interrupt handler and main thread volatile uint16_t rIndex; // reading index. Precautionally declared as volatile due to access from interrupt handler and main thread uint16_t flag; // custom flag (for custom usage by the application) @@ -16,12 +16,12 @@ typedef struct typedef struct { - USART_TypeDef *uart; + USART_TypeDef * uart; uint32_t dma_rcc; uint8_t dma_channel; - DMA_Stream_TypeDef *dma_streamRX; - DMA_Stream_TypeDef *dma_streamTX; - volatile uint32_t txBytes[_UART_CNT]; + DMA_Stream_TypeDef * dma_streamRX; + DMA_Stream_TypeDef * dma_streamTX; + volatile uint32_t txBytes; } SERIAL_CFG; extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; @@ -30,29 +30,29 @@ extern SERIAL_CFG Serial[_UART_CNT]; void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); -// retrieve the next reading index in the message queue of the provided serial port: +// retrieve the next reading index in the RX message queue of the provided serial port: // - port: index of serial port // // - return value: next reading index -static inline uint16_t Serial_GetReadingIndex(uint8_t port) +static inline uint16_t Serial_GetReadingIndexRX(uint8_t port) { return dmaL1DataRX[port].rIndex; } -// retrieve the next writing index in the message queue of the provided serial port -// based on Interrupt/DMA status while writing serial data in the background +// retrieve the next writing index in the RX message queue of the provided serial port +// based on Interrupt/DMA status while writing serial data in the background: // - port: index of serial port // // - return value: next writing index -static inline uint16_t Serial_GetWritingIndex(uint8_t port) +static inline uint16_t Serial_GetWritingIndexRX(uint8_t port) { return dmaL1DataRX[port].cacheSize - Serial[port].dma_streamRX->NDTR; } -// send character to a uart port. TX interrupt based serial writing is always used even if TX DMA based serial writing is enabled +// send character to a uart port void Serial_PutChar(uint8_t port, const char ch); -// send a zero terminated message to uart port. TX interrupt based serial writing is used if TX DMA based serial writing is disabled +// send a zero terminated message to uart port void Serial_Put(uint8_t port, const char * msg); #endif From b5eb8af21e9f957c38cc5244c79d3b906ce54246 Mon Sep 17 00:00:00 2001 From: digant73 Date: Mon, 25 Sep 2023 22:15:45 +0200 Subject: [PATCH 07/20] minor fix --- TFT/src/User/Hal/gd32f20x/Serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT/src/User/Hal/gd32f20x/Serial.c b/TFT/src/User/Hal/gd32f20x/Serial.c index 34fb8cc5ae..c72abdc050 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.c +++ b/TFT/src/User/Hal/gd32f20x/Serial.c @@ -99,7 +99,7 @@ void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uin dmaL1DataTX[port].cache = malloc(cacheSizeTX); while (!dmaL1DataTX[port].cache); // malloc failed, blocking! - UART_Config(port, baudrate, USART_IT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt + UART_Config(port, baudrate, USART_INT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt Serial_DMA_Config(port); } From f463f06ae00ac58963b4332fb9134b4c3a3748fd Mon Sep 17 00:00:00 2001 From: digant73 Date: Mon, 25 Sep 2023 22:30:09 +0200 Subject: [PATCH 08/20] minor cleanup --- TFT/src/User/API/interfaceCmd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/TFT/src/User/API/interfaceCmd.c b/TFT/src/User/API/interfaceCmd.c index 638ae249c4..cfe3522a92 100644 --- a/TFT/src/User/API/interfaceCmd.c +++ b/TFT/src/User/API/interfaceCmd.c @@ -203,6 +203,7 @@ bool moveCacheToCmd(void) void clearCmdQueue(void) { cmdQueue.count = cmdQueue.index_w = cmdQueue.index_r = 0; + cmdCache.count = cmdCache.index_w = cmdCache.index_r = 0; } // Strip out any leading space from the passed command. From 87c5ac4c202e6d39fbcf98907f54f4cf60a43f41 Mon Sep 17 00:00:00 2001 From: digant73 Date: Tue, 26 Sep 2023 20:00:30 +0200 Subject: [PATCH 09/20] Added other TX and RX KPIs on Monitoring menu --- TFT/src/User/API/SerialConnection.c | 3 +-- TFT/src/User/API/interfaceCmd.c | 2 ++ TFT/src/User/API/parseACK.c | 2 ++ TFT/src/User/Hal/stm32f2_f4xx/Serial.c | 12 +++++----- TFT/src/User/Menu/Monitoring.c | 12 ++++++++-- TFT/src/User/Menu/Monitoring.h | 33 +++++++++++++++++++++++++- TFT/src/User/os_timer.c | 3 ++- 7 files changed, 55 insertions(+), 12 deletions(-) diff --git a/TFT/src/User/API/SerialConnection.c b/TFT/src/User/API/SerialConnection.c index 2a3aa18a22..70f3d0996a 100644 --- a/TFT/src/User/API/SerialConnection.c +++ b/TFT/src/User/API/SerialConnection.c @@ -212,9 +212,8 @@ uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize) } rIndex = 0; - uint32_t maxIndex = bufSize - (cacheSize - dmaL1Data_ptr->rIndex); // used dmaL1Data_ptr->rIndex and not rIndex - while (rIndex < maxIndex) + while (rIndex <= flag) { *(buf++) = cache[rIndex++]; } diff --git a/TFT/src/User/API/interfaceCmd.c b/TFT/src/User/API/interfaceCmd.c index 8173857671..d3e1cfb338 100644 --- a/TFT/src/User/API/interfaceCmd.c +++ b/TFT/src/User/API/interfaceCmd.c @@ -278,6 +278,8 @@ bool sendCmd(bool purge, bool avoidTerminal) if (!purge) // if command is not purged, send it to printer { + UPD_TX_KPIS(cmd_len); // debug monitoring KPI + if (infoMachineSettings.firmwareType != FW_REPRAPFW) Serial_Put(SERIAL_PORT, cmd_ptr); else diff --git a/TFT/src/User/API/parseACK.c b/TFT/src/User/API/parseACK.c index 9af0883514..bf07e95ac8 100644 --- a/TFT/src/User/API/parseACK.c +++ b/TFT/src/User/API/parseACK.c @@ -358,6 +358,8 @@ void parseACK(void) { while ((ack_len = Serial_Get(SERIAL_PORT, ack_cache, ACK_CACHE_SIZE)) != 0) // if some data have been retrieved { + UPD_RX_KPIS(ack_len); // debug monitoring KPI + #if defined(SERIAL_DEBUG_PORT) && defined(DEBUG_SERIAL_COMM) // dump raw serial data received to debug port Serial_Put(SERIAL_DEBUG_PORT, "<<"); diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c index 0df4a27d6b..269a58a35e 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c @@ -34,8 +34,8 @@ SERIAL_CFG Serial[_UART_CNT] = { // clear all DMA RX and TX interrupt flags for a serial port void Serial_DMAClearITflags(uint8_t port) { - // Channel to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 - // Channel to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 + // Stream # to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 + // Stream # to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) { @@ -74,8 +74,8 @@ void Serial_DMAClearITflags(uint8_t port) // clear all DMA RX interrupt flags for a given serial port void Serial_DMAClearITflagsRX(uint8_t port) { - // Channel to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 - // Channel to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 + // Stream # to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 + // Stream # to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) { @@ -108,8 +108,8 @@ void Serial_DMAClearITflagsRX(uint8_t port) // clear all DMA TX interrupt flags for a given serial port void Serial_DMAClearITflagsTX(uint8_t port) { - // Channel to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 - // Channel to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 + // Stream # to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 + // Stream # to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) { diff --git a/TFT/src/User/Menu/Monitoring.c b/TFT/src/User/Menu/Monitoring.c index b8dc42f618..42c41825d2 100644 --- a/TFT/src/User/Menu/Monitoring.c +++ b/TFT/src/User/Menu/Monitoring.c @@ -23,7 +23,9 @@ void menuMonitoring(void) GUI_DispString(0, ICON_START_Y, (uint8_t *)"Buffered gcodes : "); GUI_DispString(0, ICON_START_Y + 1 * (BYTE_HEIGHT + 4), (uint8_t *)"Pending gcodes : "); GUI_DispString(0, ICON_START_Y + 2 * (BYTE_HEIGHT + 4), (uint8_t *)"Free TX slots : "); - GUI_DispString(0, ICON_START_Y + 3 * (BYTE_HEIGHT + 4), (uint8_t *)"Scan rate : "); + GUI_DispString(0, ICON_START_Y + 3 * (BYTE_HEIGHT + 4), (uint8_t *)"TX gcodes/bytes : "); + GUI_DispString(0, ICON_START_Y + 4 * (BYTE_HEIGHT + 4), (uint8_t *)"RX acks/bytes : "); + GUI_DispString(0, ICON_START_Y + 5 * (BYTE_HEIGHT + 4), (uint8_t *)"Scan rate : "); // draw bottom line and text GUI_HLine(0, LCD_HEIGHT - (BYTE_HEIGHT*2), LCD_WIDTH); @@ -48,9 +50,15 @@ void menuMonitoring(void) sprintf(str, "%d ", infoHost.tx_slots); GUI_DispString(18 * BYTE_WIDTH, ICON_START_Y + 2 * (BYTE_HEIGHT + 4), (uint8_t *)str); - sprintf(str, "%d ", infoMonitoring.scan_rate_per_second); + sprintf(str, " %d/%d ", infoMonitoring.tx_cmd_rate, infoMonitoring.tx_bytes_rate); GUI_DispString(18 * BYTE_WIDTH, ICON_START_Y + 3 * (BYTE_HEIGHT + 4), (uint8_t *)str); + sprintf(str, "%d/%d ", infoMonitoring.rx_ack_rate, infoMonitoring.rx_bytes_rate); + GUI_DispString(18 * BYTE_WIDTH, ICON_START_Y + 4 * (BYTE_HEIGHT + 4), (uint8_t *)str); + + sprintf(str, "%d ", infoMonitoring.scan_rate_per_second); + GUI_DispString(18 * BYTE_WIDTH, ICON_START_Y + 5 * (BYTE_HEIGHT + 4), (uint8_t *)str); + GUI_RestoreColorDefault(); } diff --git a/TFT/src/User/Menu/Monitoring.h b/TFT/src/User/Menu/Monitoring.h index e561f5a358..2329babc54 100644 --- a/TFT/src/User/Menu/Monitoring.h +++ b/TFT/src/User/Menu/Monitoring.h @@ -10,21 +10,52 @@ extern "C" { #ifdef DEBUG_MONITORING typedef struct { + uint32_t tx_cmd; + uint32_t tx_cmd_rate; + uint32_t tx_bytes; + uint32_t tx_bytes_rate; + uint32_t rx_ack; + uint32_t rx_ack_rate; + uint32_t rx_bytes; + uint32_t rx_bytes_rate; uint32_t scan_rate_counter; uint32_t scan_rate_per_second; } MONITORING; extern MONITORING infoMonitoring; + #define UPD_TX_KPIS(bytes) \ + { \ + infoMonitoring.tx_cmd++; \ + infoMonitoring.tx_bytes += bytes; \ + } + + #define UPD_RX_KPIS(bytes) \ + { \ + infoMonitoring.rx_ack++; \ + infoMonitoring.rx_bytes += bytes; \ + } + #define UPD_SCAN_RATE() infoMonitoring.scan_rate_counter++ - #define AVG_SCAN_RATE() \ + + #define AVG_KPIS() \ { \ + infoMonitoring.tx_cmd_rate = infoMonitoring.tx_cmd; \ + infoMonitoring.tx_cmd = 0; \ + infoMonitoring.tx_bytes_rate = infoMonitoring.tx_bytes; \ + infoMonitoring.tx_bytes = 0; \ + infoMonitoring.rx_ack_rate = infoMonitoring.rx_ack; \ + infoMonitoring.rx_ack = 0; \ + infoMonitoring.rx_bytes_rate = infoMonitoring.rx_bytes; \ + infoMonitoring.rx_bytes = 0; \ infoMonitoring.scan_rate_per_second = infoMonitoring.scan_rate_counter; \ infoMonitoring.scan_rate_counter = 0; \ } void menuMonitoring(void); #else + #define UPD_TX_KPIS(bytes) + #define UPD_RX_KPIS(bytes) #define UPD_SCAN_RATE() #define AVG_SCAN_RATE() #endif diff --git a/TFT/src/User/os_timer.c b/TFT/src/User/os_timer.c index f72e407f22..954699e011 100644 --- a/TFT/src/User/os_timer.c +++ b/TFT/src/User/os_timer.c @@ -61,8 +61,9 @@ void TIM7_IRQHandler(void) if (os_counter % 1000 == 0) { + AVG_KPIS(); // debug monitoring KPI + updatePrintTime(); - AVG_SCAN_RATE(); // debug monitoring KPI } loopTouchScreen(); From cb3f84c5ee73ade89503ab7e153f55394c289223 Mon Sep 17 00:00:00 2001 From: digant73 Date: Tue, 26 Sep 2023 20:05:57 +0200 Subject: [PATCH 10/20] added missing change --- TFT/src/User/Menu/Monitoring.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TFT/src/User/Menu/Monitoring.h b/TFT/src/User/Menu/Monitoring.h index 2329babc54..a79341d25f 100644 --- a/TFT/src/User/Menu/Monitoring.h +++ b/TFT/src/User/Menu/Monitoring.h @@ -57,7 +57,7 @@ extern "C" { #define UPD_TX_KPIS(bytes) #define UPD_RX_KPIS(bytes) #define UPD_SCAN_RATE() - #define AVG_SCAN_RATE() + #define AVG_KPIS() #endif #ifdef __cplusplus From 39007c764d92e184b4f5b756e8fa099ef40a07d3 Mon Sep 17 00:00:00 2001 From: digant73 Date: Tue, 26 Sep 2023 20:15:30 +0200 Subject: [PATCH 11/20] minor cleanup --- TFT/src/User/Menu/Monitoring.c | 2 +- TFT/src/User/os_timer.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/TFT/src/User/Menu/Monitoring.c b/TFT/src/User/Menu/Monitoring.c index 42c41825d2..6804222a60 100644 --- a/TFT/src/User/Menu/Monitoring.c +++ b/TFT/src/User/Menu/Monitoring.c @@ -50,7 +50,7 @@ void menuMonitoring(void) sprintf(str, "%d ", infoHost.tx_slots); GUI_DispString(18 * BYTE_WIDTH, ICON_START_Y + 2 * (BYTE_HEIGHT + 4), (uint8_t *)str); - sprintf(str, " %d/%d ", infoMonitoring.tx_cmd_rate, infoMonitoring.tx_bytes_rate); + sprintf(str, "%d/%d ", infoMonitoring.tx_cmd_rate, infoMonitoring.tx_bytes_rate); GUI_DispString(18 * BYTE_WIDTH, ICON_START_Y + 3 * (BYTE_HEIGHT + 4), (uint8_t *)str); sprintf(str, "%d/%d ", infoMonitoring.rx_ack_rate, infoMonitoring.rx_bytes_rate); diff --git a/TFT/src/User/os_timer.c b/TFT/src/User/os_timer.c index 954699e011..c09816a9e9 100644 --- a/TFT/src/User/os_timer.c +++ b/TFT/src/User/os_timer.c @@ -43,8 +43,9 @@ void TIMER6_IRQHandler(void) if (os_counter % 1000 == 0) { + AVG_KPIS(); // debug monitoring KPI + updatePrintTime(); - AVG_SCAN_RATE(); // debug monitoring KPI } loopTouchScreen(); From f20c195c5ddf190190a7f571b4559a14419a6b66 Mon Sep 17 00:00:00 2001 From: digant73 Date: Thu, 28 Sep 2023 22:09:16 +0200 Subject: [PATCH 12/20] update Serial.c/h on STM32F2 and STM32F1 --- TFT/src/User/API/RRFSendCmd.c | 64 +--- TFT/src/User/API/SerialConnection.c | 45 ++- TFT/src/User/API/SerialConnection.h | 12 +- TFT/src/User/API/parseACK.c | 5 +- TFT/src/User/Hal/stm32f10x/Serial.c | 254 ++++++++-------- TFT/src/User/Hal/stm32f10x/Serial.h | 42 +-- TFT/src/User/Hal/stm32f10x/uart.h | 2 +- TFT/src/User/Hal/stm32f2_f4xx/Serial.c | 399 ++++++++----------------- TFT/src/User/Hal/stm32f2_f4xx/Serial.h | 44 +-- 9 files changed, 335 insertions(+), 532 deletions(-) diff --git a/TFT/src/User/API/RRFSendCmd.c b/TFT/src/User/API/RRFSendCmd.c index e753cdcae7..b0ed58a8cd 100644 --- a/TFT/src/User/API/RRFSendCmd.c +++ b/TFT/src/User/API/RRFSendCmd.c @@ -1,58 +1,24 @@ -#include "RRFSendCmd.h" -#include "Serial.h" -#include +#include "includes.h" -static uint8_t n_sent = 0; static uint32_t line_number = 0; -static uint8_t checksum = 0; -void sendCharAndChecksum(const char c) +void rrfSendCmd(const char * cmd_ptr) { - checksum ^= c; - Serial_PutChar(SERIAL_PORT, c); - n_sent++; -} + char rrfCmd[CMD_MAX_SIZE]; + char * rrfCmd_ptr = rrfCmd; + uint8_t checksum = 0; -void sendChar(const char c) -{ - if (c == '\n') - { - if (n_sent != 0) - { - Serial_PutChar(SERIAL_PORT, '*'); - char digit0 = checksum % 10 + '0'; - checksum /= 10; - char digit1 = checksum % 10 + '0'; - checksum /= 10; - if (checksum != 0) - { - Serial_PutChar(SERIAL_PORT, checksum + '0'); - } - Serial_PutChar(SERIAL_PORT, digit1); - Serial_PutChar(SERIAL_PORT, digit0); - } - Serial_PutChar(SERIAL_PORT, c); - n_sent = 0; - } - else - { - if (n_sent == 0) - { - char number[11]; - checksum = 0; - sendCharAndChecksum('N'); - snprintf(number, 11, "%lu", line_number++); - rrfSendCmd(number); - sendCharAndChecksum(' '); - } - sendCharAndChecksum(c); - } -} + sprintf(rrfCmd, "N%lu %s", line_number++, cmd_ptr); -void rrfSendCmd(const char* cmd_ptr) -{ - while (*cmd_ptr != 0) + // calculate checksum + while (*rrfCmd_ptr != '\n') { - sendChar(*cmd_ptr++); + checksum ^= *rrfCmd_ptr++; } + + // add checksum and finalize formatting the RRF command + sprintf(rrfCmd_ptr, "*%u\n", checksum); + + // send the command to the serial port + Serial_Put(SERIAL_PORT, rrfCmd); } diff --git a/TFT/src/User/API/SerialConnection.c b/TFT/src/User/API/SerialConnection.c index 70f3d0996a..ba1dcc98f3 100644 --- a/TFT/src/User/API/SerialConnection.c +++ b/TFT/src/User/API/SerialConnection.c @@ -1,18 +1,15 @@ #include "SerialConnection.h" #include "includes.h" -// uncomment this line to use inline copy (fast code) instead of memcpy() (less code) -#define USE_INLINE_COPY - -#define SERIAL_PORT_RX_QUEUE_SIZE NOBEYOND(ACK_CACHE_SIZE, RAM_SIZE * 64, 4096) // ACK messages reading queue from mainboard +#define SERIAL_PORT_RX_QUEUE_SIZE NOBEYOND(256, RAM_SIZE * 64, 4096) // ACK messages reading queue from mainboard #define SERIAL_PORT_2_RX_QUEUE_SIZE 512 #define SERIAL_PORT_3_RX_QUEUE_SIZE 512 #define SERIAL_PORT_4_RX_QUEUE_SIZE 512 #define SERIAL_PORT_TX_QUEUE_SIZE 256 // gcodes writing queue to mainboard -#define SERIAL_PORT_2_TX_QUEUE_SIZE 128 -#define SERIAL_PORT_3_TX_QUEUE_SIZE 128 -#define SERIAL_PORT_4_TX_QUEUE_SIZE 128 +#define SERIAL_PORT_2_TX_QUEUE_SIZE 256 +#define SERIAL_PORT_3_TX_QUEUE_SIZE 256 +#define SERIAL_PORT_4_TX_QUEUE_SIZE 256 const SERIAL_PORT_INFO serialPort[SERIAL_PORT_COUNT] = { {SERIAL_PORT , SERIAL_PORT_RX_QUEUE_SIZE , SERIAL_PORT_TX_QUEUE_SIZE , "" , "1 - Printer"}, @@ -141,7 +138,7 @@ void Serial_Forward(SERIAL_PORT_INDEX portIndex, const char * msg) } } -uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize) +bool Serial_NewDataAvailable(uint8_t port) { // NOTE: used 32 bit variables for performance reasons (in particular for data copy) @@ -161,18 +158,23 @@ uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize) flag = (flag + 1) % cacheSize; } - if (flag == wIndex) // if "\n" was not found (message incomplete), update flag and exit - { - dmaL1DataRX[port].flag = flag; // update queue's custom flag with flag (also equal to wIndex) + dmaL1DataRX[port].flag = flag; // update queue's custom flag with flag - return 0; - } + // return "true" if "\n" was found (message complete), "False" otherwise + return (flag != wIndex); +} + +uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize) +{ + // NOTE: used 32 bit variables for performance reasons (in particular for data copy) // rIndex: L1 cache's reading index (not dynamically changed (by L1 cache's interrupt handler) variables/attributes) // DMA_CIRCULAR_BUFFER * dmaL1Data_ptr = &dmaL1DataRX[port]; char * cache = dmaL1Data_ptr->cache; + uint32_t cacheSize = dmaL1Data_ptr->cacheSize; uint32_t rIndex = dmaL1Data_ptr->rIndex; + uint32_t flag = dmaL1Data_ptr->flag; while (cache[rIndex] == ' ' && rIndex != flag) // remove leading empty space, if any { @@ -193,20 +195,14 @@ uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize) // if data is one chunk only, retrieve data from upper part of circular cache if (rIndex <= flag) { - #ifdef USE_INLINE_COPY while (rIndex <= flag) { *(buf++) = cache[rIndex++]; } - #else - memcpy(buf, &cache[rIndex], msgSize); - buf += msgSize; - #endif } else // data at end and beginning of cache { - #ifdef USE_INLINE_COPY - while (rIndex <= cacheSize) + while (rIndex < cacheSize) { *(buf++) = cache[rIndex++]; } @@ -217,13 +213,6 @@ uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize) { *(buf++) = cache[rIndex++]; } - #else - memcpy(buf, &cache[rIndex], cacheSize - rIndex); - buf += cacheSize - rIndex; - - memcpy(buf, cache, flag + 1); - buf += flag; - #endif } *buf = '\0'; // end character @@ -250,7 +239,7 @@ void Serial_GetFromUART(void) #endif ) { - while (Serial_Get(serialPort[portIndex].port, cmd, CMD_MAX_SIZE) != 0) // if some data have been retrieved + while (Serial_NewDataAvailable(serialPort[portIndex].port) && Serial_Get(serialPort[portIndex].port, cmd, CMD_MAX_SIZE) != 0) { handleCmd(cmd, portIndex); } diff --git a/TFT/src/User/API/SerialConnection.h b/TFT/src/User/API/SerialConnection.h index 5e2ede44a2..c988e3a175 100644 --- a/TFT/src/User/API/SerialConnection.h +++ b/TFT/src/User/API/SerialConnection.h @@ -10,10 +10,6 @@ extern "C" { #include "variants.h" // for SERIAL_PORT_2 etc. #include "uart.h" // for _UART_CNT etc. -// size of buffer where read ACK messages are stored (including terminating null character '\0'). -// Use a power of 2 for performance reasons! -#define ACK_CACHE_SIZE NOBEYOND(256, 512, 512) - #define BAUDRATE_COUNT 10 typedef enum @@ -60,7 +56,7 @@ void Serial_Init(SERIAL_PORT_INDEX portIndex); // - specific port index: specific serial port void Serial_DeInit(SERIAL_PORT_INDEX portIndex); -// forward a message to the provided serial port/s, if enabled: +// forward a zero terminated message to the provided serial port/s, if enabled: // - portIndex: // - ALL_PORTS: all serial ports (primary and supplementary) // - SUP_PORTS: all supplementary serial ports @@ -68,6 +64,12 @@ void Serial_DeInit(SERIAL_PORT_INDEX portIndex); // - msg: message to send void Serial_Forward(SERIAL_PORT_INDEX portIndex, const char * msg); +// test if a new message is available in the message queue of the provided physical serial port: +// - port: physical serial port where data availability is tested +// +// - return value: "true" if a new message is available. "false" otherwise +bool Serial_NewDataAvailable(uint8_t port); + // retrieve a message from the provided physical serial port: // - port: physical serial port where data are read from // - buf: buffer where data are stored diff --git a/TFT/src/User/API/parseACK.c b/TFT/src/User/API/parseACK.c index bf07e95ac8..e3c8e9b72d 100644 --- a/TFT/src/User/API/parseACK.c +++ b/TFT/src/User/API/parseACK.c @@ -43,6 +43,9 @@ const char magic_echo[] = "echo:"; const char magic_warning[] = "Warning:"; // RRF warning const char magic_message[] = "message"; // RRF message in Json format +// size of buffer where read ACK messages are stored (including terminating null character '\0') +#define ACK_CACHE_SIZE 512 + char ack_cache[ACK_CACHE_SIZE]; // buffer where read ACK messages are stored uint16_t ack_len; // length of data present in ack_cache without the terminating null character '\0' uint16_t ack_index; @@ -356,7 +359,7 @@ void hostActionCommands(void) void parseACK(void) { - while ((ack_len = Serial_Get(SERIAL_PORT, ack_cache, ACK_CACHE_SIZE)) != 0) // if some data have been retrieved + while (Serial_NewDataAvailable(SERIAL_PORT) && (ack_len = Serial_Get(SERIAL_PORT, ack_cache, ACK_CACHE_SIZE)) != 0) // if some data have been retrieved { UPD_RX_KPIS(ack_len); // debug monitoring KPI diff --git a/TFT/src/User/Hal/stm32f10x/Serial.c b/TFT/src/User/Hal/stm32f10x/Serial.c index ef5a554dda..74f142b65a 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.c +++ b/TFT/src/User/Hal/stm32f10x/Serial.c @@ -4,48 +4,80 @@ // set this line to "true" to enable IDLE Line interrupt. It is no more needed, so always set this macro to "false" #define IDLE_LINE_IT false -// uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used -//#define TX_DMA_WRITE - -// uncomment this line to use compact code (less code) instead of fast code -#define USE_COMPACT_CODE - -#ifndef USE_COMPACT_CODE - // uncomment this line to use inline copy (fast code) instead of memcpy() (less code) - #define USE_INLINE_COPY -#endif - -//#define DEFAULT_WRITE // old unbuffered TX serial writing (just for comparison with new TX solutions) - -DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer -DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer - -// config for USART channel -const SERIAL_CFG Serial[_UART_CNT] = { +DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA / interrupt RX buffer +DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA / interrupt TX buffer + +// config for USART DMA channels +const SERIAL_CFG Serial[_UART_CNT] = { // RM0008 Table 78-79 +#ifdef TX_DMA_WRITE +// USART TCC DMA Chan RX DMA Chan TX + {USART1, RCC_AHBPeriph_DMA1, DMA1_Channel5, DMA1_Channel4}, + {USART2, RCC_AHBPeriph_DMA1, DMA1_Channel6, DMA1_Channel7}, + {USART3, RCC_AHBPeriph_DMA1, DMA1_Channel3, DMA1_Channel2}, + {UART4, RCC_AHBPeriph_DMA2, DMA2_Channel3, DMA2_Channel5}, + {UART5, -1, -1, -1, }, // UART5 don't support DMA +#else +// USART TCC DMA Chan RX {USART1, RCC_AHBPeriph_DMA1, DMA1_Channel5}, {USART2, RCC_AHBPeriph_DMA1, DMA1_Channel6}, {USART3, RCC_AHBPeriph_DMA1, DMA1_Channel3}, {UART4, RCC_AHBPeriph_DMA2, DMA2_Channel3}, - //{UART5, -1, -1}, // UART5 don't support DMA + {UART5, -1, -1 }, // UART5 don't support DMA +#endif }; -void Serial_DMA_Config(uint8_t port) +void Serial_DMAConfig(uint8_t port) { const SERIAL_CFG * cfg = &Serial[port]; - RCC_AHBPeriphClockCmd(cfg->dma_rcc, ENABLE); // DMA RCC EN + RCC_AHBPeriphClockCmd(cfg->dma_rcc, ENABLE); // enable DMA clock + + cfg->uart->CR3 |= (1<<6); // DMA enable receiver (DMAR) + + cfg->dma_channelRX->CCR &= ~(1<<0); // RX disable DMA + + cfg->dma_channelRX->CPAR = (uint32_t)(&cfg->uart->DR); // RX peripheral address (usart) + cfg->dma_channelRX->CMAR = (uint32_t)(dmaL1DataRX[port].cache); // RX destination address (memory) + cfg->dma_channelRX->CNDTR = dmaL1DataRX[port].cacheSize; // RX buffer size + cfg->dma_channelRX->CCR = 0; // RX clear control register + + // primary serial port priority at highest level (TX higher than RX) + if (port == SERIAL_PORT) + cfg->dma_channelRX->CCR |= (2<<12); // RX priority level: High + else + cfg->dma_channelRX->CCR |= (0<<12); // RX priority level: Low + +//cfg->dma_channelRX->CCR |= (0<<10); // RX memory data size: 8 bit +//cfg->dma_channelRX->CCR |= (0<<8); // RX peripheral data size: 8 bit + cfg->dma_channelRX->CCR |= (1<<7); // RX memory increment mode +//cfg->dma_channelRX->CCR |= (0<<6); // RX peripheral no increment mode + cfg->dma_channelRX->CCR |= (1<<5); // RX circular mode enabled + + cfg->dma_channelRX->CCR |= (1<<0); // RX enable RX DMA + +#ifdef TX_DMA_WRITE // TX DMA based serial writing + cfg->uart->CR3 |= (3<<6); // DMA enable transmitter (DMAT) and receiver (DMAR) - cfg->dma_chanel->CCR &= ~(1<<0); // DMA disable - cfg->uart->CR3 |= 1<<6; // DMA enable receiver + cfg->dma_channelTX->CCR &= ~(1<<0); // TX disable DMA - cfg->dma_chanel->CPAR = (uint32_t)(&cfg->uart->DR); - cfg->dma_chanel->CMAR = (uint32_t)(dmaL1DataRX[port].cache); - cfg->dma_chanel->CNDTR = dmaL1DataRX[port].cacheSize; - cfg->dma_chanel->CCR = 0X00000000; - cfg->dma_chanel->CCR |= 3<<12; // Channel priority level - cfg->dma_chanel->CCR |= 1<<7; // Memory increment mode - cfg->dma_chanel->CCR |= 1<<5; // Circular mode enabled - cfg->dma_chanel->CCR |= 1<<0; // DMA EN + cfg->dma_channelTX->CPAR = (uint32_t)(&cfg->uart->DR); // TX peripheral address (usart) + cfg->dma_channelTX->CCR = 0; // TX clear control register + + // primary serial port priority at highest level (TX higher than RX) + if (port == SERIAL_PORT) + cfg->dma_channelTX->CCR |= (3<<12); // TX priority level: Very high + else + cfg->dma_channelTX->CCR |= (1<<12); // TX priority level: Medium + +//cfg->dma_channelTX->CCR |= (0<<10); // TX memory data size: 8 bit +//cfg->dma_channelTX->CCR |= (0<<8); // TX peripheral data size: 8 bit + cfg->dma_channelTX->CCR |= (1<<7); // TX memory increment mode +//cfg->dma_channelTX->CCR |= (0<<6); // TX peripheral no increment mode +//cfg->dma_channelTX->CCR |= (0<<5); // TX circular mode disabled + +// NOTE: it will be enabled later when needed +//cfg->dma_channelTX->CCR |= (1<<4); // TX enable TX DMA Transfer Complete interrupt +#endif } void Serial_ClearData(uint8_t port) @@ -67,65 +99,80 @@ void Serial_ClearData(uint8_t port) } } -void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate) +void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate) { Serial_ClearData(port); dmaL1DataRX[port].cacheSize = cacheSizeRX; dmaL1DataRX[port].cache = malloc(cacheSizeRX); - while (!dmaL1DataRX[port].cache); // malloc failed, blocking! + while (!dmaL1DataRX[port].cache); // RX malloc failed, blocking! dmaL1DataTX[port].cacheSize = cacheSizeTX; dmaL1DataTX[port].cache = malloc(cacheSizeTX); - while (!dmaL1DataTX[port].cache); // malloc failed, blocking! + while (!dmaL1DataTX[port].cache); // TX malloc failed, blocking! UART_Config(port, baudrate, USART_IT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt - Serial_DMA_Config(port); + + Serial_DMAConfig(port); } void Serial_DeConfig(uint8_t port) { Serial_ClearData(port); - Serial[port].dma_chanel->CCR &= ~(1<<0); // disable DMA + Serial[port].dma_channelRX->CCR &= ~(1<<0); // disable RX DMA + +#ifdef TX_DMA_WRITE + Serial[port].dma_channelTX->CCR &= ~(1<<0); // disable TX DMA +#endif + UART_DeConfig(port); } -#ifdef DEFAULT_WRITE // unbuffered TX serial writing +#ifdef TX_DMA_WRITE // TX DMA based serial writing -void Serial_PutChar(uint8_t port, const char ch) +// DMA serial write support function +void Serial_Send_TX(uint8_t port) { - while ((Serial[port].uart->SR & USART_SR_TC) == (uint16_t)RESET); - Serial[port].uart->DR = (uint8_t) ch; + // setup DMA transfer, and wait for serial Transfer Complete (TX) interrupt in ISR + if (dmaL1DataTX[port].wIndex >= dmaL1DataTX[port].rIndex) + dmaL1DataTX[port].flag = dmaL1DataTX[port].wIndex - dmaL1DataTX[port].rIndex; + else + dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts + + Serial[port].dma_channelTX->CCR &= ~(1<<0); // disable TX DMA + Serial[port].dma_channelTX->CMAR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // TX data start address + Serial[port].dma_channelTX->CNDTR = dmaL1DataTX[port].flag; // number of bytes to transfer + Serial[port].uart->CR1 |= USART_CR1_TCIE; // enable Transfer Complete (TC) serial interrupt + Serial[port].dma_channelTX->CCR |= (1<<0); // enable TX DMA } void Serial_Put(uint8_t port, const char * msg) { while (*msg) { - while ((Serial[port].uart->SR & USART_SR_TC) == (uint16_t)RESET); - Serial[port].uart->DR = ((uint16_t)*msg++ & (uint16_t)0x01FF); - } -} + // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) + if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && + ((Serial[port].uart->CR1 & USART_CR1_TCIE) == 0)) + Serial_Send_TX(port); -#else // use TX interrupt based serial writing + // blocking! wait for buffer to become available + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { } -void Serial_PutChar(uint8_t port, const char ch) -{ - // wait for enough free buffer in TX cache to store all data - while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) - { - } + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg; // copy character to cache + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex - dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; - dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; + if ((*msg == '\n') && ((Serial[port].uart->CR1 & USART_CR1_TCIE) == 0)) + Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already - Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer + msg++; // let the compiler optimize this, no need to do it manually! + } } +#else // TX interrupt based serial writing + void Serial_Put(uint8_t port, const char * msg) { -#ifdef USE_COMPACT_CODE // less code while (*msg) { // blocking! wait for buffer to become available @@ -136,75 +183,6 @@ void Serial_Put(uint8_t port, const char * msg) Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer } -#else // fast code - // NOTE: used 32 bit variables for performance reasons (in particular for data copy) - - DMA_CIRCULAR_BUFFER * dmaL1Data_ptr = &dmaL1DataTX[port]; - char * cache = dmaL1Data_ptr->cache; - uint32_t cacheSize = dmaL1Data_ptr->cacheSize; - uint32_t wIndex = dmaL1Data_ptr->wIndex; - uint32_t msgSize = strlen(msg); - - // if cache size is not enough to store the data, skip the data copy - // - // NOTE: the following check should never be matched if cache has a proper size. - // If so, the check could be commented out just to improve performance. Just keep it to make the code more robust - // - if ((cacheSize - 1) < msgSize) - return; - - // wait for enough free buffer in TX cache to store all data. - // Used dmaL1Data_ptr->rIndex dynamically changed by TX cache's interrupt handler - // - // NOTE: -1 is needed because full cache usage will introduce a conflict on rIndex and wIndex - // (wIndex == rIndex will indicate an empty cache or a full cache) - // - while ((((cacheSize - 1) - wIndex + dmaL1Data_ptr->rIndex) % cacheSize) < msgSize) - { - } - - uint32_t maxIndex; - - // if data is one chunk only, store data on upper part of circular cache - if ((cacheSize - wIndex) >= msgSize) - { - #ifdef USE_INLINE_COPY - maxIndex = wIndex + msgSize; - - while (wIndex < maxIndex) - { - cache[wIndex++] = *(msg++); - } - #else - memcpy(&cache[wIndex], msg, msgSize); - #endif - } - else // data at end and beginning of cache - { - #ifdef USE_INLINE_COPY - while (wIndex < cacheSize) - { - cache[wIndex++] = *(msg++); - } - - wIndex = 0; - maxIndex = msgSize - (cacheSize - dmaL1Data_ptr->wIndex); // used dmaL1Data_ptr->wIndex and not wIndex - - while (wIndex < maxIndex) - { - cache[wIndex++] = *(msg++); - } - #else - memcpy(&cache[wIndex], msg, cacheSize - wIndex); - memcpy(cache, &msg[cacheSize - wIndex], msgSize - (cacheSize - wIndex)); - #endif - } - - // update queue's writing index with next index - dmaL1Data_ptr->wIndex = (dmaL1Data_ptr->wIndex + msgSize) % cacheSize; - - Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer -#endif // USE_COMPACT_CODE } #endif @@ -218,13 +196,32 @@ void USART_IRQHandler(uint8_t port) Serial[port].uart->SR; // clear IDLE Line bit Serial[port].uart->DR; - dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - Serial[port].dma_chanel->CNDTR; + dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - Serial[port].dma_channelRX->CNDTR; } #endif -#ifndef DEFAULT_WRITE // TX interrupt based serial writing +#ifdef TX_DMA_WRITE // TX DMA based serial writing + if ((Serial[port].uart->SR & USART_SR_TC) != RESET) // check for Transfer Complete (TC) interrupt + { + Serial[port].uart->SR &= ~USART_SR_TC; // clear Transfer Complete (TC) bit + + // NOTE 1: use the serial TC, not the DMA TC because this only indicates DMA is done, peripheral might be still busy + // NOTE 2: the TC interrupt is sometimes called while DMA is still active, so check NDTR status! + // + if (Serial[port].dma_channelTX->CNDTR == 0) // sending is complete + { + dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + dmaL1DataTX[port].flag) % dmaL1DataTX[port].cacheSize; + dmaL1DataTX[port].flag = 0; - if ((Serial[port].uart->SR & USART_SR_TXE) != 0) // check for TXE interrupt + if (dmaL1DataTX[port].wIndex != dmaL1DataTX[port].rIndex) // is more data available? + Serial_Send_TX(port); // continue sending data + else + Serial[port].uart->CR1 &= ~USART_CR1_TCIE; // disable Transfer Complete (TC) interrupt, nothing more to do + } + // else: more data is coming, wait for next Transfer Complete (TC) interrupt + } +#else // TX interrupt based serial writing + if ((Serial[port].uart->SR & USART_SR_TXE) != RESET) // check for TXE interrupt { if (dmaL1DataTX[port].rIndex == dmaL1DataTX[port].wIndex) // no more data? { @@ -237,8 +234,7 @@ void USART_IRQHandler(uint8_t port) dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + 1) % dmaL1DataTX[port].cacheSize; // increase reading index } } - -#endif // DEFAULT_WRITE +#endif } void USART1_IRQHandler(void) diff --git a/TFT/src/User/Hal/stm32f10x/Serial.h b/TFT/src/User/Hal/stm32f10x/Serial.h index 7efd74e655..58395389f2 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.h +++ b/TFT/src/User/Hal/stm32f10x/Serial.h @@ -5,51 +5,57 @@ #include "variants.h" // for uint32_t etc... #include "uart.h" +// uncomment this line to use TX DMA based serial writing. Otherwise, TX interrupt based serial writing will be used +//#define TX_DMA_WRITE + typedef struct { char * cache; - volatile uint16_t wIndex; // writing index. Precautionally declared as volatile due to access from interrupt handler and main thread - volatile uint16_t rIndex; // reading index. Precautionally declared as volatile due to access from interrupt handler and main thread - uint16_t flag; // custom flag (for custom usage by the application) - uint16_t cacheSize; + uint32_t cacheSize; + volatile uint32_t wIndex; // writing index + volatile uint32_t rIndex; // reading index + volatile uint32_t flag; // custom flag (for custom usage by the application) } DMA_CIRCULAR_BUFFER; +// config for USART DMA channels typedef struct { - USART_TypeDef * uart; + USART_TypeDef * uart; // uint32_t uint32_t dma_rcc; - DMA_Channel_TypeDef * dma_chanel; + DMA_Channel_TypeDef * dma_channelRX; +#ifdef TX_DMA_WRITE + DMA_Channel_TypeDef * dma_channelTX; +#endif } SERIAL_CFG; extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; extern const SERIAL_CFG Serial[_UART_CNT]; -void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); +void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); -// retrieve the next reading index in the RX message queue of the provided serial port: -// - port: index of serial port +// retrieve the next reading index in the RX message queue of the provided physical serial port: +// - port: physical serial port // // - return value: next reading index -static inline uint16_t Serial_GetReadingIndexRX(uint8_t port) +static inline uint32_t Serial_GetReadingIndexRX(uint8_t port) { return dmaL1DataRX[port].rIndex; } -// retrieve the next writing index in the RX message queue of the provided serial port +// retrieve the next writing index in the RX message queue of the provided physical serial port // based on Interrupt/DMA status while writing serial data in the background: -// - port: index of serial port +// - port: physical serial port // // - return value: next writing index -static inline uint16_t Serial_GetWritingIndexRX(uint8_t port) +static inline uint32_t Serial_GetWritingIndexRX(uint8_t port) { - return dmaL1DataRX[port].cacheSize - Serial[port].dma_chanel->CNDTR; + return dmaL1DataRX[port].cacheSize - Serial[port].dma_channelRX->CNDTR; } -// send character to a uart port -void Serial_PutChar(uint8_t port, const char ch); - -// send a zero terminated message to uart port +// send a zero terminated message to UART port +// - port: index of serial port +// - msg: message to send void Serial_Put(uint8_t port, const char * msg); #endif diff --git a/TFT/src/User/Hal/stm32f10x/uart.h b/TFT/src/User/Hal/stm32f10x/uart.h index 28bac32834..3e481bdeb4 100644 --- a/TFT/src/User/Hal/stm32f10x/uart.h +++ b/TFT/src/User/Hal/stm32f10x/uart.h @@ -9,7 +9,7 @@ #define _USART3 2 #define _UART4 3 #define _UART5 4 // UART5 don't support DMA -#define _UART_CNT 6 +#define _UART_CNT 5 void UART_Config(uint8_t port, uint32_t baud, uint16_t usart_it, bool idle_interrupt); void UART_DeConfig(uint8_t port); diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c index 269a58a35e..34988ce97a 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c @@ -4,75 +4,32 @@ // set this line to "true" to enable IDLE Line interrupt. It is no more needed, so always set this macro to "false" #define IDLE_LINE_IT false -// uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used -#define TX_DMA_WRITE +DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA / interrupt RX buffer +DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA / interrupt TX buffer -// uncomment this line to use compact code (less code) instead of fast code -#define USE_COMPACT_CODE - -#ifndef USE_COMPACT_CODE - // uncomment this line to use inline copy (fast code) instead of memcpy() (less code) - #define USE_INLINE_COPY -#endif - -//#define DEFAULT_WRITE // old unbuffered TX serial writing (just for comparison with new TX solutions) - -DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer -DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer - -// config for USART channel +// config for USART DMA channels SERIAL_CFG Serial[_UART_CNT] = { -// USART DMA1 or DMA2 Channel RX Stream TX Stream TX Bytes - {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2, DMA2_Stream7, 0}, - {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5, DMA1_Stream6, 0}, - {USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1, DMA1_Stream3, 0}, - {UART4 , RCC_AHB1Periph_DMA1, 4, DMA1_Stream2, DMA1_Stream4, 0}, - {UART5 , RCC_AHB1Periph_DMA1, 4, DMA1_Stream0, DMA1_Stream7, 0}, - {USART6, RCC_AHB1Periph_DMA2, 5, DMA2_Stream1, DMA2_Stream6, 0}, +#ifdef TX_DMA_WRITE +// USART TCC DMAx Channel RX_STREAM TX_STREAM + {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2, DMA2_Stream7}, + {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5, DMA1_Stream6}, + {USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1, DMA1_Stream3}, + {UART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2, DMA1_Stream4}, + {UART5, RCC_AHB1Periph_DMA1, 4, DMA1_Stream0, DMA1_Stream7}, + {USART6, RCC_AHB1Periph_DMA2, 5, DMA2_Stream1, DMA2_Stream6}, +#else +// USART TCC DMAx Channel RX_STREAM + {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2}, + {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5}, + {USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1}, + {UART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2}, + {UART5, RCC_AHB1Periph_DMA1, 4, DMA1_Stream0}, + {USART6, RCC_AHB1Periph_DMA2, 5, DMA2_Stream1}, +#endif }; -// clear all DMA RX and TX interrupt flags for a serial port -void Serial_DMAClearITflags(uint8_t port) -{ - // Stream # to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 - // Stream # to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 - - switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) - { - case _USART1: - DMA2->LIFCR = (0x3F << 16); // DMA2_Stream2 low bits:16-21 Channel 4 - DMA2->HIFCR = (0x3F << 22); // DMA2_Stream7 high bits:22-27 Channel 4 - break; - - case _USART2: - DMA1->HIFCR = (0x3F << 6) | // DMA1_Stream5 high bits: 6-11 Channel 4 - (0x3F << 16); // DMA1_Stream6 high bits:16-21 Channel 4 - break; - - case _USART3: - DMA1->LIFCR = (0x3F << 6) | // DMA1_Stream1 low bits: 6-11 Channel 4 - (0x3F << 22); // DMA1_Stream3 low bits:22-27 Channel 4 - break; - - case _UART4: - DMA1->LIFCR = (0x3F << 16); // DMA1_Stream2 low bits:16-21 Channel 4 - DMA1->HIFCR = (0x3F << 0); // DMA1_Stream4 high bits: 0- 5 Channel 4 - break; - - case _UART5: - DMA1->LIFCR = (0x3F << 0); // DMA1_Stream0 low bits: 0- 5 Channel 4 - DMA1->HIFCR = (0x3F << 22); // DMA1_Stream7 high bits:22-27 Channel 4 - break; - - case _USART6: - DMA2->LIFCR = (0x3F << 6); // DMA2_Stream1 low bits: 6-11 Channel 5 - DMA2->HIFCR = (0x3F << 16); // DMA2_Stream6 high bits:16-21 Channel 5 - break; - } -} - -// clear all DMA RX interrupt flags for a given serial port -void Serial_DMAClearITflagsRX(uint8_t port) +// clear all DMA interrupts RX for a serial port +void Serial_DMAClearInterruptFlagsRX(uint8_t port) { // Stream # to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 // Stream # to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 @@ -105,8 +62,8 @@ void Serial_DMAClearITflagsRX(uint8_t port) } } -// clear all DMA TX interrupt flags for a given serial port -void Serial_DMAClearITflagsTX(uint8_t port) +// clear all DMA interrupts TX for a serial port +void Serial_DMAClearInterruptFlagsTX(uint8_t port) { // Stream # to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 // Stream # to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 @@ -139,60 +96,60 @@ void Serial_DMAClearITflagsTX(uint8_t port) } } -void Serial_DMA_Config(uint8_t port) +void Serial_DMAConfig(uint8_t port) { const SERIAL_CFG * cfg = &Serial[port]; - RCC_AHB1PeriphClockCmd(cfg->dma_rcc, ENABLE); // DMA RCC enable - - cfg->dma_streamRX->CR &= ~(1<<0); // disable RX DMA - cfg->dma_streamTX->CR &= ~(1<<0); // disable TX DMA + RCC_AHB1PeriphClockCmd(cfg->dma_rcc, ENABLE); // enable DMA clock - Serial_DMAClearITflags(port); // clear DMA RX-TX interrupt flags + cfg->uart->CR3 |= (1<<6); // DMA enable receiver (DMAR) - cfg->uart->CR3 |= 3<<6; // DMA enable transmitter (DMAT) and receiver (DMAR) + Serial_DMAClearInterruptFlagsRX(port); // clear DMA RX interrupt flags cfg->dma_streamRX->PAR = (uint32_t)(&cfg->uart->DR); // RX peripheral address (usart) - cfg->dma_streamTX->PAR = (uint32_t)(&cfg->uart->DR); // TX peripheral address (usart) - - cfg->dma_streamRX->M0AR = (uint32_t)(dmaL1DataRX[port].cache); // RX destination data (sram) + cfg->dma_streamRX->M0AR = (uint32_t)(dmaL1DataRX[port].cache); // RX destination address (memory) cfg->dma_streamRX->NDTR = dmaL1DataRX[port].cacheSize; // RX buffer size + cfg->dma_streamRX->CR = (cfg->dma_channel << 25); // RX channel selection, set to 0 all the other CR bits - cfg->dma_streamRX->CR = cfg->dma_channel << 25; // RX channel selection, set to 0 all the other CR bits - cfg->dma_streamTX->CR = cfg->dma_channel << 25; // TX channel selection, set to 0 all the other CR bits - + // primary serial port priority at highest level (TX higher than RX) if (port == SERIAL_PORT) - { // primary serial port priority at highest level (TX higher than RX) - cfg->dma_streamRX->CR |= 2<<16; // RX priority level: High - cfg->dma_streamTX->CR |= 3<<16; // TX priority level: Very high - } + cfg->dma_streamRX->CR |= (2<<16); // RX priority level: High else - { // secondary serial ports priority at medium level - cfg->dma_streamRX->CR |= 0<<16; // RX priority level: Low - cfg->dma_streamTX->CR |= 1<<16; // TX priority level: Medium - } + cfg->dma_streamRX->CR |= (0<<16); // RX priority level: Low -//cfg->dma_streamRX->CR &= ~(3<<13); // RX memory data size: 8 bit -//cfg->dma_streamTX->CR &= ~(3<<13); // TX memory data size: 8 bit +//cfg->dma_streamRX->CR &= ~(3<<13); // RX memory data size: 8 bit +//cfg->dma_streamRX->CR &= ~(3<<11); // RX peripheral data size: 8 bit + cfg->dma_streamRX->CR |= (1<<10); // RX memory increment mode +//cfg->dma_streamRX->CR &= ~(1<<9); // RX peripheral no increment mode + cfg->dma_streamRX->CR |= (1<<8); // RX circular mode enabled +//cfg->dma_streamRX->CR &= ~(1<<6); // RX data transfer direction: Peripheral-to-memory -//cfg->dma_streamRX->CR &= ~(3<<11); // RX peripheral data size: 8 bit -//cfg->dma_streamTX->CR &= ~(3<<11); // TX peripheral data size: 8 bit + cfg->dma_streamRX->CR |= (1<<0); // RX enable RX DMA - cfg->dma_streamRX->CR |= 1<<10; // RX memory increment mode - cfg->dma_streamTX->CR |= 1<<10; // TX memory increment mode +#ifdef TX_DMA_WRITE // TX DMA based serial writing + cfg->uart->CR3 |= (3<<6); // DMA enable transmitter (DMAT) and receiver (DMAR) -//cfg->dma_streamRX->CR &= ~(1<<9); // peripheral no increment mode -//cfg->dma_streamTX->CR &= ~(1<<9); // peripheral no increment mode + Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags - cfg->dma_streamRX->CR |= 1<<8; // RX circular mode enabled -//cfg->dma_streamTX->CR &= ~(1<<8); // TX circular mode disabled + cfg->dma_streamTX->PAR = (uint32_t)(&cfg->uart->DR); // TX peripheral address (usart) + cfg->dma_streamTX->CR = (cfg->dma_channel << 25); // TX channel selection, set to 0 all the other CR bits -//cfg->dma_streamRX->CR &= ~(1<<6); // RX data transfer direction: Peripheral-to-memory - cfg->dma_streamTX->CR |= 1<<6; // TX data transfer direction: Memory-to-Peripheral + // primary serial port priority at highest level (TX higher than RX) + if (port == SERIAL_PORT) + cfg->dma_streamTX->CR |= (3<<16); // TX priority level: Very high + else + cfg->dma_streamTX->CR |= (1<<16); // TX priority level: Medium -//cfg->dma_streamTX->CR |= 1<<4; // enable TX DMA Transfer Complete interrupt, done later when needed +//cfg->dma_streamTX->CR &= ~(3<<13); // TX memory data size: 8 bit +//cfg->dma_streamTX->CR &= ~(3<<11); // TX peripheral data size: 8 bit + cfg->dma_streamTX->CR |= (1<<10); // TX memory increment mode +//cfg->dma_streamTX->CR &= ~(1<<9); // TX peripheral no increment mode +//cfg->dma_streamTX->CR &= ~(1<<8); // TX circular mode disabled + cfg->dma_streamTX->CR |= (1<<6); // TX data transfer direction: Memory-to-Peripheral - cfg->dma_streamRX->CR |= 1<<0; // re-enable RX DMA +// NOTE: it will be enabled later when needed +//cfg->dma_streamTX->CR |= (1<<4); // TX enable TX DMA Transfer Complete interrupt +#endif } void Serial_ClearData(uint8_t port) @@ -212,24 +169,23 @@ void Serial_ClearData(uint8_t port) free(dmaL1DataTX[port].cache); dmaL1DataTX[port].cache = NULL; } - - Serial[port].txBytes = 0; } -void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate) +void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate) { Serial_ClearData(port); dmaL1DataRX[port].cacheSize = cacheSizeRX; dmaL1DataRX[port].cache = malloc(cacheSizeRX); - while (!dmaL1DataRX[port].cache); // malloc failed, blocking! + while (!dmaL1DataRX[port].cache); // RX malloc failed, blocking! dmaL1DataTX[port].cacheSize = cacheSizeTX; dmaL1DataTX[port].cache = malloc(cacheSizeTX); - while (!dmaL1DataTX[port].cache); // malloc failed, blocking! + while (!dmaL1DataTX[port].cache); // TX malloc failed, blocking! UART_Config(port, baudrate, USART_IT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt - Serial_DMA_Config(port); + + Serial_DMAConfig(port); } void Serial_DeConfig(uint8_t port) @@ -237,161 +193,33 @@ void Serial_DeConfig(uint8_t port) Serial_ClearData(port); Serial[port].dma_streamRX->CR &= ~(1<<0); // disable RX DMA + Serial_DMAClearInterruptFlagsRX(port); + +#ifdef TX_DMA_WRITE Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA + Serial_DMAClearInterruptFlagsTX(port); +#endif - Serial_DMAClearITflags(port); UART_DeConfig(port); } -#ifdef DEFAULT_WRITE // unbuffered TX serial writing - -void Serial_PutChar(uint8_t port, const char ch) -{ - while ((Serial[port].uart->SR & USART_SR_TC) == (uint16_t)RESET); - Serial[port].uart->DR = (uint8_t) ch; -} - -void Serial_Put(uint8_t port, const char * msg) -{ - while (*msg) - { - while ((Serial[port].uart->SR & USART_SR_TC) == (uint16_t)RESET); - Serial[port].uart->DR = ((uint16_t)*msg++ & (uint16_t)0x01FF); - } -} - -#elif !defined(TX_DMA_WRITE) // TX interrupt based serial writing - -void Serial_PutChar(uint8_t port, const char ch) -{ - // wait for enough free buffer in TX cache to store all data - while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) - { - } - - dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; - dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; - - Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer -} - -void Serial_Put(uint8_t port, const char * msg) -{ -#ifdef USE_COMPACT_CODE // less code - while (*msg) - { - // blocking! wait for buffer to become available - while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { }; - - dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg++; - dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; - - Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer - } -#else // fast code - // NOTE: used 32 bit variables for performance reasons (in particular for data copy) - - DMA_CIRCULAR_BUFFER * dmaL1Data_ptr = &dmaL1DataTX[port]; - char * cache = dmaL1Data_ptr->cache; - uint32_t cacheSize = dmaL1Data_ptr->cacheSize; - uint32_t wIndex = dmaL1Data_ptr->wIndex; - uint32_t msgSize = strlen(msg); - - // if cache size is not enough to store the data, skip the data copy - // - // NOTE: the following check should never be matched if cache has a proper size. - // If so, the check could be commented out just to improve performance. Just keep it to make the code more robust - // - if ((cacheSize - 1) < msgSize) - return; - - // wait for enough free buffer in TX cache to store all data. - // Used dmaL1Data_ptr->rIndex dynamically changed by TX cache's interrupt handler - // - // NOTE: -1 is needed because full cache usage will introduce a conflict on rIndex and wIndex - // (wIndex == rIndex will indicate an empty cache or a full cache) - // - while ((((cacheSize - 1) - wIndex + dmaL1Data_ptr->rIndex) % cacheSize) < msgSize) - { - } - - uint32_t maxIndex; - - // if data is one chunk only, store data on upper part of circular cache - if ((cacheSize - wIndex) >= msgSize) - { - #ifdef USE_INLINE_COPY - maxIndex = wIndex + msgSize; - - while (wIndex < maxIndex) - { - cache[wIndex++] = *(msg++); - } - #else - memcpy(&cache[wIndex], msg, msgSize); - #endif - } - else // data at end and beginning of cache - { - #ifdef USE_INLINE_COPY - while (wIndex < cacheSize) - { - cache[wIndex++] = *(msg++); - } - - wIndex = 0; - maxIndex = msgSize - (cacheSize - dmaL1Data_ptr->wIndex); // used dmaL1Data_ptr->wIndex and not wIndex - - while (wIndex < maxIndex) - { - cache[wIndex++] = *(msg++); - } - #else - memcpy(&cache[wIndex], msg, cacheSize - wIndex); - memcpy(cache, &msg[cacheSize - wIndex], msgSize - (cacheSize - wIndex)); - #endif - } - - // update queue's writing index with next index - dmaL1Data_ptr->wIndex = (dmaL1Data_ptr->wIndex + msgSize) % cacheSize; - - Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer -#endif // USE_COMPACT_CODE -} - -#else // TX DMA based serial writing +#ifdef TX_DMA_WRITE // TX DMA based serial writing -// DMA Serial Write support function +// DMA serial write support function void Serial_Send_TX(uint8_t port) { // setup DMA transfer, and wait for serial Transfer Complete (TX) interrupt in ISR if (dmaL1DataTX[port].wIndex >= dmaL1DataTX[port].rIndex) - Serial[port].txBytes = dmaL1DataTX[port].wIndex - dmaL1DataTX[port].rIndex; + dmaL1DataTX[port].flag = dmaL1DataTX[port].wIndex - dmaL1DataTX[port].rIndex; else - Serial[port].txBytes = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts - - Serial[port].dma_streamTX->M0AR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // start address TX data - Serial_DMAClearITflagsTX(port); // clear DMA TX interrupt flags - Serial[port].uart->CR1 |= USART_CR1_TCIE; // enable Transfer Complete (TX) serial Interrupt - Serial[port].dma_streamTX->NDTR = Serial[port].txBytes; // no. bytes to transfer - Serial[port].dma_streamTX->CR |= 1<<0; // enable TX DMA -} - -void Serial_PutChar(uint8_t port, const char ch) -{ - // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) - if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && - ((Serial[port].uart->CR1 & USART_CR1_TCIE) == 0)) - Serial_Send_TX(port); - - // wait until space becomes available, blocking! - while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { } - - dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = ch; // copy character to cache - dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex - - if ((ch == '\n') && ((Serial[port].uart->CR1 & USART_CR1_TCIE) == 0)) - Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already + dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts + + Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA + Serial[port].dma_streamTX->M0AR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // TX data start address + Serial[port].dma_streamTX->NDTR = dmaL1DataTX[port].flag; // number of bytes to transfer + Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags + Serial[port].uart->CR1 |= USART_CR1_TCIE; // enable Transfer Complete (TC) serial interrupt + Serial[port].dma_streamTX->CR |= (1<<0); // enable TX DMA } void Serial_Put(uint8_t port, const char * msg) @@ -403,7 +231,7 @@ void Serial_Put(uint8_t port, const char * msg) ((Serial[port].uart->CR1 & USART_CR1_TCIE) == 0)) Serial_Send_TX(port); - // wait until space becomes available, blocking! + // blocking! wait for buffer to become available while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { } dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg; // copy character to cache @@ -416,6 +244,22 @@ void Serial_Put(uint8_t port, const char * msg) } } +#else // TX interrupt based serial writing + +void Serial_Put(uint8_t port, const char * msg) +{ + while (*msg) + { + // blocking! wait for buffer to become available + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { }; + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg++; + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; + + Serial[port].uart->CR1 |= USART_CR1_TXEIE; // set TXE interrupt bit to start the serial transfer + } +} + #endif // ISR, serial interrupt handler @@ -431,11 +275,28 @@ void USART_IRQHandler(uint8_t port) } #endif -#ifndef DEFAULT_WRITE +#ifdef TX_DMA_WRITE // TX DMA based serial writing + if ((Serial[port].uart->SR & USART_SR_TC) != RESET) // check for Transfer Complete (TC) interrupt + { + Serial[port].uart->SR &= ~USART_SR_TC; // clear Transfer Complete (TC) bit -#ifndef TX_DMA_WRITE // TX interrupt based serial writing + // NOTE 1: use the serial TC, not the DMA TC because this only indicates DMA is done, peripheral might be still busy + // NOTE 2: the TC interrupt is sometimes called while DMA is still active, so check NDTR status! + // + if (Serial[port].dma_streamTX->NDTR == 0) // sending is complete + { + dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + dmaL1DataTX[port].flag) % dmaL1DataTX[port].cacheSize; + dmaL1DataTX[port].flag = 0; - if ((Serial[port].uart->SR & USART_SR_TXE) != 0) // check for TXE interrupt + if (dmaL1DataTX[port].wIndex != dmaL1DataTX[port].rIndex) // is more data available? + Serial_Send_TX(port); // continue sending data + else + Serial[port].uart->CR1 &= ~USART_CR1_TCIE; // disable Transfer Complete (TC) interrupt, nothing more to do + } + // else: more data is coming, wait for next Transfer Complete (TC) interrupt + } +#else // TX interrupt based serial writing + if ((Serial[port].uart->SR & USART_SR_TXE) != RESET) // check for TXE interrupt { if (dmaL1DataTX[port].rIndex == dmaL1DataTX[port].wIndex) // no more data? { @@ -448,31 +309,7 @@ void USART_IRQHandler(uint8_t port) dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + 1) % dmaL1DataTX[port].cacheSize; // increase reading index } } - -#else // TX DMA based serial writing - - if ((Serial[port].uart->SR & USART_SR_TC) != 0) // check for Transfer Complete (TC) interrupt - { - Serial[port].uart->SR &= ~USART_SR_TC; // clear Transfer Complete (TC) bit - - // NOTE: the ISR is sometimes called while DMA is still active, so check NDTR status! - // - if (Serial[port].dma_streamTX->NDTR == 0) // sending is complete - { - dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + Serial[port].txBytes) % dmaL1DataTX[port].cacheSize; - Serial[port].txBytes = 0; - - if (dmaL1DataTX[port].wIndex != dmaL1DataTX[port].rIndex) // is more data available? - Serial_Send_TX(port); // continue sending data - else // removed causes double line transfers - Serial[port].uart->CR1 &= ~USART_CR1_TCIE; // disable Transfer Complete (TC) interrupt, nothing more to do - } - // else: more data is coming, wait for next Transfer Complete (TC) interrupt - } - -#endif // TX_DMA_WRITE - -#endif // DEFAULT_WRITE +#endif } void USART1_IRQHandler(void) diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h index 36f6665d44..7de1cf6c2e 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h @@ -5,54 +5,58 @@ #include "variants.h" // for uint32_t etc... #include "uart.h" +// uncomment this line to use TX DMA based serial writing. Otherwise, TX interrupt based serial writing will be used +#define TX_DMA_WRITE + typedef struct { char * cache; - volatile uint16_t wIndex; // writing index. Precautionally declared as volatile due to access from interrupt handler and main thread - volatile uint16_t rIndex; // reading index. Precautionally declared as volatile due to access from interrupt handler and main thread - uint16_t flag; // custom flag (for custom usage by the application) - uint16_t cacheSize; + uint32_t cacheSize; + volatile uint32_t wIndex; // writing index + volatile uint32_t rIndex; // reading index + volatile uint32_t flag; // custom flag (for custom usage by the application) } DMA_CIRCULAR_BUFFER; +// config for USART DMA channels typedef struct { - USART_TypeDef * uart; + USART_TypeDef * uart; // uint32_t uint32_t dma_rcc; - uint8_t dma_channel; - DMA_Stream_TypeDef * dma_streamRX; - DMA_Stream_TypeDef * dma_streamTX; - volatile uint32_t txBytes; + uint32_t dma_channel; + DMA_Stream_TypeDef * dma_streamRX; // uint32_t +#ifdef TX_DMA_WRITE + DMA_Stream_TypeDef * dma_streamTX; // uint32_t +#endif } SERIAL_CFG; extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; extern SERIAL_CFG Serial[_UART_CNT]; -void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); +void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); -// retrieve the next reading index in the RX message queue of the provided serial port: -// - port: index of serial port +// retrieve the next reading index in the RX message queue of the provided physical serial port: +// - port: physical serial port // // - return value: next reading index -static inline uint16_t Serial_GetReadingIndexRX(uint8_t port) +static inline uint32_t Serial_GetReadingIndexRX(uint8_t port) { return dmaL1DataRX[port].rIndex; } -// retrieve the next writing index in the RX message queue of the provided serial port +// retrieve the next writing index in the RX message queue of the provided physical serial port // based on Interrupt/DMA status while writing serial data in the background: -// - port: index of serial port +// - port: physical serial port // // - return value: next writing index -static inline uint16_t Serial_GetWritingIndexRX(uint8_t port) +static inline uint32_t Serial_GetWritingIndexRX(uint8_t port) { return dmaL1DataRX[port].cacheSize - Serial[port].dma_streamRX->NDTR; } -// send character to a uart port -void Serial_PutChar(uint8_t port, const char ch); - -// send a zero terminated message to uart port +// send a zero terminated message to UART port +// - port: index of serial port +// - msg: message to send void Serial_Put(uint8_t port, const char * msg); #endif From 5e28f0c1fde85c7f3720f48a4f78122f079a30c1 Mon Sep 17 00:00:00 2001 From: digant73 Date: Fri, 29 Sep 2023 18:28:59 +0200 Subject: [PATCH 13/20] cleanup on Serial.c/h for STM32F2 and STM32F1 --- TFT/src/User/Hal/stm32f10x/Serial.c | 79 ++++++++++++++++++++------ TFT/src/User/Hal/stm32f10x/Serial.h | 3 +- TFT/src/User/Hal/stm32f2_f4xx/Serial.c | 35 ++++++++---- 3 files changed, 88 insertions(+), 29 deletions(-) diff --git a/TFT/src/User/Hal/stm32f10x/Serial.c b/TFT/src/User/Hal/stm32f10x/Serial.c index 74f142b65a..9877908f61 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.c +++ b/TFT/src/User/Hal/stm32f10x/Serial.c @@ -4,35 +4,76 @@ // set this line to "true" to enable IDLE Line interrupt. It is no more needed, so always set this macro to "false" #define IDLE_LINE_IT false +// DMA registers: +// +// CCR DMA stream x configuration register +// CNDTR DMA stream x number of data register +// CPAR DMA stream x peripheral address register +// CMAR DMA stream x memory 0 address register +// +// ISR DMA low interrupt status register +// ISR DMA high interrupt status register +// IFCR DMA low interrupt flag clear register +// IFCR DMA high interrupt flag clear register + DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA / interrupt RX buffer DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA / interrupt TX buffer // config for USART DMA channels const SERIAL_CFG Serial[_UART_CNT] = { // RM0008 Table 78-79 #ifdef TX_DMA_WRITE -// USART TCC DMA Chan RX DMA Chan TX - {USART1, RCC_AHBPeriph_DMA1, DMA1_Channel5, DMA1_Channel4}, - {USART2, RCC_AHBPeriph_DMA1, DMA1_Channel6, DMA1_Channel7}, - {USART3, RCC_AHBPeriph_DMA1, DMA1_Channel3, DMA1_Channel2}, - {UART4, RCC_AHBPeriph_DMA2, DMA2_Channel3, DMA2_Channel5}, - {UART5, -1, -1, -1, }, // UART5 don't support DMA +// USART TCC DMAx DMAx RX Channel TX Channel + {USART1, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel5, DMA1_Channel4}, + {USART2, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel6, DMA1_Channel7}, + {USART3, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel3, DMA1_Channel2}, + {UART4, RCC_AHBPeriph_DMA2, DMA2, DMA2_Channel3, DMA2_Channel5}, + {UART5, -1, -1, -1, -1 }, // UART5 don't support DMA #else -// USART TCC DMA Chan RX - {USART1, RCC_AHBPeriph_DMA1, DMA1_Channel5}, - {USART2, RCC_AHBPeriph_DMA1, DMA1_Channel6}, - {USART3, RCC_AHBPeriph_DMA1, DMA1_Channel3}, - {UART4, RCC_AHBPeriph_DMA2, DMA2_Channel3}, - {UART5, -1, -1 }, // UART5 don't support DMA +// USART TCC DMAx DMAx RX Channel + {USART1, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel5}, + {USART2, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel6}, + {USART3, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel3}, + {UART4, RCC_AHBPeriph_DMA2, DMA2, DMA2_Channel3}, + {UART5, -1, -1 -1, }, // UART5 don't support DMA #endif }; +#ifdef TX_DMA_WRITE + +// clear all DMA interrupts TX for a serial port +void Serial_DMAClearInterruptFlagsTX(uint8_t port) +{ + // clear 4 bits, shift = channel * 4 + + switch (port) + { + case _USART1: + Serial[port].dma_controller->IFCR = (0x0F << 16); + break; + + case _USART2: + Serial[port].dma_controller->IFCR = (0x0F << 28); + break; + + case _USART3: + Serial[port].dma_controller->IFCR = (0x0F << 8); + break; + + case _UART4: + Serial[port].dma_controller->IFCR = (0x0F << 20); + break; + } +} + +#endif + void Serial_DMAConfig(uint8_t port) { const SERIAL_CFG * cfg = &Serial[port]; RCC_AHBPeriphClockCmd(cfg->dma_rcc, ENABLE); // enable DMA clock - cfg->uart->CR3 |= (1<<6); // DMA enable receiver (DMAR) + cfg->uart->CR3 |= (1<<6); // enable DMA receiver (DMAR) cfg->dma_channelRX->CCR &= ~(1<<0); // RX disable DMA @@ -56,10 +97,12 @@ void Serial_DMAConfig(uint8_t port) cfg->dma_channelRX->CCR |= (1<<0); // RX enable RX DMA #ifdef TX_DMA_WRITE // TX DMA based serial writing - cfg->uart->CR3 |= (3<<6); // DMA enable transmitter (DMAT) and receiver (DMAR) + cfg->uart->CR3 |= (3<<6); // enable DMA transmitter (DMAT) and receiver (DMAR) cfg->dma_channelTX->CCR &= ~(1<<0); // TX disable DMA + Serial_DMAClearInterruptFlagsTX(port); // TX clear DMA interrupt flags + cfg->dma_channelTX->CPAR = (uint32_t)(&cfg->uart->DR); // TX peripheral address (usart) cfg->dma_channelTX->CCR = 0; // TX clear control register @@ -74,9 +117,6 @@ void Serial_DMAConfig(uint8_t port) cfg->dma_channelTX->CCR |= (1<<7); // TX memory increment mode //cfg->dma_channelTX->CCR |= (0<<6); // TX peripheral no increment mode //cfg->dma_channelTX->CCR |= (0<<5); // TX circular mode disabled - -// NOTE: it will be enabled later when needed -//cfg->dma_channelTX->CCR |= (1<<4); // TX enable TX DMA Transfer Complete interrupt #endif } @@ -124,6 +164,8 @@ void Serial_DeConfig(uint8_t port) #ifdef TX_DMA_WRITE Serial[port].dma_channelTX->CCR &= ~(1<<0); // disable TX DMA + + Serial_DMAClearInterruptFlagsTX(port); #endif UART_DeConfig(port); @@ -140,9 +182,10 @@ void Serial_Send_TX(uint8_t port) else dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts - Serial[port].dma_channelTX->CCR &= ~(1<<0); // disable TX DMA + Serial[port].dma_channelTX->CCR &= ~(1<<0); // disable TX DMA Serial[port].dma_channelTX->CMAR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // TX data start address Serial[port].dma_channelTX->CNDTR = dmaL1DataTX[port].flag; // number of bytes to transfer + Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags Serial[port].uart->CR1 |= USART_CR1_TCIE; // enable Transfer Complete (TC) serial interrupt Serial[port].dma_channelTX->CCR |= (1<<0); // enable TX DMA } diff --git a/TFT/src/User/Hal/stm32f10x/Serial.h b/TFT/src/User/Hal/stm32f10x/Serial.h index 58395389f2..6d60b7b32d 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.h +++ b/TFT/src/User/Hal/stm32f10x/Serial.h @@ -6,7 +6,7 @@ #include "uart.h" // uncomment this line to use TX DMA based serial writing. Otherwise, TX interrupt based serial writing will be used -//#define TX_DMA_WRITE +#define TX_DMA_WRITE typedef struct { @@ -22,6 +22,7 @@ typedef struct { USART_TypeDef * uart; // uint32_t uint32_t dma_rcc; + DMA_TypeDef * dma_controller; DMA_Channel_TypeDef * dma_channelRX; #ifdef TX_DMA_WRITE DMA_Channel_TypeDef * dma_channelTX; diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c index 34988ce97a..fe1c4f6406 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c @@ -4,13 +4,25 @@ // set this line to "true" to enable IDLE Line interrupt. It is no more needed, so always set this macro to "false" #define IDLE_LINE_IT false +// DMA registers: +// +// CR DMA stream x configuration register +// NDTR DMA stream x number of data register +// PAR DMA stream x peripheral address register +// M0AR DMA stream x memory 0 address register +// +// LISR DMA low interrupt status register +// HISR DMA high interrupt status register +// LIFCR DMA low interrupt flag clear register +// HIFCR DMA high interrupt flag clear register + DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA / interrupt RX buffer DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA / interrupt TX buffer // config for USART DMA channels SERIAL_CFG Serial[_UART_CNT] = { #ifdef TX_DMA_WRITE -// USART TCC DMAx Channel RX_STREAM TX_STREAM +// USART TCC DMAx Channel RX Stream TX Stream {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2, DMA2_Stream7}, {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5, DMA1_Stream6}, {USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1, DMA1_Stream3}, @@ -18,7 +30,7 @@ SERIAL_CFG Serial[_UART_CNT] = { {UART5, RCC_AHB1Periph_DMA1, 4, DMA1_Stream0, DMA1_Stream7}, {USART6, RCC_AHB1Periph_DMA2, 5, DMA2_Stream1, DMA2_Stream6}, #else -// USART TCC DMAx Channel RX_STREAM +// USART TCC DMAx Channel RX Stream {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2}, {USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5}, {USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1}, @@ -102,9 +114,11 @@ void Serial_DMAConfig(uint8_t port) RCC_AHB1PeriphClockCmd(cfg->dma_rcc, ENABLE); // enable DMA clock - cfg->uart->CR3 |= (1<<6); // DMA enable receiver (DMAR) + cfg->uart->CR3 |= (1<<6); // enable DMA receiver (DMAR) + + cfg->dma_streamRX->CR &= ~(1<<0); // RX disable DMA - Serial_DMAClearInterruptFlagsRX(port); // clear DMA RX interrupt flags + Serial_DMAClearInterruptFlagsRX(port); // RX clear DMA interrupt flags cfg->dma_streamRX->PAR = (uint32_t)(&cfg->uart->DR); // RX peripheral address (usart) cfg->dma_streamRX->M0AR = (uint32_t)(dmaL1DataRX[port].cache); // RX destination address (memory) @@ -127,9 +141,11 @@ void Serial_DMAConfig(uint8_t port) cfg->dma_streamRX->CR |= (1<<0); // RX enable RX DMA #ifdef TX_DMA_WRITE // TX DMA based serial writing - cfg->uart->CR3 |= (3<<6); // DMA enable transmitter (DMAT) and receiver (DMAR) + cfg->uart->CR3 |= (3<<6); // enable DMA transmitter (DMAT) and receiver (DMAR) + + cfg->dma_streamTX->CR &= ~(1<<0); // TX disable DMA - Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags + Serial_DMAClearInterruptFlagsTX(port); // TX clear DMA interrupt flags cfg->dma_streamTX->PAR = (uint32_t)(&cfg->uart->DR); // TX peripheral address (usart) cfg->dma_streamTX->CR = (cfg->dma_channel << 25); // TX channel selection, set to 0 all the other CR bits @@ -146,9 +162,6 @@ void Serial_DMAConfig(uint8_t port) //cfg->dma_streamTX->CR &= ~(1<<9); // TX peripheral no increment mode //cfg->dma_streamTX->CR &= ~(1<<8); // TX circular mode disabled cfg->dma_streamTX->CR |= (1<<6); // TX data transfer direction: Memory-to-Peripheral - -// NOTE: it will be enabled later when needed -//cfg->dma_streamTX->CR |= (1<<4); // TX enable TX DMA Transfer Complete interrupt #endif } @@ -193,10 +206,12 @@ void Serial_DeConfig(uint8_t port) Serial_ClearData(port); Serial[port].dma_streamRX->CR &= ~(1<<0); // disable RX DMA + Serial_DMAClearInterruptFlagsRX(port); #ifdef TX_DMA_WRITE Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA + Serial_DMAClearInterruptFlagsTX(port); #endif @@ -214,7 +229,7 @@ void Serial_Send_TX(uint8_t port) else dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts - Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA + Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA Serial[port].dma_streamTX->M0AR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // TX data start address Serial[port].dma_streamTX->NDTR = dmaL1DataTX[port].flag; // number of bytes to transfer Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags From 229b066984c67b4ee131ac9d9e4d987597d22db3 Mon Sep 17 00:00:00 2001 From: digant73 Date: Fri, 29 Sep 2023 22:28:07 +0200 Subject: [PATCH 14/20] added missing baudrate for WiFi ESP3D --- TFT/src/User/API/SerialConnection.c | 6 ++++-- TFT/src/User/API/SerialConnection.h | 2 +- TFT/src/User/API/Settings.h | 4 ++-- TFT/src/User/Configuration.h | 2 +- TFT/src/User/Hal/stm32f10x/Serial.c | 2 ++ TFT/src/User/config.ini | 10 +++++----- TFT/src/User/config_rrf.ini | 10 +++++----- 7 files changed, 20 insertions(+), 16 deletions(-) diff --git a/TFT/src/User/API/SerialConnection.c b/TFT/src/User/API/SerialConnection.c index ba1dcc98f3..b8e2047540 100644 --- a/TFT/src/User/API/SerialConnection.c +++ b/TFT/src/User/API/SerialConnection.c @@ -24,8 +24,10 @@ const SERIAL_PORT_INFO serialPort[SERIAL_PORT_COUNT] = { #endif }; -const uint32_t baudrateValues[BAUDRATE_COUNT] = { 0, 2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000}; -const char * const baudrateNames[BAUDRATE_COUNT] = {"OFF", "2400", "9600", "19200", "38400", "57600", "115200", "250000", "500000", "1000000"}; +const uint32_t baudrateValues[BAUDRATE_COUNT] = { + 0, 2400, 9600, 19200, 38400, 57600, 115200, 230400, 250000, 500000, 921600, 1000000}; +const char * const baudrateNames[BAUDRATE_COUNT] = { + "OFF", "2400", "9600", "19200", "38400", "57600", "115200", "230400", "250000", "500000", "921600", "1000000"}; static inline void Serial_InitPrimary(void) { diff --git a/TFT/src/User/API/SerialConnection.h b/TFT/src/User/API/SerialConnection.h index c988e3a175..5c71575613 100644 --- a/TFT/src/User/API/SerialConnection.h +++ b/TFT/src/User/API/SerialConnection.h @@ -10,7 +10,7 @@ extern "C" { #include "variants.h" // for SERIAL_PORT_2 etc. #include "uart.h" // for _UART_CNT etc. -#define BAUDRATE_COUNT 10 +#define BAUDRATE_COUNT 12 typedef enum { diff --git a/TFT/src/User/API/Settings.h b/TFT/src/User/API/Settings.h index 499116f564..2dfa637a96 100644 --- a/TFT/src/User/API/Settings.h +++ b/TFT/src/User/API/Settings.h @@ -14,10 +14,10 @@ extern "C" { // Config version support (format YYYYMMDD) // change if new elements/keywords are added/removed/changed in the Configuration.h // this number should match CONFIG_VERSION in Configuration.h -#define CONFIG_SUPPPORT 20230821 +#define CONFIG_SUPPPORT 20230929 #define FONT_FLASH_SIGN 20230821 // (YYYYMMDD) change if fonts require updating -#define CONFIG_FLASH_SIGN 20230821 // (YYYYMMDD) change if any keyword(s) in config.ini is added or removed +#define CONFIG_FLASH_SIGN 20230929 // (YYYYMMDD) change if any keyword(s) in config.ini is added or removed #define LANGUAGE_FLASH_SIGN 20230821 // (YYYYMMDD) change if any keyword(s) in language pack is added or removed #define ICON_FLASH_SIGN 20230821 // (YYYYMMDD) change if any icon(s) is added or removed diff --git a/TFT/src/User/Configuration.h b/TFT/src/User/Configuration.h index 679fd4772e..0e16056daa 100644 --- a/TFT/src/User/Configuration.h +++ b/TFT/src/User/Configuration.h @@ -1,7 +1,7 @@ #ifndef _CONFIGURATION_H_ #define _CONFIGURATION_H_ -#define CONFIG_VERSION 20230821 +#define CONFIG_VERSION 20230929 //==================================================================================================== //=============================== Settings Configurable On config.ini ================================ diff --git a/TFT/src/User/Hal/stm32f10x/Serial.c b/TFT/src/User/Hal/stm32f10x/Serial.c index 9877908f61..b9459e25ce 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.c +++ b/TFT/src/User/Hal/stm32f10x/Serial.c @@ -93,6 +93,7 @@ void Serial_DMAConfig(uint8_t port) cfg->dma_channelRX->CCR |= (1<<7); // RX memory increment mode //cfg->dma_channelRX->CCR |= (0<<6); // RX peripheral no increment mode cfg->dma_channelRX->CCR |= (1<<5); // RX circular mode enabled +// cfg->dma_channelRX->CCR &= ~(1<<4); // RX data transfer direction: Peripheral-to-memory cfg->dma_channelRX->CCR |= (1<<0); // RX enable RX DMA @@ -117,6 +118,7 @@ void Serial_DMAConfig(uint8_t port) cfg->dma_channelTX->CCR |= (1<<7); // TX memory increment mode //cfg->dma_channelTX->CCR |= (0<<6); // TX peripheral no increment mode //cfg->dma_channelTX->CCR |= (0<<5); // TX circular mode disabled + cfg->dma_channelTX->CCR |= (1<<4); // TX data transfer direction: Memory-to-Peripheral #endif } diff --git a/TFT/src/User/config.ini b/TFT/src/User/config.ini index 969a6421f2..9d189db678 100644 --- a/TFT/src/User/config.ini +++ b/TFT/src/User/config.ini @@ -103,11 +103,11 @@ # P2: WIFI (e.g. ESP3D) # P3: UART 3 (e.g. OctoPrint) # P4: UART 4 -# Value range: P1: [min: 1, max: 9] -# P2: [min: 0, max: 9] -# P3: [min: 0, max: 9] -# P4: [min: 0, max: 9] -# Options: [OFF (port disabled): 0, 2400: 1, 9600: 2, 19200: 3, 38400: 4, 57600: 5, 115200: 6, 250000: 7, 500000: 8, 1000000: 9] +# Value range: P1: [min: 1, max: 11] +# P2: [min: 0, max: 11] +# P3: [min: 0, max: 11] +# P4: [min: 0, max: 11] +# Options: [OFF (port disabled): 0, 2400: 1, 9600: 2, 19200: 3, 38400: 4, 57600: 5, 115200: 6, 230400: 7, 250000: 8, 500000: 9, 921600: 10, 1000000: 11] serial_port:P1:6 P2:0 P3:0 P4:0 #### TX Slots diff --git a/TFT/src/User/config_rrf.ini b/TFT/src/User/config_rrf.ini index 02d29b005c..818af634c2 100644 --- a/TFT/src/User/config_rrf.ini +++ b/TFT/src/User/config_rrf.ini @@ -66,11 +66,11 @@ # P2: WIFI (e.g. ESP3D) # P3: UART 3 (e.g. OctoPrint) # P4: UART 4 -# Value range: P1: [min: 1, max: 9] -# P2: [min: 0, max: 9] -# P3: [min: 0, max: 9] -# P4: [min: 0, max: 9] -# Options: [OFF (port disabled): 0, 2400: 1, 9600: 2, 19200: 3, 38400: 4, 57600: 5, 115200: 6, 250000: 7, 500000: 8, 1000000: 9] +# Value range: P1: [min: 1, max: 11] +# P2: [min: 0, max: 11] +# P3: [min: 0, max: 11] +# P4: [min: 0, max: 11] +# Options: [OFF (port disabled): 0, 2400: 1, 9600: 2, 19200: 3, 38400: 4, 57600: 5, 115200: 6, 230400: 7, 250000: 8, 500000: 9, 921600: 10, 1000000: 11] serial_port:P1:5 P2:0 P3:0 P4:0 #### TX Slots From c593802245f2bbd06546f096cc5114b3bb1aed3e Mon Sep 17 00:00:00 2001 From: digant73 Date: Sat, 30 Sep 2023 19:03:43 +0200 Subject: [PATCH 15/20] added Serial.c/h for GD TFT variant --- TFT/src/User/API/Printing.c | 2 +- TFT/src/User/API/parseACK.c | 8 +- TFT/src/User/Hal/gd32f20x/Serial.c | 285 ++++++++++++++++++++----- TFT/src/User/Hal/gd32f20x/Serial.h | 42 ++-- TFT/src/User/Hal/stm32f10x/Serial.c | 49 ++--- TFT/src/User/Hal/stm32f10x/Serial.h | 2 +- TFT/src/User/Hal/stm32f2_f4xx/Serial.c | 75 ++++--- TFT/src/User/Hal/stm32f2_f4xx/Serial.h | 2 +- 8 files changed, 325 insertions(+), 140 deletions(-) diff --git a/TFT/src/User/API/Printing.c b/TFT/src/User/API/Printing.c index 097fd556c2..78b6906ef5 100644 --- a/TFT/src/User/API/Printing.c +++ b/TFT/src/User/API/Printing.c @@ -310,7 +310,7 @@ void initPrintSummary(void) infoPrintSummary = (PRINT_SUMMARY){.name[0] = '\0', 0, 0, 0, 0, false}; // save print filename (short or long filename) - sprintf(infoPrintSummary.name, "%." STRINGIFY(SUMMARY_NAME_LEN) "s", getPrintFilename()); + strncpy_no_pad(infoPrintSummary.name, getPrintFilename(), SUMMARY_NAME_LEN); } void preparePrintSummary(void) diff --git a/TFT/src/User/API/parseACK.c b/TFT/src/User/API/parseACK.c index e3c8e9b72d..9add4e9968 100644 --- a/TFT/src/User/API/parseACK.c +++ b/TFT/src/User/API/parseACK.c @@ -646,7 +646,7 @@ void parseACK(void) speedQuerySetWait(false); } // parse and store flow rate percentage - else if (ack_seen("Flow: ")) + else if (ack_seen("Flow:")) { speedSetCurPercent(1, ack_value()); speedQuerySetWait(false); @@ -919,15 +919,15 @@ void parseACK(void) mblUpdateStatus(true); } // parse G30, feedback to get the 4 corners Z value returned by Marlin for LevelCorner menu - else if (ack_seen("Bed X: ")) + else if (ack_seen("Bed X:")) { float x = ack_value(); float y = 0; - if (ack_continue_seen("Y: ")) + if (ack_continue_seen("Y:")) y = ack_value(); - if (ack_continue_seen("Z: ")) + if (ack_continue_seen("Z:")) levelingSetProbedPoint(x, y, ack_value()); // save probed Z value } #if DELTA_PROBE_TYPE != 0 diff --git a/TFT/src/User/Hal/gd32f20x/Serial.c b/TFT/src/User/Hal/gd32f20x/Serial.c index c72abdc050..3ac0a9197a 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.c +++ b/TFT/src/User/Hal/gd32f20x/Serial.c @@ -4,68 +4,153 @@ // set this line to "true" to enable IDLE Line interrupt. It is no more needed, so always set this macro to "false" #define IDLE_LINE_IT false -// uncomment this line to use TX DMA based serial writing. Otherwise only TX interrupt based serial writing will be used -//#define TX_DMA_WRITE +DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA / interrupt RX buffer +DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA / interrupt TX buffer -// uncomment this line to use compact code (less code) instead of fast code -#define USE_COMPACT_CODE - -#ifndef USE_COMPACT_CODE - // uncomment this line to use inline copy (fast code) instead of memcpy() (less code) - #define USE_INLINE_COPY +// config for USART DMA channels +const SERIAL_CFG Serial[_UART_CNT] = { +#ifdef TX_DMA_WRITE +// USART RCU DMAx DMAx RX Channel TX Channel + {USART0, RCU_DMA0, DMA0, 4, 3}, + {USART1, RCU_DMA0, DMA0, 5, 6}, + {USART2, RCU_DMA0, DMA0, 2, 1}, + {UART3, RCU_DMA1, DMA1, 2, 4}, + {UART4, RCU_DMA1, DMA1, 1, 3}, + {USART5, RCU_DMA1, DMA1, 5, 6}, +#else +// USART RCU DMAx DMAx RX Channel + {USART0, RCU_DMA0, DMA0, 4}, + {USART1, RCU_DMA0, DMA0, 5}, + {USART2, RCU_DMA0, DMA0, 2}, + {UART3, RCU_DMA1, DMA1, 2}, + {UART4, RCU_DMA1, DMA1, 1}, + {USART5, RCU_DMA1, DMA1, 5}, #endif +}; + +// disable RX DMA and clear all interrupt flags for a serial port +void Serial_DMA_DisableAndClearFlagsRX(uint8_t port) +{ + DMA_CHCTL(Serial[port].dma_stream, Serial[port].dma_channelRX) &= ~(1<<0); // disable RX DMA -//#define DEFAULT_WRITE // old unbuffered TX serial writing (just for comparison with new TX solutions) + // Table 12.5.2, 4 bits per channel, shift = channel * 4 -DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA RX buffer -DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA TX buffer + switch (port) + { + case _USART1: + DMA_INTC(DMA0) = (0x0F << 16); // DMA0 channel 4 + break; -// config for USART channel -const SERIAL_CFG Serial[_UART_CNT] = { - {USART0, RCU_DMA0, 4, DMA0}, - {USART1, RCU_DMA0, 5, DMA0}, - {USART2, RCU_DMA0, 2, DMA0}, - {UART3, RCU_DMA1, 2, DMA1}, - {UART4, RCU_DMA1, 1, DMA1}, - {USART5, RCU_DMA1, 5, DMA1}, -}; + case _USART2: + DMA_INTC(DMA0) = (0x0F << 20); // DMA0 channel 5 + break; -void Serial_DMAClearFlag(uint8_t port) + case _USART3: + DMA_INTC(DMA0) = (0x0F << 8); // DMA0 channel 2 + break; + + case _UART4: + DMA_INTC(DMA1) = (0x0F << 8); // DMA1 channel 2 + break; + + case _UART5: + DMA_INTC(DMA1) = (0x0F << 4); // DMA1 channel 1 + break; + + case _USART6: + DMA_INTC(DMA1) = (0x0F << 20); // DMA1 channel 5 + break; + } +} + +#ifdef TX_DMA_WRITE + +// disable TX DMA and clear all interrupt flags for a serial port +void Serial_DMA_DisableAndClearFlagsTX(uint8_t port) { - switch(port) + DMA_CHCTL(Serial[port].dma_stream, Serial[port].dma_channelTX) &= ~(1<<0); // disable TX DMA + + // Table 12.5.2, 4 bits per channel, shift = channel * 4 + + switch (port) { - case _USART1: DMA_INTC(DMA0) = (0x0F << 12); break; - case _USART2: DMA_INTC(DMA0) = (0x0F << 20); break; - case _USART3: DMA_INTC(DMA0) = (0x0F << 8); break; - case _UART4: DMA_INTC(DMA1) = (0x0F << 8); break; - case _UART5: DMA_INTC(DMA1) = (0x0F << 4); break; - case _USART6: DMA_INTC(DMA1) = (0x0F << 20); break; + case _USART1: + DMA_INTC(DMA0) = (0x0F << 12); // DMA0 channel 3 + break; + + case _USART2: + DMA_INTC(DMA0) = (0x0F << 24); // DMA0 channel 6 + break; + + case _USART3: + DMA_INTC(DMA0) = (0x0F << 4); // DMA0 channel 1 + break; + + case _UART4: + DMA_INTC(DMA1) = (0x0F << 16); // DMA1 channel 4 + break; + + case _UART5: + DMA_INTC(DMA1) = (0x0F << 12); // DMA1 channel 3 + break; + + case _USART6: + DMA_INTC(DMA1) = (0x0F << 24); // DMA1 channel 6 + break; } } +#endif + void Serial_DMA_Config(uint8_t port) { const SERIAL_CFG * cfg = &Serial[port]; - rcu_periph_clock_enable(cfg->dma_rcc); + rcu_periph_clock_enable(cfg->dma_rcc); // enable DMA clock + + Serial_DMA_DisableAndClearFlagsRX(port); // RX disable DMA and clear all interrupt flags + + DMA_CHPADDR(cfg->dma_stream, cfg->dma_channelRX) = (uint32_t)(&USART_DATA(cfg->uart)); // RX peripheral address (usart) + DMA_CHMADDR(cfg->dma_stream, cfg->dma_channelRX) = (uint32_t)(dmaL1DataRX[port].cache); // RX destination address (memory) + DMA_CHCNT(cfg->dma_stream, cfg->dma_channelRX) = (uint32_t)(dmaL1DataRX[port].cacheSize); // RX buffer size + + // primary serial port priority at highest level (TX higher than RX) + if (port == SERIAL_PORT) + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (2<<12); // RX priority level: High + else + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (0<<12); // RX priority level: Low + +//DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (0<<10); // RX memory data size: 8 bit +//DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (0<<8); // RX peripheral data size: 8 bit + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (1<<7); // RX memory increment mode +//DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (0<<6); // RX peripheral no increment mode + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (1<<5); // RX circular mode enabled +//DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (0<<4); // RX data transfer direction: Peripheral-to-memory + +#ifdef TX_DMA_WRITE // TX DMA based serial writing + Serial_DMA_DisableAndClearFlagsTX(port); // TX disable DMA and clear all interrupt flags + + DMA_CHPADDR(cfg->dma_stream, cfg->dma_channelTX) = (uint32_t)(&USART_DATA(cfg->uart)); // TX peripheral address (usart) + + // primary serial port priority at highest level (TX higher than RX) + if (port == SERIAL_PORT) + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (3<<12); // TX priority level: Very high + else + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (1<<12); // TX priority level: Medium - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) &= ~(1<<0); // Disable DMA - Serial_DMAClearFlag(port); - USART_CTL2(cfg->uart) |= 1<<6; // DMA enable receiver +//DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (0<<10); // TX memory data size: 8 bit +//DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (0<<8); // TX peripheral data size: 8 bit + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (1<<7); // TX memory increment mode +//DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (0<<6); // TX peripheral no increment mode +//DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (0<<5); // TX circular mode disabled + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (1<<4); // TX data transfer direction: Memory-to-Peripheral - DMA_CHPADDR(cfg->dma_stream, cfg->dma_channel) = (uint32_t)(&USART_DATA(cfg->uart)); - DMA_CHMADDR(cfg->dma_stream, cfg->dma_channel) = (uint32_t)(dmaL1DataRX[port].cache); - DMA_CHCNT(cfg->dma_stream, cfg->dma_channel) = dmaL1DataRX[port].cacheSize; + USART_CTL2(cfg->uart) |= (1<<7); // enable DMA transmitter (DMAT) +//DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (1<<0); // TX enable DMA, done later when needed +#endif - // DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) = cfg->dma_channel << 25; - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 3<<12; // Priority level: Very high - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 0<<10; // Memory data size: 8 - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 0<<8; // Peripheral data size: 8 - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 1<<7; // Memory increment mode - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 0<<6; // Peripheral not increment mode - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 1<<5; // Circular mode enabled - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 0<<4; // Data transfer direction: Peripheral-to-memory - DMA_CHCTL(cfg->dma_stream, cfg->dma_channel) |= 1<<0; // Enable DMA + USART_CTL2(cfg->uart) |= (1<<6); // enable DMA receiver (DMAR) + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (1<<0); // RX enable DMA } void Serial_ClearData(uint8_t port) @@ -87,19 +172,20 @@ void Serial_ClearData(uint8_t port) } } -void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate) +void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate) { Serial_ClearData(port); dmaL1DataRX[port].cacheSize = cacheSizeRX; dmaL1DataRX[port].cache = malloc(cacheSizeRX); - while (!dmaL1DataRX[port].cache); // malloc failed, blocking! + while (!dmaL1DataRX[port].cache); // RX malloc failed, blocking! dmaL1DataTX[port].cacheSize = cacheSizeTX; dmaL1DataTX[port].cache = malloc(cacheSizeTX); - while (!dmaL1DataTX[port].cache); // malloc failed, blocking! + while (!dmaL1DataTX[port].cache); // TX malloc failed, blocking! UART_Config(port, baudrate, USART_INT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt + Serial_DMA_Config(port); } @@ -107,36 +193,121 @@ void Serial_DeConfig(uint8_t port) { Serial_ClearData(port); - DMA_CHCTL(Serial[port].dma_stream, Serial[port].dma_channel) &= ~(1<<0); // Disable DMA - Serial_DMAClearFlag(port); + Serial_DMA_DisableAndClearFlagsRX(port); // disable RX DMA and clear all interrupt flags + +#ifdef TX_DMA_WRITE + Serial_DMA_DisableAndClearFlagsTX(port); // disable TX DMA and clear all interrupt flags +#endif + UART_DeConfig(port); } -void Serial_PutChar(uint8_t port, const char ch) +#ifdef TX_DMA_WRITE // TX DMA based serial writing + +// DMA serial write support function +void Serial_Send_TX(uint8_t port) { - while ((USART_STAT0(Serial[port].uart) & (1 << USART_FLAG_TC)) == (uint16_t)RESET); - USART_DATA(Serial[port].uart) = (uint8_t) ch; + // setup DMA transfer, and wait for serial Transfer Complete (TX) interrupt in ISR + if (dmaL1DataTX[port].wIndex >= dmaL1DataTX[port].rIndex) + dmaL1DataTX[port].flag = dmaL1DataTX[port].wIndex - dmaL1DataTX[port].rIndex; + else // split transfer into 2 parts + dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; + + Serial_DMA_DisableAndClearFlagsTX(port); // disable TX DMA and clear all interrupt flags + + DMA_CHMADDR(Serial[port].dma_stream, Serial[port].dma_channelTX) = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // TX data start address + DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channelTX) = dmaL1DataTX[port].flag; // number of bytes to transfer + + USART_CTL0(Serial[port].uart) |= USART_CTL0_TCIE; // enable Transfer Complete (TC) serial interrupt + DMA_CHCTL(Serial[port].dma_stream, Serial[port].dma_channelTX) |= (1<<0); // enable TX DMA } void Serial_Put(uint8_t port, const char * msg) { while (*msg) { - while ((USART_STAT0(Serial[port].uart) & (1 << USART_FLAG_TC)) == (uint16_t)RESET); - USART_DATA(Serial[port].uart) = ((uint16_t)*msg++ & (uint16_t)0x01FF); + // setup TX DMA, if no '\n' yet, but buffer is full AND DMA is not in progress already (waiting for Transfer Complete interrupt) + if ((((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) && + ((USART_CTL0(Serial[port].uart) & USART_CTL0_TCIE) == 0)) + Serial_Send_TX(port); + + // blocking! wait for buffer to become available + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { } + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg; // copy character to cache + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; // update wIndex + + if ((*msg == '\n') && ((USART_CTL0(Serial[port].uart) & USART_CTL0_TCIE) == 0)) + Serial_Send_TX(port); // start DMA process if command is complete and DMA is not in progress already + + msg++; // let the compiler optimize this, no need to do it manually! } } +#else // TX interrupt based serial writing + +void Serial_Put(uint8_t port, const char * msg) +{ + while (*msg) + { + // blocking! wait for buffer to become available + while (((dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize) == dmaL1DataTX[port].rIndex) { }; + + dmaL1DataTX[port].cache[dmaL1DataTX[port].wIndex] = *msg++; + dmaL1DataTX[port].wIndex = (dmaL1DataTX[port].wIndex + 1) % dmaL1DataTX[port].cacheSize; + + USART_CTL0(Serial[port].uart) |= USART_CTL0_TBEIE; // set TBE interrupt bit to start the serial transfer + } +} + +#endif + // ISR, serial interrupt handler void USART_IRQHandler(uint8_t port) { #if IDLE_LINE_IT == true // IDLE Line interrupt - if ((USART_STAT0(Serial[port].uart) & (1<<4)) != 0) // check for IDLE Line interrupt + if ((USART_STAT0(Serial[port].uart) & USART_STAT0_IDLEF) != RESET) // check for IDLE Line interrupt { - USART_STAT0(Serial[port].uart); // clear IDLE Line bit + USART_STAT0(Serial[port].uart); // clear IDLE Line bit USART_DATA(Serial[port].uart); - dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channel); + dmaL1DataRX[port].wIndex = dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channelRX); + } +#endif + +#ifdef TX_DMA_WRITE // TX DMA based serial writing + if ((USART_STAT0(Serial[port].uart) & USART_STAT0_TC) != RESET) // check for Transfer Complete (TC) interrupt + { + USART_STAT0(Serial[port].uart) &= ~USART_STAT0_TC; // clear Transfer Complete (TC) bit + + // NOTE 1: use the serial TC, not the DMA TC because this only indicates DMA is done, peripheral might be still busy + // NOTE 2: the TC interrupt is sometimes called while DMA is still active, so check NDTR status! + // + if (DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channelTX) == 0) // sending is complete + { + dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + dmaL1DataTX[port].flag) % dmaL1DataTX[port].cacheSize; + dmaL1DataTX[port].flag = 0; + + if (dmaL1DataTX[port].wIndex != dmaL1DataTX[port].rIndex) // is more data available? + Serial_Send_TX(port); // continue sending data + else + USART_CTL0(Serial[port].uart) &= ~USART_CTL0_TCIE; // disable Transfer Complete (TC) interrupt, nothing more to do + } + // else: more data is coming, wait for next Transfer Complete (TC) interrupt + } +#else // TX interrupt based serial writing + if ((USART_STAT0(Serial[port].uart) & USART_STAT0_TBE) != RESET) // check for TBE interrupt + { + if (dmaL1DataTX[port].rIndex == dmaL1DataTX[port].wIndex) // no more data? + { + USART_CTL0(Serial[port].uart) &= ~USART_CTL0_TBEIE; // disable TBE interrupt + } + else + { + USART_DATA(Serial[port].uart) = (uint8_t)dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]; // write next available character + + dmaL1DataTX[port].rIndex = (dmaL1DataTX[port].rIndex + 1) % dmaL1DataTX[port].cacheSize; // increase reading index + } } #endif } diff --git a/TFT/src/User/Hal/gd32f20x/Serial.h b/TFT/src/User/Hal/gd32f20x/Serial.h index b84e281600..35e30eef71 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.h +++ b/TFT/src/User/Hal/gd32f20x/Serial.h @@ -5,52 +5,58 @@ #include "variants.h" // for uint32_t etc... #include "uart.h" +// uncomment this line to use TX DMA based serial writing. Otherwise, TX interrupt based serial writing will be used +#define TX_DMA_WRITE + typedef struct { char * cache; - volatile uint16_t wIndex; // writing index. Precautionally declared as volatile due to access from interrupt handler and main thread - volatile uint16_t rIndex; // reading index. Precautionally declared as volatile due to access from interrupt handler and main thread - uint16_t flag; // custom flag (for custom usage by the application) - uint16_t cacheSize; + uint32_t cacheSize; + volatile uint32_t wIndex; // writing index + volatile uint32_t rIndex; // reading index + volatile uint32_t flag; // custom flag (for custom usage by the application) } DMA_CIRCULAR_BUFFER; +// config for USART DMA channels typedef struct { uint32_t uart; - rcu_periph_enum dma_rcc; - uint8_t dma_channel; + rcu_periph_enum dma_rcc; // uint32_t uint32_t dma_stream; + uint32_t dma_channelRX; +#ifdef TX_DMA_WRITE + uint32_t dma_channelTX; +#endif } SERIAL_CFG; extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; extern const SERIAL_CFG Serial[_UART_CNT]; -void Serial_Config(uint8_t port, uint16_t cacheSizeRX, uint16_t cacheSizeTX, uint32_t baudrate); +void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); -// retrieve the next reading index in the RX message queue of the provided serial port: -// - port: index of serial port +// retrieve the next reading index in the RX message queue of the provided physical serial port: +// - port: physical serial port // // - return value: next reading index -static inline uint16_t Serial_GetReadingIndexRX(uint8_t port) +static inline uint32_t Serial_GetReadingIndexRX(uint8_t port) { return dmaL1DataRX[port].rIndex; } -// retrieve the next writing index in the RX message queue of the provided serial port +// retrieve the next writing index in the RX message queue of the provided physical serial port // based on Interrupt/DMA status while writing serial data in the background: -// - port: index of serial port +// - port: physical serial port // // - return value: next writing index -static inline uint16_t Serial_GetWritingIndexRX(uint8_t port) +static inline uint32_t Serial_GetWritingIndexRX(uint8_t port) { - return dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channel); + return dmaL1DataRX[port].cacheSize - DMA_CHCNT(Serial[port].dma_stream, Serial[port].dma_channelRX); } -// send character to a uart port -void Serial_PutChar(uint8_t port, const char ch); - -// send a zero terminated message to uart port +// send a zero terminated message to UART port +// - port: index of serial port +// - msg: message to send void Serial_Put(uint8_t port, const char * msg); #endif diff --git a/TFT/src/User/Hal/stm32f10x/Serial.c b/TFT/src/User/Hal/stm32f10x/Serial.c index b9459e25ce..34cc882f8d 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.c +++ b/TFT/src/User/Hal/stm32f10x/Serial.c @@ -40,9 +40,11 @@ const SERIAL_CFG Serial[_UART_CNT] = { // RM0008 Table 78-79 #ifdef TX_DMA_WRITE -// clear all DMA interrupts TX for a serial port -void Serial_DMAClearInterruptFlagsTX(uint8_t port) +// disable TX DMA and clear all interrupt flags for a serial port +void Serial_DMA_DisableAndClearFlagsTX(uint8_t port) { + Serial[port].dma_channelTX->CCR &= ~(1<<0); // disable TX DMA + // clear 4 bits, shift = channel * 4 switch (port) @@ -67,19 +69,18 @@ void Serial_DMAClearInterruptFlagsTX(uint8_t port) #endif -void Serial_DMAConfig(uint8_t port) +void Serial_DMA_Config(uint8_t port) { const SERIAL_CFG * cfg = &Serial[port]; RCC_AHBPeriphClockCmd(cfg->dma_rcc, ENABLE); // enable DMA clock - cfg->uart->CR3 |= (1<<6); // enable DMA receiver (DMAR) - cfg->dma_channelRX->CCR &= ~(1<<0); // RX disable DMA cfg->dma_channelRX->CPAR = (uint32_t)(&cfg->uart->DR); // RX peripheral address (usart) cfg->dma_channelRX->CMAR = (uint32_t)(dmaL1DataRX[port].cache); // RX destination address (memory) cfg->dma_channelRX->CNDTR = dmaL1DataRX[port].cacheSize; // RX buffer size + cfg->dma_channelRX->CCR = 0; // RX clear control register // primary serial port priority at highest level (TX higher than RX) @@ -93,18 +94,13 @@ void Serial_DMAConfig(uint8_t port) cfg->dma_channelRX->CCR |= (1<<7); // RX memory increment mode //cfg->dma_channelRX->CCR |= (0<<6); // RX peripheral no increment mode cfg->dma_channelRX->CCR |= (1<<5); // RX circular mode enabled -// cfg->dma_channelRX->CCR &= ~(1<<4); // RX data transfer direction: Peripheral-to-memory - - cfg->dma_channelRX->CCR |= (1<<0); // RX enable RX DMA +//cfg->dma_channelRX->CCR &= ~(1<<4); // RX data transfer direction: Peripheral-to-memory #ifdef TX_DMA_WRITE // TX DMA based serial writing - cfg->uart->CR3 |= (3<<6); // enable DMA transmitter (DMAT) and receiver (DMAR) - - cfg->dma_channelTX->CCR &= ~(1<<0); // TX disable DMA - - Serial_DMAClearInterruptFlagsTX(port); // TX clear DMA interrupt flags + Serial_DMA_DisableAndClearFlagsTX(port); // TX disable DMA and clear all interrupt flags cfg->dma_channelTX->CPAR = (uint32_t)(&cfg->uart->DR); // TX peripheral address (usart) + cfg->dma_channelTX->CCR = 0; // TX clear control register // primary serial port priority at highest level (TX higher than RX) @@ -119,7 +115,13 @@ void Serial_DMAConfig(uint8_t port) //cfg->dma_channelTX->CCR |= (0<<6); // TX peripheral no increment mode //cfg->dma_channelTX->CCR |= (0<<5); // TX circular mode disabled cfg->dma_channelTX->CCR |= (1<<4); // TX data transfer direction: Memory-to-Peripheral + + cfg->uart->CR3 |= (1<<7); // enable DMA transmitter (DMAT) +//cfg->dma_channelTX->CCR |= (1<<0); // TX enable DMA, done later when needed #endif + + cfg->uart->CR3 |= (1<<6); // enable DMA receiver (DMAR) + cfg->dma_channelRX->CCR |= (1<<0); // RX enable DMA } void Serial_ClearData(uint8_t port) @@ -155,7 +157,7 @@ void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uin UART_Config(port, baudrate, USART_IT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt - Serial_DMAConfig(port); + Serial_DMA_Config(port); } void Serial_DeConfig(uint8_t port) @@ -165,9 +167,7 @@ void Serial_DeConfig(uint8_t port) Serial[port].dma_channelRX->CCR &= ~(1<<0); // disable RX DMA #ifdef TX_DMA_WRITE - Serial[port].dma_channelTX->CCR &= ~(1<<0); // disable TX DMA - - Serial_DMAClearInterruptFlagsTX(port); + Serial_DMA_DisableAndClearFlagsTX(port); // disable TX DMA and clear all interrupt flags #endif UART_DeConfig(port); @@ -181,15 +181,16 @@ void Serial_Send_TX(uint8_t port) // setup DMA transfer, and wait for serial Transfer Complete (TX) interrupt in ISR if (dmaL1DataTX[port].wIndex >= dmaL1DataTX[port].rIndex) dmaL1DataTX[port].flag = dmaL1DataTX[port].wIndex - dmaL1DataTX[port].rIndex; - else - dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts + else // split transfer into 2 parts + dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; + + Serial_DMA_DisableAndClearFlagsTX(port); // disable TX DMA and clear all interrupt flags - Serial[port].dma_channelTX->CCR &= ~(1<<0); // disable TX DMA Serial[port].dma_channelTX->CMAR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // TX data start address - Serial[port].dma_channelTX->CNDTR = dmaL1DataTX[port].flag; // number of bytes to transfer - Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags - Serial[port].uart->CR1 |= USART_CR1_TCIE; // enable Transfer Complete (TC) serial interrupt - Serial[port].dma_channelTX->CCR |= (1<<0); // enable TX DMA + Serial[port].dma_channelTX->CNDTR = dmaL1DataTX[port].flag; // number of bytes to transfer + + Serial[port].uart->CR1 |= USART_CR1_TCIE; // enable Transfer Complete (TC) serial interrupt + Serial[port].dma_channelTX->CCR |= (1<<0); // enable TX DMA } void Serial_Put(uint8_t port, const char * msg) diff --git a/TFT/src/User/Hal/stm32f10x/Serial.h b/TFT/src/User/Hal/stm32f10x/Serial.h index 6d60b7b32d..594528a6d5 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.h +++ b/TFT/src/User/Hal/stm32f10x/Serial.h @@ -20,7 +20,7 @@ typedef struct // config for USART DMA channels typedef struct { - USART_TypeDef * uart; // uint32_t + USART_TypeDef * uart; // uint32_t uint32_t dma_rcc; DMA_TypeDef * dma_controller; DMA_Channel_TypeDef * dma_channelRX; diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c index fe1c4f6406..88baa21a07 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.c +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.c @@ -20,7 +20,7 @@ DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT] = {0}; // DMA / interrupt RX buffer DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT] = {0}; // DMA / interrupt TX buffer // config for USART DMA channels -SERIAL_CFG Serial[_UART_CNT] = { +const SERIAL_CFG Serial[_UART_CNT] = { #ifdef TX_DMA_WRITE // USART TCC DMAx Channel RX Stream TX Stream {USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2, DMA2_Stream7}, @@ -40,13 +40,17 @@ SERIAL_CFG Serial[_UART_CNT] = { #endif }; -// clear all DMA interrupts RX for a serial port -void Serial_DMAClearInterruptFlagsRX(uint8_t port) +// disable RX DMA and clear all interrupt flags for a serial port +void Serial_DMA_DisableAndClearFlagsRX(uint8_t port) { + Serial[port].dma_streamRX->CR &= ~(1<<0); // disable RX DMA + // Stream # to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 // Stream # to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 + // + // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) - switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) + switch (port) { case _USART1: DMA2->LIFCR = (0x3F << 16); // DMA2_Stream2 low bits:16-21 Channel 4 @@ -74,13 +78,19 @@ void Serial_DMAClearInterruptFlagsRX(uint8_t port) } } -// clear all DMA interrupts TX for a serial port -void Serial_DMAClearInterruptFlagsTX(uint8_t port) +#ifdef TX_DMA_WRITE + +// disable TX DMA and clear all interrupt flags for a serial port +void Serial_DMA_DisableAndClearFlagsTX(uint8_t port) { + Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA + // Stream # to bits: Low 0: 0-5, 1: 6-11, 2: 16-21, 3: 22-27 // Stream # to bits: High 4: 0-5, 5: 6-11, 6: 16-21, 7: 22-27 + // + // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) - switch (port) // DMA low/high interrupt Control Register (DMA_LIFCR/DMA_HIFCR) + switch (port) { case _USART1: DMA2->HIFCR = (0x3F << 22); // DMA2_Stream7 high bits:22-27 Channel 4 @@ -108,21 +118,20 @@ void Serial_DMAClearInterruptFlagsTX(uint8_t port) } } -void Serial_DMAConfig(uint8_t port) +#endif + +void Serial_DMA_Config(uint8_t port) { const SERIAL_CFG * cfg = &Serial[port]; RCC_AHB1PeriphClockCmd(cfg->dma_rcc, ENABLE); // enable DMA clock - cfg->uart->CR3 |= (1<<6); // enable DMA receiver (DMAR) - - cfg->dma_streamRX->CR &= ~(1<<0); // RX disable DMA - - Serial_DMAClearInterruptFlagsRX(port); // RX clear DMA interrupt flags + Serial_DMA_DisableAndClearFlagsRX(port); // RX disable DMA and clear all interrupt flags cfg->dma_streamRX->PAR = (uint32_t)(&cfg->uart->DR); // RX peripheral address (usart) cfg->dma_streamRX->M0AR = (uint32_t)(dmaL1DataRX[port].cache); // RX destination address (memory) cfg->dma_streamRX->NDTR = dmaL1DataRX[port].cacheSize; // RX buffer size + cfg->dma_streamRX->CR = (cfg->dma_channel << 25); // RX channel selection, set to 0 all the other CR bits // primary serial port priority at highest level (TX higher than RX) @@ -138,16 +147,11 @@ void Serial_DMAConfig(uint8_t port) cfg->dma_streamRX->CR |= (1<<8); // RX circular mode enabled //cfg->dma_streamRX->CR &= ~(1<<6); // RX data transfer direction: Peripheral-to-memory - cfg->dma_streamRX->CR |= (1<<0); // RX enable RX DMA - #ifdef TX_DMA_WRITE // TX DMA based serial writing - cfg->uart->CR3 |= (3<<6); // enable DMA transmitter (DMAT) and receiver (DMAR) - - cfg->dma_streamTX->CR &= ~(1<<0); // TX disable DMA - - Serial_DMAClearInterruptFlagsTX(port); // TX clear DMA interrupt flags + Serial_DMA_DisableAndClearFlagsTX(port); // TX disable DMA and clear all interrupt flags cfg->dma_streamTX->PAR = (uint32_t)(&cfg->uart->DR); // TX peripheral address (usart) + cfg->dma_streamTX->CR = (cfg->dma_channel << 25); // TX channel selection, set to 0 all the other CR bits // primary serial port priority at highest level (TX higher than RX) @@ -162,7 +166,13 @@ void Serial_DMAConfig(uint8_t port) //cfg->dma_streamTX->CR &= ~(1<<9); // TX peripheral no increment mode //cfg->dma_streamTX->CR &= ~(1<<8); // TX circular mode disabled cfg->dma_streamTX->CR |= (1<<6); // TX data transfer direction: Memory-to-Peripheral + + cfg->uart->CR3 |= (1<<7); // enable DMA transmitter (DMAT) +//cfg->dma_streamTX->CR |= (1<<0); // TX enable DMA, done later when needed #endif + + cfg->uart->CR3 |= (1<<6); // enable DMA receiver (DMAR) + cfg->dma_streamRX->CR |= (1<<0); // RX enable DMA } void Serial_ClearData(uint8_t port) @@ -198,21 +208,17 @@ void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uin UART_Config(port, baudrate, USART_IT_IDLE, IDLE_LINE_IT); // configure serial line with or without IDLE Line interrupt - Serial_DMAConfig(port); + Serial_DMA_Config(port); } void Serial_DeConfig(uint8_t port) { Serial_ClearData(port); - Serial[port].dma_streamRX->CR &= ~(1<<0); // disable RX DMA - - Serial_DMAClearInterruptFlagsRX(port); + Serial_DMA_DisableAndClearFlagsRX(port); // disable RX DMA and clear all interrupt flags #ifdef TX_DMA_WRITE - Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA - - Serial_DMAClearInterruptFlagsTX(port); + Serial_DMA_DisableAndClearFlagsTX(port); // disable TX DMA and clear all interrupt flags #endif UART_DeConfig(port); @@ -226,15 +232,16 @@ void Serial_Send_TX(uint8_t port) // setup DMA transfer, and wait for serial Transfer Complete (TX) interrupt in ISR if (dmaL1DataTX[port].wIndex >= dmaL1DataTX[port].rIndex) dmaL1DataTX[port].flag = dmaL1DataTX[port].wIndex - dmaL1DataTX[port].rIndex; - else - dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; // split transfer into 2 parts + else // split transfer into 2 parts + dmaL1DataTX[port].flag = dmaL1DataTX[port].cacheSize - dmaL1DataTX[port].rIndex; + + Serial_DMA_DisableAndClearFlagsTX(port); // disable TX DMA and clear all interrupt flags - Serial[port].dma_streamTX->CR &= ~(1<<0); // disable TX DMA Serial[port].dma_streamTX->M0AR = (uint32_t)(&dmaL1DataTX[port].cache[dmaL1DataTX[port].rIndex]); // TX data start address - Serial[port].dma_streamTX->NDTR = dmaL1DataTX[port].flag; // number of bytes to transfer - Serial_DMAClearInterruptFlagsTX(port); // clear DMA TX interrupt flags - Serial[port].uart->CR1 |= USART_CR1_TCIE; // enable Transfer Complete (TC) serial interrupt - Serial[port].dma_streamTX->CR |= (1<<0); // enable TX DMA + Serial[port].dma_streamTX->NDTR = dmaL1DataTX[port].flag; // number of bytes to transfer + + Serial[port].uart->CR1 |= USART_CR1_TCIE; // enable Transfer Complete (TC) serial interrupt + Serial[port].dma_streamTX->CR |= (1<<0); // enable TX DMA } void Serial_Put(uint8_t port, const char * msg) diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h index 7de1cf6c2e..564fb5ee80 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h @@ -30,7 +30,7 @@ typedef struct } SERIAL_CFG; extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT]; -extern SERIAL_CFG Serial[_UART_CNT]; +extern const SERIAL_CFG Serial[_UART_CNT]; void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate); void Serial_DeConfig(uint8_t port); From 987d945d306b911cd536f928f1f8b11ffab8720a Mon Sep 17 00:00:00 2001 From: digant73 Date: Sat, 30 Sep 2023 21:26:03 +0200 Subject: [PATCH 16/20] minor cleanup --- TFT/src/User/Hal/gd32f20x/Serial.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/TFT/src/User/Hal/gd32f20x/Serial.c b/TFT/src/User/Hal/gd32f20x/Serial.c index 3ac0a9197a..0a67aebc01 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.c +++ b/TFT/src/User/Hal/gd32f20x/Serial.c @@ -33,7 +33,7 @@ void Serial_DMA_DisableAndClearFlagsRX(uint8_t port) { DMA_CHCTL(Serial[port].dma_stream, Serial[port].dma_channelRX) &= ~(1<<0); // disable RX DMA - // Table 12.5.2, 4 bits per channel, shift = channel * 4 + // table 12.5.2, 4 bits per channel, shift = channel * 4 switch (port) { @@ -70,7 +70,7 @@ void Serial_DMA_DisableAndClearFlagsTX(uint8_t port) { DMA_CHCTL(Serial[port].dma_stream, Serial[port].dma_channelTX) &= ~(1<<0); // disable TX DMA - // Table 12.5.2, 4 bits per channel, shift = channel * 4 + // table 12.5.2, 4 bits per channel, shift = channel * 4 switch (port) { @@ -114,6 +114,8 @@ void Serial_DMA_Config(uint8_t port) DMA_CHMADDR(cfg->dma_stream, cfg->dma_channelRX) = (uint32_t)(dmaL1DataRX[port].cache); // RX destination address (memory) DMA_CHCNT(cfg->dma_stream, cfg->dma_channelRX) = (uint32_t)(dmaL1DataRX[port].cacheSize); // RX buffer size + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) = 0; // RX clear control register + // primary serial port priority at highest level (TX higher than RX) if (port == SERIAL_PORT) DMA_CHCTL(cfg->dma_stream, cfg->dma_channelRX) |= (2<<12); // RX priority level: High @@ -132,6 +134,8 @@ void Serial_DMA_Config(uint8_t port) DMA_CHPADDR(cfg->dma_stream, cfg->dma_channelTX) = (uint32_t)(&USART_DATA(cfg->uart)); // TX peripheral address (usart) + DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) = 0; // TX clear control register + // primary serial port priority at highest level (TX higher than RX) if (port == SERIAL_PORT) DMA_CHCTL(cfg->dma_stream, cfg->dma_channelTX) |= (3<<12); // TX priority level: Very high From 38e901e76ae99618a6397be08a852556b14ac501 Mon Sep 17 00:00:00 2001 From: digant73 Date: Mon, 2 Oct 2023 14:09:35 +0200 Subject: [PATCH 17/20] minor cleanup --- TFT/src/User/API/SerialConnection.c | 2 +- TFT/src/User/Hal/gd32f20x/Serial.h | 2 +- TFT/src/User/Hal/stm32f10x/Serial.h | 2 +- TFT/src/User/Hal/stm32f2_f4xx/Serial.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TFT/src/User/API/SerialConnection.c b/TFT/src/User/API/SerialConnection.c index b8e2047540..532254420e 100644 --- a/TFT/src/User/API/SerialConnection.c +++ b/TFT/src/User/API/SerialConnection.c @@ -142,7 +142,7 @@ void Serial_Forward(SERIAL_PORT_INDEX portIndex, const char * msg) bool Serial_NewDataAvailable(uint8_t port) { - // NOTE: used 32 bit variables for performance reasons (in particular for data copy) + // NOTE: used 32 bit variables for performance reasons // wIndex: update L1 cache's writing index (dynamically changed (by L1 cache's interrupt handler) variables/attributes) // and make a static access (32 bit) to it to speedup performance on this function diff --git a/TFT/src/User/Hal/gd32f20x/Serial.h b/TFT/src/User/Hal/gd32f20x/Serial.h index 35e30eef71..d66960ba5a 100644 --- a/TFT/src/User/Hal/gd32f20x/Serial.h +++ b/TFT/src/User/Hal/gd32f20x/Serial.h @@ -5,7 +5,7 @@ #include "variants.h" // for uint32_t etc... #include "uart.h" -// uncomment this line to use TX DMA based serial writing. Otherwise, TX interrupt based serial writing will be used +// comment out this line to use TX interrupt based serial writing instead of TX DMA based serial writing #define TX_DMA_WRITE typedef struct diff --git a/TFT/src/User/Hal/stm32f10x/Serial.h b/TFT/src/User/Hal/stm32f10x/Serial.h index 594528a6d5..bd51bf52a0 100644 --- a/TFT/src/User/Hal/stm32f10x/Serial.h +++ b/TFT/src/User/Hal/stm32f10x/Serial.h @@ -5,7 +5,7 @@ #include "variants.h" // for uint32_t etc... #include "uart.h" -// uncomment this line to use TX DMA based serial writing. Otherwise, TX interrupt based serial writing will be used +// comment out this line to use TX interrupt based serial writing instead of TX DMA based serial writing #define TX_DMA_WRITE typedef struct diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h index 564fb5ee80..a1bdbdc6c8 100644 --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h @@ -5,7 +5,7 @@ #include "variants.h" // for uint32_t etc... #include "uart.h" -// uncomment this line to use TX DMA based serial writing. Otherwise, TX interrupt based serial writing will be used +// comment out this line to use TX interrupt based serial writing instead of TX DMA based serial writing #define TX_DMA_WRITE typedef struct From 795cfb6ff2263569dedd5f42919055e3c32abb16 Mon Sep 17 00:00:00 2001 From: digant73 Date: Mon, 2 Oct 2023 14:22:02 +0200 Subject: [PATCH 18/20] minor cleanup --- TFT/src/User/Configuration.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/TFT/src/User/Configuration.h b/TFT/src/User/Configuration.h index 0e16056daa..efc9266f7a 100644 --- a/TFT/src/User/Configuration.h +++ b/TFT/src/User/Configuration.h @@ -30,11 +30,11 @@ * P2: WIFI (e.g. ESP3D) * P3: UART 3 (e.g. OctoPrint) * P4: UART 4 - * Value range: P1: [min: 1, max: 9] - * P2: [min: 0, max: 9] - * P3: [min: 0, max: 9] - * P4: [min: 0, max: 9] - * Options: [OFF (port disabled): 0, 2400: 1, 9600: 2, 19200: 3, 38400: 4, 57600: 5, 115200: 6, 250000: 7, 500000: 8, 1000000: 9] + * Value range: P1: [min: 1, max: 11] + * P2: [min: 0, max: 11] + * P3: [min: 0, max: 11] + * P4: [min: 0, max: 11] + * Options: [OFF (port disabled): 0, 2400: 1, 9600: 2, 19200: 3, 38400: 4, 57600: 5, 115200: 6, 230400: 7, 250000: 8, 500000: 9, 921600: 10, 1000000: 11] */ #define SP_1 6 // Default: 6 #define SP_2 0 // Default: 0 @@ -1117,7 +1117,7 @@ * Monitoring Debug * Uncomment/Enable to monitor/show system resources usage in Monitoring menu. */ -//#define DEBUG_MONITORING // Default: commented (disabled) +#define DEBUG_MONITORING // Default: commented (disabled) /** * Generic Debug @@ -1350,7 +1350,7 @@ * Enable alternative Move Menu Buttons Layout matching the direction * of actual printer axis Update the icons from alternate icon folder. */ -#define ALTERNATIVE_MOVE_MENU // Default: uncommented (enabled) +//#define ALTERNATIVE_MOVE_MENU // Default: uncommented (enabled) /** * Friendly Z Offset Language @@ -1431,7 +1431,7 @@ * Uncomment to enable a progress bar with 10% markers. * Comment to enable a standard progress bar. */ -//#define MARKED_PROGRESS_BAR // Default: commented (disabled) +#define MARKED_PROGRESS_BAR // Default: commented (disabled) /** * Live Text Common Color Layout (Status Screen menu) From 2c956b4402e0c06557ee4990651b82efa3688a5c Mon Sep 17 00:00:00 2001 From: digant73 Date: Wed, 4 Oct 2023 18:51:20 +0200 Subject: [PATCH 19/20] minor cleanup --- TFT/src/User/API/Notification.c | 2 +- TFT/src/User/Menu/Popup.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/TFT/src/User/API/Notification.c b/TFT/src/User/API/Notification.c index dededf3055..cdd5577ff7 100644 --- a/TFT/src/User/API/Notification.c +++ b/TFT/src/User/API/Notification.c @@ -145,7 +145,7 @@ void addNotification(DIALOG_TYPE style, char *title, char *text, bool ShowDialog } // store message - msglist[nextMsgIndex].style = style; + msglist[nextMsgIndex].style = style; strncpy_no_pad(msglist[nextMsgIndex].text, text, MAX_MSG_LENGTH); strncpy_no_pad(msglist[nextMsgIndex].title, title, MAX_MSG_TITLE_LENGTH); diff --git a/TFT/src/User/Menu/Popup.c b/TFT/src/User/Menu/Popup.c index ca07b9ad17..181c0380b2 100644 --- a/TFT/src/User/Menu/Popup.c +++ b/TFT/src/User/Menu/Popup.c @@ -62,6 +62,8 @@ void windowReDrawButton(uint8_t position, uint8_t pressed) void popupDrawPage(DIALOG_TYPE type, BUTTON * btn, const uint8_t * title, const uint8_t * context, const uint8_t * yes, const uint8_t * no) { + window.type = type; // window.type is used by GUI_DrawWindow() function so it must be set before the function invokation + if (btn != NULL) { buttonNum = 0; @@ -90,7 +92,6 @@ void popupDrawPage(DIALOG_TYPE type, BUTTON * btn, const uint8_t * title, const } TSC_ReDrawIcon = windowReDrawButton; - window.type = type; } void menuDialog(void) From 306b1a57fc30187771c6b58bad091432e0308d4a Mon Sep 17 00:00:00 2001 From: digant73 Date: Mon, 23 Oct 2023 10:46:02 +0200 Subject: [PATCH 20/20] update --- TFT/src/User/API/parseACK.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TFT/src/User/API/parseACK.c b/TFT/src/User/API/parseACK.c index 9add4e9968..607d539cf1 100644 --- a/TFT/src/User/API/parseACK.c +++ b/TFT/src/User/API/parseACK.c @@ -937,10 +937,14 @@ void parseACK(void) BUZZER_PLAY(SOUND_SUCCESS); if (infoMachineSettings.EEPROM == 1) + { popupDialog(DIALOG_TYPE_SUCCESS, LABEL_DELTA_CONFIGURATION, LABEL_EEPROM_SAVE_INFO, LABEL_CONFIRM, LABEL_CANCEL, saveEepromSettings, NULL, NULL); + } else + { popupReminder(DIALOG_TYPE_SUCCESS, LABEL_DELTA_CONFIGURATION, LABEL_PROCESS_COMPLETED); + } } #endif