-
Notifications
You must be signed in to change notification settings - Fork 23
/
mod.rs
365 lines (322 loc) · 10.8 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
//! Functions for accessing and writing text to the LCD.
//!
//! The display has two layers that are blended on top of each other, and a background layer
//! with an uniform color.
pub use self::color::Color;
pub use self::init::init;
pub use self::stdout::init as init_stdout;
use core::{fmt, ptr};
use stm32f7::stm32f7x6::LTDC;
#[macro_use]
pub mod stdout;
mod color;
mod init;
/// The height of the display in pixels.
pub const HEIGHT: usize = 272;
/// The width of the display in pixels.
pub const WIDTH: usize = 480;
/// The number of bytes per pixel for layer 1.
pub const LAYER_1_OCTETS_PER_PIXEL: usize = 4;
/// The length of the layer 1 buffer in bytes.
pub const LAYER_1_LENGTH: usize = HEIGHT * WIDTH * LAYER_1_OCTETS_PER_PIXEL;
/// The number of bytes per pixel for layer 2.
pub const LAYER_2_OCTETS_PER_PIXEL: usize = 2;
/// The length of the layer 1 buffer in bytes.
pub const LAYER_2_LENGTH: usize = HEIGHT * WIDTH * LAYER_2_OCTETS_PER_PIXEL;
/// Start address of the SDRAM where the framebuffers live.
pub const SDRAM_START: usize = 0xC000_0000;
/// Start address of the layer 1 framebuffer.
pub const LAYER_1_START: usize = SDRAM_START;
/// Start address of the layer 2 framebuffer.
pub const LAYER_2_START: usize = SDRAM_START + LAYER_1_LENGTH;
/// Represents the LCD and provides methods to access both layers.
pub struct Lcd<'a> {
controller: &'a mut LTDC,
layer_1_in_use: bool,
layer_2_in_use: bool,
}
impl<'a> Lcd<'a> {
fn new(ltdc: &'a mut LTDC) -> Self {
Self {
controller: ltdc,
layer_1_in_use: false,
layer_2_in_use: false,
}
}
/// Sets the color of the background layer.
pub fn set_background_color(&mut self, color: Color) {
self.controller
.bccr
.modify(|_, w| unsafe { w.bc().bits(color.to_rgb()) });
}
/// Sets the color `i` in the lookup table for layer 2
pub fn set_color_lookup_table(&mut self, i: u8, color: Color) {
self.controller
.l2clutwr
.write(|w| unsafe { w
.clutadd().bits(i)
.red().bits(color.red)
.green().bits(color.green)
.blue().bits(color.blue)
});
}
/// Returns a reference to layer 1.
pub fn layer_1(&mut self) -> Option<Layer<FramebufferArgb8888>> {
if self.layer_1_in_use {
None
} else {
Some(Layer {
framebuffer: FramebufferArgb8888::new(LAYER_1_START),
})
}
}
/// Returns a reference to layer 2.
pub fn layer_2(&mut self) -> Option<Layer<FramebufferAl88>> {
if self.layer_2_in_use {
None
} else {
Some(Layer {
framebuffer: FramebufferAl88::new(LAYER_2_START),
})
}
}
}
/// Represents a buffer of pixels.
pub trait Framebuffer {
/// Set the pixel at the specified coordinates to the specified color.
fn set_pixel(&mut self, x: usize, y: usize, color: Color);
}
/// A framebuffer in the ARGB8888 format.
///
/// It uses 8bits for alpha, red, green, and black respectively, totaling in 32bits per pixel.
pub struct FramebufferArgb8888 {
base_addr: usize,
}
impl FramebufferArgb8888 {
const fn new(base_addr: usize) -> Self {
Self { base_addr }
}
}
impl Framebuffer for FramebufferArgb8888 {
fn set_pixel(&mut self, x: usize, y: usize, color: Color) {
let pixel = y * WIDTH + x;
let pixel_ptr = (self.base_addr + pixel * LAYER_1_OCTETS_PER_PIXEL) as *mut u32;
unsafe { ptr::write_volatile(pixel_ptr, color.to_argb8888()) };
}
}
/// A framebuffer in the AL88 format.
///
/// There are 8bits for the alpha channel and 8 bits for specifying a color using a
/// lookup table. Thus, each pixel is represented by 16bits.
pub struct FramebufferAl88 {
base_addr: usize,
}
impl FramebufferAl88 {
const fn new(base_addr: usize) -> Self {
Self { base_addr }
}
}
impl Framebuffer for FramebufferAl88 {
fn set_pixel(&mut self, x: usize, y: usize, color: Color) {
let pixel = y * WIDTH + x;
let pixel_ptr = (self.base_addr + pixel * LAYER_2_OCTETS_PER_PIXEL) as *mut u16;
unsafe { ptr::write_volatile(pixel_ptr, u16::from(color.alpha) << 8 | u16::from(color.red)) };
}
}
/// Represents a layer of the LCD controller.
pub struct Layer<T> {
framebuffer: T,
}
impl<T: Framebuffer> Layer<T> {
/// Fill the layer with horizontal stripes.
///
/// Useful for testing.
pub fn horizontal_stripes(&mut self) {
let colors = [
0xff_ff_ff, 0xcc_cc_cc, 0x99_99_99, 0x66_66_66, 0x33_33_33, 0x00_00_00, 0xff_00_00, 0x00_00_ff,
];
// horizontal stripes
for i in 0..HEIGHT {
for j in 0..WIDTH {
self.framebuffer.set_pixel(
j,
i,
Color::from_rgb888(colors[(i / 10) % colors.len()]),
);
}
}
}
/// Fill the layer with vertical stripes.
///
/// Useful for testing.
pub fn vertical_stripes(&mut self) {
let colors = [
0xcc_cc_cc, 0x99_99_99, 0x66_66_66, 0x33_33_33, 0x00_00_00, 0xff_00_00, 0x00_00_ff, 0xff_ff_ff,
];
// vertical stripes
for i in 0..HEIGHT {
for j in 0..WIDTH {
self.framebuffer.set_pixel(
j,
i,
Color::from_rgb888(colors[(j / 10) % colors.len()]),
);
}
}
}
/// Clear all pixels.
///
/// This method sets each pixel to transparent or black, depending on the framebuffer format.
pub fn clear(&mut self) {
for i in 0..HEIGHT {
for j in 0..WIDTH {
self.framebuffer.set_pixel(j, i, Color::from_argb8888(0));
}
}
}
/// Sets the pixel at the specified coordinates to white.
pub fn print_point_at(&mut self, x: usize, y: usize) {
self.print_point_color_at(x, y, Color::from_hex(0xff_ff_ff));
}
/// Sets the pixel at the specified coordinates to the specified color.
pub fn print_point_color_at(&mut self, x: usize, y: usize, color: Color) {
assert!(x < WIDTH);
assert!(y < HEIGHT);
self.framebuffer.set_pixel(x, y, color);
}
/// Creates a text writer on this layer.
pub fn text_writer(&mut self) -> TextWriter<T> {
TextWriter {
layer: self,
x_pos: 0,
y_pos: 0,
}
}
}
/// Allows to print audio data.
pub struct AudioWriter {
next_pixel: usize,
next_col: usize,
prev_value: (usize, usize),
}
impl AudioWriter {
/// Creates a new audio writer starting at the left edge of the screen.
pub const fn new() -> Self {
AudioWriter {
next_pixel: 0,
next_col: 0,
prev_value: (0, 0),
}
}
/// Sets the next pixel on the layer.
///
/// Useful for testing.
pub fn set_next_pixel<F: Framebuffer>(&mut self, layer: &mut Layer<F>, color: Color) {
layer.print_point_color_at(self.next_pixel % WIDTH, self.next_pixel / WIDTH, color);
self.next_pixel = (self.next_pixel + 1) % (HEIGHT * WIDTH);
}
/// Sets the next column of the screen according to the passed audio data.
pub fn set_next_col<F: Framebuffer>(&mut self, layer: &mut Layer<F>, value0: u32, value1: u32) {
let value0 = value0 + 2u32.pow(15);
let value0 = value0 as u16 as usize;
let value0 = value0 / 241;
let value1 = value1 + 2u32.pow(15);
let value1 = value1 as u16 as usize;
let value1 = value1 / 241;
for i in 0..HEIGHT {
let mut color = Color::from_argb8888(0);
if value0 >= self.prev_value.0 {
if i >= self.prev_value.0 && i <= value0 {
color.red = 0xff;
color.alpha = 0xff;
}
} else if i <= self.prev_value.0 && i >= value0 {
color.red = 0xff;
color.alpha = 0xff;
}
if value1 >= self.prev_value.1 {
if i >= self.prev_value.0 && i <= value1 {
color.green = 0xff;
color.alpha = 0xff;
}
} else if i <= self.prev_value.0 && i >= value1 {
color.green = 0xff;
color.alpha = 0xff;
}
let i = i as usize;
layer.print_point_color_at(self.next_col, i, color);
}
self.next_col = (self.next_col + 1) % WIDTH;
self.prev_value = (value0, value1);
}
}
/// Allows writing text to the wrapped layer.
///
/// This struct implements the [fmt::Write](core::fmt::Write) trait, which makes it possible
/// to use the `writeln!` macro with this struct.
pub struct TextWriter<'a, T: Framebuffer + 'a> {
layer: &'a mut Layer<T>,
/// Column position of the cursor
pub x_pos: usize,
/// Row/Line position of the cursor
pub y_pos: usize,
}
impl<'a, T: Framebuffer> TextWriter<'a, T> {
fn newline(&mut self) {
self.y_pos += 8;
self.carriage_return()
}
fn carriage_return(&mut self) {
if self.y_pos >= HEIGHT {
self.clear();
} else {
self.x_pos = 0;
}
}
/// Erases all text on the screen
pub fn clear(&mut self) {
self.x_pos = 0;
self.y_pos = 0;
self.layer.clear();
}
}
impl<'a, T: Framebuffer> fmt::Write for TextWriter<'a, T> {
fn write_str(&mut self, s: &str) -> fmt::Result {
use font8x8::{self, UnicodeFonts};
for c in s.chars() {
if c == '\n' {
self.newline();
continue;
} else if c == '\r' {
self.carriage_return();
continue;
}
match c {
' '..='~' => {
if self.x_pos >= WIDTH {
self.newline();
}
let rendered = font8x8::BASIC_FONTS
.get(c)
.expect("character not found in basic font");
for (y, byte) in rendered.iter().enumerate() {
for (x, bit) in (0..8).enumerate() {
let alpha = if *byte & (1 << bit) == 0 { 0 } else { 255 };
let color = Color {
red: 255,
green: 255,
blue: 255,
alpha,
};
self.layer
.print_point_color_at(self.x_pos + x, self.y_pos + y, color);
}
}
}
_ => panic!("unprintable character"),
}
self.x_pos += 8;
}
Ok(())
}
}