1. 前言
上篇文章介绍了使用SPI子系统设计的基于MCP2515的Linux CAN总线驱动程序,这篇文章主要介绍MCP2515的字符设备驱动功能函数的实现。
2. 硬件设计
MCP2515与S3C2416的硬件连接图如图3所示。如硬件原理图可知MCP2515芯片连接在S3C2416芯片的SPI0上,中断接在GPF1上;MCP2515输出连接SN65HVD230 CAN总线收发器,SN65HVD230是德州仪器公司生产的3.3V CAN收发器。为了节省功耗,缩小电路体积,MCP2515 CAN总线控制器的逻辑电平采用LVTTL,SN65HVD230就是与其配套的收发器。
3. MCP2515功能函数的实现
3.1 MCP2515设备结构体
首先需要定义MCP2515的设备结构体,包含MCP2515驱动所需要的相关属性。
struct mcp2515_chip {
canid_t own_id; // CAN ID
canid_t broadcast_id; // Broadcase ID
CanBandRate bandrate; // 波特率
struct cdev cdev; // 字符设备结构体
struct spi_device *spi; // SPI 设备结构体
struct class *class; // Class类
struct work_struct irq_work; // 工作队列
uint32_t count; // CAN 设备计数
uint8_t *spi_transfer_buf; // SPI传输缓冲区
/* SPI输入输出缓冲区 */
struct can_frame spi_tx_buf[MCP2515_BUF_LEN];
struct can_frame spi_rx_buf[MCP2515_BUF_LEN];
uint32_t rxbin; // 接收报文计数
uint32_t rxbout; // 读报文计数
uint32_t txbin; // 发送报文计数
uint32_t txbout; // 发送报文计数
wait_queue_head_t rwq; // 读报文等待队列头
};
在整个驱动中我们都需要使用这个设备结构体,所以在驱动注册成功后在probe函数中要创建这个设备结构体,并且对这个结构体相应的初始化。
static int __devinit mcp2515_probe(struct spi_device *spi)
{
struct mcp2515_chip *chip;
int ret;
/* 为设备结构体申请空间 */
chip = kmalloc(sizeof(struct mcp2515_chip), GFP_KERNEL);
if (!chip) {
ret = -ENOMEM;
goto error_alloc;
}
/* 初始化设备结构体 */
dev_set_drvdata(&spi->dev, chip);
……
/* 初始化工作队列 */
INIT_WORK(&chip->irq_work, mcp2515_irq_handler);
/* 申请中断 */
ret = request_irq(IRQ_EINT(1), mcp2515_irq,
IRQF_DISABLED | IRQF_TRIGGER_FALLING, DEVICE_NAME, spi);
if (ret < 0) {
printk("MCP2515: Request_irq() Error!\n");
goto error_irq;
}
/* 初始化等待队列头 */
init_waitqueue_head(&chip->rwq);
/* 注册设备 */
……
printk ("MCP2515: MCP2515 Can Device Driver.\n");
}
使用dev_set_drvdata函数把spi与设备结构体chip关联起来,在只有spi参数传入的情况下,可以使用dev_get_drvdata获取设备结构体chip。
3.2 MCP2515中断函数
根据芯片手册可知,MCP2515有8个中断源,当中断发生时,INT引脚将被MCP2515拉低为低电平,并且保持低电平状态直至MCU清除中断。
由原理图可知MCP2515芯片外部中断接到S3C2416的外部中断1口上,所以在probe时使用request_irq申请外部中断1的中断。我们可以通过判断中断标志位的方式来接收数据。然而我们使用req