#![allow(clippy::missing_safety_doc)]
const BCM2711_GPIO_REGISTER_COUNT: usize = 58;
const GPIO_MMAP_LEN: usize = BCM2711_GPIO_REGISTER_COUNT * std::mem::size_of::<u32>();
const GPFSEL0: usize = 0x00;
const GPSET0: usize = 0x1c / std::mem::size_of::<u32>();
const GPCLR0: usize = 0x28 / std::mem::size_of::<u32>();
const GPLEV0: usize = 0x34 / std::mem::size_of::<u32>();
#[repr(u32)]
pub enum SaksPins {
Buzzer = 12,
Ds = 6,
Stcp = 13,
Shcp = 19,
Di = 25,
Clk = 5,
}
impl From<SaksPins> for u32 {
fn from(pin_num: SaksPins) -> Self {
pin_num as Self
}
}
#[repr(u32)]
pub enum PinMode {
Input = 0b000,
Output = 0b001,
}
impl From<PinMode> for u32 {
fn from(pin_mode: PinMode) -> Self {
pin_mode as Self
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum VoltageLevel {
Low = 0,
High = 1,
}
impl From<VoltageLevel> for bool {
#[inline]
fn from(level: VoltageLevel) -> Self {
match level {
VoltageLevel::Low => false,
VoltageLevel::High => true,
}
}
}
impl std::ops::Not for VoltageLevel {
type Output = Self;
#[inline]
fn not(self) -> Self {
match self {
Self::Low => Self::High,
Self::High => Self::Low,
}
}
}
pub struct Saks {
mapped_addr: *mut u32,
}
impl Default for Saks {
fn default() -> Self {
Self::new()
}
}
impl Saks {
#[must_use]
pub fn new() -> Self {
let model = std::fs::read_to_string("/sys/firmware/devicetree/base/model").unwrap();
if !model.starts_with("Raspberry Pi 4 Model") {
panic!("only support Raspberry Pi 4");
}
let fd = unsafe { libc::open("/dev/gpiomem\0".as_ptr().cast(), libc::O_RDWR) };
if fd == -1 {
panic!("{}", std::io::Error::last_os_error());
}
let mapped_addr = unsafe {
libc::mmap(
std::ptr::null_mut(),
GPIO_MMAP_LEN,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
0, )
};
if mapped_addr == libc::MAP_FAILED {
panic!("{}", std::io::Error::last_os_error());
}
unsafe {
libc::close(fd);
}
let saks = Self {
mapped_addr: mapped_addr.cast(),
};
saks.init_pins();
saks
}
fn set_mode(&self, pin: SaksPins, pin_mode: PinMode) {
let bcm_pin_num = u32::from(pin);
let gpfsel_index = bcm_pin_num / 10;
let pin_mode_shift = (bcm_pin_num % 10) * 3;
let reg_ptr = unsafe {
self.mapped_addr
.offset(GPFSEL0 as isize + gpfsel_index as isize)
};
let mut reg_val = unsafe { *reg_ptr };
let bit_mask_to_clear_bcm_pin_num_pin_mode = !(0b111 << pin_mode_shift);
reg_val &= bit_mask_to_clear_bcm_pin_num_pin_mode;
reg_val |= (u32::from(pin_mode)) << pin_mode_shift;
unsafe {
*reg_ptr = reg_val;
}
}
pub fn set_level(&self, pin: SaksPins, level: VoltageLevel) {
self.set_is_high_level(pin, bool::from(level));
}
pub fn set_is_high_level(&self, pin: SaksPins, is_high_level: bool) {
let bcm_pin_num = u32::from(pin);
let addr_offset = if is_high_level { GPSET0 } else { GPCLR0 } + bcm_pin_num as usize / 32;
unsafe {
*self.mapped_addr.add(addr_offset) = 1 << (bcm_pin_num % 32);
}
}
pub fn get_level(&self, pin: SaksPins) -> VoltageLevel {
let bcm_pin_num = u32::from(pin);
let addr_offset = GPLEV0 + bcm_pin_num as usize / 32;
let reg_val = unsafe { *self.mapped_addr.add(addr_offset) };
if reg_val & (1 << (bcm_pin_num % 32)) == 0 {
VoltageLevel::Low
} else {
VoltageLevel::High
}
}
pub fn led_row_write_a_byte(&self, byte: u8) {
for bit_mask in 0..8 {
self.set_is_high_level(SaksPins::Ds, (byte >> bit_mask) & 1 == 1);
self.set_level(SaksPins::Shcp, VoltageLevel::Low);
self.set_level(SaksPins::Shcp, VoltageLevel::High);
}
self.set_level(SaksPins::Stcp, VoltageLevel::Low);
self.set_level(SaksPins::Stcp, VoltageLevel::High);
}
fn init_pins(&self) {
self.set_mode(SaksPins::Buzzer, PinMode::Output);
self.set_mode(SaksPins::Ds, PinMode::Output);
self.set_mode(SaksPins::Stcp, PinMode::Output);
self.set_mode(SaksPins::Shcp, PinMode::Output);
self.set_mode(SaksPins::Di, PinMode::Output);
self.set_mode(SaksPins::Clk, PinMode::Output);
}
unsafe fn clear_all(&self) {
*self.mapped_addr.add(GPCLR0) = 0b1111_1111;
*self.mapped_addr.add(GPCLR0 + 1) = 0b1111_1111;
}
pub fn i2c_bus_start(&self) {
self.set_level(SaksPins::Clk, VoltageLevel::High);
self.set_level(SaksPins::Di, VoltageLevel::High);
self.i2c_bus_delay();
self.set_level(SaksPins::Di, VoltageLevel::Low);
self.i2c_bus_delay();
self.set_level(SaksPins::Clk, VoltageLevel::Low);
self.i2c_bus_delay();
}
pub fn i2c_bus_delay(&self) {
std::thread::sleep(std::time::Duration::from_millis(1));
}
pub fn i2c_bus_write_byte(&self, byte: u8) {
for bit_mask in 0..8 {
let di_level = (byte >> bit_mask) & 1 == 1;
self.set_level(SaksPins::Clk, VoltageLevel::Low);
self.i2c_bus_delay();
if di_level {
self.set_level(SaksPins::Di, VoltageLevel::High);
} else {
self.set_level(SaksPins::Di, VoltageLevel::Low);
}
self.i2c_bus_delay();
self.set_level(SaksPins::Clk, VoltageLevel::High);
self.i2c_bus_delay();
}
self.set_level(SaksPins::Clk, VoltageLevel::Low);
self.i2c_bus_delay();
self.set_level(SaksPins::Di, VoltageLevel::High);
self.i2c_bus_delay();
self.set_level(SaksPins::Clk, VoltageLevel::High);
self.i2c_bus_delay();
}
pub fn i2c_bus_write_command(&self, command: u8) {
self.i2c_bus_start();
self.i2c_bus_write_byte(command);
self.i2c_bus_start();
}
}
impl Drop for Saks {
fn drop(&mut self) {
unsafe {
self.clear_all();
self.set_level(SaksPins::Buzzer, VoltageLevel::High);
let ret = libc::munmap(self.mapped_addr.cast(), GPIO_MMAP_LEN);
if ret == -1 {
panic!("{}", std::io::Error::last_os_error());
}
}
}
}