Linux +xenomai 下 CAN 卡中断通信 --WT

本文详细介绍了在Xenomai实时系统下,基于Linux-4.9.38+Xenomai 3.0.5如何进行CAN卡中断通信。主要涉及中断申请和中断类别,包括接收数据、错误、发送数据和唤醒四种中断的处理流程,重点解析了中断服务函数如何识别并处理不同类型的中断事件。

xenomai下can卡中断通信

本文基于 linux-4.9.38+Xenomai 3.0.5 的实时系统
加载用的是xenomai下can模块 以及pci总线驱动rtcan_adv_pci,以及rtcan_sja1000的can控制器驱动。

一、中断申请

中断处理程序不是编译内核时就完全确定的,因此要为开发者留下了编程接口。

int rtdm_irq_request(rtdm_irq_t *irq_handle, unsigned int irq_no,rtdm_irq_handler_t handler, unsigned long flags, const char *device_name, void *arg)

此函数将提供的中断处理程序注册到 IRQ 号上并启用该中断。

  1. [in,out]irq_handle IRQ句柄
  2. [in] irq_no 中断通道号,无符号整数
  3. [in] handler 中断处理函数
  4. [in] flags 注册中断标志
    RTDM_IRQTYPE_SHARED 与其他实时驱动程序一起启用IRQ共享
    RTDM_IRQTYPE_EDGE 将IRQ标记为边缘触发,与正确处理共享的边缘触发的IRQ有关。(Mark IRQ as edge-triggered, relevant for correct handling of shared edge-triggered IRQs).
  5. [in] device_name 显示在实时IRQ列表中的设备名称
  6. [in] arg 调用时将传递给中断处理程序的指针

则可利用设备描述结构体中的pci_dev中的中断号申请并启用中断,等待中断的到来。

/*Register and setup interrupt handling 注册和设置中断处理*/
chip->irq_flags = RTDM_IRQTYPE_SHARED//中断共享
chip->irq_num = pdev->irq;
int rtcan_sja1000_register(struct rtcan_device *dev)
{
   
   
    int                         ret;
    struct rtcan_sja1000 *chip = dev->priv;

    if (chip == NULL)
	return -EINVAL;

    /* Set dummy state for following call 为以下调用设置虚拟状态 */
    dev->state = CAN_STATE_ACTIVE;
    /* Enter reset mode 进入重置模式 */
    rtcan_sja_mode_stop(dev, NULL);

    if ((chip->read_reg(dev, SJA_SR) &
	 (SJA_SR_RBS | SJA_SR_DOS | SJA_SR_TBS)) != SJA_SR_TBS) {
   
   
	printk("ERROR! No SJA1000 device found!\n");
	return -ENODEV;
    }

    dev->ctrl_name = sja_ctrl_name;

    dev->hard_start_xmit = rtcan_sja_start_xmit;
    dev->do_set_mode = rtcan_sja_set_mode;
    dev->do_get_state = rtcan_sja_get_state;
    dev->do_set_bit_time = rtcan_sja_set_bit_time;
    dev->do_enable_bus_err = rtcan_sja_enable_bus_err;
#ifndef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD
    dev->bittiming_const = &sja1000_bittiming_const;
#endif

    chip->bus_err_on = 1;
	// rtdm 中断请求 
    ret = rtdm_irq_request(&dev->irq_handle,
			   chip->irq_num, rtcan_sja_interrupt,
			   chip->irq_flags, sja_ctrl_name, dev);
    if (ret) {
   
   
	printk(KERN_ERR "ERROR %d: IRQ %d is %s!\n",
	       ret, chip->irq_num, ret == -EBUSY ?
	       "busy, check shared interrupt support" : "invalid");
	return ret;
    }

    sja1000_chip_config(dev);

    /* Register RTDM device 注册RTDM设备*/
    ret = rtcan_dev_register(dev);
    if (ret) {
   
   
	    printk(KERN_ERR
		   "ERROR %d while trying to register RTCAN device!\n", ret);
	goto out_irq_free;
    }

    rtcan_sja_create_proc(dev);

    return 0;

 out_irq_free:
    rtdm_irq_free(&dev->irq_handle);

    return ret;
}

二、中断类别

当外界中断触发过后将会将会进入设定得中断服务函数,辨识中断的在驱动程序的类别,中断服务函数根据寄存器的配置将中断响应分为了4类情况的中断,进而执行不同的功能:
1、接收数据的中断:SJA1000使用64字节的FIFO来保存接收到的CAN报文,当接收FIFO中包含有效报文且RIE位置1,产生中断
2、发生错误的中断:错误状态或总线出错等状态发生
3、发送数据的中断:当SJA1000发送缓冲区由锁定变为释放且TIE置位时,产生发送中断,SR.3置位表示上一报文成功发送。
4、唤醒中断:处于睡眠模式检测到总线活动

static int rtcan_sja_interrupt(rtdm_irq_t *irq_handle)
{
   
   
    struct rtcan_device *dev;
    struct rtcan_sja1000 *chip;
    struct rtcan_skb skb;
    int recv_lock_free = 1;
    int irq_count = 0;
    int ret = RTDM_IRQ_NONE;//未处理的中断标志
    u8 irq_source;


    /* Get the ID of the device which registered this IRQ. 获取注册此IRQ的设备的ID。 */
    dev = (struct rtcan_device *)rtdm_irq_get_arg(irq_handle, void);
    chip = (struct rtcan_sja1000 *)dev->priv;

    /* Take spinlock protecting HW register access and device structures. 采用自旋锁保护硬件寄存器访问和设备结构。 */
    rtdm_lock_get(&dev->device_lock);

    /* Loop as long as the device reports an event  循环,不停询问是否发生了中断  */
    while ((irq_source = chip->read_reg(dev, SJA_IR))) //获得中断寄存器的值
    {
   
   
	ret = RTDM_IRQ_HANDLED;//表示已处理的中断
	irq_count++;

	/* Now look up which interrupts appeared 看看出现了种中断*/

	/* Wake-up interrupt?  唤醒中断*/
	if (irq_source & SJA_IR_WUI)
	    dev->state = dev->state_before_sleep;

	/* Error Interrupt?  错误中断?*/
	if (irq_source & (SJA_IR_EI | SJA_IR_DOI | SJA_IR_EPI |
			  SJA_IR_ALI | SJA_IR_BEI)) {
   
   

	    /* Check error condition and fill error frame 检查错误条件并填充错误帧*/
	    if (!((irq_source & SJA_IR_BEI) && (chip->bus_err_on-- < 2))) {
   
   
		rtcan_sja_err_interrupt(dev, chip, &skb, irq_source);

		if (recv_lock_free) {
   
   
		    recv_lock_free = 0;
		    rtdm_lock_get(&rtcan_recv_list_lock);
		    rtdm_lock_get(&rtcan_socket_lock);
		}
		/* Pass error frame out to the sockets 将错误帧传递到套接字*/
		rtcan_rcv(dev, &skb);
	    }
	}

	/* Transmit Interrupt? 发送中断? */
	if (irq_source & SJA_IR_TI) {
   
   
	    /* Wake up a sender */
	    rtdm_sem_up(&dev->tx_sem);

	    if (rtcan_loopback_pending(dev)) {
   
   

		if (recv_lock_free) {
   
   
		    recv_lock_free = 0;
		    rtdm_lock_get(&rtcan_recv_list_lock);
		    rtdm_lock_get(&rtcan_socket_lock);
		}

		rtcan_loopback(dev);
	    }
	}

	/* Receive Interrupt? 接受中断?*/
	if (irq_source & SJA_IR_RI) {
   
   

	    /* Read out HW registers 读出硬件寄存器*/
	    rtcan_sja_rx_interrupt(dev, &skb);

	    /* Take more locks. Ensure that they are taken and
	     * released only once in the IRQ handler. */
	    /* WARNING: Nested locks are dangerous! But they are
	     * nested only in this routine so a deadlock should
	     * not be possible. */
	     
	    if (recv_lock_free) {
   
   
		recv_lock_free = 0;
		rtdm_lock_get(&rtcan_recv_list_lock);
		rtdm_lock_get(&rtcan_socket_lock);
	    }

	    /* Pass received frame out to the sockets 将接收到的帧传递到套接字*/
	    rtcan_rcv(dev, &skb);
	}
    }

    if (chip->irq_ack)
	chip->irq_ack(dev);

    /* Release spinlocks 释放旋转锁 */
    if (!recv_lock_free) {
   
   
	rtdm_lock_put(&rtcan_socket_lock);
	rtdm_lock_put(&rtcan_recv_list_lock);
    }
    rtdm_lock_put(&dev->device_lock);

    return ret;
}

当外界中断事件发生后,SJA1000中断寄存器(IR)置1,INT引脚变为低电平,中断输出,此中断函数被激活,中断函数中将会询问SJA1000控制器硬件的中断控制寄存器的值,然后根据寄存器的值进一步判断中断的类型,进而转入相应的工作。

1、接收中断:
读取中断------利用PCI 驱动读出数据经过pci总线进而传递到内核接收缓冲区,将控制器中的CAN帧数据读出,在中断中需要构建一个临时缓冲区套接字结构skd,然后它被传递给接收处理程序。

if (irq_source & SJA_IR_RI) //SJA_IR_RI   接收数据掩码
{
   
   

	    /* Read out HW registers 读出硬件寄存器*/
	    rtcan_sja_rx_interrupt(dev, &skb);
	    
	    if (recv_lock_free) {
   
   
		recv_lock_free = 0;
		rtdm_lock_get(&rtcan_recv_list_lock);
		rtdm_lock_get(&rtcan_socket_lock);
	    }
	    /* Pass received frame out to the sockets 将接收到的帧传递到套接字*/
	    rtcan_rcv(dev, &skb);
	}

                
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值