Xenomai: Hard Real Time Serial Port (RS232) Example with RTDM (Real Time Driver Model)
UPDATE: This example now works on Xenomai 2.2.0
This is a simple Xenomai (formerly RTAI/fusion) hard real time serial port example. It will send some characters over the serial port,
and if something is received, those characters are displayed.
It was developed using the Atmel ATmega16 board, which
will just return any character sent to it via the serial port.
Or use a simple RS232 dummy adapter (loopback plug): Connect PIN 2 (RX) to PIN 3 (TX) (9-pin SUB-D RS232 connector on your machine),
or if you use a 25-pin SUB-D connector: Connect PIN 3 (RX) to PIN 2 (TX).
Tested on Xenomai 2.2.0.
Also see:
RTAI serial port example (classic driver)
RTAI 3.3: Hard Real Time Serial Port (RS232) Example with RTDM (Real Time Driver Model)
Serial (RS232) Port Pins and Registers
RTAI versus Xenomai comparison on the Serial Port Example with RTDM (Real Time Driver Model)
UPDATES:
rt_task_set_mode() is usually not needed - it was needed for the example to work on Xenomai 2.0.
Anyway, never ever use printf in production RT code - it breaks real time.
Also see: Xenomai 2.1 and 2.2 Notes
rt_timer_start and rt_timer_stop is deprecated in Xenomai 2.1 and up. Just leave it out.
The timer is managed by the skins - it is started when the first skin is loaded.
The aperiodic timer is active by default and the periodic isn't even compiled in. If you need the periodic
timer, compile it in (see Xenomai->Nucleus->Timing in the kernel config).
Also see: Xenomai 2.1 and 2.2 Notes
There seems to be a minor compilation problem with the 16550A serial kernel module with Xeno 2.2.0
("couldn't find the kernel version the module was compiled for"), if you compile
it as module. It is actually more convinient to have it as module, since you can configure the
serial ports without having to reboot the machine.
In order to recompile the serial module, go to
/usr/src/xenomai-2.2.0/ksrc/drivers/16550A
and use this Makefile
obj-m := 16550A.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
EXTRA_CFLAGS := -I/usr/realtime/include -I/usr/include/
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
to recompile the module. Instead of modprobe xeno_16550A.o or xeno_16550A.ko, use insmod ./16550A.o or ko.
-UPDATES-END-
Compile the example with the Makefile, load stuff with loadmods.sh (remove with rmmods.sh)
and run "rt_serial_uprog". Make sure you have some device returning any character hooked up on the serial port,
otherwise you won't see any characters received.
You'll see this on your console:
# ./loadmods.sh
# ./rt_serial_uprog
rt_serial_uprog: PRESS CTRL-C to EXIT
rt_serial_uprog: timer started
rt_serial_uprog: rtser_file opened
rt_serial_uprog: serial-port-config written
rt_serial_uprog: read-task created
rt_serial_uprog: write-task created
rt_serial_uprog: starting write-task
rt_serial_uprog: starting read-task
write_task: rt_dev_write written=17 sz=17
read_task : rt_dev_read=CAPTAIN WAS HERE
write_task: rt_dev_write written=17 sz=17
read_task : rt_dev_read=CAPTAIN WAS HERE
If you have problems, check the Xenomai serial port troubleshooting howto.
rt_serial_uprog.c
// derived from:
// http://www.rts.uni-hannover.de/mitarbeiter/kiszka/rtaddon/
// rtserial documentation:
// http://download.gna.org/xenomai/documentation/html/api/group__rtserial.html
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <native/task.h>
#include <native/timer.h>
#include <rtdm/rtserial.h>
#define LOG_PREFIX "rt_serial_uprog: "
#define WTASK_PREFIX "write_task: "
#define RTASK_PREFIX "read_task : "
#define RTSER_FILE "rtser0"
#define READ_FILE "rtser0"
int timer_started = 0;
int my_fd = -1;
#define STATE_FILE_OPENED 1
#define STATE_TASK_CREATED 2
unsigned int my_state = 0;
// --s-ms-us-ns
RTIME write_task_period_ns = 1000000000llu;
RT_TASK write_task;
RT_TASK read_task;
static const struct rtser_config ser_config = {
0xFFFF, /* config_mask */
9600, /* baud_rate */
RTSER_ODD_PARITY , /* parity */
RTSER_8_BITS , /* data_bits */
RTSER_1_STOPB , /* stop_bits */
RTSER_NO_HAND , /* handshake */
RTSER_DEF_FIFO_DEPTH, /* fifo_depth*/
RTSER_DEF_TIMEOUT, /* rx_timeout */
RTSER_DEF_TIMEOUT, /* tx_timeout */
RTSER_DEF_TIMEOUT, /* event_timeout */
RTSER_RX_TIMESTAMP_HISTORY, /* timestamp_history */
RTSER_EVENT_RXPEND /* event mask */
};
static int close_file( int fd, unsigned char *name) {
int ret,i=0;
do {
i++;
ret = rt_dev_close(fd);
switch(-ret){
case EBADF: printf(LOG_PREFIX "%s -> invalid fd or context\n",name);
break;
case EAGAIN: printf(LOG_PREFIX "%s -> EAGAIN (%d times)\n",name,i);
rt_task_sleep(50000); // wait 50us
break;
case 0: printf(LOG_PREFIX "%s -> closed\n",name);
break;
default: printf(LOG_PREFIX "%s -> ???\n",name);
break;
}
} while (ret == -EAGAIN && i < 10);
return ret;
}
void cleanup_all(void) {
if (my_state & STATE_FILE_OPENED) {
close_file( my_fd, RTSER_FILE " (rtser)");
my_state &= ~STATE_FILE_OPENED;
}
if (my_state & STATE_TASK_CREATED) {
printf(LOG_PREFIX "delete write_task\n");
rt_task_delete(&write_task);
my_state &= ~STATE_TASK_CREATED;
}
if (my_state & STATE_TASK_CREATED) {
printf(LOG_PREFIX "delete read_task\n");
rt_task_delete(&read_task);
my_state &= ~STATE_TASK_CREATED;
}
}
void catch_signal(int sig) {
cleanup_all();
printf(LOG_PREFIX "exit\n");
return;
}
void write_task_proc(void *arg) {
int ret;
ssize_t sz = sizeof(RTIME);
ssize_t written = 0;
unsigned char buf[17] = "CAPTAIN WAS HERE\0";
unsigned long overrun;
ret = rt_task_set_periodic(NULL, TM_NOW, rt_timer_ns2ticks(write_task_period_ns));
if (ret) {
printf(WTASK_PREFIX "error while set periodic, code %d\n",ret);
goto exit_write_task;
}
while (1) {
ret = rt_task_wait_period(&overrun);
if (ret) {
printf(WTASK_PREFIX "error while rt_task_wait_period, code %d\n",ret);
goto exit_write_task;
}
sz = sizeof(buf);
written = rt_dev_write(my_fd, &buf, sizeof(buf));
printf(WTASK_PREFIX "rt_dev_write written=%d sz=%d\n", written, sz);
if (written != sz ) {
if (written < 0 ) {
printf(WTASK_PREFIX "error while rt_dev_write, code %d\n",written);
} else {
printf(WTASK_PREFIX "only %d / %d byte transmitted\n",written, sz);
}
goto exit_write_task;
}
}
exit_write_task:
if (my_state & STATE_FILE_OPENED) {
if (!close_file( my_fd, RTSER_FILE " (write)")) {
my_state &= ~STATE_FILE_OPENED;
}
}
printf(WTASK_PREFIX "exit\n");
}
void read_task_proc(void *arg) {
int ret;
// RTIME irq_time = 0;
ssize_t sz = sizeof(RTIME);
ssize_t red = 0;
struct rtser_event rx_event;
unsigned char buf[17];
while (1) {
/* waiting for event */
ret = rt_dev_ioctl(my_fd, RTSER_RTIOC_WAIT_EVENT, &rx_event );
if (ret) {
printf(RTASK_PREFIX "error while RTSER_RTIOC_WAIT_EVENT, code %d\n",ret);
goto exit_read_task;
}
// if you need a receive timestamp, you can get it this way
//irq_time = rx_event.rxpend_timestamp;
sz = sizeof(buf);
red = rt_dev_read(my_fd, &buf, sizeof(buf));
if (red == sz ) {
printf(RTASK_PREFIX "rt_dev_read=%s\n",buf);
} else {
if (red < 0 ) {
printf(RTASK_PREFIX "error while rt_dev_read, code %d\n",red);
} else {
printf(RTASK_PREFIX "only %d / %d byte received \n",red,sz);
}
goto exit_read_task;
}
}
exit_read_task:
if (my_state & STATE_FILE_OPENED) {
if (!close_file( my_fd, READ_FILE " (rtser)")) {
my_state &= ~STATE_FILE_OPENED;
}
}
printf(RTASK_PREFIX "exit\n");
}
int main(int argc, char* argv[]) {
int ret = 0;
signal(SIGTERM, catch_signal);
signal(SIGINT, catch_signal);
printf(LOG_PREFIX "PRESS CTRL-C to EXIT\n");
/* no memory-swapping for this programm */
mlockall(MCL_CURRENT | MCL_FUTURE);
/* open rtser0 */
my_fd = rt_dev_open( RTSER_FILE, 0);
if (my_fd < 0) {
printf(LOG_PREFIX "can't open %s\n", RTSER_FILE);
goto error;
}
my_state |= STATE_FILE_OPENED;
printf(LOG_PREFIX "rtser_file opened\n");
/* writing write-config */
ret = rt_dev_ioctl(my_fd, RTSER_RTIOC_SET_CONFIG, &ser_config);
if (ret) {
printf(LOG_PREFIX "error while RTSER_RTIOC_SET_CONFIG, code %d\n",ret);
goto error;
}
printf(LOG_PREFIX "serial-port-config written\n");
/* create read_task */
ret = rt_task_create(&read_task,"read_task",0,51,0);
if (ret) {
printf(LOG_PREFIX "failed to create read_task, code %d\n",ret);
goto error;
}
my_state |= STATE_TASK_CREATED;
printf(LOG_PREFIX "read-task created\n");
/* create write_task */
ret = rt_task_create(&write_task,"write_task",0,50,0);
if (ret) {
printf(LOG_PREFIX "failed to create write_task, code %d\n",ret);
goto error;
}
my_state |= STATE_TASK_CREATED;
printf(LOG_PREFIX "write-task created\n");
/* start write_task */
printf(LOG_PREFIX "starting write-task\n");
ret = rt_task_start(&write_task,&write_task_proc,NULL);
if (ret) {
printf(LOG_PREFIX "failed to start write_task, code %d\n",ret);
goto error;
}
/* start read_task */
printf(LOG_PREFIX "starting read-task\n");
ret = rt_task_start(&read_task,&read_task_proc,NULL);
if (ret) {
printf(LOG_PREFIX "failed to start read_task, code %d\n",ret);
goto error;
}
pause();
return 0;
error:
cleanup_all();
return ret;
}
Makefile
TARGET = rt_serial_uprog
ifeq ($(XENODIR),)
XENODIR = /usr/realtime
endif
XENO_LIB_DIR = $(shell $(XENODIR)/bin/xeno-config --library-dir)
CC = $(shell $(XENODIR)/bin/xeno-config --cc)
CFLAGS = -g -I $(XENODIR)/include -lpthread -lnative -lrtdm -L $(XENO_LIB_DIR) -Wall -O2
all: $(TARGET)
$(TARGET): $(TARGET).c
clean:
$(RM) $(TARGET)
loadmods.sh
#!/bin/sh
setserial /dev/ttyS0 uart none
modprobe xeno_16550A.ko ioaddr=0x3f8 irq=4
rmmods.sh
#!/bin/sh
rmmod xeno_16550A.ko
Last-Modified: Thu, 20 Jul 2006 17:22:54 GMT