mirror of
https://github.com/iceHtwoO/novaOS.git
synced 2026-04-17 04:32:27 +00:00
180 lines
4.7 KiB
Rust
180 lines
4.7 KiB
Rust
use core::result::Result;
|
|
use core::result::Result::Ok;
|
|
|
|
use crate::timer::{delay_nops, sleep_ms};
|
|
use crate::{mmio_read, mmio_write};
|
|
|
|
const GPFSEL_BASE: u32 = 0x3F20_0000;
|
|
const GPSET_BASE: u32 = 0x3F20_001C;
|
|
const GPCLR_BASE: u32 = 0x3F20_0028;
|
|
const GPLEV_BASE: u32 = 0x3F20_0034;
|
|
const GPPUD: u32 = 0x3F20_0094;
|
|
const GPPUDCLK_BASE: u32 = 0x3F20_0098;
|
|
const GPREN_BASE: u32 = 0x3F20_004C;
|
|
const GPFEN_BASE: u32 = 0x3F20_0058;
|
|
|
|
#[repr(u8)]
|
|
pub enum SpecificGpio {
|
|
OnboardLed = 29,
|
|
}
|
|
|
|
#[repr(u32)]
|
|
pub enum GPIOFunction {
|
|
Input = 0b000,
|
|
Output = 0b001,
|
|
Alternative0 = 0b100,
|
|
Alternative1 = 0b101,
|
|
Alternative2 = 0b110,
|
|
Alternative3 = 0b111,
|
|
Alternative4 = 0b011,
|
|
Alternative5 = 0b010,
|
|
}
|
|
|
|
/// Set the function of the GPIO pin
|
|
pub fn set_gpio_function(gpio: u8, state: GPIOFunction) -> Result<(), &'static str> {
|
|
let register_index = gpio / 10;
|
|
let register_offset = (gpio % 10) * 3;
|
|
let register_addr = GPFSEL_BASE + (register_index as u32 * 4);
|
|
let current = mmio_read(register_addr);
|
|
|
|
let mask = !(0b111 << register_offset);
|
|
let cleared = current & mask;
|
|
|
|
let new_val = cleared | ((state as u32) << register_offset);
|
|
|
|
mmio_write(register_addr, new_val);
|
|
Ok(())
|
|
}
|
|
|
|
/// Set the GPIO to high
|
|
///
|
|
/// Should be used when GPIO function is set to `OUTPUT` via `set_gpio_function`
|
|
pub fn gpio_high(gpio: u8) -> Result<(), &'static str> {
|
|
let register_index = gpio / 32;
|
|
let register_offset = gpio % 32;
|
|
let register_addr = GPSET_BASE + (register_index as u32 * 4);
|
|
|
|
mmio_write(register_addr, 1 << register_offset);
|
|
Ok(())
|
|
}
|
|
|
|
/// Set the GPIO to low
|
|
///
|
|
/// Should be used when GPIO function is set to `OUTPUT` via `set_gpio_function`
|
|
pub fn gpio_low(gpio: u8) -> Result<(), &'static str> {
|
|
let register_index = gpio / 32;
|
|
let register_offset = gpio % 32;
|
|
let register_addr = GPCLR_BASE + (register_index as u32 * 4);
|
|
|
|
mmio_write(register_addr, 1 << register_offset);
|
|
Ok(())
|
|
}
|
|
|
|
/// Read the current GPIO power state
|
|
pub fn gpio_get_state(gpio: u8) -> u8 {
|
|
let register_index = gpio / 32;
|
|
let register_offset = gpio % 32;
|
|
let register_addr = GPLEV_BASE + (register_index as u32 * 4);
|
|
|
|
let state = mmio_read(register_addr);
|
|
((state >> register_offset) & 0b1) as u8
|
|
}
|
|
|
|
/// Pull GPIO up
|
|
///
|
|
/// Should be used when GPIO function is set to `INPUT` via `set_gpio_function`
|
|
pub fn gpio_pull_up(gpio: u8) {
|
|
gpio_pull_up_down(gpio, 0b10);
|
|
}
|
|
|
|
/// Pull GPIO down
|
|
///
|
|
/// Should be used when GPIO function is set to `INPUT` via `set_gpio_function`
|
|
pub fn gpio_pull_down(gpio: u8) {
|
|
gpio_pull_up_down(gpio, 0b01);
|
|
}
|
|
|
|
fn gpio_pull_up_down(gpio: u8, val: u32) {
|
|
// Determine GPPUDCLK Register
|
|
let register_addr = GPPUDCLK_BASE + 4 * (gpio as u32 / 32);
|
|
let register_offset = gpio % 32;
|
|
|
|
// 1. Write Pull up
|
|
mmio_write(GPPUD, val);
|
|
|
|
// 2. Delay 150 cycles
|
|
delay_nops(150);
|
|
|
|
// 3. Write to clock
|
|
let new_val = 0b1 << register_offset;
|
|
mmio_write(register_addr, new_val);
|
|
|
|
// 4. Delay 150 cycles
|
|
delay_nops(150);
|
|
|
|
// 5. reset GPPUD
|
|
mmio_write(GPPUD, 0);
|
|
|
|
// 6. reset clock
|
|
mmio_write(register_addr, 0);
|
|
}
|
|
|
|
/// Get the current status if falling edge detection is set
|
|
pub fn read_falling_edge_detect(gpio: u8) -> bool {
|
|
let register_addr = GPFEN_BASE + 4 * (gpio as u32 / 32);
|
|
let register_offset = gpio % 32;
|
|
|
|
let current = mmio_read(register_addr);
|
|
((current >> register_offset) & 0b1) != 0
|
|
}
|
|
|
|
/// Get the current status if falling edge detection is set
|
|
pub fn read_rising_edge_detect(gpio: u8) -> bool {
|
|
let register_addr = GPREN_BASE + 4 * (gpio as u32 / 32);
|
|
let register_offset = gpio % 32;
|
|
|
|
let current = mmio_read(register_addr);
|
|
((current >> register_offset) & 0b1) != 0
|
|
}
|
|
|
|
/// Enables falling edge detection
|
|
pub fn set_falling_edge_detect(gpio: u8, enable: bool) {
|
|
let register_addr = GPFEN_BASE + 4 * (gpio as u32 / 32);
|
|
let register_offset = gpio % 32;
|
|
|
|
let current = mmio_read(register_addr);
|
|
let mask = 0b1 << register_offset;
|
|
let new_val = if enable {
|
|
current | mask
|
|
} else {
|
|
current & !mask
|
|
};
|
|
|
|
mmio_write(register_addr, new_val);
|
|
}
|
|
|
|
/// Enables rising edge detection
|
|
pub fn set_rising_edge_detect(gpio: u8, enable: bool) {
|
|
let register_addr = GPREN_BASE + 4 * (gpio as u32 / 32);
|
|
let register_offset = gpio % 32;
|
|
|
|
let current = mmio_read(register_addr);
|
|
|
|
let mask = 0b1 << register_offset;
|
|
let new_val = if enable {
|
|
current | mask
|
|
} else {
|
|
current & !mask
|
|
};
|
|
|
|
mmio_write(register_addr, new_val);
|
|
}
|
|
|
|
pub fn blink_gpio(gpio: u8, duration_ms: u64) {
|
|
let _ = gpio_high(gpio);
|
|
|
|
sleep_ms(duration_ms);
|
|
let _ = gpio_low(gpio);
|
|
sleep_ms(duration_ms);
|
|
}
|