Having done further programming with the Buffalo DAC, I realized that the register address values I used for the OPUS code are wrongly coded in 8-bit format where the I2C protocol of the Arduino Wire library is based on 7-bit register addresses. This means that the OPUS DAC code for the WM8741, although seemingly working, it is using the wrong addresses for the registers. If you want to reuse this code, please redefine the address to 7-bit values as specified in the datasheet.
Register address values for the OPUS DAC/WM8741 Code Leave a comment
Where is the latest code? Leave a comment
You may want to check the CODE section on the main blog…
Fixed a Bug in the Code (v.B06e) 23 comments
Code was missing
“#include <pins_arduino.h>” under Libraries.
For some reason copying html from the Arduino environment and pasting into wordpress will result in missing that part of the code. I have added it to the code just now.
I also copied the fixed code and pasted it in a blank sketch in Arduino and compiles fine.
Let me know if you find any bugs (use the comments section)
Code for Buffalo II DAC vB06e 5 comments
/*************************************************************************************************** * HIFIDUINO v. B06e * * Saturday January 8th, 2011 * * Arduino code for Buffalo II DAC and other DACs based on the Sabre32 DAC chip. This code is adapted * from an earlier version developed for the Wolfson 8741 DAC chip. For more information visit * www.hifiduino.wordpress.com or www.hifiduino.blogspot.com * * (Although this code is based on a confidential data sheet for which I've signed an NDA, I've also * requested and have received permission to publish this code). * * Change log: * v. B06e 01/08/11: Supports 32-bit I2S input in accordance to Buffalo II input wiring of SabreDAC * Rearranged code for default conditions, correct reading of sample rate in I2S * Allows manual "best DPLL bandwidth" setting. Allows manual selection of I2S or * SPDIF. Can set default startup to I2S or SPDIF (in the code) * v. B05 11/25/10: Added remote control capability using the Apple Aluminum Remote Control. This * version enables remote volume control only. * v. B04 11/06/10: Added large numbers, tidy-up the UI, "pulse" indicator -every time the status * information is read. Also status indicator for PCM/DSD and Signal Lock * v. B03 11/02/10: Supports s/w debounce, added setting of FIR filter, DPLL bandwidth, jitter * reduction and adjustment of LCD brightness. * v. B021 10/18/10: No new functionality, but cleaned up the code and comments. * v. B02 10/15/10: Added reading of sample rate. * v. B01 10/11/10: Volume control, LCD, Rotary Encoder. ***************************************************************************************************/ /*************************************************************************************************** * look for "spdifStart" and set to true if you want the DAC to startup in SPDIF mode and false if * you want the DAC to startup in I2S mode * * ***************************************************************************************************/ Read the rest of this entry »
Code for Buffalo II DAC vB05 1 comment
/*************************************************************************************************** * HIFIDUINO v. B05 * * November 25, 2010 * * Arduino code for Buffalo II DAC and other DACs based on the Sabre32 DAC chip. This code is adapted * from an earlier version developed for the Wolfson 8741 DAC chip. For more information visit * www.hifiduino.wordpress.com or www.hifiduino.blogspot.com * * (Although this code is based on a confidential data sheet for which I've signed an NDA, I've also * requested and have received permission to publish this code). * * Change log: * v. B05 11/25/10: Added remote control capability using the Apple Aluminum Remote Control. This version enables remote volume control only. * v. B04 11/06/10: Added large numbers, tidy-up the UI, "pulse" indicator -every time the status * information is read. Also status indicator for PCM/DSD and Signal Lock * v. B03 11/02/10: Supports s/w debounce, added setting of FIR filter, DPLL bandwidth, jitter * reduction and adjustment of LCD brightness. * v. B021 10/18/10: No new functionality, but cleaned up the code and comments. * v. B02 10/15/10: Added reading of sample rate. * v. B01 10/11/10: Volume control, LCD, Rotary Encoder. ***************************************************************************************************/ Read the rest of this entry »
Code for Buffalo II DAC v. B04 1 comment
/*************************************************************************************************** * HIFIDUINO v. B04 * * November 06, 2010 * * Arduino code for Buffalo II DAC and other DACs based on the Sabre32 DAC chip. This code is adapted * from an earlier version developed for the Wolfson 8741 DAC chip. For more information visit * www.hifiduino.wordpress.com or www.hifiduino.blogspot.com * * (Although this code is based on a confidential data sheet for which I've signed an NDA, I've also * requested and have received permission to publish this code). * * Change log: * v. B04 11/06/10: Added large numbers, tidy-up the UI, "pulse" indicator -every time the status * information is read. Also status indicator for PCM/DSD and Signal Lock * v. B03 11/02/10: Supports s/w debounce, added setting of FIR filter, DPLL bandwidth, jitter * reduction and adjustment of LCD brightness. * v. B021 10/18/10: No new functionality, but cleaned up the code and comments. * v. B02 10/15/10: Added reading of sample rate. * v. B01 10/11/10: Volume control, LCD, Rotary Encoder. ***************************************************************************************************/ Read the rest of this entry »
Code for Buffalo II DAC v. B03 1 comment
/*************************************************************************************************** * HIFIDUINO v. B03 * * November 02, 2010 * Arduino code for Buffalo II DAC and other DACs based on the Sabre32 DAC chip. This code is adapted * from an earlier version developed for the Wolfson 8741 DAC chip. For more information visit * www.hifiduino.wordpress.com or www.hifiduino.blogspot.com * * Although this code is based on a confidential data sheet for which I've signed an NDA, I've also * requested and have received permission to publish this code. * * Change log: * v. B03 11/02/10: Supports s/w debounce, added setting of FIR filter, DPLL bandwidth, jitter reduction and adjustment of LCD brightness. * v. B021 10/18/10: No new functionality, but cleaned up the code and comments. * v. B02 10/15/10: Added reading of sample rate. * v. B01 10/11/10: Volume control, LCD, Rotary Encoder. ***************************************************************************************************/
v B021 (Cleaned Up the code for v B02) Leave a comment
/*************************************************************************************************** * HIFIDUINO v. B021 * * October 15, 2010 * Arduino code for Buffalo II DAC and other DACs based on the Sabre32 DAC chip. This code is adapted * from an earlier version developed for the Wolfson 8741 DAC chip. For more information visit * www.hifiduino.wordpress.com or www.hifiduino.blogspot.com * * Although this code is based on a confidential data sheet for which I've signed an NDA, I've also * requested and have received permission to publish this code. * * Change log: * v. B01 10/11/10: Volume control, LCD, Rotary Encoder * v. B02 10/15/10: Added reading of sample rate * v. B021 10/18/10: No new functionality, but cleaned up the code and comments ***************************************************************************************************/ // LIBRARIES #include <LiquidCrystal.h> // For LCD // Initialize the library with the numbers of the interface pins LiquidCrystal lcd(12, 11, 10, 9, 8, 7); #include <Wire.h> // For I2C // CONSTANT DEFINITION // (The digital volume for Sabre32 is 0 to -127 db in .5 db steps) #define DEFAULTVOL 0x64 //-50 dB this is 50x2=100 or 0x64 #define MINVOL 0xC6 //-99dB this is 99X2=198 or 0xC6 #define MAXVOL 0x00 //-0 dB #define DIMVOL 0x3C //-60dB The volume level when dimming the volume #define VOLUPPIN 4 // Button to increase volume or RotEnc A terminal #define VOLDOWNPIN 2 // Button to decrease volume or RotEnc B terminal #define INTERVAL 2 // Time interval in seconds for doing something like reading the sample rate // GLOBAL VARIABLE DECLARATION byte regVal; // Variable to pass register value byte regAddr; // Variable to pass register value byte currVol=DEFAULTVOL; // Variable to hold the current volume value unsigned long previousMillis = 0; // Stores last recorded time /* LCD For the LCD, I am using a "standard" HD44780 20x4 display, and I am using the official Arduino LiquidCrystal library that comes with the standard installation. This is as standard as it can be and this type of LCD is available everywhere The pin assignment is different from the example code and it is as follows: * LCD RS pin to digital pin 12 * LCD Enable pin to digital pin 11 * LCD D4 pin to digital pin 10 * LCD D5 pin to digital pin 9 * LCD D6 pin to digital pin 8 * LCD D7 pin to digital pin 7 */ /* ROTARY ENCODER The rotary encoder is connected to pins 2 (A) and pin 4 (B). It does not matter which terminal is connected to which pin. The third terminal is connected to GND. At each cycle, the rotary encoder will pull the pins LOW and the transition is detected (an interrupt is generated). It is then compared with the level in the other pin. The interrupt service routine is specified below. Debouncing For this code to work, the rotary encoder is "debounced" by installing capacitors between the signal pins an GND in order to minimize the noise generated by the mechanical switches of the rotary encoder. Without this "debouncing", spurious readings will make the volume jump several clicks. If an optical rotary encoder or a high quality encoder is used, this modification may not be needed. The rotary encoder used here is a low cost model ($1.00) and requires the installation of the capacitors. INTERRUPT SERVICE ROUTINE It is used to determines direction. The code displays volume attenuation, so if the value increased, there is more attenuation and the volume level goes down. This is counter clockwise motion in the rotary encoder. Likewise if the value decreases, there is less attenuation and the volume level goes up. This is clockwise direction */ volatile boolean attenuUp=false; // flag to indicate more attenuation or counter clockwise motion volatile boolean attenuDown=false; // flag to indicate less attenuation or clockwise motion void rotEncoder() { if (digitalRead(2) == digitalRead(4)) { attenuUp = true; //if on interrupt the encoder channels are the same, direction is CCW } else { attenuDown = true; //if they are not the same, direction is CW } } /* READING THE SAMPLE RATE The sample rate can be calculated by reading the registers of the 32 bit DPLL value. For SPDIF this value is divided by (2^32/Crystal-Frequency). In Buffalo II, the Crystal frequency is 80,000,000 Hz. In Arduino (and other small microprocessors) it is NOT advisable to do floating point math because "it is very slow"; therefore we will use interger math to calculate the sample rate. In order to increase the accuracy of the integer calculation, I did some evaluation of the DPLL register values for sample rates ranging from 44.1K to 192K. I noticed that I could multiply the value of the DPLL number by up to 400 without overflowing the 32-bits. The value of 2^32/80000000 is 53.687091 (which is a floating point number). If we use the integer part (53 or 54) we will have an error in the calculation. For example, if we divide the DPLL number for a 44.1K sample rate signal by 53, the resultant values would be 44.677K and 43.849K if divided by 54. If we divide by the actual number 53.687091, then we obtain 44.105K (The 4 Hz deviation from ideal is within the SPDIF specification and crystal tolerances of the source signal). Clearly the integer math is not very accurate. However, since we have 32 bit number to work with, we can multiply the DPLL number by 400 and then divide by 400X53.687091=21475. If we do this, we obtain 44.105K which is within rounding off error of the exact value (the one using floating math) */ // Sample rate reading routines volatile unsigned long DPLLNum; // Variable to hold DPLL value byte readDPLL(byte regAddr) { Wire.beginTransmission(0x48); // Hard coded the Sabre/Buffalo device address Wire.send(regAddr); // Specify the DPLL register from which to read value Wire.endTransmission(); Wire.requestFrom(0x48,1); // Hard coded to Buffalo, request from address specified with Wire.send() while(!Wire.available()) { } // Do nothing and wait for the data on the wire -if needed... return Wire.receive(); } unsigned long sampleRate() { DPLLNum=0; // Reading the 4 registers of DPLL one byte at a time // and stuffing into a single 32-bit number DPLLNum|=readDPLL(31); DPLLNum<<=8; DPLLNum|=readDPLL(30); DPLLNum<<=8; DPLLNum|=readDPLL(29); DPLLNum<<=8; DPLLNum|=readDPLL(28); // Calculating the sample rate for SPDIF DPLLNum*=400; DPLLNum/=21475; return DPLLNum; } /* CONTROLLING THE DIGITAL ATTENUATION (VOLUME) IN THE DAC The device address of Sabre DAC Datasheet specifies the address as 0x90 which is an 8-bit value. The wire library in Arduino uses 7-bit device addresses and the 8th R/W bit is added automatically depending on whether you use the write call [beginTransmission()] or the read call [requestFrom()]. Therefore, you will use the 7 most significant bits of the 8-bit address. In our example, 0x90 becomes 0x48 as follows: 0x90: 0101000 (we eliminate the rightmost bit) 0x48: 0010100 */ void writeVolRegister(byte regAddr, byte regVal) { Wire.beginTransmission(0x48); //Hard coded to the the Sabre/Buffalo device address Wire.send(regAddr); // Specifying the address of volume register Wire.send(regVal); // Writing the volume value into the register Wire.endTransmission(); } void setSabreVolume(byte regVal) { writeVolRegister(0, regVal); // set up volume in DAC1 writeVolRegister(1, regVal); // set up volume in DAC2 writeVolRegister(2, regVal); // set up volume in DAC3 writeVolRegister(3, regVal); // set up volume in DAC4 writeVolRegister(4, regVal); // set up volume in DAC5 writeVolRegister(5, regVal); // set up volume in DAC6 writeVolRegister(6, regVal); // set up volume in DAC7 writeVolRegister(7, regVal); // set up volume in DAC8 } /* PRINTING (DISPLAYING) THE VAVLUE OF VOLUME "NICELY FORMATTED" For now, it is hardcoded to print on line 3 (4th line) of the LCD. This routine takes in the actual value of the volume level and displays it with proper format in the LDC. */ void printVol(byte regVal) { if (regVal==9) // transition between two digit and one digit display { lcd.setCursor(12,3); lcd.print("0 "); // Add a leading zero } if (regVal<10) // two digit to one digit transistion, right justification { lcd.setCursor(13,3); lcd.print(regVal, DEC); } else { lcd.setCursor(12,3); lcd.print(regVal, DEC); } } /*************************MAIN PROGRAM*************************************************************/ void setup() { // Set up the LCD's number of columns and rows: lcd.begin(20, 4); // Join the I2C bus as a master Wire.begin(); // Attach Interrupts attachInterrupt(0, rotEncoder, CHANGE); // ISR for rotary encoder // Set up the pin modes pinMode(VOLUPPIN, INPUT); // Button or Encoder pin for volume up digitalWrite(VOLUPPIN, HIGH); // Enable pull-up resistor pinMode(VOLDOWNPIN, INPUT); // Button or Encoder pin for volume down digitalWrite(VOLDOWNPIN, HIGH); // Enable pull-down resistor // Print the welcome message and other labels to the LCD lcd.setCursor(0,1); lcd.print("BUFFALO CONTROL v0.2"); lcd.setCursor(0,2); lcd.print("Sample Rate: "); lcd.setCursor(0,3); lcd.print(" Volume: - dB"); // Set the default volume at power up, otherwise the DAC defaults at full volume setSabreVolume(DEFAULTVOL); printVol(DEFAULTVOL/2); } void loop() { // Print the sample rate (once every "INTERVAL" time) if(millis() - previousMillis > INTERVAL*1000) { previousMillis = millis(); // Saving last time we display sample rate lcd.setCursor(13,2); lcd.print(sampleRate(), DEC); } // Asjusting the volume down (larger numbers since we are displaying attenuation) while(attenuUp==true) // While there is CCW motion in the rotary encoder { attenuUp=false; // Reset the flag if (currVol// Check if already at maximum attenuation (minimum Volume) { currVol=currVol+2; // Increase 1 dB setSabreVolume(currVol); // Write value into registers printVol(currVol/2); // Divide by 2 to print in whole dBs } } // Adjusting the volume up (smaller numbers since we are displaying attenuation) while(attenuDown==true) // While there is CW motion in rotary encoder { attenuDown=false; // reset the flag if (currVol>MAXVOL) // Check if already at max Volume { currVol=currVol-2; // Increase 1 dB setSabreVolume(currVol); // Write value into registers printVol(currVol/2); // Divide by 2 to print in whole dBs } } }
Code for Buffalo II DAC v B02 1 comment
/* HIFIDUINO v. B02 October 15, 2010 Arduino code for Buffalo II DAC and other DACs based on the Sabre32 DAC chip This code is adapted from an earlier version developed for the Wolfson 8741 DAC chip For more information visit www.hifiduino.wordpress.com Although this is based on a confidential data sheet for which I've signed an NDA, I've also received permission to publish this code. Change log: v. B01 10/11/10: Volume control, LCD, Rotary Encoder v. B02 10/15/10: Added reading of sample rate */ /* For the LCD, I am using a "standard" HD44780 20x4 display, and I am using the official Arduino LiquidCrystal library that comes with the standard installation. The pin assignment is different from the example code and it is as follows: * LCD RS pin to digital pin 12 * LCD Enable pin to digital pin 11 * LCD D4 pin to digital pin 10 * LCD D5 pin to digital pin 9 * LCD D6 pin to digital pin 8 * LCD D7 pin to digital pin 7 */ // LIBRARIES USED IN THIS CODE // include the LCD library code: #include <LiquidCrystal.h> // initialize the library with the numbers of the interface pins LiquidCrystal lcd(12, 11, 10, 9, 8, 7); // include the library code for I2C: #include <Wire.h> // #DEFINES // Volume for Sabre32 is 0 to -127 db in .5 db steps #define DEFAULTVOL 0x64 //-50 dB this is 50x2=100 or 0x64 #define MINVOL 0xC6 //-99dB this is 99X2=198 or 0xC6 #define MAXVOL 0x00 //-0 dB #define DIMVOL 0x3C //-60dB The volume level when dimming the volume #define VOLUPPIN 4 // Button to increase volume or RotEnc A terminal #define VOLDOWNPIN 2 // Button to decrease volume or RotEnc B terminal #define INTERVAL 2000 // Time interval in msec for doing something like reading the sample rate // VARIABLE DECLARATIOM byte regVal; // Variable to pass register value byte regAddr; // Variable to pass register value byte currVol=DEFAULTVOL; // Varialble to hold the current volume value unsigned long previousMillis = 0; // Stores last recorded time /* ROTARY ENCODER The rotary encoder is connected to pins 2 (A) and pin 4 (B) and GND. It does not matter which terminal you connect to which pin. The third terminal is connected to GND. At each cycle, the rotary encoder would pull the pins LOW and we detect that transition and compare it with the level of the other pin For this code to work, the rotary encoder is "debounced" by installing capacitors between the signal pins an GND in order to minimize the noise generated by the mechanical switches of the rotary encoder. If you are using an optical rotary encoder or a high quality encoder, you might not need the capacitors, but the model I am using is a low cost ~ $1 rotary encoder */ // Interrupt service routine for rotary encoder. Determines direction. volatile byte volUp=0; // flags for the interrupt routine volatile byte volDown=0; void rotEncoder() { if (digitalRead(2) == digitalRead(4)) { volUp = 1; //if on interrupt the encoder channels are the same, direction is clockwise } else { volDown = 1; //if they are not the same, direction is ccw } } /* Reading the Sample Rate in the Buffalo II DAC The sample rate can be calculated by reading the registers of the 32 bit DPLL value. For SPDIF this value is divided by 2^32/Crystal-Frequency. In Buffalo II, the Crystal frequency is 80,000,000 Hz. To read the DPLL value we need to read 4 bytes because it is a 32-bit (4 bytes) number In Arduino (and other small microprocessors) it is NOT advisible to do floating point math because "it is very slow"; therefor we will use interger math. In order to increase the accuracy of the integer calculation, I did some evaluation of the DPLL register values for sample rates ranging from 44.1K to 192K. I noticed that I could multiply the value of the DPLL number by up to 400X without overflowing the 32-bits. The value of 2^32/80000000 is 53.687091 (which is a floating point number). If we use the integer part (53 or 54) we will have an error in the calculation. For example, if we divide the DPLL number for a 44.1K sample rate signal by 53, the resultant values would be 44.677K and 43.849K if divided by 54. If we divide by the actual number 53.687091, then we obtain 44.105K (The 4 Hz deviation from ideal is within the SPDIF specification and crystal tolerances of the source signal). Clearly the integer math is not very accurate. However, since we have 32 bit number to work with, we can multiply the DPLL number by 400 and then device by 400X53.687091=21475. If we do this, we obtain 44.105K which is within rounding off value of the exact result (the one using floating math) */ // Sample rate reading routines volatile unsigned long DPLLNum; // Variable to hold DPLL value byte readDPLL(byte regAddr) { Wire.beginTransmission(0x48); // Hard coded the Sabre/Buffalo device address Wire.send(regAddr); // Specify the DPLL register from which to read value Wire.endTransmission(); Wire.requestFrom(0x48,1); // Hard coded to Buffalo, request from address specified with Wire.send() while(!Wire.available()) { } // Wait for the data on the wire -if needed... return Wire.receive(); } unsigned long sampleRate() { DPLLNum=0; // Reading the 4 registers of DPLL and stuffing into a single 32-bit number DPLLNum|=readDPLL(31); DPLLNum<<=8; DPLLNum|=readDPLL(30); DPLLNum<<=8; DPLLNum|=readDPLL(29); DPLLNum<<=8; DPLLNum|=readDPLL(28); // Calculating the sample rate for SPDIF DPLLNum=DPLLNum*400; DPLLNum=DPLLNum/21475; return DPLLNum; } /* Buffalo volume setting routine The device address of Sabre DAC Datasheet specifies the address as 0x90 which is an 8-bit value. The wire library in Arduino uses 7-bit device addresses and the 8th R/W bit is added automatically depending on whether you use the write call [beginTransmission()] or the read call [requestFrom()]. Therefore, you will use the 7 most significant bits of the 8-bit address. In our example, 0.x90 becomes 0x48 as follows 0x90: 0101000 (we eliminate the rightmost bit) 0x48: 0010100 */ void setSabreVolume(byte regVal) { Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x00); // Specifying the vol register of DAC 1 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x01); // Specifying the vol register of DAC 1 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x02); // Specifying the vol register of DAC 2 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x03); // Specifying the vol register of DAC 3 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x04); // Specifying the vol register of DAC 4 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x05); // Specifying the vol register of DAC 5 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x06); // Specifying the vol register of DAC 6 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x07); // Specifying the vol register of DAC 7 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); } /* A little print routine... hardcoded to print on line 3 (4th line) of LCD Takes in the volume value in whole dB increment This means dividing the volumen register value by 2 first */ void printVol(byte regVal) { if (regVal==9) // transition between two digit and one digit display { lcd.setCursor(12,3); lcd.print("0 "); // Add a leading zero } if (regVal<10) // two digit to one digit transistion, right justification { lcd.setCursor(13,3); lcd.print(regVal, DEC); } else { lcd.setCursor(12,3); lcd.print(regVal, DEC); } } void setup() { // set up the LCD's number of columns and rows: lcd.begin(20, 4); // Join the I2C bus as a master Wire.begin(); // Attach Interrupts attachInterrupt(0, rotEncoder, CHANGE); // ISR for rotary encoder // Set up the pin modes pinMode(VOLUPPIN, INPUT); // Button or Encoder pin for volume up digitalWrite(VOLUPPIN, HIGH); // Enable pull-up resistor pinMode(VOLDOWNPIN, INPUT); // Button or Encoder pin for volume down digitalWrite(VOLDOWNPIN, HIGH); // Enable pull-down resistor // Print the welcome message to the LCD. // And volume indicator lcd.setCursor(0,1); lcd.print("BUFFALO CONTROL v0.2"); lcd.setCursor(0,3); lcd.print(" Volume: - dB"); // Set the default volume at power up setSabreVolume(DEFAULTVOL); printVol(DEFAULTVOL/2); } void loop() { // Print the sample rate (once every "INTERVAL" time) if(millis() - previousMillis > INTERVAL) { previousMillis = millis(); // Saving last time we display sample rate lcd.setCursor(0,2); lcd.print("Sample Rate: "); lcd.print(sampleRate(), DEC); } // The following is to adjust the volume down (larger number) while(volUp==1) // While there is CW motion in the rotary encoder { volUp=0; // Reset the flag if (currVol// Check if already at min numerical Volume { currVol=currVol+2; // Increase 1 dB setSabreVolume(currVol); // Write value into registers printVol(currVol/2); } } // The following is to adjust the volume up (smaller numbers) while(volDown==1) // While there is CCW motion in rotary encoder { volDown=0; // clear the flag if (currVol>MAXVOL) // Check if already at max Volume { currVol=currVol-2; // Increase 1 dB setSabreVolume(currVol); // Write value into registers printVol(currVol/2); } } }
Code for Buffalo II DAC v B01 1 comment
/* HIFIDUINO v. B01 October 11, 2010 Arduino code for Buffalo II DAC and other DACs based on the Sabre32 DAC chip This code is adapted from an earlier version developed for the Wolfson 8741 DAC chip For more information visit www.hifiduino.wordpress.com Although this is based on a confidential data sheet for which I've sigend an NDA, I've also received permission to publish this code. */ /* For the LCD, I am using a "standard" HD44780 20x4 display, and I am using the official Arduino LiquidCrystal library that comes with the standard installation. The pin assignment is different from the example code and it is as follows: * LCD RS pin to digital pin 12 * LCD Enable pin to digital pin 11 * LCD D4 pin to digital pin 10 * LCD D5 pin to digital pin 9 * LCD D6 pin to digital pin 8 * LCD D7 pin to digital pin 7 */ // LIBRARIES USED IN THIS CODE // include the LCD library code: #include <LiquidCrystal.h> // initialize the library with the numbers of the interface pins LiquidCrystal lcd(12, 11, 10, 9, 8, 7); // include the library code for I2C: #include <Wire.h> // #DEFINES // Volume for Sabre32 is 0 to -127 db in .5 db steps #define DEFAULTVOL 0x64 //-50 dB this is 50x2=100 or 0x64 #define MINVOL 0xC6 //-99dB this is 99X2=198 or 0xC6 #define MAXVOL 0x00 //-0 dB #define DIMVOL 0x3C //-60dB The volume level when dimming the volume #define VOLUPPIN 4 // Button to increase volume or RotEnc A terminal #define VOLDOWNPIN 2 // Button to decrease volume or RotEnc B terminal // VARIABLE DECLARATIOM byte regVal; // variable to pass register value byte currVol=DEFAULTVOL; // varialble to hold the current volume value /* ROTARY ENCODER The rotary encoder is connected to pins 2 (A) and pin 4 (B) and GND. It does not matter which terminal you connect to which pin. The third terminal is connected to GND. At each cycle, the rotary encoder would pull the pins LOW and we detect that transition and compare it with the level of the other pin For this code to work, the rotary encoder is "debounced" by installing capacitors between the signal pins an GND in order to minimize the noise generated by the mechanical switches of the rotary encoder. If you are using an optical rotary encoder or a high quality encoder, you might not need the capacitors, but the model I am using is a low cost ~ $1 rotary encoder */ // Interrupt service routine for rotary encoder. Determines direction. volatile byte volUp=0; // flags for the interrupt routine volatile byte volDown=0; void rotEncoder() { if (digitalRead(2) == digitalRead(4)) { volUp = 1; //if on interrupt the encoder channels are the same, direction is clockwise } else { volDown = 1; //if they are not the same, direction is ccw } } /* Buffalo volume setting routine The device address of Sabre DAC Datasheet specifies the address as 0x90 which is an 8-bit value. The wire library in Arduino uses 7-bit device addresses and the 8th R/W bit is added automatically depending on whether you use the write call [beginTransmission()] or the read call [requestFrom()]. Therefore, you will use the 7 most significant bits of the 8-bit address. In our example, 0.x90 becomes 0x48 as follows 0x90: 0101000 (we eliminate the rightmost bit) 0x48: 0010100 */ void setSabreVolume(byte regVal) { Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x00); // Specifying the vol register of DAC 1 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x01); // Specifying the vol register of DAC 1 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x02); // Specifying the vol register of DAC 2 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x03); // Specifying the vol register of DAC 3 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x04); // Specifying the vol register of DAC 4 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x05); // Specifying the vol register of DAC 5 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x06); // Specifying the vol register of DAC 6 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); Wire.beginTransmission(0x48); //hard coded the Sabre/Buffalo device address Wire.send(0x07); // Specifying the vol register of DAC 7 Wire.send(regVal); // Writing volume value into register Wire.endTransmission(); } /* A little print routine... hardcoded to print on line 3 (4th line) of LCD Takes in the volume value in whole dB increment This means dividing the volumen register value by 2 first */ void printVol(byte regVal) { if (regVal==9) // transition between two digit and one digit display { lcd.setCursor(12,3); lcd.print("0 "); // Add a leading zero } if (regVal<10) // two digit to one digit transistion, right justification { lcd.setCursor(13,3); lcd.print(regVal, DEC); } else { lcd.setCursor(12,3); lcd.print(regVal, DEC); } } void setup() { // set up the LCD's number of columns and rows: lcd.begin(20, 4); // Join the I2C bus as a master Wire.begin(); // Attach Interrupts attachInterrupt(0, rotEncoder, CHANGE); // ISR for rotary encoder // Set up the pin modes pinMode(VOLUPPIN, INPUT); // Button or Encoder pin for volume up digitalWrite(VOLUPPIN, HIGH); // Enable pull-up resistor pinMode(VOLDOWNPIN, INPUT); // Button or Encoder pin for volume down digitalWrite(VOLDOWNPIN, HIGH); // Enable pull-down resistor // Print the welcome message to the LCD. // And volume indicator lcd.setCursor(0,1); lcd.print("BUFFALO II DAC v0.1"); lcd.setCursor(0,3); lcd.print(" Volume: - dB"); // Set the default volume at power up setSabreVolume(DEFAULTVOL); printVol(DEFAULTVOL/2); } void loop() { // The following is to adjust the volume down (larger number) while(volUp==1) // While there is CW motion in the rotary encoder { volUp=0; // Reset the flag if (currVol<MINVOL) // Check if already at min numerical Volume { currVol=currVol+2; // Increase 1 dB setSabreVolume(currVol); // Write value into registers printVol(currVol/2); } } // The following is to adjust the volume up (smaller numbers) while(volDown==1) // While there is CCW motion in rotary encoder { volDown=0; // clear the flag if (currVol>MAXVOL) // Check if already at max Volume { currVol=currVol-2; // Increase 1 dB setSabreVolume(currVol); // Write value into registers printVol(currVol/2); } } }