📄 s3c44b0-mcp2510.c
字号:
* CanData为CAN数据结构 *
\***********************************************************/
static void MCP2510_Read_Can(unsigned char nbuffer, PCanData candata)
{
unsigned char mcp_addr = (nbuffer<<4) + 0x31, ctrl;
int IsExt;
char dlc;
IsExt=MCP2510_Read_Can_ID( mcp_addr, &(candata->id));
ctrl=MCP2510_Read(mcp_addr-1);
dlc=MCP2510_Read( mcp_addr+4);
if ((ctrl & 0x08)) {
candata->rxRTR = TRUE;
}
else{
candata->rxRTR = FALSE;
}
dlc &= DLC_MASK;
MCP2510_SRead(mcp_addr+5, candata->data, dlc);
candata->dlc=dlc;
}
/*******************************************\
* 设置MCP2510 CAN总线ID *
* 参数: address为MCP2510寄存器地址*
* can_id为设置的ID值 *
* IsExt表示是否为扩展ID *
\*******************************************/
static void MCP2510_Write_Can_ID(int address, __u32 can_id, int IsExt)
{
__u32 tbufdata;
if (IsExt) {
can_id&=0x1fffffff; //29位
tbufdata=can_id &0xffff;
tbufdata<<=16;
tbufdata|=((can_id>>(18-5))&(~0x1f));
tbufdata |= TXB_EXIDE_M;
}
else{
can_id&=0x7ff; //11位
tbufdata= (can_id>>3)|((can_id&0x7)<<13);
}
MCP2510_Swrite(address, (unsigned char*)&tbufdata, 4);
MCP2510_Read_Can_ID(address, &tbufdata);
DPRINTK("write can id=%x, result id=%x\n",can_id, tbufdata);
}
/***********************************************************\
* 写入MCP2510 发送的数据 *
* 参数: *
* nbuffer为第几个缓冲区可以为0、1、2 *
* CanData为CAN数据结构 *
\***********************************************************/
static void MCP2510_Write_Can( unsigned char nbuffer, PCanData candata)
{
unsigned char dlc;
unsigned char mcp_addr = (nbuffer<<4) + 0x31;
dlc=candata->dlc;
MCP2510_Swrite(mcp_addr+5, candata->data, dlc); // write data bytes
MCP2510_Write_Can_ID( mcp_addr, candata->id,candata->IsExt); // write CAN id
if (candata->rxRTR)
dlc |= RTR_MASK; // if RTR set bit in byte
MCP2510_Write((mcp_addr+4), dlc); // write the RTR and DLC
}
/***********************************************************\
* write and send Can data *
* we must set can id first. *
* parament: *
* nbuffer: which buffer, should be: 0, 1, 2 *
* pbuffer: send data *
* nbuffer: size of data *
\***********************************************************/
static void MCP2510_Write_CanData( unsigned char nbuffer, char *pbuffer, int nsize)
{
unsigned char dlc;
unsigned char mcp_addr = (nbuffer<<4) + 0x31;
dlc=nsize&DLC_MASK; //nbuffer must <= 8
MCP2510_Swrite(mcp_addr+5, pbuffer, dlc); // write data bytes
MCP2510_Write((mcp_addr+4), dlc); // write the RTR and DLC
}
/***********************************************************\
* write and send Remote Can Frame *
* we must set can id first. *
* parament: *
* nbuffer: which buffer, should be: 0, 1, 2 *
\***********************************************************/
static void MCP2510_Write_CanRTR( unsigned char nbuffer)
{
unsigned char dlc=0;
unsigned char mcp_addr = (nbuffer<<4) + 0x31;
dlc |= RTR_MASK; // if RTR set bit in byte
MCP2510_Write((mcp_addr+4), dlc); // write the RTR and DLC
}
// Setup the CAN buffers used by the application.
// We currently use only one for reception and one for transmission.
// It is possible to use several to get a simple form of queue.
//
// We setup the unit to receive all CAN messages.
// As we only have at most 4 different messages to receive, we could use the
// filters to select them for us.
//
// mcp_init() should already have been called.
static void MCP2510_Setup(PCanFilter pfilter)
{
// As no filters are active, all messages will be stored in RXB0 only if
// no roll-over is active. We want to recieve all CAN messages (standard and extended)
// (RXM<1:0> = 11).
//SPI_mcp_write_bits(RXB0CTRL, RXB_RX_ANY, 0xFF);
//SPI_mcp_write_bits(RXB1CTRL, RXB_RX_ANY, 0xFF);
// But there is a bug in the chip, so we have to activate roll-over.
if(pfilter){ //有过滤器
MCP2510_WriteBits(RXB0CTRL, (RXB_BUKT|RXB_RX_STDEXT|RXB_RXRTR|RXB_RXF0), 0xFF);
MCP2510_WriteBits(RXB1CTRL, RXB_RX_STDEXT, 0xFF);
}
else{
MCP2510_WriteBits(RXB0CTRL, (RXB_BUKT|RXB_RX_ANY|RXB_RXRTR), 0xFF);
MCP2510_WriteBits(RXB1CTRL, RXB_RX_ANY, 0xFF);
}
}
/***********************************************************************************\
发送数据
参数:
data,发送数据
Note: 使用三个缓冲区循环发送,没有做缓冲区有效检测
\***********************************************************************************/
static int ntxbuffer=0;
static inline void MCP2510_canTxBuffer(void)
{
switch(ntxbuffer){
case 0:
MCP2510_transmit(TXB0CTRL);
ntxbuffer=1;
break;
case 1:
MCP2510_transmit(TXB1CTRL);
ntxbuffer=2;
break;
case 2:
MCP2510_transmit(TXB2CTRL);
ntxbuffer=0;
break;
}
}
static inline void MCP2510_canWrite(PCanData data)
{
MCP2510_Write_Can(ntxbuffer, data);
MCP2510_canTxBuffer();
}
static inline void MCP2510_canWriteData(char *pbuffer, int nbuffer)
{
MCP2510_Write_CanData(ntxbuffer, pbuffer, nbuffer);
MCP2510_canTxBuffer();
}
static inline void MCP2510_canWriteRTR(void)
{
MCP2510_Write_CanRTR(ntxbuffer);
MCP2510_canTxBuffer();
}
/***********************************************************************************\
中断服务程序
\***********************************************************************************/
static void s3c44b0_isr_mcp2510(void)
{
unsigned char byte;
DPRINTK("enter interrupt!\n");
byte=MCP2510_Read(CANINTF);
if(byte & RX0INT){
MCP2510_Read_Can(3,&(mcp2510dev.MCP2510_Candata[mcp2510dev.nCanRevpos]));
MCP2510_WriteBits(CANINTF, ~RX0INT, RX0INT); // Clear interrupt
NextCanDataPos(mcp2510dev.nCanRevpos);
}
if(byte & RX1INT){
MCP2510_Read_Can(4,&(mcp2510dev.MCP2510_Candata[mcp2510dev.nCanRevpos]));
MCP2510_WriteBits(CANINTF, ~RX1INT, RX1INT); // Clear interrupt
NextCanDataPos(mcp2510dev.nCanRevpos);
}
if(byte & (RX0INT|RX1INT)){
wake_up_interruptible(&(mcp2510dev.wq));
}
}
/*********************************************************************\
CAN设备初始化函数
参数: bandrate,CAN波特率
\*********************************************************************/
static int init_MCP2510(CanBandRate bandrate)
{
unsigned char i,j,a;
MCP2510_Reset();
MCP2510_SetBandRate(bandrate,FALSE);
// Disable interrups.
MCP2510_Write(CANINTE, NO_IE);
// Mark all filter bits as don't care:
MCP2510_Write_Can_ID(RXM0SIDH, 0, TRUE);
MCP2510_Write_Can_ID(RXM1SIDH, 0, TRUE);
// Anyway, set all filters to 0:
MCP2510_Write_Can_ID(RXF0SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF1SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF2SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF3SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF4SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF5SIDH, 0, 0);
//Enable clock output
MCP2510_Write(CLKCTRL, MODE_LOOPBACK| CLKEN | CLK1);
// Clear, deactivate the three transmit buffers
a = TXB0CTRL;
for (i = 0; i < 3; i++) {
for (j = 0; j < 14; j++) {
MCP2510_Write(a, 0);
a++;
}
a += 2; // We did not clear CANSTAT or CANCTRL
}
// and the two receive buffers.
MCP2510_Write(RXB0CTRL, 0);
MCP2510_Write(RXB1CTRL, 0);
// The two pins RX0BF and RX1BF are used to control two LEDs; set them as outputs and set them as 00.
MCP2510_Write(BFPCTRL, 0x3C);
return 0;
}
static void MCP2510_SetFilter(PCanFilter pfilter)
{
MCP2510_Write(MCP2510REG_CANCTRL, MODE_CONFIG);
// Disable interrups.
MCP2510_Write(CANINTE, NO_IE);
if(!pfilter){
// Mark all filter bits as don't care:
MCP2510_Write_Can_ID(RXM0SIDH, 0, TRUE);
MCP2510_Write_Can_ID(RXM1SIDH, 0, TRUE);
// Anyway, set all filters to 0:
MCP2510_Write_Can_ID(RXF0SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF1SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF2SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF3SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF4SIDH, 0, 0);
MCP2510_Write_Can_ID(RXF5SIDH, 0, 0);
}
else{
// Mark
MCP2510_Write_Can_ID(RXM0SIDH, pfilter->Mask, TRUE);
MCP2510_Write_Can_ID(RXM1SIDH, pfilter->Mask, TRUE);
// set all filters to same = pfilter->Filter:
MCP2510_Write_Can_ID(RXF0SIDH, pfilter->Filter, pfilter->IsExt);
MCP2510_Write_Can_ID(RXF1SIDH, pfilter->Filter, pfilter->IsExt);
MCP2510_Write_Can_ID(RXF2SIDH, pfilter->Filter, pfilter->IsExt);
MCP2510_Write_Can_ID(RXF3SIDH, pfilter->Filter, pfilter->IsExt);
MCP2510_Write_Can_ID(RXF4SIDH, pfilter->Filter, pfilter->IsExt);
MCP2510_Write_Can_ID(RXF5SIDH, pfilter->Filter, pfilter->IsExt);
}
//Enable clock output
if(mcp2510dev.loopbackmode)
MCP2510_Write(CLKCTRL, MODE_LOOPBACK| CLKEN | CLK1);
else
MCP2510_Write(CLKCTRL, MODE_NORMAL| CLKEN | CLK1);
// and the two receive buffers.
MCP2510_Write(RXB0CTRL, 0);
MCP2510_Write(RXB1CTRL, 0);
MCP2510_Setup(pfilter);
}
static int s3c44b0_mcp2510_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int flags;
local_irq_save(flags);
switch (cmd){
case UPCAN_IOCTRL_SETBAND: //set can bus band rate
MCP2510_SetBandRate((CanBandRate)arg ,TRUE);
mdelay(10);
break;
case UPCAN_IOCTRL_SETID: //set can frame id data
MCP2510_Write_Can_ID(TXB0SIDH, arg, arg&UPCAN_EXCAN);
MCP2510_Write_Can_ID(TXB1SIDH, arg, arg&UPCAN_EXCAN);
MCP2510_Write_Can_ID(TXB2SIDH, arg, arg&UPCAN_EXCAN);
break;
case UPCAN_IOCTRL_SETLPBK: //set can device in loop back mode or normal mode
if(arg){
MCP2510_Write(CLKCTRL, MODE_LOOPBACK| CLKEN | CLK1);
mcp2510dev.loopbackmode=1;
}
else{
MCP2510_Write(CLKCTRL, MODE_NORMAL| CLKEN | CLK1);
mcp2510dev.loopbackmode=0;
}
break;
case UPCAN_IOCTRL_SETFILTER://set a filter for can device
MCP2510_SetFilter((PCanFilter)arg);
break;
}
local_irq_restore(flags);
DPRINTK("IO control command=0x%x\n", cmd);
return 0;
}
/***************************************************************\
* write and send Can data interface for can device file *
* there are 2 mode for send data: *
* 1, if write data size = sizeof(CanData) then send a full can frame *
* 2, if write data size <=8 then send can data, *
* we must set frame id first *
\****************************************************************/
static ssize_t s3c44b0_mcp2510_write(struct file *file, const char *buffer,
size_t count, loff_t * ppos)
{
char sendbuffer[sizeof(CanData)];
if(count==sizeof(CanData)){
//send full Can frame---frame id and frame data
copy_from_user(sendbuffer, buffer, sizeof(CanData));
MCP2510_canWrite((PCanData)sendbuffer);
DPRINTK("Send a Full Frame\n");
return count;
}
if(count>8)
return 0;
//count <= 8
copy_from_user(sendbuffer, buffer, count);
MCP2510_canWriteData(sendbuffer, count);
DPRINTK("Send data size=%d\n", count);
DPRINTK("data=%x,%x,%x,%x,%x,%x,%x,%x\n",
sendbuffer[0],sendbuffer[1],sendbuffer[2],sendbuffer[3],
sendbuffer[4],sendbuffer[5],sendbuffer[6],sendbuffer[7]);
return count;
}
static int RevRead(CanData *candata_ret)
{
spin_lock_irq(&(mcp2510dev.lock));
memcpy(candata_ret,
&(mcp2510dev.MCP2510_Candata[mcp2510dev.nCanReadpos]),
sizeof(CanData));
NextCanDataPos(mcp2510dev.nCanReadpos);
spin_unlock_irq(&(mcp2510dev.lock));
return sizeof(CanData);
}
static ssize_t s3c44b0_mcp2510_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
CanData candata_ret;
retry:
if (mcp2510dev.nCanReadpos != mcp2510dev.nCanRevpos) {
int count;
count = RevRead(&candata_ret);
if (count) copy_to_user(buffer, (char *)&candata_ret, count);
DPRINTK("read data size=%d\n", count);
DPRINTK("id=%x, data=%x,%x,%x,%x,%x,%x,%x,%x\n",
candata_ret.id, candata_ret.data[0],
candata_ret.data[1], candata_ret.data[2],
candata_ret.data[3], candata_ret.data[4],
candata_ret.data[5], candata_ret.data[6],
candata_ret.data[7]);
return count;
} else {
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
interruptible_sleep_on(&(mcp2510dev.wq));
if (signal_pending(current))
return -ERESTARTSYS;
goto retry;
}
DPRINTK("read data size=%d\n", sizeof(candata_ret));
return sizeof(candata_ret);
}
static int s3c44b0_mcp2510_open(struct inode *inode, struct file *file)
{
int i,j,a;
if(opencount==1)
return -EBUSY;
opencount++;
memset(&mcp2510dev, 0 ,sizeof(mcp2510dev));
init_waitqueue_head(&(mcp2510dev.wq));
//Enable clock output
MCP2510_Write(CLKCTRL, MODE_NORMAL| CLKEN | CLK1);
// Clear, deactivate the three transmit buffers
a = TXB0CTRL;
for (i = 0; i < 3; i++) {
for (j = 0; j < 14; j++) {
MCP2510_Write(a, 0);
a++;
}
a += 2; // We did not clear CANSTAT or CANCTRL
}
// and the two receive buffers.
MCP2510_Write(RXB0CTRL, 0);
MCP2510_Write(RXB1CTRL, 0);
//Open Interrupt
MCP2510_Write(CANINTE, RX0IE|RX1IE);
MCP2510_Setup(NULL);
MCP2510_OPEN_INT();
MOD_INC_USE_COUNT;
DPRINTK("device open\n");
return 0;
}
static int s3c44b0_mcp2510_release(struct inode *inode, struct file *filp)
{
opencount--;
MCP2510_Write(CANINTE, NO_IE);
MCP2510_Write(CLKCTRL, MODE_LOOPBACK| CLKEN | CLK1);
MCP2510_CLOSE_INT();
MOD_DEC_USE_COUNT;
DPRINTK("device release\n");
return 0;
}
static struct file_operations s3c44b0_fops = {
owner: THIS_MODULE,
write: s3c44b0_mcp2510_write,
read: s3c44b0_mcp2510_read,
ioctl: s3c44b0_mcp2510_ioctl,
open: s3c44b0_mcp2510_open,
release: s3c44b0_mcp2510_release,
};
#ifdef CONFIG_DEVFS_FS
static devfs_handle_t devfs_spi_dir, devfs_spiraw;
#endif
static int __init s3c44b0_mcp2510_init(void)
{
int ret;
int flags;
local_irq_save(flags);
EXTINT &= ~(0xf<<(24)); //EINT6 low level
MCP2510_CLOSE_INT();
init_MCP2510(BandRate_250kbps);
local_irq_restore(flags);
ret = register_chrdev(0, DEVICE_NAME, &s3c44b0_fops);
if (ret < 0) {
printk(DEVICE_NAME " can't get major number\n");
return ret;
}
tsMajor = ret;
/* Enable touch interrupt */
ret = request_irq(IRQ_MCP2510, s3c44b0_isr_mcp2510, SA_INTERRUPT,
DEVICE_NAME, s3c44b0_isr_mcp2510);
if (ret) return ret;
#ifdef CONFIG_DEVFS_FS
devfs_spi_dir = devfs_mk_dir(NULL, "can", NULL);
devfs_spiraw = devfs_register(devfs_spi_dir, "0", DEVFS_FL_DEFAULT,
tsMajor, SPIRAW_MINOR, S_IFCHR | S_IRUSR | S_IWUSR,
&s3c44b0_fops, NULL);
#endif
printk(DEVICE_NAME " initialized\n");
return 0;
}
static void __exit s3c44b0_mcp2510_exit(void)
{
printk(DEVICE_NAME " unloaded\n");
}
module_init(s3c44b0_mcp2510_init);
module_exit(s3c44b0_mcp2510_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -