From 8a1d9f8e55e21be2463ca024d9858cdb62f35b3f Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sat, 20 Jul 2024 09:10:44 -0700 Subject: [PATCH 01/23] Added board wavershare-rp2040-lcs-1-28 Added board wavershare-rp2040-lcs-1-28, added a driver for this board. --- boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md | 42 + boards/waveshare-rp2040-lcd-1-28/Cargo.toml | 52 ++ boards/waveshare-rp2040-lcd-1-28/README.md | 97 +++ .../examples/gc9a01a_driver.rs | 424 ++++++++++ .../examples/waveshare_rp2040_lcd_demo.rs | 166 ++++ boards/waveshare-rp2040-lcd-1-28/src/lib.rs | 785 ++++++++++++++++++ 6 files changed, 1566 insertions(+) create mode 100644 boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md create mode 100644 boards/waveshare-rp2040-lcd-1-28/Cargo.toml create mode 100644 boards/waveshare-rp2040-lcd-1-28/README.md create mode 100644 boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs create mode 100644 boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs create mode 100644 boards/waveshare-rp2040-lcd-1-28/src/lib.rs diff --git a/boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md b/boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md new file mode 100644 index 00000000..375c34a6 --- /dev/null +++ b/boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md @@ -0,0 +1,42 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +## 0.8.0 - 2024-04-07 + +### Changed + +- Update to rp2040-hal 0.10.0 +- Update to embedded-hal 1.0.0 + +## 0.7.0 - 2023-09-02 + +### Changed + +- Update to rp2040-hal 0.9.0 + +## 0.6.0 - 2023-02-18 + +### Changed + +- Update to rp2040-hal 0.8.0 + +## 0.5.0 - 2022-12-11 + +### Changed + +- Update to rp2040-hal 0.7.0 + +## 0.4.0 - 2022-11-15 + +### Changed + +- Inital release +- Copied from waveshare-rp2040-zero +- Update board name + diff --git a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml new file mode 100644 index 00000000..98ff2d8d --- /dev/null +++ b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "waveshare-rp2040-lcd-1-28" +version = "0.8.0" +authors = ["RenУЉ van Dorst ", "Andrea Nall ", "TilCreator ", "The rp-rs Developers"] +edition = "2018" +homepage = "https://github.com/rp-rs/rp-hal-boards/tree/main/boards/waveshare-rp2040-lcd-0_96" +description = "Board Support Package for the Waveshare RP2040 LCD 0.96 inch" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rp-rs/rp-hal-boards.git" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cortex-m-rt = { workspace = true, optional = true } +rp2040-boot2 = { workspace = true, optional = true } +rp2040-hal.workspace = true +embedded-hal = { version = "0.2" } + +[dev-dependencies] +cortex-m.workspace = true +panic-halt.workspace = true +fugit.workspace = true +nb.workspace = true +embedded-graphics.workspace = true +st7735-lcd = { workspace = true, features = ["graphics"] } + +[features] +# This is the set of features we enable by default +default = ["boot2", "rt", "critical-section-impl", "rom-func-cache"] + +# critical section that is safe for multicore use +critical-section-impl = ["rp2040-hal/critical-section-impl"] + +# 2nd stage bootloaders for rp2040 +boot2 = ["rp2040-boot2"] + +# Minimal startup / runtime for Cortex-M microcontrollers +rt = ["cortex-m-rt","rp2040-hal/rt"] + +# This enables a fix for USB errata 5: USB device fails to exit RESET state on busy USB bus. +# Only required for RP2040 B0 and RP2040 B1, but it doesn't hurt to enable it +rp2040-e5 = ["rp2040-hal/rp2040-e5"] + +# Memoize(cache) ROM function pointers on first use to improve performance +rom-func-cache = ["rp2040-hal/rom-func-cache"] + +# Disable automatic mapping of language features (like floating point math) to ROM functions +disable-intrinsics = ["rp2040-hal/disable-intrinsics"] + +# This enables ROM functions for f64 math that were not present in the earliest RP2040s +rom-v2-intrinsics = ["rp2040-hal/rom-v2-intrinsics"] + diff --git a/boards/waveshare-rp2040-lcd-1-28/README.md b/boards/waveshare-rp2040-lcd-1-28/README.md new file mode 100644 index 00000000..b0c5489b --- /dev/null +++ b/boards/waveshare-rp2040-lcd-1-28/README.md @@ -0,0 +1,97 @@ +# [waveshare-rp2040-lcd-0-96] - Board Support for the [Waveshare RP2040 LCD 0.96] + +You should include this crate if you are writing code that you want to run on +an [Waveshare RP2040 LCD 0.96] - a very small RP2040 breakout board with USB-C, +a 65K IPS LCD 160x80, 16MBit Flash and 1A battery charger from Waveshare. + +This crate includes the [rp2040-hal], but also configures each pin of the +RP2040 chip according to how it is connected up on the Feather. + +[Waveshare RP2040 LCD 0.96]: https://www.waveshare.com/wiki/RP2040-LCD-0.96 +[waveshare-rp2040-lcd-0-96]: https://github.com/rp-rs/rp-hal-boards/tree/main/boards/waveshare-rp2040-lcd-0-96 +[rp2040-hal]: https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal +[Raspberry Silicon RP2040]: https://www.raspberrypi.org/products/rp2040/ + +## Using + +To use this crate, your `Cargo.toml` file should contain: + +```toml +waveshare_rp2040_lcd_0_96 = "0.8.0" +``` + +In your program, you will need to call `waveshare_rp2040_lcd_0_96::Pins::new` to create +a new `Pins` structure. This will set up all the GPIOs for any on-board +devices. See the [examples](./examples) folder for more details. + +## Examples + +### General Instructions + +To compile an example, clone the _rp-hal-boards_ repository and run: + +```console +rp-hal-boards/boards/waveshare-rp2040-lcd-0-96 $ cargo build --release --example +``` + +You will get an ELF file called +`./target/thumbv6m-none-eabi/release/examples/`, where the `target` +folder is located at the top of the _rp-hal-boards_ repository checkout. Normally +you would also need to specify `--target=thumbv6m-none-eabi` but when +building examples from this git repository, that is set as the default. + +If you want to convert the ELF file to a UF2 and automatically copy it to the +USB drive exported by the RP2040 bootloader, simply boot your board into +bootloader mode and run: + +```console +rp-hal-boards/boards/waveshare-rp2040-lcd-0-96 $ cargo run --release --example +``` + +If you get an error about not being able to find `elf2uf2-rs`, try: + +```console +$ cargo install elf2uf2-rs, then repeating the `cargo run` command above. +``` + +### [waveshare_rp2040_lcd_demo](./examples/waveshare_rp2040_lcd_demo.rs) + +Draws a red and green line with a blue regtangle. +After that is fills the screen line for line, that end it starts over with an +other colour, RED, GREEN and BLUE. + +## Contributing + +Contributions are what make the open source community such an amazing place to +be, learn, inspire, and create. Any contributions you make are **greatly +appreciated**. + +The steps are: + +1. Fork the Project by clicking the 'Fork' button at the top of the page. +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Make some changes to the code or documentation. +4. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +5. Push to the Feature Branch (`git push origin feature/AmazingFeature`) +6. Create a [New Pull Request](https://github.com/rp-rs/rp-hal-boards/pulls) +7. An admin will review the Pull Request and discuss any changes that may be required. +8. Once everyone is happy, the Pull Request can be merged by an admin, and your work is part of our project! + +## Code of Conduct + +Contribution to this crate is organized under the terms of the [Rust Code of +Conduct][CoC], and the maintainer of this crate, the [rp-rs team], promises +to intervene to uphold that code of conduct. + +[CoC]: CODE_OF_CONDUCT.md +[rp-rs team]: https://github.com/orgs/rp-rs/teams/rp-rs + +## License + +The contents of this repository are dual-licensed under the _MIT OR Apache +2.0_ License. That means you can choose either the MIT license or the +Apache-2.0 license when you re-use this code. See `MIT` or `APACHE2.0` for more +information on each specific license. + +Any submissions to this project (e.g. as Pull Requests) must be made available +under these terms. diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs new file mode 100644 index 00000000..6790c2ad --- /dev/null +++ b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs @@ -0,0 +1,424 @@ +#![no_std] + +//! This crate provides a ST7735 driver to connect to TFT displays. + +//mod instruction; + +//use crate::instruction::Instruction; + +use embedded_hal::blocking::delay::DelayMs; +use embedded_hal::blocking::spi; +use embedded_hal::digital::v2::OutputPin; + +pub enum Instruction { + NOP = 0x00, + SWRESET = 0x01, + RDDID = 0x04, + RDDST = 0x09, + SLPIN = 0x10, + SLPOUT = 0x11, + PTLON = 0x12, + NORON = 0x13, + INVOFF = 0x20, + INVON = 0x21, + DISPOFF = 0x28, + DISPON = 0x29, + CASET = 0x2A, + RASET = 0x2B, + RAMWR = 0x2C, + RAMRD = 0x2E, + PTLAR = 0x30, + COLMOD = 0x3A, + MADCTL = 0x36, + FRMCTR1 = 0xB1, + FRMCTR2 = 0xB2, + FRMCTR3 = 0xB3, + INVCTR = 0xB4, + DISSET5 = 0xB6, + PWCTR1 = 0xC0, + PWCTR2 = 0xC1, + PWCTR3 = 0xC2, + PWCTR4 = 0xC3, + PWCTR5 = 0xC4, + VMCTR1 = 0xC5, + RDID1 = 0xDA, + RDID2 = 0xDB, + RDID3 = 0xDC, + RDID4 = 0xDD, + PWCTR6 = 0xFC, + GMCTRP1 = 0xE0, + GMCTRN1 = 0xE1, +} + +/// ST7735 driver to connect to TFT displays. +pub struct GC9A01A +where + SPI: spi::Write, + DC: OutputPin, + CS: OutputPin, + RST: OutputPin, +{ + /// SPI + spi: SPI, + + /// Data/command pin. + dc: DC, + + //pin + cs:CS, + + /// Reset pin. + rst: RST, + + /// Whether the display is RGB (true) or BGR (false) + rgb: bool, + + /// Whether the colours are inverted (true) or not (false) + inverted: bool, + + /// Global image offset + dx: u16, + dy: u16, + width: u32, + height: u32, +} + +/// Display orientation. +#[derive(Clone, Copy)] +pub enum Orientation { + Portrait = 0x00, + Landscape = 0x60, + PortraitSwapped = 0xC0, + LandscapeSwapped = 0xA0, +} + +impl GC9A01A +where + SPI: spi::Write, + DC: OutputPin, + CS: OutputPin, + RST: OutputPin, +{ + /// Creates a new driver instance that uses hardware SPI. + pub fn new( + spi: SPI, + dc: DC, + cs: CS, + rst: RST, + rgb: bool, + inverted: bool, + width: u32, + height: u32, + ) -> Self { + let display = GC9A01A { + spi, + dc, + cs, + rst, + rgb, + inverted, + dx: 0, + dy: 0, + width, + height, + }; + + display + } + + /// Runs commands to initialize the display. + pub fn init(&mut self, delay: &mut DELAY) -> Result<(), ()> + where + DELAY: DelayMs, + { + + self.hard_reset(delay)?; + self.write_command(0xEF as u8, &[])?; + self.write_command(0xEB as u8, &[0x14])?; + self.write_command(0xFE, &[])?; + self.write_command(0xEF, &[])?; + self.write_command(0xEB, &[0x14])?; + self.write_command(0x84, &[0x40])?; + self.write_command(0x85, &[0xFF])?; + self.write_command(0x86, &[0xFF])?; + self.write_command(0x87, &[0xFF])?; + self.write_command(0x88, &[0x0A])?; + self.write_command(0x89, &[0x21])?; + self.write_command(0x8A, &[0x00])?; + self.write_command(0x8B, &[0x80])?; + self.write_command(0x8C, &[0x01])?; + self.write_command(0x8D, &[0x01])?; + self.write_command(0x8E, &[0xFF])?; + self.write_command(0x8F, &[0xFF])?; + self.write_command(0xB6, &[0x00, 0x20])?; + self.write_command(0x36, &[0x98])?; + self.write_command(0x3A, &[0x05])?; + self.write_command(0x90, &[0x08, 0x08, 0x08, 0x08])?; + self.write_command(0xBD, &[0x06])?; + self.write_command(0xBC, &[0x00])?; + self.write_command(0xFF, &[0x60, 0x01, 0x04])?; + self.write_command(0xC3, &[0x13])?; + self.write_command(0xC4, &[0x13])?; + self.write_command(0xC9, &[0x22])?; + self.write_command(0xBE, &[0x11])?; + self.write_command(0xE1, &[0x10, 0x0E])?; + self.write_command(0xDF, &[0x21, 0x0C, 0x02])?; + self.write_command(0xF0, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])?; + self.write_command(0xF1, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6F])?; + self.write_command(0xF2, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])?; + self.write_command(0xF3, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6F])?; + self.write_command(0xED, &[0x1B, 0x0B])?; + self.write_command(0xAE, &[0x77])?; + self.write_command(0xCD, &[0x63])?; + self.write_command(0x70, &[0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03])?; + self.write_command(0xE8, &[0x34])?; + self.write_command(0x62, &[0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70])?; + self.write_command(0x63, &[0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70])?; + self.write_command(0x64, &[0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07])?; + self.write_command(0x66, &[0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00])?; + self.write_command(0x67, &[0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98])?; + self.write_command(0x74, &[0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00])?; + self.write_command(0x98, &[0x3E, 0x07])?; + self.write_command(0x35, &[])?; + self.write_command(0x21, &[])?; + self.write_command(0x11, &[])?; + self.write_command(0x29, &[])?; + + delay.delay_ms(200); + + Ok(()) + } + + pub fn hard_reset(&mut self, delay: &mut DELAY) -> Result<(), ()> + where + DELAY: DelayMs, + { + self.rst.set_high().map_err(|_| ())?; + delay.delay_ms(10); + self.rst.set_low().map_err(|_| ())?; + delay.delay_ms(10); + self.rst.set_high().map_err(|_| ())?; + delay.delay_ms(10); + + Ok(()) + } + + fn write_command(&mut self, command : u8, params: &[u8]) -> Result<(), ()> { + self.cs.set_high().map_err(|_| ())?; + self.dc.set_low().map_err(|_| ())?; + self.cs.set_low().map_err(|_| ())?; + self.spi.write(&[command]).map_err(|_| ())?; + if !params.is_empty() { + self.start_data()?; + self.write_data(params)?; + } + self.cs.set_high().map_err(|_| ())?; + Ok(()) + } + + fn start_data(&mut self) -> Result<(), ()> { + self.dc.set_high().map_err(|_| ()) + } + + fn write_data(&mut self, data: &[u8]) -> Result<(), ()> { + self.cs.set_high().map_err(|_| ())?; + self.dc.set_high().map_err(|_| ())?; + self.cs.set_low().map_err(|_| ())?; + self.spi.write(data).map_err(|_| ()); + self.cs.set_high().map_err(|_| ())?; + Ok(()) + } + + /// Writes a data word to the display. + fn write_word(&mut self, value: u16) -> Result<(), ()> { + self.write_data(&value.to_be_bytes()) + } + + fn write_words_buffered(&mut self, words: impl IntoIterator) -> Result<(), ()> { + let mut buffer = [0; 32]; + let mut index = 0; + for word in words { + let as_bytes = word.to_be_bytes(); + buffer[index] = as_bytes[0]; + buffer[index + 1] = as_bytes[1]; + index += 2; + if index >= buffer.len() { + self.write_data(&buffer)?; + index = 0; + } + } + self.write_data(&buffer[0..index]) + } + + pub fn set_orientation(&mut self, orientation: &Orientation) -> Result<(), ()> { + if self.rgb { + self.write_command(Instruction::MADCTL as u8, &[*orientation as u8])?; + } else { + self.write_command(Instruction::MADCTL as u8, &[*orientation as u8 | 0x08])?; + } + Ok(()) + } + + /// Sets the global offset of the displayed image + pub fn set_offset(&mut self, dx: u16, dy: u16) { + self.dx = dx; + self.dy = dy; + } + + /// Sets the address window for the display. + pub fn set_address_window(&mut self, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), ()> { + self.write_command(Instruction::CASET as u8, &[])?; + self.start_data()?; + self.write_word(sx + self.dx)?; + self.write_word(ex + self.dx)?; + self.write_command(Instruction::RASET as u8, &[])?; + self.start_data()?; + self.write_word(sy + self.dy)?; + self.write_word(ey + self.dy) + } + + /// Sets a pixel color at the given coords. + pub fn set_pixel(&mut self, x: u16, y: u16, color: u16) -> Result<(), ()> { + self.set_address_window(x, y, x, y)?; + self.write_command(Instruction::RAMWR as u8, &[])?; + self.start_data()?; + self.write_word(color) + } + + /// Writes pixel colors sequentially into the current drawing window + pub fn write_pixels>(&mut self, colors: P) -> Result<(), ()> { + self.write_command(Instruction::RAMWR as u8, &[])?; + self.start_data()?; + for color in colors { + self.write_word(color)?; + } + Ok(()) + } + pub fn write_pixels_buffered>( + &mut self, + colors: P, + ) -> Result<(), ()> { + self.write_command(Instruction::RAMWR as u8, &[])?; + self.start_data()?; + self.write_words_buffered(colors) + } + + /// Sets pixel colors at the given drawing window + pub fn set_pixels>( + &mut self, + sx: u16, + sy: u16, + ex: u16, + ey: u16, + colors: P, + ) -> Result<(), ()> { + self.set_address_window(sx, sy, ex, ey)?; + self.write_pixels(colors) + } + + pub fn set_pixels_buffered>( + &mut self, + sx: u16, + sy: u16, + ex: u16, + ey: u16, + colors: P, + ) -> Result<(), ()> { + self.set_address_window(sx, sy, ex, ey)?; + self.write_pixels_buffered(colors) + } +} + + +extern crate embedded_graphics; + +use self::embedded_graphics::{ + draw_target::DrawTarget, + pixelcolor::{ + raw::{RawData, RawU16}, + Rgb565, + }, + prelude::*, + primitives::Rectangle, +}; + + +impl DrawTarget for GC9A01A +where + SPI: spi::Write, + DC: OutputPin, + CS: OutputPin, + RST: OutputPin, +{ + type Error = (); + type Color = Rgb565; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for Pixel(coord, color) in pixels.into_iter() { + // Only draw pixels that would be on screen + if coord.x >= 0 + && coord.y >= 0 + && coord.x < self.width as i32 + && coord.y < self.height as i32 + { + self.set_pixel( + coord.x as u16, + coord.y as u16, + RawU16::from(color).into_inner(), + )?; + } + } + + Ok(()) + } + + fn fill_contiguous(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error> + where + I: IntoIterator, + { + // Clamp area to drawable part of the display target + let drawable_area = area.intersection(&Rectangle::new(Point::zero(), self.size())); + + if drawable_area.size != Size::zero() { + self.set_pixels_buffered( + drawable_area.top_left.x as u16, + drawable_area.top_left.y as u16, + (drawable_area.top_left.x + (drawable_area.size.width - 1) as i32) as u16, + (drawable_area.top_left.y + (drawable_area.size.height - 1) as i32) as u16, + area.points() + .zip(colors) + .filter(|(pos, _color)| drawable_area.contains(*pos)) + .map(|(_pos, color)| RawU16::from(color).into_inner()), + )?; + } + + Ok(()) + } + + fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> { + self.set_pixels_buffered( + 0, + 0, + self.width as u16 - 1, + self.height as u16 - 1, + core::iter::repeat(RawU16::from(color).into_inner()) + .take((self.width * self.height) as usize), + ) + } +} + + +impl OriginDimensions for GC9A01A +where + SPI: spi::Write, + DC: OutputPin, + CS: OutputPin, + RST: OutputPin, +{ + fn size(&self) -> Size { + Size::new(self.width, self.height) + } +} diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs new file mode 100644 index 00000000..fe8a916e --- /dev/null +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -0,0 +1,166 @@ +//! Example of graphics on the LCD of the Waveshare RP2040-LCD-0.96 +//! +//! Draws a red and green line with a blue rectangle. +//! After that it fills the screen line for line, at the end it starts over with +//! another colour, RED, GREEN and BLUE. +#![no_std] +#![no_main] + +mod gc9a01a_driver; + +use cortex_m::delay::Delay; +use embedded_graphics::primitives::Line; +use fugit::RateExtU32; + +use panic_halt as _; + +use waveshare_rp2040_lcd_1_28::entry; +use waveshare_rp2040_lcd_1_28::{ + hal::{ + self, + clocks::{init_clocks_and_plls, Clock}, + pac, + pio::PIOExt, + watchdog::Watchdog, + Sio, + }, + Pins, XOSC_CRYSTAL_FREQ, +}; + +use embedded_hal::PwmPin; + +use embedded_graphics::{ + pixelcolor::Rgb565, + prelude::*, + primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle}, +}; +use gc9a01a_driver::{Orientation, GC9A01A}; + +const LCD_WIDTH: u32 = 240; +const LCD_HEIGHT: u32 = 240; + +#[entry] +fn main() -> ! { + let mut pac = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::take().unwrap(); + + let mut watchdog = Watchdog::new(pac.WATCHDOG); + + let clocks = init_clocks_and_plls( + XOSC_CRYSTAL_FREQ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + let sio = Sio::new(pac.SIO); + let pins = Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + // Set up the delay for the first core. + let sys_freq = clocks.system_clock.freq().to_Hz(); + let mut delay = Delay::new(core.SYST, sys_freq); + + let (mut _pio, _sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS); + + // https://www.waveshare.com/wiki/RP2040-LCD-0.96 + // ST7735S LCD + let lcd_dc = pins.gp8.into_push_pull_output(); + let lcd_cs = pins.gp9.into_push_pull_output(); + let lcd_clk = pins.gp10.into_function::(); + let lcd_mosi = pins.gp11.into_function::(); + let lcd_rst = pins + .gp12 + .into_push_pull_output_in_state(hal::gpio::PinState::High); + + let mut _lcd_bl = pins + .gp25 + .into_push_pull_output_in_state(hal::gpio::PinState::High); + + let spi = hal::Spi::<_, _, _, 8>::new(pac.SPI1, (lcd_mosi, lcd_clk)); + + // Exchange the uninitialised SPI driver for an initialised one + let spi = spi.init( + &mut pac.RESETS, + clocks.peripheral_clock.freq(), + 40.MHz(), + embedded_hal::spi::MODE_0, + ); + + // LCD is a 65K IPS LCD 160x80, color order is BGR and a offset 1,26 pixel. + // LCD controller can correct this by settings the order bit (bit 3) in MADCTL register. + // Also the colours are inverted, LCD controller can also correct this by writing to INVON register with no paramters. + // All this is handled by the ST7735 crate. + let mut display = GC9A01A::new(spi, lcd_dc, lcd_cs, lcd_rst, false, true, LCD_WIDTH, LCD_HEIGHT); + + display.init(&mut delay).unwrap(); + display.set_orientation(&Orientation::Landscape).unwrap(); + + //display.set_offset(1, 26); + + let lcd_zero = Point::zero(); + let lcd_max_corner = Point::new((LCD_WIDTH - 1) as i32, (LCD_HEIGHT - 1) as i32); + + let style = PrimitiveStyleBuilder::new() + .fill_color(Rgb565::BLUE) + .build(); + + Rectangle::with_corners(lcd_zero, lcd_max_corner) + .into_styled(style) + .draw(&mut display) + .unwrap(); + + let style = PrimitiveStyleBuilder::new() + .fill_color(Rgb565::BLACK) + .build(); + + Rectangle::with_corners( + Point::new(1, 1), + Point::new((LCD_WIDTH - 2) as i32, (LCD_HEIGHT - 2) as i32), + ) + .into_styled(style) + .draw(&mut display) + .unwrap(); + + Line::new(lcd_zero, lcd_max_corner) + .into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 1)) + .draw(&mut display) + .unwrap(); + + Line::new( + Point::new(0, (LCD_HEIGHT - 1) as i32), + Point::new((LCD_WIDTH - 1) as i32, 0), + ) + .into_styled(PrimitiveStyle::with_stroke(Rgb565::GREEN, 1)) + .draw(&mut display) + .unwrap(); + + // Infinite colour wheel loop + let mut l: i32 = 0; + let mut c = Rgb565::RED; + loop { + Line::new(Point::new(0, l), Point::new((LCD_WIDTH - 1) as i32, l)) + .into_styled(PrimitiveStyle::with_stroke(c, 1)) + .draw(&mut display) + .unwrap(); + delay.delay_ms(10); + l += 1; + if l == LCD_HEIGHT as i32 { + l = 0; + c = match c { + Rgb565::RED => Rgb565::GREEN, + Rgb565::GREEN => Rgb565::BLUE, + _ => Rgb565::RED, + } + } + } +} diff --git a/boards/waveshare-rp2040-lcd-1-28/src/lib.rs b/boards/waveshare-rp2040-lcd-1-28/src/lib.rs new file mode 100644 index 00000000..3556a898 --- /dev/null +++ b/boards/waveshare-rp2040-lcd-1-28/src/lib.rs @@ -0,0 +1,785 @@ +#![no_std] + +pub extern crate rp2040_hal as hal; + +#[cfg(feature = "rt")] +extern crate cortex_m_rt; +#[cfg(feature = "rt")] +pub use hal::entry; + +/// The linker will place this boot block at the start of our program image. We +/// need this to help the ROM bootloader get our code up and running. +#[cfg(feature = "boot2")] +#[link_section = ".boot2"] +#[no_mangle] +#[used] +pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +pub use hal::pac; + +hal::bsp_pins!( + /// GPIO 0 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 RX` | [crate::Gp0Spi0Rx] | + /// | `UART0 TX` | [crate::Gp0Uart0Tx] | + /// | `I2C0 SDA` | [crate::Gp0I2C0Sda] | + /// | `PWM0 A` | [crate::Gp0Pwm0A] | + /// | `PIO0` | [crate::Gp0Pio0] | + /// | `PIO1` | [crate::Gp0Pio1] | + Gpio0 { + name: gp0, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio0]. + FunctionUart, PullNone: Gp0Uart0Tx, + /// SPI Function alias for pin [crate::Pins::gpio0]. + FunctionSpi, PullNone: Gp0Spi0Rx, + /// I2C Function alias for pin [crate::Pins::gpio0]. + FunctionI2C, PullUp: Gp0I2C0Sda, + /// PWM Function alias for pin [crate::Pins::gpio0]. + FunctionPwm, PullNone: Gp0Pwm0A, + /// PIO0 Function alias for pin [crate::Pins::gpio0]. + FunctionPio0, PullNone: Gp0Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio0]. + FunctionPio1, PullNone: Gp0Pio1 + } + }, + + /// GPIO 1 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 CSn` | [crate::Gp1Spi0Csn] | + /// | `UART0 RX` | [crate::Gp1Uart0Rx] | + /// | `I2C0 SCL` | [crate::Gp1I2C0Scl] | + /// | `PWM0 B` | [crate::Gp1Pwm0B] | + /// | `PIO0` | [crate::Gp1Pio0] | + /// | `PIO1` | [crate::Gp1Pio1] | + Gpio1 { + name: gp1, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio1]. + FunctionUart, PullNone: Gp1Uart0Rx, + /// SPI Function alias for pin [crate::Pins::gpio1]. + FunctionSpi, PullNone: Gp1Spi0Csn, + /// I2C Function alias for pin [crate::Pins::gpio1]. + FunctionI2C, PullUp: Gp1I2C0Scl, + /// PWM Function alias for pin [crate::Pins::gpio1]. + FunctionPwm, PullNone: Gp1Pwm0B, + /// PIO0 Function alias for pin [crate::Pins::gpio1]. + FunctionPio0, PullNone: Gp1Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio1]. + FunctionPio1, PullNone: Gp1Pio1 + } + }, + + /// GPIO 2 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 SCK` | [crate::Gp2Spi0Sck] | + /// | `UART0 CTS` | [crate::Gp2Uart0Cts] | + /// | `I2C1 SDA` | [crate::Gp2I2C1Sda] | + /// | `PWM1 A` | [crate::Gp2Pwm1A] | + /// | `PIO0` | [crate::Gp2Pio0] | + /// | `PIO1` | [crate::Gp2Pio1] | + Gpio2 { + name: gp2, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio2]. + FunctionUart, PullNone: Gp2Uart0Cts, + /// SPI Function alias for pin [crate::Pins::gpio2]. + FunctionSpi, PullNone: Gp2Spi0Sck, + /// I2C Function alias for pin [crate::Pins::gpio2]. + FunctionI2C, PullUp: Gp2I2C1Sda, + /// PWM Function alias for pin [crate::Pins::gpio2]. + FunctionPwm, PullNone: Gp2Pwm1A, + /// PIO0 Function alias for pin [crate::Pins::gpio2]. + FunctionPio0, PullNone: Gp2Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio2]. + FunctionPio1, PullNone: Gp2Pio1 + } + }, + + /// GPIO 3 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 TX` | [crate::Gp3Spi0Tx] | + /// | `UART0 RTS` | [crate::Gp3Uart0Rts] | + /// | `I2C1 SCL` | [crate::Gp3I2C1Scl] | + /// | `PWM1 B` | [crate::Gp3Pwm1B] | + /// | `PIO0` | [crate::Gp3Pio0] | + /// | `PIO1` | [crate::Gp3Pio1] | + Gpio3 { + name: gp3, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio3]. + FunctionUart, PullNone: Gp3Uart0Rts, + /// SPI Function alias for pin [crate::Pins::gpio3]. + FunctionSpi, PullNone: Gp3Spi0Tx, + /// I2C Function alias for pin [crate::Pins::gpio3]. + FunctionI2C, PullUp: Gp3I2C1Scl, + /// PWM Function alias for pin [crate::Pins::gpio3]. + FunctionPwm, PullNone: Gp3Pwm1B, + /// PIO0 Function alias for pin [crate::Pins::gpio3]. + FunctionPio0, PullNone: Gp3Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio3]. + FunctionPio1, PullNone: Gp3Pio1 + } + }, + + /// GPIO 4 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 RX` | [crate::Gp4Spi0Rx] | + /// | `UART1 TX` | [crate::Gp4Uart1Tx] | + /// | `I2C0 SDA` | [crate::Gp4I2C0Sda] | + /// | `PWM2 A` | [crate::Gp4Pwm2A] | + /// | `PIO0` | [crate::Gp4Pio0] | + /// | `PIO1` | [crate::Gp4Pio1] | + Gpio4 { + name: gp4, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio4]. + FunctionUart, PullNone: Gp4Uart1Tx, + /// SPI Function alias for pin [crate::Pins::gpio4]. + FunctionSpi, PullNone: Gp4Spi0Rx, + /// I2C Function alias for pin [crate::Pins::gpio4]. + FunctionI2C, PullUp: Gp4I2C0Sda, + /// PWM Function alias for pin [crate::Pins::gpio4]. + FunctionPwm, PullNone: Gp4Pwm2A, + /// PIO0 Function alias for pin [crate::Pins::gpio4]. + FunctionPio0, PullNone: Gp4Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio4]. + FunctionPio1, PullNone: Gp4Pio1 + } + }, + + /// GPIO 5 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 CSn` | [crate::Gp5Spi0Csn] | + /// | `UART1 RX` | [crate::Gp5Uart1Rx] | + /// | `I2C0 SCL` | [crate::Gp5I2C0Scl] | + /// | `PWM2 B` | [crate::Gp5Pwm2B] | + /// | `PIO0` | [crate::Gp5Pio0] | + /// | `PIO1` | [crate::Gp5Pio1] | + Gpio5 { + name: gp5, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio5]. + FunctionUart, PullNone: Gp5Uart1Rx, + /// SPI Function alias for pin [crate::Pins::gpio5]. + FunctionSpi, PullNone: Gp5Spi0Csn, + /// I2C Function alias for pin [crate::Pins::gpio5]. + FunctionI2C, PullUp: Gp5I2C0Scl, + /// PWM Function alias for pin [crate::Pins::gpio5]. + FunctionPwm, PullNone: Gp5Pwm2B, + /// PIO0 Function alias for pin [crate::Pins::gpio5]. + FunctionPio0, PullNone: Gp5Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio5]. + FunctionPio1, PullNone: Gp5Pio1 + } + }, + + /// GPIO 6 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 SCK` | [crate::Gp6Spi0Sck] | + /// | `UART1 CTS` | [crate::Gp6Uart1Cts] | + /// | `I2C1 SDA` | [crate::Gp6I2C1Sda] | + /// | `PWM3 A` | [crate::Gp6Pwm3A] | + /// | `PIO0` | [crate::Gp6Pio0] | + /// | `PIO1` | [crate::Gp6Pio1] | + Gpio6 { + name: gp6, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio6]. + FunctionUart, PullNone: Gp6Uart1Cts, + /// SPI Function alias for pin [crate::Pins::gpio6]. + FunctionSpi, PullNone: Gp6Spi0Sck, + /// I2C Function alias for pin [crate::Pins::gpio6]. + FunctionI2C, PullUp: Gp6I2C1Sda, + /// PWM Function alias for pin [crate::Pins::gpio6]. + FunctionPwm, PullNone: Gp6Pwm3A, + /// PIO0 Function alias for pin [crate::Pins::gpio6]. + FunctionPio0, PullNone: Gp6Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio6]. + FunctionPio1, PullNone: Gp6Pio1 + } + }, + + /// GPIO 7 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 TX` | [crate::Gp7Spi0Tx] | + /// | `UART1 RTS` | [crate::Gp7Uart1Rts] | + /// | `I2C1 SCL` | [crate::Gp7I2C1Scl] | + /// | `PWM3 B` | [crate::Gp7Pwm3B] | + /// | `PIO0` | [crate::Gp7Pio0] | + /// | `PIO1` | [crate::Gp7Pio1] | + Gpio7 { + name: gp7, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio7]. + FunctionUart, PullNone: Gp7Uart1Rts, + /// SPI Function alias for pin [crate::Pins::gpio7]. + FunctionSpi, PullNone: Gp7Spi0Tx, + /// I2C Function alias for pin [crate::Pins::gpio7]. + FunctionI2C, PullUp: Gp7I2C1Scl, + /// PWM Function alias for pin [crate::Pins::gpio7]. + FunctionPwm, PullNone: Gp7Pwm3B, + /// PIO0 Function alias for pin [crate::Pins::gpio7]. + FunctionPio0, PullNone: Gp7Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio7]. + FunctionPio1, PullNone: Gp7Pio1 + } + }, + + /// GPIO 8 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 RX` | [crate::Gp8Spi1Rx] | + /// | `UART1 TX` | [crate::Gp8Uart1Tx] | + /// | `I2C0 SDA` | [crate::Gp8I2C0Sda] | + /// | `PWM4 A` | [crate::Gp8Pwm4A] | + /// | `PIO0` | [crate::Gp8Pio0] | + /// | `PIO1` | [crate::Gp8Pio1] | + Gpio8 { + name: gp8, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio8]. + FunctionUart, PullNone: Gp8Uart1Tx, + /// SPI Function alias for pin [crate::Pins::gpio8]. + FunctionSpi, PullNone: Gp8Spi1Rx, + /// I2C Function alias for pin [crate::Pins::gpio8]. + FunctionI2C, PullUp: Gp8I2C0Sda, + /// PWM Function alias for pin [crate::Pins::gpio8]. + FunctionPwm, PullNone: Gp8Pwm4A, + /// PIO0 Function alias for pin [crate::Pins::gpio8]. + FunctionPio0, PullNone: Gp8Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio8]. + FunctionPio1, PullNone: Gp8Pio1 + } + }, + + /// GPIO 9 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 CSn` | [crate::Gp9Spi1Csn] | + /// | `UART1 RX` | [crate::Gp9Uart1Rx] | + /// | `I2C0 SCL` | [crate::Gp9I2C0Scl] | + /// | `PWM4 B` | [crate::Gp9Pwm4B] | + /// | `PIO0` | [crate::Gp9Pio0] | + /// | `PIO1` | [crate::Gp9Pio1] | + Gpio9 { + name: gp9, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio9]. + FunctionUart, PullNone: Gp9Uart1Rx, + /// SPI Function alias for pin [crate::Pins::gpio9]. + FunctionSpi, PullNone: Gp9Spi1Csn, + /// I2C Function alias for pin [crate::Pins::gpio9]. + FunctionI2C, PullUp: Gp9I2C0Scl, + /// PWM Function alias for pin [crate::Pins::gpio9]. + FunctionPwm, PullNone: Gp9Pwm4B, + /// PIO0 Function alias for pin [crate::Pins::gpio9]. + FunctionPio0, PullNone: Gp9Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio9]. + FunctionPio1, PullNone: Gp9Pio1 + } + }, + + /// GPIO 10 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 SCK` | [crate::Gp10Spi1Sck] | + /// | `UART1 CTS` | [crate::Gp10Uart1Cts] | + /// | `I2C1 SDA` | [crate::Gp10I2C1Sda] | + /// | `PWM5 A` | [crate::Gp10Pwm5A] | + /// | `PIO0` | [crate::Gp10Pio0] | + /// | `PIO1` | [crate::Gp10Pio1] | + Gpio10 { + name: gp10, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio10]. + FunctionUart, PullNone: Gp10Uart1Cts, + /// SPI Function alias for pin [crate::Pins::gpio10]. + FunctionSpi, PullNone: Gp10Spi1Sck, + /// I2C Function alias for pin [crate::Pins::gpio10]. + FunctionI2C, PullUp: Gp10I2C1Sda, + /// PWM Function alias for pin [crate::Pins::gpio10]. + FunctionPwm, PullNone: Gp10Pwm5A, + /// PIO0 Function alias for pin [crate::Pins::gpio10]. + FunctionPio0, PullNone: Gp10Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio10]. + FunctionPio1, PullNone: Gp10Pio1 + } + }, + + /// GPIO 11 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 TX` | [crate::Gp11Spi1Tx] | + /// | `UART1 RTS` | [crate::Gp11Uart1Rts] | + /// | `I2C1 SCL` | [crate::Gp11I2C1Scl] | + /// | `PWM5 B` | [crate::Gp11Pwm5B] | + /// | `PIO0` | [crate::Gp11Pio0] | + /// | `PIO1` | [crate::Gp11Pio1] | + Gpio11 { + name: gp11, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio11]. + FunctionUart, PullNone: Gp11Uart1Rts, + /// SPI Function alias for pin [crate::Pins::gpio11]. + FunctionSpi, PullNone: Gp11Spi1Tx, + /// I2C Function alias for pin [crate::Pins::gpio11]. + FunctionI2C, PullUp: Gp11I2C1Scl, + /// PWM Function alias for pin [crate::Pins::gpio11]. + FunctionPwm, PullNone: Gp11Pwm5B, + /// PIO0 Function alias for pin [crate::Pins::gpio11]. + FunctionPio0, PullNone: Gp11Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio11]. + FunctionPio1, PullNone: Gp11Pio1 + } + }, + + /// GPIO 12 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 RX` | [crate::Gp12Spi1Rx] | + /// | `UART0 TX` | [crate::Gp12Uart0Tx] | + /// | `I2C0 SDA` | [crate::Gp12I2C0Sda] | + /// | `PWM6 A` | [crate::Gp12Pwm6A] | + /// | `PIO0` | [crate::Gp12Pio0] | + /// | `PIO1` | [crate::Gp12Pio1] | + Gpio12 { + name: gp12, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio12]. + FunctionUart, PullNone: Gp12Uart0Tx, + /// SPI Function alias for pin [crate::Pins::gpio12]. + FunctionSpi, PullNone: Gp12Spi1Rx, + /// I2C Function alias for pin [crate::Pins::gpio12]. + FunctionI2C, PullUp: Gp12I2C0Sda, + /// PWM Function alias for pin [crate::Pins::gpio12]. + FunctionPwm, PullNone: Gp12Pwm6A, + /// PIO0 Function alias for pin [crate::Pins::gpio12]. + FunctionPio0, PullNone: Gp12Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio12]. + FunctionPio1, PullNone: Gp12Pio1 + } + }, + + /// GPIO 13 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 CSn` | [crate::Gp13Spi1Csn] | + /// | `UART0 RX` | [crate::Gp13Uart0Rx] | + /// | `I2C0 SCL` | [crate::Gp13I2C0Scl] | + /// | `PWM6 B` | [crate::Gp13Pwm6B] | + /// | `PIO0` | [crate::Gp13Pio0] | + /// | `PIO1` | [crate::Gp13Pio1] | + Gpio13 { + name: gp13, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio13]. + FunctionUart, PullNone: Gp13Uart0Rx, + /// SPI Function alias for pin [crate::Pins::gpio13]. + FunctionSpi, PullNone: Gp13Spi1Csn, + /// I2C Function alias for pin [crate::Pins::gpio13]. + FunctionI2C, PullUp: Gp13I2C0Scl, + /// PWM Function alias for pin [crate::Pins::gpio13]. + FunctionPwm, PullNone: Gp13Pwm6B, + /// PIO0 Function alias for pin [crate::Pins::gpio13]. + FunctionPio0, PullNone: Gp13Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio13]. + FunctionPio1, PullNone: Gp13Pio1 + } + }, + + /// GPIO 14 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 SCK` | [crate::Gp14Spi1Sck] | + /// | `UART0 CTS` | [crate::Gp14Uart0Cts] | + /// | `I2C1 SDA` | [crate::Gp14I2C1Sda] | + /// | `PWM7 A` | [crate::Gp14Pwm7A] | + /// | `PIO0` | [crate::Gp14Pio0] | + /// | `PIO1` | [crate::Gp14Pio1] | + Gpio14 { + name: gp14, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio14]. + FunctionUart, PullNone: Gp14Uart0Cts, + /// SPI Function alias for pin [crate::Pins::gpio14]. + FunctionSpi, PullNone: Gp14Spi1Sck, + /// I2C Function alias for pin [crate::Pins::gpio14]. + FunctionI2C, PullUp: Gp14I2C1Sda, + /// PWM Function alias for pin [crate::Pins::gpio14]. + FunctionPwm, PullNone: Gp14Pwm7A, + /// PIO0 Function alias for pin [crate::Pins::gpio14]. + FunctionPio0, PullNone: Gp14Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio14]. + FunctionPio1, PullNone: Gp14Pio1 + } + }, + + /// GPIO 15 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 TX` | [crate::Gp15Spi1Tx] | + /// | `UART0 RTS` | [crate::Gp15Uart0Rts] | + /// | `I2C1 SCL` | [crate::Gp15I2C1Scl] | + /// | `PWM7 B` | [crate::Gp15Pwm7B] | + /// | `PIO0` | [crate::Gp15Pio0] | + /// | `PIO1` | [crate::Gp15Pio1] | + Gpio15 { + name: gp15, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio15]. + FunctionUart, PullNone: Gp15Uart0Rts, + /// SPI Function alias for pin [crate::Pins::gpio15]. + FunctionSpi, PullNone: Gp15Spi1Tx, + /// I2C Function alias for pin [crate::Pins::gpio15]. + FunctionI2C, PullUp: Gp15I2C1Scl, + /// PWM Function alias for pin [crate::Pins::gpio15]. + FunctionPwm, PullNone: Gp15Pwm7B, + /// PIO0 Function alias for pin [crate::Pins::gpio15]. + FunctionPio0, PullNone: Gp15Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio15]. + FunctionPio1, PullNone: Gp15Pio1 + } + }, + + /// GPIO 16 is connected internally to a single Neopixel RGB LED + Gpio16 { name: neopixel }, + + /// GPIO 17 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 CSn` | [crate::Gp17Spi0Csn] | + /// | `UART0 RX` | [crate::Gp17Uart0Rx] | + /// | `I2C0 SCL` | [crate::Gp17I2C0Scl] | + /// | `PWM0 B` | [crate::Gp17Pwm0B] | + /// | `PIO0` | [crate::Gp17Pio0] | + /// | `PIO1` | [crate::Gp17Pio1] | + Gpio17 { + name: gpio17, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio17]. + FunctionUart, PullNone: Gp17Uart0Rx, + /// SPI Function alias for pin [crate::Pins::gpio17]. + FunctionSpi, PullNone: Gp17Spi0Csn, + /// I2C Function alias for pin [crate::Pins::gpio17]. + FunctionI2C, PullUp: Gp17I2C0Scl, + /// PWM Function alias for pin [crate::Pins::gpio17]. + FunctionPwm, PullNone: Gp17Pwm0B, + /// PIO0 Function alias for pin [crate::Pins::gpio17]. + FunctionPio0, PullNone: Gp17Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio17]. + FunctionPio1, PullNone: Gp17Pio1 + } + }, + + /// GPIO 18 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 SCK` | [crate::Gp18Spi0Sck] | + /// | `UART0 CTS` | [crate::Gp18Uart0Cts] | + /// | `I2C1 SDA` | [crate::Gp18I2C1Sda] | + /// | `PWM1 A` | [crate::Gp18Pwm1A] | + /// | `PIO0` | [crate::Gp18Pio0] | + /// | `PIO1` | [crate::Gp18Pio1] | + Gpio18 { + name: gpio18, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio18]. + FunctionUart, PullNone: Gp18Uart0Cts, + /// SPI Function alias for pin [crate::Pins::gpio18]. + FunctionSpi, PullNone: Gp18Spi0Sck, + /// I2C Function alias for pin [crate::Pins::gpio18]. + FunctionI2C, PullUp: Gp18I2C1Sda, + /// PWM Function alias for pin [crate::Pins::gpio18]. + FunctionPwm, PullNone: Gp18Pwm1A, + /// PIO0 Function alias for pin [crate::Pins::gpio18]. + FunctionPio0, PullNone: Gp18Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio18]. + FunctionPio1, PullNone: Gp18Pio1 + } + }, + + /// GPIO 19 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 TX` | [crate::Gp19Spi0Tx] | + /// | `UART0 RTS` | [crate::Gp19Uart0Rts] | + /// | `I2C1 SCL` | [crate::Gp19I2C1Scl] | + /// | `PWM1 B` | [crate::Gp19Pwm1B] | + /// | `PIO0` | [crate::Gp19Pio0] | + /// | `PIO1` | [crate::Gp19Pio1] | + Gpio19 { + name: gpio19, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio19]. + FunctionUart, PullNone: Gp19Uart0Rts, + /// SPI Function alias for pin [crate::Pins::gpio19]. + FunctionSpi, PullNone: Gp19Spi0Tx, + /// I2C Function alias for pin [crate::Pins::gpio19]. + FunctionI2C, PullUp: Gp19I2C1Scl, + /// PWM Function alias for pin [crate::Pins::gpio19]. + FunctionPwm, PullNone: Gp19Pwm1B, + /// PIO0 Function alias for pin [crate::Pins::gpio19]. + FunctionPio0, PullNone: Gp19Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio19]. + FunctionPio1, PullNone: Gp19Pio1 + } + }, + + /// GPIO 20 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 RX` | [crate::Gp20Spi0Rx] | + /// | `UART1 TX` | [crate::Gp20Uart1Tx] | + /// | `I2C0 SDA` | [crate::Gp20I2C0Sda] | + /// | `PWM2 A` | [crate::Gp20Pwm2A] | + /// | `PIO0` | [crate::Gp20Pio0] | + /// | `PIO1` | [crate::Gp20Pio1] | + Gpio20 { + name: gpio20, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio20]. + FunctionUart, PullNone: Gp20Uart1Tx, + /// SPI Function alias for pin [crate::Pins::gpio20]. + FunctionSpi, PullNone: Gp20Spi0Rx, + /// I2C Function alias for pin [crate::Pins::gpio20]. + FunctionI2C, PullUp: Gp20I2C0Sda, + /// PWM Function alias for pin [crate::Pins::gpio20]. + FunctionPwm, PullNone: Gp20Pwm2A, + /// PIO0 Function alias for pin [crate::Pins::gpio20]. + FunctionPio0, PullNone: Gp20Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio20]. + FunctionPio1, PullNone: Gp20Pio1 + } + }, + + /// GPIO 21 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 CSn` | [crate::Gp21Spi0Csn] | + /// | `UART1 RX` | [crate::Gp21Uart1Rx] | + /// | `I2C0 SCL` | [crate::Gp21I2C0Scl] | + /// | `PWM2 B` | [crate::Gp21Pwm2B] | + /// | `PIO0` | [crate::Gp21Pio0] | + /// | `PIO1` | [crate::Gp21Pio1] | + Gpio21 { + name: gpio21, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio21]. + FunctionUart, PullNone: Gp21Uart1Rx, + /// SPI Function alias for pin [crate::Pins::gpio21]. + FunctionSpi, PullNone: Gp21Spi0Csn, + /// I2C Function alias for pin [crate::Pins::gpio21]. + FunctionI2C, PullUp: Gp21I2C0Scl, + /// PWM Function alias for pin [crate::Pins::gpio21]. + FunctionPwm, PullNone: Gp21Pwm2B, + /// PIO0 Function alias for pin [crate::Pins::gpio21]. + FunctionPio0, PullNone: Gp21Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio21]. + FunctionPio1, PullNone: Gp21Pio1 + } + }, + + /// GPIO 22 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI0 SCK` | [crate::Gp22Spi0Sck] | + /// | `UART1 CTS` | [crate::Gp22Uart1Cts] | + /// | `I2C1 SDA` | [crate::Gp22I2C1Sda] | + /// | `PWM3 A` | [crate::Gp22Pwm3A] | + /// | `PIO0` | [crate::Gp22Pio0] | + /// | `PIO1` | [crate::Gp22Pio1] | + Gpio22 { + name: gpio22, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio22]. + FunctionUart, PullNone: Gp22Uart1Cts, + /// SPI Function alias for pin [crate::Pins::gpio22]. + FunctionSpi, PullNone: Gp22Spi0Sck, + /// I2C Function alias for pin [crate::Pins::gpio22]. + FunctionI2C, PullUp: Gp22I2C1Sda, + /// PWM Function alias for pin [crate::Pins::gpio22]. + FunctionPwm, PullNone: Gp22Pwm3A, + /// PIO0 Function alias for pin [crate::Pins::gpio22]. + FunctionPio0, PullNone: Gp22Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio22]. + FunctionPio1, PullNone: Gp22Pio1 + } + }, + + /// GPIO 23 + Gpio23 { + name: gp23, + aliases: { + FunctionUart, PullNone: Gp23Uart1Rts, + FunctionSpi, PullNone: Gp23Spi0Tx, + FunctionI2C, PullUp: Gp23I2C1Scl, + FunctionPwm, PullNone: Gp23Pwm3B, + FunctionPio0, PullNone: Gp23Pio0, + FunctionPio1, PullNone: Gp23Pio1 + } + }, + + /// GPIO 24 + Gpio24 { + name: gp24, + aliases: { + FunctionUart, PullNone: Gp24Uart1Tx, + FunctionSpi, PullNone: Gp24Spi1Rx, + FunctionI2C, PullUp: Gp24I2C0Sda, + FunctionPwm, PullNone: Gp24Pwm4A, + FunctionPio0, PullNone: Gp24Pio0, + FunctionPio1, PullNone: Gp24Pio1 + } + }, + + /// GPIO 25 + Gpio25 { + name: gp25, + aliases: { + FunctionUart, PullNone: Gp25Uart1Rx, + FunctionSpi, PullNone: Gp25Spi1Csn, + FunctionI2C, PullUp: Gp25I2C0Scl, + FunctionPwm, PullNone: Gp25Pwm4B, + FunctionPio0, PullNone: Gp25Pio0, + FunctionPio1, PullNone: Gp25Pio1 + } + }, + + /// GPIO 26 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 SCK` | [crate::Gp26Spi1Sck] | + /// | `UART1 CTS` | [crate::Gp26Uart1Cts] | + /// | `I2C1 SDA` | [crate::Gp26I2C1Sda] | + /// | `PWM5 A` | [crate::Gp26Pwm5A] | + /// | `PIO0` | [crate::Gp26Pio0] | + /// | `PIO1` | [crate::Gp26Pio1] | + /// + /// ADC0 + Gpio26 { + name: gp26, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio26]. + FunctionUart, PullNone: Gp26Uart1Cts, + /// SPI Function alias for pin [crate::Pins::gpio26]. + FunctionSpi, PullNone: Gp26Spi1Sck, + /// I2C Function alias for pin [crate::Pins::gpio26]. + FunctionI2C, PullUp: Gp26I2C1Sda, + /// PWM Function alias for pin [crate::Pins::gpio26]. + FunctionPwm, PullNone: Gp26Pwm5A, + /// PIO0 Function alias for pin [crate::Pins::gpio26]. + FunctionPio0, PullNone: Gp26Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio26]. + FunctionPio1, PullNone: Gp26Pio1 + } + }, + + /// GPIO 27 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 TX` | [crate::Gp27Spi1Tx] | + /// | `UART1 RTS` | [crate::Gp27Uart1Rts] | + /// | `I2C1 SCL` | [crate::Gp27I2C1Scl] | + /// | `PWM5 B` | [crate::Gp27Pwm5B] | + /// | `PIO0` | [crate::Gp27Pio0] | + /// | `PIO1` | [crate::Gp27Pio1] | + /// + /// ADC1 + Gpio27 { + name: gp27, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio27]. + FunctionUart, PullNone: Gp27Uart1Rts, + /// SPI Function alias for pin [crate::Pins::gpio27]. + FunctionSpi, PullNone: Gp27Spi1Tx, + /// I2C Function alias for pin [crate::Pins::gpio27]. + FunctionI2C, PullUp: Gp27I2C1Scl, + /// PWM Function alias for pin [crate::Pins::gpio27]. + FunctionPwm, PullNone: Gp27Pwm5B, + /// PIO0 Function alias for pin [crate::Pins::gpio27]. + FunctionPio0, PullNone: Gp27Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio27]. + FunctionPio1, PullNone: Gp27Pio1 + } + }, + + /// GPIO 28 supports following functions: + /// + /// | Function | Alias with applied function | + /// |--------------|-----------------------------| + /// | `SPI1 RX` | [crate::Gp28Spi1Rx] | + /// | `UART0 TX` | [crate::Gp28Uart0Tx] | + /// | `I2C0 SDA` | [crate::Gp28I2C0Sda] | + /// | `PWM6 A` | [crate::Gp28Pwm6A] | + /// | `PIO0` | [crate::Gp28Pio0] | + /// | `PIO1` | [crate::Gp28Pio1] | + /// + /// ADC2 + Gpio28 { + name: gp28, + aliases: { + /// UART Function alias for pin [crate::Pins::gpio28]. + FunctionUart, PullNone: Gp28Uart0Tx, + /// SPI Function alias for pin [crate::Pins::gpio28]. + FunctionSpi, PullNone: Gp28Spi1Rx, + /// I2C Function alias for pin [crate::Pins::gpio28]. + FunctionI2C, PullUp: Gp28I2C0Sda, + /// PWM Function alias for pin [crate::Pins::gpio28]. + FunctionPwm, PullNone: Gp28Pwm6A, + /// PIO0 Function alias for pin [crate::Pins::gpio28]. + FunctionPio0, PullNone: Gp28Pio0, + /// PIO1 Function alias for pin [crate::Pins::gpio28]. + FunctionPio1, PullNone: Gp28Pio1 + } + }, + + /// GPIO 29 + /// + /// ADC3 + Gpio29 { + name: gp29, + aliases: { + FunctionUart, PullNone: Gp29Uart0Rx, + FunctionSpi, PullNone: Gp29Spi1Csn, + FunctionI2C, PullUp: Gp29I2C0Scl, + FunctionPwm, PullNone: Gp29Pwm6B, + FunctionPio0, PullNone: Gp29Pio0, + FunctionPio1, PullNone: Gp29Pio1 + } + }, +); + +pub const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; From 60831590a80775b703295ae222ba514d09a6ddf3 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sat, 20 Jul 2024 10:56:44 -0700 Subject: [PATCH 02/23] Fixed the static on the screen. Set the backlight low (off) then clear the screen to black, initialize the screen, set the backlight high (on). This prevents the unset memory as showing as static on the screen. --- Cargo.toml | 1 + .../examples/waveshare_rp2040_lcd_demo.rs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c6a0cbee..347d95a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "boards/vcc-gnd-yd-rp2040", "boards/waveshare-rp2040-zero", "boards/waveshare-rp2040-lcd-0-96", + "boards/waveshare-rp2040-lcd-1-28", ] [workspace.dependencies] diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index fe8a916e..555f1ec8 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -84,7 +84,7 @@ fn main() -> ! { let mut _lcd_bl = pins .gp25 - .into_push_pull_output_in_state(hal::gpio::PinState::High); + .into_push_pull_output_in_state(hal::gpio::PinState::Low); let spi = hal::Spi::<_, _, _, 8>::new(pac.SPI1, (lcd_mosi, lcd_clk)); @@ -103,7 +103,7 @@ fn main() -> ! { let mut display = GC9A01A::new(spi, lcd_dc, lcd_cs, lcd_rst, false, true, LCD_WIDTH, LCD_HEIGHT); display.init(&mut delay).unwrap(); - display.set_orientation(&Orientation::Landscape).unwrap(); + //display.set_orientation(&Orientation::Landscape).unwrap(); //display.set_offset(1, 26); @@ -114,11 +114,19 @@ fn main() -> ! { .fill_color(Rgb565::BLUE) .build(); + display.clear(Rgb565::BLACK); + delay.delay_ms(1000); + // Set the backlight pin high + _lcd_bl.set_high().unwrap(); + delay.delay_ms(1000); + Rectangle::with_corners(lcd_zero, lcd_max_corner) .into_styled(style) .draw(&mut display) .unwrap(); + delay.delay_ms(1000); + let style = PrimitiveStyleBuilder::new() .fill_color(Rgb565::BLACK) .build(); @@ -131,6 +139,8 @@ fn main() -> ! { .draw(&mut display) .unwrap(); + delay.delay_ms(1000); + Line::new(lcd_zero, lcd_max_corner) .into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 1)) .draw(&mut display) From 21dd71492ce3d2c8276d92798e92704df0b250c5 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sat, 20 Jul 2024 10:57:20 -0700 Subject: [PATCH 03/23] Update waveshare_rp2040_lcd_demo.rs Use the V2::outputPin for the backlight. --- .../examples/waveshare_rp2040_lcd_demo.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 555f1ec8..a366a657 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -28,6 +28,7 @@ use waveshare_rp2040_lcd_1_28::{ }; use embedded_hal::PwmPin; +use embedded_hal::digital::v2::OutputPin; use embedded_graphics::{ pixelcolor::Rgb565, From f4ccaa6d81741246b1fb7fb68006ecbeaa83fd58 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sat, 20 Jul 2024 19:47:06 -0700 Subject: [PATCH 04/23] Added image capabilities --- .gitignore | 1 + .../examples/gc9a01a_driver.rs | 19 +++++++++++++++++++ .../examples/waveshare_rp2040_lcd_demo.rs | 13 ++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ef0f5ea4..e10a4319 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ target Cargo.lock .vscode *.orig +*.raw diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs index 6790c2ad..fb7bee59 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs @@ -327,6 +327,23 @@ where self.set_address_window(sx, sy, ex, ey)?; self.write_pixels_buffered(colors) } + + /// Draws an image from a slice of RGB565 data + pub fn draw_image(&mut self, image_data: &[u8]) -> Result<(), ()> { + // Assuming the image dimensions match the display dimensions + let width = self.width as u16; + let height = self.height as u16; + + self.set_address_window(0, 0, width - 1, height - 1)?; + self.write_command(Instruction::RAMWR as u8, &[])?; + self.start_data()?; + + for chunk in image_data.chunks(32) { + self.write_data(chunk)?; + } + + Ok(()) + } } @@ -408,6 +425,8 @@ where .take((self.width * self.height) as usize), ) } + + } diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index a366a657..7ebb2da8 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -115,6 +115,7 @@ fn main() -> ! { .fill_color(Rgb565::BLUE) .build(); + //Please note that you must clear the screen before you turn on the backlight or else the screen will display static. display.clear(Rgb565::BLACK); delay.delay_ms(1000); // Set the backlight pin high @@ -141,7 +142,7 @@ fn main() -> ! { .unwrap(); delay.delay_ms(1000); - + Line::new(lcd_zero, lcd_max_corner) .into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 1)) .draw(&mut display) @@ -155,6 +156,14 @@ fn main() -> ! { .draw(&mut display) .unwrap(); + // Include your RGB565 data from the file + let image_data = include_bytes!("2wd-big-endian.raw"); + + // Draw the image to the display + display.draw_image(image_data).unwrap(); + + delay.delay_ms(10000); + // Infinite colour wheel loop let mut l: i32 = 0; let mut c = Rgb565::RED; @@ -173,5 +182,7 @@ fn main() -> ! { _ => Rgb565::RED, } } + } + } From 6eba90e3415c6d5b4b07dcff05b626959f97e098 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sun, 21 Jul 2024 06:56:13 -0700 Subject: [PATCH 05/23] Added double buffering Added two buffers so that I can swap between them and prevent screen flickering when updating the display. --- boards/waveshare-rp2040-lcd-1-28/Cargo.toml | 1 + .../examples/frame_buffer.rs | 64 ++++++ .../examples/gc9a01a_driver.rs | 19 ++ .../examples/waveshare_rp2040_lcd_demo.rs | 195 +++++++++++++++++- 4 files changed, 272 insertions(+), 7 deletions(-) create mode 100644 boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs diff --git a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml index 98ff2d8d..7c5cdf94 100644 --- a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml +++ b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml @@ -15,6 +15,7 @@ cortex-m-rt = { workspace = true, optional = true } rp2040-boot2 = { workspace = true, optional = true } rp2040-hal.workspace = true embedded-hal = { version = "0.2" } +libm = "0.2" [dev-dependencies] cortex-m.workspace = true diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs b/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs new file mode 100644 index 00000000..b00f9a9c --- /dev/null +++ b/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs @@ -0,0 +1,64 @@ +#![no_std] + +use embedded_graphics::{pixelcolor::Rgb565, prelude::*,}; +use core::convert::Infallible; + +pub struct FrameBuffer { + buffer: &'static mut [u8], + width: u32, + height: u32, +} + +impl FrameBuffer { + pub fn new(buffer: &'static mut [u8], width: u32, height: u32) -> Self { + Self { + buffer, + width, + height, + } + } + + pub fn get_buffer(&self) -> &[u8] { + &self.buffer + } + + pub fn get_mut_buffer(&mut self) -> &mut [u8] { + &mut self.buffer + } + + pub fn clear(&mut self, color: Rgb565) { + let raw_color = color.into_storage(); + for chunk in self.buffer.chunks_exact_mut(2) { + chunk[0] = (raw_color >> 8) as u8; + chunk[1] = raw_color as u8; + } + } +} + +impl DrawTarget for FrameBuffer { + type Color = Rgb565; + type Error = Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for Pixel(coord, color) in pixels { + if coord.x >= 0 && coord.x < self.width as i32 && coord.y >= 0 && coord.y < self.height as i32 { + let index = ((coord.y as u32 * self.width + coord.x as u32) * 2) as usize; + let raw_color = color.into_storage(); + self.buffer[index] = (raw_color >> 8) as u8; + self.buffer[index + 1] = raw_color as u8; + } + } + Ok(()) + } +} + +impl OriginDimensions for FrameBuffer { + fn size(&self) -> Size { + Size::new(self.width, self.height) + } +} + + diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs index fb7bee59..9bdfdc9b 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs @@ -344,6 +344,25 @@ where Ok(()) } + + + pub fn show(&mut self, buffer: &[u8]) -> Result<(), ()> { + self.write_command(Instruction::CASET as u8, &[])?; + self.write_data(&[0x00, 0x00, 0x00, 0xEF])?; + + self.write_command(Instruction::RASET as u8, &[])?; + self.write_data(&[0x00, 0x00, 0x00, 0xEF])?; + + self.write_command(Instruction::RAMWR as u8, &[])?; + + self.cs.set_high().map_err(|_| ())?; + self.dc.set_high().map_err(|_| ())?; + self.cs.set_low().map_err(|_| ())?; + self.spi.write(buffer).map_err(|_| ())?; + self.cs.set_high().map_err(|_| ())?; + + Ok(()) + } } diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 7ebb2da8..8a8cdb92 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -7,11 +7,12 @@ #![no_main] mod gc9a01a_driver; +mod frame_buffer; use cortex_m::delay::Delay; use embedded_graphics::primitives::Line; use fugit::RateExtU32; - +use frame_buffer::FrameBuffer; use panic_halt as _; use waveshare_rp2040_lcd_1_28::entry; @@ -33,13 +34,20 @@ use embedded_hal::digital::v2::OutputPin; use embedded_graphics::{ pixelcolor::Rgb565, prelude::*, - primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle}, + primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, Triangle}, + image::{ImageRaw, Image}, }; +use libm::{cos,sin}; use gc9a01a_driver::{Orientation, GC9A01A}; const LCD_WIDTH: u32 = 240; const LCD_HEIGHT: u32 = 240; +// Define static buffers +static mut FRAME_BUFFER_1: [u8; (LCD_WIDTH * LCD_HEIGHT * 2) as usize] = [0; (LCD_WIDTH * LCD_HEIGHT * 2) as usize]; +static mut FRAME_BUFFER_2: [u8; (LCD_WIDTH * LCD_HEIGHT * 2) as usize] = [0; (LCD_WIDTH * LCD_HEIGHT * 2) as usize]; + + #[entry] fn main() -> ! { let mut pac = pac::Peripherals::take().unwrap(); @@ -106,6 +114,11 @@ fn main() -> ! { display.init(&mut delay).unwrap(); //display.set_orientation(&Orientation::Landscape).unwrap(); + // Create two frame buffers for double buffering + let mut frame_buffer_1 = unsafe { FrameBuffer::new(&mut FRAME_BUFFER_1, LCD_WIDTH, LCD_HEIGHT) }; + let mut frame_buffer_2 = unsafe { FrameBuffer::new(&mut FRAME_BUFFER_2, LCD_WIDTH, LCD_HEIGHT) }; + + //display.set_offset(1, 26); let lcd_zero = Point::zero(); @@ -155,19 +168,93 @@ fn main() -> ! { .into_styled(PrimitiveStyle::with_stroke(Rgb565::GREEN, 1)) .draw(&mut display) .unwrap(); +// Load image data +let image_data = include_bytes!("2wd-big-endian.raw"); + // Copy image data to the frame buffer + //frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); + //frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - // Include your RGB565 data from the file - let image_data = include_bytes!("2wd-big-endian.raw"); + + // Draw image on the frame buffer + let raw_image: ImageRaw = ImageRaw::new(image_data, LCD_WIDTH); + let image = Image::new(&raw_image, Point::zero()); + image.draw(&mut frame_buffer_1).unwrap(); + image.draw(&mut frame_buffer_2).unwrap(); + + display.show(frame_buffer_1.get_buffer()).unwrap(); + + delay.delay_ms(1000); + + let points = [ + Point::new(120, 100), + Point::new(100, 140), + Point::new(140, 140), + ]; + + // Draw a filled triangle on the frame buffers + let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::RED).build(); + Triangle::new(points[0], points[1], points[2]) + .into_styled(style) + .draw(&mut frame_buffer_1) + .unwrap(); + /* + Triangle::new(points[0], points[1], points[2]) + .into_styled(style) + .draw(&mut frame_buffer_2) + .unwrap(); + */ + // Show the initial buffer + display.show(frame_buffer_1.get_buffer()).unwrap(); + delay.delay_ms(1000); // Draw the image to the display - display.draw_image(image_data).unwrap(); + //display.draw_image(image_data).unwrap(); + + // Draw a filled triangle on the frame buffers + let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::GREEN).build(); + Triangle::new(points[0], points[1], points[2]) + .into_styled(style) + .draw(&mut frame_buffer_2) + .unwrap(); + + display.show(frame_buffer_2.get_buffer()).unwrap(); + delay.delay_ms(1000); + - delay.delay_ms(10000); + //Reset the frame buffers. + image.draw(&mut frame_buffer_1).unwrap(); + image.draw(&mut frame_buffer_2).unwrap(); + + // Calculate the center of the image + let arrow_rotate_point_x = 240 / 2; + let arrow_rotate_point_y = (240 / 10) * 8; + create_arrow_image_5(&mut frame_buffer_1, 45, arrow_rotate_point_x,arrow_rotate_point_y ); + create_arrow_image_5(&mut frame_buffer_2, 46, arrow_rotate_point_x,arrow_rotate_point_y ); + //frame_buffer_2.clear(Rgb565::BLACK); + //image.draw(&mut frame_buffer_2).unwrap(); + //frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); // Infinite colour wheel loop let mut l: i32 = 0; + let mut use_first_buffer = true; let mut c = Rgb565::RED; loop { + // Alternate between buffers + // Alternate between buffers + let current_buffer = if use_first_buffer { + frame_buffer_1.get_buffer() + } else { + frame_buffer_2.get_buffer() + }; + + + // Clear the current buffer + //current_buffer.clear(Rgb565::BLACK); + // Call the show function with the buffer to display the image and arrow + display.show(current_buffer).unwrap(); + //delay of 16 for 60Hz or 60 Frames a second. + delay.delay_ms(1000); + /* Line::new(Point::new(0, l), Point::new((LCD_WIDTH - 1) as i32, l)) .into_styled(PrimitiveStyle::with_stroke(c, 1)) .draw(&mut display) @@ -182,7 +269,101 @@ fn main() -> ! { _ => Rgb565::RED, } } - + */ + // Toggle the buffer + use_first_buffer = !use_first_buffer; } } + +fn create_arrow_image_5( + framebuffer: &mut FrameBuffer<>, + angle: i32, + compass_center_x: i32, + compass_center_y: i32, +) { + let compass_center = Point::new(compass_center_x, compass_center_y); + let north_angle = angle - 180; + let south_angle = angle; + let north_left_angle = north_angle - 2; + let north_right_angle = north_angle + 2; + let south_left_angle = south_angle + 10; + let south_right_angle = south_angle - 10; + + let circle_1 = 128; + let circle_2 = 125; + let circle_3 = 36; + let circle_4 = 32; + + let north = get_coordinates(compass_center, circle_1, north_angle); + let south = get_coordinates(compass_center, circle_4, south_angle); + let north_left = get_coordinates(compass_center, circle_2, north_left_angle); + let north_right = get_coordinates(compass_center, circle_2, north_right_angle); + let south_left = get_coordinates(compass_center, circle_3, south_left_angle); + let south_right = get_coordinates(compass_center, circle_3, south_right_angle); + + let merged_points = [ + north, + north_left, + south_left, + south, + south_right, + north_right, + ]; + + let left_points = [ + north, + north_left, + south_left, + south, + Point::zero(), // unused but needed to keep array size fixed + Point::zero(), // unused but needed to keep array size fixed + ]; + + let right_points = [ + north, + north_right, + south_right, + south, + Point::zero(), // unused but needed to keep array size fixed + Point::zero(), // unused but needed to keep array size fixed + ]; + + let red = Rgb565::new(255, 0, 0); + let red_9 = Rgb565::new(174, 31, 31); + + let style_red = PrimitiveStyleBuilder::new().fill_color(red).build(); + let style_red_9 = PrimitiveStyleBuilder::new().fill_color(red_9).build(); + + draw_polygon(framebuffer, &merged_points, style_red_9); + draw_polygon(framebuffer, &left_points[0..4], style_red); + draw_polygon(framebuffer, &right_points[0..4], style_red_9); +} + + +fn draw_polygon( + framebuffer: &mut FrameBuffer<>, + points: &[Point], + style: PrimitiveStyle, +) { + if points.len() < 3 { + return; // Not enough points to form a polygon + } + + // We will use a fan triangulation from the first point + let first_point = points[0]; + + for i in 1..points.len() - 1 { + let triangle = Triangle::new(first_point, points[i], points[i + 1]) + .into_styled(style); + triangle.draw(framebuffer).unwrap(); + } +} + +// Helper function to calculate the coordinates based on the angle and radius +fn get_coordinates(center: Point, radius: i32, angle: i32) -> Point { + let angle_rad = (angle as f32).to_radians() as f64; + let x = center.x + (radius as f32 * cos(angle_rad) as f32) as i32; + let y = center.y + (radius as f32 * sin(angle_rad) as f32) as i32; + Point::new(x, y) +} \ No newline at end of file From 5a82f4fcb53808d18b0201e605870091c70a020c Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sun, 21 Jul 2024 07:20:12 -0700 Subject: [PATCH 06/23] Formatted the code for reading Added comments and formmating. --- .../examples/waveshare_rp2040_lcd_demo.rs | 138 +++++++----------- 1 file changed, 50 insertions(+), 88 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 8a8cdb92..4665a553 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -47,14 +47,16 @@ const LCD_HEIGHT: u32 = 240; static mut FRAME_BUFFER_1: [u8; (LCD_WIDTH * LCD_HEIGHT * 2) as usize] = [0; (LCD_WIDTH * LCD_HEIGHT * 2) as usize]; static mut FRAME_BUFFER_2: [u8; (LCD_WIDTH * LCD_HEIGHT * 2) as usize] = [0; (LCD_WIDTH * LCD_HEIGHT * 2) as usize]; - #[entry] fn main() -> ! { + // Take ownership of peripheral instances let mut pac = pac::Peripherals::take().unwrap(); let core = pac::CorePeripherals::take().unwrap(); + // Initialize watchdog let mut watchdog = Watchdog::new(pac.WATCHDOG); + // Initialize clocks and PLLs let clocks = init_clocks_and_plls( XOSC_CRYSTAL_FREQ, pac.XOSC, @@ -67,6 +69,7 @@ fn main() -> ! { .ok() .unwrap(); + // Initialize SIO let sio = Sio::new(pac.SIO); let pins = Pins::new( pac.IO_BANK0, @@ -75,29 +78,22 @@ fn main() -> ! { &mut pac.RESETS, ); - // Set up the delay for the first core. + // Set up the delay for the first core let sys_freq = clocks.system_clock.freq().to_Hz(); let mut delay = Delay::new(core.SYST, sys_freq); let (mut _pio, _sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS); - // https://www.waveshare.com/wiki/RP2040-LCD-0.96 - // ST7735S LCD + // Initialize LCD pins let lcd_dc = pins.gp8.into_push_pull_output(); let lcd_cs = pins.gp9.into_push_pull_output(); let lcd_clk = pins.gp10.into_function::(); let lcd_mosi = pins.gp11.into_function::(); - let lcd_rst = pins - .gp12 - .into_push_pull_output_in_state(hal::gpio::PinState::High); - - let mut _lcd_bl = pins - .gp25 - .into_push_pull_output_in_state(hal::gpio::PinState::Low); + let lcd_rst = pins.gp12.into_push_pull_output_in_state(hal::gpio::PinState::High); + let mut _lcd_bl = pins.gp25.into_push_pull_output_in_state(hal::gpio::PinState::Low); + // Initialize SPI let spi = hal::Spi::<_, _, _, 8>::new(pac.SPI1, (lcd_mosi, lcd_clk)); - - // Exchange the uninitialised SPI driver for an initialised one let spi = spi.init( &mut pac.RESETS, clocks.peripheral_clock.freq(), @@ -105,47 +101,40 @@ fn main() -> ! { embedded_hal::spi::MODE_0, ); - // LCD is a 65K IPS LCD 160x80, color order is BGR and a offset 1,26 pixel. - // LCD controller can correct this by settings the order bit (bit 3) in MADCTL register. - // Also the colours are inverted, LCD controller can also correct this by writing to INVON register with no paramters. - // All this is handled by the ST7735 crate. + // Initialize the display let mut display = GC9A01A::new(spi, lcd_dc, lcd_cs, lcd_rst, false, true, LCD_WIDTH, LCD_HEIGHT); - display.init(&mut delay).unwrap(); - //display.set_orientation(&Orientation::Landscape).unwrap(); // Create two frame buffers for double buffering let mut frame_buffer_1 = unsafe { FrameBuffer::new(&mut FRAME_BUFFER_1, LCD_WIDTH, LCD_HEIGHT) }; let mut frame_buffer_2 = unsafe { FrameBuffer::new(&mut FRAME_BUFFER_2, LCD_WIDTH, LCD_HEIGHT) }; - - //display.set_offset(1, 26); - + // Define LCD dimensions let lcd_zero = Point::zero(); let lcd_max_corner = Point::new((LCD_WIDTH - 1) as i32, (LCD_HEIGHT - 1) as i32); + // Define a style for the rectangle let style = PrimitiveStyleBuilder::new() .fill_color(Rgb565::BLUE) .build(); - //Please note that you must clear the screen before you turn on the backlight or else the screen will display static. + // Clear the screen before turning on the backlight display.clear(Rgb565::BLACK); delay.delay_ms(1000); - // Set the backlight pin high _lcd_bl.set_high().unwrap(); delay.delay_ms(1000); + // Draw a blue rectangle Rectangle::with_corners(lcd_zero, lcd_max_corner) .into_styled(style) .draw(&mut display) .unwrap(); + delay.delay_ms(1000); - delay.delay_ms(1000); - + // Draw a black rectangle inside the blue rectangle let style = PrimitiveStyleBuilder::new() .fill_color(Rgb565::BLACK) .build(); - Rectangle::with_corners( Point::new(1, 1), Point::new((LCD_WIDTH - 2) as i32, (LCD_HEIGHT - 2) as i32), @@ -153,14 +142,13 @@ fn main() -> ! { .into_styled(style) .draw(&mut display) .unwrap(); - delay.delay_ms(1000); + // Draw red and green lines Line::new(lcd_zero, lcd_max_corner) .into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 1)) .draw(&mut display) .unwrap(); - Line::new( Point::new(0, (LCD_HEIGHT - 1) as i32), Point::new((LCD_WIDTH - 1) as i32, 0), @@ -168,78 +156,56 @@ fn main() -> ! { .into_styled(PrimitiveStyle::with_stroke(Rgb565::GREEN, 1)) .draw(&mut display) .unwrap(); -// Load image data -let image_data = include_bytes!("2wd-big-endian.raw"); - // Copy image data to the frame buffer - //frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - //frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - - - // Draw image on the frame buffer - let raw_image: ImageRaw = ImageRaw::new(image_data, LCD_WIDTH); - let image = Image::new(&raw_image, Point::zero()); - image.draw(&mut frame_buffer_1).unwrap(); - image.draw(&mut frame_buffer_2).unwrap(); - display.show(frame_buffer_1.get_buffer()).unwrap(); + // Load image data + let image_data = include_bytes!("2wd-big-endian.raw"); + let raw_image: ImageRaw = ImageRaw::new(image_data, LCD_WIDTH); + let image = Image::new(&raw_image, Point::zero()); - delay.delay_ms(1000); - - let points = [ - Point::new(120, 100), - Point::new(100, 140), - Point::new(140, 140), - ]; + // Draw the image on both frame buffers + image.draw(&mut frame_buffer_1).unwrap(); + image.draw(&mut frame_buffer_2).unwrap(); + display.show(frame_buffer_1.get_buffer()).unwrap(); + delay.delay_ms(1000); - // Draw a filled triangle on the frame buffers + // Draw a filled red triangle on the first frame buffer + let points = [ + Point::new(120, 100), + Point::new(100, 140), + Point::new(140, 140), + ]; let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::RED).build(); Triangle::new(points[0], points[1], points[2]) .into_styled(style) .draw(&mut frame_buffer_1) .unwrap(); - /* - Triangle::new(points[0], points[1], points[2]) - .into_styled(style) - .draw(&mut frame_buffer_2) - .unwrap(); - */ - // Show the initial buffer display.show(frame_buffer_1.get_buffer()).unwrap(); delay.delay_ms(1000); - // Draw the image to the display - //display.draw_image(image_data).unwrap(); - - // Draw a filled triangle on the frame buffers + // Draw a filled green triangle on the second frame buffer let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::GREEN).build(); Triangle::new(points[0], points[1], points[2]) .into_styled(style) .draw(&mut frame_buffer_2) .unwrap(); - - display.show(frame_buffer_2.get_buffer()).unwrap(); + display.show(frame_buffer_2.get_buffer()).unwrap(); delay.delay_ms(1000); - - //Reset the frame buffers. + // Reset the frame buffers image.draw(&mut frame_buffer_1).unwrap(); image.draw(&mut frame_buffer_2).unwrap(); - // Calculate the center of the image - let arrow_rotate_point_x = 240 / 2; - let arrow_rotate_point_y = (240 / 10) * 8; - create_arrow_image_5(&mut frame_buffer_1, 45, arrow_rotate_point_x,arrow_rotate_point_y ); - create_arrow_image_5(&mut frame_buffer_2, 46, arrow_rotate_point_x,arrow_rotate_point_y ); - //frame_buffer_2.clear(Rgb565::BLACK); - //image.draw(&mut frame_buffer_2).unwrap(); - //frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - + // Calculate the center of the image + let arrow_rotate_point_x = 240 / 2; + let arrow_rotate_point_y = (240 / 10) * 8; + create_arrow_image_5(&mut frame_buffer_1, 45, arrow_rotate_point_x, arrow_rotate_point_y); + create_arrow_image_5(&mut frame_buffer_2, 46, arrow_rotate_point_x, arrow_rotate_point_y); + // Infinite colour wheel loop let mut l: i32 = 0; let mut use_first_buffer = true; let mut c = Rgb565::RED; loop { - // Alternate between buffers // Alternate between buffers let current_buffer = if use_first_buffer { frame_buffer_1.get_buffer() @@ -247,12 +213,11 @@ let image_data = include_bytes!("2wd-big-endian.raw"); frame_buffer_2.get_buffer() }; - - // Clear the current buffer - //current_buffer.clear(Rgb565::BLACK); - // Call the show function with the buffer to display the image and arrow + // Display the current buffer display.show(current_buffer).unwrap(); //delay of 16 for 60Hz or 60 Frames a second. + //delay.delay_ms(1000); + //delay of 1 second for demo. delay.delay_ms(1000); /* Line::new(Point::new(0, l), Point::new((LCD_WIDTH - 1) as i32, l)) @@ -273,11 +238,10 @@ let image_data = include_bytes!("2wd-big-endian.raw"); // Toggle the buffer use_first_buffer = !use_first_buffer; } - } fn create_arrow_image_5( - framebuffer: &mut FrameBuffer<>, + framebuffer: &mut FrameBuffer, angle: i32, compass_center_x: i32, compass_center_y: i32, @@ -330,7 +294,7 @@ fn create_arrow_image_5( ]; let red = Rgb565::new(255, 0, 0); - let red_9 = Rgb565::new(174, 31, 31); + let red_9 = Rgb565::new(19,1,1); let style_red = PrimitiveStyleBuilder::new().fill_color(red).build(); let style_red_9 = PrimitiveStyleBuilder::new().fill_color(red_9).build(); @@ -340,9 +304,8 @@ fn create_arrow_image_5( draw_polygon(framebuffer, &right_points[0..4], style_red_9); } - fn draw_polygon( - framebuffer: &mut FrameBuffer<>, + framebuffer: &mut FrameBuffer, points: &[Point], style: PrimitiveStyle, ) { @@ -350,9 +313,8 @@ fn draw_polygon( return; // Not enough points to form a polygon } - // We will use a fan triangulation from the first point + // Use fan triangulation from the first point let first_point = points[0]; - for i in 1..points.len() - 1 { let triangle = Triangle::new(first_point, points[i], points[i + 1]) .into_styled(style); @@ -360,10 +322,10 @@ fn draw_polygon( } } -// Helper function to calculate the coordinates based on the angle and radius +// Helper function to calculate coordinates based on angle and radius fn get_coordinates(center: Point, radius: i32, angle: i32) -> Point { let angle_rad = (angle as f32).to_radians() as f64; let x = center.x + (radius as f32 * cos(angle_rad) as f32) as i32; let y = center.y + (radius as f32 * sin(angle_rad) as f32) as i32; Point::new(x, y) -} \ No newline at end of file +} From 248646bd7c8f3520c6f8163b5b650679f53b1409 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sun, 21 Jul 2024 08:27:02 -0700 Subject: [PATCH 07/23] Added window region updates Added arrow and window region updates. --- .../examples/gc9a01a_driver.rs | 48 +++++++ .../examples/waveshare_rp2040_lcd_demo.rs | 134 ++++++++++++++---- 2 files changed, 157 insertions(+), 25 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs index 9bdfdc9b..a65fbcc7 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs @@ -363,6 +363,54 @@ where Ok(()) } + + /// Updates only the specified region of the display with the provided buffer. + pub fn show_region(&mut self, buffer: &[u8], region: Rectangle) -> Result<(), ()> { + let Rectangle { top_left, size } = region; + let sx = top_left.x as u16; + let sy = top_left.y as u16; + let ex = (top_left.x + size.width as i32 - 1) as u16; + let ey = (top_left.y + size.height as i32 - 1) as u16; + + self.set_address_window(sx, sy, ex, ey)?; + self.write_command(Instruction::RAMWR as u8, &[])?; + self.start_data()?; + + for chunk in buffer.chunks(32) { + self.write_data(chunk)?; + } + + Ok(()) + } + // Updates only the specified region of the display with the provided buffer. +// Updates only the specified region of the display with the provided buffer. +pub fn show_region_2(&mut self, buffer: &[u8], region: Rectangle) -> Result<(), ()> { + let Rectangle { top_left, size } = region; + let sx = top_left.x as u16; + let sy = top_left.y as u16; + let ex = (top_left.x + size.width as i32 - 1) as u16; + let ey = (top_left.y + size.height as i32 - 1) as u16; + + // Calculate the buffer offset for the region + let buffer_width = self.width as usize; + let bytes_per_pixel = 2; // For RGB565 + + self.set_address_window(sx, sy, ex, ey)?; + self.write_command(Instruction::RAMWR as u8, &[])?; + self.start_data()?; + + for y in sy..=ey { + let start_index = ((y as usize) * buffer_width + (sx as usize)) * bytes_per_pixel; + let end_index = start_index + (size.width as usize) * bytes_per_pixel; + + for chunk in buffer[start_index..end_index].chunks(32) { + self.write_data(chunk)?; + } + } + + Ok(()) +} + } diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 4665a553..95f720f9 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -34,7 +34,7 @@ use embedded_hal::digital::v2::OutputPin; use embedded_graphics::{ pixelcolor::Rgb565, prelude::*, - primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, Triangle}, + primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, Triangle, Circle}, image::{ImageRaw, Image}, }; use libm::{cos,sin}; @@ -158,7 +158,9 @@ fn main() -> ! { .unwrap(); // Load image data - let image_data = include_bytes!("2wd-big-endian.raw"); + //let image_data = include_bytes!("2wd-big-endian.raw"); + let image_data = include_bytes!("fuel-big-endian.raw"); + let raw_image: ImageRaw = ImageRaw::new(image_data, LCD_WIDTH); let image = Image::new(&raw_image, Point::zero()); @@ -205,36 +207,49 @@ fn main() -> ! { let mut l: i32 = 0; let mut use_first_buffer = true; let mut c = Rgb565::RED; + + let mut angle = 45; + let mut increasing = true; + let mut bounding_box : Rectangle; + loop { // Alternate between buffers + if use_first_buffer { + // Reset the frame buffers + //image.draw(&mut frame_buffer_2).unwrap(); + frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); + bounding_box = create_arrow_image_5(&mut frame_buffer_2, angle, arrow_rotate_point_x, arrow_rotate_point_y); + create_button_image_1(&mut frame_buffer_2, arrow_rotate_point_x, arrow_rotate_point_y); + } else { + //image.draw(&mut frame_buffer_1).unwrap(); + frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); + bounding_box = create_arrow_image_5(&mut frame_buffer_1, angle, arrow_rotate_point_x, arrow_rotate_point_y); + create_button_image_1(&mut frame_buffer_1, arrow_rotate_point_x, arrow_rotate_point_y); + } + + // Adjust the angle for the next iteration + if increasing { + angle += 1; + if angle >= 135 { + increasing = false; + } + } else { + angle -= 1; + if angle <= 45 { + increasing = true; + } + } + + // Display the current buffer let current_buffer = if use_first_buffer { frame_buffer_1.get_buffer() } else { frame_buffer_2.get_buffer() }; + //display.show(current_buffer).unwrap(); + display.show_region_2(current_buffer, bounding_box).unwrap(); + delay.delay_ms(16); - // Display the current buffer - display.show(current_buffer).unwrap(); - //delay of 16 for 60Hz or 60 Frames a second. - //delay.delay_ms(1000); - //delay of 1 second for demo. - delay.delay_ms(1000); - /* - Line::new(Point::new(0, l), Point::new((LCD_WIDTH - 1) as i32, l)) - .into_styled(PrimitiveStyle::with_stroke(c, 1)) - .draw(&mut display) - .unwrap(); - delay.delay_ms(10); - l += 1; - if l == LCD_HEIGHT as i32 { - l = 0; - c = match c { - Rgb565::RED => Rgb565::GREEN, - Rgb565::GREEN => Rgb565::BLUE, - _ => Rgb565::RED, - } - } - */ // Toggle the buffer use_first_buffer = !use_first_buffer; } @@ -245,7 +260,7 @@ fn create_arrow_image_5( angle: i32, compass_center_x: i32, compass_center_y: i32, -) { +) -> Rectangle{ let compass_center = Point::new(compass_center_x, compass_center_y); let north_angle = angle - 180; let south_angle = angle; @@ -302,6 +317,11 @@ fn create_arrow_image_5( draw_polygon(framebuffer, &merged_points, style_red_9); draw_polygon(framebuffer, &left_points[0..4], style_red); draw_polygon(framebuffer, &right_points[0..4], style_red_9); + + // Calculate the bounding box of the arrow + let bounding_box = calculate_bounding_box(&merged_points, Some(5)); + + bounding_box } fn draw_polygon( @@ -329,3 +349,67 @@ fn get_coordinates(center: Point, radius: i32, angle: i32) -> Point { let y = center.y + (radius as f32 * sin(angle_rad) as f32) as i32; Point::new(x, y) } +/// Converts RGB888 color to RGB565 format. +fn convert_rgb888_to_color565(r: u8, g: u8, b: u8, big_endian: bool) -> Rgb565 { + let val16 = ((r & 0xf8) as u16) << 8 | ((g & 0xfc) as u16) << 3 | (b >> 3) as u16; + let value = if big_endian { + val16.swap_bytes() + } else { + val16 + }; + + Rgb565::new((value >> 11) as u8 & 0x1f, (value >> 5) as u8 & 0x3f, (value & 0x1f) as u8) +} + +/// Draws a circle on the frame buffer. +fn draw_circle(framebuffer: &mut FrameBuffer, color: Rgb565, center: Point, radius: i32) { + let style = PrimitiveStyleBuilder::new() + .fill_color(color) + .build(); + // Calculate the top-left corner of the circle based on the center point and radius + let top_left = Point::new(center.x - radius, center.y - radius); + let diameter = (radius * 2) as u32; + + Circle::new(top_left, diameter as u32) + .into_styled(style) + .draw(framebuffer) + .unwrap(); +} +/// Creates a button image on the frame buffer. +fn create_button_image_1(framebuffer: &mut FrameBuffer, center_x: i32, center_y: i32) { + let button_color_top = Rgb565::new(8, 16, 8); + let button_color_shadow = Rgb565::new(12, 23, 12); + + let circle_radius = 14; + draw_circle(framebuffer, button_color_shadow, Point::new(center_x - 1, center_y - 1), circle_radius); + draw_circle(framebuffer, button_color_top, Point::new(center_x, center_y), circle_radius); +} + +/// Helper function to calculate the bounding box of a set of points with an optional padding. +fn calculate_bounding_box(points: &[Point], padding: Option) -> Rectangle { + let mut min_x = points[0].x; + let mut max_x = points[0].x; + let mut min_y = points[0].y; + let mut max_y = points[0].y; + + for point in points.iter().skip(1) { + if point.x < min_x { + min_x = point.x; + } + if point.x > max_x { + max_x = point.x; + } + if point.y < min_y { + min_y = point.y; + } + if point.y > max_y { + max_y = point.y; + } + } + + let padding = padding.unwrap_or(0) as i32; + Rectangle::new( + Point::new(min_x - padding, min_y - padding), + Size::new((max_x - min_x + 2 * padding) as u32, (max_y - min_y + 2 * padding) as u32), + ) +} \ No newline at end of file From 09e28aafa3d41aee6ec6c4cab1bce88e5780d207 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sun, 21 Jul 2024 08:52:42 -0700 Subject: [PATCH 08/23] Added Precomputation details on the arrow. Added Precomputation details on the arrow. --- .../examples/waveshare_rp2040_lcd_demo.rs | 128 +++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 95f720f9..d0082aa5 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -208,22 +208,29 @@ fn main() -> ! { let mut use_first_buffer = true; let mut c = Rgb565::RED; + let compass_center = Point::new(240 / 2, (240 / 10) * 8); + let arrow_points = precompute_arrow_points(compass_center, 45, 135); let mut angle = 45; let mut increasing = true; let mut bounding_box : Rectangle; + let mut angle_index = 0; loop { + let points = &arrow_points[angle_index]; + // Alternate between buffers if use_first_buffer { // Reset the frame buffers //image.draw(&mut frame_buffer_2).unwrap(); frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - bounding_box = create_arrow_image_5(&mut frame_buffer_2, angle, arrow_rotate_point_x, arrow_rotate_point_y); + //bounding_box = create_arrow_image_5(&mut frame_buffer_2, angle, arrow_rotate_point_x, arrow_rotate_point_y); + bounding_box = create_arrow_image_6(&mut frame_buffer_2, points); create_button_image_1(&mut frame_buffer_2, arrow_rotate_point_x, arrow_rotate_point_y); } else { //image.draw(&mut frame_buffer_1).unwrap(); frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - bounding_box = create_arrow_image_5(&mut frame_buffer_1, angle, arrow_rotate_point_x, arrow_rotate_point_y); + //bounding_box = create_arrow_image_5(&mut frame_buffer_1, angle, arrow_rotate_point_x, arrow_rotate_point_y); + bounding_box = create_arrow_image_6(&mut frame_buffer_1, points); create_button_image_1(&mut frame_buffer_1, arrow_rotate_point_x, arrow_rotate_point_y); } @@ -240,6 +247,19 @@ fn main() -> ! { } } + // Adjust the angle index for the next iteration + if increasing { + angle_index += 1; + if angle_index >= arrow_points.len() { + increasing = false; + } + } else { + angle_index -= 1; + if angle_index <= 0 { + increasing = true; + } + } + // Display the current buffer let current_buffer = if use_first_buffer { frame_buffer_1.get_buffer() @@ -412,4 +432,108 @@ fn calculate_bounding_box(points: &[Point], padding: Option) -> Rectangle { Point::new(min_x - padding, min_y - padding), Size::new((max_x - min_x + 2 * padding) as u32, (max_y - min_y + 2 * padding) as u32), ) +} + +#[derive(Copy, Clone)] +struct ArrowPoints { + north: Point, + south: Point, + north_left: Point, + north_right: Point, + south_left: Point, + south_right: Point, +} + +fn precompute_arrow_points( + compass_center: Point, + start_angle: i32, + end_angle: i32, +) -> [ArrowPoints; 91] { + let mut points_array = [ArrowPoints { + north: Point::zero(), + south: Point::zero(), + north_left: Point::zero(), + north_right: Point::zero(), + south_left: Point::zero(), + south_right: Point::zero(), + }; 91]; + + for angle in start_angle..=end_angle { + let north_angle = angle - 180; + let south_angle = angle; + let north_left_angle = north_angle - 2; + let north_right_angle = north_angle + 2; + let south_left_angle = south_angle + 10; + let south_right_angle = south_angle - 10; + + let circle_1 = 128; + let circle_2 = 125; + let circle_3 = 36; + let circle_4 = 32; + + let north = get_coordinates(compass_center, circle_1, north_angle); + let south = get_coordinates(compass_center, circle_4, south_angle); + let north_left = get_coordinates(compass_center, circle_2, north_left_angle); + let north_right = get_coordinates(compass_center, circle_2, north_right_angle); + let south_left = get_coordinates(compass_center, circle_3, south_left_angle); + let south_right = get_coordinates(compass_center, circle_3, south_right_angle); + + points_array[(angle - start_angle) as usize] = ArrowPoints { + north, + south, + north_left, + north_right, + south_left, + south_right, + }; + } + + points_array +} + +fn create_arrow_image_6( + framebuffer: &mut FrameBuffer, + points: &ArrowPoints, +) -> Rectangle { + let merged_points = [ + points.north, + points.north_left, + points.south_left, + points.south, + points.south_right, + points.north_right, + ]; + + let left_points = [ + points.north, + points.north_left, + points.south_left, + points.south, + Point::zero(), // unused but needed to keep array size fixed + Point::zero(), // unused but needed to keep array size fixed + ]; + + let right_points = [ + points.north, + points.north_right, + points.south_right, + points.south, + Point::zero(), // unused but needed to keep array size fixed + Point::zero(), // unused but needed to keep array size fixed + ]; + + let red = Rgb565::new(255, 0, 0); + let red_9 = Rgb565::new(19, 1, 1); + + let style_red = PrimitiveStyleBuilder::new().fill_color(red).build(); + let style_red_9 = PrimitiveStyleBuilder::new().fill_color(red_9).build(); + + draw_polygon(framebuffer, &merged_points, style_red_9); + draw_polygon(framebuffer, &left_points[0..4], style_red); + draw_polygon(framebuffer, &right_points[0..4], style_red_9); + + // Calculate the bounding box of the arrow + let bounding_box = calculate_bounding_box(&merged_points, Some(5)); + + bounding_box } \ No newline at end of file From 78301d33168a6593a6e42c8e3535b46ad732abb5 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sun, 21 Jul 2024 09:01:18 -0700 Subject: [PATCH 09/23] Added frame rate delay adjustment Added frame rate delay adjustment --- .../examples/waveshare_rp2040_lcd_demo.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index d0082aa5..944034ce 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -216,6 +216,7 @@ fn main() -> ! { let mut angle_index = 0; loop { + let start_time = cortex_m::peripheral::SYST::get_current(); let points = &arrow_points[angle_index]; // Alternate between buffers @@ -268,10 +269,18 @@ fn main() -> ! { }; //display.show(current_buffer).unwrap(); display.show_region_2(current_buffer, bounding_box).unwrap(); - delay.delay_ms(16); + // Toggle the buffer use_first_buffer = !use_first_buffer; + + // Calculate the frame time and adjust delay to achieve ~60 FPS + let frame_time = cortex_m::peripheral::SYST::get_current().wrapping_sub(start_time); + let frame_time_ms = (frame_time as f64) / (sys_freq as f64 / 1_000.0); + let target_frame_time_ms = 16.67; + if frame_time_ms < target_frame_time_ms { + delay.delay_ms((target_frame_time_ms - frame_time_ms) as u32); + } } } From 046142d2fa832256702848f6b2b181b4bdde6b5c Mon Sep 17 00:00:00 2001 From: GordonCox Date: Sun, 21 Jul 2024 09:13:03 -0700 Subject: [PATCH 10/23] Fixed a defect in the angle_index Fixed a defect in the angle_index --- .../examples/waveshare_rp2040_lcd_demo.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 944034ce..c66672f0 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -236,6 +236,7 @@ fn main() -> ! { } // Adjust the angle for the next iteration + /* if increasing { angle += 1; if angle >= 135 { @@ -247,11 +248,11 @@ fn main() -> ! { increasing = true; } } - + */ // Adjust the angle index for the next iteration if increasing { angle_index += 1; - if angle_index >= arrow_points.len() { + if angle_index >= arrow_points.len() - 1{ increasing = false; } } else { From ece85981733f336d43bf06ca2a13e3d4e8b9f1c5 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Mon, 29 Jul 2024 07:10:54 -0700 Subject: [PATCH 11/23] Modified usage of buffers Used one buffer for the background, one buffer for the LCD, copy data from background to LCD buffer before displaying the region on screen. --- .../examples/waveshare_rp2040_lcd_demo.rs | 77 +++++++------------ 1 file changed, 29 insertions(+), 48 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index c66672f0..34b3baea 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -203,52 +203,41 @@ fn main() -> ! { create_arrow_image_5(&mut frame_buffer_1, 45, arrow_rotate_point_x, arrow_rotate_point_y); create_arrow_image_5(&mut frame_buffer_2, 46, arrow_rotate_point_x, arrow_rotate_point_y); - // Infinite colour wheel loop - let mut l: i32 = 0; - let mut use_first_buffer = true; - let mut c = Rgb565::RED; - let compass_center = Point::new(240 / 2, (240 / 10) * 8); let arrow_points = precompute_arrow_points(compass_center, 45, 135); - let mut angle = 45; let mut increasing = true; let mut bounding_box : Rectangle; - let mut angle_index = 0; + let mut previous_bounding_box = Rectangle::new(Point::new(0, 0), Size::new(0, 0)); + // Define a rectangle at (0, 0) with width 0 and height 0 + let mut angle_index: usize = 0; + + //Create a background buffer with frame_buffer_1 + frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); + //Create a drawing to lcd buffer with frame_buffer_2 + frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); + //Show the display to present the initial image. + display.show(frame_buffer_2.get_buffer()).unwrap(); loop { let start_time = cortex_m::peripheral::SYST::get_current(); let points = &arrow_points[angle_index]; - // Alternate between buffers - if use_first_buffer { - // Reset the frame buffers - //image.draw(&mut frame_buffer_2).unwrap(); - frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - //bounding_box = create_arrow_image_5(&mut frame_buffer_2, angle, arrow_rotate_point_x, arrow_rotate_point_y); - bounding_box = create_arrow_image_6(&mut frame_buffer_2, points); - create_button_image_1(&mut frame_buffer_2, arrow_rotate_point_x, arrow_rotate_point_y); - } else { - //image.draw(&mut frame_buffer_1).unwrap(); - frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - //bounding_box = create_arrow_image_5(&mut frame_buffer_1, angle, arrow_rotate_point_x, arrow_rotate_point_y); - bounding_box = create_arrow_image_6(&mut frame_buffer_1, points); - create_button_image_1(&mut frame_buffer_1, arrow_rotate_point_x, arrow_rotate_point_y); - } + //Copy the previous bounding box from the background buffer (frame_buffer_1) into the lcd buffer (frame_buffer_2) + //This prevents the whole reload of the image_data. + let previous_bounding_box_buffer = &frame_buffer_1.get_buffer()[(previous_bounding_box.top_left.y as usize * LCD_WIDTH as usize * 2) + (previous_bounding_box.top_left.x as usize * 2)..]; + let destination_buffer = &mut frame_buffer_2.get_mut_buffer()[(previous_bounding_box.top_left.y as usize * LCD_WIDTH as usize * 2) + (previous_bounding_box.top_left.x as usize * 2)..]; - // Adjust the angle for the next iteration - /* - if increasing { - angle += 1; - if angle >= 135 { - increasing = false; - } - } else { - angle -= 1; - if angle <= 45 { - increasing = true; - } + for row in 0..previous_bounding_box.size.height as usize { + let source_row_start = row * LCD_WIDTH as usize * 2; + let source_row_end = source_row_start + previous_bounding_box.size.width as usize * 2; + destination_buffer[source_row_start..source_row_end].copy_from_slice(&previous_bounding_box_buffer[source_row_start..source_row_end]); } - */ + + //Draw the arrow and return the new bounding box + bounding_box = create_arrow_image_6(&mut frame_buffer_2, points); + //Draw the center button + create_button_image_1(&mut frame_buffer_2, arrow_rotate_point_x, arrow_rotate_point_y); + // Adjust the angle index for the next iteration if increasing { angle_index += 1; @@ -262,25 +251,17 @@ fn main() -> ! { } } - // Display the current buffer - let current_buffer = if use_first_buffer { - frame_buffer_1.get_buffer() - } else { - frame_buffer_2.get_buffer() - }; - //display.show(current_buffer).unwrap(); - display.show_region_2(current_buffer, bounding_box).unwrap(); - - - // Toggle the buffer - use_first_buffer = !use_first_buffer; + //the bounding box has a pixel padding of 5 pixels around the arrow to prevent the need to draw the background buffer before the next arrow is drawn. + //This improves performance as only one draw operation occurs instead of 2. + display.show_region_2(frame_buffer_2.get_buffer(), bounding_box).unwrap(); + previous_bounding_box = bounding_box; // Calculate the frame time and adjust delay to achieve ~60 FPS let frame_time = cortex_m::peripheral::SYST::get_current().wrapping_sub(start_time); let frame_time_ms = (frame_time as f64) / (sys_freq as f64 / 1_000.0); let target_frame_time_ms = 16.67; if frame_time_ms < target_frame_time_ms { - delay.delay_ms((target_frame_time_ms - frame_time_ms) as u32); + //delay.delay_ms((target_frame_time_ms - frame_time_ms) as u32); } } } From a20e5d69839b934bce4e402afcb7d1e4fbda1619 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Mon, 29 Jul 2024 07:14:23 -0700 Subject: [PATCH 12/23] Added back frame rate delay --- .../examples/waveshare_rp2040_lcd_demo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 34b3baea..64e5d803 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -261,7 +261,7 @@ fn main() -> ! { let frame_time_ms = (frame_time as f64) / (sys_freq as f64 / 1_000.0); let target_frame_time_ms = 16.67; if frame_time_ms < target_frame_time_ms { - //delay.delay_ms((target_frame_time_ms - frame_time_ms) as u32); + delay.delay_ms((target_frame_time_ms - frame_time_ms) as u32); } } } From f5facdc48dce50ce191d9ce9e0d2d14412e7768f Mon Sep 17 00:00:00 2001 From: GordonCox Date: Mon, 29 Jul 2024 07:20:41 -0700 Subject: [PATCH 13/23] Added comments to the code. --- .../examples/waveshare_rp2040_lcd_demo.rs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 64e5d803..8d96b964 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -47,6 +47,7 @@ const LCD_HEIGHT: u32 = 240; static mut FRAME_BUFFER_1: [u8; (LCD_WIDTH * LCD_HEIGHT * 2) as usize] = [0; (LCD_WIDTH * LCD_HEIGHT * 2) as usize]; static mut FRAME_BUFFER_2: [u8; (LCD_WIDTH * LCD_HEIGHT * 2) as usize] = [0; (LCD_WIDTH * LCD_HEIGHT * 2) as usize]; +/// Main entry point for the application #[entry] fn main() -> ! { // Take ownership of peripheral instances @@ -211,19 +212,19 @@ fn main() -> ! { // Define a rectangle at (0, 0) with width 0 and height 0 let mut angle_index: usize = 0; - //Create a background buffer with frame_buffer_1 + // Create a background buffer with frame_buffer_1 frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - //Create a drawing to lcd buffer with frame_buffer_2 + // Create a drawing to lcd buffer with frame_buffer_2 frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - //Show the display to present the initial image. + // Show the display to present the initial image. display.show(frame_buffer_2.get_buffer()).unwrap(); loop { let start_time = cortex_m::peripheral::SYST::get_current(); let points = &arrow_points[angle_index]; - //Copy the previous bounding box from the background buffer (frame_buffer_1) into the lcd buffer (frame_buffer_2) - //This prevents the whole reload of the image_data. + // Copy the previous bounding box from the background buffer (frame_buffer_1) into the lcd buffer (frame_buffer_2) + // This prevents the whole reload of the image_data. let previous_bounding_box_buffer = &frame_buffer_1.get_buffer()[(previous_bounding_box.top_left.y as usize * LCD_WIDTH as usize * 2) + (previous_bounding_box.top_left.x as usize * 2)..]; let destination_buffer = &mut frame_buffer_2.get_mut_buffer()[(previous_bounding_box.top_left.y as usize * LCD_WIDTH as usize * 2) + (previous_bounding_box.top_left.x as usize * 2)..]; @@ -233,9 +234,9 @@ fn main() -> ! { destination_buffer[source_row_start..source_row_end].copy_from_slice(&previous_bounding_box_buffer[source_row_start..source_row_end]); } - //Draw the arrow and return the new bounding box + // Draw the arrow and return the new bounding box bounding_box = create_arrow_image_6(&mut frame_buffer_2, points); - //Draw the center button + // Draw the center button create_button_image_1(&mut frame_buffer_2, arrow_rotate_point_x, arrow_rotate_point_y); // Adjust the angle index for the next iteration @@ -251,8 +252,8 @@ fn main() -> ! { } } - //the bounding box has a pixel padding of 5 pixels around the arrow to prevent the need to draw the background buffer before the next arrow is drawn. - //This improves performance as only one draw operation occurs instead of 2. + // The bounding box has a pixel padding of 5 pixels around the arrow to prevent the need to draw the background buffer before the next arrow is drawn. + // This improves performance as only one draw operation occurs instead of 2. display.show_region_2(frame_buffer_2.get_buffer(), bounding_box).unwrap(); previous_bounding_box = bounding_box; @@ -266,6 +267,7 @@ fn main() -> ! { } } +/// Create an arrow image at a specified angle and position fn create_arrow_image_5( framebuffer: &mut FrameBuffer, angle: i32, @@ -335,6 +337,7 @@ fn create_arrow_image_5( bounding_box } +/// Draw a polygon on the frame buffer fn draw_polygon( framebuffer: &mut FrameBuffer, points: &[Point], @@ -360,6 +363,7 @@ fn get_coordinates(center: Point, radius: i32, angle: i32) -> Point { let y = center.y + (radius as f32 * sin(angle_rad) as f32) as i32; Point::new(x, y) } + /// Converts RGB888 color to RGB565 format. fn convert_rgb888_to_color565(r: u8, g: u8, b: u8, big_endian: bool) -> Rgb565 { let val16 = ((r & 0xf8) as u16) << 8 | ((g & 0xfc) as u16) << 3 | (b >> 3) as u16; @@ -386,6 +390,7 @@ fn draw_circle(framebuffer: &mut FrameBuffer, color: Rgb565, center: Point, radi .draw(framebuffer) .unwrap(); } + /// Creates a button image on the frame buffer. fn create_button_image_1(framebuffer: &mut FrameBuffer, center_x: i32, center_y: i32) { let button_color_top = Rgb565::new(8, 16, 8); @@ -435,6 +440,7 @@ struct ArrowPoints { south_right: Point, } +/// Precompute arrow points for animation fn precompute_arrow_points( compass_center: Point, start_angle: i32, @@ -482,6 +488,7 @@ fn precompute_arrow_points( points_array } +/// Create an arrow image on the frame buffer fn create_arrow_image_6( framebuffer: &mut FrameBuffer, points: &ArrowPoints, @@ -527,4 +534,4 @@ fn create_arrow_image_6( let bounding_box = calculate_bounding_box(&merged_points, Some(5)); bounding_box -} \ No newline at end of file +} From a8d79b45812a70323c60f81891a5e524f17370d5 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Mon, 29 Jul 2024 10:28:59 -0700 Subject: [PATCH 14/23] Clean up and simplify the code. Clean up and simplify the code. --- boards/waveshare-rp2040-lcd-1-28/README.md | 18 +- .../examples/gc9a01a_driver.rs | 21 +- .../examples/waveshare_rp2040_lcd_demo.rs | 208 +++--------------- 3 files changed, 43 insertions(+), 204 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/README.md b/boards/waveshare-rp2040-lcd-1-28/README.md index b0c5489b..17233d76 100644 --- a/boards/waveshare-rp2040-lcd-1-28/README.md +++ b/boards/waveshare-rp2040-lcd-1-28/README.md @@ -1,14 +1,14 @@ -# [waveshare-rp2040-lcd-0-96] - Board Support for the [Waveshare RP2040 LCD 0.96] +# [waveshare-rp2040-lcd-1-28] - Board Support for the [Waveshare RP2040 LCD 1.28] You should include this crate if you are writing code that you want to run on -an [Waveshare RP2040 LCD 0.96] - a very small RP2040 breakout board with USB-C, -a 65K IPS LCD 160x80, 16MBit Flash and 1A battery charger from Waveshare. +an [Waveshare RP2040 LCD 1.28] - a very small RP2040 breakout board with USB-C, +a 65K IPS LCD 240x240, 16MBit Flash and 1A battery charger from Waveshare. This crate includes the [rp2040-hal], but also configures each pin of the RP2040 chip according to how it is connected up on the Feather. -[Waveshare RP2040 LCD 0.96]: https://www.waveshare.com/wiki/RP2040-LCD-0.96 -[waveshare-rp2040-lcd-0-96]: https://github.com/rp-rs/rp-hal-boards/tree/main/boards/waveshare-rp2040-lcd-0-96 +[Waveshare RP2040 LCD 1.28]: https://www.waveshare.com/wiki/RP2040-LCD-1.28 +[waveshare-rp2040-lcd-1-28]: https://github.com/rp-rs/rp-hal-boards/tree/main/boards/waveshare-rp2040-lcd-1-28 [rp2040-hal]: https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal [Raspberry Silicon RP2040]: https://www.raspberrypi.org/products/rp2040/ @@ -17,10 +17,10 @@ RP2040 chip according to how it is connected up on the Feather. To use this crate, your `Cargo.toml` file should contain: ```toml -waveshare_rp2040_lcd_0_96 = "0.8.0" +waveshare_rp2040_lcd_1_28 = "0.8.0" ``` -In your program, you will need to call `waveshare_rp2040_lcd_0_96::Pins::new` to create +In your program, you will need to call `waveshare_rp2040_lcd_1_28::Pins::new` to create a new `Pins` structure. This will set up all the GPIOs for any on-board devices. See the [examples](./examples) folder for more details. @@ -31,7 +31,7 @@ devices. See the [examples](./examples) folder for more details. To compile an example, clone the _rp-hal-boards_ repository and run: ```console -rp-hal-boards/boards/waveshare-rp2040-lcd-0-96 $ cargo build --release --example +rp-hal-boards/boards/waveshare-rp2040-lcd-1-28 $ cargo build --release --example ``` You will get an ELF file called @@ -45,7 +45,7 @@ USB drive exported by the RP2040 bootloader, simply boot your board into bootloader mode and run: ```console -rp-hal-boards/boards/waveshare-rp2040-lcd-0-96 $ cargo run --release --example +rp-hal-boards/boards/waveshare-rp2040-lcd-1-28 $ cargo run --release --example ``` If you get an error about not being able to find `elf2uf2-rs`, try: diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs index a65fbcc7..1c825c27 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs @@ -364,27 +364,8 @@ where Ok(()) } - /// Updates only the specified region of the display with the provided buffer. - pub fn show_region(&mut self, buffer: &[u8], region: Rectangle) -> Result<(), ()> { - let Rectangle { top_left, size } = region; - let sx = top_left.x as u16; - let sy = top_left.y as u16; - let ex = (top_left.x + size.width as i32 - 1) as u16; - let ey = (top_left.y + size.height as i32 - 1) as u16; - - self.set_address_window(sx, sy, ex, ey)?; - self.write_command(Instruction::RAMWR as u8, &[])?; - self.start_data()?; - - for chunk in buffer.chunks(32) { - self.write_data(chunk)?; - } - - Ok(()) - } - // Updates only the specified region of the display with the provided buffer. // Updates only the specified region of the display with the provided buffer. -pub fn show_region_2(&mut self, buffer: &[u8], region: Rectangle) -> Result<(), ()> { +pub fn show_region(&mut self, buffer: &[u8], region: Rectangle) -> Result<(), ()> { let Rectangle { top_left, size } = region; let sx = top_left.x as u16; let sy = top_left.y as u16; diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 8d96b964..08dc2a22 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -37,7 +37,7 @@ use embedded_graphics::{ primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, Triangle, Circle}, image::{ImageRaw, Image}, }; -use libm::{cos,sin}; +use libm::{cos, sin}; use gc9a01a_driver::{Orientation, GC9A01A}; const LCD_WIDTH: u32 = 240; @@ -92,7 +92,7 @@ fn main() -> ! { let lcd_mosi = pins.gp11.into_function::(); let lcd_rst = pins.gp12.into_push_pull_output_in_state(hal::gpio::PinState::High); let mut _lcd_bl = pins.gp25.into_push_pull_output_in_state(hal::gpio::PinState::Low); - + // Initialize SPI let spi = hal::Spi::<_, _, _, 8>::new(pac.SPI1, (lcd_mosi, lcd_clk)); let spi = spi.init( @@ -155,13 +155,12 @@ fn main() -> ! { Point::new((LCD_WIDTH - 1) as i32, 0), ) .into_styled(PrimitiveStyle::with_stroke(Rgb565::GREEN, 1)) - .draw(&mut display) - .unwrap(); + .draw(&mut display) + .unwrap(); // Load image data - //let image_data = include_bytes!("2wd-big-endian.raw"); - let image_data = include_bytes!("fuel-big-endian.raw"); - + let image_data = include_bytes!("rust-logo-240x240.raw"); + let raw_image: ImageRaw = ImageRaw::new(image_data, LCD_WIDTH); let image = Image::new(&raw_image, Point::zero()); @@ -193,24 +192,17 @@ fn main() -> ! { .unwrap(); display.show(frame_buffer_2.get_buffer()).unwrap(); delay.delay_ms(1000); - + // Reset the frame buffers image.draw(&mut frame_buffer_1).unwrap(); image.draw(&mut frame_buffer_2).unwrap(); // Calculate the center of the image - let arrow_rotate_point_x = 240 / 2; - let arrow_rotate_point_y = (240 / 10) * 8; - create_arrow_image_5(&mut frame_buffer_1, 45, arrow_rotate_point_x, arrow_rotate_point_y); - create_arrow_image_5(&mut frame_buffer_2, 46, arrow_rotate_point_x, arrow_rotate_point_y); - - let compass_center = Point::new(240 / 2, (240 / 10) * 8); - let arrow_points = precompute_arrow_points(compass_center, 45, 135); - let mut increasing = true; - let mut bounding_box : Rectangle; + let image_center = Point::new(240 / 2, 240 / 2); + let mut bounding_box: Rectangle; let mut previous_bounding_box = Rectangle::new(Point::new(0, 0), Size::new(0, 0)); - // Define a rectangle at (0, 0) with width 0 and height 0 - let mut angle_index: usize = 0; + // Define a rectangle at (0, 0) with width 0 and height 0 + let mut angle: f32 = 0.0; // Create a background buffer with frame_buffer_1 frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); @@ -221,7 +213,6 @@ fn main() -> ! { loop { let start_time = cortex_m::peripheral::SYST::get_current(); - let points = &arrow_points[angle_index]; // Copy the previous bounding box from the background buffer (frame_buffer_1) into the lcd buffer (frame_buffer_2) // This prevents the whole reload of the image_data. @@ -235,45 +226,33 @@ fn main() -> ! { } // Draw the arrow and return the new bounding box - bounding_box = create_arrow_image_6(&mut frame_buffer_2, points); + bounding_box = create_arrow(&mut frame_buffer_2, angle as i32, image_center.x, image_center.y); // Draw the center button - create_button_image_1(&mut frame_buffer_2, arrow_rotate_point_x, arrow_rotate_point_y); - - // Adjust the angle index for the next iteration - if increasing { - angle_index += 1; - if angle_index >= arrow_points.len() - 1{ - increasing = false; - } - } else { - angle_index -= 1; - if angle_index <= 0 { - increasing = true; - } + create_button(&mut frame_buffer_2, image_center.x, image_center.y); + + // Increment the angle + angle += 0.1; + if angle >= 360.0 { + angle = 0.0; } - // The bounding box has a pixel padding of 5 pixels around the arrow to prevent the need to draw the background buffer before the next arrow is drawn. + // The bounding box has a pixel padding of 5 pixels around the arrow to prevent the need to draw the background buffer before the next arrow is drawn. // This improves performance as only one draw operation occurs instead of 2. - display.show_region_2(frame_buffer_2.get_buffer(), bounding_box).unwrap(); + display.show_region(frame_buffer_2.get_buffer(), bounding_box).unwrap(); previous_bounding_box = bounding_box; - // Calculate the frame time and adjust delay to achieve ~60 FPS - let frame_time = cortex_m::peripheral::SYST::get_current().wrapping_sub(start_time); - let frame_time_ms = (frame_time as f64) / (sys_freq as f64 / 1_000.0); - let target_frame_time_ms = 16.67; - if frame_time_ms < target_frame_time_ms { - delay.delay_ms((target_frame_time_ms - frame_time_ms) as u32); - } + // Delay to achieve ~30 FPS + delay.delay_ms(33); } } /// Create an arrow image at a specified angle and position -fn create_arrow_image_5( +fn create_arrow( framebuffer: &mut FrameBuffer, angle: i32, compass_center_x: i32, compass_center_y: i32, -) -> Rectangle{ +) -> Rectangle { let compass_center = Point::new(compass_center_x, compass_center_y); let north_angle = angle - 180; let south_angle = angle; @@ -282,8 +261,8 @@ fn create_arrow_image_5( let south_left_angle = south_angle + 10; let south_right_angle = south_angle - 10; - let circle_1 = 128; - let circle_2 = 125; + let circle_1 = 88; + let circle_2 = 85; let circle_3 = 36; let circle_4 = 32; @@ -322,7 +301,7 @@ fn create_arrow_image_5( ]; let red = Rgb565::new(255, 0, 0); - let red_9 = Rgb565::new(19,1,1); + let red_9 = Rgb565::new(19, 1, 1); let style_red = PrimitiveStyleBuilder::new().fill_color(red).build(); let style_red_9 = PrimitiveStyleBuilder::new().fill_color(red_9).build(); @@ -331,10 +310,10 @@ fn create_arrow_image_5( draw_polygon(framebuffer, &left_points[0..4], style_red); draw_polygon(framebuffer, &right_points[0..4], style_red_9); - // Calculate the bounding box of the arrow - let bounding_box = calculate_bounding_box(&merged_points, Some(5)); + // Calculate the bounding box of the arrow + let bounding_box = calculate_bounding_box(&merged_points, Some(5)); - bounding_box + bounding_box } /// Draw a polygon on the frame buffer @@ -364,24 +343,12 @@ fn get_coordinates(center: Point, radius: i32, angle: i32) -> Point { Point::new(x, y) } -/// Converts RGB888 color to RGB565 format. -fn convert_rgb888_to_color565(r: u8, g: u8, b: u8, big_endian: bool) -> Rgb565 { - let val16 = ((r & 0xf8) as u16) << 8 | ((g & 0xfc) as u16) << 3 | (b >> 3) as u16; - let value = if big_endian { - val16.swap_bytes() - } else { - val16 - }; - - Rgb565::new((value >> 11) as u8 & 0x1f, (value >> 5) as u8 & 0x3f, (value & 0x1f) as u8) -} - /// Draws a circle on the frame buffer. fn draw_circle(framebuffer: &mut FrameBuffer, color: Rgb565, center: Point, radius: i32) { let style = PrimitiveStyleBuilder::new() .fill_color(color) .build(); - // Calculate the top-left corner of the circle based on the center point and radius + // Calculate the top-left corner of the circle based on the center point and radius let top_left = Point::new(center.x - radius, center.y - radius); let diameter = (radius * 2) as u32; @@ -392,13 +359,9 @@ fn draw_circle(framebuffer: &mut FrameBuffer, color: Rgb565, center: Point, radi } /// Creates a button image on the frame buffer. -fn create_button_image_1(framebuffer: &mut FrameBuffer, center_x: i32, center_y: i32) { - let button_color_top = Rgb565::new(8, 16, 8); - let button_color_shadow = Rgb565::new(12, 23, 12); - +fn create_button(framebuffer: &mut FrameBuffer, center_x: i32, center_y: i32) { let circle_radius = 14; - draw_circle(framebuffer, button_color_shadow, Point::new(center_x - 1, center_y - 1), circle_radius); - draw_circle(framebuffer, button_color_top, Point::new(center_x, center_y), circle_radius); + draw_circle(framebuffer, Rgb565::BLACK, Point::new(center_x, center_y), circle_radius); } /// Helper function to calculate the bounding box of a set of points with an optional padding. @@ -430,108 +393,3 @@ fn calculate_bounding_box(points: &[Point], padding: Option) -> Rectangle { ) } -#[derive(Copy, Clone)] -struct ArrowPoints { - north: Point, - south: Point, - north_left: Point, - north_right: Point, - south_left: Point, - south_right: Point, -} - -/// Precompute arrow points for animation -fn precompute_arrow_points( - compass_center: Point, - start_angle: i32, - end_angle: i32, -) -> [ArrowPoints; 91] { - let mut points_array = [ArrowPoints { - north: Point::zero(), - south: Point::zero(), - north_left: Point::zero(), - north_right: Point::zero(), - south_left: Point::zero(), - south_right: Point::zero(), - }; 91]; - - for angle in start_angle..=end_angle { - let north_angle = angle - 180; - let south_angle = angle; - let north_left_angle = north_angle - 2; - let north_right_angle = north_angle + 2; - let south_left_angle = south_angle + 10; - let south_right_angle = south_angle - 10; - - let circle_1 = 128; - let circle_2 = 125; - let circle_3 = 36; - let circle_4 = 32; - - let north = get_coordinates(compass_center, circle_1, north_angle); - let south = get_coordinates(compass_center, circle_4, south_angle); - let north_left = get_coordinates(compass_center, circle_2, north_left_angle); - let north_right = get_coordinates(compass_center, circle_2, north_right_angle); - let south_left = get_coordinates(compass_center, circle_3, south_left_angle); - let south_right = get_coordinates(compass_center, circle_3, south_right_angle); - - points_array[(angle - start_angle) as usize] = ArrowPoints { - north, - south, - north_left, - north_right, - south_left, - south_right, - }; - } - - points_array -} - -/// Create an arrow image on the frame buffer -fn create_arrow_image_6( - framebuffer: &mut FrameBuffer, - points: &ArrowPoints, -) -> Rectangle { - let merged_points = [ - points.north, - points.north_left, - points.south_left, - points.south, - points.south_right, - points.north_right, - ]; - - let left_points = [ - points.north, - points.north_left, - points.south_left, - points.south, - Point::zero(), // unused but needed to keep array size fixed - Point::zero(), // unused but needed to keep array size fixed - ]; - - let right_points = [ - points.north, - points.north_right, - points.south_right, - points.south, - Point::zero(), // unused but needed to keep array size fixed - Point::zero(), // unused but needed to keep array size fixed - ]; - - let red = Rgb565::new(255, 0, 0); - let red_9 = Rgb565::new(19, 1, 1); - - let style_red = PrimitiveStyleBuilder::new().fill_color(red).build(); - let style_red_9 = PrimitiveStyleBuilder::new().fill_color(red_9).build(); - - draw_polygon(framebuffer, &merged_points, style_red_9); - draw_polygon(framebuffer, &left_points[0..4], style_red); - draw_polygon(framebuffer, &right_points[0..4], style_red_9); - - // Calculate the bounding box of the arrow - let bounding_box = calculate_bounding_box(&merged_points, Some(5)); - - bounding_box -} From 959f6f0574d9668983bceaf62383da653aba23c1 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Mon, 29 Jul 2024 10:47:23 -0700 Subject: [PATCH 15/23] Increased buffer removed references to old code. --- boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md | 29 ++----------------- .../examples/gc9a01a_driver.rs | 7 ----- .../examples/waveshare_rp2040_lcd_demo.rs | 11 ++++--- 3 files changed, 7 insertions(+), 40 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md b/boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md index 375c34a6..65e89914 100644 --- a/boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md +++ b/boards/waveshare-rp2040-lcd-1-28/CHANGELOG.md @@ -7,36 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -## 0.8.0 - 2024-04-07 - -### Changed - -- Update to rp2040-hal 0.10.0 -- Update to embedded-hal 1.0.0 - -## 0.7.0 - 2023-09-02 - -### Changed - -- Update to rp2040-hal 0.9.0 - -## 0.6.0 - 2023-02-18 - -### Changed - -- Update to rp2040-hal 0.8.0 - -## 0.5.0 - 2022-12-11 - -### Changed - -- Update to rp2040-hal 0.7.0 - -## 0.4.0 - 2022-11-15 +## 0.1.0 - 2024-07-29 ### Changed - Inital release -- Copied from waveshare-rp2040-zero +- Copied from waveshare-rp2040-0.96 - Update board name diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs index 1c825c27..b2f5865c 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs @@ -1,11 +1,5 @@ #![no_std] -//! This crate provides a ST7735 driver to connect to TFT displays. - -//mod instruction; - -//use crate::instruction::Instruction; - use embedded_hal::blocking::delay::DelayMs; use embedded_hal::blocking::spi; use embedded_hal::digital::v2::OutputPin; @@ -50,7 +44,6 @@ pub enum Instruction { GMCTRN1 = 0xE1, } -/// ST7735 driver to connect to TFT displays. pub struct GC9A01A where SPI: spi::Write, diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 08dc2a22..86a3aa80 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -202,7 +202,7 @@ fn main() -> ! { let mut bounding_box: Rectangle; let mut previous_bounding_box = Rectangle::new(Point::new(0, 0), Size::new(0, 0)); // Define a rectangle at (0, 0) with width 0 and height 0 - let mut angle: f32 = 0.0; + let mut angle: f32 = 90.0; // Create a background buffer with frame_buffer_1 frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); @@ -231,7 +231,7 @@ fn main() -> ! { create_button(&mut frame_buffer_2, image_center.x, image_center.y); // Increment the angle - angle += 0.1; + angle += 6.0; if angle >= 360.0 { angle = 0.0; } @@ -241,8 +241,8 @@ fn main() -> ! { display.show_region(frame_buffer_2.get_buffer(), bounding_box).unwrap(); previous_bounding_box = bounding_box; - // Delay to achieve ~30 FPS - delay.delay_ms(33); + // Delay to achieve 1 Hz + delay.delay_ms(1000); } } @@ -311,7 +311,7 @@ fn create_arrow( draw_polygon(framebuffer, &right_points[0..4], style_red_9); // Calculate the bounding box of the arrow - let bounding_box = calculate_bounding_box(&merged_points, Some(5)); + let bounding_box = calculate_bounding_box(&merged_points, Some(10)); bounding_box } @@ -392,4 +392,3 @@ fn calculate_bounding_box(points: &[Point], padding: Option) -> Rectangle { Size::new((max_x - min_x + 2 * padding) as u32, (max_y - min_y + 2 * padding) as u32), ) } - From b955b88b48ce10bd461652f2c53ed76ea1e94443 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Mon, 29 Jul 2024 10:50:21 -0700 Subject: [PATCH 16/23] Create rust-logo-240x240.raw Background Image for the LCD Clock Display. --- .../examples/rust-logo-240x240.raw | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 boards/waveshare-rp2040-lcd-1-28/examples/rust-logo-240x240.raw diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/rust-logo-240x240.raw b/boards/waveshare-rp2040-lcd-1-28/examples/rust-logo-240x240.raw new file mode 100644 index 00000000..2ddaa4a5 --- /dev/null +++ b/boards/waveshare-rp2040-lcd-1-28/examples/rust-logo-240x240.raw @@ -0,0 +1,20 @@ +џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџОюњцкїžџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюњЋы››ЃЊоxџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї|їџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїЋЊ›››››Hяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїžї\џпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюйД ЃiЃ‰ФЯџžџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХ1›››šцšЦšцМŽџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпЭRЃŠЃiЋыоxџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї\ЃHšч›šчšЦД,џžџџџџџџџџџџџџџџџџџџџџџџџџџџцЙšцšч›šцšЦšЦšЦ›о6џџџџџџџџџџџџџџџџџџџџџџџџџџџџМЯ›››››цЙџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭR›šчšц›šцšцМnџпџџџџџџџџџџџџџџџџџџџџџџџпД šЦšц›šцšЦšЦšЦ›ЃЊї}џџџџџџџџџџџџџџџџџџџџџџџџХ››››››МŽџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџОЋЊšцšЦšЦšцšЦšч›МЏџпџџџџџџџџџџџџџџџџџџџџеД’цšІ’Ц’цšІšЅšЅ’Ц’Ц’ЦФаџџџџџџџџџџџџџџџџџџџџџџЭR››››››››Hї\џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцxšцšЦšЦšЦšЦšц››šчФёџџџџџџџџџџџџџџџџџџя;›’І’І’І’І’…’…’…’І’…’e’…ц™џџџџџџџџџџџџџџџџџџЭs’ц’ц’ч’ч’ч››››еДџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМŽšЦšЦšЦšЦšц’ц’Ц’І’Ѕ’ЅФёџџџџџџџџџџџџџџџџДNŠDŠDŠ…Š…Š…ŠeŠeŠ…Š…ŠeŠDŠDЃЋџпџџџџџџџџџџџџџџЭДŠІŠІŠІ’І’Ц’Ц’Ц’Ц’ц’чЌ џпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}е”Ф№еДїџџџџџџџџџџџџџџџџџџџџџџџџџџї\ЃšЅ’Ѕ’Ѕ’І’І’І’Ѕ’e’dŠdŠdХ2џџџџџџџџџџџџо7Š$Š$Š$‚eŠEŠ$Š$Š$Š$ŠDŠ$Š$Š$ŠDЭSџџџџџџџџџџџџеЕŠeŠeŠ…Š…Š…Š…Š†ŠІŠІŠІŠІ’ЦцкџџџџџџџџџџџџџџџџџџџџџџџџџџџожХЭsяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяЃŠšЦšЦšЦЋЪцЙџџџџџџџџџџџџџџџџџџџџџџџџеД’…’e’…’eŠeŠ…Š…Š…ŠDŠDŠ$Š$Š$ЭSџџџџџџџџїž“)‚$‚$‚$‚E‚$‚‚‚‚‚$‚$‚‚$‚$Š†яџџџџџџџџо7ŠE‚$‚E‚e‚e‚e‚e‚eŠeŠ…Š…Š…Š…МёџџџџџџџџџџџџџџџџџџџџџџџџяЌ ››››Hц˜џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМŽ›šцšЦšц››'ЭsџпџџџџџџџџџџџџџџџџџџџџЋЋŠeŠeŠeŠEŠ$ŠE‚e‚D‚E‚$‚$‚$‚‚$ЭЕџџџџџџМё‚E‚E‚$‚‚‚%‚‚‚‚$zE‚$‚‚‚‚Ќ џџџџџџо7‚E‚‚$‚E‚E‚E‚E‚E‚e‚e‚e‚e‚e‚e“(їžџџџџџџџџџџџџџџџџџџџџеѕ›H“››››ЋЫџОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїЃi››šцšчšцšч›Д яџџџџџџџџџџџџџџџџцкŠEŠE‚e‚E‚$‚$‚$‚$‚‚‚%‚$‚‚‚‚%ЌNЌ.Ћ‹ŠEŠDŠ$‚‚‚z%z%‚z$z%z%‚‚‚‚‚‚›jЌ ДoŠЇz%z%z%zEzEzEzEzE‚E‚E‚E‚E‚E‚E‚eжџџџџџџџџџџџџџџџџї}Д’Ц’Ц’ч’ч“““›(юњџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюњ›'šцšц›šчšЦšІ’Ц’ІšчеѕџџџџџџџџџџџџџџМб‚$‚$‚E‚$‚‚‚‚‚‚z%z%‚‚‚‚‚ŠŠŠŠ$ŠŠ‚zzz%‚Š‚$‚$‚zzzzz%z%zzzzzz%z%z%zEzEzEzEzEzEzEzEzE‚EЌ џџџџџџџџџџџџџџоx›IŠ†ŠІŠІŠІ’Ц’Ц’ч’ч“оwџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоWšчšЦšЦšЦ’Ц’…’…’ІŠ…ŠeŠDДoїџџџџџџџџџО’ш‚‚‚$‚%‚‚‚zz%z%z%z%z%z%zz‚$‚$‚$‚$‚D‚‚z%z%z%zE‚Š‚D‚$‚zzzzzz%zzzzz%z%z%z%z%z%z%z%z%z%z%zEzEzE‚†я<џџџџџџџџџоН‚e‚e‚e‚eŠ…Š…ŠІŠІŠІ’Ц’цЭ“џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭ“šч’Ц’Ѕ’Ѕ’…’dŠeŠ…ŠeŠDŠ$Š$›)о™џџџџџџеі‚%‚‚z%z%z%z%z%z%z%z%zz%z%z%z‚‚‚$‚E‚E‚E‚E‚%z%z%z%z%‚%‚$‚E‚$‚zz%zzzz%zzzz%zEzEzEzEzEzEzEzEz%z%z%z%z%z%z%Нџџџџџџюћ›ЋzEzE‚E‚e‚e‚e‚e‚…Š…Š…ŠІŠІДџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМŽ’Ѕ’ІŠІŠ…ŠDŠDŠD‚e‚D‚‚‚‚%‚eНХtЃЬ‚fz%z%z%z%zzz%z%zEz%zzz%z%zz‚Š$Š$ŠeŠ…’e’e’…’e’e’e’ešЅ’І’І’ІŠІŠІŠІŠІŠ†‚f‚f‚E‚%zEzErErErErEzEzEzEzEzEzEzEzEzEz%z%zf›‹Н2Хt‚ЇzEzEzEzEzEzE‚E‚E‚e‚e‚e‚…Š…ЃЋџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЃŠ’dŠeŠ…ŠDŠ$Š$‚$‚$‚%‚$‚‚z%z%z%z%z$zzzz%z%zzzzz%z%z%‚E‚†Šf’e’e’…š…š…šІš…’e’D’DŠ$Š$Š$Š$‚$zEzE‚E‚ezf‚†‚†ŠІŠІŠІ’Ч’І’Ч’ЧŠЇŠІ‚†‚fzfzErErErErEzEzEzEzEzEzEzEz%z%z%z%z%zEzEzEzEzEzEzEzE‚E‚e‚e“ї}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}›ŠeŠe‚E‚$‚‚‚‚‚z%z%zzz%z%zzzzzz%z%z%z$‚%ŠE’e’І’Ч’ЦŠІŠf‚%zqфyфqФqфqфqФqУqФqфqфqфqфqфqфqФiФiФiфiфiфaфaфiфjjr%r%zE‚†ŠІŠІ’Ч’ЧŠІ‚†‚fzErErErErErErErEzEzEzEzEz%z%z%z%zEzEzEzEzEzEzEzE‚†цћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпя<їџџџџџџџџџџџџџџџџџџџџџџџџџџюњŠ†‚E‚$‚‚‚‚‚$‚z$z%z$zzz%zzzzzz%‚fŠ†ŠІ’І’e’eŠ%z%rqфiФiфqфyфzz$z‚z%‚$‚‚z%zEzE‚$‚z%zzzz%rErErEr%r%r%r%rjjiфaфiфjr%zfŠ†ŠІ’ЧŠЇŠ†‚fzErErErErErErEzEzEzEzEz%z%z%z%z%zEzEzEzEzEоWџџџџџџџџџџџџџџџџџџџџџџџџџџїžя;џОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцxЋыЃHЃiМаюкџџџџџџџџџџџџџџџџџџџџџџо7‚$‚%‚‚‚‚z%zzzzz%z%z%zEzz‚$ŠEŠІ’Ч’ЦŠ†zer%qфqфqфrz$z‚‚EzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEz%z%z%rErErErErErErErErEzEzEzEz%r%r%jiфaфiфjzE‚†ŠІ’ЧŠІ‚†zfrErErErErErErErEr%z%z%z%z%z%zEzEzEХSџџџџџџџџџџџџџџџџџџџџџџяХЃŠ›HЃЊеѕџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцк›(›››››'ДnоxџпџџџџџџџџџџџџџџџџХ2‚‚$‚‚‚z%z%zzzzz%zEz%zEŠešeЂ…šeŠez%jiфrz$z%zEzEzEzE‚$‚%zEzEzEzEzEzEzEzEzEzEzEzEzE‹Ќ.ДЌ›‹zfzEzErErErErErErErEzEzEzEzEzEzEzErErEr%r%rjaфiфr%zfŠІ’ЧŠЇ‚†zFrEr%rErErErErErErEr%r%z%z%z%ЌOџџџџџџџџџџџџџџџџџџц™Дo’чŠЦ’ч“““ж6џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФ№››››šч’ц’Ц’ІЃЋееїžџџџџџџџџџџџџЌ.‚z%zzzzz%z%zzz%zE‚e’eš…’ІŠDzqфqфrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzE‚ШЭЕїžџџџџџџџџцКЃэzEzEzErErErErEzEzEzEzEzEzEzEzErEzEzEzEzEzEzEr%riфaфjzFŠІŠЧŠЇ‚†zFr%r%rErErErErErEr%r%r%“‹џџџџџџџџџџџџџпж7ЃЬ‚…‚…‚…ŠІŠІŠЦ’ч“ЌMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМЏ››šчšЦ’Ѕ’ІŠІŠ…ŠeŠDšшХ2ї\џџџџџџї\“ z%z%z%zzzz%zEzE‚e’ІšЦ’ІŠ$ziфrz$zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzE“Jя<џџџџџџџџџџџџџџџџМђzEzErErErErEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEr%r%jaфjzfŠІ’ЧŠІzfrEr%rErErErErErEr%‚ЇцКџџџџџџїžЭЕ“JzEzEze‚e‚e‚…‚†ŠІŠЦŠЦЃЋџОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМЏ›šц’Ѕ’…’eŠ…Š…ŠeŠ$‚$‚‚ŠЇДбцкХŠ†zzzzEz%zz‚EŠ…šЦšЦ’…z%iфqфr$zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzE‚ЇцћџџџџџџџџџџџџџџџџџџџџЄ.zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErEr%r%iхaфj%‚†ŠЧŠЧ‚†zFr%rErErErErErEz‡ДБцњН3Šшz%z%z%zEzEzEze‚e‚e‚…‚†ŠІЃьџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМЏ’Ц’Ѕ’…ŠdŠDŠe‚e‚E‚‚‚‚z$z%z$zzzzzzz$‚E’…šЦšЦŠeriфrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEН2џџџџџџџџџџџџџџџџџџџџџџя‚ЇzEzEzEzErErErErErErErErErEzErErEzEzEzEzEzEzEzErErErErErEr%jaфiхzFŠЇŠЧ‚†zFr%rErErErErErEzFr%r%r%r%z%z%zEzEzEzEze‚e‚e‚…ЃьџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМа’…’eŠeŠDŠD‚e‚E‚E‚$‚‚‚zzzzzz‚‚‚$’…šЦ’І‚Eiфiфz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEz†яџџџџџџџџџџџџџџџџџџџџџџџџЄ.rErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфr%‚І’Ч‚†zFr%rErErErErErErEr%r%r%r%z%z%z%zEzEzE‚e‚eЃЫџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФаŠdŠDŠe‚E‚E‚$‚‚‚$‚zzzzzz%‚%‚Š$’ešЦ’Іz%iфrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErE‹*џпџџџџџџџџџџџџџџџџџџџџџџџџХSrErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфr%‚ІŠЧ‚†zFr%rErErErErErErEr%r%r%r%z%z%zEzEzEzEЄ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМаŠDŠ$‚E‚E‚E‚‚‚zzzzzz%z%zE‚E’Eš…šez$iфrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErE“jџпџџџџџџџџџџџџџџџџџџџџџџџџХ•rErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфr%‚І’Ч‚†zFr%rErErErErErErEr%r%r%r%z%z%zEzEЌ.џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФё‚$‚$‚‚‚$zzzzzzzz%z%z%ŠEš…še‚iУrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErE‹)џпџџџџџџџџџџџџџџџџџџџџџџџџНSrErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфr%ŠІ’Ч‚†rEr%rErErErErErErEr%r%r%r%z%z%Є.џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХS‚‚‚‚zz%z%z%zzzzz‚%šeš…‚$qФqФzzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErErErErEz†яџџџџџџџџџџџџџџџџџџџџџџџџœrErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфrEŠІ’ЦzfrErErErErErErErEr%r%r%r%r%ЌOџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХS‚‚zz$zzzzzzzz’Eš…ŠEqФqФzz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErErErErErErErErErEДђџџџџџџџџџџџџџџџџџџџџџџчz‡rFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErErErErErErErErErErErErErErErErErErErEr%jaфzf’ЧŠІzErErErErErErErErEr%r%r%ДБџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХS‚zz%zzzzzzz‚%š…’eyфiФyфz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErErErErErErErErErErErErEz‡цКџџџџџџџџџџџџџџџџџџџџ›эrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErErErErErErErErErErErErEr%aхj‚†’Ч‚†rErErErErErErEzEzEzEДАџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїžџџџџџџџџџџџџџџџџџџџџџџџџџџеіz%z%zzzzzzz’eš…‚$iФqфz%z%zEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErEz‡“‹ЌЭецњН3rErErErFrFrFrFrFrFƒ цкџџџџџџџџџџџџџџџпЌrFrFrFrFrFrFrFrFrFrFН3цћЭіЌБ“ŒzЇrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErErErErErErErErEjaфrEŠЧŠЇzFrErErErErErErErEДбџџџџџџџџџџџџџџџџџџџџџџџџџџїОџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоЋыЃ‰ЋыФ№жяџпџџџџџџџџџџџџџџџџеіz%z%zzzzz‚%š…’eqфiФz%zEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErf‹JЌжя<џпџџџџџџџџН3rFrFrFrFrFrFrFrFrFzЇХtї}џџџџџџџпоy“ЌrFrFrFrFrFrFrFrFrFrFХ•џџџџџџџџџџя\жXДб“‹rfrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErErErEr%baх‚†ŠЧ‚†rErErErErErErEН3џџџџџџџџџџџџџџџџџџя<ж7НЃЬ“I›ЋЭДџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоšЦšЦšЦ’Ѕ’…Š…ŠІ“)ЌoЭ”цЙїžџџџџџџюћ“)z$z%zzzzŠ†šІ‚EiФqфz%zEz%z%zEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErEzf“ЌНSцњџоџџџџџџџџџџџџџџџџџџЌБrFrFrFrFrFrFrFrFrFrFzШ“ЭЄOœ.ƒ*rFrFrFrFrFjFrFrFrFrFrFХЕџџџџџџџџџџџџџџџџџџџпяХ•›эzЇrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErEjaфrEŠЧŠІrFrErErErErEƒ оyџџџџџџџОцњЭеД“J‚І‚e‚e‚…Š†ŠІŠЦХSџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋЫšЦšЦ’Ѕ’…’eŠe‚E‚Ez%‚$‚%ŠЧЃЬХSЭ•ŠЇzzzEz%z‚$’…’†qфiфr%zEzEz%zzz%zEzEzEzEzErErErErErErErErErErErErErErErF“‹НTчџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌАrFrFrFrFrFrFrFrFrFjFrFrFjFjFjFjFjFjFjFjFjFjFjFrgЭіџџџџџџџџџџџџџџџџџџџџџџџџџџџџя\Х•“ЭrgjFjFjFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFr%aхj‚†ŠЧzfrErErErErEz‡ХtХ”Є.‹ zfz%zEzEzEzE‚E‚e‚eŠ…ŠІ“(ї}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ЃHšЦ’Ѕ’…Š…ŠD‚D‚$z$‚‚‚zzzzzzzz%z‚%š…ŠEiФqФzz%zEzEzzzz%zEzEzErErErErErErErErErErErErErErE‚шДбоКџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌrFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFrgж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцћЕƒ jFjFjFjFjFjFjFjFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFr%jaхzfŠЧ‚†rErErErErErErErErErEr%z%zEzEzEzE‚E‚e‚e‚…ŠЦюњџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпЃЫ’Ц’…Še‚e‚D‚$‚$‚‚zzzzzzz%zzzŠEš…‚iФrz%zzzz%z%zz%zErErErErErErErErErErErErErErErE“‹ХЕїџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпœ.jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFr‡ж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїОЮ“ЬrFjFjFjFjFjFjFjFjFjFjFrFrFrFrFrFrFrFrFrFrFrFrFrFj%aхr%ŠЧ‚ІrFrErErErErErErErErEr%r%z%zEzEzEzE‚e‚e’чї}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМаŠІŠ†‚…‚E‚$‚$z$zzzzzzz‚$z%‚‚’ešeyфiФzz%zEzEzzz%zEz%rErErErErErErErErErErErErErErF›ЭжXџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпœ.jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFzШо™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™Є/rgjFjFjFjFjFjFjFjFjFjFjFjFjFrFrFrFrFrFrFrFrFrFr%aхb‚ЇŠЇrFr%rErErErErErErErEr%r%z%zEzEzEzE‚eЃЫџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭеŠ†Š…‚E‚$‚‚zzzzrrzz‚z$‚$še’eqФqФzzzz%zEzzzzzrErErErErErErErErErErErErFЄоЙџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџп“ЭjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFzШоКџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцћЄorgjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFrFrFrFrFrFrFr%jaхz†ŠЧzfr%rFrErErErErErErErEr%r%zEzEzEzEМёџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцњŠІ‚e‚$‚zzzzzzrrz‚z‚%šeŠEiФqфzzzzzzzzzz%rErErErErErErErErErErErE›Эоxџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїž“ЌjFjFjFjFjFjFjFjFjFjFjFjFjFƒ цкџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцћœ.jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFrFrFrFrFrFj%aхzfŠЧzfr&rFrFrFrErErErErErEr%r%z%zEzEж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџО“‚Ez%zzzzzzzrz‚zŠ%š…‚%iФrzzzz%z%zzzzz%rErErErErErErErErErErF‹Kж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїž“ЌjFjFjFjFjFjFjFjFjFjFjFƒ чџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™“ЌjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFrFrFj%aхrFŠЧz†r&rFrFrFrFrFrErErEr%r%r%r%‚†я<џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌ ‚%z%zzrzzzzzzzŠEš…‚iФzz%zzzz%z%zzzr%rErErErErErErErErErFzЈНtџоџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ƒJjFjFjFjFjFjFjFjFjFƒJяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭіƒ jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj%aхj%ŠЧz†rFrFrFrFrFrFrFrEr%r%r%r%“jџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХSz$zrrrzr%rrr%zŠEš…ziФzzz%z%zz%rErEr%r%rErErErErErErErErFrFrFЄOя<џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя\‹kjFjFjFjFjFjFjF‹Œя}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ЌбrfjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхj%ŠЇ‚†rFrFrFrFrFrFrFrFr%r%r%Ќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™z$zrrr%r%z%rrr‚Eš…ziФzzzzzzrErErEr%r%rErErErErErErFrFrFƒ ж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїžЕ{ jgjFjg{ Е3џОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™‹kjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхb‚Ї‚‡jFrFrFrFrFrFrFrFr&r%ЭжџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїŠШz%z%z%z%z%zzr‚Eš…ziФzzzzzzzrEr%zzrrrErErErFrFrFrFЌї}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїОоКжyокїОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпДђrfjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&bb‚Ї‚‡jFrFrFrFrFrFrFrFrfцћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ›Ьr%r%z‚z%zzŠEš…riфzzzzzzzzr%r%rrrrr%rFrFrFrFzШЭіџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™ƒ*jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&bb‚Ї‚‡jFjFrFrFrFrFrF‹*џОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџНrrrzzzŠEš…riФzz%z%z%rErEr%r%zrr%r%rrr%rFrFrFrF“Ья<џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїœOjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&bb‚Їz‡j&jFjFrFrFrFЄOџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХSrrrzEzŠ%š…ziФr%zzzr%rErErErEr%r%r%rEr%r%rFrFrFrFДёџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџНtjgjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&bb‚Чz†j&rFrFrFrFЌБџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцК‚†rrz%z%‚%š…ziФzz%z%zzzrErErErErErFrFrFrFrFrFrFz‡ЭіџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоyzщjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхj%‚Чzfr%rFrFrFrfж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџц˜МЏД-Ќ ДnДДДАНМђНЭ”ЭЕЭЕ‹ zzzzz%š…‚%aФrzrEr%zzr%rErErErErFrFrFrFrFrFrFƒ цкџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя<‹ŒjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхjFŠЧzfr&rFrFrFzШХ”ХЕХЕН3ННДБЌoЌЌoЃэЃэЄ.жџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџж’Ц’ЅŠeŠd‚D‚$‚$zzzzzzr%zzzzz’f‚EaФrzz%r%rEr%r%r%rErErFrFrFrFrFrFrFrF“Œя\џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїОœ.jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&YхrFŠЧrfr&rFrFrFrFrFrFrFr%rrr%r%r%z%z%zEzfНџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїЃi’ЅŠ…ŠeŠD‚$zzzzrzzrrzzzz’eŠEaФr%rEzzzr%rEr%rr%rFrFrFrFrFrFrFrFœїОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌБjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&YхzfŠЧrFrFrFrFrFrFrFrFr%rrrr%r%zzzzE‚Їюћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцк’ЦŠ…ŠeŠD‚E‚$zzz%r%rzrrrzzzŠE’eiФqфz%r%zrrrrrrrFrFrFrFrFrFrFrFЌџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЕjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj%aх‚‡‚ЇrFrFrFrFrFrFr%rrrrrr%rzzz%zeжџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя<’чŠeŠD‚D‚$‚zz%z%zzz%zrzzz%‚E’…qфiФzzr%rrrrrrr%rFrFrFrFrFrFjFЌџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџН”jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFbbŠЇ‚†rFrFrFrFr%rrrrrrrrrzz%z%оxџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌ-ŠD‚E‚$‚zzrr%rzz%z%zzz‚%š†zaфrzr%r%rrrrrrr%rFrFrFrFjFjFДђџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХ•jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFbj%ŠЧzfrrrrrrrrrrrr%rrz“)џОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоx‚D‚E‚$‚zrrr%rzzr%r%rz%’e‚%aФrrrr%r%rrrrrrrFrFrFjFjFjFЌАџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџН•jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхrFŠЇrrrrrrrrrrrrrrrХ”џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпЃ‹‚Ezzzrr%r%rzzr%r%j%ŠfŠeaФqфrrrrr%r%rrr%rFrFrFjFjFjFjFЌАџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџНSjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj%aх‚†Š†r%r%r%rrrrrrrrrr‚Јїџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџеіz%zzzr%r%r%r%rrrrzE’fiфiфrrrrrr%rFrFrFrFrFrFjFjFjFjF“Ќя\ї}я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]ї}їОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЕjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj%bŠЇz†jFrFr%rrrrrrrrr%НџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџО“)rrr%r%r%r%r%zzzz%’fzaФrrrrrrrFrFrFrFrFjFjFjFjFjFjFrfrgrgrgrgrgrgrgrgrgrgrgrgrgrgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgrЈ{*“ЭЄХЕцкїОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЄpj&jFjFjFjFjFjFjFjFjFjFjFj&j&j&rFbjŠ†zFjFrErrrr%rrrr%zЇя<џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХtzrr%r%r%rzzzzŠeŠEaФrrrr%r%rr%rFrFrFrFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&j&jFjFjFjFb&b&b&b&b&bFbFbFb&b&b&b&b&b&b&b&b&b&bFb&bFb&b&b&b&b&b&bFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbF{*œOЭїя]џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџп“юj&j&jFjFjFjFjFjFjFjFjFjjjj%r%aФz%Š†r%rrrrr%rrr%rFЌџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ŠЇzr%r%rzrrr‚E’fiФiхr%rrr%rFrFrFrFrFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&j&jFjFjFjFb&b&b&b&b&bFbFbFb&b&b&b&b&b&b&b&b&bFb&b&b&b&b&b&bFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFrЈœOжyџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ƒKjFj&j&j&jFjFjFjFjFjFjjjjrEjaФŠf‚Errrrrrrr%rgцћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМђrr%r%rrrrz%’fzaФrr%rrrFrFrFrFrFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&j&jFjFjFb&b&b&b&b&bFbFbFb&b&b&b&b&b&b&b&bFb&b&b&b&b&b&bFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFƒkХжџоџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцћrЈjFjFjFj&j&jFjFjFjFj&jjjj&rFiхiх’†z%rrrrrrr%Є.џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя Date: Mon, 29 Jul 2024 17:09:36 -0700 Subject: [PATCH 17/23] Removed gc9a01a_driver Removed the display driver and cleaned up the examples so they always use the frame buffer. --- boards/waveshare-rp2040-lcd-1-28/Cargo.toml | 2 +- .../examples/frame_buffer.rs | 1 + .../examples/gc9a01a_driver.rs | 484 ------------------ .../examples/waveshare_rp2040_lcd_demo.rs | 25 +- 4 files changed, 18 insertions(+), 494 deletions(-) delete mode 100644 boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs diff --git a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml index 7c5cdf94..94031a49 100644 --- a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml +++ b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml @@ -16,6 +16,7 @@ rp2040-boot2 = { workspace = true, optional = true } rp2040-hal.workspace = true embedded-hal = { version = "0.2" } libm = "0.2" +gc9a01a_driver = { version = "0.1.1" } [dev-dependencies] cortex-m.workspace = true @@ -23,7 +24,6 @@ panic-halt.workspace = true fugit.workspace = true nb.workspace = true embedded-graphics.workspace = true -st7735-lcd = { workspace = true, features = ["graphics"] } [features] # This is the set of features we enable by default diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs b/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs index b00f9a9c..e7f70b4e 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs @@ -1,4 +1,5 @@ #![no_std] +#![no_main] use embedded_graphics::{pixelcolor::Rgb565, prelude::*,}; use core::convert::Infallible; diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs b/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs deleted file mode 100644 index b2f5865c..00000000 --- a/boards/waveshare-rp2040-lcd-1-28/examples/gc9a01a_driver.rs +++ /dev/null @@ -1,484 +0,0 @@ -#![no_std] - -use embedded_hal::blocking::delay::DelayMs; -use embedded_hal::blocking::spi; -use embedded_hal::digital::v2::OutputPin; - -pub enum Instruction { - NOP = 0x00, - SWRESET = 0x01, - RDDID = 0x04, - RDDST = 0x09, - SLPIN = 0x10, - SLPOUT = 0x11, - PTLON = 0x12, - NORON = 0x13, - INVOFF = 0x20, - INVON = 0x21, - DISPOFF = 0x28, - DISPON = 0x29, - CASET = 0x2A, - RASET = 0x2B, - RAMWR = 0x2C, - RAMRD = 0x2E, - PTLAR = 0x30, - COLMOD = 0x3A, - MADCTL = 0x36, - FRMCTR1 = 0xB1, - FRMCTR2 = 0xB2, - FRMCTR3 = 0xB3, - INVCTR = 0xB4, - DISSET5 = 0xB6, - PWCTR1 = 0xC0, - PWCTR2 = 0xC1, - PWCTR3 = 0xC2, - PWCTR4 = 0xC3, - PWCTR5 = 0xC4, - VMCTR1 = 0xC5, - RDID1 = 0xDA, - RDID2 = 0xDB, - RDID3 = 0xDC, - RDID4 = 0xDD, - PWCTR6 = 0xFC, - GMCTRP1 = 0xE0, - GMCTRN1 = 0xE1, -} - -pub struct GC9A01A -where - SPI: spi::Write, - DC: OutputPin, - CS: OutputPin, - RST: OutputPin, -{ - /// SPI - spi: SPI, - - /// Data/command pin. - dc: DC, - - //pin - cs:CS, - - /// Reset pin. - rst: RST, - - /// Whether the display is RGB (true) or BGR (false) - rgb: bool, - - /// Whether the colours are inverted (true) or not (false) - inverted: bool, - - /// Global image offset - dx: u16, - dy: u16, - width: u32, - height: u32, -} - -/// Display orientation. -#[derive(Clone, Copy)] -pub enum Orientation { - Portrait = 0x00, - Landscape = 0x60, - PortraitSwapped = 0xC0, - LandscapeSwapped = 0xA0, -} - -impl GC9A01A -where - SPI: spi::Write, - DC: OutputPin, - CS: OutputPin, - RST: OutputPin, -{ - /// Creates a new driver instance that uses hardware SPI. - pub fn new( - spi: SPI, - dc: DC, - cs: CS, - rst: RST, - rgb: bool, - inverted: bool, - width: u32, - height: u32, - ) -> Self { - let display = GC9A01A { - spi, - dc, - cs, - rst, - rgb, - inverted, - dx: 0, - dy: 0, - width, - height, - }; - - display - } - - /// Runs commands to initialize the display. - pub fn init(&mut self, delay: &mut DELAY) -> Result<(), ()> - where - DELAY: DelayMs, - { - - self.hard_reset(delay)?; - self.write_command(0xEF as u8, &[])?; - self.write_command(0xEB as u8, &[0x14])?; - self.write_command(0xFE, &[])?; - self.write_command(0xEF, &[])?; - self.write_command(0xEB, &[0x14])?; - self.write_command(0x84, &[0x40])?; - self.write_command(0x85, &[0xFF])?; - self.write_command(0x86, &[0xFF])?; - self.write_command(0x87, &[0xFF])?; - self.write_command(0x88, &[0x0A])?; - self.write_command(0x89, &[0x21])?; - self.write_command(0x8A, &[0x00])?; - self.write_command(0x8B, &[0x80])?; - self.write_command(0x8C, &[0x01])?; - self.write_command(0x8D, &[0x01])?; - self.write_command(0x8E, &[0xFF])?; - self.write_command(0x8F, &[0xFF])?; - self.write_command(0xB6, &[0x00, 0x20])?; - self.write_command(0x36, &[0x98])?; - self.write_command(0x3A, &[0x05])?; - self.write_command(0x90, &[0x08, 0x08, 0x08, 0x08])?; - self.write_command(0xBD, &[0x06])?; - self.write_command(0xBC, &[0x00])?; - self.write_command(0xFF, &[0x60, 0x01, 0x04])?; - self.write_command(0xC3, &[0x13])?; - self.write_command(0xC4, &[0x13])?; - self.write_command(0xC9, &[0x22])?; - self.write_command(0xBE, &[0x11])?; - self.write_command(0xE1, &[0x10, 0x0E])?; - self.write_command(0xDF, &[0x21, 0x0C, 0x02])?; - self.write_command(0xF0, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])?; - self.write_command(0xF1, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6F])?; - self.write_command(0xF2, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])?; - self.write_command(0xF3, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6F])?; - self.write_command(0xED, &[0x1B, 0x0B])?; - self.write_command(0xAE, &[0x77])?; - self.write_command(0xCD, &[0x63])?; - self.write_command(0x70, &[0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03])?; - self.write_command(0xE8, &[0x34])?; - self.write_command(0x62, &[0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70])?; - self.write_command(0x63, &[0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70])?; - self.write_command(0x64, &[0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07])?; - self.write_command(0x66, &[0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00])?; - self.write_command(0x67, &[0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98])?; - self.write_command(0x74, &[0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00])?; - self.write_command(0x98, &[0x3E, 0x07])?; - self.write_command(0x35, &[])?; - self.write_command(0x21, &[])?; - self.write_command(0x11, &[])?; - self.write_command(0x29, &[])?; - - delay.delay_ms(200); - - Ok(()) - } - - pub fn hard_reset(&mut self, delay: &mut DELAY) -> Result<(), ()> - where - DELAY: DelayMs, - { - self.rst.set_high().map_err(|_| ())?; - delay.delay_ms(10); - self.rst.set_low().map_err(|_| ())?; - delay.delay_ms(10); - self.rst.set_high().map_err(|_| ())?; - delay.delay_ms(10); - - Ok(()) - } - - fn write_command(&mut self, command : u8, params: &[u8]) -> Result<(), ()> { - self.cs.set_high().map_err(|_| ())?; - self.dc.set_low().map_err(|_| ())?; - self.cs.set_low().map_err(|_| ())?; - self.spi.write(&[command]).map_err(|_| ())?; - if !params.is_empty() { - self.start_data()?; - self.write_data(params)?; - } - self.cs.set_high().map_err(|_| ())?; - Ok(()) - } - - fn start_data(&mut self) -> Result<(), ()> { - self.dc.set_high().map_err(|_| ()) - } - - fn write_data(&mut self, data: &[u8]) -> Result<(), ()> { - self.cs.set_high().map_err(|_| ())?; - self.dc.set_high().map_err(|_| ())?; - self.cs.set_low().map_err(|_| ())?; - self.spi.write(data).map_err(|_| ()); - self.cs.set_high().map_err(|_| ())?; - Ok(()) - } - - /// Writes a data word to the display. - fn write_word(&mut self, value: u16) -> Result<(), ()> { - self.write_data(&value.to_be_bytes()) - } - - fn write_words_buffered(&mut self, words: impl IntoIterator) -> Result<(), ()> { - let mut buffer = [0; 32]; - let mut index = 0; - for word in words { - let as_bytes = word.to_be_bytes(); - buffer[index] = as_bytes[0]; - buffer[index + 1] = as_bytes[1]; - index += 2; - if index >= buffer.len() { - self.write_data(&buffer)?; - index = 0; - } - } - self.write_data(&buffer[0..index]) - } - - pub fn set_orientation(&mut self, orientation: &Orientation) -> Result<(), ()> { - if self.rgb { - self.write_command(Instruction::MADCTL as u8, &[*orientation as u8])?; - } else { - self.write_command(Instruction::MADCTL as u8, &[*orientation as u8 | 0x08])?; - } - Ok(()) - } - - /// Sets the global offset of the displayed image - pub fn set_offset(&mut self, dx: u16, dy: u16) { - self.dx = dx; - self.dy = dy; - } - - /// Sets the address window for the display. - pub fn set_address_window(&mut self, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), ()> { - self.write_command(Instruction::CASET as u8, &[])?; - self.start_data()?; - self.write_word(sx + self.dx)?; - self.write_word(ex + self.dx)?; - self.write_command(Instruction::RASET as u8, &[])?; - self.start_data()?; - self.write_word(sy + self.dy)?; - self.write_word(ey + self.dy) - } - - /// Sets a pixel color at the given coords. - pub fn set_pixel(&mut self, x: u16, y: u16, color: u16) -> Result<(), ()> { - self.set_address_window(x, y, x, y)?; - self.write_command(Instruction::RAMWR as u8, &[])?; - self.start_data()?; - self.write_word(color) - } - - /// Writes pixel colors sequentially into the current drawing window - pub fn write_pixels>(&mut self, colors: P) -> Result<(), ()> { - self.write_command(Instruction::RAMWR as u8, &[])?; - self.start_data()?; - for color in colors { - self.write_word(color)?; - } - Ok(()) - } - pub fn write_pixels_buffered>( - &mut self, - colors: P, - ) -> Result<(), ()> { - self.write_command(Instruction::RAMWR as u8, &[])?; - self.start_data()?; - self.write_words_buffered(colors) - } - - /// Sets pixel colors at the given drawing window - pub fn set_pixels>( - &mut self, - sx: u16, - sy: u16, - ex: u16, - ey: u16, - colors: P, - ) -> Result<(), ()> { - self.set_address_window(sx, sy, ex, ey)?; - self.write_pixels(colors) - } - - pub fn set_pixels_buffered>( - &mut self, - sx: u16, - sy: u16, - ex: u16, - ey: u16, - colors: P, - ) -> Result<(), ()> { - self.set_address_window(sx, sy, ex, ey)?; - self.write_pixels_buffered(colors) - } - - /// Draws an image from a slice of RGB565 data - pub fn draw_image(&mut self, image_data: &[u8]) -> Result<(), ()> { - // Assuming the image dimensions match the display dimensions - let width = self.width as u16; - let height = self.height as u16; - - self.set_address_window(0, 0, width - 1, height - 1)?; - self.write_command(Instruction::RAMWR as u8, &[])?; - self.start_data()?; - - for chunk in image_data.chunks(32) { - self.write_data(chunk)?; - } - - Ok(()) - } - - - pub fn show(&mut self, buffer: &[u8]) -> Result<(), ()> { - self.write_command(Instruction::CASET as u8, &[])?; - self.write_data(&[0x00, 0x00, 0x00, 0xEF])?; - - self.write_command(Instruction::RASET as u8, &[])?; - self.write_data(&[0x00, 0x00, 0x00, 0xEF])?; - - self.write_command(Instruction::RAMWR as u8, &[])?; - - self.cs.set_high().map_err(|_| ())?; - self.dc.set_high().map_err(|_| ())?; - self.cs.set_low().map_err(|_| ())?; - self.spi.write(buffer).map_err(|_| ())?; - self.cs.set_high().map_err(|_| ())?; - - Ok(()) - } - -// Updates only the specified region of the display with the provided buffer. -pub fn show_region(&mut self, buffer: &[u8], region: Rectangle) -> Result<(), ()> { - let Rectangle { top_left, size } = region; - let sx = top_left.x as u16; - let sy = top_left.y as u16; - let ex = (top_left.x + size.width as i32 - 1) as u16; - let ey = (top_left.y + size.height as i32 - 1) as u16; - - // Calculate the buffer offset for the region - let buffer_width = self.width as usize; - let bytes_per_pixel = 2; // For RGB565 - - self.set_address_window(sx, sy, ex, ey)?; - self.write_command(Instruction::RAMWR as u8, &[])?; - self.start_data()?; - - for y in sy..=ey { - let start_index = ((y as usize) * buffer_width + (sx as usize)) * bytes_per_pixel; - let end_index = start_index + (size.width as usize) * bytes_per_pixel; - - for chunk in buffer[start_index..end_index].chunks(32) { - self.write_data(chunk)?; - } - } - - Ok(()) -} - -} - - -extern crate embedded_graphics; - -use self::embedded_graphics::{ - draw_target::DrawTarget, - pixelcolor::{ - raw::{RawData, RawU16}, - Rgb565, - }, - prelude::*, - primitives::Rectangle, -}; - - -impl DrawTarget for GC9A01A -where - SPI: spi::Write, - DC: OutputPin, - CS: OutputPin, - RST: OutputPin, -{ - type Error = (); - type Color = Rgb565; - - fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> - where - I: IntoIterator>, - { - for Pixel(coord, color) in pixels.into_iter() { - // Only draw pixels that would be on screen - if coord.x >= 0 - && coord.y >= 0 - && coord.x < self.width as i32 - && coord.y < self.height as i32 - { - self.set_pixel( - coord.x as u16, - coord.y as u16, - RawU16::from(color).into_inner(), - )?; - } - } - - Ok(()) - } - - fn fill_contiguous(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error> - where - I: IntoIterator, - { - // Clamp area to drawable part of the display target - let drawable_area = area.intersection(&Rectangle::new(Point::zero(), self.size())); - - if drawable_area.size != Size::zero() { - self.set_pixels_buffered( - drawable_area.top_left.x as u16, - drawable_area.top_left.y as u16, - (drawable_area.top_left.x + (drawable_area.size.width - 1) as i32) as u16, - (drawable_area.top_left.y + (drawable_area.size.height - 1) as i32) as u16, - area.points() - .zip(colors) - .filter(|(pos, _color)| drawable_area.contains(*pos)) - .map(|(_pos, color)| RawU16::from(color).into_inner()), - )?; - } - - Ok(()) - } - - fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> { - self.set_pixels_buffered( - 0, - 0, - self.width as u16 - 1, - self.height as u16 - 1, - core::iter::repeat(RawU16::from(color).into_inner()) - .take((self.width * self.height) as usize), - ) - } - - -} - - -impl OriginDimensions for GC9A01A -where - SPI: spi::Write, - DC: OutputPin, - CS: OutputPin, - RST: OutputPin, -{ - fn size(&self) -> Size { - Size::new(self.width, self.height) - } -} diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 86a3aa80..bf915d12 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -6,9 +6,9 @@ #![no_std] #![no_main] -mod gc9a01a_driver; mod frame_buffer; +use gc9a01a_driver::{GC9A01A,Orientation}; use cortex_m::delay::Delay; use embedded_graphics::primitives::Line; use fugit::RateExtU32; @@ -38,7 +38,6 @@ use embedded_graphics::{ image::{ImageRaw, Image}, }; use libm::{cos, sin}; -use gc9a01a_driver::{Orientation, GC9A01A}; const LCD_WIDTH: u32 = 240; const LCD_HEIGHT: u32 = 240; @@ -103,7 +102,7 @@ fn main() -> ! { ); // Initialize the display - let mut display = GC9A01A::new(spi, lcd_dc, lcd_cs, lcd_rst, false, true, LCD_WIDTH, LCD_HEIGHT); + let mut display = GC9A01A::new(spi, lcd_dc, lcd_cs, lcd_rst, false, LCD_WIDTH, LCD_HEIGHT); display.init(&mut delay).unwrap(); // Create two frame buffers for double buffering @@ -120,7 +119,8 @@ fn main() -> ! { .build(); // Clear the screen before turning on the backlight - display.clear(Rgb565::BLACK); + frame_buffer_1.clear(Rgb565::BLACK); + display.show(frame_buffer_1.get_buffer()).unwrap(); delay.delay_ms(1000); _lcd_bl.set_high().unwrap(); delay.delay_ms(1000); @@ -128,8 +128,9 @@ fn main() -> ! { // Draw a blue rectangle Rectangle::with_corners(lcd_zero, lcd_max_corner) .into_styled(style) - .draw(&mut display) + .draw(&mut frame_buffer_1) .unwrap(); + display.show(frame_buffer_1.get_buffer()).unwrap(); delay.delay_ms(1000); // Draw a black rectangle inside the blue rectangle @@ -141,22 +142,25 @@ fn main() -> ! { Point::new((LCD_WIDTH - 2) as i32, (LCD_HEIGHT - 2) as i32), ) .into_styled(style) - .draw(&mut display) + .draw(&mut frame_buffer_1) .unwrap(); + display.show(frame_buffer_1.get_buffer()).unwrap(); delay.delay_ms(1000); // Draw red and green lines Line::new(lcd_zero, lcd_max_corner) .into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 1)) - .draw(&mut display) + .draw(&mut frame_buffer_1) .unwrap(); Line::new( Point::new(0, (LCD_HEIGHT - 1) as i32), Point::new((LCD_WIDTH - 1) as i32, 0), ) .into_styled(PrimitiveStyle::with_stroke(Rgb565::GREEN, 1)) - .draw(&mut display) + .draw(&mut frame_buffer_1) .unwrap(); + display.show(frame_buffer_1.get_buffer()).unwrap(); + delay.delay_ms(1000); // Load image data let image_data = include_bytes!("rust-logo-240x240.raw"); @@ -193,6 +197,9 @@ fn main() -> ! { display.show(frame_buffer_2.get_buffer()).unwrap(); delay.delay_ms(1000); + frame_buffer_1.clear(Rgb565::BLACK); + display.show(frame_buffer_1.get_buffer()).unwrap(); + // Reset the frame buffers image.draw(&mut frame_buffer_1).unwrap(); image.draw(&mut frame_buffer_2).unwrap(); @@ -238,7 +245,7 @@ fn main() -> ! { // The bounding box has a pixel padding of 5 pixels around the arrow to prevent the need to draw the background buffer before the next arrow is drawn. // This improves performance as only one draw operation occurs instead of 2. - display.show_region(frame_buffer_2.get_buffer(), bounding_box).unwrap(); + display.show_region(frame_buffer_2.get_buffer(), bounding_box.top_left.x as u16, bounding_box.top_left.y as u16, bounding_box.size.width as u16, bounding_box.size.height as u16).unwrap(); previous_bounding_box = bounding_box; // Delay to achieve 1 Hz From e40f848638e15c1ab2a0a4f04320a5da1cc5706a Mon Sep 17 00:00:00 2001 From: GordonCox Date: Mon, 29 Jul 2024 18:45:22 -0700 Subject: [PATCH 18/23] Updated to use crate for gca01a_driver --- boards/waveshare-rp2040-lcd-1-28/Cargo.toml | 2 +- .../examples/waveshare_rp2040_lcd_demo.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml index 94031a49..89008670 100644 --- a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml +++ b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml @@ -16,7 +16,7 @@ rp2040-boot2 = { workspace = true, optional = true } rp2040-hal.workspace = true embedded-hal = { version = "0.2" } libm = "0.2" -gc9a01a_driver = { version = "0.1.1" } +gc9a01a_driver = { version = "0.1.2" } [dev-dependencies] cortex-m.workspace = true diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index bf915d12..c66adbcb 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -8,7 +8,7 @@ mod frame_buffer; -use gc9a01a_driver::{GC9A01A,Orientation}; +use gc9a01a_driver::GC9A01A; use cortex_m::delay::Delay; use embedded_graphics::primitives::Line; use fugit::RateExtU32; @@ -28,7 +28,6 @@ use waveshare_rp2040_lcd_1_28::{ Pins, XOSC_CRYSTAL_FREQ, }; -use embedded_hal::PwmPin; use embedded_hal::digital::v2::OutputPin; use embedded_graphics::{ From 41d224bfc1d34b3ec98f3fe5709e63cb0f8f0fa9 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Tue, 30 Jul 2024 08:06:56 -0700 Subject: [PATCH 19/23] Simplified the example made it more like 0.96 Simplified the example made it more like 0.96 --- boards/waveshare-rp2040-lcd-1-28/Cargo.toml | 2 +- .../examples/frame_buffer.rs | 65 ---- .../examples/rust-logo-240x240.raw | 20 -- .../examples/waveshare_rp2040_lcd_demo.rs | 300 ++---------------- 4 files changed, 32 insertions(+), 355 deletions(-) delete mode 100644 boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs delete mode 100644 boards/waveshare-rp2040-lcd-1-28/examples/rust-logo-240x240.raw diff --git a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml index 89008670..a5891b98 100644 --- a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml +++ b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml @@ -16,7 +16,7 @@ rp2040-boot2 = { workspace = true, optional = true } rp2040-hal.workspace = true embedded-hal = { version = "0.2" } libm = "0.2" -gc9a01a_driver = { version = "0.1.2" } +gc9a01a_driver = { version = "0.1.3" } [dev-dependencies] cortex-m.workspace = true diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs b/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs deleted file mode 100644 index e7f70b4e..00000000 --- a/boards/waveshare-rp2040-lcd-1-28/examples/frame_buffer.rs +++ /dev/null @@ -1,65 +0,0 @@ -#![no_std] -#![no_main] - -use embedded_graphics::{pixelcolor::Rgb565, prelude::*,}; -use core::convert::Infallible; - -pub struct FrameBuffer { - buffer: &'static mut [u8], - width: u32, - height: u32, -} - -impl FrameBuffer { - pub fn new(buffer: &'static mut [u8], width: u32, height: u32) -> Self { - Self { - buffer, - width, - height, - } - } - - pub fn get_buffer(&self) -> &[u8] { - &self.buffer - } - - pub fn get_mut_buffer(&mut self) -> &mut [u8] { - &mut self.buffer - } - - pub fn clear(&mut self, color: Rgb565) { - let raw_color = color.into_storage(); - for chunk in self.buffer.chunks_exact_mut(2) { - chunk[0] = (raw_color >> 8) as u8; - chunk[1] = raw_color as u8; - } - } -} - -impl DrawTarget for FrameBuffer { - type Color = Rgb565; - type Error = Infallible; - - fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> - where - I: IntoIterator>, - { - for Pixel(coord, color) in pixels { - if coord.x >= 0 && coord.x < self.width as i32 && coord.y >= 0 && coord.y < self.height as i32 { - let index = ((coord.y as u32 * self.width + coord.x as u32) * 2) as usize; - let raw_color = color.into_storage(); - self.buffer[index] = (raw_color >> 8) as u8; - self.buffer[index + 1] = raw_color as u8; - } - } - Ok(()) - } -} - -impl OriginDimensions for FrameBuffer { - fn size(&self) -> Size { - Size::new(self.width, self.height) - } -} - - diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/rust-logo-240x240.raw b/boards/waveshare-rp2040-lcd-1-28/examples/rust-logo-240x240.raw deleted file mode 100644 index 2ddaa4a5..00000000 --- a/boards/waveshare-rp2040-lcd-1-28/examples/rust-logo-240x240.raw +++ /dev/null @@ -1,20 +0,0 @@ -џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџОюњцкїžџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюњЋы››ЃЊоxџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї|їџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїЋЊ›››››Hяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїžї\џпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюйД ЃiЃ‰ФЯџžџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХ1›››šцšЦšцМŽџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпЭRЃŠЃiЋыоxџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї\ЃHšч›šчšЦД,џžџџџџџџџџџџџџџџџџџџџџџџџџџџцЙšцšч›šцšЦšЦšЦ›о6џџџџџџџџџџџџџџџџџџџџџџџџџџџџМЯ›››››цЙџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭR›šчšц›šцšцМnџпџџџџџџџџџџџџџџџџџџџџџџџпД šЦšц›šцšЦšЦšЦ›ЃЊї}џџџџџџџџџџџџџџџџџџџџџџџџХ››››››МŽџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџОЋЊšцšЦšЦšцšЦšч›МЏџпџџџџџџџџџџџџџџџџџџџџеД’цšІ’Ц’цšІšЅšЅ’Ц’Ц’ЦФаџџџџџџџџџџџџџџџџџџџџџџЭR››››››››Hї\џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцxšцšЦšЦšЦšЦšц››šчФёџџџџџџџџџџџџџџџџџџя;›’І’І’І’І’…’…’…’І’…’e’…ц™џџџџџџџџџџџџџџџџџџЭs’ц’ц’ч’ч’ч››››еДџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМŽšЦšЦšЦšЦšц’ц’Ц’І’Ѕ’ЅФёџџџџџџџџџџџџџџџџДNŠDŠDŠ…Š…Š…ŠeŠeŠ…Š…ŠeŠDŠDЃЋџпџџџџџџџџџџџџџџЭДŠІŠІŠІ’І’Ц’Ц’Ц’Ц’ц’чЌ џпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}е”Ф№еДїџџџџџџџџџџџџџџџџџџџџџџџџџџї\ЃšЅ’Ѕ’Ѕ’І’І’І’Ѕ’e’dŠdŠdХ2џџџџџџџџџџџџо7Š$Š$Š$‚eŠEŠ$Š$Š$Š$ŠDŠ$Š$Š$ŠDЭSџџџџџџџџџџџџеЕŠeŠeŠ…Š…Š…Š…Š†ŠІŠІŠІŠІ’ЦцкџџџџџџџџџџџџџџџџџџџџџџџџџџџожХЭsяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяЃŠšЦšЦšЦЋЪцЙџџџџџџџџџџџџџџџџџџџџџџџџеД’…’e’…’eŠeŠ…Š…Š…ŠDŠDŠ$Š$Š$ЭSџџџџџџџџїž“)‚$‚$‚$‚E‚$‚‚‚‚‚$‚$‚‚$‚$Š†яџџџџџџџџо7ŠE‚$‚E‚e‚e‚e‚e‚eŠeŠ…Š…Š…Š…МёџџџџџџџџџџџџџџџџџџџџџџџџяЌ ››››Hц˜џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМŽ›šцšЦšц››'ЭsџпџџџџџџџџџџџџџџџџџџџџЋЋŠeŠeŠeŠEŠ$ŠE‚e‚D‚E‚$‚$‚$‚‚$ЭЕџџџџџџМё‚E‚E‚$‚‚‚%‚‚‚‚$zE‚$‚‚‚‚Ќ џџџџџџо7‚E‚‚$‚E‚E‚E‚E‚E‚e‚e‚e‚e‚e‚e“(їžџџџџџџџџџџџџџџџџџџџџеѕ›H“››››ЋЫџОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїЃi››šцšчšцšч›Д яџџџџџџџџџџџџџџџџцкŠEŠE‚e‚E‚$‚$‚$‚$‚‚‚%‚$‚‚‚‚%ЌNЌ.Ћ‹ŠEŠDŠ$‚‚‚z%z%‚z$z%z%‚‚‚‚‚‚›jЌ ДoŠЇz%z%z%zEzEzEzEzE‚E‚E‚E‚E‚E‚E‚eжџџџџџџџџџџџџџџџџї}Д’Ц’Ц’ч’ч“““›(юњџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюњ›'šцšц›šчšЦšІ’Ц’ІšчеѕџџџџџџџџџџџџџџМб‚$‚$‚E‚$‚‚‚‚‚‚z%z%‚‚‚‚‚ŠŠŠŠ$ŠŠ‚zzz%‚Š‚$‚$‚zzzzz%z%zzzzzz%z%z%zEzEzEzEzEzEzEzEzE‚EЌ џџџџџџџџџџџџџџоx›IŠ†ŠІŠІŠІ’Ц’Ц’ч’ч“оwџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоWšчšЦšЦšЦ’Ц’…’…’ІŠ…ŠeŠDДoїџџџџџџџџџО’ш‚‚‚$‚%‚‚‚zz%z%z%z%z%z%zz‚$‚$‚$‚$‚D‚‚z%z%z%zE‚Š‚D‚$‚zzzzzz%zzzzz%z%z%z%z%z%z%z%z%z%z%zEzEzE‚†я<џџџџџџџџџоН‚e‚e‚e‚eŠ…Š…ŠІŠІŠІ’Ц’цЭ“џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭ“šч’Ц’Ѕ’Ѕ’…’dŠeŠ…ŠeŠDŠ$Š$›)о™џџџџџџеі‚%‚‚z%z%z%z%z%z%z%z%zz%z%z%z‚‚‚$‚E‚E‚E‚E‚%z%z%z%z%‚%‚$‚E‚$‚zz%zzzz%zzzz%zEzEzEzEzEzEzEzEz%z%z%z%z%z%z%Нџџџџџџюћ›ЋzEzE‚E‚e‚e‚e‚e‚…Š…Š…ŠІŠІДџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМŽ’Ѕ’ІŠІŠ…ŠDŠDŠD‚e‚D‚‚‚‚%‚eНХtЃЬ‚fz%z%z%z%zzz%z%zEz%zzz%z%zz‚Š$Š$ŠeŠ…’e’e’…’e’e’e’ešЅ’І’І’ІŠІŠІŠІŠІŠ†‚f‚f‚E‚%zEzErErErErEzEzEzEzEzEzEzEzEzEz%z%zf›‹Н2Хt‚ЇzEzEzEzEzEzE‚E‚E‚e‚e‚e‚…Š…ЃЋџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЃŠ’dŠeŠ…ŠDŠ$Š$‚$‚$‚%‚$‚‚z%z%z%z%z$zzzz%z%zzzzz%z%z%‚E‚†Šf’e’e’…š…š…šІš…’e’D’DŠ$Š$Š$Š$‚$zEzE‚E‚ezf‚†‚†ŠІŠІŠІ’Ч’І’Ч’ЧŠЇŠІ‚†‚fzfzErErErErEzEzEzEzEzEzEzEz%z%z%z%z%zEzEzEzEzEzEzEzE‚E‚e‚e“ї}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}›ŠeŠe‚E‚$‚‚‚‚‚z%z%zzz%z%zzzzzz%z%z%z$‚%ŠE’e’І’Ч’ЦŠІŠf‚%zqфyфqФqфqфqФqУqФqфqфqфqфqфqфqФiФiФiфiфiфaфaфiфjjr%r%zE‚†ŠІŠІ’Ч’ЧŠІ‚†‚fzErErErErErErErEzEzEzEzEz%z%z%z%zEzEzEzEzEzEzEzE‚†цћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпя<їџџџџџџџџџџџџџџџџџџџџџџџџџџюњŠ†‚E‚$‚‚‚‚‚$‚z$z%z$zzz%zzzzzz%‚fŠ†ŠІ’І’e’eŠ%z%rqфiФiфqфyфzz$z‚z%‚$‚‚z%zEzE‚$‚z%zzzz%rErErEr%r%r%r%rjjiфaфiфjr%zfŠ†ŠІ’ЧŠЇŠ†‚fzErErErErErErEzEzEzEzEz%z%z%z%z%zEzEzEzEzEоWџџџџџџџџџџџџџџџџџџџџџџџџџџїžя;џОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцxЋыЃHЃiМаюкџџџџџџџџџџџџџџџџџџџџџџо7‚$‚%‚‚‚‚z%zzzzz%z%z%zEzz‚$ŠEŠІ’Ч’ЦŠ†zer%qфqфqфrz$z‚‚EzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEz%z%z%rErErErErErErErErEzEzEzEz%r%r%jiфaфiфjzE‚†ŠІ’ЧŠІ‚†zfrErErErErErErErEr%z%z%z%z%z%zEzEzEХSџџџџџџџџџџџџџџџџџџџџџџяХЃŠ›HЃЊеѕџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцк›(›››››'ДnоxџпџџџџџџџџџџџџџџџџХ2‚‚$‚‚‚z%z%zzzzz%zEz%zEŠešeЂ…šeŠez%jiфrz$z%zEzEzEzE‚$‚%zEzEzEzEzEzEzEzEzEzEzEzEzE‹Ќ.ДЌ›‹zfzEzErErErErErErErEzEzEzEzEzEzEzErErEr%r%rjaфiфr%zfŠІ’ЧŠЇ‚†zFrEr%rErErErErErErEr%r%z%z%z%ЌOџџџџџџџџџџџџџџџџџџц™Дo’чŠЦ’ч“““ж6џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФ№››››šч’ц’Ц’ІЃЋееїžџџџџџџџџџџџџЌ.‚z%zzzzz%z%zzz%zE‚e’eš…’ІŠDzqфqфrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzE‚ШЭЕїžџџџџџџџџцКЃэzEzEzErErErErEzEzEzEzEzEzEzEzErEzEzEzEzEzEzEr%riфaфjzFŠІŠЧŠЇ‚†zFr%r%rErErErErErEr%r%r%“‹џџџџџџџџџџџџџпж7ЃЬ‚…‚…‚…ŠІŠІŠЦ’ч“ЌMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМЏ››šчšЦ’Ѕ’ІŠІŠ…ŠeŠDšшХ2ї\џџџџџџї\“ z%z%z%zzzz%zEzE‚e’ІšЦ’ІŠ$ziфrz$zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzE“Jя<џџџџџџџџџџџџџџџџМђzEzErErErErEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEr%r%jaфjzfŠІ’ЧŠІzfrEr%rErErErErErEr%‚ЇцКџџџџџџїžЭЕ“JzEzEze‚e‚e‚…‚†ŠІŠЦŠЦЃЋџОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМЏ›šц’Ѕ’…’eŠ…Š…ŠeŠ$‚$‚‚ŠЇДбцкХŠ†zzzzEz%zz‚EŠ…šЦšЦ’…z%iфqфr$zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzE‚ЇцћџџџџџџџџџџџџџџџџџџџџЄ.zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErEr%r%iхaфj%‚†ŠЧŠЧ‚†zFr%rErErErErErEz‡ДБцњН3Šшz%z%z%zEzEzEze‚e‚e‚…‚†ŠІЃьџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМЏ’Ц’Ѕ’…ŠdŠDŠe‚e‚E‚‚‚‚z$z%z$zzzzzzz$‚E’…šЦšЦŠeriфrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEН2џџџџџџџџџџџџџџџџџџџџџџя‚ЇzEzEzEzErErErErErErErErErEzErErEzEzEzEzEzEzEzErErErErErEr%jaфiхzFŠЇŠЧ‚†zFr%rErErErErErEzFr%r%r%r%z%z%zEzEzEzEze‚e‚e‚…ЃьџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМа’…’eŠeŠDŠD‚e‚E‚E‚$‚‚‚zzzzzz‚‚‚$’…šЦ’І‚Eiфiфz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEz†яџџџџџџџџџџџџџџџџџџџџџџџџЄ.rErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфr%‚І’Ч‚†zFr%rErErErErErErEr%r%r%r%z%z%z%zEzEzE‚e‚eЃЫџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФаŠdŠDŠe‚E‚E‚$‚‚‚$‚zzzzzz%‚%‚Š$’ešЦ’Іz%iфrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErE‹*џпџџџџџџџџџџџџџџџџџџџџџџџџХSrErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфr%‚ІŠЧ‚†zFr%rErErErErErErEr%r%r%r%z%z%zEzEzEzEЄ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМаŠDŠ$‚E‚E‚E‚‚‚zzzzzz%z%zE‚E’Eš…šez$iфrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErE“jџпџџџџџџџџџџџџџџџџџџџџџџџџХ•rErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфr%‚І’Ч‚†zFr%rErErErErErErEr%r%r%r%z%z%zEzEЌ.џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФё‚$‚$‚‚‚$zzzzzzzz%z%z%ŠEš…še‚iУrz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErE‹)џпџџџџџџџџџџџџџџџџџџџџџџџџНSrErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфr%ŠІ’Ч‚†rEr%rErErErErErErEr%r%r%r%z%z%Є.џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХS‚‚‚‚zz%z%z%zzzzz‚%šeš…‚$qФqФzzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErErErErEz†яџџџџџџџџџџџџџџџџџџџџџџџџœrErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErErEr%jaфrEŠІ’ЦzfrErErErErErErErEr%r%r%r%r%ЌOџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХS‚‚zz$zzzzzzzz’Eš…ŠEqФqФzz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErErErErErErErErErEДђџџџџџџџџџџџџџџџџџџџџџџчz‡rFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErErErErErErErErErErErErErErErErErErErEr%jaфzf’ЧŠІzErErErErErErErErEr%r%r%ДБџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХS‚zz%zzzzzzz‚%š…’eyфiФyфz%zEzEzEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErErErErErErErErErErErErEz‡цКџџџџџџџџџџџџџџџџџџџџ›эrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErErErErErErErErErErErErEr%aхj‚†’Ч‚†rErErErErErErEzEzEzEДАџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїžџџџџџџџџџџџџџџџџџџџџџџџџџџеіz%z%zzzzzzz’eš…‚$iФqфz%z%zEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErEz‡“‹ЌЭецњН3rErErErFrFrFrFrFrFƒ цкџџџџџџџџџџџџџџџпЌrFrFrFrFrFrFrFrFrFrFН3цћЭіЌБ“ŒzЇrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErErErErErErErErEjaфrEŠЧŠЇzFrErErErErErErErEДбџџџџџџџџџџџџџџџџџџџџџџџџџџїОџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоЋыЃ‰ЋыФ№жяџпџџџџџџџџџџџџџџџџеіz%z%zzzzz‚%š…’eqфiФz%zEzEzEzEzEzEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErErf‹JЌжя<џпџџџџџџџџН3rFrFrFrFrFrFrFrFrFzЇХtї}џџџџџџџпоy“ЌrFrFrFrFrFrFrFrFrFrFХ•џџџџџџџџџџя\жXДб“‹rfrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErErErEr%baх‚†ŠЧ‚†rErErErErErErEН3џџџџџџџџџџџџџџџџџџя<ж7НЃЬ“I›ЋЭДџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоšЦšЦšЦ’Ѕ’…Š…ŠІ“)ЌoЭ”цЙїžџџџџџџюћ“)z$z%zzzzŠ†šІ‚EiФqфz%zEz%z%zEzEzEzEzEzEzEzEzErErErErErErErErErErErErErErEzf“ЌНSцњџоџџџџџџџџџџџџџџџџџџЌБrFrFrFrFrFrFrFrFrFrFzШ“ЭЄOœ.ƒ*rFrFrFrFrFjFrFrFrFrFrFХЕџџџџџџџџџџџџџџџџџџџпяХ•›эzЇrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrErErEjaфrEŠЧŠІrFrErErErErEƒ оyџџџџџџџОцњЭеД“J‚І‚e‚e‚…Š†ŠІŠЦХSџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋЫšЦšЦ’Ѕ’…’eŠe‚E‚Ez%‚$‚%ŠЧЃЬХSЭ•ŠЇzzzEz%z‚$’…’†qфiфr%zEzEz%zzz%zEzEzEzEzErErErErErErErErErErErErErErErF“‹НTчџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌАrFrFrFrFrFrFrFrFrFjFrFrFjFjFjFjFjFjFjFjFjFjFjFrgЭіџџџџџџџџџџџџџџџџџџџџџџџџџџџџя\Х•“ЭrgjFjFjFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFr%aхj‚†ŠЧzfrErErErErEz‡ХtХ”Є.‹ zfz%zEzEzEzE‚E‚e‚eŠ…ŠІ“(ї}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ЃHšЦ’Ѕ’…Š…ŠD‚D‚$z$‚‚‚zzzzzzzz%z‚%š…ŠEiФqФzz%zEzEzzzz%zEzEzErErErErErErErErErErErErErErE‚шДбоКџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌrFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFrgж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцћЕƒ jFjFjFjFjFjFjFjFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFrFr%jaхzfŠЧ‚†rErErErErErErErErErEr%z%zEzEzEzE‚E‚e‚e‚…ŠЦюњџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпЃЫ’Ц’…Še‚e‚D‚$‚$‚‚zzzzzzz%zzzŠEš…‚iФrz%zzzz%z%zz%zErErErErErErErErErErErErErErErE“‹ХЕїџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпœ.jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFr‡ж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїОЮ“ЬrFjFjFjFjFjFjFjFjFjFjFrFrFrFrFrFrFrFrFrFrFrFrFrFj%aхr%ŠЧ‚ІrFrErErErErErErErErEr%r%z%zEzEzEzE‚e‚e’чї}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМаŠІŠ†‚…‚E‚$‚$z$zzzzzzz‚$z%‚‚’ešeyфiФzz%zEzEzzz%zEz%rErErErErErErErErErErErErErErF›ЭжXџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпœ.jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFzШо™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™Є/rgjFjFjFjFjFjFjFjFjFjFjFjFjFrFrFrFrFrFrFrFrFrFr%aхb‚ЇŠЇrFr%rErErErErErErErEr%r%z%zEzEzEzE‚eЃЫџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭеŠ†Š…‚E‚$‚‚zzzzrrzz‚z$‚$še’eqФqФzzzz%zEzzzzzrErErErErErErErErErErErErFЄоЙџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџп“ЭjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFzШоКџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцћЄorgjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFrFrFrFrFrFrFr%jaхz†ŠЧzfr%rFrErErErErErErErEr%r%zEzEzEzEМёџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцњŠІ‚e‚$‚zzzzzzrrz‚z‚%šeŠEiФqфzzzzzzzzzz%rErErErErErErErErErErErE›Эоxџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїž“ЌjFjFjFjFjFjFjFjFjFjFjFjFjFƒ цкџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцћœ.jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFrFrFrFrFrFj%aхzfŠЧzfr&rFrFrFrErErErErErEr%r%z%zEzEж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџО“‚Ez%zzzzzzzrz‚zŠ%š…‚%iФrzzzz%z%zzzzz%rErErErErErErErErErErF‹Kж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїž“ЌjFjFjFjFjFjFjFjFjFjFjFƒ чџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™“ЌjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFrFrFj%aхrFŠЧz†r&rFrFrFrFrFrErErEr%r%r%r%‚†я<џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌ ‚%z%zzrzzzzzzzŠEš…‚iФzz%zzzz%z%zzzr%rErErErErErErErErErFzЈНtџоџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ƒJjFjFjFjFjFjFjFjFjFƒJяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭіƒ jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj%aхj%ŠЧz†rFrFrFrFrFrFrFrEr%r%r%r%“jџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХSz$zrrrzr%rrr%zŠEš…ziФzzz%z%zz%rErEr%r%rErErErErErErErErFrFrFЄOя<џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя\‹kjFjFjFjFjFjFjF‹Œя}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ЌбrfjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхj%ŠЇ‚†rFrFrFrFrFrFrFrFr%r%r%Ќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™z$zrrr%r%z%rrr‚Eš…ziФzzzzzzrErErEr%r%rErErErErErErFrFrFƒ ж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїžЕ{ jgjFjg{ Е3џОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™‹kjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхb‚Ї‚‡jFrFrFrFrFrFrFrFr&r%ЭжџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїŠШz%z%z%z%z%zzr‚Eš…ziФzzzzzzzrEr%zzrrrErErErFrFrFrFЌї}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїОоКжyокїОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпДђrfjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&bb‚Ї‚‡jFrFrFrFrFrFrFrFrfцћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ›Ьr%r%z‚z%zzŠEš…riфzzzzzzzzr%r%rrrrr%rFrFrFrFzШЭіџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџо™ƒ*jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&bb‚Ї‚‡jFjFrFrFrFrFrF‹*џОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџНrrrzzzŠEš…riФzz%z%z%rErEr%r%zrr%r%rrr%rFrFrFrF“Ья<џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїœOjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&bb‚Їz‡j&jFjFrFrFrFЄOџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХSrrrzEzŠ%š…ziФr%zzzr%rErErErEr%r%r%rEr%r%rFrFrFrFДёџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџНtjgjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&bb‚Чz†j&rFrFrFrFЌБџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцК‚†rrz%z%‚%š…ziФzz%z%zzzrErErErErErFrFrFrFrFrFrFz‡ЭіџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоyzщjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхj%‚Чzfr%rFrFrFrfж7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџц˜МЏД-Ќ ДnДДДАНМђНЭ”ЭЕЭЕ‹ zzzzz%š…‚%aФrzrEr%zzr%rErErErErFrFrFrFrFrFrFƒ цкџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя<‹ŒjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхjFŠЧzfr&rFrFrFzШХ”ХЕХЕН3ННДБЌoЌЌoЃэЃэЄ.жџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџж’Ц’ЅŠeŠd‚D‚$‚$zzzzzzr%zzzzz’f‚EaФrzz%r%rEr%r%r%rErErFrFrFrFrFrFrFrF“Œя\џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїОœ.jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&YхrFŠЧrfr&rFrFrFrFrFrFrFr%rrr%r%r%z%z%zEzfНџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїЃi’ЅŠ…ŠeŠD‚$zzzzrzzrrzzzz’eŠEaФr%rEzzzr%rEr%rr%rFrFrFrFrFrFrFrFœїОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌБjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&YхzfŠЧrFrFrFrFrFrFrFrFr%rrrr%r%zzzzE‚Їюћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцк’ЦŠ…ŠeŠD‚E‚$zzz%r%rzrrrzzzŠE’eiФqфz%r%zrrrrrrrFrFrFrFrFrFrFrFЌџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЕjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj%aх‚‡‚ЇrFrFrFrFrFrFr%rrrrrr%rzzz%zeжџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя<’чŠeŠD‚D‚$‚zz%z%zzz%zrzzz%‚E’…qфiФzzr%rrrrrrr%rFrFrFrFrFrFjFЌџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџН”jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFbbŠЇ‚†rFrFrFrFr%rrrrrrrrrzz%z%оxџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌ-ŠD‚E‚$‚zzrr%rzz%z%zzz‚%š†zaфrzr%r%rrrrrrr%rFrFrFrFjFjFДђџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХ•jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFbj%ŠЧzfrrrrrrrrrrrr%rrz“)џОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоx‚D‚E‚$‚zrrr%rzzr%r%rz%’e‚%aФrrrr%r%rrrrrrrFrFrFjFjFjFЌАџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџН•jFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&aхrFŠЇrrrrrrrrrrrrrrrХ”џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпЃ‹‚Ezzzrr%r%rzzr%r%j%ŠfŠeaФqфrrrrr%r%rrr%rFrFrFjFjFjFjFЌАџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџНSjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj%aх‚†Š†r%r%r%rrrrrrrrrr‚Јїџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџеіz%zzzr%r%r%r%rrrrzE’fiфiфrrrrrr%rFrFrFrFrFrFjFjFjFjF“Ќя\ї}я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]я]ї}їОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЕjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj%bŠЇz†jFrFr%rrrrrrrrr%НџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџО“)rrr%r%r%r%r%zzzz%’fzaФrrrrrrrFrFrFrFrFjFjFjFjFjFjFrfrgrgrgrgrgrgrgrgrgrgrgrgrgrgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgjgrЈ{*“ЭЄХЕцкїОџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЄpj&jFjFjFjFjFjFjFjFjFjFjFj&j&j&rFbjŠ†zFjFrErrrr%rrrr%zЇя<џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХtzrr%r%r%rzzzzŠeŠEaФrrrr%r%rr%rFrFrFrFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&j&jFjFjFjFb&b&b&b&b&bFbFbFb&b&b&b&b&b&b&b&b&b&bFb&bFb&b&b&b&b&b&bFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbF{*œOЭїя]џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџп“юj&j&jFjFjFjFjFjFjFjFjFjjjj%r%aФz%Š†r%rrrrr%rrr%rFЌџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ŠЇzr%r%rzrrr‚E’fiФiхr%rrr%rFrFrFrFrFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&j&jFjFjFjFb&b&b&b&b&bFbFbFb&b&b&b&b&b&b&b&b&bFb&b&b&b&b&b&bFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFrЈœOжyџпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї}ƒKjFj&j&j&jFjFjFjFjFjFjjjjrEjaФŠf‚Errrrrrrr%rgцћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМђrr%r%rrrrz%’fzaФrr%rrrFrFrFrFrFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFjFj&j&jFjFjFb&b&b&b&b&bFbFbFb&b&b&b&b&b&b&b&bFb&b&b&b&b&b&bFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFbFƒkХжџоџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцћrЈjFjFjFj&j&jFjFjFjFj&jjjj&rFiхiх’†z%rrrrrrr%Є.џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя ! { // Initialize the display let mut display = GC9A01A::new(spi, lcd_dc, lcd_cs, lcd_rst, false, LCD_WIDTH, LCD_HEIGHT); display.init(&mut delay).unwrap(); + display.set_orientation(&Orientation::Landscape).unwrap(); + + // Clear the screen before turning on the backlight + display.clear(Rgb565::BLACK).unwrap(); + _lcd_bl.set_high().unwrap(); + delay.delay_ms(1000); - // Create two frame buffers for double buffering - let mut frame_buffer_1 = unsafe { FrameBuffer::new(&mut FRAME_BUFFER_1, LCD_WIDTH, LCD_HEIGHT) }; - let mut frame_buffer_2 = unsafe { FrameBuffer::new(&mut FRAME_BUFFER_2, LCD_WIDTH, LCD_HEIGHT) }; - - // Define LCD dimensions let lcd_zero = Point::zero(); let lcd_max_corner = Point::new((LCD_WIDTH - 1) as i32, (LCD_HEIGHT - 1) as i32); - // Define a style for the rectangle let style = PrimitiveStyleBuilder::new() .fill_color(Rgb565::BLUE) .build(); - // Clear the screen before turning on the backlight - frame_buffer_1.clear(Rgb565::BLACK); - display.show(frame_buffer_1.get_buffer()).unwrap(); - delay.delay_ms(1000); - _lcd_bl.set_high().unwrap(); - delay.delay_ms(1000); - - // Draw a blue rectangle Rectangle::with_corners(lcd_zero, lcd_max_corner) .into_styled(style) - .draw(&mut frame_buffer_1) + .draw(&mut display) .unwrap(); - display.show(frame_buffer_1.get_buffer()).unwrap(); delay.delay_ms(1000); - // Draw a black rectangle inside the blue rectangle let style = PrimitiveStyleBuilder::new() .fill_color(Rgb565::BLACK) .build(); + Rectangle::with_corners( Point::new(1, 1), Point::new((LCD_WIDTH - 2) as i32, (LCD_HEIGHT - 2) as i32), ) .into_styled(style) - .draw(&mut frame_buffer_1) + .draw(&mut display) .unwrap(); - display.show(frame_buffer_1.get_buffer()).unwrap(); - delay.delay_ms(1000); - // Draw red and green lines Line::new(lcd_zero, lcd_max_corner) .into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 1)) - .draw(&mut frame_buffer_1) + .draw(&mut display) .unwrap(); + Line::new( Point::new(0, (LCD_HEIGHT - 1) as i32), Point::new((LCD_WIDTH - 1) as i32, 0), ) .into_styled(PrimitiveStyle::with_stroke(Rgb565::GREEN, 1)) - .draw(&mut frame_buffer_1) - .unwrap(); - display.show(frame_buffer_1.get_buffer()).unwrap(); - delay.delay_ms(1000); - - // Load image data - let image_data = include_bytes!("rust-logo-240x240.raw"); - - let raw_image: ImageRaw = ImageRaw::new(image_data, LCD_WIDTH); - let image = Image::new(&raw_image, Point::zero()); - - // Draw the image on both frame buffers - image.draw(&mut frame_buffer_1).unwrap(); - image.draw(&mut frame_buffer_2).unwrap(); - display.show(frame_buffer_1.get_buffer()).unwrap(); - delay.delay_ms(1000); - - // Draw a filled red triangle on the first frame buffer - let points = [ - Point::new(120, 100), - Point::new(100, 140), - Point::new(140, 140), - ]; - let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::RED).build(); - Triangle::new(points[0], points[1], points[2]) - .into_styled(style) - .draw(&mut frame_buffer_1) - .unwrap(); - display.show(frame_buffer_1.get_buffer()).unwrap(); - delay.delay_ms(1000); - - // Draw a filled green triangle on the second frame buffer - let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::GREEN).build(); - Triangle::new(points[0], points[1], points[2]) - .into_styled(style) - .draw(&mut frame_buffer_2) - .unwrap(); - display.show(frame_buffer_2.get_buffer()).unwrap(); - delay.delay_ms(1000); - - frame_buffer_1.clear(Rgb565::BLACK); - display.show(frame_buffer_1.get_buffer()).unwrap(); - - // Reset the frame buffers - image.draw(&mut frame_buffer_1).unwrap(); - image.draw(&mut frame_buffer_2).unwrap(); - - // Calculate the center of the image - let image_center = Point::new(240 / 2, 240 / 2); - let mut bounding_box: Rectangle; - let mut previous_bounding_box = Rectangle::new(Point::new(0, 0), Size::new(0, 0)); - // Define a rectangle at (0, 0) with width 0 and height 0 - let mut angle: f32 = 90.0; - - // Create a background buffer with frame_buffer_1 - frame_buffer_1.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - // Create a drawing to lcd buffer with frame_buffer_2 - frame_buffer_2.get_mut_buffer()[..image_data.len()].copy_from_slice(image_data); - // Show the display to present the initial image. - display.show(frame_buffer_2.get_buffer()).unwrap(); + .draw(&mut display) + .unwrap(); + // Infinite colour wheel loop + let mut l: i32 = 0; + let mut c = Rgb565::RED; loop { - let start_time = cortex_m::peripheral::SYST::get_current(); - - // Copy the previous bounding box from the background buffer (frame_buffer_1) into the lcd buffer (frame_buffer_2) - // This prevents the whole reload of the image_data. - let previous_bounding_box_buffer = &frame_buffer_1.get_buffer()[(previous_bounding_box.top_left.y as usize * LCD_WIDTH as usize * 2) + (previous_bounding_box.top_left.x as usize * 2)..]; - let destination_buffer = &mut frame_buffer_2.get_mut_buffer()[(previous_bounding_box.top_left.y as usize * LCD_WIDTH as usize * 2) + (previous_bounding_box.top_left.x as usize * 2)..]; - - for row in 0..previous_bounding_box.size.height as usize { - let source_row_start = row * LCD_WIDTH as usize * 2; - let source_row_end = source_row_start + previous_bounding_box.size.width as usize * 2; - destination_buffer[source_row_start..source_row_end].copy_from_slice(&previous_bounding_box_buffer[source_row_start..source_row_end]); + Line::new(Point::new(0, l), Point::new((LCD_WIDTH - 1) as i32, l)) + .into_styled(PrimitiveStyle::with_stroke(c, 1)) + .draw(&mut display) + .unwrap(); + delay.delay_ms(10); + l += 1; + if l == LCD_HEIGHT as i32 { + l = 0; + c = match c { + Rgb565::RED => Rgb565::GREEN, + Rgb565::GREEN => Rgb565::BLUE, + _ => Rgb565::RED, + } } - - // Draw the arrow and return the new bounding box - bounding_box = create_arrow(&mut frame_buffer_2, angle as i32, image_center.x, image_center.y); - // Draw the center button - create_button(&mut frame_buffer_2, image_center.x, image_center.y); - - // Increment the angle - angle += 6.0; - if angle >= 360.0 { - angle = 0.0; - } - - // The bounding box has a pixel padding of 5 pixels around the arrow to prevent the need to draw the background buffer before the next arrow is drawn. - // This improves performance as only one draw operation occurs instead of 2. - display.show_region(frame_buffer_2.get_buffer(), bounding_box.top_left.x as u16, bounding_box.top_left.y as u16, bounding_box.size.width as u16, bounding_box.size.height as u16).unwrap(); - previous_bounding_box = bounding_box; - - // Delay to achieve 1 Hz - delay.delay_ms(1000); } } - -/// Create an arrow image at a specified angle and position -fn create_arrow( - framebuffer: &mut FrameBuffer, - angle: i32, - compass_center_x: i32, - compass_center_y: i32, -) -> Rectangle { - let compass_center = Point::new(compass_center_x, compass_center_y); - let north_angle = angle - 180; - let south_angle = angle; - let north_left_angle = north_angle - 2; - let north_right_angle = north_angle + 2; - let south_left_angle = south_angle + 10; - let south_right_angle = south_angle - 10; - - let circle_1 = 88; - let circle_2 = 85; - let circle_3 = 36; - let circle_4 = 32; - - let north = get_coordinates(compass_center, circle_1, north_angle); - let south = get_coordinates(compass_center, circle_4, south_angle); - let north_left = get_coordinates(compass_center, circle_2, north_left_angle); - let north_right = get_coordinates(compass_center, circle_2, north_right_angle); - let south_left = get_coordinates(compass_center, circle_3, south_left_angle); - let south_right = get_coordinates(compass_center, circle_3, south_right_angle); - - let merged_points = [ - north, - north_left, - south_left, - south, - south_right, - north_right, - ]; - - let left_points = [ - north, - north_left, - south_left, - south, - Point::zero(), // unused but needed to keep array size fixed - Point::zero(), // unused but needed to keep array size fixed - ]; - - let right_points = [ - north, - north_right, - south_right, - south, - Point::zero(), // unused but needed to keep array size fixed - Point::zero(), // unused but needed to keep array size fixed - ]; - - let red = Rgb565::new(255, 0, 0); - let red_9 = Rgb565::new(19, 1, 1); - - let style_red = PrimitiveStyleBuilder::new().fill_color(red).build(); - let style_red_9 = PrimitiveStyleBuilder::new().fill_color(red_9).build(); - - draw_polygon(framebuffer, &merged_points, style_red_9); - draw_polygon(framebuffer, &left_points[0..4], style_red); - draw_polygon(framebuffer, &right_points[0..4], style_red_9); - - // Calculate the bounding box of the arrow - let bounding_box = calculate_bounding_box(&merged_points, Some(10)); - - bounding_box -} - -/// Draw a polygon on the frame buffer -fn draw_polygon( - framebuffer: &mut FrameBuffer, - points: &[Point], - style: PrimitiveStyle, -) { - if points.len() < 3 { - return; // Not enough points to form a polygon - } - - // Use fan triangulation from the first point - let first_point = points[0]; - for i in 1..points.len() - 1 { - let triangle = Triangle::new(first_point, points[i], points[i + 1]) - .into_styled(style); - triangle.draw(framebuffer).unwrap(); - } -} - -// Helper function to calculate coordinates based on angle and radius -fn get_coordinates(center: Point, radius: i32, angle: i32) -> Point { - let angle_rad = (angle as f32).to_radians() as f64; - let x = center.x + (radius as f32 * cos(angle_rad) as f32) as i32; - let y = center.y + (radius as f32 * sin(angle_rad) as f32) as i32; - Point::new(x, y) -} - -/// Draws a circle on the frame buffer. -fn draw_circle(framebuffer: &mut FrameBuffer, color: Rgb565, center: Point, radius: i32) { - let style = PrimitiveStyleBuilder::new() - .fill_color(color) - .build(); - // Calculate the top-left corner of the circle based on the center point and radius - let top_left = Point::new(center.x - radius, center.y - radius); - let diameter = (radius * 2) as u32; - - Circle::new(top_left, diameter as u32) - .into_styled(style) - .draw(framebuffer) - .unwrap(); -} - -/// Creates a button image on the frame buffer. -fn create_button(framebuffer: &mut FrameBuffer, center_x: i32, center_y: i32) { - let circle_radius = 14; - draw_circle(framebuffer, Rgb565::BLACK, Point::new(center_x, center_y), circle_radius); -} - -/// Helper function to calculate the bounding box of a set of points with an optional padding. -fn calculate_bounding_box(points: &[Point], padding: Option) -> Rectangle { - let mut min_x = points[0].x; - let mut max_x = points[0].x; - let mut min_y = points[0].y; - let mut max_y = points[0].y; - - for point in points.iter().skip(1) { - if point.x < min_x { - min_x = point.x; - } - if point.x > max_x { - max_x = point.x; - } - if point.y < min_y { - min_y = point.y; - } - if point.y > max_y { - max_y = point.y; - } - } - - let padding = padding.unwrap_or(0) as i32; - Rectangle::new( - Point::new(min_x - padding, min_y - padding), - Size::new((max_x - min_x + 2 * padding) as u32, (max_y - min_y + 2 * padding) as u32), - ) -} From 2a6e68bed77a37e04eebf4ef79a006fa6f7a4d3c Mon Sep 17 00:00:00 2001 From: GordonCox Date: Tue, 30 Jul 2024 08:14:49 -0700 Subject: [PATCH 20/23] Update waveshare_rp2040_lcd_demo.rs Fixed a typo. --- .../examples/waveshare_rp2040_lcd_demo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index 9d23ed89..dd55637e 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -1,4 +1,4 @@ -//! Example of graphics on the LCD of the Waveshare RP2040-LCD-0.96 +//! Example of graphics on the LCD of the Waveshare RP2040-LCD-1.28 //! //! Draws a red and green line with a blue rectangle. //! After that it fills the screen line for line, at the end it starts over with From 8bf59b4542c261721b2779667c83fad409d8057c Mon Sep 17 00:00:00 2001 From: GordonCox Date: Tue, 30 Jul 2024 20:09:52 -0700 Subject: [PATCH 21/23] Formated code using cargo fmt --- .../examples/waveshare_rp2040_lcd_demo.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index dd55637e..a3e6eee6 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -6,10 +6,9 @@ #![no_std] #![no_main] - -use gc9a01a_driver::{Orientation,GC9A01A}; use cortex_m::delay::Delay; use fugit::RateExtU32; +use gc9a01a_driver::{Orientation, GC9A01A}; use panic_halt as _; use waveshare_rp2040_lcd_1_28::entry; @@ -30,7 +29,7 @@ use embedded_hal::digital::v2::OutputPin; use embedded_graphics::{ pixelcolor::Rgb565, prelude::*, - primitives::{Rectangle,PrimitiveStyleBuilder, Line, PrimitiveStyle}, + primitives::{Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle}, }; const LCD_WIDTH: u32 = 240; @@ -80,8 +79,12 @@ fn main() -> ! { let lcd_cs = pins.gp9.into_push_pull_output(); let lcd_clk = pins.gp10.into_function::(); let lcd_mosi = pins.gp11.into_function::(); - let lcd_rst = pins.gp12.into_push_pull_output_in_state(hal::gpio::PinState::High); - let mut _lcd_bl = pins.gp25.into_push_pull_output_in_state(hal::gpio::PinState::Low); + let lcd_rst = pins + .gp12 + .into_push_pull_output_in_state(hal::gpio::PinState::High); + let mut _lcd_bl = pins + .gp25 + .into_push_pull_output_in_state(hal::gpio::PinState::Low); // Initialize SPI let spi = hal::Spi::<_, _, _, 8>::new(pac.SPI1, (lcd_mosi, lcd_clk)); @@ -96,7 +99,7 @@ fn main() -> ! { let mut display = GC9A01A::new(spi, lcd_dc, lcd_cs, lcd_rst, false, LCD_WIDTH, LCD_HEIGHT); display.init(&mut delay).unwrap(); display.set_orientation(&Orientation::Landscape).unwrap(); - + // Clear the screen before turning on the backlight display.clear(Rgb565::BLACK).unwrap(); _lcd_bl.set_high().unwrap(); From 3f8ae441d538ae946f77813d9ec60be41f9e310a Mon Sep 17 00:00:00 2001 From: GordonCox Date: Tue, 30 Jul 2024 20:14:28 -0700 Subject: [PATCH 22/23] Removed unused dependency Removed unused dependency --- boards/waveshare-rp2040-lcd-1-28/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml index a5891b98..05405746 100644 --- a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml +++ b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml @@ -15,7 +15,6 @@ cortex-m-rt = { workspace = true, optional = true } rp2040-boot2 = { workspace = true, optional = true } rp2040-hal.workspace = true embedded-hal = { version = "0.2" } -libm = "0.2" gc9a01a_driver = { version = "0.1.3" } [dev-dependencies] From 9e0a513a326a06a1cd3bba5da522c360e6c3b359 Mon Sep 17 00:00:00 2001 From: GordonCox Date: Wed, 31 Jul 2024 03:20:17 -0700 Subject: [PATCH 23/23] Backlight and Dependancy Moved Dependency to Workspace, updated backlight code. --- Cargo.toml | 1 + boards/waveshare-rp2040-lcd-1-28/Cargo.toml | 5 ++--- .../examples/waveshare_rp2040_lcd_demo.rs | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 347d95a7..8139ab8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,3 +63,4 @@ usb-device = "0.3.1" usbd-hid = "0.7.0" usbd-serial = "0.2.1" ws2812-pio = "0.8.0" +gc9a01a_driver = "0.1.3" diff --git a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml index 05405746..8e17dbec 100644 --- a/boards/waveshare-rp2040-lcd-1-28/Cargo.toml +++ b/boards/waveshare-rp2040-lcd-1-28/Cargo.toml @@ -14,15 +14,15 @@ repository = "https://github.com/rp-rs/rp-hal-boards.git" cortex-m-rt = { workspace = true, optional = true } rp2040-boot2 = { workspace = true, optional = true } rp2040-hal.workspace = true -embedded-hal = { version = "0.2" } -gc9a01a_driver = { version = "0.1.3" } [dev-dependencies] cortex-m.workspace = true panic-halt.workspace = true +embedded-hal.workspace = true fugit.workspace = true nb.workspace = true embedded-graphics.workspace = true +gc9a01a_driver = { workspace = true } [features] # This is the set of features we enable by default @@ -49,4 +49,3 @@ disable-intrinsics = ["rp2040-hal/disable-intrinsics"] # This enables ROM functions for f64 math that were not present in the earliest RP2040s rom-v2-intrinsics = ["rp2040-hal/rom-v2-intrinsics"] - diff --git a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs index a3e6eee6..6581d749 100644 --- a/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs +++ b/boards/waveshare-rp2040-lcd-1-28/examples/waveshare_rp2040_lcd_demo.rs @@ -24,8 +24,6 @@ use waveshare_rp2040_lcd_1_28::{ Pins, XOSC_CRYSTAL_FREQ, }; -use embedded_hal::digital::v2::OutputPin; - use embedded_graphics::{ pixelcolor::Rgb565, prelude::*, @@ -102,7 +100,7 @@ fn main() -> ! { // Clear the screen before turning on the backlight display.clear(Rgb565::BLACK).unwrap(); - _lcd_bl.set_high().unwrap(); + _lcd_bl.into_push_pull_output_in_state(hal::gpio::PinState::High); delay.delay_ms(1000); let lcd_zero = Point::zero();