Page MenuHomec4science

sbcp_slave.c
No OneTemporary

File Metadata

Created
Wed, Nov 13, 22:01

sbcp_slave.c

/*
sbcp.c
Copyright (C) 2010 Rico Moeckel <rico.moeckel at epfl dot ch>,
EPFL Biorobotics Laboratory (http://biorob.epfl.ch)
EPFL Ecole Polytechnique Federale de Lausanne (http://www.epfl.ch)
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, see <http://www.gnu.org/licenses/>.
*/
/**
\defgroup sbcp SBCP
\brief Simple Binary Communication Protocol.
This file contains the implements the Simple Binary Communication
Protocol (Slave device) - a communication protocol for a single
master - multiple slave, half-duplex communication bus.
\author Rico Moeckel
\version 1.1
\date 2011-04-28
*/
/*@{*/
/** \file sbcp.c
\brief Simple Binary Communication Protocol.
*/
#include "sbcp.h"
#include <p33Fxxxx.h>
#include "sbcp.h"
#include "timer.h"
#include "command.h"
#include "config.h"
#include "dma_sbcp.h"
#include "port.h"
#include <string.h>
/*************************************************************************
*
* VARIABLES
*
*************************************************************************/
extern const tSBCP_CmdTbl sbcp_cmd_tbl[];
extern const int SBCP_COMMAND_TABLE_LENGTH;
//static tSBCP_AppVar SbcpVar;
volatile tSBCP_AppVar sbcp; ///< Container struct storing the local SBCP variables.
/*******************************************************************************
*
* INLINE FUNCTION !!!!
*
******************************************************************************/
/**
* COmputes the checksum of a given packet
* @param packet the pointer to the packet buffer
* @return the checksum of the packet
*/
inline unsigned char sbcp_compute_checksum_of_packet(const unsigned int * packet){
unsigned char res=SBCP_MY_CLASS + SBCP_MY_ID;
unsigned int i;
res += packet[SBCP_POS_PAYLOAD_LENGTH];
res += packet[SBCP_POS_INSTRUCTION];
unsigned int size = (packet[SBCP_POS_PAYLOAD_LENGTH] + 5) & DMA_SBCP_MAX_PACKET_SIZE_MASK;
for( i = SBCP_POS_PAYLOAD_START;
i < size;
++i){
res += packet[i];
}
return res;
}
/*************************************************************************
*
* FUNCTIONS
*
*************************************************************************/
/** \brief Function for initializing SBCP module.
This function initializes the SBCP module. It should be
called once in the main initialization function before executing
the sbcp_main() outine.
\param no parameters
\return no returns
*/
void sbcp_init() {
DMA_PACKET_QUEUE_SET_EMPTY(sbcp.bus_ready_packet_queue);
sbcp.bus_tx_flags = SBCP_TX_BUFFER_FREE;
sbcp.bus_low_latency_flags = SBCP_LL_NOTHING;
sbcp.bus_low_latency_payload[0] = 0x00;
sbcp.bus_low_latency_payload[1] = 0x00;
sbcp.bus_low_latency_payload[2] = 0x00;
sbcp.bus_low_latency_payload[3] = 0x00;
sbcp.bus_low_latency_previous_error_code = SBCP_STANDARD_ACKNOWLEDGEMENT;
PORT_RS485_DRIVER_DISABLE;
PORT_RS485_RECEIVER_ENABLE;
DMA_PACKET_QUEUE_BUS_TX_BUFFER_CPU[SBCP_POS_HEADER] = SBCP_HEADER;
DMA_PACKET_QUEUE_BUS_TX_BUFFER_CPU[SBCP_POS_CLASS] = SBCP_MY_CLASS;
DMA_PACKET_QUEUE_BUS_TX_BUFFER_CPU[SBCP_POS_ID] = SBCP_MY_ID;
DMA_PACKET_QUEUE_BUS_LL_TX_BUFFER_CPU[SBCP_POS_HEADER] = SBCP_HEADER;
DMA_PACKET_QUEUE_BUS_LL_TX_BUFFER_CPU[SBCP_POS_CLASS] = SBCP_MY_CLASS;
DMA_PACKET_QUEUE_BUS_LL_TX_BUFFER_CPU[SBCP_POS_ID] = SBCP_MY_ID;
DMA_PACKET_QUEUE_BUS_LL_TX_BUFFER_CPU[SBCP_POS_PAYLOAD_LENGTH] =
SBCP_LOW_LATENCY_INSTRUCTION_FEEDBACK_PAYLOAD_SIZE;
DMA_PACKET_QUEUE_BUS_LL_TX_BUFFER_CPU[SBCP_POS_INSTRUCTION] =
SBCP_STANDARD_ACKNOWLEDGEMENT;
DMA_PACKET_QUEUE_BUS_LL_ERROR_TX_BUFFER_CPU[SBCP_POS_HEADER] = SBCP_HEADER;
DMA_PACKET_QUEUE_BUS_LL_ERROR_TX_BUFFER_CPU[SBCP_POS_CLASS] = SBCP_MY_CLASS;
DMA_PACKET_QUEUE_BUS_LL_ERROR_TX_BUFFER_CPU[SBCP_POS_ID] = SBCP_MY_ID;
DMA_PACKET_QUEUE_BUS_LL_ERROR_TX_BUFFER_CPU[SBCP_POS_PAYLOAD_LENGTH] = 0x01;
DMA_PACKET_QUEUE_BUS_LL_ERROR_TX_BUFFER_CPU[SBCP_POS_INSTRUCTION] =
SBCP_ERROR_FLAG_COM_ERROR;
DMA_PACKET_QUEUE_BUS_LL_ERROR_TX_BUFFER_CPU[SBCP_POS_PAYLOAD_START] =
SBCP_COM_ERROR_CODE_CHECKSUM_INCORRECT;
DMA_PACKET_QUEUE_BUS_LL_ERROR_TX_BUFFER_CPU[SBCP_POS_PAYLOAD_START + 1] =
SBCP_MY_CLASS
+ SBCP_MY_ID
+ 0x01
+ SBCP_ERROR_FLAG_COM_ERROR
+ SBCP_COM_ERROR_CODE_CHECKSUM_INCORRECT;
}
/** \brief SBCP command search
Function calls function corresponding to the SBCP instruction (as being registered in table sbcp_cmd_tbl[]
in file command.c) found in the SBCP packet. This function assumes that the entire
packet is already received and the the check sum is correct.
\param buffer:
pointer to the ring buffer that contains the SBCP packet
\param packet_start:
start position of SBCP packet in ring buffer. This is not a pointer but just
an index.
\return no returns
*/
#define SBCP_SEARCH_AND_EXECUTE_INSTRUCTION(buffer) do{\
unsigned char instruction = buffer[SBCP_POS_INSTRUCTION];\
/* check if instruction is valid number - meaning if entry in command table can exist*/\
if (instruction >= SBCP_COMMAND_TABLE_LENGTH) {\
sbcp_bus_send_com_error_code(SBCP_COM_ERROR_CODE_UNKNOWN_INSTRUCTION);\
} else { \
/* sbcp_bus_send_standard_acknowlegement();*/\
sbcp_cmd_tbl[instruction].cmd_function( buffer );\
}\
}while(0)
/** \brief SBCP main function.
This function implements all SBCP state machines for packet analysation. It should
be peridically called in the main() function.
\param no parameters
\return no returns
*/
void sbcp_main()
{
//first clear overrun errors that may occurs, even if it is really, really bad
if(U1STAbits.OERR == 1 ){
U1STAbits.OERR = 0;//we lose all buffer data, but it means that DMA was
// activated while data was inside ! this is bad. To avoid this really
// bad situation, DMA0Channel should always be on, most of the time
// (could be disabled while Tx over the bus however).
// if we fail to do so, the UART will fill up, and we may loose some
// bytes. This is really, really bad since :
// - If master send a new packet very quickly we may miss our packet if
// we don't listen quickly enough.
// - If we start the recption inside a packet. We may, with bad luck
// start to see the sequence 0xff xx xx XX with XX being a relatively
// large number. Then we are screwed and may loose even more packet,
// for a long long time.
}
// Handle timeout
//right now if their is any error, we don't read the packet at all. Thus the
//master will issue a timeout. It would be nice, in case of an Frame error
//to send back an error packet
if(dma_sbcp.bus_rx_error){
//disable UART and DMA
U1MODEbits.UARTEN = 0;
U1STAbits.UTXEN = 0;
DMA0CONbits.CHEN = 0;
DMA0STA = DMA_PACKET_QUEUE_BUS_GET_DMA(dma_sbcp.bus_rx_queue.tail);
DMA0CNT = 0;
dma_sbcp.bus_rx_state = DMA_WAIT_FOR_HEADER_BYTE;
DMA0CONbits.CHEN = 1;
U1MODEbits.UARTEN = 1;
U1STAbits.UTXEN = 1 ;
dma_sbcp.bus_rx_error = 0;
}
if(!DMA_PACKET_QUEUE_EMPTY(sbcp.bus_ready_packet_queue)) {
unsigned int * packet = DMA_PACKET_QUEUE_BUS_GET_CPU(sbcp.bus_ready_packet_queue.head);
if (packet[SBCP_POS_ID] == SBCP_MY_ID &&
packet[SBCP_POS_CLASS] == SBCP_MY_CLASS) {
unsigned char csPos =
(packet[SBCP_POS_PAYLOAD_LENGTH] + SBCP_POS_PAYLOAD_START) & DMA_SBCP_MAX_PACKET_SIZE_MASK;
unsigned char correctCS = SBCP_MY_ID + SBCP_MY_CLASS;
correctCS += packet[SBCP_POS_PAYLOAD_LENGTH];
correctCS += packet[SBCP_POS_INSTRUCTION];
unsigned int size = (packet[SBCP_POS_PAYLOAD_LENGTH] + 5) & DMA_SBCP_MAX_PACKET_SIZE_MASK;
unsigned int i = SBCP_POS_PAYLOAD_START;
for(; i < size; ++i){
correctCS += packet[i];
}
if(packet[csPos] == correctCS){
SBCP_SEARCH_AND_EXECUTE_INSTRUCTION(packet);
} else {
sbcp_bus_send_com_error_code(SBCP_COM_ERROR_CODE_CHECKSUM_INCORRECT);
}
} else if(DMA0CONbits.CHEN == 0){
DMA_INIT_BUS_RX;
}
DMA_PACKET_QUEUE_INCREMENT_HEAD(sbcp.bus_ready_packet_queue);
DMA_PACKET_QUEUE_INCREMENT_HEAD(dma_sbcp.bus_rx_queue);
}
if (sbcp.bus_tx_flags == SBCP_TX_BUFFER_OCCUPIED) { //FLAGS IN_TX not set !
DMA1STA = DMA_PACKET_QUEUE_BUS_TX_BUFFER_DMA;
DMA1CNT = ((DMA_PACKET_QUEUE_BUS_TX_BUFFER_CPU[SBCP_POS_PAYLOAD_LENGTH] + 5 ) & DMA_SBCP_MAX_PACKET_SIZE_MASK );
PORT_RS485_RECEIVER_DISABLE;
PORT_RS485_DRIVER_ENABLE;
DMA1CONbits.CHEN = 1;
DMA1REQbits.FORCE = 1;
sbcp.bus_tx_flags |= SBCP_TX_BUFFER_IN_TX;//mark it to not send it lot of time !!!!
}
if(dma_sbcp.bus_rx_state == DMA_OVERFLOW_PENDING ){
if(!DMA_PACKET_QUEUE_FULL(dma_sbcp.bus_rx_queue)){
DMA_INIT_BUS_RX;
}
}
// Transfering packets from BUS TX ring buffer to BUS UART output
// Only transmit data to UART buffer if UART buffer is empty
// and ring buffer is not empty. Do not waste time with busy waiting.
// for UART that is connected to RS485
// check if we wait for a new packet
}
/** \brief Function for transmitting a messages via the communication bus.
This function sends a message via the communication bus by copying it into
the correct output ring buffer and taking control over the communication bus.
\param message:
pointer to the linear message buffer that contains the SBCP packet that
should be transmitted
\param message_length:
length of the message that should be transmitted.
\return success/error flag
*/
/** \brief Function for generation of standard acknowledgement messages.
This function generates a standard acknowledgement message
and sends it via the communication bus.
\param no parameters
\return no returns
*/
void sbcp_bus_send_standard_acknowlegement()
{
unsigned int * message = DMA_PACKET_QUEUE_BUS_TX_BUFFER_CPU;
sbcp.bus_tx_flags = SBCP_TX_BUFFER_OCCUPIED;
message[SBCP_POS_PAYLOAD_LENGTH] = 0x00;
message[SBCP_POS_INSTRUCTION] = SBCP_STANDARD_ACKNOWLEDGEMENT;
message[SBCP_POS_PAYLOAD_START] = SBCP_MY_CLASS + SBCP_MY_ID + SBCP_STANDARD_ACKNOWLEDGEMENT;
}
/** \brief Function for generation of error messages.
This function generates a standard error message according to the
given error code and sends it via the communication bus.
\param error_code:
Error code to be transmitted.
\return no returns
*/
void sbcp_bus_send_com_error_code(unsigned char error_code) {
unsigned int * message = DMA_PACKET_QUEUE_BUS_TX_BUFFER_CPU;
sbcp.bus_tx_flags = SBCP_TX_BUFFER_OCCUPIED;
message[SBCP_POS_PAYLOAD_LENGTH] = 0x01;
message[SBCP_POS_INSTRUCTION] = SBCP_ERROR_FLAG_COM_ERROR;
message[SBCP_POS_PAYLOAD_START] = error_code;
message[SBCP_POS_PAYLOAD_START + 1] = SBCP_MY_CLASS + SBCP_MY_ID + 1 + SBCP_ERROR_FLAG_COM_ERROR + error_code;
}

Event Timeline