From 2ef467587344be3ab9cd82a383f1475ed9f432f1 Mon Sep 17 00:00:00 2001 From: hama Date: Sat, 25 Jun 2016 01:52:23 +0200 Subject: [PATCH] implemented: spi transfer --- Server/bcm2835.cpp | 132 +++++++++++++++++++++++++++++++++++++++ Server/bcm2835.hpp | 49 ++++++++++++++- Server/bcm2835def.hpp | 6 ++ Server/client.cpp | 2 +- Server/gpioprocessor.cpp | 39 ++++++++---- Server/gpioprocessor.hpp | 2 + 6 files changed, 217 insertions(+), 13 deletions(-) diff --git a/Server/bcm2835.cpp b/Server/bcm2835.cpp index 9881481..e00ef47 100644 --- a/Server/bcm2835.cpp +++ b/Server/bcm2835.cpp @@ -4,6 +4,16 @@ Bcm2835* Bcm2835::m_instance = NULL; +const int GPIO_FSEL_ALT0 = 0x00; +const int GPIO_FSEL_ALT1 = 0x05; +const int GPIO_FSEL_ALT2 = 0x06; +const int GPIO_FSEL_ALT3 = 0x07; +const int GPIO_FSEL_ALT4 = 0x03; +const int GPIO_FSEL_ALT5 = 0x02; +const int GPIO_FSEL_MASK = 0x07; + +const int OFFSET_SPI_CONTROL = 0; + /** * Constructor. * @@ -167,6 +177,9 @@ void Bcm2835::setMode(PinNumber pin, PinMode mode) case mOutput: mode2 = 1; break; + case mSpi: + mode2 = GPIO_FSEL_ALT0; + break; default: break; } @@ -221,6 +234,45 @@ void Bcm2835::setInputPullX(PinNumber pin, bool pullUpNotDown){ writeWord(m_gpioBase + offsetPullXEnableClock0 / sizeof (uint32_t) + pin/32, 0); } +/** + * Sets a SPI specific parameter. + * + * @param mode specify the parameter to change + * @param value the value of the parameter to set + * @param value2 2nd value of the parameter to set + */ +void Bcm2835::setSpiMode(SpiMode mode, int value, int value2){ + switch(mode){ + case spimClockDivider: + { + const int OFFSET_SPI_CLOCK = 0x0008; + writeWord(m_spioBase + OFFSET_SPI_CLOCK/4, value); + break; + } + case spimDataMode: + { + // Clock polarity + clock phase: + const int mask = (0x4 | 0x8); + setBits(m_spioBase + OFFSET_SPI_CONTROL/4, value << 2, mask); + break; + } + case spimChipSelect: + { + const int mask = 0x3; + setBits(m_spioBase + OFFSET_SPI_CONTROL/4, value, mask); + break; + } + case spimChipSelectPolarity: + { + int shift = 21 + value; + setBits(m_spioBase + OFFSET_SPI_CONTROL/4, value2 << shift, 1 << shift); + break; + } + default: + break; + } +} + /** * Sets the state of an output pin. * @@ -244,6 +296,86 @@ void Bcm2835::setState(PinNumber pin, PinState state){ } } +/** + * Stops a SPI session. + */ +void Bcm2835::spiEnd () { + // Set pins to input mode: + setMode(pinSpiCE1, mInput); + setMode(pinSpiCE0, mInput); + setMode(pinSpiMISO, mInput); + setMode(pinSpiMOSI, mInput); + setMode(pinSpiCLK, mInput); +} + +/** + * Starts a SPI session. + */ +void Bcm2835::spiStart () { + setMode(pinSpiCE1, mSpi); + setMode(pinSpiCE0, mSpi); + setMode(pinSpiMISO, mSpi); + setMode(pinSpiMOSI, mSpi); + setMode(pinSpiCLK, mSpi); + + const int OFFSET_SPI_CONTROL_STATUS = 0; + const int CS_CLEAR = 0x00000030; + volatile uint32_t* address = m_spioBase + OFFSET_SPI_CONTROL_STATUS/4; + writeWord(address, 0); + writeWord(address, CS_CLEAR); +} + +/** + * Writes a number of bytes and reads bytes via SPI. + * + * @param input content to send + * @param maxOutput maximal count of bytes to read + * @param output OUT: data read via SPI + * @param microseconds 0: busy waiting
+ * otherwise: pause between reads +*/ +void Bcm2835::spiTransfer(const DynBuffer& input, size_t maxOutput, + DynBuffer& output, int microseconds) +{ + const int OFFSET_SPI_CONTROL_STATUS = 0; + const int OFFSET_FIFO = 4; + volatile uint32_t* control = m_spioBase + OFFSET_SPI_CONTROL_STATUS/4; + volatile uint32_t* fifo = m_spioBase + OFFSET_FIFO/4; + size_t ixInput = 0; + + /* Clear TX and RX fifos, start transfer */ + const int MASK_CLEAR_TX_RX = 0x30; + const int MASK_TRANSFER_ACTIVE = 0x80; + const int MASK_TX_FIFO = 0x40000; + const int MASK_RX_FIFO = 0x20000; + const int MASK_SPI_TRANSFER_DONE = 0x10000; + + setBits(control, MASK_CLEAR_TX_RX, MASK_CLEAR_TX_RX); + setBits(control, MASK_TRANSFER_ACTIVE, MASK_TRANSFER_ACTIVE); + + /* Use the FIFO's to reduce the interbyte times */ + while(ixInput < input.length() || output.length() < maxOutput) + { + // Fill the sender fifo: + while (ixInput < input.length() && (readWord(control) & MASK_TX_FIFO) != 0){ + writeWord(fifo, input.at(ixInput++)); + } + // read the receiver fifo: + while (output.length() < maxOutput + && (readWord(control) & MASK_RX_FIFO) != 0){ + output.append((char) readWord(fifo)); + } + if (microseconds > 0) + delay(microseconds); + } + // wait for the end of transfer: + while( (readWord(control) & MASK_SPI_TRANSFER_DONE) != 0){ + if (microseconds > 0) + delay(microseconds); + } + setBits(control, 0, MASK_TRANSFER_ACTIVE); +} + /** * Sets the state of a pin in output mode. * diff --git a/Server/bcm2835.hpp b/Server/bcm2835.hpp index 9157300..b10920d 100644 --- a/Server/bcm2835.hpp +++ b/Server/bcm2835.hpp @@ -8,7 +8,8 @@ enum PinState { enum PinMode { mUndef, mOutput, - mInput + mInput, + mSpi, }; /** @@ -37,6 +38,42 @@ public: * This is the heart of the raspberry pi. */ class Bcm2835 { +public: + enum SpiMode { + spimUndef, + spimClockDivider, + spimDataMode, + spimChipSelect, + spimChipSelectPolarity, + }; + enum SpiValue { + spivChipSelect0 = 0, + spivChipSelect1 = 1, + spivInactive = 0, + spivActive = 1, + spivLow = 0, + spivHigh = 1, + spivMode0 = 0, + spivMode1 = 1, + spivMode2 = 2, + spivMode3 = 3, + spivDivider2 = 2, //< 125 MHz + spivDivider4 = 4, //< 62.5 MHz + spivDivider8 = 8, //< 31.25 MHz + spivDivider16 = 16, //< 15.6 MHz + spivDivider32 = 32, //< 7.8 MHz + spivDivider64 = 64, //< 3.9 MHz + spivDivider128 = 128, //< 1.9 MHz + spivDivider256 = 256, //< 976 kHz + spivDivider512 = 512, //< 488 kHz + spivDivider1024 = 1024, //< 244 kHz + spivDivider2048 = 2048, //< 122 kHz + spivDivider4096 = 4096, //< 61 kHz + spivDivider8192 = 8192, //< 30.5 kHz + spivDivider16384 = 16384, //< 15.2 kHz + spivDivider32768 = 32768, //< 7.62 kHz + spivDivider65536 = 0, //< 3.81 kHz + }; protected: Bcm2835(Announcer* logger, bool simulation = false); public: @@ -52,7 +89,12 @@ public: PinState readFromGPIO(PinNumber pin); void setInputPullX(PinNumber pin, bool pullUpNotDown); void setMode(PinNumber pin, PinMode mode); + void setSpiMode(SpiMode, int value, int value2 = 0); void setState(PinNumber pin, PinState state); + void spiEnd(); + void spiStart(); + void spiTransfer(const DynBuffer& input, size_t maxOutput, + DynBuffer& output, int microseconds = 0); /** * Returns whether the instance is valid. * @return true: the instance is valid (can work) @@ -65,6 +107,11 @@ protected: uint32_t readWord(volatile uint32_t* addr); void setBits(volatile uint32_t* addr, uint32_t value, uint32_t mask); void writeWord(volatile uint32_t* addr, uint32_t value); + /** + */ + inline void writeWordUnchecked(volatile uint32_t* addr, uint32_t value){ + *addr = value; + } private: static Bcm2835* m_instance; private: diff --git a/Server/bcm2835def.hpp b/Server/bcm2835def.hpp index f618a8e..8fa9221 100644 --- a/Server/bcm2835def.hpp +++ b/Server/bcm2835def.hpp @@ -81,6 +81,12 @@ enum PinNumber { pinWiring_14 = 11, pinWiring_15 = 14, pinWiring_16 = 15, + + pinSpiCE1 = 7, + pinSpiCE0 = 8, + pinSpiMISO = 9, + pinSpiMOSI = 10, + pinSpiCLK = 11 }; extern PinNumber pinNameToNumber(const char* name); #endif // BCM2835DEF_HPP diff --git a/Server/client.cpp b/Server/client.cpp index 74b92bd..2fe9cd4 100644 --- a/Server/client.cpp +++ b/Server/client.cpp @@ -178,7 +178,7 @@ int main (int argc, char **argv) { if (argc > 3 && (count = toNumber(argv[3], length)) == 0) usage("invalid count", argv[2]); int delay = 1000; - if (argc > 4 && (count = toNumber(argv[4], length)) == 0) + if (argc > 4 && (delay = toNumber(argv[4], length)) == 0) usage("invalid delay", argv[3]); prepareTrace(pin, count, delay, buffer); } else if (strcmp(command, "getbuffer") == 0){ diff --git a/Server/gpioprocessor.cpp b/Server/gpioprocessor.cpp index e39dfb0..4340148 100644 --- a/Server/gpioprocessor.cpp +++ b/Server/gpioprocessor.cpp @@ -9,7 +9,8 @@ GPIOProcessor::GPIOProcessor(Announcer* logger, bool simulation) : m_pins(), m_logger(logger), m_pool(128, logger), - m_buffers() + m_buffers(), + m_lastBuffer(-1) { memset(m_buffers, 0, sizeof m_buffers); } @@ -47,6 +48,29 @@ void GPIOProcessor::blink(DynBuffer& buffer){ } } +/** + * Returns the next "free" buffer. + * + * @param size the size of the buffer + * @param name OUT: the name of the buffer + * @return NULL: no free buffer
+ * otherwise: a free buffer + */ +DynBuffer* GPIOProcessor::findBuffer(int size, char& name){ + int start; + DynBuffer* rc = NULL; + if ( (start = ++m_lastBuffer) >= 26) + start = m_lastBuffer = 0; + for (int ii = 0; ii < 26; ii++){ + int ix = (start + ii) % 26; + if (m_buffers[ix] == NULL){ + name = 'A' + ix; + rc = m_buffers[ix] = new DynBuffer(size); + break; + } + } + return rc; +} /** * Gets a buffer with data assembled from 'trace'. * @@ -174,19 +198,12 @@ void GPIOProcessor::trace(DynBuffer& buffer){ int delay = buffer.valueOfLE(4, 4 + 1 + 4); printf("Pin: %d count: %d delay: %d\n", pin, count, delay); char name = 0; - int ix; - for (ix = 0; ix < 26; ix++){ - if (m_buffers[ix] == NULL){ - name = 'A' + ix; - break; - } - } - if (name == 0){ + DynBuffer* outputBuffer = findBuffer(count, name); + if (outputBuffer == NULL){ buffer.set("ERR too few buffers: 26"); } else { - m_buffers[ix] = new DynBuffer(count); TraceInputTimer* timer = new TraceInputTimer(pin, delay, - count, m_buffers[ix], this, m_logger, &m_pool); + count,outputBuffer, this, m_logger, &m_pool); timer->start(); buffer.set("OK #").append(name); } diff --git a/Server/gpioprocessor.hpp b/Server/gpioprocessor.hpp index 9a6bec5..ac7b3ce 100644 --- a/Server/gpioprocessor.hpp +++ b/Server/gpioprocessor.hpp @@ -15,6 +15,7 @@ private: GPIOProcessor& operator=(const GPIOProcessor& other); private: void blink(DynBuffer& buffer); + DynBuffer* findBuffer(int size, char& name); void getBuffer(DynBuffer& buffer); void melody(DynBuffer& buffer); void pulseWidthModulation(DynBuffer& buffer); @@ -26,6 +27,7 @@ private: Announcer* m_logger; ThreadPool m_pool; DynBuffer* m_buffers[26]; + int m_lastBuffer; }; #endif // GPIOPROCESSOR_H -- 2.39.5