
为0.96寸TFT_LCD屏编写驱动
2026年1月13日大约 15 分钟
为0.96寸TFT_LCD屏编写驱动
此类屏幕多使用st7735驱动
主程序(随便写写)
main.rs
#![no_std]
#![no_main]
mod encoder;
use core::cell::RefCell;
use core::fmt::Write;
use core::sync::atomic::Ordering;
use cortex_m::interrupt::{CriticalSection, Mutex};
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
use embassy_stm32::spi::{Config as SpiConfig, Spi};
use embassy_stm32::time::{Hertz, khz, mhz};
use embassy_stm32::timer::{
low_level::Timer,
qei::{Qei, QeiPin},
};
use embassy_stm32::usart::{Config as UartConfig, Uart};
use embassy_stm32::{Config as BoardClockConfig, bind_interrupts, interrupt, peripherals, usart};
use embassy_time::Timer as Delay;
use encoder::COUNT;
use heapless::String;
use {defmt_rtt as _, panic_probe as _};
mod lcd;
use lcd::{Config as TftConfig, FONT_7X10, St7735Color, TftLcd};
mod by5002;
use by5002::MusicPlayer;
bind_interrupts!(struct Irqs {
USART2 => usart::InterruptHandler<peripherals::USART2>;
});
static LED: Mutex<RefCell<Option<Output<'static>>>> = Mutex::new(RefCell::new(None));
static ENCODER: Mutex<RefCell<Option<Qei<'static, peripherals::TIM4>>>> = Mutex::new(RefCell::new(None));
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(enable100_mhz());
Delay::after_millis(300).await;
let mut usart = Uart::new(
p.USART2,
p.PA3,
p.PA2,
Irqs,
p.DMA1_CH6,
p.DMA1_CH5,
UartConfig::default(),
)
.unwrap();
let mut usart6_config = UartConfig::default();
usart6_config.baudrate = 9600;
let usart6 = Uart::new_blocking(p.USART6, p.PA12, p.PA11, usart6_config).unwrap();
let mut music = MusicPlayer::new(usart6);
Delay::after_millis(100).await;
let mut spi_config = SpiConfig::default();
spi_config.frequency = mhz(50);
let spi1 = Spi::new_txonly(p.SPI1, p.PA5, p.PA7, p.DMA2_CH5, spi_config);
// 将引脚配置为输出类型
let blk_pin = Output::new(p.PA4, Level::High, Speed::Low);
let dc_pin = Output::new(p.PC4, Level::Low, Speed::VeryHigh);
let rst_pin = Output::new(p.PC5, Level::Low, Speed::VeryHigh);
let mut tft = TftLcd::new(spi1, None, dc_pin, rst_pin, Some(blk_pin), TftConfig::default());
tft.init().await;
tft.fill_screen(St7735Color::Black);
tft.draw_string(0, 0, "Ciallo", FONT_7X10, St7735Color::Yellow, St7735Color::Black);
tft.draw_i32(0, 11, -12345, FONT_7X10, St7735Color::Blue, St7735Color::Black);
tft.draw_u32(0, 22, 67890, FONT_7X10, St7735Color::Green, St7735Color::Black);
tft.draw_f32(0, 33, -123.3456, FONT_7X10, St7735Color::Red, St7735Color::Black);
Delay::after_millis(1000).await;
// 初始化编码器
let enc_a = QeiPin::new(p.PB6);
let enc_b = QeiPin::new(p.PB7);
let encoder = Qei::new(p.TIM4, enc_a, enc_b);
let led = Output::new(p.PC15, Level::Low, Speed::Low);
let button = Input::new(p.PC13, Pull::Up);
cortex_m::interrupt::free(|cs: &CriticalSection| {
LED.borrow(cs).replace(Some(led));
ENCODER.borrow(cs).replace(Some(encoder));
});
// 配置TIM10定时器中断
let tim14 = Timer::new(p.TIM14);
tim14.set_frequency(Hertz(10));
tim14.enable_update_interrupt(true);
tim14.start();
unsafe {
cortex_m::peripheral::NVIC::unmask(interrupt::TIM8_TRG_COM_TIM14);
}
tft.fill_screen(St7735Color::Black);
let mut i: u16 = 0;
loop {
Delay::after_millis(15).await;
let cnt = COUNT.load(Ordering::Relaxed);
let _ = tft.draw_i32(0, 0, cnt, FONT_7X10, St7735Color::Orange, St7735Color::Black);
if button.is_low() {
Delay::after_millis(15).await;
while button.is_low() {}
Delay::after_millis(15).await;
let _ = tft.fill_screen(St7735Color::Black);
}
// let mut count_delta: i16 = 0;
// cortex_m::interrupt::free(|cs: &CriticalSection| {
// if let Some(ref mut enc) = ENCODER.borrow(cs).borrow_mut().as_mut() {
// count_delta = enc.count() as i16;
// }
// });
// let mut cnt: i32 = 0;
// cnt += count_delta as i32;
// tft.draw_i32(0, 0, cnt, FONT_7X10, St7735Color::Green, St7735Color::Black);
// let _ = music.set_volume(cnt as i16);
// let mut s: String<32> = String::new();
// core::write!(&mut s, "Current Count: {}\r\n", cnt).unwrap(); // 必须带上use core::fmt::Write;
// usart.write(s.as_bytes()).await.unwrap();
// if button.is_low() {
// Delay::after_millis(15).await;
// while button.is_low() {}
// Delay::after_millis(15).await;
// i += 1;
// let _ = music.select_track(i);
// if i > 3 {
// i = 0;
// }
// }
// led.toggle();
}
}
#[interrupt]
fn TIM8_TRG_COM_TIM14() {
let tim14 = embassy_stm32::pac::TIM14;
tim14.sr().write(|w| w.set_uif(false)); // 清除中断标志
// Code
cortex_m::interrupt::free(|cs| {
if let Some(ref mut l) = LED.borrow(cs).borrow_mut().as_mut() {
l.toggle();
}
if let Some(ref mut enc) = ENCODER.borrow(cs).borrow_mut().as_mut() {
let cnt: i16 = enc.count() as i16;
enc.reset_count();
COUNT.fetch_add(cnt as i32, Ordering::Relaxed);
}
});
// Code
}
fn enable100_mhz() -> BoardClockConfig {
let mut config = BoardClockConfig::default();
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Oscillator,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL100,
divp: Some(PllPDiv::DIV2),
divq: Some(PllQDiv::DIV4),
divr: Some(PllRDiv::DIV2),
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV2;
config.rcc.apb2_pre = APBPrescaler::DIV1;
config.rcc.sys = Sysclk::PLL1_P;
config
}TFT_LCD驱动
mod.rs
pub mod st7735;
pub mod fonts;
pub use fonts::{FONT_7X10, FontDef};
pub use st7735::{Config, St7735Color, TftLcd};st7735.rs
use embassy_stm32::gpio::Output;
use embassy_time::Timer;
use embedded_hal::spi::SpiBus;
use heapless::String;
use crate::lcd::FontDef;
pub struct St7735Driver<SPI: SpiBus> {
spi: SPI,
cs: Option<Output<'static>>,
dc: Output<'static>,
rst: Output<'static>,
}
impl<SPI: SpiBus> St7735Driver<SPI> {
pub fn new(spi: SPI, cs: Option<Output<'static>>, dc: Output<'static>, rst: Output<'static>) -> Self {
Self { spi, cs, dc, rst }
}
pub fn select(&mut self, select: bool) {
if let Some(cs) = self.cs.as_mut() {
if select {
cs.set_low();
} else {
cs.set_high();
}
}
}
pub async fn reset(&mut self) {
self.rst.set_low();
Timer::after_millis(5).await;
self.rst.set_high();
// Timer::after_millis(5).await;
}
pub fn write_command(&mut self, cmd: &[u8]) -> Result<(), SPI::Error> {
self.dc.set_low();
let result = self.spi.write(cmd);
self.spi.flush()?;
result
}
pub fn write_data(&mut self, data: &[u8]) -> Result<(), SPI::Error> {
self.dc.set_high();
let result = self.spi.write(data);
self.spi.flush()?;
result
}
}
const DELAY: u8 = 0x80;
const ST7735_MADCTL_MY: u8 = 0x80; // 行地址顺序控制位(MY),设置为1时行地址从大到小排列
const ST7735_MADCTL_MX: u8 = 0x40; // 列地址顺序控制位(MX),设置为1时列地址从大到小排列
const ST7735_MADCTL_MV: u8 = 0x20; // 行列交换控制位(MV),设置为1时交换行和列地址
const ST7735_MADCTL_ML: u8 = 0x10; // 垂直刷新方向控制位(ML),设置为1时垂直方向从下到上刷新
const ST7735_MADCTL_RGB: u8 = 0x00; // RGB/BGR颜色顺序控制位(RGB模式),设置为0时使用RGB顺序
const ST7735_MADCTL_BGR: u8 = 0x08; // RGB/BGR颜色顺序控制位(BGR模式),设置为1时使用BGR顺序
const ST7735_MADCTL_MH: u8 = 0x04; // 水平刷新方向控制位(MH),设置为1时水平方向从右到左刷新
// ST7735命令常量定义
const ST7735_NOP: u8 = 0x00; // 无操作命令
const ST7735_SWRESET: u8 = 0x01; // 软件复位命令
const ST7735_RDDID: u8 = 0x04; // 读取显示ID命令
const ST7735_RDDST: u8 = 0x09; // 读取显示状态命令
const ST7735_SLPIN: u8 = 0x10; // 睡眠模式开启命令
const ST7735_SLPOUT: u8 = 0x11; // 睡眠模式退出命令
const ST7735_PTLON: u8 = 0x12; // 部分显示模式开启命令
const ST7735_NORON: u8 = 0x13; // 正常显示模式命令
const ST7735_INVOFF: u8 = 0x20; // 关闭显示反转命令
const ST7735_INVON: u8 = 0x21; // 开启显示反转命令
const ST7735_GAMSET: u8 = 0x26; // GAMMA设置命令
const ST7735_DISPOFF: u8 = 0x28; // 关闭显示命令
const ST7735_DISPON: u8 = 0x29; // 开启显示命令
const ST7735_CASET: u8 = 0x2A; // 列地址设置命令
const ST7735_RASET: u8 = 0x2B; // 行地址设置命令
const ST7735_RAMWR: u8 = 0x2C; // 内存写入命令
const ST7735_RAMRD: u8 = 0x2E; // 内存读取命令
const ST7735_PTLAR: u8 = 0x30; // 部分区域设置命令
const ST7735_COLMOD: u8 = 0x3A; // 颜色模式设置命令
const ST7735_MADCTL: u8 = 0x36; // 内存数据访问控制命令
const ST7735_FRMCTR1: u8 = 0xB1; // 帧率控制命令1
const ST7735_FRMCTR2: u8 = 0xB2; // 帧率控制命令2
const ST7735_FRMCTR3: u8 = 0xB3; // 帧率控制命令3
const ST7735_INVCTR: u8 = 0xB4; // 显示反转控制命令
const ST7735_DISSET5: u8 = 0xB6; // 显示功能设置命令5
const ST7735_PWCTR1: u8 = 0xC0; // 电源控制命令1
const ST7735_PWCTR2: u8 = 0xC1; // 电源控制命令2
const ST7735_PWCTR3: u8 = 0xC2; // 电源控制命令3
const ST7735_PWCTR4: u8 = 0xC3; // 电源控制命令4
const ST7735_PWCTR5: u8 = 0xC4; // 电源控制命令5
const ST7735_VMCTR1: u8 = 0xC5; // VCOM控制命令1
const ST7735_RDID1: u8 = 0xDA; // 读取ID1命令
const ST7735_RDID2: u8 = 0xDB; // 读取ID2命令
const ST7735_RDID3: u8 = 0xDC; // 读取ID3命令
const ST7735_RDID4: u8 = 0xDD; // 读取ID4命令
const ST7735_PWCTR6: u8 = 0xFC; // 电源控制命令6
const ST7735_GMCTRP1: u8 = 0xE0; // 正极性Gamma校正设置命令
const ST7735_GMCTRN1: u8 = 0xE1; // 负极性Gamma校正设置命令
enum Rotation {
RotationRight,
RotationLeft,
RotationDefault,
}
impl Rotation {
fn value(&self) -> u8 {
match self {
Rotation::RotationRight => ST7735_MADCTL_MY | ST7735_MADCTL_MV | ST7735_MADCTL_BGR,
Rotation::RotationLeft => ST7735_MADCTL_MX | ST7735_MADCTL_MV | ST7735_MADCTL_BGR,
Rotation::RotationDefault => ST7735_MADCTL_MX | ST7735_MADCTL_MY | ST7735_MADCTL_BGR,
}
}
}
pub struct Config {
pub width: u8,
pub height: u8,
pub x_start: u8,
pub y_start: u8,
pub rotation: Rotation,
}
impl Default for Config {
fn default() -> Self {
Self {
width: 160,
height: 80,
x_start: 1,
y_start: 26,
rotation: Rotation::RotationRight,
}
}
}
#[derive(Copy, Clone)]
pub enum St7735Color {
Black,
Blue,
Red,
Green,
Cyan,
Magenta,
Yellow,
White,
Orange, // 新增:橙色 (RGB: 255, 165, 0)
Purple, // 新增:紫色 (RGB: 128, 0, 128)
}
impl St7735Color {
fn value(&self) -> u16 {
match self {
St7735Color::Black => 0x0000,
St7735Color::Blue => 0x001F,
St7735Color::Red => 0xF800,
St7735Color::Green => 0x07E0,
St7735Color::Cyan => 0x07FF,
St7735Color::Magenta => 0xF81F,
St7735Color::Yellow => 0xFFE0,
St7735Color::White => 0xFFFF,
St7735Color::Orange => 0xFD20,
St7735Color::Purple => 0x7C0F,
}
}
}
pub struct TftLcd<SPI: SpiBus> {
driver: St7735Driver<SPI>,
blk_pin: Option<Output<'static>>,
config: Config,
}
impl<SPI: SpiBus> TftLcd<SPI> {
pub fn new(
spi: SPI,
cs_pin: Option<Output<'static>>,
dc_pin: Output<'static>,
rst_pin: Output<'static>,
blk_pin: Option<Output<'static>>,
config: Config,
) -> Self {
let st7735driver = St7735Driver::new(spi, cs_pin, dc_pin, rst_pin);
Self {
driver: st7735driver,
blk_pin,
config,
}
}
pub async fn init(&mut self) {
if let Some(blk) = self.blk_pin.as_mut() {
blk.set_high();
}
let cmds1: [u8; 59] = [
15, // 15 commands in list:
ST7735_SWRESET,
DELAY, // 1: Software reset, 0 args, w/delay
150, // 150 ms delay
ST7735_SLPOUT,
DELAY, // 2: Out of sleep mode, 0 args, w/delay
255, // 500 ms delay
ST7735_FRMCTR1,
3, // 3: Frame rate ctrl - normal mode, 3 args:
0x01,
0x2C,
0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
ST7735_FRMCTR2,
3, // 4: Frame rate control - idle mode, 3 args:
0x01,
0x2C,
0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
ST7735_FRMCTR3,
6, // 5: Frame rate ctrl - partial mode, 6 args:
0x01,
0x2C,
0x2D, // Dot inversion mode
0x01,
0x2C,
0x2D, // Line inversion mode
ST7735_INVCTR,
1, // 6: Display inversion ctrl, 1 arg, no delay:
0x07, // No inversion
ST7735_PWCTR1,
3, // 7: Power control, 3 args, no delay:
0xA2,
0x02, // -4.6V
0x84, // AUTO mode
ST7735_PWCTR2,
1, // 8: Power control, 1 arg, no delay:
0xC5, // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD
ST7735_PWCTR3,
2, // 9: Power control, 2 args, no delay:
0x0A, // Opamp current small
0x00, // Boost frequency
ST7735_PWCTR4,
2, // 10: Power control, 2 args, no delay:
0x8A, // BCLK/2, Opamp current small & Medium low
0x2A,
ST7735_PWCTR5,
2, // 11: Power control, 2 args, no delay:
0x8A,
0xEE,
ST7735_VMCTR1,
1, // 12: Power control, 1 arg, no delay:
0x0E,
ST7735_INVOFF,
0, // 13: Don't invert display, no args, no delay
ST7735_MADCTL,
1, // 14: Memory access control (directions), 1 arg:
self.config.rotation.value(), // row addr/col addr, bottom to top refresh
ST7735_COLMOD,
1, // 15: set color mode, 1 arg, no delay:
0x05,
];
let cmds2: [u8; 15] = [
3, // 3 commands in list:
ST7735_CASET,
4, // 1: Column addr set, 4 args, no delay:
0x00,
0x00, // XSTART = 0
0x00,
0x4F, // XEND = 79
ST7735_RASET,
4, // 2: Row addr set, 4 args, no delay:
0x00,
0x00, // XSTART = 0
0x00,
0x9F, // XEND = 159
ST7735_INVON,
0,
];
let cmds3 = [
4, // 4 commands in list:
ST7735_GMCTRP1,
16, // 1: Gamma Adjustments (pos. polarity), 16 args, no delay:
0x02,
0x1c,
0x07,
0x12,
0x37,
0x32,
0x29,
0x2d,
0x29,
0x25,
0x2B,
0x39,
0x00,
0x01,
0x03,
0x10,
ST7735_GMCTRN1,
16, // 2: Gamma Adjustments (neg. polarity), 16 args, no delay:
0x03,
0x1d,
0x07,
0x06,
0x2E,
0x2C,
0x29,
0x2D,
0x2E,
0x2E,
0x37,
0x3F,
0x00,
0x00,
0x02,
0x10,
ST7735_NORON,
DELAY, // 3: Normal display on, no args, w/delay
10, // 10 ms delay
ST7735_DISPON,
DELAY, // 4: Main screen turn on, no args w/delay
100,
];
self.driver.select(true);
self.driver.reset().await;
self.execute_command_list(&cmds1).await;
self.execute_command_list(&cmds2).await;
self.execute_command_list(&cmds3).await;
}
async fn execute_command_list(&mut self, cmd_list: &[u8]) {
let mut idx = 0;
let num_commands = cmd_list[idx];
idx += 1;
for _ in 0..num_commands {
let cmd = cmd_list[idx];
idx += 1;
self.driver.write_command(&[cmd]).unwrap(); // Write command
let mut num_args = cmd_list[idx];
idx += 1;
let needs_delay = (num_args & DELAY) != 0; // Check if delay is required (high bit set)
num_args &= 0x7F;
if num_args > 0 {
// Write arguments
let args = &cmd_list[idx..idx + num_args as usize];
self.driver.write_data(args).unwrap();
idx += num_args as usize;
}
if needs_delay {
let mut ms = cmd_list[idx] as u64;
idx += 1;
if ms == 255 {
ms = 500;
}
Timer::after_millis(ms).await; // Perform async delay
}
}
}
fn set_address_window(&mut self, x0: u8, y0: u8, x1: u8, y1: u8) {
// column address set
let _ = self.driver.write_command(&[ST7735_CASET]);
let mut data: [u8; 4] = [0x00u8, x0 + self.config.x_start, 0x00, x1 + self.config.x_start];
let _ = self.driver.write_data(&data);
// row address set
let _ = self.driver.write_command(&[ST7735_RASET]);
data[1] = y0 + self.config.y_start;
data[3] = y1 + self.config.y_start;
let _ = self.driver.write_data(&data);
let _ = self.driver.write_command(&[ST7735_RAMWR]);
}
pub fn draw_pixel(&mut self, x: u8, y: u8, color: St7735Color) {
if x >= self.config.width || y >= self.config.height {
return;
}
self.driver.select(true);
self.set_address_window(x, y, x + 1, y + 1);
let color_value = color.value();
let data = [(color_value >> 8) as u8, color_value as u8];
let _ = self.driver.write_data(&data);
self.driver.select(false);
}
pub fn fill_rect(&mut self, x: u8, y: u8, w: u8, h: u8, color: St7735Color) {
if x >= self.config.width || y >= self.config.height {
return;
}
let mut width = w;
if x + width - 1 >= self.config.width {
width = self.config.width - x;
}
let mut height = h;
if y + height - 1 >= self.config.height {
height = self.config.height - y;
}
self.driver.select(true);
self.set_address_window(x, y, x + width - 1, y + height - 1);
let color_value = color.value();
let data = [(color_value >> 8) as u8, color_value as u8];
self.driver.dc.set_high();
for _row in 0..height {
for _col in 0..width {
let _ = self.driver.write_data(&data);
}
}
}
pub fn fill_screen(&mut self, color: St7735Color) {
self.fill_rect(0, 0, self.config.width, self.config.height, color);
}
/// Draw a character,在Rust中char是Unicode字符,这里用u8表示ASCII字符
fn draw_char(&mut self, x: u8, y: u8, ch: u8, font: FontDef, ch_color: St7735Color, bg_color: St7735Color) {
self.set_address_window(x, y, x + font.width - 1, y + font.height - 1);
// 确定字符索引(减去32是因为字体数据从空格字符开始)
let char_offset = (ch as u16 - 32) * font.height as u16;
// 遍历字体的每一行
for i in 0..font.height as u16 {
// 获取当前行的位图数据
let b = font.data[char_offset as usize + i as usize];
// 遍历当前行的每一位(即字体的每一列)
for j in 0..font.width as u16 {
// 检查当前位是否为前景色
let pixel_color = if (b << j) & 0x8000 != 0 {
ch_color.value()
} else {
bg_color.value()
};
// 准备颜色数据
let data = [(pixel_color >> 8) as u8, pixel_color as u8];
// 写入像素数据
let _ = self.driver.write_data(&data);
}
}
}
pub fn draw_string(&mut self, x: u8, y: u8, s: &str, font: FontDef, fg_color: St7735Color, bg_color: St7735Color) {
let mut current_x = x;
let mut current_y = y;
for c in s.chars() {
// 检查是否需要换行
if current_x + font.width > self.config.width {
current_x = 0;
current_y += font.height;
// 如果换行后超出屏幕范围,则停止显示
if current_y + font.height > self.config.height {
break;
}
}
// 检查当前行是否超出屏幕范围
if current_y + font.height <= self.config.height {
// 转换char为u8(仅适用于ASCII字符)
let char_byte = if c.is_ascii() { c as u8 } else { ' ' as u8 }; // 非ASCII字符显示为空格
self.draw_char(current_x, current_y, char_byte, font, fg_color, bg_color);
current_x += font.width; // 移动到下一个字符位置
}
}
}
fn i32_to_string(&self, mut num: i32) -> String<12> {
let mut tmp = [0u8; 12];
let mut i = 11;
let neg = num < 0;
if neg {
num = num.wrapping_abs();
}
loop {
tmp[i] = b'0' + (num % 10) as u8;
i -= 1;
num /= 10;
if num == 0 {
break;
}
}
if neg {
tmp[i] = b'-';
} else {
i += 1;
}
let s = core::str::from_utf8(&tmp[i..]).unwrap_or("ERR");
String::try_from(s).unwrap_or_else(|_| {
let mut fallback: String<12> = String::new();
let _ = fallback.push_str("ERR");
fallback
})
}
/// 失败时返回"ERR"
fn u32_to_string(&self, mut num: u32) -> String<10> {
let mut buf = [0u8; 10]; // 4294967295
let mut i = 10;
// 特殊情况:0
if num == 0 {
buf[9] = b'0';
i = 9;
} else {
while num > 0 {
i -= 1;
buf[i] = b'0' + (num % 10) as u8;
num /= 10;
}
}
let s = core::str::from_utf8(&buf[i..]).unwrap_or("ERR");
String::try_from(s).unwrap_or_else(|_| {
let mut err: String<10> = String::new();
let _ = err.push_str("ERR");
err
})
}
/// 失败时返回 "ERR" 保留 3 位小数,四舍五入
fn f32_to_string(&self, mut val: f32) -> String<16> {
let mut s: String<16> = String::new();
// 负号
if val < 0.0 {
let _ = s.push('-');
val = -val;
}
// 整数部分
let int_part = val as u32;
let mut n = int_part;
let mut buf = [0u8; 10];
let mut len = 0;
loop {
len += 1;
buf[10 - len] = b'0' + (n % 10) as u8;
n /= 10;
if n == 0 {
break;
}
}
for &b in &buf[10 - len..] {
if s.push(b as char).is_err() {
return String::try_from("ERR").unwrap_or_default();
}
}
// 小数点
if s.push('.').is_err() {
return String::try_from("ERR").unwrap_or_default();
}
// 小数部分(3位,四舍五入)
let frac = (val - int_part as f32) * 1000.0 + 0.5;
let mut frac_int = frac as u32;
if frac_int > 999 {
frac_int = 999;
}
for i in 0..3 {
let digit = (frac_int / 10_u32.pow(2 - i)) % 10;
if s.push(char::from(b'0' + digit as u8)).is_err() {
return String::try_from("ERR").unwrap_or_default();
}
}
s
}
pub fn draw_i32(&mut self, x: u8, y: u8, num: i32, font: FontDef, fg_color: St7735Color, bg_color: St7735Color) {
let s = self.i32_to_string(num);
self.draw_string(x, y, &s, font, fg_color, bg_color);
}
pub fn draw_u32(&mut self, x: u8, y: u8, num: u32, font: FontDef, fg_color: St7735Color, bg_color: St7735Color) {
let s = self.u32_to_string(num);
self.draw_string(x, y, &s, font, fg_color, bg_color);
}
pub fn draw_f32(&mut self, x: u8, y: u8, val: f32, font: FontDef, fg_color: St7735Color, bg_color: St7735Color) {
let s = self.f32_to_string(val);
self.draw_string(x, y, &s, font, fg_color, bg_color);
}
}fonts.rs
// 定义字体数据数组
const FONT7X10: [u16; 950] = [
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // sp
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x0000, 0x0000, // !
0x2800, 0x2800, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // "
0x2400, 0x2400, 0x7C00, 0x2400, 0x4800, 0x7C00, 0x4800, 0x4800, 0x0000, 0x0000, // #
0x3800, 0x5400, 0x5000, 0x3800, 0x1400, 0x5400, 0x5400, 0x3800, 0x1000, 0x0000, // $
0x2000, 0x5400, 0x5800, 0x3000, 0x2800, 0x5400, 0x1400, 0x0800, 0x0000, 0x0000, // %
0x1000, 0x2800, 0x2800, 0x1000, 0x3400, 0x4800, 0x4800, 0x3400, 0x0000, 0x0000, // &
0x1000, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // '
0x0800, 0x1000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x1000, 0x0800, // (
0x2000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x1000, 0x2000, // )
0x1000, 0x3800, 0x1000, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // *
0x0000, 0x0000, 0x1000, 0x1000, 0x7C00, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, // +
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x1000, 0x1000, // ,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, // -
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, // .
0x0800, 0x0800, 0x1000, 0x1000, 0x1000, 0x1000, 0x2000, 0x2000, 0x0000, 0x0000, // /
0x3800, 0x4400, 0x4400, 0x5400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // 0
0x1000, 0x3000, 0x5000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // 1
0x3800, 0x4400, 0x4400, 0x0400, 0x0800, 0x1000, 0x2000, 0x7C00, 0x0000, 0x0000, // 2
0x3800, 0x4400, 0x0400, 0x1800, 0x0400, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // 3
0x0800, 0x1800, 0x2800, 0x2800, 0x4800, 0x7C00, 0x0800, 0x0800, 0x0000, 0x0000, // 4
0x7C00, 0x4000, 0x4000, 0x7800, 0x0400, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // 5
0x3800, 0x4400, 0x4000, 0x7800, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // 6
0x7C00, 0x0400, 0x0800, 0x1000, 0x1000, 0x2000, 0x2000, 0x2000, 0x0000, 0x0000, // 7
0x3800, 0x4400, 0x4400, 0x3800, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // 8
0x3800, 0x4400, 0x4400, 0x4400, 0x3C00, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // 9
0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, // :
0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x1000, 0x1000, 0x1000, // ;
0x0000, 0x0000, 0x0C00, 0x3000, 0x4000, 0x3000, 0x0C00, 0x0000, 0x0000, 0x0000, // <
0x0000, 0x0000, 0x0000, 0x7C00, 0x0000, 0x7C00, 0x0000, 0x0000, 0x0000, 0x0000, // =
0x0000, 0x0000, 0x6000, 0x1800, 0x0400, 0x1800, 0x6000, 0x0000, 0x0000, 0x0000, // >
0x3800, 0x4400, 0x0400, 0x0800, 0x1000, 0x1000, 0x0000, 0x1000, 0x0000, 0x0000, // ?
0x3800, 0x4400, 0x4C00, 0x5400, 0x5C00, 0x4000, 0x4000, 0x3800, 0x0000, 0x0000, // @
0x1000, 0x2800, 0x2800, 0x2800, 0x2800, 0x7C00, 0x4400, 0x4400, 0x0000, 0x0000, // A
0x7800, 0x4400, 0x4400, 0x7800, 0x4400, 0x4400, 0x4400, 0x7800, 0x0000, 0x0000, // B
0x3800, 0x4400, 0x4000, 0x4000, 0x4000, 0x4000, 0x4400, 0x3800, 0x0000, 0x0000, // C
0x7000, 0x4800, 0x4400, 0x4400, 0x4400, 0x4400, 0x4800, 0x7000, 0x0000, 0x0000, // D
0x7C00, 0x4000, 0x4000, 0x7C00, 0x4000, 0x4000, 0x4000, 0x7C00, 0x0000, 0x0000, // E
0x7C00, 0x4000, 0x4000, 0x7800, 0x4000, 0x4000, 0x4000, 0x4000, 0x0000, 0x0000, // F
0x3800, 0x4400, 0x4000, 0x4000, 0x5C00, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // G
0x4400, 0x4400, 0x4400, 0x7C00, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // H
0x3800, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x3800, 0x0000, 0x0000, // I
0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // J
0x4400, 0x4800, 0x5000, 0x6000, 0x5000, 0x4800, 0x4800, 0x4400, 0x0000, 0x0000, // K
0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x7C00, 0x0000, 0x0000, // L
0x4400, 0x6C00, 0x6C00, 0x5400, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // M
0x4400, 0x6400, 0x6400, 0x5400, 0x5400, 0x4C00, 0x4C00, 0x4400, 0x0000, 0x0000, // N
0x3800, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // O
0x7800, 0x4400, 0x4400, 0x4400, 0x7800, 0x4000, 0x4000, 0x4000, 0x0000, 0x0000, // P
0x3800, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x5400, 0x3800, 0x0400, 0x0000, // Q
0x7800, 0x4400, 0x4400, 0x4400, 0x7800, 0x4800, 0x4800, 0x4400, 0x0000, 0x0000, // R
0x3800, 0x4400, 0x4000, 0x3000, 0x0800, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // S
0x7C00, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // T
0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // U
0x4400, 0x4400, 0x4400, 0x2800, 0x2800, 0x2800, 0x1000, 0x1000, 0x0000, 0x0000, // V
0x4400, 0x4400, 0x5400, 0x5400, 0x5400, 0x6C00, 0x2800, 0x2800, 0x0000, 0x0000, // W
0x4400, 0x2800, 0x2800, 0x1000, 0x1000, 0x2800, 0x2800, 0x4400, 0x0000, 0x0000, // X
0x4400, 0x4400, 0x2800, 0x2800, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // Y
0x7C00, 0x0400, 0x0800, 0x1000, 0x1000, 0x2000, 0x4000, 0x7C00, 0x0000, 0x0000, // Z
0x1800, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1800, // [
0x2000, 0x2000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0000, 0x0000, /* \ */
0x3000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x3000, // ]
0x1000, 0x2800, 0x2800, 0x4400, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ^
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFE00, // _
0x2000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // `
0x0000, 0x0000, 0x3800, 0x4400, 0x3C00, 0x4400, 0x4C00, 0x3400, 0x0000, 0x0000, // a
0x4000, 0x4000, 0x5800, 0x6400, 0x4400, 0x4400, 0x6400, 0x5800, 0x0000, 0x0000, // b
0x0000, 0x0000, 0x3800, 0x4400, 0x4000, 0x4000, 0x4400, 0x3800, 0x0000, 0x0000, // c
0x0400, 0x0400, 0x3400, 0x4C00, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0000, 0x0000, // d
0x0000, 0x0000, 0x3800, 0x4400, 0x7C00, 0x4000, 0x4400, 0x3800, 0x0000, 0x0000, // e
0x0C00, 0x1000, 0x7C00, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // f
0x0000, 0x0000, 0x3400, 0x4C00, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0400, 0x7800, // g
0x4000, 0x4000, 0x5800, 0x6400, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // h
0x1000, 0x0000, 0x7000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // i
0x1000, 0x0000, 0x7000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0xE000, // j
0x4000, 0x4000, 0x4800, 0x5000, 0x6000, 0x5000, 0x4800, 0x4400, 0x0000, 0x0000, // k
0x7000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // l
0x0000, 0x0000, 0x7800, 0x5400, 0x5400, 0x5400, 0x5400, 0x5400, 0x0000, 0x0000, // m
0x0000, 0x0000, 0x5800, 0x6400, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // n
0x0000, 0x0000, 0x3800, 0x4400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // o
0x0000, 0x0000, 0x5800, 0x6400, 0x4400, 0x4400, 0x6400, 0x5800, 0x4000, 0x4000, // p
0x0000, 0x0000, 0x3400, 0x4C00, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0400, 0x0400, // q
0x0000, 0x0000, 0x5800, 0x6400, 0x4400, 0x4400, 0x6400, 0x5800, 0x4000, 0x4000, // r
0x0000, 0x0000, 0x3800, 0x4400, 0x3000, 0x0800, 0x4400, 0x3800, 0x0000, 0x0000, // s
0x2000, 0x2000, 0x7800, 0x2000, 0x2000, 0x2000, 0x2000, 0x1800, 0x0000, 0x0000, // t
0x0000, 0x0000, 0x4400, 0x4400, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0000, 0x0000, // u
0x0000, 0x0000, 0x4400, 0x4400, 0x2800, 0x2800, 0x2800, 0x1000, 0x0000, 0x0000, // v
0x0000, 0x0000, 0x5400, 0x5400, 0x5400, 0x6C00, 0x2800, 0x2800, 0x0000, 0x0000, // w
0x0000, 0x0000, 0x4400, 0x2800, 0x1000, 0x1000, 0x2800, 0x4400, 0x0000, 0x0000, // x
0x0000, 0x0000, 0x4400, 0x4400, 0x2800, 0x2800, 0x1000, 0x1000, 0x1000, 0x6000, // y
0x0000, 0x0000, 0x7C00, 0x0800, 0x1000, 0x2000, 0x4000, 0x7C00, 0x0000, 0x0000, // z
0x1800, 0x1000, 0x1000, 0x1000, 0x2000, 0x2000, 0x1000, 0x1000, 0x1000, 0x1800, // {
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, // |
0x3000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x1000, 0x1000, 0x1000, 0x3000, // }
0x0000, 0x0000, 0x0000, 0x7400, 0x4C00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ~
];
// 定义字体结构体
#[derive(Copy, Clone)]
pub struct FontDef {
pub width: u8,
pub height: u8,
pub data: &'static [u16],
}
// 定义字体实例
pub const FONT_7X10: FontDef = FontDef {
width: 7,
height: 10,
data: &FONT7X10,
};