Saturday, 27 July 2013

Sampling analogue signals using the ADC on the Beaglebone Black

NOTE: THIS CODE IS OLD. TRIGGER HAS NOT BEEN IMPLEMENTED IN THE IIO DRIVER ANYMORE. FOR RUNNING THE LATEST IIO DRIVER, you have to modify generic_buffer.c and remove trigger checks.
06-Jun-2014



7 channels of the ADC are available on the expansion port of the Beaglebone Black. Accessing them can seem like a daunting task at first but it is quite simple once you get the hang of it.

The kernel that ships with the BBB as of 27 July doesn't support the stuff below. So compile one from the latest sources available at 
https://github.com/beagleboard/kernel/tree/3.8.

If you don't know how to compile the latest kernel sources, check the following links for help on how to compile the kernel.

http://beagleboard.org/linux

http://wiki.beyondlogic.org/index.php/BeagleBoneBlack_Building_Kernel#Compiling_the_BeagleBone_Black_Kernel_2

http://eewiki.net/display/linuxonarm/BeagleBone+Black


After booting the BBB, you have to configure the ADC pins and enable the Touchscreen/ADC (TSADC) module in the processor. This is done via the device tree(DT) files.

DT is a standard format since kernel 3.8 that tells the kernel what it needs to do to enable the TSADC module and configure the ADC pins.

Thanks to the great people at BB.org, you only need to do this
 
    echo BB-ADC > /sys/devices/bone_capemgr.*/slots

Check that it loaded correctly using dmesg
You can also list the current configured peripherals using

    cat /sys/devices/bone_capemgr.*/slots


Now checking the analogue value at one instant is simple
All you need to do is type the following

    cat /sys/bus/iio/iio:deviceX/in_voltageY_raw

where X is your adc device number and usually 0.
And Y is your analogue channel number.

Some people might be happy with one sample every once in a while in their applications.

However, the ADC is capable of much more. If you need to sample a signal continuously via a C/C++ application from the userspace, you are in luck.

The am335x driver is a standard IIO driver. And the IIO subsystem provides a sample test application to test the driver. Or in our case, to read from the driver.

The test application can be found in your kernel sources in the drivers/staging/iio/Documentation directory titled generic_buffer.c

Or if you want to take the easy route, check the following link for sources.

https://github.com/ZubairLK/adc-iio-continuous-sampling-userspace

There are two parts to continuous sampling.
Configuring the ADC and the buffer.
Triggering the driver to tell it when to start filling in the buffer.

The IIO subsystem supports a trigger mechanism which is separate from the main ADC driver. The ADC driver supports any standard IIO trigger. And IIO provides trigger files which enable triggering via GPIOs or sysfs files.

Here I'll explain the sysfs way of doing things.

To add a sysfs trigger file type the following

    echo 1 > /sys/bus/iio/iio_sysfs_trigger/add_trigger

this creates a trigger directory in


    /sys/bus/iio/trigger0

Note: If you dont see this directory. You don't have a supported kernel. Compile the one using the latest sources.


Running the following pulses the trigger.


    echo 1 > /sys/bus/iio/trigger0/trigger_now
 
The trigger alone is useless. We need to connect this trigger to the adc driver.

Compile generic_buffer.c from the above github repo.

Then run the file using the following parameters

    ./generic_buffer -n TI-am335x-adc -t sysfstrig1 -l 128
 
The application searches for an IIO driver named 'TI-am335x-adc'.
Connects the trigger named "sysfstrig1" to the driver.
And configures the buffer size for 128 samples.

You might have noticed that you haven't exactly specified which channel you wish to sample. That is enabled separately via sysfs.

All this might seem confusing at first. But the IIO subsystem is designed to be highly configurable and adaptable to accommodate a variety of peripherals such as accelerometers, gyros, adcs etc.

Enabling which channel to sample using the triggered buffer mechanism is as simple as

    echo 1 > /sys/bus/iio/iio:deviceX/scan_elements/in_voltageX_en 

Where X is the channel number.

If you wish to enable another channel. Go ahead. The generic_buffer application will list both channels. Just note that the buffer is common for both channels.

e.g. 128 will result in 64 samples of 2 channels.

generic_buffer.c is just a starting point for your userspace application.
To sum up, you need to run the following to read continuously from the ADC.


    echo 1 > /sys/bus/iio/devices/iio_sysfs_trigger/add_trigger
    echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage7_en
    echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage5_en
    /home/root/main -n TI-am335x-adc -t sysfstrig1 -l 128 

And then run this in another terminal to trigger the driver to start sampling

    echo 1 > /sys/bus/iio/trigger0/trigger_now 

The generic_buffer application will display the length of samples.

Enjoy hacking with the BBB.
Leave a comment if you have a question or notice a bug somewhere.

31 comments:

  1. Nice. I got it all working with the 2013.07.31 angstrom demo files (which still uses the driver name tiadc rather than TI-am335x-adc).

    Trying to cross compiling the kernel on a mac is a bad idea...

    Any suggestions on how to set the sampling rate for continuous acquisition?

    ReplyDelete
    Replies
    1. An aspect I am not looking into at the moment.
      There is no interface at the moment. Max sampling I think.

      You'll have to check TRM and manually fix registers to your required rate..

      Delete
    2. When I run generic_buffer, it reports that it can't find tiadc or TI-am335x-adc (I've tried both). I'm using the prebuilt systemd image, so I may be missing a required package. Any idea what it could be?

      Delete
    3. Things have changed since this post. I have yet to update the blog..

      Sorry for the confusion.

      Also. You need the latest kernel uImage file for these.

      Delete
    4. Hi, Fred. Do you bother to shed some light into cross compiling kernel on Mac? What was your setup and what would it be right now?

      Delete
  2. Works with BBB-eMMC-flasher-2013.08.21.img.xz.
    "modprobe iio-trig-sysfs" is needed though.

    Several typos in the post, e.g.
    /sys/bus/iio/trigger0/trigger_now
    /sys/bus/iio/iio_sysfs_trigger/add_trigger
    should be
    /sys/bus/iio/device/trigger0/trigger_now
    /sys/bus/iio/device/iio_sysfs_trigger
    /add_trigger

    ReplyDelete
    Replies
    1. Thank-you for pointing it out.
      But the interface has changed significantly since this post.

      The linux kernel folk didn't like the trigger handling this way as it wasn't how iio usually handled triggers.

      Feel free to use this code if it works for you. But I'll be updating the blog soon with the new stuff.

      Delete
  3. Is it possible to start ADC with the iio-trig-gpio (HW continuous mode) on BBB?
    Why "modprobe iio-trig-gpio" doesn't work on BBB?

    Thanks in advanve

    ReplyDelete
    Replies
    1. I haven't worked on gpio. I asked the iio mailing list and Lars Peter replied

      "You need to register a platform device for your IIO trigger, like this:

      static struct resource iio_gpio_trigger_resources[] = {
      [0] = {
      .start = IRQ_NR_FOR_YOUR_GPIO,
      .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE,
      },
      };

      static struct platform_device iio_gpio_trigger = {
      .name = "iio_interrupt_trigger",
      .num_resources = ARRAY_SIZE(iio_gpio_trigger_resources),
      .resource = iio_gpio_trigger_resources,
      };

      platform_device_register(&iio_gpio_trigger);

      Once you've done that the trigger will appear in the list of available
      triggers and you can assign it to your device."

      Delete
    2. It's seems like you did that before: "....It took a while to restructure the driver".
      I'm wondering where to insert this struct ressource for register the iio-trig-gpio platform device: I have no information about that?
      Please I need some references for me to follow it step by step?
      Thank you in advance.

      Delete
    3. I haven't done it before.
      if i remember correctly, you are looking for board-generic.c in arch/arm/mach-omap2/

      Delete
    4. I can insert this modole (insmod iio-trig-gpio.ko) from the "/lib/modules/3.8.13/drivers/staging/iio/trigger/" directory and find it in the "/sys/module/iio_trig_gpio" like installed module; but it can't be activated like device in "/sys/bus/iio/devices/ directory". This is not the same with the iio-trig-sysfs module.
      Did you notice that before? Any help will be very welcome.
      Thank you again.

      Delete
    5. Sorry I can't help out more. I have no experience. You could try emailing the IIO list.

      Delete
  4. I'm using the tiadc driver with command " -n tiadc -t sysfstrig1 -l 128" and it is able to run the application with return some adc reading value, but the application only return 60++ value to me instead 128 sample adc value.Anything go wrong or i'm doing it wrongly?

    ReplyDelete
  5. Lim Kean, You must have enabled two channels of the ADC.
    128 samples divided on 2 channels makes 64 samples each..

    ReplyDelete
  6. Using this driver and your procedure I managed to get about 4000 samples per second. For sound capturing we need at least 16ksamples/s. Is there a way to do that ?

    ReplyDelete
  7. The sampling frequency select side isn't set up in the driver. I am not sure what it has been set at in the code. Check the TRM. If I remember correctly, this is max sampling.

    also. Why do you wish to use the ADC for sound capturing?

    There is an Audio cape available. Needs a bit of a workaround to run with beaglebone black.
    And there are 10$ usb audio dongles that work..

    Check https://groups.google.com/forum/#!topic/beagleboard/8Zc9DPd7rxc

    ReplyDelete
  8. Hello-- Your code has a buffer overflow in iio_utils.h build_channel_array() which is always triggered. The problem occurs at the line
    fscanf(sysfsfp, "%u", &current->enabled);
    Even after the ci_array has been filled, the loop continues to check the remaining files, and while this happens "current" points *beyond* the end of the array. This fscanf() causes random memory to be stomped on and unpredictable behavior. I changed "current->enabled" to a local variable "current_enabled" and it worked fine then. Another way to solve the problem is end the loop after ci_array has been filled.

    ReplyDelete
    Replies
    1. Could you please post a patch on the IIO list and cc me as well?

      Thanks

      Delete
  9. I'm not a member of iio mailing list, nor do I know where iio_utils.h came from other than your sample code. I posted a commit here which resolves the problem:

    https://github.com/CBGoodBuddy/adc-iio-continuous-sampling-userspace/commit/280d5243a31d35a7282dd0575a615107289976f0

    Can you please forward as appropriate?

    ReplyDelete
    Replies
    1. Hi Craig,

      I forwarded your patch to the IIO mailing list and it has been accepted.

      The subsystem maintainer (Jonathan Cameron) was asking about your email address..

      Details here :

      http://beagleboard-gsoc13.blogspot.co.uk/2013/09/success-at-last.html?showComment=1388592362802#c8166626499201609384

      Thanks
      Zubair

      Delete
  10. sorry if this has already been covered... but ive spent a few literally sleepless nights so the brain is a little claged.. but shouldnt "echo BB-ADC > /sys/devices/bone_capemgr.*/slots" actually have a "9" rather than a "*" ? for me "*" did work till i tried adding it to my /etc/rc.local and it failed and after some more searching i found using the "9" got it working automatically (along with my w1 which is also in there)

    apologies if none of that is clear.... ill try my best to explain better in the morning

    ReplyDelete
  11. Hi Zubair,

    I need to read all 7 analog inputs of the BBB every 5 milliseconds for a real-time
    audio app. However, reading using the usual file io interface to the analog pins consumes too much cpu. (See https://groups.google.com/forum/#!category-topic/beagleboard/beaglebone-black/9NYdGWOT_Mg).

    Are the techniques discussed here still available in a current BBB build? I'm using Debian and here's the output of uname -a:

    Linux beaglebone 3.8.13-bone43 #1 SMP Wed Mar 26 14:21:39 UTC 2014 armv7l GNU/Linux

    Any pointers will be greatly appreciated since I'm in a bit of a dead end.

    Rafael.

    ReplyDelete
    Replies
    1. Clarification: I wont use the ADCs to sample audio, but control signals

      Delete
    2. Replied on the post

      https://groups.google.com/forum/#!category-topic/beagleboard/beaglebone-black/9NYdGWOT_Mg

      Delete
  12. This comment has been removed by the author.

    ReplyDelete
  13. Hi Zubair,

    In response to

    ./generic_buffer -n tiadc -t sysfstrig1 -l 128

    this is what I get:

    iio device number being used is 0
    iio trigger number being used is 0
    sysfstrig1
    Could not open /trigger/current_trigger
    Failed to write current_trigger file

    I verified that /trigger/current_trigger exists in the /iio/devices directory.

    I also tried:
    root@arm:/home/ubuntu# echo sysfstrig1 > /sys/bus/iio/devices/iio:device0/trigger/current_trigger

    root@arm:/home/ubuntu# cat /sys/bus/iio/devices/iio:device0/trigger/current_trigger
    sysfstrig1


    Do you have any thoughts?

    Thanks in advance

    ReplyDelete
    Replies
    1. This is an old post. And trigger is not implemented by the driver anymore.

      to use the latest driver, you have to modify generic_buffer.c and remove the trigger checks

      Delete
  14. When I run the generic buffer application, it says cannot find TI-am335x-adc.
    Is there any other other way I can read from the raw voltage files continuously at certain intervals of time?

    ReplyDelete
    Replies
    1. try

      ./generic_buffer -n tiadc -t sysfstrig1 -l 128

      instead of

      .generic_buffer -n TI-am335x-adc -t sysfstrig1 -l 128

      Delete
    2. it depends on kernel versions. Naming conventions changed between 3.8 and 3.12..

      The name should be in sysfs somewhere

      Delete