Open arakis opened 11 years ago
I unfortunately also know very little about SPI nor do I have a SPI device to test. The BCM2835 library which I wrapped with my GPIOMem class does have SPI functions. These could be wrapped with a class. Do you have a SPI device to test with?
My plan is to communicate with a ATMega AVR chip. But in the moment, i have no device here. I2C will be requied in the future, too.
I want give you a small hint: I found out, acessing the GPIO's is slow, very slow. The matter is, it needs to be compiled in release mode, because then the "Debug.WriteLine" calls are ignored than. Maybe you should write this hint in the readme, or maybe make a bool flag or a compilation symbol.
Without the Debug.WriteLine calls, it's round about 10x faster.
Greetings, Sebastian
I have now a SPI Device here. It's a OLED display. At the moment, i emulate SPI via standard GPIO, but it's slow, very slow, even a Commodore C64 is faster ;)
So, native SPI would be very important.
Greetings, Sebastian
Regarding it being slow, are you using the GPIOFile or the GPIOMem class? The GPIOFile class is very slow but that's nothing I can fix. The GPIOMem class is extremely fast as it uses the BCM2835 C library.
As for the Debug.WriteLine statements, I believe that would be expected behavior.
I'll have to do some research on the SPI stuff.
Hey, I'm using GPIOMem, of course. After some optimazions, of example removing all unneeded lines in the "Write"-Method (Exporting-Pin, ok, it will check it, but i need every percent of performence), and the DebugLine method and some other stuff. I didnt removed it, i use #ifdef-blocks. So, the pure call to the bcm-lib is now there.
So, after that changes, it's really fast - faster than native SPI?! (ok, this is NOT logical....). My Raspberry needs now 1000ms to update the OLED screen.
I've added a quick and dirty SPI-Support in my local version of your library:
public static class BCM2835Native {
//Low, high
public const uint LOW = 0x0;
public const uint HIGH = 0x1;
//bcm2835SPIBitOrder
public const uint BCM2835_SPI_BIT_ORDER_MSBFIRST = 1;
public const uint BCM2835_SPI_BIT_ORDER_LSBFIRST = 0;
//bcm2835SPIMode
public const uint BCM2835_SPI_MODE0 = 0;
public const uint BCM2835_SPI_MODE1 = 1;
public const uint BCM2835_SPI_MODE2 = 2;
public const uint BCM2835_SPI_MODE3 = 3;
//bcm2835SPIChipSelect
public const uint BCM2835_SPI_CS0 = 0;
public const uint BCM2835_SPI_CS1 = 1;
public const uint BCM2835_SPI_CS2 = 2;
public const uint BCM2835_SPI_CS_NONE = 3;
//bcm2835SPIClockDivider
public const uint BCM2835_SPI_CLOCK_DIVIDER_65536 = 0; //< 65536 = 262.144us = 3.814697260kHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768; //< 32768 = 131.072us = 7.629394531kHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384; //< 16384 = 65.536us = 15.25878906kHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192; //< 8192 = 32.768us = 30/51757813kHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096; //< 4096 = 16.384us = 61.03515625kHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048; //< 2048 = 8.192us = 122.0703125kHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024; //< 1024 = 4.096us = 244.140625kHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_512 = 512; //< 512 = 2.048us = 488.28125kHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_256 = 256; //< 256 = 1.024us = 976.5625MHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_128 = 128; //< 128 = 512ns = = 1.953125MHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_64 = 64; //< 64 = 256ns = 3.90625MHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_32 = 32; //< 32 = 128ns = 7.8125MHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_16 = 16; //< 16 = 64ns = 15.625MHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_8 = 8; //< 8 = 32ns = 31.25MHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_4 = 4; //< 4 = 16ns = 62.5MHz
public const uint BCM2835_SPI_CLOCK_DIVIDER_2 = 2; //< 2 = 8ns = 125MHz, fastest you can get
public const uint BCM2835_SPI_CLOCK_DIVIDER_1 = 1; //< 0 = 262.144us = 3.814697260kHz, same as 0/65536
[DllImport("libbcm2835.so", EntryPoint = "bcm2835_spi_begin")]
public static extern void bcm2835_spi_begin();
[DllImport("libbcm2835.so", EntryPoint = "bcm2835_spi_setBitOrder")]
public static extern void bcm2835_spi_setBitOrder(uint order);
[DllImport("libbcm2835.so", EntryPoint = "bcm2835_spi_setDataMode")]
public static extern void bcm2835_spi_setDataMode(uint mode);
[DllImport("libbcm2835.so", EntryPoint = "bcm2835_spi_setClockDivider")]
public static extern void bcm2835_spi_setClockDivider(uint divider);
[DllImport("libbcm2835.so", EntryPoint = "bcm2835_spi_chipSelect")]
public static extern void bcm2835_spi_chipSelect(uint cs);
[DllImport("libbcm2835.so", EntryPoint = "bcm2835_spi_setChipSelectPolarity")]
public static extern void bcm2835_spi_setChipSelectPolarity(uint cs, uint active);
[DllImport("libbcm2835.so", EntryPoint = "bcm2835_spi_transfer")]
public static extern int bcm2835_spi_transfer(uint data);
[DllImport("libbcm2835.so", EntryPoint = "bcm2835_spi_end")]
public static extern void bcm2835_spi_end();
[DllImport("libbcm2835.so", EntryPoint = "bcm2835_close")]
public static extern void bcm2835_close();
}
Bug it seems, it more slower than bit banging the GPIOs manually (emulating SPI), so in the moment, i do not use the native SPI. Maybe i will go back to this solution, when i found out, why the native SPI is slower than emulating SPI.
I'm using this code:
public class TSPI_BCM : TSPIDevice
{
private GPIO CS;
public TSPI_BCM(GPIO CS = null) {
this.CS = CS;
GPIOMem.Initialize();
BCM2835Native.bcm2835_spi_begin();
BCM2835Native.bcm2835_spi_setBitOrder(BCM2835Native.BCM2835_SPI_BIT_ORDER_MSBFIRST); // The default
BCM2835Native.bcm2835_spi_setDataMode(BCM2835Native.BCM2835_SPI_MODE0); // 0, 3
BCM2835Native.bcm2835_spi_setClockDivider(BCM2835Native.BCM2835_SPI_CLOCK_DIVIDER_16); // The default
BCM2835Native.bcm2835_spi_chipSelect(BCM2835Native.BCM2835_SPI_CS_NONE); // The default
//BCM2835Native.bcm2835_spi_setChipSelectPolarity(BCM2835Native.BCM2835_SPI_CS0, BCM2835Native.LOW); // the default
CS.Write(true);
}
//private int cnt;
public override void writeBits(bool[] bits) {
//System.Threading.Thread.Sleep(1);
CS.Write(false);
var bytes = IOUtils.ToByteArray(bits);
foreach (var b in bytes) {
//if (cnt++ < 20) Console.WriteLine(b);
BCM2835Native.bcm2835_spi_transfer(b);
}
//System.Threading.Thread.Sleep(1);
CS.Write(true);
}
public override IEnumerable<bool> readBits() {
throw new NotImplementedException();
}
}
Ok, i control the CS-Pin manually, because otherwise it will not work. (in my case)
Greetings, Sebastian
For emulating SPI, i'm using this code:
public class TSPIEmulator : TSPIDevice
{
private GPIO SDI;
private GPIO SDO;
private GPIO SCK;
private GPIO CS;
public TSPIEmulator(GPIO SDI, GPIO SDO, GPIO SCK, GPIO CS) {
this.SDI = SDI;
this.SDO = SDO;
this.SCK = SCK;
this.CS = CS;
CS.Write(true); //ensure it's true
SCK.Write(false);
}
public override void writeBits(bool[] bits) {
CS.Write(false);
foreach (var bit in bits) {
SDI.Write(bit);
SCK.Write(true);
SCK.Write(false);
}
CS.Write(true);
}
public override IEnumerable<bool> readBits() {
throw new NotImplementedException();
}
}
Please keep in mind, that this pice of code is using a fixed configuration. (bit order, CS polarity, Clock phase).
Oh, and for performance speed, i've added this code in your write-method of GPIOMem:
if (currentValue != -1) {
var currValue = currentValue == 0 ? false : true;
if (currValue == value) return;
}
Write(_pin, value);
currentValue = (value ? 1 : 0);
Normally, because i never change the direction, this is no problem.
Hello, my knowledge about the SPI-Interface is nearly zero, but i need it for controlling a display. The raspberry has SPI-Pins. Will you support SPI with this library?
Greetings, Sebastian