Are you the publisher? Claim or contact us about this channel


Embed this content in your HTML

Search

Report adult content:

click to rate:

Account: (login)

More Channels


Channel Catalog


    0 0
  • 09/02/15--15:35: libfann bugs
  • Looking for a general purpose neural network library, fann seemed reasonable.  Jury's still out, but I see a number of shortcomings:


    • I see people warning against using its builtin input/output scaling.  So make sure your training set (inputs and outputs) is all scaled to [0,1]

    • fann_create_train(...) shows up in the docs but doesn't appear to actually exist (version 2.1.0)

    • I see no examples of fann_create_train_from_callback(), and the docs are unclear, but it looks like it allocates the memory itself and then calls the callback num_data times.  So the num_data value that gets passed to the callback is 1..n, and the two pointers point directly to the elements to be filled in.
    • #include <doublefann.h> with gcc -lfann caused data corruption for me, because the internal library calls had fann_type = float while my main program had fann_type = double.  So either use #include <fann.h> with gcc -lfann  or  #include <doublefann.h> with gcc -ldoublefann!


    0 0

    I wanted a pin on my Arduino Due to switch between pulling down toward ground and turning off (going high impedance).

    I tried using pinMode(x, INPUT);, but there's a bug in the arduino libraries that sets the output high when I switch back to pinMode(x, OUTPUT).  That's no good!

    So I read up in the datasheet and found the register that enables open drain mode.  The pinout was also handy so I could see that pin 53 (according to Arduino) is port B, pin 14 according to the ARM chip.

    Here's the code snippet.

     // Sketch for Arduino Due that enables open drain mode for pin B14  
    void setup() {
    pinMode(53, OUTPUT);
    digitalWrite(53, 0);
    // Enable open drain mode on pin 14 of port B using the Multi-Driver Enable Register
    REG_PIOB_MDER = 1 << 14;
    // To switch it back to a normal output:
    // REG_PIOB_MDDR = 1 << 14;
    }
    void loop() {
    // Turn on B14 (should be about equivalent to digitalWrite(53, 1))
    REG_PIOB_SODR = 1 << 14;
    // Pause 1ms so it's easy to see on the scope
    delay(1);
    // Turn off B14
    REG_PIOB_CODR = 1 << 14;
    // Pause 2ms
    delay(2);
    }

    0 0

    Out of the box, the stock debian distro for my Beaglebone Black didn't recognize my Edimax USB wifi adapter.  But after plugging it into wired ethernet and doing an apt-get update ; apt-get dist-upgrade, it shows up just fine.

    I got it to associate with an access point once, but I got a lot of packet loss, and haven't gotten it to work since.  After an hour or two of fiddling, I'm just going to buy a different adapter.

    Update: Looks like "ifconfig wlan0 up" gets it to associate with an AP.  See http://credentiality2.blogspot.com/2015/09/getting-beaglebone-black-and-green-to.html

    0 0

    Wifi adapters: No luck out of the box with an Edimax EW-7811UN, Keebox W150NUIEEE, or dlink DWA-121.  The latter two were recognized by my Beaglebone Green, but wouldn't associate with my access point.  The Edimax showed up on my Beaglebone Black after an apt-get dist-upgrade, and associated once, but had tons of packet loss and never associated again.

    Error message from the Keebox and D-Link usb wifi adapters:

    # iwconfig wlan0 essid MyWifiName
    Error for wireless request "Set ESSID" (8B1A) :
        SET failed on device wlan0 ; Operation not permitted.

    Fix: 

    # ifconfig wlan0 up

    Then "iwconfig wlan0 essid MyWifiName" works.

    I tried this after connecting via ethernet and running apt-get update ; apt-get dist-upgrade, so I'm not sure if it'd work out of the box.



    0 0
  • 09/11/15--21:50: Beaglebone PRU GPIO example
  • Executive Summary

    If you're just trying to do ordinary GPIO on your beaglebone, this is not the page you're looking for.

    This is about how to use certain GPIO pins on the beaglebone using the two embedded 200MHz PRU microcontrollers using their super-fast Enhanced GPIO mode.  The PRUs can also be used to access other GPIO pins, but not as quickly, and I don't cover that here.

    Reading all the way through chapters 6 and 13 of Exploring BeagleBone was the best resource I found for understanding all this, but at the end of the day, here's what I had to do:

    1. sudo apt-get update && sudo apt-get dist-upgrade
    2. Create a device tree overlay, compile it, reboot, and enable it
    3. Assemble my PRU code (just 3 instructions!)
    4. Compile a tiny C program to send the code to the PRU.

    One challenge is that most of the info out there is from 2013 or 2014, when you had to install PRUSS manually.  Fortunately, that stuff all came by default on my BeagleBone Green and BeagleBone Black.  So you don't have to worry about installing am335x_pru_package to get pasm and libprussdrv!

    Turns out programming the PRU is the easy part.  The hard part is sorting out all the different ways of doing GPIO and getting the right mode enabled in the device tree.

    Choosing the Right Pins (it's harder than you think)

    This table shows which GPIO pins you can access from the PRUs using Enhanced GPIO (EGP).  The "BB Header" column shows you the physical header pin on the BeagleBone.  The R30 and R31 columns show you which pins you'll be writing or reading when you access that bit on those registers from the PRU0 or PRU1 microcontrollers.

    But it's not the whole story -- if you  have a BeagleBone Black, all of the pins for PRU1 are used by the HDMI or onboard flash (emmc2).  So to use those pins, you have to disable HDMI or onboard flash (and thereafter boot from a microSD card) first, which requires editing the uEnv.txt file and rebooting.  (BeagleBone Green doesn't come with HDMI, freeing up those pins by default.)

    These tables for headers P8 and P9 from Exploring BeagleBone highlight those reserved pins in red, and then show in the rightmost column that they're reserved for HDMI or emmc2.

    Note from the first table that even though each PRU supports 16 EGP pins, the BeagleBone headers don't expose them all.  So don't expect to do fast 16-bit parallel I/O from a PRU on your BeagleBone.

    Finally, it looks like pins 41 and 42 on P9 are yet another special case, and are overloaded with other GPIO pins somehow, and so you have to set those pins as inputs before using them from the PRU.  (I didn't try).

    Let's avoid all those special cases and pick two pins that aren't already spoken for.  We'll use pin 11 on header P8 for output.  The tables show us that P8_11 correspond to PRU 0, register 30, bit 15.  In the PRUs, R30 is a "magic" register, and writing to it lets us set the state of output pins.

    Let's also pick a pin for input, pin 16 on P8.  There the tables show us that P8_16 corresponds to PRU 0, register 31 bit 14.  R31 is the other "magic" PRU register, and reading from it reads input pins.

    Get the Pin Configuration Right!

    This is really easy to screw up.  The first thing we need to find is the multiplexer mode, which determines whether the pin will be hooked up to the PRU, HDMI port, etc.

    The modes are nicely laid out as 7 columns in the P8 and P9 tables from the book.  At first you might think that Mode6 is "PRU input" mode, since there are lots of green cells like "pru_pruX_r31_XX", and r31 is the magic PRU input register.  But P8_11 and P8_12 break this rule.  So it's better to assume the pinmux modes were assigned at random, and check carefully in the tables.  I'm glad I printed them out, since I've had to stare at them a lot.

    We want P8_11 to be our output pin, because the P8 table doesn't list it as colliding with anything important, and because it's highlighted in green, showing that it can work with PRU EGP.  Its name in that cell is pr1_pru0_pru_r30_15.  "pru0" tells us it's for PRU0.  "r30" tells us it can be used as an output, since r30 is the magic output register.  And it's in the "mode6" column, so we know we need to set that pin to mode 6 if we want to use it from the PRU0's register 30.

    We can also get that from the elinux.org table, since the "Pinmux Mode" to the right of the R30(output) column is Mode_6.

    For our input pin P8_16, when we follow the tables we see that it's also mode 6 in this case.

    But as explained in the Exploring BeagleBone book (and less clearly in table 9-60 of the TRM), we're not done yet!  There are 4 other settings that can apply to each pin:

    Bits 0..2 are the multiplexer mode (these two pins are both 6)
    Bit 3 enables (0) or disables (1) the internal pullup/pulldown resistor
    Bit 4 is 0 for pulldown, 1 for pullup
    Bit 5 is 0 to disable input, 1 to enable input.
    Bit 6 is 1 if you want slow rise/fall times (for long i2c buses)

    So for our output pin, P8_11, our configuration value is just 0x6, since none of the other bits are set.

    But for our input pin P8_16, we need to turn on bit 5, so the value is 0x26  (10000 binary ORed with 110 binary).


    Device Tree Overlays (there is NO escape!)

    I looked all over, and there doesn't seem to be a way around creating and editing device tree overlays.

    Now that we've picked what pins we want to use with the PRU, we have to use the device tree to enable the PRUs and put those pins into the right mode.

    Here's a DTS file that sets up P8_11 for output via PRU EGP, and P8_16 for input via PRU EGP.  It's much shorter than it looks because I added a lot of comments:

     // This DTS overlay sets up one input and one output pin for use by  
    // PRU0 via its Enhanced GPIO mode, which will let us access those pins
    // by writing to R30 bit 15 or reading from R31 bit 14.

    // Save this file wherever you want (but I recommend /lib/firmware), as
    // "PRU-GPIO-EXAMPLE-00A0.dts".

    // Compile with:
    // dtc -O dtb -I dts -o /lib/firmware/PRU-GPIO-EXAMPLE-00A0.dtbo -b 0 -@ PRU-GPIO-EXAMPLE-00A0.dts

    // You'll have to reboot, after which you can do this as root to activate it:
    // echo PRU-GPIO-EXAMPLE > /sys/devices/bone_capemgr.?/slots

    /dts-v1/;
    /plugin/;

    / {
    // This determines which boards can use this DTS overlay
    compatible = "ti,beaglebone", "ti,beaglebone-green", "ti,beaglebone-black";

    // I think part-number is supposed to correspond with the filename,
    // so we'd save this as "PRU-GPIO-EXAMPLE-00A0.dts".
    part-number = "PRU-GPIO-EXAMPLE";

    // This always seems to be 00A0, and all the .dtbo files in /lib/firmware
    // seem to be named foo-00A0.dtbo, but then are loaded without that suffix. So
    // for foo-00A0.dtbo we'd do 'echo foo > /sys/devices/bone_capemgr.?/slots'
    version = "00A0";

    // List the pins and resources we'll be using. This table:
    // http://elinux.org/Ti_AM33XX_PRUSSv2#Beaglebone_PRU_connections_and_modes
    // shows which pins can be used with PRU0 and PRU1 for input and output via
    // registers R31 and R30.
    // Our output pin, P8_11, corresponds to PRU 0, register 30, bit 15
    // Our input pin, P8_16, corresponds to PRU 0, register 31, bit 14
    //
    // Beware: Many other PRU EGP pins are reserved by HDMI or onboard flash, which
    // would need to be disabled first by editing uEnv.txt and rebooting.
    exclusive-use =
    "P8.11", "P8.16", "pru0";

    fragment@0 {
    target = <&am33xx_pinmux>;
    __overlay__ {
    example_pins: pinmux_pru_pru_pins {

    // The offset and mode for pins P8_11 and P8_16 also come from the table linked above.
    //
    // That table gives offset 0x34 for P8_11, and 0x38 for P8_16.
    // It also shows us we want pinmux mode 6 for P8_11 in output mode,
    // and again pinmux mode 6 for P8_16 in input mode.
    //
    // Table 9-60 in the TRM: http://www.ti.com/lit/ug/spruh73l/spruh73l.pdf
    // helps us calculate the rest of the configuration value.
    //
    // For P8_11, the other fields are all 0, so the value is just 0x06.
    //
    // For P8_16, we want it to be an input, so we also set bit 5, yielding
    // a value of 0x26. We could also set bits 3 and 4 to enable a pullup
    // or pulldown.
    pinctrl-single,pins = <
    0x34 0x06
    0x38 0x26
    >;
    };
    };
    };

    // This enables the PRU and assigns the GPIO pins to it for use in EGP mode.
    fragment@1 {
    target = <&pruss>;
    __overlay__ {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&example_pins>;
    };
    };
    };


    After saving that to /lib/firmware/PRU-GPIO-EXAMPLE-00A0.dts, I compiled it:

     root@beaglebone:/lib/firmware# dtc -O dtb -I dts -o /lib/firmware/PRU-GPIO-EXAMPLE-00A0.dtbo -b 0 -@ PRU-GPIO-EXAMPLE-00A0.dts  

    And then I had to reboot before the system would let me load it.  The "PRU-GPIO-EXAMPLE" and the  "L" in "P-O-L" shows me that the overlay loaded successfully.

     root@beaglebone:/lib/firmware# echo PRU-GPIO-EXAMPLE > /sys/devices/bone_capemgr.?/slots  
    root@beaglebone:/lib/firmware# cat /sys/devices/bone_capemgr.?/slots
    0: 54:PF---
    1: 55:PF---
    2: 56:PF---
    3: 57:PF---
    4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
    5: ff:P-O-L Override Board Name,00A0,Override Manuf,BB-UART2
    6: ff:P-O-L Override Board Name,00A0,Override Manuf,PRU-GPIO-EXAMPLE


    Writing Code (finally!)

    Now that the hard part's done, we can write some assembly code for the PRU and some C code to load it.  Here's the C code that runs on the main CPU and lets us load an arbitrary PRU .bin file into PRU 0:

     // Loads an arbitrary .bin file into PRU0 and waits for it to signal  
    // that it has finished.
    //
    // Pass in the filename of the .bin file on the command line, eg:
    // $ ./pru_loader foo.bin
    //
    // Compile with:
    // gcc -o pru_loader pru_loader.c -lprussdrv

    #include <stdio.h>
    #include <prussdrv.h>
    #include <pruss_intc_mapping.h>

    int main(int argc, char **argv) {
    if (argc != 2) {
    printf("Usage: %s pru_code.bin\n", argv[0]);
    return 1;
    }

    // If this segfaults, make sure you're executing as root.
    prussdrv_init();
    if (prussdrv_open(PRU_EVTOUT_0) == -1) {
    printf("prussdrv_open() failed\n");
    return 1;
    }

    tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
    prussdrv_pruintc_init(&pruss_intc_initdata);

    // Change to 1 to use PRU1
    int which_pru = 0;
    printf("Executing program and waiting for termination\n");
    prussdrv_exec_program(which_pru, argv[1]);

    // Wait for the PRU to let us know it's done
    prussdrv_pru_wait_event(PRU_EVTOUT_0);
    printf("All done\n");

    prussdrv_pru_disable(which_pru);
    prussdrv_exit();

    return 0;
    }


    And here, finally, is the assembly code that runs on the PRU.  "set r30, r30, 15" is all it takes to turn on our pin, and "clr r30, r30, 15" is all it takes to shut it off!

    Here's a lovely PRU instruction set quick reference, also from the book.  Print it out too.

     // Demonstrates using Enhanced GPIO (EGP), the fast way to  
    // do GPIO on certain pins with a PRU.
    //
    // Writing to r30 with PRU0 or PRU1 sets the pins given in this table:
    // http://elinux.org/Ti_AM33XX_PRUSSv2#Beaglebone_PRU_connections_and_modes
    //
    // But only if the Pinmux Mode has been set correctly with a device
    // tree overlay!
    //
    // Assemble with:
    // pasm -b pru_egp_output.p

    // Boilerplate
    .origin 0
    .entrypoint TOP

    TOP:
    // Writing bit 15 in the magic PRU GPIO output register
    // PRU0, register 30, bit 15 turns on pin 11 on BeagleBone
    // header P8.
    set r30, r30, 15

    // Uncomment to turn the pin off instead.
    //clr r30, r30, 15

    // Interrupt the host so it knows we're done
    mov r31.b0, 19 + 16

    // Don't forget to halt or the PRU will keep executing and probably
    // require rebooting the system before it'll work again!
    halt


    I assembled with "pasm -b pru_egp_output.p", loaded it with "sudo ./pru_egp_loader pru_egp_output.bin", and verified with my voltmeter that P8_11 showed 3.3v.  Then I uncommented the clr, reassembled, re-ran and verified that it dropped to 0.  Success!

    Now we're ready for the big leagues, 5 whole instructions to copy the value of the input pin to the output pin:


     // Demonstrates using Enhanced GPIO (EGP), the fast way to  
    // do GPIO on certain pins with a PRU.
    //
    // Writing to r30 or reading from r31 with PRU0 or PRU1 sets or reads the pins
    // given in this table:
    // http://elinux.org/Ti_AM33XX_PRUSSv2#Beaglebone_PRU_connections_and_modes
    //
    // But only if the Pinmux Mode has been set correctly with a device
    // tree overlay!
    //
    // Assemble with:
    // pasm -b pru_egp_io.p

    // Boilerplate
    .origin 0
    .entrypoint TOP

    TOP:
    // Reading bit 14 in the magic PRU GPIO input register 31
    // bit 14 for PRU0 reads pin 16 on BeagleBone header P8.
    // If the input bit is high, set the output bit high, and vice versa.
    QBBS HIGH, r31, 14
    QBBC LOW, r31, 14

    HIGH:
    // Writing bit 15 in the magic PRU GPIO output register
    // register 30, bit 15 for PRU0 turns on pin 11 on BeagleBone
    // header P8.
    set r30, r30, 15
    QBA DONE

    LOW:
    clr r30, r30, 15

    DONE:
    // Interrupt the host so it knows we're done
    mov r31.b0, 19 + 16

    // Don't forget to halt or the PRU will keep executing and probably
    // require rebooting the system before it'll work again!
    halt


    I tested it by installing a jumper from P9_01 (GND) or P9_03 (DC_3.3V) to P8_16.  Be sure not to connect to P9_05 or P9_06 though, since those are at 5V and could blow up your board!

    Where to now?

    If you're still with me, you probably have something fancier in mind than just turning a pin on or off.  Even though am335x_pru_package came installed on my BBB, the examples weren't there.  You can find them here on github: https://github.com/beagleboard/am335x_pru_package/tree/master/pru_sw/example_apps

    In particular, PRU_memAccessPRUDataRam was super helpful.  It doesn't use any GPIO pins, so it only requires that the PRUs are enabled (which you get if you use my overlay above).  I was trying to get some assembly code to work, and couldn't get any signal back from the PRU to know if it wasn't working or if it just couldn't toggle any GPIO pins.  I had deleted that pesky "interrupt the host" instruction at the end of my listing above, so I was running the code totally blind.  When I discovered that PRU_memAccessPRUDataRam worked fine on a clean boot, but would no longer work after running my code, I quickly realized that I had forgotten to put a HALT instruction at the end of my code.

    One other thing to explore is the new C compiler for the PRUs, PRUSS-C.  It's also installed by default on my beaglebones.  It looks pretty neat, but I haven't managed to get the code onto a PRU yet.  Something to do with .cmd scripts for hexpru, I think.

    Finally, TI also has a GUI development suite for their processors.  I was tempted to try it, but they make you create a login to myTI first, and I'd rather use command line tools anyway.

    0 0

    Here's some C and PRU assembly code I wrote to see how fast the PRU can write to system (DDR) memory.


     // Loads a .bin file into a BeagleBone PRU and then interacts with it  
    // in shared PRU memory and (system-wide) DDR memory.
    //
    // Pass in the filename of the .bin file on the command line, eg:
    // $ ./pru_loader foo.bin
    //
    // Compile with:
    // gcc -std=gnu99 -o pru_loader pru_loader.c -lprussdrv

    #include <unistd.h>
    #include <stdio.h>
    #include <inttypes.h>
    #include <prussdrv.h>
    #include <pruss_intc_mapping.h>

    int main(int argc, char **argv) {
    if (argc != 2) {
    printf("Usage: %s pru_code.bin\n", argv[0]);
    return 1;
    }

    // If this segfaults, make sure you're executing as root.
    prussdrv_init();
    if (prussdrv_open(PRU_EVTOUT_0) == -1) {
    printf("prussdrv_open() failed\n");
    return 1;
    }

    tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
    prussdrv_pruintc_init(&pruss_intc_initdata);

    // Pointer into the 8KB of shared PRU DRAM
    volatile void *shared_memory_void = NULL;
    // Useful if we're storing data there in 4-byte chunks
    volatile uint32_t *shared_memory = NULL;
    prussdrv_map_prumem(PRUSS0_SHARED_DATARAM, (void **) &shared_memory_void);
    shared_memory = (uint32_t *) shared_memory_void;

    // Pointer into the DDR RAM mapped by the uio_pruss kernel module.
    volatile void *shared_ddr = NULL;
    prussdrv_map_extmem((void **) &shared_ddr);
    unsigned int shared_ddr_len = prussdrv_extmem_size();
    unsigned int physical_address = prussdrv_get_phys_addr((void *) shared_ddr);

    printf("%u bytes of shared DDR available.\n Physical (PRU-side) address:%x\n",
    shared_ddr_len, physical_address);
    printf("Virtual (linux-side) address: %p\n\n", shared_ddr);

    // We'll use the first 8 bytes of PRU memory to tell it where the
    // shared segment of system memory is.
    shared_memory[0] = physical_address;
    shared_memory[1] = shared_ddr_len;

    // Change to 0 to use PRU0
    int which_pru = 1;
    prussdrv_exec_program(which_pru, argv[1]);

    for (int i = 0; i < 10; i++) {
    sleep(1);
    // See if it's successfully writing the physical address of each word at
    // the (virtual, from our viewpoint) address
    printf("DDR[%d] is: %p / 0x%x\n", i, ((unsigned int *)shared_ddr) + i,
    ((unsigned int *) shared_ddr)[i]);

    int passes = shared_memory[0];
    int bytes_written = passes * shared_ddr_len;
    printf("Bytes written: %d\n", bytes_written);
    }

    // Wait for the PRU to let us know it's done
    prussdrv_pru_wait_event(PRU_EVTOUT_0);
    printf("All done\n");

    prussdrv_pru_disable(which_pru);
    prussdrv_exit();

    return 0;
    }


    And here's the assembly:
     .origin 0  
    .entrypoint TOP

    #define DDR r29
    #define DDR_SIZE r28
    #define SHARED_RAM r27

    #define SHARED_RAM_ADDRESS 0x10000

    TOP:
    // Enable OCP master ports in SYSCFG register
    LBCO r0, C4, 4, 4
    CLR r0, r0, 4
    SBCO r0, C4, 4, 4

    MOV SHARED_RAM, SHARED_RAM_ADDRESS

    // From shared RAM, grab the address of the shared DDR segment
    LBBO DDR, SHARED_RAM, 0, 4
    // And the size of the segment from SHARED_RAM + 4
    LBBO DDR_SIZE, SHARED_RAM, 4, 4

    // BIGLOOP is one pass overwriting the shared DDR memory segment
    mov r12, 0
    mov r14, 10000
    BIGLOOP:

    // Start at the beginning of the segment
    MOV r10, DDR
    ADD r11, DDR, DDR_SIZE

    // Tight loop writing the physical address of each word into that word
    LOOP0:
    SBBO r10, r10, 0, 4
    ADD r10, r10, 4
    // XXX: This means r10 < r11, opposite what I expected!
    QBLT LOOP0, r11, r10

    ADD r12, r12, 1
    SBBO r12, SHARED_RAM, 0, 4
    QBGT BIGLOOP, r12, r14

    // Interrupt the host so it knows we're done
    MOV r31.b0, 19 + 16

    // Don't forget to halt!
    HALT


    Here's the output I get, about 200MB/sec:

     262144 bytes of shared DDR available.  
    Physical (PRU-side) address:9e6c0000
    Virtual (linux-side) address: 0xb6d78000

    DDR[0] is: 0xb6d78000 / 0x9e6c0000
    Bytes written: 200540160
    DDR[1] is: 0xb6d78004 / 0x9e6c0004
    Bytes written: 401342464
    DDR[2] is: 0xb6d78008 / 0x9e6c0008
    Bytes written: 601882624
    DDR[3] is: 0xb6d7800c / 0x9e6c000c
    Bytes written: 802160640
    DDR[4] is: 0xb6d78010 / 0x9e6c0010
    Bytes written: 1002176512
    DDR[5] is: 0xb6d78014 / 0x9e6c0014
    Bytes written: 1202454528
    DDR[6] is: 0xb6d78018 / 0x9e6c0018
    Bytes written: 1402470400
    DDR[7] is: 0xb6d7801c / 0x9e6c001c
    Bytes written: 1602748416
    DDR[8] is: 0xb6d78020 / 0x9e6c0020
    Bytes written: 1802764288
    DDR[9] is: 0xb6d78024 / 0x9e6c0024
    Bytes written: 2003042304
    All done



    If I crank up the number of bytes written by SBBO from 4 to 8 (in the SBBO and ADD after LOOP0), then I think it ends up writing the contents of r10 and r11 into memory, and I get 320MB/sec.  If I crank it up to 16 bytes per write, I get 450MB/sec.

    So the PRU really can write very quickly to system RAM.

    0 0

    Using a PWM channel to get square waves (don't care about duty cycle) from my BeagleBone, looks like I can get up to 50MHz with:

    root@beaglebone:~# echo 10 > /sys/devices/ocp.3/pwm_test_P9_14.12/period
    root@beaglebone:~# echo 5 > /sys/devices/ocp.3/pwm_test_P9_14.12/duty



    0 0

    I picked up some printer heads for an Okidata LED printer and checked them out under the microscope.  

    This ebay auction shows what the complete head looks like.  An LED printer is basically a laser printer, except that instead of scanning a laser beam across the page to make the toner stick to the page, an array of LEDs does the work.

    Here's the lens assembly and LED array removed from the housing:


    The lens assembly has two staggered rows of lenslets.  The head has some sort of tilt arrangement that I suspect they use to vibrate the lens assembly back and forth and then power the LEDs when the lenses are in the desired position.  (But don't quote me on that).


    Putting the LED array under the microscope, we can see where the PCB is wire bonded to the LED driver circuitry.  Normally wirebonding is used inside a chip to go from the wafer to the pins, and then the whole chip is sealed up in plastic or ceramic.  But here the tiny gold wires are exposed, making them very easy to damage (which I did when removing the board from the head).



    Below is a closeup.  The wires at the top are all going to a common trace on the upper part of the PCB.  The wires at the bottom are address/control lines going to the green/purple wafers.  At first I thought this was the LED array, but it's just the control circuitry.  That wafer is then wirebonded to the actual LED array, which just looks like a black line with dark gray squares between the top and middle rows of wires.

    So you can see they had to run a wire for each and every LED in the array, and they're too densely packed to be able to run the wires to pads on the PCB, so instead they go wafer to wafer.  Then they just need to run control lines out to the PCB so it can tell the control wafer which LEDs to turn on.








    0 0

    Looks like I get about 4.3MB/sec when writing to the onboard flash on my BeagleBone Black, and about 7.1MB/sec when writing to a 64GB Sandisk Ultra 64GB microSD card.

    On BeagleBone Green, I get 9.4MB/s to onboard flash, and 6.8MB/s to the same SanDisk microSD card.

    I used this command to test:
    $ time ( dd of=foo if=/dev/zero bs=1M count=100 ; sync )


    Ignored dd's report, and divided 100 / elapsed time as reported by the time command.

    0 0

    USB Host (big type A jack): 20MB/s writing to a Seagate USB3 2TB portable (spinning) hard disk (required plugging a 5V 4A power supply into the BeagleBone Black's power jack).  On BeagleBone Green, I got corruption with the Seagate disk, even when I powered the board from a bench supply.  With this Samsung 64GB USB flash drive I get 14-18MB/s write on both BeagleBone Green and Black.

    Disk: 4.3MB/s writing to onboard flash, and 7.1MB/s to a SanDisk Ultra 64GB microSD card.  On BeagleBone Green, I get 9.4MB/s to onboard flash, and 6.8MB/s to the same SanDisk microSD card.

    Network: Using netcat with the USB ethernet interface, I get 7.6MB/s upstream (to my laptop).  With the 100baseT jack I get 11.2MB/s upstream.  If I use ssh with its default cipher, I get about 10MB/s, but that goes back up to 11.1MB/s if I use "-c arcfour".

    Compression: gzip -1 gives me 4.1MB/s on text generated by "cat /dev/urandom data | od -x".  I tried lz4 as well and it was almost exactly the same speed.


    0 0

    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.

    0 0

    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.

    0 0
  • 01/26/16--18:07: 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.

    0 0

    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.

    0 0

    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 


    0 0
  • 06/09/16--18:02: 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.

    0 0

    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



    0 0

    Seems like every time I try to use the Xadow board, I have to spend hours getting it to program via the Arduino IDE.  This time, the solution seems to have been putting this in a file called /etc/udev/rules.d/78-xadow.rules and then rebooting:

    ATTR{idVendor}=="2886",  ENV{ID_MM_DEVICE_IGNORE}="1"



    0 0

    Update: Huh, I just discovered that this exists: Settings > developer options > networking > select USB configuration > RNDIS (USB ethernet).  But it doesn't seem to work.  So I suspect the RNDIS option there doesn't actually do anything.

    The BeagleBone comes with a neat feature: when you plug it into your computer's USB port, the BeagleBone pretends it is a USB hub connected to several things: a serial port, USB storage and an ethernet adapter.  Thus you can point your PC's browser to http://192.168.7.2 and pull up the BeagleBone's built-in webserver, login via /dev/ttyACM0, or look through the files on its boot partition.

    This all happens thanks to the g_multi Linux kernel module, which is one of several modules that allow devices with USB OTG ports to pretend to have things like storage, serial ports, ethernet and even MIDI ports.  g_multi aggregates a bunch of those together and presents them all to the host under a virtual USB hub.

    This works great when connecting the BeagleBone to my Linux workstation over USB, and it also partly works when I connect the BeagleBone to my (stock) Nexus 5X: I can attach the BeagleBone to the phone using this VicTsing OTG adapter, and the BeagleBone will power itself from the phone and let me browse its USB storage.

    I haven't had any success with the serial port, unfortunately.  I tried a bunch of different Android serial apps, and the phone seems to realize that the BeagleBone is offering a serial port, but I can't get any data between the devices.

    Ethernet: if you want to talk from your Android phone to your beaglebone via TCP/IP over the USB cable, the first thing you'll have to do is, sadly, turn on airplane mode so that cellular and wifi data are disabled.  If one of those interfaces is active, pings and HTTP requests no longer make it to the BeagleBone.

    Here's the other issue: recent phones like the Nexus 6P have a smaller set of USB Ethernet drivers enabled in the stock distro.  So with the BeagleBone in its default configuration, the Nexus 5X sees the ethernet interface just fine, but the Nexus 6P doesn't.

    Fortunately, the g_multi can pretend to be one of two different *types* of USB Ethernet device.  The default is RNDIS, which apparently is a common thing on Windows, and is one of the drivers that got removed on the Nexus 6P.  The other is CDC, which is apparently a cleaner standard that manufacturers of USB Ethernet adapters can follow (so that each manufacturer doesn't have to invent their own driver interface).

    Annoyingly, I can't find docs on g_multi's module parameters anywhere.  But I did manage to switch my BeagleBone from RNDIS to CDC ethernet with this change to /opt/scripts/boot/am335x_evm.sh.

    As you can see, I just commented out the block of code that decides how to load g_multi, and instead I load the standalone g_cdc CDC Ethernet driver.  (This means you'll no longer get the USB storage and serial interfaces):


     # Added these two lines:  
    modprobe g_cdc ${g_network}
    usb0=enable

    # And commented out this big block:
    ##g_multi: Do we have image file?
    #if [ -f ${usb_image_file} ] ; then
    # modprobe g_multi file=${usb_image_file} cdrom=0 ro=0 stall=0 removable=1 nofua=1 ${g_network} || true
    # usb0="enable"
    # ttyGS0="enable"
    #else
    # #g_multi: Do we have a non-rootfs "fat" partition?
    # unset root_drive
    # root_drive="$(cat /proc/cmdline | sed 's/ /\n/g' | grep root=UUID= | awk -F 'root=''{print $2}' || true)"
    # if [ ! "x${root_drive}" = "x" ] ; then
    # root_drive="$(/sbin/findfs ${root_drive} || true)"
    # else
    # root_drive="$(cat /proc/cmdline | sed 's/ /\n/g' | grep root= | awk -F 'root=''{print $2}' || true)"
    # fi
    #
    # if [ "x${root_drive}" = "x/dev/mmcblk0p1" ] || [ "x${root_drive}" = "x/dev/mmcblk1p1" ] ; then
    # #g_ether: Do we have udhcpd/dnsmasq?
    # if [ -f /usr/sbin/udhcpd ] || [ -f /usr/sbin/dnsmasq ] ; then
    # modprobe g_ether ${g_network} || true
    # usb0="enable"
    # else
    # #g_serial: As a last resort...
    # modprobe g_serial || true
    # ttyGS0="enable"
    # fi
    # else
    # boot_drive="${root_drive%?}1"
    # modprobe g_multi file=${boot_drive} cdrom=0 ro=0 stall=0 removable=1 nofua=1 ${g_network} || true
    # usb0="enable"
    # ttyGS0="enable"
    # fi
    #
    #fi



    Be careful not to comment too much: you still want the "if [ "x${usb0}" = "xenable" ] " block below to be active so that the network interface gets set up correctly.

    You might want to do all this while booted from a microSD card so that if you screw it up and can't access the BeagleBone over USB anymore, you can just pop out the card and fix am335x_evm.sh from your PC.

    0 0

    As I mentioned in my last post, BeagleBone uses the Linux g_multi kernel module so that when you plug it into a PC, it appears to be a USB hub with USB-serial, USB-ethernet and USB storage devices attached.

    In the other post I talk about how to plug a BeagleBone into an Android phone via USB and communicate between them using Ethernet.  Unfortunately it requires shutting off Wifi and cellular data.

    So instead, I investigated communicating via USB serial instead of USB ethernet.  It should work out of the box, but none of the dozens of Android apps I tried could find the USB Serial device the BeagleBone provided.

    Thus I used the same trick as before, replacing g_multi with a single-purpose device.  As in the last post, you'll have to comment out a big block of shell script in /opt/scripts/boot/am335x_evm.sh.

    Then, instead of adding "modprobe g_cdc ${g_network}" and "usb0=enable" to explicitly load the USB Ethernet CDC driver, add:

    modprobe g_serial
    ttyGS0="enable"

    When you reboot, you won't be able to "ssh 192.168.7.2" from your PC anymore.  Instead, you'll only get /dev/ttyACM0, and you'll need to login using a terminal emulator like minicom or screen.

    (As before, you may want to make these changes while booting from a microSD card so that you can just pop the card out if it doesn't work).

    After verifying that I could get in from my workstation, I connected my phone to the BeagleBone with an OTG adapter (careful!  Not all of them work right), then used the "Android USB Serial Monitor Lite" to connect to the BeagleBone.  (Open the "three dots" menu and select "Open Device", then send a newline and you should get a login prompt).

    0 0



    I've been having fun with this RGB LED string from Amazon with 300 5050 LEDs in a 16.4ft string.

    One bug: after using it for a few minutes, it seems to have swapped the red and green columns of colored buttons: red displays green and green displays red.  The controller is nonvolatile: it remembers settings across powerdowns.

    I set the controller to white (W button) full brightness and powered it with a bench supply.  I went as high as 13.8V since that's typical for a running car, and they know people will put them in their cars.  I briefly ran it upwards of 15V and it didn't immediately die, drawing about 3A.

    The color temperature changes with applied voltage, going from yellow at lower voltages to very blue at high voltages.

    The red LED first barely comes on at 5V 10mA.  The blue LED is last to come on, at around 9V 0.85A (so looks like you could power the string from a 9V battery) .  At 10V it draws 1.19A.  At 11.0V, it draws 1.56A.  At 12V it draws 1.96A.  At 13V it draws 2.33A, and at 13.8V it draws 2.65A.

    At 12V and max brightness with just one color lit, red draws 1.17A, green draws 0.95A and blue draws 0.87A.

    The output of the controller has 4 pins: White, Green, Red, Blue.  White connects to the positive rail (12V) and the three color channels are pulled down toward ground with PWM.  Each PWM channel supports 8 brightness levels if you've pushed one of the colored buttons.

    If you press one of the DIY buttons, though, you get 64 levels per color.

    PWM period is 4.1ms (244Hz frequency).

    Under the hood, the heavy lifting is done by a trio of 70T03GH MOSFETs.  The microcontroller has no markings.  The remaining IC is a 24C02 EEPROM.

    Here's an amusing aside: observe the wire harness in the top right of the photo below, which feeds the LED strip.  The LED strip's wires are colored white (12v), R, G, B, as expected.  But in the cable coming from the board: 12v is black instead of white, the green channel is a red wire marked B (but ever since my controller glitched, it's actually red), the red channel is a red wire marked G (but is actually green), and the blue channel is a blue wire marked R (and is actually blue).  So I bet the glitch I observed is part of a broader bug in which the controller flips around its channels, and which the manufacturer kept trying to fix by changing the order of wires in the harness.



    The IR signal from the remote is received by an integrated IR receiver that produces serial for the microcontroller.  Normally high (5V), it pulls low for about 9ms for a start bit, back to high for 4ms, then sends data bits.  The data bits look to me like an RTZ code: high 600us, then high or low for 600us (depending on the data bit), then high 600us, then low 600us.  Looks like about 24 data bits.




    0 0

    I have a cheap Stanley 24-inch level like this one:


    The other day I noticed it didn't seem to be very accurate.  I checked by flipping it end for end on my workpiece, and sure enough the bubble settled in a different spot.

    To measure how far off it is, I propped up the two ends with stacks of about 15 sheets of printer paper on my kitchen counter.  (It was important to support both ends because my counter isn't perfectly flat).  I added paper to one side until the bubble was dead center, then flipped the level end for end and had 9 sheets of paper (0.036") to one side before it was dead center again.

    atan(0.036 / 24) = 0.086 degrees.  Since the vial is crooked and I'm flipping end for end, 0.086 degrees represents the angle between it being crooked to one side and being crooked to the other side, which is double the crookedness.  So the vial in my level is crooked by 0.043 degrees, +- 1/64" over 2 feet, or +-0.00075 inches per inch.

    That doesn't sound like much, but it means that if I set two 8 foot beams indicated as "level", but flip the level around in between, they'll vary by 1/32 every 2 feet, for a total of 1/8" over the whole length.

    Stanley doesn't make any accuracy claims for my level, but their fancier "professional I-beam level" claims accuracy of only 0.0015 inches per inch, twice as bad as mine!  But then their 24-inch aluminum box-beam level claims 0.0005 inches per inch, somewhat better than my 0.00075.

    A fancier brand, Stabila, claims 1/32" over 72", which is also 0.0005 inches per inch.  The manual for their fancy digital level (which doesn't exactly inspire confidence with its poor formatting) claims 0.05 degrees within 0.1 degrees of level, and 0.2 degrees elsewhere.  So that's worse than my crappy level's 0.043 degrees even in the best case.  But then just below it they have a heading of "measuring accuracy of level" and claim 0.029 degrees, which is pretty confusing but just works out to the familiar 0.005 inches per inch.  Bottom line: the level could be off by 3x the allowed 0.0005 inches per inch, and the digital readout could still read it as dead level.

    So the bottom line seems to be that for best results, I want a spirit level that'll allow me to adjust it, and even fancy digital levels aren't guaranteed to be any better than perhaps 3/16" over 8 feet.

    (Just for my future reference, I also measured how much angle change there is between level and when the bubble just touches the first line.  That came out to 0.120" (about 1/8"), so a rise of about 1/16" per foot.)

    0 0

    The NVidia Jetson TX1 dev kit comes with a 5MP camera and a bewildering array of different libraries for accessing it.  One of the routes is using v4l2 through the /dev/video0 device.  This route only offers raw bayer data, rather than the more usual YUV-style formats.  Also it only supports V4L2_MEMORY_MMAP, not V4L2_MEMORY_USERPTR or read()ing from /dev/video0.

    If you 'sudo apt-get install v4l-utils' you can use this to capture a frame from the camera:

    v4l2-ctl --stream-mmap --stream-to=foo.raw --stream-count=1

    foo.raw is 10077696 bytes, 2 bytes for each of the 2592x1944 pixels.  If you want to write your own code to grab frames like this, this capture example gets you most of the way there, but you need to request the V4L2_PIX_FMT_SRGGB10 (raw bayer) format instead of the default.  So change init_device to be like this:

     static void init_device(void)  
    {
    struct v4l2_format format = {0};
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.width = 2592;
    format.fmt.pix.height = 1944;
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB10;
    format.fmt.pix.field = V4L2_FIELD_NONE;
    int retval = xioctl(fd, VIDIOC_S_FMT, &format);
    if (retval == -1) { perror("Setting format\n"); exit(3); }

    if (io != IO_METHOD_MMAP) {
    printf("Sorry, Jetson TX1 v4l2 only supports mmap\n");
    exit(4);
    }

    init_mmap();
    }



    0 0


    My friend likes to make wooden mallets:

    So I thought he might get a kick out of making one with a brass head.  Here's the head so far.  (he's still thinking about what to do for a handle).


    This much 1.5 inch hex bar stock cost me about $50 on ebay, enough to make two heads:

    The bar was too big to fit through the headstock on the lathe, so on the mill I clamped it sideways and faced the ends, indicated it vertical (overkill), used the edge finder to find the center, then center drilled both ends:

    I tapered a piece of scrap steel rod in the chuck to make a dead center.  That let me turn the piece "between centers", which is a lot more accurate than clamping the bar in the 3-jaw chuck.

    I was inspired by this brass hammer, but I wasn't sure if I wanted to use the same shape for the head.  I decided on a taper, and tried several different variants.  Because of the center hole, I had to discard the first half inch or so of stock, so it was a great place to experiment.  Here's a 20 degree taper:

    Collar grooves look nice but ultimately I kept it really simple:

    This profile was really tempting as well: flat / taper / flat leaves a sort of brickish look that's very hammer-like:


    In the end, a 10 degree taper at each end was all I wanted.  It produces a beautiful parabola shape and emphasizes the intrinsic beauty of brass.

    I did a pass all the way across to cut off the corners (which were a little banged up), and should have gone a little deeper since there were still a few blemishes.  I didn't notice them until I had already cut off the piece, so I couldn't put it back between centers, and I don't trust the 3-jaw chuck to hold it true, so I didn't have an easy way to clean up the corners later.

    Here you can see the dimple I put in the very center of the head to make it easy to put whatever kind of hole my friend decides to use for attaching the handle.

    I also rounded off the round edge of each face of the hammer so that it doesn't immediately mushroom when used on a flat surface.  But really, this hammer is more decorative than useful; it's several pounds, so it's not great for delicate work, and the whole point of a brass hammer is to get dinged up instead of the steel part you're trying to nudge.

    0 0

    There are lots of threads about problems compiling new kernels for their Jetson TX1 boards.  A lot of the issues seem to be related to the cross compilation environment.

    The TX1 is pretty beefy, so I figure it can compile its own kernel.  I installed an SSD to give me enough disk space, then copied the source_sync.sh script from the Jetpack installation to the device.  I had it download the 'tegra-l4t-r24.2.1' tag which is currently the latest version.

    I copied over the .config file from /usr/src/linux-headers-3.10.96-tegra/ to my kernel/ directory to start with the stock kernel config.  Then I ran make menuconfig to set the additional modules I wanted.

    Alas, when I tried to compile with make -j6 zImage, I got errors like this:

      VDSO32C arch/arm64/kernel/vdso32/vgettimeofday.o
    /bin/sh: 1: -Wp,-MD,arch/arm64/kernel/vdso32/.vgettimeofday.o.d: not found
    /mnt/ocz_ssd/leopard/kernel/arch/arm64/kernel/vdso32/Makefile:40: recipe for target 'arch/arm64/kernel/vdso32/vgettimeofday.o' failed
    make[2]: *** [arch/arm64/kernel/vdso32/vgettimeofday.o] Error 127
    scripts/Makefile.build:455: recipe for target 'arch/arm64/kernel/vdso32' failed

    Apparently it has something to do with backward compatibility to 32-bit ARM, which I don't even care about.   To get around that, I installed the 32-bit toolchain with 'sudo apt-get install gcc-arm-linux-gnueabihf', then export CROSS32CC=arm-linux-gnueabihf-gcc

    The next issue was this:





    drivers/platform/tegra/tegra21_clocks.c: In function ‘tegra21_cpu_clk_init’:
    drivers/platform/tegra/tegra21_clocks.c:1064:31: error: logical not is only applied to the left hand side of comparison [-Werror=logical-not-parentheses]
    c->state = (!is_lp_cluster() == (c->u.cpu.mode == MODE_G)) ? ON : OFF;


    That was fixed with an extra set of parentheses:

      c->state = ((!is_lp_cluster()) == (c->u.cpu.mode == MODE_G)) ? ON : OFF;


    That seems to do it.  I could then make -j6 zImage as well as Image, modules and (sudo) modules_install, then copy the zImage and Image to /boot.