[ell-i-developers] Interim implementation for SPI.h

  • From: Pekka Nikander <pekka.nikander@xxxxxx>
  • To: "ell-i-developers@xxxxxxxxxxxxx" <ell-i-developers@xxxxxxxxxxxxx>
  • Date: Thu, 20 Feb 2014 14:13:06 +0200

Below is an interim implementation for SPI.h, showing where I'm heading to with 
the interface.

The lower-level C-language interface is mostly written already, but not tested. 
 

--Pekka

/*
 * Copyright (c) 2014 ELL-i co-operative
 *
 * This file is part of ELL-i software.
 *
 * ELL-i software 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.
 *
 * ELL-i software 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 should have received a copy of the GNU General Public License
 * along with ELL-i software.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @author Pekka Nikander <pekka.nikander@xxxxxxxxx>  2014
 *
 * @brief The Arduino library SPI class
 */

#ifndef _SPI_CLASS_H_
#define _SPI_CLASS_H_

#include <stm32f0xx.h>
#include <ellduino_gpio.h>   // XXX To be placed into the variant.h!
#include <ellduino_spi.h>    // XXX To be placed into the variant.h!
#include <arduelli_thread.h> // XXX TBD -- is this the right file name?

enum SPITransferMode {
    SPI_CONTINUE,
    SPI_LAST
};

enum SPIBitOrder {
    MSBFIRST = !SPI_CR1_LSBFIRST,
    LSBFIRST =  SPI_CR1_LSBFIRST,
};

enum SPIClockDivider {
    SPI_CLOCK_DIV2   = !SPI_CR1_BR_2 | !SPI_CR1_BR_1 | !SPI_CR1_BR_0,
    SPI_CLOCK_DIV4   = !SPI_CR1_BR_2 | !SPI_CR1_BR_1 |  SPI_CR1_BR_0,
    SPI_CLOCK_DIV8   = !SPI_CR1_BR_2 |  SPI_CR1_BR_1 | !SPI_CR1_BR_0,
    SPI_CLOCK_DIV16  = !SPI_CR1_BR_2 |  SPI_CR1_BR_1 |  SPI_CR1_BR_0,
    SPI_CLOCK_DIV32  =  SPI_CR1_BR_2 | !SPI_CR1_BR_1 | !SPI_CR1_BR_0,
    SPI_CLOCK_DIV64  =  SPI_CR1_BR_2 | !SPI_CR1_BR_1 |  SPI_CR1_BR_0,
    SPI_CLOCK_DIV128 =  SPI_CR1_BR_2 |  SPI_CR1_BR_1 | !SPI_CR1_BR_0,
    SPI_CLOCK_DIV256 =  SPI_CR1_BR_2 |  SPI_CR1_BR_1 |  SPI_CR1_BR_0,
};

enum SPIDataMode {
    SPI_MODE0 = !SPI_CR1_CPHA | !SPI_CR1_CPOL,
    SPI_MODE1 =  SPI_CR1_CPHA | !SPI_CR1_CPOL,
    SPI_MODE2 = !SPI_CR1_CPHA |  SPI_CR1_CPOL,
    SPI_MODE3 =  SPI_CR1_CPHA |  SPI_CR1_CPOL,
};

XXX TODO: Figure out the data structure to store per-SS-pin data
XXX TODO: Figure out a nice implementation for setClock

class SPIClass {
public:
    const SPI spi_;
    constexpr SPIClass(const SPI &);
    void begin(const uint8_t ss_pin = BOARD_DEFAULT_SS) const {
        digitalWrite(ss_pin, 1); /* Avoid glitch */
        pinMode(ss_pin, OUTPUT);
        spi_master_begin(&spi_);
        spi_activate();
    };

    void end(const uint8_t ss_pin) const {
        spi_master_end(&spi_);
        pinMode(ss_pin, INPUT /* XXX DEFAULT */);
    };

    void end(void) const {
        /* XXX Semantic inconsistency with begin(void) */
        spi_deactivate();
    }

    void setBitOrder(const SPIBitOrder bitOrder) const {
        setBitOrder(BOARD_SPI_DEFAULT_SS, bitOrder);
    }
    void setBitOrder(const uint8_t ss_pin, const SPIBitOrder bitOrder) const {
        XXX[ss_pin] &= ~SP1_CR1_LSBFIRST;
        XXX[ss_pin] |= bitOrder;
    }

    uint32_t setClockDivider(const SPIClockDivider clockDivider) const {
        return setClockDivider(BOARD_SPI_DEFAULT_SS, clockDivider);
    }
    uint32_t setClockDivider(const unit8_t ss_pin, const SPIClockDivider 
clockDivider) const {
        uint32_t oldValue = XXX[ss_pin] & SPI_CR1_BR;
        XXX[ss_pin] &= ~SPI_CR1_BR;
        XXX[ss_pin] |= clockDivider;
        return oldValue;
    }

    uint32_t setClock(const uint32_t hertz) const {
        return setClock(BOARD_SPI_DEFAULT_SS, hertz);
    }
    uint32_t setClock(const uint8_t ss_pin, const uint32_t hertz) const {
        extern "C" { extern volatile uint32_t SystemCoreClock; }
        const uint32_t wantedDivider = SystemCoreClock / hertz;
        
        XXX;

        setClockDivider(ss_pin, XXX);
    }

    void setDataMode(SPIDataMode dataMode) const {
        setDataMode(BOARD_SPI_DEFAULT_SS, dataMode);
    }
    void setDataMode(uint8_t ss_pin, SPIDataMode dataMode) const {
        XXX[ss_pin] &= ~(SPI_CR1_CPHA | SPI_CR1_CPOL);
        XXX[ss_pin] |= dataMode;
    }

    uint8_t transfer(uint8_t data, SPITransferMode mode = SPI_LAST) const {
        return transfer(BOARD_SPI_DEFAULT_SS, data, mode);
    }

    uint8_t transfer(uint8_t ss_pin, uint8_t data, SPITransferMode mode = 
SPI_LAST) const {
        return transfer(ss_pin, &data, 1, mode);
    }

    uint8_t transfer(uint8_t ss_pin, uint8_t data[], uint8_t len, 
SPITransferMode mode = SPI_LAST) const {
        activate_ss(ss_pin);

        const uint32_t cr1 = XXX[ss_pin];

        len = spi_transfer(&spi_, cr1, data, len);

        if (mode == SPI_LAST)
            deactivate_ss(ss_pin);
        return len;
    };
private:
    static inline void activate_ss(uint8_t ss_pin) { XXX };
    static inline void deactive_ss(uint8_t ss_pin) { XXX };
};

#define DEFINE_SPI(spi_number, ss_port, ss_pin, ss_af, miso_port, miso_pin, 
miso_af, mosi_port, mosi_pin, mosi_af, clk_port, clk_pin, clk_af) \
    ({ DEFINE_SPI_STRUCT(spi_number,                                    \
                         ss_port, ss_pin, ss_af,                        \
                         miso_port, miso_pin, miso_af,                  \
                         mosi_port, mosi_pin, mosi_af,                  \
                         clk_port, clk_pin, clk_af) })


constexpr SPIClass::SPIClass(const SPI &spi)
    : spi_(spi) {}

#endif//_SPI_CLASS_H_


Other related posts: