Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F102511546
sbcp_bus.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, 11:59
Size
10 KB
Mime Type
text/x-c
Expires
Sun, Feb 23, 11:59 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
24339253
Attached To
R6617 Oncilla SBCP Master Firmware
sbcp_bus.c
View Options
#include "sbcp_bus.h"
#include "sbcp_host.h"
#include <sbcp-uc/buffers.h>
#include <sbcp-uc/uart.h>
#include <sbcp-uc/register.h>
#include <p33Fxxxx.h>
#include "sbcp_slave.h"
volatile unsigned int __attribute__((space(dma))) bus_rx_buffer[SBCP_RX_BUFFER_SIZE];
typedef enum bus_state {
BUS_STATE_IDLE = 0,
BUS_STATE_TX,
BUS_STATE_INTERN_PROCESSING,
BUS_STATE_RX,
BUS_STATE_FINISHING_RX
} bus_state;
typedef struct bus_data {
volatile bus_state state;
buffer_fifo data_fifo;
volatile buffer_fifo ready_fifo;
packet_mdata pck_mdata[SBCP_FRAME_SIZE];
volatile packet_mdata * cur_rx_pck, * cur_tx_pck;
} bus_data;
bus_data bus;
#define rs485_take_ownership() do { \
_LATB6 = 1; \
_LATB7 = 1; \
}while(0)
#define rs485_release_ownership() do { \
_LATB7 = 0; \
_LATB6 = 0; \
}while(0)
#define timeout_cur_pck(pck) do { \
DMA0CONbits.CHEN = 0; \
(pck)->rx_error = PCK_RX_TERR; \
buffer_fifo_increment_tail(bus.ready_fifo); \
rs485_take_ownership(); \
bus.state = BUS_STATE_FINISHING_RX; \
} while(0)
void bus_process_idle();
void bus_process_finishing_rx();
void sbcp_process_loopback();
void init_sbcp_bus_data(){
unsigned int i;
for (i = 0 ; i < SBCP_FRAME_SIZE ; ++i){
bus.pck_mdata[i].data = &(bus_rx_buffer[i * SBCP_PACKET_SIZE]);
bus.pck_mdata[i].dma_adr = __builtin_dmaoffset(bus_rx_buffer) + i * SBCP_PACKET_SIZE * 2;
bus.pck_mdata[i].id = i;
pck_clean_mdata(&(bus.pck_mdata[i]));
}
buffer_fifo_set_empty(bus.data_fifo);
buffer_fifo_set_empty(bus.ready_fifo);
}
void init_dma_for_bus(){
DMA0CONbits.CHEN = 0;
//initialize DMA 0 for UART1 RX
DMA0CONbits.SIZE = 0; // use word
DMA0CONbits.DIR = 0; // from peripherical to RAM
DMA0CONbits.HALF = 0; // inetrrupt after all receive
DMA0CONbits.NULLW = 0; // non null write
DMA0CONbits.AMODE = 0; // indirect register with post increment
DMA0CONbits.MODE = 1; // One shot, no ping pong
DMA0PAD = (volatile unsigned int) &U1RXREG;
// Count and address will be enabled on demand
// DMA0CNT = 0; //1 transfers
// DMA0STA = 0;
DMA0REQ = 0x000b; //UART 1 RX irq
// IEC1bits.DMA2IE = 0;
// IPC6bits.DMA2IP = 5; //higher priority
IFS0bits.DMA0IF = 0; //clear DMA 2 interrupt flag
IEC0bits.DMA0IE = 1; //enable DMA 2 interrupt
DMA0CONbits.CHEN = 0; //disable channel 0
//initialize DMA 1 for BUS TX
DMA1CONbits.CHEN = 0;
DMA1CONbits.SIZE = 0; //use word
DMA1CONbits.DIR = 1; //from RAM to periph
DMA1CONbits.HALF = 0; //interrupt after all send char
DMA1CONbits.NULLW = 0; //normal mode
DMA1CONbits.AMODE = 0; //indirect register with post increment
DMA1CONbits.MODE = 1; //One shot, no ping pong
DMA1PAD = (volatile unsigned int) &U1TXREG;
DMA1REQ = 0x000c; //UART 1 TX IRQ
// IEC0bits.DMA0IE = 0;
// IPC0bits.DMA0IP = 5; //higher priority
IFS0bits.DMA1IF = 0; // CLEAR DMA1 interrupt flag
IEC0bits.DMA1IE = 1; // ENABLE DMA1 INTERRUPT !!!!
DMA1CONbits.CHEN = 0;
}
void init_sbcp_bus(){
init_sbcp_bus_data();
TRISBbits.TRISB6 = 0;
TRISBbits.TRISB7 = 0;
rs485_take_ownership();
uart_map_rx_input(UART_1,RP_5);
map_pin_to_periph_output(8,RP_U1TX);
init_uart_for_sbcp(UART_1,BAUDRATE,FCY);
init_dma_for_bus();
init_sbcp_slave();
}
void __attribute__((__interrupt__ , no_auto_psv)) _DMA0Interrupt(void) {
volatile packet_mdata * pck = bus.cur_rx_pck;
switch (pck->rx_state) {
case PCK_RX_WAIT_FOR_HEADER_BYTE : {
if(pck->data[SBCP_HEADER_POS] != 0X00ff) {
//simply restart the channel, do ont change state or report anything
DMA0CONbits.CHEN = 0;
DMA0CONbits.CHEN = 1;
} else {
// we disable channel for modification
DMA0CONbits.CHEN = 0;
// adds 2 to the channel address
DMA0STA += 2;
// we wait for 3 byte : class, id, size
DMA0CNT = 2;
//restart channel ASAP
DMA0CONbits.CHEN = 1;
pck_start_header_rx(pck);
}
break;
}
case PCK_RX_WAIT_FOR_FULL_HEADER :{
// we disable channel for modification
DMA0CONbits.CHEN = 0;
//last state read 3 byte
DMA0STA += 6;
//checks for the size
sbcp_payload_size length = pck_payload_size(pck);
if (length > SBCP_MAX_PAYLOAD_SIZE) {
length = SBCP_MAX_PAYLOAD_SIZE;
}
//final size is length + inst + cs
DMA0CNT = length + 1;
DMA0CONbits.CHEN = 1;
pck_start_data_rx(pck);
break;
}
case PCK_RX_WAIT_FOR_FULL_DATA : {
pck_finish_rx(pck);
buffer_fifo_increment_tail(bus.ready_fifo);
bus.state = BUS_STATE_FINISHING_RX;
rs485_take_ownership();
//desactivate the interruption.
IEC0bits.DMA0IE = 0;
break;
}
default:
break;
}
//clear the interrupt flag !!
IFS0bits.DMA0IF = 0;
}
void __attribute__((__interrupt__ , no_auto_psv)) _DMA1Interrupt(void) {
//this interupt fire when the last char is put in the uart buffer
//we need to fire an interrupt when the last char is transmitted
U1STAbits.UTXISEL0 = 1; // interruption when buffer is empty
IFS0bits.U1TXIF = 0; // yes it is set since we finished to transfer the data
IEC0bits.U1TXIE = 1; // enable interruption
//clear flag
IFS0bits.DMA1IF = 0;
IEC0bits.DMA1IE = 0;
}
#define prepare_bus_rx() do{ \
DMA0CONbits.CHEN = 0; \
buffer_fifo_increment_tail(bus.data_fifo); \
bus.cur_rx_pck = &(bus.pck_mdata[bus.data_fifo.tail]); \
DMA0STA = bus.cur_rx_pck->dma_adr; \
DMA0CNT = 0; \
pck_init_rx(bus.cur_rx_pck); \
} while(0)
#define start_bus_rx() do{ \
IFS0bits.DMA0IF = 0; \
IEC0bits.DMA0IE = 1; \
DMA0CONbits.CHEN = 1; \
bus.state = BUS_STATE_RX; \
bus.cur_tx_pck->end_tx_date = current_time(); \
} while(0)
void __attribute__((__interrupt__, no_auto_psv)) _U1TXInterrupt(void) {
//even disable UART 1, we must not read any bad packet
U1MODEbits.UARTEN = 0;
U1STAbits.UTXEN = 0;
rs485_release_ownership();
U1MODEbits.UARTEN = 1;
U1STAbits.UTXEN = 1;
// init the bus rx
start_bus_rx();
IEC0bits.U1TXIE = 0; // Disable UART 1 TX interrupt
U1STAbits.UTXISEL0 = 0; // Interruption every char !
IFS0bits.U1TXIF = 0; // Clear interrupt !
};
void sbcp_process_loopback() {
//todo : real implementation
// this is a rather simple loopback implementation
//we cannot process something if our fifo is full, we return
if ( buffer_fifo_full(bus.data_fifo) ) {
return;
}
//checks for incoming packet
volatile packet_mdata * pck = next_host_packet();
if (pck != 0 ){
unsigned int i;
//allocate some space in the ring buffer. it is possible because it is not full !
buffer_fifo_increment_tail(bus.data_fifo);
packet_mdata * loop_pck = &(bus.pck_mdata[bus.data_fifo.tail]);
//simply copy the incoming packet .
for (i = 0; i < pck_byte_size(pck); ++i){
loop_pck->data[i] = pck->data[i];
}
// we do not need host data anymore
release_host_packet(pck);
//copy the metadata too, to test error reporting
loop_pck->rx_state = pck->rx_state;
loop_pck->rx_error = pck->rx_error;
//marks the packet as ready for downstream processing
buffer_fifo_increment_tail(bus.ready_fifo);
}
}
void bus_process_idle(){
//during idle time all DMA channels are disabled :)
if(buffer_fifo_full(bus.data_fifo)) {
//we do nothing because we do not have the resources
return;
}
bus.cur_tx_pck = next_host_packet();
if ( bus.cur_tx_pck == 0 ) {
return;
}
// TODO : check for packet RX errors
//we can already allocate and prepare for rx
prepare_bus_rx();
sbcp_class klass = bus.cur_tx_pck->data[SBCP_CLASS_POS];
sbcp_id id = bus.cur_tx_pck->data[SBCP_ID_POS];
//checks if we are the dest
if( klass == SBCP_MY_CLASS && id == SBCP_MY_ID) {
// we process the packet internally
// cur_tx_pck is the incoming packet on the "bus"
// cur_rx_pck is the response packet over the bus
// volatile could be discarded becaus in this scope nobody will
// touch packet_mdata anymore
sbcp_slave_process_pck((const packet_mdata *) bus.cur_tx_pck,
( packet_mdata *) bus.cur_rx_pck);
buffer_fifo_increment_tail(bus.ready_fifo);
bus_process_finishing_rx();
return;
}
//the packet is not for us, we transmit over the bus
bus.state = BUS_STATE_TX;
//simply starts the transmission over the bus of the packet
DMA1STA = bus.cur_tx_pck->dma_adr;
DMA1CNT = pck_byte_size(bus.cur_tx_pck) - 1;
DMA1CONbits.CHEN = 1;
DMA1REQbits.FORCE = 1;
IEC0bits.DMA1IE = 1;
}
void bus_process_finishing_rx() {
//no DMA channel are enabled in this state :)
release_host_packet(bus.cur_tx_pck);
bus.state = BUS_STATE_IDLE;
}
void bus_process_rx() {
//yes using priority to disable it, because we do not know its state, as it defines the timeout condition
IPC3bits.U1TXIP = 0;
IPC1bits.DMA0IP = 0;
volatile packet_mdata * pck = bus.cur_rx_pck;
packet_rx_state pck_state = pck->rx_state;
IPC1bits.DMA0IP = 4;
IPC3bits.U1TXIP = 4;
ticks timeout = bus.cur_tx_pck->data[SBCP_INSTRUCTION_POS] == 0xff ? LLA_PACKET_RSP_TIMEOUT : STD_PACKET_RSP_TIMEOUT;
if( pck_state == PCK_RX_WAIT_FOR_HEADER_BYTE ) {
if( current_time() - bus.cur_tx_pck->end_tx_date >= timeout ) {
IPC1bits.DMA0IP = 0;
if(bus.cur_rx_pck->rx_state != PCK_RX_WAIT_FOR_HEADER_BYTE) {
//we started the reception between the fetch and now,
//the double check fails, we stop the error.
IEC0bits.DMA0IE = 1;
return;
}
timeout_cur_pck(pck);
bus_process_finishing_rx();
// ensures that no interuption could fire.
IFS0bits.DMA0IF = 0;
IPC1bits.DMA0IP = 4;
}
return;
}
if (pck_rx_timeouted(pck)) {
IPC1bits.DMA0IP = 0;
if(pck->rx_state == PCK_RX_FINISHED){
//We finished the reception between the pck extraction time and now
IEC0bits.DMA0IE = 1;
return;
}
timeout_cur_pck(pck);
bus_process_finishing_rx();
// ensures that no interuption could fire.
IFS0bits.DMA0IF = 0;
IPC1bits.DMA0IP = 4;
}
}
void sbcp_process_bus(){
IPC3bits.U1TXIP = 0; //yes using priority to disable it, because we do not know its state
IPC1bits.DMA0IP = 0;
IPC3bits.DMA1IP = 0;
bus_state state = bus.state;
IPC3bits.DMA1IP = 4;
IPC1bits.DMA0IP = 4;
IPC3bits.U1TXIP = 4; //default priority
switch(state) {
case BUS_STATE_IDLE :
bus_process_idle();
break;
case BUS_STATE_RX :
bus_process_rx();
break;
case BUS_STATE_FINISHING_RX :
bus_process_finishing_rx();
break;
case BUS_STATE_TX :
case BUS_STATE_INTERN_PROCESSING :
default :
break;
}
}
volatile packet_mdata * next_bus_packet(){
IPC1bits.DMA0IP = 0;
char empty = buffer_fifo_empty(bus.ready_fifo);
IPC1bits.DMA0IP = 4;
if( empty ) {
//no packet are present
return 0;
}
return &(bus.pck_mdata[bus.ready_fifo.head]);
}
void release_bus_packet(volatile packet_mdata * pck ){
if ( pck->id != bus.ready_fifo.head ) {
//TODO : report error
return;
}
buffer_fifo_increment_head(bus.ready_fifo);
buffer_fifo_increment_head(bus.data_fifo);
}
Event Timeline
Log In to Comment