aboutsummaryrefslogtreecommitdiffstats
path: root/src/datenklo/uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/datenklo/uart.c')
-rw-r--r--src/datenklo/uart.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/datenklo/uart.c b/src/datenklo/uart.c
new file mode 100644
index 0000000..1521410
--- /dev/null
+++ b/src/datenklo/uart.c
@@ -0,0 +1,166 @@
+/* Software UART
+ *
+ * (C) 2019 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../libdebug/debug.h"
+#include "uart.h"
+
+static uint32_t calc_parity(uint32_t data, uint8_t data_bits, enum uart_parity parity)
+{
+ int i;
+
+ for (i = 0; i < data_bits; i++)
+ parity |= (data >> i);
+ parity &= 1;
+
+ switch (parity) {
+ case UART_PARITY_NONE:
+ case UART_PARITY_SPACE:
+ return 0;
+ case UART_PARITY_MARK:
+ return 1;
+ case UART_PARITY_EVEN:
+ return parity;
+ case UART_PARITY_ODD:
+ return parity ^ 1;
+ }
+
+ return 0; /* never reached */
+}
+
+int uart_init(uart_t *uart, void *inst, uint8_t data_bits, enum uart_parity parity, uint8_t stop_bits, int (*tx_cb)(void *inst), void (*rx_cb)(void *inst, int data, uint32_t flags))
+{
+ memset(uart, 0, sizeof(*uart));
+
+ uart->inst = inst;
+ uart->tx_cb = tx_cb;
+ uart->rx_cb = rx_cb;
+ uart->data_bits = data_bits;
+ if (uart->data_bits > 9) {
+ PDEBUG(DUART, DEBUG_ERROR, "Illegal number of data bits, please fix!\n");
+ abort();
+ }
+ uart->parity = parity;
+ uart->stop_bits = stop_bits;
+ if (uart->stop_bits < 1 || uart->stop_bits > 2) {
+ PDEBUG(DUART, DEBUG_ERROR, "Illegal number of stop bits, please fix!\n");
+ abort();
+ }
+ uart->tx_pos = -1;
+ uart->rx_pos = -1;
+ uart->length = uart->stop_bits + !!uart->parity + uart->data_bits;
+
+ return 0;
+}
+
+/* called by modulator to get next bit from uart */
+int uart_tx_bit(uart_t *uart)
+{
+ uint32_t bit, parity;
+
+ if (uart->tx_pos < 0) {
+ /* no transmission, get data */
+ uart->tx_data = uart->tx_cb(uart->inst);
+ /* return 1, if no data has not be sent */
+ if (uart->tx_data > 0x7fffffff)
+ return 1;
+ /* all bits after data are stop bits */
+ uart->tx_data |= 0xffffffff << uart->data_bits;
+ /* calculate parity */
+ if (uart->parity)
+ parity = calc_parity(uart->tx_data, uart->data_bits, uart->parity);
+ /* add parity bit */
+ if (uart->parity) {
+ /* erase bit for parity */
+ uart->tx_data ^= 1 << uart->data_bits;
+ /* put parity bit */
+ uart->tx_data |= parity << uart->data_bits;
+ }
+ /* start with the first bit */
+ uart->tx_pos = 0;
+ /* return start bit */
+ return 0;
+ }
+ /* get bit to be send */
+ bit = (uart->tx_data >> uart->tx_pos) & 1;
+ /* go to next bit and set tx_pos to -1, if there is no more bit */
+ if (++uart->tx_pos == uart->length)
+ uart->tx_pos = -1;
+ /* return bit */
+ return bit;
+}
+
+int uart_is_tx(uart_t *uart)
+{
+ if (uart->tx_pos >= 0)
+ return 1;
+ return 0;
+}
+
+/* called by demodulator to indicate bit for uart */
+void uart_rx_bit(uart_t *uart, int bit)
+{
+ uint32_t flags = 0;
+ uint32_t parity;
+
+ bit &= 1;
+
+ /* if no data is receivd, check for start bit */
+ if (uart->rx_pos < 0) {
+ /* if no start bit */
+ if (bit != 0 || uart->last_bit != 1)
+ goto out;
+ /* start bit */
+ uart->rx_data = 0;
+ uart->rx_pos = 0;
+ return;
+ }
+ /* shift bit */
+ uart->rx_data |= bit << (uart->rx_pos);
+ /* end of transmission */
+ if (++uart->rx_pos == uart->length) {
+ /* turn off reception */
+ uart->rx_pos = -1;
+ /* check if parity is invalid */
+ if (uart->parity) {
+ parity = calc_parity(uart->rx_data, uart->data_bits, uart->parity);
+ if (((uart->rx_data >> uart->data_bits) & 1) != parity)
+ flags |= UART_PARITY_ERROR;
+ }
+ /* check if last stop bit is invalid */
+ if (((uart->rx_data >> (uart->length - 1)) & 1) == 0) {
+ flags |= UART_CODE_VIOLATION;
+ }
+ /* check if all bits are 0 */
+ if (!uart->rx_data) {
+ flags |= UART_BREAK;
+ }
+ /* clear all bits after data */
+ uart->rx_data &= ~(0xffffffff << uart->data_bits);
+ uart->rx_cb(uart->inst, uart->rx_data, flags);
+ }
+
+out:
+ /* remember last bit for start bit detection (1 -> 0 transition) */
+ uart->last_bit = bit;
+}
+