diff --git a/command.c b/command.c index 239ec2e..ead8879 100644 --- 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. */ diff --git a/pigpio.c b/pigpio.c index 3f0b2f8..2ce81da 100644 --- 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,73 @@ 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); @@ -9901,6 +10059,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) - 1; + 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 +10162,103 @@ 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; + char txByte, rxByte; + wfRx_t *w; + + DBG(DBG_USER, "CS=%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++) + { + if (PI_SPI_FLAGS_GET_TX_LSB(w->S.spiFlags)) + { + txByte = inBuf[pos]; + } + else + { + txByte = inBuf[len - pos - 1]; + } + rxByte = bbSPIXferByte(w, txByte); + if (PI_SPI_FLAGS_GET_RX_LSB(w->S.spiFlags)) + { + outBuf[pos] = rxByte; + } + else + { + outBuf[len - pos - 1] = rxByte; + } + DBG(DBG_INTERNAL, "pos=%d len=%d sent=%d recvd=%d", pos, len, txByte, rxByte); + } + bbSPIStop(w); + + status = len; + + return status; +} /*-------------------------------------------------------------------------*/ int bbI2CZip( diff --git a/pigpio.h b/pigpio.h index e9c3a5f..07d81d9 100644 --- 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 250000 + #define PI_BB_SER_MIN_BAUD 50 #define PI_BB_SER_MAX_BAUD 250000 @@ -2619,6 +2626,92 @@ 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-250000 +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. + +T is 1 if the least significant bit is transmitted on MOSI first, the +default (0) shifts the most significant bit out first. + +R is 1 if the least significant bit is received on MISO first, the +default (0) receives the most significant bit first. + +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 +len: size of data transfer +. . + +Returns >= 0 if OK (the number of bytes read), otherwise +PI_BAD_USER_GPIO, PI_NOT_SPI_GPIO or PI_BAD_POINTER. + +The returned SPI data is stored in consecutive locations of outBuf. +D*/ + /*F*/ int bbI2CZip( unsigned SDA, @@ -5450,6 +5543,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 +5751,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 diff --git a/pigpio.py b/pigpio.py index 0fdddba..73b6c6e 100644 --- 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 @@ -359,6 +363,19 @@ FROM_START=0 FROM_CURRENT=1 FROM_END=2 +SPI_CS_HIGH_ACTIVE = 1 << 2 +SPI_CS0_HIGH_ACTIVE = 1 << 2 +SPI_CS1_HIGH_ACTIVE = 1 << 3 +SPI_CS2_HIGH_ACTIVE = 1 << 4 +SPI_RX_LSBFIRST = 1 << 15 +SPI_TX_LSBFIRST = 1 << 14 +SPI_MODE_0 = 0 +SPI_MODE_1 = 1 +SPI_MODE_2 = 2 +SPI_MODE_3 = 3 +SPI_CPOL = 2 +SPI_CPHA = 1 + # pigpio command numbers _PI_CMD_MODES= 0 @@ -497,6 +514,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 +661,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 +805,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 +2970,133 @@ class pi(): return bytes, data + def bb_spi_open(self, CS, MISO, MOSI, SCLK, baud=100000, spi_flags=0): + """ + 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-250000 + 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, defaults to 0 + + ... + Mode CPOL CPHA + 0 0 0 + 1 0 1 + 2 1 0 + 3 1 1 + ... + + Use the following constants to set the Mode: + pigpio.SPI_MODE_0, + pigpio.SPI_MODE_1, + pigpio.SPI_MODE_2 or + pigpio.SPI_MODE_3 + or use + pigpio.SPI_CPOL and/ or + pigpio.SPI_CPHA + + p0 is 0 if CS is active low (default) and 1 for active high. + Use pigpio.SPI_CS_HIGH_ACTIVE to set this flag. + + T is 1 if the least significant bit is transmitted on MOSI first, + the default (0) shifts the most significant bit out first. + Use pigpio.SPI_TX_LSBFIRST to set this flag. + + R is 1 if the least significant bit is received on MISO first, + the default (0) receives the most significant bit first. + Use pigpio.SPI_RX_LSBFIRST to set this flag. + + The other bits in spiFlags should be set to zero. + + Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_SPI_BAUD, or + PI_GPIO_IN_USE. + ... + pi.bb_spi_open(CS, MISO, MOSI, SCLK, + baud=100000, + spi_flags=pigpio.SPI_MODE_1 | pigpio.SPI_CS_HIGH_ACTIVE) + ... + """ + # 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): + """ + This function executes an bit banged SPI transfer. The data + to be sent is specified by the contents of data, received data + is returned as a bytestring. + + CS:= 0-31 (as used in a prior call to [*bbSPIOpen*]) + data:= data to be sent + + Returns >= 0 if OK (the number of bytes read), otherwise + PI_BAD_USER_GPIO, PI_NOT_SPI_GPIO or PI_BAD_POINTER. + + The received SPI data returned as ab bytearray + ... + pi.bb_spi_xfer(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