Page MenuHomec4science

dma_sbcp_slave.c
No OneTemporary

File Metadata

Created
Mon, Jul 22, 11:35

dma_sbcp_slave.c

/*
* dma_sbcp.c
*
* Copyright (c) 2011 Alexandre Tuleu
*
*/
#include <p33Fxxxx.h>
#include "dma_sbcp.h"
#include "sbcp.h"
#include "config.h"
#include "timer.h"
#include "port.h"
//note: now dma for sbcp buffer are allocated in dma_memory_mapping_sbcp.c module
volatile DMA_SBCP_data dma_sbcp;
/*
DMA_packet_tx_queue dma_usb_tx_queue = {
0, //head
0, //tail
0, //nb_packet
{0,0,0,0}, //data_dma
{0,0,0,0} //data_cpu
};
DMA_packet_tx_queue dma_bus_tx_queue = {
0, //head
0, //tail
0, //nb_packet
{0,0,0,0}, //data_dma
{0,0,0,0} //data_cpu
};
*/
/* Interruption for the first dma channel.*/
void __attribute__((__interrupt__ , no_auto_psv)) _DMA0Interrupt(void) {
switch (dma_sbcp.bus_rx_state) {
case DMA_WAIT_FOR_HEADER_BYTE : {
if(*(dma_sbcp.current_bus_rx_packet) != 0X00ff){
DMA0CONbits.CHEN = 0;
DMA0CONbits.CHEN = 1;
} else {
DMA_BUS_CHANNEL_CONFIG_GET_END_HEADER;
TIMER_4_START;
//DMA_BUS_ENABLE_ERR_DETECTION;
}
break;
}
case DMA_WAIT_FOR_FULL_HEADER :
{
DMA_BUS_CHANNEL_CONFIG_GET_END_PACKET;
break;
}
case DMA_WAIT_FOR_FULL_PACKET : {
//first of all, we check if the packet is for us.
if(dma_sbcp.current_bus_rx_packet[SBCP_POS_ID] != SBCP_MY_ID ||
dma_sbcp.current_bus_rx_packet[SBCP_POS_CLASS] != SBCP_MY_CLASS){
//the packet is not for us, we re-init the DMA 0 channel, and we
//the current incoming packet.
DMA0STA -= 8; // we remove the 4 first pointed bytes
DMA0CNT = 0; //just one byte reception
DMA0CONbits.CHEN = 1; //channel is resetted
dma_sbcp.bus_rx_state = DMA_WAIT_FOR_HEADER_BYTE;
//we are waiting the first byte
} else { //the packet is for us
if(sbcp.bus_low_latency_flags & SBCP_LL_IS_DISABLED){
IEC0bits.DMA0IE = 0;
return;
}
//We check if it is the special low latency instruction
//this instruction should be responded ASAP, so in this interruption.
if (dma_sbcp.current_bus_rx_packet[SBCP_POS_INSTRUCTION] == SBCP_LOW_LATENCY_INSTRUCTION) {
//of course checksum should be checked
unsigned char correctChecksum = SBCP_MY_CLASS
+ SBCP_MY_ID
+ SBCP_LOW_LATENCY_INSTRUCTION_COMMAND_PAYLOAD_SIZE
+ SBCP_LOW_LATENCY_INSTRUCTION;
correctChecksum += dma_sbcp.current_bus_rx_packet[SBCP_POS_PAYLOAD_START];
correctChecksum += dma_sbcp.current_bus_rx_packet[SBCP_POS_PAYLOAD_START + 1];
correctChecksum += dma_sbcp.current_bus_rx_packet[SBCP_POS_PAYLOAD_START + 2];
correctChecksum += dma_sbcp.current_bus_rx_packet[SBCP_POS_PAYLOAD_START + 3];
if(correctChecksum == dma_sbcp.current_bus_rx_packet[SBCP_POS_PAYLOAD_START + 4]) {
//we use the already prepared standard message
DMA1STA = DMA_PACKET_QUEUE_BUS_LL_TX_BUFFER_DMA;
DMA1CNT = SBCP_LOW_LATENCY_INSTRUCTION_FEEDBACK_PAYLOAD_SIZE + 5;
PORT_RS485_RECEIVER_DISABLE;
PORT_RS485_DRIVER_ENABLE;
DMA1CONbits.CHEN = 1;
DMA1REQbits.FORCE = 1;
//we save the incomming payload.
sbcp.bus_low_latency_payload[0] =
dma_sbcp.current_bus_rx_packet[SBCP_POS_PAYLOAD_START];
sbcp.bus_low_latency_payload[1] =
dma_sbcp.current_bus_rx_packet[SBCP_POS_PAYLOAD_START + 1];
sbcp.bus_low_latency_payload[2] =
dma_sbcp.current_bus_rx_packet[SBCP_POS_PAYLOAD_START + 2];
sbcp.bus_low_latency_payload[3] =
dma_sbcp.current_bus_rx_packet[SBCP_POS_PAYLOAD_START + 3];
//we specify that new data is available to treat it at the appropriate time
sbcp.bus_low_latency_flags |= SBCP_LL_RX_IS_AVAILABLE;
} else { //we get a bad checksum
//we use the already prepared error
DMA1STA = DMA_PACKET_QUEUE_BUS_LL_ERROR_TX_BUFFER_DMA;
DMA1CNT = 6;
PORT_RS485_RECEIVER_DISABLE;
PORT_RS485_DRIVER_ENABLE;
DMA1CONbits.CHEN = 1;
DMA1REQbits.FORCE = 1;
}
sbcp.bus_tx_flags = SBCP_TX_BUFFER_IN_TX;
//we mark it as treated since it was for us, and we have treated it.
DMA_PACKET_QUEUE_INCREMENT_HEAD(sbcp.bus_ready_packet_queue);
}
//the packet is for us (just imagine that this line is just
// after the else :) ) so we mark it as ready for treatment. If
// it was a low latency one, it would already have been already
//treated.
DMA_PACKET_QUEUE_INCREMENT_TAIL(sbcp.bus_ready_packet_queue);
}
//now we clear the timer 4. Interruption may have already occured,
//but masked by this one that have a higher priority. To not mess up
//the whole thing, we should clear the corresponding interrupt flag.
TIMER_4_STOP_AND_CLEAR;
IFS1bits.T4IF = 0;
//if the interruption have occured for some magic reason, or
//obviously deficient programming skill of mine, it should remove
//any uterly bad effect
dma_sbcp.bus_rx_error &= ~(SBCP_COM_ERROR_CODE_BUS_TIMEOUT);
break;
}
default :
break;
}
//clear the interrupt flag !
IFS0bits.DMA0IF = 0;
}
//Transfer is completely in UART Queue
void __attribute__((__interrupt__ , no_auto_psv )) _DMA1Interrupt(void){
U1STAbits.UTXISEL0 = 1; //stop only when all operation are finished
//clear any pending flags (which is set BTW)
IFS0bits.U1TXIF = 0;
//enable TX interrupt to detect when trnasfer is actually finished (not just in queue)
IEC0bits.U1TXIE = 1;
//clear the interrupt flag !
IFS0bits.DMA1IF = 0;
}
void __attribute__((__interrupt__ , no_auto_psv)) _U1TXInterrupt(void) {
/*
while(!(U1STAbits.TRMT)){
Nop();
}
*/
PORT_RS485_DRIVER_DISABLE;
PORT_RS485_RECEIVER_ENABLE;
//here all Tx operations are done
//We start the reset of the UART. indeed , UART has been of for a time now
//so a OERR could have occur, this is safer for two instructions !
U1MODEbits.UARTEN = 0; //disabled.
U1STAbits.UTXEN = 0;
//we switch the port back to Rx mode
// TODO we may need to re-enable Tx ! check doc for this !
// TODO check the error handling code too !
DMA_INIT_BUS_RX;
//now reception is OK !
sbcp.bus_tx_flags = SBCP_TX_BUFFER_FREE;
IEC0bits.U1TXIE = 0; //disable interrut
U1STAbits.UTXISEL0 = 0; //Interrupt every char
IFS0bits.U1TXIF = 0; //clear interrupt !
U1MODEbits.UARTEN = 1; //resetted, buffer are now empty !
U1STAbits.UTXEN = 1;
};
void dma_sbcp_init(){
//initialize data
dma_sbcp.bus_uart_error_enable_flags = DMA_UART_ALL_ERRORS_DISABLED;
dma_sbcp.bus_rx_state = DMA_WAIT_FOR_HEADER_BYTE;
DMA_PACKET_QUEUE_SET_EMPTY(dma_sbcp.bus_rx_queue);
dma_sbcp.bus_rx_error = 0;
//initialize DMA 0 for BUS 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
DMA0CNT = 0; //1 transfers
DMA0PAD = (volatile unsigned int) &U1RXREG;
DMA0STA = DMA_PACKET_QUEUE_BUS_GET_DMA(dma_sbcp.bus_rx_queue.tail);
DMA0REQ = 0x000b; //UART 1 RX irq
IFS0bits.DMA0IF = 0; //clear DMA interrupt flag
IEC0bits.DMA0IE = 1; //enable dmao interrupt
DMA0CONbits.CHEN = 0; //enable channel 0
//activate DMA 0
//initialize DMA 1 for BUS TX
DMA1CONbits.SIZE = 0; //use word
DMA1CONbits.DIR = 1; //from RAM to periph
DMA1CONbits.HALF = 0; //interrupt after all received char
DMA1CONbits.NULLW = 0; //normal mode
DMA1CONbits.AMODE = 0; //indirect register with post increment
DMA1CONbits.MODE = 1; //One shot, no ping pong
DMA1CNT = 0; //1 byte, should be overrwriten when launching
DMA1PAD = (volatile unsigned int) &U1TXREG;
DMA1STA = DMA_PACKET_QUEUE_BUS_TX_BUFFER_DMA; //should be overwritten when launching
DMA1REQ = 0x000c; //UART 1 TX IRQ
IFS0bits.DMA1IF = 0; //clear DMA interrupt flag
IEC0bits.DMA1IE = 1; //ENABLE DMA1 INTERRUPT !!!!
//DISABLE CHANNEL, WILL BE CONFIGURED AND ENABLED WHEN NEEDED
DMA1CONbits.CHEN = 0;
PORT_RS485_RECEIVER_ENABLE;
PORT_RS485_DRIVER_DISABLE;
//we disable the frame error detection while waiting for the header byte
//in case the terminal resistor is not put on the bus.
//DMA_BUS_DISABLE_ERR_DETECTION;
DMA_INIT_BUS_RX;
}

Event Timeline