/******************************************************* Name: Generator Module Test Version: 2.0 January 2023 Written by: Alexander Butovsky ******************************************************** Hardware: - Arduino NANO - Generator module vers.2.0 with 12-bit DAC MCP4725 and two frequency ranges - Range pushbutton connected to Nano input pin 5 - Module Range input connected to Nano output pin 6 - MCP4725 on I2C bus (A4 - SDA, A5 - SCL) - Rotary encoder connection: CLK - pin 2, DT - pin 3, SW - pin 4 (toggles coarse or fine tuning mode) ******************************************************** Version history 1.0 September 2023 1st version with rotary encoder 2.0 January 2023 Handling of 2 frequency ranges ********************************************************/ #include // I2C addresses for MCP4725: /******************************************* | IC name | IC marking | ADDR=0 | ADDR=1 | |------------------------------------------| | MCP4725A0 | AJxx | 0x60 | 0x61 | | MCP4725A1 | APxx | 0x62 | 0x63 | | MCP4725A2 | AQxx | 0x64 | 0x65 | | MCP4725A3 | ARxx | 0x65 | 0x67 | *******************************************/ #define DACaddr 0x61 // Pins for encoder: #define encA 2 //Encoder CLK output #define encB 3 //Encoder DT output #define encSW 4 //Encoder switch // Pins for range control: #define rangeButtonPin 5 #define rangeOut 6 /**************************************************** * Standard one-third octave frequencies(in Hz) * ***************************************************** * Range=0 --> 16, 20, 25, 32, 40, 50, 63, 80, 100, * * 125, 160, 200, 250, 315, 400, 500 * * Range=1 --> 630, 800, 1000, 1250, 1600, 2000, * * 2500, 3150, 4000, 5000, 6300, 8000, * * 10000, 12500, 16000, 20000, 25000 * ***************************************************** * fCode values corresponding to frequencies above: * * freqCode array elements [0]...[15] - Range 0 * * freqCode array elements [16]...[32] - Range 1 * ****************************************************/ uint16_t freqCode[33] = { 81, 102, 128, 162, 207, 260, 329, 421, 525, 659, 845, 1057, 1322, 1666, 2117, 2647, 86, 112, 141, 176, 225, 281, 352, 445, 564, 704, 888, 1128, 1415, 1772, 2247, 2814, 3505 }; //----- Global variables ----- enum display_flag { nothing, range_changed, stepMode_changed, frequency_changed }; display_flag df = nothing; //Keeps information what exactly //was changed in each loop uint16_t fCode; //Current frequency code in DAC register. int ind = 0; //Current index in freqCode array (0...32). uint8_t range = 0; //Range: "0" - 16-600 Hz, "1" - 0.6-25 kHz uint8_t stepMode = 0; //Step mode: "0" - 1/3 octave (Coarse) // "1" - 1% steps (Fine) uint8_t currentStateA; //Encoder CLK output value now uint8_t lastStateA; //Encoder CLK output value before // DAC object Adafruit_MCP4725 dac; void waitButtonRelease(int buttonPin) { do { delay(5); } while (digitalRead(buttonPin) == LOW); } /************************************************ * Handle frequency change in Coarse tuning mode * * dir = true --> 1 step up * * = false --> 1 step down * * Returns frequency code from next or previous * * element of freqCode[] array * ************************************************/ uint16_t handleCoarseMode(bool dir) { if (dir) { //Get next frequency in freqArray if (ind < 32) { ind++; if (ind == 16) {range = 1;} } } else { //Get previous frequency in freqArray if (ind > 0) { ind--; if (ind == 15) {range = 0;} } } return freqCode[ind]; } /************************************************* * Handle frequency change in Fine tuning mode * * dir = true --> 1% of current frequency up * * = false --> 1% of current frequency down * * Returns frequency code increased or decreased * * by small steps * *************************************************/ #define fineModeStep 1.01 // Step is 1% uint16_t handleFineMode(bool dir, uint16_t oldFCode) { uint16_t newFCode = oldFCode; if (dir) { //Clockwise rotation - step up if (oldFCode < 4000) { //upper limit newFCode = oldFCode * fineModeStep; if (newFCode == oldFCode) { newFCode++; //add 1 if float result was truncated //to the same integer value as oldFCode } if ((newFCode > 3100) && (range == 0)) { //switch to range 1 range = 1; newFCode = newFCode / 37.43; } } } else { //Counterclockwise rotation - step down if (fCode > 50) { //lower limit newFCode = oldFCode / fineModeStep; if ((newFCode < 80) && (range == 1)) { //switch to range 0 range = 0; newFCode = newFCode * 37.43; } } } return newFCode; } /********************************************* * Set new fCode and range on encoder events * * dir = true --> 1 step up * * = false --> 1 step down * *********************************************/ void setFreq(bool dir) { if (stepMode == 0) { //Coarse mode fCode = handleCoarseMode(dir); //Get new fCode value } else { //Fine mode fCode = handleFineMode(dir, fCode); //Get new fCode value } } /********************************************** * Set frequency by sending new frequency code * * and range to Generator module * **********************************************/ void setVoltageAndRange(uint16_t newFreq, uint8_t newRange) { dac.setVoltage(newFreq, false); //Set DAC output voltage digitalWrite(rangeOut, newRange); //Write range to rangeOut df = frequency_changed; } void setup() { pinMode(rangeButtonPin, INPUT_PULLUP); pinMode(rangeOut, OUTPUT); pinMode(encSW, INPUT); //Use INPUT_PULLUP if encoder doesn't //have pullup resistor pinMode(encA, INPUT); pinMode(encB, INPUT); Serial.begin(9600); Serial.println("---- GENERATOR START ----"); if(dac.begin(DACaddr)) { Serial.println("----- DAC is ready ------"); fCode = freqCode[ind]; setVoltageAndRange(fCode, range); lastStateA = digitalRead(encA); //Get initial state of CLK } else{ Serial.println("-> DAC error. Press RESET"); while (true){}; //stop program operation } } void loop() { // ==================================== // Read range button and toggle range // ==================================== if (digitalRead(rangeButtonPin) == LOW) { waitButtonRelease(rangeButtonPin); range = range ^ 0x1; //invert range value setVoltageAndRange(fCode, range); } // ==================================== // Read encoder button and toggle step // ==================================== if (digitalRead(encSW) == LOW) { waitButtonRelease(encSW); stepMode = stepMode ^ 0x1; //Invert stepMode value df = stepMode_changed; if (stepMode == 0) { //If we have returned from fine mode fCode = freqCode[ind]; //to сoarse - recall old frequency setVoltageAndRange(fCode, range); } } // ==================================== // Read encoder and set frequency // ==================================== currentStateA = digitalRead(encA); if ((currentStateA != lastStateA) && (currentStateA == LOW)) { setFreq(digitalRead(encB) == HIGH); //if true - clockwise //else - counterclockwise setVoltageAndRange(fCode, range); } lastStateA = currentStateA; // ==================================== // Display if any changes have happened // ==================================== switch (df) { case range_changed: Serial.print("Range="); Serial.println(range); break; case stepMode_changed: Serial.print("StepMode="); Serial.println(stepMode); break; case frequency_changed: Serial.print("Range="); Serial.print(range); if (stepMode == 0) { Serial.print(" ind="); Serial.print(ind); } Serial.print(" fCode="); Serial.println(fCode); break; } df = nothing; }