Nico Bollen / LIN

Dependents:   MBED_LIN_RGB_Master_Example

Revision:
3:3656b0de0e43
Parent:
2:6d4c7f841a5d
Child:
4:41b153e9a39c
--- a/LinMaster.cpp	Sun May 11 13:14:16 2014 +0000
+++ b/LinMaster.cpp	Tue May 26 08:33:46 2015 +0000
@@ -25,75 +25,134 @@
  
 #include "LinMaster.h"
 
-LinMaster::LinMaster(PinName Pin)
+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)
 {
-    DriverStat   = INIT;
-    FrameStat    = READY;
-    MyPin        = Pin;
-    u8BreakLen   = 20;
-    u8DelimLen   = 4;
-    (void)Baudrate(9600);
+    this->DriverState  = INIT;
+    this->LastError    = NoError;
+    this->MyInPin      = InPin;
+    this->MyOutPin     = OutPin;
+    (void)this->baudrate(9600);
 }
 
 LinMaster::~LinMaster()
 {
-    
+    this->MyTicker.detach(); 
+    this->MyTimer.stop();
 }
 
-bool LinMaster::Init(void)
+bool LinMaster::init(void)
 {
-    DigitalInOut LinPin(MyPin);
-    LinPin.output();
-    LinPin.write(1);
+    DigitalInOut LinOutPin(this->MyOutPin);
+    LinOutPin.output();
+    LinOutPin.write(1);
+    DigitalInOut LinInPin(this->MyInPin);
+    LinInPin.input();
     
-    DriverStat = IDLE;
+    this->DriverState = IDLE;
     
     return ( true );
 }
 
-bool LinMaster::Baudrate(uint16_t uBaud)
+bool LinMaster::baudrate(uint16_t uBaud)
 {
     bool blReturn = false;
     
     if ((uBaud > 0) && (uBaud <= 20000))
     {
-        u16BitPeriod = 1000000/uBaud;
+        this->u16HalfBitPeriod = 1000000/(2*uBaud);
         blReturn = true;
     }
     
     return ( blReturn );
 }
 
-uint16_t LinMaster::Baudrate(void)
+uint16_t LinMaster::baudrate(void)
 {
-    return ( 1000000/u16BitPeriod );
+    return ( 1000000 / (2 * this->u16HalfBitPeriod) );
 }
 
-bool LinMaster::SendFrame(FrameDir Dir, uint8_t u8ID, uint8_t* ptrData, uint8_t u8Len)
+bool LinMaster::tx_frame(Frame_t * ptrFrame)
 {
     bool blReturn = false;
-    uint8_t i;
+    DigitalInOut LinInPin(this->MyInPin);
+    LinInPin.input();
     
-    if (DriverStat == IDLE)
+    if ( (this->DriverState == IDLE) && (LinInPin.read() == 1) )
     {
-        DriverStat   = TRANSMIT;
-        FrameStat    = BREAK;
-        u8NextBusLvl = 0;
-        u8TickCnt    = 0;
-        Direction    = Dir;
-        u8FrameID    = u8ID;
-        u8FrameLen   = u8Len;
+        /* Clear and initialize all registers */
+        
+        /* Disable half bit interrupt */
+        this->MyTicker.detach();
         
-        for (i=0; i<u8Len; i++)
-        {
-            u8FrameData[i]  = *ptrData++;
-        }
+        InterruptIn IntPin(this->MyInPin);
+        IntPin.fall(this, &LinMaster::PinEventHndl);
+        IntPin.disable_irq();
         
-        DigitalInOut LinPin(MyPin);        
+        DigitalInOut LinPin(this->MyOutPin);        
         LinPin.output();
         LinPin.write(1);
         
-        MyTicker.attach_us(this, &LinMaster::TickEventHndl, u16BitPeriod);
+        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 */
+            this->RXtimeout = ((ptrFrame->DataLen + 1) * 14) * 2;   /* Calculate RX timeout in half bit-times */
+            this->RXtimeoutSubCTR = 0;
+        }
+    
+        /* Configure and start the half bit timer */
+        this->MyTicker.attach_us(this, &LinMaster::TickEventHndl, this->u16HalfBitPeriod);
         
         blReturn = true;
     }
@@ -101,134 +160,429 @@
     return ( blReturn );
 }
 
+bool LinMaster::rx_frame(Frame_t *ptrFrame)
+{
+    uint16_t u16Crc;
+    uint8_t i;
+
+    if (this->DriverState != IDLE)
+    {
+        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)
 {
-    DigitalInOut LinPin(MyPin);        
-    bool blByteSend = false;
+    DigitalInOut LinOutPin(this->MyOutPin);  
+    LinOutPin.output();
     
-    if (u8NextBusLvl != 0)
+    if (this->FrameStatus < Break_OK)
     {
-        LinPin.output();
-        LinPin.write(1);
+        /* Do break field transmission */
+        if (this->breakLength > 2)
+        {
+            /* Dominant Level */
+            LinOutPin.write(0); 
+        }
+        else
+        {
+            /* Recessive Level */
+            LinOutPin.write(1); 
+        }
+
+        if (this->breakLength > 0)
+        {
+            this->breakLength--;
+        }
+        else
+        {
+            this->FrameStatus = Break_OK;
+        }
     }
     else
     {
-        LinPin.output();
-        LinPin.write(0);
-    }
-    
-    u8TickCnt++;
-    
-    switch (FrameStat)
-    {
-    case BREAK:
-        u8NextBusLvl = 0;
-        if (u8TickCnt >= u8BreakLen)
+        /* Break field was transmitted */
+        if (this->FrameLength == 0)
         {
-            u8TickCnt = 0;
-            FrameStat = DELIMITER;
-            u8NextBusLvl = 1;
+            /* No data needs to be transmitted */
+            
+            /* Disable half bit interrupt */
+            this->MyTicker.detach();
+            
+            /* Disable LIN bus level interrupt */
+            InterruptIn IntPin(this->MyInPin);
+            IntPin.disable_irq();
         }
-        break;
-        
-    case DELIMITER:
-        if (u8TickCnt >= u8DelimLen)
+        else
         {
-            u8TickCnt = 0;
-            FrameStat = SYNC;
-            u8Byte = 0xAA;
-        }
-        break;
-        
-    case SYNC:
-        blByteSend = true;
-        if (u8TickCnt >= 10)
-        {
-            u8TickCnt = 0;
-            FrameStat = ID;
-            u8Byte = u8FrameID;
-        }
-        break;
-        
-    case ID:
-        blByteSend = true;
-        if (u8TickCnt >= 10)
-        {
-            u8TickCnt = 0;
-            FrameStat = DATA;
-            u8ByteOnLin = 0;
-            if (Direction == M2S)
+            DigitalInOut LinInPin(this->MyInPin);
+            LinInPin.input();
+            
+            /* Data needs to be transmitted or received */
+            if ( (this->linMessageType == S2M) &&
+                 (this->FrameStatus >= ID_OK))
             {
-                u8Byte = u8FrameData[u8ByteOnLin];
+                if (this->ByteStatus > BStart)
+                {
+                    /* Not waiting for start bit */
+                    this->ByteStatus = static_cast<ByteStatus_t>(static_cast<int>(this->ByteStatus)+1);
+                }
+
+                if (this->RXtimeout > 0)
+                {
+                    this->RXtimeout--;
+                }
+                else
+                {
+                    this->DriverState = IDLE;
+                    this->LastError = NoSlaveResp;
+                    
+                    /* Disable half bit interrupt */
+                    this->MyTicker.detach();
+                    
+                    /* Disable LIN bus level interrupt */
+                    InterruptIn IntPin(this->MyInPin);
+                    IntPin.disable_irq();
+                }
+
+                /* S2M message data receiving */
+                switch (this->ByteStatus)
+                {
+                case StartbitSample:
+                    if (LinInPin.read() == 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] |= (LinInPin.read() << 7);
+                    break;
+
+                case StopbitSample:
+                    /* End of stop bit, stop Timer IRQ */
+                    this->RXbufIndex++;
+                    this->FrameStatus = static_cast<FrameStatus_t>(static_cast<int>(this->FrameStatus)+1);
+                    this->ByteStatus = BStart;
+
+                    /* Check the current bus level */
+                    if (LinInPin.read() == 0)
+                    {
+                        this->LastError = FramingErr;       /* stop bit not valid => framing error */
+                    }
+
+                    if ((this->RXbufIndex >= this->FrameLength) ||
+                        (this->LastError != NoError))
+                    {
+                        /* All requested data bytes are received or an error occurred */
+                        this->DriverState = IDLE;
+                        
+                        /* Disable half bit interrupt */
+                        this->MyTicker.detach();
+                        
+                        /* Disable LIN bus level interrupt */
+                        InterruptIn IntPin(this->MyInPin);
+                        IntPin.disable_irq();
+                    }
+                    else
+                    {
+                        /* Wait for a new data byte */
+                        
+                        /* Disable LIN bus level interrupt */
+                        this->MyTimer.start();
+                        InterruptIn IntPin(this->MyInPin);
+                        IntPin.enable_irq();
+                    }
+
+                    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
             {
-                blByteSend = false;
-            }
-        }
-        break;
-        
-    case DATA:
-        if (Direction == M2S)
-        {
-            blByteSend = true;
-        }
-        else
-        {
-            blByteSend = false;
-        }
-        
-        if (u8TickCnt >= 10)
-        {
-            if (Direction == S2M)
-            {
-                u8FrameData[u8ByteOnLin] = u8Byte;
-            }
+                /* 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 */
+                    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 */
+                        LinOutPin.write(1); 
+                    }
+                    else
+                    {
+                        /* Dominant Level */
+                        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] |= (LinInPin.read() << 7);
+                    break;
+
+                case StopbitEdge:
+                    /* Stop bit : start */
+                    LinOutPin.write(1); 
+                    break;
 
-            u8ByteOnLin++;
-            u8TickCnt = 0;
-            
-            if (u8ByteOnLin >= u8FrameLen)
-            {
-                /* Sending/Receiving is ended */
-                FrameStat = READY;
-                blByteSend = false;
-                u8NextBusLvl = 1;
+                case StopbitSample:
+                    /* Stop bit : mid / level check */
+                    if (LinInPin.read() == 0)
+                    {
+                        /* Stop bit not valid => framing error */
+                        this->LastError = FramingErr;
+                        this->DriverState = IDLE;
+                        
+                        /* Disable half bit interrupt */
+                        this->MyTicker.detach();
+                        
+                        /* Disable LIN bus level interrupt */
+                        InterruptIn IntPin(this->MyInPin);
+                        IntPin.disable_irq();
+                    }
+
+                    break;
+
+                case BDone:
+                    /* Stop bit : finished */
+                    this->ByteStatus = BStart;
+                    this->FrameStatus = static_cast<FrameStatus_t>(static_cast<int>(this->FrameStatus)+1);
+                    this->TXbufIndex++;
+                    this->RXbufIndex++;
+
+                    if (this->linMessageType == S2M)
+                    {
+                        /* S2M frame */
+                        if (this->FrameStatus == ID_OK)
+                        {
+                            /* Stop bit of header is sent, now start receiving data bytes */
+                            this->MyTimer.start();
+                            InterruptIn IntPin(this->MyInPin);
+                            IntPin.enable_irq();
+                        }
+                    }
+                    else if (this->TXbufIndex >= this->FrameLength)
+                    {
+                        /* M2S frame, Last byte is sent */
+                        this->LastError = NoError;
+                        this->DriverState = IDLE;
+                        
+                        /* Disable half bit interrupt */
+                        this->MyTicker.detach();
+                        
+                        /* Disable LIN bus level interrupt */
+                        InterruptIn IntPin(this->MyInPin);
+                        IntPin.disable_irq();
+                    }
+
+                    break;
+
+                default:
+                    break;
+                }
             }
-            else if (Direction == M2S)
-            {
-                u8Byte = u8FrameData[u8ByteOnLin];
-            }
-        }
-        break;
-        
-    case READY:
-    default:
-        u8NextBusLvl = 1;
-        MyTicker.detach();
-        FrameStat  = READY;
-        DriverStat = IDLE;
-        break;
-    }
-    
-    if (blByteSend == true)
-    {
-        switch (u8TickCnt)
-        {
-        case 0:
-            /* Start Bit */
-            u8NextBusLvl = 0;
-            break;
-        case 9:
-            /* Stop Bit */
-            u8NextBusLvl = 1;
-            break;
-        default:
-            u8NextBusLvl = u8Byte & 0x1;
-            u8Byte >>= 1;
-            break;
         }
     }
 }
 
+void LinMaster::PinEventHndl(void)
+{
+    switch (this->DriverState)
+    {
+    case TRANSMIT:
+    case RECEIVE:
+    {
+        this->ByteStatus = StartbitEdge;                    /* Set status of the received byte */
+        this->RXtimeoutSubCTR += this->MyTimer.read_us();
+        this->MyTimer.stop();
+        
+        /* Reset ticker */
+        this->MyTicker.detach();
+        this->MyTicker.attach_us(this, &LinMaster::TickEventHndl, this->u16HalfBitPeriod);
+        
+        /* Disable LIN bus level interrupt */
+        InterruptIn IntPin(this->MyInPin);
+        IntPin.disable_irq();
+
+        if (this->RXtimeoutSubCTR > this->u16HalfBitPeriod)
+        {
+            this->RXtimeoutSubCTR -= this->u16HalfBitPeriod;
+            if (this->RXtimeout > 0)
+            {
+                this->RXtimeout--;
+            }
+        }
+
+        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 */    
\ No newline at end of file