Drexel University's Logo

Clayton McNeil

PCM-3680 (with Ardence RTX) Driver Guide


Home
About Me
Tutorials
Media Archive
Resources

Introduction

The PCM-3680 is a “dual port CAN interface module” for the PC/104 embedded computer standard. Unfortunately, the only straight-forward ‘drivers’ available for it seem to be a pair of C files written for pre-NT kernel Windows. This presents a problem because the PCM-3680 works by directly mapping its control registers into the physical memory of the PC/104 computer and direct access to physical memory from within a user mode application is very difficult in NT kernel operating systems.

One way around this problem would be to write a proper NT kernel driver, although this requires some experience and use of special tools from Microsoft. Another solution to this problem is to map an area of the relevant area of physical memory into the program’s virtual address space. Because our particular application required the use of Ardence RTX, this was very simple to accomplish. The drawback of this method is that the PCM-3680 must be polled as opposed to relying on IRQs, however this trade-off was considered acceptable.

Overview and Assumptions:

The updated C files (CAN841.C and CAN841.H) were written to function like the original files described in the PCM-3680 documentation. The only difference is that it only supports the IRQ_POLLING mode and must be used in conjunction with Ardence RTX (or something similar that will map physical memory into the program’s virtual address space). This tutorial will demonstrate how to use the C files to create a very simple program that will send messages between the PCM-3680’s two ports when they are connected together.

Before beginning, please note that this tutorial assumes the following:
  1. The reader is familiar with the PCM-3680 documentation supplied by the manufacturer.
  2. The reader is familiar with the CAN standard.
  3. The reader has an understanding of the C programming language and the development tools he or she will be using.
  4. The reader knows how to build a project that includes Ardence RTX library files.

Writing the Program

Step 1:
Start by downloading the update library files (CAN841.zip). You can also download a complete version of the program code here, if desired.
Step 2:
Create a new project (or whatever is appropriate in your IDE) and ensure that it is configured to include the Ardence RTX library files.
Step 3:
Add CAN841.C, CAN841.H, and, if necessary, RTX.H to the project.
Step 4:
Now, add a new source file to the project. This will where the main code of the program resides. First, add the following includes and defines.
 
#include "CAN841.h"
#include "RTX.h"
#include <stdio.h>

#define PORT1 0
#define PORT2 1
#define PHYS_SEG 0xDA00;
The definition PHYS_SEG refers to the base address in physical memory where the PCM-3680 module will begin its mapping. This address in configurable on the hardware and therefore may be different. Refer to manufacturer’s documentation for more information.
Step 5:
Next, add the function header and general variable declarations that follow.
 
void main(void)
{
    CAN_STRUCT can1, can2;
    MSG_STRUCT txmsg, rxmsg;

    int i;

    char str_input[8];
    char str_quit;

Step 6:
Before anything involving the PCM-3680 module can happen, the area of physical memory it accesses must be mapped into the program’s virtual address space via RTX functions. The code to accomplish this is adapted from examples provided in the Ardence RTX documentation.
 
    // Map physical memory to virtual address space
    UI gSegment=PHYS_SEG;
    PVOID vAddress;
    unsigned long temp;
    LARGE_INTEGER physAddr;

    temp = PHYS_SEG;
    temp = temp << 4;
    physAddr.QuadPart = temp;
    vAddress = RtMapMemory(physAddr, 0xDFFF, 1);
    if(vAddress==NULL) printf("\n>>> Failure on RtMapMemory..!\n");
    else
    {
If the memory fails to map correctly, the program must stop. The if-statement in the last few lines above will ensure this occurs.
Step 7:
Assuming the mapping is successful, the CAN ports can be configured and started. Because the purpose of this program is to simply test that the module can send a receive messages, the ports are being configured to allow all messages from each other through. More information on what each value of the CAN structure means is provided in the manufacturer’s documentation.
 
        // Initalize the CAN module
        canInitHW(vAddress, IRQ_POLLING, IRQ_POLLING);

        canReset(PORT1);
        canReset(PORT2);;

        // Configure the structures for each CAN port
        can1.acc_code=0;
        can1.acc_mask=0xFF;
        can1.bt0=0xC0;
        can1.bt1=0x14;

        can2.acc_code=0;
        can2.acc_mask=0xFF;
        can2.bt0=0xC0;
        can2.bt1=0x14;

        canConfig(PORT1,can1);
        canConfig(PORT2,can2);

        // Run CAN ports so they can transmit and receive messages
        canNormalRun(PORT1);
        canNormalRun(PORT2);

Step 8:
In order to add a little bit of interactivity, the program will prompt the user to input a short string and that will be what is sent and echoed. These lines of code take care of obtaining the string.
 
        // Clear stdin and str_input of any previous values
        rewind(stdin);
        for(i=0; i < 8; i++)
            str_input[i] = 0;

        // Get string to echo from user
        printf("\n>>> Enter a 1-8 character string: ");
        scanf("%8s", &str_input);

Step 9:
Once the data is acquired, it can be transmitted over the first port. This involves configuring the message structure and copying the string to it as demonstrated below.
 
        // Configure message structure
        txmsg.id=0;
        txmsg.rtr=0;
        txmsg.dlen=8;

        for(i=0; i < txmsg.dlen; i++)
            txmsg.data[i]=str_input[i];
        
        if (!canSendMsg(PORT1, txmsg))
        {
            printf(">>> 0x%2X\n", CAN_ERR_CODE);
            break;
        }

        Sleep(500);
    		
Step 10:
After a short pause, the second port can be polled for the anticipated data. The following lines of code do just that, printing each byte of received data to the screen on a separate line.
 
        while(1)
        {
            if(canReceiveMsg(PORT2, &rxmsg) == 1)
            {
                printf(">>> Data Received on Port 2:\n");
                
                for(i=0; i < rxmsg.dlen; i++)
                    printf(">>>> 0x%2X '%c'\n",
                    rxmsg.data[i], rxmsg.data[i]);
                break;                
                
            }
        }

Step 11:
That takes care of everything pertaining to sending and receiving messages. The next few lines of code prompt the user to quit, allowing to the program to loop and run again if desired.
 
        // Ask user to quit program
        rewind(stdin);

        printf("\n>>> Quit (y or n)? ");
        scanf("%c", &str_quit);
        if (str_quit == 'y' || str_quit == 'Y')
            break;
    }
Step 12:
The last thing to do is a little clean up before the program exits.
 
    canReset(PORT1);
    canReset(PORT2);
    canExitHW();
    BOOL temp = RtUnmapMemory(vAddress);

The program can now be compiled and run. Note, however, that the two ports on the PCM-3680 must be physically connected for the function properly. The final result should look similar to this:

Screen showing CAN Test Program running

Home | About Me | Tutorials | Media Archive | Resources