/* * Tritium MCP2515 CAN Interface * Copyright (c) 2006, Tritium Pty Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the distribution. * - Neither the name of Tritium Pty Ltd nor the names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Last Modified: J.Kennedy, Tritium Pty Ltd, 18 December 2006 * * - Implements the following CAN interface functions * - can_init * - can_transmit * - can_receive * */ // Include files #include "Sunseeker2021.h" #define can0_select() P4OUT &= ~CAN0_CSn #define can0_deselect() P4OUT |= CAN0_CSn // Public structures can_message TX_can_message; can_message RX_can_message; // Public variables unsigned char canctrl = 0; unsigned char canctrlold = 0; char canstat; extern unsigned long can_msg_count; extern unsigned long can_err_count; extern unsigned long can_read_cnt; // Private variables unsigned char buffer[16]; void can0_canctrl(unsigned char mask, unsigned char value) { can0_read(CANCTRL, &canctrlold, 1); canctrl = (canctrl & ~mask) | (value & mask); can0_write(CANCTRL, &canctrl, 1); } /************************************************************************************************** * PUBLIC FUNCTIONS *************************************************************************************************/ /* * Initialises MCP2515 CAN controller * - Resets MCP2515 via SPI port (switches to config mode, clears errors) * - Changes CLKOUT to /4 rate (4 MHz) * - Sets up bit timing for 1 Mbit operation * - Sets up receive filters and masks * - Rx Filter 0 = Motor controller velocity * - Rx Filter 1 = Unused * - Rx Filter 2 = Driver controls packets (for remote frame requests) * - Rx Filter 3 = Unused * - Rx Filter 4 = Unused * - Rx Filter 5 = Unused * - Rx Mask 0 = Exact message must match (all 11 bits) * - Rx Mask 1 = Block address must match (upper 6 bits) * - Enables ERROR and RX interrupts on IRQ pin * - Switches to normal (operating) mode */ void can0_init() { // Set up reset and clocking can0_reset(); //can_mode(interface, CANCTRL, 0x03, 0x02); // CANCTRL register, modify lower 2 bits, CLK = /4 canctrl = 0x87; // Set up bit timing & interrupts buffer[0] = 0x02; // CNF3 register: PHSEG2 = 3Tq, No wakeup, CLKOUT = CLK buffer[1] = 0xC9; // CNF2 register: set PHSEG2 in CNF3, Triple sample, PHSEG1= 2Tq, PROP = 2Tq // buffer[2] = 0x00; // CNF1 register: SJW = 1Tq, BRP = 0 buffer[2] = 0x03; // CNF1 register: SJW = 1Tq, BRP = 0 buffer[3] = 0x23; // CANINTE register: enable ERROR, RX0 & RX1 interrupts on IRQ pin buffer[4] = 0x00; // CANINTF register: clear all IRQ flags buffer[5] = 0x00; // EFLG register: clear all user-changable error flags can0_write(CNF3, &buffer[0], 6);// Write to registers // Set up external interrupt pins RX_BF and RX1BF buffer[0] = 0x0F; // BFPCTRL register: enabele RXxBF as interrupts buffer[1] = 0x00; // TXRTSCTRL register: request to send TX can0_write(BFPCTRL, &buffer[0], 2);// Write to registers // Set up receive filtering & masks // RXF0 - Buffer 0 buffer[ 0] = (unsigned char)((DC_CAN_BASE + DC_SWITCH) >> 3); buffer[ 1] = (unsigned char)((DC_CAN_BASE + DC_SWITCH) << 5); buffer[ 2] = 0x00; buffer[ 3] = 0x00; // RXF1 - Buffer 0 buffer[ 4] = 0x00; buffer[ 5] = 0x00; buffer[ 6] = 0x00; buffer[ 7] = 0x00; // RXF2 - Buffer 1 // buffer[ 8] = (unsigned char)((DC_CAN_BASE + DC_SWITCH) >> 3); // buffer[ 9] = (unsigned char)((DC_CAN_BASE + DC_SWITCH) << 5); buffer[ 8] = (unsigned char)((MC_CAN_BASE1) >> 3); buffer[ 9] = (unsigned char)((MC_CAN_BASE1) << 5); buffer[10] = 0x00; buffer[11] = 0x00; can0_write(RXF0SIDH, &buffer[0], 12 ); // RXF3 - Buffer 1 buffer[ 0] = 0x00; buffer[ 1] = 0x00; buffer[ 2] = 0x00; buffer[ 3] = 0x00; // RXF4 - Buffer 1 buffer[ 4] = 0x00; buffer[ 5] = 0x00; buffer[ 6] = 0x00; buffer[ 7] = 0x00; // RXF5 - Buffer 1 buffer[ 8] = 0x00; buffer[ 9] = 0x00; buffer[10] = 0x00; buffer[11] = 0x00; can0_write(RXF3SIDH, &buffer[0], 12 ); // RXM0 - Buffer 0 // buffer[ 0] = 0xFF; // Match entire 11 bit ID (ID is left-justified in 32-bit mask register) buffer[ 0] = 0xC0; // Match 2 MSB of 11 bit ID (ID is left-justified in 32-bit mask register) // buffer[ 1] = 0xE0; buffer[ 1] = 0x00; buffer[ 2] = 0x00; buffer[ 3] = 0x00; // RXM1 - Buffer 1 // buffer[ 4] = 0xFF; // Match entire 11 bit ID (ID is left-justified in 32-bit mask register) buffer[ 4] = 0xC0; //Match 2 MSB of 11 bit ID (ID is left-justified in 32-bit mask register) // buffer[ 5] = 0xE0; buffer[ 5] = 0x00; buffer[ 6] = 0x00; buffer[ 7] = 0x00; can0_write(RXM0SIDH, &buffer[0], 8 ); // buffer[0] = 0x04; //enable filters & rollover // can0_write(RXB0CTRL, &buffer[0], 1); // buffer[0] = 0x04; //enable filters // can0_write(RXB1CTRL, &buffer[0], 1); // Switch out of config mode into normal operating mode //can_mode(interface, CANCTRL, 0xE0, 0x00 ); // CANCTRL register, modify upper 3 bits, mode = Normal can0_canctrl(0xE0, 0x00); } void can2_init() { } void can0_sources(unsigned addr0, unsigned addr1) { // can_read(interface, CANCTRL, &buffer[0], 1); // buffer[0] = (buffer[0] & 0x1F) | 0x84; // can_write(interface, CANCTRL, &buffer[0], 1); can0_canctrl(0xE0, 0x80); buffer[0] = buffer[4] = 0xFF; buffer[1] = buffer[5] = 0xE0; buffer[2] = buffer[6] = 0x00; buffer[3] = buffer[7] = 0x00; can0_write(RXM0SIDH, &buffer[0], 8); buffer[0] = (unsigned char)(addr0 >> 3); buffer[1] = (unsigned char)(addr0 << 5); buffer[2] = buffer[3] = 0; buffer[4] = (unsigned char)(addr1 >> 3); buffer[5] = (unsigned char)(addr1 << 5); buffer[6] = buffer[7] = 0; buffer[8] = buffer[9] = 0; buffer[10] = buffer[11] = 0; can0_write(RXF0SIDH, &buffer[0], 12); buffer[0] = buffer[1] = 0; buffer[4] = buffer[5] = 0; can0_write(RXF3SIDH, &buffer[0], 12); //can_read(interface, CANCTRL, &buffer[0], 1); //buffer[0] = buffer[0] & 0x1F; //can_write(interface, CANCTRL, &buffer[0], 1); can0_canctrl(0xE0, 0x00); } /* * Receives a CAN message from the MCP2515 * - Run this routine when an IRQ is received * - Query the controller to identify the source of the IRQ * - If it was an ERROR IRQ, read & clear the Error Flag register, and return it * - If it was an RX IRQ, read the message and address, and return them * - If both, handle the error preferentially to the message * - Clear the appropriate IRQ flag bits */ void can0_receive() { can_message *RXPtr_can_message; unsigned char flags; extern char ucFLAG; can_msg_count++; // if (can_msg_count == 0x0040) // { // can_msg_count = 0; // Breakpoint to observe message queue // } RXPtr_can_message=&(*decode_queue.PutPt); // RXPtr_can_message=&(decode_queue.msg_fifo[0]); // RXPtr_can_message=&decode_queue[decode_queue->PutPt]; // RXPtr_can_message = &RX_can_message; // Local storage // Read out the interrupt flags register can0_read(CANINTF, &flags, 1 ); // Check for errors if(( flags & MCP_IRQ_ERR ) != 0x00 ) { // Read error flags and counters can0_read(EFLAG, &buffer[0], 1 ); can0_read(TEC, &buffer[1], 2 ); // Clear error flags can0_mode(EFLAG, buffer[0], 0x00 ); // Modify (to '0') all bits that were set // Return error code, a blank address field, and error registers in data field RXPtr_can_message->status = CAN_ERROR; RXPtr_can_message->address = 0x0000; RXPtr_can_message->data[0] = flags; // CANINTF RXPtr_can_message->data[1] = buffer[0]; // EFLG RXPtr_can_message->data[2] = buffer[1]; // TEC RXPtr_can_message->data[3] = buffer[2]; // REC // Clear the IRQ flag can0_mode(CANINTF, MCP_IRQ_ERR, 0x00 ); can_err_count++; } // No error, check for received messages, buffer 0 else if(( flags & MCP_IRQ_RXB0 ) != 0x00 ){ // Read in the info, address & message data can0_read(RXB0CTRL, &buffer[0], 14 ); // Clear the IRQ flag as fast as possible can0_mode(CANINTF, MCP_IRQ_RXB0, 0x00 ); // Fill out return structure // check for Remote Frame requests and indicate the status correctly if(( buffer[0] & MCP_RXB0_RTR ) == 0x00 ) { RXPtr_can_message->status = CAN_OK; // We've received a standard data packet fill_data(RXPtr_can_message, buffer); // Fill in the data } else { RXPtr_can_message->status = CAN_RTR; // We've received a remote frame request - data irrelevant } RXPtr_can_message->address = ((int)(buffer[1]) << 3) | ((int)(buffer[2]) >> 5); //convert address //add message to queue to be decoded message_fifo_PUT(&decode_queue, *RXPtr_can_message); ucFLAG |= 0x20; can_read_cnt++; } // No error, check for received messages, buffer 1 else if(( flags & MCP_IRQ_RXB1 ) != 0x00 ) { // Read in the info, address & message data can0_read(RXB1CTRL, &buffer[0], 14 ); // Clear the IRQ flag as fast as possible can0_mode(CANINTF, MCP_IRQ_RXB1, 0x00 ); // Fill out return structure // check for Remote Frame requests and indicate the status correctly if(( buffer[0] & MCP_RXB1_RTR ) == 0x00 ) { RXPtr_can_message->status = CAN_OK; // We've received a standard data packet fill_data(RXPtr_can_message, buffer); // Fill in the data } else { RXPtr_can_message->status = CAN_RTR; // We've received a remote frame request - data irrelevant } RXPtr_can_message->address = ((int)(buffer[1]) << 3) | ((int)(buffer[2]) >> 5); //convert address message_fifo_PUT(&decode_queue, *RXPtr_can_message); //add message to queue to be decoded ucFLAG |= 0x20; can_read_cnt++; } //not handled by buffers or errors else { RXPtr_can_message->status = CAN_ERROR; RXPtr_can_message->address = 0x0001; RXPtr_can_message->data[0] = flags; // CANINTF can_err_count++; } //added to tritum code to account for the fact that we could have more then one intrrupt pending at a given time //NOTE: YOU MUST CLEAR THE MAIN_MODE STATUS BEFORE ENTERING THIS FUNCTION OR YOU WILL CLEAR THE RETURN FLAG if((P2IN & CAN0_INTn)==0){ can0_read(CANINTF, &flags, 1 ); if(( flags & MCP_IRQ_ERR ) != 0x00 ) { canstat = 1; can0_read(EFLAG, &buffer[0], 1 ); can0_read(TEC, &buffer[1], 2 ); // Clear error flags can0_mode(EFLAG, buffer[0], 0x00 ); // Modify (to '0') all bits that were set // Clear the IRQ flag can0_mode(CANINTF, MCP_IRQ_ERR, 0x00 ); } } } /* * Transmits a CAN message to the bus * - Busy waits while message is sent to CAN controller * - Uses all available transmit buffers (3 available in CAN controller) to maximise throughput * - Only modifies address information if it's different from what is already set up in CAN controller * - Assumes constant 8-byte data length value */ void can0_transmit() { static unsigned int buf_addr[3] = {0xFFFF, 0xFFFF, 0xFFFF}; // Fill data into buffer, it's used by any address // Allow room at the start of the buffer for the address info if needed buffer[ 5] = TX_can_message.data[0]; buffer[ 6] = TX_can_message.data[1]; buffer[ 7] = TX_can_message.data[2]; buffer[ 8] = TX_can_message.data[3]; buffer[ 9] = TX_can_message.data[4]; buffer[10] = TX_can_message.data[5]; buffer[11] = TX_can_message.data[6]; buffer[12] = TX_can_message.data[7]; // Check if the incoming address has already been configured in a mailbox if( TX_can_message.address == buf_addr[0] ){ // Mailbox 0 setup matches our new message // Write to TX Buffer 0, start at data registers, and initiate transmission can0_write_tx(0x01, &buffer[5] ); can0_rts(0); } else if( TX_can_message.address == buf_addr[1] ){ // Mailbox 1 setup matches our new message // Write to TX Buffer 1, start at data registers, and initiate transmission can0_write_tx(0x03, &buffer[5] ); can0_rts(1 ); } else if( TX_can_message.address == buf_addr[2] ){ // Mailbox 2 setup matches our new message // Write to TX Buffer 2, start at data registers, and initiate transmission can0_write_tx(0x05, &buffer[5] ); can0_rts(2 ); } else{ // No matches in existing mailboxes // No mailboxes already configured, so we'll need to load an address - set it up buffer[0] = (unsigned char)(TX_can_message.address >> 3); buffer[1] = (unsigned char)(TX_can_message.address << 5); buffer[2] = 0x00; // EID8 buffer[3] = 0x00; // EID0 buffer[4] = 0x08; // DLC = 8 bytes // Check if we've got any un-setup mailboxes free and use them // Otherwise, find a non-busy mailbox and set it up with our new address if( buf_addr[0] == 0xFFFF ){ // Mailbox 0 is free // Write to TX Buffer 0, start at address registers, and initiate transmission can0_write_tx(0x00, &buffer[0] ); can0_rts(0); buf_addr[0] = TX_can_message.address; } else if( buf_addr[1] == 0xFFFF ){ // Mailbox 1 is free // Write to TX Buffer 1, start at address registers, and initiate transmission can0_write_tx(0x02, &buffer[0] ); can0_rts(1); buf_addr[1] = TX_can_message.address; } else if( buf_addr[2] == 0xFFFF ){ // Mailbox 2 is free // Write to TX Buffer 2, start at address registers, and initiate transmission can0_write_tx(0x04, &buffer[0] ); can0_rts(2); buf_addr[2] = TX_can_message.address; } else { // No mailboxes free, wait until at least one is not busy while(( can0_read_status() & 0x54 ) == 0x54); // Is it mailbox 0? if(( can0_read_status() & 0x04 ) == 0x00) { // Setup mailbox 0 and send the message can0_write_tx(0x00, &buffer[0] ); can0_rts(0); buf_addr[0] = TX_can_message.address; } // Is it mailbox 1? else if(( can0_read_status() & 0x10 ) == 0x00) { // Setup mailbox 1 and send the message can0_write_tx(0x02, &buffer[0] ); can0_rts(1); buf_addr[1] = TX_can_message.address; } // Is it mailbox 2? else if(( can0_read_status() & 0x40 ) == 0x00) { // Setup mailbox 2 and send the message can0_write_tx(0x04, &buffer[0] ); can0_rts(2); buf_addr[2] = TX_can_message.address; } } } } /************************************************************************************************** * PRIVATE FUNCTIONS *************************************************************************************************/ //fills data of a message from the buffer void fill_data(can_message *can, unsigned char buff[]) { can->data[0] = buffer[ 6]; can->data[1] = buffer[ 7]; can->data[2] = buffer[ 8]; can->data[3] = buffer[ 9]; can->data[4] = buffer[10]; can->data[5] = buffer[11]; can->data[6] = buffer[12]; can->data[7] = buffer[13]; } /* * Resets MCP2515 CAN controller via SPI port * - SPI port must be already initialised */ void can0_reset() { can0_select(); spi1_transmit(MCP_RESET ); can0_deselect(); } /* * Reads data bytes from the MCP2515 * - Pass in starting address, pointer to array of bytes for return data, and number of bytes to read */ unsigned char can0_result[10] = {0,0,0,0,0,0,0,0,0,0}; void can0_read(unsigned char address, unsigned char *ptr, unsigned char bytes ) { unsigned char i; if(bytes > 1) { can0_select(); spi1_transmit(MCP_READ ); spi1_transmit(address ); for( i = 0; i < bytes; i++ ) { *ptr++ = spi1_exchange(0x00); } can0_deselect(); if(address == CANCTRL) can0_result[0] = *--ptr; } else { can0_select(); spi1_transmit(MCP_READ); spi1_transmit(address); *ptr = spi1_exchange(0); can0_deselect(); if(address == CANCTRL) can0_result[0] = *ptr; if(address == 0x2C) can0_result[1] = *ptr; } } /* * Reads data bytes from receive buffers * - Pass in buffer number and start position as defined in MCP2515 datasheet * - For starting at data, returns 8 bytes * - For starting at address, returns 13 bytes */ void can0_read_rx(unsigned char address, unsigned char *ptr ) { unsigned char i; address &= 0x03; // Force upper bits of address to be zero (they're invalid) address <<= 1; // Shift input bits to correct location in command byte address |= MCP_READ_RX; // Construct command byte for MCP2515 can0_select(); spi1_transmit(address ); if(( address & 0x02 ) == 0x00 ) // Start at address registers { for( i = 0; i < 13; i++ ) { *ptr++ = spi1_exchange(0x00); } } else // Start at data registers { for( i = 0; i < 8; i++ ) { *ptr++ = spi1_exchange(0x00 ); } } can0_deselect(); } /* * Writes data bytes to the MCP2515 * - Pass in starting address, pointer to array of bytes, and number of bytes to write */ void can0_write(unsigned char address, unsigned char *ptr, unsigned char bytes ) { unsigned char i; can0_select(); spi1_transmit(MCP_WRITE ); spi1_transmit(address ); for( i = 0; i < (bytes-1); i++ ){ spi1_transmit(*ptr++ ); } spi1_transmit(*ptr ); can0_deselect(); } /* * Writes data bytes to transmit buffers * - Pass in buffer number and start position as defined in MCP2515 datasheet * - For starting at data, accepts 8 bytes * - For starting at address, accepts 13 bytes */ void can0_write_tx(unsigned char address, unsigned char *ptr ) { unsigned char i; address &= 0x07; // Force upper bits of address to be zero (they're invalid) address |= MCP_WRITE_TX; // Construct command byte for MCP2515 can0_select(); spi1_transmit(address ); if(( address & 0x01 ) == 0x00 ){ // Start at address registers for( i = 0; i < 13; i++ ){ spi1_transmit(*ptr++); } } else{ // Start at data registers for( i = 0; i < 8; i++ ){ spi1_transmit(*ptr++); } } can0_deselect(); } /* * Request to send selected transmit buffer * - Pass in address of buffer to transmit: 0, 1 or 2 */ void can0_rts(unsigned char address ) { unsigned char i; // Set up address bits in command byte i = MCP_RTS; if( address == 0 ) i |= 0x01; else if( address == 1 ) i |= 0x02; else if( address == 2 ) i |= 0x04; // Write command can0_select(); spi1_transmit(i); can0_deselect(); } /* * Reads MCP2515 status register */ unsigned char can0_read_status() { unsigned char status; can0_select(); spi1_transmit(MCP_STATUS ); status = spi1_exchange(0x00 ); can0_deselect(); return status; } /* * Reads MCP2515 RX status (filter match) register */ unsigned char can0_read_filter() { unsigned char status; can0_select(); spi1_transmit(MCP_FILTER ); status = spi1_exchange(0x00); can0_deselect(); return status; } /* * Modifies selected register in MCP2515 * - Pass in register to be modified, bit mask, and bit data */ void can0_mode(unsigned char address, unsigned char mask, unsigned char data ) { can0_select(); spi1_transmit(MCP_MODIFY); spi1_transmit(address); spi1_transmit(mask); spi1_transmit(data); can0_deselect(); }