more refactor
This commit is contained in:
Generated
+1
@@ -964,6 +964,7 @@ dependencies = [
|
|||||||
"mipidsi",
|
"mipidsi",
|
||||||
"smart-leds",
|
"smart-leds",
|
||||||
"smart-leds-trait",
|
"smart-leds-trait",
|
||||||
|
"static_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
+2
-1
@@ -18,10 +18,11 @@ esp-rtos = { version = "0.4", features = ["embassy", "esp32c6"] }
|
|||||||
mipidsi = "0.10"
|
mipidsi = "0.10"
|
||||||
smart-leds = "0.4"
|
smart-leds = "0.4"
|
||||||
smart-leds-trait = "0.3"
|
smart-leds-trait = "0.3"
|
||||||
|
static_cell = { version = "2.1.1" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
incremental = false
|
incremental = false
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|||||||
+10
-5
@@ -5,6 +5,7 @@ use esp_hal::ledc::timer::{self, Timer, TimerIFace};
|
|||||||
use esp_hal::ledc::{LSGlobalClkSource, Ledc, LowSpeed};
|
use esp_hal::ledc::{LSGlobalClkSource, Ledc, LowSpeed};
|
||||||
use esp_hal::peripherals::LEDC;
|
use esp_hal::peripherals::LEDC;
|
||||||
use esp_hal::time::Rate;
|
use esp_hal::time::Rate;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
/// PWM frequency for the backlight (Hz).
|
/// PWM frequency for the backlight (Hz).
|
||||||
const BACKLIGHT_FREQ: Rate = Rate::from_khz(5);
|
const BACKLIGHT_FREQ: Rate = Rate::from_khz(5);
|
||||||
@@ -13,11 +14,13 @@ const BACKLIGHT_FREQ: Rate = Rate::from_khz(5);
|
|||||||
/// running the display above 50% brightness, so `level = 1.0` maps to this.
|
/// running the display above 50% brightness, so `level = 1.0` maps to this.
|
||||||
const MAX_DUTY_PCT: u8 = 50;
|
const MAX_DUTY_PCT: u8 = 50;
|
||||||
|
|
||||||
|
static BACKLIGHT_TIMER: StaticCell<(Ledc, Timer<'static, LowSpeed>)> = StaticCell::new();
|
||||||
|
|
||||||
/// Create and configure the LEDC timer used by the backlight.
|
/// Create and configure the LEDC timer used by the backlight.
|
||||||
///
|
///
|
||||||
/// Returns the `Ledc` and `Timer` so the caller can build a [`LcdBacklight`]
|
/// Returns the `Ledc` and `Timer` so the caller can build a [`Backlight`]
|
||||||
/// that borrows the timer.
|
/// that borrows the timer.
|
||||||
pub fn setup_timer(
|
fn setup_timer(
|
||||||
ledc: LEDC<'static>,
|
ledc: LEDC<'static>,
|
||||||
) -> Result<(Ledc<'static>, Timer<'static, LowSpeed>), timer::Error> {
|
) -> Result<(Ledc<'static>, Timer<'static, LowSpeed>), timer::Error> {
|
||||||
let mut ledc = Ledc::new(ledc);
|
let mut ledc = Ledc::new(ledc);
|
||||||
@@ -37,15 +40,17 @@ pub type Error = channel::Error;
|
|||||||
|
|
||||||
/// Backlight PWM driver. The channel holds a reference to the caller-owned
|
/// Backlight PWM driver. The channel holds a reference to the caller-owned
|
||||||
/// timer, so this type's lifetime is bounded by the timer's lifetime.
|
/// timer, so this type's lifetime is bounded by the timer's lifetime.
|
||||||
pub struct LcdBacklight<'a> {
|
pub struct Backlight<'a> {
|
||||||
channel: Channel<'a, LowSpeed>,
|
channel: Channel<'a, LowSpeed>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LcdBacklight<'a> {
|
impl<'a> Backlight<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
timer: &'a dyn TimerIFace<LowSpeed>,
|
ledc: LEDC<'static>,
|
||||||
pin: impl PeripheralOutput<'a>,
|
pin: impl PeripheralOutput<'a>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
|
let (_ledc, timer) = BACKLIGHT_TIMER.init(setup_timer(ledc)
|
||||||
|
.expect("Failed to init backlight timer"));
|
||||||
let mut channel = Channel::new(channel::Number::Channel0, pin);
|
let mut channel = Channel::new(channel::Number::Channel0, pin);
|
||||||
channel.configure(channel::config::Config {
|
channel.configure(channel::config::Config {
|
||||||
timer,
|
timer,
|
||||||
|
|||||||
+77
-76
@@ -9,6 +9,7 @@ use mipidsi::interface::SpiInterface;
|
|||||||
use mipidsi::models::ST7789;
|
use mipidsi::models::ST7789;
|
||||||
use mipidsi::options::{ColorInversion, ColorOrder, Orientation, Rotation};
|
use mipidsi::options::{ColorInversion, ColorOrder, Orientation, Rotation};
|
||||||
use mipidsi::Builder;
|
use mipidsi::Builder;
|
||||||
|
use crate::framebuffer::Framebuffer;
|
||||||
|
|
||||||
pub const WIDTH: u32 = 172;
|
pub const WIDTH: u32 = 172;
|
||||||
pub const HEIGHT: u32 = 320;
|
pub const HEIGHT: u32 = 320;
|
||||||
@@ -72,93 +73,93 @@ pub struct LcdPins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The raw LCD resources, ready to drive the async framebuffer path.
|
/// The raw LCD resources, ready to drive the async framebuffer path.
|
||||||
pub struct LcdParts {
|
pub struct Display {
|
||||||
pub spi: SpiDmaBus,
|
pub spi: SpiDmaBus,
|
||||||
pub cs: Output<'static>,
|
pub cs: Output<'static>,
|
||||||
pub dc: Output<'static>,
|
pub dc: Output<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_parts(pins: LcdPins) -> LcdParts {
|
impl Display {
|
||||||
let mut buffer = [0u8; 4096];
|
pub fn new(pins: LcdPins) -> Self {
|
||||||
let dma_tx_buf = esp_hal::dma_tx_buffer!(DMA_BUF_SIZE).expect("Failed to create DMA TX buffer");
|
let mut buffer = [0u8; 4096];
|
||||||
let dma_rx_buf = esp_hal::dma_rx_buffer!(4).expect("Failed to create DMA RX buffer");
|
let dma_tx_buf = esp_hal::dma_tx_buffer!(DMA_BUF_SIZE).expect("Failed to create DMA TX buffer");
|
||||||
|
let dma_rx_buf = esp_hal::dma_rx_buffer!(4).expect("Failed to create DMA RX buffer");
|
||||||
|
|
||||||
let spi = Spi::new(
|
let spi = Spi::new(
|
||||||
pins.spi2,
|
pins.spi2,
|
||||||
SpiConfig::default().with_frequency(SPI_FREQ).with_mode(Mode::_0),
|
SpiConfig::default().with_frequency(SPI_FREQ).with_mode(Mode::_0),
|
||||||
)
|
)
|
||||||
.expect("Failed to configure SPI")
|
.expect("Failed to configure SPI")
|
||||||
.with_sck(pins.sck)
|
.with_sck(pins.sck)
|
||||||
.with_mosi(pins.mosi)
|
.with_mosi(pins.mosi)
|
||||||
.with_dma(pins.dma_ch)
|
.with_dma(pins.dma_ch)
|
||||||
.with_buffers(dma_rx_buf, dma_tx_buf)
|
.with_buffers(dma_rx_buf, dma_tx_buf)
|
||||||
.into_async();
|
.into_async();
|
||||||
|
|
||||||
let cs_pin = Output::new(pins.cs, Level::High, OutputConfig::default());
|
let cs_pin = Output::new(pins.cs, Level::High, OutputConfig::default());
|
||||||
let device = LcdDevice {
|
let device = LcdDevice {
|
||||||
spi,
|
spi,
|
||||||
cs: cs_pin,
|
cs: cs_pin,
|
||||||
};
|
};
|
||||||
|
|
||||||
let dc_pin = Output::new(pins.dc, Level::Low, OutputConfig::default());
|
let dc_pin = Output::new(pins.dc, Level::Low, OutputConfig::default());
|
||||||
let interface = SpiInterface::new(device, dc_pin, &mut buffer);
|
let interface = SpiInterface::new(device, dc_pin, &mut buffer);
|
||||||
|
|
||||||
let mut rst_pin = Output::new(pins.rst, Level::High, OutputConfig::default());
|
let mut rst_pin = Output::new(pins.rst, Level::High, OutputConfig::default());
|
||||||
rst_pin.set_high();
|
rst_pin.set_high();
|
||||||
|
|
||||||
let mut delay = esp_hal::delay::Delay::new();
|
let mut delay = esp_hal::delay::Delay::new();
|
||||||
|
|
||||||
let lcd = Builder::new(ST7789, interface)
|
let lcd = Builder::new(ST7789, interface)
|
||||||
.display_size(WIDTH as u16, HEIGHT as u16)
|
.display_size(WIDTH as u16, HEIGHT as u16)
|
||||||
.display_offset(COLUMN_OFFSET, 0)
|
.display_offset(COLUMN_OFFSET, 0)
|
||||||
.orientation(Orientation::new().rotate(Rotation::Deg0))
|
.orientation(Orientation::new().rotate(Rotation::Deg0))
|
||||||
.color_order(ColorOrder::Rgb)
|
.color_order(ColorOrder::Rgb)
|
||||||
.invert_colors(ColorInversion::Inverted)
|
.invert_colors(ColorInversion::Inverted)
|
||||||
.reset_pin(rst_pin)
|
.reset_pin(rst_pin)
|
||||||
.init(&mut delay)
|
.init(&mut delay)
|
||||||
.expect("Failed to init display");
|
.expect("Failed to init display");
|
||||||
|
|
||||||
let (interface, _model, _rst) = lcd.release();
|
let (interface, _model, _rst) = lcd.release();
|
||||||
let (device, dc) = interface.release();
|
let (device, dc) = interface.release();
|
||||||
let (spi, cs) = device.into_inner();
|
let (spi, cs) = device.into_inner();
|
||||||
|
|
||||||
LcdParts { spi, cs, dc }
|
Display { spi, cs, dc }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stream a full framebuffer to the panel asynchronously (DMA).
|
||||||
|
///
|
||||||
|
/// Sends the address window and RAMWR command, then the pixel bytes as one
|
||||||
|
/// (internally chunked) DMA transfer. Returns when the transfer completes.
|
||||||
|
pub async fn send_framebuffer(
|
||||||
|
&mut self,
|
||||||
|
framebuffer: &Framebuffer,
|
||||||
|
) -> Result<(), SpiError> {
|
||||||
|
let x0 = COLUMN_OFFSET;
|
||||||
|
let x1 = COLUMN_OFFSET + WIDTH as u16 - 1;
|
||||||
|
let y1 = HEIGHT as u16 - 1;
|
||||||
|
|
||||||
|
self.cs.set_low();
|
||||||
|
|
||||||
|
// MIPI DCS: DC low for the command byte, high for its arguments.
|
||||||
|
self.dc.set_low();
|
||||||
|
self.spi.write_async(&[0x2A]).await?;
|
||||||
|
self.dc.set_high();
|
||||||
|
self.spi.write_async(&[(x0 >> 8) as u8, x0 as u8, (x1 >> 8) as u8, x1 as u8])
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.dc.set_low();
|
||||||
|
self.spi.write_async(&[0x2B]).await?;
|
||||||
|
self.dc.set_high();
|
||||||
|
self.spi.write_async(&[0, 0, (y1 >> 8) as u8, y1 as u8]).await?;
|
||||||
|
|
||||||
|
self.dc.set_low();
|
||||||
|
self.spi.write_async(&[0x2C]).await?;
|
||||||
|
self.dc.set_high();
|
||||||
|
|
||||||
|
self.spi.write_async(framebuffer.as_bytes()).await?;
|
||||||
|
self.cs.set_high();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stream a full framebuffer to the panel asynchronously (DMA).
|
|
||||||
///
|
|
||||||
/// Sends the address window and RAMWR command, then the pixel bytes as one
|
|
||||||
/// (internally chunked) DMA transfer. Returns when the transfer completes.
|
|
||||||
pub async fn send_framebuffer(
|
|
||||||
spi: &mut SpiDmaBus,
|
|
||||||
dc: &mut Output<'static>,
|
|
||||||
cs: &mut Output<'static>,
|
|
||||||
bytes: &[u8],
|
|
||||||
) -> Result<(), SpiError> {
|
|
||||||
let x0 = COLUMN_OFFSET;
|
|
||||||
let x1 = COLUMN_OFFSET + WIDTH as u16 - 1;
|
|
||||||
let y1 = HEIGHT as u16 - 1;
|
|
||||||
|
|
||||||
cs.set_low();
|
|
||||||
|
|
||||||
// MIPI DCS: DC low for the command byte, high for its arguments.
|
|
||||||
dc.set_low();
|
|
||||||
spi.write_async(&[0x2A]).await?;
|
|
||||||
dc.set_high();
|
|
||||||
spi.write_async(&[(x0 >> 8) as u8, x0 as u8, (x1 >> 8) as u8, x1 as u8])
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
dc.set_low();
|
|
||||||
spi.write_async(&[0x2B]).await?;
|
|
||||||
dc.set_high();
|
|
||||||
spi.write_async(&[0, 0, (y1 >> 8) as u8, y1 as u8]).await?;
|
|
||||||
|
|
||||||
dc.set_low();
|
|
||||||
spi.write_async(&[0x2C]).await?;
|
|
||||||
dc.set_high();
|
|
||||||
|
|
||||||
spi.write_async(bytes).await?;
|
|
||||||
cs.set_high();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
+8
-5
@@ -1,9 +1,10 @@
|
|||||||
use esp_hal::gpio::interconnect::PeripheralOutput;
|
use esp_hal::gpio::interconnect::PeripheralOutput;
|
||||||
use esp_hal::rmt::TxChannelCreator;
|
use esp_hal::peripherals::RMT;
|
||||||
|
use esp_hal::rmt::Rmt;
|
||||||
use esp_hal::time::Rate;
|
use esp_hal::time::Rate;
|
||||||
use esp_hal::Blocking;
|
use esp_hal::Blocking;
|
||||||
use esp_hal_smartled::{RmtSmartLeds, WS2812_TIMING, buffer_size, color_order};
|
use esp_hal_smartled::{buffer_size, color_order, RmtSmartLeds, WS2812_TIMING};
|
||||||
use smart_leds_trait::{RGB8, SmartLedsWrite};
|
use smart_leds_trait::{SmartLedsWrite, RGB8};
|
||||||
|
|
||||||
const RMT_FREQ: Rate = Rate::from_mhz(80);
|
const RMT_FREQ: Rate = Rate::from_mhz(80);
|
||||||
|
|
||||||
@@ -15,11 +16,13 @@ pub struct RgbLed {
|
|||||||
|
|
||||||
impl RgbLed {
|
impl RgbLed {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
channel: impl TxChannelCreator<'static, Blocking>,
|
rmt_periph: RMT<'static>,
|
||||||
pin: impl PeripheralOutput<'static>,
|
pin: impl PeripheralOutput<'static>,
|
||||||
) -> Result<Self, esp_hal_smartled::Error> {
|
) -> Result<Self, esp_hal_smartled::Error> {
|
||||||
|
let rmt = Rmt::new(rmt_periph, Rate::from_mhz(80))
|
||||||
|
.expect("Failed to init RMT");
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
led: SmartLed::new_with_memsize(WS2812_TIMING, channel, pin, 2, RMT_FREQ)?,
|
led: SmartLed::new_with_memsize(WS2812_TIMING, rmt.channel0, pin, 2, RMT_FREQ)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-16
@@ -5,7 +5,6 @@ mod backlight;
|
|||||||
mod display;
|
mod display;
|
||||||
mod framebuffer;
|
mod framebuffer;
|
||||||
mod led;
|
mod led;
|
||||||
pub mod board;
|
|
||||||
|
|
||||||
use embedded_graphics::pixelcolor::Rgb565;
|
use embedded_graphics::pixelcolor::Rgb565;
|
||||||
use embedded_graphics::prelude::*;
|
use embedded_graphics::prelude::*;
|
||||||
@@ -15,9 +14,11 @@ use embassy_time::{Instant, Timer};
|
|||||||
use esp_backtrace as _;
|
use esp_backtrace as _;
|
||||||
use esp_bootloader_esp_idf::esp_app_desc;
|
use esp_bootloader_esp_idf::esp_app_desc;
|
||||||
use esp_hal::timer::timg::TimerGroup;
|
use esp_hal::timer::timg::TimerGroup;
|
||||||
use esp_hal::{init, rmt::Rmt, time::Rate, Config};
|
use esp_hal::{init, Config};
|
||||||
|
use esp_hal::peripherals::{GPIO22, LEDC};
|
||||||
use esp_println::println;
|
use esp_println::println;
|
||||||
use smart_leds::hsv::{hsv2rgb, Hsv};
|
use smart_leds::hsv::{hsv2rgb, Hsv};
|
||||||
|
use crate::display::Display;
|
||||||
|
|
||||||
const SAT: u8 = 255;
|
const SAT: u8 = 255;
|
||||||
const VAL: u8 = 255;
|
const VAL: u8 = 255;
|
||||||
@@ -29,8 +30,7 @@ async fn led_task(
|
|||||||
rmt_periph: esp_hal::peripherals::RMT<'static>,
|
rmt_periph: esp_hal::peripherals::RMT<'static>,
|
||||||
led_pin: esp_hal::peripherals::GPIO8<'static>,
|
led_pin: esp_hal::peripherals::GPIO8<'static>,
|
||||||
) {
|
) {
|
||||||
let rmt = Rmt::new(rmt_periph, Rate::from_mhz(80)).expect("Failed to init RMT");
|
let mut led = led::RgbLed::new(rmt_periph, led_pin).expect("Failed to init RGB LED");
|
||||||
let mut led = led::RgbLed::new(rmt.channel0, led_pin).expect("Failed to init RGB LED");
|
|
||||||
|
|
||||||
let mut frame = 0u32;
|
let mut frame = 0u32;
|
||||||
loop {
|
loop {
|
||||||
@@ -49,16 +49,14 @@ async fn led_task(
|
|||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn display_task(
|
async fn display_task(
|
||||||
pins: display::LcdPins,
|
pins: display::LcdPins,
|
||||||
ledc: esp_hal::peripherals::LEDC<'static>,
|
ledc: LEDC<'static>,
|
||||||
bl_pin: esp_hal::peripherals::GPIO22<'static>,
|
bl_pin: GPIO22<'static>,
|
||||||
) {
|
) {
|
||||||
let mut parts = display::init_parts(pins);
|
let mut display = Display::new(pins);
|
||||||
println!("LCD {}x{} initialized", display::WIDTH, display::HEIGHT);
|
println!("LCD {}x{} initialized", display::WIDTH, display::HEIGHT);
|
||||||
|
|
||||||
let (_ledc, timer) =
|
let mut backlight = backlight::Backlight::new(ledc, bl_pin)
|
||||||
backlight::setup_timer(ledc).expect("Failed to init backlight timer");
|
.expect("Failed to init backlight");
|
||||||
let mut backlight =
|
|
||||||
backlight::LcdBacklight::new(&timer, bl_pin).expect("Failed to init backlight");
|
|
||||||
backlight.set_level(0.5).unwrap();
|
backlight.set_level(0.5).unwrap();
|
||||||
println!("backlight set to 0.5");
|
println!("backlight set to 0.5");
|
||||||
|
|
||||||
@@ -74,6 +72,10 @@ async fn display_task(
|
|||||||
let mut frame = 0u32;
|
let mut frame = 0u32;
|
||||||
let mut fps_window_start = Instant::now();
|
let mut fps_window_start = Instant::now();
|
||||||
let mut fps_frames = 0u32;
|
let mut fps_frames = 0u32;
|
||||||
|
let mut window_draw_us: u64 = 0;
|
||||||
|
let mut window_send_us: u64 = 0;
|
||||||
|
let mut last_draw_us: u64;
|
||||||
|
let mut last_send_us: u64;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let hue = (frame % 256) as u8;
|
let hue = (frame % 256) as u8;
|
||||||
@@ -101,30 +103,40 @@ async fn display_task(
|
|||||||
vy = -vy;
|
vy = -vy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let draw_start = Instant::now();
|
||||||
fb.clear();
|
fb.clear();
|
||||||
Triangle::new(pts[0], pts[1], pts[2])
|
Triangle::new(pts[0], pts[1], pts[2])
|
||||||
.into_styled(PrimitiveStyle::with_fill(accent))
|
.into_styled(PrimitiveStyle::with_fill(accent))
|
||||||
.draw(&mut fb)
|
.draw(&mut fb)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
last_draw_us = draw_start.elapsed().as_micros() as u64;
|
||||||
|
|
||||||
display::send_framebuffer(&mut parts.spi, &mut parts.dc, &mut parts.cs, fb.as_bytes())
|
let send_start = Instant::now();
|
||||||
.await
|
display.send_framebuffer(&fb).await.unwrap();
|
||||||
.unwrap();
|
last_send_us = send_start.elapsed().as_micros() as u64;
|
||||||
|
|
||||||
if frame % 32 == 0 {
|
if frame % 32 == 0 {
|
||||||
println!(
|
println!(
|
||||||
"frame {frame}: apex=({}, {}), v=({}, {}), hue={hue}",
|
"frame {frame}: apex=({}, {}), v=({}, {}), hue={hue} | draw {last_draw_us} us, send {last_send_us} us",
|
||||||
pts[0].x, pts[0].y, vx, vy
|
pts[0].x, pts[0].y, vx, vy
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fps_frames += 1;
|
fps_frames += 1;
|
||||||
|
window_draw_us += last_draw_us;
|
||||||
|
window_send_us += last_send_us;
|
||||||
if fps_frames >= 60 {
|
if fps_frames >= 60 {
|
||||||
let elapsed_ms = fps_window_start.elapsed().as_millis().max(1);
|
let elapsed_ms = fps_window_start.elapsed().as_millis().max(1);
|
||||||
let fps = (fps_frames as u64 * 1000) / elapsed_ms;
|
let fps = (fps_frames as u64 * 1000) / elapsed_ms;
|
||||||
println!("fps: {fps} ({fps_frames} frames in {elapsed_ms} ms)");
|
let avg_draw_us = window_draw_us / fps_frames as u64;
|
||||||
|
let avg_send_us = window_send_us / fps_frames as u64;
|
||||||
|
println!(
|
||||||
|
"fps: {fps} ({fps_frames} frames in {elapsed_ms} ms) | avg draw {avg_draw_us} us, avg send {avg_send_us} us"
|
||||||
|
);
|
||||||
fps_frames = 0;
|
fps_frames = 0;
|
||||||
fps_window_start = Instant::now();
|
fps_window_start = Instant::now();
|
||||||
|
window_draw_us = 0;
|
||||||
|
window_send_us = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame = frame.wrapping_add(1);
|
frame = frame.wrapping_add(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user