Skip to content

Commit 01cdf55

Browse files
authored
Merge pull request #943 from OYTIS/hstx-support
2 parents 4d56c7d + 125aa54 commit 01cdf55

File tree

7 files changed

+723
-2
lines changed

7 files changed

+723
-2
lines changed

rp235x-hal/src/clocks/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,10 @@ impl ClocksManager {
647647
self.adc_clock
648648
.configure_clock(pll_usb, pll_usb.get_freq())?;
649649

650+
// CLK HSTX = PLL SYS (150MHz) / 1 = 150MHz
651+
self.hstx_clock
652+
.configure_clock(pll_sys, pll_sys.get_freq())?;
653+
650654
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
651655
// Normally choose clk_sys or clk_usb
652656
self.peripheral_clock

rp235x-hal/src/gpio/func.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ pub trait Function: func_sealed::Function {}
2727
pub enum DynFunction {
2828
/// The 'XIP' (or Execute-in-place) function, which means talking to the QSPI Flash.
2929
Xip,
30+
/// The 'HSTX' function.
31+
Hstx,
3032
/// The 'SPI' (or serial-peripheral-interface) function.
3133
Spi,
3234
/// The 'UART' (or serial-port) function.
@@ -107,7 +109,9 @@ macro_rules! pin_func {
107109
})*
108110
};
109111
}
110-
pin_func!(Xip, Spi, Uart, I2c as I2C, Pwm, Pio0, Pio1, Pio2, Clock, XipCs1, Usb, UartAux, Null);
112+
pin_func!(
113+
Xip, Hstx, Spi, Uart, I2c as I2C, Pwm, Pio0, Pio1, Pio2, Clock, XipCs1, Usb, UartAux, Null
114+
);
111115

112116
//==============================================================================
113117
// SIO sub-types
@@ -207,6 +211,9 @@ pin_valid_func!(
207211
[UartAux],
208212
[2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27]
209213
);
214+
215+
pin_valid_func!(bank0 as Gpio, [Hstx], [12, 13, 14, 15, 16, 17, 18, 19]);
216+
210217
pin_valid_func!(bank0 as Gpio, [Clock], [20, 21, 22, 23, 24, 25]);
211218
pin_valid_func!(bank0 as Gpio, [XipCs1], [0, 8, 19, 47]);
212219
pin_valid_func!(qspi as Qspi, [Xip, Null], [Sclk, Sd0, Sd1, Sd2, Sd3, Ss]);

rp235x-hal/src/gpio/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,13 @@ pub unsafe fn new_pin(id: DynPinId) -> Pin<DynPinId, DynFunction, DynPullType> {
198198
.variant()
199199
.expect("Invalid funcsel read from register.");
200200
let function = match funcsel {
201-
FUNCSEL_A::JTAG => DynFunction::Xip,
201+
FUNCSEL_A::JTAG => {
202+
if id.bank == DynBankId::Qspi {
203+
DynFunction::Xip
204+
} else {
205+
DynFunction::Hstx
206+
}
207+
}
202208
FUNCSEL_A::SPI => DynFunction::Spi,
203209
FUNCSEL_A::UART => DynFunction::Uart,
204210
FUNCSEL_A::I2C => DynFunction::I2c,

rp235x-hal/src/gpio/pin.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ pub(crate) fn set_function<P: PinId>(pin: &P, function: DynFunction) {
147147
let funcsel = match function {
148148
// The XIP function has a value of 0, which on bank0 is called JTAG.
149149
DynFunction::Xip => FUNCSEL_A::JTAG,
150+
DynFunction::Hstx => FUNCSEL_A::JTAG,
150151
DynFunction::Spi => FUNCSEL_A::SPI,
151152
DynFunction::Uart => FUNCSEL_A::UART,
152153
DynFunction::I2c => FUNCSEL_A::I2C,

rp235x-hal/src/hstx.rs

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
//! High-speed transmitter (HSTX)
2+
//!
3+
//! See [Section 12.3](https://rptl.io/rp2350-datasheet#section_hstx) for more details.
4+
//!
5+
//! ## Usage
6+
//!
7+
//! ```no_run
8+
//! use rp235x_hal::{
9+
//! self as hal,
10+
//! hstx::{Hstx, HstxPins, HstxBitConfig},
11+
//! Sio,
12+
//! };
13+
//!
14+
//! let mut peripherals = hal::pac::Peripherals::take().unwrap();
15+
//! let sio = Sio::new(peripherals.SIO);
16+
//! let pins = hal::gpio::Pins::new(
17+
//! peripherals.IO_BANK0,
18+
//! peripherals.PADS_BANK0,
19+
//! sio.gpio_bank0,
20+
//! &mut peripherals.RESETS,
21+
//! );
22+
//!
23+
//! let bit0p = pins.gpio12.into_function();
24+
//! let bit3p = pins.gpio15.into_function();
25+
//!
26+
//! let hstx_pins = HstxPins::new().add_bit0_pin(bit0p).add_bit3_pin(bit3p);
27+
//!
28+
//! let mut hstx = Hstx::new(peripherals.HSTX_CTRL, peripherals.HSTX_FIFO, hstx_pins,
29+
//! &mut peripherals.RESETS);
30+
//! hstx.configure_clk(5, 0);
31+
//! hstx.configure_shifter(2, 5);
32+
//! hstx.configure_bit0(HstxBitConfig::Clk { inv: false });
33+
//! hstx.configure_bit3(HstxBitConfig::Shift { sel_p:0, sel_n: 1, inv: false });
34+
//!
35+
//! let mut hstx = hstx.enable();
36+
//!
37+
//! loop {
38+
//! hstx.fifo_write(0xaabbccdd);
39+
//! }
40+
//! ```
41+
42+
use crate::{
43+
dma::{EndlessWriteTarget, WriteTarget},
44+
pac::{dma::ch::ch_ctrl_trig::TREQ_SEL_A, HSTX_CTRL, HSTX_FIFO, RESETS},
45+
resets::SubsystemReset,
46+
typelevel::{Is, OptionTSome, Sealed},
47+
};
48+
use core::marker::PhantomData;
49+
50+
mod pins;
51+
pub use pins::*;
52+
/// State of HSTX
53+
pub trait State: Sealed {}
54+
55+
/// HSTX is disabled, FIFO is not being drained
56+
pub struct Disabled {
57+
__private: (),
58+
}
59+
60+
/// HSTX is enabled, data from FIFO is being consumed
61+
pub struct Enabled {
62+
__private: (),
63+
}
64+
65+
impl State for Disabled {}
66+
impl Sealed for Disabled {}
67+
impl State for Enabled {}
68+
impl Sealed for Enabled {}
69+
70+
/// HSTX peripheral
71+
pub struct Hstx<S: State, P: ValidHstxPinout> {
72+
ctrl: HSTX_CTRL,
73+
fifo: HSTX_FIFO,
74+
pins: P,
75+
state: PhantomData<S>,
76+
}
77+
78+
impl<P: ValidHstxPinout> Hstx<Disabled, P> {
79+
/// Instantiate and reset HSTX peripheral
80+
pub fn new(
81+
ctrl: HSTX_CTRL,
82+
fifo: HSTX_FIFO,
83+
pins: P,
84+
resets: &mut RESETS,
85+
) -> Hstx<Disabled, P> {
86+
ctrl.reset_bring_down(resets);
87+
ctrl.reset_bring_up(resets);
88+
Hstx {
89+
ctrl,
90+
fifo,
91+
pins,
92+
state: PhantomData,
93+
}
94+
}
95+
96+
/// Free HSTX peripheral and get registers and pins back
97+
pub fn free(self) -> (HSTX_CTRL, HSTX_FIFO, P) {
98+
(self.ctrl, self.fifo, self.pins)
99+
}
100+
101+
/// Configure clock generator
102+
pub fn configure_clk(&mut self, div: u8, phase: u8) {
103+
if phase >= 2 * div {
104+
panic!("phase should be strictly less than double the div");
105+
}
106+
107+
self.ctrl
108+
.csr()
109+
.modify(|_, w| unsafe { w.clkdiv().bits(div).clkphase().bits(phase) });
110+
}
111+
112+
/// Configure output shifter
113+
pub fn configure_shifter(&mut self, shift: u8, n_shifts: u8) {
114+
self.ctrl
115+
.csr()
116+
.modify(|_, w| unsafe { w.shift().bits(shift).n_shifts().bits(n_shifts) });
117+
}
118+
119+
/// Configure expand shifter
120+
pub fn configure_expand_shifter(
121+
&mut self,
122+
enc_shift: u8,
123+
enc_n_shifts: u8,
124+
raw_shift: u8,
125+
raw_n_shifts: u8,
126+
) {
127+
self.ctrl.expand_shift().write(|w| unsafe {
128+
w.enc_shift()
129+
.bits(enc_shift)
130+
.enc_n_shifts()
131+
.bits(enc_n_shifts)
132+
.raw_shift()
133+
.bits(raw_shift)
134+
.raw_n_shifts()
135+
.bits(raw_n_shifts)
136+
});
137+
}
138+
139+
/// Configure TMDS expander
140+
pub fn configure_tmds(
141+
&mut self,
142+
l0_rot: u8,
143+
l0_nbits: u8,
144+
l1_rot: u8,
145+
l1_nbits: u8,
146+
l2_rot: u8,
147+
l2_nbits: u8,
148+
) {
149+
self.ctrl.expand_tmds().write(|w| unsafe {
150+
w.l0_rot()
151+
.bits(l0_rot)
152+
.l0_nbits()
153+
.bits(l0_nbits)
154+
.l1_rot()
155+
.bits(l1_rot)
156+
.l1_nbits()
157+
.bits(l1_nbits)
158+
.l2_rot()
159+
.bits(l2_rot)
160+
.l2_nbits()
161+
.bits(l2_nbits)
162+
});
163+
}
164+
165+
/// Enable expander
166+
pub fn enable_expander(&mut self) {
167+
self.ctrl.csr().modify(|_, w| w.expand_en().set_bit());
168+
}
169+
170+
/// Disable expander
171+
pub fn disable_expander(&mut self) {
172+
self.ctrl.csr().modify(|_, w| w.expand_en().clear_bit());
173+
}
174+
175+
/// Enable the output and start shifting data from the FIFO
176+
pub fn enable(self) -> Hstx<Enabled, P> {
177+
self.ctrl.csr().modify(|_, w| w.en().set_bit());
178+
179+
Hstx {
180+
ctrl: self.ctrl,
181+
fifo: self.fifo,
182+
pins: self.pins,
183+
state: PhantomData,
184+
}
185+
}
186+
}
187+
188+
impl<P: ValidHstxPinout> Hstx<Enabled, P> {
189+
/// Disable the output, stop shifting data from the FIFO
190+
pub fn disable(self) -> Hstx<Disabled, P> {
191+
self.ctrl.csr().modify(|_, w| w.en().clear_bit());
192+
193+
Hstx {
194+
ctrl: self.ctrl,
195+
fifo: self.fifo,
196+
pins: self.pins,
197+
state: PhantomData,
198+
}
199+
}
200+
201+
/// Blocking write to FIFO
202+
pub fn fifo_write(&mut self, word: u32) {
203+
while self.fifo_is_full() {}
204+
205+
self.fifo.fifo().write(|w| unsafe { w.bits(word) });
206+
}
207+
208+
/// Check if fifo is empty
209+
pub fn fifo_is_empty(&self) -> bool {
210+
self.fifo.stat().read().empty().bit_is_set()
211+
}
212+
213+
/// Check if fifo is full
214+
pub fn fifo_is_full(&self) -> bool {
215+
self.fifo.stat().read().full().bit_is_set()
216+
}
217+
218+
/// Check if fifo was written while full
219+
pub fn fifo_is_wof(&self) -> bool {
220+
self.fifo.stat().read().wof().bit_is_set()
221+
}
222+
223+
/// Clear "written while full" bit
224+
pub fn clear_fifo_wof(&mut self) {
225+
self.fifo.stat().write(|w| w.wof().clear_bit_by_one());
226+
}
227+
}
228+
229+
// Safety: This only writes to the TX fifo, so it doesn't
230+
// interact with rust-managed memory.
231+
unsafe impl<P: ValidHstxPinout> WriteTarget for Hstx<Enabled, P> {
232+
type TransmittedWord = u32;
233+
234+
fn tx_treq() -> Option<u8> {
235+
Some(TREQ_SEL_A::HSTX.into())
236+
}
237+
238+
fn tx_address_count(&mut self) -> (u32, u32) {
239+
(self.fifo.fifo().as_ptr() as u32, u32::MAX)
240+
}
241+
242+
fn tx_increment(&self) -> bool {
243+
false
244+
}
245+
}
246+
247+
impl<P: ValidHstxPinout> EndlessWriteTarget for Hstx<Enabled, P> {}
248+
249+
/// Configuration of HSTX bit
250+
pub enum HstxBitConfig {
251+
/// Configure a bit as a clock output
252+
Clk {
253+
/// Invert the output
254+
inv: bool,
255+
},
256+
257+
/// Configure a bit to output a bit from the output shifter
258+
Shift {
259+
/// Shift register bit for the first half of HSTX clock cycle
260+
sel_p: u8,
261+
262+
/// Shift register bit for the second half of HSTX clock cycle
263+
sel_n: u8,
264+
265+
/// Invert the output
266+
inv: bool,
267+
},
268+
}
269+
270+
macro_rules! configure_bits_hstx {
271+
( $( $bit:expr ),* ) => {
272+
paste::paste!{
273+
$(
274+
impl<P: ValidHstxPinout, B> Hstx<Disabled, P>
275+
where P::[<Bit $bit>]: Is<Type = OptionTSome<B>>,
276+
B: [<ValidHstxBit $bit Pin>] {
277+
#[doc = "Configure output bit " $bit]
278+
pub fn [<configure_bit$bit>](&mut self, config: HstxBitConfig) {
279+
match config {
280+
HstxBitConfig::Clk{inv: inv} => {
281+
self.ctrl.[<bit $bit>]().write(|w| {
282+
w.clk().set_bit();
283+
if inv {
284+
w.inv().set_bit();
285+
}
286+
w
287+
})
288+
},
289+
HstxBitConfig::Shift{sel_p: sel_p, sel_n: sel_n, inv: inv} => {
290+
self.ctrl.[<bit $bit>]().write(|w| unsafe {
291+
w.sel_p().bits(sel_p)
292+
.sel_n().bits(sel_n);
293+
294+
if inv {
295+
w.inv().set_bit();
296+
}
297+
298+
w
299+
})
300+
}
301+
}
302+
}
303+
}
304+
)*
305+
}
306+
}
307+
}
308+
309+
configure_bits_hstx!(0, 1, 2, 3, 4, 5, 6, 7);
310+
311+
/// "RAW" opcode for expander, OR with length argument to use
312+
pub const EXPANDER_CMD_RAW: u32 = 0;
313+
314+
/// "RAW_REPEAT" opcode for expander shifted to appropriate position, OR with length argument to use
315+
pub const EXPANDER_CMD_RAW_REPEAT: u32 = 1 << 12;
316+
317+
/// "TMDS" opcode for expander shifted to appropriate position, OR with length argument to use
318+
pub const EXPANDER_CMD_TMDS: u32 = 2 << 12;
319+
320+
/// "TMDS_REPEAT" opcode for expander shifted to appropriate position, OR with length argument to use
321+
pub const EXPANDER_CMD_TMDS_REPEAT: u32 = 3 << 12;
322+
323+
/// "NOP" opcode for expander
324+
pub const EXPANDER_CMD_NOP: u32 = 0xf << 12;

0 commit comments

Comments
 (0)