http://www.perlmonks.org?node_id=1098516

Peterpion has asked for the wisdom of the Perl Monks concerning the following question:

Greetings monks.

I am trying to use peri_read and its associated peri functions in the module Device::BCM2835 and failing miserably. I hit seg faults with every slight use of the peri functions and I can't work out what I am doing wrong, to the point of suspecting a fault with the module or its installation.

First, briefly - why am I trying to use low level access? I need to toggle groups of bits on a raspberry pi GPIO interface at high audio frequencies and setting individual bits one by one in perl is proving too slow. I believe that if I write a whole byte to the register at once I will probably hit my speed requirements. I have got it all working fine in C, but I am doing something which needs rapid code modifications (as I modify the hardware) which perl suits far better hence my attempts here.

But the documentation for the Device::BCM2835 module is quite sparse regarding the 3 peri functions and there are no examples, which is probably why I can't 'get it'. Although there are no examples of people using these functions on the searchable web, and so I doubt anyone here will have played with them personally, I wonder if more experienced eyes cast over the docs will detect my mistake.

Essentially I am doing this:

$|=1; use Device::BCM2835; Device::BCM2835::init()|| die "Could not init library"; my @pins = (&Device::BCM2835::RPI_GPIO_P1_07, &Device::BCM2835::RPI_GPIO_P1_11,&Device::BCM2835::RPI_GPIO_P1_12, &Device::BCM2835::RPI_V2_GPIO_P1_13,&Device::BCM2835::RPI_GPIO_P1_15, &Device::BCM2835::RPI_GPIO_P1_16,&Device::BCM2835::RPI_GPIO_P1_18, &Device::BCM2835::RPI_GPIO_P1_22); Device::BCM2835::gpio_fsel($_, &Device::BCM2835::BCM2835_GPIO_FSEL_OUT +P) for @pins; Device::BCM2835::peri_write(&Device::BCM2835::BCM2835_PERI_BASE + 0x2 +00000, 0x1C); print "No seg fault!!\n";

I have tried various permutations (many) of the peri_write arguments but all have the same effect, which is immediate seg fault.

Perhaps I am mixing access methods, but it seems to me I need to set the hardware pin modes one way or another and using the higher level methods would seem to be fine, after all the regisers will remain in the state I set them to (using fsel).

The code works fine if I use Device::BCM2835::gpio_write instead of peri_write but that only sets a single bit at a time.

I suspect my address arguments to peri_write are wrong, but I have tried several different ones, using the constants as well as the actual physical addresses which the function apparently takes. I can't find the actual perl in the module which the various functions are defined in so I have not been able to really see what the function peri_write looks like. I suspect the peri_write function is just a wrapper round the C, but looking at the C function it seems it should take the physical numerical addresses I have tried. So I am at that unhappy point of not knowing what to try next, and hoping that a wise monk could point me in the right direction.

TIA, Pete

Replies are listed 'Best First'.
Re: Device::BCM2835, hardware access on a pi, SIGSEGV
by Anonymous Monk on Aug 25, 2014 at 12:02 UTC

    There is an example given in Device::BCM2835

    # Low level register access Device::BCM2835::peri_read(&Device::BCM2835::BCM2835_GPIO_BASE + &De +vice::BCM2835::BCM2835_GPFSEL1); Device::BCM2835::peri_write(&Device::BCM2835::BCM2835_GPIO_BASE + &D +evice::BCM2835::BCM2835_GPFSEL2, 0xdeadbeef) Device::BCM2835::peri_set_bits(&Device::BCM2835::BCM2835_GPIO_BASE + + &Device::BCM2835::BCM2835_GPFSEL3, 0xdeadbeef, 0x1f);

    Note the GPIO_BASE. If these constants do not work either, you can resort to a little debugging. Insert print statements in both C and perl versions and verify that the constants are the same.

    Your for loop may be simplified as:

    Device::BCM2835::gpio_fsel($_, &Device::BCM2835::BCM2835_GPIO_FSEL_OUT +P) for @pins;

    Another approach, that might prove superior, is to go with Inline::C. You can embed C code in your perl to do the heavy lifting. Pack your i/o sequences (address/value pairs) in vectors on the perl side, to be passed to your C function as necessary.

      Hi and thanks, I changed the for loop for your neat shortcut so thanks.

      Re inline C, I was going to try this, but when I tried to install the modules I got several errors. Searching for the errors I found that some suggest that cpan dosent run well if at all on the pi because of the limited memory. There are alternatives (cpanm for one IIRC) but I didn't go that path yet. Because of the way that the C functions need the memory mapped addresses and I don't see a way to get these out of the perl module, I guess I would have to implment it all myself inline, which seems like reinventing the wheel a bit, which put me off. That said, I might do this as I have never used inline C with perl before and it seems like a good learning excercise.

      On the other note, I tried the example code initially, and just silently exits as it does with all values I have yet tried with peri_write. I strace it to produce this output:

      open("/dev/mem", O_RDWR|O_SYNC) = 3 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x20200) = 0xb6 +f05000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x2020c) = 0xb6 +f04000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x20101) = 0xb6 +f03000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x20100) = 0xb6 +edb000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x20204) = 0xb6 +eda000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x20205) = 0xb6 +ed9000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x20804) = 0xb6 +ed8000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x20003) = 0xb6 +ed7000 close(3) = 0 --- SIGSEGV (Segmentation fault) @ 0 (0) --- +++ killed by SIGSEGV +++
      I'm no expert in understanding the output of strace but I found (with print statements etc) that the mmaps are from the gpio_fsel statements, but theres no further info once execution hits the peri_write line unfortunately.

      I tried the print suggestion you made, but the results are maybe not of much help, unless I am not doing it right. The C program uses mmap and inputs the base address of the hw, and gets a memory location back, which changes every time the program is executed. In the peri_write function, I am sending the same base address as the C version, but theres no memory mapping going on that I can see (whether this is my problem and I need to somehow get the mmap address back out of the perl module I am not sure). The perl version as far as I can tell keeps the mmap location hidden and internal, and just asks the user to provide the hardware address which it internally translates itself, having stored the offset after making the mmap call - I believe. But am not sure.

      If you can see that I am interpreting this wrong and I have to get that mmap location myself and use it, that must be my problem. But I can't see any functions in the module which return that location.

      Thanks, Pete
Re: Device::BCM2835, hardware access on a pi, SIGSEGV
by hardburn (Abbot) on Aug 25, 2014 at 18:02 UTC

    I'd warn you that using a Linux distro on an Rpi won't be able to make anything like hard real-time guarantees. A log write in another process could hang yours for a noticeable time.

    You may want to look at Arduino or raw AVR programming instead, or possibly an RTOS on the Pi.


    "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

      Thanks - I do realise the pi & raspian is not 'real time' and I will get glitches - which I am observing, exactly as you say, when log files are written mostly. My hardware responds to pulsed signals and has a time constant of around 20 pulses before it flips state, so the idea is to smooth out the glitches with the hardware slow response. The reason to pulse the output of the micro is to do my best to make sure the hardware does not crash in a logic high state which would destroy the things its controlling within a few seconds (mainly a marine reef tank chemical addition pumps). The use of pulses which are converted in hardware to on / off controls seems to me to greatly reduce the chance of a software caused 'fail to on state' (thats the idea anyway!). My hardware requires a specific frequency for several pulses before it will turn on - higher or lower frequency will not cause the change.

      Im really interested in the PIC approach but have zero experience with it - I had to look up what a AVR is - I guess youre implying that its real time capable which is interesting. But the gear and work involved in starting out seems significant whereas the pi and linux is comfortable territory for me hence the current project. But I might spend a bit of time today looking into the most simple way to start out in that area with the most basic setup.

      Thanks, Pete

        If you're interested in AVRs, Arduino might be the best place to start. It's around $10 for a Nano version, which can do a lot, and you'll only need to add a USB cable to get started. The libraries are technically C++, but they tend to hide the complexities of the language until you get really deep into it.

        Raw AVRs are more verbose, but have the advantage that you can use a single chip for buck or two. You can get programmers for around $15-30.

        They're not necessarily hard real time systems, with a bunch of rigorous guarantees about latency. But as microcontrollers, nothing is running except your code, which is usually good enough.


        "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.