Skip to content

Commit 014e757

Browse files
authored
Merge pull request #6 from Neotron-Compute/feature/video-output
Add video output
2 parents 1833b4d + e4ef0f5 commit 014e757

6 files changed

Lines changed: 5685 additions & 50 deletions

File tree

.cargo/config.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
[target.thumbv6m-none-eabi]
2+
# This will make a UF2 and copy it to the RP2040's Mass Storage Device bootloader
23
# runner = "elf2uf2-rs -d"
3-
runner = "probe-run-rp --chip RP2040"
4+
# This will flash over SWD with any compatible probe it finds. You need 0.3.1 or higher for RP2040 support.
5+
runner = "probe-run --chip RP2040"
46

57
rustflags = [
68
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x

.github/workflows/rust.yml

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,43 @@ name: Build
22

33
on: [push, pull_request]
44

5+
env:
6+
CARGO_TERM_COLOR: always
7+
58
jobs:
69
build:
7-
8-
runs-on: ubuntu-latest
9-
10+
continue-on-error: ${{ matrix.experimental || false }}
11+
strategy:
12+
matrix:
13+
# All generated code should be running on stable now
14+
rust: [nightly, stable]
15+
include:
16+
# Nightly is only for reference and allowed to fail
17+
- rust: nightly
18+
experimental: true
19+
os:
20+
- ubuntu-latest
21+
- macOS-latest
22+
- windows-latest
23+
runs-on: ${{ matrix.os }}
1024
steps:
11-
- name: Checkout
12-
uses: actions/checkout@v1
13-
with:
14-
submodules: true
15-
- name: Install GCC ARM
16-
run: sudo apt-get update && sudo apt-get install -y gcc-arm-none-eabi
17-
- name: Add Target
18-
run: rustup target add thumbv6m-none-eabi
19-
- name: Build
20-
run: cargo build --verbose
21-
- name: Build Release
22-
run: cargo build --release --verbose
25+
- name: Checkout
26+
uses: actions/checkout@v2
27+
with:
28+
submodules: true
29+
- name: Build
30+
uses: actions-rs/toolchain@v1
31+
with:
32+
profile: minimal
33+
toolchain: ${{ matrix.rust }}
34+
override: true
35+
- name: Install Rust targets for ${{ matrix.rust }}
36+
run: rustup target install --toolchain=${{ matrix.rust }} thumbv6m-none-eabi
37+
- name: Build
38+
run: cargo build --verbose
39+
- name: Build Release
40+
run: cargo build --release --verbose
41+
- name: Clippy
42+
uses: actions-rs/clippy-check@v1
43+
with:
44+
token: ${{ secrets.GITHUB_TOKEN }}

Cargo.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[package]
22
authors = ["Jonathan 'theJPster' Pallant <github@thejpster.org.uk>"]
33
edition = "2018"
4+
resolver = "2"
45
readme = "README.md"
56
license = "GPL-3.0-or-later"
67
name = "neotron-pico-bios"
@@ -18,17 +19,21 @@ neotron-common-bios = { git = "https://github.com/Neotron-Compute/Neotron-Common
1819
# For time keeping/handling
1920
embedded-time = "0.12"
2021
# For the RP2040 bootloader
21-
rp2040-boot2 = "0.1"
22+
rp2040-boot2 = "0.2"
2223
# For hardware abstraction traits
2324
embedded-hal ="0.2"
2425
# Gives us formatted PC-side logging
25-
defmt = "0.2"
26+
defmt = "0.3"
2627
# Sends defmt logs to the SWD debugger
27-
defmt-rtt = "0.2"
28+
defmt-rtt = "0.3"
2829
# Send panics to the debugger
2930
panic-probe = "0.2"
3031
# Fetches the BIOS version from git
3132
git-version = "0.3"
33+
# RP2040 PIO driver
34+
pio = { git = "https://github.com/rp-rs/pio-rs.git", branch = "main" }
35+
# RP2040 PIO driver macros
36+
pio-proc = { git = "https://github.com/rp-rs/pio-rs.git", branch = "main" }
3237

3338
[features]
3439
default = [

src/main.rs

Lines changed: 135 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@
3333
#![no_std]
3434
#![no_main]
3535

36+
// -----------------------------------------------------------------------------
37+
// Sub-modules
38+
// -----------------------------------------------------------------------------
39+
40+
pub mod vga;
41+
3642
// -----------------------------------------------------------------------------
3743
// Imports
3844
// -----------------------------------------------------------------------------
@@ -43,11 +49,20 @@ use defmt_rtt as _;
4349
use embedded_hal::digital::v2::OutputPin;
4450
use embedded_time::rate::*;
4551
use git_version::git_version;
46-
use hal::clocks::Clock;
4752
use panic_probe as _;
48-
use pico;
49-
use pico::hal;
50-
use pico::hal::pac;
53+
use pico::{
54+
self,
55+
hal::{
56+
self,
57+
pac::{self, interrupt},
58+
},
59+
};
60+
61+
// -----------------------------------------------------------------------------
62+
// Types
63+
// -----------------------------------------------------------------------------
64+
65+
// None
5166

5267
// -----------------------------------------------------------------------------
5368
// Static and Const Data
@@ -61,58 +76,111 @@ use pico::hal::pac;
6176
/// See `memory.x` for a definition of the `.boot2` section.
6277
#[link_section = ".boot2"]
6378
#[used]
64-
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
79+
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
6580

6681
/// BIOS version
6782
const GIT_VERSION: &str = git_version!();
6883

69-
// -----------------------------------------------------------------------------
70-
// Types
71-
// -----------------------------------------------------------------------------
72-
73-
// None
84+
/// Create a new Text Console
85+
static TEXT_CONSOLE: vga::TextConsole = vga::TextConsole::new();
7486

7587
// -----------------------------------------------------------------------------
7688
// Functions
7789
// -----------------------------------------------------------------------------
7890

91+
/// Prints to the screen
92+
#[macro_export]
93+
macro_rules! print {
94+
($($arg:tt)*) => {
95+
{
96+
use core::fmt::Write as _;
97+
write!(&TEXT_CONSOLE, $($arg)*).unwrap();
98+
}
99+
};
100+
}
101+
102+
/// Prints to the screen and puts a new-line on the end
103+
#[macro_export]
104+
macro_rules! println {
105+
() => (print!("\n"));
106+
($($arg:tt)*) => {
107+
{
108+
use core::fmt::Write as _;
109+
writeln!(&TEXT_CONSOLE, $($arg)*).unwrap();
110+
}
111+
};
112+
}
113+
79114
/// This is the entry-point to the BIOS. It is called by cortex-m-rt once the
80115
/// `.bss` and `.data` sections have been initialised.
81116
#[entry]
82117
fn main() -> ! {
118+
cortex_m::interrupt::disable();
119+
83120
info!("Neotron BIOS {} starting...", GIT_VERSION);
84121

85122
// Grab the singleton containing all the RP2040 peripherals
86123
let mut pac = pac::Peripherals::take().unwrap();
87124
// Grab the singleton containing all the generic Cortex-M peripherals
88-
let core = pac::CorePeripherals::take().unwrap();
125+
let _core = pac::CorePeripherals::take().unwrap();
126+
127+
// Reset the DMA engine. If we don't do this, starting from probe-run
128+
// (as opposed to a cold-start) is unreliable.
129+
pac.RESETS.reset.modify(|_r, w| w.dma().set_bit());
130+
cortex_m::asm::nop();
131+
pac.RESETS.reset.modify(|_r, w| w.dma().clear_bit());
132+
while pac.RESETS.reset_done.read().dma().bit_is_clear() {}
89133

90134
// Needed by the clock setup
91135
let mut watchdog = hal::watchdog::Watchdog::new(pac.WATCHDOG);
92136

93-
// Get ourselves up to a decent clock speed.
94-
let clocks = hal::clocks::init_clocks_and_plls(
95-
pico::XOSC_CRYSTAL_FREQ,
96-
pac.XOSC,
97-
pac.CLOCKS,
137+
// Run at 126 MHz SYS_PLL, 48 MHz, USB_PLL
138+
139+
let xosc = hal::xosc::setup_xosc_blocking(pac.XOSC, pico::XOSC_CRYSTAL_FREQ.Hz())
140+
.map_err(|_x| false)
141+
.unwrap();
142+
143+
// Configure watchdog tick generation to tick over every microsecond
144+
watchdog.enable_tick_generation((pico::XOSC_CRYSTAL_FREQ / 1_000_000) as u8);
145+
146+
let mut clocks = hal::clocks::ClocksManager::new(pac.CLOCKS);
147+
148+
let pll_sys = hal::pll::setup_pll_blocking(
98149
pac.PLL_SYS,
150+
xosc.operating_frequency().into(),
151+
hal::pll::PLLConfig {
152+
vco_freq: Megahertz(1512),
153+
refdiv: 1,
154+
post_div1: 6,
155+
post_div2: 2,
156+
},
157+
&mut clocks,
158+
&mut pac.RESETS,
159+
)
160+
.map_err(|_x| false)
161+
.unwrap();
162+
163+
let pll_usb = hal::pll::setup_pll_blocking(
99164
pac.PLL_USB,
165+
xosc.operating_frequency().into(),
166+
hal::pll::common_configs::PLL_USB_48MHZ,
167+
&mut clocks,
100168
&mut pac.RESETS,
101-
&mut watchdog,
102169
)
103-
.ok()
170+
.map_err(|_x| false)
104171
.unwrap();
105172

106-
info!("Clocks OK");
173+
clocks
174+
.init_default(&xosc, &pll_sys, &pll_usb)
175+
.map_err(|_x| false)
176+
.unwrap();
107177

108-
// Create an object we can use to busy-wait for specified numbers of
109-
// milliseconds. For this to work, it needs to know our clock speed.
110-
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer());
178+
info!("Clocks OK");
111179

112180
// sio is the *Single-cycle Input/Output* peripheral. It has all our GPIO
113181
// pins, as well as some mailboxes and other useful things for inter-core
114182
// communications.
115-
let sio = hal::sio::Sio::new(pac.SIO);
183+
let mut sio = hal::sio::Sio::new(pac.SIO);
116184

117185
// Configure and grab all the RP2040 pins the Pico exposes.
118186
let pins = pico::Pins::new(
@@ -122,18 +190,54 @@ fn main() -> ! {
122190
&mut pac.RESETS,
123191
);
124192

193+
// Disable power save mode to force SMPS into low-efficiency, low-noise mode.
194+
let mut b_power_save = pins.b_power_save.into_push_pull_output();
195+
b_power_save.set_high().unwrap();
196+
197+
// Give H-Sync, V-Sync and 12 RGB colour pins to PIO0 to output video
198+
let _h_sync = pins.gpio0.into_mode::<hal::gpio::FunctionPio0>();
199+
let _v_sync = pins.gpio1.into_mode::<hal::gpio::FunctionPio0>();
200+
let _red0 = pins.gpio2.into_mode::<hal::gpio::FunctionPio0>();
201+
let _red1 = pins.gpio3.into_mode::<hal::gpio::FunctionPio0>();
202+
let _red2 = pins.gpio4.into_mode::<hal::gpio::FunctionPio0>();
203+
let _red3 = pins.gpio5.into_mode::<hal::gpio::FunctionPio0>();
204+
let _green0 = pins.gpio6.into_mode::<hal::gpio::FunctionPio0>();
205+
let _green1 = pins.gpio7.into_mode::<hal::gpio::FunctionPio0>();
206+
let _green2 = pins.gpio8.into_mode::<hal::gpio::FunctionPio0>();
207+
let _green3 = pins.gpio9.into_mode::<hal::gpio::FunctionPio0>();
208+
let _blue0 = pins.gpio10.into_mode::<hal::gpio::FunctionPio0>();
209+
let _blue1 = pins.gpio11.into_mode::<hal::gpio::FunctionPio0>();
210+
let _blue2 = pins.gpio12.into_mode::<hal::gpio::FunctionPio0>();
211+
let _blue3 = pins.gpio13.into_mode::<hal::gpio::FunctionPio0>();
212+
125213
info!("Pins OK");
126214

127-
// Grab the LED pin
128-
let mut led_pin = pins.led.into_push_pull_output();
215+
vga::init(
216+
pac.PIO0,
217+
pac.DMA,
218+
&mut pac.RESETS,
219+
&mut pac.PPB,
220+
&mut sio.fifo,
221+
&mut pac.PSM,
222+
);
223+
224+
TEXT_CONSOLE.set_text_buffer(unsafe { &mut vga::CHAR_ARRAY });
129225

130-
// Do some blinky so we can see it work.
226+
info!("VGA intialised");
227+
228+
let mut x: u32 = 0;
131229
loop {
132-
debug!("Loop...");
133-
led_pin.set_high().unwrap();
134-
delay.delay_ms(500);
135-
led_pin.set_low().unwrap();
136-
delay.delay_ms(500);
230+
println!("x = {}", x);
231+
x = x.wrapping_add(1);
232+
}
233+
}
234+
235+
/// Called when DMA raises IRQ0; i.e. when a DMA transfer to the pixel FIFO or
236+
/// the timing FIFO has completed.
237+
#[interrupt]
238+
fn DMA_IRQ_0() {
239+
unsafe {
240+
vga::irq();
137241
}
138242
}
139243

0 commit comments

Comments
 (0)