In the last section, we introduced the GPIO as an input mode peripheral and the ADC for an analog detection of the input voltage.  In this section, we will configure the Analog Comparator (ACMP) to function as a discrete logic device, as both an input and output. 


We will use the ACMP to level-shift the detected LED pulse of the last section from a weak signal to one that can be used as normal input logic to the MCU.

Analog Comparator (ACMP)


The Analog Comparator (ACMP) compares two analog input values and outputs a digital high or low signal depending on whether the positive input is higher than the negative input.  The inputs can be a pair of pins on the MCU, or an internal, software-controlled reference value with 64 divisions of the reference for one of the inputs. 


The ACMP result can be read by software or optionally sent out to a physical pin.  By routing the digital output to a physical pin, the ACMP can be used as a discrete circuit element that can be utilized by external electronic circuitry, including external feedback, with no firmware required beyond the initial configuration.


You could think of the ACMP as a slower and lower-resolution ADC.  Since the ACMP has an internal reference voltage that is configurable by software to 64 divisions of VCC (or 1.25v, or 2.5V internal references), you can sweep through those programmable reference levels to compare to the input at each reference value and thereby slowly determine the analog value of the input pin.

The ACMP has eight available pins in its input mux that can be used for either the positive or negative input, each of which must be used one at a time. 


To demonstrate the operation of the ACMP, we will use the ACMP as a discrete level shifter.  One of the things that the ACMP can do is shift the voltage of a signal from a low-voltage device so that you can interface it to a higher-voltage device.  We will once again use the light sensor on the starter kit to detect potentially faint pulses from a blinking LED and convert those pulses into full-swing 3.3V signals.

To generate light pulses, we will configure a simple GPIO on PD1 to blink at a rate based on the TIMER0 CNT register, and then wire an LED to the PD1 pin with a 300-ohm resistor in series.  The necessary code to set up an LED blink rate is almost exactly that of the code to trigger the ADC in the previous section, except we simply configure the TIMER0 to trigger 10 times a second instead of once per second.  Then, inside the interrupt handler, we clear the interrupt and toggle the PD1 pin.  


      CMU_ClockEnable(cmuClock_TIMER0, true);
      CMU_ClockEnable(cmuClock_GPIO, true);
      // Create a timerInit object, based on the API default
      TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT;
      timerInit.prescale = timerPrescale1024;
      TIMER_IntEnable(TIMER0, TIMER_IF_OF);
      // Enable TIMER0 interrupt vector in NVIC
      // Set TIMER Top value
      TIMER_Init(TIMER0, &timerInit);
      // Wait for the timer to get going
      while (TIMER0->CNT == 0)
      // Excite the light sensor on PD6
      GPIO_PinModeSet(gpioPortD, 6, gpioModePushPull, 1);
      // Set up a GPIO output pin to push pull to the blink LED
      GPIO_PinModeSet(gpioPortD, 1, gpioModePushPull, 0);
void TIMER0_IRQHandler(void)
      TIMER_IntClear(TIMER0, TIMER_IF_OF);
      GPIO_PinOutToggle(gpioPortD, 1);


The voltage level detected by the light sensor from this blinking LED can be seen on an oscilloscope.


The voltage swing generated by the light sensor (with the LED at about two inches from the light sensor) is between 500mV and 1V.  If we were to try to decipher the pulses from the light sensor with an ordinary GPIO pin in input mode, the voltage would never cross the thresholds needed to indicate a digital low/high transition.  Therefore, we will use the ACMP to “level shift” the light sensor output into full-swing 3.3V pulses on pin PE2.

The following code will configure the ACMP to act as a discrete analog comparator, and will output the result on pin PE2. 


      CMU_ClockEnable(cmuClock_GPIO, true);
      CMU_ClockEnable(cmuClock_ACMP0, true);
      ACMP_Init_TypeDef acmp_init = ACMP_INIT_DEFAULT;
      acmp_init.fullBias = true;
      acmp_init.vddLevel = 10;
      /* Init and set ACMP0 channel on PC6 */
      ACMP_Init(ACMP0, &acmp_init);
      ACMP_ChannelSet(ACMP0, acmpChannelVDD, acmpChannel6);
      /* Set up GPIO as output on location 1, which is PE2, without inverting */
      ACMP_GPIOSetup(ACMP0, 1, true, false);
      GPIO_PinModeSet(gpioPortE, 2, gpioModePushPull, 0);
      /* Wait for warmup */

The ACMP negative input reference required to sample the light sensor waveform is around 600mV (at least, that is what the ambient lighting conditions in the room required when I measured the above scope shot).  This can be achieved by setting the VDD reference type and using 11 divisions of 64 using the formula 11/64 * 3.3V = 567mV.  It is important to remember that this is an ambient light sensor, and that the baseline of this waveform will move as the amount of ambient light in the room changes.  Therefore, the value of 11 divisions of VDD could change depending on the ambient lighting conditions.  For example, a bright room might require a value of 24 for the VDD reference scaling factor.  It would be best to stop the LED blinking, sample the room ambient voltage, and then set the VDD reference just above the room ambient voltage value.


In the oscilloscope waveforms, the original waveform from the light sensor is shown on channel 1, and the output of the ACMP is shown on channel 2.  The ACMP output waveform remains a steady 3.3V rail-to-rail voltage even as the distance between the LED and the light sensor varies over time, as shown in the waveform on the right.


Note that once the analog comparator is set up as a discrete component, it will continue to function throughout interrupts, debug halts, and in energy modes EM0 through EM3. 


It is also possible to sample the output of the comparator in software on ACMP0 Channel 6.  It can therefore be used to operate as a rudimentary ADC for quiescent signals by sweeping the negative input though all possible 64 steps to find which step triggers the output to go high.


More details about the ACMP can be found in Application Note AN0020 Analog Comparator.

The Voltage Comparator (VCMP) is a simplified instance of the ACMP that is tied directly to the VDD pin of the EFM32 MCU.  This gives your firmware the ability to detect the value of the supply voltage without using a GPIO pin or a channel of the ACMP.  More details about the VCMP peripheral can be found in Application Note AN0018 Supply Voltage Monitoring.  The application note also explains how to prepare for and react to dying battery situations.


In the next section, we will use the TIMER and PCNT peripherals to count the input events, such as the pulses generated by a quadrature decoder or LED pulses.

  • Blog Posts
  • Makers