6

I'm trying to make RPi generate high-speed square wave through its GPIO pin.

My code controlling GPIO output is based on this article

The essential part of my code is the following:

speed = 1000000; // 1 MHz, or 0.5 MHz anyway (just notice the order of MHz)
s2us = 1000000; // second to microsecond
while(1){
    GPIO_SET = 1<<7; // set GPIO 07 HIGH
    usleep(1/speed * s2us); // sleep for 1 us
    GPIO_CLR = 1<<7; // set GPIO 07  LOW
    usleep(1/speed * s2us); // sleep for 1 us
}

When I run my code, it shows the square wave with the period of 4.2 ms, which means about only 240 Hz. It's not a tolerable error since the order of hundreds Hz and MHz.

I've googled for this problem and found that usleep, nanosleep cannot work properly if the sleep time is too short since some kind of linux system scheduling. But I have no idea to solve this problem. Some suggest to use delay instead of sleep but I'm not sure it will work or not.

I use RPi model B rev 2.0 and Raspbina wheezy 2014. Fresh install and nothings installed.

4 Answers 4

9

My pigpio library will let you generate repeating waveforms with a resolution of microseconds. If you need less than microseconds you'll need to find another solution.

Python

#!/usr/bin/env python

import time

import pigpio

GPIO=4

square = []

#                          ON       OFF    MICROS
square.append(pigpio.pulse(1<<GPIO, 0,       4))
square.append(pigpio.pulse(0,       1<<GPIO, 4))

pi = pigpio.pi() # connect to local Pi

pi.set_mode(GPIO, pigpio.OUTPUT)

pi.wave_add_generic(square)

wid = pi.wave_create()

if wid >= 0:
   pi.wave_send_repeat(wid)
   time.sleep(60)
   pi.wave_tx_stop()
   pi.wave_delete(wid)

pi.stop()

C

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>

#include <pigpio.h>

#define GPIO 4
#define ON   10
#define OFF  10

static int gpio = GPIO;
static int on   = ON;
static int off  = OFF;

gpioPulse_t pulse[2];

void fatal(char *fmt, ...)
{
   char buf[256];
   va_list ap;

   va_start(ap, fmt);
   vsnprintf(buf, sizeof(buf), fmt, ap);
   va_end(ap);

   fprintf(stderr, "%s\n", buf);

   exit(EXIT_FAILURE);
}

void usage()
{
   fprintf(stderr, "\n" \
      "Usage: sudo ./square [OPTION] ...\n" \
      "   -f value, off micros, 1- (%d)\n" \
      "   -g value, gpio, 0-31     (%d)\n" \
      "   -n value, on micros, 1-  (%d)\n" \
      "EXAMPLE\n" \
      "sudo ./square -g 23\n" \
      "  Generate square wave on gpio 23.\n" \
   "\n", OFF, GPIO, ON);
}

static void initOpts(int argc, char *argv[])
{
   int i, opt;

   while ((opt = getopt(argc, argv, "f:g:n:")) != -1)
   {
      i = -1;

      switch (opt)
      {
         case 'f':
            i = atoi(optarg);
            if (i >= 1) off = i;
            else fatal("invalid -f option (%d)", i);
            break;

         case 'g':
            i = atoi(optarg);
            if ((i >= 1) && (i <= 31)) gpio = i;
            else fatal("invalid -g option (%d)", i);
            break;

         case 'n':
            i = atoi(optarg);
            if (i >= 1) on = i;
            else fatal("invalid -n option (%d)", i);
            break;

         default: /* '?' */
            usage();
            exit(EXIT_FAILURE);
        }
    }
}

int main(int argc, char *argv[])
{
   int wid;

   initOpts(argc, argv);

   printf("gpio #%d, on %dus, off %dus\n", gpio, on, off);

   if (gpioInitialise()<0) return -1;

   gpioSetMode(gpio, PI_OUTPUT);

   pulse[0].gpioOn = (1<<gpio);
   pulse[0].gpioOff = 0;
   pulse[0].usDelay = on;

   pulse[1].gpioOn = 0;
   pulse[1].gpioOff = (1<<gpio);
   pulse[1].usDelay = off;

   gpioWaveClear();

   gpioWaveAddGeneric(2, pulse);

   wid = gpioWaveCreate();

   if (wid >= 0)
   {
      gpioWaveTxSend(wid, PI_WAVE_MODE_REPEAT);

      while (1) sleep(1);
   }

   gpioTerminate();
}
2
  • Thanks, it worked. I can see the square wave with the period of 10 us and duty cycle 50%. Though there is some overshoot and undershoot as period gets shorter and shorter. But it's okay since I don't need such an extremely short period.
    – Jeon
    Commented Jun 30, 2014 at 7:10
  • 1
    If all you want is a square wave you could use the SPI clock, or transmit UUUU repeatedly on the UART, or use the PWM gpio. pigpio waveforms allow multiple gpios to be controlled simultaneously and were really intended for CNC and IR transmitter type applications.
    – joan
    Commented Jun 30, 2014 at 7:37
1

I've managed to make a 1us period square wave output on the GPIO pins using the ChibiOS realtime os (http://chibios.sourceforge.net/html/index.html). Here's an article that links to the BCM2835 drivers http://www.stevebate.net/chibios-rpi/GettingStarted.html

Follow the guide to install the OS, then check out the folder for GPIO control.

0

What you are trying to do is impossible using Linux, because it is not a realtime OS.
This is not just a problem with the delay routines.

Try your code, omitting the usleep calls, which might give you some idea what the maximum frequency might be, but even here you will find quite long and unpredictable delays due to interrupt handling.

It is not impossible to set the Pi hardware to generate a square wave, but this requires a totally different approach.

3
  • You mean not Linux on RPi, but some low-level approach to RPi? Like, manipulating ARM directly? Or could you recommend some other operating systems or devices that can replace Linux or RPi?
    – Jeon
    Commented Jun 30, 2014 at 5:50
  • I have not tried this myself, but reading "General Purpose GPIO Clocks" on the BMC data sheets indicates that it is possible and there are a few projects which use this. Try searching for "raspberry frequency generator"
    – Milliways
    Commented Jun 30, 2014 at 7:14
  • Well, impossible is a hard word here, there is something possible for sure with some limits. But of course I would not use it in my space ship.
    – Thomas
    Commented Nov 30, 2016 at 12:27
0

linux is NOT a realtime operating system, so you can't expect the sleep time to be exact. It will not be accurate, because of which you'll NOT get a steady tune, instead the tune will fluctuate in frequency. If you try to do same in microcontroller (e.g. arduino), the frequency will not fluctuate. You can use this snippet to get a beep by adjusting the timing parameters. But you cannot make beautiful melodies :(.

To generate a square wave on GPIO pin;

def beep(repeat):
   for i in range(0, repeat):
      for pulse in range(60):  # square wave loop
         GPIO.output(7, True)
         time.sleep(0.001)     # high for .001 sec
         GPIO.output(7, False)      
         time.sleep(0.001)     # low for .001 sec
      time.sleep(0.02)         # add a pause between each cycle
beep(4)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.