Matlab生成dsp程序——官方例程学习(6)

官方链接:官方链接
ccs程序即模型:模型及程序
程序与程序中有我看的时候的中文标注,可能看起来更容易一点。

IPC通信

一、主要目的

这个例程是使用IPC进行2837xD的两个核的通信,CPU1分别启用了四个IPC通道进行数据传输。这里应该使用的是寄存器传输(看到程序后发现这里是重新封装了IPC,还是用的内存共享,而且封装的较为巧妙建议阅读一程序中的**IPCInit()**函数),一次传送数据不超过32位。

通道号传送内容采样时间
IPC016位整数(0-2^16-1循环)[16 bits]1
IPC132位无符号整型数(1234567)[32 bits]0.5
IPC21×10矩阵(1 2 3 … 10)[16 bits]0.1
IPC3单精度浮点数(1.234 2.345 3.456)[32 bits]0.01

在这里插入图片描述

CPU2中对CPU1传送过来的数据进行接收。

IPC0和IPC1传送的数据由IPC0中断和IPC1中断进行接收。

IPC2和IPC3传送的数据由系统轮询进行获取。

因此IPC0和IPC1的接收模块采样时间由中断函数进行决定,在IPC Receive中设置为-1(即继承上一级采样时间)

IPC2和IPC3采样时间则与CPU1的设置完全一样。

在这里插入图片描述

二、程序分析

1. CPU1程序

void rt_OneStep(void)
//每0.01s进入一次    采样时间:IPC0-1 IPC1-0.5 IPC2-0.1 IPC3-0.01s
//按照这个逻辑
//IPC3应该是每进入一次中断执行一次
//IPC2 每进入10次执行一次
//IPC1 每进入50次执行一次
//IPC0 每进入100次执行一次

{
  boolean_T eventFlags[4];
  int_T i;

  /* Check base rate for overrun */
  if (isRateRunning[0]++) {                        //1.{0 0 0}变为{1 0 0 0}
    IsrOverrun = 1;
    isRateRunning[0]--;                /* allow future iterations to succeed*/
    return;
  }

  /*
   * For a bare-board target (i.e., no operating system), the rates
   * that execute this base step are buffered locally to allow for
   * overlapping preemption.  The generated code includes function
   * writeCodeInfoFcn() which sets the rates
   * that need to run this time step.  The return values are 1 and 0
   * for true and false, respectively.
   */
  c2837xd_ipc_cpu1_tx_SetEventsForThisBaseStep(eventFlags);      //TID[1-3]赋值给eventFlags[1-3]
  enableTimer0Interrupt();
  c2837xd_ipc_cpu1_tx_step0();                               //TD[1-3]自加,TD[1]在0-9循环 TD[2]在0-49循环 TD[3]在0-99循环

  /* Get model outputs here */
  disableTimer0Interrupt();
  isRateRunning[0]--;                                        //{1 0 0}变为{0 0 0 0}
  for (i = 1; i < 4; i++)
  {
    if (eventFlags[i])             //eventFlags[i]不为0进入if //1.{0 2 2 2}
    {
      if (need2runFlags[i]++)      //need2runFlags[i]不为0进入if {0 1 1 1}
      {
        IsrOverrun = 1;
        need2runFlags[i]--;            /* allow future iterations to succeed*/
        break;
      }
    }
  }

  for (i = 1; i < 4; i++)
  {
    if (isRateRunning[i])
    {
      /* Yield to higher priority*/
      return;
    }

    if (need2runFlags[i])
    {
      isRateRunning[i]++;                 //{0 1 0 0}
      enableTimer0Interrupt();

      /* Step the model for subrate "i" */
      switch (i)
      {
       case 1 :
        c2837xd_ipc_cpu1_tx_step1();

        /* Get model outputs here */
        break;

       case 2 :
        c2837xd_ipc_cpu1_tx_step2();

        /* Get model outputs here */
        break;

       case 3 :
        c2837xd_ipc_cpu1_tx_step3();

        /* Get model outputs here */
        break;

       default :
        break;
      }

      disableTimer0Interrupt();
      need2runFlags[i]--;         
      isRateRunning[i]--;          
    }
  }
}

以上部分是Timer0中断函数中的主要程序,这个程序实现了采样时间的分配。即

每0.01s进入一次Timer0中断函数 采样时间:IPC0-1 IPC1-0.5 IPC2-0.1 IPC3-0.01s
按照这个逻辑
IPC3应该是每进入一次中断执行一次
IPC2 每进入10次执行一次
IPC1 每进入50次执行一次
IPC0 每进入100次执行一次

  明白这一机理需要了解一个结构体,c2837xd_ipc_cpu1_tx_M:

struct tag_RTM_c2837xd_ipc_cpu1_tx_T {
  const char_T *errorStatus;
  /*
   * Timing:
   * The following substructure contains information regarding
   * the timing information for the model.
   */
  struct 
  {
    struct 
    {
      uint8_T TID[4];
    } TaskCounters;
  } Timing;
};

 其中的TID[4]就是任务时间分配的工作,这里IPC0-IPC3一共四个任务,并且四个任务的采样时间都不同,所以TID数组中有四个元素。可以推理,当有更多的不同采样时间时,也会有更多的元素。

  • TID[0]对应IPC3的采样时间(0.01 s)

  • TID[1]对应IPC2的采样时间(0.1 s)

  • TID[2]对应IPC1的采样时间(0.5 s)

  • TID[3]对应IPC0的采样时间(1 s)

  试想一下,我要控制IPC2每0.1 s执行一次,是不是只用弄个计数器,一但IPC3执行了十次,那么是不是就代表过去了0.1 s,这时便可以执行IPC2。因此程序中规定:TD[1]在0-9循环,TD[2]在0-49循环,TD[3]在0-99循环。

 对应这里顺便提一下,Simulink中的步长设置为自动还是有好处的,因为每个采样时间必须是步长的整数倍。生成的代码也从侧面反应了Simulink中的计算方式吧。

 TID中的元素会在每个步长(IPC3)程序中进行自加1。c2837xd_ipc_cpu1_tx_SetEventsForThisBaseStep(eventFlags)这个函数当中会判断TID[x]是否等于0(等于0就代表完成了一次计数周期)。一旦达到了计数周期就会把相应的eventFlags位置置为1。这是通过switch函数进行选择执行函数。
(eg:当TID[1]完成了一次0-9的计数周期,会将eventFlags[1]中的元素置为1,然后通过switch函数执行**step1()**函数)困扰了几天的问题终于解决了。。(遇到逻辑问题可以借助DEBUG或C语言编写程序进行模拟)

 这个时序控制的方式比较有意思,看下面这个函数:实现了每0.5 s翻转一次LED的操作。

while(1)
{
	LED =1;
	DELAY_MS(500);
	LED=0;
	DELAY_MS(500);
}

 换句话说,按照时序的观点,是不是我建立一个定时器。一旦计时到达了0.5 s 我就对LED进行一次操作。这就是生成代码的时序的观点。与我们进行自己写程序时,有所不同。这将是我们在将代码转换成Simulimk模型时,需要注意的事情。

CPU2程序中的程序就是进行中断接收和轮询方式接收了,没什么特别的。

三、生成代码中结构体记录

 看程序的时候,一定要对照这些结构体和simulink中的函数参数进行仔细查看。

1)控制模型时序的结构体,实例化对象c2837xd_ipc_cpu1_tx_M

struct tag_RTM_c2837xd_ipc_cpu1_tx_T {
  const char_T *errorStatus;
  struct 
  {
    struct 
    {
      uint8_T TID[4];
    } TaskCounters;
  } Timing;
};
//控制模型时序的结构体
//别名:RT_MODEL_c2837xd_ipc_cpu1_tx_T
//实例化对象:c2837xd_ipc_cpu1_tx_M

2)模块信号结构体,实例化对象:c2837xd_ipc_cpu1_tx_B

typedef struct {
  uint16_T Output;             /* '<S1>/Output' */
} B_c2837xd_ipc_cpu1_tx_T;
//模块信号结构体
//实例化对象:c2837xd_ipc_cpu1_tx_B

3)模块状态结构体,实例化对象:c2837xd_ipc_cpu1_tx_DW

typedef struct {
  uint16_T Output_DSTATE;     /* '<S1>/Output' */
} DW_c2837xd_ipc_cpu1_tx_T;

4)模块有关参数寄存器,实例化对象:c2837xd_ipc_cpu1_tx_P(这个函数里面的所有内容都和模块有关系。我们看到的Simulink模块也是经过封装,可以直接拖拽使用,这里转为程序时,将每个模块的底层需要重新实现)

struct P_c2837xd_ipc_cpu1_tx_T_ {
  uint16_T WrapToZero_Threshold;       /* Mask Parameter: WrapToZero_Threshold
     //65535 = 2^16-1                    * Referenced by: '<S3>/FixPt Switch'*/
     //显然和IPC0传送的(0-2^16-1)数有关,这是最大值。     
    
  real32_T Vectoroftypesingle_Value[3];  //float 32位
    //{1.234F 2.345F 3.456F}  
    //IPC3传送的单精度数
                                        /* Expression: [single(1.234) single(2.345) single(3.456)]
                                        * Referenced by: '<Root>/Vector of type single'*/
                                         
  uint32_T uin32constant_Value;         //32位
    //  1234567U  
    //  IPC1传送的32位无符号整型数
    									/* Computed Parameter: uin32constant_Value
                                         * Referenced by: '<Root>/uin32 constant'
                                         */
  uint16_T Constant_Value;             //一旦 加到2^16-1  时,需要把计数器的值重新修改为0。这个函数就代表这个值。
    // 0U    
    //IPC0传送值的下限
    									 /* Computed Parameter: Constant_Value
                                          * Referenced by: '<S3>/Constant'
                                          */
  uint16_T Vector1to10_Value[10];       
    // { 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U },
    //IPC2传送的值
    									 /* Computed Parameter: Vector1to10_Value
                                          * Referenced by: '<Root>/Vector 1 to 10 '
                                          */
  uint16_T Output_InitialCondition;    
    // 0U
                                         /* Computed Parameter: Output_InitialCondition
                                          * Referenced by: '<S1>/Output'
                                          */
  uint16_T FixPtConstant_Value;     
    //1U    
    									 /* Computed Parameter: FixPtConstant_Value
                                          * Referenced by: '<S2>/FixPt Constant'
                                          */
};

上面官方给的注释就是在Simulink主界面的各个元件,这种应该是Subsystem(子函数)也就是封装的底层。

这个IPC传送函数封装的还是很好的,希望有机会能够移植到自己的代码中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Quikk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值