Compare commits
2 Commits
6b6e79ddfc
...
ae5a2444f7
Author | SHA1 | Date |
---|---|---|
Zheng-Jiakun | ae5a2444f7 | |
Zheng-Jiakun | 233d53372e |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -0,0 +1,12 @@
|
||||||
|
# Generated by Yosys 0.9+3521 (git sha1 12132b6850, g++ 9.3.0 -fPIC -Os)
|
||||||
|
|
||||||
|
.model top
|
||||||
|
.inputs key
|
||||||
|
.outputs led
|
||||||
|
.names $false
|
||||||
|
.names $true
|
||||||
|
1
|
||||||
|
.names $undef
|
||||||
|
.names $true led
|
||||||
|
1 1
|
||||||
|
.end
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Project setup
|
||||||
|
PROJ = blinky
|
||||||
|
BUILD = ./build
|
||||||
|
DEVICE = 8k
|
||||||
|
FOOTPRINT = ct256
|
||||||
|
|
||||||
|
# Files
|
||||||
|
FILES = top.v
|
||||||
|
|
||||||
|
.PHONY: all clean burn
|
||||||
|
|
||||||
|
all:
|
||||||
|
# if build folder doesn't exist, create it
|
||||||
|
mkdir -p $(BUILD)
|
||||||
|
# synthesize using Yosys
|
||||||
|
yosys -p "synth_ice40 -top top -blif $(BUILD)/$(PROJ).blif" $(FILES)
|
||||||
|
# Place and route using arachne
|
||||||
|
arachne-pnr -d $(DEVICE) -P $(FOOTPRINT) -o $(BUILD)/$(PROJ).asc -p pinmap.pcf $(BUILD)/$(PROJ).blif
|
||||||
|
# Convert to bitstream using IcePack
|
||||||
|
icepack $(BUILD)/$(PROJ).asc $(BUILD)/$(PROJ).bin
|
||||||
|
|
||||||
|
burn:
|
||||||
|
iceprog $(BUILD)/$(PROJ).bin
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm build/*
|
|
@ -0,0 +1,69 @@
|
||||||
|
# example.pcf
|
||||||
|
set_io --warn-no-port HW_CLK R9
|
||||||
|
|
||||||
|
set_io --warn-no-port LED T15
|
||||||
|
set_io --warn-no-port KEY T16
|
||||||
|
|
||||||
|
# set_io --warn-no-port io0 R6
|
||||||
|
# set_io --warn-no-port io1 T8
|
||||||
|
# set_io --warn-no-port io2 T5
|
||||||
|
# set_io --warn-no-port io3 R9
|
||||||
|
# set_io --warn-no-port io4 R5
|
||||||
|
# set_io --warn-no-port io5 T9
|
||||||
|
# set_io --warn-no-port io6 T3
|
||||||
|
# set_io --warn-no-port io7 R10
|
||||||
|
# set_io --warn-no-port io8 R3
|
||||||
|
# set_io --warn-no-port io9 T10
|
||||||
|
# set_io --warn-no-port io10 T2
|
||||||
|
# set_io --warn-no-port io11 T11
|
||||||
|
# set_io --warn-no-port io12 R2
|
||||||
|
# set_io --warn-no-port io13 T13
|
||||||
|
# set_io --warn-no-port io14 T1
|
||||||
|
# set_io --warn-no-port io15 T14
|
||||||
|
|
||||||
|
|
||||||
|
set_io --warn-no-port ADC_DAT[0] J15
|
||||||
|
set_io --warn-no-port ADC_DAT[1] K16
|
||||||
|
set_io --warn-no-port ADC_DAT[2] K15
|
||||||
|
set_io --warn-no-port ADC_DAT[3] L16
|
||||||
|
set_io --warn-no-port ADC_DAT[4] M16
|
||||||
|
set_io --warn-no-port ADC_DAT[5] M15
|
||||||
|
set_io --warn-no-port ADC_DAT[6] N16
|
||||||
|
set_io --warn-no-port ADC_DAT[7] P16
|
||||||
|
set_io --warn-no-port ADC_CLK P15
|
||||||
|
|
||||||
|
|
||||||
|
set_io --warn-no-port FSMC_ADD[0] A9
|
||||||
|
set_io --warn-no-port FSMC_ADD[1] B9
|
||||||
|
set_io --warn-no-port FSMC_ADD[2] A10
|
||||||
|
set_io --warn-no-port FSMC_ADD[3] C10
|
||||||
|
set_io --warn-no-port FSMC_ADD[4] C9
|
||||||
|
set_io --warn-no-port FSMC_ADD[5] C8
|
||||||
|
set_io --warn-no-port FSMC_ADD[6] C7
|
||||||
|
set_io --warn-no-port FSMC_ADD[7] C11
|
||||||
|
|
||||||
|
set_io --warn-no-port FSMC_DAT[0] B10
|
||||||
|
set_io --warn-no-port FSMC_DAT[1] A11
|
||||||
|
set_io --warn-no-port FSMC_DAT[2] B11
|
||||||
|
set_io --warn-no-port FSMC_DAT[3] B12
|
||||||
|
set_io --warn-no-port FSMC_DAT[4] A1
|
||||||
|
set_io --warn-no-port FSMC_DAT[5] A2
|
||||||
|
set_io --warn-no-port FSMC_DAT[6] C3
|
||||||
|
set_io --warn-no-port FSMC_DAT[7] B3
|
||||||
|
set_io --warn-no-port FSMC_DAT[8] B4
|
||||||
|
set_io --warn-no-port FSMC_DAT[9] A5
|
||||||
|
set_io --warn-no-port FSMC_DAT[10] B5
|
||||||
|
set_io --warn-no-port FSMC_DAT[11] A6
|
||||||
|
set_io --warn-no-port FSMC_DAT[12] B6
|
||||||
|
set_io --warn-no-port FSMC_DAT[13] A7
|
||||||
|
set_io --warn-no-port FSMC_DAT[14] B7
|
||||||
|
set_io --warn-no-port FSMC_DAT[15] B8
|
||||||
|
|
||||||
|
set_io --warn-no-port FSMC_NL C14
|
||||||
|
set_io --warn-no-port FSMC_NWAIT B15
|
||||||
|
set_io --warn-no-port FSMC_NOE B14
|
||||||
|
set_io --warn-no-port FSMC_NWE A15
|
||||||
|
set_io --warn-no-port FSMC_NBL[0] C13
|
||||||
|
set_io --warn-no-port FSMC_NBL[1] C12
|
||||||
|
set_io --warn-no-port FSMC_NE1 A16
|
||||||
|
set_io --warn-no-port FSMC_CLK B13
|
|
@ -0,0 +1,59 @@
|
||||||
|
module top (
|
||||||
|
HW_CLK,
|
||||||
|
LED,
|
||||||
|
KEY,
|
||||||
|
ADC_CLK,
|
||||||
|
ADC_DAT,
|
||||||
|
FSMC_CLK,
|
||||||
|
FSMC_ADD,
|
||||||
|
FSMC_DAT,
|
||||||
|
FSMC_NL,
|
||||||
|
FSMC_NWAIT,
|
||||||
|
FSMC_NOE,
|
||||||
|
FSMC_NWE,
|
||||||
|
FSMC_NBL,
|
||||||
|
FSMC_NE1
|
||||||
|
);
|
||||||
|
|
||||||
|
/* I/O */
|
||||||
|
input HW_CLK;
|
||||||
|
input KEY;
|
||||||
|
output LED;
|
||||||
|
|
||||||
|
input FSMC_NL;
|
||||||
|
input FSMC_NWAIT;
|
||||||
|
input FSMC_NOE;
|
||||||
|
input FSMC_NWE;
|
||||||
|
input FSMC_NE1;
|
||||||
|
input [1:0]FSMC_NBL;
|
||||||
|
input FSMC_CLK;
|
||||||
|
input [7:0]FSMC_ADD;
|
||||||
|
output [15:0]FSMC_DAT;
|
||||||
|
|
||||||
|
output ADC_CLK;
|
||||||
|
input [7:0]ADC_DAT;
|
||||||
|
|
||||||
|
reg [7:0] adc_result = 8'b0;
|
||||||
|
|
||||||
|
/* Counter register */
|
||||||
|
reg [31:0] counter = 32'b0;
|
||||||
|
/* LED drivers */
|
||||||
|
assign LED = counter[24];
|
||||||
|
// assign LED = ~KEY;
|
||||||
|
|
||||||
|
/* always */
|
||||||
|
always @ (posedge HW_CLK) begin
|
||||||
|
counter <= counter + 1;
|
||||||
|
|
||||||
|
FSMC_DAT = 200;
|
||||||
|
|
||||||
|
ADC_CLK = ~ADC_CLK;
|
||||||
|
end
|
||||||
|
|
||||||
|
always @ (posedge ADC_CLK) begin
|
||||||
|
adc_result = ADC_DAT;
|
||||||
|
|
||||||
|
// FSMC_DAT = 200;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
|
@ -0,0 +1,202 @@
|
||||||
|
use enc424j600::smoltcp_phy;
|
||||||
|
|
||||||
|
use smoltcp::wire::{
|
||||||
|
EthernetAddress, IpAddress, IpCidr, Ipv6Cidr
|
||||||
|
};
|
||||||
|
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface};
|
||||||
|
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
|
||||||
|
use core::str;
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
use smoltcp::time::Instant;
|
||||||
|
|
||||||
|
use cortex_m_rt::exception;
|
||||||
|
|
||||||
|
///spi
|
||||||
|
use stm32f1xx_hal::{
|
||||||
|
time::U32Ext,
|
||||||
|
delay::Delay
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Timer
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use cortex_m::interrupt::Mutex;
|
||||||
|
use stm32f1xx_hal::{
|
||||||
|
time::MilliSeconds,
|
||||||
|
timer::{Timer, Event as TimerEvent},
|
||||||
|
rcc::Clocks,
|
||||||
|
stm32::SYST
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Rate in Hz
|
||||||
|
const TIMER_RATE: u32 = 20;
|
||||||
|
/// Interval duration in milliseconds
|
||||||
|
const TIMER_DELTA: u32 = 1000 / TIMER_RATE;
|
||||||
|
/// Elapsed time in milliseconds
|
||||||
|
static TIMER_MS: Mutex<RefCell<u32>> = Mutex::new(RefCell::new(0));
|
||||||
|
|
||||||
|
/// Setup SysTick exception
|
||||||
|
fn timer_setup(syst: SYST, clocks: Clocks) {
|
||||||
|
let timer = Timer::syst(syst, &clocks);
|
||||||
|
timer.start_count_down(TIMER_RATE.hz()).listen(TimerEvent::Update);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SysTick exception (Timer)
|
||||||
|
#[exception]
|
||||||
|
fn SysTick() {
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
*TIMER_MS.borrow(cs)
|
||||||
|
.borrow_mut() += TIMER_DELTA;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain current time in milliseconds
|
||||||
|
pub fn timer_now() -> MilliSeconds {
|
||||||
|
let ms = cortex_m::interrupt::free(|cs| {
|
||||||
|
*TIMER_MS.borrow(cs)
|
||||||
|
.borrow()
|
||||||
|
});
|
||||||
|
ms.ms()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NetStorage {
|
||||||
|
ip_addrs: [IpCidr; 1],
|
||||||
|
neighbor_cache: [Option<(IpAddress, smoltcp::iface::Neighbor)>; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut NET_STORE: NetStorage = NetStorage {
|
||||||
|
// Placeholder for the real IP address, which is initialized at runtime.
|
||||||
|
ip_addrs: [IpCidr::Ipv6(
|
||||||
|
Ipv6Cidr::SOLICITED_NODE_PREFIX,
|
||||||
|
)],
|
||||||
|
neighbor_cache: [None; 8],
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn ethernet_init<SpiEth> (
|
||||||
|
spi_eth: SpiEth,
|
||||||
|
delay: Delay,
|
||||||
|
clocks: &Clocks
|
||||||
|
)
|
||||||
|
// -> EthernetInterface<smoltcp_phy::SmoltcpDevice<SpiEth>>
|
||||||
|
{
|
||||||
|
// Init controller
|
||||||
|
match spi_eth.reset(&mut delay) {
|
||||||
|
Ok(_) => {
|
||||||
|
// serial_tx.write_fmt(format_args!("Initializing Ethernet...\n")).unwrap();
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
panic!("Ethernet initialization failed!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read MAC
|
||||||
|
let mut eth_mac_addr: [u8; 6] = [0; 6];
|
||||||
|
spi_eth.read_mac_addr(&mut eth_mac_addr);
|
||||||
|
for i in 0..6 {
|
||||||
|
let byte = eth_mac_addr[i];
|
||||||
|
match i {
|
||||||
|
// 0 => {
|
||||||
|
// serial_tx.write_fmt(format_args!("MAC Address = {:02x}-", byte)).unwrap();
|
||||||
|
// },
|
||||||
|
// 1..=4 => {
|
||||||
|
// serial_tx.write_fmt(format_args!("{:02x}-", byte)).unwrap();
|
||||||
|
// },
|
||||||
|
// 5 => {
|
||||||
|
// serial_tx.write_fmt(format_args!("{:02x}\n", byte)).unwrap();
|
||||||
|
// },
|
||||||
|
_ => ()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init Rx/Tx buffers
|
||||||
|
spi_eth.init_rxbuf();
|
||||||
|
spi_eth.init_txbuf();
|
||||||
|
// serial_tx.write_fmt(format_args!("Ethernet controller initialized\n")).unwrap();
|
||||||
|
|
||||||
|
// Init smoltcp interface
|
||||||
|
let eth_iface = {
|
||||||
|
let device = smoltcp_phy::SmoltcpDevice::new(spi_eth);
|
||||||
|
|
||||||
|
let store = unsafe { &mut NET_STORE };
|
||||||
|
store.ip_addrs[0] = IpCidr::new(IpAddress::v4(192, 168, 1, 88), 24);
|
||||||
|
let neighbor_cache = NeighborCache::new(&mut store.neighbor_cache[..]);
|
||||||
|
|
||||||
|
EthernetInterfaceBuilder::new(device)
|
||||||
|
.ethernet_addr(EthernetAddress(eth_mac_addr))
|
||||||
|
.neighbor_cache(neighbor_cache)
|
||||||
|
.ip_addrs(&mut store.ip_addrs[..])
|
||||||
|
.finalize()
|
||||||
|
};
|
||||||
|
// serial_tx.write_fmt(format_args!("Ethernet interface initialized\n")).unwrap();
|
||||||
|
|
||||||
|
// Setup SysTick after releasing SYST from Delay
|
||||||
|
// Reference to stm32-eth:examples/ip.rs
|
||||||
|
timer_setup(delay.free(), *clocks);
|
||||||
|
// serial_tx.write_fmt(format_args!("Timer initialized\n")).unwrap();
|
||||||
|
|
||||||
|
// eth_iface
|
||||||
|
let iface = eth_iface;
|
||||||
|
|
||||||
|
let greet_socket = {
|
||||||
|
static mut TCP_SERVER_RX_DATA: [u8; 256] = [0; 256];
|
||||||
|
static mut TCP_SERVER_TX_DATA: [u8; 256] = [0; 256];
|
||||||
|
let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] });
|
||||||
|
let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] });
|
||||||
|
TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer)
|
||||||
|
};
|
||||||
|
let mut socket_set_entries = [None, None];
|
||||||
|
let mut socket_set = SocketSet::new(&mut socket_set_entries[..]);
|
||||||
|
let greet_handle = socket_set.add(greet_socket);
|
||||||
|
{
|
||||||
|
let store = unsafe { &mut NET_STORE };
|
||||||
|
// serial_tx.write_fmt(format_args!("TCP sockets will listen at {}\n", store.ip_addrs[0].address())).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied / modified from:
|
||||||
|
// smoltcp:examples/loopback.rs, examples/server.rs;
|
||||||
|
// stm32-eth:examples/ip.rs,
|
||||||
|
// git.m-labs.hk/M-Labs/tnetplug
|
||||||
|
loop {
|
||||||
|
// Poll
|
||||||
|
let now = timer_now().0;
|
||||||
|
let instant = Instant::from_millis(now as i64);
|
||||||
|
match iface.poll(&mut socket_set, instant) {
|
||||||
|
Ok(_) => {
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
// serial_tx.write_fmt(format_args!("[{}] Poll error: {:?}\n", instant, e)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Control the "greeting" socket (:4321)
|
||||||
|
{
|
||||||
|
let mut socket = socket_set.get::<TcpSocket>(greet_handle);
|
||||||
|
if !socket.is_open() {
|
||||||
|
// serial_tx.write_fmt(format_args!("[{}] Listening to port 4321 for greeting, please connect to the port\n", instant)).unwrap();
|
||||||
|
socket.listen(4321).unwrap();
|
||||||
|
// socket.set_timeout(Some(smoltcp::time::Duration::from_millis(10000)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if socket.can_send() {
|
||||||
|
let greeting = "Welcome to the server demo for STM32F103!";
|
||||||
|
write!(socket, "{}\n", greeting).unwrap();
|
||||||
|
// serial_tx.write_fmt(format_args!("[{}] Greeting sent, socket closed\n", instant)).unwrap();
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if socket.can_recv() {
|
||||||
|
// serial_tx.write_fmt(format_args!("[{}] Received packet: {:?}\n",
|
||||||
|
// instant, socket.recv(|buffer| {(buffer.len(), str::from_utf8(buffer).unwrap())}))).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// pub fn ethernet_test<SpiEth>
|
||||||
|
// (mut iface: EthernetInterface<smoltcp_phy::SmoltcpDevice<SpiEth>>)
|
||||||
|
// {
|
||||||
|
// // Copied / modified from smoltcp:
|
||||||
|
// // examples/loopback.rs
|
||||||
|
|
||||||
|
// }
|
|
@ -0,0 +1,92 @@
|
||||||
|
use embedded_hal::{
|
||||||
|
digital::v2::{OutputPin, InputPin},
|
||||||
|
blocking::spi::Transfer,
|
||||||
|
blocking::delay::DelayUs,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FPGAFlashError {
|
||||||
|
SPICommunicationError,
|
||||||
|
NegotiationError,
|
||||||
|
ResetStatusError,
|
||||||
|
}
|
||||||
|
|
||||||
|
const DATA: &'static [u8] = include_bytes!("../FPGA/build/blinky.bin");
|
||||||
|
// const DATA: &'static [u8] = include_bytes!("../build/top.bin");
|
||||||
|
|
||||||
|
// A public method to flash iCE40 FPGA on Humpback
|
||||||
|
pub fn flash_ice40_fpga<SPI: Transfer<u8>,
|
||||||
|
SS: OutputPin,
|
||||||
|
RST: OutputPin,
|
||||||
|
DELAY: DelayUs<u32>,
|
||||||
|
DONE: InputPin>
|
||||||
|
(mut spi: SPI, mut ss: SS, mut creset: RST, cdone: DONE, mut delay: DELAY) -> Result<(), FPGAFlashError>
|
||||||
|
{
|
||||||
|
// Data buffer setup
|
||||||
|
let mut dummy_byte :[u8; 1] = [0x00];
|
||||||
|
let mut dummy_13_bytes :[u8; 13] = [0x00; 13];
|
||||||
|
|
||||||
|
// Drive CRESET_B low
|
||||||
|
creset.set_low()
|
||||||
|
.map_err(|_| FPGAFlashError::NegotiationError)?;
|
||||||
|
|
||||||
|
// Drive SPI_SS_B low
|
||||||
|
ss.set_low()
|
||||||
|
.map_err(|_| FPGAFlashError::NegotiationError)?;
|
||||||
|
|
||||||
|
// Wait at least 200ns
|
||||||
|
delay.delay_us(1_u32);
|
||||||
|
|
||||||
|
// Drive CRESET_B high
|
||||||
|
creset.set_high()
|
||||||
|
.map_err(|_| FPGAFlashError::NegotiationError)?;
|
||||||
|
|
||||||
|
// Wait at least another 1200us to clear internal config memory
|
||||||
|
delay.delay_us(1200_u32);
|
||||||
|
|
||||||
|
// Before data transmission starts, check if C_DONE is truly low
|
||||||
|
// If C_DONE is high, the FPGA reset procedure is unsuccessful
|
||||||
|
match cdone.is_low() {
|
||||||
|
Ok(true) => {},
|
||||||
|
_ => return Err(FPGAFlashError::ResetStatusError),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set SPI_SS_B high
|
||||||
|
ss.set_high()
|
||||||
|
.map_err(|_| FPGAFlashError::NegotiationError)?;
|
||||||
|
|
||||||
|
// Send 8 dummy clock, effectively 1 byte of 0x00
|
||||||
|
spi.transfer(&mut dummy_byte)
|
||||||
|
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
|
||||||
|
|
||||||
|
// Drive SPI_SS_B low
|
||||||
|
ss.set_low()
|
||||||
|
.map_err(|_| FPGAFlashError::NegotiationError)?;
|
||||||
|
|
||||||
|
// Send the whole image without interruption
|
||||||
|
for byte in DATA.into_iter() {
|
||||||
|
let mut single_byte_slice = [*byte];
|
||||||
|
spi.transfer(&mut single_byte_slice)
|
||||||
|
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drive SPI_SS_B high
|
||||||
|
ss.set_high()
|
||||||
|
.map_err(|_| FPGAFlashError::NegotiationError)?;
|
||||||
|
|
||||||
|
// Send at another 100 dummy clocks (choosing 13 bytes)
|
||||||
|
spi.transfer(&mut dummy_13_bytes)
|
||||||
|
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
|
||||||
|
|
||||||
|
// Check the CDONE output from FPGA
|
||||||
|
// CDONE needs to be high
|
||||||
|
match cdone.is_high() {
|
||||||
|
Ok(true) => {},
|
||||||
|
_ => return Err(FPGAFlashError::ResetStatusError),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send at least another 49 clock cycles to activate IO pins (choosing same 13 bytes)
|
||||||
|
spi.transfer(&mut dummy_13_bytes).map_err(|_| FPGAFlashError::SPICommunicationError)?;
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
}
|
301
src/main.rs
301
src/main.rs
|
@ -1,14 +1,19 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
// use cortex_m::{asm, singleton};
|
use cortex_m::{singleton};
|
||||||
|
|
||||||
|
pub mod ethernet;
|
||||||
|
use ethernet::{
|
||||||
|
ethernet_init,
|
||||||
|
// ethernet_test
|
||||||
|
};
|
||||||
|
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
use core::str;
|
// use core::str;
|
||||||
use core::fmt::Write;
|
// use core::fmt::Write;
|
||||||
// use nb::block;
|
// use nb::block;
|
||||||
extern crate panic_itm;
|
extern crate panic_itm;
|
||||||
// use cortex_m::{iprintln, iprint};
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
|
|
||||||
use embedded_hal::{
|
use embedded_hal::{
|
||||||
|
@ -22,35 +27,20 @@ use stm32f1xx_hal::{
|
||||||
flash::FlashExt,
|
flash::FlashExt,
|
||||||
gpio::GpioExt,
|
gpio::GpioExt,
|
||||||
time::U32Ext,
|
time::U32Ext,
|
||||||
// stm32::ITM,
|
|
||||||
delay::Delay,
|
delay::Delay,
|
||||||
spi::Spi,
|
spi::Spi,
|
||||||
pac,
|
pac,
|
||||||
// adc,
|
adc,
|
||||||
prelude::*
|
prelude::*
|
||||||
// time::Hertz
|
// time::Hertz
|
||||||
};
|
};
|
||||||
use enc424j600::smoltcp_phy;
|
|
||||||
|
|
||||||
use smoltcp::wire::{
|
|
||||||
EthernetAddress, IpAddress, IpCidr, Ipv6Cidr
|
|
||||||
};
|
|
||||||
use smoltcp::iface::{
|
|
||||||
NeighborCache,
|
|
||||||
EthernetInterfaceBuilder,
|
|
||||||
// EthernetInterface
|
|
||||||
};
|
|
||||||
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
|
|
||||||
|
|
||||||
/// Timer
|
/// Timer
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use cortex_m::interrupt::Mutex;
|
use cortex_m::interrupt::Mutex;
|
||||||
use cortex_m_rt::exception;
|
// use cortex_m_rt::exception;
|
||||||
use stm32f1xx_hal::{
|
use stm32f1xx_hal::{
|
||||||
rcc::Clocks,
|
timer::{Timer},
|
||||||
time::MilliSeconds,
|
|
||||||
timer::{Timer, Event as TimerEvent},
|
|
||||||
stm32::SYST,
|
|
||||||
serial::{
|
serial::{
|
||||||
Config,
|
Config,
|
||||||
Serial,
|
Serial,
|
||||||
|
@ -61,38 +51,11 @@ use stm32f1xx_hal::{
|
||||||
// pac::*
|
// pac::*
|
||||||
// dma::Half
|
// dma::Half
|
||||||
};
|
};
|
||||||
use smoltcp::time::Instant;
|
use stm32f1xx_hal::{
|
||||||
|
gpio::{gpioc},
|
||||||
/// Rate in Hz
|
pac::{interrupt, Interrupt, TIM3},
|
||||||
const TIMER_RATE: u32 = 20;
|
timer::{CountDownTimer, Event},
|
||||||
/// Interval duration in milliseconds
|
};
|
||||||
const TIMER_DELTA: u32 = 1000 / TIMER_RATE;
|
|
||||||
/// Elapsed time in milliseconds
|
|
||||||
static TIMER_MS: Mutex<RefCell<u32>> = Mutex::new(RefCell::new(0));
|
|
||||||
|
|
||||||
/// Setup SysTick exception
|
|
||||||
fn timer_setup(syst: SYST, clocks: Clocks) {
|
|
||||||
let timer = Timer::syst(syst, &clocks);
|
|
||||||
timer.start_count_down(TIMER_RATE.hz()).listen(TimerEvent::Update);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SysTick exception (Timer)
|
|
||||||
#[exception]
|
|
||||||
fn SysTick() {
|
|
||||||
cortex_m::interrupt::free(|cs| {
|
|
||||||
*TIMER_MS.borrow(cs)
|
|
||||||
.borrow_mut() += TIMER_DELTA;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Obtain current time in milliseconds
|
|
||||||
pub fn timer_now() -> MilliSeconds {
|
|
||||||
let ms = cortex_m::interrupt::free(|cs| {
|
|
||||||
*TIMER_MS.borrow(cs)
|
|
||||||
.borrow()
|
|
||||||
});
|
|
||||||
ms.ms()
|
|
||||||
}
|
|
||||||
|
|
||||||
///spi
|
///spi
|
||||||
use stm32f1xx_hal::{
|
use stm32f1xx_hal::{
|
||||||
|
@ -109,18 +72,36 @@ type SpiEth = enc424j600::Enc424j600<
|
||||||
PC13<Output<PushPull>>
|
PC13<Output<PushPull>>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub struct NetStorage {
|
|
||||||
ip_addrs: [IpCidr; 1],
|
|
||||||
neighbor_cache: [Option<(IpAddress, smoltcp::iface::Neighbor)>; 8],
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut NET_STORE: NetStorage = NetStorage {
|
// A type definition for the GPIO pin to be used for our LED
|
||||||
// Placeholder for the real IP address, which is initialized at runtime.
|
type LEDPIN = gpioc::PC0<Output<PushPull>>;
|
||||||
ip_addrs: [IpCidr::Ipv6(
|
// Make LED pin globally available
|
||||||
Ipv6Cidr::SOLICITED_NODE_PREFIX,
|
static G_LED: Mutex<RefCell<Option<LEDPIN>>> = Mutex::new(RefCell::new(None));
|
||||||
)],
|
// Make timer interrupt registers globally available
|
||||||
neighbor_cache: [None; 8],
|
static G_TIM: Mutex<RefCell<Option<CountDownTimer<TIM3>>>> = Mutex::new(RefCell::new(None));
|
||||||
};
|
// Define an interupt handler, i.e. function to call when interrupt occurs.
|
||||||
|
// This specific interrupt will "trip" when the timer TIM2 times out
|
||||||
|
#[interrupt]
|
||||||
|
fn TIM3() {
|
||||||
|
static mut LED: Option<LEDPIN> = None;
|
||||||
|
static mut TIM: Option<CountDownTimer<TIM3>> = None;
|
||||||
|
|
||||||
|
let led = LED.get_or_insert_with(|| {
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
// Move LED pin here, leaving a None in its place
|
||||||
|
G_LED.borrow(cs).replace(None).unwrap()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let tim = TIM.get_or_insert_with(|| {
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
G_TIM.borrow(cs).replace(None).unwrap()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let _ = led.toggle();
|
||||||
|
let _ = tim.wait();
|
||||||
|
}
|
||||||
|
|
||||||
#[entry()]
|
#[entry()]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
|
@ -137,7 +118,7 @@ fn main() -> ! {
|
||||||
let mut flash = dp.FLASH.constrain();
|
let mut flash = dp.FLASH.constrain();
|
||||||
let mut rcc = dp.RCC.constrain();
|
let mut rcc = dp.RCC.constrain();
|
||||||
|
|
||||||
let dma1_chs = dp.DMA1.split(&mut rcc.ahb);
|
// let dma1_chs = dp.DMA1.split(&mut rcc.ahb);
|
||||||
|
|
||||||
let clocks = rcc
|
let clocks = rcc
|
||||||
.cfgr
|
.cfgr
|
||||||
|
@ -172,9 +153,10 @@ fn main() -> ! {
|
||||||
);
|
);
|
||||||
|
|
||||||
// let mut serial_tx = serial.split().0.with_dma(dma1_chs.4);
|
// let mut serial_tx = serial.split().0.with_dma(dma1_chs.4);
|
||||||
let mut serial_tx = serial.split().0;
|
let (mut serial_tx, mut _serial_rx) = serial.split();
|
||||||
|
|
||||||
|
|
||||||
|
// //ADC1
|
||||||
// let dma_ch1 = dma1_chs.1;
|
// let dma_ch1 = dma1_chs.1;
|
||||||
// // Setup ADC
|
// // Setup ADC
|
||||||
// let adc1 = adc::Adc::adc1(dp.ADC1, &mut rcc.apb2, clocks);
|
// let adc1 = adc::Adc::adc1(dp.ADC1, &mut rcc.apb2, clocks);
|
||||||
|
@ -195,13 +177,6 @@ fn main() -> ! {
|
||||||
// // let (_adc1, _adc_ch15, _dma_ch1) = adc_dma.split();
|
// // let (_adc1, _adc_ch15, _dma_ch1) = adc_dma.split();
|
||||||
|
|
||||||
|
|
||||||
// Init ITM
|
|
||||||
// let mut itm = cp.ITM;
|
|
||||||
// let stim0 = &mut itm.stim[0];
|
|
||||||
|
|
||||||
// iprintln!(stim0,
|
|
||||||
// "Eth TCP Server on STM32-F103 via NIC100/ENC424J600");
|
|
||||||
|
|
||||||
// NIC100 / ENC424J600 Set-up
|
// NIC100 / ENC424J600 Set-up
|
||||||
let spi1 = dp.SPI1;
|
let spi1 = dp.SPI1;
|
||||||
|
|
||||||
|
@ -212,165 +187,37 @@ fn main() -> ! {
|
||||||
let spi1_nss = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
|
let spi1_nss = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
|
||||||
|
|
||||||
// Create SPI1 for HAL
|
// Create SPI1 for HAL
|
||||||
let eth_iface = {
|
let spi_eth_port = Spi::spi1(
|
||||||
let mut spi_eth = {
|
spi1,
|
||||||
let spi_eth_port = Spi::spi1(
|
(spi1_sck, spi1_miso, spi1_mosi),
|
||||||
spi1,
|
&mut afio.mapr,
|
||||||
(spi1_sck, spi1_miso, spi1_mosi),
|
enc424j600::spi::interfaces::SPI_MODE,
|
||||||
&mut afio.mapr,
|
// Hertz(enc424j600::spi::interfaces::SPI_CLOCK_FREQ),
|
||||||
enc424j600::spi::interfaces::SPI_MODE,
|
9.mhz(),
|
||||||
// Hertz(enc424j600::spi::interfaces::SPI_CLOCK_FREQ),
|
clocks,
|
||||||
9.mhz(),
|
&mut rcc.apb2,);
|
||||||
clocks,
|
|
||||||
&mut rcc.apb2,);
|
|
||||||
|
|
||||||
SpiEth::new(spi_eth_port, spi1_nss)
|
let spi_eth = SpiEth::new(spi_eth_port, spi1_nss).cpu_freq_mhz(72);
|
||||||
.cpu_freq_mhz(72)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Init controller
|
ethernet_init(spi_eth, delay, &clocks);
|
||||||
match spi_eth.reset(&mut delay) {
|
|
||||||
Ok(_) => {
|
|
||||||
// iprintln!(stim0, "Initializing Ethernet...")
|
|
||||||
serial_tx.write_fmt(format_args!("Initializing Ethernet...\n")).unwrap();
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
panic!("Ethernet initialization failed!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read MAC
|
|
||||||
let mut eth_mac_addr: [u8; 6] = [0; 6];
|
|
||||||
spi_eth.read_mac_addr(&mut eth_mac_addr).unwrap();
|
|
||||||
for i in 0..6 {
|
|
||||||
let byte = eth_mac_addr[i];
|
|
||||||
match i {
|
|
||||||
// 0 => iprint!(stim0, "MAC Address = {:02x}-", byte),
|
|
||||||
// 1..=4 => iprint!(stim0, "{:02x}-", byte),
|
|
||||||
// 5 => iprint!(stim0, "{:02x}\n", byte),
|
|
||||||
0 => {
|
|
||||||
serial_tx.write_fmt(format_args!("MAC Address = {:02x}-", byte)).unwrap();
|
|
||||||
},
|
|
||||||
1..=4 => {
|
|
||||||
serial_tx.write_fmt(format_args!("{:02x}-", byte)).unwrap();
|
|
||||||
},
|
|
||||||
5 => {
|
|
||||||
serial_tx.write_fmt(format_args!("{:02x}\n", byte)).unwrap();
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init Rx/Tx buffers
|
|
||||||
spi_eth.init_rxbuf().unwrap();
|
|
||||||
spi_eth.init_txbuf().unwrap();
|
|
||||||
// iprintln!(stim0, "Ethernet controller initialized");
|
|
||||||
serial_tx.write_fmt(format_args!("Ethernet controller initialized\n")).unwrap();
|
|
||||||
|
|
||||||
// Init smoltcp interface
|
|
||||||
let eth_iface = {
|
|
||||||
let device = smoltcp_phy::SmoltcpDevice::new(spi_eth);
|
|
||||||
|
|
||||||
let store = unsafe { &mut NET_STORE };
|
|
||||||
store.ip_addrs[0] = IpCidr::new(IpAddress::v4(192, 168, 1, 88), 24);
|
|
||||||
let neighbor_cache = NeighborCache::new(&mut store.neighbor_cache[..]);
|
|
||||||
|
|
||||||
EthernetInterfaceBuilder::new(device)
|
|
||||||
.ethernet_addr(EthernetAddress(eth_mac_addr))
|
|
||||||
.neighbor_cache(neighbor_cache)
|
|
||||||
.ip_addrs(&mut store.ip_addrs[..])
|
|
||||||
.finalize()
|
|
||||||
};
|
|
||||||
// iprintln!(stim0, "Ethernet interface initialized");
|
|
||||||
serial_tx.write_fmt(format_args!("Ethernet interface initialized\n")).unwrap();
|
|
||||||
|
|
||||||
eth_iface
|
|
||||||
};
|
|
||||||
|
|
||||||
// Setup SysTick after releasing SYST from Delay
|
|
||||||
// Reference to stm32-eth:examples/ip.rs
|
|
||||||
timer_setup(delay.free(), clocks);
|
|
||||||
// iprintln!(stim0, "Timer initialized");
|
|
||||||
|
|
||||||
let mut led = gpioc.pc0.into_push_pull_output(&mut gpioc.crl);
|
let mut led = gpioc.pc0.into_push_pull_output(&mut gpioc.crl);
|
||||||
led.set_high().unwrap();
|
led.set_high().unwrap();
|
||||||
|
// Move the pin into our global storage
|
||||||
|
cortex_m::interrupt::free(|cs| *G_LED.borrow(cs).borrow_mut() = Some(led));
|
||||||
|
// Set up a timer expiring after 1s
|
||||||
|
let mut timer = Timer::tim3(dp.TIM3, &clocks, &mut rcc.apb1).start_count_down(1.hz());
|
||||||
|
// Generate an interrupt when the timer expires
|
||||||
|
timer.listen(Event::Update);
|
||||||
|
// Move the timer into our global storage
|
||||||
|
cortex_m::interrupt::free(|cs| *G_TIM.borrow(cs).borrow_mut() = Some(timer));
|
||||||
|
unsafe {
|
||||||
|
cortex_m::peripheral::NVIC::unmask(Interrupt::TIM3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// let stim0 = &mut c.resources.itm.stim[0];
|
// ethernet_test();
|
||||||
let mut iface = eth_iface;
|
|
||||||
|
|
||||||
// Copied / modified from smoltcp:
|
|
||||||
// examples/loopback.rs
|
|
||||||
let echo_socket = {
|
|
||||||
static mut TCP_SERVER_RX_DATA: [u8; 1024] = [0; 1024];
|
|
||||||
static mut TCP_SERVER_TX_DATA: [u8; 1024] = [0; 1024];
|
|
||||||
let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] });
|
|
||||||
let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] });
|
|
||||||
TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer)
|
|
||||||
};
|
|
||||||
let greet_socket = {
|
|
||||||
static mut TCP_SERVER_RX_DATA: [u8; 256] = [0; 256];
|
|
||||||
static mut TCP_SERVER_TX_DATA: [u8; 256] = [0; 256];
|
|
||||||
let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] });
|
|
||||||
let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] });
|
|
||||||
TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer)
|
|
||||||
};
|
|
||||||
let mut socket_set_entries = [None, None];
|
|
||||||
let mut socket_set = SocketSet::new(&mut socket_set_entries[..]);
|
|
||||||
let greet_handle = socket_set.add(greet_socket);
|
|
||||||
{
|
|
||||||
let store = unsafe { &mut NET_STORE };
|
|
||||||
// iprintln!(stim0,
|
|
||||||
// "TCP sockets will listen at {}", store.ip_addrs[0].address());
|
|
||||||
serial_tx.write_fmt(format_args!("TCP sockets will listen at {}\n", store.ip_addrs[0].address())).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied / modified from:
|
|
||||||
// smoltcp:examples/loopback.rs, examples/server.rs;
|
|
||||||
// stm32-eth:examples/ip.rs,
|
|
||||||
// git.m-labs.hk/M-Labs/tnetplug
|
|
||||||
loop {
|
|
||||||
// Poll
|
|
||||||
let now = timer_now().0;
|
|
||||||
let instant = Instant::from_millis(now as i64);
|
|
||||||
match iface.poll(&mut socket_set, instant) {
|
|
||||||
Ok(_) => {
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
// iprintln!(stim0, "[{}] Poll error: {:?}", instant, e)
|
|
||||||
serial_tx.write_fmt(format_args!("[{}] Poll error: {:?}\n", instant, e)).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Control the "greeting" socket (:4321)
|
|
||||||
{
|
|
||||||
let mut socket = socket_set.get::<TcpSocket>(greet_handle);
|
|
||||||
if !socket.is_open() {
|
|
||||||
// iprintln!(stim0,
|
|
||||||
// "[{}] Listening to port 4321 for greeting, \
|
|
||||||
// please connect to the port", instant);
|
|
||||||
serial_tx.write_fmt(format_args!("[{}] Listening to port 4321 for greeting, please connect to the port\n", instant)).unwrap();
|
|
||||||
socket.listen(4321).unwrap();
|
|
||||||
// socket.set_timeout(Some(smoltcp::time::Duration::from_millis(10000)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if socket.can_send() {
|
|
||||||
let greeting = "Welcome to the server demo for STM32F103!";
|
|
||||||
write!(socket, "{}\n", greeting).unwrap();
|
|
||||||
// iprintln!(stim0,
|
|
||||||
// "[{}] Greeting sent, socket closed", instant);
|
|
||||||
serial_tx.write_fmt(format_args!("[{}] Greeting sent, socket closed\n", instant)).unwrap();
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if socket.can_recv() {
|
|
||||||
// iprintln!(stim0,
|
|
||||||
// "[{}] Received packet: {:?}", instant, socket.recv(|buffer| {
|
|
||||||
// (buffer.len(), str::from_utf8(buffer).unwrap())
|
|
||||||
// }));
|
|
||||||
serial_tx.write_fmt(format_args!("[{}] Received packet: {:?}\n",
|
|
||||||
instant, socket.recv(|buffer| {(buffer.len(), str::from_utf8(buffer).unwrap())}))).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue