Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: MBED_LIN_RGB_Master_Example
LinMaster.cpp
- Committer:
- bollenn
- Date:
- 2015-05-26
- Revision:
- 3:3656b0de0e43
- Parent:
- 2:6d4c7f841a5d
- Child:
- 4:41b153e9a39c
File content as of revision 3:3656b0de0e43:
/* * Master device LIN communication library for mbed * * Copyright (C) 2014 TASS Belgium NV * * 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) { 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) { DigitalInOut LinOutPin(this->MyOutPin); LinOutPin.output(); LinOutPin.write(1); DigitalInOut LinInPin(this->MyInPin); LinInPin.input(); this->DriverState = IDLE; return ( true ); } bool LinMaster::baudrate(uint16_t uBaud) { bool blReturn = false; if ((uBaud > 0) && (uBaud <= 20000)) { this->u16HalfBitPeriod = 1000000/(2*uBaud); blReturn = true; } return ( blReturn ); } uint16_t LinMaster::baudrate(void) { return ( 1000000 / (2 * this->u16HalfBitPeriod) ); } bool LinMaster::tx_frame(Frame_t * ptrFrame) { bool blReturn = false; DigitalInOut LinInPin(this->MyInPin); LinInPin.input(); if ( (this->DriverState == IDLE) && (LinInPin.read() == 1) ) { /* Clear and initialize all registers */ /* Disable half bit interrupt */ this->MyTicker.detach(); InterruptIn IntPin(this->MyInPin); IntPin.fall(this, &LinMaster::PinEventHndl); IntPin.disable_irq(); DigitalInOut LinPin(this->MyOutPin); LinPin.output(); LinPin.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 */ 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; } 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 LinOutPin(this->MyOutPin); LinOutPin.output(); if (this->FrameStatus < Break_OK) { /* 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 { /* Break field was transmitted */ if (this->FrameLength == 0) { /* 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(); } else { DigitalInOut LinInPin(this->MyInPin); LinInPin.input(); /* 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); } 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 { /* 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; 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; } } } } } 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 */