Lab Assignment: ADC + PWM

Objective

Implement an ADC driver, implement a PWM driver, and design and implement an embedded application, which uses both drivers. 

This lab will utilize:

  • ADC Driver
  • PWM Driver
  • FreeRTOS Tasks
  • A potentiometer
  • An RGB LED

Assignment

Part 1: Implement an ADC Driver

Using the following header file,

  • Implement adcDriver.cpp such that it implements all the methods in adcDriver.h below.
  • Every method must accomplish its task as indicated in the comments.
  • You may add any other methods to enhance the functionality of this driver.

For proper operation of the SJOne board, do NOT configure any pins as ADC except for 0.26, 1.31, 1.30

#ifndef ADC_DRIVER_H_
#define ADC_DRIVER_H_

#include "stdio.h"
#include "io.hpp"


class ADCDriver
{
public:
    enum ADC_PIN
    {
        ADC_PIN_0_23 = 0,   // AD0.0
        ADC_PIN_0_24,       // AD0.1
        ADC_PIN_0_25,       // AD0.2
        ADC_PIN_0_26,       // AD0.3
        ADC_PIN_1_30,       // AD0.4
        ADC_PIN_1_31,       // AD0.5
        ADC_PIN_0_3,        // AD0.6
        ADC_PIN_0_2         // AD0.7
    };

    /**
    * Nothing needs to be done within the default constructor
    */
    ADCDriver();
    
    /**
    * 1) Powers up ADC peripheral
    * 2) Set peripheral clock
    * 2) Enable ADC
    * 3) Select ADC channels
    * 4) Enable burst mode
    *
    * @param channels_to_enable (optional param) bit mask to enable up to 8 ADC channels
    */
    void adcInitBurstMode(uint8_t channels_to_enable = 0xFF);

    /**
    * 1) Selects ADC functionality of any of the 8 pins that are ADC capable
    * 
    * @param adc_pin_arg is the ADC_PIN enumeration of the desired pin.
    *
    * WARNING: For proper operation of the SJOne board, do NOT configure any pins
    *           as ADC except for 0.26, 1.31, 1.30
    */
    void adcSelectPin(ADC_PIN adc_pin_arg);
    
    /**
    * 1) Returns the value of the 12bit register reading of a given ADC pin
    *
    * @param adc_pin_arg is the ADC_PIN enumeration of the desired pin.
    */
    uint16_t readADCRawByPin(ADC_PIN adc_pin_arg);
    
    /**
    * 1) Returns the value of the 12bit register reading of a given ADC channel
    *
    * @param adc_channel_arg is the number (0 through 7) of the desired ADC channel.
    */
    uint16_t readADCRawByChannel(uint8_t adc_channel_arg);
    
    /**
    * 1) Returns the voltage reading of a given ADC pin
    *
    * @param adc_pin_arg is the ADC_PIN enumeration of the desired pin.
    */
    float readADCVoltageByPin(ADC_PIN adc_pin_arg);
    
    /**
    * 1) Returns the voltage reading of a given ADC channel
    *
    * @param adc_channel_arg is the number (0 through 7) of the desired ADC channel.
    */
    float readADCVoltageByChannel(uint8_t adc_channel_arg);
};

#endif

Part 2: Implement a PWM Driver

Using the following header file,

  • Implement pwmDriver.cpp such that it implements all the methods in pwmDriver.h below.
  • Every method must accomplish its task as indicated in the comments.
  • You may add any other methods to enhance the functionality of this driver.
#ifndef PWM_DRIVER_H_
#define PWM_DRIVER_H_

#include "stdint.h"
  
class PWMDriver
{
public:
    enum PWM_PIN
    {
        PWM_PIN_2_0,    // PWM1.1
        PWM_PIN_2_1,    // PWM1.2
        PWM_PIN_2_2,    // PWM1.3
        PWM_PIN_2_3,    // PWM1.4
        PWM_PIN_2_4,    // PWM1.5
        PWM_PIN_2_5,    // PWM1.6
    };

    /**
    * Nothing needs to be done within the default constructor
    */
    PWMDriver() {}

    /**
    * 1) Select PWM functionality on all PWM-able pins.
    */  
    void pwmSelectAllPins();
  
	/**
    * 1) Select PWM functionality of pwm_pin_arg
    *
    * @param pwm_pin_arg is the PWM_PIN enumeration of the desired pin.
    */
    void pwmSelectPin(PWM_PIN pwm_pin_arg);
  
    /**
    * 1) Power up the PWM peripheral
    * 2) Set the PWM clock to divide by 1 (to simplify frequency calculation
    * 3) Enable timer and prescalar counters. Enable PWM Mode
    * 4) Configure Counter to reset when MR0 is matched (i.e. MR0 represents frequency)
    * 5) Disable all capture features
    * 6) Enable Timer mode and disable counter/capture modes
    * 7) Enable single edge mode
    * 8) Enable output on all six pwm channels
    * 9) Set frequency
    * 10) Set all pwm channels to zero duty cycle
    *
    * @param frequency_Hz is the initial frequency in Hz. 
    */
    void pwmInitSingleEdgeMode(uint32_t frequency_Hz);

	/**
    * 1) Convert duty_cycle_percentage to the appropriate match register value (depends on current frequency)
    * 2) Assign the above value to the appropriate MRn register (depends on pwm_pin_arg)
    *
    * @param pwm_pin_arg is the PWM_PIN enumeration of the desired pin.
    * @param duty_cycle_percentage is the desired duty cycle percentage.
    */
	void setDutyCycle(PWM_PIN pwm_pin_arg, float duty_cycle_percentage);
  
	/**
    * 1) Convert frequency_Hz to the appropriate match register value
    * 2) Assign the above value to MR0
    *
    * @param frequency_hz is the desired frequency of all pwm pins
    */  	
	void setFrequency(uint32_t frequency_Hz);
};

#endif

Part 3: Application

In order to demonstrate that both drivers function, you are required to interface a potentiometer and an RGB LED to the SJOne board. The potentiometer ADC input shall control the duty cycle of the RGB LED pwm outputs. Note that an RGB LED has three input pins that you will connect to three different PWM output pins. You must use your own ADC and PWM drivers, as well as your own FreeRTOS task. 

Extra credit can be earned with an interesting/cool/creative RGB output.

Requirements

  • Using your own ADC Driver, read input voltage from a potentiometer
    • Print the voltage reading every 1s.
  • Using your own PWM Driver, drive an RGB LED.
    • Print the duty cycle of all three RGB pins every 1s.
  • The PWM output to the RGB LED must be a function of the ADC input from the potentiometer.
  • You must have an application on/off button (you can use the SJOne on-board buttons).
    • You may use the driver provided within the SJSU-Dev for the onboard buttons.
    • In an off state, the screen output should indicate that the system is in fact in an off state.
    • Also, in an off state, the LED should be off, and manipulating the potentiometer should be inconsequential. 
Back to top