use crate::Bus;
use crate::Error;
pub mod bank;
pub mod config;
use crate::bus::memory_map::*;
pub use bank::*;
pub use config::*;
use std::sync::Mutex;
#[derive(Debug)]
pub struct Gpio<'a> {
bus: &'a Bus,
mode_pin_map: Mutex<u16>,
state_pin_map: Mutex<u16>,
function_pin_map: Mutex<u16>,
prescaler_bank_map: Mutex<u16>,
banks: Mutex<Vec<Bank<'a>>>,
}
impl<'a> Gpio<'a> {
pub fn new(bus: &'a Bus) -> Gpio {
Gpio {
bus,
mode_pin_map: Mutex::new(0x0),
state_pin_map: Mutex::new(0x0),
function_pin_map: Mutex::new(0x0),
prescaler_bank_map: Mutex::new(0x0),
banks: Mutex::new(Bank::new_set(&bus)),
}
}
fn is_pin_valid(pin: u8) -> Result<(), Error> {
if pin > 15 {
return Err(Error::InvalidGpioPin);
}
Ok(())
}
}
impl<'a> Gpio<'a> {
pub fn get_state(&self, pin: u8) -> bool {
Gpio::is_pin_valid(pin).unwrap();
let mut data: [u32; 3] = [0; 3];
self.bus_read(&mut data, 2, 1);
let mask = 0x1 << pin;
let state = (data[2] & mask) >> pin;
match state {
0 => false,
1 => true,
_ => {
panic!("Error retrieving current pin state. Digital value returned was not 0 or 1")
}
}
}
pub fn get_states(&self) -> [bool; 16] {
let mut data: [u32; 3] = [0; 3];
self.bus_read(&mut data, 2, 1);
let mut pins: [bool; 16] = [false; 16];
for i in 0..16 {
let mask = 0x1 << i;
let state = ((data[2] & mask) >> i) as u8;
pins[i] = match state {
0 => false,
1 => true,
_ => panic!(
"Error retrieving current pin state. Digital value returned was not 0 or 1"
),
};
}
pins
}
fn bus_read(&self, buffer: &mut [u32], buffer_length: u32, address_offset: u16) {
buffer[0] = (fpga_address::GPIO + address_offset) as u32;
buffer[1] = buffer_length;
self.bus
.read(unsafe { std::mem::transmute::<&mut [u32], &mut [u8]>(buffer) });
}
}
impl<'a> Gpio<'a> {
pub fn set_config<T>(&self, pin: u8, config: T) -> Result<(), Error>
where
T: PinConfig,
{
Gpio::is_pin_valid(pin)?;
let (value, fpga_address_offset) = config.update_pin_map(pin, self)?;
self.bus_write(value, fpga_address_offset);
Ok(())
}
pub fn set_configs<T>(&self, pins: &[u8], config: T) -> Result<(), Error>
where
T: PinConfig,
{
for pin in pins.iter() {
let (value, fpga_address_offset) = config.update_pin_map(*pin, self)?;
self.bus_write(value, fpga_address_offset);
}
Ok(())
}
fn bus_write(&self, value: u16, address_offset: u16) {
let mut buffer: [u32; 3] = [0; 3];
buffer[0] = (fpga_address::GPIO + address_offset) as u32; buffer[1] = 2; buffer[2] = value as u32;
self.bus
.write(unsafe { std::mem::transmute::<&mut [u32], &mut [u8]>(&mut buffer) });
}
pub fn set_prescaler(&self, bank: usize, prescaler: u16) -> Result<(), Error> {
let mask = 0xF << (4 * bank);
let mut bank_prescaler = self.prescaler_bank_map.lock()?;
*bank_prescaler = prescaler << (4 * bank) | (*bank_prescaler & !mask);
self.bus_write(*bank_prescaler, 3);
Ok(())
}
pub fn set_pwm(&self, pin: u8, frequency: f32, percentage: f32) -> Result<(), Error> {
Gpio::is_pin_valid(pin)?;
const GPIO_PRESCALER: u16 = 0x5;
let period_seconds = 1.0 / frequency;
let fpga_clock = self.bus.fpga_frequency;
let period_counter: u32 =
((period_seconds * fpga_clock as f32) / ((1 << GPIO_PRESCALER) * 2) as f32) as u32;
let duty_counter = ((period_counter as f32 * percentage) / 100.0) as u16;
let bank = (pin / 4) as u16;
let channel = (pin % 4) as u16;
self.set_prescaler(bank as usize, GPIO_PRESCALER)?;
let bank = &self.banks.lock()?[0];
bank.set_period(period_counter as u16);
bank.set_duty(channel, duty_counter);
Ok(())
}
pub fn set_servo_angle(&self, pin: u8, angle: u32, min_pulse_ms: f32) -> Result<(), Error> {
Gpio::is_pin_valid(pin)?;
let mut min_pulse_ms = min_pulse_ms;
if min_pulse_ms > 1.5 {
min_pulse_ms = 1.5;
} else if min_pulse_ms < 0.0 {
min_pulse_ms = 0.0;
}
const GPIO_PRESCALER: u16 = 0x5;
const PERIOD_SECONDS: f32 = 0.02;
let period_counter: u32 = ((PERIOD_SECONDS * self.bus.fpga_frequency as f32)
/ (((1 << GPIO_PRESCALER) * 2) as f32)) as u32;
let servo_middle = (period_counter as f32 * 0.075) as u32;
let servo_offset = (period_counter as f32 * (min_pulse_ms / 20.0)) as u32;
let servo_ratio = (servo_middle - servo_offset) / 90;
let duty_counter = (servo_ratio * angle as u32) + servo_offset;
let bank = pin / 4;
let channel = pin % 4;
self.set_prescaler(bank as usize, GPIO_PRESCALER)?;
let bank = &self.banks.lock()?[0];
bank.set_period(period_counter as u16);
bank.set_duty(channel as u16, duty_counter as u16);
Ok(())
}
}