From a0b3b47c48e11f129a68e51b8adf66d78a74ed50 Mon Sep 17 00:00:00 2001 From: kokomojoe Date: Thu, 7 Oct 2010 04:59:18 +0000 Subject: [PATCH] OP-21 Flight/Bootloader - experimental modules implementing a simple serial packet (SSP) protocol. ssp_test is a test framework. ssp is the module, main_ssp.c prototype module on how this could be used in the bootloader, it is not fully functional, additional logic required for start-up detection to decide if serial initialization is required. git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1909 ebee16cc-31ac-478f-84a7-5cbb03baadba --- flight/Bootloaders/OpenPilot/inc/ssp.h | 121 ++++ flight/Bootloaders/OpenPilot/main_ssp.c | 300 +++++++++ flight/Bootloaders/OpenPilot/ssp.c | 815 ++++++++++++++++++++++++ flight/Bootloaders/OpenPilot/ssp_test.c | 229 +++++++ 4 files changed, 1465 insertions(+) create mode 100644 flight/Bootloaders/OpenPilot/inc/ssp.h create mode 100644 flight/Bootloaders/OpenPilot/main_ssp.c create mode 100644 flight/Bootloaders/OpenPilot/ssp.c create mode 100644 flight/Bootloaders/OpenPilot/ssp_test.c diff --git a/flight/Bootloaders/OpenPilot/inc/ssp.h b/flight/Bootloaders/OpenPilot/inc/ssp.h new file mode 100644 index 000000000..59ef87e0e --- /dev/null +++ b/flight/Bootloaders/OpenPilot/inc/ssp.h @@ -0,0 +1,121 @@ +/******************************************************************* + * + * NAME: ssp.h + * + * + *******************************************************************/ +#ifndef SSP_H +#define SSP_H +/** INCLUDE FILES **/ +#include + +/** LOCAL DEFINITIONS **/ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define SSP_TX_IDLE 0 // not expecting a ACK packet (no current transmissions in progress) +#define SSP_TX_WAITING 1 // waiting for a valid ACK to arrive +#define SSP_TX_TIMEOUT 2 // failed to receive a valid ACK in the timeout period, after retrying. +#define SSP_TX_ACKED 3 // valid ACK received before timeout period. +#define SSP_TX_BUFOVERRUN 4 // amount of data to send execeds the transmission buffer sizeof +#define SSP_TX_BUSY 5 // Attempted to start a transmission while a transmission was already in progress. +//#define SSP_TX_FAIL - failure... + +#define SSP_RX_IDLE 0 +#define SSP_RX_RECEIVING 1 +#define SSP_RX_COMPLETE 2 + +// types of packet that can be received +#define SSP_RX_DATA 5 +#define SSP_RX_ACK 6 +#define SSP_RX_SYNCH 7 + +typedef enum decodeState_ { + decode_len1_e = 0, + decode_seqNo_e, + decode_data_e, + decode_crc1_e, + decode_crc2_e, + decode_idle_e +} DecodeState_t; + +typedef enum ReceiveState { + state_escaped_e = 0, + state_unescaped_e +} ReceiveState_t; + +typedef struct +{ + uint8_t *pbuff; + uint16_t length; + uint16_t crc; + uint8_t seqNo; +} Packet_t; + +typedef struct { + + uint8_t *rxBuf; // Buffer used to store rcv data + uint16_t rxBufSize; // rcv buffer size. + uint8_t *txBuf; // Length of data in buffer + uint16_t txBufSize; // CRC for data in Packet buff + uint16_t max_retry; // Maximum number of retrys for a single transmit. + int32_t timeoutLen; // how long to wait for each retry to succeed + void (*pfCallBack)( uint8_t *, uint16_t); // call back function that is called when a full packet has been received + int16_t (*pfSerialRead)(void); // function to call to read a byte from serial hardware + void (*pfSerialWrite)( uint8_t ); // function used to write a byte to serial hardware for transmission + uint32_t (*pfGetTime)(void); // function returns time in number of seconds that has elapsed from a given reference point +} PortConfig_t; + +typedef struct Port_tag { + void (*pfCallBack)( uint8_t *, uint16_t); // call back function that is called when a full packet has been received + int16_t (*pfSerialRead)(void); // function to read a character from the serial input stream + void (*pfSerialWrite)( uint8_t ); // function to write a byte to be sent out the serial port + uint32_t (*pfGetTime)(void); // function returns time in number of seconds that has elapsed from a given reference point + uint8_t retryCount; // how many times have we tried to transmit the 'send' packet + uint8_t maxRetryCount; // max. times to try to transmit the 'send' packet + int32_t timeoutLen; // how long to wait for each retry to succeed + int32_t timeout; // current timeout. when 'time' reaches this point we have timed out + uint8_t txSeqNo; // current 'send' packet sequence number + uint16_t rxBufPos; // current buffer position in the receive packet + uint16_t rxBufLen; // number of 'data' bytes in the buffer + uint8_t rxSeqNo; // current 'receive' packet number + uint16_t rxBufSize; // size of the receive buffer. + uint16_t txBufSize; // size of the transmit buffer. + uint8_t *txBuf; // transmit buffer. REquired to store a copy of packet data in case a retry is needed. + uint8_t *rxBuf; // receive buffer. Used to store data as a packet is received. + uint16_t sendSynch; // flag to indicate that we should send a synchronize packet to the host + // this is required when switching from the application to the bootloader + // and vice-versa. This fixes the firwmare download timeout. + // when this flag is set to true, the next time we send a packet we will first + // send a synchronize packet. + ReceiveState_t InputState; + DecodeState_t DecodeState; + uint16_t SendState; + uint16_t crc; + uint32_t RxError; + uint32_t TxError; + uint16_t flags; +} Port_t; + + + +/** Public Data **/ + +/** PUBLIC FUNCTIONS **/ +int16_t ssp_ReceiveProcess( Port_t *thisport ); +int16_t ssp_SendProcess( Port_t *thisport ); +uint16_t ssp_SendString( Port_t *thisport, char *str ); +int16_t ssp_SendData(Port_t *thisport, const uint8_t * data,const uint16_t length ); +void ssp_Init( Port_t *thisport, const PortConfig_t* const info); +int16_t ssp_ReceiveByte(Port_t *thisport ); +uint16_t ssp_Synchronise( Port_t *thisport ); + + +/** EXTERNAL FUNCTIONS **/ + +#endif diff --git a/flight/Bootloaders/OpenPilot/main_ssp.c b/flight/Bootloaders/OpenPilot/main_ssp.c new file mode 100644 index 000000000..e84ff8092 --- /dev/null +++ b/flight/Bootloaders/OpenPilot/main_ssp.c @@ -0,0 +1,300 @@ +/** + ****************************************************************************** + * @addtogroup OpenPilotSystem OpenPilot System + * @brief These files are the core system files of OpenPilot. + * They are the ground layer just above PiOS. In practice, OpenPilot actually starts + * in the main() function of openpilot.c + * @{ + * @addtogroup OpenPilotCore OpenPilot Core + * @brief This is where the OP firmware starts. Those files also define the compile-time + * options of the firmware. + * @{ + * @file openpilot.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * @brief Sets up and runs main OpenPilot tasks. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* OpenPilot Includes */ +//#include "openpilot.h" +#include +#include "pios_opahrs.h" +#include "stopwatch.h" +#include "op_dfu.h" +#include "usb_lib.h" +/* Prototype of PIOS_Board_Init() function */ +extern void PIOS_Board_Init(void); +extern void FLASH_Download(); +#define BSL_HOLD_STATE ((PIOS_USB_DETECT_GPIO_PORT->IDR & PIOS_USB_DETECT_GPIO_PIN) ? 0 : 1) + +/* Private typedef -----------------------------------------------------------*/ +typedef void (*pFunction)(void); +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +pFunction Jump_To_Application; +uint32_t JumpAddress; + +/// LEDs PWM +uint32_t period1 = 50; // *100 uS -> 5 mS +uint32_t sweep_steps1 = 100; // * 5 mS -> 500 mS +uint32_t period2 = 50; // *100 uS -> 5 mS +uint32_t sweep_steps2 = 100; // * 5 mS -> 500 mS + +/* Extern variables ----------------------------------------------------------*/ +DFUStates DeviceState; +uint8_t JumpToApp = FALSE; +uint8_t GO_dfu = FALSE; +uint8_t USB_connected = FALSE; +uint8_t User_DFU_request = FALSE; +static uint8_t mReceive_Buffer[64]; + +uint8_t comm_port; + +/* Private function prototypes -----------------------------------------------*/ +uint32_t LedPWM(uint32_t pwm_period, uint32_t pwm_sweep_steps, uint32_t count); +uint8_t processRX(); +void jump_to_app(); + +#define BLUE LED1 +#define RED LED2 + +int main() { + /* NOTE: Do NOT modify the following start-up sequence */ + /* Any new initialization functions should be added in OpenPilotInit() */ + + /* Brings up System using CMSIS functions, enables the LEDs. */ + PIOS_SYS_Init(); + if (BSL_HOLD_STATE == 0) + USB_connected = TRUE; + + if( USB_connected == TRUE ) { + comm_port = PIOS_COM_TELEM_USB; + } else { + // check for user request to enter bootloader + // if true then: + // if( check_user_request() == TRUE ) { + // USER_DFU_request = TRUE; + // comm_port = PIOS_COM_TELEM_RF; + // ssp_Init( rf_port, rf_port_config ); + // } + } + if ((USB_connected==TRUE) || (User_DFU_request==TRUE)) { + GO_dfu = TRUE; + PIOS_Board_Init(); + PIOS_OPAHRS_Init(); + DeviceState = BLidle; + STOPWATCH_Init(100); + USB_connected = TRUE; + PIOS_SPI_RC_PinSet(PIOS_OPAHRS_SPI, 0); + //OPDfuIni(false); + + } else { + JumpToApp = TRUE; + } + + STOPWATCH_Reset(); + + while (TRUE) { + if (JumpToApp == TRUE) { + jump_to_app(); + } + flash_led(); + + if (STOPWATCH_ValueGet() > 100 * 50 * 100) + STOPWATCH_Reset(); + if ((STOPWATCH_ValueGet() > 60000) && (DeviceState == BLidle)) + JumpToApp = TRUE; + + //processRX(); + processComm(); + + DataDownload(start); + //DelayWithDown(10);//1000000); + } +} + + +void jump_to_app() { + if (((*(__IO uint32_t*) START_OF_USER_CODE) & 0x2FFE0000) == 0x20000000) { /* Jump to user application */ + FLASH_Lock(); + RCC_APB2PeriphResetCmd(0xffffffff, ENABLE); + RCC_APB1PeriphResetCmd(0xffffffff, ENABLE); + RCC_APB2PeriphResetCmd(0xffffffff, DISABLE); + RCC_APB1PeriphResetCmd(0xffffffff, DISABLE); + _SetCNTR(0); // clear interrupt mask + _SetISTR(0); // clear all requests + + JumpAddress = *(__IO uint32_t*) (START_OF_USER_CODE + 4); + Jump_To_Application = (pFunction) JumpAddress; + /* Initialize user application's Stack Pointer */ + __set_MSP(*(__IO uint32_t*) START_OF_USER_CODE); + Jump_To_Application(); + } else { + DeviceState = failed_jump; + return; + } +} +uint32_t LedPWM(uint32_t pwm_period, uint32_t pwm_sweep_steps, uint32_t count) { + uint32_t pwm_duty = ((count / pwm_period) % pwm_sweep_steps) + / (pwm_sweep_steps / pwm_period); + if ((count % (2 * pwm_period * pwm_sweep_steps)) > pwm_period + * pwm_sweep_steps) + pwm_duty = pwm_period - pwm_duty; // negative direction each 50*100 ticks + return ((count % pwm_period) > pwm_duty) ? 1 : 0; +} + +uint8_t processRX() { + while(PIOS_COM_ReceiveBufferUsed(PIOS_COM_TELEM_USB)>=63) + { + for (int32_t x = 0; x < 63; ++x) { + mReceive_Buffer[x] = PIOS_COM_ReceiveBuffer(PIOS_COM_TELEM_USB); + } + //PIOS_IRQ_Enable(); + processComand(mReceive_Buffer); + } + return TRUE; +} + +#define PACKET_SIZE 64 + +uint8_t packet_available = false; +uint8_t data_buffer[PACKET_SIZE]; + +// this function is called from the serial transport layer receive state machine when a valid +// data packet is received. +void PacketCallback(uint8_t *buf, uint16_t length ) +{ + uint16_t x; + for( x = 0; x < length; x++ ) { + data_buffer[x] = buf[x]; + } + if( packet_available == true ) { + // overrun condition... + // TODO act on overrun condition + } else { + packet_available = true; + } +} + + + +void processComm(void) +{ + uint16_t x = 0; + if (comm_port == PIOS_COM_TELEM_RF ) { + ssp_ReceiveProcess(); // pump the receive state machine + ssp_SendProcess(); // pump the transmit state machine. + // check to see if any data is available. this is updated in the callback function from the receive state machine. + if( packet_available == true ) { + // reset packet_available to let the call back function know that the packet data has been copied + // from the buffer. this will allow an overrun condition to be detected. + // also note that the only way the receive buffer is modified is through the ssp_ReceiveBuffer() function, + // otherwise data is buffered at the PIOS_COM layer. + } + } else { + if( PIOS_COM_ReceiveBufferUsed( PIOS_COM_TELEM_USB ) >= 63 ) { + for( x = 0; x < 63; x++ ) { + data_buffer[x] = PIOS_COM_Receivebuffer(PIOS_COM_TELEM_USB); + } + packet_available = true; + } + } + if( packet_available == true) { + processCommand( data_buffer); + packet_available = false; + } +} +// an alternate processComm func if both USB and serial used the same transport layer +void alt_ProcessComm(void) +{ + ssp_ReceiveProcess(port); + ssp_SendProcess(port); + if( packet_available == true) { + packet_available = false; + processComand(data_buffer); + } +} + + +// sends out data... +uint16_t SendBuffer( uint8_t buf, uint16_t length ) +{ + if( comm_port == PIOS_COM_TELEM_RF ) { + ssp_SendData( rf_port, buf, length ); + // or ssp_SendDataBlock( rf_port, buf, length ); + } else { + PIOS_COM_BufferPut( PIOS_COM_TELEM_USB, buf, length ); + } +} + +void flash_led(void) +{ + switch (DeviceState) { + case Last_operation_Success: + case uploadingStarting: + case DFUidle: + period1 = 50; + sweep_steps1 = 100; + PIOS_LED_Off(RED); + period2 = 0; + break; + case uploading: + period1 = 50; + sweep_steps1 = 100; + period2 = 25; + sweep_steps2 = 50; + break; + case downloading: + period1 = 25; + sweep_steps1 = 50; + PIOS_LED_Off(RED); + period2 = 0; + break; + case BLidle: + period1 = 0; + PIOS_LED_On(BLUE); + period2 = 0; + break; + default://error + period1 = 50; + sweep_steps1 = 100; + period2 = 50; + sweep_steps2 = 100; + } + + if (period1 != 0) { + if (LedPWM(period1, sweep_steps1, STOPWATCH_ValueGet())) { + PIOS_LED_On(BLUE); + } else { + PIOS_LED_Off(BLUE); + } + } else { + PIOS_LED_On(BLUE); + } + if (period2 != 0) { + if (LedPWM(period2, sweep_steps2, STOPWATCH_ValueGet())) { + PIOS_LED_On(RED); + } else { + PIOS_LED_Off(RED); + } + } else { + PIOS_LED_Off(RED); + } +} diff --git a/flight/Bootloaders/OpenPilot/ssp.c b/flight/Bootloaders/OpenPilot/ssp.c new file mode 100644 index 000000000..a72e8fae5 --- /dev/null +++ b/flight/Bootloaders/OpenPilot/ssp.c @@ -0,0 +1,815 @@ +/*********************************************************************************************************** +* +* NAME: ssp.c +* DESCRIPTION: simple serial protocol - packet based serial transport layer. +* AUTHOR: Joe Hlebasko +* HISTORY: Created 1/1/2010 +* +* Packet Formats +* Format: +* +------+----+------+---------------------------+--------+ +* | 225 | L1 | S# | App Data (0-254 bytes) | CRC 16 | +* +------+----+------+---------------------------+--------+ +* +* 225 = sync byte, indicates start of a packet +* L1 = 1 byte for size of data payload. (sequence number is part of data payload.) +* S# = 1 byte for sequence number. +* Seq of 0 = seq # synchronise request, forces other end to reset receive sequence number to 1. +* sender of synchronise request will reset the tx seq number to 1 +* Seq # of 1..127 = normal data packets. Sequence number is incremented by for each transmitted +* packet. Rolls over from 127 to 1. +* if most sig. bit is set then the packet is an ACK packet of data packet sequence number of the +* lower 7 bits (1..127) +* App Data may contain 0..254 bytes. The sequence number is consider part of the payload. +* CRC 16 - 16 bits of CRC values of Sequence # and data bytes. +* +* Protocol has two types of packets: data and ack packets. ACK packets have the most sig. bit set in the +* sequence number, this implies that valid sequence numbers are 1..127 +* +* This protocol uses the concept of sequences numbers to determine if a given packet has been received. This +* requires both devices to be able to synchronize sequence numbers. This is accomplished by sending a packet +* length 1 and sequence number = 0. The receive then resets it's transmit sequence number to 1. +* +* ACTIVE_SYNCH is a version that will automatically send a synch request if it receives a synch packet. Only +* one device in the communication should do otherwise you end up with an endless loops of synchronization. +* Right now each side needs to manually issues a synch request. +* +* This protocol is best used in cases where one device is the master and the other is the slave, or a don't +* speak unless spoken to type of approach. +* +* The following are items are required to initialize a port for communications: +* 1. The number attempts for each packet +* 2. time to wait for an ack. +* 3. pointer to buffer to be used for receiving. +* 4. pointer to a buffer to be used for transmission +* 5. length of each buffer (rx and tx) +* 6. Four functions: +* 1. write byte = writes a byte out the serial port (or other comm device) +* 2. read byte = retrieves a byte from the serial port. Returns -1 if a byte is not available +* 3. callback = function to call when a valid data packet has been received. This function is responsible +* to do what needs to be done with the data when it is received. The primary mission of this function +* should be to copy the data to a private buffer out of the working receive buffer to prevent overrun. +* processing should be kept to a minimum. +* 4. get time = function should return the current time. Note that time units are not specified it just +* needs to be some measure of time that increments as time passes by. The timeout values for a given +* port should the units used/returned by the get time function. +* +* All of the state information of a communication port is contained in a Port_t structure. This allows this +* module to operature on multiple communication ports with a single code base. +* +* The ssp_ReceiveProcess and ssp_SendProcess functions need to be called to process data through the +* respective state machines. Typical implementation would have a serial ISR to pull bytes out of the UART +* and place into a circular buffer. The serial read function would then pull bytes out this buffer +* processing. The TX side has the write function placing bytes into a circular buffer with the TX ISR +* pulling bytes out of the buffer and putting into the UART. It is possible to run the receive process from +* the receive ISR but care must be taken on processing data when it is received to avoid holding up the ISR +* and sending ACK packets from the receive ISR. +* +***********************************************************************************************************/ + +/** INCLUDE FILES **/ + +#include +#include +#include + +#include "ssp.h" + +/** PRIVATE DEFINITIONS **/ +#define SYNC 225 // Sync character used in Serial Protocol +#define ESC 224 // ESC character used in Serial Protocol +#define ESC_SYNC 1 // ESC_SYNC character used in Serial Protocol +#define ACK_BIT 0x80 // Ack bit, bit 7 of sequence number, 1 = Acknowledge, 0 = + // new packet +// packet location definitions. +#define LENGTH 0 +#define SEQNUM 1 +#define DATA 2 + + +// Make larger sized integers from smaller sized integers +#define MAKEWORD16( ub, lb ) ((uint16_t)0x0000 | ((uint16_t)(ub) << 8) | (uint16_t)(lb) ) +#define MAKEWORD32( uw, lw ) ((uint32_t)(0x0UL | ((uint32_t)(uw) << 16) | (uint32_t)(lw)) ) +#define MAKEWORD32B( b3, b2, b1, b0 ) ((uint32_t)((uint32_t)(b3)<< 24) | ((uint32_t)(b2)<<16) | ((uint32_t)(b1)<<8) | ((uint32_t)(b0) ) + + +// Used to extract smaller integers from larger sized intergers +#define LOWERBYTE( w ) (uint8_t)((w) & 0x00ff ) +#define UPPERBYTE( w ) (uint8_t)(((w) & 0xff00) >> 8 ) +#define UPPERWORD(lw) (uint16_t)(((lw) & 0xffff0000) >> 16 ) +#define LOWERWORD(lw) (uint16_t)((lw) & 0x0000ffff) + +// Macros to operate on a target and bitmask. +#define CLEARBIT( a, b ) ((a) = (a) & ~(b)) +#define SETBIT( a, b ) ((a) = (a) | (b) ) +#define TOGGLEBIT(a,b) ((a) = (a) ^ (b) ) + +// test bit macros operate using a bit mask. +#define ISBITSET( a, b ) ( ((a) & (b)) == (b) ? TRUE : FALSE ) +#define ISBITCLEAR( a, b) ( (~(a) & (b)) == (b) ? TRUE : FALSE ) + +/** PRIVATE FUNCTIONS **/ +//static void sf_SendSynchPacket( Port_t *thisport ); +static uint16_t sf_crc16( uint16_t crc, uint8_t data ); +static void sf_write_byte( Port_t *thisport, uint8_t c ); +static void sf_SetSendTimeout( Port_t *thisport ); +static uint16_t sf_CheckTimeout( Port_t *thisport ); +static int16_t sf_DecodeState( Port_t *thisport, uint8_t c ); +static int16_t sf_ReceiveState( Port_t *thisport, uint8_t c ); + +static void sf_SendPacket( Port_t *thisport ); +static void sf_SendAckPacket( Port_t *thisport, uint8_t seqNumber); +static void sf_MakePacket( uint8_t *buf, const uint8_t * pdata, uint16_t length, uint8_t seqNo ); +static int16_t sf_ReceivePacket(Port_t *thisport); + +/* Flag bit masks...*/ +#define SENT_SYNCH (0x01) +#define ACK_RECEIVED (0x02) +#define ACK_EXPECTED (0x04) + +#define SSP_AWAITING_ACK 0 +#define SSP_ACKED 1 +#define SSP_IDLE 2 + +/** PRIVATE DATA **/ +static const uint16_t CRC_TABLE[] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 + }; + +/** EXTERNAL DATA **/ + +/** EXTERNAL FUNCTIONS **/ + +/** VERIFICATION FUNCTIONS **/ + + +/***********************************************************************************************************/ + +/*! + * \brief Initializes the communication port for use + * \param thisport = pointer to port structure to initialize + * \param info = config struct with default values. + * \return None. + * + * \note + * Must be called before calling the Send or REceive process functions. + */ + +void ssp_Init( Port_t *thisport, const PortConfig_t* const info) +{ + thisport->pfCallBack = info->pfCallBack; + thisport->pfSerialRead = info->pfSerialRead; + thisport->pfSerialWrite = info->pfSerialWrite; + thisport->pfGetTime = info->pfGetTime; + + thisport->maxRetryCount = info->max_retry; + thisport->timeoutLen = info->timeoutLen; + thisport->txBufSize = info->txBufSize; + thisport->rxBufSize = info->rxBufSize; + thisport->txBuf = info->txBuf; + thisport->rxBuf = info->rxBuf; + thisport->retryCount = 0; + thisport->sendSynch = FALSE; //TRUE; + thisport->rxSeqNo = 255; + thisport->txSeqNo = 255; + thisport->SendState = SSP_IDLE; +} + +/*! + * \brief Runs the send process, checks for receipt of ack, timeouts and resends if needed. + * \param thisport = which port to use + * \return SSP_TX_WAITING - waiting for a valid ACK to arrive + * \return SSP_TX_TIMEOUT - failed to receive a valid ACK in the timeout period, after retrying. + * \return SSP_TX_IDLE - not expecting a ACK packet (no current transmissions in progress) + * \return SSP_TX_ACKED - valid ACK received before timeout period. + * + * \note + * + */ +int16_t ssp_SendProcess( Port_t *thisport ) +{ + int16_t value = SSP_TX_WAITING; + + if (thisport->SendState == SSP_AWAITING_ACK ) { + if (sf_CheckTimeout(thisport) == TRUE) { + if (thisport->retryCount < thisport->maxRetryCount) { + // Try again + sf_SendPacket(thisport); + sf_SetSendTimeout(thisport); + value = SSP_TX_WAITING; + } else { + // Give up, # of trys has exceded the limit + value = SSP_TX_TIMEOUT; + CLEARBIT( thisport->flags, ACK_RECEIVED); + thisport->SendState = SSP_IDLE; + } + } else { + value = SSP_TX_WAITING; + } + } else if( thisport->SendState == SSP_ACKED ) { + SETBIT( thisport->flags, ACK_RECEIVED); + value = SSP_TX_ACKED; + thisport->SendState = SSP_IDLE; + } else { + thisport->SendState = SSP_IDLE; + value = SSP_TX_IDLE; + } + return value; +} + +/*! + * \brief Runs the receive process. fetches a byte at a time and runs the byte through the protocol receive state machine. + * \param thisport - which port to use. + * \return receive status. + * + * \note + * + */ +int16_t ssp_ReceiveProcess(Port_t *thisport) +{ + int16_t b; + int16_t packet_status = SSP_RX_IDLE; + + do { + b = thisport->pfSerialRead(); // attempt to read a char from the serial buffer + if (b != -1) { + packet_status = sf_ReceiveState(thisport, b); // process the newly received byte in the receive state machine + } + // keep going until either we received a full packet or there are no more bytes to process + } while (packet_status != SSP_RX_COMPLETE && b != -1); + return packet_status; +} + +/*! + * \brief processes a single byte through the receive state machine. + * \param thisport = which port to use + * \return current receive status + * + * \note + * + */ + +int16_t ssp_ReceiveByte(Port_t *thisport ) +{ + int16_t b; + int16_t packet_status = SSP_RX_IDLE; + + b = thisport->pfSerialRead(); + if( b != -1 ) { + packet_status = sf_ReceiveState(thisport, b); + } + return packet_status; +} + +/*! + * \brief Sends a data packet and blocks until timeout or ack is received. + * \param thisport = which port to use + * \param data = pointer to data to send + * \param length = number of data bytes to send. Must be less than 254 + * \return true = ack was received within number of retries + * \return false = ack was not received. + * + * \note + * + */ +uint16_t ssp_SendDataBlock( Port_t *thisport, uint8_t *data, uint16_t length ) +{ + int16_t packet_status = SSP_TX_WAITING; + uint16_t retval = FALSE; + + packet_status = ssp_SendData( thisport, data, length ); // send the data + while( packet_status == SSP_TX_WAITING ) { // check the status + (void)ssp_ReceiveProcess( thisport ); // process any bytes received. + packet_status = ssp_SendProcess( thisport ); // check the send status + } + if( packet_status == SSP_TX_ACKED ) { // figure out what happened to the packet + retval = TRUE; + } else { + retval = FALSE; + } + return retval; +} + +/*! + * \brief sends a chunk of data and does not block + * \param thisport = which port to use + * \param data = pointer to data to send + * \param length = number of bytes to send + * \return SSP_TX_BUFOVERRUN = tried to send too much data + * \return SSP_TX_WAITING = data sent and waiting for an ack to arrive + * \return SSP_TX_BUSY = a packet has already been sent, but not yet acked + * + * \note + * + */ +int16_t ssp_SendData( Port_t *thisport, const uint8_t *data, const uint16_t length ) +{ + + int16_t value = SSP_TX_WAITING; + + if( (length + 2) > thisport->txBufSize ) { + // TRYING to send too much data. + value = SSP_TX_BUFOVERRUN; + } else if( thisport->SendState == SSP_IDLE ) { +#ifdef ACTIVE_SYNCH + if( thisport->sendSynch == TRUE ) { + sf_SendSynchPacket(thisport); + } +#endif + +#ifdef SYNCH_SEND + if( length == 0 ) { + // TODO this method could allow a task/user to start a synchronisation step if a zero is mistakenly passed to this function. + // could add a check for a NULL data pointer, or use some sort of static flag that can only be accessed by a static function + // that must be called before calling this function. + // we are attempting to send a synch packet + thisport->txSeqNo = 0; // make this zero to cause the other end to re-synch with us + SETBIT(thisport->flags, SENT_SYNCH); + } else { + // we are sending a data packet + CLEARBIT( thisport->txSeqNo, ACK_BIT ); // make sure we are not sending a ACK packet + thisport->txSeqNo++; // update the sequence number. + if( thisport->txSeqNo > 0x7F) { // check for sequence number rollover + thisport->txSeqNo = 1; // if we do have rollover then reset to 1 not zero, + // zero is reserviced for synchronization requests + } + } + +#else + CLEARBIT( thisport->txSeqNo, ACK_BIT ); // make sure we are not sending a ACK packet + thisport->txSeqNo++; // update the sequence number. + if( thisport->txSeqNo > 0x7F) { // check for sequence number rollover + thisport->txSeqNo = 1; // if we do have rollover then reset to 1 not zero, + // zero is reserved for synchronization requests + } +#endif + CLEARBIT( thisport->flags, ACK_RECEIVED); + thisport->SendState = SSP_AWAITING_ACK; + value = SSP_TX_WAITING; + thisport->retryCount = 0; // zero out the retry counter for this transmission + sf_MakePacket( thisport->txBuf, data, length, thisport->txSeqNo ); + sf_SendPacket( thisport ); // punch out the packet to the serial port + sf_SetSendTimeout( thisport ); // do the timeout values + } else { + // error we are already sending a packet. Need to wait for the current packet to be acked or timeout. + value = SSP_TX_BUSY; + } + return value; +} + +/*! + * \brief Attempts to synchronize the sequence numbers with the other end of the connectin. + * \param thisport = which port to use + * \return true = success + * \return false = failed to receive an ACK to our synch request + * + * \note + * A. send a packet with a sequence number equal to zero + * B. if timed out then: + * send synch packet again + * increment try counter + * if number of tries exceed maximum try limit then exit + * C. goto A + */ +uint16_t ssp_Synchronise( Port_t *thisport ) +{ + int16_t packet_status; + uint16_t retval = FALSE; + +#ifndef USE_SENDPACKET_DATA + thisport->txSeqNo = 0; // make this zero to cause the other end to re-synch with us + SETBIT(thisport->flags, SENT_SYNCH); + // TODO - should this be using ssp_SendPacketData()?? + sf_MakePacket( thisport->txBuf, NULL, 0, thisport->txSeqNo ); // construct the packet + sf_SendPacket( thisport ); + sf_SetSendTimeout( thisport ); + thisport->SendState = SSP_AWAITING_ACK; + packet_status = SSP_TX_WAITING; +#else + packet_status = ssp_SendData( thisport, NULL, 0 ); +#endif + while( packet_status == SSP_TX_WAITING ) { // we loop until we time out. + (void)ssp_ReceiveProcess( thisport ); // do the receive process + packet_status = ssp_SendProcess( thisport ); // do the send process + } + thisport->sendSynch = FALSE; + switch( packet_status ) { + case SSP_TX_ACKED: + retval = TRUE; + break; + case SSP_TX_BUSY: // intentional fall through. + case SSP_TX_TIMEOUT: // intentional fall through. + case SSP_TX_BUFOVERRUN: + retval = FALSE; + break; + default: + retval = FALSE; + break; + }; + return retval; +} + + +/*! + * \brief sends out a preformatted packet for a give port + * \param thisport = which port to use. + * \return none. + * + * \note + * Packet should be formed through the use of sf_MakePacket before calling this function. + */ +static void sf_SendPacket(Port_t *thisport) + { + // add 3 to packet data length for: 1 length + 2 CRC (packet overhead) + uint8_t packetLen = thisport->txBuf[LENGTH] + 3; + + // use the raw serial write function so the SYNC byte does not get 'escaped' + thisport->pfSerialWrite(SYNC); + for( uint8_t x = 0; x < packetLen; x++ ) { + sf_write_byte(thisport, thisport->txBuf[x] ); + } + thisport->retryCount++; +} + + +/*! + * \brief converts data to transport layer protocol packet format. + * \param txbuf = buffer to use when forming the packet + * \param pdata = pointer to data to use + * \param length = number of bytes to use + * \param seqNo = sequence number of this packet + * \return none. + * + * \note + * 1. This function does not try to interpret ACK or SYNCH packets. This should + * be done by the caller of this function. + * 2. This function will attempt to format all data upto the size of the tx buffer. + * Any extra data beyond that will be ignored. + * 3. TODO: Should this function return an error if data length to be sent is greater th tx buffer size? + * + */ +void sf_MakePacket( uint8_t *txBuf, const uint8_t * pdata, uint16_t length, uint8_t seqNo ) +{ + uint16_t crc = 0xffff; + uint16_t bufPos = 0; + uint8_t b; + + // add 1 for the seq. number + txBuf[LENGTH] = length + 1; + txBuf[SEQNUM] = seqNo; + crc = sf_crc16( crc, seqNo ); + + length = length + 2; // add two for the length and seqno bytes which are added before the loop. + for( bufPos = 2; bufPos < length; bufPos++ ) { + b = *pdata++; + txBuf[bufPos] = b; + crc = sf_crc16( crc, b ); // update CRC value + } + txBuf[bufPos++] = LOWERBYTE(crc); + txBuf[bufPos] = UPPERBYTE(crc); + +} + +/*! + * \brief sends out an ack packet to given sequence number + * \param thisport = which port to use + * \param seqNumber = sequence number of the packet we would like to ack + * \return none. + * + * \note + * + */ + +static void sf_SendAckPacket( Port_t *thisport, uint8_t seqNumber) +{ + uint8_t AckSeqNumber = SETBIT( seqNumber, ACK_BIT ); + + // create the packet, note we pass AckSequenceNumber directly + sf_MakePacket( thisport->txBuf, NULL, 0, AckSeqNumber ); + sf_SendPacket( thisport ); + // we don't set the timeout for an ACK because we don't ACK our ACKs in this protocol +} + +/*! + * \brief writes a byte out the output channel. Adds escape byte where needed + * \param thisport = which port to use + * \param c = byte to send + * \return none. + * + * \note + * + */ +static void sf_write_byte( Port_t *thisport, uint8_t c ) +{ + if( c == SYNC ) { // check for SYNC byte + thisport->pfSerialWrite( ESC ); // since we are not starting a packet we must ESCAPE the SYNCH byte + thisport->pfSerialWrite( ESC_SYNC ); // now send the escaped synch char + } else if( c == ESC ) { // Check for ESC character + thisport->pfSerialWrite( ESC ); // if it is, we need to send it twice + thisport->pfSerialWrite( ESC ); + } else { + thisport->pfSerialWrite( c ); // otherwise write the byte to serial port + } +} + +/************************************************************************************************************ +* +* NAME: uint16_t ssp_crc16( uint16_t crc, uint16_t data ) +* DESCRIPTION: Uses crc_table to calculate new crc +* ARGUMENTS: +* arg1: crc +* arg2: data - byte to calculate into CRC +* RETURN: New crc +* CREATED: 5/8/02 +* +*************************************************************************************************************/ +/*! + * \brief calculates the new CRC value for 'data' + * \param crc = current CRC value + * \param data = new byte + * \return updated CRC value + * + * \note + * + */ + +static uint16_t sf_crc16( uint16_t crc, uint8_t data ) +{ + return (crc >> 8) ^ CRC_TABLE[( crc ^ data ) & 0x00FF ]; +} + + +/*! + * \brief sets the timeout for the given packet + * \param thisport = which port to use + * \return none. + * + * \note + * + */ + +static void sf_SetSendTimeout( Port_t *thisport ) +{ + uint32_t timeout; + timeout = thisport->pfGetTime() + thisport->timeoutLen; + thisport->timeout = timeout; +} + +/*! + * \brief checks to see if a timeout occured + * \param thisport = which port to use + * \return true = a timeout has occurred + * \return false = has not timed out + * + * \note + * + */ +static uint16_t sf_CheckTimeout( Port_t *thisport ) +{ + uint16_t retval = FALSE; + uint32_t current_time; + + current_time = thisport->pfGetTime(); + if( current_time > thisport->timeout ) { + retval = TRUE; + } + return retval; +} + + +/**************************************************************************** + * NAME: sf_ReceiveState + * DESC: Implements the receive state handling code for escaped and unescaped data + * ARGS: thisport - which port to operate on + * c - incoming byte + * RETURN: + * CREATED: + * NOTES: + * 1. change from using pointer to functions. + ****************************************************************************/ +/*! + * \brief implements the receive state handling code for escaped and unescaped data + * \param thisport = which port to use + * \param c = byte to process through the receive state machine + * \return receive status + * + * \note + * + */ +static int16_t sf_ReceiveState( Port_t *thisport, uint8_t c ) +{ + int16_t retval = SSP_RX_RECEIVING; + + switch( thisport->InputState ) { + case state_unescaped_e: + if( c == SYNC ) { + thisport->DecodeState = decode_len1_e; + } else if ( c == ESC ) { + thisport->InputState = state_escaped_e; + } else { + retval = sf_DecodeState( thisport, c); + } + break; // end of unescaped state. + case state_escaped_e: + thisport->InputState = state_unescaped_e; + if( c == SYNC ) { + thisport->DecodeState = decode_len1_e; + } else if (c == ESC_SYNC ) { + retval = sf_DecodeState( thisport, SYNC); + } else { + retval = sf_DecodeState( thisport, c); + } + break; // end of the escaped state. + default: + break; + } + return retval; +} + +/**************************************************************************** + * NAME: sf_DecodeState + * DESC: Implements the receive state finite state machine + * ARGS: thisport - which port to operate on + * c - incoming byte + * RETURN: + * CREATED: + * NOTES: + * 1. change from using pointer to functions. + ****************************************************************************/ + +/*! + * \brief implements the receiving decoding state machine + * \param thisport = which port to use + * \param c = byte to process + * \return receive status + * + * \note + * + */ +static int16_t sf_DecodeState( Port_t *thisport, uint8_t c ) +{ + int16_t retval; + switch( thisport->DecodeState ) { + case decode_idle_e: + // 'c' is ignored in this state as the only way to leave the idle state is + // recognition of the SYNC byte in the sf_ReceiveState function. + retval = SSP_RX_IDLE; + break; + case decode_len1_e: + thisport->rxBuf[LENGTH]= c; + thisport->rxBufLen = c; + if( thisport->rxBufLen <= thisport->rxBufSize ) { + thisport->DecodeState = decode_seqNo_e; + retval = SSP_RX_RECEIVING; + } else { + thisport->DecodeState = decode_idle_e; + retval = SSP_RX_IDLE; + } + break; + case decode_seqNo_e: + thisport->rxBuf[SEQNUM] = c; + thisport->crc = 0xffff; + thisport->rxBufLen--; // subtract 1 for the seq. no. + thisport->rxBufPos = 2; + + thisport->crc = sf_crc16( thisport->crc, c ); + if( thisport->rxBufLen > 0 ) { + thisport->DecodeState = decode_data_e; + } else { + thisport->DecodeState = decode_crc1_e; + } + retval = SSP_RX_RECEIVING; + break; + case decode_data_e: + thisport->rxBuf[ (thisport->rxBufPos)++] = c; + thisport->crc = sf_crc16( thisport->crc, c ); + if( thisport->rxBufPos == (thisport->rxBufLen+2) ) { + thisport->DecodeState = decode_crc1_e; + } + retval = SSP_RX_RECEIVING; + break; + case decode_crc1_e: + thisport->crc = sf_crc16( thisport->crc, c ); + thisport->DecodeState = decode_crc2_e; + retval = SSP_RX_RECEIVING; + break; + case decode_crc2_e: + thisport->DecodeState = decode_idle_e; + // verify the CRC value for the packet + if( sf_crc16( thisport->crc, c) == 0) { + // TODO shouldn't the return value of sf_ReceivePacket() be checked? + sf_ReceivePacket( thisport ); + retval = SSP_RX_COMPLETE; + } else { + thisport->RxError++; + retval = SSP_RX_IDLE; + } + break; + default: + thisport->DecodeState = decode_idle_e; // unknown state so reset to idle state and wait for the next start of a packet. + retval = SSP_RX_IDLE; + break; + } + return retval; +} + +/************************************************************************************************************ +* +* NAME: int16_t sf_ReceivePacket( ) +* DESCRIPTION: Receive one packet, assumed that data is in rec.buff[] +* ARGUMENTS: +* RETURN: 0 . no new packet was received, could be ack or same packet +* 1 . new packet received +* SSP_PACKET_? +* SSP_PACKET_COMPLETE +* SSP_PACKET_ACK +* CREATED: 5/8/02 +* +*************************************************************************************************************/ +/*! + * \brief receive one packet. calls the callback function if needed. + * \param thisport = which port to use + * \return true = valid data packet received. + * \return false = otherwise + * + * \note + * + * Created: Oct 7, 2010 12:07:22 AM by joe + */ + +static int16_t sf_ReceivePacket(Port_t *thisport) +{ + int16_t value = FALSE; + + if( ISBITSET(thisport->rxBuf[SEQNUM], ACK_BIT ) ) { + // Received an ACK packet, need to check if it matches the previous sent packet + if( ( thisport->rxBuf[SEQNUM] & 0x7F) == (thisport->txSeqNo & 0x7f)) { + // It matches the last packet sent by us + SETBIT( thisport->txSeqNo, ACK_BIT ); + thisport->SendState = SSP_ACKED; + value = FALSE; + } + // else ignore the ACK packet + } else { + // Received a 'data' packet, figure out what type of packet we received... + if( thisport->rxBuf[SEQNUM] == 0 ) { + // Synchronize sequence number with host +#ifdef ACTIVE_SYNCH + thisport->sendSynch = TRUE; +#endif + sf_SendAckPacket( thisport, thisport->rxBuf[SEQNUM] ); + thisport->rxSeqNo = 0; + value = FALSE; + } else if( thisport->rxBuf[SEQNUM] == thisport->rxSeqNo ) { + // Already seen this packet, just ack it, don't act on the packet. + sf_SendAckPacket( thisport, thisport->rxBuf[SEQNUM] ); + value = FALSE; + } else { + //New Packet + thisport->rxSeqNo = thisport->rxBuf[SEQNUM]; + // Let the application do something with the data/packet. + if( thisport->pfCallBack != NULL ) { + // skip the first two bytes (length and seq. no.) in the buffer. + thisport->pfCallBack( &(thisport->rxBuf[2]), thisport->rxBufLen); + } + // after we send the ACK, it is possible for the host to send a new packet. + // Thus the application needs to copy the data and reset the receive buffer + // inside of thisport->pfCallBack() + sf_SendAckPacket( thisport, thisport->rxBuf[SEQNUM] ); + value = TRUE; + } + } + return value; +} + diff --git a/flight/Bootloaders/OpenPilot/ssp_test.c b/flight/Bootloaders/OpenPilot/ssp_test.c new file mode 100644 index 000000000..b733b5ba5 --- /dev/null +++ b/flight/Bootloaders/OpenPilot/ssp_test.c @@ -0,0 +1,229 @@ +// test functions for the SSP module. +// this module performs unit test on the SSP functions. + +#include "ssp.h" +#include "buffer.h" + +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif + +#define MAX_PACKET_DATA_LEN 255 +#define MAX_PACKET_BUF_SIZE (1+1+255+2) +// master buffers... +uint8_t masterTxBuf[MAX_PACKET_BUF_SIZE]; +uint8_t masterRxBuf[MAX_PACKET_BUF_SIZE]; + +// slave buffers... +uint8_t slaveTxBuf[MAX_PACKET_BUF_SIZE]; +uint8_t slaveRxBuf[MAX_PACKET_BUF_SIZE]; + +void masterCallBack(uint8_t *buf, uint16_t len); +int16_t masterSerialRead(void); +void masterSerialWrite(uint8_t); +uint32_t masterGetTime(void); + +void slaveCallBack(uint8_t *buf, uint16_t len); +int16_t slaveSerialRead(void); +void slaveSerialWrite(uint8_t); +uint32_t slaveGetTime(void); + +PortConfig_t masterPortConfig = { + .rxBuf = masterRxBuf, + .rxBufSize = MAX_PACKET_DATA_LEN, + .txBuf = masterTxBuf, + .txBufSize = 255, + .max_retry = 3, + .timeoutLen = 100, + .pfCallBack = masterCallBack, + .pfSerialRead = masterSerialRead, + .pfSerialWrite = masterSerialWrite, + .pfGetTime = masterGetTime, +}; +PortConfig_t slavePortConfig = { + .rxBuf = slaveRxBuf, + .rxBufSize = MAX_PACKET_DATA_LEN, + .txBuf = slaveTxBuf, + .txBufSize = 255, + .max_retry = 3, + .timeoutLen = 100, + .pfCallBack = slaveCallBack, + .pfSerialRead = slaveSerialRead, + .pfSerialWrite = slaveSerialWrite, + .pfGetTime = slaveGetTime, +}; + +Port_t master_port; +Port_t slave_port; + +cBuffer m2sBuffer; +cBuffer s2mBuffer; + +#define BUFFER 1024 + +// buffer space for the simulated serial buffers. +uint8_t m2sDataBuffer[BUFFER]; +uint8_t s2mDataBuffer[BUFFER]; + +void ssp_test(void) +{ + uint8_t masterSendBuf[255]; +// uint8_t slaveSendBuf[255]; + Port_t *master = &master_port; + Port_t *slave = &slave_port; + int16_t packet_status; + int16_t retval; + + uint8_t master_respond = TRUE; + uint8_t slave_respond = TRUE; + uint8_t master_send_respond = TRUE; + + bufferInit(&m2sBuffer, m2sDataBuffer, BUFFER); + bufferInit(&s2mBuffer, s2mDataBuffer, BUFFER); + + ssp_Init( master, &masterPortConfig); + ssp_Init( slave, &slavePortConfig); + + masterSendBuf[0] = 0; + masterSendBuf[1] = 1; + masterSendBuf[2] = 2; + masterSendBuf[3] = 3; + masterSendBuf[4] = 4; + + ssp_Synchronise(master); + while (1) { + packet_status = ssp_SendData( master, masterSendBuf, 5 ); // send the data + while( packet_status == SSP_TX_WAITING ) { // check the status + if( slave_respond == TRUE ) { + (void)ssp_ReceiveProcess(slave); // process simulated input to the slave + } + if( master_respond == TRUE ) { + (void)ssp_ReceiveProcess( master ); // process any bytes received. + } + if( master_send_respond == TRUE ) { + packet_status = ssp_SendProcess( master );// check the packet + } + } + if (packet_status == SSP_TX_ACKED ) { + retval = TRUE; + } else { + // figure out what happened to the packet + // possible errors are: timeout, busy, bufoverrun (tried to send too much data. + retval = FALSE; + } + // just a more explicit way to see what happened... + switch( packet_status ) { + case SSP_TX_ACKED: + // quick data manipulation to see something different... + for (int32_t x = 0; x < 5; ++x) { + masterSendBuf[x] += 5; + } + retval = TRUE; + break; + case SSP_TX_BUSY: + retval = FALSE; + break; + case SSP_TX_TIMEOUT: + retval = FALSE; + break; + case SSP_TX_BUFOVERRUN: + retval = false; + break; + default: + retval = -3; + break; + } +#ifdef OLD_CODE + do { + packetStatus = ssp_SendPacketData( master, masterSendBuf, 5); + if( packetStatus == SSP_TX_FAIL) { + ssp_ReceiveProcess(slave); + ssp_ReceiveProcess(master); + ssp_SendProcess(master); + } + } while( packetStatus != SSP_TX_WAITING ); + + do { + // let the slave process simulated input. + ssp_ReceiveProcess(slave); + // process simulated input from the slave to master. Slave 'may' have sent an ACK + if( ssp_ReceiveProcess(master) == SSP_RX_COMPLETE) { + // at this point an ACK or 'data' packet was received. + + } + packetStatus = ssp_SendProcess(master); + } while ( packetStatus == SSP_TX_WAITING); +#endif + + } +} + + +// these functions implement a simulated serial in/out for both a master +// and a slave device. In reality these functions do not send anything out +// but just puts them into a circular buffer. +// In a real system these would use the PIOS_COM_xxxx functions. + +void masterCallBack(uint8_t *buf, uint16_t len) +{ + len = len; +} + +// simulates checking for character from a serial buffer. + +int16_t masterSerialRead(void) +{ + int16_t retval = -1; + static uint16_t count = 0; + + if( bufferBufferedData(&s2mBuffer)) { + retval = bufferGetFromFront( &s2mBuffer); + } + count++; + if( count % 5 == 0 ) { + ssp_ReceiveByte(&slave_port); + } + return retval; +} + +void masterSerialWrite(uint8_t b) +{ + bufferAddToEnd( &m2sBuffer, b); +} + +uint32_t masterTime = 0; +uint32_t slaveTime = 0; + +uint32_t masterGetTime(void) +{ + masterTime++; + return masterTime; +} + +void slaveCallBack(uint8_t *buf, uint16_t len) +{ + len = len; +} + +int16_t slaveSerialRead(void) +{ + int16_t retval = -1; + if( bufferBufferedData(&m2sBuffer)) { + retval = bufferGetFromFront( &m2sBuffer); + } + return retval; +} + +void slaveSerialWrite(uint8_t b) +{ + bufferAddToEnd( &s2mBuffer, b); +} + +uint32_t slaveGetTime(void) +{ + slaveTime++; + return slaveTime; +}