diff --git a/.gitignore b/.gitignore index 98748da..140d796 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.o *.so +*.so.* *.pyc pig2vcd pigpiod diff --git a/command.c b/command.c index 4a1da4d..ffc3463 100644 --- a/command.c +++ b/command.c @@ -201,7 +201,8 @@ cmdInfo_t cmdInfo[]= {PI_CMD_WVBSY, "WVBSY", 101, 2, 1}, // gpioWaveTxBusy {PI_CMD_WVCHA, "WVCHA", 197, 0, 0}, // gpioWaveChain {PI_CMD_WVCLR, "WVCLR", 101, 0, 1}, // gpioWaveClear - {PI_CMD_WVCRE, "WVCRE", 101, 2, 1}, // gpioWaveCreate + {PI_CMD_WVCRE, "WVCRE", 101, 2, 1}, // gpioWaveCreate + {PI_CMD_WVCAP, "WVCAP", 112, 2, 1}, // gpioWaveCreatePad {PI_CMD_WVDEL, "WVDEL", 112, 0, 1}, // gpioWaveDelete {PI_CMD_WVGO, "WVGO" , 101, 2, 0}, // gpioWaveTxStart {PI_CMD_WVGOR, "WVGOR", 101, 2, 0}, // gpioWaveTxStart @@ -693,7 +694,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 BSPIC + WVCAP WVDEL WVSC WVSM WVSP WVTX WVTXR BSPIC One positive parameter. */ diff --git a/pigpio.c b/pigpio.c index 7cd7cbf..fe2dcde 100644 --- a/pigpio.c +++ b/pigpio.c @@ -396,6 +396,7 @@ bit 0 READ_LAST_NOT_SET_ERROR #define DMA_DEST_WIDTH (1<< 5) #define DMA_DEST_INC (1<< 4) #define DMA_WAIT_RESP (1<< 3) +#define DMA_TDMODE (1<< 1) #define DMA_DEBUG_READ_ERR (1<<2) #define DMA_DEBUG_FIFO_ERR (1<<1) @@ -664,6 +665,7 @@ bit 0 READ_LAST_NOT_SET_ERROR /* --------------------------------------------------------------- */ #define NORMAL_DMA (DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP) +#define TWO_BEAT_DMA (DMA_TDMODE | DMA_BURST_LENGTH(1)) #define TIMED_DMA(x) (DMA_DEST_DREQ | DMA_PERIPHERAL_MAPPING(x)) @@ -2434,6 +2436,27 @@ static int myDoCommand(uintptr_t *p, unsigned bufSize, char *buf) case PI_CMD_WVCRE: res = gpioWaveCreate(); break; + case PI_CMD_WVCAP: + /* Make WVCAP variadic */ + if (p[3] == 4) + { + memcpy(&tmp3, buf, 4); /* percent TOOL */ + res = gpioWaveCreatePad(p[1], p[2], tmp3); /* rawWaveAdd* usage */ + break; + } + if (p[2] && p[3]==0) + { + res = gpioWaveCreatePad(p[1], p[2], 0); + break; + } + if (p[2]==0 && p[3]==0) + { + res = gpioWaveCreatePad(p[1], p[1], 0); /* typical usage */ + break; + } + res = PI_BAD_WAVE_ID; // FIX? + break; + case PI_CMD_WVDEL: res = gpioWaveDelete(p[1]); break; case PI_CMD_WVGO: res = gpioWaveTxStart(PI_WAVE_MODE_ONE_SHOT); break; @@ -2977,8 +3000,7 @@ static void waveCBsOOLs(int *numCBs, int *numBOOLs, int *numTOOLs) for (i=0; iinfo = TWO_BEAT_DMA; + p->src = waveOOLPOadr(botOOL); + waveSetOOL(botOOL++, waves[i].gpioOn); + s_stride = waveOOLPOadr(botOOL) - p->src; + waveSetOOL(botOOL++, waves[i].gpioOff); + p->dst = ((GPIO_BASE + (GPSET0*4)) & 0x00ffffff) | PI_PERI_BUS; + p->length = (2<<16) + 4; // 2 transfers of 4 bytes each + p->stride = (12<<16) + s_stride; // d_stride = (GPCLR0-GPSET0)*4 = 12 + p->next = waveCbPOadr(botCB); + } + if (waves[i].gpioOn && !waves[i].gpioOff) { waveSetOOL(botOOL, waves[i].gpioOn); @@ -3052,8 +3090,7 @@ static int wave2Cbs(unsigned wave_mode, int *CB, int *BOOL, int *TOOL) p->length = 4; p->next = waveCbPOadr(botCB); } - - if (waves[i].gpioOff) + if (waves[i].gpioOff && !waves[i].gpioOn) { waveSetOOL(botOOL, waves[i].gpioOff); @@ -3065,7 +3102,6 @@ static int wave2Cbs(unsigned wave_mode, int *CB, int *BOOL, int *TOOL) p->length = 4; p->next = waveCbPOadr(botCB); } - if (waves[i].flags & WAVE_FLAG_READ) { p = rawWaveCBAdr(botCB++); @@ -3130,6 +3166,28 @@ static int wave2Cbs(unsigned wave_mode, int *CB, int *BOOL, int *TOOL) } } + if (numCB) + { + /* Pad the wave */ + + botCB = *CB + numCB - 1; + botOOL = *BOOL + numBOOL - 1; + topOOL = *TOOL - numTOOL; + + /* Link the last CB to end of wave */ + + p->next = waveCbPOadr(botCB); + + /* Insert sentinel CB at end of DMA */ + + p = rawWaveCBAdr(botCB++); + p->info = NORMAL_DMA | DMA_DEST_IGNORE; + p->src = waveOOLPOadr(botOOL++); + p->dst = ((GPIO_BASE + (GPSET0*4)) & 0x00ffffff) | PI_PERI_BUS; + p->length = 4; + p->next = 0; + } + if (p != NULL) { if (wave_mode == PI_WAVE_MODE_ONE_SHOT) @@ -3324,9 +3382,7 @@ int rawWaveAddGeneric(unsigned numIn1, rawWave_t *in1) cbs += waveDelayCBs(tDelay); - if (out[outPos].gpioOn) cbs++; /* one cb if gpio on */ - - if (out[outPos].gpioOff) cbs++; /* one cb if gpio off */ + if (out[outPos].gpioOn || out[outPos].gpioOff) cbs++; if (out[outPos].flags & WAVE_FLAG_READ) { @@ -9566,7 +9622,7 @@ int gpioWaveCreate(void) /* What resources are needed? */ - waveCBsOOLs(&numCB, &numBOOL, &numTOOL); + waveCBsOOLs(&numCB, &numBOOL, &numTOOL); wid = -1; @@ -9589,10 +9645,10 @@ int gpioWaveCreate(void) { /* Are there enough spare resources? */ - if ((numCB+waveOutBotCB) >= NUM_WAVE_CBS) + if ((numCB+waveOutBotCB) > NUM_WAVE_CBS) return PI_TOO_MANY_CBS; - if ((numBOOL+waveOutBotOOL) >= (waveOutTopOOL-numTOOL)) + if ((numBOOL+waveOutBotOOL) > (waveOutTopOOL-numTOOL)) return PI_TOO_MANY_OOL; if (wid >= PI_MAX_WAVES) @@ -9619,7 +9675,7 @@ int gpioWaveCreate(void) BOOL = waveInfo[wid].botOOL; TOOL = waveInfo[wid].topOOL; - wave2Cbs(PI_WAVE_MODE_ONE_SHOT, &CB, &BOOL, &TOOL); + wave2Cbs(PI_WAVE_MODE_ONE_SHOT, &CB, &BOOL, &TOOL, 0, 0, 0); /* Sanity check. */ @@ -9633,6 +9689,9 @@ int gpioWaveCreate(void) numTOOL, waveInfo[wid].topOOL-TOOL); } + DBG(DBG_USER, "Wave Stats: wid=%d CBs %d BOOL %d TOOL %d", wid, + numCB, numBOOL, numTOOL); + waveInfo[wid].deleted = 0; /* Consume waves. */ @@ -9646,6 +9705,124 @@ int gpioWaveCreate(void) return wid; } +int gpioWaveCreatePad(int pctCB, int pctBOOL, int pctTOOL) +{ + int i, wid; + int numCB, numBOOL, numTOOL; + int CB, BOOL, TOOL; + + DBG(DBG_USER, "%d, %d, %d", pctCB, pctBOOL, pctTOOL); + + CHECK_INITED; + + if (pctCB < 0 || pctCB > 100) + SOFT_ERROR(PI_BAD_PARAM, "bad wave param, pctCB=(%d)", pctCB); + if (pctBOOL < 0 || pctBOOL > 100) + SOFT_ERROR(PI_BAD_PARAM, "bad wave param, pctBOOL=(%d)", pctBOOL); + if (pctTOOL < 0 || pctTOOL > 100) + SOFT_ERROR(PI_BAD_PARAM, "bad wave param, pctTOOL=(%d)", pctTOOL); + + if (wfc[wfcur] == 0) return PI_EMPTY_WAVEFORM; + + /* What resources are needed? */ + waveCBsOOLs(&numCB, &numBOOL, &numTOOL); + + /* Amount of pad required */ + CB = (NUM_WAVE_CBS - PI_WAVE_COUNT_PAGES*CBS_PER_OPAGE) * pctCB / 100; + BOOL = (NUM_WAVE_OOL - PI_WAVE_COUNT_PAGES*OOL_PER_OPAGE) * pctBOOL /100; + TOOL = (NUM_WAVE_OOL - PI_WAVE_COUNT_PAGES*OOL_PER_OPAGE) * pctTOOL /100; + + /* Reject if wave is too big */ + if (numCB > CB) return PI_TOO_MANY_CBS; + if (numBOOL > BOOL) return PI_TOO_MANY_OOL; + if (numTOOL > TOOL) return PI_TOO_MANY_OOL; + + /* Set the padding */ + numCB = CB; + numBOOL = BOOL; + numTOOL = TOOL; + + + wid = -1; + + /* Is there an exact fit with a deleted wave. */ + + for (i=0; i NUM_WAVE_CBS) + return PI_TOO_MANY_CBS; + + if ((numBOOL+waveOutBotOOL) > (waveOutTopOOL-numTOOL)) + return PI_TOO_MANY_OOL; + + if (wid >= PI_MAX_WAVES) + return PI_NO_WAVEFORM_ID; + + wid = waveOutCount++; + + waveInfo[wid].botCB = waveOutBotCB; + waveInfo[wid].topCB = waveOutBotCB + numCB -1; + waveInfo[wid].botOOL = waveOutBotOOL; + waveInfo[wid].topOOL = waveOutTopOOL; + waveInfo[wid].numCB = numCB; + waveInfo[wid].numBOOL = numBOOL; + waveInfo[wid].numTOOL = numTOOL; + + waveOutBotCB += numCB; + waveOutBotOOL += numBOOL; + waveOutTopOOL -= numTOOL; + } + + /* Must be room if got this far. */ + + CB = waveInfo[wid].botCB; + BOOL = waveInfo[wid].botOOL; + TOOL = waveInfo[wid].topOOL; + + wave2Cbs(PI_WAVE_MODE_ONE_SHOT, &CB, &BOOL, &TOOL, numCB, numBOOL, numTOOL); + + /* Sanity check. */ + + if ( (numCB != (CB-waveInfo[wid].botCB)) || + (numBOOL != (BOOL-waveInfo[wid].botOOL)) || + (numTOOL != (waveInfo[wid].topOOL-TOOL)) ) + { + DBG(DBG_ALWAYS, "ERROR wid=%d CBs %d=%d BOOL %d=%d TOOL %d=%d", wid, + numCB, CB-waveInfo[wid].botCB, + numBOOL, BOOL-waveInfo[wid].botOOL, + numTOOL, waveInfo[wid].topOOL-TOOL); + } + + DBG(DBG_USER, "Wave padding: wid=%d CBs %d BOOL %d TOOL %d", wid, + numCB, numBOOL, numTOOL); + + waveInfo[wid].deleted = 0; + + /* Consume waves. */ + + wfc[0] = 0; + wfc[1] = 0; + wfc[2] = 0; + + wfcur = 0; + + return wid; +} /* ----------------------------------------------------------------------- */ int gpioWaveDelete(unsigned wave_id) diff --git a/pigpio.h b/pigpio.h index 7701b45..b5ac1b3 100644 --- a/pigpio.h +++ b/pigpio.h @@ -1986,6 +1986,39 @@ Returns the new waveform id if OK, otherwise PI_EMPTY_WAVEFORM, PI_NO_WAVEFORM_ID, PI_TOO_MANY_CBS, or PI_TOO_MANY_OOL. D*/ +int gpioWaveCreatePad(int pctCB, int pctBOOL, int pctTOOL); +/*D +Similar to gpioWaveCreate(), this function creates a waveform but pads the consumed +resources. Padded waves of equal dimension can be re-cycled efficiently allowing +newly created waves to re-use the resources of deleted waves of the same dimension. + +. . +pctCB: 0-100, the percent of all DMA control blocks to consume. +pctBOOL: 0-100, the percent of On-Off-Level (OOL) buffer to consume for wave output. +pctTOOL: 0-100, the percent of OOL buffer to consume for wave input (flags). +. . + +Upon success a wave id greater than or equal to 0 is returned, otherwise +PI_EMPTY_WAVEFORM, PI_TOO_MANY_CBS, PI_TOO_MANY_OOL, or PI_NO_WAVEFORM_ID. + +Waveform data provided by [*gpioWaveAdd**] and [*rawWaveAdd**] functions are +consumed by this function. + +A usage would be the creation of two waves where one is filled while the other +is being transmitted. Each wave is assigned 50% of the resources. +This buffer structure allows the transmission of infinite wave sequences. + +Step 1. [*gpioWaveClear*] to clear all waveforms and added data. + +Step 2. [*gpioWaveAdd*] calls to supply the waveform data. + +Step 3. gpioWaveCreatePad(50,50,0) to create a 50% padded waveform and get a unique id + +Step 4. [*gpioWaveTxSend*] with the wave id and PI_WAVE_MODE_ONE_SHOT_SYNC. + +Repeat steps 2-4 as needed always waiting for the active waveform to be transmitted +before marking it as deleted. +D*/ /*F*/ int gpioWaveDelete(unsigned wave_id); @@ -6271,6 +6304,7 @@ PARAMS*/ #define PI_CMD_EVT 116 #define PI_CMD_PROCU 117 +#define PI_CMD_WVCAP 118 /*DEF_E*/ diff --git a/pigpio.py b/pigpio.py index cfaf845..a9f10e3 100644 --- a/pigpio.py +++ b/pigpio.py @@ -571,6 +571,7 @@ _PI_CMD_EVM =115 _PI_CMD_EVT =116 _PI_CMD_PROCU=117 +_PI_CMD_WVCAP=118 # pigpio error numbers @@ -2304,6 +2305,42 @@ class pi(): """ return _u2i(_pigpio_command(self.sl, _PI_CMD_WVCRE, 0, 0)) + def wave_create_and_pad(self, percent): + """ + This function creates a waveform like wave_create but pads the consumed + resources. Where percent gives the percentage of the resources to use (in terms + of the theoretical maximum, not the current amount free). This allows the reuse + of deleted waves while a transmission is active. Upon success a wave id + greater than or equal to 0 is returned, otherwise PI_EMPTY_WAVEFORM, + PI_TOO_MANY_CBS, PI_TOO_MANY_OOL, or PI_NO_WAVEFORM_ID. + + . . + pi: >=0 (as returned by [*pigpio_start*]). + . . + + The data provided by the [*wave_add_**] functions is consumed by this + function. + + As many waveforms may be created as there is space available. The + wave id is passed to [*wave_send_**] to specify the waveform to transmit. + + A usage would be the creation of two waves where one is filled while the other + is being transmitted. Each wave is assigned 50% of the available resources. + This buffer structure allows the transmission of infinite wave sequences. + + Step 1. [*wave_clear*] to clear all waveforms and added data. + + Step 2. [*wave_add_**] calls to supply the waveform data. + + Step 3. [*wave_create_and_pad*] to create a 50% padded waveform and get a unique id + + Step 4. [*wave_send_**] with the id of the waveform to transmit. + + Repeat steps 2-4 as needed always waiting for the active waveform to be transmitted + before marking it as deleted. + """ + return _u2i(_pigpio_command(self.sl, _PI_CMD_WVCAP, percent, 0)) + def wave_delete(self, wave_id): """ This function deletes the waveform with id wave_id. diff --git a/pigpiod_if2.c b/pigpiod_if2.c index 90fb5fb..7af8313 100644 --- a/pigpiod_if2.c +++ b/pigpiod_if2.c @@ -953,6 +953,9 @@ int wave_add_serial( int wave_create(int pi) {return pigpio_command(pi, PI_CMD_WVCRE, 0, 0, 1);} +int wave_create_and_pad(int pi, int percent) + {return pigpio_command(pi, PI_CMD_WVCAP, percent, 0, 1);} + int wave_delete(int pi, unsigned wave_id) {return pigpio_command(pi, PI_CMD_WVDEL, wave_id, 0, 1);} diff --git a/pigpiod_if2.h b/pigpiod_if2.h index 8d6f132..5406186 100644 --- a/pigpiod_if2.h +++ b/pigpiod_if2.h @@ -1371,6 +1371,43 @@ Returns the new waveform id if OK, otherwise PI_EMPTY_WAVEFORM, PI_NO_WAVEFORM_ID, PI_TOO_MANY_CBS, or PI_TOO_MANY_OOL. D*/ +int wave_create_and_pad(int pi, int percent); +/*D +This function creates a waveform like wave_create but pads the consumed +resources. Where percent gives the percentage of the resources to use (in terms +of the theoretical maximum, not the current amount free). This allows the reuse +of deleted waves while a transmission is active. Upon success a wave id +greater than or equal to 0 is returned, otherwise PI_EMPTY_WAVEFORM, +PI_TOO_MANY_CBS, PI_TOO_MANY_OOL, or PI_NO_WAVEFORM_ID. + +. . +pi: >=0 (as returned by [*pigpio_start*]). +. . + +The data provided by the [*wave_add_**] functions is consumed by this +function. + +As many waveforms may be created as there is space available. The +wave id is passed to [*wave_send_**] to specify the waveform to transmit. + +A usage would be the creation of two waves where one is filled while the other +is beeing transmitted. Each wave is assigned 50% of the available resources. +This buffer structure allows the transmission of infinite wave sequences. + +Step 1. [*wave_clear*] to clear all waveforms and added data. + +Step 2. [*wave_add_**] calls to supply the waveform data. + +Step 3. [*wave_create_and_pad*] to create a 50% padded waveform and get a unique id + +Step 4. [*wave_send_**] with the id of the waveform to transmit. + +Repeat steps 2-4 as needed always waiting for the active waveform to be transmitted +before marking it as deleted. + +Returns the new waveform id if OK, otherwise PI_EMPTY_WAVEFORM, +PI_NO_WAVEFORM_ID, PI_TOO_MANY_CBS, or PI_TOO_MANY_OOL. +D*/ /*F*/ int wave_delete(int pi, unsigned wave_id); diff --git a/x_pigpio.c b/x_pigpio.c index cd15389..8119d73 100644 --- a/x_pigpio.c +++ b/x_pigpio.c @@ -459,6 +459,51 @@ To the lascivious pleasing of a lute.\n\ c = gpioWaveGetMaxCbs(); CHECK(5, 21, c, 25016, 0, "wave get max cbs"); + + /* waveCreatePad tests */ + gpioWaveTxStop(); + gpioWaveClear(); + gpioSetAlertFunc(GPIO, t5cbf); + + e = gpioWaveAddGeneric(2, (gpioPulse_t[]) + { {1<