rust: Generalize SPI peripheral

This commit is contained in:
Wladimir J. van der Laan 2019-05-18 18:14:26 +00:00
parent f69a77eb84
commit 13b91fe993
6 changed files with 393 additions and 316 deletions

View File

@ -8,12 +8,13 @@ use k210_hal::pac;
use k210_hal::prelude::*;
use k210_hal::stdout::Stdout;
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT,MSA300_SLV_ADDR,MSA300_ADDR_BITS,MSA300_CLK};
use k210_shared::board::lcd;
use k210_shared::board::lcd::{LCD,self};
use k210_shared::board::lcd_colors;
use k210_shared::board::msa300;
use k210_shared::soc::fpioa;
use k210_shared::soc::i2c;
use k210_shared::soc::sleep::usleep;
use k210_shared::soc::spi::SPIExt;
use k210_shared::soc::sysctl;
use libm::F32Ext;
use riscv_rt::entry;
@ -75,9 +76,11 @@ fn main() -> ! {
io_mux_init();
io_set_power();
lcd::init();
lcd::set_direction(lcd::direction::YX_LRUD);
lcd::clear(lcd_colors::PURPLE);
let spi = p.SPI0.constrain();
let lcd = LCD::new(spi);
lcd.init();
lcd.set_direction(lcd::direction::YX_LRUD);
lcd.clear(lcd_colors::PURPLE);
let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
@ -105,7 +108,7 @@ fn main() -> ! {
}
}
lcd::draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
// usleep(10000);
}
}

View File

@ -8,12 +8,13 @@ use k210_hal::pac;
use k210_hal::prelude::*;
use k210_hal::stdout::Stdout;
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT,NS2009_SLV_ADDR,NS2009_CAL,NS2009_ADDR_BITS,NS2009_CLK};
use k210_shared::board::lcd;
use k210_shared::board::lcd::{LCD,self};
use k210_shared::board::lcd_colors;
use k210_shared::board::ns2009::TouchScreen;
use k210_shared::soc::fpioa;
use k210_shared::soc::i2c;
use k210_shared::soc::sleep::usleep;
use k210_shared::soc::spi::SPIExt;
use k210_shared::soc::sysctl;
use riscv_rt::entry;
@ -153,9 +154,11 @@ fn main() -> ! {
io_mux_init();
io_set_power();
lcd::init();
lcd::set_direction(lcd::direction::YX_LRUD);
lcd::clear(lcd_colors::PURPLE);
let spi = p.SPI0.constrain();
let lcd = LCD::new(spi);
lcd.init();
lcd.set_direction(lcd::direction::YX_LRUD);
lcd.clear(lcd_colors::PURPLE);
let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
@ -205,7 +208,7 @@ fn main() -> ! {
}
}
}
lcd::draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
universe.iterate();
}

View File

@ -14,10 +14,11 @@ use k210_hal::pac;
use k210_hal::prelude::*;
use k210_hal::stdout::Stdout;
use k210_shared::board::def::io;
use k210_shared::board::lcd;
use k210_shared::board::lcd::{LCD,self};
use k210_shared::board::lcd_colors;
use k210_shared::soc::fpioa;
use k210_shared::soc::sleep::usleep;
use k210_shared::soc::spi::SPIExt;
use k210_shared::soc::sysctl;
use riscv_rt::entry;
@ -114,9 +115,11 @@ fn main() -> ! {
.unwrap();
/* LCD init */
lcd::init();
lcd::set_direction(lcd::direction::YX_RLDU);
lcd::clear(lcd_colors::PURPLE);
let spi = p.SPI0.constrain();
let lcd = LCD::new(spi);
lcd.init();
lcd.set_direction(lcd::direction::YX_RLDU);
lcd.clear(lcd_colors::PURPLE);
let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
let mut console: Console = Console::new();
@ -192,7 +195,7 @@ fn main() -> ! {
}
console.render(&mut image);
lcd::draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
writeln!(stdout, "test {}", frame).unwrap();
usleep(1_000_000);

View File

@ -6,7 +6,7 @@ use pac::spi0::spi_ctrlr0;
use crate::soc::gpio;
use crate::soc::gpiohs;
use crate::soc::sleep::usleep;
use crate::soc::spi;
use crate::soc::spi::SPI;
pub const SPI_SLAVE_SELECT: u32 = 3;
pub const DCX_GPIONUM: u8 = 2;
@ -111,36 +111,45 @@ pub enum direction {
pub const DIR_XY_MASK: u8 = 0x20;
pub const DIR_MASK: u8 = 0xE0;
pub struct LCD<SPI> {
spi: SPI,
}
impl<X: SPI> LCD<X> {
pub fn new(spi: X) -> Self {
Self { spi }
}
/* Low-level functions */
fn init_dcx() {
fn init_dcx(&self) {
gpiohs::set_direction(DCX_GPIONUM, gpio::direction::OUTPUT);
gpiohs::set_pin(DCX_GPIONUM, true);
}
fn set_dcx_control() {
fn set_dcx_control(&self) {
gpiohs::set_pin(DCX_GPIONUM, false);
}
fn set_dcx_data() {
fn set_dcx_data(&self) {
gpiohs::set_pin(DCX_GPIONUM, true);
}
fn init_rst() {
fn init_rst(&self) {
gpiohs::set_direction(RST_GPIONUM, gpio::direction::OUTPUT);
gpiohs::set_pin(RST_GPIONUM, true);
}
fn set_rst(val: bool) {
fn set_rst(&self, val: bool) {
gpiohs::set_pin(RST_GPIONUM, val);
}
pub fn hard_init() {
init_dcx();
init_rst();
set_rst(false);
spi::clk_init();
spi::init(
pub fn hard_init(&self) {
self.init_dcx();
self.init_rst();
self.set_rst(false);
self.spi.set_clk_rate(10000000);
self.spi.configure(
ctrlr0::WORK_MODEW::MODE0,
ctrlr0::FRAME_FORMATW::OCTAL,
8,
@ -151,13 +160,12 @@ pub fn hard_init() {
spi_ctrlr0::AITMW::AS_FRAME_FORMAT,
ctrlr0::TMODW::TRANS,
);
spi::set_clk_rate(10000000);
set_rst(true);
self.set_rst(true);
}
pub fn write_command(cmd: command) {
set_dcx_control();
spi::init(
pub fn write_command(&self, cmd: command) {
self.set_dcx_control();
self.spi.configure(
ctrlr0::WORK_MODEW::MODE0,
ctrlr0::FRAME_FORMATW::OCTAL,
8,
@ -168,12 +176,12 @@ pub fn write_command(cmd: command) {
spi_ctrlr0::AITMW::AS_FRAME_FORMAT,
ctrlr0::TMODW::TRANS,
);
spi::send_data(SPI_SLAVE_SELECT, &[cmd as u8]);
self.spi.send_data(SPI_SLAVE_SELECT, &[cmd as u8]);
}
pub fn write_byte(data_buf: &[u8]) {
set_dcx_data();
spi::init(
pub fn write_byte(&self, data_buf: &[u8]) {
self.set_dcx_data();
self.spi.configure(
ctrlr0::WORK_MODEW::MODE0,
ctrlr0::FRAME_FORMATW::OCTAL,
8,
@ -184,12 +192,12 @@ pub fn write_byte(data_buf: &[u8]) {
spi_ctrlr0::AITMW::AS_FRAME_FORMAT,
ctrlr0::TMODW::TRANS,
);
spi::send_data(SPI_SLAVE_SELECT, data_buf);
self.spi.send_data(SPI_SLAVE_SELECT, data_buf);
}
pub fn write_half(data_buf: &[u16]) {
set_dcx_data();
spi::init(
pub fn write_half(&self, data_buf: &[u16]) {
self.set_dcx_data();
self.spi.configure(
ctrlr0::WORK_MODEW::MODE0,
ctrlr0::FRAME_FORMATW::OCTAL,
16,
@ -200,12 +208,12 @@ pub fn write_half(data_buf: &[u16]) {
spi_ctrlr0::AITMW::AS_FRAME_FORMAT,
ctrlr0::TMODW::TRANS,
);
spi::send_data(SPI_SLAVE_SELECT, data_buf);
self.spi.send_data(SPI_SLAVE_SELECT, data_buf);
}
pub fn write_word(data_buf: &[u32]) {
set_dcx_data();
spi::init(
pub fn write_word(&self, data_buf: &[u32]) {
self.set_dcx_data();
self.spi.configure(
ctrlr0::WORK_MODEW::MODE0,
ctrlr0::FRAME_FORMATW::OCTAL,
32,
@ -216,12 +224,12 @@ pub fn write_word(data_buf: &[u32]) {
spi_ctrlr0::AITMW::AS_FRAME_FORMAT,
ctrlr0::TMODW::TRANS,
);
spi::send_data(SPI_SLAVE_SELECT, data_buf);
self.spi.send_data(SPI_SLAVE_SELECT, data_buf);
}
pub fn fill_data(data: u32, length: usize) {
set_dcx_data();
spi::init(
pub fn fill_data(&self, data: u32, length: usize) {
self.set_dcx_data();
self.spi.configure(
ctrlr0::WORK_MODEW::MODE0,
ctrlr0::FRAME_FORMATW::OCTAL,
32,
@ -232,29 +240,29 @@ pub fn fill_data(data: u32, length: usize) {
spi_ctrlr0::AITMW::AS_FRAME_FORMAT,
ctrlr0::TMODW::TRANS,
);
spi::fill_data(SPI_SLAVE_SELECT, data, length);
self.spi.fill_data(SPI_SLAVE_SELECT, data, length);
}
/* High-level functions */
pub fn init() {
hard_init();
pub fn init(&self) {
self.hard_init();
/*soft reset*/
write_command(command::SOFTWARE_RESET);
self.write_command(command::SOFTWARE_RESET);
usleep(100000);
/*exit sleep*/
write_command(command::SLEEP_OFF);
self.write_command(command::SLEEP_OFF);
usleep(100000);
/*pixel format*/
write_command(command::PIXEL_FORMAT_SET);
write_byte(&[0x55]);
set_direction(direction::XY_LRUD);
self.write_command(command::PIXEL_FORMAT_SET);
self.write_byte(&[0x55]);
self.set_direction(direction::XY_LRUD);
/*display on*/
write_command(command::DISPLAY_ON);
self.write_command(command::DISPLAY_ON);
}
pub fn set_direction(dir: direction) {
pub fn set_direction(&self, dir: direction) {
/* No support for YX orientations right now --
lcd_ctl.dir = dir;
if (dir & DIR_XY_MASK)
@ -269,40 +277,41 @@ pub fn set_direction(dir: direction) {
}
*/
write_command(command::MEMORY_ACCESS_CTL);
write_byte(&[dir as u8]);
self.write_command(command::MEMORY_ACCESS_CTL);
self.write_byte(&[dir as u8]);
}
pub fn set_area(x1: u16, y1: u16, x2: u16, y2: u16) {
write_command(command::HORIZONTAL_ADDRESS_SET);
write_byte(&[
pub fn set_area(&self, x1: u16, y1: u16, x2: u16, y2: u16) {
self.write_command(command::HORIZONTAL_ADDRESS_SET);
self.write_byte(&[
(x1 >> 8) as u8,
(x1 & 0xff) as u8,
(x2 >> 8) as u8,
(x2 & 0xff) as u8,
]);
write_command(command::VERTICAL_ADDRESS_SET);
write_byte(&[
self.write_command(command::VERTICAL_ADDRESS_SET);
self.write_byte(&[
(y1 >> 8) as u8,
(y1 & 0xff) as u8,
(y2 >> 8) as u8,
(y2 & 0xff) as u8,
]);
write_command(command::MEMORY_WRITE);
self.write_command(command::MEMORY_WRITE);
}
pub fn clear(color: u16) {
pub fn clear(&self, color: u16) {
let data = ((color as u32) << 16) | (color as u32);
//set_area(0, 0, lcd_ctl.width, lcd_ctl.height);
set_area(0, 0, LCD_X_MAX - 1, LCD_Y_MAX - 1);
fill_data(data, (LCD_X_MAX as usize) * (LCD_Y_MAX as usize) / 2);
self.set_area(0, 0, LCD_X_MAX - 1, LCD_Y_MAX - 1);
self.fill_data(data, (LCD_X_MAX as usize) * (LCD_Y_MAX as usize) / 2);
}
pub fn draw_picture(x1: u16, y1: u16, width: u16, height: u16, data: &[u32]) {
set_area(x1, y1, x1 + width - 1, y1 + height - 1);
pub fn draw_picture(&self, x1: u16, y1: u16, width: u16, height: u16, data: &[u32]) {
self.set_area(x1, y1, x1 + width - 1, y1 + height - 1);
assert!(data.len() == (width as usize) * (height as usize) / 2);
write_word(data);
self.write_word(data);
}
}

View File

@ -1,19 +1,73 @@
use core::cmp;
use core::ops::Deref;
use k210_hal::pac;
use pac::{SPI0,SPI1,spi0};
use pac::spi0::ctrlr0;
use pac::spi0::spi_ctrlr0;
use crate::soc::sysctl;
pub fn clk_init() {
sysctl::clock_enable(sysctl::clock::SPI0);
sysctl::clock_set_threshold(sysctl::threshold::SPI0, 0);
unsafe {
(*pac::SPI0::ptr()).baudr.write(|w| w.bits(0x14)); // Set baudrate to some default
/// Extension trait that constrains SPI peripherals
pub trait SPIExt: Sized {
/// Constrains SPI peripheral so it plays nicely with the other abstractions
fn constrain(self) -> SPIImpl<Self>;
}
/// Trait for generalizing over SPI0 and SPI1 (SPI2 is slave-only and SPI3 is !!!special!!!)
pub trait SPI01: Deref<Target = spi0::RegisterBlock> {
#[doc(hidden)]
const CLK: sysctl::clock;
#[doc(hidden)]
const DIV: sysctl::threshold;
}
impl SPI01 for SPI0 {
const CLK: sysctl::clock = sysctl::clock::SPI0;
const DIV: sysctl::threshold = sysctl::threshold::SPI0;
}
impl SPI01 for SPI1 {
const CLK: sysctl::clock = sysctl::clock::SPI1;
const DIV: sysctl::threshold = sysctl::threshold::SPI1;
}
impl<SPI: SPI01> SPIExt for SPI {
fn constrain(self) -> SPIImpl<SPI> {
SPIImpl::<SPI>::new(self)
}
}
pub fn init(
pub struct SPIImpl<IF> {
spi: IF,
}
pub trait SPI {
fn configure(
&self,
work_mode: ctrlr0::WORK_MODEW,
frame_format: ctrlr0::FRAME_FORMATW,
data_bit_length: u8,
endian: u32,
instruction_length: u8,
address_length: u8,
wait_cycles: u8,
instruction_address_trans_mode: spi_ctrlr0::AITMW,
tmod: ctrlr0::TMODW,
);
fn set_clk_rate(&self, spi_clk: u32) -> u32;
fn send_data<X: Into<u32> + Copy>(&self, chip_select: u32, tx: &[X]);
fn fill_data(&self, chip_select: u32, value: u32, tx_len: usize);
}
impl<IF: SPI01> SPIImpl<IF> {
pub fn new(spi: IF) -> Self {
Self { spi }
}
}
impl<IF: SPI01> SPI for SPIImpl<IF> {
/// Configure SPI transaction
fn configure(
&self,
work_mode: ctrlr0::WORK_MODEW,
frame_format: ctrlr0::FRAME_FORMATW,
data_bit_length: u8,
@ -38,14 +92,13 @@ pub fn init(
let addr_l: u8 = address_length / 4;
unsafe {
let ptr = pac::SPI0::ptr();
(*ptr).imr.write(|w| w.bits(0x00));
(*ptr).dmacr.write(|w| w.bits(0x00));
(*ptr).dmatdlr.write(|w| w.bits(0x10));
(*ptr).dmardlr.write(|w| w.bits(0x00));
(*ptr).ser.write(|w| w.bits(0x00));
(*ptr).ssienr.write(|w| w.bits(0x00));
(*ptr).ctrlr0.write(|w| {
self.spi.imr.write(|w| w.bits(0x00));
self.spi.dmacr.write(|w| w.bits(0x00));
self.spi.dmatdlr.write(|w| w.bits(0x10));
self.spi.dmardlr.write(|w| w.bits(0x00));
self.spi.ser.write(|w| w.bits(0x00));
self.spi.ssienr.write(|w| w.bits(0x00));
self.spi.ctrlr0.write(|w| {
w.work_mode()
.variant(work_mode)
.tmod()
@ -55,7 +108,7 @@ pub fn init(
.data_length()
.bits(data_bit_length - 1)
});
(*ptr).spi_ctrlr0.write(|w| {
self.spi.spi_ctrlr0.write(|w| {
w.aitm()
.variant(instruction_address_trans_mode)
.addr_length()
@ -65,69 +118,72 @@ pub fn init(
.wait_cycles()
.bits(wait_cycles)
});
(*ptr).endian.write(|w| w.bits(endian));
self.spi.endian.write(|w| w.bits(endian));
}
}
pub fn set_clk_rate(spi_clk: u32) -> u32 {
/// Set SPI clock rate
fn set_clk_rate(&self, spi_clk: u32) -> u32 {
sysctl::clock_enable(IF::CLK);
sysctl::clock_set_threshold(IF::DIV, 0);
let clock_freq: u32 = sysctl::clock_get_freq(sysctl::clock::SPI0);
let spi_baudr = clock_freq / spi_clk;
// Clamp baudrate divider to valid range
let spi_baudr = cmp::min(cmp::max(spi_baudr, 2), 65534);
unsafe {
(*pac::SPI0::ptr()).baudr.write(|w| w.bits(spi_baudr));
self.spi.baudr.write(|w| w.bits(spi_baudr));
}
clock_freq / spi_baudr
}
pub fn send_data<X: Into<u32> + Copy>(chip_select: u32, tx: &[X]) {
/// Send arbitrary data
fn send_data<X: Into<u32> + Copy>(&self, chip_select: u32, tx: &[X]) {
unsafe {
let ptr = pac::SPI0::ptr();
(*ptr).ser.write(|w| w.bits(1 << chip_select));
(*ptr).ssienr.write(|w| w.bits(0x01));
self.spi.ser.write(|w| w.bits(1 << chip_select));
self.spi.ssienr.write(|w| w.bits(0x01));
// TODO: write this using iterators / slices
let mut i = 0;
let mut tx_len = tx.len();
while tx_len != 0 {
let fifo_len = (32 - (*ptr).txflr.read().bits()) as usize;
let fifo_len = (32 - self.spi.txflr.read().bits()) as usize;
let fifo_len = cmp::min(fifo_len, tx_len);
for _ in 0..fifo_len {
(*ptr).dr[0].write(|f| f.bits(tx[i].into()));
self.spi.dr[0].write(|f| f.bits(tx[i].into()));
i += 1;
}
tx_len -= fifo_len;
}
while ((*ptr).sr.read().bits() & 0x05) != 0x04 {
while (self.spi.sr.read().bits() & 0x05) != 0x04 {
// IDLE
}
(*ptr).ser.write(|w| w.bits(0x00));
(*ptr).ssienr.write(|w| w.bits(0x00));
self.spi.ser.write(|w| w.bits(0x00));
self.spi.ssienr.write(|w| w.bits(0x00));
}
}
pub fn fill_data(chip_select: u32, value: u32, mut tx_len: usize) {
/// Send repeated data
fn fill_data(&self, chip_select: u32, value: u32, mut tx_len: usize) {
unsafe {
let ptr = pac::SPI0::ptr();
(*ptr).ser.write(|w| w.bits(1 << chip_select));
(*ptr).ssienr.write(|w| w.bits(0x01));
self.spi.ser.write(|w| w.bits(1 << chip_select));
self.spi.ssienr.write(|w| w.bits(0x01));
while tx_len != 0 {
let fifo_len = (32 - (*ptr).txflr.read().bits()) as usize;
let fifo_len = (32 - self.spi.txflr.read().bits()) as usize;
let fifo_len = cmp::min(fifo_len, tx_len);
for _ in 0..fifo_len {
(*ptr).dr[0].write(|f| f.bits(value));
self.spi.dr[0].write(|f| f.bits(value));
}
tx_len -= fifo_len;
}
while ((*ptr).sr.read().bits() & 0x05) != 0x04 {
while (self.spi.sr.read().bits() & 0x05) != 0x04 {
// IDLE
}
(*ptr).ser.write(|w| w.bits(0x00));
(*ptr).ssienr.write(|w| w.bits(0x00));
self.spi.ser.write(|w| w.bits(0x00));
self.spi.ssienr.write(|w| w.bits(0x00));
}
}
}

View File

@ -10,10 +10,11 @@ use k210_hal::pac;
use k210_hal::prelude::*;
use k210_hal::stdout::Stdout;
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT};
use k210_shared::board::lcd;
use k210_shared::board::lcd::{LCD,self};
use k210_shared::board::lcd_colors;
use k210_shared::soc::fpioa;
use k210_shared::soc::sleep::usleep;
use k210_shared::soc::spi::SPIExt;
use k210_shared::soc::sysctl;
use riscv_rt::entry;
@ -79,9 +80,11 @@ fn main() -> ! {
io_mux_init();
io_set_power();
lcd::init();
lcd::set_direction(lcd::direction::YX_RLDU);
lcd::clear(lcd_colors::PURPLE);
let spi = p.SPI0.constrain();
let lcd = LCD::new(spi);
lcd.init();
lcd.set_direction(lcd::direction::YX_RLDU);
lcd.clear(lcd_colors::PURPLE);
let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
@ -96,7 +99,7 @@ fn main() -> ! {
ofs += 1;
}
}
lcd::draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
frame += 1;
zoom *= 0.98f32;