Compare commits

..

5 Commits

Author SHA1 Message Date
fbca758e1b Add some testings from Rust-discord #dark-arts 2025-09-12 19:05:32 +03:00
f2fda32009 Configure ravedude 2025-09-12 16:11:25 +03:00
91d04ff3cc Remove alpha 2025-09-12 14:25:28 +03:00
b31d6255bf Try to debug hi.qoi 2025-09-12 14:23:14 +03:00
7225591e7d Tweak qoi-parsing 2025-09-12 13:09:52 +03:00
12 changed files with 252 additions and 112 deletions

View File

@ -1,6 +1,6 @@
[build] [build]
target = "avr-none" target = "avr-none"
rustflags = ["-C", "target-cpu=atmega328p"] rustflags = ["-C", "target-cpu=atmega328p", "--emit=llvm-ir"]
[target.'cfg(target_arch = "avr")'] [target.'cfg(target_arch = "avr")']
runner = "ravedude" runner = "ravedude"

57
Cargo.lock generated
View File

@ -68,6 +68,16 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.3" version = "1.0.3"
@ -122,6 +132,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "723dce4e9f25b6e6c5f35628e144794e5b459216ed7da97b7c4b66cdb3fa82ca" checksum = "723dce4e9f25b6e6c5f35628e144794e5b459216ed7da97b7c4b66cdb3fa82ca"
[[package]]
name = "find-msvc-tools"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
[[package]] [[package]]
name = "hash32" name = "hash32"
version = "0.3.1" version = "0.3.1"
@ -198,12 +214,52 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "stackalloc"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b5b7088084b7f78305a42468c9a3693918620f0b8ec86260f1132dc0521e78"
dependencies = [
"cc",
"rustc_version",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -225,6 +281,7 @@ dependencies = [
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"mipidsi", "mipidsi",
"panic-halt", "panic-halt",
"stackalloc",
"ufmt", "ufmt",
] ]

View File

@ -12,6 +12,7 @@ atmega-hal = { git = "https://github.com/Rahix/avr-hal?tab=readme-ov-file", rev=
ufmt = "0.2.0" ufmt = "0.2.0"
mipidsi = "0.9.0" mipidsi = "0.9.0"
embedded-graphics-core = "0.4.0" embedded-graphics-core = "0.4.0"
stackalloc = { version = "1.2.1", features = ["no_std"] }
# nb = "1.1.0" # nb = "1.1.0"
# pwm-pca9685 = "1.0.0" # pwm-pca9685 = "1.0.0"
# infrared = "0.14.1" # infrared = "0.14.1"
@ -24,6 +25,9 @@ panic = "abort"
[profile.release] [profile.release]
panic = "abort" panic = "abort"
opt-level = "s" # Size is more important than performance on MSP430.
codegen-units = 1 # Better size optimization.
lto = "fat" # _Much_ better size optimization.
[build] [build]
rustflags = ["-C", "link-args=-lc"] rustflags = ["-C", "link-args=-lc", "--emit=llvm-ir"]

View File

@ -1,7 +1,14 @@
[general] [general]
board = "uno" port = "/dev/ttyUSB0"
serial-baudrate = 57600 serial-baudrate = 57600
open-console = true open-console = true
[board.avrdude]
# avrdude configuration
programmer = "arduino"
partno = "m328p"
baudrate = 57600
do-chip-erase = true
# For documentation about this file, check here: # For documentation about this file, check here:
# https://github.com/Rahix/avr-hal/blob/main/ravedude/README.md#ravedudetoml-format # https://github.com/Rahix/avr-hal/blob/main/ravedude/README.md#ravedudetoml-format

BIN
images/hi.qoi Normal file

Binary file not shown.

BIN
images/hi.xcf Normal file

Binary file not shown.

BIN
images/large.qoi Normal file

Binary file not shown.

BIN
images/large.xcf Normal file

Binary file not shown.

BIN
images/sheep.qoi Normal file

Binary file not shown.

BIN
images/sheep.xcf Normal file

Binary file not shown.

View File

@ -4,9 +4,11 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(iter_array_chunks)] #![feature(iter_array_chunks)]
#![feature(const_slice_make_iter)]
#![feature(slice_as_chunks)]
use atmega_hal::{ use atmega_hal::{
Adc, Usart, Adc, Eeprom, Usart,
port::{Pin, mode}, port::{Pin, mode},
spi::{self, Settings}, spi::{self, Settings},
usart::Baudrate, usart::Baudrate,
@ -42,6 +44,9 @@ fn main() -> ! {
Baudrate::<CoreClock>::new(57600), Baudrate::<CoreClock>::new(57600),
); );
// let eeprom = Eeprom::new(dp.EEPROM);
// ufmt::uwriteln!(serial, "Eeprom capacity: {}", eeprom.capacity()).unwrap();
let cs = pins.pb2.into_output(); let cs = pins.pb2.into_output();
let (mut spi, mut cs) = spi::Spi::new( let (mut spi, mut cs) = spi::Spi::new(
dp.SPI, dp.SPI,

View File

@ -1,4 +1,7 @@
use core::slice::Iter; use core::{
hint::black_box,
slice::{Chunks, Iter},
};
use atmega_hal::{ use atmega_hal::{
Atmega, Usart, Atmega, Usart,
@ -13,9 +16,57 @@ use crate::{
display::{Color, Display, Position, Rgb565, Writeable}, display::{Color, Display, Position, Rgb565, Writeable},
}; };
#[repr(u8)]
enum NonBssByte {
V = 1,
}
pub struct NonBss<Buf> {
_hack: NonBssByte,
pub buf: Buf,
}
impl<const N: usize> NonBss<[u8; N]> {
pub const fn new(buf: [u8; N]) -> Self {
Self {
_hack: NonBssByte::V,
buf,
}
}
}
macro_rules! non_bss_statics {(
$(
$(#$attr:tt)*
$pub:vis
static ref $NAME:ident : [$u8:ty] = *include_bytes!($($args:tt)*);
)*
) => (
$(
$(#$attr)*
$pub
const $NAME: &[u8] = {
static $NAME: NonBss<[$u8; ::core::include_bytes!($($args)*).len()]> =
NonBss::new(*::core::include_bytes!($($args)*));
&$NAME.buf
};
)*
)}
// https://qoiformat.org/qoi-specification.pdf // https://qoiformat.org/qoi-specification.pdf
const image: &[u8; 288] = include_bytes!("../images/Image.qoi"); // static LARGE: [u8; 1966] = *include_bytes!("../images/large.qoi");
const BUFFER_SIZE: usize = 64; // static SHEEP: [u8; 852] = *include_bytes!("../images/sheep.qoi");
non_bss_statics! {
static ref SHEEP: [u8] = *include_bytes!("../images/sheep.qoi");
static ref LARGE: [u8] = *include_bytes!("../images/large.qoi");
}
// const LARGE_C1: [u8; 512] = LARGE.as_chunks().0[0];
// const LARGE_C2: [u8; 512] = LARGE.as_chunks().0[1];
// const LARGE_C3: [u8; 512] = LARGE.as_chunks().0[2];
// const LARGE_C4: &[u8] = LARGE.as_chunks::<512>().1;
#[derive(Debug, uDebug)] #[derive(Debug, uDebug)]
pub enum QoiErr { pub enum QoiErr {
@ -23,64 +74,85 @@ pub enum QoiErr {
UnexpectedEOF, UnexpectedEOF,
} }
// #[derive(Default)]
// struct LargeIter<const N: usize> {
// index: usize,
// }
// impl<const N: usize> Iterator for LargeIter<N> {
// type Item = u8;
// fn next(&mut self) -> Option<Self::Item> {
// if self.index >= N {
// return None;
// }
// let old_idx = self.index;
// self.index += 1;
// Some(SHEEP[old_idx])
// }
// }
pub fn draw_image<T: DelayNs, DCPin: PinOps, RSTPin: PinOps>( pub fn draw_image<T: DelayNs, DCPin: PinOps, RSTPin: PinOps>(
serial: &mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>, serial: &mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
display: &mut Display<T, DCPin, RSTPin>, display: &mut Display<T, DCPin, RSTPin>,
position: Position, position: Position,
) -> Result<(), QoiErr> { ) -> Result<(), QoiErr> {
let mut iter = image.iter(); let a = LARGE[black_box(50)];
ufmt::uwriteln!(serial, "Successfully read QOI header {}", a).unwrap();
let mut iter = SHEEP.iter().map(|v| *v);
if let (Some(113), Some(111), Some(105), Some(102)) = if let (Some(113), Some(111), Some(105), Some(102)) =
(iter.next(), iter.next(), iter.next(), iter.next()) (iter.next(), iter.next(), iter.next(), iter.next())
{ {
fn next<'a>(iter: &mut Iter<'a, u8>) -> Result<&'a u8, QoiErr> { fn next(iter: &mut dyn Iterator<Item = u8>) -> Result<u8, QoiErr> {
iter.next().ok_or(QoiErr::UnexpectedEOF) iter.next().ok_or(QoiErr::UnexpectedEOF)
} }
let width = u32::from_be_bytes([ let width = u32::from_be_bytes([
*next(&mut iter)?, next(&mut iter)?,
*next(&mut iter)?, next(&mut iter)?,
*next(&mut iter)?, next(&mut iter)?,
*next(&mut iter)?, next(&mut iter)?,
]) as u16; ]) as u16;
let height = u32::from_be_bytes([ let height = u32::from_be_bytes([
*next(&mut iter)?, next(&mut iter)?,
*next(&mut iter)?, next(&mut iter)?,
*next(&mut iter)?, next(&mut iter)?,
*next(&mut iter)?, next(&mut iter)?,
]) as u16; ]) as u16;
let channels = *next(&mut iter)?; let channels = next(&mut iter)?;
let colorspace = *next(&mut iter)?; let colorspace = next(&mut iter)?;
ufmt::uwriteln!(serial, "Successfully read QOI header").unwrap(); ufmt::uwriteln!(serial, "Successfully read QOI header").unwrap();
display.set_window( display.set_window(
position, position,
Position { Position {
x: position.x + width, x: position.x + width - 1,
y: position.y + height, y: position.y + height - 1,
}, },
); );
let qoi_iter = QoiIterator::from(iter, serial); let qoi_iter = QoiIterator::from(&mut iter, serial, width * height);
let mut prev_pixels = [0u8; 64];
let mut chunks = qoi_iter.array_chunks::<BUFFER_SIZE>(); // let mut chunks = qoi_iter.array_chunks::<BUFFER_SIZE>();
while let Some(array) = chunks.next() { // while let Some(array) = chunks.next() {
let mut colors = [0u8; BUFFER_SIZE * 2]; // let mut colors = [0u8; BUFFER_SIZE * 2];
for (i, pixel) in array.iter().enumerate() { // for (i, pixel) in array.iter().enumerate() {
let [c1, c2] = pixel.as_color().bytes; // let [c1, c2] = pixel.as_color().bytes;
colors[i * 2] = c1; // colors[i * 2] = c1;
colors[i * 2 + 1] = c2; // colors[i * 2 + 1] = c2;
} // }
display.write(Writeable::Data(&colors)); // display.write(Writeable::Data(&colors));
} // }
if let Some(remainder) = chunks.into_remainder() { let mut counter = 0u32;
for pixel in remainder { for pixel in qoi_iter {
let [c1, c2] = pixel.as_color().bytes; let [c1, c2] = pixel.as_color().bytes;
display.write(Writeable::Data(&[c1, c2])); display.write(Writeable::Data(&[c1, c2]));
} counter += 1;
} }
ufmt::uwriteln!(serial, "Counter: {}", counter).unwrap();
// display.draw_rect( // display.draw_rect(
// position, // position,
@ -97,28 +169,29 @@ pub fn draw_image<T: DelayNs, DCPin: PinOps, RSTPin: PinOps>(
} }
struct QoiIterator<'a> { struct QoiIterator<'a> {
inner: Iter<'a, u8>, inner: &'a mut dyn Iterator<Item = u8>,
serial: &'a mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>, serial: &'a mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
prev_pixels: [Rgb565; 64], prev_pixels: [Rgb565; 64],
prev_alphas: [u8; 64],
last_pixel: Rgb565, last_pixel: Rgb565,
last_alpha: u8,
repeat: u8, repeat: u8,
expected_colors: u16,
parsed_colors: u16,
} }
impl<'a> QoiIterator<'a> { impl<'a> QoiIterator<'a> {
fn from( fn from(
bytes: Iter<'a, u8>, bytes: &'a mut dyn Iterator<Item = u8>,
serial: &'a mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>, serial: &'a mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
expected: u16,
) -> Self { ) -> Self {
QoiIterator { QoiIterator {
inner: bytes, inner: bytes,
serial, serial,
prev_pixels: [Rgb565(255, 255, 255); 64], prev_pixels: [Rgb565(0, 255, 0); 64],
prev_alphas: [255; 64],
last_pixel: Rgb565(0, 0, 0), last_pixel: Rgb565(0, 0, 0),
last_alpha: 255,
repeat: 0, repeat: 0,
expected_colors: expected,
parsed_colors: 0,
} }
} }
} }
@ -127,54 +200,54 @@ impl<'a> Iterator for QoiIterator<'a> {
type Item = Rgb565; type Item = Rgb565;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.parsed_colors >= self.expected_colors {
ufmt::uwriteln!(self.serial, "Reached expected").unwrap();
return None;
}
self.parsed_colors += 1;
if self.repeat > 0 { if self.repeat > 0 {
self.repeat -= 1; self.repeat -= 1;
return Some(self.last_pixel); return Some(self.last_pixel);
} }
if let Some(byte) = self.inner.next() { if let Some(byte) = self.inner.next() {
let (color, alpha) = if *byte == 0xff { let color = if byte == 0xff {
let red = self.inner.next().unwrap(); let red = self.inner.next().unwrap();
let green = self.inner.next().unwrap(); let green = self.inner.next().unwrap();
let blue = self.inner.next().unwrap(); let blue = self.inner.next().unwrap();
let alpha = self.inner.next().unwrap(); let _alpha = self.inner.next().unwrap();
ufmt::uwriteln!(self.serial, "RGBA: {} {} {} {}", red, green, blue, alpha).unwrap(); // ufmt::uwriteln!(self.serial, "RGBA: {} {} {} {}", red, green, blue, alpha).unwrap();
(Rgb565(*red, *green, *blue), *alpha) Rgb565(red, green, blue)
} else if *byte == 0b11111110 { } else if byte == 0b11111110 {
let red = self.inner.next().unwrap(); let red = self.inner.next().unwrap();
let green = self.inner.next().unwrap(); let green = self.inner.next().unwrap();
let blue = self.inner.next().unwrap(); let blue = self.inner.next().unwrap();
ufmt::uwriteln!(self.serial, "RGB: {} {} {}", red, green, blue).unwrap(); // ufmt::uwriteln!(self.serial, "RGB: {} {} {}", red, green, blue).unwrap();
(Rgb565(*red, *green, *blue), self.last_alpha) Rgb565(red, green, blue)
} else { } else {
let tag = (0b11000000 & byte) >> 6; let tag = (0b11000000 & byte) >> 6;
let data = 0b00111111 & byte; let data = 0b00111111 & byte;
ufmt::uwriteln!(self.serial, "Tag: {}", tag).unwrap(); // ufmt::uwriteln!(self.serial, "Tag: {}", tag).unwrap();
if tag == 0 { if tag == 0 {
( self.prev_pixels[data as usize]
self.prev_pixels[data as usize],
self.prev_alphas[data as usize],
)
} else if tag == 1 { } else if tag == 1 {
let dr = ((0b110000 & data) >> 4) as i8 - 2; let dr = ((0b110000 & data) >> 4) as i8 - 2;
let dg = ((0b001100 & data) >> 2) as i8 - 2; let dg = ((0b001100 & data) >> 2) as i8 - 2;
let db = (0b000011 & data) as i8 - 2; let db = (0b000011 & data) as i8 - 2;
ufmt::uwriteln!( // ufmt::uwriteln!(
self.serial, // self.serial,
"Diffs: {} {} {}", // "Diffs: {} {} {}",
((0b110000 & data) >> 4), // ((0b110000 & data) >> 4),
((0b001100 & data) >> 2), // ((0b001100 & data) >> 2),
(0b000011 & data) // (0b000011 & data)
) // )
.unwrap(); // .unwrap();
( Rgb565(
Rgb565( (self.last_pixel.0 as i8).wrapping_add(dr) as u8,
(self.last_pixel.0 as i8).wrapping_add(dr) as u8, (self.last_pixel.1 as i8).wrapping_add(dg) as u8,
(self.last_pixel.1 as i8).wrapping_add(dg) as u8, (self.last_pixel.2 as i8).wrapping_add(db) as u8,
(self.last_pixel.2 as i8).wrapping_add(db) as u8,
),
self.last_alpha,
) )
} else if tag == 2 { } else if tag == 2 {
let second = self.inner.next().unwrap(); let second = self.inner.next().unwrap();
@ -183,60 +256,54 @@ impl<'a> Iterator for QoiIterator<'a> {
let db_dg = (0b00001111 & second) as i8 - 8; let db_dg = (0b00001111 & second) as i8 - 8;
let dr = dr_dg + dg; let dr = dr_dg + dg;
let db = db_dg + dg; let db = db_dg + dg;
ufmt::uwriteln!(
self.serial, let a = self.last_pixel.0 as i8;
"Last: {} {} {}", let b = self.last_pixel.1 as i8;
self.last_pixel.0 as i8, let c = self.last_pixel.2 as i8;
self.last_pixel.1 as i8, // ufmt::uwriteln!(self.serial, "Hello there! {} {} {}", a, b, c).unwrap();
self.last_pixel.2 as i8 // ufmt::uwriteln!(self.serial, "Values: {} {} {}", dr_dg, dg, db_dg).unwrap();
) // ufmt::uwriteln!(self.serial, "Diffs: {} {} {}", dr, dg, db).unwrap();
.unwrap(); // ufmt::uwriteln!(
ufmt::uwriteln!(self.serial, "Values: {} {} {}", dr_dg, dg, db_dg).unwrap(); // self.serial,
ufmt::uwriteln!(self.serial, "Diffs: {} {} {}", dr, dg, db).unwrap(); // "Res: {} {} {}",
ufmt::uwriteln!( // self.last_pixel.0 as i8 + dr,
self.serial, // self.last_pixel.1 as i8 + dg,
"Res: {} {} {}", // self.last_pixel.2 as i8 + db
self.last_pixel.0 as i8 + dr, // )
self.last_pixel.1 as i8 + dg, // .unwrap();
self.last_pixel.2 as i8 + db Rgb565(
) (self.last_pixel.0 as i8 + dr) as u8,
.unwrap(); (self.last_pixel.1 as i8 + dg) as u8,
( (self.last_pixel.2 as i8 + db) as u8,
Rgb565(
(self.last_pixel.0 as i8).wrapping_add(dr) as u8,
(self.last_pixel.1 as i8).wrapping_add(dg) as u8,
(self.last_pixel.2 as i8).wrapping_add(db) as u8,
),
self.last_alpha,
) )
} else if tag == 3 { } else if tag == 3 {
// QOI_OP_RUN // QOI_OP_RUN
self.repeat = data; self.repeat = data;
ufmt::uwriteln!(self.serial, "Repeat: {}", self.repeat).unwrap(); // ufmt::uwriteln!(self.serial, "Repeat: {}", self.repeat).unwrap();
(self.last_pixel, self.last_alpha) self.last_pixel
} else { } else {
(Rgb565::green(), 255) Rgb565::green()
} }
}; };
self.last_pixel = color; self.last_pixel = color;
self.last_alpha = alpha;
let hash = let hash =
((color.0 as u32 * 3 + color.1 as u32 * 5 + color.2 as u32 * 7 + alpha as u32 * 11) ((color.0 as u32 * 3 + color.1 as u32 * 5 + color.2 as u32 * 7 + 255 as u32 * 11)
% 64) as usize; % 64) as usize;
ufmt::uwriteln!( // ufmt::uwriteln!(
self.serial, // self.serial,
"Color: {} {} {} {}, Hash: {}", // "Color: {} {} {} {}, Hash: {}",
color.0, // color.0,
color.1, // color.1,
color.2, // color.2,
alpha, // alpha,
hash // hash
) // )
.unwrap(); // .unwrap();
self.prev_pixels[hash] = color; self.prev_pixels[hash] = color;
Some(color) Some(color)
} else { } else {
ufmt::uwriteln!(self.serial, "End reached").unwrap();
None None
} }
} }