Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F102522000
sbcp_master.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, Feb 21, 14:56
Size
18 KB
Mime Type
text/x-c
Expires
Sun, Feb 23, 14:56 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
24340580
Attached To
R6617 Oncilla SBCP Master Firmware
sbcp_master.c
View Options
/*
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 (Master 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 "config.h"
#include "port.h"
#include "dma_sbcp.h"
#include "timer.h"
#include "led.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=0x00;
unsigned int i;
unsigned int size = (packet[SBCP_POS_PAYLOAD_LENGTH] + 5) & DMA_SBCP_MAX_PACKET_SIZE_MASK;
for( i = SBCP_POS_CLASS;
i <size;
++i){
res += packet[i];
}
return res;
}
/**
* Mark the correct checksum flags for a finished packet
*/
#define SBCP_CHECK_CHECKSUM(bh) do{ \
unsigned int *_sbcp_packet = DMA_PACKET_QUEUE_USB_GET_CPU(bh); \
unsigned char _sbcp_correctCS = 0x00; \
_sbcp_correctCS += _sbcp_packet[SBCP_POS_CLASS]; \
_sbcp_correctCS += _sbcp_packet[SBCP_POS_ID]; \
_sbcp_correctCS += _sbcp_packet[SBCP_POS_PAYLOAD_LENGTH]; \
_sbcp_correctCS += _sbcp_packet[SBCP_POS_INSTRUCTION]; \
unsigned int _sbcp_i = SBCP_POS_PAYLOAD_START; \
unsigned int _sbcp_size = (_sbcp_packet[SBCP_POS_PAYLOAD_LENGTH] + 5) & DMA_SBCP_MAX_PACKET_SIZE_MASK; \
for(; _sbcp_i < _sbcp_size; ++_sbcp_i){ \
_sbcp_correctCS += _sbcp_packet[_sbcp_i]; \
} \
if(_sbcp_correctCS == _sbcp_packet[_sbcp_size]){ \
dma_sbcp.packet_flags[bh] |= SBCP_CS_CHECKED | SBCP_CS_IS_CORRECT; \
}else { \
dma_sbcp.packet_flags[bh] |= SBCP_CS_CHECKED; \
} \
}while(0)
#define SBCP_CHECK_CHECKSUM_IF_NEEDED(bh) do{ \
if(!(dma_sbcp.packet_flags[bh] & SBCP_CS_CHECKED)){ \
SBCP_CHECK_CHECKSUM(bh); \
} \
}while(0);
#define SBCP_CHECKED_CHECKSUM_IS_CORRECT(bh) \
((dma_sbcp.packet_flags[bh]) & SBCP_CS_IS_CORRECT)
/**
* Mark the correct destination flags for a finished packet.
*/
#define SBCP_CHECK_DESTINATION(bh) do{ \
unsigned int * _sbcp_packet = DMA_PACKET_QUEUE_USB_GET_CPU(bh); \
if(_sbcp_packet[SBCP_POS_CLASS] == SBCP_MY_CLASS && \
_sbcp_packet[SBCP_POS_ID] == SBCP_MY_ID ){ \
dma_sbcp.packet_flags[bh] |= SBCP_DEST_CHECKED | SBCP_DEST_IS_US; \
} else { \
dma_sbcp.packet_flags[bh] |= SBCP_DEST_CHECKED; \
} \
}while(0)
#define SBCP_CHECK_DESTINATION_IF_NEEDED(bh) do{ \
if(!(dma_sbcp.packet_flags[bh] & SBCP_DEST_CHECKED)){ \
SBCP_CHECK_DESTINATION(bh); \
} \
}while(0)
#define SBCP_CHECKED_DESTINATION_IS_CORRECT(bh) \
((dma_sbcp.packet_flags[bh]) & SBCP_DEST_IS_US)
/*
#define MARK_FIRST_READY_USB_PACKET_AS_TREATED do{\
sbcp.usb_packet_flags[sbcp.usb_ready_packet_queue.head] = SBCP_UNCHECKED;\
DMA_PACKET_QUEUE_INCREMENT_HEAD(sbcp.usb_ready_packet_queue);\
}while(0)
*/
/*************************************************************************
*
* 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()
{
PORT_RS485_TAKE_DRIVE();
}
/** \brief SBCP instruction search and execution.
*
* 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 buffer that contains the SBCP packet
* \return no returns
*/
static
void sbcp_search_and_execute_instruction(DMA_packet_queue_buffer_handler bh,
unsigned int *packet)
{
unsigned char instruction;
instruction = packet[SBCP_POS_INSTRUCTION];
// check if instruction is valid number - meaning if entry in command table can exist
if( instruction >= SBCP_COMMAND_TABLE_LENGTH )
{
sbcp_usb_send_com_error_code(bh,SBCP_COM_ERROR_CODE_UNKNOWN_INSTRUCTION);
return;
}
sbcp_cmd_tbl[instruction].cmd_function(packet);
} // sbcp_search_and_execute_command
/** \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() {
//==========================================================================
// TIMEOUT
//==========================================================================
// First let handle any pending timeout.
if(dma_sbcp.usb_rx_error != 0) { //timeout while getting packet from host
//we simply ignore the current packet, It may already be in
//transmission, so we fake the current checksum if it is the case.
U2MODEbits.UARTEN = 0; //disable UART, ignoring data from now;
DMA2CONbits.CHEN = 0; //disable the channel before anything
if (dma_sbcp.bus_tx_state == SBCP_TX_UNFINISHED_PACKET) {
//we are in trouble, the unsfunished packet is on transmission ! we must
//fake data, and still finish transmission
U1STAbits.UTXEN = 0; //disable transmission
//compute the chacksum of packet
unsigned int * packet = dma_sbcp.current_usb_rx_packet;
packet[packet[SBCP_POS_PAYLOAD_LENGTH] + SBCP_POS_PAYLOAD_START] =
~(sbcp_compute_checksum_of_packet(packet)); // this is a bad checksum
//specify that we have finished the packet (it is a bad one, but now
//it is finished
U1STAbits.UTXEN = 1; //reenable TX of data, will finish to send data, with bad checksum
//now mark that we are transmitting a finished packet.
dma_sbcp.bus_tx_state = SBCP_TX_FINISHED_PACKET;
} else {
sbcp_usb_send_com_error_code(dma_sbcp.rx_queue.tail,
dma_sbcp.usb_rx_error);
}
TIMER_2_STOP_AND_CLEAR();
//reinitialize Rx of USB data
DMA2STA = DMA_PACKET_QUEUE_USB_GET_DMA(dma_sbcp.rx_queue.tail);
DMA2CNT = 0; //get one byte
dma_sbcp.usb_rx_state = DMA_WAIT_FOR_HEADER_BYTE;
DMA2CONbits.CHEN = 1;
U2MODEbits.UARTEN = 1; //re-enable reception and transmission
U2STAbits.UTXEN = 1;
//clear time out
dma_sbcp.usb_rx_error = 0;
}
if(DMA0CONbits.CHEN == 1){
IEC0bits.DMA0IE = 0;
if(IFS1bits.T4IF == 1){
dma_sbcp.bus_rx_error |= SBCP_COM_ERROR_CODE_BUS_TIMEOUT;
TIMER_4_STOP_AND_CLEAR();
}
IEC0bits.DMA0IE = 1;
}
//this is a far more tricky situation. We get an error while receiving over
//the bus but we should not make any mistake, since we already started to
//make a packet. so don't start a new one, but only modify the existing one
if (dma_sbcp.bus_rx_error != 0) {
DMA0CONbits.CHEN = 0;
U1MODEbits.UARTEN = 0; //disable the UART reception from the slave
//disbale the DMA channel we don't need to
//receive anymore
if (DMA3CONbits.CHEN == 1
&& dma_sbcp.usb_tx_state == SBCP_TX_UNFINISHED_PACKET) {
//we were already transmitting back, so we must fake data, and
//finish transmission
U2STAbits.UTXEN = 0; //stop transmission now !!!
unsigned int * packet = dma_sbcp.current_bus_rx_packet;
packet[packet[SBCP_POS_PAYLOAD_LENGTH] + SBCP_POS_PAYLOAD_START] =
~(sbcp_compute_checksum_of_packet(packet)); // this is a bad checksum
//specify that the packet is finished
U2STAbits.UTXEN = 1; //finsih the launch with another bunch of data.
dma_sbcp.usb_tx_state = SBCP_TX_FINISHED_PACKET;
} else {
//if we don't even have started the transmission, send an error
//but here the data is already allocated, so we must change instead
//of realocating a new packet !
unsigned int * message = DMA_PACKET_QUEUE_BUS_GET_CPU(dma_sbcp.bus_rx_packet);
//we are alway receiving on the tail.
//now we completely erase our message being received, and remplace
//it by an error one.
message[SBCP_POS_HEADER] = SBCP_HEADER;
message[SBCP_POS_CLASS] = SBCP_MY_CLASS;
message[SBCP_POS_ID] = SBCP_MY_ID;
message[SBCP_POS_PAYLOAD_LENGTH] = 1;
message[SBCP_POS_INSTRUCTION] = SBCP_ERROR_FLAG_COM_ERROR;
message[SBCP_POS_PAYLOAD_START] = dma_sbcp.bus_rx_error;
message[SBCP_POS_PAYLOAD_START + 1] = SBCP_MY_CLASS
+ SBCP_MY_ID
+ 1
+ SBCP_ERROR_FLAG_COM_ERROR
+ dma_sbcp.bus_rx_error;
//specify to the cpu that data is ready to send
dma_sbcp.packet_states[dma_sbcp.bus_rx_packet] = SBCP_FEEDBACK_FULLY_RECEIVED;
}
TIMER_4_STOP_AND_CLEAR();
//don't reinit Rx of bus. We getted and error, we are now the master again !
PORT_RS485_TAKE_DRIVE();
dma_sbcp.bus_rx_error = 0; //clear the error flag, otherwise we are trapped !
dma_sbcp.bus_tx_state = SBCP_IDLE;
U1MODEbits.UARTEN = 1;
U1STAbits.UTXEN = 1; //re - enable UART, it may be useful :D
}
if (dma_sbcp.usb_tx_state == SBCP_IDLE) {
DMA_packet_queue_buffer_handler packet = dma_sbcp.rx_queue.head;
eSBCP_packet_state packet_state = dma_sbcp.packet_states[packet];
if (packet_state == SBCP_FEEDBACK_DEST_AND_SIZE_KNOWN ||
packet_state == SBCP_FEEDBACK_FULLY_RECEIVED) {
DMA3STA = DMA_PACKET_QUEUE_BUS_GET_DMA(packet);
DMA3CNT = ((DMA_PACKET_QUEUE_BUS_GET_CPU(packet))[SBCP_POS_PAYLOAD_LENGTH] + 5) & DMA_SBCP_MAX_PACKET_SIZE_MASK;
DMA3CONbits.CHEN = 1;
DMA3REQbits.FORCE = 1;
IEC0bits.DMA0IE = 0;
if (dma_sbcp.packet_states[packet] == SBCP_FEEDBACK_DEST_AND_SIZE_KNOWN) {
dma_sbcp.usb_tx_state = SBCP_TX_UNFINISHED_PACKET;
} else {
dma_sbcp.usb_tx_state = SBCP_TX_FINISHED_PACKET;
}
IEC0bits.DMA0IE = 1;
led_on(LED_YELLOW);
}
}
if (dma_sbcp.bus_tx_state == SBCP_IDLE) {
IEC2bits.DMA3IE = 0;
DMA_packet_queue_buffer_handler packet = dma_sbcp.rx_queue.head;
signed int nb_packet_treatable = dma_sbcp.rx_queue.nb_packet;
IEC2bits.DMA3IE = 1;
while (nb_packet_treatable > 0) {
if (dma_sbcp.packet_states[packet] == SBCP_FORWARD_NEED_TO_BE_TREATED_LOCALLY) {
dma_sbcp.bus_rx_packet = packet;
sbcp_search_and_execute_instruction(packet, DMA_PACKET_QUEUE_USB_GET_CPU(packet));
nb_packet_treatable = 0; //we should not treat other packet now
}
if (dma_sbcp.packet_states[packet] == SBCP_FORWARD_NEED_TO_BE_TRANSMITTED) {
dma_sbcp.bus_rx_packet = packet;
DMA1STA = DMA_PACKET_QUEUE_USB_GET_DMA(packet);
DMA1CNT = (DMA_PACKET_QUEUE_USB_GET_CPU(packet)[SBCP_POS_PAYLOAD_LENGTH] + 5) & DMA_SBCP_MAX_PACKET_SIZE_MASK;
DMA1CONbits.CHEN = 1;
DMA1REQbits.FORCE = 1;
IEC1bits.DMA2IE = 0; //desactivate the interrupt, because if we
//finish insideb this if, it will be catastrophic
if (packet == dma_sbcp.rx_queue.tail) {
dma_sbcp.bus_tx_state = SBCP_TX_UNFINISHED_PACKET;
} else {
dma_sbcp.bus_tx_state = SBCP_TX_FINISHED_PACKET;
}
IEC1bits.DMA2IE = 1;
dma_sbcp.packet_states[packet] = SBCP_FORWARD_IN_TX;
nb_packet_treatable = 0; //we should not treat other packet now, or we will get inversions !
led_on(LED_GREEN_2);
}
--nb_packet_treatable;
packet = (packet + 1) & DMA_PACKET_QUEUE_MASK;
}
}
IEC2bits.DMA3IE = 0;
DMA_packet_queue_buffer_handler packet = dma_sbcp.rx_queue.head;
signed int nb_packet_treatable = dma_sbcp.rx_queue.nb_packet;
unsigned int isFirstPacket = 1;
while (nb_packet_treatable > 0) {
if(nb_packet_treatable > DMA_PACKET_QUEUE_LENGTH){
//super super bad error, we report it,and quit
led_blink_with_params(LED_RED,6);
break;
}
if (isFirstPacket == 0 &&
dma_sbcp.packet_states[packet] == SBCP_INEXISTANT) {
//REPORT Error this is not the head packet (that could from time to
//time become inexistant as it just finished) and it is inexistant
//this is really really bad !
led_blink_with_params(LED_RED, 1);
}
isFirstPacket = 0;
/*
if (dma_sbcp.packet_states[packet] == SBCP_FORWARD_STARTED_RECEPTION) {
// DO Nothing
}
*/
if (dma_sbcp.packet_states[packet] == SBCP_FORWARD_DEST_AND_SIZE_KNOWN) {
SBCP_CHECK_DESTINATION_IF_NEEDED(packet);
// comment this lines if you don't want to transmit packet ASAP on
//the bus, even when checksum is not finished.
if ((dma_sbcp.rx_queue.head == packet) &&
!SBCP_CHECKED_DESTINATION_IS_CORRECT(packet)) { //we only permit to fast forward transmit the first packet.
dma_sbcp.packet_states[packet] = SBCP_FORWARD_NEED_TO_BE_TRANSMITTED;
}
}
if (dma_sbcp.packet_states[packet] == SBCP_FORWARD_READY_TO_TREAT) {
SBCP_CHECK_DESTINATION_IF_NEEDED(packet);
SBCP_CHECK_CHECKSUM_IF_NEEDED(packet);
if (!SBCP_CHECKED_CHECKSUM_IS_CORRECT(packet)) {
sbcp_usb_send_com_error_code(packet,
SBCP_COM_ERROR_CODE_CHECKSUM_INCORRECT);
}
if (SBCP_CHECKED_DESTINATION_IS_CORRECT(packet)) {
dma_sbcp.packet_states[packet] = SBCP_FORWARD_NEED_TO_BE_TREATED_LOCALLY;
} else {
dma_sbcp.packet_states[packet] = SBCP_FORWARD_NEED_TO_BE_TRANSMITTED;
}
}
if (dma_sbcp.packet_states[packet] > SBCP_FEEDBACK_IN_TRANSMISSION) {
led_blink_with_params(LED_RED, 2);
}
--nb_packet_treatable;
packet = (packet + 1) & DMA_PACKET_QUEUE_MASK;
}
IEC2bits.DMA3IE = 1;
//here we have some time for processing checksum, and checking destination
signed int dist = dma_sbcp.bus_rx_packet - dma_sbcp.rx_queue.head;
if (dist < 0) {
dist += 4;
}
if (dist > 1 && dma_sbcp.bus_tx_state != SBCP_IDLE) {
led_blink_with_params(LED_RED, 3);
}
// in case of an overflow, we have to check when we are free again
if(dma_sbcp.usb_rx_state == DMA_OVERFLOW_PENDING &&
!DMA_PACKET_QUEUE_FULL(dma_sbcp.rx_queue)) {
DMA_INIT_USB_RX;
}
if (dma_sbcp.bus_tx_state == SBCP_IDLE) {
led_off(LED_GREEN_2);
}
if (dma_sbcp.usb_tx_state == SBCP_IDLE) {
led_off(LED_YELLOW);
}
}
/**
* @return a free pointer to a buffer for usb transmission.
*/
/*
unsigned int * sbcp_get_new_usb_tx_packet(){
while (DMA_PACKET_QUEUE_FULL(dma_sbcp.bus_rx_queue)) {
}
//stop channel2 Interruption, to prevent buffer order swapping, if we finish
//the reception of a packet at the same time !
IEC1bits.DMA2IE = 0;
DMA_PACKET_QUEUE_INCREMENT_TAIL(dma_sbcp.bus_rx_queue);
IEC1bits.DMA2IE = 1;
return DMA_PACKET_QUEUE_BUS_GET_CPU(dma_sbcp.bus_rx_queue.tail);
}*/
/** \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_usb_send_standard_acknowlegement(DMA_packet_queue_buffer_handler bh){
unsigned int * message = DMA_PACKET_QUEUE_BUS_GET_CPU(bh);
dma_sbcp.packet_states[bh] = SBCP_FEEDBACK_FULLY_RECEIVED;
message[SBCP_POS_HEADER] = SBCP_HEADER;
message[SBCP_POS_CLASS] = SBCP_MY_CLASS;
message[SBCP_POS_ID] = SBCP_MY_ID;
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;
//will tell the main fnction that it can send some data
// DMA_PACKET_QUEUE_INCREMENT_TAIL(sbcp.bus_ready_packet_queue);
}
/** \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 USB interface.
\param error_code:
Error code to be transmitted.
\return no returns
*/
void sbcp_usb_send_com_error_code(DMA_packet_queue_buffer_handler bh,
unsigned char error_code) {
unsigned int * message = DMA_PACKET_QUEUE_BUS_GET_CPU(bh);
dma_sbcp.packet_states[bh] = SBCP_FEEDBACK_FULLY_RECEIVED;
message[SBCP_POS_HEADER] = SBCP_HEADER;
message[SBCP_POS_CLASS] = SBCP_MY_CLASS;
message[SBCP_POS_ID] = SBCP_MY_ID;
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;
//will tell the main fnction that it can send some data
// DMA_PACKET_QUEUE_INCREMENT_TAIL(sbcp.bus_ready_packet_queue);
}
void sbcp_usb_send_message(DMA_packet_queue_buffer_handler bh,
unsigned char error_code,
unsigned char size,
unsigned char * data){
unsigned int * message = DMA_PACKET_QUEUE_BUS_GET_CPU(bh);
dma_sbcp.packet_states[bh] = SBCP_FEEDBACK_FULLY_RECEIVED;
message[SBCP_POS_HEADER] = SBCP_HEADER;
message[SBCP_POS_CLASS] = SBCP_MY_CLASS;
message[SBCP_POS_ID] = SBCP_MY_ID;
message[SBCP_POS_PAYLOAD_LENGTH] = size;
message[SBCP_POS_INSTRUCTION] = error_code;
message[SBCP_POS_PAYLOAD_START + size] = SBCP_MY_CLASS + SBCP_MY_ID + size
+ error_code;
unsigned int i;
for(i = 0; i < size; ++i){
message[SBCP_POS_PAYLOAD_START + i] = data[i];
message[SBCP_POS_PAYLOAD_START + size] += data[i];
}
message[SBCP_POS_PAYLOAD_START + size] &= 0x00ff;
}
/*@}*/
Event Timeline
Log In to Comment