Friday, October 24, 2014

BeagleBone Black: Analog Input Pins

Sources

http://en.wikipedia.org/wiki/Analog-to-digital_converter
http://www.linux.com/learn/tutorials/787511-how-to-get-analog-input-on-the-beaglebone-black
http://hipstercircuits.com/reading-analog-adc-values-on-beaglebone-black/

Introduction

An Analog-to-Digital Converter (ADC, A/D, or A to D) is a device or chip that converts a continuous physical quantity (analog signal) to a digital number that represents the quantity's amplitude.  Typically an ADC converts an analog voltage to a digital voltage.  This tutorial will go over the ADC pin properties and how to enable them on the BeagleBone Black (BBB).  

Analog to Digital Converter Layout

The BBB ADC pins are labelled as AIN pins and are located on the P9 header under the DC barrel jack.  
Figure 1:  BeagleBone Black GPIO pinout.

Table 1:  ADC pin names and numbers.
Name Header Number
AIN0 P9 39
AIN1 P9 40
AIN2 P9 37
AIN3 P9 38
AIN4 P9 35
AIN5 P9 36
AIN6 P9 33
GNDA_ADC P9 34


Analog to Digital Properties

The BBB ADC can sink an maximum of 1.8V.  You will break the pin and likely the board if you provide an ADC more than 1.8V!  Since you will likely be measuring voltages greater than 1.8V, you can use a voltage divider to cut the voltage down.  


  
Figure 2:  Voltage divider circuit and equation.

A voltage divider is just a ratio of resistor 2 (Z2) to the total resistance (Z1 + Z2).  You can think of it in percentages.  If we want to cut down from a 5V source to 1.8V, then with a couple resistors we get:

Vin     =  5V
Z1      =  1000 Ω  =  64%
Z2      =  1777 Ω  =  36% 
Z1 + Z2 = 2777 Ω  =  100%

Using the equation in Figure 2 we find Vout =  1.8V, which is 36% of Vin!

Enabling ADC Pins

To enable the ADC driver to read the pins type the either of the following into the terminal.

 echo cape-bone-iio > /sys/devices/bone_capemgr.*/slots  
or
 echo BB-ADC > /sys/devices/bone_capemgr.*/slots  

You should be able to enable the drivers on boot by appending the uEnv.txt file with

 optargs=quiet drm.debug=7 capemgr.enable_partno=BB-ADC  
or
 optargs=quiet drm.debug=7 capemgr.enable_partno=cape-bone-iio  

However, I have had no luck with this so I just echo every time the BBB gets power-cycled.

Reading ADC Pins Via Terminal

To read the ADC pins from the terminal use the cat command.  The following command will read ADC 1 (AIN1). 

 cat /sys/devices/ocp.2/helper.11/AIN1  

You should see a value between 0-1799 representing milliVolts.  If you ground the pin, the value won't likely go all the way to 0 but that is O.K.

If you are reading the pins faster than about 250ns you could run into bogus values.  The ADC can only operate so fast because it must convert from a continuous signal to a digital one with discrete time steps.

Reading ADC Pins Via C Code

The following code enables the ADC pins inside the code (so you don't have to remember to do it!), reads the 7 ADC pins and prints the values to the terminal.  

 #include <stdlib.h>  
 #include <stdio.h>  
 #include <string.h>  
 #include <unistd.h>     //close()  
 #include <fcntl.h>     //define O_WONLY and O_RDONLY  
 #define MAX_BUF 64     //This is plenty large  
 
 //Function declarations  
 int readADC(unsigned int pin);  
 
 //main program  
 int main()  
 {  
      //Enable ADC pins within code  
      system("echo BB-ADC > /sys/devices/bone_capemgr.*/slots");  
      
      //Read ADCs  
      int adc0 = readADC(0);  
      int adc1 = readADC(1);  
      int adc2 = readADC(2);  
      int adc3 = readADC(3);  
      int adc4 = readADC(4);  
      int adc5 = readADC(5);  
      int adc6 = readADC(6);  
      
      //Print ADC readings  
      printf("ADC 0: %d\n",adc0);  
      printf("ADC 1: %d\n",adc1);  
      printf("ADC 2: %d\n",adc2);  
      printf("ADC 3: %d\n",adc3);  
      printf("ADC 4: %d\n",adc4);  
      printf("ADC 5: %d\n",adc5);  
      printf("ADC 6: %d\n",adc6);  
      
      return 0;  
 }//end main  

 //Function definitions  
 int readADC(unsigned int pin)  
 {  
      int fd;          //file pointer  
      char buf[MAX_BUF];     //file buffer  
      char val[4];     //holds up to 4 digits for ADC value  
      
      //Create the file path by concatenating the ADC pin number to the end of the string  
      //Stores the file path name string into "buf"  
      snprintf(buf, sizeof(buf), "/sys/devices/ocp.2/helper.14/AIN%d", pin);     //Concatenate ADC file name  
      
      fd = open(buf, O_RDONLY);     //open ADC as read only  
      
      //Will trigger if the ADC is not enabled  
      if (fd < 0) {  
           perror("ADC - problem opening ADC");  
      }//end if  
      
      read(fd, &val, 4);     //read ADC ing val (up to 4 digits 0-1799)  
      close(fd);     //close file and stop reading  
      
      return atoi(val);     //returns an integer value (rather than ascii)  
 }//end read ADC()

To compile, copy the text to a file and save the file as ADC_Example.c.   Then in the Terminal type:

 gcc -Wall ADC_Example.c -o ADC_Example.o  

Then run the program with:

 ./ADC_Example.o  


XBEE Series 1 Configuration

Sources

http://examples.digi.com/get-started/basic-xbee-802-15-4-chat/

Introduction

In this tutorial we will be configuring an XBee to send text from one computer to another, and eventually from a microprocessor or microcomputer to a computer.  XBee is a wireless communication module that allows point-2-point communication or mesh communication.  It is a complex device that provides a simple solution to wireless communications.

XBee is an RF device.  It is essentially a radio.  Other forms of wireless communications are Bluetooth and Wifi.

There are two types of XBee modules, the Series 1 (S1) and the Series 2 (S2).  The S1 is an Xbee Wireless 802.15.4 Wireless Module while the S2 is an Xbee ZB ZigBee Module.  The two are not compatible as they use different protocols.  In this tutorial we will be dealing with the XBee S1.

Equipment and Software

Figure 1:  2 XBee S1 modules and 2 XBee USB breakout boards.

Figures 2 and 3:  Close ups of XBee S1 and XBee USB breakout board.

Figure 4:  XBee module lines up with silk screen on USB board.


If you don't already have a Virtual Com Port (VCP) driver you will need to download and install one from FTDI.  You can check if you already have a compatible VCP or if your FTDI driver installed properly by opening Terminal (on OSX and Linux.  Windows should just skip ahead and use the software provided) and typing the following command.


 ls /dev/tty*  

Figure 5:  Without module inserted into USB drive.

Figure 6:  With module inserted into USB drive.  
A new device appears for our wireless module. 

You will need a serial port viewing program.  You can use a multitude of programs but this tutorial will be using CoolTerm because it is a small and simple program

Download and open CoolTerm with the device plugged in.  Click Options.

Figure 7:  CoolTerm Serial Port settings.

Select the Port that we saw earlier in Terminal.  If using Windows, the Port will be "COMx" where x is a number, e.g. COM3, COM5.  Baudrate could range from 1200 to 115200, you may need to try a few different rates.  Data Bits should be 8, this designates the number of bits per word.  Parity should be "none" and Stop Bits should be 1.   Now click the "Terminal" tab.

Figure 8:  CoolTerm Terminal settings.  This configures the window that you type in.

Make sure "Local Echo" is checked.  This echoes what you type to the screen so you can see what you typed (and you thought computers just did this didn't you?  Nope your OS echoes keyboard inputs to the screen so you can have visual feedback).

Click "OK" then "Connect" on the top bar menu.  Now we will program the XBee.  All commands except for the start command begin with "AT" and the last two letters are abbreviations for what you are doing.  When typing "+++" do not hit "Enter", wait about 3 seconds until you see "OK" appear. If you do not see "OK" appear then try a different Baud Rate. For all following commands, hit "Enter" to submit setting.  You should see "OK" after every command.  The Xbee drops out of configuration mode after 10 seconds of none use. 

+++      -   Command to begin setting the XBee
ATID   -   Personal Area Network ID (PAN ID).  Only XBees on the same PAN ID can communication with one another.  This allows you to have many sets of XBees buzzing around in the same area.
ATMY -   32-bit address of XBee being programmed.  
ATDH  -   16-bit High byte of address of XBEE being written to.
ATDL  -   16-bit Low byte of address of XBEE being written to.  ATDH + ATDL must = ATMY
ATBD  -   Baud rate are:
    0 = 1200
    1 = 2400
    2 = 4800
    3 = 9600
    4 = 19200
    5 = 38400
    6 = 57600
    7 = 115200
ATWR -   Saves XBee settings to non-volatile memory (will not change if power-cycled).

Table 1:  AT Command Layout.
Command Parameter Description
+++ none Enters configuration mode
ATID PAN ID 0 to FFFFFFFFFFFFFFFF
ATMY My Address 0 to FFFE
ATDH Destination Address High MSB of other XBee Address
ATDL Destination Address Low LSB of other XBee Address
ATBD Baud Rate 1 through 7
ATWR none Saves settings to XBee


Table 2:  Example settings for two XBee S1 modules.
Command XBee A Parameter XBee B Parameter
+++
ATID DEAD DEAD
ATMY 1 2
ATDH 0 0
ATDL 2 1
ATBD 7 7
ATWR

Figure 9: Configuring XBee A with CoolTerm.


Figure 10: Configuring XBee B with CoolTerm.

Now that you have both your Xbees configured you can now transmit data wirelessly!  To check that the two Xbees are communicating somehow have two serial ports open connected to the XBee modules, either one one computer or two different computers.  If you open the two ports with CoolTerm you will see what you type into one window appear in the other.

Head on over to BeagleBone Black UART Tutorial to learn how to send and receive data via a serial port on the BeagleBone Black.  In theory, this is the same thing just on a microcomputer, a UART serial port instead of a USB serial port, and code instead of CoolTerm.



Wednesday, October 1, 2014

BeagleBone Black: Enabling UART

There are 4.5 UART ports that can be enabled on the BeagleBone Black.  The 0.5 occurs because there is only the transmit line (Tx) on UART3.  The process of enabling the Device Tree Overlay has been described before here:
http://beaglebone.cameon.net/home/serial-ports-uart




UART1:  [P9] Pin 24 - Tx, Pin 26 - Rx
UART2:  [P9] Pin 21 - Tx, Pin 22 - Rx
UART3:  [P9] Pin 42 - Tx (There is no Rx line)
UART4:  [P9] Pin 13 - Tx, Pin 11 - Rx
UART5:  [P8] Pin 37 - Tx, Pin 38 - Rx



BeagleBone Black: Enabling SPI0

Setting up SPI for Beaglebone Black has been described many times on other websites such as the following:
http://elinux.org/BeagleBone_Black_Enable_SPIDEV#SPI0
http://www.linux.com/learn/tutorials/746860-how-to-access-chips-over-the-spi-on-beaglebone-black

This post will follow the steps described from the previous websites and provide insight and extra information so other do not run into the same problems I did.

This post assumes the BeagleBone Black is flashed with Angstrom 3.8.13.  If not, update your board here http://beagleboard.org/getting-started#update.

 
Pin mapping for SPI ports on BeagleBone Black.
The HDMI overlay must be removed to use SPI1.







SPI0 pins
Chip Select - CS0     - pin 17
Clock          - SCLK  - pin 22
MOSI          - D0       - pin 21
MISO          - D1       - pin 18


Device Tree Overlay

First, open a new document in gedit (if using BBB standalone) or nano (if using Terminal).  Copy the following code into the new document.  The following instructions assume the user is user nano in Terminal.

 nano BB-SPI0-01-00A0.dts  

 /dts-v1/;  
 /plugin/;  
 / {  
   compatible = "ti,beaglebone", "ti,beaglebone-black";  
   /* identification */  
   part-number = "spi0pinmux";  
   fragment@0 {  
     target = <&am33xx_pinmux>;  
     __overlay__ {  
       spi0_pins_s0: spi0_pins_s0 {  
         pinctrl-single,pins = <  
          0x150 0x30 /* spi0_sclk, INPUT_PULLUP | MODE0 */  
          0x154 0x30 /* spi0_d0, INPUT_PULLUP | MODE0 */  
          0x158 0x10 /* spi0_d1, OUTPUT_PULLUP | MODE0 */  
          0x15c 0x10 /* spi0_cs0, OUTPUT_PULLUP | MODE0 */  
         >;  
       };  
     };  
   };  
   fragment@1 {  
     target = <&spi0>;  
     __overlay__ {  
        #address-cells = <1>;  
        #size-cells = <0>;  
        status = "okay";  
        pinctrl-names = "default";  
        pinctrl-0 = <&spi0_pins_s0>;  
        spidev@0 {  
          spi-max-frequency = <24000000>;  
          reg = <0>;  
          compatible = "linux,spidev";  
       };  
     };  
   };  
 };  

Save the file with Ctrl-o ("write out") and exit nano with Ctrl-x.  Now compile the Device Tree File.
dtc - Device Tree Compiler
dts - Device Tree Source
dtbo - Device Tree Binary Out

 dtc -O dtb -o BB-SPI0-01-00A0.dtbo -b 0 -@ BB-SPI0-01-00A0.dts  

Copy the compiled binary file to /lib/firmware/:

 cp BB-SPI0-01-00A0.dtbo /lib/firmware/  

Now enable the Device Tree overlay with the following:

 echo BB-SPI0-01 > /sys/devices/bone_capemgr.*/slots  

My board only has bone_capemgr.8.  Don't worry if yours is different.

Now go into the file name uEnv.txt.  The appears in the external device BEAGLEBONE if you have the board plugged into a computer.  If using an external monitor go to Computer click the BEAGLEBONE device and open uEnv.txt with gedit.

Delete any lines that may appear in uEnv.txt and add the following and save.  This change will tell the board to apply the SPI0 Device Tree Overlay we created on startup.

 optargs=quiet drm.debug=7 capemgr.enable_partno=BB-SPI0-01  

Reboot the BeagleBone Black.
Check that the overlay is enabled.  My board shows spidev1.0.

 ls -al /dev/spidev*  

Also check the pingroups:

 cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pingroups  


Now try a test with your new overlay.  Create the following file.

 nano spi_test.c  

And paste and save the following code.  (Ctrl-o to save, Ctrl-x to exit nano).  The test code is a slightly modified version of https://www.kernel.org/doc/Documentation/spi/spidev_test.c.  Some macros and struct variables were removed because of compilation errors.  There is some disparity between the spidev library on the board and the online documentation.

 /*  
  * SPI testing utility (using spidev driver)  
  *  
  * Copyright (c) 2007 MontaVista Software, Inc.  
  * Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com>  
  *  
  * This program is free software; you can redistribute it and/or modify  
  * it under the terms of the GNU General Public License as published by  
  * the Free Software Foundation; either version 2 of the License.  
  *  
  * Cross-compile with cross-gcc -I/path/to/cross-kernel/include  
  */  
 #include <stdint.h>  
 #include <unistd.h>  
 #include <stdio.h>  
 #include <stdlib.h>  
 #include <getopt.h>  
 #include <fcntl.h>  
 #include <sys/ioctl.h>  
 #include <linux/types.h>  
 #include <linux/spi/spidev.h>  
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))  
 static void pabort(const char *s)  
 {  
      perror(s);  
      abort();  
 }  
 static const char *device = "/dev/spidev1.0";  
 static uint32_t mode;  
 static uint8_t bits = 8;  
 static uint32_t speed = 500000;  
 static uint16_t delay;  
 static void transfer(int fd)  
 {  
      int ret;  
      uint8_t tx[] = {  
           0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  
           0x40, 0x00, 0x00, 0x00, 0x00, 0x95,  
           0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  
           0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  
           0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  
           0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,  
           0xF0, 0x0D,  
      };  
      uint8_t rx[ARRAY_SIZE(tx)] = {0, };  
      struct spi_ioc_transfer tr = {  
           .tx_buf = (unsigned long)tx,  
           .rx_buf = (unsigned long)rx,  
           .len = ARRAY_SIZE(tx),  
           .delay_usecs = delay,  
           .speed_hz = speed,  
           .bits_per_word = bits,  
      };  
      if (mode & SPI_TX_QUAD)  
           tr.tx_nbits = 4;  
      else if (mode & SPI_TX_DUAL)  
           tr.tx_nbits = 2;  
      if (mode & SPI_RX_QUAD)  
           tr.rx_nbits = 4;  
      else if (mode & SPI_RX_DUAL)  
           tr.rx_nbits = 2;  
      if (!(mode & SPI_LOOP)) {  
           if (mode & (SPI_TX_QUAD | SPI_TX_DUAL))  
                tr.rx_buf = 0;  
           else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL))  
                tr.tx_buf = 0;  
      }  
      ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);  
      if (ret < 1)  
           pabort("can't send spi message");  
      for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {  
           if (!(ret % 6))  
                puts("");  
           printf("%.2X ", rx[ret]);  
      }  
      puts("");  
 }  
 static void print_usage(const char *prog)  
 {  
      printf("Usage: %s [-DsbdlHOLC3]\n", prog);  
      puts(" -D --device  device to use (default /dev/spidev1.1)\n"  
         " -s --speed  max speed (Hz)\n"  
         " -d --delay  delay (usec)\n"  
         " -b --bpw   bits per word \n"  
         " -l --loop   loopback\n"  
         " -H --cpha   clock phase\n"  
         " -O --cpol   clock polarity\n"  
         " -L --lsb   least significant bit first\n"  
         " -C --cs-high chip select active high\n"  
         " -3 --3wire  SI/SO signals shared\n"  
         " -N --no-cs  no chip select\n"  
         " -R --ready  slave pulls low to pause\n"  
         " -2 --dual   dual transfer\n"  
         " -4 --quad   quad transfer\n");  
      exit(1);  
 }  
 static void parse_opts(int argc, char *argv[])  
 {  
      while (1) {  
           static const struct option lopts[] = {  
                { "device", 1, 0, 'D' },  
                { "speed",  1, 0, 's' },  
                { "delay",  1, 0, 'd' },  
                { "bpw",   1, 0, 'b' },  
                { "loop",  0, 0, 'l' },  
                { "cpha",  0, 0, 'H' },  
                { "cpol",  0, 0, 'O' },  
                { "lsb",   0, 0, 'L' },  
                { "cs-high", 0, 0, 'C' },  
                { "3wire",  0, 0, '3' },  
                { "no-cs",  0, 0, 'N' },  
                { "ready",  0, 0, 'R' },  
                { "dual",  0, 0, '2' },  
                { "quad",  0, 0, '4' },  
                { NULL, 0, 0, 0 },  
           };  
           int c;  
           c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24", lopts, NULL);  
           if (c == -1)  
                break;  
           switch (c) {  
           case 'D':  
                device = optarg;  
                break;  
           case 's':  
                speed = atoi(optarg);  
                break;  
           case 'd':  
                delay = atoi(optarg);  
                break;  
           case 'b':  
                bits = atoi(optarg);  
                break;  
           case 'l':  
                mode |= SPI_LOOP;  
                break;  
           case 'H':  
                mode |= SPI_CPHA;  
                break;  
           case 'O':  
                mode |= SPI_CPOL;  
                break;  
           case 'L':  
                mode |= SPI_LSB_FIRST;  
                break;  
           case 'C':  
                mode |= SPI_CS_HIGH;  
                break;  
           case '3':  
                mode |= SPI_3WIRE;  
                break;  
           case 'N':  
                mode |= SPI_NO_CS;  
                break;  
           case 'R':  
                mode |= SPI_READY;  
                break;  
           case '2':  
                mode |= SPI_TX_DUAL;  
                break;  
           case '4':  
                mode |= SPI_TX_QUAD;  
                break;  
           default:  
                print_usage(argv[0]);  
                break;  
           }  
      }  
      if (mode & SPI_LOOP) {  
           if (mode & SPI_TX_DUAL)  
                mode |= SPI_RX_DUAL;  
           if (mode & SPI_TX_QUAD)  
                mode |= SPI_RX_QUAD;  
      }  
 }  
 int main(int argc, char *argv[])  
 {  
      int ret = 0;  
      int fd;  
      parse_opts(argc, argv);  
      fd = open(device, O_RDWR);  
      if (fd < 0)  
           pabort("can't open device");  
      /*  
       * spi mode  
       */  
      ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);  
      if (ret == -1)  
           pabort("can't set spi mode");  
      ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);  
      if (ret == -1)  
           pabort("can't get spi mode");  
      /*  
       * bits per word  
       */  
      ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);  
      if (ret == -1)  
           pabort("can't set bits per word");  
      ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);  
      if (ret == -1)  
           pabort("can't get bits per word");  
      /*  
       * max speed hz  
       */  
      ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);  
      if (ret == -1)  
           pabort("can't set max speed hz");  
      ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);  
      if (ret == -1)  
           pabort("can't get max speed hz");  
      printf("spi mode: 0x%x\n", mode);  
      printf("bits per word: %d\n", bits);  
      printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);  
      transfer(fd);  
      close(fd);  
      return ret;  
 }  


Connect pin 21 and pin 18 on P9.  Compile and run the code.

 gcc -Wall spi_test.c -o spi_test.o  

 ./spi_test.o  
 spi mode: 0x0  
 bits per word: 8  
 max speed: 500000 Hz (500 KHz)  
 FF FF FF FF FF FF   
 40 00 00 00 00 95   
 FF FF FF FF FF FF   
 FF FF FF FF FF FF   
 FF FF FF FF FF FF   
 DE AD BE EF BA AD   
 F0 0D   

Common Problems

Poor Pin Connection

If pins 21 and 18 on P9 are not connected properly you will see:

 ./spi_test.o  
 spi mode: 0x0  
 bits per word: 8  
 max speed: 500000 Hz (500 KHz)  
 FF FF FF FF FF FF   
 FF FF FF FF FF FF  
 FF FF FF FF FF FF   
 FF FF FF FF FF FF   
 FF FF FF FF FF FF   
 FF FF FF FF FF FF   
 FF FF   


Problems with Device Tree Overlay

If the Device Tree Overlay is not enabled you will see:

 ./spi_test.o  
 spi mode: 0x0  
 bits per word: 8  
 max speed: 500000 Hz (500 KHz)  
 00 00 00 00 00 00  
 00 00 00 00 00 00  
 00 00 00 00 00 00  
 00 00 00 00 00 00  
 00 00 00 00 00 00  
 00 00 00 00 00 00   
 00 00  

I ran into this problem when I had both UART2 and SPI0 device tree overlays enabled.  The two use some common pins so of course problems will occur if the BeagleBone tries to use both.  If the uEnv.txt file looks something like:

 optargs=quiet drm.debug=7 capemgr.enable_partno=BB-SPI0-01,BB-UART2  

with both BB-SPI0-01 and BB-UART2 (order is of no consequence), then delete BB-UART2 and use a different UART port.