2020-08-08

2020-08-08 Longan Nano GD32VF103 Demo

 


>>>Longan Nano GD32VF103<<<

Longan Nano GD32VF103 Risc-V 108MHz 32b MCU toolchain, libraries and applications

>>>Longan Nano Demo<<<


Display Class: Interfaces with the ST7735 80x160 display using SPI0 and DMA0.
Screen Class: Provides asynchronous methods to print on the display



1Longan nano GD32VF103 Demo

I built Display, Screen and Chrono class drivers for the Longan nano GD32VF103 board.

This Demo application is meant to show off the features of the drivers and jump start the development of the application.

I spent extra effort in testing and documentation to make sure the drivers are as stable as possible since they are going to be the building blocks for all my future applications on this board.


1.1Chrono Class Driver

The Chrono class uses the 64bit 27MHz@108MHz CPU clock timer to provide accurate timings.

Time units are defined in the Longan_nano::Chrono::Unit enum.

The Chrono class has two modes of operation:

  • start/stop elapsed timer: measures DeltaT

  • start/accumulate: integrate a DeltaT on an accumulator

Those two modes of operations can be used to profile uptime, elapsed time, time spent running code and more.


1.2Display Class Driver

The Display class interfaces directly with the LH096T ST7735S 0.96 inches 80x160 oled color screen.

The library uses the .init method to initialize the display has two modes of operations:

  • register_sprite/update for asynchronous non blocking operations.

  • draw_sprite for synchronous blocking operations.

The Display class uses optional DMA acceleration and the SPI0 and a few GPIOs to communicate with the physical screen.

The Display class does not use interrupts. While using interrupts can make the driver transparent to the user by automagically calling the update method, it can interfere with other real time operations. As design philosophy, the driver is meant to be secondary to the application and meant to show information, giving the application control over the amount of resources used by deciding the call speed of the update method. Calling .update() every 100us will result in about 1250 sprites updated per second. If the user makes more print calls than what the display can handle, the display will simply ignore older calls displaying the most recent sprite. At top load a refresh rate of about 1250/100=12.5 frames per seconds can be expected. The refresh rate becomes 1.25 fps at full load if .update() is executed every 1000us instead of 100us.


1.3Screen Class Driver

The scope of the screen class is to support ascii print of fixed size on fixed grid, and is meant for the common use case of showing debug and runtime number of the application.

The Screen class add a sprite based abstraction layer with print to reduce the size of the frame buffer and CPU use. It also provides a large number of overloaded print methods. The Screen class inherit the Display class, allowing to decouple the physical screen from the print implementation and simplify a move to a bigger screen if needed.

The Screen class supports two ascii fonts. 8x10 Courier Now and 8x16 NSimSum that can be toggled by setting the define FONT_HEIGHT and recompiling, the smaller font shows 8x20=160 sprites on screen, while the bigger font shows 5x20=100 sprites on screen and is easier to read.


2DEMOS

There are ten demo, showcasing the use of the print and timing functions to display an HMI.

  1. Clear the screen to a random color every 500ms

  2. Print a random character in a random position of the screen every 1ms

  3. Same as above but with random foreground and background colors

  4. Print a random string in a random position of the screen every 25ms

  5. Same as above but with random foreground and background colors

  6. Print numbers with right and left adjust

  7. CPU execution and uptime with engineering format 4 significant digit and SI suffix

  8. Same as above but with screen builtin pending and error methods

  9. Same as above but with random foreground and background colors

  10. Constant workload demo. Print 10 sprites every 25ms and show CPU use

Illustration 1 - Demo


 
Video 1 - Demo

3Documentation and Style

I made a point to learn more C++ features, with this project I elected to use .hpp files with header and implementation together since inline needs to be declared alongside the header anyway. I also experimented with scoping of typedef and enum to allow the same enum name to be in multiple libraries without conflicts.

I also integrated the Doxygen documentation style comments and generated the automatic documentation, as well as integrating the documentation alongside the code repository in GitHub.

The same style is going to be used for my next classes and drivers.


4Conclusions

I begun this project to learn a Risc-V MCU. The Longan Nano Demo provides an example application for the scheduler, Chrono, Screen and PA8 button, and can be used as a base to develop applications with the Longan Nano GD32VF103.

I am satisfied with the performance, and how the drivers have turned out. The board provides great value, and in my opinion is held back by the poor examples. Hopefully more people will adopt the Longan Nano and help in building libraries and example code to make it easier to develop applications on Risc-V MCUs to come.

The first application of this MCU will be as new motor controller for OrangeBot.


5Source Code

>>>GitHub Repository<<<


5.1Doxygen Documentation

>>>DoxyGen<<<




5.2Source Code

>>>Screen and Display classes<<<



/****************************************************************************
** OrangeBot Project
*****************************************************************************
** /
** /
** /
** ______ \
** \
** \
*****************************************************************************
** Longan Nano Demo
*****************************************************************************
** Development of the display class for the ST7735S LCD controller
** Demo to show all the uses of the classes. Demo is selected using PA8 boot button
** Codesize exploded. -fno-exceptions as compiler option brought it back under control
****************************************************************************/
/****************************************************************************
** INCLUDES
****************************************************************************/
//C++ std random number generators
#include <random>
//Longan Nano HAL
#include <gd32vf103.h>
//LED class
#include "longan_nano_led.hpp"
//Time class
#include "longan_nano_chrono.hpp"
//Higher level abstraction layer to base Display Class. Provides character sprites and print methods with color
#include "longan_nano_screen.hpp"
/****************************************************************************
** NAMESPACES
****************************************************************************/
/****************************************************************************
** DEFINES
****************************************************************************/
//forever
#define EVER (;;)
/****************************************************************************
** MACROS
****************************************************************************/
/****************************************************************************
** ENUM
****************************************************************************/
//Configurations
typedef enum _Config
{
//Microseconds between calls of the screen update method. User decides how much CPU to allocate to the screen by tuning this time.
//Longer time will mean fewer sprites rendered per second, but no matter what, the screen class will not crash as only the most recent sprite is drawn
SCREEN_US = 100,
//Microseconds between led toggles
LED_BLINK_US = 250000,
//Microseconds between calls of the demos. Various demos can be executed at differing rates depending on how many sprites they update
SLOW_DEMO_US = 500000,
MEDIUM_DEMO_US = 25000,
FAST_DEMO_US = 1000,
} Config;
//List of DEMOS
typedef enum _Demo
{
//Periodically clear the full screen with a new color
TEST_CLEAR_BLINK,
//Periodically write a random ascii character in a random position in the screen
TEST_CHAR_CONSOLE,
//Periodically write a random ascii character in a random position with random colors on the screen
TEST_CHAR_CONSOLE_COLOR,
//Periodically write a random string with random length in a random position
TEST_STRING_CONSOLE,
//Periodically write a random string with random length in a random position with random colors on the screen
TEST_STRING_CONSOLE_COLOR,
//Show numbers updating on the screen
TEST_NUMBERS,
//Engineering format strings
TEST_ENG_NUMBERS,
//Profiling +Pending for update
TEST_PENDING,
//Test the change_color
TEST_CHANGE_COLORS,
//Profile execution time with constant workload
TEST_WORKLOAD,
//Total number of demos installed
NUM_DEMOS,
//Maximum length of a demo string
MAX_STR_LEN = 25,
} Demo;
/****************************************************************************
** STRUCT
****************************************************************************/
typedef struct _Scheduler
{
bool f_screen : 1;
bool f_demo : 1;
bool f_overrun : 1;
} Scheduler;
/****************************************************************************
** PROTOTYPES
****************************************************************************/
extern void init_pa8_button_interrupt( void );
/****************************************************************************
** GLOBAL VARIABILES
****************************************************************************/
volatile bool g_rtc_flag = false;
//C++ Standard Random Number Generator
std::default_random_engine g_rng_engine;
//C++ standard random number distributions
std::uniform_int_distribution<uint8_t> g_rng_char( ' ', '~' );
std::uniform_int_distribution<int> g_rng_height( 0, Longan_nano::Screen::Config::FRAME_BUFFER_HEIGHT -1 );
std::uniform_int_distribution<int> g_rng_width( 0, Longan_nano::Screen::Config::FRAME_BUFFER_WIDTH -1 );
std::uniform_int_distribution<uint8_t> g_rng_color( 0, Longan_nano::Screen::Config::PALETTE_SIZE -1 );
//String length reroll
std::uniform_int_distribution<uint8_t> g_rng_length( 0, MAX_STR_LEN );
//Scheduler for the tasks
Scheduler g_scheduler = { 0 };
//True when the PA8 button is released
volatile bool g_f_pa8_button_up = false;
/****************************************************************************
** FUNCTIONS
****************************************************************************/
/****************************************************************************
** @brief main
** main | void
****************************************************************************/
//! @return int |
//! @details Entry point of program
/***************************************************************************/
int main( void )
{
//----------------------------------------------------------------
// VARS
//----------------------------------------------------------------
//Systick timer used to schedule activities
Longan_nano::Chrono my_timer;
//Systick timers to profile resource use
Longan_nano::Chrono timer_uptime;
Longan_nano::Chrono timer_screen;
Longan_nano::Chrono timer_demo;
//Display Driver
Longan_nano::Screen g_screen;
//elapsed time
int elapsed_us;
//Demo scheduler prescaler
uint16_t scheduler_cnt = 0;
uint16_t demo_pre = 100;
//Default Demo to be executed
Demo demo_index = Demo::TEST_WORKLOAD;
//Demo is not initialized
bool f_demo_init = false;
//----------------------------------------------------------------
// INIT
//----------------------------------------------------------------
//Initialize LEDs
Longan_nano::Leds::init();
Longan_nano::Leds::set_color( Longan_nano::Leds::Color::BLACK );
//Initialize the PA8 Boot button with interrupt. Used to switch between demos
init_pa8_button_interrupt();
//Initialize the Display
g_screen.init();
//Snap start
my_timer.start();
timer_uptime.start();
timer_screen.start();
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
for EVER
{
//----------------------------------------------------------------
// Scheduler
//----------------------------------------------------------------
// Hardwired scheduler to release tasks
// Thanks to the Longan Nano SysTick timer there is no need to use peripherals for timekeeping
//Snap stop and get time since last start in microseconds
elapsed_us = my_timer.stop( Longan_nano::Chrono::Unit::microseconds );
//If: for some reason, the timing is invalid. Algorithmic error
if (elapsed_us < 0)
{
Longan_nano::Leds::set( Longan_nano::Leds::Color::BLUE );
}
//If: enough time has passed between screen executions
else if (elapsed_us >= Config::SCREEN_US)
{
//----------------------------------------------------------------
// Screen
//----------------------------------------------------------------
// The screen is the fastest task
//If: the previous task was not cleared
if (g_scheduler.f_screen == true)
{
//There was an overrun. Not enough CPU to keep up
g_scheduler.f_overrun = true;
}
else
{
//Issue a screen update
g_scheduler.f_screen = true;
}
//Snap start. Restart the timer
my_timer.start();
//----------------------------------------------------------------
// Prescaler
//----------------------------------------------------------------
// A prescaler is used to schedule the execution of slow tasks without the need of additional timers
//Prescaler counter
scheduler_cnt++;
//----------------------------------------------------------------
// Demo
//----------------------------------------------------------------
//If: enough ticks of the prescaler counter have elapsed
if (scheduler_cnt%demo_pre == 0)
{
//Issue the execution of the demo
g_scheduler.f_demo = true;
}
//----------------------------------------------------------------
// LED Blink
//----------------------------------------------------------------
//If: enough ticks of the prescaler counter have elapsed
if (scheduler_cnt%(Config::LED_BLINK_US/Config::SCREEN_US) == 0)
{
Longan_nano::Leds::toggle( Longan_nano::Leds::Color::RED );
//----------------------------------------------------------------
// Demo Switch
//----------------------------------------------------------------
// PA8 boot button is used to switch between demos. I read it slowly to avoid bouncing
//If: button released
if (g_f_pa8_button_up == true)
{
//Clear flag
g_f_pa8_button_up = false;
//Next demo
demo_index = (Demo)( ((uint8_t)demo_index < (uint8_t)Demo::NUM_DEMOS-1)?((uint8_t)demo_index +1):(0));
//Initialize the demo
f_demo_init = false;
}
}
} //If: enough time has passed between screen executions
//Default
else
{
//Nothing to do
}
//----------------------------------------------------------------
// OVERRUN
//----------------------------------------------------------------
// Triggered when a task is not completed before the next issue
//If: overrun
if (g_scheduler.f_overrun == true)
{
//Clear error
g_scheduler.f_overrun = false;
//Signal overrun condition
Longan_nano::Leds::toggle( Longan_nano::Leds::Color::BLUE );
}
//----------------------------------------------------------------
// Screen Update
//----------------------------------------------------------------
// Ask the driver to sync with the display
// The driver will do nothing if its not time to update
// Use all the spare CPU time to do so
//If: screen is authorized to update. User controls the CPU dedicated to this task
if (g_scheduler.f_screen == true)
{
//Reset flag
g_scheduler.f_screen = false;
//Snap start
timer_screen.start();
//Execute a step in the screen update
g_screen.update();
//Accumulate DeltaT into timer accumulator
timer_screen.accumulate();
}
//----------------------------------------------------------------
// DEMO
//----------------------------------------------------------------
//If: demo is authorized to execute
if (g_scheduler.f_demo == true)
{
//Profile time spent running the DEMO
timer_demo.start();
//Clear execution flag
g_scheduler.f_demo = false;
//Switch: for each demo
switch (demo_index)
{
//----------------------------------------------------------------
// TEST_CLEAR_BLINK
//----------------------------------------------------------------
// Periodically clear the full screen with a new color
case Demo::TEST_CLEAR_BLINK:
{
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::WHITE );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::SLOW_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
//Clear the screen to a random color
g_screen.clear( (Longan_nano::Screen::Color)g_rng_color( g_rng_engine ) );
}
break;
}
//----------------------------------------------------------------
// TEST_CHAR_CONSOLE
//----------------------------------------------------------------
// Periodically write a random ascii character in a random position in the screen
case Demo::TEST_CHAR_CONSOLE:
{
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::BLACK );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::FAST_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
//Randomly generate character
char char_tmp = g_rng_char( g_rng_engine );
int height_tmp = g_rng_height( g_rng_engine );
int width_tmp = g_rng_width( g_rng_engine );
//Ask the screen driver to print the character
g_screen.print( height_tmp, width_tmp, char_tmp );
}
break;
}
//----------------------------------------------------------------
// TEST_CHAR_CONSOLE_COLOR
//----------------------------------------------------------------
// Periodically write a random ascii character in a random position with random colors on the screen
case Demo::TEST_CHAR_CONSOLE_COLOR:
{
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::BLACK );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::FAST_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
//Randomly generate character
uint8_t char_tmp = g_rng_char( g_rng_engine );
int height_tmp = g_rng_height( g_rng_engine );
int width_tmp = g_rng_width( g_rng_engine );
Longan_nano::Screen::Color background_tmp = (Longan_nano::Screen::Color)g_rng_color( g_rng_engine );
Longan_nano::Screen::Color foreground_tmp = (Longan_nano::Screen::Color)g_rng_color( g_rng_engine );
//Ask the screen driver to print the character
g_screen.print( height_tmp, width_tmp, (char)char_tmp,background_tmp, foreground_tmp );
}
break;
}
//----------------------------------------------------------------
// TEST_STRING_CONSOLE
//----------------------------------------------------------------
// Periodically write a random string with random length in a random position
case Demo::TEST_STRING_CONSOLE:
{
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::BLACK );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::MEDIUM_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
//Randomly generate character
uint8_t len_tmp = g_rng_length( g_rng_engine );
//Temp string
char str[MAX_STR_LEN +1];
//Construct string
for (uint8_t t = 0; t < len_tmp;t++)
{
str[t] = g_rng_char( g_rng_engine );
}
//append terminator
str[len_tmp] = '\0';
//Random position
int height_tmp = g_rng_height( g_rng_engine );
int width_tmp = g_rng_width( g_rng_engine );
//Ask the screen driver to print the character
g_screen.print( height_tmp, width_tmp, str );
}
break;
}
//----------------------------------------------------------------
// TEST_STRING_CONSOLE_COLOR
//----------------------------------------------------------------
// Periodically write a random string with random length in a random position with random colors
case Demo::TEST_STRING_CONSOLE_COLOR:
{
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::BLACK );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::MEDIUM_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
//Randomly generate character
uint8_t len_tmp = g_rng_length( g_rng_engine );
//Temp string
char str[MAX_STR_LEN +1];
//Construct string
for (uint8_t t = 0; t < len_tmp;t++)
{
str[t] = g_rng_char( g_rng_engine );
}
//append terminator
str[len_tmp] = '\0';
int height_tmp = g_rng_height( g_rng_engine );
int width_tmp = g_rng_width( g_rng_engine );
Longan_nano::Screen::Color background_tmp = (Longan_nano::Screen::Color)g_rng_color( g_rng_engine );
Longan_nano::Screen::Color foreground_tmp = (Longan_nano::Screen::Color)g_rng_color( g_rng_engine );
//Ask the screen driver to print the character
g_screen.print( height_tmp, width_tmp, str, background_tmp, foreground_tmp );
}
break;
}
//----------------------------------------------------------------
// TEST_NUMBERS
//----------------------------------------------------------------
// Test writing numbers on screen using the number to string screen print
// Profile execution times as well using the Chrono class
case Demo::TEST_NUMBERS:
{
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::BLACK );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::MEDIUM_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
//Header
g_screen.print( 0, 0, "DEMO: Numeric String" );
//Demo counter
static int demo_cnt = 0;
demo_cnt++;
//Show a counter left aligned
g_screen.print( 1, 1, "Counter: " );
g_screen.set_format( 8, Longan_nano::Screen::Format_align::ADJ_LEFT, Longan_nano::Screen::Format_format::NUM );
g_screen.print( 1, 11, demo_cnt );
//Show a counter right aligned
g_screen.print( 2, 1, "Counter: " );
g_screen.set_format( 8, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::NUM );
g_screen.print( 2, 19, demo_cnt );
//Temp
int tmp;
//Show uptime in microseconds
g_screen.print( 3, 1, "Uptime:" );
g_screen.print( 3, 18, "mS" );
g_screen.set_format( 10, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::NUM );
tmp = timer_uptime.stop( Longan_nano::Chrono::Unit::milliseconds );
g_screen.print( 3, 17, tmp );
//Show cpu time spent updating the screen
g_screen.print( 4, 1, "Screen:" );
g_screen.print( 4, 18, "mS" );
tmp = timer_screen.get_accumulator( Longan_nano::Chrono::Unit::milliseconds );
g_screen.print( 4, 17, tmp );
}
break;
}
//----------------------------------------------------------------
// TEST_ENG_NUMBERS
//----------------------------------------------------------------
// Test writing numbers on screen using the number to string screen print
// Profile execution times as well using the Chrono class
case Demo::TEST_ENG_NUMBERS:
{
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::BLACK );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::MEDIUM_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
int16_t num_sprites_changed = 0;
//Header
num_sprites_changed += g_screen.print( 0, 0, "DEMO: Profile eng" );
num_sprites_changed += g_screen.print( 1, 4, "|Time[s]|CPU [%]" );
//Show uptime in microseconds
int tmp_uptime;
num_sprites_changed += g_screen.print( 2, 0, "Time|" );
num_sprites_changed += g_screen.print( 2, 12, '|' );
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
tmp_uptime = timer_uptime.stop( Longan_nano::Chrono::Unit::milliseconds );
num_sprites_changed += g_screen.print( 2, 11, tmp_uptime );
//Show cpu time spent updating the screen
int tmp_screen;
num_sprites_changed += g_screen.print( 3, 0, "LCD |" );
num_sprites_changed += g_screen.print( 3, 12, '|' );
tmp_screen = timer_screen.get_accumulator( Longan_nano::Chrono::Unit::milliseconds );
num_sprites_changed += g_screen.print( 3, 11, tmp_screen );
//Compute CPU usage for the screen
int64_t cpu_tmp = (int64_t)1 *tmp_screen *100000 /tmp_uptime;
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
num_sprites_changed += g_screen.print( 3, 19, (int)cpu_tmp );
//Show CPU time spent running the DEMO
num_sprites_changed += g_screen.print( 4, 0, "DEMO|" );
num_sprites_changed += g_screen.print( 4, 12, '|' );
tmp_screen = timer_demo.get_accumulator( Longan_nano::Chrono::Unit::milliseconds );
g_screen.print( 4, 11, tmp_screen );
//Compute CPU usage for the DEMO
cpu_tmp = (int64_t)1 *tmp_screen *100000 /tmp_uptime;
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
num_sprites_changed += g_screen.print( 4, 19, (int)cpu_tmp );
//Profile the number of sprites updated by the previous functions
g_screen.set_format( 4, Longan_nano::Screen::Format_align::ADJ_LEFT, Longan_nano::Screen::Format_format::NUM );
g_screen.print( 1, 0, num_sprites_changed);
}
break;
}
//----------------------------------------------------------------
// TEST_PENDING
//----------------------------------------------------------------
// Test writing numbers on screen using the number to string screen print
// Shows the number of sprites pending for update
case Demo::TEST_PENDING:
{
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::BLACK );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::MEDIUM_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
//Header
g_screen.print( 0, 0, "DEMO: Profile eng" );
g_screen.print( 1, 4, "|Time[s]|CPU [%]" );
//Show uptime in microseconds
int tmp_uptime;
g_screen.print( 2, 0, "Time|" );
g_screen.print( 2, 12, '|' );
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
tmp_uptime = timer_uptime.stop( Longan_nano::Chrono::Unit::milliseconds );
g_screen.print( 2, 11, tmp_uptime );
//Show cpu time spent updating the screen
int tmp_screen;
g_screen.print( 3, 0, "LCD |" );
g_screen.print( 3, 12, '|' );
tmp_screen = timer_screen.get_accumulator( Longan_nano::Chrono::Unit::milliseconds );
g_screen.print( 3, 11, tmp_screen );
//Compute CPU usage for the screen
int64_t cpu_tmp = (int64_t)1 *tmp_screen *100000 /tmp_uptime;
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
g_screen.print( 3, 19, (int)cpu_tmp );
//Show CPU time spent running the DEMO
g_screen.print( 4, 0, "DEMO|" );
g_screen.print( 4, 12, '|' );
tmp_screen = timer_demo.get_accumulator( Longan_nano::Chrono::Unit::milliseconds );
g_screen.print( 4, 11, tmp_screen );
//Compute CPU usage for the DEMO
cpu_tmp = (int64_t)1 *tmp_screen *100000 /tmp_uptime;
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
g_screen.print( 4, 19, (int)cpu_tmp );
//Profile the number of sprites pending for update
g_screen.set_format( 4, Longan_nano::Screen::Format_align::ADJ_LEFT, Longan_nano::Screen::Format_format::NUM );
int pending_cnt = g_screen.get_pending();
g_screen.print( 1, 0, pending_cnt);
//Show the error of the screen library
g_screen.print_err( 2, 13 );
}
break;
}
//----------------------------------------------------------------
// TEST_CHANGE_COLORS
//----------------------------------------------------------------
// Test the methods that handles the colors of the sprites without changing the content
// "change_color" change a palette color for another
// "set_palette_color"
case Demo::TEST_CHANGE_COLORS:
{
static uint8_t background_change_cnt;
//Randomly change the defaults background and foreground colors
Longan_nano::Screen::Color background_tmp = (Longan_nano::Screen::Color)g_rng_color( g_rng_engine );
Longan_nano::Screen::Color foreground_tmp = (Longan_nano::Screen::Color)g_rng_color( g_rng_engine );
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::BLACK );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::MEDIUM_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
int16_t num_sprites_changed = 0;
//Header
num_sprites_changed += g_screen.print( 0, 0, "DEMO: Eng Num Color" );
num_sprites_changed += g_screen.print( 1, 4, "|Time[s]|CPU [%]" );
//Show uptime in microseconds
int tmp_uptime;
num_sprites_changed += g_screen.print( 2, 0, "Time|" );
num_sprites_changed += g_screen.print( 2, 12, '|' );
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
tmp_uptime = timer_uptime.stop( Longan_nano::Chrono::Unit::milliseconds );
num_sprites_changed += g_screen.print( 2, 11, tmp_uptime );
//Show cpu time spent updating the screen
int tmp_screen;
num_sprites_changed += g_screen.print( 3, 0, "LCD |" );
num_sprites_changed += g_screen.print( 3, 12, '|' );
tmp_screen = timer_screen.get_accumulator( Longan_nano::Chrono::Unit::milliseconds );
num_sprites_changed += g_screen.print( 3, 11, tmp_screen );
//Compute CPU usage for the screen
int64_t cpu_tmp = (int64_t)1 *tmp_screen *100000 /tmp_uptime;
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
num_sprites_changed += g_screen.print( 3, 19, (int)cpu_tmp );
//Show CPU time spent running the DEMO
num_sprites_changed += g_screen.print( 4, 0, "DEMO|" );
num_sprites_changed += g_screen.print( 4, 12, '|' );
tmp_screen = timer_demo.get_accumulator( Longan_nano::Chrono::Unit::milliseconds );
g_screen.print( 4, 11, tmp_screen );
//Compute CPU usage for the DEMO
cpu_tmp = (int64_t)1 *tmp_screen *100000 /tmp_uptime;
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
num_sprites_changed += g_screen.print( 4, 19, (int)cpu_tmp );
//Randomly color a sprite
background_tmp = (Longan_nano::Screen::Color)g_rng_color( g_rng_engine );
foreground_tmp = (Longan_nano::Screen::Color)g_rng_color( g_rng_engine );
background_change_cnt++;
if (background_change_cnt > 15)
{
background_change_cnt = 0;
num_sprites_changed += g_screen.set_default_colors( background_tmp, foreground_tmp );
}
//Profile the number of sprites updated by the previous functions
g_screen.set_format( 4, Longan_nano::Screen::Format_align::ADJ_LEFT, Longan_nano::Screen::Format_format::NUM );
g_screen.print( 1, 0, num_sprites_changed);
}
break;
}
//----------------------------------------------------------------
// TEST_WORKLOAD
//----------------------------------------------------------------
// Profile execution time with constant workload
// Allow to test improvements in the Driver Class and Screen Class
case Demo::TEST_WORKLOAD:
{
//If: demo is yet to be initialized
if (f_demo_init == false)
{
g_screen.reset_colors();
//Clear the screen
g_screen.clear( Longan_nano::Screen::Color::BLACK );
//Configure prescaler to achieve the correct execution time
demo_pre = Config::MEDIUM_DEMO_US/Config::SCREEN_US;
//Demo is now initialized
f_demo_init = true;
}
//If: demo is initialized and can be run
else
{
//Print random color squares on the screen
for (uint8_t t = 0;t < 10;t++)
{
int th = g_rng_height( g_rng_engine );
int tw = g_rng_width( g_rng_engine );
Longan_nano::Screen::Color color_tmp = (Longan_nano::Screen::Color)g_rng_color( g_rng_engine );
//Ask the screen driver to print the character
g_screen.paint( th, tw, color_tmp );
}
//Header
g_screen.print( 0, 0, "CPU |" );
g_screen.print( 0, 12, '|' );
//Compute CPU usage for the screen
int tmp_uptime = timer_uptime.stop( Longan_nano::Chrono::Unit::milliseconds );
int tmp_deltat = timer_screen.get_accumulator( Longan_nano::Chrono::Unit::milliseconds );
int cpu_tmp = (int64_t)1 *tmp_deltat *100000 /tmp_uptime;
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
g_screen.print( 0, 11, (int)cpu_tmp );
//Compute CPU usage for the DEMO
tmp_deltat = timer_demo.get_accumulator( Longan_nano::Chrono::Unit::milliseconds );
cpu_tmp = (int64_t)1 *tmp_deltat *100000 /tmp_uptime;
g_screen.set_format( User::String::STRING_SIZE_SENG -1, Longan_nano::Screen::Format_align::ADJ_RIGHT, Longan_nano::Screen::Format_format::ENG, -3 );
g_screen.print( 0, 19, (int)cpu_tmp );
}
break;
}
//Unhandled demo
default:
{
//Do nothing
}
} //End Switch: for each demo
//Profile time spent running the DEMO
timer_demo.accumulate();
} //End If: demo is authorized to execute
} //End forever
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return 0;
} //end function: main
/****************************************************************************
** @brief init
** init_pa8_button_interrupt | void
****************************************************************************/
//! @details
//! User clicked the PA8 boot button
//! This button is used to switch between demos
/***************************************************************************/
void init_pa8_button_interrupt( void )
{
//Clock the GPIO banks
rcu_periph_clock_enable(RCU_GPIOA);
//Setup the boot button
gpio_init( GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_8 );
//Initialize the ECLIC IRQ lines
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
eclic_irq_enable(EXTI5_9_IRQn, 1, 1);
//Initialize the EXTI. IRQ can be generated from GPIO edge detectors
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_8);
exti_init(EXTI_8, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
//Clear interrupt flag. Ensure no spurious execution at start
exti_interrupt_flag_clear(EXTI_8);
//Enable the interrupts. From now on interrupt handlers can be executed
eclic_global_interrupt_enable();
return;
} //End init: init_pa8_button_interrupt | void
/****************************************************************************
** @brief isr
** EXTI5_9_IRQHandler | void
****************************************************************************/
//! @details
//! User clicked the PA8 boot button
//! This button is used to switch between demos
/***************************************************************************/
extern "C"
void EXTI5_9_IRQHandler( void )
{
//If: interrupt from PA8 boot button
if (exti_interrupt_flag_get(EXTI_8) != RESET)
{
//Clear the interrupt from PA8 boot button
exti_interrupt_flag_clear(EXTI_8);
//If: the button is released after ISR (UP)
if (gpio_input_bit_get( GPIOA, GPIO_PIN_8 ) == RESET)
{
//Signal event
g_f_pa8_button_up = true;
}
}
//Default: interrupt from an unhandled GPIO
else
{
//Do nothing (should clear the interrupt flags)
}
} //End isr: EXTI5_9_IRQHandler | void
view raw main.cpp hosted with ❤ by GitHub






No comments: