Thursday, June 09, 2016

Which GPIO pins work with Adafruit_BBIO

Working with the Beaglebone can be really frustrating.  There are a lot of buggy libraries and incomplete docs, and the Sitara chip is really complicated, overloading its pins to do lots of different things.

Today I needed 8 GPIO pins for a test harness.  I looked at the diagrams for the P8 and P9 headers and picked some likely-looking pins.  I used the Adafruit_BBIO python library and set them up as outputs, then toggled them and watched with the meter.  Three of them didn't work.

So I changed my script to try to toggle *all* the pins on P9, even the ground and VCC pins.  Of course this doesn't bother Adafruit_BBIO because it has no error checking (ugh).  Here are the pins that worked as GPIO outputs.

Note that this is from my Beaglebone *Green*.  On the Beaglebone *Black* you'd probably need to disable HDMI for some of these pins to work.

Beware: I repeated this test several times across reboot and power down, and some of the pins worked the second or third time that didn't work the first.  It's possible that's sloppiness on my part, but my intuition says no, and that Adafruit_BBIO isn't initializing the pins correctly, so there's some random chance at play.  The best reference I've found so far in how stuff actually works is "Exploring Beaglebone", which is unfortunately a non-free book, but very well written.

P8_7
P8_8
P8_9
P8_10
P8_11
P8_12
P8_13
P8_14
P8_15
P8_16
P8_17
P8_18
P8_19
P8_26
P8_27
P8_28
P8_29
P8_30
P8_31
P8_32
P8_33
P8_34
P8_35
P8_36
P8_37
P8_38
P8_39
P8_40
P8_41
P8_42
P8_43
P8_44
P8_45

P9_11
P9_12
P9_13
P9_14
P9_15
P9_16
P9_23
P9_24
P9_25
P9_26
P9_27
P9_28
P9_29
P9_30
P9_31
P9_41
P9_42


Adafruit_BBIO bugs

First bug was mine.  I was doing "import Adafruit_BBIO as GPIO" instead of "import Adafruit_BBIO.GPIO as GPIO".  That gave this error once I started trying to use it:

$ ./test.py
Traceback (most recent call last):
  File "./test.py", line 5, in <module>
    GPIO.setup("P9_11", GPIO.OUT)
AttributeError: 'module' object has no attribute 'setup'

The other bug was theirs: the code appeared to be working, but the GPIO pins weren't changing state.  Turns out there's no error checking in the library (ugh).  So it wasn't giving any errors when I failed to run the script as root.

Wednesday, February 24, 2016

Beaglebone black PWM minimum frequency

By trial and error, I just figured out that 1 billion is the longest period you can set for (at least this particular) PWM channel.

root@beaglebone:/sys/devices/ocp.3/pwm_test_P9_31.13# echo 1000000000 > period
root@beaglebone:/sys/devices/ocp.3/pwm_test_P9_31.13# echo 1000000001 > period 
bash: echo: write error: Numerical result out of range

The value is in nanoseconds, so that gives a minimum frequency of 1Hz for PWM on beaglebone black.

Also note that it won't let you set the period lower than the duty cycle setting (which we should really call the pulse width instead):

root@beaglebone:/sys/devices/ocp.3/pwm_test_P9_31.13# echo 2000 > duty
root@beaglebone:/sys/devices/ocp.3/pwm_test_P9_31.13# echo 1000 > period 
bash: echo: write error: Invalid argument
root@beaglebone:/sys/devices/ocp.3/pwm_test_P9_31.13# echo 500 > duty
root@beaglebone:/sys/devices/ocp.3/pwm_test_P9_31.13# echo 1000 > period 

Friday, February 19, 2016

Connect beaglebone black to android via USB OTG

With the right USB OTG cables, I was able to connect my Nexus 5X to a beaglebone black and a beaglebone green.  I had to try several cables before the beaglebone would power up; I suspect it's the USB-C adapter that's the most problematic.  This is the USB-C OTG cable that worked.

Once the board booted, I got a notification on my phone about the beaglebone USB storage device becoming available.  But I wanted to send data back and forth between an android app and a beaglebone process, so the network interface was the important thing to me.

When I connect the beaglebone to my PC, it shows up as a USB ethernet adapter, and I can talk to it at 192.168.7.2.  I downloaded an android app called "Terminal Emulator", and when I ran "ifconfig" I could see that I had an eth0 device with IP 192.168.7.1.  But I couldn't connect to it.

But if I turn on airplane mode, oddly, I can connect just fine by putting "192.168.7.2" in the address bar of the browser.  I haven't figured out yet whether it's possible to have LTE or Wifi on and still reach the beaglebone; perhaps it's just something to do with the IP addresses used by the Wifi or beaglebone.

Tuesday, January 26, 2016

BeagleBone DMA notes

I've been transferring data between the BeagleBone's PRUs and main memory.  If I use the PRU's SBBO instruction to store a range of PRU registers to main (DDR) memory, I find that I get around 600MB/s -- not bad!

But surprisingly, when I try to read that data back, the main CPU seems to go much slower.

I wrote some sample code for the main CPU to sum up all the bytes in a big (many MB) ordinary buffer.  I got ~300MB/sec.  Using the LDM instruction I got that up to 600MB/sec and in one case over 1GB/sec.  So in general the main CPU seems to have no trouble accessing main memory.

But when I run the same code on the buffer allocated by the uio_pruss kernel module, I get only about a tenth of that: 30MB/s, or closer to to 40MB/s when using LDM.

Kumar Abhishek from the BeagleLogic project helped me understand what was going on.  The uio_pruss module allocates that buffer using dma_alloc_coherent(), which is the standard way that linux kernel modules talk to DMA-based peripherals when they need to exchange smallish amounts of data quickly.  It tells the kernel that somebody else is going to be writing to main memory via DMA, so for this block of data, make sure we bypass the cache for every single memory access.

For larger blocks of data travelling in one direction, CPU -> peripheral or peripheral <- CPU, the Dynamic DMA mapping Guide describes that the standard approach is to kmalloc() the memory in the kernel module, then use functions like dma_map_single(), dma_unmap_single(), dma_sync_single_for_cpu(), dma_sync_single_for_device() to make sure entire buffers are safe for access by the peripheral or CPU.

That way, rather than every memory access having to bypass the cache, the kernel can make sure it's safe for the CPU to access everything in the block now that the peripheral is done with it, or vice versa.

Unfortunately, though, on the ARM A8 CPU in the beaglebone, making sure a buffer doesn't already have stale data in the cache (which can happen unexpectedly due to things like speculative preloading) requires the kernel to walk cache line by cache line through the entire buffer, taking longer than the memory transfer it's preparing for!

Kumar reports that he gets upward of 200MB/sec using this approach, dominated by the dma_* kernel calls.  I tried it myself with a simple kernel module and got a bit over 100MB/sec, so it seems plausible to me.

This thread, "dma_sync_single_for_cpu takes a really long time", is worth reading all the way through.

The only other way I can think of to get faster CPU access to big chunks of data from the PRUs is to tell the L1 and L2 caches to flush themselves, then access the data without calling the dma_sync_* functions at all.  The danger there is that it's very much tied to the specific CPU architecture and is very much not the recommended approach, so nobody's going to sympathize if you get corrupt data, and the only way to know if you've done it right is to try to test all the edge cases you can think of.

Monday, December 21, 2015

BeagleBone Access Point (and working around udhcpd)

Warning: it's easy to screw this stuff up and lose the ability to ssh into the beaglebone when you reboot.  Usually it was as simple as manually setting the IP address on my laptop, but you may not be so lucky.  You may not want to attempt this if you don't have a good handle on TCP/IP networking.

My Keebox W150NU seems to be doing a good job with a BeagleBone black as a wifi access point.  (I get about 3MB/s beaglebone -> laptop).  Beware that lots of other adapters (eg., Edimax and D-Link) work really poorly or not at all with the BeagleBone.

With a newer BeagleBone green, the W150NU was recognized out of the box, but on an older BBB with another adapter I had to update the kernel first:
Update kernel if your wifi adapter isn't detected (or if you just want to be up to date):
First I did 'sudo apt-get update ; sudo apt-get dist-upgrade'
Then I upgraded the kernel so it'd recognize the usb wifi adapter:
'cd /opt/scripts/tools ; git pull ; ./update_kernel.sh' 
On BeagleBone Green they tweaked a file to say "BBG" instead of "BBB", so I had to revert it with: 'cd /opt/scripts ; git checkout tools/eMMC/init-eMMC-flasher-v3.sh' then 'git pull' again before I could run the 'update_kernel.sh' script.
Rebooting, the W150NU appeared as wifi2.

Next, I followed the instructions here to set up hostapd.  

First, 'sudo apt-get install dnsmasq hostapd'

Here's my /etc/hostapd/hostapd.conf (beware leading and trailing spaces, or hostapd.conf will refuse to start):

### Wireless network name ###
interface=wlan0
#
### Set your bridge name ###
#bridge=br0

#driver
driver=nl80211

country_code=US

ssid=beaglebone

channel=7

hw_mode=g

# # Static WPA2 key configuration
# #1=wpa1, 2=wpa2, 3=both
wpa=2

wpa_passphrase=yourpassword

## Key management algorithms ##
wpa_key_mgmt=WPA-PSK
#
## Set cipher suites (encryption algorithms) ##
## TKIP = Temporal Key Integrity Protocol
## CCMP = AES in Counter mode with CBC-MAC
wpa_pairwise=TKIP
#rsn_pairwise=CCMP
#
## Shared Key Authentication ##
auth_algs=1
## Accept all MAC address ###
macaddr_acl=0
#enables/disables broadcasting the ssid
ignore_broadcast_ssid=0
# Needed for Windows clients
eapol_key_index_workaround=0

And don't forget to set this in /etc/defaults/hostapd:

DAEMON_CONF="/etc/hostapd/hostapd.conf"
I couldn't get dnsmasq or isc-dhcp-server to work consistently, though.  Turns out that 'netstat -nlp' showed udhcpd was binding to 0.0.0.0 on port 67 (which is a bug, since it ignores the "interface" option), so the other dhcp servers can't start.

Hint: /var/log/daemon.log is where a lot of the error messages show up.

I fixed that with 'mv /usr/sbin/udhcpd /usr/sbin/udhcpd.disabled', although it would probably have been better to 'apt-get purge udhcpd'.

Here's my /etc/dnsmasq.conf:

interface=usb0
dhcp-range=192.168.7.1,192.168.7.1,4h

interface=wlan0

dhcp-range=192.168.4.2,192.168.4.10,4h

And I also added this to /etc/network/interfaces:
auto wlan0
iface wlan0 inet static
    address 192.168.4.1
    netmask 255.255.255.0
    network 192.168.4.0
    gateway 192.168.4.1

That seems to do it, except that I have to "ifup wlan0" after startup on my BeagleBone Green.  The Black doesn't seem to need that for some reason I haven't figured out yet.

Getting BeagleBone to recognize wifi adapters by upgrading the kernel

My beaglebone black wasn't recognizing my wifi adapter.  apt-get update ; apt-get dist-upgrade didn't help, and I noticed that it wasn't upgrading the kernel.

Looks like the way to get kernel updates is to use /opt/scripts/tools/update_kernel.sh.  When I first tried it, I got errors like "The certificate of `rcn-ee.net' is not trusted".

So the first step was to "git pull" down the latest version of the update_kernel.sh script, then run it.  Upon reboot, it recognized the wifi adapter.

Also note that beaglebone doesn't always do USB hotplug right, so I made sure to reboot after plugging in the adapter.

Also, even after updating the kernel, my Edimax and D-Link adapters show up but won't associate to an access point.  The Keebox W150NU seems to be working well, though.

Update: Even with the W150NU, I had trouble connecting to public networks.  I noticed this in dmesg: "deauthenticating from by local choice (reason=3)", which led me to a page recommending that I kill wpa_supplicant, and that fixed it.