LINUX的字符设备驱动基本原理用到单片机系统中

LINUX设备的驱动比较复杂,要用到单片机这种小型的控制系统,就不需要搞那么复杂了,只要写个中间件,把上层应用和底层驱动完全隔离开,让字符设备的应用使用一个统一的接口,这里设计思路是把一个字符设备当成一个对象,用结构体cdev表示:

typedef struct cdev   
{
	int devno;  
  	struct file_operations *ops; 
  	void *private_data;
  	struct list_head list; 
}CDEV;
devno为设备号,一个单片机系统的每个设备号都是唯一,考虑到单片机系统很少用到动态安装和卸载驱动或设备,所以设备号都是系统定死了,可以用enum的方式枚举系统的所有设备。

ops为设备用的驱动,这和linux相似:

struct file_operations  
{
	char (*open)(struct cdev*);
	char (*read)(struct cdev*,int,void *,int);
	char (*write)(struct cdev*,int,void *,int);
	char (*ioctr)(struct cdev*,int,long);  
};
只是操作设备的形式不再是文件,而是cdev,对字符设备的读写控制函数,在用到实时操作系统的时候可以添加设备的互斥回调函数lock,unlock。

private_data是底层与应用之间的数据通道,用于传递私有数据,比如在open一个串口设备的时候,需要传递波特率等参数,这样就应用在调用cdevopen的时候就可以把参数通过private_data传递给底层驱动open来设置。

list是一个双向链表,用于管理系统所有设备,可以遍历任何一个设备,这里的主要目的是在打开一个设备的时候,可以通过devno找到对应的cdev。

所以单片机的字符设备基本中间件设计:

static struct list_head cdevhead;   

void cdev_init(struct cdev *cdev, struct file_operations *fops)
{  
	memset(cdev, 0, sizeof(struct cdev));  
	INIT_LIST_HEAD(&cdev->list);
	cdev->ops = fops;
}  

int cdev_add(struct cdev *p, int devno)
{
	p->devno = devno;
	list_add(&p->list,&cdevhead);   
	
	return 0;
}


void cdevheadinit()
{
	INIT_LIST_HEAD(&cdevhead);	
}


CDEV *cdevopen(int devno)
{
	struct list_head *cdevnode;
	struct cdev *tmp;
	
	__list_for_each(cdevnode, &cdevhead) 
	{
		tmp = list_entry(cdevnode,struct cdev, list); 
		
		if(tmp->devno == devno)  
		{
			 tmp->ops->open(tmp);
			 return tmp;
		}
	}
	
	return NULL;
}

char cdevread(CDEV *dev,char *buf,int size,int offset)
{
	return dev->ops->read(dev,offset,buf,size);
}

char cdevwrite(CDEV *dev,char *buf,int size,int offset)
{
	return dev->ops->write(dev,offset,buf,size);
}

char cdevioctr(CDEV *dev,int cmd,long arg)
{
	return dev->ops->ioctr(dev,cmd,arg);
}
以如果以串口为例,驱动文件为:

char uartOpen(struct cdev *uartdev,char *para)	
{
	long bt;
	char cReturn=0,dateBit,stopBit;
	static char openflag=0;
	char chek;

	if(openflag != 0)
	{
		return 0;
	}
	
	sscanf((const char*)para,"%d,%1d,%1c,%1d",(int *)&bt,(int *)&dateBit,&chek,(int *)&stopBit);
	
	if(uartdev->devno == DEV_UART0)
	{
		UARTInit(COM1,bt,dateBit,chek,stopBit); 
	}
	
	openflag =1;  
}


char uartRead(struct cdev *uartdev,int offset,char *buf,int len)
{
	if(uartdev->devno == DEV_UART0)
	{
		while(len--)
		{
			UARTGet(COM1,buf);
			buf++;
		}
	}
}



char uartWrite(struct cdev *uartdev,int offset,char *buf,int len)
{
	if(uartdev->devno == DEV_UART0)
	{
		while(len--)
		{
			UARTSend(COM1,*buf);
			buf++;
		}
	}
}


char uartIoctr(struct cdev *uartdev,int cmd,long arg)
{
	if(uartdev->devno == DEV_UART0)
	{
		
	}
	
}
那么应用可以这样写:

static struct file_operations uartOpt;

struct uartcdev
{
	struct cdev *cdevp;
	char uartpara[12];	
};

struct cdev uart0Dev;

char cdev_uart_test()
{
	struct cdev *uart0;
	char buffer[100];
	char uartpara[12] = "115200,8,n,1";
	struct uartcdev uartcdevs;
	int i,j=1000;
	
	memcpy(uartcdevs.uartpara,uartpara,sizeof(uartpara));
	/
	uartcdevs.cdevp = &uart0Dev;
	cdev_init(uartcdevs.cdevp,&uartOpt);
	
	uartOpt.read = uartRead;
	uartOpt.write = uartWrite;
	uartOpt.open = uartOpen;
	uartOpt.ioctr = uartIoctr;
	cdev_add(uartcdevs.cdevp,DEV_UART0);
	uartcdevs.cdevp->private_data = uartpara;  
	
	/
	
	uart0 = cdevopen(DEV_UART0);
	while(1)
	{
		cdevwrite(uart0,"abcdedf\r\n",10,0);  
		if(checkesc()==1)
		{
			break;
		}
	}
}

void main()
{
	cdevheadinit();
	
	while(1)
	{
	<span style="white-space:pre">	</span>cdev_uart_test();
	}
}
这样就应用和驱动简单隔离了,换单片机平台直接修改驱动代码,应用不需要做任何改动,提高了单片机系统应用跨平台的能力。同时在系统管理上可以把cdev部分封装成库,对上提供应用层的api,对下提供驱动层的api,统一系统接口规范,方便对字符设备的管理。



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值