-
Notifications
You must be signed in to change notification settings - Fork 118
Screen API implementation #568
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "libtock_display" | ||
version = "0.1.0" | ||
authors = ["Tock Project Developers <[email protected]>"] | ||
license = "Apache-2.0 OR MIT" | ||
edition = "2021" | ||
repository = "https://www.github.com/tock/libtock-rs" | ||
rust-version.workspace = true | ||
description = "libtock display driver" | ||
|
||
[dependencies] | ||
libtock_platform = { path = "../../../platform" } | ||
|
||
[dev-dependencies] | ||
libtock_unittest = { path = "../../../unittest" } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,318 @@ | ||
#![no_std] | ||
use core::cell::Cell; | ||
use libtock_platform::allow_ro::AllowRo; | ||
use libtock_platform::share; | ||
use libtock_platform::subscribe::Subscribe; | ||
use libtock_platform::{self as platform, DefaultConfig}; | ||
use libtock_platform::{ErrorCode, Syscalls}; | ||
|
||
pub struct Screen<S: Syscalls, C: Config = DefaultConfig>(S, C); | ||
|
||
impl<S: Syscalls, C: Config> Screen<S, C> { | ||
/// Check if the Screen driver exists | ||
pub fn exists() -> Result<(), ErrorCode> { | ||
let val = S::command(DRIVER_NUM, command::EXISTS, 0, 0).is_success(); | ||
if val { | ||
Ok(()) | ||
} else { | ||
Err(ErrorCode::Fail) | ||
} | ||
} | ||
|
||
/// Perform initial screen setup | ||
pub fn screen_setup() -> Result<u32, ErrorCode> { | ||
S::command(DRIVER_NUM, command::SCREEN_SETUP, 0, 0).to_result() | ||
} | ||
|
||
/// Turn on Screen power | ||
pub fn set_power(value: usize) -> Result<(), ErrorCode> { | ||
if value != 0 { | ||
S::command(DRIVER_NUM, command::SET_POWER, value as u32, 0).to_result() | ||
} else { | ||
Err(ErrorCode::Invalid) | ||
} | ||
} | ||
|
||
/// Set screen brightness, wait for completion via subscribe | ||
pub fn set_brightness(value: usize) -> Result<(), ErrorCode> { | ||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope(|subscribe| { | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = S::command(DRIVER_NUM, command::SET_BRIGHTNESS, value as u32, 0).to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
/// Turn on screen color inversion | ||
pub fn set_invert_on() -> Result<(), ErrorCode> { | ||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope(|subscribe| { | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = S::command(DRIVER_NUM, command::SET_INVERT_ON, 0, 0).to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
/// Turn off screen color inversion | ||
pub fn set_invert_off() -> Result<(), ErrorCode> { | ||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope(|subscribe| { | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = S::command(DRIVER_NUM, command::SET_INVERT_OFF, 0, 0).to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
/// Set inversion using a numeric value (non-zero = on) | ||
pub fn set_invert(value: usize) -> Result<(), ErrorCode> { | ||
if value != 0 { | ||
S::command(DRIVER_NUM, command::SET_INVERT, value as u32, 0).to_result() | ||
} else { | ||
Err(ErrorCode::Invalid) | ||
} | ||
} | ||
|
||
/// Get the number of supported resolution modes | ||
pub fn get_resolution_modes_count() -> Result<u32, ErrorCode> { | ||
S::command(DRIVER_NUM, command::GET_RESOLUTION_MODES_COUNT, 0, 0).to_result() | ||
} | ||
|
||
/// Get width and height for a given resolution index | ||
pub fn get_resolution_width_height(index: usize) -> Result<(u32, u32), ErrorCode> { | ||
S::command( | ||
DRIVER_NUM, | ||
command::GET_RESOLUTION_WIDTH_HEIGHT, | ||
index as u32, | ||
0, | ||
) | ||
.to_result() | ||
} | ||
|
||
/// Get the number of supported pixel modes | ||
pub fn pixel_modes_count() -> Result<u32, ErrorCode> { | ||
S::command(DRIVER_NUM, command::PIXEL_MODES_COUNT, 0, 0).to_result() | ||
} | ||
|
||
/// Get the pixel format at a specific index | ||
pub fn pixel_format(index: usize) -> Result<u32, ErrorCode> { | ||
S::command(DRIVER_NUM, command::PIXEL_FORMAT, index as u32, 0).to_result() | ||
} | ||
|
||
/// Get the current rotation of the screen | ||
pub fn get_rotation() -> Result<u32, ErrorCode> { | ||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope(|subscribe| { | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = S::command(DRIVER_NUM, command::GET_ROTATION, 0, 0).to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
/// Set the screen rotation | ||
pub fn set_rotation(rotation: usize) -> Result<(), ErrorCode> { | ||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope(|subscribe| { | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = S::command(DRIVER_NUM, command::SET_ROTATION, rotation as u32, 0).to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
/// Get the currently set screen resolution | ||
pub fn get_resolution() -> Result<(u32, u32), ErrorCode> { | ||
S::command(DRIVER_NUM, command::GET_RESOLUTION, 0, 0).to_result() | ||
} | ||
|
||
/// Set the screen resolution | ||
pub fn set_resolution(width: usize, height: usize) -> Result<(), ErrorCode> { | ||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope(|subscribe| { | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = S::command( | ||
DRIVER_NUM, | ||
command::SET_RESOLUTION, | ||
width as u32, | ||
height as u32, | ||
) | ||
.to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
/// Get the currently set pixel format | ||
pub fn get_pixel_format() -> Result<u32, ErrorCode> { | ||
S::command(DRIVER_NUM, command::GET_PIXEL_FORMAT, 0, 0).to_result() | ||
} | ||
|
||
/// Set the pixel format | ||
pub fn set_pixel_format(format: usize) -> Result<(), ErrorCode> { | ||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope(|subscribe| { | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = | ||
S::command(DRIVER_NUM, command::SET_PIXEL_FORMAT, format as u32, 0).to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
/// Define the region of the screen that will be written to | ||
pub fn set_write_frame(x: u32, y: u32, width: u32, height: u32) -> Result<(), ErrorCode> { | ||
let data1: u32 = ((x & 0xFFFF) << 16_u8) | (y & 0xFFFF); | ||
let data2: u32 = ((width & 0xFFFF) << 16_u8) | (height & 0xFFFF); | ||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope(|subscribe| { | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = S::command(DRIVER_NUM, command::SET_WRITE_FRAME, data1, data2).to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
/// Write data to the screen using the given buffer | ||
pub fn write(s: &[u8]) -> Result<(), ErrorCode> { | ||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope::< | ||
( | ||
AllowRo<_, DRIVER_NUM, { allow_ro::WRITE_BUFFER_ID }>, | ||
Subscribe<_, DRIVER_NUM, { subscribe::WRITE }>, | ||
), | ||
_, | ||
_, | ||
>(|handle| { | ||
let (allow_ro, subscribe) = handle.split(); | ||
S::allow_ro::<C, DRIVER_NUM, { allow_ro::WRITE_BUFFER_ID }>(allow_ro, s)?; | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = S::command(DRIVER_NUM, command::WRITE, s.len() as u32, 0).to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
/// Fill the screen | ||
pub fn fill(s: &mut [u8], color: u16) -> Result<(), ErrorCode> { | ||
if s.len() >= 2 { | ||
s[0] = ((color >> 8) & 0xFF) as u8; | ||
s[1] = (color & 0xFF) as u8; | ||
|
||
let called: Cell<Option<(u32,)>> = Cell::new(None); | ||
share::scope::< | ||
( | ||
AllowRo<_, DRIVER_NUM, { allow_ro::WRITE_BUFFER_ID }>, | ||
Subscribe<_, DRIVER_NUM, { subscribe::WRITE }>, | ||
), | ||
_, | ||
_, | ||
>(|handle| { | ||
let (allow_ro, subscribe) = handle.split(); | ||
S::allow_ro::<C, DRIVER_NUM, { allow_ro::WRITE_BUFFER_ID }>(allow_ro, s)?; | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?; | ||
let val = S::command(DRIVER_NUM, command::FILL, 0, 0).to_result(); | ||
loop { | ||
S::yield_wait(); | ||
if let Some((_,)) = called.get() { | ||
return val; | ||
} | ||
} | ||
}) | ||
} else { | ||
Err(ErrorCode::Fail) | ||
} | ||
} | ||
} | ||
|
||
pub trait Config: | ||
platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config | ||
{ | ||
} | ||
|
||
impl<T: platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config> | ||
Config for T | ||
{ | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Driver number and command IDs | ||
// ----------------------------------------------------------------------------- | ||
|
||
const DRIVER_NUM: u32 = 0x90001; | ||
|
||
// Command IDs | ||
#[allow(unused)] | ||
mod command { | ||
pub const EXISTS: u32 = 0; | ||
pub const SCREEN_SETUP: u32 = 1; | ||
pub const SET_POWER: u32 = 2; | ||
pub const SET_BRIGHTNESS: u32 = 3; | ||
pub const SET_INVERT_ON: u32 = 4; | ||
pub const SET_INVERT_OFF: u32 = 5; | ||
pub const SET_INVERT: u32 = 6; | ||
pub const GET_RESOLUTION_MODES_COUNT: u32 = 11; | ||
pub const GET_RESOLUTION_WIDTH_HEIGHT: u32 = 12; | ||
pub const PIXEL_MODES_COUNT: u32 = 13; | ||
pub const PIXEL_FORMAT: u32 = 14; | ||
pub const GET_ROTATION: u32 = 21; | ||
pub const SET_ROTATION: u32 = 22; | ||
pub const GET_RESOLUTION: u32 = 23; | ||
pub const SET_RESOLUTION: u32 = 24; | ||
pub const GET_PIXEL_FORMAT: u32 = 25; | ||
pub const SET_PIXEL_FORMAT: u32 = 26; | ||
pub const SET_WRITE_FRAME: u32 = 100; | ||
pub const WRITE: u32 = 200; | ||
pub const FILL: u32 = 300; | ||
} | ||
|
||
// Subscribe ID used for callbacks | ||
mod subscribe { | ||
pub const WRITE: u32 = 0; | ||
} | ||
|
||
// Allow-readonly buffer ID | ||
mod allow_ro { | ||
pub const WRITE_BUFFER_ID: u32 = 0; | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.