RTThread驱动移植要点及AT25256驱动Word文件下载.docx
《RTThread驱动移植要点及AT25256驱动Word文件下载.docx》由会员分享,可在线阅读,更多相关《RTThread驱动移植要点及AT25256驱动Word文件下载.docx(12页珍藏版)》请在冰豆网上搜索。
rt_err_t(*init)(rt_device_tdev);
rt_err_t(*open)(rt_device_tdev,rt_uint16_toflag);
rt_err_t(*close)(rt_device_tdev);
rt_size_t(*read)(rt_device_tdev,rt_off_tpos,void*buffer,rt_size_tsize);
rt_size_t(*write)(rt_device_tdev,rt_off_tpos,constvoid*buffer,rt_size_tsize);
rt_err_t(*control)(rt_device_tdev,rt_uint8_tcmd,void*args);
#ifdefRT_USING_DEVICE_SUSPEND
rt_err_t(*suspend)(rt_device_tdev);
rt_err_t(*resumed)(rt_device_tdev);
#endif
/*设备私有数据*/
void*private;
};
当前RT-Thread支持的设备类型包括:
enumrt_device_class_type
RT_Device_Class_Char=0,/*字符设备*/
RT_Device_Class_Block,/*块设备*/
RT_Device_Class_NetIf,/*网络接口设备*/
RT_Device_Class_MTD,/*内存设备*/
RT_Device_Class_CAN,/*CAN设备*/
RT_Device_Class_Unknown/*未知设备*/
注意:
suspend、resume回调函数只会在RT_USING_DEVICE_SUSPEND宏使能的情况下才会有效。
接口函数说明:
每一个设备的驱动都对应着一个设备管理控制块,在编写驱动的同时要定义该设备的控制块结构体,这个结构体中包含了一些私有数据和一些公共接口函数,公共设备接口函数是设备必须实现的,其中,当设备使用挂起功能时才需要用到
这两个接口函数,其他的六个接口,可以看成是底层设备驱动必须提供的接口。
设备的初始化。
设备初始化完成后,设备控制块的flag会被置成已激活状态(RT_DEVICEFLAG_ACTIVATED)。
如果设备控制块的flag不是已激活状态,那么在设备框架调用rt_device_init_all接口时调用此设备驱动的init接口进行设备初始化。
打开设备。
有些设备并不是系统一启动就已经打开开始运行的,或者设备需要进行数据接收,但如果上层应用还未准备好,设备也不应默认已经使能开始接收数据。
所以建议底层驱动程序,在调用open接口时进行设备的使能。
关闭设备。
在打开设备时,设备框架中会自动进行打开计数(设备控制块中的ocount数据域),只有当打开计数为零的时候,底层设备驱动的close接口才会被调用。
从设备中读取数据。
参数pos指出读取数据的偏移量,但是有些设备并不一定需要制定偏移量,例如串口设备,那么忽略这个参数即可。
这个接口返回的类型是rt_size_t即读到的字节数,如果返回零建议检查errno值。
如果errno值并不是RT_EOK,那么或者已经读取了所有数据,或者有错误发生。
往设备中写入数据。
同样pos参数在一些情况下是不必要的,略过即可。
根据不同的cmd命令控制设备。
命令往往是由底层设备驱动自定义实现的。
2.设备驱动实现的步骤
1.实现RT-Thread中定义的设备公共接口,开始可以是空函数(返回类型是rt_err_t的可默认返回RT_EOK)。
2.根据自己的设备类型定义自己的私有数据域。
特别是可以有多个相同设备的情况下,设备接口可以用同一套,不同的只是各自的数据域(例如寄存器基地址)。
3.按照RT-Thread的对象模型,扩展一个对象有两种方式:
(a)定义自己的私有数据结构,然后赋值到RT-Thread设备控制块的private指针上。
(b)从structrt_device结构中进行派生。
4.根据设备的类型,注册到RT-Thread设备框架中。
3.注册设备
在一个设备能够被上层应用访问前,需要先把这个设备注册到系统中,并添加一些相应的属性。
这些注册的设备均可以采用“查找设备接口”通过设备名来查找设备,获得该设备控制块。
注册设备的原始如下:
rt_err_trt_device_register(rt_device_tdev,constchar*name,rt_uint8_tflags)
其中调用的flags参数支持如下列表中的参数(可以采用或的方式支持多种参数):
#defineRT_DEVICE_FLAG_DEACTIVATE0x000/*未初始化设备*/
#defineRT_DEVICE_FLAG_RDONLY0x001/*只读设备*/
#defineRT_DEVICE_FLAG_WRONLY0x002/*只写设备*/
#defineRT_DEVICE_FLAG_RDWR0x003/*读写设备*/
#defineRT_DEVICE_FLAG_REMOVABLE0x004/*可移除设备*/
#defineRT_DEVICE_FLAG_STANDALONE0x008/*独立设备*/
#defineRT_DEVICE_FLAG_ACTIVATED0x010/*已激活设备*/
#defineRT_DEVICE_FLAG_SUSPENDED0x020/*挂起设备*/
#defineRT_DEVICE_FLAG_STREAM0x040/*设备处于流模式*/
#defineRT_DEVICE_FLAG_INT_RX0x100/*设备处于中断接收模式*/
#defineRT_DEVICE_FLAG_DMA_RX0x200/*设备处于DMA接收模式*/
#defineRT_DEVICE_FLAG_INT_TX0x400/*设备处于中断发送模式*/
#defineRT_DEVICE_FLAG_DMA_TX0x800/*设备处于DMA发送模式*/
RT_DEVICE_FLAG_STREAM参数用于向串口终端输出字符串,当输出的字符是”n”时,自动在前面补一个”r”做分行。
4.卸载设备
将设备从设备系统中卸载,被卸载的设备将不能通过“查找设备接口”找到该设备,可以通过如下接口完成:
rt_err_trt_device_unregister(rt_device_tdev)
Note:
卸载设备并不会释放设备控制块所占用的内存。
5.初始化所有设备
初始化所有注册到设备对象管理器中的未初始化的设备,可以通过如下接口完成:
rt_err_trt_device_init_all(void)
如果设备的flags域已经是RT_DEVICE_FLAG_ACTIVATED,调用这个接口将不再重复做初始化,一个设备初始化完成后它的flags域RT_DEVICE_FLAG_ACTIVATED应该被置位。
6.查找设备
根据指定的设备名称来查找设备,可以通过如下接口完成:
rt_device_trt_device_find(constchar*name)
使用以上接口时,在设备对象类型所对应的对象容器中遍历寻找设备对象,然后返回该设备,如果没有找到相应的设备对象,则返回RT_NULL。
7.打开设备
根据设备控制块来打开设备,可以通过如下接口完成:
rt_err_trt_device_open(rt_device_tdev,rt_uint16_toflags)
其中oflags支持以下列表中的参数:
#defineRT_DEVICE_OFLAG_RDONLY0x001/*只读模式访问*/
#defineRT_DEVICE_OFLAG_WRONLY0x002/*只写模式访问*/
#defineRT_DEVICE_OFLAG_RDWR0x003/*读写模式访问*/
如果设备flags域包含RT_DEVICE_FLAG_STANDALONE参数,将不允许重复打开。
8.关闭设备
根据设备控制块来关闭设备,可以通过如下接口完成:
rt_err_trt_device_close(rt_device_tdev)
9.读设备
根据设备控制块来读取设备,可以通过如下接口完成:
rt_size_trt_device_read(rt_device_tdev,rt_off_tpos,void*buffer,rt_size_tsize)
根据底层驱动的实现,通常这个接口并不会阻塞上层应用线程。
返回值是读到数据的大小(以字节为单位),如果返回值是0,需要读取当前线程的errno来判断错误状态。
10.写设备
根据设备控制块来写入设备,可以通过如下接口完成:
rt_size_trt_device_write(rt_device_tdev,rt_off_tpos,constvoid*buffer,rt_size_tsize)
返回值是写入数据的大小(以字节为单位),如果返回值是0,需要读取当前线程的errno来判断错误状态。
11.控制设备
根据设备控制块来控制设备,可以通过如下接口完成:
rt_err_trtdevicecontrol(rt_device_tdev,rt_uint8_tcmd,void*arg)
cmd命令参数通常是和设备驱动程序相关的。
12.设置数据接收指示
设置一个回调函数,当硬件设备收到数据时回调给应用程序以通知有数据达到。
可以通过如下接口完成设置接收指示:
rt_err_trt_device_set_rx_indicate(rt_device_tdev,rt_err_t(*rx_ind)(rt_device_tdev,rt_size_tsize))
回调函数rx_ind由调用者提供,当硬件设备接收到数据时,会回调这个函数并把收到的数据长度放在size参数中传递给上层应用。
上层应用线程应在收到指示时,立刻从设备中读取数据。
13.设置发送完成指示
在上层应用调用rt_device_write写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。
这个回调函数会在底层硬件给出发送完成时(例如DMA传送完成或FIFO已经写入完毕产生完成中断时)被调用。
可以通过如下接口完成设备发送完成指示:
rt_err_trt_device_set_tx_complete(rt_device_tdev,rt_err_t(*tx_done)(rt_device_tdev,void*buffer))
回调函数tx_done由调用者提供,当硬件设备发送完数据时,由驱动程序回调这个函数并把发送完成的数据块地址buffer做为参数传递给上层应用。
上层应用(线程)在收到指示时应根据发送buffer的情况,释放buffer内存块或为下一个写数据做缓存。
14.AT25256驱动例子
AT25256概述
AT2526是一个SPI总线接口器件,它由片选信号CS,时钟信号SCK,输入信号SI,输出信号SO这四根信号线组成。
AT25256的始终信号始终是输入的,因此它一般作为从设备。
单片机在选择该器件工作时必须将CS信号拉低,同时设置时钟信号满足AT25256基本时序要求即可以和单片机通信。
单片机在选择AT25256工作时必须先写入操作码,使得从设备来完成相应的工作,AT25256的操作码如下图:
具体的操作码对应的功能详解查看AT25256的Datasheet。
14.2内核态AT25256驱动实现
14.2.1设备类型的定义,在RT-Theard操作系统中定义一个“AT25256”设备
structrt_at256_lpc
structrt_deviceparent;
/*bufferforreception*/
rt_uint8_tread_index,save_index;
rt_uint8_trx_buffer[RT_UART_RX_BUFFER_SIZE];
structrt_at256_lpcat256_device;
14.2.2定义并实现RT-Thread中定义的设备公共接口
voidrt_hw_at256_init(void)
structrt_at256_lpc*at256;
/*getuartdevice*/
at256=&
at256_device;
/*deviceinitialization*/
at256->
parent.type=RT_Device_Class_Char;
/*deviceinterface*/
parent.init=rt_at256_init;
//设备初始化操作
parent.open=rt_at256_open;
//打开设备
parent.close=rt_at256_close;
//关闭设备
parent.read=rt_at256_read;
//设备读操作
parent.write=rt_at256_write;
//设备写操作
parent.control=RT_NULL;
parent.user_data=RT_NULL;
rt_device_register(&
at256->
parent,
"
at256"
RT_DEVICE_FLAG_RDWR|RT_DEVICE_FLAG_STREAM|RT_DEVICE_FLAG_INT_RX);
//注册设备
}
初始化函数主要是实现了内核中必须的公共接口函数,这些接口也是上层应用通过RT-Thread设备接口进行访问的实际底层接口,同时向内核设备框架注册,在一个设备能够被上层应用访问前,需要先把这个设备注册到系统中,并添加一些相应的属性。
rt_at256_init函数主要实现的是AT25256具体初始化操作函数,包括片选信号CS信号的控制,SCK时钟信号的设置。
staticrt_err_trt_at256_init(rt_device_tdev)
PINSEL_CFG_TypePinCfg;
//unsignedcharstate=0;
PinCfg.Funcnum=3;
PinCfg.OpenDrain=0;
PinCfg.Pinmode=0;
PinCfg.Portnum=0;
PinCfg.Pinnum=15;
PINSEL_ConfigPin(&
PinCfg);
PinCfg.Pinnum=17;
PinCfg.Pinnum=18;
PinCfg.Pinnum=16;
PinCfg.Funcnum=0;
//initializeSPIconfigurationstructuretodefault
SPI_ConfigStructInit(&
SPI_ConfigStruct);
//SPI_ConfigStruct.ClockRate=400000;
SPI_ConfigStruct.ClockRate=200000;
SPI_ConfigStruct.CPHA=SPI_CPHA_FIRST;
SPI_ConfigStruct.CPOL=SPI_CPOL_HI;
//InitializeSPIperipheralwithparametergiveninstructureabove
SPI_Init(SPI,&
//Initialize/CSpintoGPIOfunction
CS_Init();
WriteStateRegister(0x02);
returnRT_EOK;
rt_at256_open可以为空,默认返回RT_EOK,因为在设备初始化完成的时候,该设备已经是处于可工作的状态。
staticrt_err_trt_at256_open(rt_device_tdev,rt_uint16_toflag)
rt_at256_close可以为空,默认返回RT_EOK。
staticrt_err_trt_at256_close(rt_device_tdev)
rt_at256_read函数主要实现的是单片机对AT25256的读操作函数,即通过该函数,单片机可以读取从设备指定地址的数据。
staticrt_size_trt_at256_read(rt_device_tdev,unsignedshortaddr,char*readbuf,unsignedcharcount)
//voidAddrAdjustRead(unsignedshortaddr,unsignedcharcount,char*readbuf)
unsignedshortpage=0;
//unsignedcharremaincount=0;
unsignedcharpagecount=0;
page=(unsignedshort)(addr/PAGE_SIZE);
pagecount=START_PAGE_ADDR(page+1)-addr;
if(page<
PAGE_NUM)
{
if((addr+count)<
=START_PAGE_ADDR(page+1))
ReadAt25256(addr,readbuf,count);
}
else
ReadAt25256(addr,readbuf,pagecount);
ReadAt25256(START_PAGE_ADDR(page+1),&
readbuf[pagecount],count-pagecount);
else//最后一个扇区的操作
ReadAt25256(START_PAGE_ADDR(0),&
return0;
rt_at256_write函数实现的是单片机对AT25256的写操作函数,通过该函数,可以实现向从设备指定地址写入指定的数据。
staticrt_size_trt_at256_write(rt_device_tdev,unsignedshortaddr,char*writebuf,unsignedcharcount)
//voidAddrAdjustWrite(unsignedshortaddr,unsignedcharcount,char*writebuf)
page=(unsignedshort)(addr/PAGE