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 + -
显示快捷键?