this is another UART implementation I made to learn some basics about XMOS programming. It was made to be part of a command line interface for my software. Not all features of the CLI are fully working yet, that's why it's not open source yet.
I put the UART implementation here because it may be useful for others. It was made with low resource and code size in mind while having a reasonable performance. If you think that something should or may be improved, please post it here.
More details can be found in the comments.
Have fun,
Thomas
(wanted to attach it as file, but *.xc and *.txt are not allowed :/ )
Code: Select all
/*
* Low resource UART implementation.
* Version 0.1
*
* (c) 2010 Thomas 'skoe' Giesel
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Thomas Giesel skoe@directbox.com
*
* Usage:
* Create a streaming channel between a thread (e.g. cli_thread) and
* uart_thread. Input from UART is always and only sent to cli_thread.
* Any other thread can send characters to UART by calling uart_putc().
* This function allocates a temporary channel end, sends the character
* and a stop token and releases the temporary channel end.
*
* -------------- ---------------
* | cli_thread | streaming chan | uart_thread |
* | |<=========================>| |
* | | =========>| |
* -------------- / ---------------
* uart_putc()
* /
* --------------
* | any_thread |-
* | ||
* --------------|
* --------------
*
* cli_thread can send characters to uart_thread directly, each character
* must be sent as int and terminated with a stop token:
* {
* int c = 'a';
* uart_ce <: c;
* soutct(uart_ce, XS1_CT_END);
* }
*
* Bytes received from UART are sent to the channel without stop tokens.
* The receiver thread can read them or generate events from them (select)
* directly:
* {
* int c;
* uart_ce :> c;
* }
*
* This version was tested with up to 500 kbps with compiler optimizations
* enabled. When all 8 threads are in use, possibly only lower speeds can
* be reached.
*
* I didn't fetch my logic analyser to check if everything is okay end where
* the timing could still be improved. Mhh, this could be done with the XMOS
* simulation and analysis tools, I guess.
*/
#include <xs1.h>
#include <autoconf.h>
#include <uart.h>
#define BIT_RATE 115200
//#define BIT_RATE 500000
#define BIT_TIME ((XS1_TIMER_HZ + BIT_RATE / 2) / BIT_RATE)
// Ports for RX and TX, must be 1 bit ports
out port txd = PORT_UART_TX;
in port rxd = PORT_UART_RX;
// copy of channel end id for uart thread
static unsigned uart_chanend;
/*******************************************************************************
* This is the UART thread. Really.
*
******************************************************************************/
void uart_thread(streaming chanend ce)
{
/*
* State for TX: We put also the stop bit into this sequence. We send and
* shift this value until it becomes 0. Then all bits have been sent. When
* the last bit is shifted out, we may start to send the next byte
* imediately. That's why the last bit is set two times to get a whole
* stop bit.
*/
unsigned tx_value = 0;
/*
* State for RX: Wenn we receive a start bit, we put 1 << 31 into this
* variable. Each time we get a bit we shift it into this value until the
* the former 1 << 31 reached position 1 << 22. Then the RX byte incl.
* stop bit is complete.
*/
unsigned rx_value = 0;
int t_next;
int t_next_rx;
int t_next_tx;
timer t;
asm (
"stw %0, dp[uart_chanend] \n"
: : "r"(ce)
);
// Make sure the tx line is high (idle) for a while
txd <: 1;
t :> t_next;
t_next += 10 * BIT_TIME;
t when timerafter(t_next) :> void;
for (;;)
{
select
{
case !tx_value => ce :> tx_value:
// discard stop token
sinct(ce);
// add one stop bit, it is set two times
// because we have no pause after the last bit in the loop below
tx_value = (tx_value & 0xff) | 0x300;
// this order because the next bits will also have overhead
t :> t_next_tx;
t_next_tx += BIT_TIME;
// start bit
txd <: 0;
// check which will be the next timed event
if (rx_value && t_next_tx - t_next_rx > 0)
t_next = t_next_rx;
else
t_next = t_next_tx;
break;
case !rx_value => rxd when pinsneq(1) :> void:
t :> t_next_rx;
t_next_rx += BIT_TIME + BIT_TIME / 2;
// mark that rx is in progress
rx_value = 1 << 31;
// calculate which will be the next timed event
if (tx_value && t_next_rx - t_next_tx > 0)
t_next = t_next_tx;
else
t_next = t_next_rx;
break;
case tx_value || rx_value => t when timerafter(t_next) :> void:
if (rx_value && t_next == t_next_rx)
{
rxd :> >> rx_value;
// rx complete?
if (rx_value & (1 << 22))
{
ce <: (rx_value >> 23) & 0xff;
rx_value = 0;
}
else
t_next_rx += BIT_TIME;
}
if (tx_value && t_next == t_next_tx)
{
txd <: >> tx_value;
t_next_tx += BIT_TIME;
}
// check which will be the next timed event
if (rx_value)
{
if (tx_value && t_next_rx - t_next_tx > 0)
t_next = t_next_tx;
else
t_next = t_next_rx;
}
else
t_next = t_next_tx;
break;
}
}
}
/*******************************************************************************
* Print the given character to UART. Wait if the queue is full.
* May be called from different threads.
*
******************************************************************************/
void uart_putc(char c)
{
asm
(
// allocate channel end
// todo: check if it was available
"getr r2, 2 \n" /* XS1_RES_TYPE_CHANEND */
// set channel destination
"ldw r3, dp[uart_chanend] \n"
"setd res[r2], r3 \n"
// send byte and end token
"out res[r2], %0 \n"
"outct res[r2], 1 \n" /* XS1_CT_END */
// release channel end
"freer res[r2] \n"
: /* no output */
: "r"(c)
: "r2", "r3"
);
// avoid warning
(void)uart_chanend;
}