Heart Equation

This program was my attempt to plot a given equation on a VGA screen using Prof. Hunter Adam's VGA Library for the RaspberryPi Pico. The resources for the project include the C SDK User Guide, the RP2040 Datasheet and Prof. Hunter's website.


The Equation

I chose the following equation to plot on the VGA screen:

\begin{align} \left(sin\left(a \times \frac{\pi}{10}\right) + x\right)^2 + \left(cos\left(a \times \frac{\pi}{10}\right) + y\right)^2 = 0.7|x|y + 1 \end{align}

Here, different values of $a$ give different graphs, which when put together give the shape of a heart.

Using desmos, the plotted equation looks as shown.

The plot of the equation on Desmos

The complete code

/*
 * Parth Sarthi Sharma (pss242@cornell.edu)
 * Code based on examples from Raspberry Pi Foundation.
 * This code is an implementation of a custom heart function
 * on the Raspberry Pi Pico to draw a pattern.
 */
#include <stdio.h> //The standard C library
#include <math.h> //The standard math library
#include "pico/stdlib.h" //Standard library for Pico
#include "hardware/pio.h" //The hardware PIO library
#include "hardware/dma.h" //The hardware DMA library
#include "pico/time.h" //The pico time library
#include "hardware/gpio.h" //The hardware GPIO library
#include "pico/multicore.h" //The pico multicore library
#include "vga_graphics.h" //The graphics library

#define HEIGHT 480 //Height of the VGA screen
#define WIDTH 640 //Width of the VGA screen
#define PI 3.14159265358 //The value of PI

void core1_entry() { //The program running on core 1
    double error = 0.01, step = 0.01, x, y, a, ans; //The error, step, x coordiante, y coordinate, iterator, and the answer variables
    for(a = 11; a <= 20; a += 0.5){ //For x in range [11, 20] with 0.5 step size
        for(x = -3; x < 3; x += step){ //For x in range [-3, 3) with 0.01 step size
            for(y = -3; y < 3; y += step){ //For y in range [-3, 3) with 0.01 step size
                ans = pow((sin(a * PI / 10.0) + x), 2.0) + pow((cos(a * PI / 10.0) + y), 2.0) - (0.7 *  fabs(x) * y); //Solve for the equation
                if((ans < 1 + error) && (ans > 1 - error)){ //If the solution is within the given error limits
                    fillCircle((50 * x) + (WIDTH / 2), (HEIGHT / 2) - (50 * y), 1, RED); //Draw a red circle to create an enlarged image
                }
            }
        }
    }
}

int main(){ //The program running on core 0
    double error = 0.01, step = 0.01, x, y, a, ans; //The error, step, x coordiante, y coordinate, iterator, and the answer variables

    stdio_init_all(); //Initialize all of the present standard stdio types that are linked into the binary
    initVGA(); //Initialize the VGA screen and functions
    multicore_launch_core1(core1_entry); //Reset core1 and enter the core1_entry function on core 1 using the default core 1 stack

    for(a = 1; a <= 10; a += 0.5){ //For x in range [1, 10] with 0.5 step size
        for(x = -3; x < 3; x += step){ //For x in range [-3, 3) with 0.01 step size
            for(y = -3; y < 3; y += step){ //For y in range [-3, 3) with 0.01 step size
                ans = pow((sin(a * PI / 10.0) + x), 2.0) + pow((cos(a * PI / 10.0) + y), 2.0) - (0.7 *  fabs(x) * y); //Solve for the equation
                if((ans < 1 + error) && (ans > 1 - error)){ //If the solution is within the given error limits
                    fillCircle((50 * x) + (WIDTH / 2), (HEIGHT / 2) - (50 * y), 1, RED); //Draw a red circle to create an enlarged image
                }
            }
        }
    }
}


Code Organization

The code has a VGA library which has been explained really nicely by Prof. Adams and the main file.

The main crux of the program lies in the fact that I needed to compute the equation within a given error as trying to find the exact solution can be really computationally expensive. Therefore, I divided the x-coordinates and the y-coordinates into a series of small steps and computed the equation on using those coordinates. If the answer is within the given error limit, draw a red circle at the given location.


The output

Plotting the equation on RaspberryPi Pico

CMakeLists.txt

cmake_minimum_required(VERSION 3.13)

include(pico_sdk_import.cmake)

project(HeartEquation-project)

pico_sdk_init()

add_executable(HeartEquation)

pico_enable_stdio_usb(HeartEquation 1)
pico_enable_stdio_uart(HeartEquation 1)

pico_generate_pio_header(HeartEquation ${CMAKE_CURRENT_LIST_DIR}/hsync.pio)
pico_generate_pio_header(HeartEquation ${CMAKE_CURRENT_LIST_DIR}/vsync.pio)
pico_generate_pio_header(HeartEquation ${CMAKE_CURRENT_LIST_DIR}/rgb.pio)

target_sources(HeartEquation PRIVATE HeartEquation.c vga_graphics.c)

target_link_libraries(HeartEquation PRIVATE pico_stdlib hardware_pio hardware_dma hardware_adc hardware_irq pico_time pico_multicore)

pico_add_extra_outputs(HeartEquation)