使用DSP和FPGA的视频图像处理卡每秒处理24帧图像,计算出目标物体(球)的位置(一般是通过颜 {MOD}差异分解出物体),然后给ARM中断,ARM通过HPI口读取相关数据。
由于hpi并不是一种真正的字符设备,看起来又很像一种块设备,给定随机地址可以读取大块的dsp数据,这让我不知道怎么去实现,但是块设备的驱动程序我还没怎么看过,所以还是选择字符设备来实现,可是也不能像字符设备一样创建一个缓冲区,把接受到的数据放进去,因为主机从不被动接受数据,都是去主动读取,于是我想不管怎么样,读写就是先设定地址,然后把读写指定大小的数据就是了,对于中断,原来我想的是中断来了后,通过中断服务程序把指定的数据读到内核的一个buffer里,然后通知应用程序来取,不过不像串口那样数据不断累积,直到buffer满,由于我需要传输的数据是图像中物体的位置信息,每秒24帧,我只需要取当前的最新帧的数据就可以了,所以每次都是覆盖先前的数据的,所以该有一个标志位,在中断把数据取到内核时置1,在应用程序读走当前数据后再置0,这样它可以用来作为进程进入睡眠的条件。而我想如果把read函数改成中断驱动的话,那就不能再读别的数据了,而且由于每次传输的数据只有3个u16类型的数据,于是我就想不入用ioctl来读取这3个数据,并且把请求开启和关闭中断的功能也放到ioctl中去,保留原来的read和write,这样我通过read和write可以随意读写dsp的RAM,而又可以在需要是开启中断来获取每帧中目标的信息.
在读写的时候需要对资源进行保护,例如一个流式缓冲区,同一时间只允许一个进程在读或写,于是就用到了信号量或自旋锁来保护资源,自旋锁在单cpu的机器上是无效的,这里只讨论信号量。在内核用的都是互斥信号量,只能获取一次,一旦被一个进程先获取,另一个进程只能阻塞等待信号量被释放。
另一种阻塞的情况是没有数据可读或缓冲区满不能写的时候,进程进入睡眠状态,驱动程序一定是先判断条件,如果不满足,则释放信号量,不能持有信号量进入睡眠,不然进程容易死锁。
这里还是要用到自旋锁,因为中断是随时产生的,如果我在读取共享变量的过程中发生了中断,我读取得数据可能就不是同一帧中的,所以必须在读取共享变量是作保护,使用自旋锁.虽然自旋锁在单cpu情况下无效,不过其实我们用的只是它的关中断的功能.
spin_lock_irq(&dev->hpi_lock)在单cpu的情况下只是调用了cli关中断,所以如果不用自旋锁,只用关中断也是可以的.另外在用interruptible_sleep_on之前要释放锁,否则就是死锁,这样在中断中可以通过wake_up_interruptible唤醒进程,这样实时性应该比查询要好很多了吧.
spin_lock_irq(&dev->hpi_lock);
while(!dev->new_frame)
{
spin_unlock_irq(&dev->hpi_lock);
interruptible_sleep_on(&dev->hpi_queue);
if(signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&dev->hpi_lock);
}
dev->new_frame=0;
ret=copy_to_user((void *)arg,&dev->ball,sizeof(dev->ball))?-EFAULT : sizeof(dev->ball);
spin_unlock_irq(&dev->hpi_lock);
原本是想用wait_event_interruptible来使进程进入睡眠的,不过ms它不能让我在睡眠前释放锁.
基本就是这些了,通过获取的三个变量,来打造机器人的决策进程吧
class CBall
{
public:
BALL_INFO m_ball_info;
int angle;
int fd;
void hpi_init();
};
void CBall::hpi_init(int portNo)
{
if((fd=open("/dev/hpi",O_RDWR))<0)
{
perror("can not open device/n");
exit(1);
}
ioctl(fd,HPI_RESET,0);
ioctl(fd,Enable_int,0);
}
CBall类用来初始化和保存数据.
void* Get_ball_info(void*)//获取球的信息
{
ioctl(m_data.m_ball.fd,Get_ball,&m_data.m_ball.m_ball_info);
m_data.m_ball.angle=25*(2*m_data.m_ball.m_ball_info.cx-240)/240;
}
在main函数中创建一个新的线程用来从内核获取BALL_INFO数据结构
ret=pthread_create(&id3,NULL,Get_ball_info,NULL);
if(ret!=0)
{
printf ("Create Get_ball_info pthread error!/n");
exit (1);
}
当然还要有一个做决策的线程了.
主函数main如下:
int main()
{
pthread_t id1,id2,id3;
int ret;
struct itimerval value, ovalue;
m_data.m_drive.drive_init(1);//驱动初始化
m_data.m_sensor.sensor_init(2);//传感器初始化
m_data.m_ball.hpi_init();
ret=pthread_create(&id1,NULL,Decision,NULL);//创建决策线程
if(ret!=0)
{
printf ("Create Decision pthread error!/n");
exit (1);
}
ret=pthread_create(&id2,NULL,Sensorlisten,NULL);//创建传感器串口监听线程
if(ret!=0)
{
printf ("Create Sensorlisten pthread error!/n");
exit (1);
}
ret=pthread_create(&id3,NULL,Get_ball_info,NULL);
if(ret!=0)
{
printf ("Create Get_ball_info pthread error!/n");
exit (1);
}
signal(SIGALRM, sigroutine);//创建定时器和定时函数
value.it_value.tv_sec=0;
value.it_value.tv_usec=200000;
value.it_interval.tv_sec=0;
value.it_interval.tv_usec=200000;
setitimer(ITIMER_REAL, &value, &ovalue);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
}