Hello...
As I have near finished driver for EPP boards I have one problem. As
computer is common staretd with CNC machine off the EPP board respond to
al IO with timeout. EPP timeout is aprox 10uSec. So if I wan't to write
5 bytes that can least over 50uSec. And in that case the computer
freeze. The solution seems to be to check EPP timeout after every IO.
That's works but make big overhead. On my EPP board (sem's to be slowest
one) the fastest cycle is 2uS long. The timeout is on aprox 11uS. But
the read from status port (to check timeout) least 1.5uS. So if I get
that approach then I have (for 3/3 byte IO)
6 x 2.0uS 12uS for real IO
6 x 1.5uS 9uS overhead to check Timeout
total 21uS and system got little slower already
Then I realize that checking how long in/out can be faster.
So if write_board routine take more than predetermined time to execute
then execution shall be aborted and checked what's realy wrong.
maybe the best way is to check if out/inp routine take more than 5uS. If
is more then probably is something wrong.
in write_board there is argument PERIOD. I can't find what is this. Time ?!?
I have only RDTSC instruction in mind but afraid to use as can be
dissabled (privileged)
Can someone help??
And here is working (but unfinished) driver hal_eppport.c
/********************************************************************
Description: hal_eppport.c
This file, 'hal_eppport.c', is a HAL component that
provides a driver for generic EPP port.
The drive scheme is simple to use and simple to extend.
Hardvare attached to EPP port should assume all
addres access as acces to byte [0] of board and all
folowed data acces as access to [next] byte.
Example board with 24 outputs and 24 inputs are in
http:// (fixme)
Author: Slavko Kocjancic
License: GPL
Copyright (c) 2010 All rights reserved.
The configuration is determined by a config string passed to
insmod when loading the module. The format consists of a base
address, followed by a parameter I and parameter O telling how many
bytes are in IO space. The parameter is single digit number ranging
from 1 to 9. Whitespace betwen I/O and number are not alowed.
example: insmod eppport cfg="0x278 I2 O3"
The example above is for one card, with its base address
set to hex 278, with 3 byte output (24 bits) and with 2 byte
input (16) bits.
The driver creates HAL pins and parameters for each port pin
as follows:
Each physical output has a correspinding HAL pin, named
'eppport.<boardnum>.out-<pinnum>', and a HAL parameter
'eppport.<boardnum>.out-<pinnum>-invert'.
Each physical input has two corresponding HAL pins, named
'eppport.<boardnum>.in-<pinnum>' and
'eppport.<boardnum>.in-<pinnum>-not'.
<boardnum> is the board number, starting from zero.
<pinnum> is the pin number, formated as two digit number. 1'st
number is byte number 0 to maxbyte-1 and 2'nd number is bit number
from that byte. (ranging from 0 to 7).
The driver exports two HAL functions for each board,
'eppport.<boardnum>.read' and 'eppport.<boardnum>.write'.
*/
/** This program is free software; you can redistribute it and/or
modify it under the terms of version 2 of the GNU General
Public License as published by the Free Software Foundation.
This library 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 library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
ANY HARM OR LOSS RESULTING FROM ITS USE. IT IS _EXTREMELY_ UNWISE
TO RELY ON SOFTWARE ALONE FOR SAFETY. Any machinery capable of
harming persons must have provisions for completely removing power
from all motors, etc, before persons enter any danger area. All
machinery must be designed to comply with local and national safety
codes, and the authors of this software can not, and do not, take
any responsibility for such compliance.
This code was written as part of the EMC HAL project. For more
information, go to www.linuxcnc.org.
*/
#include "rtapi_ctype.h" /* isspace() */
#include "rtapi.h" /* RTAPI realtime OS API */
#include "rtapi_app.h" /* RTAPI realtime module decls */
#include "hal.h" /* HAL public API decls */
/* If FASTIO is defined, uses outb() and inb() from <asm.io>,
instead of rtapi_outb() and rtapi_inb() - the <asm.io> ones
are inlined, and save a microsecond or two (on my 233MHz box)
*/
#define FASTIO
#ifdef FASTIO
#define rtapi_inb inb
#define rtapi_outb outb
#include <asm/io.h>
#endif
/* module information */
MODULE_AUTHOR("Slavko Kocjancic");
MODULE_DESCRIPTION("EPP port Driver for HAL");
MODULE_LICENSE("GPL");
static char *cfg = ""; /* config string, default no boards */
RTAPI_MP_STRING(cfg, "config string");
/***********************************************************************
* STRUCTURES AND GLOBAL VARIABLES *
************************************************************************/
/* this structure contains the runtime data needed by the
driver for a single board
*/
typedef struct {
hal_bit_t *data; /* basic pin for input or output */
union {
hal_bit_t *not; /* pin for inverted data (input only) */
hal_bit_t invert; /* param for inversion (output only) */
} io;
} io_pin_t;
typedef struct {
unsigned short base_addr; /* base I/O address (0x220, etc.) */
unsigned char port_outputs; /* number of output bytes */
unsigned char port_inputs; /* number of input bytes */
unsigned char port_timeout; /* latch for timeout flag for port */
io_pin_t port_Out[72];
io_pin_t port_Inp[72];
} epp_board_t;
/* pointer to array of epp_board_t structs in shared memory, 1 per board */
static epp_board_t *board_array;
/* other globals */
static int comp_id; /* component ID */
static int num_boards; /* number of ports configured */
/***********************************************************************
* LOCAL FUNCTION DECLARATIONS *
************************************************************************/
/* These are the functions that actually do the I/O
everything else is just init code
*/
static void read_board(void *arg, long period);
static void write_board(void *arg, long period);
/* 'pins_and_params()' does most of the work involved in setting up
the driver. It parses the command line (argv[]), then if the
command line is OK, it calls hal_init(), allocates shared memory
for the parport_t data structure(s), and exports pins and parameters
It does not set up functions, since that is handled differently in
realtime and user space.
*/
static int pins_and_params(char *argv[]);
static unsigned short parse_board_addr(char *cp);
static int export_board(int boardnum, epp_board_t * board);
static int export_byte(int boardnum, int pin_num, io_pin_t *pin, int num_pins,
int dir);
static int export_input_pin(int boardnum, int pinnum, io_pin_t *pin);
static int export_output_pin(int boardnum, int pinnum, io_pin_t *pin);
/***********************************************************************
* INIT AND EXIT CODE *
************************************************************************/
#define MAX_BOARDS 8
#define MAX_TOK ((MAX_BOARDS*2)+3)
int rtapi_app_main(void)
{
char *cp;
char *argv[MAX_TOK];
char name[HAL_NAME_LEN + 2];
int n, retval;
/* test for config string */
if ((cfg == 0) || (cfg[0] == '\0')) {
rtapi_print_msg(RTAPI_MSG_ERR, "EPPPORT: ERROR: no config string\n");
return -1;
}
/* as a RT module, we don't get a nice argc/argv command line, we only
get a single string... so we need to tokenize it ourselves */
/* and to make things worse, it seems that insmod under kernel 2.6
ends the config string at the first space, so I added the ability
to use '_' as a token separator. What an ugly hack... HAL needs
a better way to handle insmod time config data */
cp = cfg;
for (n = 0; n < MAX_TOK; n++) {
/* strip leading whitespace or token separators */
while ((*cp != '\0') && ( isspace(*cp) || ( *cp == '_') ))
cp++;
/* mark beginning of token */
argv[n] = cp;
/* find end of token */
while ((*cp != '\0') && !( isspace(*cp) || ( *cp == '_') ))
cp++;
/* mark end of this token, prepare to search for next one */
if (*cp != '\0') {
*cp = '\0';
cp++;
}
}
for (n = 0; n < MAX_TOK; n++) {
/* is token empty? */
if (argv[n][0] == '\0') {
/* yes - make pointer NULL */
argv[n] = NULL;
}
}
/* parse "command line", set up pins and parameters */
retval = pins_and_params(argv);
if (retval != 0) {
return retval;
}
/* export functions for each board */
for (n = 0; n < num_boards; n++) {
/* make read function name */
rtapi_snprintf(name, HAL_NAME_LEN, "eppport.%d.read", n);
/* export read function */
retval = hal_export_funct(name, read_board, &(board_array[n]),
0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"EPPPORT: ERROR: port %d read funct export failed\n", n);
hal_exit(comp_id);
return -1;
}
/* make write function name */
rtapi_snprintf(name, HAL_NAME_LEN, "eppport.%d.write", n);
/* export write function */
retval = hal_export_funct(name, write_board, &(board_array[n]),
0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"EPPPORT: ERROR: port %d write funct export failed\n", n);
hal_exit(comp_id);
return -1;
}
}
rtapi_print_msg(RTAPI_MSG_INFO,
"EPPPORT: installed driver for %d boards\n", num_boards);
hal_ready(comp_id);
return 0;
}
void rtapi_app_exit(void)
{
int n;
epp_board_t *board;
for ( n = 0 ; n < num_boards ; n++ ) {
board = &(board_array[n]);
}
hal_exit(comp_id);
}
/***********************************************************************
* REALTIME PORT READ AND WRITE FUNCTIONS *
************************************************************************/
static inline void epp_check_for_timeout(epp_board_t * board) {
if (board->port_timeout){
(void) rtapi_inb(board->base_addr+3); //dummy read
}
if (rtapi_inb(board->base_addr+1) & 0x01){
(void) rtapi_inb(board->base_addr+1); //some has reset by double read
rtapi_outb(0x01,board->base_addr+1); //some has reset by 1
rtapi_outb(0xFE,board->base_addr+1); //some has reset by 0
if (!board->port_timeout){
board->port_timeout=1;
rtapi_print_msg(RTAPI_MSG_ERR,
"EPPPORT: board (%04X) EPP timeout\n", board->base_addr);
}
}
else{
if (board->port_timeout){
board->port_timeout=0;
rtapi_print_msg(RTAPI_MSG_ERR,
"EPPPORT: board (%04X) EPP timeout cleared\n",
board->base_addr);
}
}
}
static void split_input(unsigned char data, io_pin_t *dest)
{
int b;
unsigned char mask;
/* splits a byte into HAL pins (and their NOTs) */
mask = 0x01;
for (b = 0 ; b < 8 ; b++ ) {
if ( data & mask ) {
*(dest->data) = 1; // input high
*(dest->io.not) = 0;
} else {
*(dest->data) = 0; // input low
*(dest->io.not) = 1;
}
mask <<= 1;
dest++;
}
}
static void read_board(void *arg, long period)
{
unsigned char indata, n;
epp_board_t *board;
board = arg;
if (!board->port_timeout){
for (n = 0 ;n < (board->port_inputs) ; n++) {
if (n == 0) {indata = rtapi_inb(board->base_addr+3);}
else {indata = rtapi_inb(board->base_addr+4);}
split_input(indata, &(board->port_Inp[n * 8]));
}
}
}
unsigned char build_output(io_pin_t *src)
{
int b;
unsigned char data, mask;
data = 0x00;
mask = 0x01;
for (b = 0; b < 8; b++) {
/* get the data, add to output byte */
if ( *(src->data) ) {
if ( !(src->io.invert) ) {
data |= mask;
}
} else {
if ( (src->io.invert) ) {
data |= mask;
}
}
mask <<= 1;
src++;
}
return data;
}
static void write_board(void *arg, long period)
{
unsigned char outdata, n;
epp_board_t *board;
board = arg;
if (!board->port_timeout){
for (n = 0 ;n < (board->port_outputs) ; n++) {
outdata = build_output(&(board->port_Out[n*8]));
if (n == 0) {rtapi_outb(~outdata, board->base_addr+3);}
else {rtapi_outb(~outdata, board->base_addr+4);}
}
}
epp_check_for_timeout(board);
}
/***********************************************************************
* LOCAL FUNCTION DEFINITIONS *
************************************************************************/
static int pins_and_params(char *argv[])
{
unsigned short board_addr[MAX_BOARDS];
unsigned char inputs[MAX_BOARDS],outputs[MAX_BOARDS];
int n, retval;
/* clear port_addr and dir_bits arrays */
for (n = 0; n < MAX_BOARDS; n++) {
board_addr[n] = 0;
inputs[n] = 0;
outputs[n] = 0;
}
/* parse config string, results in port_addr[] and data_dir[] arrays */
num_boards = 0;
n = 0;
while ((num_boards < MAX_BOARDS) && (argv[n] != 0)) {
board_addr[num_boards] = parse_board_addr(argv[n]);
if (board_addr[num_boards] == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"EPPPORT: ERROR: bad port address '%s'\n", argv[n]);
return -1;
}
n++;
if (argv[n] == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"EPPPORT: ERROR: no config info for port %s\n", argv[n-1]);
return -1;
}
if ((argv[n][0] == 'i') || (argv[n][0] == 'I')) {
if ((argv[n][1] >= '0') && (argv[n][1] <= '9')) {
inputs[num_boards] = argv[n][1]-'0';
}
}
n++;
if ((argv[n][0] == 'o') || (argv[n][0] == 'O')) {
if ((argv[n][1] >= '0') && (argv[n][1] <= '9')) {
outputs[num_boards] = argv[n][1]-'0';
}
}
n++;
// to do some error checking if bad parameter is listed?!?
num_boards++;
}
/* OK, now we've parsed everything */
if (num_boards == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"EPPPORT: ERROR: no ports configured\n");
return -1;
}
/* have good config info, connect to the HAL */
comp_id = hal_init("hal_eppport");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "EPPPORT: ERROR: hal_init() failed\n");
return -1;
}
/* allocate shared memory for board data */
board_array = hal_malloc(num_boards * sizeof(epp_board_t));
if (board_array == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"EPPPORT: ERROR: hal_malloc() failed\n");
hal_exit(comp_id);
return -1;
}
/* export all the pins and params for each board */
for (n = 0; n < num_boards; n++) {
/* config addr and direction */
board_array[n].base_addr = board_addr[n];
board_array[n].port_inputs = inputs[n];
board_array[n].port_outputs = outputs[n];
board_array[n].port_timeout = 3;
/* export all vars */
retval = export_board(n, &(board_array[n]));
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"EPPPORT: ERROR: board %d (%04X) var export failed\n", n,
board_addr[n]);
hal_exit(comp_id);
return -1;
}
}
return 0;
}
static unsigned short parse_board_addr(char *cp)
{
unsigned short result;
/* initial value */
result = 0;
/* test for leading '0x' */
if (cp[0] == '0') {
if ((cp[1] == 'X') || (cp[1] == 'x')) {
/* leading '0x', skip it */
cp += 2;
}
}
/* ok, now parse digits */
while (*cp != '\0') {
/* if char is a hex digit, add it to result */
if ((*cp >= '0') && (*cp <= '9')) {
result <<= 4;
result += *cp - '0';
} else if ((*cp >= 'A') && (*cp <= 'F')) {
result <<= 4;
result += (*cp - 'A') + 10;
} else if ((*cp >= 'a') && (*cp <= 'f')) {
result <<= 4;
result += (*cp - 'a') + 10;
} else {
/* not a valid hex digit */
return 0;
}
/* next char */
cp++;
}
return result;
}
static int export_board(int boardnum, epp_board_t * board)
{
int retval, msg, n;
unsigned char ch;
/* This function exports a lot of stuff, which results in a lot of
logging if msg_level is at INFO or ALL. So we save the current value
of msg_level and restore it later. If you actually need to log this
function's actions, change the second line below */
msg = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
retval = 0;
//export outputs as connector/pin
for (n = 0 ;n < (board->port_outputs) ; n++) {
retval += export_byte ( boardnum, n * 10 , &(board->port_Out[n*8]), 8,
1 );
}
//export inputs as connector/pin
for (n = 0 ;n < (board->port_inputs) ; n++) {
retval += export_byte ( boardnum, n * 10 , &(board->port_Inp[n*8]), 8,
0 );
}
/* initialize hardware */
outb(0x04, board->base_addr+2);
outb(0x00, board->base_addr+3); //Reset counter and byte 0 - can give wrong
cycle
if (inb(board->base_addr+1)&0x01){ //clear timeout
ch = inb(board->base_addr+1); //some boards need double read
outb(0x01, board->base_addr+1); //some board needs to write 1
outb(0xfe, board->base_addr+1); //and some boards needs to write 0
}
/* restore saved message level */
rtapi_set_msg_level(msg);
return retval;
}
static int export_byte(int boardnum, int pin_num, io_pin_t *pin, int num_pins,
int dir)
{
int n, retval;
retval = 0;
for ( n = 0 ; n < num_pins ; n++ ) {
if ( dir == 0 ) {
retval += export_input_pin(boardnum, pin_num, pin );
} else {
retval += export_output_pin(boardnum, pin_num, pin );
}
pin_num++;
pin++;
}
return retval;
}
static int export_input_pin(int boardnum, int pinnum, io_pin_t *pin)
{
char buf[HAL_NAME_LEN + 2];
int retval;
/* export read only HAL pin for input data */
rtapi_snprintf(buf, HAL_NAME_LEN, "eppport.%d.in-%02d", boardnum, pinnum);
retval = hal_pin_bit_new(buf, HAL_OUT, &(pin->data), comp_id);
if (retval != 0) {
return retval;
}
/* export additional pin for inverted input data */
rtapi_snprintf(buf, HAL_NAME_LEN, "eppport.%d.in-%02d-not", boardnum,
pinnum);
retval = hal_pin_bit_new(buf, HAL_OUT, &(pin->io.not), comp_id);
/* initialize HAL pins */
*(pin->data) = 0;
*(pin->io.not) = 1;
return retval;
}
static int export_output_pin(int boardnum, int pinnum, io_pin_t *pin)
{
char buf[HAL_NAME_LEN + 2];
int retval;
/* export read only HAL pin for output data */
rtapi_snprintf(buf, HAL_NAME_LEN, "eppport.%d.out-%02d", boardnum, pinnum);
retval = hal_pin_bit_new(buf, HAL_IN, &(pin->data), comp_id);
if (retval != 0) {
return retval;
}
/* export parameter for polarity */
rtapi_snprintf(buf, HAL_NAME_LEN, "eppport.%d.out-%02d-invert", boardnum,
pinnum);
retval = hal_param_bit_new(buf, HAL_RW, &(pin->io.invert), comp_id);
/* initialize HAL pin and param */
*(pin->data) = 0;
pin->io.invert = 0;
return retval;
}
------------------------------------------------------------------------------
Download Intel® Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
Emc-developers mailing list
Emc-developers@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/emc-developers