/***************************************************************************//** * @brief * Set the mode for a GPIO pin. * * @param[in] port * The GPIO port to access. * * @param[in] pin * The pin number in the port. * * @param[in] mode * The desired pin mode. * * @param[in] out * Value to set for pin in DOUT register. The DOUT setting is important for * even some input mode configurations, determining pull-up/down direction. ******************************************************************************/ void GPIO_PinModeSet(GPIO_Port_TypeDef port, unsigned int pin, GPIO_Mode_TypeDef mode, unsigned int out) { EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin)); /* If disabling pin, do not modify DOUT in order to reduce chance for */ /* glitch/spike (may not be sufficient precaution in all use cases) */ if (mode != gpioModeDisabled) { if (out) { GPIO_PinOutSet(port, pin); } else { GPIO_PinOutClear(port, pin); } } /* There are two registers controlling the pins for each port. The MODEL * register controls pins 0-7 and MODEH controls pins 8-15. */ if (pin < 8) { BUS_RegMaskedWrite(&GPIO->P[port].MODEL, 0xF << (pin * 4), mode << (pin * 4)); } else { BUS_RegMaskedWrite(&GPIO->P[port].MODEH, 0xF << ((pin - 8) * 4), mode << ((pin - 8) * 4)); } if (mode == gpioModeDisabled) { if (out) { GPIO_PinOutSet(port, pin); } else { GPIO_PinOutClear(port, pin); } } }
/***************************************************************************//** * @brief * Sets the drive strength for a GPIO port. * * @param[in] port * The GPIO port to access. * * @param[in] strength * Drive strength to use for port. ******************************************************************************/ void GPIO_DriveStrengthSet(GPIO_Port_TypeDef port, GPIO_DriveStrength_TypeDef strength) { EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_STRENGHT_VALID(strength)); BUS_RegMaskedWrite(&GPIO->P[port].CTRL, _GPIO_P_CTRL_DRIVESTRENGTH_MASK | _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK, strength); }
/***************************************************************************//** * @brief * Configure USART operating in synchronous mode to use a given baudrate * (or as close as possible to specified baudrate). * * @details * The configuration will be set to use a baudrate <= the specified baudrate * in order to ensure that the baudrate does not exceed the specified value. * * Fractional clock division is suppressed, although the HW design allows it. * It could cause half clock cycles to exceed specified limit, and thus * potentially violate specifications for the slave device. In some special * situations fractional clock division may be useful even in synchronous * mode, but in those cases it must be directly adjusted, possibly assisted * by USART_BaudrateCalc(): * * @param[in] usart * Pointer to USART peripheral register block. (Cannot be used on UART * modules.) * * @param[in] refFreq * USART reference clock frequency in Hz that will be used. If set to 0, * the currently configured reference clock is assumed. * * @param[in] baudrate * Baudrate to try to achieve for USART. ******************************************************************************/ void USART_BaudrateSyncSet(USART_TypeDef *usart, uint32_t refFreq, uint32_t baudrate) { #if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) uint64_t clkdiv; #else uint32_t clkdiv; #endif /* Inhibit divide by 0 */ EFM_ASSERT(baudrate); /* * CLKDIV in synchronous mode is given by: * * CLKDIV = 256 * (fHFPERCLK/(2 * br) - 1) */ /* HFPERCLK used to clock all USART/UART peripheral modules */ if (!refFreq) { refFreq = CMU_ClockFreqGet(cmuClock_HFPER); } #if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) /* Calculate and set CLKDIV without fractional bits */ clkdiv = 2 * baudrate; clkdiv = (0x100ULL * (uint64_t)refFreq) / clkdiv; /* Round up by not subtracting 256 and mask off fractional part */ clkdiv &= ~0xFF; #else /* Calculate and set CLKDIV with fractional bits */ clkdiv = 2 * refFreq; clkdiv += baudrate - 1; clkdiv /= baudrate; clkdiv -= 4; clkdiv *= 64; /* Make sure we don't use fractional bits by rounding CLKDIV */ /* up (and thus reducing baudrate, not increasing baudrate above */ /* specified value). */ clkdiv += 0xc0; clkdiv &= 0xffffff00; #endif /* Verify that resulting clock divider is within limits */ EFM_ASSERT(!(clkdiv & ~_USART_CLKDIV_DIV_MASK)); /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */ clkdiv &= _USART_CLKDIV_DIV_MASK; BUS_RegMaskedWrite(&usart->CLKDIV, _USART_CLKDIV_DIV_MASK, clkdiv); }
/***************************************************************************//** * @brief * Configure GPIO interrupt. * * @details * If reconfiguring a GPIO interrupt that is already enabled, it is generally * recommended to disable it first, see GPIO_Disable(). * * The actual GPIO interrupt handler must be in place before enabling the * interrupt. * * Notice that any pending interrupt for the selected pin is cleared by this * function. * * @note * A certain pin number can only be associated with one port. Ie, if GPIO * interrupt 1 is assigned to port A/pin 1, then it is not possibly to use * pin 1 from any other ports for interrupts. Please refer to the reference * manual. * * @param[in] port * The port to associate with @p pin. * * @param[in] pin * The GPIO interrupt number (= port pin). * * @param[in] risingEdge * Set to true if interrupts shall be enabled on rising edge, otherwise false. * * @param[in] fallingEdge * Set to true if interrupts shall be enabled on falling edge, otherwise false. * * @param[in] enable * Set to true if interrupt shall be enabled after configuration completed, * false to leave disabled. See GPIO_IntDisable() and GPIO_IntEnable(). ******************************************************************************/ void GPIO_IntConfig(GPIO_Port_TypeDef port, unsigned int pin, bool risingEdge, bool fallingEdge, bool enable) { uint32_t tmp; EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin)); /* There are two registers controlling the interrupt configuration: * The EXTIPSELL register controls pins 0-7 and EXTIPSELH controls * pins 8-15. */ if (pin < 8) { BUS_RegMaskedWrite(&GPIO->EXTIPSELL, 0xF << (4 * pin), port << (4 * pin)); } else { tmp = pin - 8; BUS_RegMaskedWrite(&GPIO->EXTIPSELH, 0xF << (4 * tmp), port << (4 * tmp)); } /* Enable/disable rising edge */ BUS_RegBitWrite(&(GPIO->EXTIRISE), pin, risingEdge); /* Enable/disable falling edge */ BUS_RegBitWrite(&(GPIO->EXTIFALL), pin, fallingEdge); /* Clear any pending interrupt */ GPIO->IFC = 1 << pin; /* Finally enable/disable interrupt */ BUS_RegBitWrite(&(GPIO->IEN), pin, enable); }
/***************************************************************************//** * @brief * Send the data from the Message Object message. * * @details * If message is configured as tx and remoteTransfer = 0, calling this function * will send the data of this Message Object if its parameters are correct. * If message is tx and remoteTransfer = 1, this function will set the data of * message to the RAM and exit, the data will be automatically sent after * reception of a remote frame. * If message is rx and remoteTransfer = 1, this function will send a remote * frame to the corresponding id. * If message is rx and remoteTransfer = 0, the user shouldn't call this * function. It will also send a remote frame. * * @param[in] can * Pointer to CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. * * @param[in] message * Message Object * * @param[in] wait * If true, wait for the end of the transfer between the MIRx registers and * the RAM to exit. If false, exit immediately, the transfer can still be * in progress. ******************************************************************************/ void CAN_SendMessage(CAN_TypeDef *can, uint8_t interface, const CAN_MessageObject_TypeDef *message, bool wait) { CAN_MIR_TypeDef * mir = &can->MIR[interface]; /* Make sure msgNum is in the correct range */ EFM_ASSERT((message->msgNum > 0) && (message->msgNum <= 32)); /* Make sure dlc is in the correct range */ EFM_ASSERT(message->dlc <= _CAN_MIR_CTRL_DLC_MASK); CAN_ReadyWait(can, interface); /* Set LEC to unused value to be sure it is reset to 0 after sending */ BUS_RegMaskedWrite(&can->STATUS, _CAN_STATUS_LEC_MASK, 0x7); /* Set which registers to read from the RAM */ mir->CMDMASK = CAN_MIR_CMDMASK_WRRD_READ | CAN_MIR_CMDMASK_ARBACC | CAN_MIR_CMDMASK_CONTROL; /* Send reading request and wait (3 to 6 cpu cycle) */ CAN_SendRequest(can, interface, message->msgNum, true); /* Reset MSGVAL */ mir->CMDMASK |= CAN_MIR_CMDMASK_WRRD; mir->ARB &= ~(0x1 << _CAN_MIR_ARB_MSGVAL_SHIFT); CAN_SendRequest(can, interface, message->msgNum, true); /* Set which registers to write to the RAM */ mir->CMDMASK |= CAN_MIR_CMDMASK_DATAA | CAN_MIR_CMDMASK_DATAB; /* If tx = 1 and remoteTransfer = 1, nothing is sent */ if ( ((mir->CTRL & _CAN_MIR_CTRL_RMTEN_MASK) == 0) || ((mir->ARB & _CAN_MIR_ARB_DIR_MASK) == _CAN_MIR_ARB_DIR_RX)) { mir->CTRL |= CAN_MIR_CTRL_TXRQST; /* DATAVALID is set only if it is not sending a remote message */ if ((mir->CTRL & _CAN_MIR_CTRL_RMTEN_MASK) == 0) { mir->CTRL |= CAN_MIR_CTRL_DATAVALID; } } /* Set the Data length Code */ mir->CTRL = (mir->CTRL & ~_CAN_MIR_CTRL_DLC_MASK) | message->dlc; /* Configure the id */ if (message->extended) { EFM_ASSERT(message->id <= _CAN_MIR_ARB_ID_MASK); mir->ARB = (mir->ARB & ~_CAN_MIR_ARB_ID_MASK) | (message->id << _CAN_MIR_ARB_ID_SHIFT) | (uint32_t)(0x1 << _CAN_MIR_ARB_MSGVAL_SHIFT) | CAN_MIR_ARB_XTD_EXT; } else { EFM_ASSERT(message->id <= _CAN_MIR_ARB_STD_ID_MAX); mir->ARB = (mir->ARB & ~(_CAN_MIR_ARB_ID_MASK | _CAN_MIR_ARB_XTD_MASK)) | (uint32_t)(0x1 << _CAN_MIR_ARB_MSGVAL_SHIFT) | (message->id << _CAN_MIR_ARB_STD_ID_SHIFT) | CAN_MIR_ARB_XTD_STD; } /* Set the data */ CAN_WriteData(can, interface, message); /* Send writing request */ CAN_SendRequest(can, interface, message->msgNum, wait); }
/***************************************************************************//** * @brief * Set I2C bus frequency. * * @details * The bus frequency is only of relevance when acting as a master. The bus * frequency should not be set higher than the max frequency accepted by the * slowest device on the bus. * * Notice that due to asymmetric requirements on low and high I2C clock * cycles by the I2C specification, the actual max frequency allowed in order * to comply with the specification may be somewhat lower than expected. * * Please refer to the reference manual, details on I2C clock generation, * for max allowed theoretical frequencies for different modes. * * @param[in] i2c * Pointer to I2C peripheral register block. * * @param[in] freqRef * I2C reference clock frequency in Hz that will be used. If set to 0, * then HFPER clock is used. Setting it to a higher than actual configured * value only has the consequence of reducing the real I2C frequency. * * @param[in] freqScl * Bus frequency to set (actual bus speed may be lower due to integer * prescaling). Safe (according to I2C specification) max frequencies for * standard, fast and fast+ modes are available using I2C_FREQ_ defines. * (Using I2C_FREQ_ defines requires corresponding setting of @p type.) * Slowest slave device on bus must always be considered. * * @param[in] i2cMode * Clock low to high ratio type to use. If not using i2cClockHLRStandard, * make sure all devices on the bus support the specified mode. Using a * non-standard ratio is useful to achieve higher bus clock in fast and * fast+ modes. ******************************************************************************/ void I2C_BusFreqSet(I2C_TypeDef *i2c, uint32_t freqRef, uint32_t freqScl, I2C_ClockHLR_TypeDef i2cMode) { uint32_t n, minFreq; int32_t div; /* Avoid divide by 0 */ EFM_ASSERT(freqScl); if (!freqScl) { return; } /* Set the CLHR (clock low to high ratio). */ i2c->CTRL &= ~_I2C_CTRL_CLHR_MASK; BUS_RegMaskedWrite(&i2c->CTRL, _I2C_CTRL_CLHR_MASK, i2cMode <<_I2C_CTRL_CLHR_SHIFT); if (!freqRef) { freqRef = CMU_ClockFreqGet(cmuClock_HFPER); } /* Check minumum HF peripheral clock */ minFreq = UINT_MAX; if (i2c->CTRL & I2C_CTRL_SLAVE) { switch(i2cMode) { case i2cClockHLRStandard: #if defined( _SILICON_LABS_32B_PLATFORM_1 ) minFreq = 4200000; break; #elif defined( _SILICON_LABS_32B_PLATFORM_2 ) minFreq = 2000000; break; #endif case i2cClockHLRAsymetric: #if defined( _SILICON_LABS_32B_PLATFORM_1 ) minFreq = 11000000; break; #elif defined( _SILICON_LABS_32B_PLATFORM_2 ) minFreq = 5000000; break; #endif case i2cClockHLRFast: #if defined( _SILICON_LABS_32B_PLATFORM_1 ) minFreq = 24400000; break; #elif defined( _SILICON_LABS_32B_PLATFORM_2 ) minFreq = 14000000; break; #endif } } else { /* For master mode, platform 1 and 2 share the same min frequencies */ switch(i2cMode) { case i2cClockHLRStandard: minFreq = 2000000; break; case i2cClockHLRAsymetric: minFreq = 9000000; break; case i2cClockHLRFast: minFreq = 20000000; break; } } /* Frequency most be larger-than */ EFM_ASSERT(freqRef > minFreq); /* SCL frequency is given by * freqScl = freqRef/((Nlow + Nhigh) * (DIV + 1) + I2C_CR_MAX) * * Thus * DIV = ((freqRef - (I2C_CR_MAX * freqScl))/((Nlow + Nhigh) * freqScl)) - 1 * * More details can be found in the reference manual, * I2C Clock Generation chapter. */ /* n = Nlow + Nhigh */ n = (uint32_t)(i2cNSum[i2cMode]); div = ((freqRef - (I2C_CR_MAX * freqScl)) / (n * freqScl)) - 1; EFM_ASSERT(div >= 0); EFM_ASSERT((uint32_t)div <= _I2C_CLKDIV_DIV_MASK); /* Clock divisor must be at least 1 in slave mode according to reference */ /* manual (in which case there is normally no need to set bus frequency). */ if ((i2c->CTRL & I2C_CTRL_SLAVE) && !div) { div = 1; } i2c->CLKDIV = (uint32_t)div; }