Close

Results 1 to 10 of 10
  1. #1

    Default Reading a BCF-2000 from an Arduino Board

    For those who are interested in such things, I took apart an old Behringer BCF-2000 because I needed some motorized faders for a project (and buying a BCF-2000 is about the cheapest way to obtain 8 motorized faders when you have an old unit laying around; motorized faders seem to go for about $20 each).

    My original intent was to wire the fader pots directly into the analog inputs on the Arduino MEGA 2560 board I'm using. However, I notice that the Berhinger circuit board contains a TI TLC1542C SPI-bit A/D converter which I presumed was being used to read the 8 pots. I traced the pins on the TLC1542C to the 40-pin connector coming off the board (to the microcontroller board in the BCF-2000) and wired that chip directly into the Arduino's SPI bus. A few lines of code later and I was reading all the fader pots on the BCF-2000 fader circuit board.

    Haven't gotten around to tracing the circuit to the motors yet, but I know that a couple of people out there were talking about using Arduinos to build a custom control surface. Thought the following code would be handy:



    Code:
    #include <SoftwareSerial.h>
    
    #include <SPI.h>
    
    /*
      Wiring on Behringer BCF-2000 fader circuit board (to Arduio MEGA2560)
      
      40-pin ribbon connector:
      
        |                                                         | Red strip along this edge
        |                                                         |
        |                                                         |
        |                                                         |
        40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21
        [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] []
        [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] []
        20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1
        
        pin 1 on cable is Vcc (+5v)
        pin 11 on cable is Gnd
        
        pin 26 is the TLC1542C CS pin (SPI bus SS signal, connect to pin 53 on Arduino MEGA2560 board)
        pin 25 is the TLC1542C Address pin (SPI bus MOSI signal, connect to pin 51 on MEGA2560 board)
        pin 5 is the TLC1542C Data out pin (SPI bus MISO signal, connect to pin 50 on MEGA2560 board)
        pin 6 is the TLC1542C I/O clock pin (SPI bus SCK signal, connect to pin 52 on MEGA2560 board)
        pin 7 is the "end of conversion" pin. (Connect to pin 48 on the MEGA2560 board) 
    */
    
    void setup() {
      Serial.begin( 9600 );
      SPI.begin();
      SPI.setClockDivider(SPI_CLOCK_DIV64);   // The TLC1542C chip on the BCF-2000 is *slow*
      SPI.setBitOrder( MSBFIRST );            // Data arrives most significant bit first
      digitalWrite( SS, HIGH );               // Initialize with TLC1542C unselected
      pinMode( 48, INPUT );                   // EOC on TLC1542 comes in on pin 48 as an input
      
      
    }
    
    void loop() {
      unsigned char resultLow, resultHigh;
      unsigned short result;
        
     Serial.print( "  Pot " );
     for( int i=0; i <=7; ++i )
      {
        digitalWrite( SS, LOW );
        SPI.transfer( i<<4 );              // Select Pot #i (0..7) before we begin
        while( digitalRead(48));           // Wait for conversion to complete
        resultLow = SPI.transfer(i<<4);    // Get the L.O. data byte (only two bits here)
        resultHigh= SPI.transfer(i<<4);    // Get the H.O. data byte (remaining 8 of 10 bits)
        result = (resultLow >> 6) + (resultHigh << 2); // Compute 10-bit result.
        digitalWrite( SS, HIGH );          // Turn off the chip
        Serial.print( i );
        Serial.print( "=" );
        Serial.print( result );
        if( i != 7 ) Serial.print( ", " );
      }
     delay(1000);
     Serial.println("" );
    
    }
    This produces output like the following (faders start at the top and wind up at the bottom as you go across the eight faders):

    Code:
      Pot 0=1014, 1=832, 2=650, 3=521, 4=384, 5=253, 6=199, 7=10
      Pot 0=1014, 1=832, 2=650, 3=521, 4=384, 5=253, 6=199, 7=11
      Pot 0=1014, 1=832, 2=650, 3=521, 4=384, 5=253, 6=199, 7=11
      Pot 0=1014, 1=832, 2=650, 3=521, 4=384, 5=253, 6=199, 7=11
      Pot 0=1014, 1=832, 2=650, 3=521, 4=384, 5=253, 6=199, 7=11
    Now I've got to trace the circuits to the motors and figure out which pins on the 40-pin cable control the motors.
    Cheers,
    Randy Hyde
    -------------------
    For the interested, SAC setup here: http://www.plantation-productions.co.../SACSetup.html
    Plantation Productions:http://www.plantation-productions.com

  2. #2
    Join Date
    Feb 2010
    Location
    Quad Cities Il
    Posts
    736

    Default Re: Reading a BCF-2000 from an Arduino Board

    Thanks for the info it will save me some time on an up coming project

    Butch

  3. #3

    Default Re: Reading a BCF-2000 from an Arduino Board

    And here is the code that drives the motors.
    I really should have a PID algorithm, but my old BCF-2000 unit is so messed up (some faders are really slow, as in *really sloooooow*, so I kludged in speed constants for my particular unit).

    Code:
    #include <SoftwareSerial.h>
    
    #include <SPI.h>
    
    
    
    // Pin # constants:
    
    const int d0 = 22;
    const int d1 = 23;
    const int d2 = 24;
    const int d3 = 25;
    const int d4 = 26;
    const int d5 = 27;
    const int d6 = 28;
    const int d7 = 29;
    const int chipA=30;
    const int chipB=31;
    const int datapin=32;
    const int mr=33;
    
    int potMax[8] = {0,0,0,0,0,0,0,0};
    int potMin[8] = {1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023};
    int motorSpeeds[8] = {2, 2, 2, 2, 5, 2, 20, 2};
    int motorSpeeds2[8] = {2, 2, 2, 2, 3, 3, 2, 2};
    
    
    /*
      Wiring on Behringer BCF-2000 fader circuit board (to Arduio MEGA2560)
      
      40-pin ribbon connector:
      
        |                                                         | Red strip along this edge
        |                                                         |
        |                                                         |
        |                                                         |
        40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21
        [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] []
        [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] []
        20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1
        
        pin 1 on cable is Vcc (+5v)
        pin 11 on cable is Gnd
        
        pin 26 is the TLC1542C CS pin (SPI bus SS signal, connect to pin 53 on Arduino MEGA2560 board)
        pin 25 is the TLC1542C Address pin (SPI bus MOSI signal, connect to pin 51 on MEGA2560 board)
        pin 5 is the TLC1542C Data out pin (SPI bus MISO signal, connect to pin 50 on MEGA2560 board)
        pin 6 is the TLC1542C I/O clock pin (SPI bus SCK signal, connect to pin 52 on MEGA2560 board)
        pin 7 is the "end of conversion" pin. (Connect to pin 48 on the MEGA2560 board) 
        
        pin 14 is the clock pin that sets +voltage to D0..D7 (pots 1-8), connect to pin 30 on MEGA2560
        pin 34 is the clock pin that sets -voltage to D0..D7 (pots 1-8), connect to pin 31 on MEGA2560
        
        pin 16 is D0, connect to pin 22 on MEGA2560
        pin 36 is D1, connect to pin 23 on MEGA2560
        pin 17 is D2, connect to pin 24 on MEGA2560
        pin 37 is D3, connect to pin 25 on MEGA2560
        pin 18 is D4, connect to pin 26 on MEGA2560
        pin 38 is D5, connect to pin 27 on MEGA2560
        pin 19 is D6, connect to pin 28 on MEGA2560
        pin 39 is D7, connect to pin 29 on MEGA2560
        
    */
    
    
    // readADC-
    //  Reads the ADC value (10 bits) for the specified pot:
    
    int readADC( int pot )
    {
        int result;
        int resultLow;
        int resultHigh;
        
        if( pot > 7 ) pot = 7;
        if( pot < 0 ) pot = 0;
        
        digitalWrite( SS, LOW );             // Activate chip select line.
        SPI.transfer( pot<<4 );              // Select Pot #i (0..7) before we begin
        while( digitalRead(48));             // Wait for conversion to complete
        resultLow = SPI.transfer(pot<<4);    // Get the L.O. data byte (only two bits here)
        resultHigh= SPI.transfer(pot<<4);    // Get the H.O. data byte (remaining 8 of 10 bits)
        result = (resultLow >> 6) + (resultHigh << 2); // Compute 10-bit result.
        digitalWrite( SS, HIGH );          // Turn off the chip
        return result;
    }
    
    
    // moveDown-
    // Moves the specified pot (pot argument) towards the bottom.
    // Time of travel is determined by time x (timeOn-timeOff).
    // timeOn and timeOff provide a pseudo-duty cycle to slow
    // down the motor and make positioning more accurate.
    
    void moveDown( int pot, int time, int timeOn, int timeOff )
    {
      // Argument check:
      
      if( pot > 7 ) pot = 7;
      if( pot < 0 ) pot = 0;
        
      // Repeat this "time" times to generate some speed.
      
      for( int i=0; i < time; ++i )
      {
        digitalWrite( d0+pot, HIGH );   // Put high on data line
        digitalWrite( chipA, LOW );     // and strobe the clock on
        digitalWrite( chipA, HIGH );    // the 74HC273 chip.
        digitalWrite( chipA, LOW );
        delay( timeOn );                // Delay with power on
        digitalWrite( d0+pot, LOW );    // Put LOW on data line
        digitalWrite( chipA, LOW );     // and strobe the clock on
        digitalWrite( chipA, HIGH );    // the 74HC273 chip.
        digitalWrite( chipA, LOW );
        delay( timeOff );
      }
    }
    
    // moveUp-
    // Moves the specified pot (pot argument) towards the top.
    // Time of travel is determined by time x (timeOn-timeOff).
    // timeOn and timeOff provide a pseudo-duty cycle to slow
    // down the motor and make positioning more accurate.
    
    void moveUp( int pot, int time, int timeOn, int timeOff )
    {
      // Argument check:
      
      if( pot > 7 ) pot = 7;
      if( pot < 0 ) pot = 0;
        
      // Repeat this "time" times to generate some speed.
      
      for( int i=0; i < time; ++i )
      {
        digitalWrite( d0+pot, HIGH );  // Put high on data line
        digitalWrite( chipB, LOW );    // and strobe the clock on
        digitalWrite( chipB, HIGH );   // the 74HC273 chip.
        digitalWrite( chipB, LOW );
        delay( timeOn );              // Delay with power on
        digitalWrite( d0+pot, LOW );  // Put LOW on data line
        digitalWrite( chipB, LOW );   // and strobe the clock on
        digitalWrite( chipB, HIGH );  // the 74HC273 chip.
        digitalWrite( chipB, LOW );
        delay( timeOff );            // Delay with power off
      }
    }
    
    
    
    // Calibrate all the faders on the BCF-2000
    // to compute their minimum and maximum values.
    
    void calibrate( void )
    {
      int potVal;
      int PotDone;
      int PotCnt;
      int PotLast;
      
      // Compute the maximum position for each pot:
      
      for( int pot=0; pot<8; ++pot)
      {
         potMax[pot] = 0;
         PotCnt  = 0;
         PotDone = 0;
         PotLast = 0;
         while( !PotDone )
          {
            moveUp( pot, 10, 2, 1 );
            potVal = readADC( pot );
            if( PotLast >= potVal )
            {
                // When we're stuck at the current location
                // for two consecutive calls, assume we've
                // jammed and this is as far as we can go.
                
                ++PotCnt;
                PotDone = PotCnt > 2;
            }
            else
            {
                PotCnt = 0;
            }
            PotLast = potVal;
            if( potVal > potMax[pot] )
            {
              potMax[pot] = potVal;
            }
          }
          Serial.print( "maxPot[" );
          Serial.print( pot );
          Serial.print( "]=" );
          Serial.println( potMax[pot] );
      }
    
      // Compute minimum position for each pot.
      
      for( int pot=0; pot<8; ++pot)
      {
          PotCnt  = 0;
          PotDone = 0;
          PotLast = 1024;
          potMin[pot] = 1024;
          while( !PotDone )
          {
            moveDown( pot, 10, 3, 2 );
            potVal = readADC( pot );
            if( PotLast <= potVal )
            {
                // When we're stuck at the current location
                // for two consecutive calls, assume we've
                // jammed and this is as far as we can go.
                
                ++PotCnt;
                PotDone = PotCnt > 2;
            }
            else
            {
                PotCnt = 0;
            }
            PotLast = potVal;
            if( potVal < potMin[pot] )
            {
              potMin[pot] = potVal;
            }
          }
          Serial.print( "minPot[" );
          Serial.print( pot );
          Serial.print( "]=" );
          Serial.println( potMin[pot] );
      }
    }
    
    
    // potGoto-
    // Positions the specified pot (pot argument) at the location specified (0..1024)
    
    void potGoto( int pot, int position )
    {
      
      int desiredPosn;
      int curPosn = readADC( pot );  
      
      // Validate arguments.
      
      if( pot > 7 ) pot = 7;
      if( pot < 0 ) pot = 0;
        
      if( position > potMax[pot] )
      {
        desiredPosn = potMax[pot];
      }
      else if( position < potMin[pot] )
      {
        desiredPosn = potMin[pot];
      }
      else
      {
        desiredPosn = position;
      }
       
      // Determine which direction we're going.
      // Note: This code will overshoot by a bit.
      // Really need a PID algorithm here. However, my
      // BCF-2000 is so messed up that it wouldn't
      // buy me anything to add it.
      
      if( desiredPosn < curPosn )
      {
        // Need to drive fader down to specified position
        
        while( desiredPosn < curPosn )
        {
          // Note: timeOn/timeOff parms are a kludge for my board.
          moveDown( pot, 1, motorSpeeds[pot], motorSpeeds2[pot] );     
          curPosn = readADC( pot );
        }    
      }
      else // desiredPosn >= curPosn
      {
        while( desiredPosn > curPosn )
        {
          // Note: timeOn/timeOff parms are a kludge for my board.
          moveUp( pot, 1,  motorSpeeds[pot], motorSpeeds2[pot] );
          curPosn = readADC( pot );
        }
      }
      Serial.print( "Pot: ");
      Serial.print( pot );
      Serial.print( ", GotoPosn: " );
      Serial.print( position );
      Serial.print( ", finalPosn: " );
      Serial.println( curPosn );
    }
    
    // Usual Arduino stuff:
    
    void setup() {
      Serial.begin( 9600 );
      SPI.begin();
      SPI.setClockDivider(SPI_CLOCK_DIV64);   // The TLC1542C chip on the BCF-2000 is *slow*
      SPI.setBitOrder( MSBFIRST );            // Data arrives most significant bit first
      digitalWrite( SS, HIGH );               // Initialize with TLC1542C unselected
      pinMode( 48, INPUT );                   // EOC on TLC1542 comes in on pin 48 as an input
      
      pinMode(d0, OUTPUT );
      pinMode(d1, OUTPUT );
      pinMode(d2, OUTPUT );
      pinMode(d3, OUTPUT );
      pinMode(d4, OUTPUT );
      pinMode(d5, OUTPUT );
      pinMode(d6, OUTPUT );
      pinMode(d7, OUTPUT );
      pinMode(chipA, OUTPUT );
      pinMode(chipB, OUTPUT );
      pinMode(mr, OUTPUT );
      digitalWrite( mr, LOW );
      digitalWrite( mr, HIGH );
      digitalWrite( d0, LOW );
      digitalWrite( d1, LOW );
      digitalWrite( d2, LOW );
      digitalWrite( d3, LOW );
      digitalWrite( d4, LOW );
      digitalWrite( d5, LOW );
      digitalWrite( d6, LOW );
      digitalWrite( d7, LOW );
      digitalWrite( chipA, LOW );
      digitalWrite( chipB, LOW );
      
      pinMode( 2, OUTPUT );
      pinMode( 3, OUTPUT );
      pinMode( 4, OUTPUT );
      
      // Calibrate the pots to determine their min and max positions:
      
      calibrate();
    }
    
    void loop() {
     
     for( int pot=0; pot < 8; ++pot )
     {
       for( int posn=100; posn < 900; posn+=100 )
       {
         potGoto( pot, posn );
       }
       for( int posn=900; posn > 100; posn-=100 )
       {
         potGoto( pot, posn );
       }
     } 
     
     delay(2000);
     Serial.println( "" );
    
    }
    Enjoy.

    Cheers,
    Randy Hyde
    Last edited by RandyHyde; 04-05-2015 at 11:49 PM.
    -------------------
    For the interested, SAC setup here: http://www.plantation-productions.co.../SACSetup.html
    Plantation Productions:http://www.plantation-productions.com

  4. #4

    Default Re: Reading a BCF-2000 from an Arduino Board

    FWIW, I've come to understand just how *terrible* the BCF-2000 motorized faders really are by working on this project.

    I take back what I said about purchasing a BCF-2000 being the cheapest way to buy a set of motorized faders. Better to pay $20 each and get good ALPS units.
    Cheers,
    Randy Hyde
    -------------------
    For the interested, SAC setup here: http://www.plantation-productions.co.../SACSetup.html
    Plantation Productions:http://www.plantation-productions.com

  5. #5

    Default Re: Reading a BCF-2000 from an Arduino Board

    Hi Randy,
    Yes the bcf faders suck, but the alps, p&g, and bourns faders are no better on a simple motor fader drivers board.

    The code requires a deceleration routine to slow the motor down as it approaches the target position.


    The bcf 2000 does not have this in it's code. That is why they slam at the bottom and top.

    Check out the German midi Controller site ucapps.de
    Last edited by KUI; 04-06-2015 at 06:54 AM. Reason: spelling

  6. #6

    Default Re: Reading a BCF-2000 from an Arduino Board

    Quote Originally Posted by KUI View Post
    Hi Randy,
    Yes the bcf faders suck, but the alps, p&g, and bourns faders are no better on a simple motor fader drivers board.

    The code requires a deceleration routine to slow the motor down as it approaches the target position.


    The bcf 2000 does not have this in it's code. That is why they slam at the bottom and top.

    Check out the German midi Controller site ucapps.de
    The big problem I'm finding on my (admittedly old and dirty) BCF-2000 is the fact that the lowest speed I can run the faders at (without stalling them) is still too fast for a PID algorithm (which handles acceleration and deceleration) to be practical. Also, the flex in the band driving the fader is so loose that accurate placement is next to impossible. I can see why the Behringer software runs the faders like a bat out of hell -- they tend to stall if you run them too slowly.

    A large part of the problem may very well be that I chose to take apart on ancient BCF-2000 with sticky faders. Perhaps an ALPS wouldn't do any better, but I have to believe that with a higher-quality motor you can successfully run the faders much slower than I've been able to achieve with the BCF-2000 unit I'm using (and running at a slow speed is the key to accurate fader placement).

    One advantage of the BCF-2000 circuit is it has the built-in A/D converters, latches (flip-flops, actually), motor driver transistors, and power supply.

    However, the circuit design driving the motors (a flip-flop) is *very* inconvenient for motor control. It would be nice to send a PWM signal to each motor to control the speed. The flip-flop circuits used in the BCF-2000 make this *very* difficult (especially as the data lines are common to the LED drivers, as well); a transparent latch would have been much better than a flip-flop, for example.

    For *my* purposes, the fact that the buttons appear to the right of the faders is also problematic. I'd really like to jam 24 faders together for the project I have in mind.

    The fact that that BCF-2000 can be so easily hacked is a really cool thing. That's why I passed the code along even though the BCF-2000 won't really work for what I have in mind.
    Cheers,
    Randy Hyde
    -------------------
    For the interested, SAC setup here: http://www.plantation-productions.co.../SACSetup.html
    Plantation Productions:http://www.plantation-productions.com

  7. Default Re: Reading a BCF-2000 from an Arduino Board

    "For *my* purposes, the fact that the buttons appear to the right of the faders is also problematic. I'd really like to jam 24 faders together for the project I have in mind."

    Ditto Randy.. that'd be awesome for me, killer with 8 on the right for 6 auxes and 2 mains as well

  8. #8

    Default Re: Reading a BCF-2000 from an Arduino Board

    Why not simply feed and read serial data to/from midi?
    3 * TIO1608 + AIC-128 + X-Touch + Dante -> AES + DADC-144DT

    SATlive is my measurement software
    DIN 15905-5 (German SPL Limit)

  9. #9

    Default Re: Reading a BCF-2000 from an Arduino Board

    Quote Originally Posted by TomyN View Post
    Why not simply feed and read serial data to/from midi?
    The device I'm using won't have a PC built into it. Providing USB host control on a micro controller is a *lot* of work (though it looks like the Teensy++ micro controller might be able to do this).

    I'm not planning on driving this from a Windows PC (or Mac or Linux) so your suggestion would be difficult to implement.

    And besides, all I really want are the 8 faders. At the time, the BCF-2000 seemed like the cheapest way to get those faders; don't believe they're worth it, however.
    Cheers,
    Randy Hyde
    -------------------
    For the interested, SAC setup here: http://www.plantation-productions.co.../SACSetup.html
    Plantation Productions:http://www.plantation-productions.com

  10. #10

    Default Re: Reading a BCF-2000 from an Arduino Board

    Hi,

    it should work with a default serial port also. Just remove the opto-coupler of the 'classic' midi input and feed the serial signal there.

    Tomy
    3 * TIO1608 + AIC-128 + X-Touch + Dante -> AES + DADC-144DT

    SATlive is my measurement software
    DIN 15905-5 (German SPL Limit)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •