Modem emulator for "Datenklo" with AM7910/AM7911 emulation
This commit is contained in:
166
src/datenklo/uart.c
Normal file
166
src/datenklo/uart.c
Normal file
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user