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.
This commit is contained in:
parent
65574f817a
commit
16a584d7cf
114
stmhal/uart.c
114
stmhal/uart.c
@ -379,27 +379,89 @@ STATIC bool uart_tx_wait(pyb_uart_obj_t *self, uint32_t timeout) {
|
||||
}
|
||||
}
|
||||
|
||||
STATIC HAL_StatusTypeDef uart_tx_data(pyb_uart_obj_t *self, uint8_t *data, uint16_t len) {
|
||||
// Waits at most timeout milliseconds for UART flag to be set.
|
||||
// Returns true if flag is/was set, false on timeout.
|
||||
STATIC bool uart_wait_flag_set(pyb_uart_obj_t *self, uint32_t flag, uint32_t timeout) {
|
||||
// Note: we don't use WFI to idle in this loop because UART tx doesn't generate
|
||||
// an interrupt and the flag can be set quickly if the baudrate is large.
|
||||
uint32_t start = HAL_GetTick();
|
||||
for (;;) {
|
||||
if (__HAL_UART_GET_FLAG(&self->uart, flag)) {
|
||||
return true;
|
||||
}
|
||||
if (timeout == 0 || HAL_GetTick() - start >= timeout) {
|
||||
return false; // timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// src - a pointer to the data to send (16-bit aligned for 9-bit chars)
|
||||
// num_chars - number of characters to send (9-bit chars count for 2 bytes from src)
|
||||
// *errcode - returns 0 for success, MP_Exxx on error
|
||||
// returns the number of characters sent (valid even if there was an error)
|
||||
STATIC size_t uart_tx_data(pyb_uart_obj_t *self, const void *src_in, size_t num_chars, int *errcode) {
|
||||
if (num_chars == 0) {
|
||||
*errcode = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t timeout;
|
||||
if (self->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) {
|
||||
// CTS can hold off transmission for an arbitrarily long time. Apply
|
||||
// the overall timeout rather than the character timeout.
|
||||
return HAL_UART_Transmit(&self->uart, data, len, self->timeout);
|
||||
timeout = self->timeout;
|
||||
} else {
|
||||
// The timeout specified here is for waiting for the TX data register to
|
||||
// become empty (ie between chars), as well as for the final char to be
|
||||
// completely transferred. The default value for timeout_char is long
|
||||
// enough for 1 char, but we need to double it to wait for the last char
|
||||
// to be transferred to the data register, and then to be transmitted.
|
||||
timeout = 2 * self->timeout_char;
|
||||
}
|
||||
// The timeout specified here is for waiting for the TX data register to
|
||||
// become empty (ie between chars), as well as for the final char to be
|
||||
// completely transferred. The default value for timeout_char is long
|
||||
// enough for 1 char, but we need to double it to wait for the last char
|
||||
// to be transferred to the data register, and then to be transmitted.
|
||||
return HAL_UART_Transmit(&self->uart, data, len, 2 * self->timeout_char);
|
||||
|
||||
const uint8_t *src = (const uint8_t*)src_in;
|
||||
size_t num_tx = 0;
|
||||
USART_TypeDef *uart = self->uart.Instance;
|
||||
|
||||
while (num_tx < num_chars) {
|
||||
if (!uart_wait_flag_set(self, UART_FLAG_TXE, timeout)) {
|
||||
*errcode = MP_ETIMEDOUT;
|
||||
return num_tx;
|
||||
}
|
||||
uint32_t data;
|
||||
if (self->char_width == CHAR_WIDTH_9BIT) {
|
||||
data = *((uint16_t*)src) & 0x1ff;
|
||||
src += 2;
|
||||
} else {
|
||||
data = *src++;
|
||||
}
|
||||
#if defined(MCU_SERIES_F4)
|
||||
uart->DR = data;
|
||||
#else
|
||||
uart->TDR = data;
|
||||
#endif
|
||||
++num_tx;
|
||||
}
|
||||
|
||||
// wait for the UART frame to complete
|
||||
if (!uart_wait_flag_set(self, UART_FLAG_TC, timeout)) {
|
||||
*errcode = MP_ETIMEDOUT;
|
||||
return num_tx;
|
||||
}
|
||||
|
||||
*errcode = 0;
|
||||
return num_tx;
|
||||
}
|
||||
|
||||
STATIC void uart_tx_char(pyb_uart_obj_t *uart_obj, int c) {
|
||||
uint8_t ch = c;
|
||||
uart_tx_data(uart_obj, &ch, 1);
|
||||
uint16_t ch = c;
|
||||
int errcode;
|
||||
uart_tx_data(uart_obj, &ch, 1, &errcode);
|
||||
}
|
||||
|
||||
void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len) {
|
||||
uart_tx_data(uart_obj, (uint8_t*)str, len);
|
||||
int errcode;
|
||||
uart_tx_data(uart_obj, str, len, &errcode);
|
||||
}
|
||||
|
||||
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) {
|
||||
uint16_t data = mp_obj_get_int(char_in);
|
||||
|
||||
// write the character
|
||||
HAL_StatusTypeDef status;
|
||||
int errcode;
|
||||
if (uart_tx_wait(self, self->timeout)) {
|
||||
status = uart_tx_data(self, (uint8_t*)&data, 1);
|
||||
uart_tx_data(self, &data, 1, &errcode);
|
||||
} else {
|
||||
status = HAL_TIMEOUT;
|
||||
errcode = MP_ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (status != HAL_OK) {
|
||||
mp_hal_raise(status);
|
||||
if (errcode != 0) {
|
||||
mp_raise_OSError(errcode);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// write the data
|
||||
HAL_StatusTypeDef status = uart_tx_data(self, (uint8_t*)buf, size >> self->char_width);
|
||||
size_t num_tx = uart_tx_data(self, buf, size >> self->char_width, errcode);
|
||||
|
||||
if (status == HAL_OK) {
|
||||
// return number of bytes written
|
||||
return size;
|
||||
} else if (status == HAL_TIMEOUT) { // UART_WaitOnFlagUntilTimeout() disables RXNE interrupt on timeout
|
||||
if (self->read_buf_len > 0) {
|
||||
__HAL_UART_ENABLE_IT(&self->uart, UART_IT_RXNE); // re-enable RXNE
|
||||
}
|
||||
// return number of bytes written
|
||||
if (self->char_width == CHAR_WIDTH_8BIT) {
|
||||
return size - self->uart.TxXferCount - 1;
|
||||
} else {
|
||||
int written = self->uart.TxXferCount * 2;
|
||||
return size - written - 2;
|
||||
}
|
||||
if (*errcode == 0 || *errcode == MP_ETIMEDOUT) {
|
||||
// return number of bytes written, even if there was a timeout
|
||||
return num_tx << self->char_width;
|
||||
} else {
|
||||
*errcode = mp_hal_status_to_errno_table[status];
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user