.. _activity_rc: **************************** Controlled RC **************************** .. note:: This activity should be done individually. Purpose ******* This activity uses a PWM output and an RC circuit to demonstrate the use of an ADC and a simple PI control routine. Additionally demonstrated is the use of a PWM output as an effective analog signal. Hardware and Tools ****************** * [|usbt|]: |m2k|, |disco|, or Bench-top Oscilloscope * |devboard_long| or |rslk_long| * Breadboard * 10 kΩ Potentiometer * 10 kΩ Resistor * 1 μF Capacitor Description *********** The :ref:`previous activity ` demonstrated one approach to performing an analog type measurement through the microcontroller's timer peripheral. The **Timer_A Capture** mode is very useful in acquiring analog signals from a time modulated digital carrier (e.g., encoder frequency). Of course, this approach is limited due to the requisite nature of the input signal; therefore, alternative means for capturing analog signals (e.g., true analog voltages) need to be explored as well. A common peripheral found within most microcontrollers is an **Analog-to-Digital Converter**, or **ADC**. This module is capable of converting an analog voltage directly to a numeric digital value (e.g., an 8-bit integer). This activity is designed to provide a quick overview of the initialization and use of an ADC in its basic mode: sample on request. The instructions below will guide you through using a PWM output to control the voltage stored on a capacitor and using an ADC to provide feedback to the control. This is implemented using a resistor-capacitor (RC) circuit; which is a simple *low-pass filter* that, if used with the right input frequencies, will effectively serve to average the signal over time: .. figure:: ../_static/activity_11/pwm_rc.svg :align: center :width: 300px PWM controlled RC circuit .. figure:: ../_static/activity_11/example_output.svg :align: center :width: 400px Example output of RC circuit (:math:`\approx 1.5 \mathrm{V}`) from PWM input (:math:`\approx 50 \%` duty cycle) The first step in this activity; however, it to ensure that the **ADC** is operating properly. This will be done using a potentiometer connected between **GND** and **3.3V**. Potentiometers --------------- Potentiometers are variable resistors (simplified) that may be used as variable voltage dividers: .. figure:: ../labs/img/pot_vdiv.svg :align: center :width: 350 px Resistor (left) and potentiometer (right) voltage dividers A potentiometer has a total resistance, :math:`R_{pot}` which is split into "legs"; e.g., :math:`R_1` and :math:`R_2`, such that :math:`R_{POT} = R_1 + R_2`. As the potentiometer is manipulated, the values of :math:`R_1` and :math:`R_2` will change, resulting in a changing output: .. math:: V_{out} = V_{in} \frac{R_2}{R_1 + R_2} = V_{in} \frac{R_2}{R_{POT}} Given this functionality, potentiometers are used very often as control knobs. For this activity, the "control knob" effectively just provides a linearly changing voltage between 0 V and 3.3 V when turning the potentiometer. .. _adc_config: ADC14 ----- The |msp_long| contains a single **ADC**, named the **ADC14**. The :ref:`DriverLib module documentation for the ADC14 ` provides a brief discussion on the module which will not be repeated here. The basic steps for setting up the **ADC14** module for sampling on a single input "channel" are: 1. Turn on the module via :code:`bool ADC14_enableModule()`, #. Initialize the module's input clock using :code:`bool ADC14_initModule()`, #. Set the desired resolution (number of bits to convert to) of the **ADC14** using :code:`void ADC14_setResolution()`, #. Set up the ADC14 input channel(s) to convert: 1. Select GPIO pin(s) that has(have) an analog alternate function and configure the pin to corresponding alternative function, #. Associate the **ADC14** with the selected GPIO pin(s) / ADC channel(s) using the analog input channel number (from the Alternate Function table) with :code:`bool ADC14_configureConversionMemory()`. This function associates the ADC channel with the conversion memory location; where the memory locations are specified with :code:`ADC_MEM0` through :code:`ADC_MEM31`. This function also requires that the voltage references be selected and specified as well. If an internally generated reference voltage is to be used, the **Ref_A** module must be configured to provide this voltage. #. Select the **ADC14** conversions to perform: (Multiple channels may be configured by the previous step but not converted): * For **Single Channel Conversions**: Use :code:`bool ADC14_configureSingleSampleMode()` to select the configured memory location (:code:`ADC_MEMx`) to convert and store. Only one conversion will occur each time the ADC is triggered. * For **Multiple Channel Conversions**: Use :code:`bool ADC14_configureMultiSequenceMode()` to select a range of configured memory locations (:code:`ADC_MEMx` to :code:`ADC_MEMy`) to convert and store. All configured channels/(:code:`ADC_MEMx`) locations will be converted each time the ADC is triggered. #. Enable the **ADC14** to either sample automatically or through a software request using :code:`bool ADC14_enableSampleTimer()`, #. And finally, indicate to the **ADC14** that it can start conversions when necessary using :code:`bool ADC14_enableConversion()`. Once configuration is complete, the **ADC14** may start converting the voltage on the specified pin(s). If the **ADC14** was configured to be triggered via a software request (:code:`ADC_MANUAL_ITERATION`), then the following steps may be taken (not using interrupts): 1. Trigger a conversion to start using :code:`bool ADC14_toggleConversionTrigger()`, #. Wait for the conversion to complete. This may be done by waiting until the **ADC14** is not "busy" through :code:`bool ADC14_isBusy()`, #. Retrieve the conversion result(s) with :code:`uint16_t ADC14_getResult()`. The conversion result from the **ADC14** will always be an unsigned integer number (:code:`uint16_t`) with the most significant bits being unused (set to 0). For example: for a specified 14-bit resolution, the conversion will always be of the format :code:`00XX XXXX XXXX XXXX`. Likewise, if the resolution was set to 10 bits, then the output would be :code:`0000 00XX XXXX XXXX`. This is known as a *Right Justified* output. The mapping of the analog values to this digital format is provided :ref:`here `. Control Routine --------------- This activity uses a **Proportional+Integral** (**PI**) control scheme to adjust the voltage output. The block diagram for the control scheme is provided below. .. figure:: ../_static/activity_11/rc_control_eq.png :align: center :width: 90% PWM controlled RC circuit As shown, the proportional control component is an **Open-Loop Control**, where the generated contribution is dependent only on the value of the desired voltage. The proportional control is supported by a crude system model that attempts to convert a desired voltage into a timer compare value, which is then scaled by the **Proportional Gain Constant**, :math:`k_p`. The integral control component is a **Closed-Loop Control**, with the encoder measurements used to calculate the response error. The response error is integrated (simplified: summed) over time and scaled by the **Integral Gain Constant**, :math:`k_i`. This control scheme is provided in the template project and does not need to be implemented. However, the values for :math:`k_p` and :math:`k_i` will need to be adjusted to cause the system to operate properly. Instructions ************ Part 1 ------ 1. Import the template project for this activity: :download:`activity_adc14-rc.zip <../_static/ccs_projects/activity_adc14-rc.zip>`. This project has **Timer_A2** preconfigured to generate a PWM output at 1 kHz and has the control algorithm described above written already. 2. Insert the potentiometer into a breadboard and connect two of the legs to ground and 3.3V (see left portion of schematic below). The wiper of the potentiometer, denoted as **DESIRED**, needs to be connected to a GPIO with analog support, such as P6.1. Pins that have analog support may be identified within the GPIO :ref:`gpio_af` table, specifically, by looking at the **tertiary** column, where analog is denoted by **a#** (where **#** is the ADC channel number). .. figure:: ../_static/activity_11/A11-schematics.svg :align: center :width: 500px Activity 11 Schematic 3. Complete the ADC initialization function :code:`ADCInit()`. Use the guidance from :ref:`above ` and: * Configure the ADC clock to be **6 MHz**. * Use the ADC in **Single Sample Mode**. * Set the microcontroller power supply voltage, 3.3 V, as the reference voltage (**AVCC**). * The ADC input channel is dependent on the GPIO pin used (e.g., P6.1). The associated channel number may be found in :ref:`gpio_af` as noted above. 4. Within the :code:`if(timer_flag)` statement inside the :code:`main()` function :code:`while(1)` loop: * add code to trigger the ADC conversion, * wait for the result, * save the result into the variable :code:`uint16_t pot_val`, * calculate the voltage measured by the ADC (use equation from the lecture or the :ref:`ADC14 Documentation `) and save into the :code:`float desired` variable. *Be careful of integer division here!* * Note: Printing of the variables is already added. 5. Connect the |usbt| or bench-top oscilloscope such that the potentiometer output value is measured. You may alternatively use one of the multimeters in the laboratory for this. 6. **Run and test the code**: If working correctly, the printed potentiometer output should match the measurement from the |usbt|. Rotate or turn the potentiometer to change the output voltage and verify that the printed and measured voltages react accordingly. Part 2 ------ 7. Build the RC circuit (10 kΩ, 1 μF, :math:`f_\mathrm{cutoff}\approx 16 \mathrm{Hz}`) on a breadboard along with the potentiometer (see right portion of schematic above). *Note that the left and right portions of the schematic are not physically connected*. The PWM will be output from P5.6 and the capacitor voltage, denoted as **ACTUAL**, connected to a GPIO with analog support, such as P6.0 (ADC input pin). 8. Modify the ADC initialization function to include conversions on the added pin: * The two conversions should be configured to use adjacent :code:`ADC_MEMx` locations, e.g., :code:`ADC_MEM0` and :code:`ADC_MEM1`. * Change the ADC to use **Multiple Channel Mode** (Multi-Sequence Mode). 9. Add additional lines of code within the :code:`main()` function to save the result for the RC circuit output into :code:`uint16_t rc_val` and calculate the corresponding voltage into :code:`float actual`. 10. Uncomment the "Control Routine" marked within :code:`if(timer_flag){`. 11. Connect the |usbt| oscilloscope, or bench-top oscilloscope, such that both the raw PWM output and capacitor voltage are being monitored. When running, the oscilloscope should show 2-5 cycles of the PWM output. 12. **Run and test the code**: The capacitor voltage (:code:`actual`) should approach the potentiometer voltage (:code:`desired`) , albeit very poorly: .. * The default control constants (:code:`kp = 0.1, ki = 0`) are *not good*. Do not expect the setpoint and capacitor value to match well for this step. You may also notice that a higher value for :code:`PWM Out` results in a lower voltage. This is because the **CCR** is set to :code:`TIMER_A_OUTPUTMODE_SET_RESET` as opposed to :code:`TIMER_A_OUTPUTMODE_RESET_SET`. * The default control constants (:code:`kp = 0.1, ki = 0`) are *not good*. Do not expect the setpoint and capacitor value to match well for this step. * Verify that the PWM is being generated via the |usbt| and the RC filter is working properly. * Rotate the potentiometer to change the desired output value. The PWM duty cycle and capacitor voltage should change, correspondingly. 13. Refer to the :xref:`Gradescope` assignment for the remainder of the activity. The steps required in the assignment are repeated here: #. Submit a screenshot of the oscilloscope showing the PWM and capacitor voltage. #. Record the actual voltage output from the RC filter for for when the desired voltage is approximately 0.5 V, 1.5 V, and 2.5 V. #. Change the proportional gain constant, `kp`, from 0.1 to 0.05 and repeat the measurements. #. Adjust the integral gain constant, `ki`, to find a value that causes the target and actual voltage to match reasonably well. For initial testing, have the actual voltage be approximately 1.5 V. Once a good value is found, ensure that the value works for other values of the target voltage. Record the working value for `ki`. #. Reset `kp` back to 0.1, leaving `ki` with the value found above. Describe how the system response changes (qualitatively) with this higher `kp`. #. Increase `ki` until the system no longer behaves well. What went wrong with the system response? #. Submit your final code.