/*
* Copyright 2015 Ettus Research LLC
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see .
*/
#include
#include
#include
static uint8_t current_bank;
static uint16_t next_pkt_ptr;
#define SET_CS_ACTIVE() SPI_PORT &= ~(1<> 5));
current_bank = (addr & BANK_MASK);
}
}
static uint8_t enc28j60_read(uint8_t addr){
enc28j60_set_bank(addr);
return enc28j60_read_op(RCR, addr);
}
static void enc28j60_write(uint8_t addr, uint16_t value){
enc28j60_set_bank(addr);
enc28j60_write_op(WCR, addr, value);
}
void enc28j60_init(uint8_t* mac_addr){
SPI_DDR |= (1 << SPI_CS);
SET_CS_PASSIVE();
SPI_DDR |= ((1 << SPI_MOSI) | (1 << SPI_SCK));
SPI_DDR &= ~(1 << SPI_MISO);
SPI_PORT &= ~(1 << SPI_MOSI);
SPI_PORT &= ~(1 << SPI_SCK);
SPCR = ((1 << SPE) | (1 << MSTR));
SPSR |= (1 << SPI2X);
enc28j60_write_op(SC, 0, SC);
next_pkt_ptr = RXSTART_INIT;
// Designate RX addresses
enc28j60_write(ERXSTL, (RXSTART_INIT & 0xFF));
enc28j60_write(ERXSTH, (RXSTART_INIT >> 8));
enc28j60_write(ERXNDL, (RXSTOP_INIT & 0xFF));
enc28j60_write(ERXNDH, (RXSTOP_INIT >> 8));
// Designate TX addresses
enc28j60_write(ETXSTL, (TXSTART_INIT & 0xFF));
enc28j60_write(ETXSTH, (TXSTART_INIT >> 8));
enc28j60_write(ETXNDL, (TXSTOP_INIT & 0xFF));
enc28j60_write(ETXNDH, (TXSTOP_INIT >> 8));
// Configure filters
enc28j60_write(ERXFCON, (UCEN|CRCEN|PMEN|BCEN));
enc28j60_write(EPMM0, 0x3F);
enc28j60_write(EPMM1, 0x30);
enc28j60_write(EPMCSL, 0xF9);
enc28j60_write(EPMCSH, 0xF7);
// MAC initialization
enc28j60_write(MACON1, (MARXEN|TXPAUS|RXPAUS));
enc28j60_write(MACON2, 0x00);
enc28j60_write_op(BFS, MACON3, (PADCFG0|TXCRCEN|FRMLNEN));
enc28j60_write(MAIPGL, 0x12);
enc28j60_write(MAIPGH, 0x0C);
enc28j60_write(MABBIPG, 0x12);
enc28j60_write(MAMXFLL, (MAX_FRAMELEN & 0xFF));
enc28j60_write(MAMXFLH, (MAX_FRAMELEN >> 8));
enc28j60_write(MAADR5, mac_addr[0]);
enc28j60_write(MAADR4, mac_addr[1]);
enc28j60_write(MAADR3, mac_addr[2]);
enc28j60_write(MAADR2, mac_addr[3]);
enc28j60_write(MAADR1, mac_addr[4]);
enc28j60_write(MAADR0, mac_addr[5]);
enc28j60_set_bank(ECON1);
enc28j60_write_op(BFS, ECON1, ENCRXEN);
}
uint16_t enc28j60_recv(uint8_t* buf, uint16_t max_len){
uint16_t rxstat, len;
// Return if no data is available
if(enc28j60_read(EPKTCNT) == 0) return 0;
enc28j60_write(ERDPTL, (next_pkt_ptr & 0xFF));
enc28j60_write(ERDPTH, (next_pkt_ptr >> 8));
next_pkt_ptr = enc28j60_read_op(RBM, 0) | ((uint16_t)enc28j60_read_op(RBM, 0) << 8);
len = enc28j60_read_op(RBM, 0) | ((uint16_t)enc28j60_read_op(RBM, 0) << 8);
len -= 4;
rxstat = enc28j60_read_op(RBM, 0) | ((uint16_t)enc28j60_read_op(RBM, 0) << 8);
// Length sanity check and actual enc28j60_read call
if(len > (max_len - 1)) len = max_len - 1;
if((rxstat & 0x80) == 0) len = 0;
else enc28j60_read_buffer(buf, len);
// Update next packet pointer
enc28j60_write(ERXRDPTL, (next_pkt_ptr & 0xFF));
enc28j60_write(ERXRDPTH, (next_pkt_ptr >> 8));
if(((next_pkt_ptr - 1) < RXSTART_INIT) || ((next_pkt_ptr - 1) > RXSTOP_INIT)){
enc28j60_write(ERXRDPTL, (RXSTOP_INIT & 0xFF));
enc28j60_write(ERXRDPTH, (RXSTOP_INIT >> 8));
}
else{
enc28j60_write(ERXRDPTL, ((next_pkt_ptr - 1) & 0xFF));
enc28j60_write(ERXRDPTH, ((next_pkt_ptr - 1) >> 8));
}
enc28j60_write_op(BFS, ECON2, PKTDEC);
return len;
}
void enc28j60_send(uint8_t* buf, uint16_t len){
// Wait for any current transmission to finish
while(enc28j60_read_op(RCR, ECON1) & TXRTS){
if(enc28j60_read(EIR) & TXERIF){
enc28j60_write_op(BFS, ECON1, TXRST);
enc28j60_write_op(BFC, ECON1, TXRST);
}
}
enc28j60_write(EWRPTL, (TXSTART_INIT & 0xFF));
enc28j60_write(EWRPTH, (TXSTART_INIT >> 8));
enc28j60_write(ETXNDL, ((TXSTART_INIT + len) & 0xFF));
enc28j60_write(ETXNDH, ((TXSTART_INIT + len) >> 8));
enc28j60_write_op(WBM, 0, 0x00);
enc28j60_write_buffer(buf, len);
enc28j60_write_op(BFS, ECON1, TXRTS);
}