DriverFramework
PX4에서 사용하는 POSIX Driver Framework이다. 현재 Snapdragon 및 linux를 사용하는 경우 구현되어 있으며 Pixhawk와 같이 Nuttx를 사용하는 경우 조만간 DriverFramework 를 적용할 예정이다.
가볍고 통합된 driver abstraction layer라 할 수 있다.
즉 향후 모든 PX4에서 DriverFramework 방식을 사용할 예정이므로 Device Driver의 구현 및 구동을 이해하기 위해서는 DriverFramework에 대해서 알아야 한다.

핵심 구조
PX4는 비동기 시스템으로 메시지를 전송에 pub/sub를 사용한다. File handle이 필요없다는 점이 특징이다.

  • 2개 주요 API :
    • publish/subscribe 시스템
      • PX4가 실행되는 시스템에 따라서 file, network이나 공유 메모리를 가진다.
    • global device registry
      • device를 열거하고 configuration을 get/set할 수 있다. linked list만큼 단순하고 file system으로 매핑이 가능하다.

개요
DriverFramework는 최상위 namespace이다.
다음과 같은 주요 class들이 있다.

  • Framework
    • driver framework를 시작/정지
  • DevMgr
    • device driver 등록/해제
    • DevHandle 객체를 얻기/해제
  • WorkMgr (driver가 사용)
    • 주기적으로 동작하는 task 스케줄
    • WorkHandle 생성/해제
  • DevObj (모든 driver의 base class)
    • virtual void _measure()는 주기적으로 호출되는 callback method이다.

framework는 주기적으로 virtual void DevObj::_measure() method를 호출하는 하나의 worker thread(HRTWorkQueue)로 구성된다. 여기서 대응하는 device driver의 데이터 갱신하는 부분을 구현한다.

framework는 새로운 driver의 base로 3개 중간 driver class를 제공한다.

  • VirtualObj
    • simulated driver용 base class
  • I2CDevObj
    • I2C driver용 base class
  • SPIDevObj
    • SPI driver용 base class

 

Framework 초기화
framework은 사용하기 전에 반드시 초기화해야만 한다. framework 내부에서 C++ static initialization이 없다.

DriverFramework::initialize()
...
DriverFramework::shutdown()

기존 조건이 비동기 방식이고 사용할 thread 호출을 block 위해서는 아래와 같이 사용한다.
DriverFramework::waitForShutdown()

이렇게 하면 DriverFramework::shutdown()이 다른 thread에서 호출될때까지 block된다.

 

Driver 초기화
driver가 초기화될때, framework로 등록하고 base class 경로의 instance를 제공한다.

예제)


#define GYRO_BASE_PATH "/dev/gyro"

class Gryo : public I2CDevObj
{
public:
Gyro() :
I2CDevObj("Gyro", GYRO_BASE_PATH, 1000)
{}
protected:
virtual void _measure();
...
};

...
void run()
{
DriverFramework::initialize();
Gyro myGyro();
myGyro.init();

...

DevHandle h;
DevMgr::getHandle("/dev/gyro0", h);

if(!h.isvalid()) {
printf("failed to get device handle\n");
}

h.read(...), h.write(...), ...
...
DevMgr::releaseHandle(h);
DriverFramework::waitForShutdown();
}

init() 호출은 가상의 /dev/gyro0 노드를 생성한다. GYRO_BASE_PATH로 등록한 두번째 driver도 가상의 /dev/gyro1 노드를 생성한다.

device에 핸들을 얻기 위해서 아래와 같이 한다.


myGyro.init();
...
DevMgr::getHandle("/dev/gyro0", h);

DevHandle 객체는 driver가 시작한 이후에 코드의 어느 부분에서든 device path를 통해서 driver에 접근한다.
DevHandle 객체는 복사하여 사용할 수 없다.

 

Driver 시작/정지
기본적으로 driver는 초기화하고 처음 시작할 때 handle을 open한다. 마지막 handle이 release될 때, 실행을 유지하고 있다.
위에서 언급한 사용 예제에서 driver와 통신하기 위해 handle을 이용한다. 하지만 또다른 사용 예제가 있다. DevHandle의 사용은 조건적이고 driver는 명시적으로 start()나 stop()을 이용해서 시작/정지할 수 있다. 이 경우 일반적으로 프로젝트에 특화된 wrapper class가 있다. 이 클래스는 driver로부터 상속하고 직접 data를 처리한다.

myGyro.start();
...
myGyro.stop();

 

POSIX 함수 호출
read, write, ioctl 호출은 DevHandle을 통해 제공된다.
POSIX를 따르는 함수와 errno 값은 getError()로 접근할 수 있다.

DevHandle h;
DevMgr::getHandle("/dev/gyro0", h);

SomeDataStruct data[3];
int ret = h.read(data, sizeof(data));
if (ret < 0) {
printf("Error read failed (%d)\n", h.getError());
}

 

테스팅
단위테스트를 빌드 및 실행하기
make build_linux/test/df_testapp

[Pixhawk] DriverFramework 이해

Leave a Reply

Your email address will not be published. Required fields are marked *