Beaglebone Black Analog to Digital Converter

Goal: Get the robot to drive around the room and avoid obstacles

Once the DRV8833 was hooked up to the two DC micro motors and the BBB’s PWMs, I wrote some simple C++ code to abstract movement (ie. forward(), reverse(), spinLeft(), spinRight() etc). It was now possible to write little programs to move around along a predetermined path. At this point the robot had no idea where it was, but was capable of driving forward for 2 seconds, turning left, doing that some more and making a square for example.

It’s worth noting at this point that although I later took some actual measurements as to the mechanical differences between the two motors and how they each uniquely responded to the same PWM duty cycle, during this early phase a DC of 50% more than adequately drove the robot in a “straight” line for small distances (and it was obvious from trial and error that there was a critical minimum DC at which the motors did not generate enough torque to turn the wheels when in contact with the floor).

Before I delved further into location awareness and odometry, I wanted to test out the Maxbotix MaxSonar-EZ1 ultrasonic transducer that I had bought and get some practice with the BeagleBone’s built-in ADC. The aim was to write a simple finite state machine to:

  1. Drive forward while measuring proximity of objects in front of robot using the ultrasonic transducer
  2. When a “safe” distance threshold was breached, slow down
  3. When a “danger” distance threshold was breached, enact collision avoidance (ie. reverse a bit, spin around, drive forward again)

With the PWMs and DRV8833 all the driving component was taken care of, the major work was in interfacing with the MaxSonar-EZ1.

MaxSonar-EZ1

For hobby work this is one of the kings of ultrasonic sensing (and the price tag reflects it!). In the past I’ve worked with bare 40kHz transducers and built 1573_HRLV-EZ Ultrasonic Range Finderan amplifier and wave generator for them, along with interfacing them back into timers for distance measurement, but a package like the EZ1 takes care of all of this for you.

The EZ1 has a typical 30cm deadzone, but outside of that has excellent resolution (they claim 1mm), a 5m range (I’ve only tested it up to about 3m in my apartment, but it gives linear readings all the way out to that distance) and supports multiple interfaces including analog voltage output (although this decreases the object detection resolution to 5mm), serial over TTL, serial over RS232 and PWM.

I really haven’t explored all the options this thing has to offer because it was so simple to get up and running, but with the other output options and the ability to start and stop its sensing, it can be used a lot more effectively than I’m currently using it here. The datasheet is easy reading and the Quick Start Guide is useful for playing around with it on the bench.

Using the BBB ADC

The MaxSonar-EZ1 supports multiple output methods but for my testing I wanted to use the analog output so I could play around with the BBB’s ADC. The EZ1 can run on anything from 2.5V – 5.5V DC and the analog output voltage it emits to represent distance obviously scales within 0V and the nominated supply voltage. In my case I am powering it from the 3.3V output on the BBB itself (available on pin 3 of the P9 header).

The BBB has a 7 channel ADC with 12-bit resolution. The most important thing to note is that the ADC inputs are 1.8V logic: do not exceed this or you’ll fry it. Because we’re supplying the EZ1 with 3.3V, its analog output (on pin 5 of the EZ1 board) must be put through a voltage divider so the BBB ADC only sees up to 1.8V.

I’m using a simple 50/50 divider built from two 200ohm 0.1% error tolerance resistors, the output of the voltage divider is being fed into pin 33 on P9, which corresponds to AIN4 (the 5th ADC input). It’s important to use low error tolerance resistors because we’re trying to measure a voltage that varies between 0 – 1.8V with a 12-bit resolution (4096 possible values). Technically this gives the ADC the ability to differentiate down to the sub-mV level (ie. one quantisation step represents around 0.44mV: if the resistors skew the real output from the EZ1 then you can lose noticeable accuracy.

The ADC also needs a GND reference connected (pin 34 of the P9 header), I simply connect this to pin 2 of the P9 header which is connected to the BBB ground.

Like with the PWM, on the version of Angstrom that my BBB came with there is a handy module that exposes the readings from the ADC into userland virtual files, called cape-bone-iio. To enable the ADCs the virtual cape itself must first be loaded (as per the PWM the # in the capemgr directory represents a BBB-specific value, your mileage may vary):

# echo cape-bone-iio > /sys/devices/bone_capemgr.#/slots

Each ADC now has its own virtual file that can be read which will return a value between 0 and 1800: the module takes care of converting the 12-bit resolution into an actual mV reading. You’ll see the OCP reference around quite a bit, I believe it stands for “On Chip Peripherals” – all the good stuff that’s baked directly into the AM3359 processor.

# cat /sys/devices/ocp.#/helper.#/AIN4
2049

The MaxSonar-EZ1’s output voltage can be converted into distance using the formula:

Vcc / 512 = mV_per_inch
distance_inches = mv_from_ADC / mv_per_inch

Because I like working in centimeters and the voltage scale the BBB ADC sees is half that of the actual EZ1 Vcc (because of the voltage divider):

distance_cm = (mv_from_ADC * 512 * 2.54) / 1800

Finally, the readings you get from the ADC might bounce around a bit (ie. try putting something solid a fixed distance in front of the uS sensor and then reading the ADC 5 times in a row, once per second). To get around this I sample the ADC several times (32) with a fixed delay between each sample (1ms: the ADC has a sample time of 125nS so this is plenty enough), then I order the samples and select the median. This is a really inefficient way of doing median filtering, there is a paper about much better alternatives here.

Hipster Circuits, the builders of the incredibly cool Replicape 3D printer cape for Beaglebone, note on their BBB ADC post that newer kernels have changed the way that the ADC is accessed and that cape-bone-iio no longer is used.

 Circuit Diagram

Screen Shot 2014-04-01 at 9.16.05 PM

 

Advertisements

Controlling motors with the Beaglebone Black

My first goal was to get the Beaglebone using the DRV8833 to drive the two micro motors forward: that seemed like an achievable start (if that wasn’t possible this was going to be a pretty short-lived hobby). It should be easy right? Build a little platform, attach the wheels, apply some voltage, watch it go.

No shell for you. One Year. SSH Host Key Corruption

Early attempts at this were thwarted by the fact I couldn’t SSH into my BBB. The Beaglebone runs an SSH daemon called Dropbear and on first boot Dropbear creates the board’s public/private keypair. If this fails (ie. you accidentally power the board off halfway through writing the files to the BBB’s flash) then you can’t SSH into the BBB. I suspect that this could be happening during factory testing rather than the end user’s first boot: the board is powered on long enough to check for basic operation, but then yanked off the production line and boxed. Other people have experienced this issue and there are some proposed solutions floating around but in the long run I just booted up a second BBB I had and used that: I’m still getting around to reflashing the old one.

BeagleBone Black Pulse Width Modulation0J3867.600

Once I had a functioning BBB the first order of business was interfacing it with the DRV8833. This is an awesome little chip from Texas Instruments that packs two H-bridges capable of driving up to 1.2A each into a tiny space: the last time I built an H-bridge it was about the size of a stamp, these are smaller than a fingernail. The Pololu carrier neatly exposes the chip’s pins and adds some additional components required for its basic operation.

The DRV8833 doesn’t need much to work: simply supply DC power (anything from 2.7V – 10.8V) on one side for the motors and four control signals on the other (the input pins are 3V and 5V compatible, which is great, because the BBB has 3.3V I/O). Each motor (called A and B) has two control signals (AIN1/AIN2 and BIN1/BIN2). Applying a HIGH signal to AIN1 makes motor A spin forwards, applying it to AIN2 makes it spin backwards and so on. See the datasheet for the gory details.

Once the motors are connected and some power applied, a simple test at this point is applying a HIGH signal to the each of the input pins (they’re internally pulled low) and checking that the motors spin in the desired direction. For the robot however, we need more control. We have the need for (variable) speed. Enter Pulse Width Modulation.

When applying a constant signal to xIN1 or xIN2 on the DRV8833 you’re telling the H-Bridge to dadc74b4230supply steady current to the motor (in one of two polarities depending on which control signal is HIGH). This drives the motor at whatever speed it will run for the given input voltage, full speed ahead.

In order to vary the speed we can pulse width modulate the control signal to the motor. A pulse width modulated signal has a specific frequency but a varying duty cycle. That is to say that the rising edges of each cycle occur the same wavelength apart (constant frequency) but the duration of the cycle where the signal is HIGH vary: we are modulating the width of the pulse. The longer that each pulse stays HIGH for, the more time the DRV8833 will deliver voltage/current to the motor and the faster it will run.

If your embedded device has any form of digital I/O then it’s possible to write your own PWM routine in software (assuming you have a reasonably stable clock source/timing) but luckily for us, the BBB has a heap of onboard peripherals, including the “Enhanced High Resolution Pulse Width Modulator” (eHRPWM).

We can use this to PWM the control signal to DRV8833’s input pins and control the motor speed. If we apply our control signal to AIN1 we can vary the forward speed of motor A, if we apply it to AIN2 we can vary the reverse speed of motor A.

Device Tree

Before delving into how the eHRPWM can be used however, a brief historical tour of the entire known universe must be undertaken to understand why Device Tree and Device Tree Overlays exist and why they are important when it comes to PWM (and almost any other hardware) on the BBB.

  1. Linus Torvalds starts writing Linux in 1991, initially the kernel is very x86-centric
  2. Linux kernel is ported to a few other CPU architectures such as Alpha and SPARC around 1995
  3. Linux begins being touted as a serious alternative to commercial UNIXes and desktop platforms around 1998-2000
  4. Custom machines/boards built on SoC concepts increase exponentially, people start porting Linux kernel to an increasing number of hardware platforms
  5. Kernel requires lots of board-specific code to describe the specific hardware on a board, many boards need their own kernel image
  6. Everyone gets sick of having to manage so many board-specific differences being introduced into the kernel
  7. Linux kernel introduces support for Device Tree, a data structure that describes the hardware in a system (ie. where to find it, how to interface with it) to the kernel

x86 PCs have been around a long time and despite supporting hardware and software from a huge variety of sources, at their heart there is a lot of standardisation other than just the x86 instruction set. One area of (relative) standardisation is the BIOS and concepts such as ACPI which allow the kernel to learn about and interface with the underlying hardware (ie. decode keystrokes from a keyboard, write a character to a display, discover devices on a bus etc). One of the kernel’s first tasks as it boots the machine is to discover and configure the hardware: how many CPUs are available? How much RAM is there? What peripheral devices are available that it may have device drivers for?

The BIOS and things like ACPI are used to do this on IBM PCs but for custom boards using SoCs which may contain many custom peripherals, there isn’t necessarily a lot of standardisation. One board might have a PWM, one might have some GPIOs, many have both, but almost all of them expose the registers, memory mappings and control interfaces to these devices in different ways. Before Device Tree, custom code had to be written to describe a SoC implementation to the kernel and this was generally compiled into the kernel image itself.

Device Tree provides a standard data structure to describe system hardware to the kernel and better yet, allows this data structure to be passed to the kernel at boot time, rather than compiled into the kernel image. Now it is possible to have one kernel image that via DT support and kernel modules can support many different board configurations, much as many Linux-on-PC users enjoy. You can learn more (yay!) about the rationale behind Device Tree here and here.

Device Tree Overlays and BeagleBone Capes

Device Tree support helped clean up the kernel for SoC board developers, but hardware like the Beaglebone Black takes the requirements one step further.GPSGPRS

Although there are many useful peripherals on the BBB, one of its major features is its extensibility via capes, which are hardware boards that fit over the top of the BBB (like a… cape… on a Beagle). A user plugs a cape (or many, they’re stackable) onto the BBB and when the board boots the kernel magically discovers the cape, configures its hardware (ie. an LCD screen) and makes it available to the rest of the OS. (By magically I mean the BBB reads an EEPROM on the cape that describes the cape and then loads the right Device Tree Overlay into the kernel to configure the hardware).

Hang on. Device Tree Overlay? “We have to learn MORE to get the motors running?” I hear you say. Just a little. Look, it’s not much more, the scroller is almost half-way down the page already. A Device Tree Overlay is a fragment of a complete device tree that can be loaded at runtime, after boot. It allows the basic hardware definition (the BBB’s hardware) to be extended (by that of the cape). The Capemgr is some kernel code that was created to allow the right overlay to be dynamically loaded (from disk/flash) based on the capes that are installed.

  1. BBB boots, kernel uses BBB device tree to find all the BBB’s on-board hardware and configure it
  2. BBB probes for EEPROMs on any expansion capes accessible via the P8/P9 expansion headers
  3. Based on data in EEPROM, Capemgr loads the correct DTO to further configure the hardware

It’s sort of like a device driver, or a companion to a device driver if you will: it describes the hardware that a device driver will ultimately control, and like a device driver, it can be loaded dynamically when it’s needed.

BeagleBone Black Built-In Hardware and Virtual Capes

While expansion capes provide some cool extra functionality, the BBB has a heap of onboard hardware (Ethernet, HDMI etc) with a lot of it built directly into the XAM3359 ARM processor. The System Reference Manual is a good place to start to fully understand the capabilities available – pg 53 has a good block diagram of the processor’s features. You probably want to steer clear of the actual AM3359 Technical Reference Manual for the time being: it’s over 4,000 pages – probably not bad if you’re suffering insomnia though.

The problem is that it has so many internal peripherals that there aren’t enough pins on the SOC package, let alone expansion header pins (the BBB has two expansion headers, P8 and P9), to give each one a unique physical interface. For example, pin 21 on the P9 header can be used as a GPIO, a PWM output, for the I2C bus, SPI bus, a UART and more, but obviously not all at the same time.

The processor internally multiplexes the functionality of its various peripherals onto its external pins (many of which are then routed onto the BBB’s P8 and P9 expansion headers for end-user consumption). When we want to use a peripheral (ie. the PWM) we use the Capemgr to load a Device Tree Overlay (DTO) that tells the kernel (and ultimately the processor) to configure the peripheral and also the multiplexer so that the peripheral becomes available on the relevant pin.

There are quite a few DTOs that ship with the default Angstrom Linux distribution on the BBB that configure and expose important peripherals and functions such as HDMI output, the PWM and ADC onto various P8/P9 pins. These are sometimes known as virtual capes: they’re not physical capes that extend the BBB, but virtual ones that reconfigure the on-board hardware in a useful way.

At this stage (in fact, well before now) it’s worth pointing out one of the most useful Beaglebone resources around, the blog and videos of Dr Derek Molloy, Senior Lecturer in the School of Electronic Engineering at Dublin City University. On his pages you’ll find many really informative videos about using the BBB and he maintains two important PDFs: a map of P8 expansion header pin modes and a map of P9 expansion header pin modes. These PDFs can be used to write your own DTOs that configure the BBB’s processor to multiplex the functionality you need out to the right pins. We’ll be doing this a bit later on…

Using the BBB eHDPWM

Luckily, with the version of Angstrom that ships with the BBB (at the moment) you won’t need to write or compile a thing, you can do everything from user-space. There is already a handy kernel module and DTO that allows you to easily interface with the PWMs.

Further information is available here, but to summarise:

  • Tell Capemgr to load the AM33xx PWM DTO (this enables the PWMs). The Capemgr is interfaced with via reading and writing to the slots virtual file, the exact directory will change depending on your Beaglebone, the # in mine is 9.
# echo am33xx_pwm > /sys/devices/bone_capemgr.#/slots
  • Expose a PWM to a specific expansion header pin. In the example below pin 13 on the P8 header will become a PWM output. From Derek Molloy’s PDFs we can see that P8_13 could also be configured as a GPIO: instead, we’re using it as a PWM.
# echo bone_pwm_P8_13 > /sys/devices/bone_capemgr.#/slots
  • Configure the PWM’s frequency, polarity and duty cycle. The # represents a number that can vary between BBBs (but not, for me, between boots, thankfully). The period and duty are specified in nanoseconds. In the example below 500000ns represents a frequency of 2kHZ and the 250000 gives us a 50% DC. Advice varies on the frequency you should use for motor PWM, this works for me, some people suggest 20kHz upwards.
# echo 500000 > /sys/devices/ocp.#/pwm_test_P8_13.#/period
# echo 250000 > /sys/devices/ocp.#/pwm_test_P8_13.#/duty
# echo 0 > /sys/devices/ocp.#/pwm_test_P8_13.#/polarity
  • Connect the expansion header pin (pin 13 on P8 in the above examples) to the DRV8833 input (ie. AIN1 or AIN2)
  • Watch the motor spin

Fast and Slow Decay

oroboto2DC motors are inductive by nature: when current is removed from them their inductance will try to sustain the current and that residual current (called recirculation current) has to have somewhere to flow. When pulse width modulating the H-Bridge inputs, the current is being applied and stopped continually so dealing with the recirculation current is a constant task for the H-Bridge.

The DRV8833 allows for two options when it comes to dealing with the recirculation current: it can allow it to flow back through the diodes that form the H-Bridge itself (called fast decay) or it can short the motor windings (called slow decay). I don’t claim to understand why, but from some reading I did people suggested that slow decay is best when dealing with DC motors.

The decay mode used is determined by the state of the “other” input signal (ie. if PWM is applied to AIN1, the state of AIN2 will determine whether or not the DRV8833 uses fast or slow decay). See the data sheet for the truth table.

End Result

I wish I had a video of this first endeavour but unfortunately all I have is a photo I took. The bot here is a bit more advanced than what is described in this post (ie. ignore the ultrasonic transducer) but essentially it is simply the BBB mounted on top of the battery case, the micro motors stuck to the case with double sided tape and a ball caster. Broom broom.

Circuit Diagram and a Note on Power

Screen Shot 2014-03-31 at 8.16.19 PM

The BBB itself draws quite a lot of current, anywhere from 210-460mA on its own without any additional hardware like the motors. For initial work I powered everything off a bench supply (well, a wall-wart I found that did 5V at 700mA or similar) but dragging a cord around isn’t very robot-like, so I quickly put together a small regulator circuit to power it from batteries.

Technically the LM7805 requires 7.3V for a 5V output, at the moment I’m using 4 NiMH rechargeables (1.2V per cell) and a 1.5V AA, this gives 6.3V and it works just fine, even with the motors – I don’t know, maybe the LM7805 doesn’t do as good a job with less than 7.3V but it seems fine. As soon as I get upgrade the battery shell the robot sits on I’ll just migrate to 6 NiMH cells.

 

All you really need to know for the moment is that the universe is a lot more complicated than you might think, even if you start from a position of thinking it’s pretty damn complicated in the first place.

It turns out that the same applies for robotics. What started as a (in hindsight, ambitious) simple idea six months ago has led me (often blindly) through the winding alleys and backwaters of mathematics, kinematics, electronics and programming.

The Idea

Build a differential steering robot with with sufficient sensor capability to generate a map of a room (or, ideally, a single floor house) that can then instruct a smaller “blind” (or less capable) robot how to move about that space (ie. avoiding obstacles). Eventually the more complex robot could become an “advance party” sent into an unknown situation to survey it and then provide detailed navigation information to a swarm of simpler, cheaper actors.

The Execution

I wanted to start playing around either with a Beaglebone Black or a Raspberry Pi and after some research decided the BBB was going to be more flexible (although it turns out that flexibility doesn’t necessarily make things simpler, often it made things more complicated) and I liked the idea of having such a powerful platform to use as a motherboard (with future visions of OpenCV etc). With a 1GHz ARM A8, 512MB RAM, 2GB flash and as many peripherals and buses as you could possibly cram on a board, it’s pretty sweet.

I also wanted to home cook as much of it as possible, so rather than using existing BBB capes or fully fledged robot kits I went with buying lots of individual components to do it myself (some of these however are pretty black box already though, for example the DRV8833, which is amazingly compact for the power it can put out, and the Maxbotix MaxSonar-EZ1, which does a heap of signal cleanup gratis).

My initial BOM looked something like this:

  1. 1x BeagleBone Black ($51.90)
  2. 1x Pololu TI DRV8833 carrier ($7.70)
  3. 2x 100:1 micro metal gearmotor ($33.90)
  4. 1x Pololu ball caster with 1/2″ ball ($4.95)
  5. 1x Battery holder ($2.20)
  6. 1x Maxbotix HRLV-MaxSonar-EZ1 ultrasonic sensor ($38.50)
  7. 1x Pololu 32x7mm wheels with silicone tyres ($6.05)

For a total of AUD$145.20 – it turns out I’m not going to be giving hundreds of these away for Christmas any time soon. Everything except the BBB came from Robot Gear who were super fast on the delivery and really helpful.

I realised that I was going to need more kit in the future, especially in the wheel encoder department, but my initial aim was just to get something up and running that could drive around and not bump into things. Seems simple enough, right?

“Oh… You’re using a Beaglebone!”