Search Microcontrollers

Sunday, October 28, 2012

MSP430G2 - the ADC10 peripheral /1

In the previous post I illustrated a few basic concepts about Analog to Digital Converters.
Having them integrated into MCUs makes it really easier to use them, we are going to play a bit with the MSP430G2553 ADC10 converter.

The two main control registers to drive the ADC10 are ADC10CTL0 and ADC10CTL1.
Using them we can set the reference voltages, which channel to be sampled, we can turn on and off the reference voltage and the peripheral itself.

The MSP430G2 family is designed to be a low power solution, this is why the peripherals and features that may potentially use some energy are turned off by default.

Finally the sampled result can be easily recovered from the ADC10MEM register :
int mySensor = ADC10MEM;

Sample & Hold and conversion

During the sampling process, I explained in the previous post, a reference voltage is V(t) is compared with the voltage coming from the signal.
If the signal fluctuates in this process, it might be impossible to compare it with the reference.
For this reason a "sample and hold" functionality is implemented, you can see some details here.

This means that the value of the signal to be sampled is maintained constant during the conversion.

    from wikipedia

This time is defined by the SHS (Sample and Hold Source) clock which, in the MSP430 can be one of the following :

00   = ADC10SC  (ADC10 internal oscillator, about 5MHz and can be  divided from 1 to 8)
01   =  Timer_A.OUT1
10   = Timer_A.OUT0
11   = Timer_A.OUT2

In this way we basically define the maximum sampling rate which is the result of the sampling time, being the sum of the Sample and Hold phase plus the conversion phase.

from the TI MSP430 User Guide

The S&H phase is software selectable  (in ADC10CTL0) in 4,8,16 or 64 ADC10CLK cycles, a higher value would be used to reduce high frequency noise.
The conversion part is then executed in 13 ADC10CLK cycles (thanks to a series of approximations performed internally).

Playing a bit with numbers, we can verify that, at a minimum we need 4 + 13 = 17 cycles.
Assuming the ADC10CLK is the 5MHz (note : we should not expect it to be EXACTLY 5MHz, it's a digital oscillator and it might have some tolerance) Internal source

1 cycle = 1 / 5.000.000 s = 200ns

The sampling time is then 17 * 200ns = 3.5us
The sampling time is 1 / 3.5us = roughly 294 Ksps (Kilo Samples Per Second)
Data then needs about 1 or 2 MCLK cycles to be made available in the ADC10MEM register (it uses an internal DMA), so the actual rate is slightly lower and it depends on the CPU frequency.

The data sheet claims a sampling rate exceeding 200 Ksps, which is in line with what we calculated and I believe is pretty good for a 16MHz mcu.

Reference Voltages

The ADC will return 0 if the signal is lower than or equal to the Vref- voltage and will return 1023 if it is higher than or equal to Vref+.

The ADC10 has different options when it comes to Reference voltages, it can use internal precisely  generated voltages (1.5V or 2.5V) , it can use Vcc as Vref+ and Vss (Ground) as Vref- or it can get them from two external pins.
All these parameters are controlled via the ADC10CTL0 register.

In the user guide we find :

SREFx Bits 15-13 Select reference.
SREF_0 000 VR+ = VCC and VR- = VSS
SREF_1 001 VR+ = VREF+ and VR- = VSS
SREF_2 010 VR+ = VeREF+ and VR- = VSS. Devices with VeREF+ only.
SREF_3 011 VR+ = Buffered VeREF+ and VR- = VSS. Devices with VeREF+ pin only.
SREF_4 100 VR+ = VCC and VR- = VREF-/ VeREF-. Devices with VeREF- pin only.
SREF_5 101 VR+ = VREF+ and VR- = VREF-/ VeREF-. Devices with VeREF+/- pins only.
SREF_6 110 VR+ = VeREF+ and VR- = VREF-/ VeREF-. Devices with VeREF+/- pins only.
SREF_7 111 VR+ = Buffered VeREF+ and VR- = VREF-/ VeREF-. Devices with VeREF+/- pins only.

The constants SREF_0 to SREF_7 are conveniently defined in the standard .h file.
In the same file we also find the other constants needed to enable the internal reference and to set it to 1.5 or 2.5 V

REF2_5V  Reference-generator voltage. REFON must also be set.
0 1.5 V
1 2.5 V
REFON Reference generator on
0 Reference off
1 Reference on

There anyways restrictions on the voltages that can be applied to the analog input (should be between 0 and Vcc) and to the eventual external references :
Vref- (specifically for the 2553, check the DS for other devices) should be between 0 and 1.2V
Vref+ (same considerations) should be between 1.4V and Vcc

This said, if you are planning to sample a higher voltage (I tried with the mains ; 250V AC) you need to use a voltage divider (with offset regulation for AC signals) and to make sure the output of your divider is always in the expected range (check with a scope if you can).

If you don't need to sample with a high frequency and you are using an internal reference, what you may want to do is to turn on and off the internal Vref to reduce power consumption.
There is also an option to burst" it so it turns off and on as needed by the ADC, however you should be aware of the fact that the internal Vref  takes a small amount of time to settle, and the sampling can happen only when it is stable.
tref (the time Vref internal needs to settle at 99.9% of its value) is 30us.

Finally, the peripheral can fire an interrupt once the conversion is done, so it's common to start the process, enter in Low Power Mode and then wake up receiving the interrupt, get the value from ADC10MEM, do whatever needed with it and start the process again.

In the next post I will present some sample code using the ADC.

Analog to Digital Converters - 1

Analog to Digital converters are, in my opinion, one of the most fun peripheral to deal with when working with Microcontrollers.
Let's face it : most of the measurements you want to do in the real world involve an analog reading, normally a voltage or a resistance returned by some kind of sensor.

Like most of the mcus, the MSP430G2 has an ADC built in (warning : the ADC10 is not included in all the devices of the family, check the datasheet or the specs for the specific device you are dealing with).
I am going to use the MSP430G2553 supplied with the launchpad and that one definitely has an ADC10 onboard.

First of all, it's called ADC10 because it has 10 bits of resolution.
10 Bits is the typical resolution you can find in "cheap"devices, more advanced ones (such as the C2000 or Cortex M3) may have a 12bit adc instead.

What's the difference?
It's about resolution : a 12 bit device, having 2 bits more, achieves a four (2^2) times higher resolution than a 10 bit one.
It's a big difference, however in many (most?) cases it does not really matter.

Let's imagine you are sampling a temperature from a sensor such as the LM35DZ (cheap and common sensor).
This sensor increases it's output by 10mV for each Centigrade, at 0C it will provide 0V (which makes it really simple to use it with mcus).
However this sensor does not provide high accuracy since it's best accuracy is about +/- 0.5C, which can be translated into +/- 5mV.

A 10 bit adc with range between 0 and 3.3V has a resolution of 3.3 / 1024 V/bit =  3mV/bit which is already higher than the accuracy of the sensor itself, therefore a higher adc resolution would be useless.
However this setup would allow us to measure temperatures between 0 and 33C, which it might be ok for some applications, but not enough for others.
The LM35 sensor itself can read temperatures from -55C to 150C (depending of the exact model, check the datasheet) but this would generate voltages from -5.5V up to 15V.
If you apply such ranges to a 3.3V tolerant ADC, you are likely to burn it, so most people normally regard this a as a bad idea.
You can offset and "scale" the signal using a voltage divider (with 3 resistors in case you need to add the offset), but at that point, should you still aim for +/- 0.5C resolution, the 10 bits are not enough anymore.

The example with the LM35 was useful to add some context, but the issue is generic.
Imagine we have a sensor that measures a quantity X and outputs linearly a voltage V(X) = V0+kX.
The constant k is the voltage gain of the sensor, it is a constant if the sensor output is linear.
The range we want to measure is [Xmin,Xmax], which gives us DX = Xmax-Xmin.
Assuming we can exactly map Xmin to the lower reference of the ADC (i.e. 0V) and Xmax to the higher reference (i.e. 3.3V), and we know we want to obtain a specific resolution R, then we can calculate how many bits (minimum) we need to sample the signal.

Say DX = 100X and the needed resolution R is 0.01X, this means that the ADC must have 100X/0.01X = 10.000 "steps".
To get the minimum number of bits you can apply a log2(10.000) or simply compare with the resolution of finite number of bits :
8 bits -> 256
10 bits -> 1.024
12 bits -> 4.096
14 bits -> 16.384

in our case we would need a 14 bit ADC, not an easy requirement as very few of the mcu integrated ADCs can get that precision.
At the same time, when sampling with such accuracy, we might have additional issues such as the stability of the reference voltages, the accuracy of the resistors used in an eventual divider, electronic noise etc.
When higher precision and speed must be achieved, the common solution is to use dedicated ADC devices.

If now you feel frustrated because your mcu only supports a 10 bit sampling device, just think this : precision instruments such as Digital Oscilloscopes use 8 bit ADCs (but they are normally extremely fast and equipped with high precision input circuits).
How do they give all that flexibility and precision with 8 bit only?
They allow to set different scales by altering the offset of the signal and the parameters of the input voltage divider.

The other important parameter is the sampling frequency.
Why achieving high frequency and high resolution at the same time can be difficult?
That's related to the way the sampling happens, which I will try to explain in a simple case.
Digital devices only understand two states : on and off, so how do they convert a voltage to an on or off state?
They can compare the voltage with a reference one and say : if the reference is higher, then set to off, else set to on.
Now if the reference voltage V(t) varies starting from V- (lower reference) and linearly climbs to V+ (higher reference) and at the same time we start a timer that counts the number of cpu cycles, at a given point in time, the signal will cross the reference and the comparison will return "on".
At that point the timer count t gives us a measurement of the voltage used in the comparison V(t).
Practically, some more advanced techniques are used, but that's  pretty much the basic concept.
You understand then that the timer speed is affecting the resolution + sampling rate combination.
If we have a 10 bit resolution, the timer must be able to count up to 1024 when V(t) = V+.
Imagine the timer uses the same clock frequency as the CPU being 16MHz -> the maximum sampling rate would be 16/1024 MHz = 15KHz.
As I previously stated, some more advanced techniques (series of approximations etc) are used, allowing the adcs to achieve better performances, but you now probably understand the relation between cpu speed, sampling rate and resolution.

In the next post I am going to experiment with the ADC10 device of the MSP430G2553

Tuesday, October 16, 2012

C2000 launchpad - code skeleton

Maybe I am just lazy, maybe laziness is a bit "the geek way".

Each time I start a new project I like to copy from a skeleton, some kind of template instead of typing everything from scratch.
The real reason, I think, is that in most of the cases I cannot recall how to start and with the C2000 Launchpad I always forget something, so I created a file with all the steps and code snippets.

I am posting it here, just in case it might turn useful for others.

First, in CCS start a new project (FIle, new Project, CCS Project )

Then  set the basic project properties (see the highlighted parts)

Some tweaks are then needed in the project properties after the project is created, right click the project name in the project explorer, select properties and add the laiunchpad specific location for the include files.
You may need to adjust that path according to your ControlSuite install dir

Finally add the library

You are now good to go, open your main.c file and copy & paste the following skeleton

Update : Make sure you select the correct linker command file, by mistake I selected 28027 instead of F28027 and spent some time to figure out why the program would not fit in memory according to the linker.


#include <stdio.h>
#include <file.h>

#include "DSP28x_Project.h"     // DSP28x Headerfile

#include "f2802x_common/include/pll.h"
#include "f2802x_common/include/clk.h"
#include "f2802x_common/include/wdog.h"

#include "f2802x_common/include/flash.h"
#include "f2802x_common/include/gpio.h"
#include "f2802x_common/include/pie.h"
#include "f2802x_common/include/adc.h"
#include "f2802x_common/include/sci.h"
#include "f2802x_common/include/sci_io.h"

extern void DSP28x_usDelay(Uint32 Count);

// you probably need these
CPU_Handle myCpu;
PLL_Handle myPll;
WDOG_Handle myWDog;
CLK_Handle myClk;

// these are optional
ADC_Handle myAdc;
FLASH_Handle myFlash;
GPIO_Handle myGpio;
PIE_Handle myPie;
SCI_Handle mySci;

void setup_handles()

    myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj));
    myPll = PLL_init((void *)PLL_BASE_ADDR, sizeof(PLL_Obj));
    myWDog = WDOG_init((void *)WDOG_BASE_ADDR, sizeof(WDOG_Obj));
    myCpu = CPU_init((void *)NULL, sizeof(CPU_Obj));

    myFlash = FLASH_init((void *)FLASH_BASE_ADDR, sizeof(FLASH_Obj));
    myGpio = GPIO_init((void *)GPIO_BASE_ADDR, sizeof(GPIO_Obj));
    myPie = PIE_init((void *)PIE_BASE_ADDR, sizeof(PIE_Obj));
    mySci = SCI_init((void *)SCIA_BASE_ADDR, sizeof(SCI_Obj));
    myAdc = ADC_init((void *)ADC_BASE_ADDR, sizeof(ADC_Obj));


void init_system()
  CLK_setOscSrc(myClk, CLK_OscSrc_Internal);
  PLL_setup(myPll, PLL_Multiplier_12, PLL_DivideSelect_ClkIn_by_2);

#ifdef _FLASH
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);

void main()

Note that this is just a skeleton, you may need to remove parts you may not need such as include files, handles etc (unless the linker is smart enough to get rid of them, which I think it's something we might expect).

Also, you will need to add specific peripheral initialization according to your needs and... your program.

Saturday, October 13, 2012

C2000 Piccolo - Serial communications

I was actually tempted to dig into the ADC functionality, however serial communications seemed to be a higher priority thing to learn.
Let's face it : we (normally) do not have a screen where we can output some debug information, our best bet with microcontrollers is the UART and use a terminal program on a PC to visualize some data.

As I already anticipated in a previous post, the serial interface is handled quite at "high level" with the C2000, in a similar way you would deal with it on a PC.
First of all , in the C2000 the uart is referred a SCI (Serial Communication Interface) and since there is a single module "A", it normally becomes SCI-A or SCIA.

I am starting to look around the SCIA using the demo program tha comes with the c2000 Launchpad, unfortunately that one does not cover the RX part, and therefore all the interrupt handling is not covered either.
We will need to look somewhere else for that.

One interesting thing to notice is that the uart has double buffered RX and TX functions which is kind of neat.
Unfortunately it looks like that a single hardware uart is supported in the piccolo (some devices may support  SCI-B), which is a pity since at least two serial ports are always handy, typically you would use one to communicate with a device and the other one to debug whatever you need with a PC.
At least this is what I normally do.
It should be possible and quite easy to setup a software uart if needed, I hope.

Another thing I noticed is that it looks like there is no hardware flow control functionality provided, no big deal probably as a couple of gpio lines could be eventually used for that, after all that's the beauty of dealing with an mcu.

So, this said, we expect to have only two pins to deal with :   RX and TX.
In fact they are accessible from the Launchpad expansion headers

(from the C2000 Launchpad pdf, by TI)

SCIRXDA (RX) is available on J1.3 (mode 1), J2.2 (mode 2), J2.9 (mode 2)
SCITXDA (TX) on J1.4 (mode 1), J1.7 (mode 2), J2.3 (mode 2)

J1.4 (mode 1) means header J1, pin 4 and MUX mode set to 1

Now, the pdf that came with the Launchpad explains that the board has an FTDI interface to which, by default, the serial communications are redirected.
That makes them available through the USB port.
The Switch S4 connects and disconnects the uart from the ftdi, so, normally, if you want to use the ttl uart signals (3.3V tolerant) you are supposed to switch S4 so that they are isolated from the USB.

But then I am wondering :
According to the Launchpad schematics, S4 connects and disconnects J1.3 and J1.4   (GPIO28 and GPIO29), which would mean that if we use another combination (i.e. the J2.2/3 option) normally we would be isolated from the USB.
That's something I will probably test later.

Let's start simple and imagine we want to connect the SCIA to the usb/ftdi, then we will have the S4 switch set to ON and configure properly the GPIO28 and 29 pin on the mcu.

The demo program has it all in this case :

    GPIO_setPullUp(myGpio, GPIO_Number_28, GPIO_PullUp_Enable);
    GPIO_setPullUp(myGpio, GPIO_Number_29, GPIO_PullUp_Disable);

    GPIO_setQualification(myGpio, GPIO_Number_28, GPIO_Qual_ASync);

    GPIO_setMode(myGpio, GPIO_Number_28, GPIO_28_Mode_SCIRXDA);
    GPIO_setMode(myGpio, GPIO_Number_29, GPIO_29_Mode_SCITXDA);

The RX is going to be configured as input (implicitly, that's done automatically when setting the pin to uart mode).
To avoid floating signals the pullup  resistor is enabled.
TX is obviously output, therefore pull resistors are not used.
The setQualification part is related to synchronizing and qualifying the signal with the internal clock.
I will not enter too much in the details here, but let's just say that if a signal uses a clock that is not defined internally (this is the case for the RX signal whose clock depends on the other device on the communication line), then it needs to be synchronized with the internal clock to detect the gap with it.
A number of samples (configurable) is taken and a high frequency clock (SYSCLK or a divided value) is used to assess the gap.
The process is described in the 2802X_system manual.

The final two lines set the MUX  mode for GPIO28 and 29 pins, the GPIO_28_Mode_SCIRXDA and GPIO_29_Mode_SCITXDA are two enum constants (value = 1).

If instead we would have preferred to use the J2 option (GPIO19 = RX, GPIO12 =TX) the needed code would have been :

    GPIO_setPullUp(myGpio, GPIO_Number_19, GPIO_PullUp_Enable);
    GPIO_setPullUp(myGpio, GPIO_Number_12, GPIO_PullUp_Disable);

    GPIO_setQualification(myGpio, GPIO_Number_19, GPIO_Qual_ASync);

    GPIO_setMode(myGpio, GPIO_Number_19, GPIO_19_Mode_SCIRXDA);
    GPIO_setMode(myGpio, GPIO_Number_12, GPIO_12_Mode_SCITXDA);

And presumably the S4 switch would not affect at all, however, before connecting a ttl serial device to them, I would make sure that GPIO28 and 29 are NOT configured as SCIA at the same time (!!).

Now that we have the pin configured properly, let's take a look to the function that configures the SCIA peripheral (still from the demo software)

 mySci = SCI_init((void *)SCIA_BASE_ADDR, sizeof(SCI_Obj));

This line initializes a SCI handler, which will be used later.

void scia_init()

We also initialized and configured properly the clock (check this other post for details) , now we are using the myClk handler to enable the clock for the SCI peripheral

    SCI_setNumStopBits(mySci, SCI_NumStopBits_One);
    SCI_setCharLength(mySci, SCI_CharLength_8_Bits);

Ok, this part is quite obvious,  we are using the classic 8-N-1 setup and we want both RX and TX channels enabled.


Also we think it is cool to have interrupts on both RX and TX buffers, then we want to set the Baud Rate.
At this point, you realize that the way these libraries have been created allow you to perform all the needed configuration without knowing much about the internals of the device.
... but some details cannot hurt.

The base clock of the SCI is the low speed clock (LSPCLK), then a 16 bit register (BRR) is provided to configure a divider.
That allows up to 65535 different BR configurations, the formula is the following :

By default the LSPCLK is set to SYSCLK / 4 ( 60MHz/4 in our case = 15MHz)
According to the formula, assuming BRR = 15
-> BR = 15.000.000 / (15 +1)*8 = 15.000.000 /128 = 117.2K  which is not exactly 115.2K  but close enough.
Using BRR = 16 we would have 15.000.000 / 136 = 110.3K and using BRR = 14 : 125K
So, finally, BRR = 15 is the best option

    // Configured for 115.2kbps
#if (CPU_FRQ_60MHZ)
    SCI_setBaudRate(mySci, SCI_BaudRate_115_2_kBaud);    // value is 15
#elif (CPU_FRQ_50MHZ)
    SCI_setBaudRate(mySci, (SCI_BaudRate_e)13);
#elif (CPU_FRQ_40MHZ)
    SCI_setBaudRate(mySci, (SCI_BaudRate_e)10);

Obviously we need a different divider if the CPU is not set to 60MHz, but hey, you have the formula, you can do the math :)
The following code enables the fifo buffers for both the RX and TX channels, then it enables the TX fifo interrupt, but not the RX one.
That's because, I suppose, the sample program is not meant to receive any data, in a real world application we would probably need that one too.
Despite the fact that the TX interrupt is enabled, there is no interrupt handler function in the demo program.
I assume the interrupt is used internally by the library in order to manage properly the output buffer.

    SCI_setTxFifoIntLevel(mySci, SCI_FifoLevel_Empty);

    SCI_setRxFifoIntLevel(mySci, SCI_FifoLevel_4_Words);

Finally, after clearing the interrupts, the buffers etc, a priority is set and the module enabled

    SCI_setPriority(mySci, SCI_Priority_FreeRun);

checking in the header file it looks that the following priorities are available :

     SCI_Priority_Immediate=(0 << 3),    //!< Denotes an immediate stop
    SCI_Priority_FreeRun=(1 << 3),      //!< Denotes free running
    SCI_Priority_AfterRxRxSeq=(2 << 3)  //!< Denotes that a stop after the current receive/transmit sequence

That completes the hardware configuration, on the software side, the port is dealt with similarly to a stream (or file) :

    status = add_device("scia", _SSA, SCI_open, SCI_close, SCI_read, SCI_write, SCI_lseek, SCI_unlink, SCI_rename);
    fid = fopen("scia","w");
    freopen("scia:", "w", stdout);
    setvbuf(stdout, NULL, _IONBF, 0);

First a "device" named scia is added, then it is opened for output and finally redirected to the standard output device (stdout).
From now on, each write to stdout will actually be directed to the uart :


This was a very high level first look into the serial communications of the C2000, will come back in the future with some more details and some experiments.

Thursday, October 11, 2012

BeagleBone : Serial ports and multiplexing

Some months ago I released a couple of  Youtube videos where I was illustrating the basics of using java and the comm library RXTX to drive serial communications with the beaglebone.

Since then I keep getting a few questions and from time to time I get requests for a test class.
So I figured I could do a post here to explain few things.
(Ok, ok... if you just want the code and skip the blah blah, you can get it here, good luck with it)

First thing : RXTX is an open source communication library, maybe it's not perfect, but I find it great for a number of reasons, one of them being it's portability.
Driving hardware (like serial ports) in java is a bit tricky, because you need native code (i.e. written in C, ASM...) for the specific architecture you are using.
To put it in simple words : you need some code which is not executed in the JVM, so it's not java.
This is obviously an issue : java is designed to be portable cross platform, you normally just need a jvm for the specific architecture you are using and you are normally pretty much done.

If you need to execute native code, then you need the specific version that can work on your device.
The native part of the RXTX library has been successfully ported to the bone, which makes it possible to use the library in java on this device.

I am not going to provide a tutorial on rxtx here, if you want to use it and you have no knowledge on it, I strongly suggest you search for tutorials in the web and first learn how to use it on a pc.
I have a software logging data from a RS485 every 30 seconds, running since about 3 months, it was developed and tested on a PC and finally deployed (smoothly) on the bone.

So, what's special with the bone?
Not much honestly and in fact, if you use a usb/serial converter you probably don't need any bone specific instruction : what runs on your pc is supposed to run on the bone just the same.

However, the bone offers also some "internal" ttl uarts (3.3V !! beware) and to use those you need some bone specific code.
Those uarts are physically accessible from the P8 and P9 headers of the board, their pin mapping -extracted from the bbone user manual- is the following :

i.e. P9.21.1 means pin 21 on the P9 header and multiplex value = 1

You should probably be familiar with the multiplexing concept, if not, here's a very quick explanation :


Many of the modern processors and microcontrollers, provide a wide range of functionality.
At this point the issue became to export all those functions with physical electrical contacts (pins).
A solution was found in assigning multiple functions to the same pin and allowing the processor to be configured, via software, to set a specific function to a specific pin.
The ARM cortex processors use a lot this feature.
To get back to our serial ports, this means that the RX function of uart2 will normally be located at pin xxx, however at the same pin you can eventually find other 7 functions, which could be GPIO, timer output, etc...
So, what we want to do is to configure properly the function, direction (input/ output) etc of the pins we need.

But wait... that sounds like an issue if we are using java, right?
Again we would need to access the hardware, so we would need native code...

No, the Linux OS comes in help with that.
To allow an easy access to these functionality, the OS exports a virtual file system which acts as a sort of interface for all those hardware configuration.
From the java side, we just need to deal with files and directories.

For that I created a specific helper class, it's not really finished, but it does the job for me.
It can be found here and if you can read java code it should be quite self explanatory.
I use it to interface the beaglebone headers (it can do gpio read/writes), not just for setting the multiplex values.
This class uses an external dat file (gpiomux.dat) which is actually a text file in which I list the contacts on the P8 and P9 header.
Except for the serial ports, for which I provide a specific method, to configure the other pins (should you need to), you need to refer to the user manual, discover which pin is assigned to the function you are looking for and find also the function number.
With that info, the BBMux class will do the configuration for you.

Allowing RXTX to see the ttl uart ports

The RXTX library uses a list of known port names to discover your available serial ports, unfortunately this list does not include the beaglebone internal serial ports.
No worries, there is an easy fix, no coding required!
The solution is documented here :
Check where it says : How does rxtx detect ports? Can I override it?

In my case I decided to specify the port list when running the program.
The internal serial ports are seen by the OS as ttyOx (O stands for Omap)

    -classpath ./:./rxtx/RXTXcomm.jar

My command line sets :
- the java classpath to include the rxtx java library (jar)
- the java library path to include the rxtx native library folder
- the rxtx serial ports to the list of ports I could use in my program

Note : I created the test class TestBBSerial by ripping code from my program, I hope it works, but it's really meant just to illustrate the concepts.
You can find great rxtx examples on the web,
Unfortunately I cannot post my other programs as they would be too complex to explain, I normally use some wrappers for rxtx to handle buffers, configurations, protocols etc.
These wrappers use json, webservices for remote configurations... so I would need to post tons of classes that would just confuse you more.