View on Github
			
/**
 * @file can_pic32.c
 * @author Sebastien CAUX (sebcaux)
 * @copyright Uniswarm 2017
 *
 * @date December 8 2017, 09:10 AM
 *
 * @brief CAN support for udevkit for PIC32MK, PIC32MX, PIC32MZDA,
 * PIC32MZEC and PIC32MZEF
 *
 * Implementation based on Microchip document DS61154C :
 *  http://ww1.microchip.com/downloads/en/DeviceDoc/61154C.pdf
 */

#include "can.h"

#include <driver/sysclock.h>
#include <archi.h>

#if !defined (CAN_COUNT) || CAN_COUNT==0
  #warning "No can on the current device or unknow device"
#endif

#define CAN_FLAG_UNUSED  0x00
typedef struct {
    union {
        struct {
            unsigned used : 1;
            unsigned enabled : 1;
            unsigned : 6;
        };
        uint8_t val;
    };
} can_status;

struct can_dev
{
    CAN_MODE mode;
    uint32_t bitRate;
    uint8_t propagSeg;
    uint8_t s1Seg;
    uint8_t s2Seg;
    can_status flags;
};

#if CAN_COUNT>=1
unsigned int CAN1FIFO[32*4];
#endif
#if CAN_COUNT>=2
unsigned int CAN2FIFO[32*4];
#endif
#if CAN_COUNT>=3
unsigned int CAN3FIFO[32*4];
#endif
#if CAN_COUNT>=4
unsigned int CAN4FIFO[32*4];
#endif

struct can_dev cans[] = {
#if CAN_COUNT>=1
    {
        .bitRate = 0,
        .flags = {{.val = CAN_FLAG_UNUSED}}
    },
#endif
#if CAN_COUNT>=2
    {
        .bitRate = 0,
        .flags = {{.val = CAN_FLAG_UNUSED}}
    },
#endif
#if CAN_COUNT>=3
    {
        .bitRate = 0,
        .flags = {{.val = CAN_FLAG_UNUSED}}
    },
#endif
#if CAN_COUNT>=4
    {
        .bitRate = 0,
        .flags = {{.val = CAN_FLAG_UNUSED}}
    },
#endif
};

/**
 * @brief Gives a free CAN bus number and open it
 * @return CAN bus number
 */
rt_dev_t can_getFreeDevice()
{
#if CAN_COUNT>=1
    uint8_t i;
    rt_dev_t device;

    for (i = 0; i < CAN_COUNT; i++)
    {
        if (cans[i].flags.used == 0)
        {
            break;
        }
    }

    if (i == CAN_COUNT)
    {
        return NULLDEV;
    }
    device = MKDEV(DEV_CLASS_CAN, i);

    can_open(device);

    return device;
#else
    return NULLDEV;
#endif
}

/**
 * @brief Opens a CAN bus
 * @param can CAN bus id
 * @return 0 if ok, -1 in case of error
 */
int can_open(rt_dev_t device)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return -1;
    }
    if (cans[can].flags.used == 1)
    {
        return -1;
    }

    cans[can].flags.used = 1;

    return 0;
#else
    return -1;
#endif
}

/**
 * @brief Closes and release a CAN bus
 * @param device CAN bus number
 * @return 0 if ok, -1 in case of error
 */
int can_close(rt_dev_t device)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return -1;
    }

    can_disable(device);

    cans[can].flags.val = CAN_FLAG_UNUSED;
    return 0;
#else
    return -1;
#endif
}

/**
 * @brief Enables the specified CAN bus
 * @param device CAN bus number
 * @return 0 if ok, -1 in case of error
 */
int can_enable(rt_dev_t device)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return -1;
    }

    cans[can].flags.enabled = 1;

    switch (can)
    {
    case 0:
        // assign memory
        C1FIFOBA = KVA_TO_PA(CAN1FIFO);
        // fifo 0 (transmit)
        C1FIFOCON0bits.FSIZE = 15;
        C1FIFOCON0SET = 0x80;
        // fifo 1 (receive)
        C1FIFOCON1bits.FSIZE = 15;
        C1FIFOCON1CLR = 0x80;
        // filter 1
        C1FLTCON0bits.FSEL1 = 1;        // Store messages in FIFO1
        C1FLTCON0bits.MSEL1 = 1;        // Use Mask 1
        C1RXF1bits.SID = 0x000;         // Filter 1 EID
        C1RXF1bits.EID = 0x00000;       // Filter 1 SID
        C1RXF1bits.EXID = 1;            // Filter EID messages
        C1FLTCON0bits.FLTEN1 = 1;       // Enable the filter
        // mask 1
        C1RXM1bits.SID = 0x000;         // Ignore all bits in comparison
        C1RXM1bits.EID = 0x00000;       // Ignore all bits in comparison
        C1RXM1bits.MIDE = 0;            // Match all message types.
        break;
#if CAN_COUNT>=2
    case 1:
        // assign memory
        C2FIFOBA = KVA_TO_PA(CAN2FIFO);
        // fifo 0 (transmit)
        C2FIFOCON0bits.FSIZE = 15;
        C2FIFOCON0SET = 0x80;
        // fifo 1 (receive)
        C2FIFOCON1bits.FSIZE = 15;
        C2FIFOCON1CLR = 0x80;
        // filter 1
        C2FLTCON0bits.FSEL1 = 1;        // Store messages in FIFO1
        C2FLTCON0bits.MSEL1 = 1;        // Use Mask 1
        C2RXF1bits.SID = 0x000;         // Filter 1 EID
        C2RXF1bits.EID = 0x00000;       // Filter 1 SID
        C2RXF1bits.EXID = 1;            // Filter EID messages
        C2FLTCON0bits.FLTEN1 = 1;       // Enable the filter
        // mask 1
        C2RXM1bits.SID = 0x000;         // Ignore all bits in comparison
        C2RXM1bits.EID = 0x00000;       // Ignore all bits in comparison
        C2RXM1bits.MIDE = 0;            // Match all message types.
        break;
#endif
#if CAN_COUNT>=3
    case 2:
        // assign memory
        C3FIFOBA = KVA_TO_PA(CAN3FIFO);
        // fifo 0 (transmit)
        C3FIFOCON0bits.FSIZE = 15;
        C3FIFOCON0SET = 0x80;
        // fifo 1 (receive)
        C3FIFOCON1bits.FSIZE = 15;
        C3FIFOCON1CLR = 0x80;
        // filter 1
        C3FLTCON0bits.FSEL1 = 1;        // Store messages in FIFO1
        C3FLTCON0bits.MSEL1 = 1;        // Use Mask 1
        C3RXF1bits.SID = 0x000;         // Filter 1 EID
        C3RXF1bits.EID = 0x00000;       // Filter 1 SID
        C3RXF1bits.EXID = 1;            // Filter only EID messages
        C3FLTCON0bits.FLTEN1 = 1;       // Enable the filter
        // mask 1
        C3RXM1bits.SID = 0x000;         // Ignore all bits in comparison
        C3RXM1bits.EID = 0x00000;       // Ignore all bits in comparison
        C3RXM1bits.MIDE = 1;            // Match only message types.
        break;
#endif
#if CAN_COUNT>=4
    case 3:
        // assign memory
        C4FIFOBA = KVA_TO_PA(CAN4FIFO);
        // fifo 0 (transmit)
        C4FIFOCON0bits.FSIZE = 15;
        C4FIFOCON0SET = 0x80;
        // fifo 1 (receive)
        C4FIFOCON1bits.FSIZE = 15;
        C4FIFOCON1CLR = 0x80;
        // filter 1
        C4FLTCON0bits.FSEL1 = 1;        // Store messages in FIFO1
        C4FLTCON0bits.MSEL1 = 1;        // Use Mask 1
        C4RXF1bits.SID = 0x000;         // Filter 1 EID
        C4RXF1bits.EID = 0x00000;       // Filter 1 SID
        C4RXF1bits.EXID = 1;            // Filter only EID messages
        C4FLTCON0bits.FLTEN1 = 1;       // Enable the filter
        // mask 1
        C4RXM1bits.SID = 0x000;         // Ignore all bits in comparison
        C4RXM1bits.EID = 0x00000;       // Ignore all bits in comparison
        C4RXM1bits.MIDE = 1;            // Match only message types.
        break;
#endif
    }

    return 0;
#else
    return -1;
#endif
}

/**
 * @brief Disables the specified CAN bus
 * @param device CAN bus number
 * @return 0 if ok, -1 in case of error
 */
int can_disable(rt_dev_t device)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return -1;
    }

    cans[can].flags.enabled = 0;

    switch (can)
    {
    case 0:
        _CAN1IE = 0;       // disable can global interrupt
        C1CONbits.REQOP = 4;
        while(C1CONbits.OPMOD != 4);
        C1CONbits.ON = 0;  // disable can
        while(C1CONbits.CANBUSY == 1);
        break;
#if CAN_COUNT>=2
    case 1:
        _CAN2IE = 0;       // disable can global interrupt
        C2CONbits.REQOP = 4;
        while(C2CONbits.OPMOD != 4);
        C2CONbits.ON = 0;  // disable can
        while(C2CONbits.CANBUSY == 1);
        break;
#endif
#if CAN_COUNT>=3
    case 2:
        _CAN3IE = 0;       // disable can global interrupt
        C3CONbits.REQOP = 4;
        while(C3CONbits.OPMOD != 4);
        C3CONbits.ON = 0;  // disable can
        while(C3CONbits.CANBUSY == 1);
        break;
#endif
#if CAN_COUNT>=4
    case 3:
        _CAN4IE = 0;       // disable can global interrupt
        C4CONbits.REQOP = 4;
        while(C4CONbits.OPMOD != 4);
        C4CONbits.ON = 0;  // disable can
        while(C4CONbits.CANBUSY == 1);
        break;
#endif
    }

    return 0;
#else
    return -1;
#endif
}

/**
 * @brief Sets configuration (can version and mode) of the specified CAN bus
 * @param device CAN bus number
 * @param mode CAN mode of operation
 * @return 0 if ok, -1 in case of error
 */
int can_setMode(rt_dev_t device, CAN_MODE mode)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    uint8_t modeBits;
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    // check parameters
    switch (mode)
    {
    case CAN_MODE_NORMAL:
        modeBits = 0b000;
        break;
    case CAN_MODE_LISTENONLY:
        modeBits = 0b011;
        break;
    case CAN_MODE_LISTENALL:
        modeBits = 0b111;
        break;
    case CAN_MODE_LOOPBACK:
        modeBits = 0b010;
        break;
    case CAN_MODE_DISABLED:
        modeBits = 0b001;
        break;
    case CAN_MODE_CONFIGURATION:
        modeBits = 0b100;
        break;
    default:
        return -1;
    }
    cans[can].mode = mode;

    switch (can)
    {
    case 0:
        C1CONbits.ON = 1;
        C1CONbits.REQOP = modeBits;
        while (C1CONbits.OPMOD != modeBits);
        break;
#if CAN_COUNT>=2
    case 1:
        C2CONbits.ON = 1;
        C2CONbits.REQOP = modeBits;
        while (C2CONbits.OPMOD != modeBits);
        break;
#endif
#if CAN_COUNT>=3
    case 2:
        C3CONbits.ON = 1;
        C3CONbits.REQOP = modeBits;
        while (C3CONbits.OPMOD != modeBits);
        break;
#endif
#if CAN_COUNT>=4
    case 3:
        C4CONbits.ON = 1;
        C4CONbits.REQOP = modeBits;
        while (C4CONbits.OPMOD != modeBits);
        break;
#endif
    }

    return 0;
#else
    return -1;
#endif
}

/**
 * @brief Returns the current bus mode of operation
 * @param device CAN device number
 * @return current mode of operation
 */
CAN_MODE can_mode(rt_dev_t device)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    return cans[can].mode;
#else
    return 0;
#endif
}

/**
 * @brief Sets bit rate and segments timing
 *
 * Sum of all segments (propagSeg, s1Seg, s2Seg) + 1 must be contained in
 * the range of 8 to 25 quantums.
 *
 * CAN Bit Timing (8-25 Tq) segments computation
 *
 * | Sync | Propag seg | Phase seg 1 |Phase seg 2 |
 *
 * | 1 Tq |   1-8 Tq   |   1-8 Tq    |   1-8 Tq    |
 *
 *                              sample point
 *
 * @param device CAN device number
 * @param bitRate bit rate speed in bit/s
 * @param propagSeg propagation segment duration in number of quantum (1-8)
 * @param s1Seg segment 1 duration in number of quantum (1-8)
 * @param s2Seg segment 2 duration in number of quantum (1-8)
 * @return 0 if ok, -1 in case of error
 */
int can_setBitTiming(rt_dev_t device, uint32_t bitRate, uint8_t propagSeg, uint8_t s1Seg, uint8_t s2Seg)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    uint8_t bitRateDiv;
    uint8_t quantum;
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    if (propagSeg > 8 || s1Seg > 8 || s2Seg > 8)
    {
        return -1;
    }
    if (propagSeg < 1 || s1Seg < 1 || s2Seg < 1)
    {
        return -1;
    }
    quantum = propagSeg + s1Seg + s2Seg + 1;
    if (quantum < 8 || quantum > 25)
    {
        return -1;
    }

    cans[can].bitRate = bitRate;
    cans[can].propagSeg = propagSeg;
    cans[can].s1Seg = s1Seg;
    cans[can].s2Seg = s2Seg;

    /* possible work arround for PIC32MK
    if (can == 2 || can == 3)
    {
        quantum -= 2;
    }*/

    bitRateDiv = sysclock_periphFreq(SYSCLOCK_CLOCK_CAN) / (bitRate * quantum * 2);
    if (bitRateDiv > 64)
    {
        bitRateDiv = 64;
    }
    bitRateDiv--;

    switch (can)
    {
    case 0:
        C1CONbits.ON = 1;
        C1CONbits.REQOP = 4;
        while (C1CONbits.OPMOD != 4);

        C1CFGbits.SJW    = 0;               // Synchronization Jump Width (1-4)
        C1CFGbits.PRSEG  = propagSeg - 1;   // Propagation Time Segment (1-8)
        C1CFGbits.SEG1PH = s1Seg - 1;       // Phase Buffer Segment 1 (1-8)
        C1CFGbits.SEG2PHTS = 1;             // Phase Buffer Segment 2 is freely programmable
        C1CFGbits.SEG2PH = s2Seg - 1;       // Phase Buffer Segment 2 (1-8) SEG2PH >= SEG1PH
        C1CFGbits.BRP    = bitRateDiv;      // bit rate divisor (1-64) * 2
        break;
#if CAN_COUNT>=2
    case 1:
        C2CONbits.ON = 1;
        C2CONbits.REQOP = 4;
        while (C2CONbits.OPMOD != 4);

        C2CFGbits.SJW    = 0;               // Synchronization Jump Width (1-4)
        C2CFGbits.PRSEG  = propagSeg - 1;   // Propagation Time Segment (1-8)
        C2CFGbits.SEG1PH = s1Seg - 1;       // Phase Buffer Segment 1 (1-8)
        C2CFGbits.SEG2PHTS = 1;             // Phase Buffer Segment 2 is freely programmable
        C2CFGbits.SEG2PH = s2Seg - 1;       // Phase Buffer Segment 2 (1-8) SEG2PH >= SEG1PH
        C2CFGbits.BRP    = bitRateDiv;      // bit rate divisor (1-64) * 2
        break;
#endif
#if CAN_COUNT>=3
    case 2:
        C3CONbits.ON = 1;
        C3CONbits.REQOP = 4;
        while (C3CONbits.OPMOD != 4);

        C3CFGbits.SJW    = 0;               // Synchronization Jump Width (1-4)
        C3CFGbits.PRSEG  = propagSeg - 1;   // Propagation Time Segment (1-8)
        C3CFGbits.SEG1PH = s1Seg - 1;       // Phase Buffer Segment 1 (1-8)
        C3CFGbits.SEG2PHTS = 1;             // Phase Buffer Segment 2 is freely programmable
        C3CFGbits.SEG2PH = s2Seg - 1;       // Phase Buffer Segment 2 (1-8) SEG2PH >= SEG1PH
        C3CFGbits.BRP    = bitRateDiv;      // bit rate divisor (1-64) * 2
        break;
#endif
#if CAN_COUNT>=4
    case 3:
        C4CONbits.ON = 1;
        C4CONbits.REQOP = 4;
        while (C4CONbits.OPMOD != 4);

        C4CFGbits.SJW    = 0;               // Synchronization Jump Width (1-4)
        C4CFGbits.PRSEG  = propagSeg - 1;   // Propagation Time Segment (1-8)
        C4CFGbits.SEG1PH = s1Seg - 1;       // Phase Buffer Segment 1 (1-8)
        C4CFGbits.SEG2PHTS = 1;             // Phase Buffer Segment 2 is freely programmable
        C4CFGbits.SEG2PH = s2Seg - 1;       // Phase Buffer Segment 2 (1-8) SEG2PH >= SEG1PH
        C4CFGbits.BRP    = bitRateDiv;      // bit rate divisor (1-64) * 2
        break;
#endif
    }

    return 0;
#else
    return -1;
#endif
}

/**
 * @brief Returns the current bit rate in bits/s
 * @param device CAN device number
 * @return bit rate in bits/s if OK, 0 in case of error
 */
uint32_t can_bitRate(rt_dev_t device)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    return cans[can].bitRate;
#else
    return 0;
#endif
}

/**
 * @brief Gets the effective bit rate in hertz
 * @param device CAN device number
 * @return speed of receive and transmit in bits/s, 0 in case of error
 */
uint32_t can_effectiveBitRate(rt_dev_t device)
{
#if CAN_COUNT>=1

    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    uint16_t bitRateDiv = 1;
    uint8_t quantums = cans[can].propagSeg + cans[can].s1Seg + cans[can].s2Seg + 1;

    switch (can)
    {
    case 0:
        bitRateDiv = (C1CFGbits.BRP + 1) << 1;      // bit rate divisor (1-64) * 2
        break;
#if CAN_COUNT>=2
    case 1:
        bitRateDiv = (C2CFGbits.BRP + 1) << 1;      // bit rate divisor (1-64) * 2
        break;
#endif
#if CAN_COUNT>=3
    case 2:
        bitRateDiv = (C3CFGbits.BRP + 1) << 1;      // bit rate divisor (1-64) * 2
        break;
#endif
#if CAN_COUNT>=4
    case 3:
        bitRateDiv = (C4CFGbits.BRP + 1) << 1;      // bit rate divisor (1-64) * 2
        break;
#endif
    }

    return sysclock_periphFreq(SYSCLOCK_CLOCK_CAN) / (bitRateDiv * quantums);
#else
    return 0;
#endif
}

/**
 * @brief Gets propagation segment duration in quantums
 * @param device CAN device number
 * @return propagation segment duration in quantums, 0 in case of error
 */
uint8_t can_propagSeg(rt_dev_t device)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    return cans[can].propagSeg;
#else
    return 0;
#endif
}

/**
 * @brief Gets segment 1 duration in quantums
 * @param device CAN device number
 * @return segment 1 duration in quantums, 0 in case of error
 */
uint8_t can_s1Seg(rt_dev_t device)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    return cans[can].s1Seg;
#else
    return 0;
#endif
}

/**
 * @brief Gets segment 2 duration in quantums
 * @param device CAN device number
 * @return segment 2 duration in quantums, 0 in case of error
 */
uint8_t can_s2Seg(rt_dev_t device)
{
#if CAN_COUNT>=1
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    return cans[can].s2Seg;
#else
    return 0;
#endif
}

/**
 * @brief Write a can message to fifo
 * @param device CAN device number
 * @param fifo fifo number to put the message
 * @param header CAN message header struct (id, flags, data size)
 * @return 0 if message is successfully putted inside fifo, -1 in case of error
 */
int can_send(rt_dev_t device, uint8_t fifo, CAN_MSG_HEADER *header, char *data)
{
#if CAN_COUNT>=1
    unsigned int i;

    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    CAN_TxMsgBuffer *buffer = NULL;

    switch (can)
    {
    case 0:
        if (C1FIFOINT0bits.TXNFULLIF == 0)
        {
            buffer = NULL;
        }
        else
        {
            buffer = (CAN_TxMsgBuffer *) (PA_TO_KVA1(C1FIFOUA0));
        }
        break;
#if CAN_COUNT>=2
    case 1:
        if (C2FIFOINT0bits.TXNFULLIF == 0)
        {
            buffer = NULL;
        }
        else
        {
            buffer = (CAN_TxMsgBuffer *) (PA_TO_KVA1(C2FIFOUA0));
        }
        break;
#endif
#if CAN_COUNT>=3
    case 2:
        if (C3FIFOINT0bits.TXNFULLIF == 0)
        {
            buffer = NULL;
        }
        else
        {
            buffer = (CAN_TxMsgBuffer *) (PA_TO_KVA1(C3FIFOUA0));
        }
        break;
#endif
#if CAN_COUNT>=4
    case 3:
        if (C4FIFOINT0bits.TXNFULLIF == 0)
        {
            buffer = NULL;
        }
        else
        {
            buffer = (CAN_TxMsgBuffer *) (PA_TO_KVA1(C4FIFOUA0));
        }
        break;
#endif
    }

    if (buffer != NULL)
    {
        // clear the message header
        buffer->messageWord[0] = 0;
        buffer->messageWord[1] = 0;

        // set can id
        if ((header->flags & CAN_VERS2BA) == CAN_VERS2BA)
        {
            buffer->msgEID.IDE = 1;    // extended id
            buffer->msgEID.EID = header->id & 0x3FFFF;   // Message EID
            buffer->msgSID.SID = header->id >> 18; // Message EID
        }
        else
        {
            buffer->msgSID.SID = header->id; // Message EID
        }

        // RTR
        if (header->flags & CAN_RTR)
        {
            buffer->msgEID.RTR = 1;
            header->size = 0;
            buffer->msgEID.DLC = 0;
        }
        else
        {
            // set data and data size
            if (header->size > 8)
            {
                header->size = 8;
            }
            buffer->msgEID.DLC = header->size; // Data Length
            for (i=0; i<header->size; i++)
            {
                buffer->data[i] = data[i];
            }
        }
    }

    switch (can)
    {
    case 0:
        if (buffer != NULL)
        {
            C1FIFOCON0SET = 0x2008; // Set the UINC and TXREQ bit
        }
        else
        {
            C1FIFOCON0SET = 0x0008; // Set the TXREQ bit
        }
        break;
#if CAN_COUNT>=2
    case 1:
        if (buffer != NULL)
        {
            C2FIFOCON0SET = 0x2008; // Set the UINC and TXREQ bit
        }
        else
        {
            C2FIFOCON0SET = 0x0008; // Set the TXREQ bit
        }
        break;
#endif
#if CAN_COUNT>=3
    case 2:
        if (buffer != NULL)
        {
            C3FIFOCON0SET = 0x2008; // Set the UINC and TXREQ bit
        }
        else
        {
            C3FIFOCON0SET = 0x0008; // Set the TXREQ bit
        }
        break;
#endif
#if CAN_COUNT>=4
    case 3:
        if (buffer != NULL)
        {
            C4FIFOCON0SET = 0x2008; // Set the UINC and TXREQ bit
        }
        else
        {
            C4FIFOCON0SET = 0x0008; // Set the TXREQ bit
        }
        break;
#endif
    }

    return 0;
#else
    return -1;
#endif
}

/**
 * @brief Read a can message from fifo
 * @param device CAN device number
 * @param fifo fifo number to read the message
 * @param header CAN message header struct (id, flags, data size)
 * @return 0 if message no readen, -1 in case of error, 1 if a message is readen
 */
int can_rec(rt_dev_t device, uint8_t fifo, CAN_MSG_HEADER *header, char *data)
{
#if CAN_COUNT>=1
    int i;
    uint8_t can = MINOR(device);
    if (can >= CAN_COUNT)
    {
        return 0;
    }

    CAN_FLAGS flagValue = 0;
    CAN_RxMsgBuffer *buffer = NULL;

    switch (can)
    {
    case 0:
        if (C1FIFOINT1bits.RXNEMPTYIF != 1)
        {
            return 0;
        }
        buffer = PA_TO_KVA1(C1FIFOUA1);
        break;
#if CAN_COUNT>=2
    case 1:
        if (C2FIFOINT1bits.RXNEMPTYIF != 1)
        {
            return 0;
        }
        buffer = PA_TO_KVA1(C2FIFOUA1);
        break;
#endif
#if CAN_COUNT>=3
    case 2:
        if (C3FIFOINT1bits.RXNEMPTYIF != 1)
        {
            return 0;
        }
        buffer = PA_TO_KVA1(C3FIFOUA1);
        break;
#endif
#if CAN_COUNT>=4
    case 3:
        if (C4FIFOINT1bits.RXNEMPTYIF != 1)
        {
            return 0;
        }
        buffer = PA_TO_KVA1(C4FIFOUA1);
        break;
#endif
    }

    // ID
    if (buffer->msgEID.IDE == 1)
    {
        flagValue = flagValue + CAN_VERS2BA; // extended ID
        header->id = buffer->msgEID.EID + (buffer->msgSID.SID << 18);
    }
    else
    {
        header->id = buffer->msgSID.SID;
    }

    // data read and copy
    header->size = buffer->msgEID.DLC;
    for (i=0; i<header->size; i++)
	    data[i] = buffer->data[i];

    // flags
    if (buffer->msgEID.SRR == 1)
    {
        flagValue += CAN_RTR;
    }
    header->flags = flagValue;

    switch (can)
    {
    case 0:
        C1FIFOCON1SET = 0x2000; // mark as read
        break;
#if CAN_COUNT>=2
    case 1:
        C2FIFOCON1SET = 0x2000; // mark as read
        break;
#endif
#if CAN_COUNT>=3
    case 2:
        C3FIFOCON1SET = 0x2000; // mark as read
        break;
#endif
#if CAN_COUNT>=4
    case 3:
        C4FIFOCON1SET = 0x2000; // mark as read
        break;
#endif
    }

    // flags
    if (buffer->msgEID.RTR == 1)
    {
        flagValue += CAN_RTR;
    }
    header->flags = flagValue;

    return 1;
#else
    return -1;
#endif
}