Sunday, September 9, 2018

Dumbot Code

This is the operating code for a robot I built called Dumbot.  It only has a motion sensor and moves randomly.  The code generates three random numbers: speed, time, movement type.  There are five different movement types: Forward, backward, turn right, turn left, and stop.  There is a check routine at the  beginning: Forward, right, backward, left.  To check to make sure the motors are installed properly.  This code was tested on a PIC16F1825, using hobby motors from Sparkfun and the brushed motor controller breakout board from Sparkfun.  A switched battery holder with three double A batteries from Jameco was used.  The wheels and frame were 3D printed.  An LED output and an audio output were included.

In order to make sound I use a 386 audio amplifier and a PCB mounted speaker.  I wanted the robot sound to be coming from the robot operations themselves, and I wanted it to be a bit like R2D2 so I send the PWM from the motors to the output.  The one trick I added was to increase the frequency of the pulses when the motor goes faster, so the speaker makes a higher pitched sound when it runs faster.



/* 
 * File:   R06Main.c
 * Author: hmikelson
 *
 * Created on September 4, 2018, 6:11 PM
 */

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>

#pragma config MCLRE=OFF,CP=OFF,WDTE=OFF,FOSC=INTOSC
#define _XTAL_FREQ 16000000
unsigned char sPORTA, sPORTC, pwmax=31;

void init()
{
    //Configure GPIO Port
    TRISA = 0b110000000;  //Set all Output
    TRISC = 0b110001000; //Set up pin 3 for input
    OSCCONbits.IRCF = 0b1111;
    OSCCONbits.SCS = 0b00;
    //OPTION_REG = 0b01111111; //Global enable weak pullups
    //WPUC = 0b00001000;     //Enable weak pullups with a 1
    // Set up analog input
    ANSELCbits.ANSC3 = 1;
    ADCON0 = 0b00011101; //Channel (CHS4:0 bits 6-2),ADON bit 1 enable Analog
    ADCON1bits.ADCS = 0b111; // clock select dedicated osc 
    ADCON1bits.ADNREF = 0; //neg ref
    ADCON1bits.ADPREF = 0; //config + voltage with vdd
    ADCON1bits.ADFM = 1; //format left justified
    ADRESH = 0x00;        //Init the AD Register
    ADRESL = 0x00;       // I don't think this is needed

    //OSCCONbits.SPLLEN = 0b1;
}

void vdelay(int n)
{
    int i;
    for (i=0;i<=n;i++)
    {
     __delay_us(100);
    }
}

int read_v()
// Read voltage from input
 {
    int val,v1,v2;
    __delay_ms(1);
    ADCON0bits.GO_nDONE = 1;
    while(ADCON0bits.GO_nDONE == 1);
    v1 = ADRESH;
    v2 = ADRESL;
    val = (v1<<8) + v2;
    return val;
 }

// Drive robot forward
void RobotFd(int time, unsigned char speed)
{
    int i, j;
    for (i=0;i<time;i++)
    {
        for (j=0;j<pwmax;j++)
        {
            if (j<speed)
            {
                PORTA = 0b00111110;  // 
                PORTC = 0b00010110;  // Enable on pin 4
                vdelay(pwmax/speed);
            }
            else
            {
                PORTA = 0b00000110; // I think there is a  clock output on pin 4 which I don't know how to disable
                PORTC = 0b00010010; // Enable on pin 4
                vdelay(pwmax/speed);
            }
            
        }
        
    }
}

// Drive Robot backward
void RobotBk(int time, unsigned char speed)
{
    int i, j;
    for (i=0;i<time;i++)
    {
        for (j=0;j<pwmax;j++)
        {
            if (j<speed)
            {
                PORTA = 0b00111101;
                PORTC = 0b00010101;
                vdelay(pwmax/speed);
            }
            else
            {
                PORTA = 0b00000101;
                PORTC = 0b00010001;
                vdelay(pwmax/speed);
            }
            
        }
        
    }
}
void RobotRt(int time, unsigned char speed)
{
    int i, j;
    for (i=0;i<time;i++)
    {
        for (j=0;j<pwmax;j++)
        {
            if (j<speed)
            {
                PORTA = 0b00111110;
                PORTC = 0b00010101;
                vdelay(pwmax/speed);
            }
            else
            {
                PORTA = 0b00000110;
                PORTC = 0b00001001;
                vdelay(pwmax/speed);
            }
            
        }
        
    }
}
void RobotLt(int time, unsigned char speed)
{
    int i, j;
    for (i=0;i<time;i++)
    {
        for (j=0;j<pwmax;j++)
        {
            if (j<speed)
            {
                PORTA = 0b00111101;
                PORTC = 0b00110110;
                vdelay(pwmax/speed);
            }
            else
            {
                PORTA = 0b00000101;
                PORTC = 0b00001010;
                vdelay(pwmax/speed);
            }
            
        }
        
    }
}

void RobotSt(int time)
{
    int i;
    for (i=0;i<time;i++)
    {
    PORTA = 0b00000100;
    PORTC = 0b00001100;
    __delay_ms(10);
    }

}

void main()
 {
  int rmode, rtime, volts,mt=31;
  unsigned char rspeed, nactions=0;
  init();
  RobotFd(100,pwmax/2);
  RobotSt(1);
  RobotRt(100,pwmax/2);
  RobotSt(1);
  RobotBk(100,pwmax/2);
  RobotSt(1);
  RobotLt(100,pwmax/2);
  RobotSt(1);
  while(1)
   {
     if (nactions > 0)
     {
         rmode=(rand() & 7) + 1;
         nactions=nactions-1;
     }
     else
     {
         rmode = 0;
     }
     rtime = (rand() & 7)+1;
     rspeed = rand() & pwmax;
     switch (rmode)
      {
       case 1:
        RobotFd(rtime*mt,rspeed);
        RobotSt(1);
        break;
       case 2:
        RobotBk(rtime*mt,rspeed);
        RobotSt(1);
        break;
       case 3:
        RobotRt(rtime*mt,rspeed);
        RobotSt(1);
        break;
       case 4:
        RobotLt(rtime*mt,rspeed);
        RobotSt(1);
       break;
       case 0:
        RobotSt(1);
        volts = read_v();
        if (volts>50)
         {
            nactions = 10;
            //blink(volts);
         }           
       break;
       default:
           RobotSt(10);
        break;
      }
   }
 }

Monday, April 2, 2018

Updated Motor Controller Code

This code uses pulses to control a motor to very slow speeds.  This code is for a 12f683 pic microcontroller.

//Motor Controller
#if defined(__XC)
    #include <xc.h>         /* XC8 General Include File */
#elif defined(HI_TECH_C)
    #include <htc.h>        /* HiTech General Include File */
#endif

#include <stdint.h>        /* For uint8_t definition */
#include <stdlib.h>

//#fuses NOMCLR,NOPROTECT,NOWDT,INTRC
#pragma config MCLRE=OFF,CP=OFF,WDTE=OFF,FOSC=INTOSCIO
#define _XTAL_FREQ 4000000

void init()
{
    //Configure GPIO Port
    //ANSEL =  0b00000001;  //Configure GPIO pins as digital GP0 set to analog
    ANSEL =  0b01111000;  //Configure GPIO pins as digital, GP4 Analog

    TRISIO = 0b11011000;  //Set GP1,GP2,GP4,GP5 as outputs
    WPU = 0x00;           //Disable weak pullups
    //Configuer AD Convertor
    ADCON0 = 0b10001101;        //AD Set up
    ADRESH = 0x00;        //Init the AD Register
    //Configure Comparator
    CMCON0 = 0xFF;   // Comparator is turned off
    CMCON1 = 0x00;   // Comparator is turned off
    //Interrupt configuration
    INTCON = 0x00;   //Disable all interrupts

    OPTION_REGbits.nGPPU = 0;
    WPU = 1<<1;     //Enable weak pullups on GP1
    //Configuer AD Convertor
    //ADCON0 = 0x00;        //AD disabled
    //ADRESH = 0x00;        //Init the AD Register
        //Configuer AD Convertor

}

int read_v()
// Read voltage from input 0
 {
    int val;
    ADCON0bits.GO=1;
    while(ADCON0bits.nDONE);
    val = (ADRESH<<2)+(ADRESL>>6);
    return val;
 }

void vdelay(int n)
 {
  int i;

  for (i=0;i<n;i++)
   {
    __delay_us(100);
   }
 }

void pwgen(int pw)
// Send pulse width
 {
  int i, n=4; // Standard time interval of 400 microseconds

  if (pw>56) // Switch over to max speed
  {
      pw = 255;
      n=1;
  }
  if (pw<16) // May not have enough drive for the motor
   {         // at low speeds, so switch to longer pulses
      n = 10;// 100 microseconds
   }
  for (i=0;i<255;i++)
   {
    if (i<pw) // Turn motor on for a fraction of the time
     {
      GPIO = 0b00000010; // Motor is connected to output 2
      vdelay(n);
     }
    if (i>pw)
     {
      GPIO = 0b00000000;
      vdelay(n);
     }
   }
 }

void main()
{
    uint8_t db_cnt;
    int v;
    init();
    GPIO = 0b00000000;
    while(1)
    {
     v = read_v();
     pwgen(v<<2); // Voltage seems to read between 0 and 15
    }

}

Thursday, February 1, 2018

Oberheim Matrix 6R Patching

I bought an Oberheim Matrix 6R used a music store in uptown Minneapolis back in the late 1980's for $500.  The Matrix series consists of 6 voice polyphonic analog synthesizers including the Matrix 6 keyboard version, the Matrix 6R rackmount, and the Matrix 1000 with 1000 preset patches.  I was original looking for a Matrix 1000 but the salesperson talked me into getting the Matrix 6R because it was programmable from the front panel.


My first impressions were not overly favorable, I think I was hoping for a Moog sounding filter and the filters at high resonance sounded very harsh.  It did have some classic Oberheim sounding horns which I liked, their was an oscillator sync patch called "Ai-Ai" which was really good but generally I was not overly impressed with the sound.

The real problem with the synthesizer was the programming interface, which was done completely through panel buttons and not a single knob or slider on the entire panel.  After awhile I got pretty fast at it though.

I began using it less and less, it was a bit too big to take out to a gig, I think I brought it to one gig.  After one of my studio rearrangements I didn't bother to even hook it up.

So now it has probably been about 10 years since I last used it and have been looking at the new polyphonic analog synthesizers on the market and longing for one when I thought I would give the M6R another try.  This is really a very interesting synthesizer.

Besides the standard features you might expect on any analog synthesizer, the M6R has a bunch of features that make it really interesting.
  • 2 DCOs (Digitally controlled oscillators)
  • 1 VCF Lowpass
  • 2 VCAs
  • 3 Envelopes
  • 2 LFOs
  • 2 Ramp generators
  • Portamento
  • Tracking generator
  • Lag processor
  • 10 point Matrix
So far, so good, a nice analog synth very similar to many others.  But since this is polyphonic there are six identical copies of all of these features.  The M6R can also play poly timbral with two different patches playing on the keyboard at the same time.

Next I want to look at the components in a little more detail.

DCOs

There has been quite a bit of complaining about the DCOs on this  synth, since it has DCOs instead of VCOs.  I don't have a problem with these oscillators.  They have a lot of functionality and they tune automatically which was one of the main reasons for them being digitally controlled if I am not mistaken.

There are actually three types of waveforms, pulse, saw, and both.  The pulse width can be modulated, the saw can produce a decent saw when set to 0 or triangle wave when set to 63, the triangle wave is actually almost a sine wave.  Both combines both waveforms together.

DCO1 can be synced and DCO2 can be detuned.  DCO2 can also be set to respond differently to the keyboard so you could actually have it fixed and not change at all.  The sync has three different settings from soft to medium to hard sync.

There are a number of preset connections, LFO1 is set up to modulate the pitch of the DCOs and oscillator 2 is set up to modulate the pulse width of the DCOs.  These can be routed manually in the matrix.

Both oscillators can also produce a click sound and DCO2 can also produce white noise.

There is a Unison mode where all 12 oscillators play at the same time.  Unfortunately they digital tuning is so effective that you can't really tell that there are 12 oscillators playing.  It would have been extremely cool if you would have been able to all of the oscillators.  Unison mode does seem to change the tone of the oscillators but I need to put them on a scope to see exactly what is happening.

VCF

So originally I really disliked the VCF.  I think I was expecting a Moog ladder sound and it was not like that at all.  Especially when you crank the resonance all the way up or get close to self oscillation it sounds particularly harsh.  It is a 4 pole lowpass only filter but sounds to me more like a two pole.

Listening to this with a little more maturity I like it much better.  I don't think polysynths sound that great at really high resonance anyway.  Back off on the Q and you can make some nice smooth pads with lots of motion.

One unique feature of the VCF is the ability to do a type of frequency modulation with DCO1 as the modulator and the VCF as the carrier.  This can be used to add some bell like sounds to the voice.  I need to spend more time experimenting with this to see if it can produce some more interesting effects.

VCA

Not too much to say about this.  I don't notice any noise or anything.  One VCA just takes a straight number for volume and the other is tied to envelope 2.

Tracking Generator

The tracking generator basically reshapes a control source in a way similar to wave shaping.  This is a feature not found on your typical analog synth (of course you can probably get on modulars).  For instance instead of having a triangle wave for the LFO you could make it into sort of a house shape.  Instead of having the keyboard increase the pitch in single steps you could have go up then down.  Applying this to an envelop could give you a much more complex envelope shape.  This was hard for me to understand at first but now that I look at it I can see it could be extremely powerful.  On the other hand, I have not really tried to mess around with it yet so I could be off base on its capabilities.

Portamento

There is a portamento feature (glide) that enables you to slide between the pitches.  It has three settings, linear, constant time, and exponential.  You could set the synth in Unison mode to have a gliding monosynth if you wanted to but mono's do this better so don't bother.  It does a nice polyphonic glide.

Envelopes

The ADSR envelopes also have a delay section at the beginning to delay the start of the envelope so they are actually five stage envelopes.  It can also be set to DADR mode delay, attack, decay, release.  There is also a free run mode that lets the envelope finish its full cycle, basically generating its own gate once it is triggered.

LFO

The LFO has the waveforms triangle, saw up, saw down, square, and random (noise sample and hold), noise, and sample and hold.  The LFOs have a lag processor so you can take the edge off of a square wave.  The sample and hold mode can sample another modulation source.

Matrix

The matrix is a virtual patch system which can handle up to 10 interconnections between the synthesizer elements.

The 20 control inputs are: Envelope 1-3, LFO 1&2, Vibrato (a special LFO), Ramp 1&2, Keyboard, Portamento, Tracking generator, Keyboard gate, Velocity, Release velocity, Pressure, Pedal 1 & 2, Lever 1-3.

The 32 destinations are: DCO Fqc, PW, Wave shape, (x2) Mix, VCF FM, Fqc, Q, VCA Volume (x2), Env Delay, Atk, Dec, Rel, Amp (x3), LFO speed, amplitude (x2), Poramento rate.

System Start Up

I powered up the system and after sitting for all those years most of the patches were gone messed up.  I think it is probably due for a new battery.  At first when I was using the system it was generating random errors and locking up.  I opened up the case and vacuumed out the dust and rubbed a dry paint brush over some of the circuits to clean them.  I couldn't see anything else wrong with it so I put it back together.  It seems to be working fine now but will probably put a new battery in it next time I do a Jameco order.  It looks like Jameco has battery clips that should fit on this board.

I created a number of patches which I will try to share here.  I created a template for recording patches since I don't have a computer with a MIDI connection near this system.

Here are a couple of patches: