/** * @file xmc_usic.c * @date 2019-07-01 * * @cond ********************************************************************************************************************* * XMClib v2.1.24 - XMC Peripheral Driver Library * * Copyright (c) 2015-2019, Infineon Technologies AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification,are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. * * 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. * * Neither the name of the copyright holders 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. * * To improve the quality of the software, users are encouraged to share modifications, enhancements or bug fixes with * Infineon Technologies AG dave@infineon.com). ********************************************************************************************************************* * * Change History * -------------- * * 2015-02-20: * - Initial draft
* - Documentation improved
* * 2015-05-08: * - Clearing bit fields PDIV, PCTQ, PPPEN in XMC_USIC_CH_SetBaudrate() API
* * 2015-06-20: * - Removed version macros and declaration of GetDriverVersion API
* * 2015-08-27: * - Added APIs for external input for BRG configuration:XMC_USIC_CH_ConfigExternalInputSignalToBRG()
* * 2015-08-28: * - Added asserts to XMC_USIC_CH_ConfigExternalInputSignalToBRG()
* * 2015-09-01: * - Fixed warning in the asserts
* * 2018-09-29: * - Added XMC_USIC_CH_SetBaudrateEx which uses the integer divider instead of the fractional divider
* * 2019-03-30: * - Changed XMC_USIC_Enable() adding polling to check clock ungate * * 2019-05-07: * - Added XMC_USIC_CH_GetBaudrate(), XMC_USIC_CH_GetSCLKFrequency() and XMC_USIC_CH_GetMCLKFrequency() * * 2019-07-01: * - XMC_USIC_CH_SetBaudrateEx() change type of input parameters * * @endcond * */ /******************************************************************************* * HEADER FILES *******************************************************************************/ #include "xmc_usic.h" #include "xmc_scu.h" #include /* abs */ /******************************************************************************* * MACROS *******************************************************************************/ #define USIC_CH_INPR_Msk (0x7UL) /******************************************************************************* * API IMPLEMENTATION *******************************************************************************/ void XMC_USIC_CH_Enable(XMC_USIC_CH_t *const channel) { XMC_ASSERT("XMC_USIC_CH_Enable: channel not valid", XMC_USIC_IsChannelValid(channel)); if ((channel == XMC_USIC0_CH0) || (channel == XMC_USIC0_CH1)) { XMC_USIC_Enable(XMC_USIC0); } #if defined(USIC1) else if((channel == XMC_USIC1_CH0) || (channel == XMC_USIC1_CH1)) { XMC_USIC_Enable(XMC_USIC1); } #endif #if defined(USIC2) else if((channel == XMC_USIC2_CH0) || (channel == XMC_USIC2_CH1)) { XMC_USIC_Enable(XMC_USIC2); } #endif else { XMC_ASSERT("USIC module not available", 0U/*Always*/); } /* USIC channel switched on*/ channel->KSCFG = (USIC_CH_KSCFG_MODEN_Msk | USIC_CH_KSCFG_BPMODEN_Msk); while ((channel->KSCFG & USIC_CH_KSCFG_MODEN_Msk) == 0U) { /* Wait till the channel is enabled */ } /* Set USIC channel in IDLE mode */ channel->CCR &= (uint32_t)~USIC_CH_CCR_MODE_Msk; } void XMC_USIC_CH_Disable(XMC_USIC_CH_t *const channel) { channel->KSCFG = (uint32_t)((channel->KSCFG & (~USIC_CH_KSCFG_MODEN_Msk)) | USIC_CH_KSCFG_BPMODEN_Msk); } XMC_USIC_CH_STATUS_t XMC_USIC_CH_SetBaudrate(XMC_USIC_CH_t *const channel, uint32_t rate, uint32_t oversampling) { XMC_USIC_CH_STATUS_t status; uint32_t peripheral_clock; uint32_t clock_divider; uint32_t clock_divider_min; uint32_t pdiv; uint32_t pdiv_int; uint32_t pdiv_int_min; uint32_t pdiv_frac; uint32_t pdiv_frac_min; /* The rate and peripheral clock are divided by 100 to be able to use only 32bit arithmetic */ if ((rate >= 100U) && (oversampling != 0U)) { peripheral_clock = XMC_SCU_CLOCK_GetPeripheralClockFrequency() / 100U; rate = rate / 100U; clock_divider_min = 1U; pdiv_int_min = 1U; pdiv_frac_min = 0x3ffU; for(clock_divider = 1023U; clock_divider > 0U; --clock_divider) { pdiv = ((peripheral_clock * clock_divider) / (rate * oversampling)); pdiv_int = pdiv >> 10U; pdiv_frac = pdiv & 0x3ffU; if ((pdiv_int < 1024U) && (pdiv_frac < pdiv_frac_min)) { pdiv_frac_min = pdiv_frac; pdiv_int_min = pdiv_int; clock_divider_min = clock_divider; } } channel->FDR = XMC_USIC_CH_BRG_CLOCK_DIVIDER_MODE_FRACTIONAL | (clock_divider_min << USIC_CH_FDR_STEP_Pos); channel->BRG = (channel->BRG & ~(USIC_CH_BRG_DCTQ_Msk | USIC_CH_BRG_PDIV_Msk | USIC_CH_BRG_PCTQ_Msk | USIC_CH_BRG_PPPEN_Msk)) | ((oversampling - 1U) << USIC_CH_BRG_DCTQ_Pos) | ((pdiv_int_min - 1U) << USIC_CH_BRG_PDIV_Pos); status = XMC_USIC_CH_STATUS_OK; } else { status = XMC_USIC_CH_STATUS_ERROR; } return status; } XMC_USIC_CH_STATUS_t XMC_USIC_CH_SetBaudrateEx(XMC_USIC_CH_t *const channel, int32_t rate, int32_t oversampling) { int32_t peripheral_clock = XMC_SCU_CLOCK_GetPeripheralClockFrequency(); int32_t brg_clock = rate * oversampling; int32_t actual_rate_upper; int32_t actual_rate_lower; uint32_t pdiv = 1; uint32_t divider_step; XMC_USIC_CH_STATUS_t status; if (peripheral_clock > brg_clock) { divider_step = peripheral_clock / brg_clock; // integer division gets truncated while (divider_step >= 1023) { pdiv++; brg_clock = rate * oversampling * pdiv; divider_step = peripheral_clock / brg_clock; // integer division gets truncated } actual_rate_upper = peripheral_clock / (divider_step * oversampling * pdiv); actual_rate_lower = peripheral_clock / ((divider_step + 1) * oversampling * pdiv); // choose better approximation if the peripheral frequency is not a multiple of the baudrate if (abs(rate - actual_rate_lower) < abs(rate - actual_rate_upper)) { divider_step += 1; } divider_step = 1024 - divider_step; channel->FDR = XMC_USIC_CH_BRG_CLOCK_DIVIDER_MODE_NORMAL | (divider_step << USIC_CH_FDR_STEP_Pos); channel->BRG = (channel->BRG & ~(USIC_CH_BRG_DCTQ_Msk | USIC_CH_BRG_PDIV_Msk | USIC_CH_BRG_PCTQ_Msk | USIC_CH_BRG_PPPEN_Msk)) | ((oversampling - 1U) << USIC_CH_BRG_DCTQ_Pos) | ((pdiv -1) << USIC_CH_BRG_PDIV_Pos); status = XMC_USIC_CH_STATUS_OK; } else { status = XMC_USIC_CH_STATUS_ERROR; } return status; } uint32_t XMC_USIC_CH_GetBaudrate(XMC_USIC_CH_t *const channel) { uint32_t divider; if ((channel->BRG & USIC_CH_BRG_CTQSEL_Msk) == USIC_CH_BRG_CTQSEL_Msk) { // CTQSEL = 3 divider = 2; } else { // CTQSEL = 0, 1, or 2 divider = (channel->BRG & USIC_CH_BRG_PPPEN_Msk) ? 2 : 1; if ((((channel->BRG & USIC_CH_BRG_CTQSEL_Msk) >> USIC_CH_BRG_CTQSEL_Pos) & 0x1) == 0) { // CTQSEL = 0 or 2 divider *= ((channel->BRG & USIC_CH_BRG_PDIV_Msk) >> USIC_CH_BRG_PDIV_Pos) + 1; if ((((channel->BRG & USIC_CH_BRG_CTQSEL_Msk) >> USIC_CH_BRG_CTQSEL_Pos) & 0x2) != 0) { // CTQSEL = 2 divider *= 2; } } } divider *= ((channel->BRG & USIC_CH_BRG_PCTQ_Msk) >> USIC_CH_BRG_PCTQ_Pos) + 1; divider *= ((channel->BRG & USIC_CH_BRG_DCTQ_Msk) >> USIC_CH_BRG_DCTQ_Pos) + 1; uint32_t fperi = XMC_SCU_CLOCK_GetPeripheralClockFrequency(); float baudrate; if ((channel->FDR & USIC_CH_FDR_DM_Msk) == XMC_USIC_CH_BRG_CLOCK_DIVIDER_MODE_FRACTIONAL) { baudrate = fperi * (((channel->FDR & USIC_CH_FDR_STEP_Msk) >> USIC_CH_FDR_STEP_Pos) / 1024.0F); } else { /* Normal divider mode */ baudrate = fperi * (1.0F / (1024 - ((channel->FDR & USIC_CH_FDR_STEP_Msk) >> USIC_CH_FDR_STEP_Pos))); } baudrate /= divider; return baudrate; } uint32_t XMC_USIC_CH_GetSCLKFrequency(XMC_USIC_CH_t *const channel) { uint32_t divider; divider = (channel->BRG & USIC_CH_BRG_PPPEN_Msk) ? 2 : 1; divider *= ((channel->BRG & USIC_CH_BRG_PDIV_Msk) >> USIC_CH_BRG_PDIV_Pos) + 1; divider *= 2; uint32_t fperi = XMC_SCU_CLOCK_GetPeripheralClockFrequency(); float baudrate; if ((channel->FDR & USIC_CH_FDR_DM_Msk) == XMC_USIC_CH_BRG_CLOCK_DIVIDER_MODE_FRACTIONAL) { /* Fractional divider mode */ baudrate = fperi * (((channel->FDR & USIC_CH_FDR_STEP_Msk) >> USIC_CH_FDR_STEP_Pos) / 1024.0F); } else { /* Normal divider mode */ baudrate = fperi * (1.0F / (1024 - ((channel->FDR & USIC_CH_FDR_STEP_Msk) >> USIC_CH_FDR_STEP_Pos))); } baudrate /= divider; return baudrate; } uint32_t XMC_USIC_CH_GetMCLKFrequency(XMC_USIC_CH_t *const channel) { uint32_t fperi = XMC_SCU_CLOCK_GetPeripheralClockFrequency(); float baudrate; if ((channel->FDR & USIC_CH_FDR_DM_Msk) == XMC_USIC_CH_BRG_CLOCK_DIVIDER_MODE_FRACTIONAL) { /* Fractional divider mode */ baudrate = fperi * (1.0F / (1024 - ((channel->FDR & USIC_CH_FDR_STEP_Msk) >> USIC_CH_FDR_STEP_Pos))); } else { /* Normal divider mode */ baudrate = fperi / (((channel->FDR & USIC_CH_FDR_STEP_Msk) >> USIC_CH_FDR_STEP_Pos) / 1024.0F); } baudrate /= 2; return baudrate; } void XMC_USIC_CH_ConfigExternalInputSignalToBRG(XMC_USIC_CH_t *const channel, const uint16_t pdiv, const uint32_t oversampling, const XMC_USIC_CH_INPUT_COMBINATION_MODE_t combination_mode) { XMC_ASSERT("XMC_USIC_CH_ConfigExternalInputSignalToBRG: Divider out of range", ((1U < pdiv) || (pdiv < 1024U))); XMC_ASSERT("XMC_USIC_CH_ConfigExternalInputSignalToBRG: Oversampling out of range", ((1U < oversampling) || (oversampling < 32U))); /* Setting the external input frequency source through DX1 */ XMC_USIC_CH_SetBRGInputClockSource(channel, XMC_USIC_CH_BRG_CLOCK_SOURCE_DX1T); /* Setting the trigger combination mode */ XMC_USIC_CH_SetInputTriggerCombinationMode(channel,XMC_USIC_CH_INPUT_DX1,combination_mode); /* Configuring the dividers and oversampling */ channel->BRG = (channel->BRG & ~(USIC_CH_BRG_DCTQ_Msk | USIC_CH_BRG_PDIV_Msk | USIC_CH_BRG_PCTQ_Msk | USIC_CH_BRG_PPPEN_Msk)) | (((oversampling) - 1U) << USIC_CH_BRG_DCTQ_Pos) | (((pdiv) - 1U) << USIC_CH_BRG_PDIV_Pos); } void XMC_USIC_CH_TXFIFO_Configure(XMC_USIC_CH_t *const channel, const uint32_t data_pointer, const XMC_USIC_CH_FIFO_SIZE_t size, const uint32_t limit) { /* Disable FIFO */ channel->TBCTR &= (uint32_t)~USIC_CH_TBCTR_SIZE_Msk; /* LOF = 0, A standard transmit buffer event occurs when the filling level equals the limit value and gets * lower due to transmission of a data word * STBTEN = 0, the trigger of the standard transmit buffer event is based on the transition of the fill level * from equal to below the limit, not the fact being below */ channel->TBCTR = (uint32_t)(channel->TBCTR & (uint32_t)~(USIC_CH_TBCTR_LIMIT_Msk | USIC_CH_TBCTR_DPTR_Msk | USIC_CH_TBCTR_SIZE_Msk)) | (uint32_t)((limit << USIC_CH_TBCTR_LIMIT_Pos) | (data_pointer << USIC_CH_TBCTR_DPTR_Pos) | ((uint32_t)size << USIC_CH_TBCTR_SIZE_Pos)); } void XMC_USIC_CH_RXFIFO_Configure(XMC_USIC_CH_t *const channel, const uint32_t data_pointer, const XMC_USIC_CH_FIFO_SIZE_t size, const uint32_t limit) { /* Disable FIFO */ channel->RBCTR &= (uint32_t)~USIC_CH_RBCTR_SIZE_Msk; /* LOF = 1, A standard receive buffer event occurs when the filling level equals the limit value and gets bigger * due to the reception of a new data word */ channel->RBCTR = (uint32_t)((channel->RBCTR & (uint32_t)~(USIC_CH_RBCTR_LIMIT_Msk | USIC_CH_RBCTR_DPTR_Msk | USIC_CH_RBCTR_LOF_Msk)) | ((limit << USIC_CH_RBCTR_LIMIT_Pos) | (data_pointer << USIC_CH_RBCTR_DPTR_Pos) | ((uint32_t)size << USIC_CH_RBCTR_SIZE_Pos) | (uint32_t)USIC_CH_RBCTR_LOF_Msk)); } void XMC_USIC_CH_TXFIFO_SetSizeTriggerLimit(XMC_USIC_CH_t *const channel, const XMC_USIC_CH_FIFO_SIZE_t size, const uint32_t limit) { /* Disable FIFO */ channel->TBCTR &= (uint32_t)~USIC_CH_TBCTR_SIZE_Msk; /* STBTEN = 0, the trigger of the standard transmit buffer event is based on the transition of the fill level * from equal to below the limit, not the fact being below */ channel->TBCTR = (uint32_t)((uint32_t)(channel->TBCTR & (uint32_t)~USIC_CH_TBCTR_LIMIT_Msk) | (limit << USIC_CH_TBCTR_LIMIT_Pos) | ((uint32_t)size << USIC_CH_TBCTR_SIZE_Pos)); } void XMC_USIC_CH_RXFIFO_SetSizeTriggerLimit(XMC_USIC_CH_t *const channel, const XMC_USIC_CH_FIFO_SIZE_t size, const uint32_t limit) { /* Disable FIFO */ channel->RBCTR &= (uint32_t)~USIC_CH_RBCTR_SIZE_Msk; channel->RBCTR = (uint32_t)((uint32_t)(channel->RBCTR & (uint32_t)~USIC_CH_RBCTR_LIMIT_Msk) | (limit << USIC_CH_RBCTR_LIMIT_Pos) | ((uint32_t)size << USIC_CH_RBCTR_SIZE_Pos)); } void XMC_USIC_CH_SetInterruptNodePointer(XMC_USIC_CH_t *const channel, const XMC_USIC_CH_INTERRUPT_NODE_POINTER_t interrupt_node, const uint32_t service_request) { channel->INPR = (uint32_t)((channel->INPR & (~(uint32_t)(USIC_CH_INPR_Msk << (uint32_t)interrupt_node))) | (service_request << (uint32_t)interrupt_node)); } void XMC_USIC_CH_TXFIFO_SetInterruptNodePointer(XMC_USIC_CH_t *const channel, const XMC_USIC_CH_TXFIFO_INTERRUPT_NODE_POINTER_t interrupt_node, const uint32_t service_request) { channel->TBCTR = (uint32_t)((channel->TBCTR & (~(uint32_t)(USIC_CH_INPR_Msk << (uint32_t)interrupt_node))) | (service_request << (uint32_t)interrupt_node)); } void XMC_USIC_CH_RXFIFO_SetInterruptNodePointer(XMC_USIC_CH_t *const channel, const XMC_USIC_CH_RXFIFO_INTERRUPT_NODE_POINTER_t interrupt_node, const uint32_t service_request) { channel->RBCTR = (uint32_t)((channel->RBCTR & (~(uint32_t)(USIC_CH_INPR_Msk << (uint32_t)interrupt_node))) | (service_request << (uint32_t)interrupt_node)); } void XMC_USIC_Enable(XMC_USIC_t *const usic) { if (usic == USIC0) { #if defined(CLOCK_GATING_SUPPORTED) XMC_SCU_CLOCK_UngatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USIC0); while (XMC_SCU_CLOCK_IsPeripheralClockGated(XMC_SCU_PERIPHERAL_CLOCK_USIC0)); #endif #if defined(PERIPHERAL_RESET_SUPPORTED) XMC_SCU_RESET_DeassertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USIC0); while (XMC_SCU_RESET_IsPeripheralResetAsserted(XMC_SCU_PERIPHERAL_RESET_USIC0)); #endif } #if defined(USIC1) else if (usic == USIC1) { #if defined(CLOCK_GATING_SUPPORTED) XMC_SCU_CLOCK_UngatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USIC1); while (XMC_SCU_CLOCK_IsPeripheralClockGated(XMC_SCU_PERIPHERAL_CLOCK_USIC1)); #endif #if defined(PERIPHERAL_RESET_SUPPORTED) XMC_SCU_RESET_DeassertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USIC1); while (XMC_SCU_RESET_IsPeripheralResetAsserted(XMC_SCU_PERIPHERAL_RESET_USIC1)); #endif } #endif #if defined(USIC2) else if (usic == USIC2) { #if defined(CLOCK_GATING_SUPPORTED) XMC_SCU_CLOCK_UngatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USIC2); while (XMC_SCU_CLOCK_IsPeripheralClockGated(XMC_SCU_PERIPHERAL_CLOCK_USIC2)); #endif #if defined(PERIPHERAL_RESET_SUPPORTED) XMC_SCU_RESET_DeassertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USIC2); while (XMC_SCU_RESET_IsPeripheralResetAsserted(XMC_SCU_PERIPHERAL_RESET_USIC2)); #endif } #endif else { XMC_ASSERT("USIC module not available", 0/*Always*/); } } void XMC_USIC_Disable(XMC_USIC_t *const usic) { if (usic == (XMC_USIC_t *)USIC0) { #if defined(PERIPHERAL_RESET_SUPPORTED) XMC_SCU_RESET_AssertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USIC0); #endif #if defined(CLOCK_GATING_SUPPORTED) XMC_SCU_CLOCK_GatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USIC0); #endif } #if defined(USIC1) else if (usic == (XMC_USIC_t *)USIC1) { #if defined(PERIPHERAL_RESET_SUPPORTED) XMC_SCU_RESET_AssertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USIC1); #endif #if defined(CLOCK_GATING_SUPPORTED) XMC_SCU_CLOCK_GatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USIC1); #endif } #endif #if defined(USIC2) else if (usic == (XMC_USIC_t *)USIC2) { #if defined(PERIPHERAL_RESET_SUPPORTED) XMC_SCU_RESET_AssertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USIC2); #endif #if defined(CLOCK_GATING_SUPPORTED) XMC_SCU_CLOCK_GatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USIC2); #endif } #endif else { XMC_ASSERT("USIC module not available", 0/*Always*/); } }