简洁大气企业网站模板优化站点
SC60开发板默认是不支持CAN接口的,需要外接转换芯片,选取MICROCHIP的MCP25625这一款。
芯片特点如下:
• Stand-Alone CAN 2.0B Controller with Integrated CAN Transceiver and Serial Peripheral
Interface (SPI)
• Up to 1 Mb/s Operation
• Very Low Standby Current (10 μA, typical)
• Up to 10 MHz SPI Clock Speed
• Interfaces Directly with Microcontrollers with 2.7V to 5.5V I/Os
• Available in SSOP-28L and 6x6 QFN-28L
• Temperature Ranges: - Extended (E): -40°C to +125°C
最主要的特点是集成了Transceiver,避免了外接Transceiver的电路,设计简单。
参考原理图:
osc1接8M晶振,CANL和CANH连接到外部CAN网络,串联120欧的阻抗匹配,使用4线SPI和主控通讯。
驱动参考:
/** CAN bus driver for Microchip 251x CAN Controller with SPI Interface** MCP2510 support and bug fixes by Christian Pellegrin* <chripell@evolware.org>** Copyright 2009 Christian Pellegrin EVOL S.r.l.** Copyright 2007 Raymarine UK, Ltd. All Rights Reserved.* Written under contract by:* Chris Elston, Katalix Systems, Ltd.** Based on Microchip MCP251x CAN controller driver written by* David Vrabel, Copyright 2006 Arcom Control Systems Ltd.** Based on CAN bus driver for the CCAN controller written by* - Sascha Hauer, Marc Kleine-Budde, Pengutronix* - Simon Kallweit, intefo AG* Copyright 2007** This program is free software; you can redistribute it and/or modify* it under the terms of the version 2 of the GNU General Public License* as published by the Free Software Foundation** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA**** Your platform definition file should specify something like:** static struct mcp251x_platform_data mcp251x_info = {* .oscillator_frequency = 8000000,* .board_specific_setup = &mcp251x_setup,* .power_enable = mcp251x_power_enable,* .transceiver_enable = NULL,* };** static struct spi_board_info spi_board_info[] = {* {* .modalias = "mcp2510",* // or "mcp2515" depending on your controller* .platform_data = &mcp251x_info,* .irq = IRQ_EINT13,* .max_speed_hz = 2*1000*1000,* .chip_select = 2,* },* };** Please see mcp251x.h for a description of the fields in* struct mcp251x_platform_data.**/#include <linux/can/core.h>
#include <linux/can/dev.h>
#include <linux/can/led.h>
#include <linux/can/platform/mcp251x.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/freezer.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/uaccess.h>
//stone added
#include <linux/of_gpio.h>/* SPI interface instruction set */
#define INSTRUCTION_WRITE 0x02
#define INSTRUCTION_READ 0x03
#define INSTRUCTION_BIT_MODIFY 0x05
#define INSTRUCTION_LOAD_TXB(n) (0x40 + 2 * (n))
#define INSTRUCTION_READ_RXB(n) (((n) == 0) ? 0x90 : 0x94)
#define INSTRUCTION_RESET 0xC0
#define RTS_TXB0 0x01
#define RTS_TXB1 0x02
#define RTS_TXB2 0x04
#define INSTRUCTION_RTS(n) (0x80 | ((n) & 0x07))/* MPC251x registers */
#define CANSTAT 0x0e
#define CANCTRL 0x0f
# define CANCTRL_REQOP_MASK 0xe0
# define CANCTRL_REQOP_CONF 0x80
# define CANCTRL_REQOP_LISTEN_ONLY 0x60
# define CANCTRL_REQOP_LOOPBACK 0x40
# define CANCTRL_REQOP_SLEEP 0x20
# define CANCTRL_REQOP_NORMAL 0x00
# define CANCTRL_OSM 0x08
# define CANCTRL_ABAT 0x10
#define TEC 0x1c
#define REC 0x1d
#define CNF1 0x2a
# define CNF1_SJW_SHIFT 6
#define CNF2 0x29
# define CNF2_BTLMODE 0x80
# define CNF2_SAM 0x40
# define CNF2_PS1_SHIFT 3
#define CNF3 0x28
# define CNF3_SOF 0x08
# define CNF3_WAKFIL 0x04
# define CNF3_PHSEG2_MASK 0x07
#define CANINTE 0x2b
# define CANINTE_MERRE 0x80
# define CANINTE_WAKIE 0x40
# define CANINTE_ERRIE 0x20
# define CANINTE_TX2IE 0x10
# define CANINTE_TX1IE 0x08
# define CANINTE_TX0IE 0x04
# define CANINTE_RX1IE 0x02
# define CANINTE_RX0IE 0x01
#define CANINTF 0x2c
# define CANINTF_MERRF 0x80
# define CANINTF_WAKIF 0x40
# define CANINTF_ERRIF 0x20
# define CANINTF_TX2IF 0x10
# define CANINTF_TX1IF 0x08
# define CANINTF_TX0IF 0x04
# define CANINTF_RX1IF 0x02
# define CANINTF_RX0IF 0x01
# define CANINTF_RX (CANINTF_RX0IF | CANINTF_RX1IF)
# define CANINTF_TX (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF)
# define CANINTF_ERR (CANINTF_ERRIF)
#define EFLG 0x2d
# define EFLG_EWARN 0x01
# define EFLG_RXWAR 0x02
# define EFLG_TXWAR 0x04
# define EFLG_RXEP 0x08
# define EFLG_TXEP 0x10
# define EFLG_TXBO 0x20
# define EFLG_RX0OVR 0x40
# define EFLG_RX1OVR 0x80
#define TXBCTRL(n) (((n) * 0x10) + 0x30 + TXBCTRL_OFF)
# define TXBCTRL_ABTF 0x40
# define TXBCTRL_MLOA 0x20
# define TXBCTRL_TXERR 0x10
# define TXBCTRL_TXREQ 0x08
#define TXBSIDH(n) (((n) * 0x10) + 0x30 + TXBSIDH_OFF)
# define SIDH_SHIFT 3
#define TXBSIDL(n) (((n) * 0x10) + 0x30 + TXBSIDL_OFF)
# define SIDL_SID_MASK 7
# define SIDL_SID_SHIFT 5
# define SIDL_EXIDE_SHIFT 3
# define SIDL_EID_SHIFT 16
# define SIDL_EID_MASK 3
#define TXBEID8(n) (((n) * 0x10) + 0x30 + TXBEID8_OFF)
#define TXBEID0(n) (((n) * 0x10) + 0x30 + TXBEID0_OFF)
#define TXBDLC(n) (((n) * 0x10) + 0x30 + TXBDLC_OFF)
# define DLC_RTR_SHIFT 6
#define TXBCTRL_OFF 0
#define TXBSIDH_OFF 1
#define TXBSIDL_OFF 2
#define TXBEID8_OFF 3
#define TXBEID0_OFF 4
#define TXBDLC_OFF 5
#define TXBDAT_OFF 6
#define RXBCTRL(n) (((n) * 0x10) + 0x60 + RXBCTRL_OFF)
# define RXBCTRL_BUKT 0x04
# define RXBCTRL_RXM0 0x20
# define RXBCTRL_RXM1 0x40
#define RXBSIDH(n) (((n) * 0x10) + 0x60 + RXBSIDH_OFF)
# define RXBSIDH_SHIFT 3
#define RXBSIDL(n) (((n) * 0x10) + 0x60 + RXBSIDL_OFF)
# define RXBSIDL_IDE 0x08
# define RXBSIDL_SRR 0x10
# define RXBSIDL_EID 3
# define RXBSIDL_SHIFT 5
#define RXBEID8(n) (((n) * 0x10) + 0x60 + RXBEID8_OFF)
#define RXBEID0(n) (((n) * 0x10) + 0x60 + RXBEID0_OFF)
#define RXBDLC(n) (((n) * 0x10) + 0x60 + RXBDLC_OFF)
# define RXBDLC_LEN_MASK 0x0f
# define RXBDLC_RTR 0x40
#define RXBCTRL_OFF 0
#define RXBSIDH_OFF 1
#define RXBSIDL_OFF 2
#define RXBEID8_OFF 3
#define RXBEID0_OFF 4
#define RXBDLC_OFF 5
#define RXBDAT_OFF 6
#define RXFSIDH(n) ((n) * 4)
#define RXFSIDL(n) ((n) * 4 + 1)
#define RXFEID8(n) ((n) * 4 + 2)
#define RXFEID0(n) ((n) * 4 + 3)
#define RXMSIDH(n) ((n) * 4 + 0x20)
#define RXMSIDL(n) ((n) * 4 + 0x21)
#define RXMEID8(n) ((n) * 4 + 0x22)
#define RXMEID0(n) ((n) * 4 + 0x23)#define GET_BYTE(val, byte) \(((val) >> ((byte) * 8)) & 0xff)
#define SET_BYTE(val, byte) \(((val) & 0xff) << ((byte) * 8))/** Buffer size required for the largest SPI transfer (i.e., reading a* frame)*/
#define CAN_FRAME_MAX_DATA_LEN 8
#define SPI_TRANSFER_BUF_LEN (6 + CAN_FRAME_MAX_DATA_LEN)
#define CAN_FRAME_MAX_BITS 128#define TX_ECHO_SKB_MAX 1#define DEVICE_NAME "mcp25625"static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
module_param(mcp251x_enable_dma, int, S_IRUGO);
MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)");static const struct can_bittiming_const mcp251x_bittiming_const = {.name = DEVICE_NAME,.tseg1_min = 3,.tseg1_max = 16,.tseg2_min = 2,.tseg2_max = 8,.sjw_max = 4,.brp_min = 1,.brp_max = 64,.brp_inc = 1,
};enum mcp251x_model {CAN_MCP251X_MCP2510 = 0x2510,CAN_MCP251X_MCP2515 = 0x2515,
};struct mcp251x_priv {struct can_priv can;struct net_device *net;struct spi_device *spi;enum mcp251x_model model;struct mutex mcp_lock; /* SPI device lock */u8 *spi_tx_buf;u8 *spi_rx_buf;dma_addr_t spi_tx_dma;dma_addr_t spi_rx_dma;struct sk_buff *tx_skb;int tx_len;struct workqueue_struct *wq;struct work_struct tx_work;struct work_struct restart_work;//stone addedstruct work_struct irq_work;int force_quit;int after_suspend;
#define AFTER_SUSPEND_UP 1
#define AFTER_SUSPEND_DOWN 2
#define AFTER_SUSPEND_POWER 4
#define AFTER_SUSPEND_RESTART 8int restart_tx;
};#define MCP251X_IS(_model) \
static inline int mcp251x_is_##_model(struct spi_device *spi) \
{ \struct mcp251x_priv *priv = spi_get_drvdata(spi); \return priv->model == CAN_MCP251X_MCP##_model; \
}MCP251X_IS(2510);
MCP251X_IS(2515);//Begin:stone added
static struct mcp251x_platform_data mcp251x_info = {.oscillator_frequency = 8000000,//.board_specific_setup = &mcp251x_setup,//.power_enable = mcp251x_power_enable,.transceiver_enable = NULL,
};
//End:stone addedstatic void mcp251x_clean(struct net_device *net)
{struct mcp251x_priv *priv = netdev_priv(net);if (priv->tx_skb || priv->tx_len)net->stats.tx_errors++;if (priv->tx_skb)dev_kfree_skb(priv->tx_skb);if (priv->tx_len)can_free_echo_skb(priv->net, 0);priv->tx_skb = NULL;priv->tx_len = 0;
}/** Note about handling of error return of mcp251x_spi_trans: accessing* registers via SPI is not really different conceptually than using* normal I/O assembler instructions, although it's much more* complicated from a practical POV. So it's not advisable to always* check the return value of this function. Imagine that every* read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0)* error();", it would be a great mess (well there are some situation* when exception handling C++ like could be useful after all). So we* just check that transfers are OK at the beginning of our* conversation with the chip and to avoid doing really nasty things* (like injecting bogus packets in the network stack).*/
static int mcp251x_spi_trans(struct spi_device *spi, int len)
{struct mcp251x_priv *priv = spi_get_drvdata(spi);struct spi_transfer t = {.tx_buf = priv->spi_tx_buf,.rx_buf = priv->spi_rx_buf,.len = len,.cs_change = 0,};struct spi_message m;int ret;spi_message_init(&m);if (mcp251x_enable_dma) {t.tx_dma = priv->spi_tx_dma;t.rx_dma = priv->spi_rx_dma;m.is_dma_mapped = 1;}spi_message_add_tail(&t, &m);ret = spi_sync(spi, &m);if (ret)dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret);return ret;
}static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
{struct mcp251x_priv *priv = spi_get_drvdata(spi);u8 val = 0;priv->spi_tx_buf[0] = INSTRUCTION_READ;priv->spi_tx_buf[1] = reg;mcp251x_spi_trans(spi, 3);val = priv->spi_rx_buf[2];return val;
}static void mcp251x_read_2regs(struct spi_device *spi, uint8_t reg,uint8_t *v1, uint8_t *v2)
{struct mcp251x_priv *priv = spi_get_drvdata(spi);priv->spi_tx_buf[0] = INSTRUCTION_READ;priv->spi_tx_buf[1] = reg;mcp251x_spi_trans(spi, 4);*v1 = priv->spi_rx_buf[2];*v2 = priv->spi_rx_buf[3];
}static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
{struct mcp251x_priv *priv = spi_get_drvdata(spi);priv->spi_tx_buf[0] = INSTRUCTION_WRITE;priv->spi_tx_buf[1] = reg;priv->spi_tx_buf[2] = val;mcp251x_spi_trans(spi, 3);
}static void mcp251x_write_bits(struct spi_device *spi, u8 reg,u8 mask, uint8_t val)
{struct mcp251x_priv *priv = spi_get_drvdata(spi);priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY;priv->spi_tx_buf[1] = reg;priv->spi_tx_buf[2] = mask;priv->spi_tx_buf[3] = val;mcp251x_spi_trans(spi, 4);
}static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,int len, int tx_buf_idx)
{struct mcp251x_priv *priv = spi_get_drvdata(spi);//stone addeddev_err(&spi->dev, "-----mcp251x_hw_tx_frame-----");//stone modify if (mcp251x_is_2510(spi)) {//if(1){int i;for (i = 1; i < TXBDAT_OFF + len; i++)mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + i,buf[i]);}else {