Differences

This shows you the differences between two versions of the page.

Link to this comparison view

test-and-measurement:analog-discovery-pro-3x50:midi [2021/10/06 14:24] – [Writing the Software] Álmos Veres-Vitályostest-and-measurement:analog-discovery-pro-3x50:midi [2022/10/04 11:38] (current) – external edit 127.0.0.1
Line 1: Line 1:
 ====== Building a MIDI Controller with Analog Discovery Pro (ADP3450/3250) ====== ====== Building a MIDI Controller with Analog Discovery Pro (ADP3450/3250) ======
 ~~TechArticle~~ ~~TechArticle~~
 +
 +{{ :test-and-measurement:analog-discovery-pro-3x50:bandlab_in_use.png?600 |}}
  
 <WRAP center round todo 60%> <WRAP center round todo 60%>
Line 8: Line 10:
  
 <WRAP group> <WRAP group>
- 
- 
 A Musical Instrument Digital Interface (MIDI) controller is a key element in electronic music production. Such devices, both hardware and software, can be quite expensive, but with a bit of work, the Analog Discovery Pro can be set up as a highly customizable MIDI controller, with wireless connection capability. This guide presents, how to configure your Test & Measurement device and your computer to be able to send and receive MIDI commands over Ethernet or Wi-Fi. A Musical Instrument Digital Interface (MIDI) controller is a key element in electronic music production. Such devices, both hardware and software, can be quite expensive, but with a bit of work, the Analog Discovery Pro can be set up as a highly customizable MIDI controller, with wireless connection capability. This guide presents, how to configure your Test & Measurement device and your computer to be able to send and receive MIDI commands over Ethernet or Wi-Fi.
 </WRAP> </WRAP>
Line 21: Line 21:
   * hardware user interface elements (buttons, switches, sliders, etc.)   * hardware user interface elements (buttons, switches, sliders, etc.)
   * passive electronic components   * passive electronic components
-  * other...+
 == Software == == Software ==
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
Line 39: Line 39:
     * WaveForms SDK - preinstalled     * WaveForms SDK - preinstalled
     * [[https://mautic.digilentinc.com/adept-utilities-download|Adept Utilities]]     * [[https://mautic.digilentinc.com/adept-utilities-download|Adept Utilities]]
-</WRAP></WRAP> +</WRAP></WRAP></WRAP>
-</WRAP>+
 ---- ----
  
-===== Introduction ===== +===== What is MIDI? What is RTP-MIDI? What is a DAW? =====
-==== What is MIDI? What is RTP-MIDI? What is a DAW? ====+
 <WRAP group> <WRAP group>
 Musical Instrument Digital Interface (MIDI) is a technical standard which is used in electronic music production. MIDI controllers are not instruments, they do not produce sounds, but a send a specific set of commands to other devices, or software, like a Digital Audio Workstation (DAW). These commands contain all the information necessary to produce the desired sound, or to apply the desired effect. The most important advantage of a MIDI controller is, that with the same commands, the user can control any instrument of their choice (for example you can "play" a clarinet or a guitar with keypresses). Musical Instrument Digital Interface (MIDI) is a technical standard which is used in electronic music production. MIDI controllers are not instruments, they do not produce sounds, but a send a specific set of commands to other devices, or software, like a Digital Audio Workstation (DAW). These commands contain all the information necessary to produce the desired sound, or to apply the desired effect. The most important advantage of a MIDI controller is, that with the same commands, the user can control any instrument of their choice (for example you can "play" a clarinet or a guitar with keypresses).
Line 71: Line 69:
 {{ :test-and-measurement:analog-discovery-pro-3x50:bandlab_midi_device.png?600 |}} {{ :test-and-measurement:analog-discovery-pro-3x50:bandlab_midi_device.png?600 |}}
 </WRAP></WRAP> </WRAP></WRAP>
 +----
  
 ==== Setting Up the Analog Discovery Pro as a MIDI Controller ==== ==== Setting Up the Analog Discovery Pro as a MIDI Controller ====
Line 77: Line 75:
 To successfully run this example, you will need the latest Linux version for your device. To update the ADP3450/ADP3250 Linux image, follow this guide: [[test-and-measurement:analog-discovery-pro-3x50:recover-linux-mode]]. To successfully run this example, you will need the latest Linux version for your device. To update the ADP3450/ADP3250 Linux image, follow this guide: [[test-and-measurement:analog-discovery-pro-3x50:recover-linux-mode]].
  
-The first step is to connect your device to the internet. If you already have internet enabled for Linux mode, just connect the Ethernet cable and skip this step.+Now connect your device the internet. If you already have internet enabled for Linux mode, just connect the Ethernet cable and skip this step. Else, follow this guide: [[test-and-measurement:analog-discovery-pro-3x50:connect-to-the-internet]].
 </WRAP> </WRAP>
  
---> Connect the Analog Discovery Pro (ADP3450/ADP3250) to the Internet # 
-<WRAP group> 
-{{section>test-and-measurement:analog-discovery-pro-3x50:connect-to-the-internet#setting_up_the_connection&noheader}} 
-</WRAP> 
-<-- 
----- 
- 
- 
-<WRAP group> 
-Now boot into Linux mode. If your device already is in Linux mode, just skip this step. 
- 
---> Booting into Linux Mode # 
-<WRAP group><WRAP column half> 
-Mode selection can be done through WaveForms using the Device Manager. With the Analog Discovery Pro connected to the host computer by Ethernet, open WaveForms. If the Device Manager hasn't opened yet, then click Settings -> Device Manager. 
-</WRAP><WRAP column half> 
-{{ :reference:software:waveforms:waveforms-sdk:eth_st.png?400 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP column half> 
-Select the Analog Discovery Pro from the device menu, then click the Boot button. The Boot Mode window should open. By default, Standard Mode which allows the user to communicate with the instrumentation device over USB or Ethernet will be selected. 
- 
-To change to Linux Mode, select Linux, then click "Apply & Reboot". 
-</WRAP><WRAP column half> 
-{{ :reference:software:waveforms:waveforms-sdk:linux_mode.png?400 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP column half> 
-At this time, an audible click should be heard and the Analog Discovery Pro will disconnect from WaveForms. About 10-15 seconds should pass before it reconnects. Confirm that the second column next to the Analog Discovery Pro says "ETH:LX", confirming the mode has successfully been switched. 
-</WRAP><WRAP column half> 
-{{ :reference:software:waveforms:waveforms-sdk:eth_lx.png?400 |}} 
-</WRAP></WRAP> 
-<-- 
-</WRAP> 
----- 
- 
-<WRAP group> 
-Connect to the device with a terminal emulator through the internet. 
---> Connect to the ADP3450 # 
 <WRAP group> <WRAP group>
 To command the ADP3450/ADP3250, you will have to connect to it through a terminal. To do so, you will need a terminal emulator program, like [[https://ttssh2.osdn.jp/|Tera Term]], [[https://www.chiark.greenend.org.uk/~sgtatham/putty/|PuTTY]] for Windows, or [[https://play.google.com/store/apps/details?id=de.kai_morich.serial_wifi_terminal|Serial WiFi Terminal]] for Android (the Analog Discovery Pro can now be controlled from any device connected to the internet). To command the ADP3450/ADP3250, you will have to connect to it through a terminal. To do so, you will need a terminal emulator program, like [[https://ttssh2.osdn.jp/|Tera Term]], [[https://www.chiark.greenend.org.uk/~sgtatham/putty/|PuTTY]] for Windows, or [[https://play.google.com/store/apps/details?id=de.kai_morich.serial_wifi_terminal|Serial WiFi Terminal]] for Android (the Analog Discovery Pro can now be controlled from any device connected to the internet).
  
-Tera Term will be used in this guide, but the steps are similar for each of the mentioned programs. +Tera Term will be used in this guide, but the steps are similar for each of the mentioned program.
-</WRAP>+
  
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
Line 134: Line 93:
 </WRAP><WRAP half column> </WRAP><WRAP half column>
 {{ :test-and-measurement:analog-discovery-pro-3x50:ssh.png?400 |}} {{ :test-and-measurement:analog-discovery-pro-3x50:ssh.png?400 |}}
-</WRAP></WRAP> +</WRAP></WRAP></WRAP>
-<-- +
-</WRAP> +
-----+
  
 <WRAP group> <WRAP group>
-First, install the necessary packages by entering the following command into the terminal:+Install the necessary packages by entering the following command into the terminal:
  
 <code>sudo apt-get install autoconf build-essential libavahi-client-dev libavahi-core-dev libasound2-dev git avahi-daemon -y</code> <code>sudo apt-get install autoconf build-essential libavahi-client-dev libavahi-core-dev libasound2-dev git avahi-daemon -y</code>
Line 178: Line 134:
 The control signals can be divided in three main categories: digital signals (coming from buttons, switches and rotary encoders), slowly changing analog signals (coming from potentiometers/sliders) and fast changing analog signals (coming from pressure sensitive pads). These three categories will be discussed separately, as the methods used to register them differ. The control signals can be divided in three main categories: digital signals (coming from buttons, switches and rotary encoders), slowly changing analog signals (coming from potentiometers/sliders) and fast changing analog signals (coming from pressure sensitive pads). These three categories will be discussed separately, as the methods used to register them differ.
  
-Digital signals can bi directly connected in the driver circuit. They must be debounced (the mechanical noise created by the "bouncing" of the pressed pushbuttons must be filtered out) and might be latched (momentary buttons acting like switches). If some signals have to increment/decrement certain counters, these counters can also be implemented in the driver circuit. When the input signals are conditioned, they de multiplexed to the digital inputs of the ADP.+Digital signals can be directly connected in the driver circuit. They must be debounced (the mechanical noise created by the "bouncing" of the pressed pushbuttons must be filtered out) and might be latched (momentary buttons acting like switches). If some signals have to increment/decrement certain counters, these counters can also be implemented in the driver circuit. When the input signals are conditioned, they can be multiplexed to the digital inputs of the ADP. 
 +</WRAP>
  
 +<WRAP group>
 Slowly changing analog signals can't be directly measured by the digital inputs of the ADP. While the analog inputs (oscilloscope channels) can measure them directly, they should be used to record fast changing analog signals, so another solution should be found. One method to transform the analog signals to digital ones is by using comparators: the comparators can compare the provided voltage level to a fast changing sawtooth signal, and output a PWM signal for which the duty cycle is proportional with the voltage level of the original signal. This duty cycle can be measured on a single digital pin, using the logic analyzer. The generated PWM signal comes connected to the driver circuit to be multiplexed to the digital inputs of the ADP. Slowly changing analog signals can't be directly measured by the digital inputs of the ADP. While the analog inputs (oscilloscope channels) can measure them directly, they should be used to record fast changing analog signals, so another solution should be found. One method to transform the analog signals to digital ones is by using comparators: the comparators can compare the provided voltage level to a fast changing sawtooth signal, and output a PWM signal for which the duty cycle is proportional with the voltage level of the original signal. This duty cycle can be measured on a single digital pin, using the logic analyzer. The generated PWM signal comes connected to the driver circuit to be multiplexed to the digital inputs of the ADP.
 </WRAP> </WRAP>
  
-<html><iframe width="1120" height="600" src="https://www.multisim.com/content/BCNHr52UHjWf3piskM4oRL/analog-to-duty/open"></iframe></html>+--> Click for Model and Simulation # 
 +<WRAP group> 
 +<html><iframe width="100%" height="600" src="https://www.multisim.com/content/BCNHr52UHjWf3piskM4oRL/analog-to-duty/open"></iframe></html> 
 +</WRAP> 
 +<--
  
 <WRAP group> <WRAP group>
Line 189: Line 151:
 </WRAP> </WRAP>
  
-==== PLD Design ==== +--> Click for Model and Simulation # 
-<WRAP group><WRAP half column> +<WRAP group> 
-As most of the control signals are digital (including the converted slow analog signals), the conditioning circuits for them will be also digital. To prototype a complex digital circuit, the easiest way is to use an FPGA development board. In this project the Arty-S7 FPGA board will be used, for which the design will be created in Multisim, to avoid using VHDL, or Verilog (which can be challenging, especially for new users) and to make the final circuit easier to understand by drawing a schematic of it. +<html><iframe width="100%" height="600" src="https://www.multisim.com/content/kekFtHeuH3GvY4PwjPwEeG/piezo-driver/open"></iframe></html> 
- +</WRAP> 
-If your chosen FPGA board isn't available in Multisim PLD design, follow the instructions in [[link_to_hackster_page|this]] guide to create the configuration files for it.+<-- 
 +----
  
-Start a new PLD design in Multisim, select your board and create the conditioning circuits for your signals. You should use some digital I/O pins of the ADP3450 as outputs to control the multiplexers connected to the input pins, this way connecting much more signals to the input, then the number of input pins. +==== User Interface Design ==== 
- +<WRAP group><WRAP half column> 
-When ready, click //Transfer -> Export to PLD... -> Generate and save programming file// to use Vivado and compile the bit file from the design.+Design the user interface circuit to contain the control and indicator elements you want. Keep in mind the offered possibilities and limitations of the ADP3450, and the signal conditioning methods discussed above.
 </WRAP><WRAP half column> </WRAP><WRAP half column>
-{{ :test-and-measurement:analog-discovery-pro-3x50:pld_design.png?600 |}}+{{ :test-and-measurement:analog-discovery-pro-3x50:pcb_3d.png?600 |}}
 </WRAP></WRAP> </WRAP></WRAP>
  
-==== Configuring the ADP3450 to Upload the Bit File ==== 
 <WRAP group> <WRAP group>
-{{section>test-and-measurement:analog-discovery-pro-3x50:programming-an-fpga#installing_the_adept_utilities&noheader}} +For a detailed presentation of the user interface circuitry used in this guide, you can check the drop-down below, or you can design your own custom user interface.
-Now you can use the "djtgcfg" tool to upload bit files to Digilent FPGA boards. +
-</WRAP>+
  
-==== Build and Connect the Circuit ====+--> The User Interface Circuit #
 <WRAP group> <WRAP group>
-The controller consists of three hardware parts: the actual user interface, the UI driver realized on the FPGA board and the Analog Discovery Pro. Before designing the driver and the software, the user interface must be built. Use several buttons, switches, sliders, rotary encoders, etc. to create your user interface. The control interface used in this design is presented in detail below.+<WRAP group> 
 +=== Parts of the User Interface === 
 +The user interface consists of five main parts: 
 +  * The most important control elements are the play/pause button, the record button, the volume slider and the channel selector button. To display the selected channel, this circuit part also contains a [[pmod:pmodssd:start|]] connected and driven by the digital driver (implemented on the Arty-S7 FPGA board). 
 +  * To further shave the sound of the controller, the second part contains three filters (low, middle and high). The analog-to-PWM converter for the volume slider and the filters is present in this block as well. Incremental rotary encoders are used to set the modulation, the attack, or the release time of the sounds. 
 +  * In the third block, six potentiometers (with analog-to-PWM converters) are used to control the effects set in the DAW. 
 +  * While traditional keypads have more buttons, this controller only has 12: for the notes C, C#, D, D#, E, F, F#, G, G#, A, A# and B. Two additional buttons can increment and decrement the octave. The index of the chosen octave is displayed on a seven segment display. The RGB LEDs used to implement visual effects are connected to a connector for an open-drain MOSFET driver module ([[pmod:pmodod1:start|]]). 
 +  * The actual drum pads are mechanical parts, each one a disk containing a piezo, but the driver is implemented on the PCB. The outputs of the piezos are clamped and "multiplexed" with the method presented above. 
 +</WRAP>
  
---> The User Interface Circuit # 
 <WRAP group> <WRAP group>
-<WRAP group><WRAP half column> +=== Connecting the User Interface === 
-Use buttons with pull-down resistors for functions such as **play/pause**, **record**, **channel change**, **octave change**, **timing select**. +Connect the output signals of the user interface to the FPGA board, but keep track of the connections, as they will have to be defined in the PLD design later, in Multisim (see below).
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:buttons.png?400 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +The BNC oscilloscope connectors are connected directly to the drum pad outputs (multiplexed) and the waveform generator channels are used to generate the reference signal for the PWM generator and the -5V DC voltage reference. The USB ports on the ADP are used to power and program the FPGA, connect the Wi-Fi dongle and supply power to the displays and LEDs through a USB connector.
-The voltages falling on the **volume**, **equalizer** (high, middle and low) and **effect** potentiometers must be converted to PWM signals (digitized) in the way discussed previously under the [[test-and-measurement:analog-discovery-pro-3x50:midi#control_signals|Control Signals]] heading. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:potentiometers.png?600 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +The Pmod connectors are used on the Arty-S7 to connect the user interface control elements, while the Chipkit analog headers are used to connect the displays and the LEDs. The ADP3450 digital pins are connected to the outer digital Chipkit header. 
-Rotary encoder modules used for **modulation** and **attack time/release time** settings are just powered from the 3.3V supply, their output signals going directly into the driver. +</WRAP> 
-</WRAP><WRAP half column> +</WRAP> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:encoders.png?400 |}} +<-- 
-</WRAP></WRAP>+</WRAP> 
 +----
  
 +==== Driver Design ====
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
-The keys of the "piano" correspond of the notes **C**, **C#**, **D**, **D#**, **E**, **F**, **F#**, **G**, **G#**, **A**, **A#**, **B** and are connected in a similar way to the control buttons. +As most of the control signals are digital (including the converted "slow" analog signals), the conditioning circuits for them will be also digital. To prototype a complex digital circuit, the easiest way is to use an FPGA development board. In this project the Arty-S7 FPGA board will be used, for which the design will be created in Multisim, to avoid using VHDL, or Verilog (which can be challenging, especially for new users) and to make the final circuit easier to understand by drawing a schematic of it.
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:keys.png?400 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +If your chosen FPGA board isn't available in Multisim PLD design, follow the instructions in the [[learn:programmable-logic:tutorials:add_fpgas_to_multisim:start|]] guide to create the configuration files for it.
-Certain parts of the controller will be lit by RGB LED lights, the color depending on the channel used. To realize the lighting, an LED strip is used, which is powered from 5V. The color is set by three PWM signals coming from the ADP, the certain color LEDs being driven by N-channel MOSFETs in the Pmod OD1 (which also includes the MOSFET drivers). +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:rgb_led_strip.png?600 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +Start a new PLD design in Multisim, select your board and create the conditioning circuits for your signals. You should use some digital I/O pins of the ADP3450 as outputs to control the multiplexers connected to the input pins, this way you can connect much more signals to the input, then the number of input pins.
-The current MIDI channel and the index of the selected octave are displayed on two dual 7 segment displays with common anodes. As the LEDs of the displays are powered from 5V, both the high and the low side need switching elements. The low side can be switched using two Pmod OD1s, like in the case of the LED strip, but the high side must be switched by P-channel MOSFETs driven by NPN BJTs. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:7sd.png?600 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +When ready, click //Transfer -> Export to PLD... -> Generate and save programming file// to use Vivado and compile the bit file from the design.
-As drum pads, piezoelectric sensors are used. These contain materials, which generate a voltage if their shape changes. The generated voltage is rectified and clamped by a Zener diode. A current limiting resistor is also used in series with the transducers. The output of the "sensor" is pulled down by a large resistor, then a differential amplifier adds to every sensor signal the inverted signal of another sensor, this way the signals are multiplexed.+
 </WRAP><WRAP half column> </WRAP><WRAP half column>
-{{ :test-and-measurement:analog-discovery-pro-3x50:drums.png?600 |}}+{{ :test-and-measurement:analog-discovery-pro-3x50:hackster_cover_w_arty.png?600 |}}
 </WRAP></WRAP> </WRAP></WRAP>
  
----- +<WRAP group> 
-</WRAP> +For a detailed presentation of the driver circuitry used in this guide, you can check the drop-down below, or you can design your own driver on an FPGA board.
-<--+
  
-As there are too many control signals to connect them directly into the ADP, they have to be multiplexed. This multiplexing and also some signal conditioning can be done by a digital "driver" realized on an FPGA board. The design used on the Arty-S7 FPGA board is presented below. +--> The Driver Circuit # 
- +<WRAP group>
---> Signal Conditioning and Multiplexing with FPGA #+
 <WRAP group> <WRAP group>
- 
 === Hierarchical Blocks === === Hierarchical Blocks ===
-<WRAP group> +Before creating the driver, design and test certain circuits which will be used multiple times in the driver, to make your final design easier to create and to understand. Some blocks to create:   
-Before creating the driver, design and test certain circuits which will be used multiple times in the driver, to make your final design easier to create and understand. +  * clock divider - several D flip-flops are used to reduce the clock frequency to the desired value. If the clock rate must be further reduced, multiple clock divider blocks can be cascaded. 
- +  * debouncer - when a button is pressed, some mechanical noise appears in the signal (the bouncing of the two metal plates on each other), so if the action is taken on the edge of the signal, the circuit will be triggered multiple times. A debouncer "validates" the output signal only after a certain time, so the bouncing can come to an end before. 
-<WRAP group><WRAP half column> +  * latch - so buttons can be used as switches (on press turns the signal high, the next one turns it low). 
-The first hierarchical block created is the clock divider. Several D flip-flops are used to reduce the clock frequency to the desired value. If the clock rate must be further reduced, multiple clock divider blocks can be cascaded. +  * 1:2 demultiplexer - Multisim contains only larger circuits (1:8). 
-</WRAP><WRAP half column> +  * up-down counter to N (0<N<15) - this circuit will be used to change the octaves (there are only 11 valid octaves, so counting to 15 is unnecessary). 
-{{ :test-and-measurement:analog-discovery-pro-3x50:clock_divider.png?600 |}} +  * pulse counter - as the Python script will use polling to measure every signal, it might miss certain events of the rotary encoders, so they should be re-encoded as numbers. This is why a pulse counter is designed, which is an up-down counter clocked by the two waves of the rotary encoder in quadrature. The block turns the incremental rotary encoders into 4-bit absolute encoders. 
-</WRAP></WRAP> +  * 7 segment display decoder - while 4-bit binary numbers might be easy to understand for machines, on a user interface (created for humans) numbers should appear in decimal representation. This is why a decoder must be used for the dual 7 segment displays, which displays a number between 1 and 16 (received in binary) in decimal representation. 
- +  * display selector - while most FPGA boards have many pins, there are some cases, when "many" is still not enough. This is why the cathodes of the two dual displays should be multiplexed (the same cathodes are used in different times). This block takes two binary numbers (and one clock) as inputs and displays the first number on the first display (decoded) and the second number on the second display.
-<WRAP group><WRAP half column> +
-One of the most important building blocks is the debouncer. When a button is pressed, some mechanical noise appears in the signal (the bouncing of the two metal plates on each other), so if the action is taken on the edge of the signal, the circuit will be triggered multiple times. A debouncer "validates" the output signal only after a certain time, so the bouncing can come to an end before. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:debouncer.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-A simple latch should also be implemented, so buttons can be used as switches (on press turns the signal high, the next one turns it low). +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:latch.png?400 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-A 1:2 demultiplexer is also implemented as Multisim contains only larger circuits (1:8). +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:dmx12.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-The next block to be prepared is an up-down counter to N (0<N<15). This circuit will be used to change the octaves (there are only 11 valid octaves, so counting to 15 is unnecessary). +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:n_counter.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-As the Python script will use polling to measure every signal, it might miss certain events of the rotary encoders, so they should be re-encoded as numbers. This is why a pulse counter is designed, which is an up-down counter clocked by the two waves of the rotary encoder in quadrature. The block turns the incremental rotary encoders into 4-bit absolute encoders. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:pulse_counter.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-While 4-bit binary numbers might be easy to understand for machines, on a user interface (created for humans) numbers should appear in decimal representation. This is why a decoder must be used for the dual 7 segment displays, which displays a number between 1 and 16 (received in binary) in decimal representation. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:7sd_decoder.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-While most FPGA boards have many pins, there are some cases, when "many" is still not enough. This is why the cathodes of the two dual displays should be multiplexed (the same cathodes are used in different times). This block takes two binary numbers (and one clock) as inputs and displays the first number on the first display (decoded) and the second number on the second display. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:7sd_mux.png?600 |}} +
-</WRAP></WRAP>+
 </WRAP> </WRAP>
  
 +<WRAP group>
 === Driver === === Driver ===
-<WRAP group> 
 After all the blocks were designed and tested, the actual driver can be built. After all the blocks were designed and tested, the actual driver can be built.
  
-<WRAP group><WRAP half column> 
-Because both the user interface circuit and the Analog Discovery Pro will be connected to the FPGA board, these connections have to be defined first. To reduce the number of the required wires on the schematic, net labels or on-page connectors can be used. 
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:connections.png?600 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP half column> 
 As almost all conditioning circuits need clock signals, the derived clocks (with the desired frequency) must be obtained from the FPGA clock. Use clock dividers to reduce the frequency. As almost all conditioning circuits need clock signals, the derived clocks (with the desired frequency) must be obtained from the FPGA clock. Use clock dividers to reduce the frequency.
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:clock_divider_wrapper.png?600 |}} 
-</WRAP></WRAP> 
  
-<WRAP group><WRAP half column> +The RGB LED control signals are just passed through the FPGA, they are entirely generated by the ADP. The PWM signals coming from the potentiometer conditioning circuits are again buffered, then sent to the multiplexers.
-The RGB LED control signals are just passed through the FPGA, they are entirely generated by the ADP. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:rgb_passthrough.png?400 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +The play/pause and record buttons are debounced and latched to act like switches. The piano keys are debounced and sent to the multiplexers.
-The play/pause and record buttons are debounced and latched to act like switches. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:ply_rec_btn.png?400 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +The button selecting the MIDI channel is debounced, then connected to an up counter. The output of the counter is connected to the multiplexers. Octaves are set by two buttons: one buttons shifts the octave up, the other one down. The N counter block is used to set the upper limit of counting to 10 (the block will count from 0 to 10 - 11 positions).
-The PWM signals coming from the potentiometer conditioning circuits are again buffered, then sent to the multiplexers. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:potentiometers_buffered.png?600 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +The pulse counter block is used to turn the incremental encoder used as modulation wheel into an absolute encoder. The same is done for the timing wheel, but the latched timing button decides, if the output is used for the attack time, or for the release time (the distinction is made from software).
-The button selecting the MIDI channel is debounced, then connected to an up counter. The output of the counter is connected to the multiplexers. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:channel_counter.png?600 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> 
-The pulse counter block is used to turn the incremental encoder used as modulation wheel into an absolute encoder. 
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:modulation_counter.png?400 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP half column> 
-The same is done for the timing wheel, but the latched timing button decides, if the output is used for the attack time, or for the release time (the distinction is made from software). 
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:timing_counter.png?400 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP half column> 
-Octaves are set by two buttons: one buttons shifts the octave up, the other one down. The N counter block is used to set the upper limit of counting to 10 (the block will count from 0 to 10 - 11 positions). 
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:octave_counter.png?600 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP half column> 
-The piano keys are debounced and sent to the multiplexers. 
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:piano_keys.png?600 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP half column> 
 The previously created hierarchical blocks are used to display the channel number and the octave index on the 7 segment displays. The previously created hierarchical blocks are used to display the channel number and the octave index on the 7 segment displays.
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:7sd_wrapper.png?600 |}} 
-</WRAP></WRAP> 
  
-<WRAP group><WRAP half column> 
 Finally, the conditioned output signals are multiplexed (4:1) to the digital I/O pins of the ADP. To generate the multiplexer addresses, two digital pins of the ADP are used, the address being generated from software. Finally, the conditioned output signals are multiplexed (4:1) to the digital I/O pins of the ADP. To generate the multiplexer addresses, two digital pins of the ADP are used, the address being generated from software.
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:driver_mux.png?200 |}} 
-</WRAP></WRAP> 
 </WRAP> </WRAP>
  
----- 
-</WRAP> 
-<-- 
- 
-Finally connect the conditioned input and output signals into the ADP. Also connect the FPGA board to one USB port of the Analog Discovery Pro, the Wi-Fi dongle to another one and use a third port as a power supply. 
- 
---> Connections to the ADP3450 # 
 <WRAP group> <WRAP group>
-The next step is to connect the three main parts of the controller. The Pmod connectors are used on the Arty-S7 to connect the user interface control elements, while the Chipkit analog headers (along with the reference and reset signals) are used to connect the displays and the LEDs. The ADP3450 digital pins are connected to the outer digital Chipkit header. +=== Connecting the Driver === 
- +In Multisim, define the connections between the FPGA board and the ADP. 
-The BNC oscilloscope connectors are connected directly to the drum pad outputs (multiplexed) and the waveform generator channels are used to generate the reference signal for the PWM generator and the -5V DC voltage reference. The USB ports on the ADP are used to power and program the FPGA, connect the Wi-Fi dongle and supply power to the displays and LEDs through a type A USB connector. +</WRAP>
- +
-{{ :test-and-measurement:analog-discovery-pro-3x50:adp3450_artys7.png?800 |}}+
 </WRAP> </WRAP>
 <-- <--
Line 413: Line 252:
 ---- ----
  
-===== Writing the Software ===== +===== Designing the Software ===== 
-===== Script Overview =====+==== Hardware Abstraction Layer ====
 <WRAP group> <WRAP group>
-The Python script which controls the Analog Discovery Pro consists of 8 modules plus the dwfconstants python module from the WaveForms SDK and a configuration file for RaveloxMIDI. In the following, these modules will be presented briefly. The full source code of the project can be downloaded {{ :test-and-measurement:analog-discovery-pro-3x50:midi_controller.zip |here}}.+To make the program simpler and easier to understand, a sort of Hardware Abstraction Layer (HAL) is needed: a set of functions, which communicate with the hardware, and make the usage of the hardware easier. In this case the WaveForms SDK is used to control the ADP3450, but to further simplify the final script, the module created in the [[test-and-measurement:guides:waveforms-sdk-getting-started|]] guide will be used.
 </WRAP> </WRAP>
 +----
 +==== Configuring the FPGA ====
 +<WRAP group>
 +To configure the FPGA board connected to the ADP3450, with the bit file created with Multisim, the [[https://mautic.digilentinc.com/adept-utilities-download|Adept Utilities]] must be installed on your device. To install the software, follow the [[test-and-measurement:analog-discovery-pro-3x50:programming-an-fpga|]] guide.
  
-=== main.py === +Create a module in which you use in a subprocess the commands 
-<WRAP group><WRAP half column> +<code>djtgcfg init -d ArtyS7</code> 
-The main.py file is a wrapper: it imports other modules, initializes the input and output devices and processes, then, in an infinite loop, decodes and sends keypad events. The script is terminated by pressing Ctrl+C, in which case a proper cleanup is done before exiting. +and 
-</WRAP><WRAP half column> +<code>djtgcfg prog -d ArtyS7 -i 0 -f ./FPGA/midi_driver_ArtyS7.bit</code>
-<code python>try: +
-    # initialize input and output processes +
-    DI.open() +
-    DO.open()+
  
-    # repeat +**Note: **//Don't forget to change the device name and the name of the bit file to match to your preferences.//
-    while True: +
-        # send decoded keypad events +
-        DO.write(DI.read())+
  
-except KeyboardInterrupt: +<WRAP center round tip 80%> 
-    # exit on Ctrl+C +In your script, check for the responses of the commands to determine if the operation was successful, or not. You can try the commands first in a terminal to check possible responses.
-    pass +
- +
-finally: +
-    # close every opened device/process +
-    DI.close() +
-    DO.close() +
-</code>+
 </WRAP></WRAP> </WRAP></WRAP>
 +----
  
-=== data_input.py === +==== MIDI ====
-<WRAP group><WRAP half column> +
-The input module gets the current state of the keypad and compares it to the previous state, then returns only the differences between the two states. This way only the keypad events like pressing and releasing the buttons will be processed. +
-</WRAP><WRAP half column> +
-<code python>def read(): +
-    # read the keypad events +
-    global last_state   # load last state +
-    current = KYPD.get_keys()   # get the current state +
-    buffer = []  # a buffer to store the results +
-    # if the two states are not equal +
-    if current != last_state: +
-        buffer = difference(current, last_state)    # get the differences +
- +
-        # if the script has to be debugged +
-        if param.DEBUG == "IN" or param.DEBUG == "ALL": +
-            # print the differences +
-            print(buffer) +
- +
-        # save the current state of the keypad +
-        last_state = current +
- +
-    # return the results +
-    return buffer +
-</code> +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-The comparison function consists of two loops: the first one takes every element from the first list and compares it with every element from the second one, and the second loop compares every element of the second list to every element of the first one. The results contain tuples of the unique elements and a True, or False constant. If the unique element appears in the first list, the constant is true, otherwise it is false. +
-</WRAP><WRAP half column> +
-<code python>def difference(a, b): +
-    # return the differences between two lists +
-    result = []  # store the result +
- +
-    # go through the first list +
-    for element_a in a: +
-        unique = True   # flag to signal uniqueness +
-        # compare the current element with every element from the second list +
-        for element_b in b: +
-            # if they are equal +
-            if element_a == element_b: +
-                unique = False  # set the flag to false +
-        # if the flag is still true +
-        if unique: +
-            # append the element and a True constant to the results +
-            result.append(tuple([element_a, True])) +
- +
-    # go through the second list +
-    for element_b in b: +
-        unique = True   # flag to signal uniqueness +
-        # compare the current element with every element from the first list +
-        for element_a in a: +
-            # if they are equal +
-            if element_b == element_a: +
-                unique = False  # set the flag to false +
-        # if the flag is still true +
-        if unique: +
-            # append the element and a False constant to the results +
-            result.append(tuple([element_b, False])) +
- +
-    # return the results +
-    return result +
-</code> +
-</WRAP></WRAP> +
- +
-=== data_output.py === +
-<WRAP group><WRAP half column> +
-The data output module decodes the keypad events according to the parameters set by the user, then sends the decoded message using RTP-MIDI protocol. +
-</WRAP><WRAP half column> +
-<code python>def write(buffer): +
-    # send the decoded keypad events +
-    # go through the buffer +
-    for element in buffer: +
-        command = None  # a variable to save the event type +
- +
-        # check for event type +
-        if element[1]: +
-            # if the key is pressed, send a NOTE_ON command +
-            command = msg.NOTE_ON +
-        else: +
-            # if the key is released, send a NOTE_OFF command +
-            command = msg.NOTE_OFF +
- +
-        # send the decoded keypress with the command +
-        MIDI.send(command, msg.CH1, [ +
-                  param.NOTE_keymap.get(element[0]), velocity]) +
- +
-        # if the script has to be debugged +
-        if param.DEBUG == "OUT" or param.DEBUG == "ALL": +
-            # print the current keypad event +
-            print(element) +
-    return +
-</code> +
-</WRAP></WRAP> +
- +
-=== parameter.py === +
-<WRAP group><WRAP half column> +
-The module containing the parameters is the place where the user can modify the behavior of the MIDI controller, by setting a specific command, or note for every button. The structure of the keypad is also set here, along with the list of pins, to which it is connected. +
-</WRAP><WRAP half column> +
-<code python># set keypad parameters +
-KYPD_rows = [7, 6, 5, 4]    # pins connected to keypad rows +
-KYPD_columns = [3, 2, 1, 0]  # pins connected to keypad columns +
-# the layout of the keypad +
-KYPD_keymap = [["1", "2", "3", "A"], +
-               ["4", "5", "6", "B"], +
-               ["7", "8", "9", "C"], +
-               ["0", "F", "E", "D"]] +
-</code> +
-<code python># set event decoding parameters +
-NOTE_velocity = 127  # note velocity +
-# what do the keys mean +
-NOTE_keymap = { +
-    "1": GREAT_O + C, "2": GREAT_O + CS, "3": GREAT_O + D, "A": GREAT_O + DS, +
-    "4": GREAT_O + E, "5": GREAT_O + F, "6": GREAT_O + FS, "B": GREAT_O + G, +
-    "7": GREAT_O + GS, "8": GREAT_O + A, "9": GREAT_O + AS, "C": GREAT_O + B, +
-    "0": SMALL_O + C, "F": SMALL_O + CS, "E": SMALL_O + D, "D": SMALL_O + DS +
-} +
-</code> +
-</WRAP></WRAP> +
- +
-=== PMOD_KYPD.py === +
-<WRAP group><WRAP half column> +
-This module contains functions specific to the Pmod KYPD. Initialization is done by initializing the WaveForms SDK, connecting the Analog Discovery Pro, turning on the power supplies and setting a specified list of pins to a starting state. +
- +
-The state of the keypad is retrieved by sweeping through the row pins, setting one to a HIGH state and the others to a LOW state in every iteration. After setting a specific row to HIGH, all the columns are checked. If the state of a column pin is HIGH, it means, that the button which is in the respective column, in the respective row, is pressed. +
-</WRAP><WRAP half column> +
-<code python>def get_keys(): +
-    # get the current state of the Pmod KYPD +
-    result = []  # buffer to store the results +
- +
-    # go through all the rows +
-    for index_1 in range(len(rows)): +
-        # set the state of every row +
-        for index_2 in range(len(rows)): +
-            # only one row is HIGH at every moment +
-            if index_1 == index_2: +
-                # set the current row HIGH +
-                WF.write(rows[index_2], True) +
-            else: +
-                # set the current row LOW +
-                WF.write(rows[index_2], False) +
- +
-        # check every column +
-        for index_2 in range(len(columns)): +
-            # if the current column is HIGH +
-            if WF.read(columns[index_2]): +
-                # append the respective key to the results +
-                result.append(keymap[index_1][index_2]) +
- +
-    # return the results +
-    return result +
-</code> +
-</WRAP></WRAP> +
- +
-=== WF_SDK.py === +
-<WRAP group><WRAP half column> +
-To make the project more modular, thus easier to debug, "user friendly" function to control certain instruments with the WaveForms SDK are implemented in a separate module. The WF_SDK.py file contains functions like the one needed to initialize communication with the Analog Discovery Pro, functions for setting digital pins as output, to read, or to write their states. +
-</WRAP><WRAP half column> +
-<code python>def read(pin): +
-    # read a digital pin +
-    # load internal buffer with current state of the pins +
-    dwf.FDwfDigitalIOStatus(hdwf) +
-    dwRead = c_uint32()  # variable for this current state +
-    # get the current state of the pins +
-    dwf.FDwfDigitalIOInputStatus(hdwf, byref(dwRead)) +
-    # convert the state to a 16 character binary string +
-    data = list(bin(dwRead.value)[2:].zfill(16)) +
- +
-    # check the required bit +
-    if data[15 - pin] != "0": +
-        # if it is one, return True (HIGH) +
-        return True +
-    else: +
-        # else return False (LOW) +
-        return False +
-</code> +
-<code python>def write(pin, state): +
-    # write a logic value on a pin +
-    global output_state  # load current state +
-    index = pow(2, pin)  # calculate the position of the required pin +
- +
-    # if a HIGH value has to be written +
-    if state != 0 and state != False: +
-        # calculate the bitmask +
-        mask = int(output_state, 2) | index +
-        # convert the current state to a list +
-        temp = list(output_state) +
-        # edit this list to save the new state +
-        temp[15 - pin] = "1" +
-        # convert back the list to string +
-        output_state = "".join(temp) +
- +
-    # if a LOW value has to be written +
-    else: +
-        # calculate the bitmask +
-        mask = int(output_state, 2) & (~index) +
-        # convert the current state to a list +
-        temp = list(output_state) +
-        # edit this list to save the new state +
-        temp[15 - pin] = "0" +
-        # convert back the list to string +
-        output_state = "".join(temp) +
- +
-    # set the bits according to the mask +
-    dwf.FDwfDigitalIOOutputSet(hdwf, c_int(mask)) +
-    return +
-</code> +
-</WRAP></WRAP> +
- +
-=== rtpmidi.py ===+
 <WRAP group> <WRAP group>
-The module which realizes the actual RTP-MIDI communication, using RaveloxMIDI, is implemented in the rtpmidi.py file. In this module the RaveloxMIDI is started as a background process, then communication is resolved through a socket. The script is a modified version of some RaveloxMIDI example scripts.+Start raveloxmidi in the background as a subprocess, to be able to communicate using RTPMIDI. Use the following command: 
 +<code>sudo raveloxmidi -c ./MIDI/address.conf</code> 
 +Don't forget to change the path of the configuration file, to a file, which contains the bind address: "0.0.0.0". 
 +<code>network.bind_address = 0.0.0.0</code>
 </WRAP> </WRAP>
  
-=== midi_message.py === 
 <WRAP group> <WRAP group>
-This module contains the list of MIDI commands for different operations. For a more detailed insight, check the drop-downs below, or the file itself.+While the background process is running, you can send data sockets containing MIDI data. An extensive list of possible MIDI messages is available on the page [[https://wiki.cockos.com/wiki/index.php/MIDI_Specification|MIDI Specification]] and [[https://anotherproducer.com/online-tools-for-musicians/midi-cc-list/|MIDI CC List for Continuous Controllers]].
  
---> MIDI Message Types # +See the drop-downs below for the most important MIDI messages.
- +
-|<100% 20% 10% 70%>| +
-^ Message Type  ^ Code ^ Meaning                                                       ^ +
-| NOTE_OFF      | 0x80 | This message is sent when a note is stopped.                  | +
-| NOTE_ON       | 0x90 | This message is sent when a note is started.                  | +
-| POLY_KEY_PRES | 0xA0 | Polyphonic Key Pressure (Aftertouch)                          | +
-| CTRL_CHG      | 0xB0 | Sent when a controller value changes.                         | +
-| PRG_CHG       | 0xC0 | Sent when a patch number changes.                             | +
-| CH_PRS_AFTT   | 0xD0 | Channel Aftertouch                                            | +
-| PITCH_BND     | 0xE0 | This message is sent to indicate a change in the pitch wheel. | +
-| SYS_EX_START  | 0xF0 | This message makes up for all that MIDI doesn't support.      |+
  
 +--> MIDI Message Types ^
 +<WRAP group>
 +|<100% 10% 10% 50% 10% 10% 10%>|
 +^ Message Type  ^ Code ^ Meaning                                                       ^ Data Byte 1       ^ Data Byte 2 ^
 +| NOTE_OFF      | 0x80 | This message is sent when a note is stopped.                  | Note number       | Velocity    |
 +| NOTE_ON       | 0x90 | This message is sent when a note is started.                  | Note number       | Velocity    |
 +| POLY_KEY_PRES | 0xA0 | Polyphonic Key Pressure (Aftertouch)                          | Note number       | Pressure    |
 +| CTRL_CHG      | 0xB0 | Sent when a controller value changes.                         | Controller number | ...         |
 +| PRG_CHG       | 0xC0 | Sent when a patch number changes.                             | Program number    | -           |
 +| CH_PRS_AFTT   | 0xD0 | Channel Aftertouch                                            | Pressure          | -           |
 +| PITCH_BND     | 0xE0 | This message is sent to indicate a change in the pitch wheel. | LSB               | MSB         |
 +| SYS_EX_START  | 0xF0 | This message makes up for all that MIDI doesn't support.      | ...               | ...         |
 +</WRAP>
 <-- <--
  
---> MIDI Notes (must be added to the octave) # +<WRAP group><WRAP column half> 
 +--> MIDI Notes ^ 
 +<WRAP group>
 |<100% 50% 50%>| |<100% 50% 50%>|
 ^ Note ^ Value ^                                                       ^ ^ Note ^ Value ^                                                       ^
Line 692: Line 319:
 | A#   | 10    | | A#   | 10    |
 | B    | 11    | | B    | 11    |
 +</WRAP>
 <-- <--
- +</WRAP><WRAP column half> 
---> MIDI Octaves (must be added to the note) # +--> MIDI Octaves ^ 
 +<WRAP group>
 |<100% 20% 10% 70%>| |<100% 20% 10% 70%>|
 ^ Notation     ^ Value ^ Meaning               ^ ^ Notation     ^ Value ^ Meaning               ^
-| CN2_O        | 0     | Starts with $C_{-2}$. | +| DBL_CONTRA_O | 0     | Starts with $C_{-1}$. | 
-| CN1_O        | 12    | Starts with $C_{-1}$. | +| SUB_CONTRA_O | 12    | Starts with $C_{0}$. | 
-| SUB_CONTRA_O | 24    | Starts with $C_{0}$. | +| CONTRA_O     | 24    | Starts with $C_{1}$. | 
-| CONTRA_O     | 36    | Starts with $C_{1}$. | +| GREAT_O      | 36    | Starts with $C_{2}$. | 
-| GREAT_O      | 48    | Starts with $C_{2}$. | +| SMALL_O      | 48    | Starts with $C_{3}$. | 
-| SMALL_O      | 60    | Starts with $C_{3}$. | +| LINE1_O      | 60    | Starts with $C_{4}$. | 
-| LINE1_O      | 72    | Starts with $C_{4}$. | +| LINE2_O      | 72    | Starts with $C_{5}$. | 
-| LINE2_O      | 84    | Starts with $C_{5}$. | +| LINE3_O      | 84    | Starts with $C_{6}$. | 
-| LINE3_O      | 96    | Starts with $C_{6}$. | +| LINE4_O      | 96    | Starts with $C_{7}$. | 
-| LINE4_O      | 108   | Starts with $C_{7}$. | +| LINE5_O      | 108   | Starts with $C_{8}$. | 
-| LINE5_O      | 120   | Starts with $C_{8}$. | +| LINE6_O      | 120   | Starts with $C_{9}$. | 
 +</WRAP>
 <-- <--
 +</WRAP></WRAP>
 +</WRAP>
  
 +<WRAP group>
 +When you are finished, kill every process with the name "raveloxmidi" to terminate the connection.
 </WRAP> </WRAP>
 ---- ----
  
 +==== MIDI Controller Script ====
 +<WRAP group>
 +In your wrapper script, define the connections between the devices and other parameters like the frequency and amplitude/offset of the output signals, then import your HAL, the previously defined MIDI commands, functions and constants and the FPGA programming function. Use these modules to upload the bit file to the FPGA, start raveloxmidi and initialize the WaveForms SDK.
  
-===== Testing ===== +Start the WaveForms instruments needed in your controller: start the power supplies, generate the necessary analog and digital signals, set static DIO pins to the desired state and initialize the oscilloscope and the logic analyzer. (Don't forget to close these instruments and disconnect from the ADP at the end of your script! Also stop raveloxmidi when the script is terminated.)
-<WRAP group><WRAP half column> +
-Copy the Python scripts to a USB flash drive, formatted in FAT32 format, then plug it in one of the USB slots of the Analog Discovery Pro. List the available USB drives by:+
  
-<code>sudo fdisk -l</code>+The rest of your code should run in an infinite loop, broken only by pressing Ctrl+C. In each iteration set the address pins of the multiplexers in the driver to 0, 1, 2, or 3 (implement a 2 bit counter here), then read the current state of the multiplexer outputs. Decode these outputs according to the current address and compare the results to the previous ones. If there is no change in the state of a user interface element, no new command should be sent, but if there is a change, send the respective MIDI command with raveloxmidi.
  
-Note the drive letter and the partition number of your drive, as you will have to use these in the following. Mount and open the content of the drive by typing the following commands in the terminal emulator:+You might also want to set some visual effects using the RGB LEDs. Use your creativity to modify the colors according to the received data. 
 +</WRAP> 
 +----
  
-<code>sudo mount /dev/sda1 /mnt +===== Testing ===== 
-cd /mnt</code>+<WRAP group><WRAP half column> 
 +Copy the project containing the Python scripts on the Analog Discovery Pro 3450, with the help of a FAT32 formatted USB flash drive, or using [[https://winscp.net/eng/index.php|WinSCP]].
  
-Run the script by typing+Navigate into the project directory, then run the script by typing
  
-<code>sudo python3 main.py</code>+<code>sudo python3 midi_controller.py</code>
 </WRAP><WRAP half column> </WRAP><WRAP half column>
-{{ :test-and-measurement:analog-discovery-pro-3x50:mount_usb.png?400 |}}+{{ :test-and-measurement:analog-discovery-pro-3x50:sftp-winscp.png?600 |}}
 </WRAP></WRAP> </WRAP></WRAP>
  
Line 744: Line 379:
 Now you can use your wireless MIDI controller, to play any instrument you like - the limit is your imagination. You can expand the capabilities of your controller by adding extra digital and analog control elements, like sliders, more buttons, switches, etc. Now you can use your wireless MIDI controller, to play any instrument you like - the limit is your imagination. You can expand the capabilities of your controller by adding extra digital and analog control elements, like sliders, more buttons, switches, etc.
 </WRAP><WRAP half column> </WRAP><WRAP half column>
-{{ :test-and-measurement:analog-discovery-pro-3x50:drums.jpeg?600 |}}+{{ :test-and-measurement:analog-discovery-pro-3x50:bandlab_in_use.png?600 |}}
 </WRAP></WRAP> </WRAP></WRAP>
 ---- ----
Line 750: Line 385:
 ===== Next Steps ===== ===== Next Steps =====
 <WRAP group> <WRAP group>
-The script can be modified as needed for your project.+The script, the PLD design and the circuit can be modified as needed for your project.
  
 For more information on WaveForms SDK, see its [[software:waveforms:waveforms-sdk:start|Resource Center]]. For more information on WaveForms SDK, see its [[software:waveforms:waveforms-sdk:start|Resource Center]].