Nico Bollen / LIN

Dependents:   MBED_LIN_RGB_Master_Example

LinMaster.cpp

Committer:
bollenn
Date:
2020-06-15
Revision:
8:63d341e53cce
Parent:
5:b42737f5dabc

File content as of revision 8:63d341e53cce:

/* 
 * Master device LIN communication library for mbed
 *
 * Copyright (C) 2015 Bollen Nico
 * 
 * Released under GPL v2
 *
 * Other licensing models might apply at the sole discretion of the copyright holders.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
#include "LinMaster.h"

const uint8_t breakPeriodMessage = 40;                      /* number of timer overflows in the break field during normal LIN messages */
const uint8_t breakPeriodAcfg = 74;                         /* number of timer overflows in the break field during autoconfig messages */

LinMaster::LinMaster(PinName InPin, PinName OutPin) : LinOutPin(OutPin), LinInPin(InPin, PullUp)
{
    this->DriverState = INIT;
    this->LastError = NoError;
    
    this->LinOutPin.write(1);

    this->LinInPin.disable_irq();
    this->LinInPin.fall(NULL);
    
    this->DriverState = IDLE;
    
    (void)this->baudrate(9600);
}

LinMaster::~LinMaster()
{
    this->LinInPin.disable_irq();
    this->LinInPin.fall(NULL);
    this->HalfbitTicker.detach(); 
    this->TimeoutTicker.detach();
}

bool LinMaster::init(void)
{
    this->LinOutPin.write(1);

    this->LinInPin.enable_irq();
    this->LinInPin.fall(NULL);

    this->DriverState = IDLE;

    return ( true );
}

bool LinMaster::baudrate(uint16_t uBaud)
{
    bool blReturn = false;

    if ((uBaud > 0) && (uBaud <= 20000))
    {
        this->u16HalfBitPeriod = std::chrono::microseconds(1000000 / ( 2 * uBaud));
        blReturn = true;
    }

    return ( blReturn );
}

uint16_t LinMaster::baudrate(void)
{
    return ( 1000000 / (2 * this->u16HalfBitPeriod.count()) );
}

bool LinMaster::send_frame(Frame_t * ptrFrame)
{
    bool blReturn = false;

    if ( (this->DriverState == IDLE) && (this->LinInPin.read() == 1) )
    {
        /* Clear and initialize all registers */
        memset(this->RXbuf, 0, 11);

        /* Disable ticker interrupt */
        this->HalfbitTicker.detach();
        this->TimeoutTicker.detach();

        this->LinInPin.fall(NULL);

        this->LinOutPin.write(1);

        this->DriverState = TRANSMIT;                       /* State of the LIN bus is transceiving a frame */
        this->LastError = NoError;
        this->FrameStatus = FStart;
        this->ByteStatus = BStart;
        this->RXbufIndex = 0;                               /* Reset index in the receiver buffer */
        this->TXbufIndex = 0;                               /* Reset index in the transmit buffer */

        /* Set the correct brake-length */
        if (ptrFrame->Brake == AutoConfig)
        {
            this->breakLength = breakPeriodAcfg;
        }
        else
        {
            this->breakLength = breakPeriodMessage;
        }

        this->FrameLength = 1 + 1 + ptrFrame->DataLen + 1;  /* Frame length = Sync + Frame ID + Data Len + CRC */
        this->linMessageType = ptrFrame->FrameType;

        /* Create the correct frame buffer */
        this->TXbuf[0] = 0x55;                              /* Sync field */
        this->TXbuf[1] = this->parity(ptrFrame->FrameID);   /* Frame ID */

        if (this->linMessageType == M2S)
        {
            uint16_t u16Crc;
            uint8_t i;

            if (ptrFrame->CrcType == Enhanced)
            {
                u16Crc = TXbuf[1];
            }
            else
            {
                u16Crc = 0;
            }

            for (i = 0; i < ptrFrame->DataLen; i++)
            {
                this->TXbuf[i + 2] = ptrFrame->Data[i];     /* Data */
                u16Crc += ptrFrame->Data[i];
                if (u16Crc >= 256)
                {
                    u16Crc -= 255;
                }
            }
            this->TXbuf[ptrFrame->DataLen + 2] = (uint8_t)(~u16Crc);
        }
        else
        {
            /* S2M message */

            /* Calculate RX timeout */
            this->FrameTimeout = std::chrono::microseconds(((ptrFrame->DataLen + 1) * 14) * 2 * this->u16HalfBitPeriod);
        }

        /* Configure and start the half bit timer */
        this->HalfbitTicker.attach(callback(this, &LinMaster::TickEventHndl), this->u16HalfBitPeriod);

        blReturn = true;
    }
    
    return ( blReturn );
}

bool LinMaster::get_rx_data(Frame_t &ptrFrame)
{
    uint16_t u16Crc;
    uint8_t i;

    if ( (this->DriverState != IDLE) || (this->LastError != NoError))
    {
        return (false);
    }

    /* Copy data and check RX frame CRC */
    if (ptrFrame.CrcType == Enhanced)
    {
        u16Crc = RXbuf[1];
    }
    else
    {
        u16Crc = 0;
    }

    for (i = 0; i < ptrFrame.DataLen; i++)
    {
        ptrFrame.Data[i] = RXbuf[1 + 1 + i];
        u16Crc += RXbuf[1 + 1 + i];
        if (u16Crc >= 256)
        {
            u16Crc -= 255;
        }
    }
    
    if (this->RXbuf[ptrFrame.DataLen + 2] == (uint8_t)(~u16Crc))
    {
        return (true);
    }
    else
    {
        return (false);
    }
}

void LinMaster::TickEventHndl(void)
{
    int PinLevel = this->LinInPin.read();

    if (this->FrameStatus < Break_OK)
    {
        /* Do break field transmission */
        if (this->breakLength > 2)
        {
            /* Dominant Level */
            this->LinOutPin.write(0); 
        }
        else
        {
            /* Recessive Level */
            this->LinOutPin.write(1); 
        }

        if (this->breakLength > 0)
        {
            this->breakLength--;
        }
        else
        {
            this->FrameStatus = Break_OK;
        }
    }
    else
    {
        /* Break field was transmitted */
        if (this->FrameLength == 0)
        {
            /* No data needs to be transmitted */
            
            /* Disable ticker interrupt */
            this->HalfbitTicker.detach();
            this->TimeoutTicker.detach();
            
            /* Disable LIN bus level interrupt */
            this->LinInPin.fall(NULL);
        }
        else
        {
            /* Data needs to be transmitted or received */
            if ( (this->linMessageType == S2M) &&
                 (this->FrameStatus >= ID_OK))
            {
                if (this->ByteStatus > BStart)
                {
                    /* Not waiting for start bit */
                    this->ByteStatus = static_cast<ByteStatus_t>(static_cast<int>(this->ByteStatus)+1);
                }

                /* S2M message data receiving */
                switch (this->ByteStatus)
                {
                case StartbitSample:
                    if (PinLevel == 0)
                    {
                        /* OK */
                    }
                    else
                    {
                        /* TODO error */
                    }

                    break;

                case Databit0Sample:
                case Databit1Sample:
                case Databit2Sample:
                case Databit3Sample:
                case Databit4Sample:
                case Databit5Sample:
                case Databit6Sample:
                case Databit7Sample:
                    /* Mid of single bit time, do sampling */
                    this->RXbuf[RXbufIndex] >>= 1;
                    this->RXbuf[RXbufIndex] |= (PinLevel << 7);
                    break;

                case StopbitSample:
                    /* End of stop bit, stop Timer IRQ */

                    /* Enable LIN bus level interrupt */
                    this->LinInPin.fall(callback(this, &LinMaster::PinEventHndl));

                    /* Disable half bit interrupt */
                    this->HalfbitTicker.detach();

                    /* Check the current bus level */
                    if (PinLevel == 0)
                    {
                        this->LastError = FramingErr;       /* stop bit not valid => framing error */
                    }

                    this->RXbufIndex++;
                    this->FrameStatus = static_cast<FrameStatus_t>(static_cast<int>(this->FrameStatus)+1);
                    this->ByteStatus = BStart;

                    if ((this->RXbufIndex >= this->FrameLength) ||
                        (this->LastError != NoError))
                    {
                        /* All requested data bytes are received or an error occurred */
                        this->DriverState = IDLE;
                        
                        /* Disable LIN bus level interrupt */
                        this->LinInPin.fall(NULL);

                        /* Disable timeout ticker */
                        this->TimeoutTicker.detach();
                    }
                    else
                    {
                        /* Wait for a new data byte */
                    }
                    break;
                    
                case Databit0Edge:
                case Databit1Edge:
                case Databit2Edge:
                case Databit3Edge:
                case Databit4Edge:
                case Databit5Edge:
                case Databit6Edge:
                case Databit7Edge:
                case StopbitEdge:
                case BDone:
                case BStart:
                default:
                    /* Do nothing */
                    break;
                }
            }
            else
            {
                /* Transmission of Sync + Frame ID and M2S frame data */
                this->ByteStatus = static_cast<ByteStatus_t>(static_cast<int>(this->ByteStatus)+1);

                switch (this->ByteStatus)
                {
                case StartbitEdge:
                    /* Start bit : start */
                    this->LinOutPin.write(0); 
                    break;

                case StartbitSample:
                    /* Start bit : mid */
                    break;

                case Databit0Edge:
                case Databit1Edge:
                case Databit2Edge:
                case Databit3Edge:
                case Databit4Edge:
                case Databit5Edge:
                case Databit6Edge:
                case Databit7Edge:
                    /* Start of new bit time */
                    if (this->TXbuf[this->TXbufIndex] & 0x01)
                    {
                        /* Recessive Level */
                        this->LinOutPin.write(1); 
                    }
                    else
                    {
                        /* Dominant Level */
                        this->LinOutPin.write(0); 
                    }

                    this->TXbuf[this->TXbufIndex] >>= 1;
                    break;

                case Databit0Sample:
                case Databit1Sample:
                case Databit2Sample:
                case Databit3Sample:
                case Databit4Sample:
                case Databit5Sample:
                case Databit6Sample:
                case Databit7Sample:
                    /* Odd overflow, mid of bit time ==> sample the bus for RX */
                    this->RXbuf[this->RXbufIndex] >>= 1;
                    this->RXbuf[this->RXbufIndex] |= ((PinLevel << 7) & 0x80);
                    break;

                case StopbitEdge:
                    /* Stop bit : start */
                    this->LinOutPin.write(1); 
                    break;

                case StopbitSample:
                    /* Stop bit : mid / level check */
                    if (PinLevel == 0)
                    {
                        /* Stop bit not valid => framing error */
                        this->LastError = FramingErr;
                        this->DriverState = IDLE;

                        /* Disable ticker interrupt */
                        this->HalfbitTicker.detach();
                        this->TimeoutTicker.detach();

                        /* Disable LIN bus level interrupt */
                        this->LinInPin.fall(NULL);
                    }

                    this->FrameStatus = static_cast<FrameStatus_t>(static_cast<int>(this->FrameStatus)+1);
                    this->TXbufIndex++;
                    this->RXbufIndex++;

                    if ((this->linMessageType == S2M) && (this->FrameStatus == ID_OK))
                    {
                        /* Stop bit of header is sent, now start receiving data bytes */

                        /* Enable LIN bus level interrupt */
                        this->LinInPin.fall(callback(this, &LinMaster::PinEventHndl));

                        /* Disable half bit interrupt */
                        this->HalfbitTicker.detach();

                        /* Enable frame timeout interrupt */
                        this->TimeoutTicker.attach(callback(this, &LinMaster::RXtimeoutEventHndl), this->FrameTimeout);
                    }
                    break;

                case BDone:
                    /* Stop bit : finished */
                    this->ByteStatus = BStart;

                    if (this->TXbufIndex >= this->FrameLength)
                    {
                        /* M2S frame, Last byte is sent */
                        this->LastError = NoError;
                        this->DriverState = IDLE;

                        /* Disable LIN bus level interrupt */
                        this->LinInPin.fall(NULL);

                        /* Disable ticker interrupts */
                        this->TimeoutTicker.detach();
                        this->HalfbitTicker.detach();
                    }
                    break;

                default:
                    break;
                }
            }
        }
    }
}

void LinMaster::RXtimeoutEventHndl(void)
{
    /* Disable LIN bus level interrupt */
    this->LinInPin.fall(NULL);

    /* Disable half bit interrupt */
    this->HalfbitTicker.detach();
    this->TimeoutTicker.detach();

    this->DriverState = IDLE;
    this->LastError = NoSlaveResp;
}

void LinMaster::PinEventHndl(void)
{
    switch (this->DriverState)
    {
    case TRANSMIT:
    case RECEIVE:
    {
        this->ByteStatus = StartbitSample;                  /* Set status of the received byte */

        /* Reset ticker */
        this->HalfbitTicker.attach(callback(this, &LinMaster::TickEventHndl), this->u16HalfBitPeriod);

        /* Disable LIN bus level interrupt */
        this->LinInPin.fall(NULL);
        break;
    }

    case IDLE:
    case DOMINANT:
    case TXWAKEUP:
        break;

    case RXWAKEUP:
    default:
        this->DriverState = RXWAKEUP;                       /* It's a wake up pulse */
        break;
    }
}

/** Calculate the parity bits
 *
 * @param u8BYTE original byte
 * @return BYTE including parity bits
 */
uint8_t LinMaster::parity(uint8_t u8BYTE)
{
    uint8_t P0 = 0;
    uint8_t P1 = 0;

    /* P0 = ID0 + ID1 + ID2 + ID4
     * P1 = ~(ID1 + ID3 + ID4 + ID5)
     */
    if ((u8BYTE & (1 << 0)) != 0)
    {
        P0 = ~P0;
    }

    if ((u8BYTE & (1 << 1)) != 0)
    {
        P0 = ~P0;
    }

    if ((u8BYTE & (1 << 2)) != 0)
    {
        P0 = ~P0;
    }

    if ((u8BYTE & (1 << 4)) != 0)
    {
        P0 = ~P0;
    }

    if ((u8BYTE & (1 << 1)) != 0)
    {
        P1 = ~P1;
    }

    if ((u8BYTE & (1 << 3)) != 0)
    {
        P1 = ~P1;
    }

    if ((u8BYTE & (1 << 4)) != 0)
    {
        P1 = ~P1;
    }

    if ((u8BYTE & (1 << 5)) != 0)
    {
        P1 = ~P1;
    }

    P1 = ~P1;

    u8BYTE &= 0x3f;                                         /* Delete MSB's */

    if (P0 != 0)
    {
        u8BYTE |= (1 << 6);
    }

    if (P1 != 0)
    {
        u8BYTE |= (1 << 7);
    }

    return (u8BYTE);
}

/* EOF */