fin SETR2
This commit is contained in:
@@ -0,0 +1,539 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : DoubleBufferedVideoController.hpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef BUFFERPOOLVIDEOCONTROLLER_HPP
|
||||
#define BUFFERPOOLVIDEOCONTROLLER_HPP
|
||||
|
||||
#include <touchgfx/widgets/VideoWidget.hpp>
|
||||
#include <MJPEGDecoder.hpp>
|
||||
#include <touchgfx/Utils.hpp>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "cmsis_os.h"
|
||||
#if defined(osCMSIS) && (osCMSIS < 0x20000)
|
||||
#define MUTEX_CREATE() osMutexCreate(0)
|
||||
#define MUTEX_LOCK(m) osMutexWait(m, osWaitForever)
|
||||
#define MUTEX_TYPE osMutexId
|
||||
#define MUTEX_UNLOCK(m) osMutexRelease(m)
|
||||
#define SEM_CREATE() osSemaphoreCreate(0, 1)
|
||||
#define SEM_POST(s) osSemaphoreRelease(s)
|
||||
#define SEM_TYPE osSemaphoreId
|
||||
#define SEM_WAIT(s) osSemaphoreWait(s, osWaitForever)
|
||||
#else
|
||||
#define MUTEX_CREATE() osMutexNew(0)
|
||||
#define MUTEX_LOCK(m) osMutexAcquire(m, osWaitForever)
|
||||
#define MUTEX_TYPE osMutexId_t
|
||||
#define MUTEX_UNLOCK(m) osMutexRelease(m)
|
||||
#define SEM_CREATE() osSemaphoreNew(1, 0, 0)
|
||||
#define SEM_POST(s) osSemaphoreRelease(s)
|
||||
#define SEM_TYPE osSemaphoreId_t
|
||||
#define SEM_WAIT(s) osSemaphoreAcquire(s, osWaitForever)
|
||||
#endif
|
||||
|
||||
template <uint32_t no_streams, uint32_t width, uint32_t height, uint32_t stride, touchgfx::Bitmap::BitmapFormat output_format>
|
||||
class DoubleBufferedVideoController : public touchgfx::VideoController
|
||||
{
|
||||
public:
|
||||
DoubleBufferedVideoController()
|
||||
: VideoController(),
|
||||
bufferRGB(0), sizeBufferRGB(0), topBufferRGB(0), allowSkipFrames(true),
|
||||
semDecode(0), mutexBuffers(0)
|
||||
{
|
||||
assert((no_streams > 0) && "Video: Number of streams zero!");
|
||||
|
||||
// Clear decoder array
|
||||
memset(mjpegDecoders, 0, sizeof(mjpegDecoders));
|
||||
|
||||
// Initialize synchronization primitives
|
||||
semDecode = SEM_CREATE(); // Binary semaphore
|
||||
mutexBuffers = MUTEX_CREATE();
|
||||
}
|
||||
|
||||
virtual Handle registerVideoWidget(touchgfx::VideoWidget& widget)
|
||||
{
|
||||
// Running in UI thread
|
||||
|
||||
// Find stream handle for Widget
|
||||
Handle handle = getFreeHandle();
|
||||
|
||||
streams[handle].isActive = true;
|
||||
|
||||
// Set Widget buffer format and address
|
||||
widget.setVideoBufferFormat(output_format, width, height);
|
||||
widget.setVideoBuffer((uint8_t*)0);
|
||||
|
||||
streams[handle].frameNumberShown = 0;
|
||||
|
||||
// Todo, make buffer size depending on widget
|
||||
const uint32_t sizeOfOneDecodeBuffer = height * stride;
|
||||
|
||||
// Allocate two buffers for this stream, if possible
|
||||
if (topBufferRGB + 2 * sizeOfOneDecodeBuffer > (bufferRGB + sizeBufferRGB))
|
||||
{
|
||||
assert(0 && "registerVideoWidget: Unable to allocate two RGB buffers!");
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
streams[handle].bufferA = (uint8_t*)topBufferRGB;
|
||||
topBufferRGB += sizeOfOneDecodeBuffer;
|
||||
streams[handle].bufferB = (uint8_t*)topBufferRGB;
|
||||
topBufferRGB += sizeOfOneDecodeBuffer;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
virtual void unregisterVideoWidget(const Handle handle)
|
||||
{
|
||||
// Running in UI thread
|
||||
|
||||
// Reset active for this handle
|
||||
streams[handle].isActive = false;
|
||||
|
||||
// If all handles are free, reset top pointer
|
||||
bool oneIsActive = false;
|
||||
for (uint32_t i = 0; i < no_streams; i++)
|
||||
{
|
||||
oneIsActive |= streams[i].isActive;
|
||||
}
|
||||
if (oneIsActive == false)
|
||||
{
|
||||
// Reset memory usage
|
||||
topBufferRGB = bufferRGB;
|
||||
}
|
||||
}
|
||||
|
||||
virtual uint32_t getCurrentFrameNumber(const Handle handle)
|
||||
{
|
||||
assert(handle < no_streams);
|
||||
const Stream& stream = streams[handle];
|
||||
return stream.frameNumberShown;
|
||||
}
|
||||
|
||||
virtual void setFrameRate(const Handle handle, uint32_t ui_frames, uint32_t video_frames)
|
||||
{
|
||||
// Running in UI thread
|
||||
|
||||
assert(handle < no_streams);
|
||||
Stream& stream = streams[handle];
|
||||
|
||||
// Reset counters
|
||||
stream.tickCount = 0;
|
||||
stream.frameCount = 0;
|
||||
|
||||
// Save requested frame rate ratio
|
||||
stream.frame_rate_ticks = ui_frames;
|
||||
stream.frame_rate_video = video_frames;
|
||||
}
|
||||
|
||||
virtual void setVideoData(const Handle handle, const uint8_t* movie, const uint32_t length)
|
||||
{
|
||||
// Running in UI thread
|
||||
|
||||
mjpegDecoders[handle]->setVideoData(movie, length);
|
||||
|
||||
clearState(handle);
|
||||
}
|
||||
|
||||
virtual void setVideoData(const Handle handle, VideoDataReader& reader)
|
||||
{
|
||||
// Running in UI thread
|
||||
mjpegDecoders[handle]->setVideoData(reader);
|
||||
|
||||
clearState(handle);
|
||||
}
|
||||
|
||||
virtual void setCommand(const Handle handle, Command cmd, uint32_t param)
|
||||
{
|
||||
// Running in UI thread
|
||||
|
||||
assert(handle < no_streams);
|
||||
Stream& stream = streams[handle];
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case PLAY:
|
||||
// Cannot Play without movie
|
||||
if (mjpegDecoders[handle]->hasVideo())
|
||||
{
|
||||
MUTEX_LOCK(mutexBuffers);
|
||||
stream.isPlaying = true;
|
||||
stream.cancelDecoding = false;
|
||||
// Reset counters
|
||||
stream.frameCount = 0;
|
||||
stream.tickCount = 0;
|
||||
// Seek to start of video if stopped
|
||||
if (stream.isStopped)
|
||||
{
|
||||
stream.seek_to_frame = 1;
|
||||
}
|
||||
stream.isStopped = false;
|
||||
// Kick decoder if next buffer is available
|
||||
stream.skip_frames = 0;
|
||||
if (stream.nextBuffer == 0)
|
||||
{
|
||||
stream.doDecodeNewFrame = true;
|
||||
SEM_POST(semDecode);
|
||||
}
|
||||
MUTEX_UNLOCK(mutexBuffers);
|
||||
}
|
||||
break;
|
||||
case PAUSE:
|
||||
stream.isPlaying = false;
|
||||
stream.isStopped = false;
|
||||
break;
|
||||
case SEEK:
|
||||
stream.seek_to_frame = param;
|
||||
// Reset counters
|
||||
stream.frameCount = 0;
|
||||
stream.tickCount = 0;
|
||||
break;
|
||||
case SHOW:
|
||||
stream.seek_to_frame = param;
|
||||
// Reset counters
|
||||
stream.frameCount = 0;
|
||||
stream.tickCount = 0;
|
||||
stream.isStopped = false;
|
||||
break;
|
||||
case STOP:
|
||||
stream.isPlaying = false;
|
||||
stream.isStopped = true;
|
||||
break;
|
||||
case SET_REPEAT:
|
||||
stream.repeat = (param > 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by VideoWidget::handleTickEvent.
|
||||
* Update frame counters.
|
||||
* Decide if widget should be invalidated.
|
||||
*/
|
||||
virtual bool updateFrame(const Handle handle, touchgfx::VideoWidget& widget)
|
||||
{
|
||||
// Running in UI thread
|
||||
|
||||
assert(handle < no_streams);
|
||||
Stream& stream = streams[handle];
|
||||
|
||||
// Increase tickCount if playing
|
||||
if (stream.isPlaying)
|
||||
{
|
||||
stream.tickCount += HAL::getInstance()->getLCDRefreshCount();
|
||||
}
|
||||
|
||||
// Assume more frames are available, flag is lowered once, when changing to the last frame
|
||||
bool hasMoreFrames = true;
|
||||
|
||||
// See if we have a nextBuffer and if next frame of specific frame is to be decoded
|
||||
if (stream.nextBuffer && (decodeForNextTick(stream) || stream.seek_to_frame > 0))
|
||||
{
|
||||
MUTEX_LOCK(mutexBuffers);
|
||||
|
||||
// Do nothing if seek to frame
|
||||
if (stream.seek_to_frame > 0)
|
||||
{
|
||||
stream.nextBuffer = 0;
|
||||
}
|
||||
|
||||
if (stream.nextBuffer != 0)
|
||||
{
|
||||
// Use nextBuffer as current
|
||||
stream.currentBuffer = stream.nextBuffer;
|
||||
stream.nextBuffer = 0;
|
||||
|
||||
// Copy frameNumber, increase count
|
||||
stream.frameNumberShown = stream.frameNumberNext;
|
||||
stream.frameCount++;
|
||||
|
||||
hasMoreFrames = stream.hasMoreFramesAfterNext;
|
||||
|
||||
// Update widget to show current buffer
|
||||
widget.setVideoBuffer(stream.currentBuffer);
|
||||
widget.invalidate();
|
||||
|
||||
if (!hasMoreFrames && !stream.repeat)
|
||||
{
|
||||
stream.isPlaying = false;
|
||||
}
|
||||
}
|
||||
|
||||
MUTEX_UNLOCK(mutexBuffers);
|
||||
}
|
||||
|
||||
// Kick decoder if playing or seeking frame and next buffer is ready
|
||||
if ((stream.isPlaying || stream.seek_to_frame > 0) && (stream.nextBuffer == 0))
|
||||
{
|
||||
stream.doDecodeNewFrame = true;
|
||||
SEM_POST(semDecode);
|
||||
}
|
||||
|
||||
return hasMoreFrames;
|
||||
}
|
||||
|
||||
virtual void draw(const Handle handle, const touchgfx::Rect& invalidatedArea, const touchgfx::VideoWidget& widget)
|
||||
{
|
||||
// Running in UI thread
|
||||
|
||||
// Nothing in this decoder
|
||||
}
|
||||
|
||||
void setRGBBuffer(uint8_t* buffer, size_t sizeOfBuffer)
|
||||
{
|
||||
// Running in UI thread / main
|
||||
|
||||
bufferRGB = buffer;
|
||||
topBufferRGB = bufferRGB;
|
||||
sizeBufferRGB = sizeOfBuffer;
|
||||
}
|
||||
|
||||
void addDecoder(MJPEGDecoder& decoder, uint32_t index)
|
||||
{
|
||||
// Running in UI thread / main
|
||||
|
||||
assert(index < no_streams);
|
||||
mjpegDecoders[index] = &decoder;
|
||||
}
|
||||
|
||||
void endFrame()
|
||||
{
|
||||
// Running in UI thread
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
void decoderTaskEntry()
|
||||
{
|
||||
// Running in Decoder thread!!
|
||||
|
||||
while (1)
|
||||
{
|
||||
// Look for a stream to decode
|
||||
uint32_t stream_index = getStreamIndexToDecode();
|
||||
|
||||
if (stream_index == NO_STREAM)
|
||||
{
|
||||
// All streams decoded, wait for synchronisation signal from UI thread
|
||||
SEM_WAIT(semDecode);
|
||||
|
||||
// Try from the beginning
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lock out UI by taking the mutex
|
||||
MUTEX_LOCK(mutexBuffers);
|
||||
|
||||
// Now decode the stream
|
||||
Stream& stream = streams[stream_index];
|
||||
|
||||
// Select the unused buffer for decoding
|
||||
uint8_t* decodeBuffer = (stream.currentBuffer == stream.bufferA) ? stream.bufferB : stream.bufferA;
|
||||
|
||||
MJPEGDecoder* const decoder = mjpegDecoders[stream_index];
|
||||
// Seek or increment video frame
|
||||
if (stream.seek_to_frame > 0)
|
||||
{
|
||||
decoder->gotoFrame(stream.seek_to_frame);
|
||||
stream.seek_to_frame = 0;
|
||||
stream.cancelDecoding = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stream.skip_frames > 0)
|
||||
{
|
||||
decoder->gotoFrame(decoder->getCurrentFrameNumber() + stream.skip_frames);
|
||||
stream.frameCount += stream.skip_frames;
|
||||
stream.skip_frames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock mutex while decoding
|
||||
MUTEX_UNLOCK(mutexBuffers);
|
||||
|
||||
// Decode frame
|
||||
const bool hasMoreFrames = decoder->decodeNextFrame(decodeBuffer, width, height, stride);
|
||||
|
||||
MUTEX_LOCK(mutexBuffers);
|
||||
|
||||
// Save new frame in stream unless cancelled
|
||||
if (stream.cancelDecoding)
|
||||
{
|
||||
stream.cancelDecoding = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.nextBuffer = decodeBuffer;
|
||||
if (hasMoreFrames)
|
||||
{
|
||||
stream.frameNumberNext = decoder->getCurrentFrameNumber() - 1; // Points to frame after this
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.frameNumberNext = 1;
|
||||
}
|
||||
stream.hasMoreFramesAfterNext = hasMoreFrames;
|
||||
|
||||
// Lower decode flag
|
||||
stream.doDecodeNewFrame = false;
|
||||
}
|
||||
|
||||
// Release the mutex
|
||||
MUTEX_UNLOCK(mutexBuffers);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void getVideoInformation(const Handle handle, touchgfx::VideoInformation* data)
|
||||
{
|
||||
assert(handle < no_streams);
|
||||
mjpegDecoders[handle]->getVideoInfo(data);
|
||||
}
|
||||
|
||||
virtual bool getIsPlaying(const Handle handle)
|
||||
{
|
||||
assert(handle < no_streams);
|
||||
Stream& stream = streams[handle];
|
||||
return stream.isPlaying;
|
||||
}
|
||||
|
||||
virtual void setVideoFrameRateCompensation(bool allow)
|
||||
{
|
||||
allowSkipFrames = allow;
|
||||
}
|
||||
|
||||
private:
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
Stream() : frameCount(0), frameNumberNext(0), frameNumberShown(0), tickCount(0),
|
||||
frame_rate_video(0), frame_rate_ticks(0),
|
||||
seek_to_frame(0), skip_frames(0),
|
||||
currentBuffer(0), nextBuffer(0), bufferA(0), bufferB(0),
|
||||
isActive(false), isPlaying(false),
|
||||
doDecodeNewFrame(false), cancelDecoding(false),
|
||||
hasMoreFramesAfterNext(false), repeat(true), isStopped(false)
|
||||
{}
|
||||
uint32_t frameCount; // Video frame count since play/speed change
|
||||
uint32_t frameNumberNext; // Next Video frame number (if any)
|
||||
uint32_t frameNumberShown; // Shown Video frame number
|
||||
uint32_t tickCount; // UI frames since play
|
||||
uint32_t frame_rate_video; // Ratio of frames wanted counter
|
||||
uint32_t frame_rate_ticks; // Ratio of frames wanted divider
|
||||
uint32_t seek_to_frame; // Requested next frame number
|
||||
uint32_t skip_frames; // Number of frames to skip to keep frame rate
|
||||
uint8_t* currentBuffer; // Current Video frame, used for drawing
|
||||
uint8_t* nextBuffer; // Already decode next video frame, or zero
|
||||
uint8_t* bufferA; // One buffer allocated for this stream
|
||||
uint8_t* bufferB; // Another buffer allocated for this stream
|
||||
bool isActive;
|
||||
bool isPlaying;
|
||||
bool doDecodeNewFrame;
|
||||
bool cancelDecoding;
|
||||
bool hasMoreFramesAfterNext;
|
||||
bool repeat;
|
||||
bool isStopped;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NO_STREAM = 0xFFFF
|
||||
};
|
||||
|
||||
MJPEGDecoder* mjpegDecoders[no_streams];
|
||||
Stream streams[no_streams];
|
||||
uint8_t* bufferRGB;
|
||||
size_t sizeBufferRGB; // Size in Bytes
|
||||
uint8_t* topBufferRGB; // Pointer to unused memory in buffer
|
||||
bool allowSkipFrames;
|
||||
|
||||
SEM_TYPE semDecode; // Post by UI, wait by Decoder thread
|
||||
MUTEX_TYPE mutexBuffers; // Mutual exclusion of the video buffers and stream data
|
||||
|
||||
/* return true, if new video frame should be decoded for the next tick (keep video decode framerate low) */
|
||||
bool decodeForNextTick(Stream& stream)
|
||||
{
|
||||
// Running in UI thread
|
||||
|
||||
// Compare tickCount/frameCount to frame_rate_ticks/frame_rate_video
|
||||
if ((stream.tickCount * stream.frame_rate_video) >= (stream.frame_rate_ticks * stream.frameCount))
|
||||
{
|
||||
if (allowSkipFrames)
|
||||
{
|
||||
stream.skip_frames = (stream.tickCount * stream.frame_rate_video - stream.frame_rate_ticks * stream.frameCount) / stream.frame_rate_ticks;
|
||||
if (stream.skip_frames > 0)
|
||||
{
|
||||
stream.skip_frames--;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle getFreeHandle()
|
||||
{
|
||||
// Running in UI thread
|
||||
|
||||
for (uint32_t i = 0; i < no_streams; i++)
|
||||
{
|
||||
if (streams[i].isActive == false)
|
||||
{
|
||||
// Reset stream parameters
|
||||
streams[i] = Stream();
|
||||
|
||||
return static_cast<VideoController::Handle>(i);
|
||||
}
|
||||
}
|
||||
|
||||
assert(0 && "Unable to find free video stream handle!");
|
||||
return static_cast<VideoController::Handle>(0);
|
||||
}
|
||||
|
||||
uint32_t getStreamIndexToDecode()
|
||||
{
|
||||
for (uint32_t i = 0; i < no_streams; i++)
|
||||
{
|
||||
Stream& stream = streams[i];
|
||||
if (stream.doDecodeNewFrame) // Marked by UI for decoding
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return NO_STREAM;
|
||||
}
|
||||
|
||||
void clearState(const Handle handle)
|
||||
{
|
||||
// Stop playing, and clear next buffer if any, cancel ongoing decoding
|
||||
setCommand(handle, STOP, 0);
|
||||
|
||||
MUTEX_LOCK(mutexBuffers);
|
||||
|
||||
Stream& stream = streams[handle];
|
||||
if (stream.nextBuffer != 0)
|
||||
{
|
||||
// Ignore any decoded buffer
|
||||
stream.nextBuffer = 0;
|
||||
}
|
||||
stream.cancelDecoding = true;
|
||||
stream.isPlaying = false;
|
||||
|
||||
MUTEX_UNLOCK(mutexBuffers);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // TOUCHGFX_BUFFERPOOLVIDEOCONTROLLER_HPP
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : MJPEGDecoder.hpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef TOUCHGFX_MJPEGDECODER_HPP
|
||||
#define TOUCHGFX_MJPEGDECODER_HPP
|
||||
|
||||
#include <touchgfx/hal/Types.hpp>
|
||||
#include <touchgfx/hal/VideoController.hpp>
|
||||
|
||||
class MJPEGDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~MJPEGDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
//Set video data for the decoder
|
||||
virtual void setVideoData(const uint8_t* movie, const uint32_t length) = 0;
|
||||
|
||||
//Set video data for the decoder
|
||||
virtual void setVideoData(touchgfx::VideoDataReader& reader) = 0;
|
||||
|
||||
/**
|
||||
* Check if MJPEGDecoder has a video.
|
||||
*
|
||||
* @return Returns true if the MJPEGDecoder has a video.
|
||||
*/
|
||||
virtual bool hasVideo() = 0;
|
||||
|
||||
//Increment position to next frame and decode and convert to RGB
|
||||
virtual bool decodeNextFrame(uint8_t* buffer, uint16_t width, uint16_t height, uint32_t stride) = 0;
|
||||
|
||||
//Increment position to next frame and decode. Used with decodeFrame.
|
||||
virtual bool gotoNextFrame() = 0;
|
||||
|
||||
//Decode part of the current frame, framebuffer is locked, area is drawn relative to frameBuffer
|
||||
virtual bool decodeFrame(const touchgfx::Rect& area, uint8_t* frameBuffer, uint32_t framebufferStride) = 0;
|
||||
|
||||
//Decode thumbnail, assumes buffer stride is width
|
||||
virtual bool decodeThumbnail(uint32_t frameno, uint8_t* buffer, uint16_t width, uint16_t height) = 0;
|
||||
|
||||
//Set current frame number
|
||||
virtual void gotoFrame(uint32_t frameno) = 0;
|
||||
|
||||
//Get current frame number
|
||||
virtual uint32_t getCurrentFrameNumber() const = 0;
|
||||
|
||||
//Get number of frames in video
|
||||
virtual uint32_t getNumberOfFrames() = 0;
|
||||
|
||||
//Read video information
|
||||
virtual void getVideoInfo(touchgfx::VideoInformation* data) = 0;
|
||||
|
||||
enum AVIErrors
|
||||
{
|
||||
AVI_NO_ERROR,
|
||||
AVI_NO_BUFFERS,
|
||||
AVI_NO_FILE,
|
||||
AVI_ERROR_NOT_RIFF,
|
||||
AVI_ERROR_AVI_HEADER_NOT_FOUND,
|
||||
AVI_ERROR_AVI_LIST_NOT_FOUND,
|
||||
AVI_ERROR_AVI_HDRL_NOT_FOUND,
|
||||
AVI_ERROR_AVI_AVIH_NOT_FOUND,
|
||||
AVI_ERROR_AVI_HEADER_TO_SHORT, //not full header provided
|
||||
AVI_ERROR_FILE_BUFFER_TO_SMALL,
|
||||
AVI_ERROR_MOVI_NOT_FOUND,
|
||||
AVI_ERROR_IDX1_NOT_FOUND,
|
||||
AVI_ERROR_FRAMENO_TO_HIGH,
|
||||
AVI_ERROR_EOF_REACHED
|
||||
};
|
||||
|
||||
AVIErrors virtual getLastError() = 0;
|
||||
};
|
||||
|
||||
#endif // TOUCHGFX_MJPEGDECODER_HPP
|
||||
156
P7_SETR2/P7_SMT4497/TouchGFX/target/generated/OSWrappers.cpp
Normal file
156
P7_SETR2/P7_SMT4497/TouchGFX/target/generated/OSWrappers.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : OSWrappers.cpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <touchgfx/hal/HAL.hpp>
|
||||
#include <touchgfx/hal/OSWrappers.hpp>
|
||||
|
||||
#include <cmsis_os2.h>
|
||||
#include <cassert>
|
||||
|
||||
static osSemaphoreId_t frame_buffer_sem = NULL;
|
||||
static osMessageQueueId_t vsync_queue = NULL;
|
||||
|
||||
// Just a dummy value to insert in the VSYNC queue.
|
||||
static uint32_t dummy = 0x5a;
|
||||
|
||||
using namespace touchgfx;
|
||||
|
||||
/*
|
||||
* Initialize frame buffer semaphore and queue/mutex for VSYNC signal.
|
||||
*/
|
||||
void OSWrappers::initialize()
|
||||
{
|
||||
// Create a queue of length 1
|
||||
frame_buffer_sem = osSemaphoreNew(1, 1, NULL); // Binary semaphore
|
||||
assert((frame_buffer_sem != NULL) && "Creation of framebuffer semaphore failed");
|
||||
|
||||
// Create a queue of length 1
|
||||
vsync_queue = osMessageQueueNew(1, 4, NULL);
|
||||
assert((vsync_queue != NULL) && "Creation of vsync message queue failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Take the frame buffer semaphore. Blocks until semaphore is available.
|
||||
*/
|
||||
void OSWrappers::takeFrameBufferSemaphore()
|
||||
{
|
||||
osSemaphoreAcquire(frame_buffer_sem, osWaitForever);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the frame buffer semaphore.
|
||||
*/
|
||||
void OSWrappers::giveFrameBufferSemaphore()
|
||||
{
|
||||
osSemaphoreRelease(frame_buffer_sem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to obtain the frame buffer semaphore. If semaphore is not available, do
|
||||
* nothing.
|
||||
*
|
||||
* Note must return immediately! This function does not care who has the taken the semaphore,
|
||||
* it only serves to make sure that the semaphore is taken by someone.
|
||||
*/
|
||||
void OSWrappers::tryTakeFrameBufferSemaphore()
|
||||
{
|
||||
osSemaphoreAcquire(frame_buffer_sem, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the frame buffer semaphore in a way that is safe in interrupt context. Called
|
||||
* from ISR.
|
||||
*
|
||||
* Release the frame buffer semaphore in a way that is safe in interrupt context.
|
||||
* Called from ISR.
|
||||
*/
|
||||
void OSWrappers::giveFrameBufferSemaphoreFromISR()
|
||||
{
|
||||
osSemaphoreRelease(frame_buffer_sem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal that a VSYNC has occurred. Should make the vsync queue/mutex available.
|
||||
*
|
||||
* Note This function is called from an ISR, and should (depending on OS) trigger a
|
||||
* scheduling.
|
||||
*/
|
||||
void OSWrappers::signalVSync()
|
||||
{
|
||||
osMessageQueuePut(vsync_queue, &dummy, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal that the rendering of the frame has completed. Used by
|
||||
* some systems to avoid using any previous vsync.
|
||||
*/
|
||||
void OSWrappers::signalRenderingDone()
|
||||
{
|
||||
// Empty implementation for CMSIS V2
|
||||
}
|
||||
|
||||
/*
|
||||
* This function blocks until a VSYNC occurs.
|
||||
*
|
||||
* Note This function must first clear the mutex/queue and then wait for the next one to
|
||||
* occur.
|
||||
*/
|
||||
void OSWrappers::waitForVSync()
|
||||
{
|
||||
uint32_t dummyGet;
|
||||
// First make sure the queue is empty, by trying to remove an element with 0 timeout.
|
||||
osMessageQueueGet(vsync_queue, &dummyGet, 0, 0);
|
||||
|
||||
// Then, wait for next VSYNC to occur.
|
||||
osMessageQueueGet(vsync_queue, &dummyGet, 0, osWaitForever);
|
||||
}
|
||||
|
||||
/*
|
||||
* A function that causes executing task to sleep for a number of milliseconds.
|
||||
*
|
||||
* A function that causes executing task to sleep for a number of milliseconds.
|
||||
* This function is OPTIONAL. It is only used by the TouchGFX in the case of
|
||||
* a specific frame refresh strategy (REFRESH_STRATEGY_OPTIM_SINGLE_BUFFER_TFT_CTRL).
|
||||
* Due to backwards compatibility, in order for this function to be useable by the HAL
|
||||
* the function must be explicitly registered:
|
||||
* hal.registerTaskDelayFunction(&OSWrappers::taskDelay)
|
||||
*
|
||||
* see HAL::setFrameRefreshStrategy(FrameRefreshStrategy s)
|
||||
* see HAL::registerTaskDelayFunction(void (*delayF)(uint16_t))
|
||||
*/
|
||||
void OSWrappers::taskDelay(uint16_t ms)
|
||||
{
|
||||
osDelay(static_cast<uint32_t>(ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that causes the executing task to yield control to
|
||||
* another thread. This function is used by the framework when it
|
||||
* is necessary to wait a little before continuing (e.g. drawing).
|
||||
*
|
||||
* The implementation should typically request the operating
|
||||
* system to change to another task of similar priority. When
|
||||
* running without an operating system, the implementation can run
|
||||
* a very short task and return.
|
||||
*/
|
||||
void OSWrappers::taskYield()
|
||||
{
|
||||
osThreadYield();
|
||||
}
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
714
P7_SETR2/P7_SMT4497/TouchGFX/target/generated/STM32DMA.cpp
Normal file
714
P7_SETR2/P7_SMT4497/TouchGFX/target/generated/STM32DMA.cpp
Normal file
@@ -0,0 +1,714 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : STM32DMA.cpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "stm32f7xx_hal.h"
|
||||
#include "stm32f7xx_hal_dma2d.h"
|
||||
#include <STM32DMA.hpp>
|
||||
#include <cassert>
|
||||
#include <touchgfx/hal/HAL.hpp>
|
||||
#include <touchgfx/hal/Paint.hpp>
|
||||
|
||||
/* Makes touchgfx specific types and variables visible to this file */
|
||||
using namespace touchgfx;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const uint16_t format;
|
||||
const uint16_t size;
|
||||
const uint32_t* const data;
|
||||
} clutData_t;
|
||||
|
||||
extern "C" DMA2D_HandleTypeDef hdma2d;
|
||||
|
||||
extern "C" {
|
||||
static void DMA2D_XferCpltCallback(DMA2D_HandleTypeDef* handle)
|
||||
{
|
||||
(void)handle; // Unused argument
|
||||
HAL::getInstance()->signalDMAInterrupt();
|
||||
}
|
||||
|
||||
static void DMA2D_XferErrorCallback(DMA2D_HandleTypeDef* handle)
|
||||
{
|
||||
(void)handle; // Unused argument
|
||||
while (1)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STM32DMA::STM32DMA()
|
||||
: DMA_Interface(dma_queue), dma_queue(queue_storage, sizeof(queue_storage) / sizeof(queue_storage[0]))
|
||||
{
|
||||
}
|
||||
|
||||
STM32DMA::~STM32DMA()
|
||||
{
|
||||
/* Disable DMA2D global Interrupt */
|
||||
NVIC_DisableIRQ(DMA2D_IRQn);
|
||||
}
|
||||
|
||||
void STM32DMA::initialize()
|
||||
{
|
||||
/* Ensure DMA2D Clock is enabled */
|
||||
__HAL_RCC_DMA2D_CLK_ENABLE();
|
||||
__HAL_RCC_DMA2D_FORCE_RESET();
|
||||
__HAL_RCC_DMA2D_RELEASE_RESET();
|
||||
|
||||
/* Add transfer error callback function */
|
||||
hdma2d.XferErrorCallback = DMA2D_XferErrorCallback;
|
||||
|
||||
/* Add transfer complete callback function */
|
||||
hdma2d.XferCpltCallback = DMA2D_XferCpltCallback;
|
||||
|
||||
/* Enable DMA2D global Interrupt */
|
||||
NVIC_EnableIRQ(DMA2D_IRQn);
|
||||
}
|
||||
|
||||
inline uint32_t STM32DMA::getChromARTInputFormat(Bitmap::BitmapFormat format)
|
||||
{
|
||||
// Default color mode set to ARGB8888
|
||||
uint32_t dma2dColorMode = DMA2D_INPUT_ARGB8888;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case Bitmap::ARGB8888: /* DMA2D input mode set to 32bit ARGB */
|
||||
dma2dColorMode = DMA2D_INPUT_ARGB8888;
|
||||
break;
|
||||
case Bitmap::RGB888: /* DMA2D input mode set to 24bit RGB */
|
||||
dma2dColorMode = DMA2D_INPUT_RGB888;
|
||||
break;
|
||||
case Bitmap::RGB565: /* DMA2D input mode set to 16bit RGB */
|
||||
dma2dColorMode = DMA2D_INPUT_RGB565;
|
||||
break;
|
||||
case Bitmap::ARGB2222: /* Fall through */
|
||||
case Bitmap::ABGR2222: /* Fall through */
|
||||
case Bitmap::RGBA2222: /* Fall through */
|
||||
case Bitmap::BGRA2222: /* Fall through */
|
||||
case Bitmap::L8: /* DMA2D input mode set to 8bit Color Look up table*/
|
||||
dma2dColorMode = DMA2D_INPUT_L8;
|
||||
break;
|
||||
case Bitmap::BW: /* Fall through */
|
||||
case Bitmap::BW_RLE: /* Fall through */
|
||||
case Bitmap::GRAY4: /* Fall through */
|
||||
case Bitmap::GRAY2: /* Fall through */
|
||||
default: /* Unsupported input format for DMA2D */
|
||||
assert(0 && "Unsupported Format!");
|
||||
break;
|
||||
}
|
||||
|
||||
return dma2dColorMode;
|
||||
}
|
||||
|
||||
inline uint32_t STM32DMA::getChromARTOutputFormat(Bitmap::BitmapFormat format)
|
||||
{
|
||||
// Default color mode set to ARGB8888
|
||||
uint32_t dma2dColorMode = DMA2D_OUTPUT_ARGB8888;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case Bitmap::ARGB8888: /* DMA2D output mode set to 32bit ARGB */
|
||||
dma2dColorMode = DMA2D_OUTPUT_ARGB8888;
|
||||
break;
|
||||
case Bitmap::RGB888: /* Fall through */
|
||||
case Bitmap::ARGB2222: /* Fall through */
|
||||
case Bitmap::ABGR2222: /* Fall through */
|
||||
case Bitmap::RGBA2222: /* Fall through */
|
||||
case Bitmap::BGRA2222: /* DMA2D output mode set to 24bit RGB */
|
||||
dma2dColorMode = DMA2D_OUTPUT_RGB888;
|
||||
break;
|
||||
case Bitmap::RGB565: /* DMA2D output mode set to 16bit RGB */
|
||||
dma2dColorMode = DMA2D_OUTPUT_RGB565;
|
||||
break;
|
||||
case Bitmap::L8: /* Fall through */
|
||||
case Bitmap::BW: /* Fall through */
|
||||
case Bitmap::BW_RLE: /* Fall through */
|
||||
case Bitmap::GRAY4: /* Fall through */
|
||||
case Bitmap::GRAY2: /* Fall through */
|
||||
default: /* Unsupported output format for DMA2D */
|
||||
assert(0 && "Unsupported Format!");
|
||||
break;
|
||||
}
|
||||
|
||||
return dma2dColorMode;
|
||||
}
|
||||
|
||||
BlitOperations STM32DMA::getBlitCaps()
|
||||
{
|
||||
return static_cast<BlitOperations>(BLIT_OP_FILL
|
||||
| BLIT_OP_FILL_WITH_ALPHA
|
||||
| BLIT_OP_COPY
|
||||
| BLIT_OP_COPY_L8
|
||||
| BLIT_OP_COPY_WITH_ALPHA
|
||||
| BLIT_OP_COPY_ARGB8888
|
||||
| BLIT_OP_COPY_ARGB8888_WITH_ALPHA
|
||||
| BLIT_OP_COPY_A4
|
||||
| BLIT_OP_COPY_A8);
|
||||
}
|
||||
|
||||
/*
|
||||
* void STM32DMA::setupDataCopy(const BlitOp& blitOp) handles blit operation of
|
||||
* BLIT_OP_COPY
|
||||
* BLIT_OP_COPY_L8
|
||||
* BLIT_OP_COPY_WITH_ALPHA
|
||||
* BLIT_OP_COPY_ARGB8888
|
||||
* BLIT_OP_COPY_ARGB8888_WITH_ALPHA
|
||||
* BLIT_OP_COPY_A4
|
||||
* BLIT_OP_COPY_A8
|
||||
*/
|
||||
void STM32DMA::setupDataCopy(const BlitOp& blitOp)
|
||||
{
|
||||
uint32_t dma2dForegroundColorMode = getChromARTInputFormat(static_cast<Bitmap::BitmapFormat>(blitOp.srcFormat));
|
||||
uint32_t dma2dBackgroundColorMode = getChromARTInputFormat(static_cast<Bitmap::BitmapFormat>(blitOp.dstFormat));
|
||||
uint32_t dma2dOutputColorMode = getChromARTOutputFormat(static_cast<Bitmap::BitmapFormat>(blitOp.dstFormat));
|
||||
|
||||
/* DMA2D OOR register configuration */
|
||||
WRITE_REG(DMA2D->OOR, blitOp.dstLoopStride - blitOp.nSteps);
|
||||
|
||||
/* DMA2D BGOR register configuration */
|
||||
WRITE_REG(DMA2D->BGOR, blitOp.dstLoopStride - blitOp.nSteps);
|
||||
|
||||
/* DMA2D FGOR register configuration */
|
||||
WRITE_REG(DMA2D->FGOR, blitOp.srcLoopStride - blitOp.nSteps);
|
||||
|
||||
/* DMA2D OPFCCR register configuration */
|
||||
WRITE_REG(DMA2D->OPFCCR, dma2dOutputColorMode);
|
||||
|
||||
/* Configure DMA2D data size */
|
||||
WRITE_REG(DMA2D->NLR, (blitOp.nLoops | (blitOp.nSteps << DMA2D_NLR_PL_Pos)));
|
||||
|
||||
/* Configure DMA2D destination address */
|
||||
WRITE_REG(DMA2D->OMAR, reinterpret_cast<uint32_t>(blitOp.pDst));
|
||||
|
||||
/* Configure DMA2D source address */
|
||||
WRITE_REG(DMA2D->FGMAR, reinterpret_cast<uint32_t>(blitOp.pSrc));
|
||||
|
||||
switch (blitOp.operation)
|
||||
{
|
||||
case BLIT_OP_COPY_A4:
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_INPUT_A4 | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (blitOp.alpha << 24));
|
||||
|
||||
/* set DMA2D foreground color */
|
||||
WRITE_REG(DMA2D->FGCOLR, blitOp.color);
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, dma2dBackgroundColorMode | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(blitOp.pDst));
|
||||
|
||||
/* Set DMA2D mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_BLEND | DMA2D_IT_TC | DMA2D_CR_START | DMA2D_IT_CE | DMA2D_IT_TE);
|
||||
break;
|
||||
case BLIT_OP_COPY_A8:
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_INPUT_A8 | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (blitOp.alpha << 24));
|
||||
|
||||
/* set DMA2D foreground color */
|
||||
WRITE_REG(DMA2D->FGCOLR, blitOp.color);
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, dma2dBackgroundColorMode | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(blitOp.pDst));
|
||||
|
||||
/* Set DMA2D mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_BLEND | DMA2D_IT_TC | DMA2D_CR_START | DMA2D_IT_CE | DMA2D_IT_TE);
|
||||
break;
|
||||
case BLIT_OP_COPY_WITH_ALPHA:
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, dma2dForegroundColorMode | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (blitOp.alpha << 24));
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, dma2dBackgroundColorMode | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(blitOp.pDst));
|
||||
|
||||
/* Set DMA2D mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_BLEND | DMA2D_IT_TC | DMA2D_CR_START | DMA2D_IT_CE | DMA2D_IT_TE);
|
||||
break;
|
||||
case BLIT_OP_COPY_L8:
|
||||
{
|
||||
bool blend = true;
|
||||
const clutData_t* const palette = reinterpret_cast<const clutData_t*>(blitOp.pClut);
|
||||
|
||||
/* Write foreground CLUT memory address */
|
||||
WRITE_REG(DMA2D->FGCMAR, reinterpret_cast<uint32_t>(&palette->data));
|
||||
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, dma2dForegroundColorMode | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (blitOp.alpha << 24));
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, dma2dBackgroundColorMode | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(blitOp.pDst));
|
||||
|
||||
/* Configure CLUT */
|
||||
switch ((Bitmap::ClutFormat)palette->format)
|
||||
{
|
||||
case Bitmap::CLUT_FORMAT_L8_ARGB8888:
|
||||
/* Write foreground CLUT size and CLUT color mode */
|
||||
MODIFY_REG(DMA2D->FGPFCCR, (DMA2D_FGPFCCR_CS | DMA2D_FGPFCCR_CCM), (((palette->size - 1) << DMA2D_FGPFCCR_CS_Pos) | (DMA2D_CCM_ARGB8888 << DMA2D_FGPFCCR_CCM_Pos)));
|
||||
break;
|
||||
case Bitmap::CLUT_FORMAT_L8_RGB888:
|
||||
if (blitOp.alpha == 255)
|
||||
{
|
||||
blend = false;
|
||||
}
|
||||
MODIFY_REG(DMA2D->FGPFCCR, (DMA2D_FGPFCCR_CS | DMA2D_FGPFCCR_CCM), (((palette->size - 1) << DMA2D_FGPFCCR_CS_Pos) | (DMA2D_CCM_RGB888 << DMA2D_FGPFCCR_CCM_Pos)));
|
||||
break;
|
||||
|
||||
case Bitmap::CLUT_FORMAT_L8_RGB565:
|
||||
default:
|
||||
assert(0 && "Unsupported format");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable the CLUT loading for the foreground */
|
||||
SET_BIT(DMA2D->FGPFCCR, DMA2D_FGPFCCR_START);
|
||||
|
||||
while ((READ_REG(DMA2D->FGPFCCR) & DMA2D_FGPFCCR_START) != 0U)
|
||||
{
|
||||
}
|
||||
DMA2D->IFCR = (DMA2D_FLAG_CTC);
|
||||
|
||||
/* Set DMA2D mode */
|
||||
if (blend)
|
||||
{
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_BLEND | DMA2D_IT_TC | DMA2D_CR_START | DMA2D_IT_CE | DMA2D_IT_TE);
|
||||
}
|
||||
else
|
||||
{
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_PFC | DMA2D_IT_TC | DMA2D_CR_START | DMA2D_IT_CE | DMA2D_IT_TE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BLIT_OP_COPY_ARGB8888:
|
||||
case BLIT_OP_COPY_ARGB8888_WITH_ALPHA:
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, dma2dForegroundColorMode | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (blitOp.alpha << 24));
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, dma2dBackgroundColorMode | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(blitOp.pDst));
|
||||
|
||||
/* Set DMA2D mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_BLEND | DMA2D_IT_TC | DMA2D_CR_START | DMA2D_IT_CE | DMA2D_IT_TE);
|
||||
break;
|
||||
default: /* BLIT_OP_COPY */
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, dma2dForegroundColorMode | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (blitOp.alpha << 24));
|
||||
|
||||
/* Perform pixel-format-conversion (PFC) If Bitmap format is not same format as framebuffer format */
|
||||
if (blitOp.srcFormat != blitOp.dstFormat)
|
||||
{
|
||||
/* Start DMA2D : PFC Mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_PFC | DMA2D_IT_TC | DMA2D_CR_START | DMA2D_IT_CE | DMA2D_IT_TE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Start DMA2D : M2M Mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M | DMA2D_IT_TC | DMA2D_CR_START | DMA2D_IT_CE | DMA2D_IT_TE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* void STM32DMA::setupDataFill(const BlitOp& blitOp) handles blit operation of
|
||||
* BLIT_OP_FILL
|
||||
* BLIT_OP_FILL_WITH_ALPHA
|
||||
*/
|
||||
void STM32DMA::setupDataFill(const BlitOp& blitOp)
|
||||
{
|
||||
uint32_t dma2dOutputColorMode = getChromARTOutputFormat(static_cast<Bitmap::BitmapFormat>(blitOp.dstFormat));
|
||||
|
||||
/* DMA2D OPFCCR register configuration */
|
||||
WRITE_REG(DMA2D->OPFCCR, dma2dOutputColorMode);
|
||||
|
||||
/* Configure DMA2D data size */
|
||||
WRITE_REG(DMA2D->NLR, (blitOp.nLoops | (blitOp.nSteps << DMA2D_NLR_PL_Pos)));
|
||||
|
||||
/* Configure DMA2D destination address */
|
||||
WRITE_REG(DMA2D->OMAR, reinterpret_cast<uint32_t>(blitOp.pDst));
|
||||
|
||||
/* DMA2D OOR register configuration */
|
||||
WRITE_REG(DMA2D->OOR, blitOp.dstLoopStride - blitOp.nSteps);
|
||||
|
||||
if (blitOp.operation == BLIT_OP_FILL_WITH_ALPHA)
|
||||
{
|
||||
/* DMA2D BGOR register configuration */
|
||||
WRITE_REG(DMA2D->BGOR, blitOp.dstLoopStride - blitOp.nSteps);
|
||||
|
||||
/* DMA2D FGOR register configuration */
|
||||
WRITE_REG(DMA2D->FGOR, blitOp.dstLoopStride - blitOp.nSteps);
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, dma2dOutputColorMode | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Write DMA2D FGPFCCR register */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_INPUT_A8 | (DMA2D_REPLACE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | ((blitOp.alpha << 24) & DMA2D_FGPFCCR_ALPHA));
|
||||
|
||||
/* DMA2D FGCOLR register configuration */
|
||||
WRITE_REG(DMA2D->FGCOLR, blitOp.color);
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(blitOp.pDst));
|
||||
|
||||
/* Configure DMA2D source address */
|
||||
WRITE_REG(DMA2D->FGMAR, reinterpret_cast<uint32_t>(blitOp.pDst));
|
||||
|
||||
/* Enable the Peripheral and Enable the transfer complete interrupt */
|
||||
WRITE_REG(DMA2D->CR, (DMA2D_IT_TC | DMA2D_CR_START | DMA2D_M2M_BLEND | DMA2D_IT_CE | DMA2D_IT_TE));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Write DMA2D FGPFCCR register */
|
||||
WRITE_REG(DMA2D->FGPFCCR, dma2dOutputColorMode | (DMA2D_NO_MODIF_ALPHA << DMA2D_FGPFCCR_AM_Pos));
|
||||
|
||||
/* DMA2D FGOR register configuration */
|
||||
WRITE_REG(DMA2D->FGOR, 0);
|
||||
|
||||
/* Set color */
|
||||
WRITE_REG(DMA2D->OCOLR, blitOp.color);
|
||||
|
||||
/* Enable the Peripheral and Enable the transfer complete interrupt */
|
||||
WRITE_REG(DMA2D->CR, (DMA2D_IT_TC | DMA2D_CR_START | DMA2D_R2M | DMA2D_IT_CE | DMA2D_IT_TE));
|
||||
}
|
||||
}
|
||||
|
||||
namespace touchgfx
|
||||
{
|
||||
namespace paint
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const clutData_t* L8CLUT = 0;
|
||||
uint32_t L8ClutLoaded = 0;
|
||||
} // namespace
|
||||
|
||||
void setL8Palette(const uint8_t* const data)
|
||||
{
|
||||
L8CLUT = reinterpret_cast<const clutData_t*>(data - offsetof(clutData_t, data));
|
||||
L8ClutLoaded = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn void tearDown();
|
||||
*
|
||||
* @brief Waits until previous DMA drawing operation has finished
|
||||
*/
|
||||
void tearDown()
|
||||
{
|
||||
/* Wait for DMA2D to finish last run */
|
||||
while ((READ_REG(DMA2D->CR) & DMA2D_CR_START) != 0U);
|
||||
|
||||
/* Clear transfer flags */
|
||||
WRITE_REG(DMA2D->IFCR, DMA2D_FLAG_TC | DMA2D_FLAG_CE | DMA2D_FLAG_TE);
|
||||
}
|
||||
|
||||
/** Flushes a line of pixels in the data cache if used.
|
||||
*
|
||||
* @brief Flushes decoded RGB pixels when rendering compressed images
|
||||
*/
|
||||
void flushLine(uint32_t* addr, int sizebytes)
|
||||
{
|
||||
// This function is used when decompressing data to flush
|
||||
// the currently decoded data in the cache to allow the DMA2D
|
||||
// to blend the pixels correctly.
|
||||
if (SCB->CCR & SCB_CCR_DC_Msk)
|
||||
{
|
||||
SCB_CleanDCache_by_Addr(addr, sizebytes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn void invalidateTextureCache();
|
||||
*
|
||||
* @brief Flushes the data cache if used.
|
||||
*/
|
||||
void invalidateTextureCache()
|
||||
{
|
||||
}
|
||||
|
||||
namespace rgb565
|
||||
{
|
||||
/**
|
||||
* @fn void lineFromColor();
|
||||
*
|
||||
* @brief Renders Canvas Widget chunks using DMA.
|
||||
* This functions will not generate an interrupt, and will not affect the DMA queue.
|
||||
*/
|
||||
void lineFromColor(uint16_t* const ptr, const unsigned count, const uint32_t color, const uint8_t alpha, const uint32_t color565)
|
||||
{
|
||||
/* Wait for DMA2D to finish last run */
|
||||
while ((READ_REG(DMA2D->CR) & DMA2D_CR_START) != 0U);
|
||||
|
||||
/* Clear transfer flags */
|
||||
WRITE_REG(DMA2D->IFCR, DMA2D_FLAG_TC | DMA2D_FLAG_CE | DMA2D_FLAG_TE);
|
||||
|
||||
/* DMA2D OPFCCR register configuration */
|
||||
WRITE_REG(DMA2D->OPFCCR, DMA2D_OUTPUT_RGB565);
|
||||
|
||||
/* Configure DMA2D data size */
|
||||
WRITE_REG(DMA2D->NLR, (1 | (count << DMA2D_NLR_PL_Pos)));
|
||||
|
||||
/* Configure DMA2D destination address */
|
||||
WRITE_REG(DMA2D->OMAR, reinterpret_cast<uint32_t>(ptr));
|
||||
|
||||
if (alpha < 0xFF)
|
||||
{
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, DMA2D_OUTPUT_RGB565 | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Write DMA2D FGPFCCR register */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_INPUT_A8 | (DMA2D_REPLACE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (alpha << DMA2D_FGPFCCR_ALPHA_Pos));
|
||||
|
||||
/* DMA2D FGCOLR register configuration */
|
||||
WRITE_REG(DMA2D->FGCOLR, color);
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, (uint32_t)ptr);
|
||||
|
||||
/* Configure DMA2D source address */
|
||||
WRITE_REG(DMA2D->FGMAR, (uint32_t)ptr);
|
||||
|
||||
/* Enable the Peripheral and Enable the transfer complete interrupt */
|
||||
WRITE_REG(DMA2D->CR, (DMA2D_CR_START | DMA2D_M2M_BLEND));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Write DMA2D FGPFCCR register */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_OUTPUT_RGB565 | (DMA2D_NO_MODIF_ALPHA << DMA2D_FGPFCCR_AM_Pos));
|
||||
|
||||
/* Set color */
|
||||
WRITE_REG(DMA2D->OCOLR, color565);
|
||||
|
||||
/* Enable the Peripheral and Enable the transfer complete interrupt */
|
||||
WRITE_REG(DMA2D->CR, (DMA2D_CR_START | DMA2D_R2M));
|
||||
}
|
||||
}
|
||||
|
||||
void lineFromRGB565(uint16_t* const ptr, const uint16_t* const data, const unsigned count, const uint8_t alpha)
|
||||
{
|
||||
/* Wait for DMA2D to finish last run */
|
||||
while ((READ_REG(DMA2D->CR) & DMA2D_CR_START) != 0U);
|
||||
|
||||
/* Clear transfer flags */
|
||||
WRITE_REG(DMA2D->IFCR, DMA2D_FLAG_TC | DMA2D_FLAG_CE | DMA2D_FLAG_TE);
|
||||
|
||||
/* DMA2D OPFCCR register configuration */
|
||||
WRITE_REG(DMA2D->OPFCCR, DMA2D_OUTPUT_RGB565);
|
||||
|
||||
/* Configure DMA2D data size */
|
||||
WRITE_REG(DMA2D->NLR, (1 | (count << DMA2D_NLR_PL_Pos)));
|
||||
|
||||
/* Configure DMA2D destination address */
|
||||
WRITE_REG(DMA2D->OMAR, reinterpret_cast<uint32_t>(ptr));
|
||||
|
||||
/* Configure DMA2D source address */
|
||||
WRITE_REG(DMA2D->FGMAR, reinterpret_cast<uint32_t>(data));
|
||||
|
||||
if (alpha < 0xFF)
|
||||
{
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_INPUT_RGB565 | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (alpha << DMA2D_FGPFCCR_ALPHA_Pos));
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, DMA2D_INPUT_RGB565 | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(ptr));
|
||||
|
||||
/* Set DMA2D mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_BLEND | DMA2D_CR_START);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_INPUT_RGB565 | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (alpha << DMA2D_FGPFCCR_ALPHA_Pos));
|
||||
|
||||
/* Start DMA2D : M2M Mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M | DMA2D_CR_START);
|
||||
}
|
||||
}
|
||||
|
||||
void lineFromARGB8888(uint16_t* const ptr, const uint32_t* const data, const unsigned count, const uint8_t alpha)
|
||||
{
|
||||
/* Wait for DMA2D to finish last run */
|
||||
while ((READ_REG(DMA2D->CR) & DMA2D_CR_START) != 0U);
|
||||
|
||||
/* Clear transfer flags */
|
||||
WRITE_REG(DMA2D->IFCR, DMA2D_FLAG_TC | DMA2D_FLAG_CE | DMA2D_FLAG_TE);
|
||||
|
||||
/* DMA2D OPFCCR register configuration */
|
||||
WRITE_REG(DMA2D->OPFCCR, DMA2D_OUTPUT_RGB565);
|
||||
|
||||
/* Configure DMA2D data size */
|
||||
WRITE_REG(DMA2D->NLR, (1 | (count << DMA2D_NLR_PL_Pos)));
|
||||
|
||||
/* Configure DMA2D destination address */
|
||||
WRITE_REG(DMA2D->OMAR, reinterpret_cast<uint32_t>(ptr));
|
||||
|
||||
/* Configure DMA2D source address */
|
||||
WRITE_REG(DMA2D->FGMAR, reinterpret_cast<uint32_t>(data));
|
||||
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_INPUT_ARGB8888 | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (alpha << DMA2D_FGPFCCR_ALPHA_Pos));
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, DMA2D_INPUT_RGB565 | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(ptr));
|
||||
|
||||
/* Set DMA2D mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_BLEND | DMA2D_CR_START);
|
||||
}
|
||||
|
||||
void lineFromL8RGB888(uint16_t* const ptr, const uint8_t* const data, const unsigned count, const uint8_t alpha)
|
||||
{
|
||||
/* wait for DMA2D to finish last run */
|
||||
while ((READ_REG(DMA2D->CR) & DMA2D_CR_START) != 0U);
|
||||
|
||||
/* DMA2D OPFCCR register configuration */
|
||||
WRITE_REG(DMA2D->OPFCCR, DMA2D_OUTPUT_RGB565);
|
||||
|
||||
/* Configure DMA2D data size */
|
||||
WRITE_REG(DMA2D->NLR, (1 | (count << DMA2D_NLR_PL_Pos)));
|
||||
|
||||
/* Configure DMA2D destination address */
|
||||
WRITE_REG(DMA2D->OMAR, reinterpret_cast<uint32_t>(ptr));
|
||||
|
||||
/* Configure DMA2D source address */
|
||||
WRITE_REG(DMA2D->FGMAR, reinterpret_cast<uint32_t>(data));
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(ptr));
|
||||
|
||||
/* Load CLUT if not already loaded */
|
||||
if (L8ClutLoaded == 0)
|
||||
{
|
||||
/* Write foreground CLUT memory address */
|
||||
WRITE_REG(DMA2D->FGCMAR, reinterpret_cast<uint32_t>(&L8CLUT->data));
|
||||
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_INPUT_L8 | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (alpha << DMA2D_FGPFCCR_ALPHA_Pos));
|
||||
|
||||
MODIFY_REG(DMA2D->FGPFCCR, (DMA2D_FGPFCCR_CS | DMA2D_FGPFCCR_CCM), (((L8CLUT->size - 1) << DMA2D_FGPFCCR_CS_Pos) | (DMA2D_CCM_RGB888 << DMA2D_FGPFCCR_CCM_Pos)));
|
||||
|
||||
/* Enable the CLUT loading for the foreground */
|
||||
SET_BIT(DMA2D->FGPFCCR, DMA2D_FGPFCCR_START);
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, DMA2D_INPUT_RGB565 | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Mark CLUT loaded */
|
||||
L8ClutLoaded = 1;
|
||||
|
||||
/* Wait for load to finish */
|
||||
while ((READ_REG(DMA2D->FGPFCCR) & DMA2D_FGPFCCR_START) != 0U);
|
||||
|
||||
/* Clear CLUT Transfer Complete flag */
|
||||
DMA2D->IFCR = (DMA2D_FLAG_CTC);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set correct alpha for these pixels */
|
||||
MODIFY_REG(DMA2D->FGPFCCR, DMA2D_BGPFCCR_ALPHA_Msk, alpha << DMA2D_FGPFCCR_ALPHA_Pos);
|
||||
}
|
||||
|
||||
/* Start pixel transfer in correct mode */
|
||||
if (alpha < 0xFF)
|
||||
{
|
||||
/* Set DMA2D mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_BLEND | DMA2D_CR_START);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set DMA2D mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_PFC | DMA2D_CR_START);
|
||||
}
|
||||
}
|
||||
|
||||
void lineFromL8ARGB8888(uint16_t* const ptr, const uint8_t* const data, const unsigned count, const uint8_t alpha)
|
||||
{
|
||||
/* wait for DMA2D to finish last run */
|
||||
while ((READ_REG(DMA2D->CR) & DMA2D_CR_START) != 0U);
|
||||
|
||||
/* DMA2D OPFCCR register configuration */
|
||||
WRITE_REG(DMA2D->OPFCCR, DMA2D_OUTPUT_RGB565);
|
||||
|
||||
/* Configure DMA2D data size */
|
||||
WRITE_REG(DMA2D->NLR, (1 | (count << DMA2D_NLR_PL_Pos)));
|
||||
|
||||
/* Configure DMA2D destination address */
|
||||
WRITE_REG(DMA2D->OMAR, reinterpret_cast<uint32_t>(ptr));
|
||||
|
||||
/* Configure DMA2D source address */
|
||||
WRITE_REG(DMA2D->FGMAR, reinterpret_cast<uint32_t>(data));
|
||||
|
||||
/* Configure DMA2D Stream source2 address */
|
||||
WRITE_REG(DMA2D->BGMAR, reinterpret_cast<uint32_t>(ptr));
|
||||
|
||||
/* Load CLUT if not already loaded */
|
||||
if (L8ClutLoaded == 0)
|
||||
{
|
||||
/* Write foreground CLUT memory address */
|
||||
WRITE_REG(DMA2D->FGCMAR, reinterpret_cast<uint32_t>(&L8CLUT->data));
|
||||
|
||||
/* Set DMA2D color mode and alpha mode */
|
||||
WRITE_REG(DMA2D->FGPFCCR, DMA2D_INPUT_L8 | (DMA2D_COMBINE_ALPHA << DMA2D_FGPFCCR_AM_Pos) | (alpha << DMA2D_FGPFCCR_ALPHA_Pos));
|
||||
|
||||
MODIFY_REG(DMA2D->FGPFCCR, (DMA2D_FGPFCCR_CS | DMA2D_FGPFCCR_CCM), (((L8CLUT->size - 1) << DMA2D_FGPFCCR_CS_Pos) | (DMA2D_CCM_ARGB8888 << DMA2D_FGPFCCR_CCM_Pos)));
|
||||
|
||||
/* Enable the CLUT loading for the foreground */
|
||||
SET_BIT(DMA2D->FGPFCCR, DMA2D_FGPFCCR_START);
|
||||
|
||||
/* Write DMA2D BGPFCCR register */
|
||||
WRITE_REG(DMA2D->BGPFCCR, DMA2D_INPUT_RGB565 | (DMA2D_NO_MODIF_ALPHA << DMA2D_BGPFCCR_AM_Pos));
|
||||
|
||||
/* Mark CLUT loaded */
|
||||
L8ClutLoaded = 1;
|
||||
|
||||
/* Wait for load to finish */
|
||||
while ((READ_REG(DMA2D->FGPFCCR) & DMA2D_FGPFCCR_START) != 0U);
|
||||
|
||||
/* Clear CLUT Transfer Complete flag */
|
||||
DMA2D->IFCR = (DMA2D_FLAG_CTC);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set correct alpha for these pixels */
|
||||
MODIFY_REG(DMA2D->FGPFCCR, DMA2D_BGPFCCR_ALPHA_Msk, alpha << DMA2D_FGPFCCR_ALPHA_Pos);
|
||||
}
|
||||
|
||||
/* Start pixel transfer in blending mode */
|
||||
WRITE_REG(DMA2D->CR, DMA2D_M2M_BLEND | DMA2D_CR_START);
|
||||
}
|
||||
|
||||
} // namespace rgb565
|
||||
} // namespace paint
|
||||
} // namespace touchgfx
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
164
P7_SETR2/P7_SMT4497/TouchGFX/target/generated/STM32DMA.hpp
Normal file
164
P7_SETR2/P7_SMT4497/TouchGFX/target/generated/STM32DMA.hpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : STM32DMA.hpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef STM32DMA_HPP
|
||||
#define STM32DMA_HPP
|
||||
|
||||
#include <touchgfx/Bitmap.hpp>
|
||||
#include <touchgfx/hal/DMA.hpp>
|
||||
|
||||
/**
|
||||
* @class STM32DMA
|
||||
*
|
||||
* @brief This class specializes DMA_Interface for the STM32 processors.
|
||||
*
|
||||
* @sa touchgfx::DMA_Interface
|
||||
*/
|
||||
class STM32DMA : public touchgfx::DMA_Interface
|
||||
{
|
||||
/**
|
||||
* @typedef touchgfx::DMA_Interface Base
|
||||
*
|
||||
* @brief Defines an alias representing the base.
|
||||
*
|
||||
Defines an alias representing the base.
|
||||
*/
|
||||
typedef touchgfx::DMA_Interface Base;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @fn STM32DMA::STM32DMA();
|
||||
*
|
||||
* @brief Default constructor.
|
||||
*
|
||||
* Default constructor.
|
||||
*/
|
||||
STM32DMA();
|
||||
|
||||
/**
|
||||
* @fn STM32DMA::~STM32DMA();
|
||||
*
|
||||
* @brief Destructor.
|
||||
*
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~STM32DMA();
|
||||
|
||||
/**
|
||||
* @fn DMAType touchgfx::STM32DMA::getDMAType()
|
||||
*
|
||||
* @brief Function for obtaining the DMA type of the concrete DMA_Interface implementation.
|
||||
*
|
||||
* Function for obtaining the DMA type of the concrete DMA_Interface implementation.
|
||||
* As default, will return DMA_TYPE_CHROMART type value.
|
||||
*
|
||||
* @return a DMAType value of the concrete DMA_Interface implementation.
|
||||
*/
|
||||
virtual touchgfx::DMAType getDMAType(void)
|
||||
{
|
||||
return touchgfx::DMA_TYPE_CHROMART;
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn touchgfx::BlitOperations STM32DMA::getBlitCaps();
|
||||
*
|
||||
* @brief Gets the blit capabilities.
|
||||
*
|
||||
* Gets the blit capabilities.
|
||||
*
|
||||
* This DMA supports a range of blit caps: BLIT_OP_COPY, BLIT_OP_COPY_ARGB8888,
|
||||
* BLIT_OP_COPY_ARGB8888_WITH_ALPHA, BLIT_OP_COPY_A4, BLIT_OP_COPY_A8.
|
||||
*
|
||||
*
|
||||
* @return Currently supported blitcaps.
|
||||
*/
|
||||
virtual touchgfx::BlitOperations getBlitCaps();
|
||||
|
||||
/**
|
||||
* @fn void STM32DMA::initialize();
|
||||
*
|
||||
* @brief Perform hardware specific initialization.
|
||||
*
|
||||
* Perform hardware specific initialization.
|
||||
*/
|
||||
virtual void initialize();
|
||||
|
||||
/**
|
||||
* @fn void STM32DMA::signalDMAInterrupt()
|
||||
*
|
||||
* @brief Raises a DMA interrupt signal.
|
||||
*
|
||||
* Raises a DMA interrupt signal.
|
||||
*/
|
||||
virtual void signalDMAInterrupt()
|
||||
{
|
||||
executeCompleted();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @fn virtual void STM32DMA::setupDataCopy(const touchgfx::BlitOp& blitOp);
|
||||
*
|
||||
* @brief Configures the DMA for copying data to the frame buffer.
|
||||
*
|
||||
* Configures the DMA for copying data to the frame buffer.
|
||||
*
|
||||
* @param blitOp Details on the copy to perform.
|
||||
*/
|
||||
virtual void setupDataCopy(const touchgfx::BlitOp& blitOp);
|
||||
|
||||
/**
|
||||
* @fn virtual void STM32DMA::setupDataFill(const touchgfx::BlitOp& blitOp);
|
||||
*
|
||||
* @brief Configures the DMA for "filling" the frame-buffer with a single color.
|
||||
*
|
||||
* Configures the DMA for "filling" the frame-buffer with a single color.
|
||||
*
|
||||
* @param blitOp Details on the "fill" to perform.
|
||||
*/
|
||||
virtual void setupDataFill(const touchgfx::BlitOp& blitOp);
|
||||
|
||||
private:
|
||||
touchgfx::LockFreeDMA_Queue dma_queue;
|
||||
touchgfx::BlitOp queue_storage[96];
|
||||
|
||||
/**
|
||||
* @fn void STM32DMA::getChromARTInputFormat()
|
||||
*
|
||||
* @brief Convert Bitmap format to ChromART Input format.
|
||||
*
|
||||
* @param format Bitmap format.
|
||||
*
|
||||
* @return ChromART Input format.
|
||||
*/
|
||||
|
||||
inline uint32_t getChromARTInputFormat(touchgfx::Bitmap::BitmapFormat format);
|
||||
|
||||
/**
|
||||
* @fn void STM32DMA::getChromARTOutputFormat()
|
||||
*
|
||||
* @brief Convert Bitmap format to ChromART Output format.
|
||||
*
|
||||
* @param format Bitmap format.
|
||||
*
|
||||
* @return ChromART Output format.
|
||||
*/
|
||||
inline uint32_t getChromARTOutputFormat(touchgfx::Bitmap::BitmapFormat format);
|
||||
};
|
||||
|
||||
#endif // STM32DMA_HPP
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
@@ -0,0 +1,579 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : SoftwareMJPEGDecoder.cpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <jinclude.h>
|
||||
#include <jpeglib.h>
|
||||
#include <string.h>
|
||||
#include <SoftwareMJPEGDecoder.hpp>
|
||||
|
||||
#define RGB565 0
|
||||
#define RGB888 1
|
||||
#define ARGB8888 2
|
||||
#define VIDEO_DECODE_FORMAT RGB565
|
||||
namespace
|
||||
{
|
||||
struct JPEG_RGB
|
||||
{
|
||||
uint8_t B;
|
||||
uint8_t G;
|
||||
uint8_t R;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
SoftwareMJPEGDecoder::SoftwareMJPEGDecoder(uint8_t* buffer)
|
||||
: frameNumber(0), currentMovieOffset(0), indexOffset(0), firstFrameOffset(0), lastFrameEnd(0), movieLength(0), movieData(0),
|
||||
reader(0), lineBuffer(buffer), aviBuffer(0), aviBufferLength(0), aviBufferStartOffset(0), lastError(AVI_NO_ERROR)
|
||||
{
|
||||
// Clear video info
|
||||
videoInfo.frame_height = 0;
|
||||
videoInfo.frame_width = 0;
|
||||
videoInfo.ms_between_frames = 0;
|
||||
videoInfo.number_of_frames = 0;
|
||||
}
|
||||
|
||||
int SoftwareMJPEGDecoder::compare(const uint32_t offset, const char* str, uint32_t num)
|
||||
{
|
||||
const char* src;
|
||||
if (reader != 0)
|
||||
{
|
||||
// Assuming data is in buffer!
|
||||
src = reinterpret_cast<const char*>(aviBuffer + (offset - aviBufferStartOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
src = (const char*)movieData + offset;
|
||||
}
|
||||
return strncmp(src, str, num);
|
||||
}
|
||||
|
||||
inline uint32_t SoftwareMJPEGDecoder::getU32(const uint32_t offset)
|
||||
{
|
||||
if (reader != 0)
|
||||
{
|
||||
// Assuming data is in buffer!
|
||||
const uint32_t index = offset - aviBufferStartOffset;
|
||||
return aviBuffer[index + 0] | (aviBuffer[index + 1] << 8) | (aviBuffer[index + 2] << 16) | (aviBuffer[index + 3] << 24);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8_t* const d = movieData + offset;
|
||||
return d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t SoftwareMJPEGDecoder::getU16(const uint32_t offset)
|
||||
{
|
||||
if (reader != 0)
|
||||
{
|
||||
// Assuming data is in buffer!
|
||||
const uint32_t index = offset - aviBufferStartOffset;
|
||||
return aviBuffer[index + 0] | (aviBuffer[index + 1] << 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8_t* const d = movieData + offset;
|
||||
return d[0] | (d[1] << 8);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* SoftwareMJPEGDecoder::readData(uint32_t offset, uint32_t length)
|
||||
{
|
||||
if (reader != 0)
|
||||
{
|
||||
if (length > aviBufferLength)
|
||||
{
|
||||
lastError = AVI_ERROR_FILE_BUFFER_TO_SMALL;
|
||||
assert(!"Buffer to small");
|
||||
}
|
||||
|
||||
reader->seek(offset);
|
||||
if (!reader->readData(aviBuffer, length))
|
||||
{
|
||||
lastError = AVI_ERROR_EOF_REACHED;
|
||||
}
|
||||
|
||||
aviBufferStartOffset = offset;
|
||||
return aviBuffer;
|
||||
}
|
||||
|
||||
return movieData + offset;
|
||||
}
|
||||
|
||||
bool SoftwareMJPEGDecoder::decodeNextFrame(uint8_t* buffer, uint16_t buffer_width, uint16_t buffer_height, uint32_t buffer_stride)
|
||||
{
|
||||
assert((frameNumber > 0) && "SoftwareMJPEGDecoder decoding without frame data!");
|
||||
|
||||
//find next frame and decode it
|
||||
readData(currentMovieOffset, 8);
|
||||
uint32_t streamNo = getU16(currentMovieOffset);
|
||||
uint32_t chunkType = getU16(currentMovieOffset + 2);
|
||||
uint32_t chunkSize = getU32(currentMovieOffset + 4);
|
||||
const uint16_t STREAM0 = 0x3030;
|
||||
const uint16_t TYPEDC = 0x6364;
|
||||
|
||||
bool isCurrentFrameLast;
|
||||
//play frame if we have it all
|
||||
if (currentMovieOffset + 8 + chunkSize < movieLength)
|
||||
{
|
||||
if (streamNo == STREAM0 && chunkType == TYPEDC && chunkSize > 0)
|
||||
{
|
||||
currentMovieOffset += 8;
|
||||
//decode frame
|
||||
const uint8_t* chunk = readData(currentMovieOffset, chunkSize);
|
||||
decodeMJPEGFrame(chunk, chunkSize, buffer, buffer_width, buffer_height, buffer_stride);
|
||||
frameNumber++;
|
||||
}
|
||||
|
||||
isCurrentFrameLast = false;
|
||||
|
||||
// Advance to next frame
|
||||
currentMovieOffset += chunkSize;
|
||||
if (chunkSize == 0) // Empty frame - Skip
|
||||
{
|
||||
currentMovieOffset += 8;
|
||||
}
|
||||
currentMovieOffset = (currentMovieOffset + 1) & 0xFFFFFFFE; //pad to next word
|
||||
|
||||
if (currentMovieOffset == lastFrameEnd)
|
||||
{
|
||||
frameNumber = 1;
|
||||
currentMovieOffset = firstFrameOffset; //start over
|
||||
isCurrentFrameLast = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
frameNumber = 1;
|
||||
currentMovieOffset = firstFrameOffset; //start over
|
||||
isCurrentFrameLast = true;
|
||||
}
|
||||
return !isCurrentFrameLast;
|
||||
}
|
||||
|
||||
bool SoftwareMJPEGDecoder::gotoNextFrame()
|
||||
{
|
||||
assert((frameNumber > 0) && "SoftwareMJPEGDecoder decoding without frame data!");
|
||||
|
||||
readData(currentMovieOffset, 8);
|
||||
uint32_t chunkSize = getU32(currentMovieOffset + 4);
|
||||
|
||||
//increment until next video frame
|
||||
while (currentMovieOffset + 8 + chunkSize < movieLength)
|
||||
{
|
||||
//increment one frame
|
||||
currentMovieOffset += chunkSize + 8;
|
||||
currentMovieOffset = (currentMovieOffset + 1) & 0xFFFFFFFE; //pad to next word
|
||||
frameNumber++;
|
||||
|
||||
//next chunk
|
||||
readData(currentMovieOffset, 8);
|
||||
//check it is a video frame
|
||||
uint32_t streamNo = getU16(currentMovieOffset);
|
||||
uint32_t chunkType = getU16(currentMovieOffset + 2);
|
||||
chunkSize = getU32(currentMovieOffset + 4);
|
||||
const uint16_t STREAM0 = 0x3030;
|
||||
const uint16_t TYPEDC = 0x6364;
|
||||
|
||||
if (streamNo == STREAM0 && chunkType == TYPEDC && chunkSize > 0)
|
||||
{
|
||||
// Found next frame
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//skip back to first frame
|
||||
frameNumber = 1;
|
||||
currentMovieOffset = firstFrameOffset; //start over
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoftwareMJPEGDecoder::setVideoData(const uint8_t* movie, const uint32_t length)
|
||||
{
|
||||
movieData = movie;
|
||||
movieLength = length;
|
||||
reader = 0; //not using reader
|
||||
|
||||
readVideoHeader();
|
||||
}
|
||||
|
||||
void SoftwareMJPEGDecoder::setVideoData(touchgfx::VideoDataReader& reader)
|
||||
{
|
||||
this->reader = &reader;
|
||||
movieData = 0;
|
||||
movieLength = reader.getDataLength();
|
||||
|
||||
readVideoHeader();
|
||||
}
|
||||
|
||||
bool SoftwareMJPEGDecoder::hasVideo()
|
||||
{
|
||||
return (reader != 0) || (movieData != 0);
|
||||
}
|
||||
|
||||
void SoftwareMJPEGDecoder::readVideoHeader()
|
||||
{
|
||||
// Start from the start
|
||||
currentMovieOffset = 0;
|
||||
lastError = AVI_NO_ERROR;
|
||||
|
||||
// Make header available in buffer
|
||||
readData(0, 72);
|
||||
|
||||
// Decode the movie header to find first frame
|
||||
// Must be RIFF file
|
||||
if (compare(currentMovieOffset, "RIFF", 4))
|
||||
{
|
||||
lastError = AVI_ERROR_NOT_RIFF;
|
||||
assert(!"RIFF header not found");
|
||||
}
|
||||
|
||||
//skip fourcc and length
|
||||
currentMovieOffset += 8;
|
||||
if (compare(currentMovieOffset, "AVI ", 4))
|
||||
{
|
||||
lastError = AVI_ERROR_AVI_HEADER_NOT_FOUND;
|
||||
assert(!"AVI header not found");
|
||||
}
|
||||
|
||||
currentMovieOffset += 4;
|
||||
if (compare(currentMovieOffset, "LIST", 4))
|
||||
{
|
||||
lastError = AVI_ERROR_AVI_LIST_NOT_FOUND;
|
||||
assert(!"AVI LIST not found");
|
||||
}
|
||||
|
||||
//save AVI List info
|
||||
const uint32_t aviListSize = getU32(currentMovieOffset + 4);
|
||||
const uint32_t aviListOffset = currentMovieOffset;
|
||||
assert(aviListSize);
|
||||
|
||||
//look into header to find frame rate
|
||||
bool foundFrame = true;
|
||||
uint32_t offset = currentMovieOffset + 8;
|
||||
if (compare(offset, "hdrl", 4))
|
||||
{
|
||||
lastError = AVI_ERROR_AVI_HDRL_NOT_FOUND;
|
||||
foundFrame = false;
|
||||
}
|
||||
|
||||
offset += 4;
|
||||
if (compare(offset, "avih", 4))
|
||||
{
|
||||
lastError = AVI_ERROR_AVI_AVIH_NOT_FOUND;
|
||||
foundFrame = false;
|
||||
}
|
||||
|
||||
if (foundFrame)
|
||||
{
|
||||
offset += 8; //skip fourcc and cb in AVIMAINHEADER
|
||||
videoInfo.ms_between_frames = getU32(offset) / 1000;
|
||||
videoInfo.number_of_frames = getU32(offset + 16);
|
||||
videoInfo.frame_width = getU32(offset + 32);
|
||||
videoInfo.frame_height = getU32(offset + 36);
|
||||
}
|
||||
//skip rest of AVI header, start from end of AVI List
|
||||
|
||||
//look for list with 'movi' header
|
||||
uint32_t listOffset = aviListOffset + aviListSize + 8;
|
||||
readData(listOffset, 12);
|
||||
while (compare(listOffset + 8, "movi", 4) && (lastError == AVI_NO_ERROR) && listOffset < movieLength)
|
||||
{
|
||||
const uint32_t listSize = getU32(listOffset + 4) + 8;
|
||||
listOffset += listSize;
|
||||
readData(listOffset, 12);
|
||||
}
|
||||
|
||||
if (lastError != AVI_NO_ERROR)
|
||||
{
|
||||
lastError = AVI_ERROR_MOVI_NOT_FOUND;
|
||||
return;
|
||||
}
|
||||
|
||||
//save first frame and end of last frame
|
||||
currentMovieOffset = listOffset + 8 + 4; //skip LIST and 'movi'
|
||||
lastFrameEnd = listOffset + 8 + getU32(listOffset + 4);
|
||||
|
||||
//find idx
|
||||
const uint32_t listSize = getU32(listOffset + 4) + 8;
|
||||
listOffset += listSize;
|
||||
readData(listOffset, 4);
|
||||
if (!compare(listOffset, "idx1", 4))
|
||||
{
|
||||
indexOffset = listOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastError = AVI_ERROR_IDX1_NOT_FOUND;
|
||||
return;
|
||||
}
|
||||
|
||||
//start on first frame
|
||||
frameNumber = 1; //next frame number is 1
|
||||
firstFrameOffset = currentMovieOffset;
|
||||
}
|
||||
|
||||
#if VIDEO_DECODE_FORMAT == RGB565 || VIDEO_DECODE_FORMAT == RGB888 || VIDEO_DECODE_FORMAT == ARGB8888
|
||||
void SoftwareMJPEGDecoder::decodeMJPEGFrame(const uint8_t* const mjpgdata, const uint32_t length, uint8_t* outputBuffer, uint16_t bufferWidth, uint16_t bufferHeight, uint32_t bufferStride)
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (outputBuffer && lineBuffer) //only decode if buffers are assigned.
|
||||
{
|
||||
/* This struct contains the JPEG decompression parameters */
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
/* This struct represents a JPEG error handler */
|
||||
struct jpeg_error_mgr jerr;
|
||||
|
||||
JSAMPROW lines[2] = { lineBuffer, 0 }; /* Output row buffer */
|
||||
|
||||
/* Step 1: allocate and initialize JPEG decompression object */
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
|
||||
/* Initialize the JPEG decompression object */
|
||||
jpeg_create_decompress(&cinfo);
|
||||
|
||||
//jpeg_stdio_src (&cinfo, file);
|
||||
jpeg_mem_src(&cinfo, const_cast<uint8_t*>(mjpgdata), length);
|
||||
|
||||
/* Step 3: read image parameters with jpeg_read_header() */
|
||||
jpeg_read_header(&cinfo, TRUE);
|
||||
|
||||
/* Step 4: set parameters for decompression */
|
||||
cinfo.dct_method = JDCT_FLOAT;
|
||||
|
||||
/* Step 5: start decompressor */
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
//restrict to minimum of movie and output buffer size
|
||||
const uint32_t width = MIN(bufferWidth, cinfo.image_width);
|
||||
const uint32_t height = MIN(bufferHeight, cinfo.output_height);
|
||||
|
||||
#if VIDEO_DECODE_FORMAT == RGB565
|
||||
uint16_t* lineptr = reinterpret_cast<uint16_t*>(outputBuffer);
|
||||
#elif VIDEO_DECODE_FORMAT == RGB888
|
||||
uint8_t* lineptr = outputBuffer;
|
||||
#else
|
||||
uint32_t* lineptr = reinterpret_cast<uint32_t*>(outputBuffer);
|
||||
#endif
|
||||
while (cinfo.output_scanline < height)
|
||||
{
|
||||
(void)jpeg_read_scanlines(&cinfo, lines, 1);
|
||||
#if VIDEO_DECODE_FORMAT == RGB565
|
||||
JPEG_RGB* RGB_matrix = (JPEG_RGB*)lineBuffer;
|
||||
JPEG_RGB* const RGB_end = RGB_matrix + width;
|
||||
while (RGB_matrix < RGB_end)
|
||||
{
|
||||
const uint16_t pix = ((RGB_matrix->R & 0xF8) << 8) | ((RGB_matrix->G & 0xFC) << 3) | ((RGB_matrix->B & 0xF8) >> 3);
|
||||
*lineptr++ = pix;
|
||||
RGB_matrix++;
|
||||
}
|
||||
lineptr += bufferWidth - width; //move to next line
|
||||
#elif VIDEO_DECODE_FORMAT == RGB888
|
||||
memcpy(lineptr, lineBuffer, width * 3);
|
||||
lineptr += bufferStride; //move to next line
|
||||
#else
|
||||
JPEG_RGB* RGB_matrix = (JPEG_RGB*)lineBuffer;
|
||||
JPEG_RGB* const RGB_end = RGB_matrix + width;
|
||||
while (RGB_matrix < RGB_end)
|
||||
{
|
||||
const uint32_t pix = (0xFF << 24) | (RGB_matrix->R << 16) | (RGB_matrix->G << 8) | (RGB_matrix->B);
|
||||
*lineptr++ = pix;
|
||||
RGB_matrix++;
|
||||
}
|
||||
lineptr += bufferWidth - width; //move to next line
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SIMULATOR
|
||||
cinfo.output_scanline = cinfo.output_height;
|
||||
#endif
|
||||
/* Step 6: Finish decompression */
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
|
||||
/* Step 7: Release JPEG decompression object */
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
}
|
||||
}
|
||||
|
||||
bool SoftwareMJPEGDecoder::decodeFrame(const touchgfx::Rect& area, uint8_t* frameBuffer, uint32_t framebuffer_width)
|
||||
{
|
||||
// Assuming that chunk is available and streamNo and chunkType is correct.
|
||||
// Check by gotoNextFrame
|
||||
|
||||
readData(currentMovieOffset, 8);
|
||||
const uint32_t length = getU32(currentMovieOffset + 4);
|
||||
|
||||
// Ensure whole frame is read
|
||||
const uint8_t* mjpgdata = readData(currentMovieOffset + 8, length);
|
||||
|
||||
assert(lineBuffer && "LineBuffer must be assigned prior to decoding directly to framebuffer");
|
||||
|
||||
/* This struct contains the JPEG decompression parameters */
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
/* This struct represents a JPEG error handler */
|
||||
struct jpeg_error_mgr jerr;
|
||||
|
||||
JSAMPROW lines[2] = { lineBuffer, 0 }; /* Output row buffer */
|
||||
|
||||
/* Step 1: allocate and initialize JPEG decompression object */
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
|
||||
/* Initialize the JPEG decompression object */
|
||||
jpeg_create_decompress(&cinfo);
|
||||
|
||||
//jpeg_stdio_src (&cinfo, file);
|
||||
jpeg_mem_src(&cinfo, const_cast<uint8_t*>(mjpgdata), length);
|
||||
|
||||
/* Step 3: read image parameters with jpeg_read_header() */
|
||||
jpeg_read_header(&cinfo, TRUE);
|
||||
|
||||
/* Step 4: set parameters for decompression */
|
||||
cinfo.dct_method = JDCT_FLOAT;
|
||||
|
||||
/* Step 5: start decompressor */
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
//restrict to minimum of movie and output buffer size
|
||||
const uint32_t startY = area.y;
|
||||
|
||||
//scan down to startY
|
||||
while (cinfo.output_scanline < startY)
|
||||
{
|
||||
(void)jpeg_read_scanlines(&cinfo, lines, 1);
|
||||
}
|
||||
|
||||
const uint32_t startX = area.x;
|
||||
const uint32_t endX = MIN((uint32_t)area.right(), cinfo.image_width);
|
||||
|
||||
#if VIDEO_DECODE_FORMAT == RGB565
|
||||
uint16_t* lineptr = reinterpret_cast<uint16_t*>(frameBuffer);
|
||||
lineptr += framebuffer_width * startY;
|
||||
#elif VIDEO_DECODE_FORMAT == RGB888
|
||||
uint8_t* lineptr = frameBuffer;
|
||||
lineptr += framebuffer_width * 3 * startY;
|
||||
#else
|
||||
uint32_t* lineptr = reinterpret_cast<uint32_t*>(frameBuffer);
|
||||
lineptr += framebuffer_width * startY;
|
||||
#endif
|
||||
const uint32_t endY = MIN((uint32_t)area.bottom(), cinfo.output_height);
|
||||
|
||||
//scan relevant part
|
||||
while (cinfo.output_scanline < endY)
|
||||
{
|
||||
(void)jpeg_read_scanlines(&cinfo, lines, 1);
|
||||
#if VIDEO_DECODE_FORMAT == RGB565
|
||||
JPEG_RGB* RGB_matrix = (JPEG_RGB*)lineBuffer;
|
||||
//loop row RGB888->RGB565 for required line part
|
||||
for (uint32_t counter = startX; counter < endX; counter++)
|
||||
{
|
||||
const uint16_t pix = ((RGB_matrix[counter].R & 0xF8) << 8) | ((RGB_matrix[counter].G & 0xFC) << 3) | ((RGB_matrix[counter].B & 0xF8) >> 3);
|
||||
*(lineptr + counter) = pix;
|
||||
}
|
||||
lineptr += framebuffer_width; //move to next line
|
||||
#elif VIDEO_DECODE_FORMAT == RGB888
|
||||
memcpy(lineptr + startX * 3, lineBuffer + startX * 3, (endX - startX) * 3);
|
||||
lineptr += framebuffer_width * 3; //move to next line
|
||||
#else
|
||||
JPEG_RGB* RGB_matrix = (JPEG_RGB*)lineBuffer;
|
||||
//loop row RGB888->ARGB8888 for required line part
|
||||
for (uint32_t counter = startX; counter < endX; counter++)
|
||||
{
|
||||
const uint32_t pix = (0xFF << 24) | (RGB_matrix[counter].R << 16) | (RGB_matrix[counter].G << 8) | RGB_matrix[counter].B;
|
||||
*(lineptr + counter) = pix;
|
||||
}
|
||||
lineptr += framebuffer_width; //move to next line
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SIMULATOR
|
||||
cinfo.output_scanline = cinfo.output_height;
|
||||
#endif
|
||||
|
||||
/* Step 6: Finish decompression */
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
|
||||
/* Step 7: Release JPEG decompression object */
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
void SoftwareMJPEGDecoder::decodeMJPEGFrame(const uint8_t* const, const uint32_t, uint8_t*, uint16_t, uint16_t, uint32_t)
|
||||
{
|
||||
}
|
||||
bool SoftwareMJPEGDecoder::decodeFrame(const touchgfx::Rect&, uint8_t*, uint32_t)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif // VIDEO_DECODE_FORMAT == RGB565 || VIDEO_DECODE_FORMAT == RGB888 || VIDEO_DECODE_FORMAT == ARGB888
|
||||
|
||||
bool SoftwareMJPEGDecoder::decodeThumbnail(uint32_t frameno, uint8_t* buffer, uint16_t width, uint16_t height)
|
||||
{
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoftwareMJPEGDecoder::gotoFrame(uint32_t frameNumber)
|
||||
{
|
||||
if (frameNumber == 0)
|
||||
{
|
||||
frameNumber = 1;
|
||||
}
|
||||
|
||||
if (frameNumber > getNumberOfFrames())
|
||||
{
|
||||
frameNumber = getNumberOfFrames();
|
||||
}
|
||||
|
||||
uint32_t offset = indexOffset + 8 + (frameNumber - 1) * 16;
|
||||
|
||||
readData(offset, 16);
|
||||
|
||||
currentMovieOffset = getU32(offset + 8) + firstFrameOffset - 4;
|
||||
this->frameNumber = frameNumber;
|
||||
}
|
||||
|
||||
uint32_t SoftwareMJPEGDecoder::getNumberOfFrames()
|
||||
{
|
||||
return videoInfo.number_of_frames;
|
||||
}
|
||||
|
||||
void SoftwareMJPEGDecoder::getVideoInfo(touchgfx::VideoInformation* data)
|
||||
{
|
||||
*data = videoInfo;
|
||||
// For unsupported decode formats, set video dimension to 0x0, to avoid drawing anything
|
||||
#if VIDEO_DECODE_FORMAT == RGB565 || VIDEO_DECODE_FORMAT == RGB888 || VIDEO_DECODE_FORMAT == ARGB8888
|
||||
#else
|
||||
data->frame_width = 0;
|
||||
data->frame_height = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//#ifndef SIMULATOR
|
||||
//link libjpeg file operation to fileinput namespace
|
||||
extern "C" {
|
||||
size_t jpeg_read_file(FILE* file, uint8_t* buf, uint32_t sizeofbuf)
|
||||
{
|
||||
assert(!"jpeg_read_file called with no implementation");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : SoftwareMJPEGDecoder.hpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef TOUCHGFX_SOFTWAREMJPEGDECODER_HPP
|
||||
#define TOUCHGFX_SOFTWAREMJPEGDECODER_HPP
|
||||
|
||||
#include <MJPEGDecoder.hpp>
|
||||
|
||||
class SoftwareMJPEGDecoder : public MJPEGDecoder
|
||||
{
|
||||
public:
|
||||
SoftwareMJPEGDecoder(uint8_t* linebuffer);
|
||||
|
||||
virtual void setVideoData(const uint8_t* movie, const uint32_t length);
|
||||
|
||||
virtual void setVideoData(touchgfx::VideoDataReader& reader);
|
||||
|
||||
virtual bool hasVideo();
|
||||
|
||||
virtual bool decodeNextFrame(uint8_t* frameBuffer, uint16_t width, uint16_t height, uint32_t framebuffer_width);
|
||||
|
||||
virtual bool gotoNextFrame();
|
||||
|
||||
virtual bool decodeFrame(const touchgfx::Rect& area, uint8_t* frameBuffer, uint32_t framebuffer_width);
|
||||
|
||||
virtual bool decodeThumbnail(uint32_t frameno, uint8_t* buffer, uint16_t width, uint16_t height);
|
||||
|
||||
virtual void gotoFrame(uint32_t frameno);
|
||||
|
||||
virtual uint32_t getCurrentFrameNumber() const
|
||||
{
|
||||
return frameNumber;
|
||||
}
|
||||
|
||||
virtual uint32_t getNumberOfFrames();
|
||||
|
||||
virtual void getVideoInfo(touchgfx::VideoInformation* data);
|
||||
|
||||
void setAVIFileBuffer(uint8_t* buffer, uint32_t size)
|
||||
{
|
||||
aviBuffer = buffer, aviBufferLength = size;
|
||||
}
|
||||
|
||||
virtual AVIErrors getLastError()
|
||||
{
|
||||
return lastError;
|
||||
}
|
||||
private:
|
||||
void readVideoHeader();
|
||||
void decodeMJPEGFrame(const uint8_t* const mjpgdata, const uint32_t length, uint8_t* buffer, uint16_t width, uint16_t height, uint32_t stride);
|
||||
int compare(const uint32_t offset, const char* str, uint32_t num);
|
||||
uint32_t getU32(const uint32_t offset);
|
||||
uint32_t getU16(const uint32_t offset);
|
||||
const uint8_t* readData(uint32_t offset, uint32_t length);
|
||||
|
||||
touchgfx::VideoInformation videoInfo;
|
||||
uint32_t frameNumber;
|
||||
uint32_t currentMovieOffset;
|
||||
uint32_t indexOffset;
|
||||
uint32_t firstFrameOffset;
|
||||
uint32_t lastFrameEnd;
|
||||
uint32_t movieLength;
|
||||
const uint8_t* movieData;
|
||||
touchgfx::VideoDataReader* reader;
|
||||
uint8_t* lineBuffer;
|
||||
uint8_t* aviBuffer;
|
||||
uint32_t aviBufferLength;
|
||||
uint32_t aviBufferStartOffset;
|
||||
AVIErrors lastError;
|
||||
};
|
||||
|
||||
#endif // TOUCHGFX_SOFTWAREMJPEGDECODER_HPP
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : TouchGFXConfiguration.cpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <texts/TypedTextDatabase.hpp>
|
||||
#include <fonts/ApplicationFontProvider.hpp>
|
||||
#include <gui/common/FrontendHeap.hpp>
|
||||
#include <BitmapDatabase.hpp>
|
||||
#include <touchgfx/VectorFontRendererImpl.hpp>
|
||||
#include <platform/driver/lcd/LCD16bpp.hpp>
|
||||
#include <STM32DMA.hpp>
|
||||
#include <TouchGFXHAL.hpp>
|
||||
#include <STM32TouchController.hpp>
|
||||
#include <stm32f7xx_hal.h>
|
||||
|
||||
extern "C" void touchgfx_init();
|
||||
extern "C" void touchgfx_taskEntry();
|
||||
extern "C" void touchgfx_components_init();
|
||||
|
||||
static STM32TouchController tc;
|
||||
static STM32DMA dma;
|
||||
static LCD16bpp display;
|
||||
static VectorFontRendererImpl vectorFontRenderer;
|
||||
|
||||
static ApplicationFontProvider fontProvider;
|
||||
static Texts texts;
|
||||
static TouchGFXHAL hal(dma, display, tc, 480, 272);
|
||||
|
||||
void touchgfx_init()
|
||||
{
|
||||
Bitmap::registerBitmapDatabase(BitmapDatabase::getInstance(), BitmapDatabase::getInstanceSize());
|
||||
TypedText::registerTexts(&texts);
|
||||
Texts::setLanguage(0);
|
||||
|
||||
display.setVectorFontRenderer(&vectorFontRenderer);
|
||||
|
||||
FontManager::setFontProvider(&fontProvider);
|
||||
|
||||
FrontendHeap& heap = FrontendHeap::getInstance();
|
||||
/*
|
||||
* we need to obtain the reference above to initialize the frontend heap.
|
||||
*/
|
||||
(void)heap;
|
||||
|
||||
/*
|
||||
* Initialize TouchGFX
|
||||
*/
|
||||
hal.initialize();
|
||||
}
|
||||
|
||||
void touchgfx_components_init()
|
||||
{
|
||||
}
|
||||
|
||||
void touchgfx_taskEntry()
|
||||
{
|
||||
/*
|
||||
* Main event loop. Will wait for VSYNC signal, and then process next frame. Call
|
||||
* this function from your GUI task.
|
||||
*
|
||||
* Note This function never returns
|
||||
*/
|
||||
hal.taskEntry();
|
||||
}
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : TouchGFXGeneratedHAL.cpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <TouchGFXGeneratedHAL.hpp>
|
||||
#include <touchgfx/hal/OSWrappers.hpp>
|
||||
#include <gui/common/FrontendHeap.hpp>
|
||||
#include <touchgfx/hal/GPIO.hpp>
|
||||
|
||||
#include <touchgfx/widgets/canvas/CWRVectorRenderer.hpp>
|
||||
|
||||
#include <DoubleBufferedVideoController.hpp>
|
||||
|
||||
#include <SoftwareMJPEGDecoder.hpp>
|
||||
uint32_t lineBuffer[480];
|
||||
|
||||
SoftwareMJPEGDecoder mjpegdecoder1((uint8_t*)lineBuffer);
|
||||
|
||||
namespace
|
||||
{
|
||||
LOCATION_PRAGMA_NOLOAD("Video_RGB_Buffer")
|
||||
uint32_t videoRGBBuffer[130560] LOCATION_ATTRIBUTE_NOLOAD("Video_RGB_Buffer");
|
||||
DoubleBufferedVideoController<1, 480, 272, 480 * 2U, Bitmap::RGB565> videoController;
|
||||
}
|
||||
|
||||
//Singleton Factory
|
||||
VideoController& VideoController::getInstance()
|
||||
{
|
||||
return videoController;
|
||||
}
|
||||
|
||||
namespace touchgfx
|
||||
{
|
||||
VectorRenderer* VectorRenderer::getInstance()
|
||||
{
|
||||
static CWRVectorRendererRGB565 renderer;
|
||||
|
||||
return &renderer;
|
||||
}
|
||||
} // namespace touchgfx
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
#include "stm32f7xx_hal_ltdc.h"
|
||||
|
||||
using namespace touchgfx;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Use the section "TouchGFX_Framebuffer" in the linker script to specify the placement of the buffer
|
||||
LOCATION_PRAGMA_NOLOAD("TouchGFX_Framebuffer")
|
||||
uint32_t frameBuf[(480 * 272 * 2 + 3) / 4 * 2] LOCATION_ATTRIBUTE_NOLOAD("TouchGFX_Framebuffer");
|
||||
static uint16_t lcd_int_active_line;
|
||||
static uint16_t lcd_int_porch_line;
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::initialize()
|
||||
{
|
||||
HAL::initialize();
|
||||
registerEventListener(*(Application::getInstance()));
|
||||
setFrameBufferStartAddresses((void*)frameBuf, (void*)(frameBuf + sizeof(frameBuf) / (sizeof(uint32_t) * 2)), (void*)0);
|
||||
|
||||
/*
|
||||
* Add software decoder to video controller
|
||||
*/
|
||||
videoController.addDecoder(mjpegdecoder1, 0);
|
||||
|
||||
videoController.setRGBBuffer((uint8_t*)videoRGBBuffer, sizeof(videoRGBBuffer));
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::configureInterrupts()
|
||||
{
|
||||
NVIC_SetPriority(DMA2D_IRQn, 9);
|
||||
NVIC_SetPriority(LTDC_IRQn, 9);
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::enableInterrupts()
|
||||
{
|
||||
NVIC_EnableIRQ(DMA2D_IRQn);
|
||||
NVIC_EnableIRQ(LTDC_IRQn);
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::disableInterrupts()
|
||||
{
|
||||
NVIC_DisableIRQ(DMA2D_IRQn);
|
||||
NVIC_DisableIRQ(LTDC_IRQn);
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::enableLCDControllerInterrupt()
|
||||
{
|
||||
lcd_int_active_line = (LTDC->BPCR & LTDC_BPCR_AVBP_Msk) - 1;
|
||||
lcd_int_porch_line = (LTDC->AWCR & LTDC_AWCR_AAH_Msk) - 1;
|
||||
|
||||
/* Sets the Line Interrupt position */
|
||||
LTDC->LIPCR = lcd_int_active_line;
|
||||
/* Line Interrupt Enable */
|
||||
LTDC->IER |= LTDC_IER_LIE;
|
||||
}
|
||||
|
||||
bool TouchGFXGeneratedHAL::beginFrame()
|
||||
{
|
||||
return HAL::beginFrame();
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::endFrame()
|
||||
{
|
||||
HAL::endFrame();
|
||||
videoController.endFrame();
|
||||
}
|
||||
|
||||
uint16_t* TouchGFXGeneratedHAL::getTFTFrameBuffer() const
|
||||
{
|
||||
return (uint16_t*)LTDC_Layer1->CFBAR;
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::setTFTFrameBuffer(uint16_t* adr)
|
||||
{
|
||||
LTDC_Layer1->CFBAR = (uint32_t)adr;
|
||||
|
||||
/* Reload immediate */
|
||||
LTDC->SRCR = (uint32_t)LTDC_SRCR_IMR;
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::flushFrameBuffer(const touchgfx::Rect& rect)
|
||||
{
|
||||
HAL::flushFrameBuffer(rect);
|
||||
}
|
||||
|
||||
bool TouchGFXGeneratedHAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes)
|
||||
{
|
||||
return HAL::blockCopy(dest, src, numBytes);
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::InvalidateCache()
|
||||
{
|
||||
// Because DMA2D access main memory directly, the DCache must be invalidated
|
||||
// becuase it could hold a wrong image of the framebuffer. That's done
|
||||
// using the function SCB_CleanInvalidateDCache(). Remember to enable
|
||||
// "CPU Cache" in the "System Core" settings for "Cortex M7" in CubeMX
|
||||
// in order for this function call to work.
|
||||
if (SCB->CCR & SCB_CCR_DC_Msk)
|
||||
{
|
||||
SCB_CleanInvalidateDCache();
|
||||
}
|
||||
}
|
||||
|
||||
void TouchGFXGeneratedHAL::FlushCache()
|
||||
{
|
||||
// If the framebuffer is placed in Write-Back cached memory (e.g. SRAM) then
|
||||
// the DCache must be flushed prior to DMA2D accessing it. That's done
|
||||
// using the function SCB_CleanInvalidateDCache(). Remember to enable
|
||||
// "CPU Cache" in the "System Core" settings for "Cortex M7" in CubeMX in
|
||||
// order for this function call to work.
|
||||
if (SCB->CCR & SCB_CCR_DC_Msk)
|
||||
{
|
||||
SCB_CleanInvalidateDCache();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void videoTaskFunc(void* argument)
|
||||
{
|
||||
videoController.decoderTaskEntry();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void HAL_LTDC_LineEventCallback(LTDC_HandleTypeDef* hltdc)
|
||||
{
|
||||
if (!HAL::getInstance())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (LTDC->LIPCR == lcd_int_active_line)
|
||||
{
|
||||
//entering active area
|
||||
HAL_LTDC_ProgramLineEvent(hltdc, lcd_int_porch_line);
|
||||
HAL::getInstance()->vSync();
|
||||
OSWrappers::signalVSync();
|
||||
|
||||
// Swap frame buffers immediately instead of waiting for the task to be scheduled in.
|
||||
// Note: task will also swap when it wakes up, but that operation is guarded and will not have
|
||||
// any effect if already swapped.
|
||||
HAL::getInstance()->swapFrameBuffers();
|
||||
GPIO::set(GPIO::VSYNC_FREQ);
|
||||
}
|
||||
else
|
||||
{
|
||||
//exiting active area
|
||||
HAL_LTDC_ProgramLineEvent(hltdc, lcd_int_active_line);
|
||||
|
||||
// Signal to the framework that display update has finished.
|
||||
HAL::getInstance()->frontPorchEntered();
|
||||
GPIO::clear(GPIO::VSYNC_FREQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : TouchGFXGeneratedHAL.hpp
|
||||
******************************************************************************
|
||||
* This file is generated by TouchGFX Generator 4.26.0. Please, do not edit!
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef TouchGFXGeneratedHAL_HPP
|
||||
#define TouchGFXGeneratedHAL_HPP
|
||||
|
||||
#include <touchgfx/hal/HAL.hpp>
|
||||
|
||||
/**
|
||||
* @class TouchGFXGeneratedHAL
|
||||
*
|
||||
* @brief HAL implementation for TouchGFXGenerated.
|
||||
*
|
||||
* @sa HAL
|
||||
*/
|
||||
class TouchGFXGeneratedHAL : public touchgfx::HAL
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @fn TouchGFXGeneratedHAL::TouchGFXGeneratedHAL(touchgfx::DMA_Interface& dma, touchgfx::LCD& display, touchgfx::TouchController& tc, uint16_t width, uint16_t height) : touchgfx::HAL(dma, display, tc, width, height)
|
||||
*
|
||||
* @brief Constructor.
|
||||
*
|
||||
* Constructor. Initializes members.
|
||||
*
|
||||
* @param [in,out] dma Reference to DMA interface.
|
||||
* @param [in,out] display Reference to LCD interface.
|
||||
* @param [in,out] tc Reference to Touch Controller driver.
|
||||
* @param width Width of the display.
|
||||
* @param height Height of the display.
|
||||
*/
|
||||
TouchGFXGeneratedHAL(touchgfx::DMA_Interface& dma, touchgfx::LCD& display, touchgfx::TouchController& tc, uint16_t width, uint16_t height) :
|
||||
touchgfx::HAL(dma, display, tc, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn void TouchGFXGeneratedHAL::initialize();
|
||||
*
|
||||
* @brief This function is responsible for initializing the entire framework.
|
||||
*
|
||||
* This function is responsible for initializing the entire framework.
|
||||
*/
|
||||
virtual void initialize();
|
||||
|
||||
/**
|
||||
* @fn virtual void TouchGFXGeneratedHAL::configureInterrupts();
|
||||
*
|
||||
* @brief Sets the DMA, LCD, and GPU2D (if enabled) interrupt priorities.
|
||||
*
|
||||
* Sets the DMA, LCD, and GPU2D (if enabled) interrupt priorities.
|
||||
*/
|
||||
virtual void configureInterrupts();
|
||||
|
||||
/**
|
||||
* @fn virtual void TouchGFXGeneratedHAL::enableInterrupts();
|
||||
*
|
||||
* @brief Enables the DMA, LCD, and GPU2D (if enabled) interrupts.
|
||||
*
|
||||
* Enables the DMA, LCD, and GPU2D (if enabled) interrupts.
|
||||
*/
|
||||
virtual void enableInterrupts();
|
||||
|
||||
/**
|
||||
* @fn virtual void TouchGFXGeneratedHAL::disableInterrupts();
|
||||
*
|
||||
* @brief Disables the DMA, LDC, and GPU2D (if enabled) interrupts.
|
||||
*
|
||||
* Disables the DMA, LDC, and GPU2D (if enabled) interrupts.
|
||||
*/
|
||||
virtual void disableInterrupts();
|
||||
|
||||
/**
|
||||
* @fn virtual void TouchGFXGeneratedHAL::enableLCDControllerInterrupt();
|
||||
*
|
||||
* @brief Configure the LCD controller to fire interrupts at VSYNC.
|
||||
*
|
||||
* Configure the LCD controller to fire interrupts at VSYNC. Called automatically
|
||||
* once TouchGFX initialization has completed.
|
||||
*/
|
||||
virtual void enableLCDControllerInterrupt();
|
||||
|
||||
/**
|
||||
* @fn virtual void TouchGFXGeneratedHAL::flushFrameBuffer();
|
||||
*
|
||||
* @brief This function is called whenever the framework has performed a complete draw.
|
||||
*
|
||||
* This specialization is only in place to keep compilers happy. Base impl. will call the
|
||||
* Rect version.
|
||||
* @see HAL::flushFrameBuffer
|
||||
*/
|
||||
virtual void flushFrameBuffer()
|
||||
{
|
||||
HAL::flushFrameBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn virtual void TouchGFXGeneratedHAL::flushFrameBuffer(const touchgfx::Rect& rect);
|
||||
*
|
||||
* @brief This function is called whenever the framework has performed a partial draw.
|
||||
*
|
||||
* This function is called whenever the framework has performed a partial draw.
|
||||
*
|
||||
* @param rect The area of the screen that has been drawn, expressed in absolute coordinates.
|
||||
*
|
||||
* @see flushFrameBuffer().
|
||||
*/
|
||||
virtual void flushFrameBuffer(const touchgfx::Rect& rect);
|
||||
|
||||
/**
|
||||
*
|
||||
* @fn virtual void TouchGFXGeneratedHAL::blockCopy();
|
||||
*
|
||||
* This function performs a platform-specific memcpy, if supported by the hardware.
|
||||
*
|
||||
* @param [out] dest Pointer to destination memory.
|
||||
* @param [in] src Pointer to source memory.
|
||||
* @param numBytes Number of bytes to copy.
|
||||
*
|
||||
* @return true if the copy succeeded, false if copy was not performed.
|
||||
*/
|
||||
virtual bool blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes);
|
||||
|
||||
/**
|
||||
* @fn virtual void TouchGFXGeneratedHAL::beginFrame();
|
||||
*
|
||||
* @brief Called when beginning to rendering a frame.
|
||||
*
|
||||
* Called when beginning to rendering a frame.
|
||||
*
|
||||
* @return true if rendering can begin, false otherwise.
|
||||
*/
|
||||
virtual bool beginFrame();
|
||||
|
||||
/**
|
||||
* @fn virtual void TouchGFXGeneratedHAL::endFrame();
|
||||
*
|
||||
* @brief Called when a rendering pass is completed.
|
||||
*
|
||||
* Called when a rendering pass is completed.
|
||||
*/
|
||||
virtual void endFrame();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @fn virtual uint16_t* TouchGFXGeneratedHAL::getTFTFrameBuffer() const;
|
||||
*
|
||||
* @brief Gets the frame buffer address used by the TFT controller.
|
||||
*
|
||||
* Gets the frame buffer address used by the TFT controller.
|
||||
*
|
||||
* @return The address of the frame buffer currently being displayed on the TFT.
|
||||
*/
|
||||
virtual uint16_t* getTFTFrameBuffer() const;
|
||||
|
||||
/**
|
||||
* @fn virtual void TouchGFXGeneratedHAL::setTFTFrameBuffer(uint16_t* adr);
|
||||
*
|
||||
* @brief Sets the frame buffer address used by the TFT controller.
|
||||
*
|
||||
* Sets the frame buffer address used by the TFT controller.
|
||||
*
|
||||
* @param [in,out] adr New frame buffer address.
|
||||
*/
|
||||
virtual void setTFTFrameBuffer(uint16_t* adr);
|
||||
|
||||
virtual void InvalidateCache();
|
||||
|
||||
virtual void FlushCache();
|
||||
|
||||
};
|
||||
#endif // TouchGFXGeneratedHAL_HPP
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
Reference in New Issue
Block a user