Skip to content

Commit

Permalink
Added TX Interrupt/DMA based serial writing (bigtreetech#2840)
Browse files Browse the repository at this point in the history
  • Loading branch information
digant73 authored Oct 31, 2023
1 parent 7d638de commit a19a095
Show file tree
Hide file tree
Showing 30 changed files with 1,208 additions and 427 deletions.
2 changes: 1 addition & 1 deletion TFT/src/User/API/Notification.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
7 changes: 5 additions & 2 deletions TFT/src/User/API/Printing.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -903,6 +903,9 @@ void loopPrintFromTFT(void)
}
}
}

if (comment_parsing) // parse comment from gcode file
parseComment();
}

if (gcode_count == 0)
Expand Down
64 changes: 15 additions & 49 deletions TFT/src/User/API/RRFSendCmd.c
Original file line number Diff line number Diff line change
@@ -1,58 +1,24 @@
#include "RRFSendCmd.h"
#include "Serial.h"
#include <stdio.h>
#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);
}
126 changes: 84 additions & 42 deletions TFT/src/User/API/SerialConnection.c
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
#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
#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 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_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
};

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)
{
InfoHost_Init(false); // initialize infoHost when 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)
Expand All @@ -53,7 +60,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]]);
}
}
Expand All @@ -64,7 +71,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]]);
}
}
Expand Down Expand Up @@ -133,54 +140,89 @@ void Serial_Forward(SERIAL_PORT_INDEX portIndex, const char * msg)
}
}

uint16_t Serial_GetReadingIndex(SERIAL_PORT_INDEX portIndex)
bool Serial_NewDataAvailable(uint8_t port)
{
if (!WITHIN(portIndex, PORT_1, SERIAL_PORT_COUNT - 1))
// 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
//
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;

return dmaL1Data[portIndex].rIndex;
uint32_t cacheSize = dmaL1DataRX[port].cacheSize;

while (dmaL1DataRX[port].cache[flag] != '\n' && flag != wIndex) // check presence of "\n" in available data
{
flag = (flag + 1) % cacheSize;
}

dmaL1DataRX[port].flag = flag; // update queue's custom flag with flag

// return "true" if "\n" was found (message complete), "False" otherwise
return (flag != wIndex);
}

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)
{
// 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)
return 0;
// 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;

DMA_CIRCULAR_BUFFER * dmaL1Data_ptr = &dmaL1Data[portIndex];
while (cache[rIndex] == ' ' && rIndex != flag) // remove leading empty space, if any
{
rIndex = (rIndex + 1) % cacheSize;
}

// make a static access to dynamically changed (by L1 cache's interrupt handler) variables/attributes
uint16_t wIndex = dmaL1Data_ptr->wIndex;
// msgSize: message size (after updating rIndex removing leading empty spaces). Terminating null character '\0' not included
uint32_t msgSize = (cacheSize + flag - rIndex) % cacheSize + 1;

// L1 cache's reading index (not dynamically changed (by L1 cache's interrupt handler) variables/attributes)
uint16_t rIndex = dmaL1Data_ptr->rIndex;
// 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;

while (dmaL1Data_ptr->cache[rIndex] == ' ' && rIndex != wIndex) // remove leading empty space, if any
// if data is one chunk only, retrieve data from upper part of circular cache
if (rIndex <= flag)
{
rIndex = (rIndex + 1) % dmaL1Data_ptr->cacheSize;
while (rIndex <= flag)
{
*(buf++) = cache[rIndex++];
}
}

for (uint16_t i = 0; i < (bufSize - 1) && rIndex != wIndex; ) // retrieve data until buf is full or L1 cache is empty
else // data at end and beginning of cache
{
buf[i] = dmaL1Data_ptr->cache[rIndex];
rIndex = (rIndex + 1) % dmaL1Data_ptr->cacheSize;

if (buf[i++] == '\n') // if data end marker is found
while (rIndex < cacheSize)
{
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++];
}

return i; // return the number of bytes stored in buf
rIndex = 0;

while (rIndex <= flag)
{
*(buf++) = cache[rIndex++];
}
}

// 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
Expand All @@ -199,7 +241,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);
}
Expand Down
24 changes: 13 additions & 11 deletions TFT/src/User/API/SerialConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -27,12 +27,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
Expand All @@ -55,26 +56,27 @@ 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
// - specific port index: specific serial port
// - msg: message to send
void Serial_Forward(SERIAL_PORT_INDEX portIndex, const char * msg);

// retrieve the next reading index in the message queue of the provided serial port:
// - portIndex: index of serial port
// 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: next reading index
uint16_t Serial_GetReadingIndex(SERIAL_PORT_INDEX portIndex);
// - return value: "true" if a new message is available. "false" otherwise
bool Serial_NewDataAvailable(uint8_t port);

// 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
Expand Down
4 changes: 2 additions & 2 deletions TFT/src/User/API/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions TFT/src/User/API/interfaceCmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit a19a095

Please sign in to comment.