Hubo LED Display Tutorial Series:

Tutorial 1: the TI DSP Controller Board

Keywords: Hubo, Hubo Lab, Texas Instruments DSP, TMS320F2808, LED Array

The photo shows the completed DSP controller board which allows you to control the LED array (which we will cover later). The big picture problem is that there is no "personality" to the Hubo robot, this display will be used to display facial features on the head.. Solving this partially or completely is important because knowledge of the Texas Instruments DSP platforms will be gained, as well as basic knowledge of controls behind the display. This tutorial shows you how to build the DSP breakout board used in the Hubo Lab and takes approximately 12 hours to complete.


Motivation and Audience

This tutorial's motivation is to get comfortable with building and using a high end DSP platform  . Readers of this tutorial assumes the reader has the following background and interests:

The rest of the tutorial is presented as follows:

Parts List and Sources

To complete this tutorial, you'll need the following items:

TABLE 1: Parts needed to build the DSP Breakout Board
PART DESCRIPTIONVENDORPARTPRICE (1999)QTY
TMS320F2808Digikey296-18335-ND$19.50 1
TPS767D301Digikey296-19002-1-ND$5.18 1
SN65HVD231Digikey296-12206-5-ND$3.04 1 
20MHz Crystal OscillatorDigikeyXC337TR-ND $2.771 
SPST push button switch - reset switch NO contacts 0.3" pin spacing ------------ 
Assortment of Surface mount components - resisitors, capacitors, LEDs, and beads------------ 
PCB Mount mini Molex-style connectors for CAN and Power--- --------- 
Misc Pin headers (0.1 inch and 0.064 inch)------ ------

                                                --- indicates that these components can be purchased at almost any electronics parts suppliers.  Recommendations include Digikey, Jameco, or Mouser.

Construction

This section gives step-by-step instructions along with photos to complete the DSP breakout board. A schematic to construct the breakout board in this tutorial can be found here.  You will need Adobe's free Acrobat reader, or an equivalent PDF reader to view the schematic.

Step 1 - Placing the DSP on the Board

    The first step, once you have the board, is to solder the DSP and the TPS767D301 in place on the board. A key point here is to line up the chip on the pads, and solder one or two pins on a corner of the chip.  Fine adjustments can then be made to further place the chip.  Once the chip is fully in place, and all the pads line up with the pins, soldering the chip can now be done.  Using a LOT of flux for the next step, get a small bead of solder onto the iron tip, and drag the bead down one side of the chip.  The flux will keep the solder from sticking to multiple pins.  If solder bridging occurs, clean the soldering iron tip on the sponge, then flux the tip, and drag the solder away from the chip.  This method is performed at the Hubo Lab, and ensures a complete and good connection between the pin and the solder pad on the PCB. Click here for a schematic diagram of the DSP breakout board.

Click here for a short video clip on removing solder from the DSP using a blade-tip soldering iron.

Click here for another close-up video on removing solder from the DSP.

Step 2 - Soldering the Components to the Board

    Next, I suggest working from a certain area of the board, and work sections of the board.  I usually start with the smaller SMD components (in a corner) and work my way to the other side of the board.  This prevents some complications of placing parts on in sequential order.  Start with placing the Oscillator on the side opposite the DSP.  Once the oscillator is on, solder the components on around the oscillator. Note: the components are marked on the board as to which components go where, so the schematic diagram is not as important for assembly.  When placing the SN65HVD231 (CAN transceiver) on the board, make note that the chip is in the proper orientation, this is indicated by the silk screen.  On the chip will be a designation for the top of the chip (either a line, dot, or notch).  DO NOT put on the pin headers yet, these will be big obstacles for placing components on! 

    For new comers, the following is a crash course in soldering surface mount components: Using a small pair of tweezers, hold the component on the board between the pads you want the component to bridge.  Clean the tip of the soldering iron, and dip it in the flux to clean it further,  This will also allow the solder to ball up at the joint rather than becoming sticky-like.  Once you have flux on the tip, add some solder to it, placing a small bead of solder at the tip.  Now, take and solder down one side of the component.  If all goes well, the solder bridges the gap, and looks smooth and shiny.  If not, continue to hold the component, add more flux to the tip of the iron, and re-solder the joint.  Once the component solder joint looks good, you can let go of the component with the tweezers, and solder the rest of the component. 

Step 2 - Soldering the Components to the Board

    Once you feel confident that everything looks good on the side you just worked on, flip the board over and solder the components on the DSP-side of the board.  Again, do not place the pin headers on until the very last, for both the pin connectors, jumper headers, and JTAG port. Second to last should go the power and CAN connectors.  place all the components on the board, starting with the reset switch, and moving clockwise around the DSP.  Note: for the SMT LED's, there will be an indicator to the anode and cathode, usually indicated by a dot (mine is a small green dot in the package) that will indicate the cathode.  On my board, the green dot-ed pin goes to to side opposite the + sign (anode).

Step 3 - Add the Pin headers

    Once the all the SMT components are soldered on, and looking good, you can now solder on the pin headers.  The same technique can be used as when you soldered on the DSP, applying a bead of solder and flux to the iron, and drag the tip along the row.  This is a faster way than actually soldering all the pins one by one.  It does not matter the sequence of pin headers, all have to go on eventually.  Note: if no Molex connectors are available for the CAN, Power and SCI ports, standard 0.1" headers can be used, just watch out for the polarity of the ports. These boards are fragile, they do not have protection for power reversal, or JTAG Port reversal.  I have dealt with these issues myself, and they are not fun. 

Step 4 - Testing

    For testing the board, apply power to the power port.  I recommend using a current limited power supply, in case something is shorted.  When the current is measured, it should be around 60mA for the board (if the DSP is new - without a flashed program), or around 200mA if there is code running on the DSP.  When all looks good at this point, connect the DSP to Code Composer Studio, and connect to the chip.  If all goes well, CCS will report that the chip is halted, and ready for code upload.

Here is a picture of the DSP all wired up with Power and JTAG:

Programming

The basic source code for the DSP is provided below.  This is only a sample project, use this project if you are starting out a new project with CCS 3.1.  The code to the Hubo LED Display will be covered in Tutorial 3.  To open, just unzip the folder, and open the project file using Code Composer Studio.


To be compiled with Code Composer Studio v3.1
Note: download project_template rather than cutting and pasting from below.

//###########################################################################
// $ Bryan Kobe
// $ TMS2808, Project template
// $ Release Date: February 23, 2008 $
//###########################################################################

//###########################################################################
// Updated 
// 1. Nothing yet. First Release 
//###########################################################################

#include <stdio.h>
#include "DSP280x_Device.h" // DSP280x Headerfile Include File
#include "DSP280x_Examples.h" // DSP280x Examples Include File

// #### PARAMETER SETTING ####################################################
// Place parameter variables here for setting program.

// #### GAIN SETTING #########################################################
// Place any gain settings here for control


// #### BOOT TO FLASH ########################################################
#pragma CODE_SECTION(cpu_timer0_isr, "ramfuncs");


// #### DECLARATION OF FUNCTIONS #############################################
// 1. CPU Interrupt Functions
interrupt void cpu_timer0_isr(void);

// 2. GPIO Functions
void Gpio_select(void);
void Pause(unsigned int _counts);

// #### DECLARATION OF VARIABLES #############################################

// These are defined by the linker (see F2808.cmd) For writting FLASH
extern Uint16 RamfuncsLoadStart;
extern Uint16 RamfuncsLoadEnd;
extern Uint16 RamfuncsRunStart;

int counter = 0;


// #### MAIN CONTROL PROGRAM ################################################
void main(void)
{
// Step 1. Initialize System Control
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the DSP280x_SysCtrl.c file.
InitSysCtrl();

// Step 2. Initalize GPIO
// This example function is found in the DSP280x_Gpio.c file and
// illustrates how to set the GPIO to it's default state for the program
Gpio_select();

// Step 3. Clear all interrupts and initialize PIE vector table
// Disable CPU interrupts 
DINT;

// Initialize the PIE control registers to their default state.
// The default state is all PIE interrupts disabled and flags
// are cleared. 
// This function is found in the DSP280x_PieCtrl.c file.
InitPieCtrl();

// Disable CPU interrupts and clear all CPU interrupt flags:
IER = 0x0000;
IFR = 0x0000;

// Initialize the PIE vector table with pointers to the shell Interrupt 
// Service Routines (ISR). 
// This will populate the entire table, even if the interrupt
// is not used in this example. This is useful for debug purposes.
// The shell ISR routines are found in DSP280x_DefaultIsr.c.
// This function is found in DSP280x_PieVect.c.
InitPieVectTable();

// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file. 
EALLOW; // This is needed to write to EALLOW protected registers

// CPU Timer Interrupt
PieVectTable.TINT0 = &cpu_timer0_isr;

EDIS; // This is needed to disable write to EALLOW protected registers


// Step 4. Initialize all the Device Peripherals
// This function is found in DSP280x_InitPeripherals.c
InitCpuTimers();

// Configure CPU-Timer 0 to interrupt at given time period
ConfigCpuTimer(&CpuTimer0, 100, 1000); // 1kHz : 1000, 100Hz : 10000
StartCpuTimer0();

// Step 5. User specific code, enable interrupts
// Copy time critical code and Flash setup code to RAM

// The RamfuncsLoadStart, RamfuncsLoadEnd, and RamfuncsRunStart
// symbols are created by the linker. Refer to the F2808.cmd file. 

// Copy the Flash API functions to SARAM
MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);

// Call Flash Initialization to setup flash waitstates
// This function must reside in RAM
InitFlash();

// Enable CPU INT1 which is connected to CPU-Timer 0:
IER |= M_INT1; // Enable CPU INT
IER |= M_INT9; // Enable PIE Group 9 for SCIB

// Enable TINT0 in the PIE: Group 1 interrupt 7
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // cpu interrupt
PieCtrlRegs.PIEIER9.bit.INTx3 = 1; // PIE Group 9, INT3 // SCIB_RX
PieCtrlRegs.PIEIER9.bit.INTx4 = 1; // PIE Group 9, INT3 // SCIB_TX

// Enable global Interrupts and higher priority real-time debug events:
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global realtime interrupt DBGM 

// Step 6. IDLE loop. Just sit and loop (or place control code here for CAN Communication

for(;;) { } // add custom code here for execution
} 
// End of main

// #### LIST OF USED FUNCTIONS ################################################
interrupt void cpu_timer0_isr(void)
{
// Put the Flash into sleep
FlashRegs.FPWR.bit.PWR = FLASH_SLEEP; 

GpioDataRegs.GPASET.bit.GPIO31 = 1; // SET led - for check time for interrupt processing

CpuTimer0.InterruptCount++; // 1 count = 1ms
counter = CpuTimer0.InterruptCount;

if (counter >= 100 ) {
GpioDataRegs.GPBTOGGLE.bit.GPIO32 = 1; // Toggle the LED at a slow rate
counter = 0;
}

CpuTimer0.InterruptCount=0;
GpioDataRegs.GPBTOGGLE.bit.GPIO32 = 1;


// Acknowledge this interrupt to receive more interrupts from group 1
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;

GpioDataRegs.GPACLEAR.bit.GPIO31 = 1; // CLEAR led - for check time of interrupt processing
}

void Gpio_select(void)
{
EALLOW;

// GPIO0, active high, so set it low: ROW 1
GpioCtrlRegs.GPAPUD.bit.GPIO0 = 1; // Enable pullup on GPIO0
GpioDataRegs.GPACLEAR.bit.GPIO0 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 0; // G0/EPWM1A
GpioCtrlRegs.GPADIR.bit.GPIO0 = 1; // 1:output, 0:input

// GPIO1, active high, so set it low: ROW 2
GpioCtrlRegs.GPAPUD.bit.GPIO1 = 1; // Enable pullup on GPIO1
GpioDataRegs.GPACLEAR.bit.GPIO1 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 0; // G1/EPWM1B/SPISIMOD
GpioCtrlRegs.GPADIR.bit.GPIO1 = 1; // 1:output, 0:input

// GPIO2, active high, so set it low: ROW 3
GpioCtrlRegs.GPAPUD.bit.GPIO2 = 1; // Enable pullup on GPIO2
GpioDataRegs.GPACLEAR.bit.GPIO2 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO2 = 0; // G2/EPWM2A
GpioCtrlRegs.GPADIR.bit.GPIO2 = 1; // 1:output, 0:input

// GPIO3, active high, so set it low: ROW 4
GpioCtrlRegs.GPAPUD.bit.GPIO3 = 1; // Enable pullup on GPIO3
GpioDataRegs.GPACLEAR.bit.GPIO3 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO3 = 0; // G3/EPWM2B/SPISOMID
GpioCtrlRegs.GPADIR.bit.GPIO3 = 1; // 1:output, 0:input

// GPIO4, active high, so set it low: ROW 5
GpioCtrlRegs.GPAPUD.bit.GPIO4 = 1; // Enable pullup on GPIO4
GpioDataRegs.GPACLEAR.bit.GPIO4 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO4 = 0; // G4/EPWM3A
GpioCtrlRegs.GPADIR.bit.GPIO4 = 1; // 1:output, 0:input

// GPIO5, active low, so set it high: COLUMN 1
GpioCtrlRegs.GPAPUD.bit.GPIO5 = 0; // Enable pullup on GPIO5
GpioDataRegs.GPASET.bit.GPIO5 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO5 = 0; // G5/EPWM3B/SPICLKD/ECAP1
GpioCtrlRegs.GPADIR.bit.GPIO5 = 1; // 1:output, 0:input

// GPIO6, active low, so set it high: COLUMN 2
GpioCtrlRegs.GPAPUD.bit.GPIO6 = 0; // Enable pullup on GPIO6
GpioDataRegs.GPASET.bit.GPIO6 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 0; // G6/EPWM4A
GpioCtrlRegs.GPADIR.bit.GPIO6 = 1; // 1:output, 0:input

// GPIO7, active low, so set it high: COLUMN 3
GpioCtrlRegs.GPAPUD.bit.GPIO7 = 0; // Enable pullup on GPIO7
GpioDataRegs.GPASET.bit.GPIO7 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO7 = 0; // G7/EPWM4B/SPISTED/ECAP2
GpioCtrlRegs.GPADIR.bit.GPIO7 = 1; // 1:output, 0:input

// GPIO8, active low, so set it high: COLUMN 4
GpioCtrlRegs.GPAPUD.bit.GPIO8 = 0; // Enable pullup on GPIO8
GpioDataRegs.GPASET.bit.GPIO8 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO8 = 0; // G8/EPWM5A/CANTXB/ADCSOCAO
GpioCtrlRegs.GPADIR.bit.GPIO8 = 1; // 1:output, 0:input

// GPIO9, active low, so set it high: COLUMN 5
GpioCtrlRegs.GPAPUD.bit.GPIO9 = 0; // Enable pullup on GPIO9
GpioDataRegs.GPASET.bit.GPIO9 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO9 = 0; // G9/EPWM5B/SCITXDB/ECAP3
GpioCtrlRegs.GPADIR.bit.GPIO9 = 1; // 1:output, 0:input

// GPIO10, active low, so set it high: COLUMN 6
GpioCtrlRegs.GPAPUD.bit.GPIO10 = 0; // Enable pullup on GPIO10
GpioDataRegs.GPASET.bit.GPIO10 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO10 = 0; // G10/EPWM6A/CANRXB/ADCSOCBO
GpioCtrlRegs.GPADIR.bit.GPIO10 = 1; // 1:output, 0:input

// GPIO11, active low, so set it high: COLUMN 7
GpioCtrlRegs.GPAPUD.bit.GPIO11 = 0; // Enable pullup on GPIO11
GpioDataRegs.GPASET.bit.GPIO11 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO11 = 0; // G11/EPWM6B/SCIRXB/ECAP4
GpioCtrlRegs.GPADIR.bit.GPIO11 = 1; // 1:output, 0:input

// GPIO12, active low, so set it high: COLUMN 8
GpioCtrlRegs.GPAPUD.bit.GPIO12 = 0; // Enable pullup on GPIO12
GpioDataRegs.GPASET.bit.GPIO12 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0; // G12/TZ1/CANTXB/SPISIMOB
GpioCtrlRegs.GPADIR.bit.GPIO12 = 1; // 1:output, 0:input

// GPIO13, active low, so set it high: COLUMN 9
GpioCtrlRegs.GPAPUD.bit.GPIO13 = 0; // Enable pullup on GPIO13
GpioDataRegs.GPASET.bit.GPIO13 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO13 = 0; // G13/TZ2/CANRXB/SPISOMIB
GpioCtrlRegs.GPADIR.bit.GPIO13 = 1; // 1:output, 0:input

// GPIO14, active low, so set it high: COLUMN 10
GpioCtrlRegs.GPAPUD.bit.GPIO14 = 0; // Enable pullup on GPIO14
GpioDataRegs.GPASET.bit.GPIO14 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO14 = 0; // G14/TZ3/SCITXDB/SPICLKB
GpioCtrlRegs.GPADIR.bit.GPIO14 = 1; // 1:output, 0:input

// GPIO15, active low, so set it high: COLUMN 11
GpioCtrlRegs.GPAPUD.bit.GPIO15 = 0; // Enable pullup on GPIO15
GpioDataRegs.GPASET.bit.GPIO15 = 1; // Load output latch
GpioCtrlRegs.GPAMUX1.bit.GPIO15 = 0; // G15/TZ4/SCIRXDB/SPISTEB
GpioCtrlRegs.GPADIR.bit.GPIO15 = 1; // 1:output, 0:input

// GPIO16, active low, so set it high: COLUMN 12
GpioCtrlRegs.GPAPUD.bit.GPIO16 = 0; // Enable pullup on GPIO16
GpioDataRegs.GPASET.bit.GPIO16 = 1; // Load output latch
GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 0; // G16/SPISIMOA/CANTXB/TZ5
GpioCtrlRegs.GPADIR.bit.GPIO16 = 1; // 1:output, 0:input

// GPIO17, active low, so set it high: COLUMN 13
GpioCtrlRegs.GPAPUD.bit.GPIO17 = 0; // Enable pullup on GPIO17
GpioDataRegs.GPASET.bit.GPIO17 = 1; // Load output latch
GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 0; // G17/SPISOMIA/CANRXB/TZ6
GpioCtrlRegs.GPADIR.bit.GPIO17 = 1; // 1:output, 0:input

// GPIO18, active low, so set it high: COLUMN 14
GpioCtrlRegs.GPAPUD.bit.GPIO18 = 0; // Enable pullup on GPIO18
GpioDataRegs.GPASET.bit.GPIO18 = 1; // Load output latch
GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 0; // G18/SPICLKA/SCITXDB
GpioCtrlRegs.GPADIR.bit.GPIO18 = 1; // 1:output, 0:input

// GPIO19, active low, so set it high: COLUMN 15
GpioCtrlRegs.GPAPUD.bit.GPIO19 = 0; // Enable pullup on GPIO19
GpioDataRegs.GPASET.bit.GPIO19 = 1; // Load output latch
GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 0; // G19/SPISTEA/SCIRXDB
GpioCtrlRegs.GPADIR.bit.GPIO19 = 1; // 1:output, 0:input

// GPIO20, active low, so set it high: COLUMN 16
GpioCtrlRegs.GPAPUD.bit.GPIO20 = 0; // Enable pullup on GPIO20
GpioDataRegs.GPASET.bit.GPIO20 = 1; // Load output latch
GpioCtrlRegs.GPAMUX2.bit.GPIO20 = 0; // G20/EQEP1A/SPISIMOC/CANTXB
GpioCtrlRegs.GPADIR.bit.GPIO20 = 1; // 1:output, 0:input

// GPIO21, active low, so set it high: COLUMN 17
GpioCtrlRegs.GPAPUD.bit.GPIO21 = 0; // Enable pullup on GPIO21
GpioDataRegs.GPASET.bit.GPIO21 = 1; // Load output latch
GpioCtrlRegs.GPAMUX2.bit.GPIO21 = 0; // G21/EQEP1B/SPISOMIC/CANRXB
GpioCtrlRegs.GPADIR.bit.GPIO21 = 1; // 1:output, 0:input

// GPIO22, active low, so set it high: COLUMN 18
GpioCtrlRegs.GPAPUD.bit.GPIO22 = 0; // Enable pullup on GPIO22
GpioDataRegs.GPASET.bit.GPIO22 = 1; // Load output latch
GpioCtrlRegs.GPAMUX2.bit.GPIO22 = 0; // G22/EQEP1S/SPICLKC/SCITXDB
GpioCtrlRegs.GPADIR.bit.GPIO22 = 1; // 1:output, 0:input

// GPIO23, active low, so set it high: COLUMN 19
GpioCtrlRegs.GPAPUD.bit.GPIO23 = 0; // Enable pullup on GPIO23
GpioDataRegs.GPASET.bit.GPIO23 = 1; // Load output latch
GpioCtrlRegs.GPAMUX2.bit.GPIO23 = 0; // G23/EQEP1I/SPISTEC/SCIRXDB
GpioCtrlRegs.GPADIR.bit.GPIO23 = 1; // 1:output, 0:input

// GPIO24, active low, so set it high: COLUMN 20
GpioCtrlRegs.GPAPUD.bit.GPIO24 = 0; // Enable pullup on GPIO24
GpioDataRegs.GPASET.bit.GPIO24 = 1; // Load output latch
GpioCtrlRegs.GPAMUX2.bit.GPIO24 = 0; // G24/ECAP1/EQEP2S/SPISTEB
GpioCtrlRegs.GPADIR.bit.GPIO24 = 1; // 1:output, 0:input

// GPIO32, set it low: LED 1
GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0; // Enable pullup on GPIO32
GpioDataRegs.GPBSET.bit.GPIO32 = 1; // Load output latch
GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 0; // G32/SDAA/EPWMSYNCI/ADCSOCAO
GpioCtrlRegs.GPBDIR.bit.GPIO32 = 1; // 1:output, 0:input

// GPIO33, set it low: LED 2
GpioCtrlRegs.GPBPUD.bit.GPIO33 = 0; // Enable pullup on GPIO33
GpioDataRegs.GPBSET.bit.GPIO33 = 1; // Load output latch
GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 0; // G33/SCLA/EPWMSYNCO/ADCSOCBO
GpioCtrlRegs.GPBDIR.bit.GPIO33 = 1; // 1:output, 0:input

EDIS; 
}

void Pause(unsigned int _counts) {
short i;
short j;

for(i=0; i < 1000; i++) 
{
for(j=0; j < _counts; j++)
{}
}
}

//===========================================================================
// End of file.
//===========================================================================




 

Final Words

This tutorial's objective was to build and test the DSP breakout board for the Hubo LED Display.  Once the concepts were conveyed the reader could understand the programming basics of the TI DSP architecture.

Speculating future work, derived from this tutorial, includes building the LED board for the DSP. In the big picture, the problem of building an LED display for the Hubo can be solved with this tutorial.

Click here to email me