Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

PIGPIO performance

Wed Jul 27, 2016 11:35 am

Hi,

Please bear with me, as my issue is a little complex to explain.... But, in essence, I am looking for guidance on the best, most efficient, most real-time way of utilising the PIGPIO library.

I am currently writing a software sequencer on the Raspberry Pi to control banks of Analogue Modular synthesisers. My code is written in C, and I plan to make extensive use of the PIGPIO library.

In principle it is a fairly straightforward step sequencer - it advances one step at a time under the control of a main Timer until it reaches the end of the sequence, at which point it starts again at step 1. At each step it outputs Voltages / Gates etc to a series of output channels, each connected to some sort of voltage-controlled oscillator / gate / envelope / filter etc. Sonic mayhem ensues....!

The PIGPIO library is absolutely perfect for my requirements, as it makes accessing SPI and I2C peripherals (of which I have many) very straightforward, simple call-back routines can be used to record the state of various switches and buttons into global variables, and it also provides nice Timer functions that I can use as my main sequencer timing loop.

My issue is that I am not getting anywhere near the sort of performance I thought I would, and wonder whether I am using the library in fundamentally the wrong way?

My code is currently a single thread, which sets up a number of callbacks and timers, then sits in a while(1=1) loop letting the Timer do its stuff, controlled by global variables, the values of which are set within the callbacks triggered by switches, buttons and external signals.

My code is written in C, and I am linking the PIGPIO library directly (not running it as a service and using pipes).

I hope you're with me so far?

I am using a little touchscreen as a display, which is connected to the SPI bus. I am displaying graphics by writing directly to its framebuffer at its native 320x240 pixel resolution - I'm not using XWindows - graphic performance should be pretty snappy, as I'm only scribbling a few pixels at a time and rarely, if ever, repainting large areas of the display.

The main timer is set to call back every x milliseconds, and this sets the timing loop for the sequence. I am using Timer 0, and this is the only timer running - all other control is effected by polling the values of the global variables, and their values are set within the GPIO callbacks.

I can happily increase the speed (decrease the period) of this timer via gpioSetTimerFunc() but, once the timer starts triggering at more than, around 4 Hz (250 Milliseconds) I start getting all manner of glitches and drop-outs. I was expecting to be able to get way better performance than this, all the way up to 100Hz (representing the minimum settable period of 10ms)

I am wondering whether I am perhaps running into threading issues?

If so, what would be the best way to segment this code so that it can take advantage of multiple threads?

Should I launch each callback in its own thread? Should the main timer run in its own thread? When I call something like i2cWriteBlock(), should these be launched as separate threads?

My code is fairly lightweight - not a lot is happening during each callback. The main timer and a few of the callbacks are listed below as an example:

Code: Select all

/* Internal Clock
 * 
 * This is the main timing loop for the 
 * whole program, and which runs continuously
 * from program start to end. This is converted
 * by this function to an internal logic level
 * that changes state at half the frequency of 
 * the master clock callback. This is held in a 
 * global variable called 'clock_level'
 */
void master_clock(void)
{
	/* Waggle the clock up and down */
	clock_level = clock_level ^ 1;
	/* If Clock Source set to Internal, write this value to Clock Out*/
	if (run_stop == RUN && clock_source == INT_CLK) {
		gpioWrite(CLOCK_OUT,clock_level);
		/* Step forward on the rising edge of the clock */
		if (clock_level == 1) next_step();
	}
}

/* External clock
 *
 * If the clock source is External, then it is this function
 * that advances the sequence to the next step
 */
void external_clock(int gpio, int level, uint32_t tick)
{
if (run_stop == RUN && clock_source == EXT_CLK) {
	gpioWrite(CLOCK_OUT,level);
	/* only advance the sequence on rising edge */
	if (level == 1) next_step();
	}
}
/* run/stop switch input
 * called whenever the switch changes state
 */
void runstop_input(int gpio, int level, uint32_t tick)
{
	if (level == 1) run_stop = RUN; else run_stop == STOP;
}
/* Reset input
 * Rising edge causes next step to be Step 1
 */
void reset_input(int gpio, int level, uint32_t tick)
{
	if (level == 1)	step_one = TRUE;
}
/* Function called to advance the sequence on to the next step */
void next_step(void)
{
	int previous_step, channel;
	previous_step = current_step;
	if (++current_step >= last_step || step_one == TRUE){
		current_step = 0;
		step_one = FALSE;
		/* 100us Trigger Pulse on to Step 1 output */
		gpioTrigger(STEP1_OUT,100,1);
		}

	/*
	* The code at this point (omitted for clarity) runs through each channel of the sequencer, 
	* updating the output voltages, setting various GPIO pins to their appropriate state for this
	* step of the sequence, scribbling on the framebuffer in order to update the TFT display.
	*
	* It is doing quite a few things, but none are more than a few lines of code, and there are
	* no dependencies or opportunities for 'fatal embraces'
	*/

	}
}
So, if you have read this far, then I thank you!

My issue is not how to get the PIGPIO library working - it is working fine, I can talk to my SPI and I2C peripherals, and the timer and callbacks function as expected.

My issue is how to implement this sort of Interrupt-driven timer-driven code in the most efficient way, which makes maximum use of the available cores / threads, whilst leveraging the simplicity of the PIGPIO library.

I'm developing on a Raspberry Pi 3 model B, but ultimately I'd like to build something that'll work with the Pi Zero (due to its size), though I'm not really interested in spawning a discussion of the merits of the 3 vs the Zero - I know the differences!

Richard (Morphology)

User avatar
joan
Posts: 14175
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: PIGPIO performance

Wed Jul 27, 2016 12:42 pm

Have you got some code for a complete program which I can run and which demonstrates the problem?

Based on a snippet I'd guess you might be blocking the whole system by running a long running process in a callback.

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 7124
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: PIGPIO performance

Wed Jul 27, 2016 12:57 pm

And your comment of a "while(1=1) loop" sounds like a very easy way of hogging an entire CPU core.
You should be waiting on a semaphore or event so that your process sleeps when it has nothing to do.
Software Engineer at Raspberry Pi Trading. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

Re: PIGPIO performance

Wed Jul 27, 2016 1:45 pm

joan wrote:Have you got some code for a complete program which I can run and which demonstrates the problem?

Based on a snippet I'd guess you might be blocking the whole system by running a long running process in a callback.
I will try and package something up this evening. What's the best way to send it to you?

The problem is likely to be that the code is very inter-dependent with the hardware - writing to the framebuffer, sending stuff to i2c peripherals - I am building my own hardware, so this isn't off-the-shelf stuff.

The longest-running code in a callback will be writing to the framebuffer to draw on the TFT screen - it has a number of nested for( ; ; ) loops in order to draw lines, fill rectangles etc though, at the end of the day, it is simply poking values into a memory array not calling any functions outside of the code.

Richard

User avatar
joan
Posts: 14175
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: PIGPIO performance

Wed Jul 27, 2016 1:54 pm

Just replace all the blocks with a delay roughly approximating what you would expect. No need to think much about that. I just need the overall structure.

joan @ abyz . co. uk will get to me.

Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

Re: PIGPIO performance

Wed Jul 27, 2016 2:12 pm

OK, I will do that this evening and change some of the I2C calls to simple GPIO writes so that it'll run on a vanilla Pi.

I may not get all the changes done this evening, so it may be tomorrow evening before I get anything to you.

Great to be speaking to the horses (pigs?) mouth by the way - PIGPIO is a fantastically useful library that makes interfacing with the outside world very easy indeed. Great work.

Richard

Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

Re: PIGPIO performance

Wed Jul 27, 2016 2:20 pm

6by9 wrote:And your comment of a "while(1=1) loop" sounds like a very easy way of hogging an entire CPU core.
You should be waiting on a semaphore or event so that your process sleeps when it has nothing to do.
Ah, when I said it was a while(1=1) loop, it is a little more complex than that.... though not a lot.

This is my main()

Code: Select all

int main(int argc, char* argv[])
{
	/* things to do when prog first starts */
	startup();
	/* draw the main front screen */
	paint_main();

while (prog_running == 1){
	/* If clock_dirty has been set, then the time period 
	 * needs to be reset
	 */
	 if (clock_dirty == 1) {
		 /* clear and re-create the timer */
		gpioSetTimerFunc(0, clock_period, NULL);
		gpioSetTimerFunc(0, clock_period, master_clock);
		clock_dirty = 0;
	 }
	/* check for touchscreen input */
	if (touched == 1){
	int x;
	touched = 0;
	getTouchSample(&rawX, &rawY, &rawPressure);
	Xsamples[sample] = rawX;
	Ysamples[sample] = rawY;
	if (sample++ >= SAMPLE_AMOUNT){
		sample = 0;
		Xaverage  = 0;
		Yaverage  = 0;

		for ( x = 0; x < SAMPLE_AMOUNT; x++ ){
			Xaverage += Xsamples[x];
			Yaverage += Ysamples[x];
		}

	Xaverage = Xaverage/SAMPLE_AMOUNT;
	Yaverage = Yaverage/SAMPLE_AMOUNT;
	/* scale these values by the known min & max raw values (obtained by
	 * experimentation, and noted in europi.h
	*/ 
	if (Xaverage <= rawXmin) Xaverage = rawXmin;
	if (Xaverage >= rawXmax) Xaverage = rawXmax;
	if (Yaverage <= rawYmin) Yaverage = rawYmin;
	if (Yaverage >= rawYmax) Yaverage = rawYmax;
	/* Er., X is upside down */
	scaledX = ((float)(Xaverage - rawXmin) / (float)(rawXmax-rawXmin))*X_MAX;
	scaledY = Y_MAX-((float)(Yaverage - rawYmin) / (float)(rawYmax-rawYmin))*Y_MAX;
	/* see whether one of the buttons has been touched. If so, select it */
	button_touched(scaledX,scaledY);

	}
	}
}
	shutdown();
	return 0;
  
}
So, it's actually checking 3 global variables, whose values are normally 0 - these tell the main loop whether to quit, whether to halt and re-create the Timer (if the user increases or decreases the tempo), and whether the Touchscreen has been touched (I am taking the interrupt line from the TFT screen and using i to set the 'touched' flag),

So, if the user hasn't asked the program to quit, hasn't altered the speed, and hasn't touched the screen, the main prog loop reduces to:

Code: Select all

while (prog_running == 1){
	 if (clock_dirty == 1) {
	 }
	if (touched == 1){
	}
}

Richard

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 7124
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: PIGPIO performance

Wed Jul 27, 2016 2:50 pm

Morphology wrote:
6by9 wrote:And your comment of a "while(1=1) loop" sounds like a very easy way of hogging an entire CPU core.
You should be waiting on a semaphore or event so that your process sleeps when it has nothing to do.
Ah, when I said it was a while(1=1) loop, it is a little more complex than that.... though not a lot.
...
So, it's actually checking 3 global variables, whose values are normally 0 - these tell the main loop whether to quit, whether to halt and re-create the Timer (if the user increases or decreases the tempo), and whether the Touchscreen has been touched (I am taking the interrupt line from the TFT screen and using i to set the 'touched' flag),

So, if the user hasn't asked the program to quit, hasn't altered the speed, and hasn't touched the screen, the main prog loop reduces to:

Code: Select all

while (prog_running == 1){
	 if (clock_dirty == 1) {
	 }
	if (touched == 1){
	}
}
Yes, that is typically called busy waiting and will hog one whole CPU core at 100% utilisation. Try running "top" in a different terminal and I suspect that you'll see your process having a large value under %CPU.
You want something like a semaphore or similar at the end of the loop that the main thread will stop at waiting for one of the external events to set their global variable and signal the semaphore to wake up the main thread again. The same can be achieved with a condition variable, or part of the userland VCOS repo includes an implementation of event groups (https://github.com/raspberrypi/userland ... nt_flags.h)
Software Engineer at Raspberry Pi Trading. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

Re: PIGPIO performance

Wed Jul 27, 2016 3:15 pm

6by9 wrote:
Morphology wrote:
6by9 wrote:And your comment of a "while(1=1) loop" sounds like a very easy way of hogging an entire CPU core.
You should be waiting on a semaphore or event so that your process sleeps when it has nothing to do.
Ah, when I said it was a while(1=1) loop, it is a little more complex than that.... though not a lot.
...
So, it's actually checking 3 global variables, whose values are normally 0 - these tell the main loop whether to quit, whether to halt and re-create the Timer (if the user increases or decreases the tempo), and whether the Touchscreen has been touched (I am taking the interrupt line from the TFT screen and using i to set the 'touched' flag),

So, if the user hasn't asked the program to quit, hasn't altered the speed, and hasn't touched the screen, the main prog loop reduces to:

Code: Select all

while (prog_running == 1){
	 if (clock_dirty == 1) {
	 }
	if (touched == 1){
	}
}
Yes, that is typically called busy waiting and will hog one whole CPU core at 100% utilisation. Try running "top" in a different terminal and I suspect that you'll see your process having a large value under %CPU.
You want something like a semaphore or similar at the end of the loop that the main thread will stop at waiting for one of the external events to set their global variable and signal the semaphore to wake up the main thread again. The same can be achieved with a condition variable, or part of the userland VCOS repo includes an implementation of event groups (https://github.com/raspberrypi/userland ... nt_flags.h)
That's very helpful, thank you.

I did try adding a call to sleep(xxx) in that main loop, though it didn't seem to make any difference. I will do as you suggest, but I will also package something up for Joan, as I need to make sure I am using the PIGPIO library in the most efficient manner.

Richard

User avatar
joan
Posts: 14175
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: PIGPIO performance

Wed Jul 27, 2016 3:27 pm

Did you mean sleep()? That has a granularity of a second. Try something like time_sleep(0.01).

Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

Re: PIGPIO performance

Wed Jul 27, 2016 3:44 pm

joan wrote:Did you mean sleep()? That has a granularity of a second. Try something like time_sleep(0.01).
Yes, I tried sleep(1), sleep(5) etc., as I was simply interested in seeing whether my main loop was hogging the CPU.

Or are you saying that sleep(1) would also block the CPU, but that time_sleep(0.01) would yield enough for the processor to do other things (much like DoEvents() used to in VB, back in the day)?

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 7124
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: PIGPIO performance

Wed Jul 27, 2016 3:45 pm

joan wrote:Did you mean sleep()? That has a granularity of a second. Try something like time_sleep(0.01).
Or the stock usleep(usecs) covers most cases.
(Sorry, I cringe when I see floats or doubles being used unnecessarily - too many years on systems where floating point performance really sucked).
Software Engineer at Raspberry Pi Trading. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

jamesh
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 23312
Joined: Sat Jul 30, 2011 7:41 pm

Re: PIGPIO performance

Wed Jul 27, 2016 3:46 pm

Some variant of sleep should work as it gives up the CPU time to something else whilst waiting. Would be a quick check before getting hands dirty with semaphores as 6x9 is completely right to suggest.
Principal Software Engineer at Raspberry Pi (Trading) Ltd.
Contrary to popular belief, humorous signatures are allowed. Here's an example...
"My grief counseller just died, luckily, he was so good, I didn't care."

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 7124
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: PIGPIO performance

Wed Jul 27, 2016 3:47 pm

Morphology wrote:
joan wrote:Did you mean sleep()? That has a granularity of a second. Try something like time_sleep(0.01).
Yes, I tried sleep(1), sleep(5) etc., as I was simply interested in seeing whether my main loop was hogging the CPU.

Or are you saying that sleep(1) would also block the CPU, but that time_sleep(0.01) would yield enough for the processor to do other things (much like DoEvents() used to in VB, back in the day)?
All of the above will suspend the task and relinquish the processor to other tasks. Your task will get put back into the scheduled state again when the sleep time completes, and will get rerun soon after when the scheduler wakes up and determines what to run.
Software Engineer at Raspberry Pi Trading. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 7124
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: PIGPIO performance

Wed Jul 27, 2016 3:51 pm

You actually say that you're on a Pi3, so hogging one CPU core shouldn't matter too much as the other 3 cores can handle the other tasks.
It will make a HUGE difference if you're serious about shifting to a PiZero as that is a single core processor.
Software Engineer at Raspberry Pi Trading. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

Re: PIGPIO performance

Wed Jul 27, 2016 4:00 pm

6by9 wrote:You actually say that you're on a Pi3, so hogging one CPU core shouldn't matter too much as the other 3 cores can handle the other tasks.
It will make a HUGE difference if you're serious about shifting to a PiZero as that is a single core processor.
Yes, point taken. For testing purposes and for narrowing down what the root cause of my performance issues are (whether it is simply that I am trying to do too much during the callbacks, or whether there is something architecturally wrong with the way I am using the PIGPIO library), I am more than happy to stick sleep(1) or whatever in my main loop. The responsiveness of the Touch UI or my various knobs & buttons isn't the issue at this stage.

Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

Re: PIGPIO performance

Thu Jul 28, 2016 7:43 am

A quick update to this; Yesterday evening I stubbed out large chunks of my code so that I could package something up to send to Joan and which would run on a vanilla Raspberry Pi. Whilst doing so I discovered two things:

1) Adding a sleep(1) or similar to my main loop made no difference to performance on a Pi 3 though, with a weather eye on making this work on a PiZero, I will do as @6x9 suggests and implement semaphores to wake up the main loop when it has something to do. I will also do some more testing on a Zero.

2) without my TFT screen and i2c code in place, performance was more than adequate for my purposes - I could clock the sequencer from an external source all the way up into audio frequencies - it reached nearly 170Hz before I started getting dropouts and glitches.

So, it may be that I am simply running out of CPU cycles, or it may be that the performance of the i2c interface is throttling my throughput. I am running the i2c bus in standard Mode (100KHz) at present. I can try it in Fast Mode to see if that will make a difference, though doing so may cause me problems further down the line (so to speak) with i2c peripherals connected through several cm of ribbon cable.

I also need to take a long, hard, look at my code to make sure I haven't done anything stupid. It does work, so I haven't done anything too stupid!

The main purpose of starting this thread on this forum still stands, however, which is: Is setting up a number of Callbacks, Timers, writing to i2c peripherals etc. within a single-threaded app the best/correct way of utilising the power of the PIGPIO library, or should I be spawning separate threads for certain purposes?

Richard

User avatar
joan
Posts: 14175
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: PIGPIO performance

Thu Jul 28, 2016 3:15 pm

I've had a look at the code and can't see anything obviously wrong or see anything I would have structured differently.

The gpioTrigger function uses a busy wait so the 100µs call you make will effectively waste 100µs of CPU time. There's no problem with that as long as it's not called too often.

You set up gpioGlitchFilter for some GPIO with the value 100µs. Again nothing wrong with that as long as all the incoming edges (you want to respond to) are longer than 100µs.

Is the gpioTrigger used to generate an input to a GPIO with a glitch filter? If so the trigger won't have a reliable affect - it will only work when the trigger delay happens to be more than 100µs.

Don't use sleep(1), use something like usleep(100) in your main loop.

What sort of clock rate are you planning to use. On my Pi2 with a clock (on GPIO12) over 300 Hz one core was 100% and the rest effectively idle. Dropping to 250Hz gave 82%, 200Hz 76%, 160Hz 56% etc.

Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

Re: PIGPIO performance

Thu Jul 28, 2016 4:13 pm

joan wrote:I've had a look at the code and can't see anything obviously wrong or see anything I would have structured differently.

The gpioTrigger function uses a busy wait so the 100µs call you make will effectively waste 100µs of CPU time. There's no problem with that as long as it's not called too often.

You set up gpioGlitchFilter for some GPIO with the value 100µs. Again nothing wrong with that as long as all the incoming edges (you want to respond to) are longer than 100µs.

Is the gpioTrigger used to generate an input to a GPIO with a glitch filter? If so the trigger won't have a reliable affect - it will only work when the trigger delay happens to be more than 100µs.

Don't use sleep(1), use something like usleep(100) in your main loop.

What sort of clock rate are you planning to use. On my Pi2 with a clock (on GPIO12) over 300 Hz one core was 100% and the rest effectively idle. Dropping to 250Hz gave 82%, 200Hz 76%, 160Hz 56% etc.
Joan, many thanks for taking the time to look at this.

In answer to your questions:

The trigger is purely a GPIO Output and no glitch filter would be applied - it is simply a pulse which is output as the sequencer passes step 1 of the sequence. The trigger function is convenient, otherwise I'd have to launch a timer in order to turn the pulse off (I may have to do that if 100uS turns out to be too short to trigger external devices) and it is interesting to note that execution waits on the trigger function.

I am using the gpioGlitch filter on certain inputs to help de-bounce external switches and/or to make sure I get a clean trigger from the rising edge of an external clock which may not be a nice neat square wave (and may even be an audio waveform). Using a simple r-c circuit to de-bounce the switches & buttons might be an equally simple option.

In the main I am hoping to use the Internal timer as my main clock, though this gives me an upper limit of 50 steps per second for a 10ms timer (because the timer has to expire twice for a full step).

50 steps per second is generally fast enough for most electronic music. Assuming 16th notes as the smallest division of 1 beat, this would give me just under 190 beats per minute, which is right up there with Jungle and Drum'n'bass.

However, most hardware analogue sequencers can also be clocked from external sources, and will clock right up to audio speeds, which can give some interesting effects - you can basically dial in a waveform using the steps of your sequence - so I was hoping to be able to do the same.

300Hz is obviously great, though I doubt I'd be able to get anywhere near that throughput once the i2c code is back in.

What I will work on is getting it to fail gracefully at higher external clock speeds - perhaps reaching a threshold above which it will deliberately miss every second step, for example.

What I'm really grateful for is your confirmation that you wouldn't have structured it any differently - I know now that I'm not missing a trick somewhere.

All the best, and many thanks to those who responded.

Richard

Morphology
Posts: 36
Joined: Tue Jan 10, 2012 11:16 am
Contact: Website

Re: PIGPIO performance

Thu Jul 28, 2016 9:19 pm

A follow-up post for anyone who is interested: After learning that there was nothing fundamentally wrong with the way I'm using the PIGPIO library, I then started looking elsewhere for the cause of the glitchy performance of my sequencer and it turns out to be something I really wasn't expecting:

It is caused by writing to the framebuffer.

The little function that pokes a 565-encoded 16-bit value into the framebuffer is as follows:

Code: Select all

void put_pixel_RGB565(char *fbp, int x, int y, unsigned short c)
{
    unsigned int pix_offset = x * 2 + y * finfo.line_length;
    *((unsigned short*)(fbp + pix_offset)) = c;
}
If I comment out the second line: *((unsigned short*)(fbp + pix_offset)) = c; everything behaves as it should.

So, it looks like I need to either use page flipping as described by -rst- in his excellent Raspberry Compote articles (which is where I got most of my low level framebuffer code from): http://raspberrycompote.blogspot.com/ or else find some other way of ensuring I only write to the framebuffer when it's not busy (during the vertical sync period, perhaps?).

I really wasn't expecting that to be the cause.

Morph

Return to “C/C++”