radioserver.c

来自「simulink real-time workshop for dragon12」· C语言 代码 · 共 1,395 行 · 第 1/3 页

C
1,395
字号
          } else {
            /* the data to be sent matches a special token, need to
               escape the data to be sent so it doesn't get
               misinterpretted */
            if(i == PAYLOAD_LEN - 1) {
              /* there's not enough space for the ESCAPE token and
                 the escaped char in this segment,
                 => EOT, send the character next time */
              SPI0DR = RADIO_EOT_CHAR;
              haveSentEOT = 1;
              
            } else {
              /* there is space for the ESCAPE token and the escaped
                 char */
              SPI0DR = RADIO_ESCAPE_CHAR;
              SPI_Pause();
              
              while(SPI0SR_SPTEF == 0) {}
              RB_POP(&cc->outRB, &SPI0DR);
              
              i++;
            }
          }
        }
      } else {
        /* we have sent EOT, but still need to fill tx_payload */
        /* fill the rest of the transmit buffer with \0 */
        SPI0DR = 0x00;
      }

    SPI_Pause();
  }
  
  /* end SPI packet */
  CSN = 1;

#ifdef TO_DEBUG  
  SCI0_OutString("T~\r\n"); /* exit Transmit() */
#endif

  // Pulse CE to start transmission (must be > 10us)
  CE = 1;
}

/* 
  EnterReceive is a private procedure to configure the module for
  receive mode.
  Must be in a standby-mode to call this method.
*/
void RadioServer_EnterReceive() {

  /* a good idea to clear all interrupts and flush buffers first */
  SPI_OutChar(FLUSH_TX);
  SPI_OutChar(FLUSH_RX);
  SPI_OutChar2(W_STATUS, RX_DR_MASK | TX_DS_MASK | MAX_RT_MASK);

  /*
    bit  CONFIG Register
      7  0  Reserved
      6  1  MASK_RX_DR  : Don't reflect RX_DR on IRQ pin
      5  1  MASK_TX_DS  : Don't reflect TX_DS on IRQ pin
      4  1  MASK_MAX_RT : Don't reflect MAX_RT on IRQ pin
      3  1  EN_CRC      : Enable CRC
      2  1  CRCO        : 2 byte CRC
      1  1  PWR_UP      : Power Up
      0  1  PRIM_RX     : PRX
      
      Enter receive mode
  */
  SPI_OutChar2(W_CONFIG, 0x7f);
 
  CE = 1;
}

/* 
  OnReceive() is a private procedure which is called when the
  RX_DR flag is set in the STATUS register.
  Returns 1 if have received EOT, otherwise returns 0
*/
void RadioServer_OnReceive() {
unsigned char header, rID, tID, in, i, toEscape, haveReceivedEOT, trustData;

#ifdef TO_DEBUG
  SCI0_OutString("R\r\n"); /* enter onReceive() */
#endif															

  /* use in as a dummy */
  in = SPI0SR;
  in = SPI0DR;  // clear SPIF

  toEscape = haveReceivedEOT = 0;

  while(SPI0SR_SPTEF == 0) {}

  CSN = 0;
  
  /* send the read command */
  SPI0DR = R_RX_PAYLOAD;
  SPI_Pause();
  in = SPI_InChar(); // discard the status byte

  while(SPI0SR_SPTEF == 0) {}
  SPI0DR = NOP;      // output a dummy value to generate SCK
  SPI_Pause();

  /* read in header byte */
  header = SPI_InChar();

  while(SPI0SR_SPTEF == 0) {}
  SPI0DR = NOP;      // output a dummy value to generate SCK
  SPI_Pause();

  /* read in rID */
  rID = SPI_InChar();

  while(SPI0SR_SPTEF == 0) {}
  SPI0DR = NOP;      // output a dummy value to generate SCK
  SPI_Pause();

  /* read in tID */
  tID = SPI_InChar();

  if(tID == cc->inRB.in) {
    /* client's tID matches expected rID, i.e. the client's sending
       requested byte next, so can probably trust */
    trustData = 1;

  } else {

    /* the client is sending a byte out of order, probably cannot
       trust */
    trustData = 0;
    
#ifdef TO_DEBUG_ERROR_CONDITIONS
    SCI0_OutString("Invalid tID: ");
    SCI0_OutUDec(tID);
    SCI0_OutString(", expected ");
    SCI0_OutUDec(cc->inRB.in);
    SCI0_OutString("\r\n");
#endif
  }

  /* may be able to trust the data if the server has been reset
     recently */
  if(cc->reset & HEADER_RESET_MASK) {
    /* server has been reset recently */

    if(header & HEADER_ACK_RESET_MASK) {
      /* client has acknowledged server reset */
      cc->reset &= ~HEADER_RESET_MASK;
      
      /* what follows must be valid, since the client has ack'd
         reset */
      trustData = 1;

#ifdef TO_DEBUG
      SCI0_OutString("ACK'd reset\r\n");
#endif
    
    } else {

      /* client has not acknowledged reset, cannot trust data,
         even if had a "valid" tID */
      trustData = 0;

#ifdef TO_DEBUG_ERROR_CONDITIONS
      SCI0_OutString("Not ACK'd reset\r\n");
#endif
    }
  }
  
  /* if the client itself has been reset, we can trust the data */
  if(header & HEADER_RESET_MASK) {
    /* Client has been reset!
       This can be bad if there's still data in the buffer,
       may get data queued from another session => clean them */
    RadioServer_CleanOut(cc->id);
    RadioServer_CleanIn(cc->id);
  
    /* acknowledge to client that the reset has been a success */
    cc->reset |= HEADER_ACK_RESET_MASK;
    
    cc->failureCount = 0;

    /* what follows is a new byte (it's byte 0) */
    trustData = 1;
#ifdef TO_DEBUG
    SCI0_OutString("Client reset\r\n");
#endif
  } else {
    /* if client hasn't reset, clear ACK_RESET flag */
    cc->reset &= ~HEADER_ACK_RESET_MASK;
  }
  
#ifdef TO_DEBUG_ERROR_CONDITIONS
  if(!trustData) {
    SCI0_OutString("Discard data\r\n");
  }
#endif

  haveReceivedEOT = (header & HEADER_EOT_MASK) != 0;

  for(i=3; i<PAYLOAD_LEN; i++) {

    while(SPI0SR_SPTEF == 0) {}
    SPI0DR = NOP;      // output a dummy value to generate SCK
    SPI_Pause();

    in = SPI_InChar(); // read in potentially valid byte

    if(trustData && (haveReceivedEOT == 0)) {
      if(toEscape == 0) {    // last character in wasn't the ESCAPE char
        switch(in) {
        case RADIO_ESCAPE_CHAR:
          toEscape = 1;
          break;
          
        case RADIO_EOT_CHAR:
          /* have received EOT */
          haveReceivedEOT = 1;
          break;
          
        default:
          if(RB_PUSH(&cc->inRB, in)) {
#ifdef TO_DEBUG_ERROR_CONDITIONS
            SCI0_OutString("Dropped char: ");
            SCI0_OutUHex(in);
            SCI0_OutString("\r\n");
#endif
          }
          break;
        }
      } else {
        if(RB_PUSH(&cc->inRB, in)) {
#ifdef TO_DEBUG_ERROR_CONDITIONS
          SCI0_OutString("Dropped char: ");
          SCI0_OutUHex(in);
          SCI0_OutString("\r\n");
#endif
        }
        toEscape = 0;
      }
    }
  }
  
  CSN = 1;
  
#ifdef TO_DEBUG_ERROR_CONDITIONS
  if(cc->outRB.out != rID) {
    SCI0_OutString("Invalid rID: ");
    SCI0_OutUDec(rID);
    SCI0_OutString(", expected ");
    SCI0_OutUDec(outRB.out);
    SCI0_OutString("\r\n");
  }
#endif

  RB_SET_OUT(&cc->outRB, rID);

#ifdef TO_DEBUG
  SCI0_OutString("R~"); /* exit onReceive() */
#endif
}

/*
  Setup timer.
*/
void RadioServer_RestartTick() {
  TICK_ENABLE = 1;
  TICK_REGISTER = TICK_PRESCALAR + TCNT;
  TICK_INTERRUPT = 1;
}




/* beginning of the interrupt service routine =================================  */
#pragma CODE_SEG __NEAR_SEG NON_BANKED /* Interrupt section for this module. Placement will be in NON_BANKED area. */

/**
 * Called regularly.
 */
__interrupt void RadioServer_OnTick(void) {
unsigned char status;

  /* first deal with the timer flags */
  TICK_ENABLE = 0;
  TICK_INTERRUPT = 1;

  /* re-enable all interrupts (to allow servicing of RxD interrupt) */
  asm cli                // re-enable interrupts to allow this one to be interrupted
  
  /* always increment the tick counter */
  tickCounter++;

#ifdef TO_DEBUG
  SCI0_OutChar('`');
#endif

  switch(moduleState) {
  
  case STATE_WAIT_FOR_EOT:

#ifdef TO_DEBUG
    SCI0_OutChar('@');
#endif

    /* read status (one byte long) */
    SPI_Read(R_STATUS, &status, 1);

    if(status & TX_DS_MASK) {
      
      /* successfully transmitted PAYLOAD_LEN bytes */
      
#ifdef TO_DEBUG
      SCI0_OutString("T'd\r\n");
#endif

      /* clear interrupt */
      SPI_OutChar2(W_STATUS, TX_DS_MASK);

      /* Since EOT was transmitted, client is about to send,
         => enter RX */

      RadioServer_EnterReceive();

      setState(STATE_WAIT_FOR_RECEIVE);
      
    } else if(status & MAX_RT_MASK) {

      /* module has informed us there's been a timeout */

#ifdef TO_DEBUG_ERROR_CONDITIONS
      SCI0_OutString("F'd\r\n");
#endif

      /* clear interrupt */

      SPI_OutChar2(W_STATUS, MAX_RT_MASK);

      RB_SET_OUT(&cc->outRB, cc->lastTID);

      cc->failureCount++;

      /* try to transmit to the next client */
      RadioServer_Transmit(RadioServer_NextClient());
      setState(STATE_WAIT_FOR_EOT);

    } else if(tickCounter >= WAIT_FOR_ACK_TICK_COUNT) {

#ifdef TO_DEBUG_ERROR_CONDITIONS
      SCI0_OutString("Q'd\r\n");
#endif

      /* this is a very bizarre state to get into, treat it similarly
         to as though we had explicitly received a timeout */

      RB_SET_OUT(&cc->outRB, cc->lastTID);

      cc->failureCount++;

      /* transmit to the next client */
      RadioServer_Transmit(RadioServer_NextClient());
      setState(STATE_WAIT_FOR_EOT);
    }
    
    break;

  case STATE_WAIT_FOR_RECEIVE:


#ifdef TO_DEBUG
    SCI0_OutChar('r');
#endif

    /* read status (one byte long) */
    SPI_Read(R_STATUS, &status, 1);

    if(status & RX_DR_MASK) {
#ifdef TO_DEBUG
      SCI0_OutString("R'd");
#endif

      /* to be pedantic, should ensure that the RX_P_NO matches
         cc */
      if((((status & RX_P_NO_MASK) >> 1) - 1) == cc->id) {
        RadioServer_OnReceive();
        /* have received EOT */
        if(RadioServer_NextClient()) {
          /* new client */
          RadioServer_Transmit(UPDATE_ADDRESS);
          /* have sent EOT */
          setState(STATE_WAIT_FOR_EOT);
        } else {
          /* retransmitting to same client, need to wait for client
             to enter RX mode */
          setState(STATE_WAIT_FOR_CLIENT);
        }
      } else {
#ifdef TO_DEBUG_ERROR_CONDITIONS
        SCI0_OutString("Wrong pipe number!");
        SCI0_OutUHex((status & RX_P_NO_MASK) >> 1);
#endif
        RadioServer_EnterReceive();
        setState(STATE_WAIT_FOR_RECEIVE);

      }
    } else if(tickCounter >= TIME_OUT_TICK_COUNT) {

      /* client has timed out */
#ifdef TO_DEBUG_ERROR_CONDITIONS
      SCI0_OutString("Timeout");
#endif      

      cc->failureCount++;

      RadioServer_Transmit(RadioServer_NextClient());
      /* have sent EOT */
      setState(STATE_WAIT_FOR_EOT);
    }
    
    break;
  
  case STATE_WAIT_FOR_CLIENT:

#ifdef TO_DEBUG
    SCI0_OutChar('C');
#endif

    /* client is now in receive mode */
    if(tickCounter >= WAIT_FOR_CLIENT_TICK_COUNT) {
      /* transmitting back to the same client that just EOT'd */
      RadioServer_Transmit(MAINTAIN_ADDRESS);
      /* have sent EOT */
      setState(STATE_WAIT_FOR_EOT);
    }
    break;
  }
 
  /* restart the timer */
  RadioServer_RestartTick();
  
}


#pragma CODE_SEG DEFAULT
/* end of the interrupt service routine =================================  */


unsigned char RadioServer_Init(unsigned char baudRate,
                               unsigned char rfChannel,
                               unsigned long serverAddressHeader,
                               unsigned long clientAddressHeader,
                               unsigned char cFlags) {
unsigned char i;

// space to test the first the receive address was correctly written
unsigned char test[ADDRESS_LENGTH];

  /* configure CE pin as an output */
  CE_DDR = 1;

  /* Enter Standby-I Mode */
  CE = 0;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?