mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-15 02:48:11 +00:00
refactor(core): extract framebuffer queue for reuse
[no changelog]
This commit is contained in:
parent
791340cc6b
commit
dac6c17f73
99
core/embed/io/display/fb_queue/fb_queue.c
Normal file
99
core/embed/io/display/fb_queue/fb_queue.c
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#ifdef KERNEL_MODE
|
||||||
|
|
||||||
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include <sys/irq.h>
|
||||||
|
|
||||||
|
#include "fb_queue.h"
|
||||||
|
|
||||||
|
// Initializes the queue and make it empty
|
||||||
|
// Clear peeked flag
|
||||||
|
void fb_queue_reset(fb_queue_t* queue) {
|
||||||
|
memset(queue, 0, sizeof(fb_queue_t));
|
||||||
|
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||||
|
queue->entries[i].index = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inserts a new element to the tail of the queue
|
||||||
|
bool fb_queue_put(fb_queue_t* queue, int16_t index) {
|
||||||
|
irq_key_t irq_key = irq_lock();
|
||||||
|
|
||||||
|
// check if the queue is full
|
||||||
|
if (queue->entries[queue->wix].index != -1) {
|
||||||
|
irq_unlock(irq_key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue->entries[queue->wix].index = index;
|
||||||
|
queue->wix = (queue->wix + 1) % FRAME_BUFFER_COUNT;
|
||||||
|
|
||||||
|
irq_unlock(irq_key);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes an element from the queue head, returns -1 if the queue is empty
|
||||||
|
// Clear peeked flag
|
||||||
|
int16_t fb_queue_take(fb_queue_t* queue) {
|
||||||
|
irq_key_t irq_key = irq_lock();
|
||||||
|
|
||||||
|
if (queue->entries[queue->rix].index == -1) {
|
||||||
|
irq_unlock(irq_key);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue->peaked = false;
|
||||||
|
int16_t index = queue->entries[queue->rix].index;
|
||||||
|
queue->entries[queue->rix].index = -1;
|
||||||
|
queue->rix = (queue->rix + 1) % FRAME_BUFFER_COUNT;
|
||||||
|
|
||||||
|
irq_unlock(irq_key);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
// Returns true if the queue is empty
|
||||||
|
bool fb_queue_empty(fb_queue_t* queue) {
|
||||||
|
irq_key_t irq_key = irq_lock();
|
||||||
|
|
||||||
|
if (queue->entries[queue->rix].index == -1) {
|
||||||
|
irq_unlock(irq_key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_unlock(irq_key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Waits until the queue is not empty
|
||||||
|
void fb_queue_wait(fb_queue_t* queue) {
|
||||||
|
while (fb_queue_empty(queue))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the head of the queue (or -1 if the queue is empty)
|
||||||
|
// Set peeked flag if the queue is not empty
|
||||||
|
int16_t fb_queue_peek(fb_queue_t* queue) {
|
||||||
|
irq_key_t irq_key = irq_lock();
|
||||||
|
|
||||||
|
if (queue->entries[queue->rix].index == -1) {
|
||||||
|
irq_unlock(irq_key);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t index = queue->entries[queue->rix].index;
|
||||||
|
queue->peaked = true;
|
||||||
|
|
||||||
|
irq_unlock(irq_key);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if the head was already peeked
|
||||||
|
bool fb_queue_peeked(fb_queue_t* queue) {
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
bool peeked = queue->peaked;
|
||||||
|
irq_unlock(key);
|
||||||
|
return peeked;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
71
core/embed/io/display/fb_queue/fb_queue.h
Normal file
71
core/embed/io/display/fb_queue/fb_queue.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Trezor project, https://trezor.io/
|
||||||
|
*
|
||||||
|
* Copyright (c) SatoshiLabs
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
// Number of frame buffers used (1 or 2)
|
||||||
|
// If 1 buffer is selected, some animations may not
|
||||||
|
// be so smooth but the memory usage is lower.
|
||||||
|
#define FRAME_BUFFER_COUNT 2
|
||||||
|
|
||||||
|
// Each frame buffer can be in one of the following states:
|
||||||
|
typedef struct {
|
||||||
|
int16_t index;
|
||||||
|
} fb_queue_entry;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// Queue entries
|
||||||
|
fb_queue_entry entries[FRAME_BUFFER_COUNT];
|
||||||
|
|
||||||
|
// Read index
|
||||||
|
// (accessed & updated in the context of the interrupt handlers
|
||||||
|
uint8_t rix;
|
||||||
|
// Write index
|
||||||
|
// (accessed & updated in context of the main thread)
|
||||||
|
uint8_t wix;
|
||||||
|
|
||||||
|
// Flag indicating that the head of the queue has been peaked
|
||||||
|
bool peaked;
|
||||||
|
|
||||||
|
} fb_queue_t;
|
||||||
|
|
||||||
|
// Initializes the queue and make it empty
|
||||||
|
// Clear peeked flag
|
||||||
|
void fb_queue_reset(fb_queue_t* queue);
|
||||||
|
|
||||||
|
// Inserts a new element to the tail of the queue
|
||||||
|
bool fb_queue_put(fb_queue_t* queue, int16_t index);
|
||||||
|
|
||||||
|
// Removes an element from the queue head, returns -1 if the queue is empty
|
||||||
|
// Clear peeked flag
|
||||||
|
int16_t fb_queue_take(fb_queue_t* queue);
|
||||||
|
// Returns true if the queue is empty
|
||||||
|
bool fb_queue_empty(fb_queue_t* queue);
|
||||||
|
|
||||||
|
// Waits until the queue is not empty
|
||||||
|
void fb_queue_wait(fb_queue_t* queue);
|
||||||
|
|
||||||
|
// Returns the head of the queue (or -1 if the queue is empty)
|
||||||
|
// Set peeked flag if the queue is not empty
|
||||||
|
int16_t fb_queue_peek(fb_queue_t* queue);
|
||||||
|
|
||||||
|
// Return if the head was already peeked
|
||||||
|
bool fb_queue_peeked(fb_queue_t* queue);
|
@ -56,6 +56,10 @@ void display_init(display_content_mode_t mode) {
|
|||||||
|
|
||||||
memset(drv, 0, sizeof(display_driver_t));
|
memset(drv, 0, sizeof(display_driver_t));
|
||||||
|
|
||||||
|
#ifdef FRAMEBUFFER
|
||||||
|
display_fb_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mode == DISPLAY_RESET_CONTENT) {
|
if (mode == DISPLAY_RESET_CONTENT) {
|
||||||
display_io_init_gpio();
|
display_io_init_gpio();
|
||||||
display_io_init_fmc();
|
display_io_init_fmc();
|
||||||
@ -149,7 +153,7 @@ int display_set_orientation(int angle) {
|
|||||||
drv->orientation_angle = angle;
|
drv->orientation_angle = angle;
|
||||||
|
|
||||||
#ifdef FRAMEBUFFER
|
#ifdef FRAMEBUFFER
|
||||||
display_physical_fb_clear();
|
display_fb_clear();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
display_panel_set_window(0, 0, INTERNAL_FB_WIDTH - 1,
|
display_panel_set_window(0, 0, INTERNAL_FB_WIDTH - 1,
|
||||||
|
@ -63,7 +63,7 @@ _Static_assert(FRAME_BUFFER_COUNT == 1 || FRAME_BUFFER_COUNT == 2);
|
|||||||
PHYSICAL_FRAME_BUFFER_ALIGNMENT)
|
PHYSICAL_FRAME_BUFFER_ALIGNMENT)
|
||||||
|
|
||||||
// Physical frame buffers in internal SRAM memory.
|
// Physical frame buffers in internal SRAM memory.
|
||||||
// Both frame buffers layes in the fixed addresses that
|
// Both frame buffers layers in the fixed addresses that
|
||||||
// are shared between bootloaders and the firmware.
|
// are shared between bootloaders and the firmware.
|
||||||
static
|
static
|
||||||
__attribute__((section(".fb1"), aligned(PHYSICAL_FRAME_BUFFER_ALIGNMENT)))
|
__attribute__((section(".fb1"), aligned(PHYSICAL_FRAME_BUFFER_ALIGNMENT)))
|
||||||
@ -91,6 +91,21 @@ void display_set_unpriv_access(bool unpriv) {
|
|||||||
}
|
}
|
||||||
#endif // USE_TRUSTZONE
|
#endif // USE_TRUSTZONE
|
||||||
|
|
||||||
|
void display_fb_init(void) {
|
||||||
|
display_driver_t *drv = &g_display_driver;
|
||||||
|
|
||||||
|
if (drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fb_queue_reset(&drv->empty_frames);
|
||||||
|
fb_queue_reset(&drv->ready_frames);
|
||||||
|
|
||||||
|
for (int16_t i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||||
|
fb_queue_put(&drv->empty_frames, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the pointer to the physical frame buffer (0.. FRAME_BUFFER_COUNT-1)
|
// Returns the pointer to the physical frame buffer (0.. FRAME_BUFFER_COUNT-1)
|
||||||
// Returns NULL if the framebuffer index is out of range.
|
// Returns NULL if the framebuffer index is out of range.
|
||||||
static uint8_t *get_fb_ptr(uint32_t index) {
|
static uint8_t *get_fb_ptr(uint32_t index) {
|
||||||
@ -105,7 +120,7 @@ static uint8_t *get_fb_ptr(uint32_t index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_physical_fb_clear(void) {
|
void display_fb_clear(void) {
|
||||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||||
mpu_set_active_fb(get_fb_ptr(i), PHYSICAL_FRAME_BUFFER_SIZE);
|
mpu_set_active_fb(get_fb_ptr(i), PHYSICAL_FRAME_BUFFER_SIZE);
|
||||||
memset(get_fb_ptr(i), 0, PHYSICAL_FRAME_BUFFER_SIZE);
|
memset(get_fb_ptr(i), 0, PHYSICAL_FRAME_BUFFER_SIZE);
|
||||||
@ -120,13 +135,7 @@ void display_physical_fb_clear(void) {
|
|||||||
static void bg_copy_callback(void) {
|
static void bg_copy_callback(void) {
|
||||||
display_driver_t *drv = &g_display_driver;
|
display_driver_t *drv = &g_display_driver;
|
||||||
|
|
||||||
if (drv->queue.rix >= FRAME_BUFFER_COUNT) {
|
fb_queue_put(&drv->empty_frames, fb_queue_take(&drv->ready_frames));
|
||||||
// This is an invalid state and we should never get here
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
drv->queue.entry[drv->queue.rix] = FB_STATE_EMPTY;
|
|
||||||
drv->queue.rix = (drv->queue.rix + 1) % FRAME_BUFFER_COUNT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interrupt routing handling TE signal
|
// Interrupt routing handling TE signal
|
||||||
@ -135,37 +144,15 @@ static void display_te_interrupt_handler(void) {
|
|||||||
|
|
||||||
__HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN);
|
__HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN);
|
||||||
|
|
||||||
if (drv->queue.rix >= FRAME_BUFFER_COUNT) {
|
if (!fb_queue_peeked(&drv->ready_frames)) {
|
||||||
// This is an invalid state and we should never get here
|
int16_t fb_idx = fb_queue_peek(&drv->ready_frames);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (drv->queue.entry[drv->queue.rix]) {
|
if (fb_idx >= 0) {
|
||||||
case FB_STATE_EMPTY:
|
|
||||||
case FB_STATE_PREPARING:
|
|
||||||
// No new frame queued
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FB_STATE_COPYING:
|
|
||||||
// Currently we are copying a data to the display.
|
|
||||||
// We need to wait for the next TE interrupt.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FB_STATE_READY:
|
|
||||||
// Now it's proper time to copy the data to the display
|
|
||||||
drv->queue.entry[drv->queue.rix] = FB_STATE_COPYING;
|
|
||||||
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
||||||
bg_copy_start_const_out_8(get_fb_ptr(drv->queue.rix),
|
bg_copy_start_const_out_8(get_fb_ptr(fb_idx),
|
||||||
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
||||||
PHYSICAL_FRAME_BUFFER_SIZE, bg_copy_callback);
|
PHYSICAL_FRAME_BUFFER_SIZE, bg_copy_callback);
|
||||||
|
}
|
||||||
// NOTE: when copying is done, this queue slot is marked empty
|
|
||||||
// (see bg_copy_callback())
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// This is an invalid state and we should never get here
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,17 +174,10 @@ bool display_get_frame_buffer(display_fb_info_t *fb) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame_buffer_state_t state;
|
fb_queue_wait(&drv->empty_frames);
|
||||||
|
uint8_t fb_idx = fb_queue_peek(&drv->empty_frames);
|
||||||
|
|
||||||
// We have to wait if the buffer was passed for copying
|
fb->ptr = get_fb_ptr(fb_idx);
|
||||||
// to the interrupt handler
|
|
||||||
do {
|
|
||||||
state = drv->queue.entry[drv->queue.wix];
|
|
||||||
} while (state == FB_STATE_READY || state == FB_STATE_COPYING);
|
|
||||||
|
|
||||||
drv->queue.entry[drv->queue.wix] = FB_STATE_PREPARING;
|
|
||||||
|
|
||||||
fb->ptr = get_fb_ptr(drv->queue.wix);
|
|
||||||
fb->stride = DISPLAY_RESX * sizeof(uint16_t);
|
fb->stride = DISPLAY_RESX * sizeof(uint16_t);
|
||||||
// Enable access to the frame buffer from the unprivileged code
|
// Enable access to the frame buffer from the unprivileged code
|
||||||
mpu_set_active_fb(fb->ptr, PHYSICAL_FRAME_BUFFER_SIZE);
|
mpu_set_active_fb(fb->ptr, PHYSICAL_FRAME_BUFFER_SIZE);
|
||||||
@ -205,6 +185,7 @@ bool display_get_frame_buffer(display_fb_info_t *fb) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BOARDLOADER
|
||||||
// Copies the frame buffer with the given index to the display
|
// Copies the frame buffer with the given index to the display
|
||||||
static void copy_fb_to_display(uint8_t index) {
|
static void copy_fb_to_display(uint8_t index) {
|
||||||
uint16_t *fb = (uint16_t *)get_fb_ptr(index);
|
uint16_t *fb = (uint16_t *)get_fb_ptr(index);
|
||||||
@ -228,6 +209,7 @@ static void wait_for_te_signal(void) {
|
|||||||
while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) {
|
while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void display_refresh(void) {
|
void display_refresh(void) {
|
||||||
display_driver_t *drv = &g_display_driver;
|
display_driver_t *drv = &g_display_driver;
|
||||||
@ -236,7 +218,7 @@ void display_refresh(void) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drv->queue.entry[drv->queue.wix] != FB_STATE_PREPARING) {
|
if (!fb_queue_peeked(&drv->empty_frames)) {
|
||||||
// No refresh needed as the frame buffer is not in
|
// No refresh needed as the frame buffer is not in
|
||||||
// the state to be copied to the display
|
// the state to be copied to the display
|
||||||
return;
|
return;
|
||||||
@ -246,35 +228,16 @@ void display_refresh(void) {
|
|||||||
mpu_set_active_fb(NULL, 0);
|
mpu_set_active_fb(NULL, 0);
|
||||||
|
|
||||||
#ifndef BOARDLOADER
|
#ifndef BOARDLOADER
|
||||||
if (is_mode_exception()) {
|
// Mark the buffer ready to switch to
|
||||||
// Disable scheduling of any new background copying
|
fb_queue_put(&drv->ready_frames, fb_queue_take(&drv->empty_frames));
|
||||||
NVIC_DisableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
|
||||||
// Wait for next TE signal. During this time the
|
|
||||||
// display might be updated in the background
|
|
||||||
wait_for_te_signal();
|
|
||||||
// Stop any background copying even if it is not finished yet
|
|
||||||
bg_copy_abort();
|
|
||||||
// Copy the frame buffer to the display manually
|
|
||||||
copy_fb_to_display(drv->queue.wix);
|
|
||||||
// Reset the buffer queue so we can eventually continue
|
|
||||||
// safely in thread mode
|
|
||||||
drv->queue.wix = 0;
|
|
||||||
drv->queue.rix = 0;
|
|
||||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
|
||||||
drv->queue.entry[i] = FB_STATE_EMPTY;
|
|
||||||
}
|
|
||||||
// Enable normal processing again
|
|
||||||
NVIC_EnableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
|
||||||
} else {
|
|
||||||
// Mark the buffer ready to switch to
|
|
||||||
drv->queue.entry[drv->queue.wix] = FB_STATE_READY;
|
|
||||||
drv->queue.wix = (drv->queue.wix + 1) % FRAME_BUFFER_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // BOARDLOADER
|
#else // BOARDLOADER
|
||||||
wait_for_te_signal();
|
wait_for_te_signal();
|
||||||
copy_fb_to_display(drv->queue.wix);
|
int16_t fb_idx = fb_queue_take(&drv->empty_frames);
|
||||||
drv->queue.entry[drv->queue.wix] = FB_STATE_EMPTY;
|
if (fb_idx >= 0) {
|
||||||
|
copy_fb_to_display(fb_idx);
|
||||||
|
fb_queue_put(&drv->empty_frames, fb_idx);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,14 +256,7 @@ void display_ensure_refreshed(void) {
|
|||||||
// so we can be sure there's not scheduled or pending
|
// so we can be sure there's not scheduled or pending
|
||||||
// background copying
|
// background copying
|
||||||
do {
|
do {
|
||||||
copy_pending = false;
|
copy_pending = !fb_queue_empty(&drv->ready_frames);
|
||||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
|
||||||
frame_buffer_state_t state = drv->queue.entry[i];
|
|
||||||
if (state == FB_STATE_READY || state == FB_STATE_COPYING) {
|
|
||||||
copy_pending = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
__WFI();
|
__WFI();
|
||||||
} while (copy_pending);
|
} while (copy_pending);
|
||||||
|
|
||||||
|
@ -24,8 +24,10 @@
|
|||||||
|
|
||||||
#ifdef FRAMEBUFFER
|
#ifdef FRAMEBUFFER
|
||||||
|
|
||||||
|
void display_fb_init(void);
|
||||||
|
|
||||||
// Clears both physical frame buffers
|
// Clears both physical frame buffers
|
||||||
void display_physical_fb_clear(void);
|
void display_fb_clear(void);
|
||||||
|
|
||||||
void display_ensure_refreshed(void);
|
void display_ensure_refreshed(void);
|
||||||
|
|
||||||
|
@ -6,37 +6,7 @@
|
|||||||
|
|
||||||
#ifdef FRAMEBUFFER
|
#ifdef FRAMEBUFFER
|
||||||
|
|
||||||
// Number of frame buffers used (1 or 2)
|
#include "../fb_queue/fb_queue.h"
|
||||||
// If 1 buffer is selected, some animations may not
|
|
||||||
// be so smooth but the memory usage is lower.
|
|
||||||
#define FRAME_BUFFER_COUNT 2
|
|
||||||
|
|
||||||
// Each frame buffer can be in one of the following states:
|
|
||||||
typedef enum {
|
|
||||||
// The frame buffer is empty and can be written to
|
|
||||||
FB_STATE_EMPTY = 0,
|
|
||||||
// The frame buffer pass passed to application
|
|
||||||
FB_STATE_PREPARING = 1,
|
|
||||||
// The frame buffer was written to and is ready
|
|
||||||
// to be copied to the display
|
|
||||||
FB_STATE_READY = 2,
|
|
||||||
// The frame buffer is currently being copied to
|
|
||||||
// the display
|
|
||||||
FB_STATE_COPYING = 3,
|
|
||||||
|
|
||||||
} frame_buffer_state_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
// Queue entries
|
|
||||||
volatile frame_buffer_state_t entry[FRAME_BUFFER_COUNT];
|
|
||||||
// Read index
|
|
||||||
// (accessed & updated in the context of the interrupt handlers
|
|
||||||
uint8_t rix;
|
|
||||||
// Write index
|
|
||||||
// (accessed & updated in context of the main thread)
|
|
||||||
uint8_t wix;
|
|
||||||
|
|
||||||
} frame_buffer_queue_t;
|
|
||||||
|
|
||||||
#endif // FRAMEBUFFER
|
#endif // FRAMEBUFFER
|
||||||
|
|
||||||
@ -49,7 +19,8 @@ typedef struct {
|
|||||||
// Framebuffer queue
|
// Framebuffer queue
|
||||||
// (accessed & updated in the context of the main thread
|
// (accessed & updated in the context of the main thread
|
||||||
// and the interrupt context)
|
// and the interrupt context)
|
||||||
volatile frame_buffer_queue_t queue;
|
fb_queue_t empty_frames;
|
||||||
|
fb_queue_t ready_frames;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Current display orientation (0, 90, 180, 270)
|
// Current display orientation (0, 90, 180, 270)
|
||||||
|
@ -47,6 +47,7 @@ def configure(
|
|||||||
sources += ["embed/io/display/st-7789/display_io.c"]
|
sources += ["embed/io/display/st-7789/display_io.c"]
|
||||||
sources += ["embed/io/display/st-7789/display_panel.c"]
|
sources += ["embed/io/display/st-7789/display_panel.c"]
|
||||||
sources += ["embed/io/display/st-7789/panels/lx154a2482.c"]
|
sources += ["embed/io/display/st-7789/panels/lx154a2482.c"]
|
||||||
|
sources += ["embed/io/display/fb_queue/fb_queue.c"]
|
||||||
paths += ["embed/io/display/inc"]
|
paths += ["embed/io/display/inc"]
|
||||||
|
|
||||||
sources += ["embed/io/display/backlight/stm32/backlight_pwm.c"]
|
sources += ["embed/io/display/backlight/stm32/backlight_pwm.c"]
|
||||||
|
Loading…
Reference in New Issue
Block a user