1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/msp430/periph/usci.c

226 lines
6.9 KiB
C
Raw Normal View History

#include "irq.h"
#include "macros/math.h"
#include "mutex.h"
#include "periph_conf.h"
#include "periph_cpu.h"
const msp430_usci_uart_params_t usci_a0_as_uart = {
.usci_params = {
.id = MSP430_USCI_ID_A0,
.dev = MSP430_USCI_B_FROM_USCI_A(&USCI_A0),
.interrupt_flag = &UC0IFG,
.interrupt_enable = &UC0IE,
.tx_irq_mask = UCA0TXIE,
.rx_irq_mask = UCA0RXIE,
},
.txd = GPIO_PIN(P3, 4),
.rxd = GPIO_PIN(P3, 5),
};
const msp430_usci_uart_params_t usci_a1_as_uart = {
.usci_params = {
.id = MSP430_USCI_ID_A1,
.dev = MSP430_USCI_B_FROM_USCI_A(&USCI_A1),
.interrupt_flag = &UC1IFG,
.interrupt_enable = &UC1IE,
.tx_irq_mask = UCA1TXIE,
.rx_irq_mask = UCA1RXIE,
},
.txd = GPIO_PIN(P3, 6),
.rxd = GPIO_PIN(P3, 7),
};
const msp430_usci_spi_params_t usci_a0_as_spi = {
.usci_params = {
.id = MSP430_USCI_ID_A0,
.dev = MSP430_USCI_B_FROM_USCI_A(&USCI_A0),
.interrupt_flag = &UC0IFG,
.interrupt_enable = &UC0IE,
.tx_irq_mask = UCA0TXIE,
.rx_irq_mask = UCA0RXIE,
},
.mosi = GPIO_PIN(P3, 4),
.miso = GPIO_PIN(P3, 5),
.sck = GPIO_PIN(P3, 0),
};
const msp430_usci_spi_params_t usci_a1_as_spi = {
.usci_params = {
.id = MSP430_USCI_ID_A1,
.dev = MSP430_USCI_B_FROM_USCI_A(&USCI_A1),
.interrupt_flag = &UC1IFG,
.interrupt_enable = &UC1IE,
.tx_irq_mask = UCA1TXIE,
.rx_irq_mask = UCA1RXIE,
},
.mosi = GPIO_PIN(P3, 7),
.miso = GPIO_PIN(P3, 6),
.sck = GPIO_PIN(P5, 0),
};
const msp430_usci_spi_params_t usci_b0_as_spi = {
.usci_params = {
.id = MSP430_USCI_ID_B0,
.dev = &USCI_B0,
.interrupt_flag = &UC0IFG,
.interrupt_enable = &UC0IE,
.tx_irq_mask = UCB0TXIE,
.rx_irq_mask = UCB0RXIE,
},
.mosi = GPIO_PIN(P3, 1),
.miso = GPIO_PIN(P3, 2),
.sck = GPIO_PIN(P3, 3),
};
const msp430_usci_spi_params_t usci_b1_as_spi = {
.usci_params = {
.id = MSP430_USCI_ID_B1,
.dev = &USCI_B1,
.interrupt_flag = &UC1IFG,
.interrupt_enable = &UC1IE,
.tx_irq_mask = UCB1TXIE,
.rx_irq_mask = UCB1RXIE,
},
.mosi = GPIO_PIN(P5, 1),
.miso = GPIO_PIN(P5, 2),
.sck = GPIO_PIN(P5, 3),
};
static mutex_t _usci_locks[MSP430_USCI_ID_NUMOF] = {
MUTEX_INIT,
MUTEX_INIT,
MUTEX_INIT,
MUTEX_INIT,
};
static uint8_t _auxiliary_clock_acquired;
void msp430_usci_acquire(const msp430_usci_params_t *params,
const msp430_usci_conf_t *conf)
{
assume((unsigned)params->id < MSP430_USCI_ID_NUMOF);
mutex_lock(&_usci_locks[params->id]);
msp430_usci_b_t *dev = params->dev;
/* We only need to acquire the auxiliary (low frequency) clock domain, as
* the subsystem main clock (SMCLK) will be acquired on-demand when activity
* is detected on RXD, as per datasheet:
*
* > The USCI module provides automatic clock activation for SMCLK for use
* > with low-power modes.
*/
switch (conf->prescaler.clk_source) {
case USCI_CLK_AUX:
msp430_clock_acquire(MSP430_CLOCK_AUXILIARY);
_auxiliary_clock_acquired |= 1U << params->id;
break;
default:
_auxiliary_clock_acquired &= ~(1U << params->id);
break;
}
/* put device in disabled/reset state */
dev->CTL1 = UCSWRST;
/* apply given configuration */
dev->CTL0 = conf->ctl0;
dev->CTL1 = conf->prescaler.clk_source | UCSWRST;
dev->BR0 = conf->prescaler.br0;
dev->BR1 = conf->prescaler.br1;
dev->MCTL = conf->prescaler.mctl;
/* disable USCI IRQs and clear any spurious IRQ flags */
uint8_t clear_irq_mask = ~(params->tx_irq_mask | params->rx_irq_mask);
unsigned irq_mask = irq_disable();
*params->interrupt_flag &= clear_irq_mask;
*params->interrupt_enable &= clear_irq_mask;
irq_restore(irq_mask);
}
void msp430_usci_release(const msp430_usci_params_t *params)
{
assume(params->id < MSP430_USCI_ID_NUMOF);
msp430_usci_b_t *dev = params->dev;
/* Disable USCI */
dev->CTL0 = UCSWRST;
/* disable USCI IRQs and clear any spurious IRQ flags */
uint8_t clear_irq_mask = ~(params->tx_irq_mask | params->rx_irq_mask);
unsigned irq_mask = irq_disable();
*params->interrupt_enable &= clear_irq_mask;
*params->interrupt_flag &= clear_irq_mask;
if (_auxiliary_clock_acquired & (1U << params->id)) {
msp430_clock_release(MSP430_CLOCK_AUXILIARY);
}
irq_restore(irq_mask);
/* Release mutex */
mutex_unlock(&_usci_locks[params->id]);
}
msp430_usci_prescaler_t msp430_usci_prescale(uint32_t target_hz)
{
msp430_usci_prescaler_t result = {
.mctl = 0,
.clk_source = USCI_CLK_SUBMAIN,
};
/* If a watch crystal is used for the auxiliary clock, allow using the
* auxiliary clock to be used as clock source for well-known
* symbol rates, so that enabling low power modes is possible while
* UART RX is active */
if ((clock_params.lfxt1_frequency == 32768)
&& (clock_params.auxiliary_clock_divier == AUXILIARY_CLOCK_DIVIDE_BY_1)) {
assert(msp430_auxiliary_clock_freq() == 32768);
result.clk_source = USCI_CLK_AUX;
/* The datasheet gives a formula that is used to estimate BRS, but
* for optimal accuracy "a detailed error calculation must be performed
* for each bit for each UCBRSx setting". We take the pre-calculated
* optimal values from the datasheet here. The idea is that if the
* clock source is slow ticking, the extra bit timing accuracy may
* be needed. Otherwise the estimation will be good enough.
*/
switch (target_hz) {
case 1200:
result.mctl = 2U << UCBRS_Pos;
result.br0 = 27;
return result;
case 2400:
result.mctl = 6U << UCBRS_Pos;
result.br0 = 13;
return result;
case 4800:
result.mctl = 7U << UCBRS_Pos;
result.br0 = 6;
return result;
case 9600:
result.mctl = 3U << UCBRS_Pos;
result.br0 = 3;
return result;
}
}
/* Otherwise, we compute BR and estimate BRS. We shift left by 7 to avoid
* floating point arithmetic. (7 is the largest shit amount for which
* clock frequencies with two-digit values in MHz don't exceed the 32 bit
* value range.) */
uint32_t tmp = DIV_ROUND(msp430_submain_clock_freq() << 7, target_hz);
/* BR is the integral part */
uint16_t br = tmp >> 7;
/* BRS is the fractional part multiplied by 8. We combine the multiplication
* by 8 (left-shift by 3) with the right-shift by 7 here to a right-shift
* by 4. */
uint8_t brs = (tmp & 0x7f) >> 4;
result.clk_source = USCI_CLK_SUBMAIN;
result.br0 = (uint8_t)br;
result.br1 = (uint8_t)(br >> 8);
result.mctl = brs << UCBRS_Pos;
return result;
}