Ardupilot下移植CAN总线驱动

前言

最近由于工作上的需要上CANopen的协议栈,所以先来把ardupilot下的总线驱动调试通过。在正式调试之前,还是先来梳理下nuttx的驱动模型;
说明一点,这篇文章主要是描述驱动框架,不会去详细介绍CAN总线的工作原理;

上半部分和下半部分

Nuttx的驱动在框架上分成了两个部分,实际上也就是面向应用层和底层,这两个部分来实现整个设备的驱动;这里直接用can来举例;
文件路径:
面向应用层:…/ardupilot/modules/PX4NuttX/nuttx/drivers/can.c
面向底层:…/ardupilot/modules/PX4NuttX/nuttx/arch/arm/src/stm32/stm32_can.c

接下来在找到了我们需要的驱动目标文件了以后,我们就深如下去分析(从底层到上层):

/****************************************************************************
 * Private Data
 ****************************************************************************/
//STM32 can寄存器组的操作函数接口(与硬件相关部分)
static const struct can_ops_s g_canops =
{
  .co_reset         = can_reset,
  .co_setup         = can_setup,
  .co_shutdown      = can_shutdown,
  .co_rxint         = can_rxint,
  .co_txint         = can_txint,
  .co_ioctl         = can_ioctl,
  .co_remoterequest = can_remoterequest,
  .co_send          = can_send,
  .co_txready       = can_txready,
  .co_txempty       = can_txempty,
};

//只拿CAN1来举例,定义了CAN底层驱动的寄存器参数
#ifdef CONFIG_STM32_CAN1
static struct stm32_can_s g_can1priv =
{
  .port             = 1,
  .canrx0           = STM32_IRQ_CAN1RX0,
  .cantx            = STM32_IRQ_CAN1TX,
  .filter           = 0,
  .base             = STM32_CAN1_BASE,
  .fbase            = STM32_CAN1_BASE,
  .baud             = CONFIG_CAN1_BAUD,
};

//ops+priv合起来就是驱动(方法+数据)
static struct can_dev_s g_can1dev =
{
  .cd_ops           = &g_canops,
  .cd_priv          = &g_can1priv,
};

其实这个很好理解,对比以前裸奔跑单片机的时候,套用STM32的标准外设库,就是将那些寄存器地址、宏定义寄存器值、寄存器操作函数归类起来:
.cd_ops = &g_canops --------->寄存器操作函数
.cd_priv = &g_can1priv------->寄存器地址、宏定义寄存器值
从g_canops的成员函数可以看出,基本上就是对应上了我们之前裸机的硬件初始化中设计的一些列函数了;

接下来看看上层的源代码:

/* This is the device structure used by the driver.  The caller of
 * can_register() must allocate and initialize this structure.  The
 * calling logic need only set all fields to zero except:
 *
 *   The elements of 'cd_ops', and 'cd_priv'
 *
 * The common logic will initialize all semaphores.
 */

struct can_dev_s
{
  uint8_t              cd_ocount;        /* The number of times the device has been opened */
  uint8_t              cd_npendrtr;      /* Number of pending RTR messages */
  uint8_t              cd_ntxwaiters;    /* Number of threads waiting to enqueue a message */
  sem_t                cd_closesem;      /* Locks out new opens while close is in progress */
  sem_t                cd_recvsem;       /* Used to wakeup user waiting for space in         cd_recv.buffer */
  struct can_txfifo_s  cd_xmit;          /* Describes transmit FIFO */
  struct can_rxfifo_s  cd_recv;          /* Describes receive FIFO */
                                         /* List of pending RTR requests */
  struct can_rtrwait_s cd_rtr[CONFIG_CAN_NPENDINGRTR];
  FAR const struct can_ops_s *cd_ops;    /* Arch-specific operations */
  FAR void            *cd_priv;          /* Used by the arch-specific logic */
};

这个结构体和下半部分的同名也叫can_dev_s,但是可以看见的是里面的成员变量基本上都是面向应用程序的,例如设备打开次数、接收/发送的信号量,接收/发送的队列…最关键的就是:
FAR const struct can_ops_s cd_ops; / Arch-specific operations */
FAR void cd_priv; / Used by the arch-specific logic */
没错就是之前分析的下半部分;到这里就能明白,这个完整驱动的全貌了。

驱动注册

之前的文章中有对Nuttx的驱动进行分析,实际上就是一个双向的链表,来组织所有的驱动程序(类似上面的can驱动);

/************************************************************************************
 * Name: can_register
 *
 * Description:
 *   Register serial console and serial ports.
 *
 ************************************************************************************/

int can_register(FAR const char *path, FAR struct can_dev_s *dev)
{
  int i;

  /* Initialize the CAN device structure */

  dev->cd_ocount = 0;

  sem_init(&dev->cd_xmit.tx_sem, 0, 0);
  sem_init(&dev->cd_recv.rx_sem, 0, 0);
  sem_init(&dev->cd_closesem, 0, 1);

  for (i = 0; i < CONFIG_CAN_NPENDINGRTR; i++)
    {
      sem_init(&dev->cd_rtr[i].cr_sem, 0, 0);
      dev->cd_rtr[i].cr_msg = NULL;
      dev->cd_npendrtr--;
    }

  /* Initialize/reset the CAN hardware */

  dev_reset(dev);

  /* Register the CAN device */

  canvdbg("Registering %s\n", path);
  return register_driver(path, &g_canops, 0666, dev);
}

register_driver就是来完成这个驱动数据结构注册到nuttx里面驱动链表里面的。当我们在操作系统中启动时候调用如上的代码,我们就能在操作系统启动后看到注册成功的驱动信息了;

结果
里面的can0 就是我们注册的驱动了,具体的配置和例子参考nuttx的example就OK啦~
Ardupilot下移植CAN总线驱动_第1张图片

关于nuttx的系统配置

也就是config.h里面的宏配置,这个和之前的nuttx配置还有些差异,说到这里其实我们有必要梳理下,在执行了 make px4-v2之后,在终端上能看见如下的信息:
Ardupilot下移植CAN总线驱动_第2张图片
其中就有这么一句话:%%Configuring NuttX for px4fmu-v2,在之前我们可以发现编译过程的路径:
Entering directory `/home/alvin/Desktop/FC/ardupilot/modules/PX4Firmware’

所以我们就直接进入该路径下查看Makefile.make,就能找到下面的这段脚本

NUTTX_ARCHIVES		 = $(foreach board,$(BOARDS),$(ARCHIVE_DIR)$(board).export)
.PHONY:			archives
archives:		checksubmodules $(NUTTX_ARCHIVES)

$(ARCHIVE_DIR)%.export:	board = $(notdir $(basename $@))
$(ARCHIVE_DIR)%.export:	configuration = nsh
$(NUTTX_ARCHIVES): $(ARCHIVE_DIR)%.export: $(NUTTX_SRC)

	#这句话就是开始配置nuttx,打印的输出的;
	@$(ECHO) %% Configuring NuttX for $(board)
	$(Q) (cd $(NUTTX_SRC) && $(RMDIR) nuttx-export)
	$(Q)+ $(MAKE) -C $(NUTTX_SRC) -r $(MQUIET) distclean
	
	#下面这段话就可以发现$(PX4_BASE)nuttx-configs/$(board) 配置文件是从这里出来的
	$(Q) (cd $(NUTTX_SRC)/configs && $(COPYDIR) $(PX4_BASE)nuttx-configs/$(board) .)
	
	#然后在执行配置
	$(Q) (cd $(NUTTX_SRC)tools && ./configure.sh $(board)/$(configuration))
	@$(ECHO) %% Exporting NuttX for $(board)
	$(Q)+ $(MAKE) -C $(NUTTX_SRC) -r $(MQUIET) CONFIG_ARCH_BOARD=$(board) export
	$(Q) $(MKDIR) -p $(dir $@)
	$(Q) $(COPY) $(NUTTX_SRC)nuttx-export.zip $@
	$(Q) (cd $(NUTTX_SRC)/configs && $(RMDIR) $(board))

所以从上面的可以得出我们要增加驱动配置的话,是需要去$(PX4_BASE)nuttx-configs/$(board)这个地方修改配置文件的;
在nuttx/tools的路径下我们可以执行配置的脚本文件configure.sh,在脚本里面可以发现这句话:
configlist=find ${TOPDIR}/configs -name defconfig
所以最终我们就能发现我们要找的配置文件就是一个叫defconfig的文件,而这个文件就在刚刚所提到的$(PX4_BASE)nuttx-configs/$(board) 这个地方:
…/ardupilot/modules/PX4Firmware/nuttx-configs/px4fmu-v2/nsh

具体如何配置可以参照之前nuttx下的kconfig配置方式。

你可能感兴趣的:(Ardupilot)