mirror of
https://github.com/paparazzi/paparazzi.git
synced 2026-06-05 15:30:08 +08:00
b8c77189c0
This was already once proposed and mergd with #1589 (and reverted again) since with these changes the simulators don't work anymore. So this needs some more work and checking before it can be integrated again.
437 lines
12 KiB
C
437 lines
12 KiB
C
/*
|
|
* Copyright (C) 2012 Gerard Toonstra
|
|
*
|
|
* This file is part of paparazzi.
|
|
*
|
|
* paparazzi is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* paparazzi is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with paparazzi; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @file subsystems/datalink/w5100.c
|
|
* W5100 ethernet chip I/O
|
|
*/
|
|
|
|
#include "mcu_periph/sys_time.h"
|
|
#include "subsystems/datalink/w5100.h"
|
|
#include "mcu_periph/spi.h"
|
|
#include "mcu_periph/gpio.h"
|
|
|
|
#define TXBUF_BASE 0x4000
|
|
#define RXBUF_BASE 0x6000
|
|
#define SOCKETS 4
|
|
|
|
#define CMD_SOCKET 1
|
|
#define TELEM_SOCKET 0
|
|
|
|
#define SOCK_OPEN 0x01
|
|
#define SOCK_CLOSE 0x10
|
|
#define SOCK_SEND 0x20
|
|
#define SOCK_RECV 0x40
|
|
#define SNMR_UDP 0x02
|
|
#define SNMR_MULTI 0x80
|
|
#define SNIR_SEND_OK 0x10
|
|
#define SNIR_TIMEOUT 0x08
|
|
#define CH_BASE 0x0400
|
|
#define CH_SIZE 0x0100
|
|
#define SMASK 0x07FF // Tx buffer MASK
|
|
#define RMASK 0x07FF // Tx buffer MASK
|
|
|
|
#define REG_MR 0x0000
|
|
#define REG_RX_MEM 0x001A
|
|
#define REG_TX_MEM 0x001B
|
|
|
|
#define REG_GAR 0x0001
|
|
#define REG_SUBR 0x0005
|
|
#define REG_SHAR 0x0009
|
|
#define REG_SIPR 0x000F
|
|
|
|
#define SOCK_MR 0x0000
|
|
#define SOCK_CR 0x0001
|
|
#define SOCK_IR 0x0002
|
|
#define SOCK_PORT 0x0004
|
|
#define SOCK_DHAR 0x0006
|
|
#define SOCK_DIPR 0x000C
|
|
#define SOCK_DPORT 0x0010
|
|
#define SOCK_TX_WR 0x0024
|
|
#define SOCK_RSR 0x0026
|
|
#define SOCK_RXRD 0x0028
|
|
|
|
#ifndef W5100_SPI_DEV
|
|
#define W5100_SPI_DEV spi1
|
|
#endif
|
|
|
|
#ifndef W5100_SLAVE_IDX
|
|
#define W5100_SLAVE_IDX SPI_SLAVE1
|
|
#endif
|
|
|
|
#ifndef W5100_DRDY_GPIO
|
|
#define W5100_DRDY_GPIO GPIOB
|
|
#endif
|
|
|
|
#ifndef W5100_DRDY_GPIO_PIN
|
|
#define W5100_DRDY_GPIO_PIN GPIO1
|
|
#endif
|
|
|
|
struct w5100_periph chip0;
|
|
uint8_t ck_a, ck_b;
|
|
uint8_t w5100_rx_buf[W5100_RX_BUFFER_SIZE];
|
|
|
|
// the media access control (ethernet hardware) address for the shield.
|
|
static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
|
|
|
//the IP address for the shield:
|
|
static uint8_t ip[] = { W5100_IP };
|
|
static uint8_t dest[] = { W5100_MULTICAST_IP };
|
|
static uint8_t subnet[] = { W5100_SUBNET };
|
|
static uint16_t dport = W5100_MULTICAST_PORT;
|
|
|
|
static const uint8_t RST = 7; // Reset BIT
|
|
|
|
uint16_t SBASE[SOCKETS]; // Tx buffer base address
|
|
uint16_t RBASE[SOCKETS]; // Rx buffer base address
|
|
static const uint16_t SSIZE = 2048; // Max Tx buffer size
|
|
static const uint16_t RSIZE = 2048; // Max Rx buffer size
|
|
|
|
struct spi_transaction w5100_spi;
|
|
|
|
static void w5100_close_socket(uint8_t _s);
|
|
static void configure_socket(uint8_t _s, uint8_t _flag, uint16_t _lport, uint16_t _dport, uint8_t *_dest);
|
|
static void w5100_read_data(uint8_t s, volatile uint8_t *src, volatile uint8_t *dst, uint16_t len);
|
|
static uint16_t w5100_read(uint16_t _addr, uint8_t *_buf, uint16_t _len);
|
|
|
|
static inline void w5100_set(uint16_t _reg, uint8_t _val)
|
|
{
|
|
w5100_spi.output_buf[0] = 0xF0;
|
|
w5100_spi.output_buf[1] = _reg >> 8;
|
|
w5100_spi.output_buf[2] = _reg & 0xFF;
|
|
w5100_spi.output_buf[3] = _val;
|
|
|
|
spi_submit(&(W5100_SPI_DEV), &w5100_spi);
|
|
|
|
// FIXME: no busy waiting! if really needed add a timeout!!!!
|
|
while (w5100_spi.status != SPITransSuccess);
|
|
}
|
|
|
|
static inline uint8_t w5100_get(uint16_t _reg)
|
|
{
|
|
w5100_spi.output_buf[0] = 0x0F;
|
|
w5100_spi.output_buf[1] = _reg >> 8;
|
|
w5100_spi.output_buf[2] = _reg & 0xFF;
|
|
|
|
spi_submit(&(W5100_SPI_DEV), &w5100_spi);
|
|
|
|
// FIXME: no busy waiting! if really needed add a timeout!!!!
|
|
while (w5100_spi.status != SPITransSuccess);
|
|
|
|
return w5100_spi.input_buf[3];
|
|
}
|
|
|
|
static inline void w5100_set_buffer(uint16_t _reg, volatile uint8_t *_buf, uint16_t _len)
|
|
{
|
|
for (int i = 0; i < _len; i++) {
|
|
w5100_set(_reg, _buf[ i ]);
|
|
_reg++;
|
|
}
|
|
}
|
|
|
|
static inline void w5100_sock_set(uint8_t _sock, uint16_t _reg, uint8_t _val)
|
|
{
|
|
w5100_set(CH_BASE + _sock * CH_SIZE + _reg, _val);
|
|
}
|
|
|
|
static inline uint8_t w5100_sock_get(uint8_t _sock, uint16_t _reg)
|
|
{
|
|
return w5100_get(CH_BASE + _sock * CH_SIZE + _reg);
|
|
}
|
|
|
|
static inline uint16_t w5100_sock_get16(uint8_t _sock, uint16_t _reg)
|
|
{
|
|
uint16_t res = w5100_sock_get(_sock, _reg);
|
|
uint16_t res2 = w5100_sock_get(_sock, _reg + 1);
|
|
res = res << 8;
|
|
res2 = res2 & 0xFF;
|
|
res = res | res2;
|
|
return res;
|
|
}
|
|
|
|
// Functions for the generic device API
|
|
static int true_function(struct w5100_periph *p __attribute__((unused)), long *fd __attribute__((unused)), uint16_t len __attribute__((unused))) { return true; }
|
|
static void dev_transmit(struct w5100_periph *p __attribute__((unused)), long fd __attribute__((unused)), uint8_t byte) { w5100_transmit(byte); }
|
|
static void dev_transmit_buffer(struct w5100_periph *p __attribute__((unused)), long fd __attribute__((unused)), uint8_t *data, uint16_t len) { w5100_transmit_buffer(data, len); }
|
|
static void dev_send(struct w5100_periph *p __attribute__((unused)), long fd __attribute__((unused))) { w5100_send(); }
|
|
static int dev_char_available(struct w5100_periph *p __attribute__((unused))) { return w5100_ch_available; }
|
|
static uint8_t dev_getch(struct w5100_periph *p __attribute__((unused)))
|
|
{
|
|
uint8_t c = 0;
|
|
w5100_receive(&c, 1);
|
|
return c;
|
|
}
|
|
|
|
void w5100_init(void)
|
|
{
|
|
|
|
// configure the SPI bus.
|
|
w5100_spi.slave_idx = W5100_SLAVE_IDX;
|
|
w5100_spi.output_length = 4;
|
|
w5100_spi.input_length = 4;
|
|
w5100_spi.select = SPISelectUnselect;
|
|
w5100_spi.cpol = SPICpolIdleLow;
|
|
w5100_spi.cpha = SPICphaEdge1;
|
|
w5100_spi.dss = SPIDss8bit;
|
|
w5100_spi.bitorder = SPIMSBFirst;
|
|
w5100_spi.cdiv = SPIDiv64;
|
|
|
|
chip0.status = W5100StatusUninit;
|
|
chip0.curbuf = 0;
|
|
w5100_spi.input_buf = &chip0.work_rx[0];
|
|
w5100_spi.output_buf = &chip0.work_tx[0];
|
|
|
|
// wait one second for proper initialization (chip getting powered up).
|
|
sys_time_usleep(1000000);
|
|
|
|
// set DRDY pin
|
|
gpio_setup_output(W5100_DRDY_GPIO, W5100_DRDY_GPIO_PIN);
|
|
gpio_clear(W5100_DRDY_GPIO, W5100_DRDY_GPIO_PIN);
|
|
sys_time_usleep(200);
|
|
gpio_set(W5100_DRDY_GPIO, W5100_DRDY_GPIO_PIN);
|
|
|
|
// allow some time for the chip to wake up.
|
|
sys_time_usleep(20000);
|
|
|
|
// write reset bit into mode register
|
|
w5100_set(REG_MR, 1 << RST);
|
|
|
|
// allow some time to wake up...
|
|
sys_time_usleep(20000);
|
|
|
|
// receive memory size
|
|
w5100_set(REG_RX_MEM, 0x55);
|
|
|
|
// transmit memory size
|
|
w5100_set(REG_TX_MEM, 0x55);
|
|
|
|
// Setting the required socket base addresses for reads and writes to/from sockets
|
|
for (int i = 0; i < SOCKETS; i++) {
|
|
SBASE[i] = TXBUF_BASE + SSIZE * i;
|
|
RBASE[i] = RXBUF_BASE + RSIZE * i;
|
|
}
|
|
|
|
uint8_t gateway[4];
|
|
gateway[0] = ip[0];
|
|
gateway[1] = ip[1];
|
|
gateway[2] = ip[2];
|
|
gateway[3] = 1;
|
|
|
|
// configure gateway, subnet, mac and ip on "NIC".
|
|
w5100_set_buffer(REG_GAR, gateway, 4);
|
|
w5100_set_buffer(REG_SUBR, subnet, 4);
|
|
w5100_set_buffer(REG_SHAR, mac, 6);
|
|
w5100_set_buffer(REG_SIPR, ip, 4);
|
|
|
|
// create a socket to send telemetry through.
|
|
configure_socket(TELEM_SOCKET, SNMR_MULTI, 1, dport, dest);
|
|
|
|
// make dest zero and configure socket to receive data
|
|
dest[ 0 ] = 0x00;
|
|
configure_socket(CMD_SOCKET, 0, dport, dport, dest);
|
|
|
|
// Configure generic device
|
|
chip0.device.periph = (void *)(&chip0);
|
|
chip0.device.check_free_space = (check_free_space_t) true_function;
|
|
chip0.device.put_byte = (put_byte_t) dev_transmit;
|
|
chip0.device.put_buffer = (put_buffer_t) dev_transmit_buffer;
|
|
chip0.device.send_message = (send_message_t) dev_send;
|
|
chip0.device.char_available = (char_available_t) dev_char_available;
|
|
chip0.device.get_byte = (get_byte_t) dev_getch;
|
|
}
|
|
|
|
void w5100_transmit(uint8_t data)
|
|
{
|
|
|
|
uint16_t temp = (chip0.tx_insert_idx[ chip0.curbuf ] + 1) % W5100_TX_BUFFER_SIZE;
|
|
|
|
if (temp == chip0.tx_extract_idx[ chip0.curbuf ]) {
|
|
// no more room in this transaction.
|
|
return;
|
|
}
|
|
|
|
// check if in process of sending data
|
|
chip0.tx_buf[ chip0.curbuf ][ chip0.tx_insert_idx[ chip0.curbuf ] ] = data;
|
|
chip0.tx_insert_idx[ chip0.curbuf ] = temp;
|
|
}
|
|
|
|
void w5100_transmit_buffer(uint8_t *data, uint16_t len)
|
|
{
|
|
int i;
|
|
for (i = 0; i < len; i++) {
|
|
w5100_transmit(data[i]);
|
|
}
|
|
}
|
|
|
|
void w5100_send()
|
|
{
|
|
// Now send off spi transaction.
|
|
uint16_t len = chip0.tx_insert_idx[ chip0.curbuf ];
|
|
uint8_t curbuf = chip0.curbuf;
|
|
|
|
// switch to other buffer to accept more chars.
|
|
chip0.curbuf++;
|
|
if (chip0.curbuf >= W5100_BUFFER_NUM) {
|
|
chip0.curbuf = 0;
|
|
}
|
|
|
|
uint16_t ptr = w5100_sock_get16(TELEM_SOCKET, SOCK_TX_WR);
|
|
uint16_t offset = ptr & SMASK;
|
|
uint16_t dstAddr = offset + SBASE[ TELEM_SOCKET ];
|
|
|
|
chip0.tx_insert_idx[ chip0.curbuf ] = 0;
|
|
chip0.tx_extract_idx[ chip0.curbuf ] = 0;
|
|
|
|
if (offset + len > SSIZE) {
|
|
// Wrap around circular buffer
|
|
uint16_t size = SSIZE - offset;
|
|
w5100_set_buffer(dstAddr, &chip0.tx_buf[curbuf][0], size);
|
|
w5100_set_buffer(SBASE[ TELEM_SOCKET ], &chip0.tx_buf[curbuf][0] + size, len - size);
|
|
} else {
|
|
w5100_set_buffer(dstAddr, &chip0.tx_buf[curbuf][0], len);
|
|
}
|
|
|
|
// Reset write pointer
|
|
ptr += len;
|
|
w5100_sock_set(TELEM_SOCKET, SOCK_TX_WR, ptr >> 8);
|
|
w5100_sock_set(TELEM_SOCKET, SOCK_TX_WR + 1, ptr & 0xFF);
|
|
|
|
// send
|
|
w5100_sock_set(TELEM_SOCKET, SOCK_CR, SOCK_SEND);
|
|
|
|
uint8_t complete = w5100_sock_get(TELEM_SOCKET, SOCK_CR);
|
|
while (complete != 0x00) {
|
|
complete = w5100_sock_get(TELEM_SOCKET, SOCK_CR);
|
|
}
|
|
}
|
|
|
|
uint16_t w5100_rx_size(uint8_t _s)
|
|
{
|
|
uint16_t val = 0, val1 = 0;
|
|
|
|
do {
|
|
val1 = w5100_sock_get16(_s, SOCK_RSR);
|
|
if (val1 != 0) {
|
|
val = w5100_sock_get16(_s, SOCK_RSR);
|
|
}
|
|
} while (val != val1);
|
|
return val;
|
|
}
|
|
|
|
static void w5100_close_socket(uint8_t _s)
|
|
{
|
|
// send command to close socket
|
|
w5100_sock_set(_s, SOCK_CR, SOCK_CLOSE);
|
|
// clear interrupt registers
|
|
w5100_sock_set(_s, SOCK_IR, 0xFF);
|
|
}
|
|
|
|
static void configure_socket(uint8_t _s, uint8_t _flag, uint16_t _lport, uint16_t _dport, uint8_t *_dest)
|
|
{
|
|
// Configure socket that receives data on 1234
|
|
w5100_close_socket(_s);
|
|
// configure type of socket
|
|
w5100_sock_set(_s, SOCK_MR, SNMR_UDP | _flag);
|
|
// configure MSB+LSB of local port number
|
|
w5100_sock_set(_s, SOCK_PORT, _lport >> 8);
|
|
w5100_sock_set(_s, SOCK_PORT + 1, _lport & 0xFF);
|
|
if (_dest[0] != 0x00) {
|
|
// configure destination to send stuff to.
|
|
w5100_set_buffer(CH_BASE + _s * CH_SIZE + SOCK_DIPR, _dest, 4);
|
|
}
|
|
|
|
// this mac corresponds to a mac for multicasting....
|
|
uint8_t mac_multi[] = { 0x01, 0x00, 0x5E, 0x01, 0x01, 0x0B };
|
|
w5100_set_buffer(CH_BASE + _s * CH_SIZE + SOCK_DHAR, mac_multi, 6);
|
|
|
|
// configure destination port
|
|
w5100_sock_set(_s, SOCK_DPORT, _dport >> 8);
|
|
w5100_sock_set(_s, SOCK_DPORT + 1, _dport & 0xFF);
|
|
|
|
// send command to open the socket
|
|
w5100_sock_set(_s, SOCK_CR, SOCK_OPEN);
|
|
}
|
|
|
|
bool w5100_ch_available()
|
|
{
|
|
if (w5100_rx_size(CMD_SOCKET) > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint16_t w5100_receive(uint8_t *buf, uint16_t len __attribute__((unused)))
|
|
{
|
|
uint8_t head[8];
|
|
uint16_t data_len = 0;
|
|
uint16_t ptr = 0;
|
|
|
|
// Get socket read pointer
|
|
ptr = w5100_sock_get16(CMD_SOCKET, SOCK_RXRD);
|
|
w5100_read_data(CMD_SOCKET, (uint8_t *)ptr, head, 0x08);
|
|
ptr += 8;
|
|
data_len = head[6];
|
|
data_len = (data_len << 8) + head[7];
|
|
|
|
// read data from buffer.
|
|
w5100_read_data(CMD_SOCKET, (uint8_t *)ptr, buf, data_len); // data copy.
|
|
ptr += data_len;
|
|
|
|
// update read pointer
|
|
w5100_sock_set(CMD_SOCKET, SOCK_RXRD, ptr >> 8);
|
|
w5100_sock_set(CMD_SOCKET, SOCK_RXRD + 1, ptr & 0xFF);
|
|
|
|
// finalize read.
|
|
w5100_sock_set(CMD_SOCKET, SOCK_CR, SOCK_RECV);
|
|
return data_len;
|
|
}
|
|
|
|
static void w5100_read_data(uint8_t s __attribute__((unused)), volatile uint8_t *src, volatile uint8_t *dst,
|
|
uint16_t len)
|
|
{
|
|
uint16_t size;
|
|
uint16_t src_mask;
|
|
uint16_t src_ptr;
|
|
|
|
src_mask = (uint16_t)src & RMASK;
|
|
src_ptr = RBASE[CMD_SOCKET] + src_mask;
|
|
|
|
if ((src_mask + len) > RSIZE) {
|
|
size = RSIZE - src_mask;
|
|
w5100_read(src_ptr, (uint8_t *)dst, size);
|
|
dst += size;
|
|
w5100_read(RBASE[CMD_SOCKET], (uint8_t *) dst, len - size);
|
|
} else {
|
|
w5100_read(src_ptr, (uint8_t *) dst, len);
|
|
}
|
|
}
|
|
|
|
static uint16_t w5100_read(uint16_t _addr, uint8_t *_buf, uint16_t _len)
|
|
{
|
|
for (int i = 0; i < _len; i++) {
|
|
_buf[i] = w5100_get(_addr);
|
|
_addr++;
|
|
}
|
|
return _len;
|
|
}
|
|
|