btlib - Bt848/878 frame capture library for QNX 6.x Neutrino

(C)2002 Paul Osmialowski, <newchief@king.net.pl>
Based on PXC200 Linux driver by Alessandro Rubini <rubini@linux.it> (C)2001
This code is absolutely free, you can use it as you wish.

Using btlib in your code

To use my library you have to include btlib.h header file:
#include "btlib.h"
If you don't use any builder nor wizard you have to create your own Makefile, for example:
CC = gcc
OBJS = btlib.o your_program.o
APPNAME = your_program
CFLAGS = -02

all: $(OBJS)
$(CC) -o $(APPNAME) $(OBJS)

btlib.o: btlib.c btlib.h
$(CC) $(CFLAGS) -c btlib.c

your_program: your_program.c your_header.h btlib.h
$(CC) $(CFLAGS) -c your_program.c
In your code you have to allocate DMA buffer. In my programs I'm placing it at data segment using static allocation:
#define DMABUFFSIZE (384*288*3*DMA_BUFFERS)
unsigned char dmabuffer[DMABUFFSIZE];
The DMA_BUFFERS value indicates the number of DMA buffers and is defined in btlib.h header file, you can change it as quantity of your memory let you do that. Using values less than 2 isn't a very good idea.
Also you have to allocate some space to store one Bt_DMA_Buffers structure. Here I'm also using static allocation:
struct Bt_DMA_Buffers dma_buffs;
It's better to make global both of those variables (dma_buffs, dmabuffer) to share them between functions (and globals are always placed in data segment), but if you wish to place them inside of some function don't forget to make them static to avoid usage of stack segment (it may not be big enough to fit dmabuffer anyway).
If you wish to use more than one device allocate one DMA buffer and one struct Bt_DMA_Buffers for each.

Initialization

In your init function you should add this piece of code:
	dma_buffs.dmabuffer=dmabuffer;
dma_buffs.dmabuffsize=DMABUFFSIZE;
dma_buffs.callback=your_callback_function_name;
You don't have to specify callback function name if you don't have one, you can put NULL there instead (i.e. when you write Photon application you may use timer widget instead of callback to check periodically if image should be redrawn).
Callback function should be declared as follows:
void * your_callback_function_name(void * some_argument)
It can return NULL, since it has to return some void * pointer.
As an argument a pointer of type BufferInfo * to recent buffer (the one ready to process) is given (or NULL if there's something wrong with pointers). You can set some global pointer with this argument value (see example.c ). Callback function should work as quick as possible, since device IRQ is masked by calling thread. Also remember to use thread-safe functions inside of your callback function.
To detect and initialize devices you have to call btInit() function. It takes no parameters and returns number of devices that were found. It prints the list of detected devices. Information about all devices is stored inside of global bt_devices array of type struct BtDevice.
Now you can bringup each (or one specific) devices by calling function bt_bringup(). It takes one parameter: pointer to struct BtDevice - device that is going to be used, one from bt_devices array. You can bringup one or more devices one by one.
To set video format use bt_set_video_format() function. It takes two arguments: one is the pointer to struct BtDevice, and another one is the integer format id. I've tested two formats: BT848_IFORM_PAL_BDGHI and BT848_IFORM_NTSC. Remember that PAL resolution (384x288) is different than NTSC (320x240). In my code I always used LO-RES resolutions that were enough for me.
Using all of my Bt878-based framegrabbers I had to call bt_switch_control_comp() function to see colors properly. It takes only one parameter (pointer to struct BtDevice). However, using my Bt848-based Imagenation PXC200F card I had to avoid calling of that function to see colors the right way. Finally in my Photon application I'd used some control which called that function as user clicked it (this function works as a switch - called again switches things back to previous state). This function can be also called while acquisition is turned on.
To start frame capture (acquisition) call bt_initialize_operation function. This function should be called only once, after you use it, to stop and start acquisition again use bt_acq_off() and bt_acq_on() functions. The bt_initialize_operation() function takes three parameters: one is the pointer to struct BtDevice, second one is the pointer to struct Bt_DMA_Buffers (using previously given allocations you can use here &dma_buffs), third is an integer flag value (it can be 0 if you don't want to set any flags). To use 24-bit (3 bytes per pixel) RGB acquisition use PX_FLAG_COLOR. If you don't use this flag, 8-bit (one byte per pixel) monochrome acquisition will be used. You can set more flags using or (|) operator (i.e. you can specify PX_FLAG_COLOR|PX_FLAG_HIRES to use high resolution: 768x576 for PAL and 640x480 for NTSC, however drawing of such big image takes much more time). bt_initialize_operation() function returns zero if everything is ok, or non-zero if it fails (so you have to call bt_shutdown() and btClose() functions and quit your program).
bt_acq_off() and bt_acq_on() function takes two arguments: one is the pointer to struct BtDevice and another one is an integer value (in most cases non_zero to make these functions lock mutex). If you've locked mutex previously, give zero as second argument to avoid next locking.
After whole initialization, if you're using timer to check periodically if image should be redrawn you can enable it. In my programs timer widget is always enabled, but I'm using some global integer variable (called timer_enabled ) initialized with zero that blocks the timer operations.

Termination

If you use timer mentioned above, before you turn off your device (or devices) disable this timer. To turn off one device call bt_shutdown(), it takes one parameter (pointer to struct BtDevice).  After you shutdown all used devices you can call btClose() function, it takes no parameters.

Using captured image

As I wrote above, it's not a very good idea to do image processing at callback function. Instead you can use timer or infinite loop to check if any buffer is ready to use (it's some kind of polling).
There are two ways of checking which buffer is the one to use:
1. You can set at your callback function some global pointer to BufferInfo (for example named readyBuffer, see example.c) initially set to NULL:
BufferInfo * readyBuffer = NULL;

void * use_now(void * arg)
{
readyBuffer = arg;
return NULL;
}
Then you can keep on checking this global pointer if it's not NULL:
	unsigned char * bitmap; /* pointer to captured image */	
for(;;) /* infinite loop */
{
SchedYield(); /* include <sys/neutrino.h> to use that */
if (readyBuffer)
{
/* mydev is a pointer to struct BtDevice */
SyncMutexLock(&(mydev->mutex)); /* include <sys/neutrino.h> to use that */
printf("Current buffer: %d\n", readyBuffer->num);
bitmap=dmabuffer+(readyBuffer->start); /* start field is an offset in dmabuffer */
SyncMutexUnlock(&(mydev->mutex));
/* use captured bitmap here: */

/* */
SyncMutexLock(&(mydev->mutex));
readyBuffer->flags &= ~(BFLAG_OWNED | BFLAG_USER);
readyBuffer = NULL;
SyncMutexUnlock(&(mydev->mutex));
}
}
To make sure that no one else is accessing BtDevice structure the mutex should be locked.
2. You can check recentbuffer field of BtDevice structure and flags of BufferInfo:
	unsigned char * bitmap; /* pointer to captured image */
for(;;) /* infinite loop */
{
SchedYield(); /* include <sys/neutrino.h> to use that */
/* mydev is a pointer to struct BtDevice */
SyncMutexLock(&(mydev->mutex)); /* include <sys/neutrino.h> to use that */
if (mydev->recentbuffer) if ((mydev->recentbuffer->flags) & (BFLAG_OWNED | BFLAG_USER))
{
printf("Current buffer: %d\n", mydev->recentbuffer->num);
bitmap=dmabuffer+(mydev->recentbuffer->start); /* start field is an offset in dmabuffer */
SyncMutexUnlock(&(mydev->mutex));
/* use captured bitmap here: */

/* */
SyncMutexLock(&(mydev->mutex));
mydev->recentbuffer->flags &= ~(BFLAG_OWNED | BFLAG_USER);
}
SyncMutexUnlock(&(mydev->mutex));
}
In this case you don't need callback function nor global pointer to recent buffer.

Few words about infinite loops. It's good to shutdown and close devices before you quit your program. One idea is to use POSIX signal triggered when Ctrl+c is pressed. That way infinite loop can be quit:
#include <signal.h>

volatile int quit = 0;
void quit_now(int i)
{
i = i;
quit = !0;
}

...
signal(SIGINT, quit_now); /* Ctrl+c triggers SIGINT POSIX signal */
while(!quit) /* infinite loop */
{
...
}
printf("Quit\n");
bt_shutdown(mydev);
btClose();
...
That's all, have fun :>