Skip to content
This repository was archived by the owner on Aug 9, 2022. It is now read-only.

Commit f6091ec

Browse files
authored
Merge pull request #43 from arjanmels/feature-spi
Added SPI peripheral driver
2 parents bc79010 + ddf5a5d commit f6091ec

File tree

7 files changed

+1146
-3
lines changed

7 files changed

+1146
-3
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ void = { version = "1.0.2", default-features = false }
4343

4444
[dev-dependencies]
4545
panic-halt = "0.2.0"
46+
ili9341 = { version = "0.3.0", features = ["graphics"] }
47+
embedded-graphics = "0.6.2"
4648

4749
[[example]]
4850
name = "alloc"

examples/gpio.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use esp32_hal::{
77
clock_control::sleep,
88
dport::Split,
99
dprintln,
10-
gpio::{Event, Floating, InputPin, OutputPin, Pin, Pull, RTCInputPin, RTCOutputPin},
10+
gpio::{Event, Floating, InputPin, Pin, Pull, RTCInputPin},
1111
interrupt::{Interrupt, InterruptLevel},
1212
prelude::*,
1313
serial::{config::Config, Serial},

examples/spi.rs

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
//! Example of using the SPI interface using the ESP32 WROVER DEVKIT
2+
//!
3+
//! This examples writes to the display. As this pushes many pixels it is quite slow in debug mode,
4+
//! so please run it in release mode to get an impression of the obtainable speed.
5+
6+
#![no_std]
7+
#![no_main]
8+
9+
use core::{fmt::Write, panic::PanicInfo};
10+
11+
use esp32_hal::{
12+
clock_control::{sleep, ClockControl, XTAL_FREQUENCY_AUTO},
13+
dport::Split,
14+
dprintln,
15+
gpio::{InputPin, OutputPin},
16+
prelude::*,
17+
serial::{self, Serial},
18+
spi::{self, SPI},
19+
target,
20+
timer::Timer,
21+
};
22+
23+
use embedded_hal::blocking::spi::WriteIter;
24+
25+
use ili9341;
26+
27+
use embedded_graphics::{
28+
fonts::{Font12x16, Text},
29+
pixelcolor::Rgb565,
30+
prelude::*,
31+
primitives::{Circle, Rectangle},
32+
style::{PrimitiveStyleBuilder, TextStyle},
33+
};
34+
35+
// Interface for ili9341 driver
36+
// ili9341 uses separate command/data pin, this interface set this pin to the appropriate state
37+
struct SPIInterface<
38+
CMD: embedded_hal::digital::v2::OutputPin,
39+
SCLK: OutputPin,
40+
SDO: OutputPin,
41+
SDI: InputPin + OutputPin,
42+
CS: OutputPin,
43+
> {
44+
spi: SPI<esp32::SPI2, SCLK, SDO, SDI, CS>,
45+
cmd: CMD,
46+
}
47+
48+
impl<
49+
CMD: embedded_hal::digital::v2::OutputPin,
50+
SCLK: OutputPin,
51+
SDO: OutputPin,
52+
SDI: InputPin + OutputPin,
53+
CS: OutputPin,
54+
> ili9341::Interface for SPIInterface<CMD, SCLK, SDO, SDI, CS>
55+
{
56+
type Error = esp32_hal::spi::Error;
57+
58+
fn write(&mut self, command: u8, data: &[u8]) -> Result<(), Self::Error> {
59+
self.cmd
60+
.set_low()
61+
.map_err(|_| esp32_hal::spi::Error::PinError)?;
62+
self.spi.write(&[command])?;
63+
self.cmd
64+
.set_high()
65+
.map_err(|_| esp32_hal::spi::Error::PinError)?;
66+
self.spi.write(data)?;
67+
Ok(())
68+
}
69+
70+
fn write_iter(
71+
&mut self,
72+
command: u8,
73+
data: impl IntoIterator<Item = u16>,
74+
) -> Result<(), Self::Error> {
75+
self.cmd
76+
.set_low()
77+
.map_err(|_| esp32_hal::spi::Error::PinError)?;
78+
self.spi.write(&[command])?;
79+
self.cmd
80+
.set_high()
81+
.map_err(|_| esp32_hal::spi::Error::PinError)?;
82+
self.spi.write_iter(data)?;
83+
Ok(())
84+
}
85+
}
86+
87+
#[entry]
88+
fn main() -> ! {
89+
let dp = target::Peripherals::take().expect("Failed to obtain Peripherals");
90+
91+
let (mut dport, dport_clock_control) = dp.DPORT.split();
92+
93+
let clkcntrl = ClockControl::new(
94+
dp.RTCCNTL,
95+
dp.APB_CTRL,
96+
dport_clock_control,
97+
XTAL_FREQUENCY_AUTO,
98+
)
99+
.unwrap();
100+
101+
let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap();
102+
let (_, _, _, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config);
103+
let (_, _, _, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config);
104+
105+
watchdog.disable();
106+
watchdog0.disable();
107+
watchdog1.disable();
108+
109+
let _lock = clkcntrl_config.lock_cpu_frequency();
110+
111+
let pins = dp.GPIO.split();
112+
113+
let mut serial: Serial<_, _, _> = Serial::new(
114+
dp.UART0,
115+
serial::Pins {
116+
tx: pins.gpio1,
117+
rx: pins.gpio3,
118+
cts: None,
119+
rts: None,
120+
},
121+
serial::config::Config {
122+
baudrate: 115200.Hz(),
123+
..serial::config::Config::default()
124+
},
125+
clkcntrl_config,
126+
&mut dport,
127+
)
128+
.unwrap();
129+
130+
// Official ili9341 spec is 10MHz, but overdrive up to 80MHz actually works.
131+
// 26MHz chosen here: will be 26MHz when using 26MHz crystal, 20MHz when using 40MHz crystal,
132+
// due to integer clock division.
133+
// Faster is no use as the cpu is not keeping up with the embedded_graphics library.
134+
let spi: SPI<_, _, _, _, _> = SPI::<esp32::SPI2, _, _, _, _>::new(
135+
dp.SPI2,
136+
spi::Pins {
137+
sclk: pins.gpio19,
138+
sdo: pins.gpio23,
139+
sdi: Some(pins.gpio25),
140+
cs: Some(pins.gpio22),
141+
},
142+
spi::config::Config {
143+
baudrate: 26.MHz().into(),
144+
bit_order: spi::config::BitOrder::MSBFirst,
145+
data_mode: spi::config::MODE_0,
146+
},
147+
clkcntrl_config,
148+
&mut dport,
149+
)
150+
.unwrap();
151+
152+
let mut gpio_backlight = pins.gpio5.into_push_pull_output();
153+
let mut gpio_reset = pins.gpio18.into_push_pull_output();
154+
let gpio_cmd = pins.gpio21.into_push_pull_output();
155+
156+
gpio_reset.set_low().unwrap();
157+
sleep(100.ms());
158+
gpio_reset.set_high().unwrap();
159+
sleep(100.ms());
160+
161+
gpio_backlight.set_low().unwrap();
162+
163+
let spi_if = SPIInterface { spi, cmd: gpio_cmd };
164+
165+
let mut display =
166+
ili9341::Ili9341::new(spi_if, gpio_reset, &mut esp32_hal::delay::Delay::new()).unwrap();
167+
168+
display
169+
.set_orientation(ili9341::Orientation::Landscape)
170+
.unwrap();
171+
172+
Rectangle::new(Point::new(0, 0), Point::new(320, 240))
173+
.into_styled(
174+
PrimitiveStyleBuilder::new()
175+
.fill_color(Rgb565::WHITE)
176+
.stroke_width(4)
177+
.stroke_color(Rgb565::BLUE)
178+
.build(),
179+
)
180+
.draw(&mut display)
181+
.unwrap();
182+
183+
let rect = Rectangle::new(Point::new(10, 80), Point::new(30, 100)).into_styled(
184+
PrimitiveStyleBuilder::new()
185+
.fill_color(Rgb565::RED)
186+
.stroke_width(1)
187+
.stroke_color(Rgb565::WHITE)
188+
.build(),
189+
);
190+
191+
let circle = Circle::new(Point::new(20, 50), 10).into_styled(
192+
PrimitiveStyleBuilder::new()
193+
.fill_color(Rgb565::GREEN)
194+
.stroke_width(1)
195+
.stroke_color(Rgb565::WHITE)
196+
.build(),
197+
);
198+
199+
Text::new("Hello Rust!", Point::new(20, 16))
200+
.into_styled(TextStyle::new(Font12x16, Rgb565::RED))
201+
.draw(&mut display)
202+
.unwrap();
203+
204+
writeln!(serial, "\n\nESP32 Started\n\n").unwrap();
205+
206+
loop {
207+
for x in (0..280).chain((0..280).rev()) {
208+
rect.translate(Point::new(x, 0)).draw(&mut display).unwrap();
209+
}
210+
211+
for x in (0..280).chain((0..280).rev()) {
212+
circle
213+
.translate(Point::new(x, 0))
214+
.draw(&mut display)
215+
.unwrap();
216+
}
217+
}
218+
}
219+
220+
#[panic_handler]
221+
fn panic(info: &PanicInfo) -> ! {
222+
dprintln!("\n\n*** {:?}", info);
223+
loop {}
224+
}

flash

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,18 @@ then
243243
then
244244
printf "\n\n"
245245
# get gen_esp32part.py and create binary partition table
246-
curl -s -S -L $GENPART_SOURCE --output target/gen_esp32part.py
246+
curl -s -S -f --max-time 2 -L $GENPART_SOURCE --output target/gen_esp32part.py_new
247+
248+
if [ $? -ne 0 ]; then
249+
if [ -f target/gen_esp32part.py ]; then
250+
printf "\n${ERROR}Failed to get gen_esp32part.py${RESET} using old one\n\n"
251+
else
252+
printf "\n${ERROR}Failed to get gen_esp32part.py${RESET}\n\n"
253+
exit 1
254+
fi
255+
else
256+
mv target/gen_esp32part.py_new target/gen_esp32part.py
257+
fi
247258

248259
rm target/partitions.bin 2> /dev/null
249260
python target/gen_esp32part.py partitions.csv target/partitions.bin
@@ -254,9 +265,23 @@ then
254265
fi
255266

256267

268+
printf "${STAGE}Getting bootloader... ${RESET}"
269+
257270
# get bootloader.bin file
258271
# (different variants exist, but only difference is flash settings which are overriden by esptool)
259-
curl -s -S -L $BOOTLOADER_SOURCE --output target/bootloader.bin
272+
curl -s -S -f --max-time 2 -L $BOOTLOADER_SOURCE --output target/bootloader.bin_new
273+
274+
if [ $? -ne 0 ]; then
275+
if [ -f target/bootloader.bin ]; then
276+
printf "\n${ERROR}Failed to get bootloader${RESET} using old one\n\n"
277+
else
278+
printf "\n${ERROR}Failed to get bootloader${RESET}\n\n"
279+
exit 1
280+
fi
281+
else
282+
mv target/bootloader.bin_new target/bootloader.bin
283+
printf "succesfully downloader bootloader\n\n"
284+
fi
260285

261286
# check if bootloader.bin and paritions.bin are already correctly flashed (to prevent unnecessary writes)
262287
printf "${STAGE}Verify bootloader and partition table...${RESET} "

src/delay.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//! Implementation of embedded hal delay traits using busy waiting
2+
use crate::units::{MicroSeconds, MilliSeconds};
3+
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
4+
5+
#[derive(Clone)]
6+
pub struct Delay {}
7+
8+
impl Delay {
9+
pub fn new() -> Delay {
10+
Delay {}
11+
}
12+
}
13+
14+
/// Delay in ms
15+
///
16+
/// *Note: Maximum duration is 2e32-1 ns ~ 4.29s *
17+
impl<UXX: Into<u32>> DelayMs<UXX> for Delay {
18+
fn delay_ms(&mut self, ms: UXX) {
19+
crate::clock_control::sleep(MilliSeconds(ms.into()))
20+
}
21+
}
22+
23+
/// Delay in us
24+
///
25+
/// *Note: Maximum duration is 2e32-1 ns ~ 4.29s *
26+
impl<UXX: Into<u32>> DelayUs<UXX> for Delay {
27+
fn delay_us(&mut self, us: UXX) {
28+
crate::clock_control::sleep(MicroSeconds(us.into()))
29+
}
30+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub use proc_macros::ram;
2828

2929
pub mod analog;
3030
pub mod clock_control;
31+
pub mod delay;
3132
pub mod dport;
3233
pub mod efuse;
3334
#[cfg(feature = "external_ram")]
@@ -37,6 +38,7 @@ pub mod gpio;
3738
pub mod interrupt;
3839
pub mod prelude;
3940
pub mod serial;
41+
pub mod spi;
4042
pub mod timer;
4143
pub mod units;
4244

0 commit comments

Comments
 (0)