CAN Device Driver Design and Application Programming Under VxWorks

Overview

VxWorks is a high-performance real-time operating system (RTOS) widely used in embedded systems that require high reliability and real-time performance. The Controller Area Network (CAN) is a common communication protocol in industrial control and automotive electronics. Combining CAN programming with VxWorks can achieve efficient communication and control systems. This article will introduce the basic steps and key points for CAN programming under VxWorks.

Hardware Preparation

Before starting programming, ensure the following hardware is ready:

  • • CAN controller: such as SJA1000 or other hardware controllers that support CAN bus.
  • • Hardware platform supported by VxWorks: usually based on PC/104 or other embedded systems.

Driver Design

Developing a CAN driver under VxWorks typically involves the following key steps:

  1. 1. Understanding the Hardware
  • • Understand the CAN controller: such as SJA1000, MCP2515, etc., know its register layout, interrupt mechanism, and how to control these hardware components through software.
  • • Address Mapping: Confirm the address range of the CAN controller in the system to ensure correct memory read and write operations.
  1. 2. Driver Framework
  • • Initialize the driver:
    • • Allocate and initialize the CAN device structure.
    • • Register the driver to VxWorks I/O system (usually through the device driver table iosDrvTbl).
  • • Implement basic I/O functions:
    • • create: Create a CAN device, set initial parameters such as baud rate.
    • • delete: Delete the device, release resources.
    • • open: Open the device, initialize device-related resources.
    • • close: Close the device, release related resources.
    • • read: Read data from the CAN bus.
    • • write: Send data to the CAN bus.
    • • ioctl: Handle various control commands, such as setting baud rate, filters, etc.
  1. 3. Interrupt Handling
  • • Interrupt Service Routine (ISR): Handle interrupts for CAN receive or send completion, timely update device status or process data.
  1. 4. Packet Handling
  • • Sending Packets: When calling write, prepare the packet to be sent to the CAN controller.
  • • Receiving Packets: Interrupt-driven packet reception, storing in a buffer for read use.
  1. 5. Error Handling
  • • Implement error detection and reporting mechanisms, handling CAN bus errors such as bus off, packet loss, etc.
  1. 6. Code Example
  • • Below is a simplified framework for a CAN driver:
#include <vxWorks.h>
#include <drv/can/canDrv.h> // Hypothetical header file
#include <intLib.h> // Interrupt handling

// CAN device structure
typedef struct {
    UINT32 baseAddr; // Hardware base address
    SEM_ID sem; // Semaphore for synchronization
    // Other member variables like buffer, status flags, etc.
} CAN_DEV;

// Device driver interface
LOCAL STATUS canDrvCreate();
LOCAL STATUS canDrvDelete();
LOCAL STATUS canDrvOpen();
LOCAL STATUS canDrvClose();
LOCAL STATUS canDrvRead();
LOCAL STATUS canDrvWrite();
LOCAL STATUS canDrvIoctl();

// Interrupt handling function
LOCAL void canIsr();

// Initialize driver
void canDrvInit() {
    /* Register driver to VxWorks I/O system */
    iosDrvInstall(canDrvCreate, canDrvDelete, 
                  canDrvOpen, canDrvClose, 
                  canDrvRead, canDrvWrite, 
                  canDrvIoctl);
}

// Create CAN device
LOCAL STATUS canDrvCreate() {
    CAN_DEV *pCan = (CAN_DEV *)calloc(sizeof(CAN_DEV), 1);
    if (pCan == NULL) return ERROR;

    // Initialize device parameters
    pCan->baseAddr = CAN_BASE_ADDRESS; // Hypothetical hardware address
    pCan->sem = semBCreate(SEM_Q_FIFO, SEM_EMPTY); // Create semaphore

    // Hardware initialization
    // ...

    return (STATUS)pCan; // Return device pointer as device ID
}

// Delete device
LOCAL STATUS canDrvDelete(CAN_DEV *pCan) {
    // Release resources
    semDelete(pCan->sem);
    free(pCan);
    return OK;
}

// Open device
LOCAL STATUS canDrvOpen(CAN_DEV *pCan) {
    // Further initialization or status check
    return OK;
}

// Close device
LOCAL STATUS canDrvClose(CAN_DEV *pCan) {
    // Cleanup operations
    return OK;
}

// Read CAN message
LOCAL STATUS canDrvRead(CAN_DEV *pCan, CAN_MSG *pMsg) {
    // Block wait or poll for receive interrupt signal
    semTake(pCan->sem, WAIT_FOREVER);
    // Read message from receive buffer
    // ...
    return OK;
}

// Send CAN message
LOCAL STATUS canDrvWrite(CAN_DEV *pCan, CAN_MSG *pMsg) {
    // Send data to CAN controller's transmit buffer
    // ...
    return OK;
}

// IO control
LOCAL STATUS canDrvIoctl(CAN_DEV *pCan, int cmd, int arg) {
    switch (cmd) {
        case CAN_SET_BAUDRATE:
            // Set baud rate
            break;
        // Other control commands
        default:
            return ERROR;
    }
    return OK;
}

// Interrupt Service Routine
LOCAL void canIsr() {
    CAN_DEV *pCan = (CAN_DEV *)intContextGet(); // Get device information from interrupt context
    // Process interrupt, such as reading from receive buffer, updating status
    semGive(pCan->sem); // Give semaphore to indicate new data is available
}
  1. 7. Testing and Debugging
  • • Use VxWorks built-in debugging tools, such as Wind River Workbench, to debug the driver.
  • • Write test programs to validate the functionality of the driver by sending and receiving CAN messages.

Notes

  • • The driver needs to consider synchronization issues in a multitasking environment.
  • • Performance optimization, such as reducing interrupt frequency and improving data transmission efficiency.
  • • Error handling and logging are crucial for debugging and maintenance.

This example is conceptual code; actual development needs to be adjusted and improved based on the specific CAN controller and version of VxWorks.

Application Programming

Here are the specific steps for CAN programming under VxWorks:

  1. 1. Initialize the CAN controller:
  • • Set basic parameters such as CAN bus speed and mode.
  • • Initialize related registers to ensure the CAN controller is in the correct working state.
  1. 2. Sending and Receiving Packets:
  • • Sending: Send data to the CAN bus through the write function.
  • • Receiving: Use the read function or interrupts to obtain data from the CAN bus.
  1. 3. Error Handling and Diagnostics:
  • • Implement error detection mechanisms, such as CAN bus errors and frame loss.
  • • Provide diagnostic functions to monitor the health status of the CAN bus.
  1. 4. Multi-channel Management:
  • • If the device supports multiple CAN channels, set different handling and error handling mechanisms for each channel.

Example Code

Here is a simplified code example for CAN programming under VxWorks. Please note that this part of the code is conceptual, and the actual implementation may need to be adjusted based on specific hardware and VxWorks versions.

#include <vxWorks.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <taskLib.h>
#include <drv/can/canDrv.h>  // Hypothetical CAN driver header file

#define CAN_BUS_SPEED 500000 /* 500 kbps */
#define CAN_MESSAGE_ID 0x123 /* Example message ID */

/* CAN driver initialization function */
void canInit() {
    CAN_HANDLE hCan;
    
    /* Initialize CAN driver, assuming there is a global CAN initialization function */
    if ((hCan = canDrvInit()) == NULL) {
        printf("CAN driver initialization failed!\n");
        return;
    }

    /* Set CAN bus speed */
    if (canIoctl(hCan, CAN_SET_BAUDRATE, CAN_BUS_SPEED) != OK) {
        printf("Failed to set CAN bus speed\n");
    }

    printf("CAN bus initialized successfully\n");
}

/* CAN send data function */
STATUS canSendMessage(CAN_HANDLE hCan, UINT32 msgId, UCHAR *data, UINT8 len) {
    CAN_MSG msg;
    STATUS status;

    /* Fill message structure */
    msg.id = msgId;
    msg.len = len;
    memcpy(msg.data, data, len);

    /* Send message */
    status = canWrite(hCan, &amp;msg);
    if (status != OK) {
        printf("Failed to send CAN message\n");
        return ERROR;
    }

    printf("Message sent successfully\n");
    return OK;
}

/* CAN receive data function */
void canReceiveTask(CAN_HANDLE hCan) {
    CAN_MSG rxMsg;
    STATUS status;

    while (1) {
        status = canRead(hCan, &amp;rxMsg);
        if (status == OK) {
            printf("Received CAN message with ID: 0x%X, Data: ", rxMsg.id);
            for (int i = 0; i < rxMsg.len; i++) {
                printf("%02X ", rxMsg.data[i]);
            }
            printf("\n");
        } elseif (status == ERROR) {
            printf("Error in reading CAN message\n");
        }
        /* Wait for new messages */
        taskDelay(10);  // Delay for a short time to avoid high CPU usage
    }
}

/* Main function or initialization task */
void canMain() {
    CAN_HANDLE hCan;

    canInit();

    /* Assume we have an initialized CAN handle */
    hCan = canOpen(0);  // Open the first CAN channel

    if (hCan == NULL) {
        printf("Failed to open CAN channel\n");
        return;
    }

    /* Send an example message */
    UCHAR dataToSend[] = {0x01, 0x02, 0x03, 0x04};
    canSendMessage(hCan, CAN_MESSAGE_ID, dataToSend, sizeof(dataToSend));

    /* Create a receive task */
    taskSpawn("tCanRx", 100, 0, 4096, (FUNCPTR)canReceiveTask, (int)hCan, 0, 0, 0, 0, 0, 0, 0, 0, 0);

    /* The main task can do other things or wait for tasks to finish */
}

int main() {
    canMain();
    return 0;
}

This example code demonstrates the basic concepts of how to initialize the CAN bus, send CAN messages, and receive CAN messages in a VxWorks environment. It is important to note:

  • • canDrvInit, canIoctl, canWrite, canRead, etc., are hypothetical functions; the actual code may need to implement these functionalities based on the specific CAN driver API.
  • • taskSpawn is used to create a new task to handle the reception of CAN messages, which conforms to VxWorks’ multitasking design philosophy.
  • • In actual development, you may need to handle more error situations and implement more CAN bus management functions.

This code is only a conceptual example and needs to be specifically implemented and optimized according to the hardware and VxWorks environment in practical applications.

Optimization and Debugging

  • • Performance optimization: Improve CAN communication efficiency by reducing interrupt frequency and optimizing data transmission paths.
  • • Debugging: Use debugging tools provided by VxWorks, such as the debugger in Wind River Workbench, to check and optimize the performance of the driver.

Conclusion

Programming CAN under VxWorks requires a thorough understanding of hardware characteristics, VxWorks I/O systems, and the CAN protocol itself. Through reasonable driver design and optimization, an efficient and reliable CAN communication system can be achieved. Whether in industrial automation or automotive electronics, the combination of VxWorks and CAN provides a powerful solution.

Please note that this is an overview article based on existing knowledge and resources; specific implementation details may vary based on hardware and versions.

Click “Read More” to access more VxWorks resources!

Leave a Comment