I sell on Tindie
Grand Prize Winner
(Dec 2012)
Second Prize Winner
(July 2012)

RGB LED PWM Demo on Petalinux

In this tutorial we are going to use the same hardware descriptor from the previous post and build an RGB LED PWM Demo driven by Petalinux. To do this we will need a Linux machine with Petalinux SDK installed on it. In this experiment we will be using SDK 2017.3 but any newer revisions up to 2020.1 should work equally well.

The process of Petalinux project creation and building is described in many places including UG1144 Reference Guide so let’s focus of specific things which will be implemented in our project.

First of all, LED control will be done through a web interface. Each colour intensity for each of the two RGB LEDs will be controlled independently by a slider. Going through the intricacies of web design is out of scope of this tutorial, instead, only most important things will be highlighted.

The slider is going to be extremely simplistic and fully implemented in the form of Stylesheets. It is defined in a separate file slider.css, the full content of which is given below:

.slidecontainer {
  width: 100%;

.slider {
  -webkit-appearance: none;
  width: 100%;
  height: 25px;
  background: #B7B5E4;
  outline: none;
  opacity: 0.5;
  -webkit-transition: .2s;
  transition: opacity .2s;

.slider:hover {
  opacity: 1;

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 25px;
  height: 25px;
  background: #3D518C;
  cursor: pointer;

.slider::-moz-range-thumb {
  width: 25px;
  height: 25px;
  background: #3D518C;
  cursor: pointer;

The slider is instantiated six times (three colours per each LED) in index.html as it is shown below. There are also JavaScript functions attached to each slider and triggering upon change in slider’s position. Every time when position of any slider changes a set_led function is invoked. To make the page more interactive, there is logic to change font colour of led0_caption and led1_caption by mixing the values of the three base colours selected by appropriate sliders.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">
    <link href="Stylesheets/magictale.css" rel="stylesheet" type="text/css">
    <link href="Stylesheets/tabs.css" rel="stylesheet" type="text/css"> 
    <link href="Stylesheets/slider.css" rel="stylesheet" type="text/css"> 
    <title>Magictale Electronics</title>
    <script src="Javascript/jquery-1.12.4.min.js"></script>
    <script src="Javascript/config.js"></script>
        $(document).ready(function() {
            set_led("mod", 128);

            var s0r = document.getElementById("led0_red");
            var s0g = document.getElementById("led0_green");
            var s0b = document.getElementById("led0_blue");

            var s1r = document.getElementById("led1_red");
            var s1g = document.getElementById("led1_green");
            var s1b = document.getElementById("led1_blue");
            var led0_caption = document.getElementById("led0_caption");
            var led1_caption = document.getElementById("led1_caption");
            s0r.oninput = function() {
              led0_caption.style.color = form_rgb_str(s0r.value, s0g.value, s0b.value);
              set_led("r0", s0r.value);

            s0g.oninput = function() {
              led0_caption.style.color = form_rgb_str(s0r.value, s0g.value, s0b.value);
              set_led("g0", s0g.value);
            s0b.oninput = function() {
              led0_caption.style.color = form_rgb_str(s0r.value, s0g.value, s0b.value);
              set_led("b0", s0b.value);

            s1r.oninput = function() {
              led1_caption.style.color = form_rgb_str(s1r.value, s1g.value, s1b.value);
              set_led("r1", s1r.value);

            s1g.oninput = function() {
              led1_caption.style.color = form_rgb_str(s1r.value, s1g.value, s1b.value);
              set_led("g1", s1g.value);
            s1b.oninput = function() {
              led1_caption.style.color = form_rgb_str(s1r.value, s1g.value, s1b.value);
              set_led("b1", s1b.value);

    <iframe src="header.html" height="100" width="100%" scrolling="no" frameborder="0"></iframe>
    <table width="600" align="center" border="0">
            <td colspan="2">
                <ul class="tab">
                    <li><a href="#" class="tablinks active">Demo</a></li>
                <div id="rgb_pwm_led_demo" class="tabcontent" style="display:block">
                    <table width="600" height="300" align="center">
                        <tr height="10">
                                <div align="center" id="led0_caption" ><h1>LED 0</h1></div>
                                <div style="color:red;" >Red:</div>
                                <input type="range" min="0" max="48" value="0" class="slider" id="led0_red">
                                <div style="color:green;" >Green:</div>
                                <input type="range" min="0" max="48" value="0" class="slider" id="led0_green">
                                <div style="color:blue;" >Blue:</div>
                                <input type="range" min="0" max="48" value="0" class="slider" id="led0_blue">
                            <td width="20">
                                <div align="center" id="led1_caption" ><h1>LED 1</h1></div>
                                <div style="color:red;" >Red:</div>
                                <input type="range" min="0" max="48" value="0" class="slider" id="led1_red">
                                <div style="color:green;" >Green:</div>
                                <input type="range" min="0" max="48" value="0" class="slider" id="led1_green">
                                <div style="color:blue;" >Blue:</div>
                                <input type="range" min="0" max="48" value="0" class="slider" id="led1_blue">
    <iframe src="footer.html" height="25" width="100%" scrolling="no" frameborder="0"></iframe>
    <div id="restart_panel" style="display: none; top: 0; right: 0; position: absolute; background-color: red; color: white; padding: 10px 20px">

The implementation of set_led JavaScript function is in config.js, the fragment of the file is given below. The function makes asynchronous call which in its turn executes rgb-led-pwm shell script located in cgi-bin folder. The script also gets two parameters, a register name and a value.

function set_led(register_name, value)
    var url = '/cgi-bin/rgb-led-pwm?register=' + register_name + '&amp;value=0x' + (+value).toString(16);

    rest_call(url, function(data) {
        if (data.Error != 0)
            alert('Unable to set register value');

function form_rgb_str(red_c, green_c, blue_c)
    return "rgb(" + (red_c * 5) + ", " + (green_c * 5) + ", " + (blue_c * 5) + ")";

function rest_call(uri, callback, complete_callback)
        url: uri,
        success: callback,
        complete: complete_callback,
        error: function(xhr, error_status, error_thrown)

The rgb-led-pwm shell script parses parameters passed by the browser translates the register name into address. With the help of the devmem tool it writes the value at a given address which is in essence writing value into a register. The script then responds with status in JSON format to keep the web browser happy.


if [ "$p1" == "r0" ]; then 
    devmem 0x43C00018 32 "$p2"
elif [ "$p1" == "g0" ]; then 
    devmem 0x43C00014 32 "$p2"
elif [ "$p1" == "b0" ]; then 
    devmem 0x43C00010 32 "$p2"
elif [ "$p1" == "r1" ]; then 
    devmem 0x43C0000C 32 "$p2"
elif [ "$p1" == "g1" ]; then 
    devmem 0x43C00008 32 "$p2"
elif [ "$p1" == "b1" ]; then 
    devmem 0x43C00004 32 "$p2"
elif [ "$p1" == "mod" ]; then 
    devmem 0x43C00000 32 "$p2"

echo "Content-Type: application/json"
echo ""
printf "{ \"param0\" : \"$p1\", \"param1\" : \"$p2\", \"Error\": \"$error\" }"

There is one more custom made shell script bootscript given below. It is going to be ran upon Linux boot and start httpd daemon to serve web pages at port 8080. There are also initial values to be written to all registers of our PWM AXI block which has been implemented in FPGA fabric earlier. The fundamental difference between Linux and bare-metal or real time OS is that Linux user applications are running in virtual address spaces so any attempt to access physical registers will inevitably cause well-known segmentation fault as the kernel prevents applications from accessing resources outside of the allowed address space. There are several ways to deal with this, in this project we will be using devmem tool which allows to access registers correctly.

busybox httpd -p 8080 -h /home/root/httpd;
devmem 0x43C00000 32 0x80
devmem 0x43C00004 32 0x0
devmem 0x43C00008 32 0x0
devmem 0x43C0000C 32 0x0
devmem 0x43C00010 32 0x0
devmem 0x43C00014 32 0x0
devmem 0x43C00018 32 0x0

The final version of a customised Yocto script bootscript.bb is given below. It packages our webpage and its resources including JavaScripts, StyleSheets, images and GGI scripts into root file system.

# This file is the bootscript recipe.

SUMMARY = "Simple bootscript application"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://bootscript \
	file://web/cgi-bin \
        file://web/images \
        file://web/Stylesheets \
        file://web/Javascript \
        file://web \

# we add files here that we want to be packaged in the target file system 
FILES_${PN} =" \
  /home/root/httpd/cgi-bin/rgb-led-pwm \
  /home/root/httpd/images/MagictaleLogo.png \
  /home/root/httpd/Javascript/config.js \
  /home/root/httpd/Javascript/jquery-1.12.4.min.js \
  /home/root/httpd/Stylesheets/magictale.css \
  /home/root/httpd/Stylesheets/slider.css \
  /home/root/httpd/Stylesheets/tabs.css \
  /home/root/httpd/A-Calling-Font_D-by-7NTypes.woff \
  /home/root/httpd/footer.html \
  /home/root/httpd/header.html \
  /home/root/httpd/index.html \
  ${bindir}/* \

S = "${WORKDIR}"

inherit update-rc.d

INITSCRIPT_NAME = "bootscript"
INITSCRIPT_PARAMS = "start 99 S ."

do_install() {
	install -d ${D}/${bindir}
	install -m 0755 ${S}/bootscript ${D}/${bindir}

	install -d ${D}/home/root/httpd/cgi-bin
	install -m 0755 web/cgi-bin/rgb-led-pwm ${D}/home/root/httpd/cgi-bin

	install -d ${D}/home/root/httpd/images
	install -m 0644 web/images/MagictaleLogo.png ${D}/home/root/httpd/images
	install -d ${D}/home/root/httpd/Javascript
	install -m 0644 web/Javascript/config.js ${D}/home/root/httpd/Javascript
	install -m 0644 web/Javascript/jquery-1.12.4.min.js ${D}/home/root/httpd/Javascript

	install -d ${D}/home/root/httpd/Stylesheets
	install -m 0644 web/Stylesheets/magictale.css ${D}/home/root/httpd/Stylesheets
	install -m 0644 web/Stylesheets/slider.css ${D}/home/root/httpd/Stylesheets
	install -m 0644 web/Stylesheets/tabs.css ${D}/home/root/httpd/Stylesheets

	install -d ${D}/home/root/httpd
	install -m 0644 web/A-Calling-Font_D-by-7NTypes.woff ${D}/home/root/httpd/
	install -m 0644 web/footer.html ${D}/home/root/httpd/
	install -m 0644 web/header.html ${D}/home/root/httpd/
	install -m 0644 web/index.html ${D}/home/root/httpd/

The final look of the web interface is given below. There are two LEDs and six sliders controlling intensity of base colour for each of the LEDs. As it is seen, the LED 0 is of red colour (only red slider has non-zero value) and the LED 1 is of light blue (which is a mix of green and blue encoded by two sliders):

RGB LED PWM Demo on Petalinux webpage Screenshot

Video tutorial:

The complete process of creating a Petalinux project and building a bootable image for QSPI flash is given below: