openblt/Target/Source/ARMCM4_S32K14/rs232.c

342 lines
14 KiB
C

/************************************************************************************//**
* \file Source/ARMCM4_S32K14/uart.c
* \brief Bootloader RS232 communication interface source file.
* \ingroup Target_ARMCM4_S32K14
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2020 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include "boot.h" /* bootloader generic header */
#if (BOOT_COM_RS232_ENABLE > 0)
#include "device_registers.h" /* device registers */
/****************************************************************************************
* Macro definitions
****************************************************************************************/
/** \brief Timeout time for the reception of a CTO packet. The timer is started upon
* reception of the first packet byte.
*/
#define RS232_CTO_RX_PACKET_TIMEOUT_MS (100U)
/** \brief Timeout for transmitting a byte in milliseconds. */
#define RS232_BYTE_TX_TIMEOUT_MS (10U)
#if (BOOT_COM_RS232_CHANNEL_INDEX == 0)
/** \brief Set the peripheral LPUART0 base pointer. */
#define LPUARTx (LPUART0)
/** \brief Set the PCC index offset for LPUART0. */
#define PCC_LPUARTx_INDEX (PCC_LPUART0_INDEX)
#elif (BOOT_COM_RS232_CHANNEL_INDEX == 1)
/** \brief Set the peripheral LPUART1 base pointer. */
#define LPUARTx (LPUART1)
/** \brief Set the PCC index offset for LPUART1. */
#define PCC_LPUARTx_INDEX (PCC_LPUART1_INDEX)
#elif (BOOT_COM_RS232_CHANNEL_INDEX == 2)
/** \brief Set the peripheral LPUART2 base pointer. */
#define LPUARTx (LPUART2)
/** \brief Set the PCC index offset for LPUART2. */
#define PCC_LPUARTx_INDEX (PCC_LPUART2_INDEX)
#endif
/****************************************************************************************
* Function prototypes
****************************************************************************************/
static blt_bool Rs232ReceiveByte(blt_int8u *data);
static void Rs232TransmitByte(blt_int8u data);
/************************************************************************************//**
** \brief Initializes the RS232 communication interface.
** \return none.
**
****************************************************************************************/
void Rs232Init(void)
{
blt_int32u sourceClockFreqHz;
blt_int32u div2RegValue;
blt_int16u baudrateSbr0_12;
blt_int8u const div2DividerLookup[] =
{
0U, /* 0b000. Output disabled. */
1U, /* 0b001. Divide by 1. */
2U, /* 0b010. Divide by 2. */
4U, /* 0b011. Divide by 4. */
8U, /* 0b100. Divide by 8. */
16U, /* 0b101. Divide by 16. */
32U, /* 0b110. Divide by 32. */
64U, /* 0b111. Divide by 64. */
};
/* Perform compile time assertion to check that the configured UART channel is actually
* supported by this driver.
*/
ASSERT_CT((BOOT_COM_RS232_CHANNEL_INDEX == 0) ||
(BOOT_COM_RS232_CHANNEL_INDEX == 1) ||
(BOOT_COM_RS232_CHANNEL_INDEX == 2));
/* Make sure the UART peripheral clock is disabled before configuring its source
* clock.
*/
PCC->PCCn[PCC_LPUARTx_INDEX] &= ~PCC_PCCn_CGC_MASK;
/* Select option 2 as the UART peripheral source clock and enable the clock. Option 2
* is the SIRCDIV2_CLK, which is available on all peripherals and configurations.
*/
PCC->PCCn[PCC_LPUARTx_INDEX] |= PCC_PCCn_PCS(2) | PCC_PCCn_CGC_MASK;
/* Obtain the DIV2 divider value of the SIRC_CLK. */
div2RegValue = (SCG->SIRCDIV & SCG_SIRCDIV_SIRCDIV2_MASK) >> SCG_SIRCDIV_SIRCDIV2_SHIFT;
/* Check if the DIV2 register value for SIRC is 0. In this case SIRCDIV2_CLK is
* currently disabled.
*/
if (div2RegValue == 0U)
{
/* Configure the DIV2 for a default divide by 1 to make sure the SIRCDIV2_CLK is
* actually enabled.
*/
div2RegValue = 1U;
SCG->SIRCDIV |= SCG_SIRCDIV_SIRCDIV2(div2RegValue);
}
/* Determine the SIRC clock frequency. If SIRC high range is enabled, it is 8 MHz. If
* SIRC low range is enabled, it is 2 MHz.
*/
sourceClockFreqHz = 8000000U;
if ((SCG->SIRCCFG & SCG_SIRCCFG_RANGE_MASK) == SCG_SIRCCFG_RANGE(0))
{
sourceClockFreqHz = 2000000U;
}
/* Now process the configured DIV2 divider factor to get the actual frequency of the
* UART peripheral source clock.
*/
sourceClockFreqHz /= div2DividerLookup[div2RegValue];
/* Configure the baudrate from BOOT_COM_RS232_BAUDRATE, taking into account that an
* oversampling of 8 will be configured. Default 8,n,1 format is used. Integer
* rounding is used to get the best value for baudrateSbr0_12. Actual baudrate equals
* sourceClockFreqHz / 8 / baudrateSbr0_12.
*/
baudrateSbr0_12 = (((sourceClockFreqHz / BOOT_COM_RS232_BAUDRATE) + (8U - 1U)) / 8U) &
LPUART_BAUD_SBR_MASK;
/* OSR=7: Over sampling ratio = 7+1=8.
* SBNS=0: One stop bit.
* BOTHEDGE=0: receiver samples only on rising edge.
* M10=0: Rx and Tx use 7 to 9 bit data characters.
* RESYNCDIS=0: Resync during rec'd data word supported.
* LBKDIE, RXEDGIE=0: interrupts disable.
* TDMAE, RDMAE, TDMAE=0: DMA requests disabled.
* MAEN1, MAEN2, MATCFG=0: Match disabled.
*/
LPUARTx->BAUD = LPUART_BAUD_SBR(baudrateSbr0_12) | LPUART_BAUD_OSR(7);
/* Clear the error/interrupt flags */
LPUARTx->STAT = FEATURE_LPUART_STAT_REG_FLAGS_MASK;
/* Reset all features/interrupts by default */
LPUARTx->CTRL = 0x00000000;
/* Reset match addresses */
LPUARTx->MATCH = 0x00000000;
#if FEATURE_LPUART_HAS_MODEM_SUPPORT
/* Reset IrDA modem features */
LPUARTx->MODIR = 0x00000000;
#endif
#if FEATURE_LPUART_FIFO_SIZE > 0U
/* Reset FIFO feature */
LPUARTx->FIFO = FEATURE_LPUART_FIFO_RESET_MASK;
/* Enable the transmit and receive FIFOs. */
LPUARTx->FIFO |= LPUART_FIFO_TXFE(1) | LPUART_FIFO_RXFE(1);
/* Set the reception water mark to 0 and the transmitter water mark to 1. */
LPUARTx->WATER = LPUART_WATER_TXWATER(1) | LPUART_WATER_RXWATER(0);
#endif
/* Enable transmitter and receiver, no parity, 8 bit char:
* RE=1: Receiver enabled.
* TE=1: Transmitter enabled.
* PE,PT=0: No hw parity generation or checking.
* M7,M,R8T9,R9T8=0: 8-bit data characters.
* DOZEEN=0: LPUART enabled in Doze mode.
* ORIE,NEIE,FEIE,PEIE,TIE,TCIE,RIE,ILIE,MA1IE,MA2IE=0: no IRQ.
* TxDIR=0: TxD pin is input if in single-wire mode.
* TXINV=0: Transmit data not inverted.
* RWU,WAKE=0: normal operation; rcvr not in standby.
* IDLCFG=0: one idle character.
* ILT=0: Idle char bit count starts after start bit.
* SBK=0: Normal transmitter operation - no break char.
* LOOPS,RSRC=0: no loop back.
*/
LPUARTx->CTRL = LPUART_CTRL_RE_MASK | LPUART_CTRL_TE_MASK;
} /*** end of Rs232Init ***/
/************************************************************************************//**
** \brief Transmits a packet formatted for the communication interface.
** \param data Pointer to byte array with data that it to be transmitted.
** \param len Number of bytes that are to be transmitted.
** \return none.
**
****************************************************************************************/
void Rs232TransmitPacket(blt_int8u *data, blt_int8u len)
{
blt_int16u data_index;
/* Verify validity of the len-paramenter. */
ASSERT_RT(len <= BOOT_COM_RS232_TX_MAX_DATA);
/* First transmit the length of the packet. */
Rs232TransmitByte(len);
/* Transmit all the packet bytes one-by-one. */
for (data_index = 0U; data_index < len; data_index++)
{
/* Keep the watchdog happy. */
CopService();
/* Write byte. */
Rs232TransmitByte(data[data_index]);
}
} /*** end of Rs232TransmitPacket ***/
/************************************************************************************//**
** \brief Receives a communication interface packet if one is present.
** \param data Pointer to byte array where the data is to be stored.
** \param len Pointer where the length of the packet is to be stored.
** \return BLT_TRUE if a packet was received, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool Rs232ReceivePacket(blt_int8u *data, blt_int8u *len)
{
static blt_int8u xcpCtoReqPacket[BOOT_COM_RS232_RX_MAX_DATA+1U]; /* One extra for length. */
static blt_int8u xcpCtoRxLength;
static blt_bool xcpCtoRxInProgress = BLT_FALSE;
static blt_int32u xcpCtoRxStartTime = 0U;
/* Start of cto packet received? */
if (xcpCtoRxInProgress == BLT_FALSE)
{
/* Store the message length when received. */
if (Rs232ReceiveByte(&xcpCtoReqPacket[0]) == BLT_TRUE)
{
if ( (xcpCtoReqPacket[0] > 0U) &&
(xcpCtoReqPacket[0] <= BOOT_COM_RS232_RX_MAX_DATA) )
{
/* Store the start time. */
xcpCtoRxStartTime = TimerGet();
/* Reset packet data count. */
xcpCtoRxLength = 0U;
/* Indicate that a cto packet is being received. */
xcpCtoRxInProgress = BLT_TRUE;
}
}
}
else
{
/* Store the next packet byte. */
if (Rs232ReceiveByte(&xcpCtoReqPacket[xcpCtoRxLength+1U]) == BLT_TRUE)
{
/* Increment the packet data count. */
xcpCtoRxLength++;
/* Check to see if the entire packet was received. */
if (xcpCtoRxLength == xcpCtoReqPacket[0])
{
/* Copy the packet data. */
CpuMemCopy((blt_int32u)data, (blt_int32u)&xcpCtoReqPacket[1], xcpCtoRxLength);
/* Done with cto packet reception. */
xcpCtoRxInProgress = BLT_FALSE;
/* Set the packet length. */
*len = xcpCtoRxLength;
/* Packet reception complete. */
return BLT_TRUE;
}
}
else
{
/* Check packet reception timeout. */
if (TimerGet() > (xcpCtoRxStartTime + RS232_CTO_RX_PACKET_TIMEOUT_MS))
{
/* Cancel cto packet reception due to timeout. Note that that automaticaly
* discards the already received packet bytes, allowing the host to retry.
*/
xcpCtoRxInProgress = BLT_FALSE;
}
}
}
/* Packet reception not yet complete. */
return BLT_FALSE;
} /*** end of Rs232ReceivePacket ***/
/************************************************************************************//**
** \brief Receives a communication interface byte if one is present.
** \param data Pointer to byte where the data is to be stored.
** \return BLT_TRUE if a byte was received, BLT_FALSE otherwise.
**
****************************************************************************************/
static blt_bool Rs232ReceiveByte(blt_int8u *data)
{
blt_bool result = BLT_FALSE;
/* Check if a new byte was received by means of the RDRF-bit. */
if (((LPUARTx->STAT & LPUART_STAT_RDRF_MASK) >> LPUART_STAT_RDRF_SHIFT) != 0U)
{
/* Retrieve and store the newly received byte. */
*data = LPUARTx->DATA;
/* Update the result. */
result = BLT_TRUE;
}
/* Give the result back to the caller. */
return result;
} /*** end of Rs232ReceiveByte ***/
/************************************************************************************//**
** \brief Transmits a communication interface byte.
** \param data Value of byte that is to be transmitted.
** \return none.
**
****************************************************************************************/
static void Rs232TransmitByte(blt_int8u data)
{
blt_int32u timeout;
/* Write the byte value in 'data' to the transmit register of the UART peripheral such
* that the transmission of the byte value is started.
*/
LPUARTx->DATA = data;
/* Set timeout time to wait for transmit completion. */
timeout = TimerGet() + RS232_BYTE_TX_TIMEOUT_MS;
/* Wait for tx holding register to be empty. */
while (((LPUARTx->STAT & LPUART_STAT_TDRE_MASK) >> LPUART_STAT_TDRE_SHIFT) == 0U)
{
/* Keep the watchdog happy. */
CopService();
/* Break loop upon timeout. this would indicate a hardware failure. */
if (TimerGet() > timeout)
{
break;
}
}
} /*** end of Rs232TransmitByte ***/
#endif /* BOOT_COM_RS232_ENABLE > 0 */
/*********************************** end of rs232.c ************************************/