My New ADM-3A Dumb Terminal

My good friend Robie has some connections in the salvage industry. We have a running deal where I do some general IT work for him, and he finds me cool geeky stuff in exchange. So far, he’s gotten me a nice handheld FLIR, a couple of old portable computers, and a cold-war era radiation detector. Yesterday he called my and said he had something really nice. I stopped by today and wasn’t let down.

20130331_202031

20130331_201608

That’s all TTL logic, save a few specialized IC’s. No microprocessors. Awesome.

It’s an ADM-3A terminal. This thing is really a piece of computing history. With Linux, ‘~’ means home in many shells. That’s because on the ADM-3A, the word “home” and ‘~’ are printed on the same key. Also, the text editor “vi” uses the HJKL keys as arrows. That’s because the corresponding arrows are printed on the ADM-3A’s keys. This standard has found its way into many programs beyond vi, including NetHack and even a Google Labs feature of Gmail. It’s quite satisfying to own such an item, and I can’t wait to use it with one of my boxen, as soon as my USB->RS232 cable arrives.

Finally, I have a question for the audience. In the research I’ve been doing, I haven’t been able to find pictures or any information about the ADM-3A in orange. It doesn’t appear that it was painted. Since I can’t find anything about the color, I’m wondering if my new little box is somehow special. If you happen to know anything about it, please do reply.

Numitron Tube Alarm Clock

This is a clock I made for my dad’s Christmas present. He’s been an incredible help to me while I’ve been returning to school for a degree in computer engineering. I wanted to make him a gift that would show thanks for all he has done for me. I thought it would be appropriate to make him something using what I have learned in school. Thanks, dad!

20130107_121002
The clock’s display is built from Soviet-era IV-9 Numitron tubes I picked up off of eBay. They’re really easy to work with compared to nixie tubes, and look (arguably) just as cool. Each segment draws about as much current as an LED, and they can work with either common anode or common cathode drivers. This allowed me to use some HEF4794B LED drivers I had on hand. These chips are chainable shift registers, so the display only required 3 microcontroller pins.

20130107_152825
I also wanted the clock to have an audible alarm. In a previous post, a reader named lasershark tipped me off about about a really interesting way of generating music with minimal code, called Bytebeat. This method generates 8-bit audio at 8000 samples per second using only one line of code. I have Bytebeat implemented in the file “sound.c” in the source code zip below.

20130107_120926
For timekeeping and battery backup I used a Chronodot RTC module from Macetech.

20121229_123922
I tried to keep the build as clean as possible since the clock is intended mostly for display. Extra effort was made on the jumpers to the Numitrons. I didn’t want things to turn into a rat’s nest while running 9 wires to every tube. I also chose Christmas colors for the protoboard and wires.

20130107_120814

Here’s the source as a CCS project:
Numitron

Hey, HDD Clock people!

I haven’t forgotten about you! I’ve been working the last couple of days on rewriting the clock code for the TI-Launchpad. I plan to release the source and build instructions sometime in January. I’ll update you on my progress soon.

My First FPGA Project — Larson Scanner

I’ve decided to take a little break from MSP430 and learn something new. FPGA’s have always intrigued me, so I picked up a Nexys 2 from Digilent and started reading a book called FPGA Prototyping by Verilog Examples by Pong P. Chu. (That reminds me, I still need to finish Pong for the MSP430.)

I read several chapters to familiarize myself with the hardware and language, but nothing beats learning by doing. My summer construction job was rained out today, so I decided to grab a Rockstar energy drink and write my first Verilog project. I figured a Kitt light/Larson Scanner/Cylon eye would be something I could figure out in a few hours. I’m not too sure how “correct” my code is, so I’d love to hear some suggestions.

module main
  (
  input wire clk,
  output wire [2:0] position,
  output wire direction,
  output wire [7:0] Led
    );
   
   
   // symbolic state declaration
  localparam   rising = 1'b1,
          falling = 1'b0;
              
  reg [24:0] position_state_count, brightness_state_count;
  reg dir_state_reg, dir_state_next;
  reg [2:0] position_state_reg, position_state_next;
  reg [3:0] brightnesses [7:0];
//  reg [2:0] brightnesses_next [7:0];
  
  // state register
  always @(posedge clk)
    begin
      dir_state_reg <= dir_state_next;
      position_state_reg <= position_state_next;
    end
  
  // next position state logic
  always @(posedge clk)
    if(position_state_count == 7500000)
      begin
        position_state_count = 0;
        if(dir_state_reg)
          position_state_next = position_state_reg + 1;
        else
          position_state_next = position_state_reg - 1;
      end
    else
      position_state_count = position_state_count + 1;
      
  // next direction state logic
  always @(posedge clk)
    if(position_state_reg == 7)
      dir_state_next = falling;
    else if(position_state_reg == 0)
      dir_state_next = rising;
      
//  assign position = position_state_reg;
//  assign direction = dir_state_reg;

  // brightness state logic
  always @(posedge clk)
    begin
      if(brightness_state_count == 1500000)
        begin
          brightness_state_count = 0;
          if(brightnesses[0] > 0)
            brightnesses[0] = brightnesses[0] - 1;
          if(brightnesses[1] > 0)
            brightnesses[1] = brightnesses[1] - 1;
          if(brightnesses[2] > 0)
            brightnesses[2] = brightnesses[2] - 1;
          if(brightnesses[3] > 0)
            brightnesses[3] = brightnesses[3] - 1;
          if(brightnesses[4] > 0)
            brightnesses[4] = brightnesses[4] - 1;
          if(brightnesses[5] > 0)
            brightnesses[5] = brightnesses[5] - 1;
          if(brightnesses[6] > 0)
            brightnesses[6] = brightnesses[6] - 1;
          if(brightnesses[7] > 0)
            brightnesses[7] = brightnesses[7] - 1;
        end
      else
        brightness_state_count = brightness_state_count + 1;
      brightnesses[position_state_reg] = 15;
    end
  
  // instantiate led pwm logic
  pwm led_ctl0 (.brightness(brightnesses[0]), .out(Led[0]), .clk(clk));
  pwm led_ctl1 (.brightness(brightnesses[1]), .out(Led[1]), .clk(clk));
  pwm led_ctl2 (.brightness(brightnesses[2]), .out(Led[2]), .clk(clk));
  pwm led_ctl3 (.brightness(brightnesses[3]), .out(Led[3]), .clk(clk));
  pwm led_ctl4 (.brightness(brightnesses[4]), .out(Led[4]), .clk(clk));
  pwm led_ctl5 (.brightness(brightnesses[5]), .out(Led[5]), .clk(clk));
  pwm led_ctl6 (.brightness(brightnesses[6]), .out(Led[6]), .clk(clk));
  pwm led_ctl7 (.brightness(brightnesses[7]), .out(Led[7]), .clk(clk));

endmodule

module pwm
  (
    input wire clk,
    // 16 levels of brightness
    input wire [3:0] brightness,
    output wire out
   );
  
  reg [2:0] counter_reg;
  wire [2:0] counter_next;
  
  always @(posedge clk)
    counter_reg <= counter_next;
  
  assign counter_next = counter_reg + 1;
  assign out =   (brightness == 4'b1111) ? 1'b1 :
              (brightness > counter_reg) ? 1'b1 : 1'b0;

endmodule

Constraints for Nexys 2:

# clock pin for Nexys 2 Board
NET "clk"   LOC = "B8"; # Bank = 0, Pin name = IP_L13P_0/GCLK8, Type = GCLK, Sch name = GCLK0

# Leds
NET "Led<0>"  LOC = "J14"; # Bank = 1, Pin name = IO_L14N_1/A3/RHCLK7, Type = RHCLK/DUAL, Sch name = JD10/LD0
NET "Led<1>"  LOC = "J15"; # Bank = 1, Pin name = IO_L14P_1/A4/RHCLK6, Type = RHCLK/DUAL, Sch name = JD9/LD1
NET "Led<2>"  LOC = "K15"; # Bank = 1, Pin name = IO_L12P_1/A8/RHCLK2, Type = RHCLK/DUAL, Sch name = JD8/LD2
NET "Led<3>"  LOC = "K14"; # Bank = 1, Pin name = IO_L12N_1/A7/RHCLK3/TRDY1, Type = RHCLK/DUAL, Sch name = JD7/LD3
NET "Led<4>"  LOC = "E17"; # Bank = 1, Pin name = IO, Type = I/O, Sch name = LD4? s3e500 only
NET "Led<5>"  LOC = "P15"; # Bank = 1, Pin name = IO, Type = I/O, Sch name = LD5? s3e500 only
NET "Led<6>"  LOC = "F4";  # Bank = 3, Pin name = IO, Type = I/O, Sch name = LD6? s3e500 only
NET "Led<7>"  LOC = "R4";  # Bank = 3, Pin name = IO/VREF_3, Type = VREF, Sch name = LD7? s3e500 only

Generating Random Numbers

I was looking for example code for RF2500 last night and came across some TI code for using the ADC as a random number generator. The function was in assembly, so I rewrote it in C:

bool getRandomBit(){
  ADC10CTL1 |= INCH_5;
  ADC10CTL0 |= SREF_1 + ADC10SHT_1 + REFON + ADC10ON;
  ADC10CTL0 |= ENC + ADC10SC;
  while(ADC10CTL1 & ADC10BUSY);
  return ADC10MEM & 0x01;
}

Pin 1.5 is floating unconnected, and is measured by the adc. The LSB is used as the random bit. There’s a twist though. Pin 1.4 is also floating unconnected, and is used as Vref+, so the top end of the range is floating as well. I thought that was pretty clever. Nice, TI!

I wrote a few console programs to help me visualize the randomness. It turned out the generator was biased toward producing 0′s. This function used with the previous function seemed to remove the bias:

EDIT: it was just dumb luck that the following function removed the bias. I did the math afterward, and this 0′s the bias when getRandomBit() returns a 1 30% of the time. I guess that means the ’0′ to ’1′ ratio of getRandomBit() is close to 7 to 3. Not great. I’m mulling over the idea of using laser speckle as a cheap way to generate the randomness.

bool get0BiasRandomBit(){
  if(getRandomBit()){
    if(getRandomBit())
      return 0;
    else
      return 1;
  }
  else{
    if(getRandomBit())
      return 1;
    else
      return 0;
  }
}

The following examples require the Full-duplex software UART for launchpad library, which was assembled by Rickta59.

To test for bias, I displayed a meandering line in a serial console. If I get more 0′s than 1′s, the line should slowly skew to the left or right:

#include "msp430g2231.h"
#include "config.h"
#include "softserial.h"
#include <stdbool.h>

#define CONSOLE_WIDTH          80

bool getRandomBit();
bool get0BiasRandomBit();

void main(){
  int linePositon = CONSOLE_WIDTH / 2;
  char cursorPosition;
  
  
  DCOCTL = CALDCO_16MHZ;
  BCSCTL1 = CALBC1_16MHZ;
  
  WDTCTL = WDTPW + WDTHOLD;          // Stop WDT
  
  SoftSerial_init();
  _enable_interrupts();
  
  while(1){
    for(cursorPosition = 0; cursorPosition < linePositon; cursorPosition++)
      SoftSerial_xmit('8');
    
    SoftSerial_xmit(' ');
    cursorPosition++;
    while(cursorPosition < CONSOLE_WIDTH){
      SoftSerial_xmit('8');
      cursorPosition++;
    }
    
    if(get0BiasRandomBit())
      linePositon++;
    else
      linePositon--;
    
    if(linePositon < 0)
      linePositon = CONSOLE_WIDTH + linePositon;
    else if(linePositon >= CONSOLE_WIDTH - 1)
      linePositon = linePositon - CONSOLE_WIDTH;
  }
}

bool get0BiasRandomBit(){
  if(getRandomBit()){
    if(getRandomBit())
      return 0;
    else
      return 1;
  }
  else{
    if(getRandomBit())
      return 1;
    else
      return 0;
  }
}

bool getRandomBit(){
  ADC10CTL1 |= INCH_5;
  ADC10CTL0 |= SREF_1 + ADC10SHT_1 + REFON + ADC10ON;
  ADC10CTL0 |= ENC + ADC10SC;
  while(ADC10CTL1 & ADC10BUSY);
  return ADC10MEM & 0x01;
}

and some example output from this code:

This sends a comma-separated list of random ints to the console:

#include "msp430g2231.h"
#include "config.h"
#include "softserial.h"
#include <string.h>

int adcGenRand16();
void reverse(char s[]);
void itoa(int n, char s[]);
void txString(char string[]);

void main(){
  int random;
  char string[7];
  
  DCOCTL = CALDCO_16MHZ;
  BCSCTL1 = CALBC1_16MHZ;
  
  WDTCTL = WDTPW + WDTHOLD;          // Stop WDT
  
  SoftSerial_init();
  _enable_interrupts();
  
  while(1){
    random = adcGenRand16();
    itoa(random, string);
    txString(string);
  }
}

void txString(char string[]){
  int iString = 0;
  while(string[iString] != 0){
    SoftSerial_xmit(string[iString]);
    iString++;
  }
  SoftSerial_xmit(',');
  SoftSerial_xmit(' ');
}

int adcGenRand16(){
  char bit;
  unsigned int random;
  
  for(bit = 0; bit < 16; bit++){
    ADC10CTL1 |= INCH_5;
    ADC10CTL0 |= SREF_1 + ADC10SHT_1 + REFON + ADC10ON;
    ADC10CTL0 |= ENC + ADC10SC;
    while(ADC10CTL1 & ADC10BUSY);
    random <<= 1;
    random |= (ADC10MEM & 0x01);
  }
  return random;
}

 /* itoa:  convert n to characters in s */
 void itoa(int n, char s[])
 {
     int i, sign;
 
     if ((sign = n) < 0)  /* record sign */
         n = -n;          /* make n positive */
     i = 0;
     do {       /* generate digits in reverse order */
         s[i++] = n % 10 + '0';   /* get next digit */
     } while ((n /= 10) > 0);     /* delete it */
     if (sign < 0)
         s[i++] = '-';
     s[i] = '\0';
     reverse(s);
 }
 
 /* reverse:  reverse string s in place */
 void reverse(char s[])
 {
     int i, j;
     char c;
 
     for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
         c = s[i];
         s[i] = s[j];
         s[j] = c;
     }
 }

I guess I got a little sidetracked from my original purpose of getting started with the RF2500, but I had fun.

MIDI Synth is polyphonic

I did some work on the MIDI synth to make it polyphonic. With the help of George Lucas and John Williams, I was able to win the 43oh.com April 2011 Project of the Month Contest. Check out my thread on the 43oh forums for the details and to find out what this has to do with George Lucas.

It uses a cheap DAC breakout from Sparkfun and an LM386.

If you use MSP430 for fun or profit, the 43oh forums are a great resource with helpful people, so check them out!

MSP430 TV output update

Just a quick update. With the new MSP430G’s and their bigger flash space, I was able to increase the resolution of my TV output program. The resolution is now 192×240. When the new 16K MSP430G’s arrive, I plan on increasing this to 384×240, which will finally approach the native aspect ratio.

I just had to change a couple lines of code to get this going, and include the larger image file. I used an MSP430G2452 with 8K flash.

NOTE: This should be compiled in CCS under debug mode. Using release mode or different compilers will likely require adjustment of the software delay toward the bottom of the code.

Here’s the code.

The example images seen below:
miss_nature_close.h
miss_nature_wide.h

If you want to make your own images to use, first create a 192×240 monochrome bitmap. To convert the bitmap into a header.h file, I used a program called Image2Code from CrystalFontz. Use this setting to get the proper image format:

Arduino-like analogRead() and timing functions for the MSP430

I saw this article on Hack a Day today for a library that supports some Arduino-like functions. I thought I’d share a couple of code snippets I have for analogRead(), millis(), and delayMillis(). The delay function on the HaD article used a software delay loop. My millis() and delayMillis() use the WDT.

Here’s my take on the time functions:


#include "msp430g2231.h"

#define MCLK_FREQUENCY      1000000
#define WDT_DIVIDER        512

const unsigned long WDT_FREQUENCY = MCLK_FREQUENCY / WDT_DIVIDER;
volatile unsigned long wdtCounter = 0;

unsigned long millis(){
  return wdtCounter / ((float)WDT_FREQUENCY / 1000);
}

void delayMillis(unsigned long milliseconds){
  unsigned long wakeTime = wdtCounter + (milliseconds * WDT_FREQUENCY / 1000);
  while(wdtCounter < wakeTime);
}

void main(void){
  DCOCTL = CALDCO_1MHZ;
  BCSCTL1 = CALBC1_1MHZ;
  WDTCTL = WDTPW + WDTTMSEL + WDTIS1;
  IE1 |= WDTIE;
  _BIS_SR(GIE);
  
  // toggle pin every second
  P1DIR |= BIT0;
  while(1){
    P1OUT ^= BIT0;
    delayMillis(1000);
  }
}
    
#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer(void){
  wdtCounter++;
}

And here’s the analog functions:


#include "msp430g2231.h"

#define ANALOG_PIN      4

unsigned int analogRead(){
  ADC10CTL0 |= ADC10SC;
  while(ADC10CTL1 & ADC10BUSY);
  return ADC10MEM;
}

void analogPinSelect(unsigned int pin){
  if(pin < 8){
    ADC10CTL0 &= ~ENC;
    ADC10CTL1 = pin << 12;
    ADC10CTL0 = ADC10ON + ENC + ADC10SHT_0;
  }
}

void main(void){
  unsigned int analogValue;
  
  DCOCTL = CALDCO_1MHZ;
  BCSCTL1 = CALBC1_1MHZ;
  WDTCTL = WDTPW + WDTHOLD;
  
  // read adc repeatedly
  analogPinSelect(ANALOG_PIN);
  while(1){
    analogValue = analogRead();
  }
}

They’re not pure Arduino, but close.

MSP430 Launchpad Simple MIDI Synth

It’s been awhile since I’ve posted anything, but I’ve been working on several projects. I’ve just been feeling lazy about writing them up. I thought this one might be the most enjoyable and useful to people. I’m going to keep it short and sweet, so please comment if you have any questions.

This is a simple square wave synthesizer. It has a standard midi interface, so it could be used with a midi keyboard or another midi controller.

Here’s the BOM:
1x TI Launchpad or another MSP430
1x 280 ohm resistor (can use internal pull-up instead)
1x 220 ohm resistor
1x Sharp PC900V optoisolator
1x Speaker (I just bought one from RadioShack)
1x MIDI female jack
1X diode
A midi cable and some kind of midi controller

You could probably get away with not using the optoisolator, but I felt like adhering to spec with the hardware. If you want to use another MSP430, be sure it has a USI and not a USCI, or you’ll have to rewrite some of the code. Also, the code expects to have the DCO calibrated for 16MHz. I think an approximation would work too, but if you’re too far off the midi clock will skew and the notes could be detuned as well.

The speaker gets hooked up to Launchpad ground and P1.2.

On the software side, the USI runs constantly and oversamples the midi data from the optoisolator. The oversampling is resolved with something like a state machine(?). I tried to use the USI properly, but I always seem to run into issues in using it with clockless serial. There’s probably a cleaner way to read the midi data, but it works, so I don’t mind. It’s all yours to fix!

Once we’re able to read the MIDI, it’s just a matter of properly adjusting the period of TimerA, the square wave sound generator, to get the proper note. The synth is monophonic, so some care is also taken to handle and store simultaneous on-notes.

Here’s the MIDI Synth main.c. This compiles on CCS.

By the way, you can use one optoisolator circuit to send data to multiple launchpads. Just assign each LP a different MIDI channel by changing this line of code:
#define MIDI_CHANNEL 0

So I hooked it up to my midi keyboard. That was OK. Then I downloaded some midi files and routed them through a USB – MIDI converter with Ableton Live. Now that gave me a warm, nerdy feeling inside.
Check it out:
Get Back

Zelda

Tainted Love

Try Again

TV Output Prototype


It still needs a battery holder. I might just buy one.

Return top