| /* |
| * Copyright (c) 2017, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file implements the OpenThread platform abstraction for UART communication. |
| * |
| */ |
| |
| #include <stdint.h> |
| #include "fsl_device_registers.h" |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <utils/code_utils.h> |
| #include "openthread/types.h" |
| #include "openthread/platform/uart.h" |
| |
| #include "fsl_clock.h" |
| #include "fsl_port.h" |
| #include "fsl_lpuart.h" |
| |
| enum |
| { |
| kPlatformClock = 32000000, |
| kBaudRate = 115200, |
| kReceiveBufferSize = 256, |
| }; |
| |
| static void processReceive(); |
| static void processTransmit(); |
| |
| static const uint8_t *sTransmitBuffer = NULL; |
| static uint16_t sTransmitLength = 0; |
| static bool sTransmitDone = false; |
| |
| typedef struct RecvBuffer |
| { |
| // The data buffer |
| uint8_t mBuffer[kReceiveBufferSize]; |
| // The offset of the first item written to the list. |
| uint16_t mHead; |
| // The offset of the next item to be written to the list. |
| uint16_t mTail; |
| } RecvBuffer; |
| |
| static RecvBuffer sReceive; |
| |
| otError otPlatUartEnable(void) |
| { |
| lpuart_config_t config; |
| |
| sReceive.mHead = 0; |
| sReceive.mTail = 0; |
| |
| /* Pin MUX */ |
| CLOCK_EnableClock(kCLOCK_PortC); |
| PORT_SetPinMux(PORTC, 6, kPORT_MuxAlt4); |
| PORT_SetPinMux(PORTC, 7, kPORT_MuxAlt4); |
| |
| /* Set OSCERCLK as LPUART Rx/Tx clock */ |
| CLOCK_SetLpuartClock(2); |
| |
| LPUART_GetDefaultConfig(&config); |
| config.enableRx = 1; |
| config.enableTx = 1; |
| config.baudRate_Bps = kBaudRate; |
| LPUART_Init(LPUART0, &config, kPlatformClock); |
| LPUART_EnableInterrupts(LPUART0, kLPUART_RxDataRegFullInterruptEnable); |
| |
| NVIC_ClearPendingIRQ(LPUART0_IRQn); |
| NVIC_EnableIRQ(LPUART0_IRQn); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError otPlatUartDisable(void) |
| { |
| NVIC_DisableIRQ(LPUART0_IRQn); |
| return OT_ERROR_NONE; |
| } |
| |
| otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| otEXPECT_ACTION(sTransmitBuffer == NULL, error = OT_ERROR_BUSY); |
| |
| sTransmitBuffer = aBuf + 1; |
| sTransmitLength = aBufLength - 1; |
| sTransmitDone = false; |
| LPUART_WriteByte(LPUART0, *aBuf); |
| LPUART_ClearStatusFlags(LPUART0, kLPUART_TxDataRegEmptyFlag); |
| LPUART_EnableInterrupts(LPUART0, kLPUART_TxDataRegEmptyInterruptEnable); |
| |
| exit: |
| return error; |
| } |
| |
| static void processTransmit(void) |
| { |
| if (sTransmitBuffer && sTransmitDone) |
| { |
| sTransmitDone = false; |
| sTransmitBuffer = NULL; |
| otPlatUartSendDone(); |
| } |
| |
| return; |
| } |
| |
| void kw41zUartProcess(void) |
| { |
| processReceive(); |
| processTransmit(); |
| } |
| |
| static void processReceive(void) |
| { |
| // Copy tail to prevent multiple reads |
| uint16_t tail = sReceive.mTail; |
| |
| if (sReceive.mHead > tail) |
| { |
| otPlatUartReceived(sReceive.mBuffer + sReceive.mHead, kReceiveBufferSize - sReceive.mHead); |
| |
| // Reset the buffer mHead back to zero |
| sReceive.mHead = 0; |
| } |
| |
| if (sReceive.mHead != tail) |
| { |
| otPlatUartReceived(sReceive.mBuffer + sReceive.mHead, tail - sReceive.mHead); |
| |
| // Set mHead to the local tail we have cached |
| sReceive.mHead = tail; |
| } |
| } |
| |
| void LPUART0_IRQHandler(void) |
| { |
| uint32_t interrupts = LPUART_GetEnabledInterrupts(LPUART0); |
| uint8_t rx_data; |
| |
| /* Check if data was received */ |
| while (LPUART_GetStatusFlags(LPUART0) & (kLPUART_RxDataRegFullFlag)) |
| { |
| rx_data = LPUART_ReadByte(LPUART0); |
| LPUART_ClearStatusFlags(LPUART0, kLPUART_RxDataRegFullFlag); |
| |
| if (sReceive.mHead != (sReceive.mTail + 1) % kReceiveBufferSize) |
| { |
| sReceive.mBuffer[sReceive.mTail] = rx_data; |
| sReceive.mTail = (sReceive.mTail + 1) % kReceiveBufferSize; |
| } |
| } |
| |
| /* Check if data Tx has end */ |
| if ((LPUART_GetStatusFlags(LPUART0) & kLPUART_TxDataRegEmptyFlag) && |
| (interrupts & kLPUART_TxDataRegEmptyInterruptEnable)) |
| { |
| if (sTransmitLength) |
| { |
| sTransmitLength--; |
| LPUART_WriteByte(LPUART0, *sTransmitBuffer++); |
| } |
| else if (!sTransmitDone) |
| { |
| sTransmitDone = true; |
| LPUART_DisableInterrupts(LPUART0, kLPUART_TxDataRegEmptyInterruptEnable); |
| } |
| } |
| |
| if (LPUART_GetStatusFlags(LPUART0) & kLPUART_RxOverrunFlag) |
| { |
| LPUART_ClearStatusFlags(LPUART0, kLPUART_RxOverrunFlag); |
| } |
| } |