TEWS Tip810 Driver for EPICS
Version 2.3
This document describes the software interface to the TEWS TIP810 Industry-Pack Module for vxWorks, written as part of an EPICS interface to the CANbus for the Gemini and UKIRT telescopes. This software is an IPAC Module Driver and uses the services of the drvIpac Industry Pack Driver to interface to the IPAC Carrier Board - see the accompanying document drvIpac - Industry Pack Driver for details of this driver software. The device support routines provided for use with EPICS are described in the document devCan - CAN Bus Device Support which briefly covers the use of the various record types supported.
The following routines provided by this driver are described in detail below:
The software provides an interface to higher level software which, other than device initialisation, is not specific to the TIP810 but could be used with a CANbus driver for a different hardware interface. The interface to the higher level software is provided in two header files: canBus.h contains all of the generic CANbus definitions and declarations, while drvTip810.h incorporates the additional declarations and definitions which are specific to the TIP810 module (and also #includes canBus.h and the vxWorks ANSI header file types.h). The routines which are specific to the TIP810 or meant for use from the vxWorks shell are described individually in this section, while the generic CANbus interface routines are described in section 3 below.
The TEWS TIP810 IP module contains a Philips pca82c200 stand-alone CAN-controller chip which performs all of the CANbus interfacing functions. The interface to this chip is defined in a separate header file pca82c200.h which declares the register interface structure and the bit-patterns for the various chip registers.
The driver uses an interrupt service routine with a different interrupt vector for each TIP810 module it controls, starting with vector 0x60 and increasing by one each time.
The driver can be built for use in a native vxWorks (i.e. non-EPICS) application. To do this, the drvTip810.c file should be compiled with the -DNO_EPICS switch to disable the EPICS-specific code.
Registers a new TIP810 device with the driver.
int t810Create (char *pbusName, ushort_t card, ushort_t slot, ushort_t irqNum, uint_t busRate);
busRate | CANbus bit rate |
5 | 5 Kbits/sec |
10 | 10 Kbits/sec |
20 | 20 Kbits/sec |
50 | 50 Kbits/sec |
100 | 100 Kbits/sec |
125 | 125 Kbits/sec |
250 | 250 Kbits/sec |
500 | 500 Kbits/sec |
1000 | 1000 Kbits/sec |
1600 | 1600 Kbits/sec |
-125 | Kvaser 125 Kbits/sec |
-250 | Kvaser 250 Kbits/sec |
-500 | Kvaser 500 Kbits/sec |
-1000 | Kvaser 1000 Kbits/sec |
This routine will usually be called from the vxWorks (EPICS) start-up script. It is used to inform the driver about each TIP810 module which it is to control, providing information on how to find the module (the IPAC carrier and slot numbers) and what CANbus bit rate is to be used. Each module is given a name which is matched during calls to the canOpen routine to identify the particular module again.
The code checks that the given name and card/slot numbers are unique and point to a real Tews Tip810 module, then it creates a new device table and initialises it and some of the chip registers. At this stage the device is not activated but held in the reset state.
int
Symbol/Value | Meaning |
0 | OK |
S_t810_badBusRate | Bus Rate not supported |
S_t810_duplicateDevice | another TIP810 already using given name and/or IPAC address |
(drvIpac) | errors returned by ipmValidate |
(vxWorks) | errno from malloc, semBCreate, or semMCreate |
-> t810Create("CAN1", 0, 1, 500) Value = 0
Reboot hook routine, resets all devices to stop interrupts.
int t810Shutdown(int startType);
When t810Initialise is run, this routine will be connected up to the vxWorks reboot hook to reset the CAN controller chips on all known TIP810 modules when the system is rebooted. It can also be called by applications programs to turn off all the module drivers in the event of a catastrophic failure, but it may be necessary to reboot the system to re-enable them afterwards.
int
Symbol/Value | Meaning |
0 | OK |
S_t810_badDevice | Bad or corrupted internal device table found |
Initialise driver and all registered hardware.
int t810Initialise(void);
Under EPICS this routine is called during iocInit, which must occur after all canCreate calls in the start-up script. It creates a message queue to hold received messages and starts a task named canRecvTask to distribute them to the routines that have asked to be informed about them. Finally it completes the initialisation of the CAN controller chip and interrupt vector registers for all known TIP810 devices and starts them running.
int
Symbol/Value | Meaning |
0 | OK |
(vxWorks) | errno from intConnect |
Display report giving status of all TEWS Tip810 devices
int t810Report(int interest);
Outputs (to stdout) a list of all the Tip810 devices created, their IP carrier & slot numbers and the bus name string. For interest=1 it adds message and error statistics; for interest=2 it lists all CAN IDs for which a call-back has been registered; for interest=3 the status of the CAN controller chip is given.
int
Symbol/Value | Meaning |
0 | OK |
S_t810_badDevice | Bad or corrupted internal device table found |
-> t810Report(1) TEWS tip810 CANbus Ip Modules 'CAN1' : IP Carrier 0 Slot 1, bus rate 500 Kbits/sec Messages Sent : 75 Messages Received : 43 Message Overruns : 0 Discarded Messages : 4 Last Discarded ID : 0x206 Error Interrupts : 0 Bus Off Events : 0 -> t810Report(2) TEWS tip810 CANbus Ip Modules 'CAN1' : IP Carrier 0 Slot 1, bus rate 500 Kbits/sec Callbacks registered: 0x1c 0x1d 0x1e 0x1f 0x200 0x202 0x204 canRead Status : Idle -> t810Report(3) TEWS tip810 CANbus Ip Modules 'CAN1' : IP Carrier 0 Slot 1, bus rate 500 Kbits/sec pca82c200 Chip Status: Bus Status : Bus-On Error Status : Ok Data Overrun : Ok Receive Status : Idle Receive Buffer Status : Empty Transmit Status : Idle Transmission Complete : Complete Transmit Buffer Access : Released
Test routine, sends a single test message to the named CANbus.
int canTest (char *pbusName, ushort_t identifier, ushort_t rtr, uchar_t length, char *data);
This routine is provided as a diagnostic, to allow the system developer to generate CANbus messages and RTR packets from the vxWorks shell. It should not be used from within an application. It makes use of the canOpen and canWrite routines, and responds to errors reported by those routines by printing a message and returning -1.
int
Symbol/Value | Meaning |
0 | OK |
-1 | ERROR |
-> canTest "CAN1", 0x33, 0, 4, "STOP"
Return device pointer for given CAN bus name.
int canOpen(char *busName, void **pcanBusID);
Searches through the list of registered Tip810 devices for one which matches the name given, and returns a device identifier for it. This identifier is a required parameter for all of the remaining can driver routines. String searching in this manner is not particularly fast if several devices have been registered so this routine is intended to be used mainly when an application starts up. It may be used as often as desired however - there is no associated canClose routine.
int
Symbol/Value | Meaning |
0 | OK |
S_can_noDevice | No matching device name found. |
void *can1; if (canOpen("CAN1", &can1)) { printf("Can't open CAN1\n"); return -1; }
Parse a CAN address string into a canIo_t structure
int canIoParse(char *canString, canIo_t *pcanIo);
canIoParse is used by the EPICS device support routines to convert record hardware addresses, but can be used by any application with similar requirements. It is intended to provide a standard way of converting the parameters which are needed to specify a portion of a particular CANbus message type from an ASCII string to their binary representation as a structure. The canIo_t structure is defined as a typedef in the canBus.h header as follows:
typedef struct { char *busName; int timeout; ushort_t identifier; ushort_t offset; int parameter; void *canBusID; } canIo_t;
The address string passed to this routine consists of a series of elements, some of which are optional.
The first element is the bus name, which should consist of alphanumeric characters only. The name is terminated immediately before the first "/" or ":" character in the string, and after omitting any leading white-space the characters forming the bus name are copied to a newly allocated buffer, the address of which is placed in pcanIo->busName.
An oblique stroke ("/") after the bus name introduces an optional timeout element, which is an integer number of milli-seconds to wait for a response for this particular type of message. This is converted into vxWorks system clock ticks and placed in pcanIo->timeout. If no timeout element is included, the timeout is set to WAIT_FOREVER (-1).
The CANbus message identifier is preceded by a colon (":"), and must result in one of the legal CANbus identifiers in the range 0 through 2047 (with holes). The identifier itself can be specified as a single number, or in several parts separated by plus signs, which are all summed. The numbers here can be given in any of the standard 'C' formats as converted by strtol, so negative, hex or octal numbers may be used as desired.
If the identifier is followed by a decimal point ("."), the following element is an optional byte offset into the CANbus message. The offset is an unsigned short integer (using strtoul again for the conversion), although to remain within the limits of the message buffer it should be restricted to a maximum value of seven. The converted value is placed in pcanIo->offset, which defaults to zero if no offset is given.
The final element is a general-purpose signed integer parameter, introduced by a space or tab character. The value is converted using strtol and placed in pcanIo->paramter.
If the string is successfully converted without errors, canIoParse will also call canOpen to initialise the pcanIo->canBusID bus identifier.
int
Symbol/Value | Meaning |
0 | OK |
S_can_badAddress | illegal input string or NULL input parameters |
S_can_noDevice | No matching device name found |
(vxWorks) | errno from malloc |
canIo_t myIo; int status; status = canIoParse("CAN1/20:0126.4 0xfff", &myIo) if (status) { printf("Address string rejected\n"); return -1; }
Writes a message to the given CANbus
int canWrite (void *canBusID, canMessage_t *pmessage, int timeout);
This routine is called to transmit a message on a particular CANbus. The canMessage_t type is defined as the following structure in canBus.h:
typedef struct { short_t identifier; /* 0 .. 2047 with holes! */ enum { SEND = 0, RTR = 1 } rtr; /* Remote Transmission Request */ uchar_t length; /* 0 .. 8 */ uchar_t data[CAN_DATA_SIZE]; /* CAN_DATA_SIZE = 8 */ } canMessage_t;
When called, canWrite obtains exclusive access to the TIP810 transmission buffer, converts the message into the correct form for the interface chip and copies it to the hardware registers. Finally it sends a Transmit Message command to the chip. The exclusive access semaphore will be released by the Interrupt Service Routine when it receives a notification from the chip that the message has been transmitted successfully.
int
Symbol/Value | Meaning |
0 | OK |
S_t810_badDevice | canBusID not valid |
S_can_badMessage | invalid field in the message buffer |
S_t810_transmitterBusy | system fault somewhere |
(vxWorks) | errno from semTake |
canIo_t myIo; canMessage_t message; char data[] = "STOP"; int status; status = canIoParse("CAN1/20:0126 0xfff", &myIo); if (status == OK) { message.identifier = myIo.identifier; message.rtr = SEND; message.length = strlen(data); memcpy(&message.data[0], data, message.length); status = canWrite(myIo.canBusID, &message, myIo.timeout); }
Register CAN message call-back
int canMessage(void *canBusID, ushort_t identifier, canMsgCallback_t *pcallback, void *pprivate);
This routine is used to add a call-back routine for a particular CAN message identifier on the given CANbus. Call-backs can be registered for any CAN message identifier, and there can be more than one call-back using the same ID - all routines are called in turn when a message with the relevant identifier is received. The call-back routine must not change the message at all, and should copy any information it needs from the message buffer before returning. The call-back used to be executed from vxWorks' Interrupt Context, thus there were restrictions in what vxWorks routines the call-back could use (see the vxWorks User Guide for details of these), however from release 2-1 a high priority task is used instead. Processing should still be kept to a minimum though as this task is run at a high priority. The call-back routine should be of type canMsgCallback_t:
void callback(void *pprivate, const canMessage_t *pmessage);
The same pprivate value supplied when the call-back was registered with canMessage is passed to the call-back routine with each message to allow it to identify its context.
int
Symbol/Value | Meaning |
0 | OK |
S_can_badMessage | bad identifier or NULL call-back routine |
S_can_badDevice | bad device pointer |
(vxWorks) | errno from malloc |
void myCallback(short *pvalue, canMessage_t *pmessage) { /* Update value whenever message arrives */ memcpy(pvalue, &pmessage->data[0], sizeof(short)); } ... int status; static short value; status = canMessage(myIo.canBusID, myIo.identifier, (canMsgCallback_t *) myCallback, &value);
Delete CAN message call-back
int canMsgDelete(void *canBusID, ushort_t identifier, canMsgCallback_t *pcallback, void *pprivate);
This routine is used to remove a call-back routine already registerd for a particular CAN message identifier on the given CANbus.
Exactly the same parameters given when the call-back was registered with canMessage must be passed to canMsgDelete for it to be successfully deleted.
int
Symbol/Value | Meaning |
0 | OK |
S_can_badMessage | bad identifier or NULL call-back routine |
S_can_noMessage | no matching call-back routine |
S_can_badDevice | bad device pointer |
To delete the call-back registered in the previous canMessage example:
status = canMsgDelete(myIo.canBusID, myIo.identifier, (canMsgCallback_t *) myCallback, &value);
Register CAN error signal call-back
int canSignal(void *canBusID, canSigCallback_t *pcallback, void *pprivate);
This routine is used to add a new call-back routine for CANbus error reports from the given CANbus. There can be any number of error call-backs on each device, and all are called in turn when the controller chip reports a Bus Error or Bus Off event. The call-back routine is called from vxWorks' Interrupt Context (Note this has not changed in release 2-1), thus there are restrictions in which vxWorks routines can be used (see vxWorks User Guide for details of these), and processing should be kept to an absolute minimum. The call-back routine should be of type canSigCallback_t:
void callback(void *pprivate, ushort_t status);
The pprivate value supplied to canSignal is passed to the call-back routine with the error status to allow it to identify its context. Status values will be one of
these being pre-processor macros defined in the header file. If the chip goes to the Bus Off state, the driver will attempt to restart it, thus a Bus Ok signal should follow almost immediately.
int
Symbol/Value | Meaning |
0 | OK |
S_can_badDevice | bad device identifier |
(vxWorks) | errno from malloc |
int status; status = canSignal(myIo.canBusID, logmsg, "CAN Signal, state %hd\n"); if (status) { printf("Couldn't register CAN signal handler.\n"); }
Reset CAN chip and message and error counters
int canBusReset(char *busName);
Resets the pca82c200 chip identified by the given busName, and clears all the counters associated with this device. This may clear some bus-related errors. All registered callbacks will remain active as before, although the chip may miss some incoming messages while resetting. This routine may also be used to restart an interface after a call to canBusStop.
int
Symbol/Value | Meaning |
0 | OK |
S_can_noDevice | No matching device name found. |
-> canBusReset "CAN1"
Stop CAN interface
int canBusStop(char *busName);
Holds the pca82c200 chip identified by the given busName in the reset state, which prevents it from sending or receiving messages, or from sending message acknowledgements to other nodes. The interface can be reactiviated using either canBusRestart or canBusReset.
int
Symbol/Value | Meaning |
0 | OK |
S_can_noDevice | No matching device name found. |
-> canBusStop "CAN1"
Restart a stopped CAN interface
int canBusReset(char *busName);
Restarts the pca82c200 chip identified by the given busName after it has been stopped by a call to canBusStop. All registered callbacks will remain active as before and the message and error counters will continue to increment from their previous values.
int
Symbol/Value | Meaning |
0 | OK |
S_can_noDevice | No matching device name found. |
-> canBusRestart "CAN1"
Send Remote Transmission Request and wait for reply.
int canRead(void *canBusID, canMessage_t *pmessage, int timeout);
Sends a CANbus Remote Transmission Request and waits for a reply on the same message identifier, returning the message received in the given buffer. On entry the message buffer must be initialised with the CANbus message identifier and length of the expected reply in the relevant fields.
The canRead routine can be used along with canWrite to create simple CANbus interface applications without the need to use the message call-back system. This will work in situations where the vxWorks system is the only device on the CANbus which initiates message traffic and there are no long delays in responses to RTRs. More complex applications which need to receive unsolicited messages will need to use the canMessage call-back functions; these can be used at the same time as canRead. Although the routine is safe to use in multi-tasking situations, the action of sending an RTR and waiting for a returned message will only be performed for one task at a time on each bus.
int
Symbol/Value | Meaning |
0 | OK |
S_t810_badDevice | bad bus ID |
S_can_badMessage | bad message Identifier or length |
(vxWorks) | errno from semTake |
int status; canMessage_t myBuffer; myBuffer.identifier = 139; myBuffer.length = 4; status = canRead(canID, &myBuffer, WAIT_FOREVER);