Linux设备驱动思想在STM32编程中的应用

这几天看了一下Linux设备驱动,发现这套思想其实也可以用在普通的单片机编程上。这种思想较好的分割了驱动层和应用层的任务,方便分层开发。

以前,我们开发STM32驱动的时候,会给设备写一套函数来控制它。假设现在有一块单片机开发板,外接设备UART和LCD。

我们会这样写函数

--------UART驱动------

void UART_Init();

int UART_Send(const char *str,int size);

int UART_Rec(char *rec_buffer);

...

--------LCD驱动-------

void LCD_Init();

void LCD_DrawPoint(int x0,int y0);

void LCD_DrawPicture(picure* mypicture);

...

然后我们拿这些函数进行应用开发。

这样做并不是不可以,但不同的设备,其函数名、形参都不一致。这导致做纯应用层开发的人,无法快速理解和使用设备。要么他需要经常去咨询驱动层的工程师,要么干脆应用和驱动全由一个人负责。

当引用Linux驱动设备的思想之后,这个问题可以得到解决。这种思想大概就是,不管驱动层实际控制的是什么设备,应用层只需要使用open,read,write,close这样的函数就能操作设备。这些函数原型都是广为人知的C语言文件操作函数。

首先定义一个结构体,名为Device,它包含了设备名等一些必要信息,和对设备的四个操作:open,read,write,close。这些操作由函数指针实现。

typedef struct device
{
    /* data */
    char  device_name[10];
    int (*open)(const char * pathname, int flags);
    int (*read)(int fd, void * buf, int count);
    int (*write)(int fd, void * buf, int count);
    int (*close)(int fd);

}device;

这个device就可以实例化为各种具体的设备。比如一个UART,我可以定义device uart;然后设置uart的device_name为"uart"。

然后定义uart_open函数,在里面进行UART的初始化。

int uart_open(const char * pathname, int flags)

{

 /*

UART引脚初始化;

UART时钟初始化;

UART波特率设置....

*/

}

同理,定义uart_read,uart_write,uart_close。uart_read的功能可为从UART判断标志位并接收数据,uart_write的功能可为UART发送数据。uart_close可以为关闭UART时钟。

 

定义好了之后,把uart的函数指针赋予初始值就行。

device uart =
{
    "uart",
    uart_open,
    uart_read,
    uart_write,
    uart_close

};

 

这样,一个活生生的设备就体现出来了。现在最重要的是uart这个结构体变量。只要拿到了这个变量,就可以控制UART。因为这里面有UART的基本信息和操作它需要用到的函数。

我们还可以定义device lcd,把它也进行赋值,操作过程和uart一致。

系统中有很多设备uart,lcd,led,button....都可以像这样定义device xxx。为了方便管理,做一个数组(或者链表等其他线性表),把所有的device变量组织在一起,取名设备管理表。

typedef struct device_list
{
    device *Device_List[DEVICE_LIST_NUM];
    int Device_List_index;
}device_list;

device_list global_device_list ;

把设备加入这张设备管理表的操作,就叫设备注册。
void Device_register(device* mydevice)
{
    int index = global_device_list.Device_List_index;

    global_device_list.Device_List[index++] = mydevice;
    global_device_list.Device_List_index = index;

}

所有的设备都注册好之后,以后需要使用哪个设备xxx,可以凭设备名device_name在这张表中搜索,最后返回搜索结果。

int Device_find(const char* device_name)
{
    int i = 0;
    int index = global_device_list.Device_List_index;
    for(i=0;i     {
         
        if(strcmp(global_device_list.Device_List[i]->device_name,device_name)==0)
        {
            return i;
        }
    }

    return DEVICE_LIST_NUM;

}

 

拿到了搜索结果,意味着拿到了device xxx这个结构体变量,就可以通过它的结构体成员open,read,write,close来控制设备了。岂不是很方便?要注意的是,read,write的函数形参里面有void *类型;这意味着你其实可以传入任何类型的数据指针,而不仅仅是传统意义上的字符串。这很有用,因为不同的设备需要进行交互的信息可能不一样,单纯字符串想要较好的表示这些信息有难度。

int Device_open(const char * pathname, int flags)
{   
    device* dev;
    int fd = Device_find(pathname);
    dev = global_device_list.Device_List[fd];

    (* dev).open(  pathname,  flags);

    return fd;
}

int Device_read(int fd, void * buf, int count)
{
    device* dev;
    dev = global_device_list.Device_List[fd];
    return (*dev).read( fd,   buf,   count);

}

int Device_write(int fd, void * buf, int count)
{
    device* dev;
    dev = global_device_list.Device_List[fd];
    return (*dev).write( fd,   buf,   count);

}
int Device_close(int fd )
{
    device* dev;
    dev = global_device_list.Device_List[fd];
    return (*dev).close( fd );

}

使用举例:

void main()
{
    int fd = 0;
 
    /*先注册*/

    Device_register(&uart);
    Device_register(&lcd);
  

    /*后使用*/
    fd = Device_open("uart",0);
 
    Device_read(fd,NULL,0);
    Device_write(fd,"*****i am uart***\n",12);
    Device_close(fd);
    printf("fd = %d \n",fd);

    fd = Device_open("lcd",0);
    Device_read(fd,NULL,0);
    Device_write(fd,"*****i am lcd****\n",12);
    Device_close(fd);
    printf("fd = %d \n",fd);


     

}

Linux设备驱动思想在STM32编程中的应用_第1张图片

这样做之后,应用层开发人员无须在各种不同的驱动API中焦头烂额。他只需要知道两点:

1.设备名。"uart","lcd","led"...

2.read和write的数据格式。

这就够了。open,read,write,close这些都是市民皆知的函数。无论什么设备,接口都能保持一致。相当完美的分离了应用层和驱动层的开发。由于不上文件系统,所以原有的open,read,write,close以Device_open,Device_read,Device_write,Device_close替代。

当然,其实文件操作的函数也不光有这四个,还有seek等等。感兴趣可以加入到device结构体里面。但要注意,定义的函数指针越多,应用开发的负担越重。在够用的情况下,尽量少定义一些没用的成员是明智之举。

 

 

 

 

你可能感兴趣的:(STM32单片机开发技术,单片机)