C

Implementing basic functions

Implementing basic functions

This chapter lists the basic functions that must be implemented to run Qt Quick Ultralite core in general. You can take the contents of platform/boards/qt/example-baremetal/platform.cpp as a basis for your own implementation and add your code at the mentioned locations.

Initializing hardware

Initialization of hardware components can be done in the Qul::Platform::initializeHardware function. It is called after initializing static variables and classes but before any graphics rendering. Hardware that needs early initialization need to be handled by the BSP before entering main(). This function can be used for peripherals that are needed after startup.

void initializeHardware()
{
#if 0
    Replace the following code with actual code for your device.

    setup_system_clocks();
    setup_flash_memory();
    setup_sd_ram();
    setup_usart();
    setup_rnd_generator();
#endif
    ...

The resource system of Qt Quick Ultralite expects assets marked as preloadable, such as fonts and graphics, to be preloaded before starting the main loop. (See the Qt Quick Ultralite documentation on how to configure the asset preloading.) For this purpose the linker script defines symbols containing the source and destination addresses of the preloadable assets. The copy can be performed using DMA or as shown in the following example, using memcpy:

    ...
// Preloading assets
extern unsigned char _preloadable_assetdata_src;
extern unsigned char _preloadable_assetdata_dst_begin;
extern unsigned char _preloadable_assetdata_dst_end;

memcpy(&_preloadable_assetdata_dst_begin,
       &_preloadable_assetdata_src,
       &_preloadable_assetdata_dst_end - &_preloadable_assetdata_dst_begin);

For IAR compiler:

    ...
#pragma section = "AssetDataPreload"
#pragma section = "AssetDataPreload_init"
char *_preloadable_assetdata_src = (char *) (__section_begin("AssetDataPreload_init"));
char *_preloadable_assetdata_dst_begin = (char *) (__section_begin("AssetDataPreload"));
char *_preloadable_assetdata_dst_end = (char *) (__section_end("AssetDataPreload"));

memcpy(_preloadable_assetdata_dst_begin,
       _preloadable_assetdata_src,
       (unsigned) _preloadable_assetdata_dst_end - (unsigned) _preloadable_assetdata_dst_begin);

Random numbers

To provide random numbers to QML applications, the platform requires a function to be implemented.

double rand()
{
    // Replace std::rand() by the proper call to the random number generator on your device, if available.
    const uint32_t number = std::rand();
    return number / (std::numeric_limits<uint32_t>::max() + 1.0);
}

Current timestamp

An essential part for rendering and timers is getting the current timestamp of the system. The function Qul::Platform::currentTimestamp has to return this system time.

uint64_t currentTimestamp()
{
    // Replace this line to make it return the system timestamp in milliseconds.
    return 0;
}

Renderloop callback schedule

The render loop and event processing of Qt Quick Ultralite must be called at certain times. The Qt Quick Ultralite core will notify the platform about the next timestamp it requires to be called back to perform tasks by calling Qul::Platform::scheduleEngineUpdate.

static uint64_t nextUpdate = 0;

void scheduleEngineUpdate(uint64_t timestamp)
{
    nextUpdate = timestamp;
}

The timestamp for the next required update is stored in a single variable. You may implement scheduling timeouts using hardware timers or OS-provided timers, in case your platform provides these.

Note: The implementation of Qul::Platform::scheduleEngineUpdate can get called from an interrupt and it must be safe for running within an interrupt context.

Main loop

Qt Quick Ultralite does not provide a main loop, but requires a custom main loop that calls the core engine at a given time.

The update function takes care of a single update of the core engine by calling Qul::PlatformInterface::updateEngine to update its timers and showing animations. It uses the timestamp set by the Qt Quick Ultralite core engine, as described in the previous section.

uint64_t update()
{
    const uint64_t timestamp = currentTimestamp();

    if (timestamp >= nextUpdate) {
        // Handle deadline or pending events
        Qul::PlatformInterface::updateEngine(timestamp);
    }

    return nextUpdate;
}

The exec loop runs forever, or at least for as long as the application is running, and is responsible for calling Qul::Platform::update at appropriate times. When no update calls are required by the core engine, the device may enter a sleep state if possible.

Qul::Platform::update returns the timestamp when it is expected to be called the next time. A timestamp lesser than the current timestamp, or even 0, should result in calling Qul::Platform::update as soon as possible. A greater timestamp value than the current timestamp means that the platform implementation should call Qul::Platform::update at that given time. Until the scheduled time, the device may enter a sleep mode.

void exec()
{
    while (true) {
        const uint64_t timestamp = Platform::update();

        if (timestamp > Platform::currentTimestamp()) {
            // The device may go to a sleep mode until timestamp or interrupt
        }
    }
}

You must replace the wait_for_interrupt() call with an appropriate call on your platform. The used function for sleeping must be able to return in case an event, like a touch event, was delivered to the Qt Quick Ultralite core from an interrupt, or the time for the next scheduled update is reached.

Note: In case a sleep or powersave is not implemented, the CPU will always run at full workload.

Note: The function body can be left empty if you are not going to use any demos, examples, tests, calling Application::exec() or app_common framework.

Additional functions for IAR

In order to make the time infrastructure work, the IAR libraries require additional functions to be implemented. For more information, see the IAR developer guide.

// The number of times an internal timing event occurs per second
int const CLOCKS_PER_SECOND = 1000;

clock_t clock(void)
{
    QUL_UNUSED(CLOCKS_PER_SECOND);

    // This function should return a value, which after division by CLOCKS_PER_SECOND,
    // is the processor time in seconds.
    return (clock_t) HAL_GetTick();
}

__time32_t __time32(__time32_t *t)
{
    uint64_t timeAtStartup = 0; // Read this from a time source like a real time clock;
    uint64_t timestamp = stm32_timestamp();
    // same timestamp as _gettimeofday
    __time32_t curtime = (__time32_t)(timeAtStartup + (timestamp / 1000));

    if (t)
        *t = curtime;

    return curtime;
}

char const *__getzone()
{
    // See <IAR>/src/lib/time/getzone.c for documentation
    // For Germany as a default timezone
    return ":GMT+1:GMT+2:0100:032502+0:102502+0";
}

__ATTRIBUTES char *_DstMalloc(size_t);
__ATTRIBUTES void _DstFree(char *);

char *_DstMalloc(size_t s)
{
    // Return a buffer that can hold the maximum number of DST entries of
    // of any timezone available on the device.
    // Each DST entry takes up a structure of 5 bytes plus regular alignment.
    // Instead of a static buffer a dynamically allocated memory can be used as well.

    // With the two entries shown above the required buffer size would be
    // 2 * (5 bytes size + 3 bytes alignment) = 16 bytes

    static char buffert[8 * 4];
    return buffert;
}

void _DstFree(char *p)
{
    // Nothing required here because of static buffer in _DstMalloc
    QUL_UNUSED(p);
}

Available under certain Qt licenses.
Find out more.