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 号上并启用该中断。
- [in,out]irq_handle IRQ句柄
- [in] irq_no 中断通道号,无符号整数
- [in] handler 中断处理函数
- [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). - [in] device_name 显示在实时IRQ列表中的设备名称
- [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);
}

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





