Release 0.4.0 of AVRoxide[1] is now packaged up and released, with a host of new features...
Thanks to bugfixes in the mainline Rust toolchain, we no longer need to use a custom toolchain to build AVRoxide applications = regular old Rust `nightly` will now do the job. Special thanks to Patryk27[2] for the critical bugfixes!
An abstraction layer for implementing storage using I2C bus attached serial devices has now been implemented. The `avrox-storage`[3] crate provides both the abstraction and initial implementation for `BR24T1M_3AM` serial EEPROMs, but the design is sufficiently generic that other devices can be trivially supported.
Once we have comparatively large storage, we want to be able to manage that storage. `SNaFuS` is a simple filesystem layer that sits on top of the storage drivers to provide a familiar, file-based interface to the storage, with the kind of format, create, open, read/write semantics you would expect.
The key differentiator between SNaFuS and "proper" filesystems like FAT (!) is that we use integers instead of strings to identify files, and do not support directories. Because who has enough spare code space for storing strings?
#[avr_oxide::main(chip="atmega4809",stacksize=1024)] pub fn main() -> ! { let supervisor = avr_oxide::oxide::instance(); // We will access the EEPROM over a serial bus let bus = StaticWrap::new(OxideSerialBus::using_bus(hardware::twi::twi0::instance().mux(hardware::twi::twi0::TwiPins::MasterASlaveC).mode(InterfaceMode::I2C, PortSpeed::Fast))); supervisor.listen(bus.borrow()); // The EEPROM listens on address 0xA0 let bus_client = OxideSerialBus::client(bus.borrow(), TwiAddr::addr(0xA0)); // Create the storage driver let storage = serprom::BR24T1M_3AM::using_client(bus_client); // And a filesystem that uses it let mut fs = SnafusFileSystem::with_driver(PageBuffer::<32,_>::with_driver(storage)); // Format the filesystem if !fs.is_formatted().unwrap() { fs.format().unwrap(); } // Create a file ('named' 1) let mut file = File::create_on(fs, FileUid::with_uid(1)).unwrap(); file.write_all(&MY_TEST_DATA).unwrap(); file.sync_all().unwrap(); supervisor.run() }
We also now have an abstraction for bitmapped display drivers, provided in the `avrox-display`[4] crate. This provides an interface to bitmap devices and also a simple set of graphics primitives (like boxes, fills, and bitmap images) to draw on the bitmap.
The graphics interface is optimised for device memory usage rather than speed: we never store the entire display bitmap in memory, but rather compute the bits to write to the graphics device on-demand when the display is painted. This means you won't be using it for animation, but for rendering simple UIs including dynamic elements it will do the job and not consume all your memory regardless of the display size.
#[avr_oxide::main(chip="atmega4809",stacksize=1024)] pub fn main() -> ! { let supervisor = avr_oxide::oxide::instance(); // We attach to the EEPROM with our graphics files, and the graphics device, // using a serial bus let bus = StaticWrap::new(OxideSerialBus::using_bus(hardware::twi::twi0::instance().mux(hardware::twi::twi0::TwiPins::MasterASlaveC).mode(InterfaceMode::I2C, PortSpeed::Standard))); supervisor.listen(bus.borrow()); // Set up our storage drivers let storagebus = OxideSerialBus::client(bus, TwiAddr::addr(0xA0)); let fs = StaticWrap::new(SnafusFileSystem::with_driver(PageBuffer::<32,_>::with_driver(serprom::BR24T1M_3AM::using_client(storagebus)))); // And also our display drivers let displaybus = OxideSerialBus::client(bus, avrox_display::displays::displayvisions::DVEAW096016DriverConfig::DEFAULT_I2C); let display = StaticWrap::new(displays::DisplayVisionsEAW096016::using_pin_and_client(board::pin_d(13), displaybus)); // Initialise the display driver display.borrow().reset().unwrap(); display.borrow().request_power_level(PowerLevel::Normal).unwrap(); display.borrow().set_double_buffering(false).unwrap(); // Now set up what we want to display static mut BIG_COUNTER : u32 = 0x00; let mut big_value = StaticWrap::new(RefCell::new(Some(0x00u32))); // We want a 7-segment display counter, scaled to double size, that will // use BIG_COUNTER as a data source let counter = ConstScaleUp::<2,2,_>::new(SevenSegmentDisplay::<5,16,_,_>::new(Grey::GREY75PC, Grey::GREY25PC, unsafe {big_value.borrow().static_ref()})); // We store a logo on the file system if !fs.borrow().is_formatted().unwrap() { println!("Filesystem not formatted!"); panic!(); } let logo_file = File::open_on(unsafe { fs.borrow().static_ref() }, ID_AVROXIDE_LOGO).unwrap(); let logo = MonochromeImage::from(ImageFile::with_file(logo_file).unwrap()); // Our scene comprises the logo and the counter, arranged left to // right, overlaid on a plain black background: let scene = Overlay::new( HorizontalPair::<_,_,position::Beginning>::new(logo, counter), SolidFill::new(Monochromatic::BLACK) ); // First time round we'll render the whole scene. We can then use // render_changed() to only update dynamic parts (e.g. the counter) display.borrow().render(&scene).unwrap(); supervisor.run() }
Since we have storage, and we also have graphics, it'd be nice to be able to display images stored as files on EEPROMs on the graphics display, right? To support this we also have a trivial image format, `TBFF`, which we can store as a file on a SNaFuS filesystem, and graphics primitives that support a filehandle as a datasource for the image to display.
The file format currently supports monochrome (single-bit) and greyscale (8 bit) image layers, along with room for RGB. An individual file may contain one or multiple of such layers, allowing for the application/graphics layer to pick the appropriate layer for some level of device independence.
A trivial "real computer" commandline application for converting more familiar graphics formats (like JPEG or PNG) into TBFF files is provided in my AVRoxide Utilities[5] repo.
pub static DATA_AVROXIDE_LOGO: [u8; 1177] = [ 0x54u8, 0x42u8, 0x46u8, 0x47u8, 0x2u8, 0x0u8, 0x40u8, 0x0u8, 0x10u8, // Header, index follows 0x0u8, 0x0u8, 0x0u8, 0x19u8, // Monochrome layer offset 0x0u8, 0x0u8, 0x0u8, 0x99u8, // Greyscale layer offset 0x0u8, 0x0u8, 0x0u8, 0x0u8, // No RGBA4 layer 0x0u8, 0x0u8, 0x0u8, 0x0u8, // No RGBA8 layer // Monochrome layer data: 0x0u8, 0x0u8, 0x0u8, /* .. snipped .. */ 0x1cu8, 0x0u8, 0x0u8, // Greyscale layer data: 0x0u8, 0x0u8, 0x0u8, /* .. snipped .. */ 0x0u8, 0x0u8, 0x0u8, // RGBA4 layer data skipped // RGBA8 layer data skipped ];
2: https://github.com/Patryk27
3: https://crates.io/crates/avrox-storage
4: https://crates.io/crates/avrox-display
5: https://gitlab.com/snowgoonspub/avrox-utils
--------------------