硬汉嵌入式论坛

 找回密码
 立即注册
查看: 2125|回复: 5
收起左侧

[RL-TCPnet教程] 【RL-TCPnet网络教程】第21章 RL-TCPnet之高效的事件触发框架

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2017-11-13 15:56:13 | 显示全部楼层 |阅读模式
第21章      RL-TCPnet之高效的事件触发框架



        本章节为大家讲解高效的事件触发框架实现方法,BSD Socket编程和后面章节要讲解到的FTP、TFTP和HTTP等都非常适合使用这种方式。实际项目中也推荐大家采用这种方式,不过仅适用于RTOS环境,比如RTX、FreeRTOS或者uCOS-III均可,裸机方式不支持。
        另外,前面章节讲解的TCP和UDP的原始socket使用这种方式不太方便,因为应用程序的编写会变的稍麻烦,不像BSD Socket那么省事。
21.1 初学者重要提示
21.2 高效的事件触发框架说明
21.3 RTX系统实例修改方法
21.4 uCOS-III系统实例修改方法
21.5 FreeRTOS系统实例修改方法
21.6 实验操作和实验例程说明
21.7      总结



21.1  初学者重要提示

1、实际项目中强烈推荐大家采用这种方式,不过仅适用于RTOS环境,比如RTX、FreeRTOS或者uCOS-III均可。后面章节配套的例子,基本也都采用这种方式。
2、面章节讲解的TCP和UDP的原始socket使用这种方式不太方便,因为应用程序的编写会变的稍麻烦,不像BSD Socket这么省事。

21.2  高效的事件触发框架说明


        讲解高效的事件触发框架之前,先看下没有使用事件触发方式时,ping的响应速度,以例程:V6-1024_RL-TCPnet实验_BSD Socket服务器之TCP(RTX)为例进行说明:
21.1.png

下面是使用了事件触发方式时,ping的响应速度,以例程:V6-1030_RL-TCPnet实验_高效的事件触发框架(RTX)为例进行说明:
21.2.png

从上面的两个响应速度的对比中,可以看出,使用了时间触发方式的例子,响应速度都在1ms以下,效果还是非常明显的。
        前面章节配套的例子里面,响应速度慢,是因为我们都是周期性的调用RL-TCPnet的主处理函数main_TcpNet(),比如前面BSD Socket服务器章节配套的例子中:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskTCPMain
  4. *    功能说明: RL-TCPnet网络主任务
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *   优 先 级: 5
  8. *********************************************************************************************************
  9. */
  10. __task void AppTaskTCPMain(void)
  11. {
  12.      while (1)
  13.      {
  14.          /* RL-TCPnet处理函数 */
  15.          main_TcpNet();
  16.          os_dly_wait(2);
  17.      }
  18. }
复制代码
这种方式有如下两个缺点:
1、没有网络通信时也要周期性的执行。
2、实时响应差,因为在延迟的这段时间内有网络数据包的话,数据包得不到及时的处理。
        另外特别注意一点,一些不理解的读者会问,我们的底层函数里面不是有以太网中断吗,为什么还会不能实时性响应呢?根本的原因就在,虽然有以太网中断,但是中断后,RL-TCPent的主处理函数main_TcpNet()不能得到及时的执行,所以我们要解决的就是让主处理函数得到实时执行。
        用户通过修改以下几个地方就可以实现:
1、修改ETH_STM32F4xx.c文件中的函数send_frame
2、修改ETH_STM32F4xx.c文件中的以太网中断函数。
3、修改RL-TCPnet的时间基准更新任务。
4、修改RL-TCPnet的网络主任务,函数main_TcpNet的调用不再采用轮询方式,改成事件标志等待方式。
        下面针对RTX、uCOS-III和FreeRTOS操作系统分别做讲解:
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-13 15:59:09 | 显示全部楼层
21.3 RTX系统实例修改方法


        下面针对RTX系统要做的具体修改做个说明,我们以例程:V6-1024_RL-TCPnet实验_BSD Socket服务器之TCP(RTX)为例。通过修改函数send_frame,以太网中断和时间基准更新任务都给网络主任务发事件标志,让其得到实时执行,从而实现高效的事件触发框架。

21.3.1 修改函数send_frame


        修改ETH_STM32F4xx.c文件中的函数send_frame,此函数的末尾添加事件标志函数os_evt_set(0x0001, HandleTaskTCPMain);
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: send_frame
  4. *    功能说明: 传递数据帧给MAC DMA发送描述符,并使能发送。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. extern OS_TID HandleTaskTCPMain;
  10. void send_frame (OS_FRAME *frame)
  11. {
  12.      U32 *sp,*dp;
  13.      U32 i,j;
  14.      j = TxBufIndex;
  15.    
  16.      /* 等待上一帧数据发送完成 */
  17.      while (Tx_Desc[j].CtrlStat & DMA_TX_OWN);
  18.      sp = (U32 *)&frame->data[0];
  19.      dp = (U32 *)(Tx_Desc[j].Addr & ~3);
  20.      /* 复制要发送的数据到DMA发送描述符中 */
  21.      for (i = (frame->length + 3) >> 2; i; i--)
  22.      {
  23.          *dp++ = *sp++;
  24.      }
  25.    
  26.      /* 设置数据帧大小 */
  27.      Tx_Desc[j].Size      = frame->length;
  28.    
  29.      /* 发送描述符由DMA控制发送 */
  30.      Tx_Desc[j].CtrlStat |= DMA_TX_OWN;
  31.    
  32.      if (++j == NUM_TX_BUF) j = 0;
  33.      TxBufIndex = j;
  34.    
  35.      /* 开始帧传输 */
  36.      /*
  37.         DMASR 以太网 DMA 状态寄存器
  38.         向ETH_DMASR寄存器[16:0]中的(未保留)位写入1会将其清零,写入 0 则不起作用。
  39.         位1 TPSS:发送过程停止状态 (Transmit process stopped status)
  40.                  当发送停止时,此位置 1。
  41.      */
  42.      ETH->DMASR   = DSR_TPSS;
  43.    
  44.      /*
  45.         DMATPDR 以太网DMA发送轮询请求寄存器
  46.        应用程序使用此寄存器来指示DMA轮询发送描述符列表。
  47.        位 31:0 TPD:发送轮询请求(Transmit poll demand)
  48.                     向这些位写入任何值时,DMA都会读取ETH_DMACHTDR寄存器指向的当前描述符。如果
  49.                     该描述符不可用(由CPU所有),则发送会返回到挂起状态,并将ETH_DMASR寄存器位2
  50.                     进行置位。如果该描述符可用,则发送会继续进行。      
  51.      */
  52.      ETH->DMATPDR = 0;
  53.    
  54.      os_evt_set(0x0001, HandleTaskTCPMain);
  55. }
复制代码

21.3.2 修改以太网中断函数


        修改ETH_STM32F4xx.c文件中的以太网中断函数,此函数的末尾添加事件标志函数:isr_evt_set(0x0001, HandleTaskTCPMain);
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: ETH_IRQHandler
  4. *    功能说明: 以太网中断,主要处理从MAC DMA接收描述符接收到的数据帧以及错误标志的处理。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void ETH_IRQHandler (void)
  10. {
  11.      OS_FRAME *frame;
  12.      U32 i, RxLen;
  13.      U32 *sp,*dp;
  14.      i = RxBufIndex;
  15.    
  16.      /* 循环所有接受描述符列表,遇到未接收到数据的退出循环 */
  17.      do
  18.      {
  19.          /*
  20.               #define DMA_RX_ERROR_MASK   (DMA_RX_ES | DMA_RX_LE | DMA_RX_RWT | \\
  21.                                               DMA_RX_RE | DMA_RX_CE)
  22.             
  23.               有错误,放弃此帧数据,错误类型包含如下:
  24.               位15 DMA_RX_ES:错误汇总(Error summary),即CRC错误,接收错误,看门狗超时,延迟冲突等。
  25.              位12 DMA_RX_LE:长度错误(Length error)
  26.                              该位置1时,指示接收帧的实际长度与长度/类型字段的值不符。该字段仅在帧类
  27.                              型位(RDES0[5])复位后有效。
  28.               位4 DMA_RX_RWT:接收看门狗超时 (Receive watchdog timeout)
  29.                              该位置1时,表示接收看门狗计时器在接收当前帧时超时,且当前帧在看门狗超
  30.                              时后被截断了
  31.               位3 DMA_RX_RE: 接收错误 (Receive error)
  32.                             该位置1时,表示在帧接收期间,当发出RX_DV信号时,会发出RX_ERR信号。
  33.               位1 DMA_RX_CE: CRC 错误(CRC error)
  34.                             该位置1时,表示接收的帧发生循环冗余校验(CRC)错误。只有最后一个描述符
  35.                              (RDES0[8])置1时,该字段才有效
  36.          */
  37.          if (Rx_Desc[i].Stat & DMA_RX_ERROR_MASK)
  38.          {
  39.               goto rel;
  40.          }
  41.         
  42.           /*
  43.               #define DMA_RX_SEG_MASK   (DMA_RX_FS | DMA_RX_LS)
  44.              位9 FS:第一个描述符 (First descriptor)
  45.                     该位置1时,指示此描述符包含帧的第一个缓冲区。如果第一个缓冲区的大小为0,则第二
  46.                     个缓冲区将包含帧的帧头。如果第二个缓冲区的大小为0,则下一个描述符将包含帧的帧头。
  47.         
  48.              位8 LS:最后一个描述符 (Last descriptor)
  49.                     该位置1时,指示此描述符指向的缓冲区为帧的最后一个缓冲区。
  50.         
  51.              下面的函数用于判断此帧数据是否只有一个缓冲,初始化接收描述符列表的时候,每个描述符仅设置了
  52.              一个缓冲。
  53.          */
  54.          if ((Rx_Desc[i].Stat & DMA_RX_SEG_MASK) != DMA_RX_SEG_MASK)
  55.          {
  56.               goto rel;
  57.          }
  58.         
  59.          RxLen = ((Rx_Desc[i].Stat >> 16) & 0x3FFF) - 4;
  60.          if (RxLen > ETH_MTU)
  61.          {
  62.               /* 数据包太大,直接放弃 */
  63.               goto rel;
  64.          }
  65.         
  66.           /* 申请动态内存,RxLen或上0x80000000表示动态内存不足了不会调用函数sys_error() */
  67.          frame = alloc_mem (RxLen | 0x80000000);
  68.         
  69.           /* 如果动态内存申请失败了,放弃此帧数据;成功了,通过函数put_in_queue存入队列中 */
  70.          if (frame != NULL)
  71.          {
  72.               sp = (U32 *)(Rx_Desc[i].Addr & ~3);
  73.               dp = (U32 *)&frame->data[0];
  74.               for (RxLen = (RxLen + 3) >> 2; RxLen; RxLen--)
  75.               {
  76.                    *dp++ = *sp++;
  77.               }
  78.               put_in_queue (frame);
  79.          }
  80.         
  81.           /* 设置此接收描述符继续接收新的数据 */
  82.          rel: Rx_Desc[i].Stat = DMA_RX_OWN;
  83.          if (++i == NUM_RX_BUF) i = 0;
  84.      }
  85.      while (!(Rx_Desc[i].Stat & DMA_RX_OWN));
  86.    
  87.      RxBufIndex = i;
  88.      /*
  89.         DMASR DMA的状态寄存器(DMA status register)
  90.         位7 RBUS:接收缓冲区不可用状态 (Receive buffer unavailable status)
  91.                  此位指示接收列表中的下一个描述符由CPU所拥有,DMA无法获取。接收过程进入挂起状态。
  92.                   要恢复处理接收描述符,CPU应更改描述符的拥有关系,然后发出接收轮询请求命令。如果
  93.                   未发出接收轮询请求命令,则当接收到下一个识别的传入帧时,接收过程会恢复。仅当上一
  94.                   接收描述符由DMA所拥有时,才能将ETH_DMASR[7]置1。
  95.    
  96.         DMAIER的接收缓冲区不可用中断RBUIE是bit7,对于的接收缓冲区不可用状态在DMA状态寄存器中也是bit7。
  97.      */
  98.      if (ETH->DMASR & INT_RBUIE)
  99.      {
  100.          /* 接收缓冲区不可用,重新恢复DMA传输 */
  101.          ETH->DMASR = ETH_DMASR_RBUS;
  102.          ETH->DMARPDR = 0;
  103.      }
  104.    
  105.      /*
  106.         DMASR DMA的状态寄存器(DMA status register)
  107.         这里实现清除中断挂起标志
  108.         位16 ETH_DMASR_NIS:所有正常中断 (Normal interrupt summary)
  109.         位15 ETH_DMASR_AIS:所有异常中断 (Abnormal interrupt summary)
  110.         位6  ETH_DMASR_RS :接收状态 (Receive status)
  111.                             此位指示帧接收已完成,具体的帧状态信息已经包含在描述符中,接收仍保持运行状态。
  112.      */
  113.      ETH->DMASR = ETH_DMASR_NIS | ETH_DMASR_AIS | ETH_DMASR_RS;
  114.    
  115.      isr_evt_set(0x0001, HandleTaskTCPMain);
  116. }
复制代码

21.3.3 修改RL-TCPnet的时间基准更新任务


        修改RL-TCPnet的时间基准更新任务,添加事件标志函数os_evt_set(0x0001, HandleTaskTCPMain);
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskStart
  4. *    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *   优 先 级: 6
  8. *********************************************************************************************************
  9. */
  10. __task void AppTaskStart(void)
  11. {
  12.      /* 初始化RL-TCPnet */
  13.      init_TcpNet ();
  14.    
  15.      /* 创建任务 */
  16.      AppTaskCreate();
  17.    
  18.      os_itv_set (100);
  19.    
  20.     while(1)
  21.     {
  22.          os_itv_wait ();
  23.         
  24.          /* RL-TCPnet时间基准更新函数 */
  25.           timer_tick ();
  26.          os_evt_set(0x0001, HandleTaskTCPMain);
  27.     }
  28. }
复制代码

21.3.4 修改RL-TCPnet的网络主任务


        修改RL-TCPnet的网络主任务,函数main_TcpNet的调用不再采用轮询方式,改成事件标志等待方式,即修改为如下形式:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskTCPMain
  4. *    功能说明: RL-TCPnet网络主任务
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *   优 先 级: 5
  8. *********************************************************************************************************
  9. */
  10. __task void AppTaskTCPMain(void)
  11. {
  12.      while (1)
  13.      {
  14.           /* RL-TCPnet处理函数 */
  15.          os_evt_wait_and(0x0001, 0xFFFF);
  16.          while (main_TcpNet() == __TRUE);
  17.      }
  18. }
复制代码

21.3.5 最后特别注意优先级安排


        最后,用户要特别注意几个任务的优先级安排,非常重要。
1、RL-TCPnet的时间基准更新任务一定要是最高优先级任务。
2、RL-TCPnet的网络主任务,即调用函数main_TcpNet的任务是次高优先级任务。
3、应用层的任务要比前面两个任务的优先级都低。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-13 16:02:04 | 显示全部楼层
21.4 uCOS-III系统实例修改方法


        下面针对uCOS-III系统要做的具体修改做个说明,我们以例程:V6-1025_RL-TCPnet实验_BSD Socket服务器之TCP(uCOS-III)为例。通过修改函数send_frame,以太网中断和时间基准更新任务都给网络主任务发事件标志,让其得到实时执行,从而实现高效的事件触发框架。

21.4.1 创建事件标志组


        创建uCOS-III的事件标志组:
  1. OS_FLAG_GRP        FLAG_TCPnet;
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: AppObjCreate
  5. *    功能说明: 创建任务通讯
  6. *    形    参: p_arg 是在创建该任务时传递的形参
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. static  void  AppObjCreate (void)
  11. {
  12.      OS_ERR      err;
  13.    
  14.      /* 创建事件标志组 */
  15.      OSFlagCreate ((OS_FLAG_GRP  *)&FLAG_TCPnet,
  16.                   (CPU_CHAR     *)"FLAG TCPnet",
  17.                   (OS_FLAGS      )0,
  18.                   (OS_ERR       *)&err);
  19. }
复制代码

21.4.2 修改函数send_frame


         修改ETH_STM32F4xx.c文件中的函数send_frame,此函数的末尾添加事件标志函数OSFlagPost(宏定义uCOS_EN在bsp.h文件里面使能,针对教程配套例子做的定义,方便管理。大家自己搞时,不必受此限制)。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: send_frame
  4. *    功能说明: 传递数据帧给MAC DMA发送描述符,并使能发送。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void send_frame (OS_FRAME *frame)
  10. {
  11.      U32 *sp,*dp;
  12.      U32 i,j;
  13. #if uCOS_EN == 1
  14.      OS_ERR  err;
  15. #endif
  16.      j = TxBufIndex;
  17.    
  18.      /* 等待上一帧数据发送完成 */
  19.      while (Tx_Desc[j].CtrlStat & DMA_TX_OWN);
  20.      sp = (U32 *)&frame->data[0];
  21.      dp = (U32 *)(Tx_Desc[j].Addr & ~3);
  22.      /* 复制要发送的数据到DMA发送描述符中 */
  23.      for (i = (frame->length + 3) >> 2; i; i--)
  24.      {
  25.          *dp++ = *sp++;
  26.      }
  27.    
  28.      /* 设置数据帧大小 */
  29.      Tx_Desc[j].Size      = frame->length;
  30.    
  31.      /* 发送描述符由DMA控制发送 */
  32.      Tx_Desc[j].CtrlStat |= DMA_TX_OWN;
  33.    
  34.      if (++j == NUM_TX_BUF) j = 0;
  35.      TxBufIndex = j;
  36.    
  37.      /* 开始帧传输 */
  38.      /*
  39.         DMASR 以太网 DMA 状态寄存器
  40.         向ETH_DMASR寄存器[16:0]中的(未保留)位写入1会将其清零,写入 0 则不起作用。
  41.         位1 TPSS:发送过程停止状态 (Transmit process stopped status)
  42.                  当发送停止时,此位置 1。
  43.      */
  44.      ETH->DMASR   = DSR_TPSS;
  45.    
  46.      /*
  47.         DMATPDR 以太网DMA发送轮询请求寄存器
  48.        应用程序使用此寄存器来指示DMA轮询发送描述符列表。
  49.        位 31:0 TPD:发送轮询请求(Transmit poll demand)
  50.                     向这些位写入任何值时,DMA都会读取ETH_DMACHTDR寄存器指向的当前描述符。如果
  51.                     该描述符不可用(由CPU所有),则发送会返回到挂起状态,并将ETH_DMASR寄存器位2
  52.                     进行置位。如果该描述符可用,则发送会继续进行。      
  53.      */
  54.      ETH->DMATPDR = 0;
  55.    
  56. #if uCOS_EN == 1
  57.      OSFlagPost ((OS_FLAG_GRP  *)&FLAG_TCPnet,
  58.                    (OS_FLAGS      )0x0001,
  59.                    (OS_OPT        )OS_OPT_POST_FLAG_SET,
  60.                    (OS_ERR       *)&err);
  61. #endif  
  62. }
复制代码

21.4.3 修改以太网中断函数


        修改ETH_STM32F4xx.c文件中的以太网中断函数,此函数的末尾添加事件标志函数:OSFlagPost(宏定义uCOS_EN在bsp.h文件里面使能,针对教程配套例子做的定义,方便管理。大家自己搞时,不必受此限制)。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: ETH_IRQHandler
  4. *    功能说明: 以太网中断,主要处理从MAC DMA接收描述符接收到的数据帧以及错误标志的处理。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void ETH_IRQHandler (void)
  10. {
  11.      OS_FRAME *frame;
  12.      U32 i, RxLen;
  13.      U32 *sp,*dp;
  14. #if uCOS_EN == 1
  15.      OS_ERR  err;
  16.      CPU_SR_ALLOC();
  17.      CPU_CRITICAL_ENTER();
  18.      OSIntEnter();                        
  19.      CPU_CRITICAL_EXIT();
  20. #endif
  21.      i = RxBufIndex;
  22.    
  23.      /* 循环所有接受描述符列表,遇到未接收到数据的退出循环 */
  24.      do
  25.      {
  26.          /*
  27.               #define DMA_RX_ERROR_MASK   (DMA_RX_ES | DMA_RX_LE | DMA_RX_RWT | \\
  28.                                               DMA_RX_RE | DMA_RX_CE)
  29.             
  30.               有错误,放弃此帧数据,错误类型包含如下:
  31.               位15 DMA_RX_ES:错误汇总(Error summary),即CRC错误,接收错误,看门狗超时,延迟冲突等。
  32.              位12 DMA_RX_LE:长度错误(Length error)
  33.                              该位置1时,指示接收帧的实际长度与长度/类型字段的值不符。该字段仅在帧类
  34.                              型位(RDES0[5])复位后有效。
  35.               位4 DMA_RX_RWT:接收看门狗超时 (Receive watchdog timeout)
  36.                              该位置1时,表示接收看门狗计时器在接收当前帧时超时,且当前帧在看门狗超
  37.                              时后被截断了
  38.               位3 DMA_RX_RE: 接收错误 (Receive error)
  39.                             该位置1时,表示在帧接收期间,当发出RX_DV信号时,会发出RX_ERR信号。
  40.               位1 DMA_RX_CE: CRC 错误(CRC error)
  41.                             该位置1时,表示接收的帧发生循环冗余校验(CRC)错误。只有最后一个描述符
  42.                              (RDES0[8])置1时,该字段才有效
  43.          */
  44.          if (Rx_Desc[i].Stat & DMA_RX_ERROR_MASK)
  45.          {
  46.               goto rel;
  47.          }
  48.         
  49.           /*
  50.               #define DMA_RX_SEG_MASK   (DMA_RX_FS | DMA_RX_LS)
  51.              位9 FS:第一个描述符 (First descriptor)
  52.                     该位置1时,指示此描述符包含帧的第一个缓冲区。如果第一个缓冲区的大小为0,则第二
  53.                     个缓冲区将包含帧的帧头。如果第二个缓冲区的大小为0,则下一个描述符将包含帧的帧头。
  54.         
  55.              位8 LS:最后一个描述符 (Last descriptor)
  56.                     该位置1时,指示此描述符指向的缓冲区为帧的最后一个缓冲区。
  57.         
  58.              下面的函数用于判断此帧数据是否只有一个缓冲,初始化接收描述符列表的时候,每个描述符仅设置了
  59.              一个缓冲。
  60.          */
  61.          if ((Rx_Desc[i].Stat & DMA_RX_SEG_MASK) != DMA_RX_SEG_MASK)
  62.          {
  63.               goto rel;
  64.          }
  65.         
  66.          RxLen = ((Rx_Desc[i].Stat >> 16) & 0x3FFF) - 4;
  67.          if (RxLen > ETH_MTU)
  68.          {
  69.               /* 数据包太大,直接放弃 */
  70.               goto rel;
  71.          }
  72.         
  73.           /* 申请动态内存,RxLen或上0x80000000表示动态内存不足了不会调用函数sys_error() */
  74.          frame = alloc_mem (RxLen | 0x80000000);
  75.         
  76.           /* 如果动态内存申请失败了,放弃此帧数据;成功了,通过函数put_in_queue存入队列中 */
  77.          if (frame != NULL)
  78.          {
  79.               sp = (U32 *)(Rx_Desc[i].Addr & ~3);
  80.               dp = (U32 *)&frame->data[0];
  81.               for (RxLen = (RxLen + 3) >> 2; RxLen; RxLen--)
  82.               {
  83.                    *dp++ = *sp++;
  84.               }
  85.               put_in_queue (frame);
  86.          }
  87.         
  88.           /* 设置此接收描述符继续接收新的数据 */
  89.          rel: Rx_Desc[i].Stat = DMA_RX_OWN;
  90.          if (++i == NUM_RX_BUF) i = 0;
  91.      }
  92.      while (!(Rx_Desc[i].Stat & DMA_RX_OWN));
  93.    
  94.      RxBufIndex = i;
  95.      /*
  96.         DMASR DMA的状态寄存器(DMA status register)
  97.         位7 RBUS:接收缓冲区不可用状态 (Receive buffer unavailable status)
  98.                  此位指示接收列表中的下一个描述符由CPU所拥有,DMA无法获取。接收过程进入挂起状态。
  99.                   要恢复处理接收描述符,CPU应更改描述符的拥有关系,然后发出接收轮询请求命令。如果
  100.                   未发出接收轮询请求命令,则当接收到下一个识别的传入帧时,接收过程会恢复。仅当上一
  101.                   接收描述符由DMA所拥有时,才能将ETH_DMASR[7]置1。
  102.    
  103.         DMAIER的接收缓冲区不可用中断RBUIE是bit7,对于的接收缓冲区不可用状态在DMA状态寄存器中也是bit7。
  104.      */
  105.      if (ETH->DMASR & INT_RBUIE)
  106.      {
  107.          /* 接收缓冲区不可用,重新恢复DMA传输 */
  108.          ETH->DMASR = ETH_DMASR_RBUS;
  109.          ETH->DMARPDR = 0;
  110.      }
  111.    
  112.      /*
  113.         DMASR DMA的状态寄存器(DMA status register)
  114.         这里实现清除中断挂起标志
  115.         位16 ETH_DMASR_NIS:所有正常中断 (Normal interrupt summary)
  116.         位15 ETH_DMASR_AIS:所有异常中断 (Abnormal interrupt summary)
  117.         位6  ETH_DMASR_RS :接收状态 (Receive status)
  118.                             此位指示帧接收已完成,具体的帧状态信息已经包含在描述符中,接收仍保持运行状态。
  119.      */
  120.      ETH->DMASR = ETH_DMASR_NIS | ETH_DMASR_AIS | ETH_DMASR_RS;
  121. #if uCOS_EN == 1
  122.      OSFlagPost ((OS_FLAG_GRP  *)&FLAG_TCPnet,
  123.                    (OS_FLAGS      )0x0001,
  124.                    (OS_OPT        )OS_OPT_POST_FLAG_SET,
  125.                    (OS_ERR       *)&err);
  126.      OSIntExit();                          
  127. #endif
  128. }
复制代码

21.4.4 修改RL-TCPnet的时间基准更新任务


        修改RL-TCPnet的时间基准更新任务,添加事件标志函数:OSFlagPost。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskStart
  4. *    功能说明: 这是一个启动任务,在多任务系统启动后,必须初始化滴答计数器。本任务主要实现RL-TCPnet的时间
  5. *             基准更新。
  6. *    形    参: p_arg 是在创建该任务时传递的形参
  7. *    返 回 值: 无
  8.      优 先 级: 2
  9. *********************************************************************************************************
  10. */
  11. static  void  AppTaskStart (void *p_arg)
  12. {
  13.      OS_ERR      err;
  14.    (void)p_arg;
  15.    
  16.      CPU_Init();    /* 此函数要优先调用,因为外设驱动中使用的us和ms延迟是基于此函数的 */
  17.      bsp_Init();  
  18.      init_TcpNet ();/* 初始化RL-TCPnet */
  19.    
  20.      BSP_Tick_Init();
  21.    
  22. #if OS_CFG_STAT_TASK_EN > 0u
  23.      OSStatTaskCPUUsageInit(&err);  
  24. #endif
  25. #ifdef CPU_CFG_INT_DIS_MEAS_EN
  26.     CPU_IntDisMeasMaxCurReset();
  27. #endif
  28.    
  29.      /* 创建任务 */
  30.     AppTaskCreate();
  31.      /* 创建任务间通信机制 */
  32.      AppObjCreate();   
  33.    
  34.     while (1)
  35.      {
  36.          /* RL-TCPnet时间基准更新函数 */
  37.          timer_tick ();
  38.         
  39.          OSFlagPost ((OS_FLAG_GRP  *)&FLAG_TCPnet,
  40.                        (OS_FLAGS      )0x0001,
  41.                        (OS_OPT        )OS_OPT_POST_FLAG_SET,
  42.                        (OS_ERR       *)&err);
  43.         
  44.          OSTimeDly(100, OS_OPT_TIME_PERIODIC, &err);
  45.     }
  46. }
复制代码

21.4.5 修改RL-TCPnet的网络主任务


        修改RL-TCPnet的网络主任务,函数main_TcpNet的调用不再采用轮询方式,改成事件标志等待方式,即修改为如下形式:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskTCPnet
  4. *    功能说明: RL-TCPnet网络主任务
  5. *    形    参: p_arg 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7.      优 先 级: 3
  8. *********************************************************************************************************
  9. */
  10. static void AppTaskTCPnet(void *p_arg)
  11. {
  12.      OS_ERR  err;
  13.      CPU_TS   ts;
  14.    
  15.      (void)p_arg;
  16.          
  17.      while(1)
  18.      {
  19.          /* RL-TCPnet处理函数 */
  20.          OSFlagPend ((OS_FLAG_GRP  *)&FLAG_TCPnet,
  21.                        (OS_FLAGS      )0x0001,
  22.                        (OS_TICK       )0,
  23.                        (OS_OPT        )OS_OPT_PEND_FLAG_SET_ANY + OS_OPT_PEND_FLAG_CONSUME,
  24.                        (CPU_TS       *)&ts,
  25.                        (OS_ERR       *)&err);
  26.         
  27.          while (main_TcpNet() == __TRUE);
  28.      }  
  29. }
复制代码

21.4.6 最后特别注意优先级安排


        最后,用户要特别注意几个任务的优先级安排,非常重要。
1、RL-TCPnet的时间基准更新任务一定要是最高优先级任务。
2、RL-TCPnet的网络主任务,即调用函数main_TcpNet的任务是次高优先级任务。
3、应用层的任务要比前面两个任务的优先级都低。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-13 16:04:50 | 显示全部楼层
21.5 FreeRTOS系统实例修改方法


        下面针对FreeRTOS系统要做的具体修改做个说明,我们以例程:V6-1026_RL-TCPnet实验_BSD Socket服务器之TCP(FreeRTOS)为例。通过修改函数send_frame,以太网中断和时间基准更新任务都给网络主任务发事件标志,让其得到实时执行,从而实现高效的事件触发框架。

21.5.1 创建事件标志组


        创建FreeRTOS的事件标志组:
  1. EventGroupHandle_t xCreatedTCPnetGroup = NULL;
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: AppObjCreate
  5. *    功能说明: 创建任务通信机制
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. static void AppObjCreate (void)
  11. {   
  12.      /* 创建事件标志组 */
  13.      xCreatedTCPnetGroup = xEventGroupCreate();
  14.    
  15.      if(xCreatedTCPnetGroup == NULL)
  16.     {
  17.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  18.     }
  19. }
复制代码

21.5.2 修改函数send_frame


        修改ETH_STM32F4xx.c文件中的函数send_frame,此函数的末尾添加事件标志函数xEventGroupSetBits(宏定义FreeRTOS_EN在bsp.h文件里面使能,针对教程配套例子做的定义,方便管理。大家自己搞时,不必受此限制)。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: send_frame
  4. *    功能说明: 传递数据帧给MAC DMA发送描述符,并使能发送。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void send_frame (OS_FRAME *frame)
  10. {
  11.      U32 *sp,*dp;
  12.      U32 i,j;
  13.      j = TxBufIndex;
  14.    
  15.      /* 等待上一帧数据发送完成 */
  16.      while (Tx_Desc[j].CtrlStat & DMA_TX_OWN);
  17.      sp = (U32 *)&frame->data[0];
  18.      dp = (U32 *)(Tx_Desc[j].Addr & ~3);
  19.      /* 复制要发送的数据到DMA发送描述符中 */
  20.      for (i = (frame->length + 3) >> 2; i; i--)
  21.      {
  22.          *dp++ = *sp++;
  23.      }
  24.    
  25.      /* 设置数据帧大小 */
  26.      Tx_Desc[j].Size      = frame->length;
  27.    
  28.      /* 发送描述符由DMA控制发送 */
  29.      Tx_Desc[j].CtrlStat |= DMA_TX_OWN;
  30.    
  31.      if (++j == NUM_TX_BUF) j = 0;
  32.      TxBufIndex = j;
  33.    
  34.      /* 开始帧传输 */
  35.      /*
  36.         DMASR 以太网 DMA 状态寄存器
  37.         向ETH_DMASR寄存器[16:0]中的(未保留)位写入1会将其清零,写入 0 则不起作用。
  38.         位1 TPSS:发送过程停止状态 (Transmit process stopped status)
  39.                  当发送停止时,此位置 1。
  40.      */
  41.      ETH->DMASR   = DSR_TPSS;
  42.    
  43.      /*
  44.         DMATPDR 以太网DMA发送轮询请求寄存器
  45.        应用程序使用此寄存器来指示DMA轮询发送描述符列表。
  46.        位 31:0 TPD:发送轮询请求(Transmit poll demand)
  47.                     向这些位写入任何值时,DMA都会读取ETH_DMACHTDR寄存器指向的当前描述符。如果
  48.                     该描述符不可用(由CPU所有),则发送会返回到挂起状态,并将ETH_DMASR寄存器位2
  49.                     进行置位。如果该描述符可用,则发送会继续进行。      
  50.      */
  51.      ETH->DMATPDR = 0;
  52.    
  53. #if USE_FreeRTOS == 1
  54.      xEventGroupSetBits(xCreatedTCPnetGroup, 0x0001);
  55. #endif
  56. }
复制代码

21.5.3 修改以太网中断函数


        修改ETH_STM32F4xx.c文件中的以太网中断函数,此函数的末尾添加事件标志函数:xEventGroupSetBitsFromISR(宏定义FreeRTOS_EN在bsp.h文件里面使能,针对教程配套例子做的定义,方便管理。大家自己搞时,不必受此限制)。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: ETH_IRQHandler
  4. *    功能说明: 以太网中断,主要处理从MAC DMA接收描述符接收到的数据帧以及错误标志的处理。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void ETH_IRQHandler (void)
  10. {
  11.      OS_FRAME *frame;
  12.      U32 i, RxLen;
  13.      U32 *sp,*dp;
  14.      i = RxBufIndex;
  15.    
  16. #if USE_FreeRTOS == 1
  17.      BaseType_t xResult;
  18.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  19. #endif
  20.      /* 循环所有接受描述符列表,遇到未接收到数据的退出循环 */
  21.      do
  22.      {
  23.          /*
  24.               #define DMA_RX_ERROR_MASK   (DMA_RX_ES | DMA_RX_LE | DMA_RX_RWT | \\
  25.                                               DMA_RX_RE | DMA_RX_CE)
  26.             
  27.               有错误,放弃此帧数据,错误类型包含如下:
  28.               位15 DMA_RX_ES:错误汇总(Error summary),即CRC错误,接收错误,看门狗超时,延迟冲突等。
  29.              位12 DMA_RX_LE:长度错误(Length error)
  30.                              该位置1时,指示接收帧的实际长度与长度/类型字段的值不符。该字段仅在帧类
  31.                              型位(RDES0[5])复位后有效。
  32.               位4 DMA_RX_RWT:接收看门狗超时 (Receive watchdog timeout)
  33.                              该位置1时,表示接收看门狗计时器在接收当前帧时超时,且当前帧在看门狗超
  34.                              时后被截断了
  35.               位3 DMA_RX_RE: 接收错误 (Receive error)
  36.                             该位置1时,表示在帧接收期间,当发出RX_DV信号时,会发出RX_ERR信号。
  37.               位1 DMA_RX_CE: CRC 错误(CRC error)
  38.                             该位置1时,表示接收的帧发生循环冗余校验(CRC)错误。只有最后一个描述符
  39.                              (RDES0[8])置1时,该字段才有效
  40.          */
  41.          if (Rx_Desc[i].Stat & DMA_RX_ERROR_MASK)
  42.          {
  43.               goto rel;
  44.          }
  45.         
  46.           /*
  47.               #define DMA_RX_SEG_MASK   (DMA_RX_FS | DMA_RX_LS)
  48.              位9 FS:第一个描述符 (First descriptor)
  49.                     该位置1时,指示此描述符包含帧的第一个缓冲区。如果第一个缓冲区的大小为0,则第二
  50.                     个缓冲区将包含帧的帧头。如果第二个缓冲区的大小为0,则下一个描述符将包含帧的帧头。
  51.         
  52.              位8 LS:最后一个描述符 (Last descriptor)
  53.                     该位置1时,指示此描述符指向的缓冲区为帧的最后一个缓冲区。
  54.         
  55.              下面的函数用于判断此帧数据是否只有一个缓冲,初始化接收描述符列表的时候,每个描述符仅设置了
  56.              一个缓冲。
  57.          */
  58.          if ((Rx_Desc[i].Stat & DMA_RX_SEG_MASK) != DMA_RX_SEG_MASK)
  59.          {
  60.               goto rel;
  61.          }
  62.         
  63.          RxLen = ((Rx_Desc[i].Stat >> 16) & 0x3FFF) - 4;
  64.          if (RxLen > ETH_MTU)
  65.          {
  66.               /* 数据包太大,直接放弃 */
  67.               goto rel;
  68.          }
  69.         
  70.           /* 申请动态内存,RxLen或上0x80000000表示动态内存不足了不会调用函数sys_error() */
  71.          frame = alloc_mem (RxLen | 0x80000000);
  72.         
  73.           /* 如果动态内存申请失败了,放弃此帧数据;成功了,通过函数put_in_queue存入队列中 */
  74.          if (frame != NULL)
  75.          {
  76.               sp = (U32 *)(Rx_Desc[i].Addr & ~3);
  77.               dp = (U32 *)&frame->data[0];
  78.               for (RxLen = (RxLen + 3) >> 2; RxLen; RxLen--)
  79.               {
  80.                    *dp++ = *sp++;
  81.               }
  82.               put_in_queue (frame);
  83.          }
  84.         
  85.           /* 设置此接收描述符继续接收新的数据 */
  86.          rel: Rx_Desc[i].Stat = DMA_RX_OWN;
  87.          if (++i == NUM_RX_BUF) i = 0;
  88.      }
  89.      while (!(Rx_Desc[i].Stat & DMA_RX_OWN));
  90.    
  91.      RxBufIndex = i;
  92.      /*
  93.         DMASR DMA的状态寄存器(DMA status register)
  94.         位7 RBUS:接收缓冲区不可用状态 (Receive buffer unavailable status)
  95.                  此位指示接收列表中的下一个描述符由CPU所拥有,DMA无法获取。接收过程进入挂起状态。
  96.                   要恢复处理接收描述符,CPU应更改描述符的拥有关系,然后发出接收轮询请求命令。如果
  97.                   未发出接收轮询请求命令,则当接收到下一个识别的传入帧时,接收过程会恢复。仅当上一
  98.                   接收描述符由DMA所拥有时,才能将ETH_DMASR[7]置1。
  99.    
  100.         DMAIER的接收缓冲区不可用中断RBUIE是bit7,对于的接收缓冲区不可用状态在DMA状态寄存器中也是bit7。
  101.      */
  102.      if (ETH->DMASR & INT_RBUIE)
  103.      {
  104.          /* 接收缓冲区不可用,重新恢复DMA传输 */
  105.          ETH->DMASR = ETH_DMASR_RBUS;
  106.          ETH->DMARPDR = 0;
  107.      }
  108.    
  109.      /*
  110.         DMASR DMA的状态寄存器(DMA status register)
  111.         这里实现清除中断挂起标志
  112.         位16 ETH_DMASR_NIS:所有正常中断 (Normal interrupt summary)
  113.         位15 ETH_DMASR_AIS:所有异常中断 (Abnormal interrupt summary)
  114.         位6  ETH_DMASR_RS :接收状态 (Receive status)
  115.                             此位指示帧接收已完成,具体的帧状态信息已经包含在描述符中,接收仍保持运行状态。
  116.      */
  117.      ETH->DMASR = ETH_DMASR_NIS | ETH_DMASR_AIS | ETH_DMASR_RS;
  118.    
  119. #if USE_FreeRTOS == 1
  120.      xResult = xEventGroupSetBitsFromISR(xCreatedTCPnetGroup, /* 事件标志组句柄 */
  121.                                               0x0001,              /* 设置bit0 */
  122.                                               &xHigherPriorityTaskWoken );
  123.    
  124.      /* 消息被成功发出 */
  125.      if( xResult != pdFAIL )
  126.      {
  127.          /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  128.          portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  129.      }
  130. #endif
  131. }
复制代码

21.5.4 修改RL-TCPnet的时间基准更新任务


        修改RL-TCPnet的时间基准更新任务,添加事件标志函数:xEventGroupSetBits
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskStart
  4. *    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 6
  8. *********************************************************************************************************
  9. */
  10. static void vTaskStart(void *pvParameters)
  11. {
  12.      TickType_t xLastWakeTime;
  13.      const TickType_t xFrequency = 100;
  14.    
  15.      /* 初始化RL-TCPnet */
  16.      init_TcpNet ();
  17.    
  18.      /* 获取当前的系统时间 */
  19.     xLastWakeTime = xTaskGetTickCount();
  20.    
  21.     while(1)
  22.     {   
  23.          /* RL-TCPnet时间基准更新函数 */
  24.          timer_tick ();
  25.          xEventGroupSetBits(xCreatedTCPnetGroup, 0x0001);
  26.         
  27.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  28.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  29.     }
  30. }
复制代码

21.5.5 修改RL-TCPnet的网络主任务


        修改RL-TCPnet的网络主任务,函数main_TcpNet的调用不再采用轮询方式,改成事件标志等待方式,即修改为如下形式:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTCPnet
  4. *    功能说明: RL-TCPnet网络主任务
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 5
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTCPnet(void *pvParameters)
  11. {
  12.     while(1)
  13.     {
  14.          /* RL-TCPnet处理函数 */
  15.          xEventGroupWaitBits(xCreatedTCPnetGroup, /* 事件标志组句柄 */
  16.                                  0x0001,             /* 等待被设置 */
  17.                                  pdTRUE,             /* 退出前bit0被清除 */
  18.                                  pdFALSE,            /* 设置为pdFALSE表示仅等待bit0被设置*/
  19.                                  portMAX_DELAY);     /* 永久等待 */
  20.         
  21.          while (main_TcpNet() == __TRUE);
  22.     }
  23. }
复制代码

21.5.6 最后特别注意优先级安排


        最后,用户要特别注意几个任务的优先级安排,非常重要。
1、RL-TCPnet的时间基准更新任务一定要是最高优先级任务。
2、RL-TCPnet的网络主任务,即调用函数main_TcpNet的任务是次高优先级任务。
3、应用层的任务要比前面两个任务的优先级都低。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-13 16:06:09 | 显示全部楼层
21.6 实验操作和实验例程说明



21.6.1 STM32F407开发板实验


    由于本章节配套的例子是由第19章的例子简单修改而来的,所以操作说明和例程说明,直接看第19章即可。不同的地方仅仅是使能了本章节讲解的事件触发方式,本章节配套了如下三个例子:
21.3.png



21.6.2 STM32F429开发板实验


    由于本章节配套的例子是由第19章的例子简单修改而来的,所以操作说明和例程说明,直接看第19章即可。不同的地方仅仅是使能了本章节讲解的事件触发方式,本章节配套了如下三个例子:
21.4.png
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-13 16:06:34 | 显示全部楼层
21.7 总结


        本章节的项目实战性很高,望初学者务必掌握,在实际项目中也推荐采用事件触发方式。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-17 02:26 , Processed in 0.199300 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表