Ruptor
Posts: 72
Joined: Mon Jan 27, 2014 12:26 pm
Location: London

High speed pulse generation

Mon Jan 27, 2014 12:39 pm

What is the fastest I can rattle a pin up and down on the GPIO? I am an engineer and I want to make a pulse generator capable of giving a 10nS pulse. I don't know anything about the Pi circuit yet and thought I would ask the question before getting involved in the schematics. The processor runs at 700 MHz so I would have thought that in assembler writting directly to a processor pin it would give me 1.43 nS pulses increasing in 1.43 nS steps allowing me to get about 10 nS with 7 cpu clock cycles but is it possible to do this with the Pi?

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

Re: High speed pulse generation

Mon Jan 27, 2014 2:41 pm

I think circa 25 MHz is the best that can be done.

It would be an almost pointless exercise as I don't think there will be any possibility of fine tuning the rate.

Ruptor
Posts: 72
Joined: Mon Jan 27, 2014 12:26 pm
Location: London

Re: High speed pulse generation

Mon Jan 27, 2014 4:39 pm

OK thanks Joan I assume you are talking about the GPIO pins. I just wonder if there is any other pin that could be used to generate fast pulses with special low level code? I shall have a look at the Broadcom chip specifications.

rst
Posts: 386
Joined: Sat Apr 20, 2013 6:42 pm
Location: Germany

Re: High speed pulse generation

Mon Jan 27, 2014 6:21 pm

Perhaps you can use the general purpose clock GPCLK0 (pp. 105 in the Broadcom manual) which can generate up to 125 MHz square wave according to this document. The signal can be accessed through GPIO4 on the P1 header (select alternate function 0). I haven't tested this.

Rene
Last edited by rst on Mon Jan 27, 2014 7:26 pm, edited 1 time in total.

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

Re: High speed pulse generation

Mon Jan 27, 2014 7:17 pm

rst wrote:Perhaps you can use the general purpose clock GPCLK0 (pp. 65 in the Broadcom manual) which can generate up to 125 MHz square wave according to this document. The signal can be accessed through GPIO4 on the P1 header (select alternate function 0). I haven't tested this.

Rene
Page 106 of the Broadcom BCM2835 ARM Peripherals does say

"Operating Frequency
The maximum operating frequency of the General Purpose clocks is ~125MHz at 1.2V but
this will be reduced if the GPIO pins are heavily loaded or have a capacitive load."

I have no idea what that actually means.

User avatar
chrisryall
Posts: 155
Joined: Wed Nov 27, 2013 11:45 am
Location: Wirral UK
Contact: Website

Re: High speed pulse generation

Mon Jan 27, 2014 7:30 pm

joan wrote:I have no idea what that actually means.
Hmm, I suspect you do ..

Image
Last edited by chrisryall on Mon Jan 27, 2014 8:33 pm, edited 1 time in total.

rst
Posts: 386
Joined: Sat Apr 20, 2013 6:42 pm
Location: Germany

Re: High speed pulse generation

Mon Jan 27, 2014 7:55 pm

joan wrote:"Operating Frequency
The maximum operating frequency of the General Purpose clocks is ~125MHz at 1.2V but
this will be reduced if the GPIO pins are heavily loaded or have a capacitive load."

I have no idea what that actually means.
I have no degree in electronics but when you have a capacitor (producing capacitive load) it takes some time to load/unload it when the level of the square wave signal changes. This will drop down the possible operating frequency. And 125 MHz is already a high frequency.

On page 105 in the Broadcom manual they wrote not to use more than 25 MHz in this "module". I think this refers to the MASH filter. Otherwise the note above would be a contradiction. Hence one could only use "int divide" and so has only limited possibilities in varying the frequency at these high frequencies.

Use it on your own risk! :)

Rene

User avatar
mikronauts
Posts: 2709
Joined: Sat Jan 05, 2013 7:28 pm
Contact: Website

Re: High speed pulse generation

Mon Jan 27, 2014 9:19 pm

I can think of three solutions:

1) hook up a DDS chip capable of generating 100Mhz square waves

2) add an i2c programmable clock generator chip

3) buy my upcoming RPi product that adds an eight core 32 bit microcontroller running at 100Mhz with 24 digital I/O's and an 8 channel 12 bit ADC. The microcontroller is capable of generating 100Mhz (even higher) with its counters :)
Ruptor wrote:What is the fastest I can rattle a pin up and down on the GPIO? I am an engineer and I want to make a pulse generator capable of giving a 10nS pulse. I don't know anything about the Pi circuit yet and thought I would ask the question before getting involved in the schematics. The processor runs at 700 MHz so I would have thought that in assembler writting directly to a processor pin it would give me 1.43 nS pulses increasing in 1.43 nS steps allowing me to get about 10 nS with 7 cpu clock cycles but is it possible to do this with the Pi?
http://Mikronauts.com - home of EZasPi, RoboPi, Pi Rtc Dio and Pi Jumper @Mikronauts on Twitter
Advanced Robotics, I/O expansion and prototyping boards for the Raspberry Pi

User avatar
jbeale
Posts: 3469
Joined: Tue Nov 22, 2011 11:51 pm
Contact: Website

Re: High speed pulse generation

Mon Jan 27, 2014 9:45 pm

mikronauts wrote: 3) buy my upcoming RPi product that adds an eight core 32 bit microcontroller running at 100Mhz with 24 digital I/O's and an 8 channel 12 bit ADC. The microcontroller is capable of generating 100Mhz (even higher) with its counters :)
You don't specify details, but this statement seems to invite questions :-). I'm not aware of many devices like that; are you using a Parallax Propeller with outboard ADC, or something else?

The Propeller is an interesting design with many unique features, and I never understood why it hasn't been more widely used. I have used one to do 10 nsec resolution timing measurements on five inputs at once, it worked well. It talked to the Pi via serial link. http://www.raspberrypi.org/phpBB3/viewt ... 5&p=181077

User avatar
mikronauts
Posts: 2709
Joined: Sat Jan 05, 2013 7:28 pm
Contact: Website

Re: High speed pulse generation

Mon Jan 27, 2014 11:24 pm

You are correct, it is based on the Parallax Propeller :) with an MCP3208 ADC.

I've also been puzzled as to why the Propeller is not used more widely; it has worked great in my projects (and products), and also apparently in your frequency drift measurement project.

The reason I did not post more information on my board yet is that it is not available for sale yet. I expect production boards later this week, and will hopefully have kits available next week. An almost complete product page is up on my site at http://www.mikronauts.com/raspberry-pi/robopi/

I don't want to take over this thread, I'll start a thread for RoboPi here when it is available. Until then, there is a thread for it on the Parallax forum at http://forums.parallax.com/showthread.p ... ler-for-Pi if you or anyone else would like to ask any questions.
jbeale wrote:
mikronauts wrote: 3) buy my upcoming RPi product that adds an eight core 32 bit microcontroller running at 100Mhz with 24 digital I/O's and an 8 channel 12 bit ADC. The microcontroller is capable of generating 100Mhz (even higher) with its counters :)
You don't specify details, but this statement seems to invite questions :-). I'm not aware of many devices like that; are you using a Parallax Propeller with outboard ADC, or something else?

The Propeller is an interesting design with many unique features, and I never understood why it hasn't been more widely used. I have used one to do 10 nsec resolution timing measurements on five inputs at once, it worked well. It talked to the Pi via serial link. http://www.raspberrypi.org/phpBB3/viewt ... 5&p=181077
http://Mikronauts.com - home of EZasPi, RoboPi, Pi Rtc Dio and Pi Jumper @Mikronauts on Twitter
Advanced Robotics, I/O expansion and prototyping boards for the Raspberry Pi

Ruptor
Posts: 72
Joined: Mon Jan 27, 2014 12:26 pm
Location: London

Re: High speed pulse generation

Tue Jan 28, 2014 12:33 am

Hi Guys
I am sure there are many things I could add to RPi like a super computer to get pico second pulses but the suggestion made by Rene seems to be a simple viable option using the RPi hardware that looks like it will do the job. Thank you for your suggestions.

User avatar
jbeale
Posts: 3469
Joined: Tue Nov 22, 2011 11:51 pm
Contact: Website

Re: High speed pulse generation

Tue Jan 28, 2014 1:50 am

GPIO 4 (pin 7 on the header) can generate pulse trains above 100 MHz, and this has been used to generate FM broadcast-band signals: http://www.icrobotics.co.uk/wiki/index. ... ransmitter

I had assumed that this was limited to continuous pulse trains, so you could not generate a single 10 nsec pulse in isolation (although you could do that with some external circuits, eg. a flip-flop that latches itself off after the first pulse goes through). If you need one fixed duration, not adjustable, It is also possible to turn a simple step into a fast pulse using a delay line (like a length of coax cable) where the physical length of the line determines how long the pulse is. See for example http://en.wikipedia.org/wiki/Pulse_forming_network

If you do discover a way to generate a single 10-ns pulse in isolation from the R-Pi without using any external circuits, I would be interested to hear how you did it.

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

Re: High speed pulse generation

Tue Jan 28, 2014 1:09 pm

I've had a bit of a play and you can sort of send one-shots with the PWM peripheral on the Pi.

Currently I seem to be able to transmit 16 '1' bits in 3 microseconds. I guess 1 bit would be sent in roughly 200 nano seconds. I have no method of timing sub-microsecond pulses.

I don't understand the timing. It doesn't seem to make much sense against the clock I think is being used by the PWM. Once I understand that I might be able to reduce the bit time.

User avatar
jbeale
Posts: 3469
Joined: Tue Nov 22, 2011 11:51 pm
Contact: Website

Re: High speed pulse generation

Tue Jan 28, 2014 4:57 pm

joan wrote:I've had a bit of a play and you can sort of send one-shots with the PWM peripheral on the Pi.
Currently I seem to be able to transmit 16 '1' bits in 3 microseconds. I guess 1 bit would be sent in roughly 200 nano seconds. I have no method of timing sub-microsecond pulses.
Interesting. If you have some code I can try, I have access to a 200 MHz analog bandwidth scope (Rigol DS1204B digital at 2 Gsamples/sec) so I should be able to measure into the 10 nsec region at least.

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

Re: High speed pulse generation

Tue Jan 28, 2014 5:37 pm

jbeale wrote:
joan wrote:I've had a bit of a play and you can sort of send one-shots with the PWM peripheral on the Pi.
Currently I seem to be able to transmit 16 '1' bits in 3 microseconds. I guess 1 bit would be sent in roughly 200 nano seconds. I have no method of timing sub-microsecond pulses.
Interesting. If you have some code I can try, I have access to a 200 MHz analog bandwidth scope (Rigol DS1204B digital at 2 Gsamples/sec) so I should be able to measure into the 10 nsec region at least.
This is the current code.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>


#define CLK_BASE  0x20101000
#define GPIO_BASE 0x20200000
#define PWM_BASE  0x2020C000

#define CLK_LEN   0xA8
#define GPIO_LEN  0xB4
#define PWM_LEN   0x28

#define GPSET0     7
#define GPSET1     8

#define GPCLR0    10
#define GPCLR1    11

#define GPLEV0    13
#define GPLEV1    14

#define PWM_CTL      0
#define PWM_STA      1
#define PWM_DMAC     2
#define PWM_RNG1     4
#define PWM_DAT1     5
#define PWM_FIFO     6
#define PWM_RNG2     8
#define PWM_DAT2     9

#define PWM_CTL_CLRF1 (1<<6)
#define PWM_CTL_USEF1 (1<<5)
#define PWM_CTL_MODE1 (1<<1)
#define PWM_CTL_PWEN1 (1<<0)

#define PWM_DMAC_ENAB      (1 <<31)
#define PWM_DMAC_PANIC(x) ((x)<< 8)
#define PWM_DMAC_DREQ(x)   (x)

#define CLK_PASSWD  (0x5A<<24)

#define CLK_CTL_MASH(x)((x)<<9)
#define CLK_CTL_BUSY    (1 <<7)
#define CLK_CTL_KILL    (1 <<5)
#define CLK_CTL_ENAB    (1 <<4)
#define CLK_CTL_SRC(x) ((x)<<0)

#define CLK_CTL_SRC_OSC  1  /*  19.2 MHz */
#define CLK_CTL_SRC_PLLD 6  /* 500.0 MHz */

#define CLK_DIV_DIVI(x) ((x)<<12)
#define CLK_DIV_DIVF(x) ((x)<< 0)

#define CLK_PCMCTL 38
#define CLK_PCMDIV 39

#define CLK_PWMCTL 40
#define CLK_PWMDIV 41

static volatile uint32_t  *clkReg  = MAP_FAILED;
static volatile uint32_t  *gpioReg = MAP_FAILED;
static volatile uint32_t  *pwmReg  = MAP_FAILED;

int gpioSetMode(unsigned gpio, unsigned mode)
{
   int reg, shift;

   reg   =  gpio/10;
   shift = (gpio%10) * 3;

   gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift);

   return 0;
}

int gpioGetMode(unsigned gpio)
{
   int reg, shift;

   reg   =  gpio/10;
   shift = (gpio%10) * 3;

   return (*(gpioReg + reg) >> shift) & 7;
}

uint32_t gpioRead_Bits_0_31(void) { return (*(gpioReg + GPLEV0)); }

uint32_t gpioRead_Bits_32_53(void) { return (*(gpioReg + GPLEV1)); }

int gpioRead(unsigned gpio)
{
   unsigned bank, bit;

   bank = gpio >> 5;

   bit = (1<<(gpio&0x1F));

   if ((*(gpioReg + GPLEV0 + bank) & bit) != 0) return 1;
   else                                         return 0;
}


int gpioWrite(unsigned gpio, unsigned level)
{
   unsigned bank, bit;

   bank = gpio >> 5;

   bit = (1<<(gpio&0x1F));

   if (level == 0) *(gpioReg + GPCLR0 + bank) = bit;
   else            *(gpioReg + GPSET0 + bank) = bit;

   return 0;
}

static void initClock(unsigned divider)
{
   clkReg[CLK_PWMCTL] = CLK_PASSWD | CLK_CTL_KILL;

   usleep(10);

   clkReg[CLK_PWMDIV] = CLK_PASSWD | CLK_DIV_DIVI(divider) | CLK_DIV_DIVF(0);

   usleep(10);

   clkReg[CLK_PWMCTL] = CLK_PASSWD | CLK_CTL_MASH(0) |
                        CLK_CTL_SRC(CLK_CTL_SRC_PLLD);

   usleep(10);

   clkReg[CLK_PWMCTL] |= (CLK_PASSWD | CLK_CTL_ENAB);

   usleep(2000);
}

static void initPWM(unsigned bits)
{
   int i, words, word;

   /* reset PWM */

   pwmReg[PWM_CTL] = 0;

   usleep(10);

   pwmReg[PWM_STA] = -1;

   usleep(10);

   /* set number of bits to transmit */

   pwmReg[PWM_RNG1] = 32;

   usleep(20);

   /* clear PWM fifo */

   pwmReg[PWM_CTL] = PWM_CTL_CLRF1;

   usleep(20);

   if (bits == 0) bits = 1;
   if (bits > 256) bits = 256;

   words = 8;

   while (bits >= 32)
   {
      pwmReg[PWM_FIFO] = 0xFFFFFFFF;
      usleep(10);
      //printf("FFFFFFFF\n");
      --words;
      bits -= 32;
   }

   if (bits)
   {
      word = 0;

      for (i=0; i<bits; i++) word |= (1<<(31-i));

      pwmReg[PWM_FIFO] = word;
      usleep(10);
      //printf("%08X\n", word);
      --words;
   }

   while (words > 0)
   {
      pwmReg[PWM_FIFO] = 0x00000000;
      usleep(10);
      //printf("00000000\n");
      --words;
   }

   //printf("\n");

   usleep(10);

   /* enable PWM channel 1 and use fifo */

   pwmReg[PWM_CTL] = PWM_CTL_USEF1 | PWM_CTL_MODE1 | PWM_CTL_PWEN1;
}


static uint32_t * mapMem(int fd, unsigned base, unsigned len)
{
   return mmap
   (
      0,
      len,
      PROT_READ|PROT_WRITE|PROT_EXEC,
      MAP_SHARED|MAP_LOCKED,
      fd,
      base
   );
}

int main(int argc, char *argv[])
{
   int fd, i, gpio;

   int div=500, bits=32, pulses=100;

   if (argc > 1) div    = atoi(argv[1]);
   if (argc > 2) bits   = atoi(argv[2]);
   if (argc > 3) pulses = atoi(argv[3]);

   fd = open("/dev/mem", O_RDWR | O_SYNC);

   if (fd<0)
   {
      printf("need to run as root, e.g. sudo %s\n", argv[0]);
      exit(1);
   }

   gpioReg = mapMem(fd, GPIO_BASE, GPIO_LEN);
   pwmReg  = mapMem(fd, PWM_BASE,  PWM_LEN);
   clkReg  = mapMem(fd, CLK_BASE,  CLK_LEN);

   close(fd);

   initClock(div);

   for (i=0; i< pulses; i++)
   {
      initPWM(bits);
      usleep(50);
   }
}
I call it pwm_pulse.c

gcc -o pwm_pulse pwm_pulse.c

sudo ./pwm_pulse clock_divider bits pulses

Currently I'm using

sudo ./pwm_pulse 2 1 100000

which should set a clock of 250Mhz (I'm using the 500MHz PLL clock source), send one bit, one hundred thousand times (50 micros between each pulse).

The bit length should be 4 nano seconds.

I'm sampling at 1 MHz and the average number of 'hits' I get would suggest the pulses are longer than 4 nano seconds.

I suggest you try

sudo ./pwm_pulse 5 1 100000

which should be a 10 nano second pulse.

Don't set a clock divider of 1. It doesn't work.

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

Re: High speed pulse generation

Tue Jan 28, 2014 5:42 pm

I should have mentioned this is gpio18 and you need to set it to ALT5 (PWM0).

I did that external to the program.

You should add a call to gpioSetMode(18, 2) to set gpio18 to ALT5. Just after the close(fd) would be fine.

User avatar
jbeale
Posts: 3469
Joined: Tue Nov 22, 2011 11:51 pm
Contact: Website

Re: High speed pulse generation

Tue Jan 28, 2014 6:39 pm

That was nicely done! Added the gpioSetMode(18, 2); line, compiled to 'pulse', and

Code: Select all

sudo ./pulse 5 1 100000
Just as advertised, a 10 nsec pulse all by itself, with the interval between pulses varying but around 1.35 msec. The variable inter-pulse delay I assume has to do with the usual Linux background task switching. At the 2 ns per division setting, my scope says pulsewidth 10.2 ns, risetime 1.6 ns, falltime 1.9 nsec but that's at the limit of what it can measure so I can't be too confident at the ns level. At any rate the R-Pi is now a handy pulse generator, in addition to everything else.
10nsec-pulse-1.png
10nsec-pulse-1.png (4.91 KiB) Viewed 20945 times
10nsec-pulse-2.png
10nsec-pulse-2.png (5.25 KiB) Viewed 20945 times
It's interesting, about 6 nsec after the initial step you see a small second step up. Might be related to my scope probe cable which is about 2 meters long. I have never had a clean fast pulse like this to measure, so this is showing me something new about my equipment.

I also tried

Code: Select all

  command           +width  rise  fall (ns)
---------------------------------------------
pulse 4 1 100000    8.1      2.0    2.4
pulse 3 1 100000    6.1      2.0    2.4
pulse 2 1 100000    4.0      1.8    2.1
but the shortest 4ns pulse is looking more rounded and is clearly at the limit of the scope.
Last edited by jbeale on Tue Jan 28, 2014 7:07 pm, edited 1 time in total.

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

Re: High speed pulse generation

Tue Jan 28, 2014 7:00 pm

I didn't realise generating a one-shot would be of any use.

Excusing my ignorance but what use would be made of such a pulse? I sort of assumed you'd need to generate a train of pulses with varying lengths.

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

Re: High speed pulse generation

Tue Jan 28, 2014 7:07 pm

The code was just adapted from another program as a quick test. There are about 150 microseconds of delays in the initPWM function. Also it doesn't seem to operate properly once you get beyond a certain number of bits (8 * 32 bit fifo, for 256 bits). At a random stage you seem to get off bits interleaved within on bits and a continuous stream rather than a one-shot. I'll see if I can tidy it up.

User avatar
jbeale
Posts: 3469
Joined: Tue Nov 22, 2011 11:51 pm
Contact: Website

Re: High speed pulse generation

Tue Jan 28, 2014 7:16 pm

Short pulses can be useful. Suppose you have a long coax cable and there is a break in it somewhere but you don't know where. Connect your pulse generator to one end of it, along with a fast scope. The break (short or open) is an impedance change that will generate a reflection which will show up on the scope as a positive or negative bump, depending on whether it is larger or smaller than the cable's characteristic impedance (eg. 50 or 75 ohms). Knowing the velocity factor of the cable, you can now calculate the distance to the break. This is called time domain reflectometry. Actually you can also use just a single fast edge (step, instead of a full pulse).

Short pulses can be used to drive a diode laser in a time-of-flight measurement, used in LIDAR for example. Also just to test the peak current handling capability of a device, eg. power FET without changing the temperature of the part, as a longer pulse or high-duty cycle PWM signal would do.

If you can synchronize the pulse to something else like an external signal, that becomes more useful. If you can generate the pulses with an accurately known repetition rate, that also opens more applications.

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

Re: High speed pulse generation

Wed Jan 29, 2014 5:51 pm

I have tidied the nano pulse generation code somewhat and made it a bit more generic.

It now expects up to three arguments on the command line
  1. nano, the pulse length in nano seconds (default 1000)
  2. pulses, the number of pulses to transmit (default 100)
  3. gap, the minimum interpulse gap in nano seconds (default 5000)
The Broadcom PWM peripheral is used to generate the pulses on the fixed gpio #18.

You can't really get a regular gap, you seem to get at least 150 microseconds between pulses whatever you do.

As a diagnostic it prints the values of nanos, pulses, and gap being used. nanos may not be what was requested if an exact fit with PWM clock divider and number of bits couldn't be found.

gcc -o nanopulse nanonpulse.c
sudo ./nanopulse nanos pulses gap

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>


#define CLK_BASE  0x20101000
#define GPIO_BASE 0x20200000
#define PWM_BASE  0x2020C000

#define CLK_LEN   0xA8
#define GPIO_LEN  0xB4
#define PWM_LEN   0x28

#define PWM_CTL      0
#define PWM_STA      1
#define PWM_RNG1     4
#define PWM_FIFO     6

#define PWM_CTL_CLRF1 (1<<6)
#define PWM_CTL_USEF1 (1<<5)
#define PWM_CTL_MODE1 (1<<1)
#define PWM_CTL_PWEN1 (1<<0)

#define PWM_STA_EMPT1 (1<<1)

#define CLK_PASSWD  (0x5A<<24)

#define CLK_CTL_MASH(x)((x)<<9)
#define CLK_CTL_BUSY    (1 <<7)
#define CLK_CTL_KILL    (1 <<5)
#define CLK_CTL_ENAB    (1 <<4)
#define CLK_CTL_SRC(x) ((x)<<0)

#define CLK_CTL_SRC_PLLD 6  /* 500.0 MHz */

#define CLK_DIV_DIVI(x) ((x)<<12)
#define CLK_DIV_DIVF(x) ((x)<< 0)

#define CLK_PWMCTL 40
#define CLK_PWMDIV 41

#define MAX_BITS 224

typedef struct
{
   unsigned divider;
   unsigned bits;
} pwm_clock_cfg_t;

unsigned base_nano[]={4, 8, 10, 20, 40, 80, 100, 200, 250, 500, 1000};

static volatile uint32_t  *clkReg  = MAP_FAILED;
static volatile uint32_t  *gpioReg = MAP_FAILED;
static volatile uint32_t  *pwmReg  = MAP_FAILED;

static void mynanosleep(unsigned nanos)
{
   struct timespec ts, tr;

   ts.tv_sec = 0;
   ts.tv_nsec = nanos;
   while (nanosleep(&ts, &tr))
   {
      ts = tr;
   }
}

int gpioSetMode(unsigned gpio, unsigned mode)
{
   int reg, shift;

   reg   =  gpio/10;
   shift = (gpio%10) * 3;

   gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift);

   return 0;
}

int gpioGetMode(unsigned gpio)
{
   int reg, shift;

   reg   =  gpio/10;
   shift = (gpio%10) * 3;

   return (*(gpioReg + reg) >> shift) & 7;
}

static void initPWM(unsigned divider)
{
   /* reset PWM clock */
   clkReg[CLK_PWMCTL] = CLK_PASSWD | CLK_CTL_KILL;

   mynanosleep(10000);

   /* set PWM clock source as 500 MHz PLLD */
   clkReg[CLK_PWMCTL] = CLK_PASSWD | CLK_CTL_SRC(CLK_CTL_SRC_PLLD);

   mynanosleep(10000);

   /* set PWM clock divider */
   clkReg[CLK_PWMDIV] = CLK_PASSWD | CLK_DIV_DIVI(divider) | CLK_DIV_DIVF(0);

   mynanosleep(10000);

   /* enable PWM clock */
   clkReg[CLK_PWMCTL] =
      CLK_PASSWD | CLK_CTL_ENAB | CLK_CTL_SRC(CLK_CTL_SRC_PLLD);

   mynanosleep(100000);

   /* reset PWM */
   pwmReg[PWM_CTL] = 0;

   /* clear PWM status bits */
   pwmReg[PWM_STA] = -1;

   mynanosleep(10000);
}

static void sendPulse(unsigned bits)
{
   int i;
   uint32_t word;

   if      (bits == 0)       bits = 1;
   else if (bits > MAX_BITS) bits = MAX_BITS;

   /* clear PWM fifo */

   pwmReg[PWM_CTL] = PWM_CTL_CLRF1;

   mynanosleep(10000);

   while (bits >= 32)
   {
      pwmReg[PWM_FIFO] = -1;
      bits -= 32;
   }

   if (bits)
   {
      word = 0;

      for (i=0; i<bits; i++) word |= (1<<(31-i));

      pwmReg[PWM_FIFO] = word;
   }

   pwmReg[PWM_FIFO] = 0;

   /* enable PWM for serialised data from fifo */
   pwmReg[PWM_CTL] = PWM_CTL_USEF1 | PWM_CTL_MODE1 | PWM_CTL_PWEN1;
}


static uint32_t * mapMem(int fd, unsigned base, unsigned len)
{
   return mmap
   (
      0,
      len,
      PROT_READ|PROT_WRITE|PROT_EXEC,
      MAP_SHARED|MAP_LOCKED,
      fd,
      base
   );
}

pwm_clock_cfg_t getDivBits(unsigned nano)
{
   pwm_clock_cfg_t cfg;

   unsigned i, base, bits, err, bestErr, bestBase, bestBits;

   bestErr = -1;

   for (i=0; i<sizeof(base_nano)/sizeof(unsigned);i++)
   {
      bits = nano / base_nano[i];

      if (bits > MAX_BITS) bits = MAX_BITS;

      err = nano - (bits * base_nano[i]);

      if (err < bestErr)
      {
         bestErr = err;
         bestBase = base_nano[i];
         bestBits = bits;
      }
   }

   cfg.divider = bestBase / 2;
   cfg.bits = bestBits;

   return cfg;
}

int main(int argc, char *argv[])
{
   int fd, i, gpio, mode;
   pwm_clock_cfg_t cfg;

   int nanos=1000, pulses=100, gap=5000;

   fd = open("/dev/mem", O_RDWR | O_SYNC);

   if (fd<0)
   {
      printf("need to run as root, e.g. sudo %s\n", argv[0]);
      exit(1);
   }

   gpioReg = mapMem(fd, GPIO_BASE, GPIO_LEN);
   pwmReg  = mapMem(fd, PWM_BASE,  PWM_LEN);
   clkReg  = mapMem(fd, CLK_BASE,  CLK_LEN);

   close(fd);

   if (argc > 1) nanos  = atoi(argv[1]);
   if (argc > 2) pulses = atoi(argv[2]);
   if (argc > 3) gap    = atoi(argv[3]);

   if      (nanos < 4)      nanos = 4;
   else if (nanos > 224000) nanos = 224000;

   if (pulses < 1) pulses = 1;

   if (gap < 0) gap = 0;

   cfg = getDivBits(nanos);

   printf("%d pulses of %d nanos with gap of %d nanos (div=%d bits=%d)\n",
      pulses, cfg.divider * 2 * cfg.bits, gap, cfg.divider, cfg.bits);

   mode = gpioGetMode(18); /* save original mode */

   gpioSetMode(18, 2); /* set to ALT5, PWM1 */

   initPWM(cfg.divider);

   for (i=0; i< pulses; i++)
   {
      sendPulse(cfg.bits);

      mynanosleep(nanos + gap);
   }

   gpioSetMode(18, mode); /* restore original mode */
}

User avatar
jbeale
Posts: 3469
Joined: Tue Nov 22, 2011 11:51 pm
Contact: Website

Re: High speed pulse generation

Thu Jan 30, 2014 7:19 am

Thank you; sure enough it works! On your previous code I noticed a difference in length between 4, 6, and 8 nsec pulses. With this one 4 and 6 ns seem to be the same thing, although 8 is longer. Anyway, thanks again for the handy pulse generator!

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

Re: High speed pulse generation

Thu Jan 30, 2014 9:20 am

jbeale wrote:Thank you; sure enough it works! On your previous code I noticed a difference in length between 4, 6, and 8 nsec pulses. With this one 4 and 6 ns seem to be the same thing, although 8 is longer. Anyway, thanks again for the handy pulse generator!
Yes, that is a feature of me not trusting dividers which give fractional results. I have no means of testing so was being fairly conservative.

The array

unsigned base_nano[]={4, 8, 10, 20, 40, 80, 100, 200, 250, 500, 1000};

is the list of nano pulses which can be generated by "safe" dividers. If you divide by 2 you get the clock divider. As you can see I use dividers (into the 500MHz clock) of 2, 4, 5, 10, 20, 40, 50, 100, 125, 250, and 500.

If you add 6 between base_nano 4 and 8 you'll get your 6 nano second pulse back.

if there are other values you need which also work when added to base_nano I would be interested.

N.B. to get other pulses I multiply a base_nano by an integer between 2 and 224 to get the required pulse length.

Ruptor
Posts: 72
Joined: Mon Jan 27, 2014 12:26 pm
Location: London

Re: High speed pulse generation

Fri Jan 31, 2014 11:32 pm

Thanks Joan this is a neat piece of code that electronics engineers will all be after. Pulses are used a lot in electronics for testing circuits but I want to use it in a medical application that will kill any pathogen. I think it is an easy task to get one pulse since the generator only needs to be stopped before the next pulse comes out. I suspect the kink in the top of the wave is due to the active drive output of the GPIO pin rather than the scope. If it is the same on my scope it is most unlikely to be the scope since my leads and scope are a completely different make.

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

Re: High speed pulse generation

Sat Feb 01, 2014 3:00 pm

I obviously didn't understand what you needed, otherwise I wouldn't have been so negative about the chances of success. Shows the danger of making assumptions I suppose.

Could the program be tailored to be more useful?

Return to “Bare metal, Assembly language”