CAN总线iso15765源码分析

 

对ISO15765-2网络协议的代码分析

对ISO15765-2网络协议的代码分析

void canbus_test(void *args) {

  iso15765_init(&handler1);
  iso15765_init(&handler2);

  frame1.msg_sz = f_sz1;
  rand_string((char *)frame1.msg, frame1.msg_sz);

  f_sz1++;

  iso15765_send(&handler1, &frame1);
  while (1) {
    iso15765_process(&handler1);
    iso15765_process(&handler2);
    delay_1ms(100);
  }
  return;
}

iso15765_init

  iso15765_init(&handler1);
  iso15765_init(&handler2);

首先这里的handler1handler2是全局变量,已经初始化了

static iso15765_t handler1 = {
  .addr_md = N_ADM_FIXED,     // canTP,CAN传输协议的寻址模式
  .can_md = CANBUS_EXTENDED,  // CAN总线模式:stand(11位canID),extend(29位canID)
  .clbs.send_frame = send_frame1,
  .clbs.on_error = on_error,
  .clbs.get_ms = getms,
  .clbs.indn = indn1,   // 指示回调函数,当接收异常时触发
  .config.stmin = 0x3,  // 传输间隔 
  .config.bs = 0x0f,    // 传输块大小
  .config.n_bs = 100,   // 等待流控帧时间间隔 
  .config.n_cr = 3};    // 等待连续帧时间间隔

static iso15765_t handler2 = {
  .addr_md = N_ADM_FIXED,
  .can_md = CANBUS_EXTENDED,
  .clbs.send_frame = send_frame2,
  .clbs.on_error = on_error,
  .clbs.indn = indn2,
  .clbs.get_ms = getms,
  .config.stmin = 0x3,
  .config.bs = 0x0f,
  .config.n_bs = 100,
  .config.n_cr = 3};
  • 初始化并检查ISO15765处理器的配置参数是否正确
  • 没有分配的回调将被自动分配给静态的UNUSED函数
  • 如果没有发生错误,系统可以启动服务
  • 最后初始化了队列

      iqueue_init(&instance->inqueue, I15765_QUEUE_ELMS, sizeof(canbus_frame_t), instance->inq_buf){
        memset(instance->inq_buf, 0x00, _element_size * _max_elements);
        instance->inqueue->frist = NULL;
        instance->inqueue->next = instance->inq_buf;
      }
    
    • 其中

        inq_buf[I15765_QUEUE_ELMS * sizeof(canbus_frame_t)];
              
        #define I15765_QUEUE_ELMS  64 // 接收缓冲区最大接收帧
        typedef struct {
          uint32_t id;    /* CAN Frame ID */
          uint8_t type;   /* STD or FD */
          uint32_t mode;  /* CAN Frame mode `canbus_md` */
          uint16_t dlc;   /* Size of data */
          uint8_t dt[64]; /* Actual data of the frame */
        } canbus_frame_t;
      

rand_string

rand_string((char *)frame1.msg, frame1.msg_sz);

frame1.msg_sz个随机字符放到frame1.msg缓冲区中

其中:

  • frame1

      n_req_t frame1 = {
          .n_ai.n_pr = 0x07, .n_ai.n_sa = 0x01, .n_ai.n_ta = 0x02,
          .n_ai.n_ae = 0x00, .n_ai.n_tt = N_TA_T_PHY, .ctp_ft = CTP_T_FD,
          .msg = {0}, .msg_sz = 0,
      };
    
    • n_req_t

      typedef struct {
        ctp_type ctp_ft;              /* CANBus Frame type*/
        n_ai_t n_ai;                  /* Address information */
        n_pci_t n_pci;                /* Protocol control information */
        uint16_t msg_sz;              /* Message actual size */
        uint8_t msg[I15765_MSG_SIZE]; /* Message to be transmitted */
      } n_req_t;
      
      #define I15765_MSG_SIZE 512 /* Max. size of the TP up to 4095 bytes */
      
    • ctp_type是CAN 15765协议的帧类型

      typedef enum {
        CTP_T_STD = 0x01, /* Standard CANBUS */
        CTP_T_FD = 0x02   /* FD CANBUS       */
      } ctp_type;
      

iso15765_send

iso15765_send(&handler1, &frame1);
  • 请求发送一个消息。根据消息的情况,可能需要调用 “iso15765_process”。
  • 只要通信是同步的,该服务每次可以发送一条消息。
  • 内容

      n_rslt iso15765_send(iso15765_t *instance, n_req_t *frame) {
        instance->out.ctp_ft = frame->ctp_ft;
        instance->out.msg_sz = frame->msg_sz;
        memmove(instance->out.msg, frame->msg, frame->msg_sz);
        memmove(&instance->out.pdu.n_ai, &frame->n_ai, sizeof(n_ai_t));
        instance->out.sts = N_S_TX_BUSY;
      }
    
    • rand_string((char *)frame1.msg, frame1.msg_sz) 配置好的帧的信息和数据放到输出缓冲区
    • 这里frame1的数据由rand_string提供
    • frame1的信息,在初始化时就已经提供

        n_req_t frame1 = {
          .n_ai.n_pr = 0x07,  // 地址信息.地址优先级
          .n_ai.n_sa = 0x01,  // 地址信息.源地址
          .n_ai.n_ta = 0x02,  // 地址信息.目标地址
          .n_ai.n_ae = 0x00,  // 地址信息.地址扩展
          .n_ai.n_tt = N_TA_T_PHY,  // 地址信息.目标地址类型:物理、功能
          .ctp_ft = CTP_T_FD, // 帧类型:FD类型
          .msg = {0},
          .msg_sz = 0,
        };
      
      • 目标地址类型在《iso15765-2-2016》的8.3.2.4节中有说明
      • N_TAtypeN_TA参数的扩展
        • 所有类型的网络层消息都应支持物理寻址(1对1通信)
        • 功能寻址(1到n通信)仅支持单帧传输

iso15765_process

iso15765_process(&handler1);
iso15765_process(&handler2);

处理入站/出站流。为了达到最佳的操作效果,这个函数应该以最小的延迟连续调用。建议在一个线程中使用它。

n_rslt iso15765_process(iso15765_t *instance) {
  /* 首先检查是否发生了超时。只针对入站流 */
  n_rslt rslt = process_timeouts(instance);

  /* 脱队所有传入的帧并进行处理 */
  canbus_frame_t frame;
  while (iqueue_dequeue(&instance->inqueue, &frame) != I_EMPTY) {
    rslt |= iso15765_process_in(instance, &frame);
  }

  /* 处理出站流 */
  rslt |= iso15765_process_out(instance);
  return rslt;
}

process_timeout

inline static n_rslt process_timeouts(iso15765_t *ih) {
  if (ih->out.sts != N_S_TX_WAIT_FC || ih->out.last_upd.n_bs == 0 ||
      ih->config.n_bs == 0 ||
      (ih->out.last_upd.n_bs + ih->config.n_bs) >= ih->clbs.get_ms()) {
    return N_OK;
  }

  /* 如果发生超时,则重置计数器并向上层报告 */
  ih->out.cf_cnt = 0x0;
  signaling(N_INDN, &ih->out, (void *)ih->clbs.indn, ih->out.msg_sz,
            N_TIMEOUT_Bs);
  ih->clbs.on_error(N_TIMEOUT_Bs);
  return N_TIMEOUT_Bs;
}
  • 超时判断

      if (ih->out.sts != N_S_TX_WAIT_FC ||  // stream status 不是在处理或等待流控帧
          ih->out.last_upd.n_bs == 0 || // 下一个FC间隔时间为无限
          ih->config.n_bs == 0 ||       // 配置的时间间隔为无限
          (ih->out.last_upd.n_bs + ih->config.n_bs) >= ih->clbs.get_ms()) {
        return N_OK;
      }
    
    • 其中n_bs的含义:
      • 接收下一个流量控制FlowControl N_PDU的时间间隔
      • 发送方没有收到FlowControl N_PDU(丢失,覆盖)
      • 或接收方没有收到FirstFrame N_PDU或ConsecutiveFrame N_PDU(丢失,覆盖)
      • 终止消息传输并发出包含<N_Result> = N_TIMEOUT_BsN_USData.confirm
    • n_cr
      • 接收端未接收到连续帧 ConsecutiveFrame N_PDU(丢失,覆盖)
      • 或发送端未接收到前一个FC N_PDU(丢失,覆盖)
      • 中止消息接收并发出含有<N_Result> = N_TIMEOUT_CrN_USData.indication

处理输出帧

iso15765_process_out(instance)
static n_rslt iso15765_process_out(iso15765_t *ih) {
  /* 查找挂起的出站流PCI类型 */
  ih->out.pdu.n_pci.pt = n_out_frame_type(ih);

  switch (ih->out.pdu.n_pci.pt) {
    case N_PCI_T_SF: // 单帧
      // 将SF的所有数据复制到出站流,打包并发送canbus帧
      ih->out.pdu.n_pci.dl = ih->out.msg_sz;
      ih->out.pdu.sz = ih->out.msg_sz;
      // 根据地址模式,计算得到canID
      // 并且把要发送的can总线帧out.msg转换成PUD格式并拷贝到out.pud中
      if (n_pdu_pack(ih->addr_md, &ih->out.pdu, &id, ih->out.msg) != N_OK)
        goto iso15765_process_out_cfm;

      // 将数据发送出去
      rslt = ih->clbs.send_frame(
                ih->can_md, id, ih->out.ctp_ft,
                n_get_closest_can_dl(// 获取最接收的CAN数据长度:8,12,16,48,64
                    ih->out.pdu.sz +
                        n_get_dt_offset(ih->addr_md, N_PCI_T_SF, ih->out.pdu.sz),
                    ih->out.ctp_ft),
                ih->out.pdu.dt) == 0
                ? N_OK
                : N_ERROR;
      goto iso15765_process_out_cfm;
      break;
      // 第1帧
  case N_PCI_T_FF:
    /* 将FF的所有数据复制到出站流以进行传输,并为多帧接收准备服务 */
    ih->out.pdu.n_pci.dl = ih->out.msg_sz;
    ih->out.wf_cnt = 0;
    ih->out.pdu.sz = ih->out.ctp_ft == CTP_T_STD
                         ? ((ih->addr_md & 0x01) == 0 ? 6 : 5)
                         : ((ih->addr_md & 0x01) == 0 ? 62 : 61);
    ih->out.msg_pos = ih->out.pdu.sz;
    if (n_pdu_pack(ih->addr_md, &ih->out.pdu, &id, ih->out.msg) != N_OK)
      goto iso15765_process_out_cfm;
    ih->out.cf_cnt = 1;

    /* 在此帧之后,我们期望流控制,然后在传输之前分配正确的标志,以避免任何问题,并启动计时器*/
    ih->out.sts = N_S_TX_WAIT_FC;
    rslt = ih->clbs.send_frame(ih->can_md, id, ih->out.ctp_ft,
                               ih->out.ctp_ft == CTP_T_STD ? 8 : 64,
                               ih->out.pdu.dt) == 0 ? N_OK : N_ERROR;
    ih->out.last_upd.n_bs = ih->clbs.get_ms();
    return (rslt == 0) ? N_OK : N_ERROR;

    // 流控帧
  case N_PCI_T_CF:
    /* 如果未达到传输之间的最小差异,则跳过 */
    if ((ih->out.last_upd.n_cs + ih->config.stmin) > ih->clbs.get_ms())
      return N_OK;

    /* 增加帧的序列号和流的CF计数器,然后将PDU打包到CANBus帧 */
    ih->out.pdu.n_pci.sn = ih->out.cf_cnt;
    ih->out.cf_cnt = ih->out.cf_cnt == 0xF ? 1 : ih->out.cf_cnt + 1;
    if (ih->out.ctp_ft == CTP_T_STD) {
      uint8_t max_payload = (ih->addr_md & 0x01) == 0 ? 7 : 6;
      ih->out.pdu.sz = ih->out.msg_sz - ih->out.msg_pos;
      ih->out.pdu.sz =
          ih->out.pdu.sz >= max_payload ? max_payload : ih->out.pdu.sz;
    } else {
      uint8_t max_payload = (ih->addr_md & 0x01) == 0 ? 63 : 62;
      ih->out.pdu.sz = ih->out.msg_sz - ih->out.msg_pos;
      ih->out.pdu.sz =
          ih->out.pdu.sz >= max_payload ? max_payload : ih->out.pdu.sz;
    }

    if (n_pdu_pack(ih->addr_md, &ih->out.pdu, &id,
                   &ih->out.msg[ih->out.msg_pos]) != N_OK)
      goto iso15765_process_out_cfm;
    /* 增加指示入站缓冲区中剩余数据的位置 */
    ih->out.msg_pos += ih->out.pdu.sz;

    /* 如果在这一帧之后,我们期望流量控制,那么在传输之前分配正确的标志,以避免任何问题,并启动计时器*/
    if (ih->out.pdu.n_pci.sn == ih->config.bs) {
      ih->out.sts = N_S_TX_WAIT_FC;
      ih->out.last_upd.n_bs = ih->clbs.get_ms();
    }
    /* send the canbus frame! */
    uint8_t of1 = (ih->addr_md & 0x01) == 0 ? 1 : 2;
    rslt = ih->clbs.send_frame(
               ih->can_md, id, ih->out.ctp_ft,
               n_get_closest_can_dl(ih->out.pdu.sz + of1, ih->out.ctp_ft),
               ih->out.pdu.dt) == 0 ? N_OK : N_ERROR;
    ih->out.last_upd.n_cs = ih->clbs.get_ms();
    if (ih->out.msg_pos >= ih->out.msg_sz)
      goto iso15765_process_out_cfm;
    return N_OK;
  }

其中:

  • n_out_frame_type(ih)

      inline static pci_type n_out_frame_type(iso15765_t *instance) {
        pci_type result = N_PCI_T_CF;
    
        if (instance->out.cf_cnt == 0) { // 如果当前帧序号为0
          if ((instance->addr_md & 0x01) == 1) {
            if (instance->out.ctp_ft == CTP_T_STD) {
              result = instance->out.msg_sz <= 6 ? N_PCI_T_SF : N_PCI_T_FF;
            } else {
              result = instance->out.msg_sz <= 61 ? N_PCI_T_SF : N_PCI_T_FF;
            }
          } else {
            if (instance->out.ctp_ft == CTP_T_STD) { // can传输协议帧类型为std
              // 传输消息大小<=7:单帧:第1帧
              result = instance->out.msg_sz <= 7 ? N_PCI_T_SF : N_PCI_T_FF;
            } else {
              // 传输消息大小<=62:单帧:第1帧
              result = instance->out.msg_sz <= 62 ? N_PCI_T_SF : N_PCI_T_FF;
            }
          }
        }
        return result;
      }
    
  • n_pdu_pack(ih->addr_md, &ih->out.pdu, &id, ih->out.msg)

    将Can总线帧转换为PDU

      inline static n_rslt n_pdu_pack(
            addr_md mode, n_pdu_t *n_pdu, 
            uint32_t *id, uint8_t *dt) {
    
        // 根据CANTP地址模式计算CanID
        switch (mode) {
          case N_ADM_EXTENDED:
            n_pdu->dt[0] = n_pdu->n_ai.n_ta;
          case N_ADM_NORMAL:
            *id = 0x80 | n_pdu->n_ai.n_pr << 8 | n_pdu->n_ai.n_ta << 3 |
                  n_pdu->n_ai.n_sa | (n_pdu->n_ai.n_tt == N_TA_T_PHY ? 0x40U : 0x00U);
            break;
            case N_ADM_FIXED:
            *id = n_pdu->n_ai.n_pr << 26 |
                  (n_pdu->n_ai.n_tt == N_TA_T_PHY ? 0xDA : 0xDB) << 16 |
                  n_pdu->n_ai.n_ta << 8 | n_pdu->n_ai.n_sa;
            break;
          ...
        }
    
        // 根据PCI部分转换Can帧,根据mode设置n_pdu的头两个数据字节内容,dt没用到
        n_pci_pack(mode, n_pdu, dt);
        // 根据mode,把dt数据拷贝到n_pdu的数据段中
        return n_pdu_pack_dt(mode, n_pdu, dt);
    
    • n_pci_pack()

      ```c
      inline static n_rslt n_pci_pack(addr_md mode, n_pdu_t *n_pdu,
                            const uint8_t *dt) {
      
        uint8_t offs = (mode & 0x01);
        // 协议控制信息类型
        switch (n_pdu->n_pci.pt) {
          case N_PCI_T_SF: // 单帧
            // 数据长度
            if (n_pdu->n_pci.dl <= (uint16_t)(7 - offs)) {
              n_pdu->dt[0 + offs] =
                  (n_pdu->n_pci.pt) << 4 | (uint8_t)(n_pdu->n_pci.dl & 0x0F);
            } else {
              n_pdu->dt[0 + offs] = 0 + ((n_pdu->n_pci.pt) << 4);
              n_pdu->dt[1 + offs] = (uint8_t)n_pdu->n_pci.dl;
            }
            result = N_OK;
            break;
          case N_PCI_T_CF: // 连续帧
            n_pdu->dt[0 + offs] = (n_pdu->n_pci.pt) << 4 | n_pdu->n_pci.sn;
            result = N_OK;
            break;
          case N_PCI_T_FF: // 第1帧
            n_pdu->dt[0 + offs] =
                (n_pdu->n_pci.pt) << 4 | (n_pdu->n_pci.dl & 0x0F00) >> 8;
            n_pdu->dt[1 + offs] = n_pdu->n_pci.dl & 0x00FF;
            result = N_OK;
            break;
          case N_PCI_T_FC: // 流控帧
            n_pdu->dt[0 + offs] = (n_pdu->n_pci.pt) << 4 | n_pdu->n_pci.fs;
            n_pdu->dt[1 + offs] = n_pdu->n_pci.bs;
            n_pdu->dt[2 + offs] = n_pdu->n_pci.st;
            result = N_OK;
            break;
        }
        ...
      ```
      
  • n_pdu_pack_dt(mode, n_pdu, dt)

      inline static n_rslt n_pdu_pack_dt(addr_md mode, n_pdu_t *n_pdu, uint8_t *dt) {
        n_rslt result = N_ERROR;
    
        if (dt != NULL) {
          // pt:帧类型:单帧、连续帧、控制帧、第1帧
          switch (n_pdu->n_pci.pt) {
          case N_PCI_T_SF:
            memmove(&n_pdu->dt[n_get_dt_offset(mode, N_PCI_T_SF, n_pdu->sz)], dt,
                    n_pdu->sz);
            break;
          case N_PCI_T_FF:
            memmove(&n_pdu->dt[n_get_dt_offset(mode, N_PCI_T_FF, n_pdu->sz)], dt,
                    n_pdu->sz);
            break;
          case N_PCI_T_CF:
            memmove(&n_pdu->dt[n_get_dt_offset(mode, N_PCI_T_CF, n_pdu->sz)], dt,
                    n_pdu->sz);
            break;
          case N_PCI_T_FC:
            memmove(&n_pdu->dt[n_get_dt_offset(mode, N_PCI_T_FC, n_pdu->sz)], dt,
                    n_pdu->sz);
    
            break;
    
          default:
            break;
          }
          result = N_OK;
        }
        return result;
      }
    
  • ih->clbs.send_frame = send_frame1

      static uint8_t send_frame1(canbus_md mode, uint32_t id, uint8_t ctp_ft,
                                uint8_t dlc, uint8_t *dt) {
        print_frame(1, handler2.addr_md, mode, id, ctp_ft, dlc, dt);
    
        // 新建一个CAN总线帧
        canbus_frame_t frame = {.id = id, .dlc = dlc, .mode = mode, .type = ctp_ft};
        // 将PDU类型的dt数据拷贝到CAN总线帧的dt数据段
        memmove(frame.dt, dt, dlc);
        // 将CAN总线帧发送到handler2的inqueue输入队列中
        iso15765_enqueue(&handler2, &frame);
        return 0;
      }
    
  • PCI: Protocol Control Information Type

      typedef enum {
        N_PCI_T_SF = 0x00U, /* Single Frame */
        N_PCI_T_FF = 0x01U, /* First Frame */
        N_PCI_T_CF = 0x02U, /* Consecutive Frame */
        N_PCI_T_FC = 0x03U, /* Flow Control Frame */
        N_PCI_T_UN = 0xFF   /* Unknown */
      } pci_type;
    

处理传入的帧

  1. 从队列中取出帧

     iqueue_dequeue(&instance->inqueue, &frame)
    

    将队列instance->inqueue中的帧取出放到frame中

     // 如果队列为空,返回
     if (_queue->first != (void *)0) {
       return I_EMPTY
     }
     // 如果队列有内容,返回内容,element_size: 80字节
     memmove(frame, (const *)inqueue->first, inqueue->element_size);
     inqueue->frist += inqueue->element_size;
     // 如果队列没内容,则first=0x0;
     _queue->first = _queue->first == _queue->next ? (void *)0 : _queue->first;
    
  2. 处理帧

     iso15765_process_in(instance, &frame)
    

    函数分析:

     inline static n_rslt iso15765_process_in(iso15765_t *ih,
                                          canbus_frame_t *frame) {
       /* 将canbus帧转换为PDU格式并按其PCI类型进行处理 */
       ih->in.ctp_ft = frame->type; // stand or FD
    
       // 从CAN总线帧格式转换成PDU格式
       if (n_pdu_unpack(ih->addr_md, &ih->in.pdu, frame->id, (uint8_t)frame->dlc,
                       frame->dt) == N_OK) {
         switch (ih->in.pdu.n_pci.pt) {
         case N_PCI_T_FC:
           return process_in_fc(ih, frame);
         case N_PCI_T_CF:
           return process_in_cf(ih, frame);
         case N_PCI_T_SF:
           return process_in_sf(ih, frame);
         case N_PCI_T_FF:
           return process_in_ff(ih, frame);
         default:
           break;
         }
       }
    
       /* According to (ref: iso15765-2 p.26) if PDU is not valid
       * we should ignore it */
       ih->clbs.on_error(N_INV_PDU);
       return N_INV_PDU;
     }
    
         - `n_pdu_unpack()`
    
       inline static n_rslt n_pdu_unpack(
             addr_md mode, n_pdu_t *n_pdu, uint32_t id,
             uint8_t dlc, uint8_t *dt) {
            
         /**********************************************************
          * CANTP的寻址模式《iso-15765-2-2016》10.3.1寻址格式
          * 网络层数据交换由三种编址格式支持:正常、扩展和混合编址。
          * 每种寻址格式需要不同数量的CAN帧数据字节来封装与要交换的数据相关联的寻址信息
          * 因此,单个CAN帧内传输的数据字节数取决于所选的寻址格式类型
         **************************************************************/
         // 根据CANTP的寻址模式,从CAN总线数据帧中提取PDU信息
         // 1. 从ID中提出地址信息
         switch (mode) {
         case N_ADM_MIXED11:
           n_pdu->n_ai.n_ae = dt[0];
         case N_ADM_NORMAL: // canID中带有地址信息
           n_pdu->n_ai.n_pr = (uint8_t)((id & 0x700U) >> 8);
           n_pdu->n_ai.n_ta = (uint8_t)((id & 0x38U) >> 3);
           n_pdu->n_ai.n_sa = (uint8_t)(id & 0x07U);
           n_pdu->n_ai.n_tt =
               (uint8_t)(((id & 0x40U) >> 6) == 1 ? N_TA_T_PHY : N_TA_T_FUNC);
           break;
         case N_ADM_MIXED29:
           // ...
           break;
         case N_ADM_FIXED:
           // ...
           break;
         case N_ADM_EXTENDED:
           // ...
           break;
         default:
           return N_UNE_PDU;
         }
    
         // 从CAN总线帧中提取PCI控制信息
         n_pci_unpack(mode, n_pdu, dlc, dt);
         // 根据PCI,把can总线帧dt中的内容拷贝到n_pdu.dt中
         n_pdu_unpack_dt(mode, n_pdu, dt);
    
         return N_OK;
       }
    
    • n_pci_unpack(mode, n_pdu, dlc, dt)

        inline static n_rslt n_pci_unpack(addr_md mode, n_pdu_t *n_pdu, uint8_t dlc,
                                        uint8_t *dt) {
      
            uint8_t offs = (mode & 0x01);
            // 得到此PDU的PCI类型:SF/FF/FC/CF
            n_pdu->n_pci.pt = (dt[0 + offs] & 0xF0) >> 4;
            // 根据PCI类型,从CAN总线帧的dt中取出
            // PCI内容: pci.fs/bs/st/dl, pdu.sz
            switch (n_pdu->n_pci.pt) {
            case N_PCI_T_SF:
              n_pdu->n_pci.dl = dlc <= 8 ? (dt[0 + offs] & 0x0F) : (dt[1 + offs]);
              result = N_OK;
              break;
            case N_PCI_T_CF:
              //...
              break;
            case N_PCI_T_FF:
              //...
              break;
            case N_PCI_T_FC:
              // ...
              break;
            default:
              result = N_ERROR;
              break;
            }
          }
          return result;
        }
      
    • n_pdu_unpack_dt(mode, n_pdu, dt)

        inline static n_rslt n_pdu_unpack_dt(
              addr_md mode, n_pdu_t *n_pdu,
              uint8_t *dt) {
      
            switch (n_pdu->n_pci.pt) {
            case N_PCI_T_SF:
              memmove(n_pdu->dt,
                      &dt[n_get_dt_offset(mode, N_PCI_T_SF, n_pdu->n_pci.dl)],
                      n_pdu->n_pci.dl);
              result = N_OK;
              break;
            case N_PCI_T_FF:
              memmove(n_pdu->dt, &dt[n_get_dt_offset(mode, N_PCI_T_FF, n_pdu->sz)],
                      n_pdu->sz);
              result = N_OK;
              break;
            case N_PCI_T_CF:
              memmove(n_pdu->dt, &dt[n_get_dt_offset(mode, N_PCI_T_CF, n_pdu->sz)],
                      n_pdu->sz);
              result = N_OK;
              break;
            case N_PCI_T_FC:
              memmove(n_pdu->dt, &dt[n_get_dt_offset(mode, N_PCI_T_FC, n_pdu->sz)],
                      n_pdu->sz);
              result = N_OK;
              break;
            default:
              result = N_ERROR;
              break;
          }
          return result;
        }
      
      - 处理PDU数据
      
    • process_in_sf(ih, frame)

      ```c
      static n_rslt process_in_sf(iso15765_t *ih, canbus_frame_t *frame) {
        /* 如果正在接收:终止当前接收,将<N_Result>设置为N_unep_PDU, 
           向上层报告N_USData.indication并显示,再将SF N_PDU处理为新接收的开始
        */
        if ((ih->in.sts & N_S_RX_BUSY) != 0) {
          // 停止接收,并上报并显示接收到的意外协议数据单元
          ih->clbs.on_error(N_UNE_PDU);
          signaling(N_INDN, &ih->in, 
                    (void *)ih->clbs.indn, ih->in.msg_sz, N_UNE_PDU);
        }
        // 接收这个数据
        memmove(&ih->in.msg[0], ih->in.pdu.dt, ih->in.pdu.n_pci.dl);
        ih->in.sts = N_S_IDLE;
        // 通过signaling把数据传给上层应用程序
        signaling(N_INDN, &ih->in, 
                  (void *)ih->clbs.indn, ih->in.pdu.n_pci.dl, N_OK);
        return N_OK;
      }
      ``` - `singaling()`   根据tp信号类型,把`sgn_rslt`, `strm.msg`, `strm.pdu`都拷贝到`sgn_indn`结构体中, 再通过`cb`回调函数把`sgn_indn`返回给上层应用,这里`sgn_indn`是一个全局变量
      
      ```c
      inline static void signaling(signal_tp tp, n_iostream_t *strm,
                         void (*cb)(void *), uint16_t msg_sz,
                         n_rslt sgn_rslt) {
            switch (tp) {
              case N_INDN:
                sgn_indn.rslt = sgn_rslt;
                sgn_indn.msg_sz = msg_sz;
                memmove(&sgn_indn.n_ai, &strm->pdu.n_ai, sizeof(n_ai_t));
                memmove(&sgn_indn.n_pci, &strm->pdu.n_pci, sizeof(n_pci_t));
                memmove(&sgn_indn.msg, strm->msg, msg_sz);
                strm->sts = N_S_IDLE;
                cb(&sgn_indn);
                break;
              case N_FF_INDN:
                sgn_ff_indn.msg_sz = msg_sz;
                memmove(&sgn_ff_indn.n_ai, &strm->pdu.n_ai, sizeof(n_ai_t));
                memmove(&sgn_ff_indn.n_pci, &strm->pdu.n_pci, sizeof(n_pci_t));
                strm->sts = (uint8_t)((uint32_t)strm->sts | (uint32_t)N_S_RX_BUSY);
                cb(&sgn_ff_indn);
                break;
              case N_CONF:
                  sgn_conf.rslt = sgn_rslt;
                  memmove(&sgn_conf.n_ai, &strm->pdu.n_ai, sizeof(n_ai_t));
                  memmove(&sgn_conf.n_pci, &strm->pdu.n_pci, sizeof(n_pci_t));
                  cb(&sgn_conf);
                  break;
              default:
                return;
              }
        return;
      }
      ```
      
    • process_in_ff(ih, frame)

        static n_rslt process_in_ff(iso15765_t *ih, canbus_frame_t *frame) {
            /* 如果正在接收:终止当前接收,将<N_Result>设置为N_unep_PDU,
            向上层报告N_USData.indication,并将FF N_PDU处理为新接收的开始 */
            if ((ih->in.sts & N_S_RX_BUSY) != 0) {
              ih->clbs.on_error(N_UNE_PDU);
              signaling(N_INDN, &ih->in, (void *)ih->clbs.indn, ih->in.msg_sz, N_UNE_PDU);
            }
      
            /* Copy all data, init the CFrames reception parameters and send a FC */
            /* 复制所有数据,初始化CFrames接收参数并发送FC */
            memmove(ih->in.msg, ih->in.pdu.dt, ih->in.pdu.sz);
            ih->in.msg_sz = ih->in.pdu.n_pci.dl;
            ih->in.msg_pos = ih->in.pdu.sz;
            ih->in.cf_cnt = 0;
            ih->in.wf_cnt = 0;
            signaling(N_FF_INDN, &ih->in, 
                      (void *)ih->clbs.ff_indn, ih->in.msg_sz, N_OK);
            send_N_PCI_T_FC(ih);
            return N_OK;
        }
      
    • process_in_cf(ih, frame)

      ```c
      static n_rslt process_in_cf(iso15765_t *ih, canbus_frame_t *frame) {
        /* 增加CF计数器,检查接收顺序是否正常 */
        ih->in.cf_cnt = ih->in.cf_cnt + 1 > 0x0F ? 1 : ih->in.cf_cnt + 1;
        if (ih->in.cf_cnt != ih->in.pdu.n_pci.sn) {
          rslt = N_INV_SEQ_NUM;
          goto in_cf_error;
        }
        /* 只要一切正常,我们就将帧数据复制到入站流缓冲区。
          然后检查消息大小是否完成,并向用户发送信号,然后重置inboud流 */
        memmove(&ih->in.msg[ih->in.msg_pos], ih->in.pdu.dt, ih->in.pdu.sz);
        ih->in.msg_pos += ih->in.pdu.sz;
      
        /*如果到达最大CF计数器,则发送FC帧*/ 
        if (ih->in.cf_cnt % ih->config.bs == 0 && ih->config.bs != 0) {
          send_N_PCI_T_FC(ih);
        }
        /* 更新cr时间: 
            接收端未接收到连续帧N_PDU(丢失,覆盖),
            或发送端未接收到前一个FC N_PDU(丢失,覆盖)的时间 */
        ih->in.last_upd.n_cr = ih->clbs.get_ms();
        return rslt;
      }
      ```
      
    • process_in_fc(ih, frame)

        static n_rslt process_in_fc(iso15765_t *ih, canbus_frame_t *frame) {
          switch (ih->in.pdu.n_pci.fs) {
          case N_WAIT:
          /* 增加WF计数器,检查我们是否达到WF限制以中止接收,并(如果不是WF溢出)更新Bs时间 */
            ih->out.wf_cnt += 1;
            if (check_max_wf_capacity(ih) != N_WFT_OVRN)
              return N_OK;
            ih->out.last_upd.n_bs = ih->clbs.get_ms();
            rslt = N_WFT_OVRN;
            break;
          /* 缓冲区溢出,表示分段消息的第一帧中指定的字节数超过了可存储在接收方实体缓冲区中的字节数 */
          case N_OVERFLOW:
            rslt = N_BUFFER_OVFLW;
            break;
          case N_CONTINUE:
          /* 将请求的传输参数(从接收器)存储到出站流,重置CFs(1)和WFs(0)的计数器,
              并将出站流状态更改为就绪 */
            ih->out.cfg_bs = ih->in.pdu.n_pci.bs;
            ih->out.stmin = ih->in.pdu.n_pci.st;
            set_stream_data(&ih->out, 1, 0, N_S_TX_READY);
            return N_OK;
          default:
            rslt = N_UNE_FC_STS;
            break;
          }
          /* 如果出现错误(此处唯一的方法),则重置出站流,
            设置CFs(0)和WFs(0)的计数器,并将出站流状态更改为空闲。
            使用on_error回调通知上层 */
          set_stream_data(&ih->out, 0, 0, N_S_IDLE);
          ih->clbs.on_error(rslt);
          ih->in.sts = N_S_IDLE;
          return rslt;
        }