mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 10:51:30 +11:00
Add Serial and GPIO registers and implement embedded_hal traits (#96)
* Add Serial and GPIO registers and implement embedded_hal traits Use VolAddress and phantom_fields to populate the SIOCNT, RCNT, and SIODATA8 registers. Implement embedded_hal serial traits around an empty SioSerial struct. Hide serial read and write traits behind a "serial" feature flag to make embedded-hal and nb dependencies optional. * UART echo example Enable the serial feature for this example. Provide a pinout diagram to assist people with wiring up a USB to UART adapter.
This commit is contained in:
parent
b354dca479
commit
51c870281c
10
Cargo.toml
10
Cargo.toml
|
@ -11,13 +11,23 @@ license = "Apache-2.0"
|
|||
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serial = ["embedded-hal", "nb"]
|
||||
|
||||
[dependencies]
|
||||
typenum = "1.10"
|
||||
voladdress = "0.2"
|
||||
gba-proc-macro = "0.5"
|
||||
embedded-hal = { version = "0.2.4", optional = true }
|
||||
nb = { version = "1.0.0", optional = true }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = "abort"
|
||||
incremental = false
|
||||
codegen-units = 1
|
||||
|
||||
[[example]]
|
||||
name = "uart_echo"
|
||||
required-features = ["serial"]
|
||||
|
|
31
examples/uart_echo.rs
Normal file
31
examples/uart_echo.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
// _ Link Cable Pinout
|
||||
// ___/ \___ 1: VCC - 3.3V
|
||||
// / \ 2: SO - TX
|
||||
// | 1 3 5 | 3: SI - RX
|
||||
// | 2 4 6 | 4: SD
|
||||
// |_________| 5: SC
|
||||
// 6: GND
|
||||
|
||||
use embedded_hal::prelude::*;
|
||||
use gba::io::sio::{BaudRate, SioSerial};
|
||||
use nb::block;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
let mut serial = SioSerial;
|
||||
SioSerial::init(BaudRate::Bps115200);
|
||||
|
||||
loop {
|
||||
if let Ok(c) = block!(serial.read()) {
|
||||
block!(serial.write(c)).ok();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ pub mod display;
|
|||
pub mod dma;
|
||||
pub mod irq;
|
||||
pub mod keypad;
|
||||
pub mod sio;
|
||||
pub mod sound;
|
||||
pub mod timers;
|
||||
pub mod window;
|
||||
|
|
183
src/io/sio.rs
Normal file
183
src/io/sio.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
//! Contains types and definitions for Serial IO registers.
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Serial IO Control. Read/Write.
|
||||
pub const SIOCNT: VolAddress<SioControlSetting> = unsafe { VolAddress::new(0x400_0128) };
|
||||
|
||||
/// Serial IO Data. Read/Write.
|
||||
pub const SIODATA8: VolAddress<u16> = unsafe { VolAddress::new(0x400_012A) };
|
||||
|
||||
/// General IO Control. Read/Write.
|
||||
pub const RCNT: VolAddress<IoControlSetting> = unsafe { VolAddress::new(0x400_0134) };
|
||||
|
||||
newtype!(
|
||||
/// Setting for the serial IO control register.
|
||||
///
|
||||
/// * 0-1: `BaudRate`
|
||||
/// * 2: Use hardware flow control
|
||||
/// * 3: Use odd parity instead of even
|
||||
/// * 4: TX buffer is full
|
||||
/// * 5: RX buffer is empty
|
||||
/// * 6: Error occurred
|
||||
/// * 7: Use 8-bit data length instead of 7-bit
|
||||
/// * 8: Use hardware FIFO
|
||||
/// * 9: Enable parity check
|
||||
/// * 10: Enable data receive
|
||||
/// * 11: Enable data transmit
|
||||
/// * 12-13: `SioMode`
|
||||
/// * 14: Trigger interrupt on RX
|
||||
SioControlSetting,
|
||||
u16
|
||||
);
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl SioControlSetting {
|
||||
phantom_fields! {
|
||||
self.0: u16,
|
||||
baud_rate: 0-1=BaudRate<Bps9600,Bps38400,Bps57600,Bps115200>,
|
||||
flow_control: 2,
|
||||
parity_odd: 3,
|
||||
tx_full: 4,
|
||||
rx_empty: 5,
|
||||
error: 6,
|
||||
data_length_8bit: 7,
|
||||
fifo_enable:8,
|
||||
parity_enable: 9,
|
||||
tx_enable: 10,
|
||||
rx_enable: 11,
|
||||
mode: 12-13=SioMode<Normal8Bit,MultiPlayer,Normal32Bit,Uart>,
|
||||
irq_enable: 14,
|
||||
}
|
||||
}
|
||||
|
||||
newtype_enum! {
|
||||
/// Supported baud rates.
|
||||
BaudRate = u16,
|
||||
/// * 9600 bps
|
||||
Bps9600 = 0,
|
||||
/// * 38400 bps
|
||||
Bps38400 = 1,
|
||||
/// * 57600 bps
|
||||
Bps57600 = 2,
|
||||
/// * 115200 bps
|
||||
Bps115200 = 3,
|
||||
}
|
||||
|
||||
newtype_enum! {
|
||||
/// Serial IO modes.
|
||||
SioMode = u16,
|
||||
/// * Normal mode: 8-bit data
|
||||
Normal8Bit = 0,
|
||||
/// * Multiplayer mode: 16-bit data
|
||||
MultiPlayer = 1,
|
||||
/// * Normal mode: 32-bit data
|
||||
Normal32Bit = 2,
|
||||
/// * UART (RS232) mode: 7 or 8-bit data
|
||||
Uart = 3,
|
||||
}
|
||||
|
||||
newtype!(
|
||||
/// Setting for the general IO control register.
|
||||
///
|
||||
/// * 0: SC state
|
||||
/// * 1: SD state
|
||||
/// * 2: SI state
|
||||
/// * 3: SO state
|
||||
/// * 4: Set SC as output, instead of input
|
||||
/// * 5: Set SD as output, instead of input
|
||||
/// * 6: Set SI as output, instead of input
|
||||
/// * 7: Set SO as output, instead of input
|
||||
/// * 8: Trigger interrupt on SI change
|
||||
/// * 14-15: `IoMode`
|
||||
IoControlSetting,
|
||||
u16
|
||||
);
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl IoControlSetting {
|
||||
phantom_fields! {
|
||||
self.0: u16,
|
||||
sc: 0,
|
||||
sd: 1,
|
||||
si: 2,
|
||||
so: 3,
|
||||
sc_output_enable: 4,
|
||||
sd_output_enable: 5,
|
||||
si_output_enable: 6,
|
||||
so_output_enable: 7,
|
||||
si_irq_enable: 8,
|
||||
mode: 14-15=IoMode<Disabled,GPIO,JoyBus>,
|
||||
}
|
||||
}
|
||||
|
||||
newtype_enum! {
|
||||
/// General IO modes.
|
||||
IoMode = u16,
|
||||
/// * IO disabled
|
||||
Disabled = 0,
|
||||
/// * General Purpose IO
|
||||
GPIO = 2,
|
||||
/// * JoyBus mode
|
||||
JoyBus = 3,
|
||||
}
|
||||
|
||||
/// Empty stuct that implements embedded_hal traits.
|
||||
#[cfg(feature = "serial")]
|
||||
pub struct SioSerial;
|
||||
|
||||
#[cfg(feature = "serial")]
|
||||
impl SioSerial {
|
||||
/// Initialize SioSerial with provided baud rate and default 8N1 settings.
|
||||
pub fn init(baud: BaudRate) {
|
||||
RCNT.write(IoControlSetting::new());
|
||||
SIOCNT.write(
|
||||
// default settings: 8N1
|
||||
SioControlSetting::new()
|
||||
.with_baud_rate(baud)
|
||||
.with_data_length_8bit(true)
|
||||
.with_mode(SioMode::Uart)
|
||||
.with_rx_enable(true)
|
||||
.with_tx_enable(true),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Serial IO error type.
|
||||
#[cfg(feature = "serial")]
|
||||
pub enum SioError {
|
||||
/// * Error bit in SIOCNT is set
|
||||
ErrorBitSet,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serial")]
|
||||
impl embedded_hal::serial::Read<u8> for SioSerial {
|
||||
type Error = SioError;
|
||||
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
match SIOCNT.read() {
|
||||
siocnt if siocnt.error() => Err(nb::Error::Other(SioError::ErrorBitSet)),
|
||||
siocnt if siocnt.rx_empty() => Err(nb::Error::WouldBlock),
|
||||
_ => Ok(SIODATA8.read() as u8),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serial")]
|
||||
impl embedded_hal::serial::Write<u8> for SioSerial {
|
||||
type Error = SioError;
|
||||
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
self.flush()?;
|
||||
SIODATA8.write(word as u16);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
match SIOCNT.read() {
|
||||
siocnt if siocnt.error() => Err(nb::Error::Other(SioError::ErrorBitSet)),
|
||||
siocnt if siocnt.tx_full() => Err(nb::Error::WouldBlock),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue