>>>Longan Nano GD32VF103<<<
>>>Longan Nano Demo<<<
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.
Clear the screen to a random color every 500ms
Print a random character in a random position of the screen every 1ms
Same as above but with random foreground and background colors
Print a random string in a random position of the screen every 25ms
Same as above but with random foreground and background colors
Print numbers with right and left adjust
CPU execution and uptime with engineering format 4 significant digit and SI suffix
Same as above but with screen builtin pending and error methods
Same as above but with random foreground and background colors
Constant workload demo. Print 10 sprites every 25ms and show CPU use
Illustration 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
5.1Doxygen Documentation
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 |
No comments:
Post a Comment