Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds padded waves to the API #329

Closed
wants to merge 11 commits into from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*.o
*.so
*.so.*
*.pyc
pig2vcd
pigpiod
Expand Down
5 changes: 3 additions & 2 deletions command.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
*/
Expand Down
171 changes: 168 additions & 3 deletions pigpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -2434,6 +2434,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;
Expand Down Expand Up @@ -2992,7 +3013,8 @@ static void waveCBsOOLs(int *numCBs, int *numBOOLs, int *numTOOLs)

/* ----------------------------------------------------------------------- */

static int wave2Cbs(unsigned wave_mode, int *CB, int *BOOL, int *TOOL)
static int wave2Cbs(unsigned wave_mode, int *CB, int *BOOL, int *TOOL,
int numCB, int numBOOL, int numTOOL)
{
int botCB=*CB, botOOL=*BOOL, topOOL=*TOOL;

Expand Down Expand Up @@ -3130,6 +3152,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)
Expand Down Expand Up @@ -9566,7 +9610,7 @@ int gpioWaveCreate(void)

/* What resources are needed? */

waveCBsOOLs(&numCB, &numBOOL, &numTOOL);
waveCBsOOLs(&numCB, &numBOOL, &numTOOL);

wid = -1;

Expand Down Expand Up @@ -9619,7 +9663,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. */

Expand All @@ -9633,6 +9677,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. */
Expand All @@ -9646,6 +9693,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)", pctCB);
if (pctTOOL < 0 || pctTOOL > 100)
SOFT_ERROR(PI_BAD_PARAM, "bad wave param, pctTOOL=(%d)", pctCB);

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 -1) * pctCB / 100;
BOOL = (NUM_WAVE_OOL - PI_WAVE_COUNT_PAGES*OOL_PER_OPAGE -1) * pctBOOL /100;
TOOL = (NUM_WAVE_OOL * 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<waveOutCount; i++)
{
if (waveInfo[i].deleted &&
(waveInfo[i].numCB == numCB) &&
(waveInfo[i].numBOOL == numBOOL) &&
(waveInfo[i].numTOOL == numTOOL))
{
/* Reuse the deleted waves resources. */
wid = i;
break;
}
}

if (wid == -1)
{
/* Are there enough spare resources? */

if ((numCB+waveOutBotCB) >= 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 Stats: 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)
Expand Down
34 changes: 34 additions & 0 deletions pigpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -6271,6 +6304,7 @@ PARAMS*/
#define PI_CMD_EVT 116

#define PI_CMD_PROCU 117
#define PI_CMD_WVCAP 118

/*DEF_E*/

Expand Down
37 changes: 37 additions & 0 deletions pigpio.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@
_PI_CMD_EVT =116

_PI_CMD_PROCU=117
_PI_CMD_WVCAP=118

# pigpio error numbers

Expand Down Expand Up @@ -2304,6 +2305,42 @@ def wave_create(self):
"""
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.
Expand Down
3 changes: 3 additions & 0 deletions pigpiod_if2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);}

Expand Down
37 changes: 37 additions & 0 deletions pigpiod_if2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down