Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/sysctrl spi driver #86

Merged
merged 6 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion examples/headsail-bsp/src/sysctrl/udma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pub mod uart;
use core::marker::PhantomData;

use crate::pac;
pub use spim::UdmaSpim;
pub use uart::UdmaUart;

/// Type-state trait for uDMA peripherals in different states
pub trait UdmaPeriphState {}

Expand All @@ -19,12 +19,16 @@ pub struct Udma<'u>(pub &'u pac::sysctrl::Udma);

pub struct UdmaParts<'u> {
pub uart: UdmaUart<'u, Disabled>,
pub spim: UdmaSpim<'u, Disabled>,
}

impl<'u> Udma<'u> {
pub fn split(self) -> UdmaParts<'u> {
UdmaParts {
uart: UdmaUart::<Disabled>(self.0, PhantomData),
spim: UdmaSpim::<Disabled>(self.0, PhantomData),
}
}
}

pub mod spim;
192 changes: 192 additions & 0 deletions examples/headsail-bsp/src/sysctrl/udma/spim.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
use core::marker::PhantomData;

use super::{Disabled, Enabled};
use crate::pac;

pub const SPI_CMD_CFG: u32 = 0x00000000;
pub const SPI_CMD_SOT: u32 = 0x10000000;
pub const SPI_CMD_EOT: u32 = 0x90000000;
pub const SPI_CMD_SEND_CMD_BASE: u32 = 0x20070000;
pub const SPI_CMD_DUMMY: u32 = 0x400F0000;
pub const SPI_CMD_RX_CHECK: u32 = 0xB0200000;
pub const SPI_CMD_RX_DATA: u32 = 0x74000000;
pub const SPI_CMD_TX_DATA: u32 = 0x64000000;
pub const SPI_CMD_SETUP_UCA: u32 = 0xD0000000;
pub const SPI_CMD_SETUP_UCS: u32 = 0xE0000000;

/// Obtain an instance by calling [Udma::split]
pub struct UdmaSpim<'u, UdmaPeriphState>(
pub(crate) &'u pac::sysctrl::Udma,
pub(crate) PhantomData<UdmaPeriphState>,
);

impl<'u> UdmaSpim<'u, Disabled> {
#[inline]
pub fn enable(self) -> UdmaSpim<'u, Enabled> {
let spim = &self.0;

// Turn on the clock gates for SPIM
spim.ctrl_cfg_cg().modify(|_r, w| w.cg_spim().set_bit());

UdmaSpim::<Enabled>(self.0, PhantomData)
}
}

impl<'u> UdmaSpim<'u, Enabled> {
#[inline]
pub fn disable(self) -> UdmaSpim<'u, Disabled> {
self.0.ctrl_cfg_cg().modify(|_r, w| w.cg_spim().clear_bit());
UdmaSpim::<Disabled>(self.0, PhantomData)
}

#[inline]
pub fn enqueue_tx(&mut self, buf: &[u8]) {
let spim = &self.0;

// Write buffer location & len
spim.spim_tx_saddr()
.write(|w| unsafe { w.bits(buf.as_ptr() as u32) });
spim.spim_tx_size()
.write(|w| unsafe { w.bits(buf.len() as u32) });

// Dispatch transmission
spim.spim_tx_cfg().write(
|w| w.en().set_bit(), // If we want "continuous mode". In continuous mode, uDMA reloads the address and transmits it again
//.continous().set_bit()
);

// Poll until finished (prevents `buf` leakage)
while spim.spim_tx_saddr().read().bits() != 0 {}
}
pub fn enqueue_rx(&mut self, buf: &[u8]) {
let spim = &self.0;

// Write buffer location & len
spim.spim_rx_saddr()
.write(|w| unsafe { w.bits(buf.as_ptr() as u32) });
spim.spim_rx_size()
.write(|w| unsafe { w.bits(buf.len() as u32) });

// Dispatch transmission
spim.spim_rx_cfg().write(
|w| w.en().set_bit(), // If we want "continuous mode". In continuous mode, uDMA reloads the address and transmits it again
//.continous().set_bit()
);

// Poll until finished (prevents `buf` leakage)
while spim.spim_rx_saddr().read().bits() != 0 {}
}

pub fn enqueue_cmd(&mut self, buf: &[u8]) {
let spim = &self.0;

// Write buffer location & len
spim.spim_cmd_saddr()
.write(|w| unsafe { w.bits(buf.as_ptr() as u32) });
spim.spim_cmd_size()
.write(|w| unsafe { w.bits(buf.len() as u32) });

// Dispatch transmission
spim.spim_cmd_cfg().write(
|w| w.en().set_bit(), // If we want "continuous mode". In continuous mode, uDMA reloads the address and transmits it again
//.continous().set_bit()
);

// Poll until finished (prevents `buf` leakage)
while spim.spim_cmd_saddr().read().bits() != 0 {}
}

/// This function sends SOT (Start Of Transmission) command.
pub fn sot(&mut self) {
let sot_cmd: [u8; 4] = SPI_CMD_SOT.to_ne_bytes();
self.enqueue_cmd(&sot_cmd);
}

/// This function sends EOT (End Of Transmission) command .
pub fn eot(&mut self) {
let eot_cmd: [u8; 4] = (SPI_CMD_EOT).to_ne_bytes();
self.enqueue_cmd(&eot_cmd);
}

/// This function sends EOT (End Of Transmission) command but keeps the cs asserted.
pub fn eot_keep_cs(&mut self) {
let eot_cmd: [u8; 4] = (SPI_CMD_EOT | 0x03).to_ne_bytes();
self.enqueue_cmd(&eot_cmd);
}

/// This function sends one dummy byte (0xFF), it should be flixable so that the
/// user can easily choose the number of repetition without using a for loop.
/// the usage for now is:
///
/// # Examples
///
/// ```
/// for _i in 0..10 {
/// spim.sot();
/// spim.send_dummy();
/// }
/// ```
pub fn send_dummy(&mut self) {
let mut buffer: [u8; 4] = [0; 4];
let cmd_cmd: [u8; 4] = (SPI_CMD_SEND_CMD_BASE | 0xFF).to_ne_bytes();

buffer[0..4].copy_from_slice(&cmd_cmd[0..4]);
self.enqueue_cmd(&buffer);
}

/// This function send data out.
/// Use this funtion to transfere data via spi to for example SD card.
///
/// # Examples
///
/// ```
/// let data: [u8; 2] = [0x01,0x02];
/// spim.sot();
/// spim.send(&data);
/// spim.eot();
///
/// ```
pub fn send(&mut self, data: &[u8]) {
let mut cmd_data: [u8; 12] = [0; 12];

cmd_data[0..4].copy_from_slice(
&(SPI_CMD_SETUP_UCA | (data.as_ptr() as u32 & 0x0000FFFF)).to_ne_bytes(),
);
cmd_data[4..8]
.copy_from_slice(&(SPI_CMD_SETUP_UCS | (data.len() - 2) as u32).to_ne_bytes()); // 4 byte but change this to depend on data i.e:((data.len() - 2) as u32)
cmd_data[8..12].copy_from_slice(
&(SPI_CMD_TX_DATA | (data.len() - 1) as u32 | (7 << 16)).to_ne_bytes(),
);

self.enqueue_cmd(&cmd_data);
self.enqueue_tx(data);
}

/// This function receives data.
/// Use this funtion to recive data via spi from for example SD card.
///
/// # Examples
///
/// ```
/// let data: [u8; 2] = [0;2];
/// spim.sot();
/// spim.receive(&data);
/// spim.eot();
///
/// ```
pub fn receive(&mut self, data: &[u8]) {
let mut cmd_data: [u8; 12] = [0; 12];

cmd_data[0..4].copy_from_slice(
&(SPI_CMD_SETUP_UCA | (data.as_ptr() as u32 & 0x0000FFFF)).to_ne_bytes(),
);
cmd_data[4..8]
.copy_from_slice(&(SPI_CMD_SETUP_UCS | (data.len() - 2) as u32).to_ne_bytes());
cmd_data[8..12].copy_from_slice(
&(SPI_CMD_RX_DATA | (data.len() - 1) as u32 | (7 << 16)).to_ne_bytes(),
);

self.enqueue_cmd(&cmd_data);
self.enqueue_rx(data);
}
}
48 changes: 48 additions & 0 deletions examples/sysctrl/hello-sysctrl/examples/spim0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! Test SPIM0 using HAL
//!
//! | Date | Status | Changes |
//! | :- | :-: | :- |
//! | 2024-10-25 | Tested | |
#![no_std]
#![no_main]

use core::arch::asm;
use headsail_bsp::apb_uart::ApbUart0;
use headsail_bsp::{pac::Sysctrl, rt::entry, sysctrl::udma::Udma};

#[entry]
fn main() -> ! {
let sysctrl = unsafe { Sysctrl::steal() };
let udma = Udma(sysctrl.udma());

let (soc_freq, baud) = (30_000_000, 115_200);
let mut uart = ApbUart0::init(soc_freq, baud);

let mut spim = udma.split().spim.enable();
uart.write_str("SPI enabled!\n\r");

let tx_data: [u8; 8] = [0x01, 0x42, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
let mut rx_data: [u8; 8] = [0; 8];

spim.sot();

// Send 8 bytes
spim.send(&tx_data);
spim.eot_keep_cs();
uart.write_str("Data sent!\n\r");
// uart.write(&tx_data);

for _ in 0..10_000 {
unsafe { asm!("nop") }
}

// Receive 8 bytes
spim.receive(&rx_data);
spim.eot();
uart.write_str("Data received!\n\r");
// uart.write(&rx_data);

loop {
continue;
}
}