FRDM-KL25Z Library for Seeed Studio CAN-BUS Shield

Here it is: http://mbed.org/users/Just4pLeisure/code/SEEED_CAN/

Week 1

I have 'won' a FRDM-KL25Z and Seeed Studio CAN-BUS shield in the great Component Database Kickoff: Shield Giveaway and here they are:

OK, the first thing I noticed was that, unlike my LPC1768 mbed, you can't simply plug a FRDM-KL25Z systems into something. In fact the FRDM system is related to the Arduino platform and the custom is to plug 'Shield' circuits into the FRDM circuit. 'Header' connectors are needed and these must be soldered in place, just like this:

Once that was done I could plug my CAN-BUS shield in place and was almost set:

Almost because the firmware on the FRDM-KL25Z must be updated otherwise it wont work with the mbed compiler. This page describes everything you need to do mbed FRDM KL25Z Getting Started. (I use Kubuntu at home and I had to use my Windows 7 work laptop to do the update :rolleyes:)

My next step was to see if I could get something to happen, anything at all - which was an emphatic NO for a while :-(. It turned out that some of you have already discovered the same limitation that was hindering my progress: the FRDM system shares it's SPI bus feature with the blue LED: KL25Z SPI Master not functioning as documented. So I used the red and green LEDs and everything was good with my world once again :-)

FRDM/CAN Hello World

#include "mbed.h"

SPI spi(PTD2, PTD3, PTD1); // mosi, miso, sclk
DigitalOut cs(PTD0);
DigitalOut myled(LED2);
DigitalOut resetled(LED3);


int main()
{
    myled = 1;
    resetled = 1;
    // Chip must be deselected
    cs = 1;

    // Setup the spi for 8 bit data, high steady state clock,
    // second edge capture, with a 1MHz clock rate
    spi.format(8,3);
    spi.frequency(1000000);

    while (1) {

        // Select the device by seting chip select low
        resetled = 0;
        cs = 0;
        // Send 0xC0, the command to reset the MCP2515 chip
        spi.write(0xC0);
        printf("Resetting the MCP2515 chip.\r\n");
        // Deselect the device
        cs = 1;
        wait_ms(10);
        resetled = 1;

        // Select the device by seting chip select low
        cs = 0;
        // Send 0xA0, the command to read the MCP2515 Status register
        spi.write(0xA0);
        // Send a dummy byte to receive the contents of the status register
        int status = spi.write(0x00);
        printf("status register = 0x%02X.\r\n", status);
        // Deselect the device
        cs = 1;

        // Select the device by seting chip select low
        cs = 0;
        // Send 0x03, the command to read an MCP2515 register
        spi.write(0x03);
        // Send 0x0F, the address of the MCP2515 CANCTRL register
        spi.write(0x0F);
        // Send a dummy byte to receive the contents of the status register
        int CANCTRL = spi.write(0x00);
        printf("CANCTRL register = 0x%02X.\r\n", CANCTRL);
        // Deselect the device
        cs = 1;

        wait(1);
        myled = !myled;
    }
}

My hello FRDM/CAN world program (above) flashes the red and green LEDs, but more importantly resets the Seeed Studio CAN-BUS shield and reads some of it's registers - as an extra bonus, the values I read back are what I expect them to be :-)

I'm now in the process of porting Seeed Studios CAN-BUS driver to the mbed...

Week 2

Progress has been slower than I'd hoped but steady nonetheless...

I have the essential wires necessary to connect power and my Seeed Studios CAN-BUS shield to my ECUs:

I've also ported Seeed Studios' Arduino CAN-BUS driver, corrected few of their bugs and written a new bitrate function which can set any of the achievable bitrates between 5,000 and 1,000,000 bits per second:

NOTE: It's not possible to set any bitrate value as they increase in unequal steps. The steps are quite close together at low bit rates, but at the higher speeds they are very coarse, the highest bitrates can only be:

  • 500,000 bps,
  • 533,333 bps,
  • 571,428 bps,
  • 615,384 bps,
  • 666,666 bps,
  • 727,272 bps,
  • 800,000 bps,
  • 888,888 bps and
  • 1,000,000 bps.

Using my port of Seeed Studio's driver I can connect to an ECU and see all of the messages it is broadcasting:

CAN message display explanation:

  • T followed by a timestamp between 0 and 60 seconds in milliseconds
  • I followed by the CAN message id
  • L followed by the number of bytes in the CAN message (0-8)
  • D followed by the data bytes in the CAN message

By using the Acceptance and Mask Filters I can receive only the messages id that I am interested in. Here I've connected to a T8 ECU and am filtering CAN id 0x301 messages only.

NOTE: the first 2 messages have a different id - I think this is because the MPC chip on the CAN-BUS shield has already received some messages before I set the filters.

Next steps are to document the driver and publish it...

Weeks 3 and 4

So I concluded it would be better to add a touch of CLASS and STRUCTure to my driver with a full re-write. It's been a bit like swimming in jelly and I've not quite finished so my library is _still_ PRIVATE for now. The good news is that when I make it PUBLIC it will come complete with a shiny new Apache license - hooray. The other good news is that the I can't think of a(n) (un)witty way to #include a poor pun including UNION. All this wordplay means that I have been learning to use more of the C++ language hopefully for the better rather than worse!

My driver is a close copy of the mbed LPC1768 CAN library and this is how it's shaping up:

constructor - done, initialises the MCP2515 at 100,000 kbps.
int frequency - nearly done - at the moment I fully re-initialise the MCP2515 at the new bit rate resetting all other configurations
int write ( CANMessage msg) - done
int read ( CANMessage &msg) - done
void reset () - done
void monitor (bool silent) - not done but seems easy.
int mode (Mode mode) - not done, similar to monitor (above) only with a few more options so fairly easy too.
unsigned char rderror () - not done but seems easy, return the content of the read error counter register
unsigned char tderror () - not done but the same as rderror but for transmit errors
void attach (void(*fptr)(void), IrqType type=RxIrq) - not done and no real clue at this stage
template<typename T >
void attach (T *tptr, void(T::*mptr)(void), IrqType type=RxIrq) - again, no clue

New and shiny functions (or methods or whatever they're called):

unsigned char Mask(int num, int ext, int ulData) - done, set (or clear) one of the 2 acceptance masks
unsigned char Filt(int num, int ext, int ulData) - done, set (or clear) one of the 6 acceptance filters
unsigned char checkError(void) - done - return error flags e.g. receive overflow, transmit error, receive/transmit warning etc

Hmm, I thought I had written most of the new driver but looking through the list I need to re-adjust my estimate to half done (though I think it's the more difficult part...

This week I hope to create a Compenent page for the Seeed Studios CAN-BUS shield and publish a working (even if incomplete) library - watch this space Components - Communication for a new addition soon.

Week 5

Library, 'Hello World' program and Component page published at last:

The Library works for the most part apart from the interrupt attach functions, I have posted a workaround on the Component page.

The library is optimised to make full use of the MCP2515's SPI instructions to maximise throughput by minimising the amount of data sent over the SPI interface:

  • Buffer read/write instructions instead of 'plain' read/write instructions save time because the MCP2515 address is a bit field in instruction instead of a separate address byte.
  • Status and RX Status instructions used wherever possible instead of 'plain' read instructions which also require the relevant MCP2515 register's address to be specified and transferred.
  • Single byte RTS message used to indicate that a meassage buffer is 'Ready To Send' - the alternative would be to use a 'Bit Modify' instruction which requires 4 bytes to be transferred over the SPI bus.

Week 6

I have updated the API with a few new functions and extended capabilities to some existing functions. All extended functions have default constructors that make the function behave as they previously did so existing programs should work without modification with the new library revision

  • Interrupts and the 'attach' functions now work !!!
  • The library can now be used with the LPC1768 mbed so you can use a shield instead of or as well as the internal CAN controllers - the default constructor for the LPC1768 platform uses pins p9 through p13.
  • The 'open' function takes a second, optional, argument to allow the operating mode to be specified when initialising the MCP2515.
  • You can specify which errors or warnings you want the 'errors' function to tell you about.
  • A new 'errorFlags' function returns the contents of MCP2515 EFLGS register.
  • A new 'interrupts' function tells you if the specified interrupt cause(s) have occurred.
  • A new 'interruptFlags' function returns the contents of MCP2515 CANINTF (interrupt flag) register.

The main problem with the 'attach' functions not working seemed to be related to the way in which I had declared the InterruptIn pin as part of a structure (Seeed_MCP_CAN_Shield). Declaring the InterruptIn pin as a single entity outside of the structure resolved that problem. I'd still like the InterruptIn pin to be part of the structure, but since it's working at the moment I may leave it alone...

Whilst 'attach' functions are now working I have found an occasional problem whereby the Interrupt never goes away even though I have read a message from the MCP2515. This is probably because there is a second message waiting in the 2nd buffer, but whatever the reason, the result is that the interrupt isn't cleared so I don't see an interrupt telling me about the message in the other buffer. I have worked around this problem by using a 'flag' variable and disabling all interrupts until all messages have been read out of the MCP2515's buffers. See the 'Hello World' program to see how I've implemented it. I'm not sure if this is a good way to deal with it or not, but again it seems to work and again it's something I'd like to understand and deal with correctly...


2 comments on FRDM-KL25Z Library for Seeed Studio CAN-BUS Shield:

13 Nov 2013

Very good job !!! I am already testing your code&library on LPC1768 wired to Sparkfun CAN-BUS Shield DEV-10039 and I also add some pieces of code in example for VW cars where is for constant reading necessary to send on CAN periodically message "Tester Present" to get data from ECU and also be able to talk to ECU. I have currently every thing running on bench connected to Lawicel CANUSB and it looks like working very well. Next step is tomorrow to test it with in car and look for future steps.

Also I think that this will be good library for use with LPC11U24 where is hardware CAN missing, this I will try later.

06 Dec 2014

Please log in to post comments.