summaryrefslogtreecommitdiffstatshomepage
path: root/stmhal
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2016-12-23 15:16:26 +1100
committerDamien George <damien.p.george@gmail.com>2016-12-28 17:18:59 +1100
commit16a584d7cf7a46248ca308bdabb21a073d33ce80 (patch)
tree1a529de6f2f9410771610e31a79f2cd3decca2b1 /stmhal
parent65574f817a949fea2316c335da866c1b8d20f1fb (diff)
downloadmicropython-16a584d7cf7a46248ca308bdabb21a073d33ce80.tar.gz
micropython-16a584d7cf7a46248ca308bdabb21a073d33ce80.zip
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.
Diffstat (limited to 'stmhal')
-rw-r--r--stmhal/uart.c118
1 files changed, 84 insertions, 34 deletions
diff --git a/stmhal/uart.c b/stmhal/uart.c
index 784b38a684..6582bda00e 100644
--- a/stmhal/uart.c
+++ b/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);
- }
- // 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);
+ 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;
+ }
+
+ 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);
-
- 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;
- }
+ size_t num_tx = uart_tx_data(self, buf, size >> self->char_width, errcode);
+
+ 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;
}
}