Envelope Followers, VU meters and LVGL : Using the NXP LPC55S69 for Embedded Graphics


Nov. 9, 2020
EliHughes-MoreFace_2k
Eli Hughes

Introduction

“Embedded” graphics technologies have come a long way since simple character displays. At one time in the not-so-distant past, microcontrollers were not powerful enough to drive high-resolution color LCD displays effectively. The proliferation of low-cost 32-bit microcontrollers has made it much easier to use high resolution color displays without needing to resort to high-end applications processors. High-resolution color LCDs are now available in virtually any size or resolution.

The LPC55S69 MiniMonkey with 240x240px IPS Display

One of the design features of the LPC55S69 MiniMonkey project was a 240x240px IPS display from buydisplay.com. The LPC55S69 is a good fit for small, low active power embedded graphics applications. It has as significant amount internal SRAM to store a framebuffer and has ample processing power to composite a scene on a small display. Putting pixels and simple graphics on a memory buffer is straight forward. However, using a dedicated library can save time when prototyping a user interface.

LVGL, Enveloper Followers and VU Meters

Light and Versatile Graphics Library (LVGL) is an open source option for embedded graphics/UI and is well documented making it simple to integrate into your project. Looking at the GitHub repo, you can see development is active and quite healthy for an embedded open-source effort. Some notable features:

  • A simple driver interface that separates the graphics logic from the physical interface to the display.
  • Input interfaces for touch controllers, encoders and buttons.
  • A simulator to run code on PC.

You can find demonstrations here that run in a web browser. While I certainly enjoy developing my own bespoke graphics library, LVGL is quite impressive in its documentation and features.

To kick the proverbial tires, I put together a simple demonstration using the available I/O on the MiniMonkey. Since the MiniMonkey has a MEMs microphone, constructing a simple audio VU meter using the LVGL gauge widget would be an effective demonstration.

A classic analog VU meter.

Creating an LVGL Driver

Since I already had code to initialize my IPS display and the microphone on the MiniMonkey, all I needed to do was write a simple driver layer for LVGL. The task was simplified as LVGL is built into MCUXpresso SDK and there is an example for the LPC55S69 using FreeRTOS or a bare metal environment.

I generally prefer to pull in code directly from the GitHub repositories, but using the version from the MCUXpresso SDK was a useful starting point to observe a basic LVGL port. Once you bring the LVGL source tree into your project, porting to a new display is straightforward. In the MCUXpresso example, a file littlevgl_support.c is provided as an example.

In addition to the driver interface code, you need to provide some configuration in lv_conf.h to enable/disable features in LVGL.

Updating lv_conf.h

Since I was familiar with the interface to the MiniMonkey IPS display through previous development with my own graphics library, I was able to get the SDK example ported in about 30 minutes. LVGL provides a “demo widgets” example which shows how to instantiate some common user interface widgets. I used this as a starting point to verify that I had things ported OK.

The LPC55S69 has plenty of RAM for LVGL’s working buffer. LVGL recommends a 1/10 screen sized buffer but you could provide a 1:1 sized back buffer with the large amount of RAM in the LPC55S69.

Microphone Signal Processing for the VU Meter

To implement the VU meter, I used an envelope detector/follower to drive the LVGL gauge widget.

https://en.wikipedia.org/wiki/Envelope_detector

The are several methods for detecting the envelop of a signal. I chose to use a simple single-pole filter applied to rectified version of the input signal with programmable attack and release times.

// attTime and relTime is in seconds

float ga = exp(-1.0f/(sampleRate*attTime));
float gr = exp(-1.0f/(sampleRate*relTime));

float envOut = 0.0f;

for( ... )
{
    // get your data into 'input'

    envIn = fabs(input);

    if( envOut < envIn )
        envOut = envIn + ga * (envOut - envIn);
    else
        envOut = envIn + gr * (envOut - envIn);

    // envOut now contains the envelope
}

I want to point out a very useful resource for many common audio effects:

https://www.musicdsp.org/en/latest/

You can find code snippets for many common audio effects to use a starting point. For my purposes, I did not need to convert the detector output to a dBu value. Instead, I just mapped my envelope detector output to a simple 0 to 100 scale for demonstration purposes.

void lv_ex_gauge_1(void)
{
    /*Describe the color for the needles*/

    static lv_color_t needle_colors[1];
    needle_colors[0] = LV_COLOR_ORANGE;

    /*Create a gauge*/

    gauge1 = lv_gauge_create(lv_scr_act(), NULL);

    lv_gauge_set_needle_count(gauge1, 1, needle_colors);

    lv_obj_set_size(gauge1, 230, 230);

    lv_obj_align(gauge1, NULL, LV_ALIGN_CENTER, 0, 0);

lv_obj_set_style_local_image_recolor_opa(gauge1, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, LV_OPA_COVER);

    lv_gauge_set_range(gauge1, 0, 100);

}

Updating the gauge value is a one-liner with LVGL:

lv_gauge_set_value(gauge1, 0, EnvelopeOut);

In the demonstration code, EnvelopeOut is computed on a per sample basis in an IRQ routine. To keep things simple, I ran the main program “open loop” to update the display as fast as possible. LVGL only updates areas of the screen that have changed using a dirty rectangle approach making animations smooth

Demonstration and Conclusion

The results were very good. Note that IPS displays are quite difficult to record via a cell phone camera. If you see some “grittiness” in the video, this is an artifact of the optical sampling. This display with LVGL looks *very* good in person.

Demonstration of a VU Meter using the LVGL Gauge on the LPC55S69 MiniMonkey

I was able to get LVGL up and running on the MiniMonkey quickly. LVGL is simple to use and there are now quite a few display drivers already in the source tree. If need to implement embedded graphics for your own product or project, LVGL should be on the list for consideration. It is open-source and cross platform making it suitable for just about any scenario. While I will contribute developing my own graphics library (similar to a game engine), LVGL will also be in the toolbox for constructing user interfaces.

References




Engage
jack in