Ejemplo n.º 1
0
Archivo: spi.c Proyecto: deepfryed/RIOT
int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
{
    /* get exclusive access to the device */
    mutex_lock(&locks[bus]);
    /* power on the device */
    poweron(bus);

    /* configure bus clock, in synchronous mode its calculated from
     * BAUD.reg = (f_ref / (2 * f_bus) - 1)
     * with f_ref := CLOCK_CORECLOCK as defined by the board */
    dev(bus)->BAUD.reg = (uint8_t)(((uint32_t)CLOCK_CORECLOCK) / (2 * clk) - 1);

    /* configure device to be master and set mode and pads,
     *
     * NOTE: we could configure the pads already during spi_init, but for
     * efficiency reason we do that here, so we can do all in one single write
     * to the CTRLA register */
    dev(bus)->CTRLA.reg = (SERCOM_SPI_CTRLA_MODE(0x3) |     /* 0x3 -> master */
                           SERCOM_SPI_CTRLA_DOPO(spi_config[bus].mosi_pad) |
                           SERCOM_SPI_CTRLA_DIPO(spi_config[bus].miso_pad) |
                           (mode <<  SERCOM_SPI_CTRLA_CPOL_Pos));
    /* also no synchronization needed here, as CTRLA is write-synchronized */

    /* finally enable the device */
    dev(bus)->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;
    while (dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) {}

    return SPI_OK;
}
Ejemplo n.º 2
0
int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed)
{
    SercomSpi* spi_dev = spi[dev].dev;

    uint8_t   dopo = 0;
    uint8_t   dipo = 0;
    uint8_t   cpha = 0;
    uint8_t   cpol = 0;
    uint32_t  f_baud = 0;

    switch(speed)
    {
        case SPI_SPEED_100KHZ:
            f_baud = 100000;
            break;
        case SPI_SPEED_400KHZ:
            f_baud = 400000;
            break;
        case SPI_SPEED_1MHZ:
            f_baud = 1000000;
            break;
        case SPI_SPEED_5MHZ:
            return -1;
        case SPI_SPEED_10MHZ:
            return -1;
    }

    switch(conf)
    {
        case SPI_CONF_FIRST_RISING: /**< first data bit is transacted on the first rising SCK edge */
            cpha = 0;
            cpol = 0;
            break;
        case SPI_CONF_SECOND_RISING:/**< first data bit is transacted on the second rising SCK edge */
            cpha = 1;
            cpol = 0;
            break;
        case SPI_CONF_FIRST_FALLING:/**< first data bit is transacted on the first falling SCK edge */
            cpha = 0;
            cpol = 1;
            break;
        case SPI_CONF_SECOND_FALLING:/**< first data bit is transacted on the second falling SCK edge */
            cpha = 1;
            cpol = 1;
            break;
    }

    /* Enable sercom4 in power manager */
    MCLK->APBCMASK.reg |= spi[dev].mclk;

    /* Setup clock */
    GCLK->PCHCTRL[ spi[dev].gclk_id ].reg =
        GCLK_PCHCTRL_CHEN |
        GCLK_PCHCTRL_GEN_GCLK0;

    while (!(GCLK->PCHCTRL[spi[dev].gclk_id].reg & GCLK_PCHCTRL_CHEN));

    /* SCLK+MOSI = output */
    gpio_init(spi[dev].sclk.pin, GPIO_DIR_OUT, GPIO_NOPULL);
    gpio_init(spi[dev].mosi.pin, GPIO_DIR_OUT, GPIO_NOPULL);
    /* MISO = input */
    gpio_init(spi[dev].miso.pin, GPIO_DIR_IN, GPIO_PULLUP);

    /*
     * Set alternate funcion (PMUX) for our ports.
     */
    gpio_init_mux(spi[dev].sclk.pin, spi[dev].sclk.pmux);
    gpio_init_mux(spi[dev].miso.pin, spi[dev].miso.pmux);
    gpio_init_mux(spi[dev].mosi.pin, spi[dev].mosi.pmux);

    /* pin pad mapping */
    dipo = spi[dev].dipo;
    dopo = spi[dev].dopo;

    /* Disable spi to write config */
    spi_dev->CTRLA.bit.ENABLE = 0;
    while (spi_dev->SYNCBUSY.reg);

    /* setup baud */
    spi_dev->BAUD.bit.BAUD = (uint8_t) (((uint32_t) GCLK_REF) / (2 * f_baud) - 1); /* Syncronous mode*/

    spi_dev->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3) /* 0x2 = slave 0x3 = master */
                          |  (SERCOM_SPI_CTRLA_DOPO(dopo))
                          |  (SERCOM_SPI_CTRLA_DIPO(dipo))
                          |  (cpha << SERCOM_SPI_CTRLA_CPHA_Pos)
                          |  (cpol << SERCOM_SPI_CTRLA_CPOL_Pos);

    while (spi_dev->SYNCBUSY.reg);
    spi_dev->CTRLB.reg = (SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_SPI_CTRLB_RXEN);
    while(spi_dev->SYNCBUSY.reg);
    spi_poweron(dev);
    return 0;
}
Ejemplo n.º 3
0
/**
 * \brief Initialize hardware and driver instance
 *
 * This function configures the clock system for the specified SERCOM module,
 * sets up the related pins and their MUX, initializes the SERCOM in SPI master
 * mode, and prepares the driver instance for operation.
 *
 * \pre \ref system_init() must have been called prior to this function.
 *
 * The SERCOM SPI module is left disabled after initialization, and must be
 * enabled with \ref spi_master_vec_enable() before a transfer can be done.
 *
 * \param[out] module Driver instance to initialize.
 * \param[in,out] sercom SERCOM module to initialize and associate driver
 * instance with.
 * \param[in] config Driver configuration to use.
 *
 * \return Status of initialization.
 * \retval STATUS_OK if initialization succeeded.
 * \retval STATUS_ERR_INVALID_ARG if driver has been misconfigured.
 */
enum status_code spi_master_vec_init(struct spi_master_vec_module *const module,
		Sercom *const sercom, const struct spi_master_vec_config *const config)
{
	Assert(module);
	Assert(sercom);
	Assert(config);

	enum status_code status;
	SercomSpi *const spi_hw = &(sercom->SPI);
	struct system_gclk_chan_config gclk_chan_conf;
	uint16_t tmp_baud;
	uint32_t sercom_index = _sercom_get_sercom_inst_index((Sercom *)spi_hw);
#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30)
	uint32_t pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos;
#else
	uint32_t pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos;
#endif
	uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE;
	uint32_t gclk_hz;

	module->sercom = sercom;

	/* Enable clock for the module interface */
	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);

	/* Set up the GCLK for the module */
	system_gclk_chan_get_config_defaults(&gclk_chan_conf);
	gclk_chan_conf.source_generator = config->gclk_generator;
	system_gclk_chan_set_config(gclk_index, &gclk_chan_conf);
	system_gclk_chan_enable(gclk_index);
	sercom_set_gclk_generator(config->gclk_generator, false);

	_spi_master_vec_wait_for_sync(spi_hw);

	/* Set up the SERCOM SPI module as master */
	spi_hw->CTRLA.reg = SERCOM_SPI_CTRLA_MODE(0x3);
	spi_hw->CTRLA.reg |= (uint32_t)config->mux_setting
			| config->transfer_mode
			| config->data_order
			| ((config->run_in_standby || system_is_debugger_present()) ?
					SERCOM_SPI_CTRLA_RUNSTDBY : 0);

	/* Get baud value from configured baudrate and internal clock rate */
	gclk_hz = system_gclk_chan_get_hz(gclk_index);
	status = _sercom_get_sync_baud_val(config->baudrate, gclk_hz, &tmp_baud);

	if (status != STATUS_OK) {
		/* Baud rate calculation error! */
		return STATUS_ERR_INVALID_ARG;
	}

	spi_hw->BAUD.reg = (uint8_t)tmp_baud;

	/* Configure the pin multiplexers */
	_spi_master_vec_pinmux_helper(config->pinmux_pad0, sercom, 0);
	_spi_master_vec_pinmux_helper(config->pinmux_pad3, sercom, 3);

	/* SERCOM PAD1 and PAD2 are used for slave SS.
	 * This is a SPI master driver, so control of slave SS must be left to
	 * the PORT module, i.e., peripheral MUX should not be set for that pin.
	 * DOPO controls which PAD is used for slave SS:
	 * If DOPO is odd, SERCOM_PAD1 is SS: SERCOM_PAD2 can be MUXed.
	 * If DOPO is even, SERCOM_PAD2 is SS: SERCOM_PAD1 can be MUXed.
	 */
	if (config->mux_setting & (1 << SERCOM_SPI_CTRLA_DOPO_Pos)) {
		_spi_master_vec_pinmux_helper(config->pinmux_pad2, sercom, 2);
	} else {
		_spi_master_vec_pinmux_helper(config->pinmux_pad1, sercom, 1);
	}

	/* Initialize our instance and register interrupt handler + data */
	module->rx_bufdesc_ptr = NULL;
	module->tx_bufdesc_ptr = NULL;
	module->direction = SPI_MASTER_VEC_DIRECTION_IDLE;
	module->status = STATUS_OK;
#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT
	CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE(module->busy_semaphore);
#endif

	_sercom_set_handler(sercom_index, _spi_master_vec_int_handler);
	_sercom_instances[sercom_index] = module;

	return STATUS_OK;
}
Ejemplo n.º 4
0
/** Initialize the SPI peripheral
 *
 * Configures the pins used by SPI, sets a default format and frequency, and enables the peripheral
 * @param[out] obj  The SPI object to initialize
 * @param[in]  mosi The pin to use for MOSI
 * @param[in]  miso The pin to use for MISO
 * @param[in]  sclk The pin to use for SCLK
 * @param[in]  ssel The pin to use for SSEL
 */
void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
{
    uint16_t baud = 0;
    uint32_t ctrla = 0;
    uint32_t ctrlb = 0;
    enum status_code error_code;

    if (g_sys_init == 0) {
        system_init();
        g_sys_init = 1;
    }

    /* TODO: Calculate SERCOM instance from pins */
    /* TEMP: Giving external SPI module value of SAMR21 for now */
    pSPI_SERCOM(obj) = EXT1_SPI_MODULE;

    /* Disable SPI */
    spi_disable(obj);

    /* Check if reset is in progress. */
    if (_SPI(obj).CTRLA.reg & SERCOM_SPI_CTRLA_SWRST) {
        return;
    }
    uint32_t sercom_index = _sercom_get_sercom_inst_index(pSPI_SERCOM(obj));
    uint32_t pm_index, gclk_index;
#if (SAML21)
    if (sercom_index == 5) {
        pm_index     = MCLK_APBDMASK_SERCOM5_Pos;
        gclk_index   =  SERCOM5_GCLK_ID_CORE;
    } else {
        pm_index     = sercom_index + MCLK_APBCMASK_SERCOM0_Pos;
        gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
    }
#else
    pm_index     = sercom_index + PM_APBCMASK_SERCOM0_Pos;
    gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
#endif

    /* Turn on module in PM */
#if (SAML21)
    if (sercom_index == 5) {
        system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index);
    } else {
        system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);
    }
#else
    system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);
#endif

    /* Set up the GCLK for the module */
    struct system_gclk_chan_config gclk_chan_conf;
    system_gclk_chan_get_config_defaults(&gclk_chan_conf);
    gclk_chan_conf.source_generator = GCLK_GENERATOR_0;
    system_gclk_chan_set_config(gclk_index, &gclk_chan_conf);
    system_gclk_chan_enable(gclk_index);
    sercom_set_gclk_generator(GCLK_GENERATOR_0, false);

#if DEVICE_SPI_ASYNCH
    /* Save the object */
    _sercom_instances[sercom_index] = obj;

    /* Configure interrupt handler */
    NVIC_SetVector((SERCOM0_IRQn + sercom_index), (uint32_t)_sercom_handlers[sercom_index]);
#endif

    /* Set the SERCOM in SPI master mode */
    _SPI(obj).CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3);
    pSPI_S(obj)->mode = SPI_MODE_MASTER;

    /* TODO: Do pin muxing here */
    struct system_pinmux_config pin_conf;
    system_pinmux_get_config_defaults(&pin_conf);
    pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT;

    uint32_t pad_pinmuxes[] = {
        EXT1_SPI_SERCOM_PINMUX_PAD0, EXT1_SPI_SERCOM_PINMUX_PAD1,
        EXT1_SPI_SERCOM_PINMUX_PAD2, EXT1_SPI_SERCOM_PINMUX_PAD3
    };

    /* Configure the SERCOM pins according to the user configuration */
    for (uint8_t pad = 0; pad < 4; pad++) {
        uint32_t current_pinmux = pad_pinmuxes[pad];
        if (current_pinmux != PINMUX_UNUSED) {
            pin_conf.mux_position = current_pinmux & 0xFFFF;
            system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf);
        }
    }