注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
辛昕.九门.上 https://home.eeworld.com.cn/space-uid-115166.html [收藏] [复制] [分享] [RSS]
日志

FreeRTOS中文尝试 四种数据传输方式(详细)(仍未完成2)

已有 9083 次阅读2015-7-12 23:48 |个人分类:FreeRTOS中文| 中文

http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_IO/Polled_Transfer_Mode.shtml

基本查询方式

FreeRTOS_read() 和 FreeRTOS_write() 都可以使用查询方式

描述

在查询模式,数据传输动作在外设状态位(估计是忙)时将等待,而中断则没用。

查询传输方式
优点缺点
  • 使用简单
  • 在大多数情况下FreeRTOS_read() or FreeRTOS_write() 都只在读写动作完成时才返回。
  • 相应的驱动无需任何另外的RAM空间做缓冲
  • 在读写持续进行的时候,就算没有动作。这个任务读或写都将在保留在准备或运行状态,因为外设状态位没有改变。这将导致CPU时间被浪费在查询任务中则导致另一个任务不能马上执行。
  • 这没有内置的互斥方法,应用的作者必须自己保证。比如说,使用一个信号锁。当几个任务同时操作一个外设时
  • 不能随时改变读写超时

查询方式是外设打开时的默认方式,并不是所有外设都能提供一个方法,可以使其退出时就返回到查询方式。

除非出错,否则,查询写操作将之在把所有数据写出到外设以后才返回。同理,查询读操作也是

例程

/* */
#include "FreeRTOS_IO.h"

void vAFunction( void )
{
/* */
Peripheral_Descriptor_t xOpenedPort;
BaseType_t xBytesTransferred;

    /*以字符串 SPI2作为打开SPI口的描述符第二个参数现在用不着可以设置成任何内容,
    而为了将来可能的需求,推荐设置为NULL */
    xOpenedPort = FreeRTOS_open( "/SPI2/", NULL );

    if( xOpenedPort != NULL )
    {
        /* 现在,xOpenedPort有了一个合法的描述符可以用于被FreeRTOS+IO API函数调用
        默认,外设将设置为查询方式读写,所以,下面,FreeRTOS_write()将写出10个字节从ucBuffer出去,
        当然这里没有定义这个缓冲*/
        xBytesTransferred = FreeRTOS_write( xOpenedPort, ucBuffer, 10 );
        
        /* 因为使用了查询方式(其实对于写应该翻译为 阻塞),所以这里应该传输10个,除非出错 */
        configASSERT( xBytesTransferred == 10 );
        
        /* 传输模式没有改变,所以接下来的读也是使用查询模式,它将读10个字节入ucBuffer */
        xBytesTransferred = FreeRTOS_read( xOpenedPort, ucBuffer, 10 );
        
        /* 同上,它将读回10个字节 */
        configASSERT( xBytesTransferred == 10 );
    }
    else
    {
        /*端口打开失败 */
    }
}
						
'
2.http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_IO/Circular_Buffer_Transfer_Mode.shtml

中断读环形缓冲方式

只能针对读

在头文件 FreeRTOSIOConfig.h中,把宏 ioconfigUSE_CIRCULAR_BUFFER_RX 设置成1,使环形缓冲传输模式可用。

同时必须在这个配置文件里,显式使能外设。

当选择了环形缓冲模式后,FreeRTOS_read()将不再直接从外设一字节一字节地读取,而是从环形缓冲中读取,而该缓冲是由FreeRTOS+IO(模式下的)数据接收中断服务函数填充的。

这个中断服务函数 和 FreeRTOS的环形缓冲已经在 FreeRTOS+IO代码中包含了,不需要应用开发者自行提供;


中断驱动模式的环形缓冲传输模式
优点缺点
  • 使用简单
  • 自动把这个调用的任务设置成阻塞态来等待读动作完成——假如不能立刻完成的话。  这包含了任务调用FreeRTOS_read()只会在缓冲中有数据时才进行操作。
  • 读超时设置可以让它不会一直阻塞下去(在一直没有数据的情况下)
  • 只是简单使用了一些RAM缓冲,使这种操作变得稳健而有效,尽管需要一次写入缓冲和读出缓冲的复制工作。
  • 从外设读到的数据将被自动填入缓冲,而不会丢失,就算此时,数据已经被接受了,却没有执行FreeRTOS_read()。
  • FreeRTOS+IO驱动需要RAM空间构建环形缓冲,这个缓冲的长度需要由调用FreeRTOS_ioctl()函数在选择这种传输模式时设置。

ioctlUSE_CIRCULAR_BUFFER_RX所需的代码将在调用 FreeRTOS_ioctl()把外设配置成中断模式的环形缓冲读模式完成;

要注意的是,这段申请代码将使能外设中断,并把中断的优先级设置为可能的最低优先级。中断优先级是可以提升,如果需要的话。

例程

/* */
#include "FreeRTOS_IO.h"

void vAFunction( void )
{
/* */
Peripheral_Descriptor_t xOpenedPort;
BaseType_t xReturned;
const uint32_t ulMaxBlock100ms = ( 100UL / portTICK_PERIOD_MS );

    /* 打开SPI2通过 “/SPI2”描述符*/
    xOpenedPort = FreeRTOS_open( "/SPI2/", NULL );

    if( xOpenedPort != NULL )
    {
        /***************** Configure the port *********************************/
    
        /*获得可用描述符
默认下,将设置为读写的普通查询模式。
把默认模式改设成中断驱动模式下的环形缓冲读传输模式。FreeRTOS_ioctl()的第三个参数设置了
缓冲长度。在这个例子里,长度被设置为20.调用FreeRTOS_ioctl()如果成功了将返回pdPASS,简而言之,
这个例子并不检查返回值*/
FreeRTOS_ioctl( xOpenedPort, ioctlUSE_CIRCULAR_BUFFER_RX, ( void * ) 20 ); /*默认情况下,设置成中断模式环形缓冲的传输模式将一直阻塞。更短的阻塞时间可以
通过FreeRTOS_read()函数的返回值获得,不管有没错误。在这个例子中,读阻塞超
时设置为100ms,同样的,这个简单例子不检查返回值。*/
FreeRTOS_ioctl( xOpenedPort, ioctlSET_RX_TIMEOUT, ( void * ) ulMaxBlock100ms ); for( ;; ) { /*通过ucBuffer,从端口读入10个字节,注意,浙江不会直接从外设直接一字节一字
节地读,而会从环形缓冲中读入,它们是由FreeRTOS+IO外设中断服务(填充)的。
被调用的任务将阻塞等待10个字节可读,假如它们不是当时就有。但这个任务不会
阻塞超过100ms,ucBuffer被假定是在外部定义的(因为这里没定义)*/
xBytesTransferred = FreeRTOS_read( xOpenedPort, ucBuffer, 10 ); if( xBytesTransferred == 10 ) { /* 此处,表明在超时时间之前,就读到了10个字节*/
  } else { /* 在从外设读到10个字节以前已经超时了,返回值可能是0到9的任何一个数值*/ } } } else { /* 端口打开失败 */ } }

3.zero copy写方式 :http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_IO/Zero_Copy_Transfer_Mode.shtml

 中断“Zero Copy”传输模式(类似中断写模式,zero copy应该指基本不需要单独存储用的媒介)

只能用于写

在头文件 FreeRTOSIOConfig.h中,把宏 ioconfigUSE_ZERO_COPY_TX 设置成1,使zero copy传输模式可用。同时必须在这个配
置文件里,显式使能外设。低优先级。中断优先级是可以提升,如果需要的话。

当zero copy传输模式被选择后,FreeRTOS_write()不会直接把数据一字节一字节写到外设,相反地,是把所有数据一次性写出去,而首字节的地址被存储在FreeRTOS+IO驱动内部的指针里。然后外设的中断服务程序将使用这些数据执行实际的写操作。一个字节一个字节的数据将被直接从(传入的)缓冲写到外设寄存器中去,不存在存储在任何队列或者缓冲等存储媒介。

FreeRTOS+IO驱动会创建和管理一个互斥锁来保证只有一个任务在一个特定时刻可以执行 一个 zero copy写操作。一个任务在开始执行 zero copy写之前必须获得互斥锁。这个锁可以通过使用 FreeRTOS_ioctl()调用ioctlOBTAIN_WRITE_MUTEX申请代码,在FreeRTOS_write()调用前必须检查FreeRTOS_ioctl()的返回值,以确定是否成功获得互斥锁。一个没有获得互斥锁使用权的任务调用FreeRTOS_write()将会失败。

zero copy写将由 FreeRTOS+IO中断服务函数实现,在调用了FreeRTOS_write()之后,写操作会返回,而中断服务程序会在一段时间后继续写到缓冲中去。这个缓冲不能重用,或者被打断,直到中断服务程序结束写操作。FreeRTOS_ioctl()为了实现目的,要求ioctlOBTAIN_WRITE_MUTEX 和 ioctlWAIT_PREVIOUS_WRITE_COMPLETE可用。

A task that holds the mutex, but does not perform a FreeRTOS_write(), must manually release the mutex by calling FreeRTOS_ioctl() with the request code set to ioctlRELEASE_WRITE_MUTEX.

一个任务保持锁,但并不马上执行一个FreeRTOS_write(),必须通过调用FreeRTOS_ioctl()手动释放锁,需要由 ioctlRELEASE_WRITE_MUTEX设置好。

中断服务程序 和 写操作锁,由 FreeRTOS+IO代码提供,不需要由应用作者提供。

Interrupt Driven Zero Copy Transfer Mode
AdvantagesDisadvantages
  • 自动使任务进入阻塞状态,一直等待写操作锁边的可用,这样可以保证调用FreeRTOS_write()只在它实际使用外设时才占用CPU时间。
  • 调用FreeRTOS_write()将立刻返回,允许被调用的任务在传输过程中执行一些其他操作。
  •  在中断服务程序中,数据直接被从FreeRTOS_write()的参数提供的缓冲中移动到外设的寄存器中,不需要额外的RAM,也不需要多余的数据复制操作。这使其成为一种非常有效的写数据方法。
  • The use of the write mutex means mutual exclusion is built into the FreeRTOS+IO driver.
  • More complex usage mode

为了使用中断模式的zero copy传输写模式,需要通过调用FreeRTOS_ctrl()来配置外设,只要设置了ioctlUSE_ZERO_COPY_TX。这个宏将使外设的中断时能,而且这个中断的优先级将被设置成尽可能的低。 The ioctlSET_INTERRUPT_PRIORITY可以把外设(中断)的优先级提高,如有必要。

Example Usage

Examples are also provided on the FreeRTOS_write() API function documentation page.

/**/
#include "FreeRTOS_IO.h"

void vAFunction( void )
{
/**/
Peripheral_Descriptor_t xOpenedPort;
BaseType_t xReturned;
const uint32_t ulMaxBlock100ms = ( 100UL / portTICK_PERIOD_MS );

    /* 使用“SPI2”这个字符串打开SPI端口,第二个参数现在用不到,
      可以设置成任何东西,为了将来的兼容性,推荐设置为NULL */
    xOpenedPort = FreeRTOS_open( "/SPI2/", NULL );

    if( xOpenedPort != NULL )
    {
        /* 设置端口:现在xOpenedPort包含了一个合法的描述符,可以被其他FreeRTOS+IO函数使用
        
        外设默认使用轮询模式去读写,要把这个默认模式改变成中断的zero copy写模式。
        FreeRTOS_ioctl()的第三个参数不需要使用可以设置成任何数值,但推荐设置为NULL,为了将来的兼容性;
        一个调用成功的FreeRTOS_ioctl()调用会返回pdPASS,为了简单起见,这个例子没有展示出检查返回值。
	 */
        FreeRTOS_ioctl( xOpenedPort, ioctlUSE_ZERO_COPY_TX, NULL );
                
        /* 使用端口:要使用zero copy传输模式,在FreeRTOS_write()调用之前必须获得写操作锁。
        接下来的调用将试图取得锁。如果锁不能马上获得,则任务将阻塞起来等待锁可用。
        但这个任务不会等待超过100ms,如果任务成功获得锁,FreeRTOS_ioctl()将返回pdPASS,否则,
        将返回pdFALL*/
        xReturned = FreeRTOS_ioctl( xOpenedPort, ioctlOBTAIN_WRITE_MUTEX, ulMaxBlock100ms );
        
        if( xReturned == pdTRUE )
        {
            /*  现在锁已经成功获得了,可以使用
             FreeRTOS_write()了,这个调用将从ucBuffer发出100个字节,ucBuffer假设定义在本函数外部。*/
            xReturned = FreeRTOS_write( xOpenedPort, ucBuffer, 100 );
            
            if( xReturned == 100 )
            {
                /*现在写已经成功了,但是,还没有完成,外设的中断服务函数将访问ucBuffer,但
               这个任务可以自由的去做任何事情,而ucBuffer里的数据不会改变。*/
            }
        }

        /* 一会后,任务想要更改ucBuffer里的数据,并且写另外的数据,首先要保证
        原来的写已经完成了,假如可以成功获得写操作锁,则可以知道已经成功地访问ucBuffer了,
        再一次,最大延迟100ms已经过了 */
        xReturned = FreeRTOS_ioctl( xOpenedPort, ioctlOBTAIN_WRITE_MUTEX, ulMaxBlock100ms );
        
        if( xReturned == pdTRUE )
        {
            /*锁获得了,所以任务可以继续执行并且更改buffer,在这个例子,所有要接着做的就是把
            它们清除为零*/
            memset( ucBuffer, 0, 100 );
            
            /* 这个任务已经获得了锁,所以可以写了。 */
            xReturned = FreeRTOS_write( xOpenedPort, ucBuffer, 100 );
            
            /* 再一次,如果返回xReturned == 100.则写已经成功了,它将可以继续执行 */
        }

        /* 在离开函数之前,这个任务要确定 写操作 已经完成,但是这个时候它并不需要执行另一次写操作,所以使用
               FreeRTOS_ioctl()函数,如果(预先)设置了宏ioctlOBTAIN_WRITE_MUTEX ioctlWAIT_PREVIOUS_WRITE_COMPLETE.......
               ——这地方很拗口,我理解是,只要原来设置了 ioctlOBTAIN_WRITE_MUTEX这个宏,然后用宏 ioctlWAIT_
              PREVIOUS_WRITE_COMPLETE这个宏作为参数调用FreeRTOS_ioctl()来释放写操作锁。*/
        FreeRTOS_ioctl( xOpenedPort, ioctlWAIT_PREVIOUS_WRITE_COMPLETE, ulMaxBlock100ms );
    }
}
						

4.(中断驱动)字节型队列读写方式:http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_IO/Character_Queue_Transfer_Mode.shtml

方向:读写
在FreeRTOSIOConfig.h中配置宏 ioconfigUSE_TX_CHAR_QUEUE 和/或者 ioconfigUSE_RX_CHAR_QUEUE 为1,使得对应的 字符型队列传输模式的读写操作可用。它必须在相同的配置文件内显式时能对应的外设可用。

当选择了字符型队列传输模式 写 时,FreeRTOS_write()并不会直接往外设写,反而,它会把数据送到一个队列里,然后外设的中断服务函数会把它们从队列里出队,送到外设里去。

 队列传输模式 读,FreeRTOS_read()不会直接从外设读数据,而是通过一个接收队列,它由FreeRTOS+IO中断服务函数把数据压入队列。

中断服务函数,以及FreeRTOS队列,都包含在FreeRTOS+IO的代码里,不需要应用作者提供

中断驱动字符型队列传输模式
优点缺点
  • 使用简单
  • 自动使任务处于阻塞态来等待读或者写操作完成-如果不能立刻完成。这将保证任务调用了 FreeRTOS_read()和 FreeRTOS_write() 只会在当实际上有动作在处理时才会占用CPU时间。
  • 可以通过设置读写超时可以保证FreeRTOS_read()和FreeRTOS_write()不会永远阻塞;
  • 外设接收的数据会被自动缓冲起来,这使得即使FreeRTOS_read()没操作时即使数据已经接收到了也不会丢失
  • 调用FreeRTOS_write()可能在任何时候被调用,而(这种写法)不需要去等待此前的写操作完成,或者等待外设空闲(时才可以写)
  • 它需要RAM来构建队列,队列的长度可以通过FreeRTOS_ioctl()在设定传输模式时的第三个参数来设定。
  • 字符型队列效率不够,所以他们应该限制使用在那些不需要大量数据读写的应用。举个例子,字符型队列为命令行提供了一个非常方便的传输模式,命令行是一个接收的只是人所能最快速度输入的字符。
  • FreeRTOS队列有一个内建的互斥机制,但只是在字节的层面上。所以,自然地如果两个任务都试图执行一个FreeRTOS_write()或者FreeRTOS_read()在同一个时刻操作队列的数据不会发生冲突。但不能保证不会发生交错(队列数据),如果发生了这个情况,(所以)应用作者可以通过使用任务的优先级或者外部的互斥机制(比如使用一个锁)如果需要的话,去保证(不发生这种数据交错)。

在调用FreeRTOS_ioctl()设置外设使用中断驱动的字符型队列读写类型时,要使用ioctlUSE_CHARACTER_QUEUE_TX and ioctlUSE_CHARACTER_QUEUE_RX来设置;主义这些配置会使能外设中断,而这个中断的优先级被配置的尽可能低,如有必要,可以通过ioctlSET_INTERRUPT_PRIORITY来提升。

例程

/* */
#include "FreeRTOS_IO.h"

void vAFunction( void )
{
/**/
Peripheral_Descriptor_t xOpenedPort;
BaseType_t xReturned;
const uint32_t ulMaxBlock100ms = ( 100UL / portTICK_PERIOD_MS );

    /**/
    xOpenedPort = FreeRTOS_open( "/SPI2/", NULL );

    if( xOpenedPort != NULL )
    {
        /* 上同,我只有一个问题,为什么要两次调用设置,难道不可以 位操作同时操作呢?
         另外就是,两次设置同一个端口,不会覆盖么?*/
        FreeRTOS_ioctl( xOpenedPort, ioctlUSE_CHARACTER_QUEUE_RX, ( void * ) 20 );
        FreeRTOS_ioctl( xOpenedPort, ioctlUSE_CHARACTER_QUEUE_TX, ( void * ) 20 );
                
        /*默认设置下,将会一直阻塞等待。
         低的读写阻塞时间可以保证FreeRTOS_read() 和FreeRTOS_write()调用后,即使出现了错误,
                  也可以返回。在这个例子中,读写都设置阻塞时间是100ms,同样的,为简单起见,这里也是不检查返回值。 */
        FreeRTOS_ioctl( xOpenedPort, ioctlSET_RX_TIMEOUT, ( void * ) ulMaxBlock100ms );
        FreeRTOS_ioctl( xOpenedPort, ioctlSET_TX_TIMEOUT, ( void * ) ulMaxBlock100ms );

        for( ;; )
        {
            /* 从ucBuffer写10个字节到打开的端口,注意到ucBuffer是在本函数之外定义的 */
            xBytesTransferred = FreeRTOS_write( xOpenedPort, ucBuffer, 10 );
            
            /* 这个时候,10个字节将被写到Tx队列里,但并没必要马上写到外设上去,
            检查是个字节都被写到队列里去了——他们应该拥有一个20字节长的队列 */
            configASSERT( xBytesTransferred == 10 );
            
            /*从同一个端口,读10个字节到ucBuffer,注意到,这同样不会直接从外设读字节,而是从Rx队列中读,那是
            FreeRTOS+IO中断服务函数读到的,被调用的任务将保持阻塞状态直到10个字节可用,如果他们并不是
            立马可用,这个任务也不会阻塞到超过100ms*/
            xBytesTransferred = FreeRTOS_read( xOpenedPort, ucBuffer, 10 );
            
            if( xBytesTransferred == 10 )
            {
                /* 超时之前读到了10个字节*/
            }
            else
            {
                /*超时*/
            }
        }
    }
    else
    {
        /*端口打开失败*/
    }
}
						



评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章