Added Software SPI
authorBjoernSch <mail@bjoern-schrader.de>
Sat, 6 Aug 2016 10:18:17 +0000 (12:18 +0200)
committerBjoernSch <mail@bjoern-schrader.de>
Sat, 6 Aug 2016 10:18:17 +0000 (12:18 +0200)
Added Software SPI:
bbSPIopen
bbSPIclose
bbSPIxfer
Some documentation is not finished yet.

command.c
pigpio.c
pigpio.h
pigpio.py

index 239ec2ef3c468d110d3c82216134a2dfbba6f3b8..ead8879274c5d263e6cfdec2a8670f505b39dc83 100644 (file)
--- a/command.c
+++ b/command.c
@@ -49,6 +49,10 @@ cmdInfo_t cmdInfo[]=
    {PI_CMD_BI2CO, "BI2CO", 131, 0}, // bbI2COpen
    {PI_CMD_BI2CZ, "BI2CZ", 193, 6}, // bbI2CZip
 
+   {PI_CMD_BSPIC, "BSPIC", 112, 0}, // bbSPIClose
+   {PI_CMD_BSPIO, "BSPIO", 134, 0}, // bbSPIOpen
+   {PI_CMD_BSPIX, "BSPIX", 193, 6}, // bbSPIXfer
+
    {PI_CMD_BR1,   "BR1",   101, 3}, // gpioRead_Bits_0_31
    {PI_CMD_BR2,   "BR2",   101, 3}, // gpioRead_Bits_32_53
 
@@ -256,6 +260,11 @@ BC2 bits         Clear GPIO in bank 2\n\
 BI2CC sda        Close bit bang I2C\n\
 BI2CO sda scl baud | Open bit bang I2C\n\
 BI2CZ sda ...    I2C bit bang multiple transactions\n\
+\n\
+BSPIC cs        Close bit bang SPI\n\
+BSPIO cs miso mosi sclk baud flag | Open bit bang SPI\n\
+BSPIX cs ...    SPI bit bang transfer\n\
+\n\
 BR1              Read bank 1 GPIO\n\
 BR2              Read bank 2 GPIO\n\
 BS1 bits         Set GPIO in bank 1\n\
@@ -529,6 +538,8 @@ static errInfo_t errInfo[]=
    {PI_FILE_IS_A_DIR    , "file is a directory"},
    {PI_BAD_SHELL_STATUS , "bad shell return status"},
    {PI_BAD_SCRIPT_NAME  , "bad script name"},
+   {PI_BAD_SPI_BAUD     , "bad SPI baud rate, not 50-500k"},
+   {PI_NOT_SPI_GPIO     , "no bit bang SPI in progress on GPIO"},
 
 };
 
@@ -599,8 +610,8 @@ int cmdParse(
    char *p8;
    int32_t *p32;
    char c;
-   uint32_t tp1=0, tp2=0, tp3=0;
-   int8_t to1, to2, to3;
+   uint32_t tp1=0, tp2=0, tp3=0, tp4=0, tp5=0;
+   int8_t to1, to2, to3, to4, to5;
    int eaten;
 
    /* Check that ext is big enough for the largest message. */
@@ -653,7 +664,7 @@ int cmdParse(
       case 112: /* BI2CC FC  GDC  GPW  I2CC  I2CRB
                    MG  MICS  MILS  MODEG  NC  NP  PADG PFG  PRG
                    PROCD  PROCP  PROCS  PRRG  R  READ  SLRC  SPIC
-                   WVDEL  WVSC  WVSM  WVSP  WVTX  WVTXR
+                   WVDEL  WVSC  WVSM  WVSP  WVTX  WVTXR  BSPIC
 
                    One positive parameter.
                 */
@@ -914,6 +925,36 @@ int cmdParse(
 
          break;
 
+      case 134: /* BSPIO
+
+                   Six parameters.  First to Fifth positive.
+                   Sixth may be negative when interpreted as an int.
+                */
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[1]);
+         ctl->eaten += getNum(buf+ctl->eaten, &tp1, &to1);
+         ctl->eaten += getNum(buf+ctl->eaten, &tp2, &to2);
+         ctl->eaten += getNum(buf+ctl->eaten, &tp3, &to3);
+         ctl->eaten += getNum(buf+ctl->eaten, &tp4, &to4);
+         ctl->eaten += getNum(buf+ctl->eaten, &tp5, &to5);
+                                    
+         if ((ctl->opt[1] > 0) && ((int)p[1] >= 0) &&
+             (to1 == CMD_NUMERIC) && ((int)tp1 >= 0) &&
+             (to2 == CMD_NUMERIC) && ((int)tp2 >= 0) &&
+             (to3 == CMD_NUMERIC) && ((int)tp3 >= 0) &&
+             (to4 == CMD_NUMERIC) && ((int)tp4 >= 0) &&
+             (to5 == CMD_NUMERIC))
+         {
+            p[3] = 5 * 4;
+            memcpy(ext, &tp1, 4);
+            memcpy(ext, &tp2, 4);
+            memcpy(ext, &tp3, 4);
+            memcpy(ext, &tp4, 4);
+            memcpy(ext, &tp5, 4);
+            valid = 1;
+         }
+
+         break;
+
       case 191: /* PROCR
 
                    One to 11 parameters, first positive,
@@ -971,6 +1012,7 @@ int cmdParse(
          break;
 
       case 193: /* BI2CZ  FW  I2CWD  I2CZ  SERW  SPIW  SPIX
+                   BSPIX
 
                    Two or more parameters, first >=0, rest 0-255.
                 */
index 3f0b2f84cb99f9bcdd3e77120241755a7e522345..3ae2a5235d54fa593556288254e5c7933b1c7d43 100644 (file)
--- a/pigpio.c
+++ b/pigpio.c
@@ -748,10 +748,14 @@ Assumes two counters per block.  Each counter 4 * 16 (16^4=65536)
 #define PI_NOTIFY_RUNNING 3
 #define PI_NOTIFY_PAUSED  4
 
-#define PI_WFRX_NONE    0
-#define PI_WFRX_SERIAL  1
-#define PI_WFRX_I2C     2
-#define PI_WFRX_I2C_CLK 3
+#define PI_WFRX_NONE     0
+#define PI_WFRX_SERIAL   1
+#define PI_WFRX_I2C      2
+#define PI_WFRX_I2C_CLK  3
+#define PI_WFRX_SPI_MISO 4
+#define PI_WFRX_SPI_MOSI 5
+#define PI_WFRX_SPI_CS   6
+#define PI_WFRX_SPI_SCLK  7
 
 #define PI_WF_MICROS   1
 
@@ -846,6 +850,8 @@ Assumes two counters per block.  Each counter 4 * 16 (16^4=65536)
 #define PI_SPI_FLAGS_GET_RESVD(x)   (((x)>>5)&7)
 #define PI_SPI_FLAGS_GET_CSPOLS(x)  (((x)>>2)&7)
 #define PI_SPI_FLAGS_GET_MODE(x)     ((x)&3)
+#define PI_SPI_FLAGS_GET_CPHA(x)     ((x)&1)
+#define PI_SPI_FLAGS_GET_CPOL(x)     ((x)&2)
 
 #define PI_STARTING 0
 #define PI_RUNNING  1
@@ -1095,6 +1101,21 @@ typedef struct
    int started;
 } wfRxI2C_t;
 
+typedef struct
+{
+   int MISO;
+   int MOSI;
+   int CS;
+   int SCLK;
+   int delay;
+   int spiFlags;
+   int MISOMode;
+   int MOSIMode;
+   int CSMode;
+   int SCLKMode;
+   int started;
+} wfRxSPI_t;
+
 typedef struct
 {
    int      mode;
@@ -1104,6 +1125,7 @@ typedef struct
    {
       wfRxSerial_t s;
       wfRxI2C_t    I;
+      wfRxSPI_t    S;
    };
 } wfRx_t;
 
@@ -1757,10 +1779,9 @@ static int myDoCommand(uint32_t *p, unsigned bufSize, char *buf)
 {
    int res, i, j;
    uint32_t mask;
-   uint32_t tmp1, tmp2, tmp3;
+   uint32_t tmp1, tmp2, tmp3, tmp4, tmp5;
    gpioPulse_t *pulse;
    int masked;
-
    res = 0;
 
    switch (p[0])
@@ -1793,7 +1814,9 @@ static int myDoCommand(uint32_t *p, unsigned bufSize, char *buf)
          }
          break;
 
-      case PI_CMD_BI2CC: res = bbI2CClose(p[1]); break;
+      case PI_CMD_BI2CC:
+         res = bbI2CClose(p[1]);
+         break;
 
       case PI_CMD_BI2CO:
          memcpy(&p[4], buf, 4);
@@ -1810,6 +1833,25 @@ static int myDoCommand(uint32_t *p, unsigned bufSize, char *buf)
          }
          break;
 
+      case PI_CMD_BSPIO:
+         memcpy(&tmp1, buf, 4);   // MISO
+         memcpy(&tmp2, buf+4, 4); // MOSI
+         memcpy(&tmp3, buf+8, 4); // SCLK
+         memcpy(&tmp4, buf+12, 4);// baud
+         memcpy(&tmp5, buf+16, 4);// flags
+         
+         res = bbSPIOpen(p[1], tmp1, tmp2, tmp3, tmp4, tmp5);
+         break;
+
+      case PI_CMD_BSPIC:
+         res = bbSPIClose(p[1]);
+         break;
+
+      case PI_CMD_BSPIX:
+         if (p[3] > bufSize) p[3] = bufSize;
+            res = bbSPIXfer(p[1], buf, buf, p[3]);
+         break;
+
       case PI_CMD_BR1: res = gpioRead_Bits_0_31(); break;
 
       case PI_CMD_BR2: res = gpioRead_Bits_32_53(); break;
@@ -1995,8 +2037,6 @@ static int myDoCommand(uint32_t *p, unsigned bufSize, char *buf)
          }
          break;
 
-
-
       case PI_CMD_MICS:
          if (p[1] <= PI_MAX_MICS_DELAY) myGpioDelay(p[1]);
          else res = PI_BAD_MICS_DELAY;
@@ -2139,13 +2179,14 @@ static int myDoCommand(uint32_t *p, unsigned bufSize, char *buf)
       case PI_CMD_SLRC: res = gpioSerialReadClose(p[1]); break;
 
       case PI_CMD_SLRO:
-            memcpy(&p[4], buf, 4);
-            res = gpioSerialReadOpen(p[1], p[2], p[4]); break;
+         memcpy(&p[4], buf, 4);
+         res = gpioSerialReadOpen(p[1], p[2], p[4]); break;
 
       case PI_CMD_SLRI: res = gpioSerialReadInvert(p[1], p[2]); break;
 
-
-      case PI_CMD_SPIC: res = spiClose(p[1]); break;
+      case PI_CMD_SPIC:
+         res = spiClose(p[1]);
+         break;
 
       case PI_CMD_SPIO:
          memcpy(&p[4], buf, 4);
@@ -6651,6 +6692,7 @@ static void *pthSocketThreadHandler(void *fdC)
          case PI_CMD_SLR:
          case PI_CMD_SPIX:
          case PI_CMD_SPIR:
+       case PI_CMD_BSPIX:
 
             if (((int)p[3]) > 0)
             {
@@ -9744,6 +9786,26 @@ static int read_SDA(wfRx_t *w)
    return gpioRead(w->I.SDA);
 }
 
+static void set_CS(wfRx_t *w)
+{
+   myGpioWrite(w->S.CS, PI_SPI_FLAGS_GET_CSPOLS(w->S.spiFlags));
+}
+
+static void clear_CS(wfRx_t *w)
+{
+   myGpioWrite(w->S.CS, !PI_SPI_FLAGS_GET_CSPOLS(w->S.spiFlags));
+}
+
+static void set_SCLK(wfRx_t *w)
+{
+   myGpioWrite(w->S.SCLK, !PI_SPI_FLAGS_GET_CPOL(w->S.spiFlags));
+}
+
+static void clear_SCLK(wfRx_t *w)
+{
+   myGpioWrite(w->S.SCLK, PI_SPI_FLAGS_GET_CPOL(w->S.spiFlags));
+}
+
 static void set_SDA(wfRx_t *w)
 {
    myGpioSetMode(w->I.SDA, PI_INPUT);
@@ -9766,6 +9828,11 @@ static void I2C_delay(wfRx_t *w)
    myGpioDelay(w->I.delay);
 }
 
+static void SPI_delay(wfRx_t *w)
+{
+   myGpioDelay(w->S.delay);
+}
+
 static void I2C_clock_stretch(wfRx_t *w)
 {
    uint32_t now, max_stretch=10000;
@@ -9775,6 +9842,30 @@ static void I2C_clock_stretch(wfRx_t *w)
    while ((gpioRead(w->I.SCL) == 0) && ((gpioTick()-now) < max_stretch));
 }
 
+static void bbSPIStart(wfRx_t *w)
+{
+   if (w->S.started)
+   {
+      clear_SCLK(w);
+      clear_CS(w);
+      SPI_delay(w);
+   }
+
+   clear_SCLK(w);
+   set_CS(w);
+
+   w->S.started = 1;
+}
+
+static void bbSPIStop(wfRx_t *w)
+{
+   clear_CS(w);
+   clear_SCLK(w);
+   SPI_delay(w);
+   
+   w->S.started = 0;
+}
+
 static void I2CStart(wfRx_t *w)
 {
    if (w->I.started)
@@ -9859,6 +9950,71 @@ static uint8_t I2CGetByte(wfRx_t *w, int nack)
    return byte;
 }
 
+static uint8_t bbSPIXferByte(wfRx_t *w, char txByte)
+{
+   uint8_t bit, rxByte=0;
+
+   if (PI_SPI_FLAGS_GET_CPHA(w->S.spiFlags))
+   {
+      for (bit=0; bit<8; bit++)
+      {
+         if (PI_SPI_FLAGS_GET_TX_LSB(w->S.spiFlags))
+         {
+            myGpioWrite(w->S.MOSI, txByte & 0x01);
+            txByte >>= 1;
+         }
+         else
+         {
+            myGpioWrite(w->S.MOSI, txByte & 0x80);
+            txByte <<= 1;
+         }
+         set_SCLK(w);
+         SPI_delay(w);
+         
+         if (PI_SPI_FLAGS_GET_RX_LSB(w->S.spiFlags))
+         {
+            rxByte = (rxByte >> 1) | myGpioRead(w->S.MISO) << 7;
+         }
+         else
+         {
+            rxByte = (rxByte << 1) | myGpioRead(w->S.MISO);
+         }
+         clear_SCLK(w);
+         SPI_delay(w);
+      }
+   }
+   else
+   {
+      for (bit=0; bit<8; bit++)
+      {
+         if (PI_SPI_FLAGS_GET_TX_LSB(w->S.spiFlags))
+         {
+            myGpioWrite(w->S.MOSI, txByte & 0x01);
+            txByte >>= 1;
+         }
+         else
+         {
+            myGpioWrite(w->S.MOSI, txByte & 0x80);
+            txByte <<= 1;
+         }
+         if (PI_SPI_FLAGS_GET_RX_LSB(w->S.spiFlags))
+         {
+            rxByte = (rxByte >> 1) | myGpioRead(w->S.MISO) << 7;
+         }
+         else
+         {
+            rxByte = (rxByte << 1) | myGpioRead(w->S.MISO);
+         }
+         set_SCLK(w);
+         SPI_delay(w);
+         clear_SCLK(w);
+         SPI_delay(w);
+      }
+   }
+   
+   return rxByte;
+}
+
 int bbI2COpen(unsigned SDA, unsigned SCL, unsigned baud)
 {
    DBG(DBG_USER, "SDA=%d SCL=%d baud=%d", SDA, SCL, baud);
@@ -9888,7 +10044,7 @@ int bbI2COpen(unsigned SDA, unsigned SCL, unsigned baud)
    wfRx[SDA].I.started = 0;
    wfRx[SDA].I.SDA = SDA;
    wfRx[SDA].I.SCL = SCL;
-   wfRx[SDA].I.delay = 500000 / baud;
+   wfRx[SDA].I.delay = (500000 / baud) - 1;
    wfRx[SDA].I.SDAMode = gpioGetMode(SDA);
    wfRx[SDA].I.SCLMode = gpioGetMode(SCL);
 
@@ -9901,6 +10057,73 @@ int bbI2COpen(unsigned SDA, unsigned SCL, unsigned baud)
    return 0;
 }
 
+int bbSPIOpen(unsigned CS, unsigned MISO, unsigned MOSI, unsigned SCLK, unsigned baud, unsigned spiFlags)
+{
+   DBG(DBG_USER, "MISO=%d MOSI=%d CS=%d SCLK=%d baud=%d", MISO, MOSI, CS, SCLK, baud);
+
+   CHECK_INITED;
+
+   if (CS > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad CS (%d)", CS);
+
+   if (MISO > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad MISO (%d)", MISO);
+
+   if (MOSI > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad MOSI (%d)", MOSI);
+
+   if (SCLK > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad SCLK (%d)", SCLK);
+
+   if ((baud < PI_BB_SPI_MIN_BAUD) || (baud > PI_BB_SPI_MAX_BAUD))
+      SOFT_ERROR(PI_BAD_SPI_BAUD,
+         "CS %d, bad baud rate (%d)", CS, baud);
+
+   if (wfRx[CS].mode != PI_WFRX_NONE)
+      SOFT_ERROR(PI_GPIO_IN_USE, "gpio %d is already being used, mode %d", CS, wfRx[CS].mode);
+
+   if (!((wfRx[MISO].mode == PI_WFRX_NONE)  || (wfRx[MISO].mode == PI_WFRX_SPI_MISO)) || (MISO == CS))
+      SOFT_ERROR(PI_GPIO_IN_USE, "gpio %d is already being used, mode %d", MISO, wfRx[MISO].mode);
+
+   if (!((wfRx[MOSI].mode == PI_WFRX_NONE)  || (wfRx[MOSI].mode == PI_WFRX_SPI_MOSI)) || (MOSI == CS) || (MOSI == MISO))
+      SOFT_ERROR(PI_GPIO_IN_USE, "gpio %d is already being used, mode %d", MOSI, wfRx[MOSI].mode);
+
+   if (!((wfRx[SCLK].mode == PI_WFRX_NONE)  || (wfRx[SCLK].mode == PI_WFRX_SPI_SCLK)) || (SCLK == CS) || (SCLK == MISO) || (SCLK == MOSI))
+      SOFT_ERROR(PI_GPIO_IN_USE, "gpio %d is already being used, mode %d", SCLK, wfRx[SCLK].mode);
+
+   wfRx[MISO].gpio = MISO;
+   wfRx[MISO].mode = PI_WFRX_SPI_MISO;
+
+   wfRx[MOSI].gpio = MOSI;
+   wfRx[MOSI].mode = PI_WFRX_SPI_MOSI;
+
+   wfRx[SCLK].gpio = SCLK;
+   wfRx[SCLK].mode = PI_WFRX_SPI_SCLK;
+
+   wfRx[CS].gpio = CS;
+   wfRx[CS].mode = PI_WFRX_SPI_CS;
+   wfRx[CS].baud = baud;
+
+   wfRx[CS].S.started = 0;
+   wfRx[CS].S.MISO = MISO;
+   wfRx[CS].S.MOSI = MOSI;
+   wfRx[CS].S.CS = CS;
+   wfRx[CS].S.SCLK = SCLK;
+   wfRx[CS].S.delay = 500000 / baud;
+   wfRx[CS].S.spiFlags = spiFlags;
+   wfRx[CS].S.MISOMode = gpioGetMode(MISO);
+   wfRx[CS].S.MOSIMode = gpioGetMode(MOSI);
+   wfRx[CS].S.CSMode = gpioGetMode(CS);
+   wfRx[CS].S.SCLKMode = gpioGetMode(SCLK);
+
+   myGpioSetMode(MISO, PI_INPUT);
+   myGpioSetMode(MOSI, PI_OUTPUT);
+   myGpioSetMode(CS, PI_OUTPUT);
+   myGpioSetMode(SCLK, PI_OUTPUT);
+
+   return 0;
+}
+
 
 /* ----------------------------------------------------------------------- */
 
@@ -9937,6 +10160,87 @@ int bbI2CClose(unsigned SDA)
    return 0;
 }
 
+int bbSPIClose(unsigned CS)
+{
+   DBG(DBG_USER, "CS=%d", CS);
+
+   CHECK_INITED;
+
+   if (CS > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", CS);
+
+   switch(wfRx[CS].mode)
+   {
+      case PI_WFRX_SPI_CS:
+
+         myGpioSetMode(wfRx[CS].S.MISO, wfRx[CS].S.MISOMode);
+         myGpioSetMode(wfRx[CS].S.MOSI, wfRx[CS].S.MOSIMode);
+         myGpioSetMode(wfRx[CS].S.CS, wfRx[CS].S.CSMode);
+         myGpioSetMode(wfRx[CS].S.SCLK, wfRx[CS].S.SCLKMode);
+
+         wfRx[wfRx[CS].S.MISO].mode = PI_WFRX_NONE;
+         wfRx[wfRx[CS].S.MOSI].mode = PI_WFRX_NONE;
+         wfRx[wfRx[CS].S.CS].mode = PI_WFRX_NONE;
+         wfRx[wfRx[CS].S.SCLK].mode = PI_WFRX_NONE;
+
+         break;
+
+      default:
+
+         SOFT_ERROR(PI_NOT_SPI_GPIO, "no SPI on gpio (%d)", CS);
+
+         break;
+
+   }
+
+   return 0;
+}
+/*-------------------------------------------------------------------------*/
+
+int bbSPIXfer(
+   unsigned CS,
+   char *inBuf,
+   char *outBuf,
+   unsigned len)
+{
+   int pos, status;
+   wfRx_t *w;
+
+   DBG(DBG_USER, "gpio=%d inBuf=%s outBuf=%08X len=%d",
+      CS, myBuf2Str(len, (char *)inBuf), (int)outBuf, len);
+
+   CHECK_INITED;
+
+   if (CS > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", CS);
+
+   if (wfRx[CS].mode != PI_WFRX_SPI_CS)
+      SOFT_ERROR(PI_NOT_SPI_GPIO, "no SPI on gpio (%d)", CS);
+
+   if (!inBuf || !len)
+      SOFT_ERROR(PI_BAD_POINTER, "input buffer can't be NULL");
+
+   if (!outBuf && len)
+      SOFT_ERROR(PI_BAD_POINTER, "output buffer can't be NULL");
+
+   w = &wfRx[CS];
+
+   status = 0;
+
+   bbSPIStart(w);
+   for (pos=0; pos < len; pos++)
+   {
+      DBG(DBG_INTERNAL, "pos=%d len=%d sent=%d",
+         pos, len, inBuf[pos]);
+      outBuf[pos] = bbSPIXferByte(w, inBuf[pos]);
+     DBG(DBG_INTERNAL, "recvd=%d", outBuf[pos]);
+   }
+   bbSPIStop(w);
+
+   status = len;
+
+   return status;
+}
 /*-------------------------------------------------------------------------*/
 
 int bbI2CZip(
index e9c3a5f4986025c23e27a794ec391b800ec9e7f3..bdc3a9ac39104f0e9842177dc10746871b44216c 100644 (file)
--- a/pigpio.h
+++ b/pigpio.h
@@ -285,6 +285,10 @@ spiRead                    Reads bytes from a SPI device
 spiWrite                   Writes bytes to a SPI device
 spiXfer                    Transfers bytes with a SPI device
 
+bbSPIOpen                  Opens GPIO for bit banging SPI
+bbSPIClose                 Closes GPIO for bit banging SPI
+bbSPIXfer                  Performs multiple bit banged SPI transactions
+
 SERIAL
 
 serOpen                    Opens a serial device
@@ -592,6 +596,9 @@ typedef void *(gpioThreadFunc_t) (void *);
 #define PI_BB_I2C_MIN_BAUD     50
 #define PI_BB_I2C_MAX_BAUD 500000
 
+#define PI_BB_SPI_MIN_BAUD     50
+#define PI_BB_SPI_MAX_BAUD 500000
+
 #define PI_BB_SER_MIN_BAUD     50
 #define PI_BB_SER_MAX_BAUD 250000
 
@@ -2619,6 +2626,86 @@ SDA: 0-31, the SDA GPIO used in a prior call to [*bbI2COpen*]
 Returns 0 if OK, otherwise PI_BAD_USER_GPIO, or PI_NOT_I2C_GPIO.
 D*/
 
+/*F*/
+int bbSPIOpen(unsigned CS, unsigned MISO, unsigned MOSI, unsigned SCLK, unsigned baud, unsigned spiFlags);
+/*D
+This function selects a set of GPIO for bit banging SPI at a
+specified baud rate.
+
+Bit banging SPI allows the use of different GPIO for SPI than
+for the hardware SPI ports.
+
+. .
+ CS:   0-31
+ MISO: 0-31
+ MOSI: 0-31
+ SCLK: 0-31
+baud: 50-500000
+spiFlags: see below
+. .
+spiFlags consists of the least significant 22 bits.
+
+. .
+21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
+ b  b  b  b  b  b  R  T  n  n  n  n  W  A u2 u1 u0 p2 p1 p0  m  m
+. .
+
+mm defines the SPI mode.
+
+. .
+Mode POL PHA
+ 0    0   0
+ 1    0   1
+ 2    1   0
+ 3    1   1
+. .
+
+p0 is 0 if CEx is active low (default) and 1 for active high.
+
+The other bits in flags should be set to zero.
+
+Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_SPI_BAUD, or
+PI_GPIO_IN_USE.
+D*/
+
+/*F*/
+int bbSPIClose(unsigned CS);
+/*D
+This function stops bit banging SPI on a set of GPIO previously
+opened with [*bbSPIOpen*].
+
+. .
+CS: 0-31, the CS GPIO used in a prior call to [*bbSPIOpen*]
+. .
+
+Returns 0 if OK, otherwise PI_BAD_USER_GPIO, or PI_NOT_SPI_GPIO.
+D*/
+
+/*F*/
+int bbSPIXfer(
+   unsigned CS,
+   char    *inBuf,
+   char    *outBuf,
+   unsigned len);
+/*D
+This function executes an bit banged SPI transfer. The data
+to be sent is specified by the contents of inBuf, received data
+is stored into outBuf.
+len specifies the amount of bytes to be transferred.
+
+. .
+   CS: 0-31 (as used in a prior call to [*bbSPIOpen*])
+ inBuf: pointer to buffer to hold data to be sent
+outBuf: pointer to buffer to hold returned data
+outLen: size of output buffer
+. .
+
+Returns >= 0 if OK (the number of bytes read), otherwise
+PI_BAD_USER_GPIO, PI_NOT_SPI_GPIO or PI_BAD_POINTER.
+
+The returned I2C data is stored in consecutive locations of outBuf.
+D*/
+
 /*F*/
 int bbI2CZip(
    unsigned SDA,
@@ -5450,6 +5537,10 @@ PARAMS*/
 
 #define PI_CMD_SHELL 110
 
+#define PI_CMD_BSPIC 111
+#define PI_CMD_BSPIO 112
+#define PI_CMD_BSPIX 113
+
 /*DEF_E*/
 
 /*
@@ -5654,6 +5745,8 @@ after this command is issued.
 #define PI_FILE_IS_A_DIR   -138 // file is a directory
 #define PI_BAD_SHELL_STATUS -139 // bad shell return status
 #define PI_BAD_SCRIPT_NAME -140 // bad script name
+#define PI_BAD_SPI_BAUD    -141 // bad SPI baud rate, not 50-500k
+#define PI_NOT_SPI_GPIO    -142 // no bit bang SPI in progress on GPIO
 
 #define PI_PIGIF_ERR_0    -2000
 #define PI_PIGIF_ERR_99   -2099
index 0fdddbab42786506729312c6caa867485afc1cbd..8175ba4807b7ddd92c086046afea7cb43276660c 100644 (file)
--- a/pigpio.py
+++ b/pigpio.py
@@ -238,6 +238,10 @@ spi_read                  Reads bytes from a SPI device
 spi_write                 Writes bytes to a SPI device
 spi_xfer                  Transfers bytes with a SPI device
 
+bb_spi_open               Opens GPIO for bit banging SPI
+bb_spi_close              Closes GPIO for bit banging SPI
+bb_spi_xfer               Transfers bytes with bit banging SPI
+
 Serial
 
 serial_open               Opens a serial device
@@ -497,6 +501,10 @@ _PI_CMD_FS   =108
 _PI_CMD_FL   =109
 _PI_CMD_SHELL=110
 
+_PI_CMD_BSPIC=111
+_PI_CMD_BSPIO=112
+_PI_CMD_BSPIX=113
+
 # pigpio error numbers
 
 _PI_INIT_FAILED     =-1
@@ -640,6 +648,8 @@ PI_NO_FILE_ACCESS   =-137
 PI_FILE_IS_A_DIR    =-138
 PI_BAD_SHELL_STATUS =-139
 PI_BAD_SCRIPT_NAME  =-140
+PI_BAD_SPI_BAUD     =-141
+PI_NOT_SPI_GPIO     =-142
 
 # pigpio error text
 
@@ -782,7 +792,8 @@ _errors=[
    [PI_FILE_IS_A_DIR     , "file is a directory"],
    [PI_BAD_SHELL_STATUS  , "bad shell return status"],
    [PI_BAD_SCRIPT_NAME   , "bad script name"],
-
+   [PI_BAD_SPI_BAUD      , "bad SPI baud rate, not 50-500k"],
+   [PI_NOT_SPI_GPIO      , "no bit bang SPI in progress on GPIO"],
 ]
 
 class _socklock:
@@ -2946,6 +2957,62 @@ class pi():
       return bytes, data
 
 
+   def bb_spi_open(self, CS, MISO, MOSI, SCLK, baud=100000, spi_flags=1):
+      """
+###
+      """
+      # I p1 CS
+      # I p2 0
+      # I p3 20
+      ## extension ##
+      # I MISO
+      # I MOSI
+      # I SCLK
+      # I baud
+      # I spi_flags
+      
+      extents = [struct.pack("IIIII", MISO, MOSI, SCLK, baud, spi_flags)]
+      return _u2i(_pigpio_command_ext(
+         self.sl, _PI_CMD_BSPIO, CS, 0, 20, extents))
+
+
+   def bb_spi_close(self, CS):
+      """
+      This function stops bit banging SPI on a set of GPIO
+      previously opened with [*bb_spi_open*].
+
+      CS:= 0-31, the CS GPIO used in a prior call to [*bb_ispi_open*]
+
+      Returns 0 if OK, otherwise PI_BAD_USER_GPIO, or PI_NOT_SPI_GPIO.
+
+      ...
+      pi.bb_spi_close(CS)
+      ...
+      """
+      return _u2i(_pigpio_command(self.sl, _PI_CMD_BSPIC, CS, 0))
+
+
+   def bb_spi_xfer(self, CS, data):
+      """
+###
+      """
+      # I p1 SDA
+      # I p2 0
+      # I p3 len
+      ## extension ##
+      # s len data bytes
+
+      # Don't raise exception.  Must release lock.
+      bytes = u2i(_pigpio_command_ext(
+         self.sl, _PI_CMD_BSPIX, CS, 0, len(data), [data], False))
+      if bytes > 0:
+         data = self._rxbuf(bytes)
+      else:
+         data = ""
+      self.sl.l.release()
+      return bytes, data
+
+
    def bb_i2c_open(self, SDA, SCL, baud=100000):
       """
       This function selects a pair of GPIO for bit banging I2C at a