Skip to content

Commit 16a584d

Browse files
committed
stmhal/uart: Provide a custom function to transmit over UART.
The HAL_UART_Transmit function has changed in the latest HAL version such that the Timeout is a timeout for the entire function, rather than a timeout between characters as it was before. The HAL function also does not allow one to reliably tell how many characters were sent before the timeout (if a timeout occurred). This patch provides a custom function to do UART transmission, completely replacing the HAL version, to fix the above-mentioned issues.
1 parent 65574f8 commit 16a584d

1 file changed

Lines changed: 84 additions & 34 deletions

File tree

stmhal/uart.c

Lines changed: 84 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -379,27 +379,89 @@ STATIC bool uart_tx_wait(pyb_uart_obj_t *self, uint32_t timeout) {
379379
}
380380
}
381381

382-
STATIC HAL_StatusTypeDef uart_tx_data(pyb_uart_obj_t *self, uint8_t *data, uint16_t len) {
382+
// Waits at most timeout milliseconds for UART flag to be set.
383+
// Returns true if flag is/was set, false on timeout.
384+
STATIC bool uart_wait_flag_set(pyb_uart_obj_t *self, uint32_t flag, uint32_t timeout) {
385+
// Note: we don't use WFI to idle in this loop because UART tx doesn't generate
386+
// an interrupt and the flag can be set quickly if the baudrate is large.
387+
uint32_t start = HAL_GetTick();
388+
for (;;) {
389+
if (__HAL_UART_GET_FLAG(&self->uart, flag)) {
390+
return true;
391+
}
392+
if (timeout == 0 || HAL_GetTick() - start >= timeout) {
393+
return false; // timeout
394+
}
395+
}
396+
}
397+
398+
// src - a pointer to the data to send (16-bit aligned for 9-bit chars)
399+
// num_chars - number of characters to send (9-bit chars count for 2 bytes from src)
400+
// *errcode - returns 0 for success, MP_Exxx on error
401+
// returns the number of characters sent (valid even if there was an error)
402+
STATIC size_t uart_tx_data(pyb_uart_obj_t *self, const void *src_in, size_t num_chars, int *errcode) {
403+
if (num_chars == 0) {
404+
*errcode = 0;
405+
return 0;
406+
}
407+
408+
uint32_t timeout;
383409
if (self->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) {
384410
// CTS can hold off transmission for an arbitrarily long time. Apply
385411
// the overall timeout rather than the character timeout.
386-
return HAL_UART_Transmit(&self->uart, data, len, self->timeout);
387-
}
388-
// The timeout specified here is for waiting for the TX data register to
389-
// become empty (ie between chars), as well as for the final char to be
390-
// completely transferred. The default value for timeout_char is long
391-
// enough for 1 char, but we need to double it to wait for the last char
392-
// to be transferred to the data register, and then to be transmitted.
393-
return HAL_UART_Transmit(&self->uart, data, len, 2 * self->timeout_char);
412+
timeout = self->timeout;
413+
} else {
414+
// The timeout specified here is for waiting for the TX data register to
415+
// become empty (ie between chars), as well as for the final char to be
416+
// completely transferred. The default value for timeout_char is long
417+
// enough for 1 char, but we need to double it to wait for the last char
418+
// to be transferred to the data register, and then to be transmitted.
419+
timeout = 2 * self->timeout_char;
420+
}
421+
422+
const uint8_t *src = (const uint8_t*)src_in;
423+
size_t num_tx = 0;
424+
USART_TypeDef *uart = self->uart.Instance;
425+
426+
while (num_tx < num_chars) {
427+
if (!uart_wait_flag_set(self, UART_FLAG_TXE, timeout)) {
428+
*errcode = MP_ETIMEDOUT;
429+
return num_tx;
430+
}
431+
uint32_t data;
432+
if (self->char_width == CHAR_WIDTH_9BIT) {
433+
data = *((uint16_t*)src) & 0x1ff;
434+
src += 2;
435+
} else {
436+
data = *src++;
437+
}
438+
#if defined(MCU_SERIES_F4)
439+
uart->DR = data;
440+
#else
441+
uart->TDR = data;
442+
#endif
443+
++num_tx;
444+
}
445+
446+
// wait for the UART frame to complete
447+
if (!uart_wait_flag_set(self, UART_FLAG_TC, timeout)) {
448+
*errcode = MP_ETIMEDOUT;
449+
return num_tx;
450+
}
451+
452+
*errcode = 0;
453+
return num_tx;
394454
}
395455

396456
STATIC void uart_tx_char(pyb_uart_obj_t *uart_obj, int c) {
397-
uint8_t ch = c;
398-
uart_tx_data(uart_obj, &ch, 1);
457+
uint16_t ch = c;
458+
int errcode;
459+
uart_tx_data(uart_obj, &ch, 1, &errcode);
399460
}
400461

401462
void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len) {
402-
uart_tx_data(uart_obj, (uint8_t*)str, len);
463+
int errcode;
464+
uart_tx_data(uart_obj, str, len, &errcode);
403465
}
404466

405467
void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len) {
@@ -806,15 +868,15 @@ STATIC mp_obj_t pyb_uart_writechar(mp_obj_t self_in, mp_obj_t char_in) {
806868
uint16_t data = mp_obj_get_int(char_in);
807869

808870
// write the character
809-
HAL_StatusTypeDef status;
871+
int errcode;
810872
if (uart_tx_wait(self, self->timeout)) {
811-
status = uart_tx_data(self, (uint8_t*)&data, 1);
873+
uart_tx_data(self, &data, 1, &errcode);
812874
} else {
813-
status = HAL_TIMEOUT;
875+
errcode = MP_ETIMEDOUT;
814876
}
815877

816-
if (status != HAL_OK) {
817-
mp_hal_raise(status);
878+
if (errcode != 0) {
879+
mp_raise_OSError(errcode);
818880
}
819881

820882
return mp_const_none;
@@ -933,24 +995,12 @@ STATIC mp_uint_t pyb_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t
933995
}
934996

935997
// write the data
936-
HAL_StatusTypeDef status = uart_tx_data(self, (uint8_t*)buf, size >> self->char_width);
937-
938-
if (status == HAL_OK) {
939-
// return number of bytes written
940-
return size;
941-
} else if (status == HAL_TIMEOUT) { // UART_WaitOnFlagUntilTimeout() disables RXNE interrupt on timeout
942-
if (self->read_buf_len > 0) {
943-
__HAL_UART_ENABLE_IT(&self->uart, UART_IT_RXNE); // re-enable RXNE
944-
}
945-
// return number of bytes written
946-
if (self->char_width == CHAR_WIDTH_8BIT) {
947-
return size - self->uart.TxXferCount - 1;
948-
} else {
949-
int written = self->uart.TxXferCount * 2;
950-
return size - written - 2;
951-
}
998+
size_t num_tx = uart_tx_data(self, buf, size >> self->char_width, errcode);
999+
1000+
if (*errcode == 0 || *errcode == MP_ETIMEDOUT) {
1001+
// return number of bytes written, even if there was a timeout
1002+
return num_tx << self->char_width;
9521003
} else {
953-
*errcode = mp_hal_status_to_errno_table[status];
9541004
return MP_STREAM_ERROR;
9551005
}
9561006
}

0 commit comments

Comments
 (0)