Portable Cd Player Cheap

A small tutorial to get started with programming the MCP2515 under avr-gcc.

UPDATE: If you want to use the MCP2515 in your own projects you should also have a look at my canlib. It supports the MCP2515 as well as the SJA1000 and the AT90CAN series. The source code of it is based on the routines presented here, but is better documented and well tested and should therefore be preferred to the demo project here.

This should be a step by step tutorial how to send messages over the CAN bus using an AVR (in this case an ATMega8) and the MCP2515. Of course it can’t be a complete description, because the MCP2515 offers a lot of functionality. But I hope that this tutorial will give you an introduction to the topic. If you don’t know what a CAN bus is or what you can do with it, you should have a look at Wikipedia or the CAN wiki. But back to the MCP2515. The tutorial is divided into several sections:

  1. Reading or writing the registers
  2. Initializing the MCP2515
  3. Sending CAN messages
  4. Receiving CAN messages

The program parts are designed for the CAN test board, but should be adaptable for other boards without major changes. The MCP2515 is controlled via SPI controlled. Since the AVR already has a hardware SPI interface, this simplifies the control enormously. To use the SPI interface you have to initialize it first: void spi_init(void) { // Activate the pins for the SPI interface DDR_SPI |= (1<<><><><>#defines. So you only have to change one pin if you want to redefine the \CS pin (the other three pins are specific for the AVR and can’t be changed, unless you program the SPI interface yourself). #define DDR_CS DDRB #define PORT_CS PORTB #define P_CS 2 #define DDR_SPI DDRB #define PORT_SPI PORTB #define P_MISO 4 #define P_MOSI 3 #define P_SCK 5 In order to not always have to look up the SPI commands or the MCP2515 register addresses in the data sheet, I have also created a file with the most important #defines for this. So you can simply use the register names or bit names in source code. The corresponding file can be downloaded here. From now on you can easily push bytes over the SPI bus: uint8_t spi_putc( uint8_t data ) { // Send a byte SPDR = data; // Wait until byte has been sent while( !( SPSR & (1<

  • /CS Set line to low level
  • send SPI command (see data sheet)
  • send or receive as much data as you like
  • pull /CS line back to high level

The functions for writing or reading registers are therefore as follows:

Write register values

void mcp2515_write_register( uint8_t address, uint8_t data ) { // Pull /CS of MCP2515 to low PORT_CS &= ~(1<

Read register values

uint8_t mcp2515_read_register(uint8_t adress) { uint8_t data; // /CS of the MCP2515 pull low PORT_CS &= ~(1<

Set/delete single bits

void mcp2515_bit_modify(uint8_t address, uint8_t mask, uint8_t data) { // /CS of MCP2515 pull low PORT_CS &= ~(1<

Setting the bit timing

Since the topic is quite complex, there are only a few links to read for yourself or ready register values for different bit rates. The bitrate is set by the three registers CNF1 to CNF3: // CAN bitrate 125 kbps #define R_CNF1 (1<<><><><><><><>

  1. Message Error Interrupt
  2. Wakeup Interrupt
  3. Error Interrupt Enable (multiple sources in EFLG register)
  4. Transmit Buffer 2 Empty Interrupt
  5. Transmit Buffer 1 Empty Interrupt
  6. Transmit Buffer 0 Empty Interrupt
  7. Receive Buffer 1 Full Interrupt
  8. Receive Buffer 0 Full Interrupt

If one of these interrupts is activated, the interrupt line is pulled low each time the condition is met. The line stays low until all interrupt conditions are resolved. In my software I only used the two buffer full interrupts to indicate that a new message has arrived. Of course there are many more possibilities to use the interrupts, see the data sheet on page 49 for a detailed description. To enable the interrupts you only have to set the corresponding bit in the CANINTE register: // Enable the Rx Buffer Interrupts mcp2515_write_register(CANINTE, (1<<>

Pin functions

The MCP2515 also offers five pins that can be used in different ways. The two pins RX0BF and. RX1BF can either be used as status lines for the “buffer full” interrupt or can be used by the user as digital outputs. For example you could connect two additional status LEDs without using I/Os of the AVR. The usage of the pins is controlled by the register BFPCTRL. Since the bit modify function can be applied to these registers, it is very easy to control these pins. // Use RX0BF as digital output mcp2515_bit_modify( BFPCTRL, (1<<><><><>


The MCP2515 offers the possibility to supply other devices with a clock signal. The clock signal of the MCP2515 is then output via the CLKOUT pin, for example to supply a microcontroller with a clock. In addition, an internal prescaler can be activated, which divides the clock signal again by two, four or eight and thus enables different clock frequencies. With 16 MHz clock, 16 MHz, 8 MHz, 4 MHz and 2 MHz clock are available. The CLKOUT pin is controlled by the last three bits in the CANCTRL register. Since the CLKOUT pin is activated by default when applying a voltage to the MCP2515, it is possible to operate the AVR that controls the MCP2515. // Set the prescaler of the CLKOUT pin to zero, // => Output the clock frequency of the MCP2515 at the CLKOUT pin mcp2515_bit_modify( CANCTRL, 0x07, (1<

Acceptance filters and masks

As already mentioned on the first page, one of the great strengths of the CAN bus (or more precisely the CAN controller) is that you can filter on different messages. The MCP2515 offers two filters for buffer 0 and four more for buffer 1, so that messages which are not intended for the node can be filtered out from the beginning without the AVR having to react to them. To the filters you can find the following table in the datasheet:

Mask Bit Filter bit Message Identifier bit
0 x x Accept
1 0 0 Accept
1 0 1 Reject
1 1 0 Reject
1 1 1 Accept

The individual bits of the identifier are only used for filtering if the corresponding bit is set in the mask. If you want to receive all messages (e.g. for test purposes), you should simply set all mask bits to zero. This way all messages, regardless of their ID, will be forwarded to the receive buffer. One should note however that the filters and/or masks can be set only in the configuration mode. And now a complete code for initialization: void mcp2515_init(void) { // initialize SPI interface spi_init(); // reset MCP2515 by software reset, // after that MCP2515 is in configuration mode PORT_CS &= ~(1< 1 uS) * * Sync Seg = 1TQ * Prop Seg = (PRSEG + 1) * TQ = 1 TQ * Phase Seg1 = (PHSEG1 + 1) * TQ = 3 TQ * Phase Seg2 = (PHSEG2 + 1) * TQ = 3 TQ * * Bus speed = 1 / (Total # of TQ) * TQ * = 1 / 8 * TQ = 125 kHz */ // BRP = 7 mcp2515_write_register( CNF1, (1<<><><><><><>

  • Write the corresponding bit in the TXBnCTRL register.
  • Send the RTS command via SPI
  • Pulling low the TXnRTS pin for the corresponding buffer.

If you have only few free pins at the microcontroller it is worth to do the whole thing via SPI. You save one to three lines (depending on how many buffers you use), without the sending takes much longer. Sending a message could look like this: /* Sends a message over buffer 0 * 2 data bytes (0x04, 0xf3) * Default ID: 0x0123 */ uint16_t id = 0x0123; /* Set message buffer to highest priority (don’t need if you only send with one buffer, see text) */ mcp2515_bit_modify( TXB0CTRL, (1<<><>>3)); mcp2515_write_register(TXB0SIDL, (uint8_t) (id<<5)); // set message length + RTR mcp2515_write_register(TXB0DLC, 2); // data mcp2515_write_register(TXB0D0, 0x04); mcp2515_write_register(TXB0D1, 0xf3); // Send CAN message PORT_CS &= ~(1<

Sending with a buffer

However, it is still not very comfortable to have to fill in the registers each time. So we need a function that does exactly that. To be able to exchange the messages better between the functions it is useful to wrap them in a struct. This struct can be passed to the send function very easily: typedef struct { uint16_t id; uint8_t rtr; uint8_t length; uint8_t data[8]; } CANMessage; // Create new message CANMessage message; // Enter data message.id = 0x0123; message.rtr = 0; message.length = 2; message.data[0] = 0x04; message. data[1] = 0xf3; // Send message can_send_message(&message); And the send function for it: void can_send_message(CANMessage *p_message) { uint8_t length = p_message->length; // Set ID mcp2515_write_register(TXB0SIDH, (uint8_t) (p_message->id>>3)); mcp2515_write_register(TXB0SIDL, (uint8_t) (p_message->id<<5)); // Is the message a "Remote Transmit Request" if (p_message->rtr) { /* An RTR message has a length, but no data */ // Set message length + RTR mcp2515_write_register(TXB0DLC, (1<data[i]); } } // Send CAN message PORT_CS &= ~(1<

Send with all three buffers

In contrast to the first example, this time we don’t want to use the predefined functions for setting the registers, but we want to speed up the transmission a bit by sending the data directly to the SPI bus and taking advantage of the fact that MCP2515 allows to send several bytes in succession. These are then stored in the following registers. uint8_t can_send_message(CANMessage *p_message) { uint8_t status, address; // Read the status of the MCP2515 PORT_CS &= ~(1<id>>3)); spi_putc((uint8_t) (p_message->id<<5)); // Extended ID spi_putc(0x00); spi_putc(0x00); uint8_t length = p_message->length; if (length > 8) { length = 8; } // Is the message a “Remote Transmit Request” ? if (p_message->rtr) { /* An RTR has a length, but contains no data */ // Set message length + RTR spi_putc((1<data[i]); } } PORT_CS |= (1<

Receive messages

To check if a new message was received there are several possibilities depending on the configuration of the MCP2515:

  • via the RXnBF pins
  • via the interrupt line
  • via SPI command

Of course these possibilities can be combined with each other. A very useful possibility is for example to lead only the interrupt line to the AVR and to display whether there is a new message and only then to query via SPI command in which buffer the message is. Because it was also realized like this on the CAN testboard I want to go into this possibility in more detail: To use the interrupt pin to display new messages you have to configure it. But since we already did this during initialization (see subsection Interrupts) we can now simply wait for the interrupt pin to go low and indicate that we can read a message. To determine in which buffer the message is located or what type it is (Standard ID, Extended ID, Remote Transfer Request Frame, etc.) the MCP2515 sets a buffer to read the message. ) the MCP2515 provides an extra SPI command (alternatively you can also read the CANINTF register): uint8_t mcp2515_read_rx_status(void) { uint8_t data; // /CS of the MCP2515 pull low PORT_CS &= ~(1<id = (uint16_t) spi_putc(0xff) << 3; p_message->id |= (uint16_t) spi_putc(0xff) >> 5; spi_putc(0xff); spi_putc(0xff); // read length uint8_t length = spi_putc(0xff) & 0x0f; p_message->length = length; // read data for (uint8_t i=0;idata[i] = spi_putc(0xff); } PORT_CS |= (1<rtr = 1; } else { p_message->rtr = 0; } // Clear interrupt flag if (bit_is_set(status,6)) { mcp2515_bit_modify(CANINTF, (1<UPDATE: Under Downloads there is a small demo program for the CAN test board. If you use a different crystal you have to change this in the Makefile, other pins for the /CS and INT line can be changed in defaults.h to set them. The values for P_MOSI, P_MISO and P_SCK must match the pins for the hardware SPI. The program should compile without problems also for other AVRs! It initializes the MCP2515, switches it to loopback mode, sends a message (internal only), receives the sent message, switches back to normal mode, sends the message again via CAN and then waits for incoming messages.


There is now a CAN library that builds on the code fragments presented here.


mcp2515_demo.zip [17.49 kB] Back to top Portable Cd Player Cheap.

Leave a comment

Your email address will not be published. Required fields are marked *