User Tools

Site Tools


arduino:sansui-repair-code
sansui_circle.ino
/*
 * This is a repair of the "source selection" circuit of the Sansui AU-X517R audio amplifier,
 * using an Arduino Nano.
 * 
 * In the second approach, I am using a circle method, because the first approach failed in 
 * handling movement of the selector button more than one position.
 * Now, I mimic exactly the behaviour of the rotary switch, dividing the circle in 360 pieces
 * and perform what is expected from the switch in each piece.
 * 
 * The original circuit utilizes an ALPS (Japan) module, containing three sets of rotary switches.
 * Each set has a total of 12 pins, where one pin is used as a common and is connected
 * in a rotary fashion to each of the other 11 pins. 
 * All three sets are rotated together via a motor. The two sets are wired to 6 sources 
 * (PHONO, TUNER, CD, LINE, TAPE1, TAPE2) and each of these sets is associated with 
 * the Left and Right channel, respectively. 
 * 
 * The third set is wired to a resistor ladder. The output of the resistor ladder is used
 * by the original microcontroller to identify the position of the switch, via an ADC. 
 * Subsequently, the controller activates the motor to bring the switch to the desired location.
 * 
 * The intermediate pins of the audio switches (2, 4, 6, 8, 10, 12) are wired to audio ground. 
 * The intermediate pins of the resistor ladder (2, 4, 6, 8, 10, 12) are left unconnected.
 * 
 * Due to a broken plastic gear of the original circuit and since the module is no longer available
 * in the market (https://www.aliexpress.com/item/bella-Japanese-original-band-switch-ALPS-Alps-motor-shaft-length-30MM-6-speed-rotary-switch/1363271059.html), 
 * I will replace it with an Arduino board which mimics the behaviour of the original
 * circuit. In detail:
 * 
 * Two digital inputs are connected to the motor control circuit (LB1641). Their input table is:
 * 
 * IN1  IN2  Action
 * ------------------
 * 0  0  Brake
 * 1  0  Forward drive
 * 0  1  Reverse drive
 * 1  1  Brake
 * 
 * Based on the above instruction, the new circuit will cycle through a set of pins which provide a GND
 * to the external resistor ladder. When the desired position is reached as defined by the setting of the 
 * control to brake, the new position will have been identified and a relay will activate the audio input 
 * from the desired audio source.
 * 
 *  Please note that we do not use the analog voltage provided by the Selector button to control our movement.
 *  This happens because the system can be remotely operated via an infrared remote control.
 *  Therefore, we only use the position of the Selector for the initial startup and
 *  we use the motor control pins for any subsequent movement.
 *  
 */
 
// Digital output pins activating the relays
// D4, D5, D6, D7, D8 and D9
const int audioControlPins[] = {4, 5, 6, 7, 8, 9};
 
// Pins connected to the switch position control resistor ladder.
// The first pin is the first resistor divider and the output of the ladder. 
// We can even use the A/D converter of the Arduino
// to read the analog voltage of this pin.
const int resistorLadderPins[] = {A5, A4, A3, A2, A1, A0};
 
// Number of audio sources. This must be the number of
// elements in the above arrays
const int NUM_SOURCES = 6;
 
// The two pins which controlled the movement of the motor
// and are now becoming our control input.
// D10 going high is forward and D11 going high is reverse 
const int motorControlPins[] = {10, 11};
 
// Pin connected to the selector resistor ladder
const int selectorAnalogPin = A6;
 
// Variable to set the current index in the above arrays 
int currentSourceIndex;
// Variable to set the previous source index
int previousSourceIndex;
 
// From 0 to 359
int positionInCircle;
// One of the 6 circle segments. From 0 to 5
int circleSegment;
 
// The time required for the simulated motor to travel one of the 360 degrees
// in milliseconds
const int ROTATION_SPEED = 5;
 
// Digital output state that activates an audio relay
const bool AUDIO_ACTIVE = HIGH;
 
//------------------------------------------------------------------------------
void setup() {
 
  // As soon as possible...
  initAudioControlPins(); // It also disables all sources
 
  // Set the two control pins as Input.
  // The circuit uses a diode in each pin to identify a 0,
  // because the chip send 2V as HIGH. That is why we need the 
  // pull-up resistor, to keep the keep at 5V HIGH on idle. 
  pinMode(motorControlPins[0], INPUT_PULLUP);
  pinMode(motorControlPins[1], INPUT_PULLUP);
 
  // Sets all switch position control ladder pins initially as INPUT 
  // and simulate a high-Z state
  initResistorLadderPins();
 
  // The pin which we use for analog reading
  pinMode(selectorAnalogPin, INPUT);
 
  // We use debugging below, so it is time to activate the serial port
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB
  }
 
  // Read selector voltage to identify the current position of the switch
  // We will set this position as the current position of the switch
  currentSourceIndex = selectorPosition(); 
  // Simulate the switch position control ladder to be equal to the
  // selector control ladder
  setControlIndex(currentSourceIndex);
  // Let the internal microcontoller to run its own A/D
  // and understand the position of the switch.
  // This is absolutely essential. 
  // ** For some reason, without this delay, the next command did not activate the arduino pin.
  // ** Needs further investigation why this happens! How can the setting of an analog pin
  // inhibit the setting of a digital pin? Is the 2mA current too low and messes with the setting
  // of the analog pins?  ***
  // Must be higher than 15 msec. Set 200 to be safe
  // In the normal loop we do not face such problem, probably because the rotation that we 
  // implement takes a lot more time until the next source is activated.
  delay(200);
  enableSource(currentSourceIndex);
  // From our point of view, we are stable and previousSourceIndex points
  // to our current position. 
  previousSourceIndex = currentSourceIndex;   
 
 
  // The circle is divided in 6 zones, each of 60 degrees, from 0 to 59
  // From 0 to 7 we have the half of the unused contact (1 of the 12 of the rotary switch)
  // From 8 to 21, we have a gap
  // From 22 to 37 we have the valid contact
  // From 38 to 51 we have a gap
  // From 52 to 59 we have the half of the next unused contact 
  // We start considering that we are in the middle of the valid contact between 22 and 37
  positionInCircle = (currentSourceIndex * 60) + 29; 
  circleSegment = currentSourceIndex;  
}
 
//------------------------------------------------------------------------------
void loop() {
 
  bool motorMoving = false;
 
  bool input_1 = digitalRead(motorControlPins[0]);
  bool input_2 = digitalRead(motorControlPins[1]);
 
  if (input_1 == HIGH && input_2 == LOW) {
    motorMoving = true;
    disableAllSources();
    ++positionInCircle;
    if (positionInCircle >= 360) 
      positionInCircle = 0;
    circleSegment = positionInCircle / 60;
    delay(ROTATION_SPEED);
    //printPosition();
  }  
  else if (input_1 == LOW && input_2 == HIGH) {  
    motorMoving = true;
    disableAllSources();
    --positionInCircle;
    if (positionInCircle < 0) 
      positionInCircle = 359; 
    circleSegment = positionInCircle / 60;   
    delay(ROTATION_SPEED);
    //printPosition();
  } else {
     motorMoving = false;
     circleSegment = positionInCircle / 60;
     currentSourceIndex = circleSegment;
     if (previousSourceIndex != currentSourceIndex) {
       // enable only if we have a change in source
       enableSource(currentSourceIndex);
       previousSourceIndex = currentSourceIndex;
     }    
  }
 
  int indexOffset = circleSegment * 60;   
 
  if (positionInCircle >= (indexOffset + 0) && positionInCircle <= (indexOffset + 7)) {
    // unused contact
    setHighZ();
  } else if (positionInCircle >= (indexOffset + 8) && positionInCircle <= (indexOffset + 21)) {
    // gap
    setHighZ();
  } else if (positionInCircle >= (indexOffset + 22) && positionInCircle <= (indexOffset + 37)) {
    // valid contact
    setControlIndex(circleSegment);
  } else if (positionInCircle >= (indexOffset + 38) && positionInCircle <= (indexOffset + 51)) {
    // gap
    setHighZ();
  } else if (positionInCircle >= (indexOffset + 52) && positionInCircle <= (indexOffset + 59)) {
    // unused contact          
    setHighZ();
  } 
 
 
}
 
//------------------------------------------------------------------------------
// Initializes the audio control pins.
// Sets the pins as OUTPUT with initial state Inactive (= LOW)
void initAudioControlPins() {
 
  for (int index = 0; index < NUM_SOURCES; ++index) {
    // Note the notation NOT(AUDIO_ACTIVE)
    digitalWrite(audioControlPins[index], !AUDIO_ACTIVE);
    pinMode(audioControlPins[index], OUTPUT);   
  }
 
}
 
//------------------------------------------------------------------------------
// Enables an audio source by enabling the digital output
// which controls a relay. 
// The input is an index to the audioControlPins array
void enableSource(int index) {
 
  digitalWrite(audioControlPins[index], AUDIO_ACTIVE);
  Serial.print("Enabling source: ");
  Serial.print(index);
  Serial.print(", ");
  printSourceName(index);
  Serial.println();
 
}
 
//------------------------------------------------------------------------------
// Disables all sources. We need to make
// sure that our actions are not connecting two or more sources to each other
void disableAllSources() {
 
  for (int index = 0; index < NUM_SOURCES; ++index) {
    digitalWrite(audioControlPins[index], !AUDIO_ACTIVE);
  }  
 
}
 
//------------------------------------------------------------------------------
// Initialize all resistor ladder pins to high-Z state
void initResistorLadderPins() {
 
  for (int index = 0; index < NUM_SOURCES; ++index) {
    // Avoid the internal pullup resistor
    digitalWrite(resistorLadderPins[index], LOW);
    // Set as inputs to create a high-Z state
    pinMode(resistorLadderPins[index], INPUT);    
  } 
 
}
 
//------------------------------------------------------------------------------
// Set all resistor ladder pins to high-Z state
// Same as initResistorLadderPins, but without setting the port to LOW.
// It is set once so there is no need to spend time doing that over and over.
void setHighZ() {
 
  for (int index = 0; index < NUM_SOURCES; ++index) {
    // Set as inputs to create a high-Z state
    pinMode(resistorLadderPins[index], INPUT);    
  } 
 
}
 
//------------------------------------------------------------------------------
 
// Sets a specific resistor ladder pin to LOW
// and subsequently source current through it.
// This is achieved by setting it to output and write a 0 to it.
// Since the port register has already been initialized to 0
// we do not need to re-write the 0. Just set pin as output.
// This action simulates the position control of the rotary switch to be at 
// a specific location. The output of the switch control resistor ladder
// will provide a voltage depending on the index, as follows:
//
// 0V   TAPE2
// 0.8V TAPE1
// 1.5V LINE
// 2.3V CD
// 3V   TUNER
// 3.6V PHONO
//
// We can monitor this if we like by doing an analogRead(resistorLadderPins[0])
 
void setControlIndex(int index) {
 
    pinMode(resistorLadderPins[index], OUTPUT);    
 
}
 
 
//------------------------------------------------------------------------------
// Checks analog input of the Selector button and 
// the output of the resistor ladder.
// Used for testing
void checkAnalogPorts() {
 
    int analogInput = analogRead(resistorLadderPins[0]);
    float voltageIn = float (analogInput * 5)/1024;
    Serial.print ("Voltage input: ");
    Serial.print(voltageIn);
    Serial.print("V");
 
 
    int analogOutput = analogRead(selectorAnalogPin);
    float voltageOut = float (analogOutput * 5)/1024;
    Serial.print ("  Voltage output: ");
    Serial.print(voltageOut);
    Serial.println("V");
 
}
 
//------------------------------------------------------------------------------
// Reads the analog Selector pin to identify the current position 
// of the selector ressistor ladder.
// NOTE: This is a separate resistor ladder from the switch control ladder
//       and is used to signal the 47C440 controller about the position of
//       the selector button.
// The function is used only upon startup
int selectorPosition() {
 
    int index;
    int analogIn = analogRead(selectorAnalogPin);
    float voltage = (float) (analogIn * 5)/1024;
 
    Serial.print("Selector voltage: ");
    Serial.print(voltage);
    Serial.println("V");
 
    // Check the ladder voltage
    if (voltage <= 0.37) {
      index = 0; // 0V -> TAPE2
    } else if (voltage > 0.37 && voltage <= 1.07) {
      index = 1; // 0.8V -> TAPE1
    } else if (voltage > 1.07 && voltage <= 1.79) {
      index = 2; // 1.4V -> LINE
    } else if (voltage > 1.79 && voltage <= 2.52) {
      index = 3; // 2.2V -> CD
    } else if (voltage > 2.52 && voltage <= 3.19) {
      index = 4; // 2.9V -> TUNER
    } else {
      index = 5; // 3.5V -> PHONO
    }            
 
     Serial.print("Current Selector Position (0-5): ");
     Serial.print(index);
     Serial.print(", Source: ");
     printSourceName(index);
     Serial.println();
 
     return index;
}
 
//------------------------------------------------------------------------------
// Prints the name of the source input, based on the index
void printSourceName(int index) {
 
  switch (index) {
    case 0:
      Serial.print("TAPE2");
      break;
    case 1:
      Serial.print("TAPE1");
      break;
    case 2:
      Serial.print("LINE");
      break;
    case 3:
      Serial.print("CD");
      break;
    case 4:
      Serial.print("TUNER");
      break;
    case 5:
      Serial.print("PHONO");
      break;
    default:
      Serial.print("Unknown");  
 
  }
 
}
 
//------------------------------------------------------------------------------
// Prints the position in the circle
void printPosition() {
 
  Serial.print("Position in circle ");
  Serial.print(positionInCircle);
  Serial.print("  Index ");
  Serial.println(circleSegment);  
 
}
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
arduino/sansui-repair-code.txt · Last modified: 2024/11/22 12:02 by 127.0.0.1

Except where otherwise noted, content on this wiki is licensed under the following license: Private License