diff options
Diffstat (limited to 'firmware/octoclock/lib')
| -rw-r--r-- | firmware/octoclock/lib/clkdist.c | 14 | ||||
| -rw-r--r-- | firmware/octoclock/lib/enc28j60.c | 488 | ||||
| -rw-r--r-- | firmware/octoclock/lib/init.c | 6 | ||||
| -rw-r--r-- | firmware/octoclock/lib/network.c | 40 | ||||
| -rw-r--r-- | firmware/octoclock/lib/state.c | 102 | ||||
| -rw-r--r-- | firmware/octoclock/lib/udp_handlers.c | 37 | ||||
| -rw-r--r-- | firmware/octoclock/lib/usart.c | 8 | 
7 files changed, 267 insertions, 428 deletions
diff --git a/firmware/octoclock/lib/clkdist.c b/firmware/octoclock/lib/clkdist.c index ed29510b6..0468ac38e 100644 --- a/firmware/octoclock/lib/clkdist.c +++ b/firmware/octoclock/lib/clkdist.c @@ -21,6 +21,8 @@  #include <clkdist.h>  #include <state.h> +#include <util/delay.h> +  #define wait() for(uint16_t u=14000; u; u--) asm("nop");  #define CLK   (PA0) // Shift by 0 bits @@ -33,7 +35,7 @@  // Table of 32-bit constants to be written to the TI chip's registers. These are  // from the "Special Settings" on Page 35 of the datasheet.  // For the GPS's 10 MHz output -static uint32_t table_Pri_Ref[] = { +static const uint32_t table_Pri_Ref[] = {      Bits_32(1,01010100,0,0),    // Reg 0      Bits_32(1,01010100,0,0),    // Outputs LVCMOS Positive&Negative Active - Non-inverted      Bits_32(1,01010100,0,0), @@ -47,7 +49,7 @@ static uint32_t table_Pri_Ref[] = {  // For the External 10 MHz input LVDS with external termination,   // Effectively DC coupled -static uint32_t table_Sec_Ref[] = { +static const uint32_t table_Sec_Ref[] = {      Bits_32(0001,01010100,0,100000),    // Reg 0 -- use Secondary Reference for all channels      Bits_32(0001,01010100,0,100000),    // Outputs LVCMOS Positive&Negative Active - Non-inverted      Bits_32(0001,01010100,0,100000), @@ -81,7 +83,6 @@ static bool get_bit(uint8_t  bit_number) {  // Send 32 bits to TI chip, LSB first.  // Don't worry about reading any bits back at this time  static void send_SPI(uint32_t bits) { -      // Basically, when the clock is low, one can set MOSI to anything, as it's      // ignored.      set_bit(CE_, Lo);    // Start SPI transaction with TI chip @@ -130,7 +131,8 @@ void setup_TI_CDCE18005(TI_Input_10_MHz which_input) {          for(uint8_t i=0; i<table_size; i++){              temp = table_Pri_Ref[i]<<4;              temp |= i; -            send_SPI(temp); // Make sure the register's address is in the LSBs +            // Make sure the register's address is in the LSBs +            send_SPI(temp);          }      } else {          // is Secondary_Ext -- External 10 MHz input from SMA connector @@ -169,6 +171,10 @@ uint32_t get_TI_CDCE18005(CDCE18005 which_register){      return receive_SPI();  } +void set_TI_CDCE18005(CDCE18005 which_register, uint32_t bits){ +    send_SPI((bits << 4) | which_register); +} +  bool check_TI_CDCE18005(TI_Input_10_MHz which_input,          CDCE18005 which_register) { diff --git a/firmware/octoclock/lib/enc28j60.c b/firmware/octoclock/lib/enc28j60.c index f0bbee0e7..ead7e4ec8 100644 --- a/firmware/octoclock/lib/enc28j60.c +++ b/firmware/octoclock/lib/enc28j60.c @@ -1,337 +1,211 @@ -/*! \file enc28j60.c \brief Microchip ENC28J60 Ethernet Interface Driver. */ -//***************************************************************************** -// -// File Name	: 'enc28j60.c' -// Title		: Microchip ENC28J60 Ethernet Interface Driver -// Author		: Pascal Stang (c)2005 -// Created		: 9/22/2005 -// Revised		: 5/19/2014 -// Version		: 0.1 -// Target MCU	: Atmel AVR series -// Editor Tabs	: 4 -// -// Description	: This driver provides initialization and transmit/receive -//	functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. -// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin -// chip, using an SPI interface to the host processor. -// -//***************************************************************************** +/* + * 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 <http://www.gnu.org/licenses/>. + */  #include <octoclock.h>  #include <net/enc28j60.h> -#include <net/enc28j60conf.h> -#include <avr/io.h>  #include <util/delay.h> -u08 Enc28j60Bank; -u16 NextPacketPtr; - -u08 enc28j60ReadOp(u08 op, u08 address) -{ -	u08 data; -    -	// assert CS -	ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); -	 -	// issue read command -	SPDR = op | (address & ADDR_MASK); -	while(!(SPSR & (1<<SPIF))); -	// read data -	SPDR = 0x00; -	while(!(SPSR & (1<<SPIF))); -	// do dummy read if needed -	if(address & 0x80) -	{ -		SPDR = 0x00; -		while(!(inb(SPSR) & (1<<SPIF))); -	} -	data = SPDR; -	 -	// release CS -	ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); - -	return data; -} +static uint8_t  current_bank; +static uint16_t next_pkt_ptr; -void enc28j60WriteOp(u08 op, u08 address, u08 data) -{ -	// assert CS -	ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); +#define SET_CS_ACTIVE()  SPI_PORT &= ~(1<<SPI_CS); +#define SET_CS_PASSIVE() SPI_PORT |= (1<<SPI_CS); +#define SPI_WAIT()       while(!(SPSR & (1<<SPIF))); -	// issue write command -	SPDR = op | (address & ADDR_MASK); -	while(!(SPSR & (1<<SPIF))); -	// write data -	SPDR = data; -	while(!(SPSR & (1<<SPIF))); +static uint8_t enc28j60_read_op(uint8_t op, uint8_t addr){ +    SET_CS_ACTIVE(); +    SPDR = (op | (addr & ADDR_MASK)); +    SPI_WAIT(); +    SPDR = 0x00; +    SPI_WAIT(); -	// release CS -	ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); -} +    if(addr & 0x80){ +        SPDR = 0x00; +        SPI_WAIT(); +    } -void enc28j60ReadBuffer(u16 len, u08* data) -{ -	// assert CS -	ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); -	 -	// issue read command -	SPDR = ENC28J60_READ_BUF_MEM; -	while(!(SPSR & (1<<SPIF))); -	while(len--) -	{ -		// read data -		SPDR = 0x00; -		while(!(SPSR & (1<<SPIF))); -		*data++ = SPDR; -	}	 -	// release CS -	ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); +    SET_CS_PASSIVE(); +    return SPDR;  } -void enc28j60WriteBuffer(u16 len, u08* data) -{ -	// assert CS -	ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); -	 -	// issue write command -	SPDR = ENC28J60_WRITE_BUF_MEM; -	while(!(SPSR & (1<<SPIF))); -	while(len--) -	{ -		// write data -		SPDR = *data++; -		while(!(SPSR & (1<<SPIF))); -	}	 -	// release CS -	ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); +static void enc28j60_write_op(uint8_t op, uint8_t addr, uint8_t value){ +    SET_CS_ACTIVE(); + +    SPDR = (op | (addr & ADDR_MASK)); +    SPI_WAIT(); +    SPDR = value; +    SPI_WAIT(); + +    SET_CS_PASSIVE();  } -void enc28j60SetBank(u08 address) -{ -	// set the bank (if needed) -	if((address & BANK_MASK) != Enc28j60Bank) -	{ -		// set the bank -		enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); -		enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); -		Enc28j60Bank = (address & BANK_MASK); -	} +static void enc28j60_read_buffer(uint8_t* buf, uint16_t len){ +    SET_CS_ACTIVE(); + +    SPDR = RBM; +    SPI_WAIT(); +    while(len){ +        len--; +        SPDR = 0x00; +        SPI_WAIT(); +        *buf = SPDR; +        buf++; +    } +    *buf = '\0'; + +    SET_CS_PASSIVE();  } -u08 enc28j60Read(u08 address) -{ -	// set the bank -	enc28j60SetBank(address); -	// do the read -	return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); +static void enc28j60_write_buffer(uint8_t* buf, uint16_t len){ +    SET_CS_ACTIVE(); + +    SPDR = WBM; +    SPI_WAIT(); +    while(len){ +        len--; +        SPDR = *buf; +        buf++; +        SPI_WAIT(); +    } + +    SET_CS_PASSIVE();  } -void enc28j60Write(u08 address, u08 data) -{ -	// set the bank -	enc28j60SetBank(address); -	// do the write -	enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); +static void enc28j60_set_bank(uint8_t addr){ +    if((addr & BANK_MASK) != current_bank){ +        enc28j60_write_op(BFC, ECON1, (BSEL1|BSEL0)); +        enc28j60_write_op(BFS, ECON1, ((addr & BANK_MASK) >> 5)); +        current_bank = (addr & BANK_MASK); +    }  } -u16 enc28j60PhyRead(u08 address) -{ -	u16 data; - -	// Set the right address and start the register read operation -	enc28j60Write(MIREGADR, address); -	enc28j60Write(MICMD, MICMD_MIIRD); - -	// wait until the PHY read completes -	while(enc28j60Read(MISTAT) & MISTAT_BUSY); - -	// quit reading -	enc28j60Write(MICMD, 0x00); -	 -	// get data value -	data  = enc28j60Read(MIRDL); -	data |= enc28j60Read(MIRDH); -	// return the data -	return data; +static uint8_t enc28j60_read(uint8_t addr){ +    enc28j60_set_bank(addr); +    return enc28j60_read_op(RCR, addr);  } -void enc28j60PhyWrite(u08 address, u16 data) -{ -	// set the PHY register address -	enc28j60Write(MIREGADR, address); -	 -	// write the PHY data -	enc28j60Write(MIWRL, data);	 -	enc28j60Write(MIWRH, data>>8); - -	// wait until the PHY write completes -	while(enc28j60Read(MISTAT) & MISTAT_BUSY); +static void enc28j60_write(uint8_t addr, uint16_t value){ +    enc28j60_set_bank(addr); +    enc28j60_write_op(WCR, addr, value);  } -void enc28j60Init(u08* macaddr) -{ -	// initialize I/O -	sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_CS); -	sbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); - -	// setup SPI I/O pins -	sbi(ENC28J60_SPI_PORT, ENC28J60_SPI_SCK);	// set SCK hi -	sbi(ENC28J60_SPI_DDR, ENC28J60_SPI_SCK);	// set SCK as output -	cbi(ENC28J60_SPI_DDR, ENC28J60_SPI_MISO);	// set MISO as input -	sbi(ENC28J60_SPI_DDR, ENC28J60_SPI_MOSI);	// set MOSI as output -	sbi(ENC28J60_SPI_DDR, ENC28J60_SPI_SS);		// SS must be output for Master mode to work -	// initialize SPI interface -	// master mode -	sbi(SPCR, MSTR); -	// select clock phase positive-going in middle of data -	cbi(SPCR, CPOL); -	// Data order MSB first -	cbi(SPCR,DORD); -	// switch to f/4 2X = f/2 bitrate -	cbi(SPCR, SPR0); -	cbi(SPCR, SPR1); -	sbi(SPSR, SPI2X); -	// enable SPI -	sbi(SPCR, SPE); - -	// perform system reset -	enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); - -    /* -     * "After sending an SPI Reset command, the PHY -     * clock is stopped but the ESTAT.CLKRDY bit is not -     * cleared. Therefore, polling the CLKRDY bit will not -     * work to detect if the PHY is ready. -     * -     * Additionally, the hardware start-up time of 300 us -     * may expire before the device is ready to operate. -     * -     * Work around -     * After issuing the Reset command, wait at least -     * 1 ms in firmware for the device to be ready." -     * -     * Source: http://ww1.microchip.com/downloads/en/DeviceDoc/80349c.pdf -     */ -	_delay_ms(1); - -	// do bank 0 stuff -	// initialize receive buffer -	// 16-bit transfers, must write low byte first -	// set receive buffer start address -	NextPacketPtr = RXSTART_INIT; -	enc28j60Write(ERXSTL, RXSTART_INIT&0xFF); -	enc28j60Write(ERXSTH, RXSTART_INIT>>8); -	// set receive pointer address -	enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF); -	enc28j60Write(ERXRDPTH, RXSTART_INIT>>8); -	// set receive buffer end -	// ERXND defaults to 0x1FFF (end of ram) -	enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF); -	enc28j60Write(ERXNDH, RXSTOP_INIT>>8); -	// set transmit buffer start -	// ETXST defaults to 0x0000 (beginnging of ram) -	enc28j60Write(ETXSTL, TXSTART_INIT&0xFF); -	enc28j60Write(ETXSTH, TXSTART_INIT>>8); - -	// do bank 2 stuff -	// enable MAC receive -	enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); -	// bring MAC out of reset -	enc28j60Write(MACON2, 0x00); -	// enable automatic padding and CRC operations -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); -	// set inter-frame gap (non-back-to-back) -	enc28j60Write(MAIPGL, 0x12); -	enc28j60Write(MAIPGH, 0x0C); -	// set inter-frame gap (back-to-back) -	enc28j60Write(MABBIPG, 0x12); -	// Set the maximum packet size which the controller will accept -	enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF);	 -	enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8); - -	// do bank 3 stuff -	// write MAC address -	// NOTE: MAC address in ENC28J60 is byte-backward -	enc28j60Write(MAADR5, macaddr[0]); -	enc28j60Write(MAADR4, macaddr[1]); -	enc28j60Write(MAADR3, macaddr[2]); -	enc28j60Write(MAADR2, macaddr[3]); -	enc28j60Write(MAADR1, macaddr[4]); -	enc28j60Write(MAADR0, macaddr[5]); - -	// no loopback of transmitted frames -	enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); - -	// switch to bank 0 -	enc28j60SetBank(ECON1); -	// enable interrutps -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); -	// enable packet reception -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); +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);  } -void enc28j60PacketSend(unsigned int len1, unsigned char* packet1, unsigned int len2, unsigned char* packet2) -{ -	//Errata: Transmit Logic reset -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); -	enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); - -	// Set the write pointer to start of transmit buffer area -	enc28j60Write(EWRPTL, TXSTART_INIT&0xff); -	enc28j60Write(EWRPTH, TXSTART_INIT>>8); -	// Set the TXND pointer to correspond to the packet size given -	enc28j60Write(ETXNDL, (TXSTART_INIT+len1+len2)); -	enc28j60Write(ETXNDH, (TXSTART_INIT+len1+len2)>>8); - -	// write per-packet control byte -	enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); - -	// copy the packet into the transmit buffer -	enc28j60WriteBuffer(len1, packet1); -	if(len2>0) enc28j60WriteBuffer(len2, packet2); -	 -	// send the contents of the transmit buffer onto the network -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); +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;  } -unsigned int enc28j60PacketReceive(unsigned int maxlen, u08* buf) -{ -	u16 rxstat; -	u16 len; - -	// check if a packet has been received and buffered -	if( !enc28j60Read(EPKTCNT) ) -		return 0; - -	// Set the read pointer to the start of the received packet -	enc28j60Write(ERDPTL, (NextPacketPtr)); -	enc28j60Write(ERDPTH, (NextPacketPtr)>>8); -	// read the next packet pointer -	NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); -	NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; -	// read the packet length -	len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); -	len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; -	// read the receive status -	rxstat  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); -	rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; - -	// limit retrieve length -	// (we reduce the MAC-reported length by 4 to remove the CRC) -	len = MIN(len, maxlen); - -	// copy the packet from the receive buffer -	enc28j60ReadBuffer(len, buf); - -	// Move the RX read pointer to the start of the next received packet -	// This frees the memory we just read out -	enc28j60Write(ERXRDPTL, (NextPacketPtr)); -	enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8); - -	// decrement the packet counter indicate we are done with this packet -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, 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);  } diff --git a/firmware/octoclock/lib/init.c b/firmware/octoclock/lib/init.c index 827ccb376..8592aa091 100644 --- a/firmware/octoclock/lib/init.c +++ b/firmware/octoclock/lib/init.c @@ -71,7 +71,7 @@ void setup_atmel_io_ports(){  // /pd_cdcd, /sync_code, /ce need to be 1 (disabled) to start  // all bits are outputs, except PA7 (gps_lock) and PA3 (MISO_CDCE) are inputs -PORTA = Bits_8(00110010); +PORTA = Bits_8(00100010);  DDRA =   1<<DDA6 | 1<<DDA5 | 1<<DDA4 | 1<<DDA2 | 1<<DDA1 | 1<<DDA0;  /* @@ -90,8 +90,8 @@ DDRA =   1<<DDA6 | 1<<DDA5 | 1<<DDA4 | 1<<DDA2 | 1<<DDA1 | 1<<DDA0;   *   */ -PORTB = Bits_8(01100001);        // Initial Value is all zeros -DDRB = 1<<DDB2 | 1<<DDB4 | 1<<DDB7;  // MOSI is an output; the Not Connected pins are also outputs +PORTB = Bits_8(01100001);   // Initial Value is all zeros +DDRB = Bits_8(11110111);    // MOSI is an output; the Not Connected pins are also outputs  /*   * Port C diff --git a/firmware/octoclock/lib/network.c b/firmware/octoclock/lib/network.c index bb49de4f6..22fc2b54b 100644 --- a/firmware/octoclock/lib/network.c +++ b/firmware/octoclock/lib/network.c @@ -27,6 +27,7 @@  #include <debug.h>  #include <octoclock.h> +#include <state.h>  #include <network.h>  #include <net/enc28j60.h> @@ -34,12 +35,14 @@  #include <net/if_arp.h>  #include <net/ethertype.h> +#include <util/delay.h> +  #include "arp_cache.h"  /***********************************************************************   * Constants + Globals   **********************************************************************/ -static const size_t out_buff_size = 512; +static const size_t out_buff_size = ETH_BUF_SIZE;  static const eth_mac_addr_t BCAST_MAC_ADDR = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};  #define MAX_UDP_LISTENERS 10 @@ -139,7 +142,7 @@ send_pkt(      //grab an out buffer and pointer      //select the output buffer based on type of packet      uint8_t *p; -    p = buf_out; +    p = eth_buf;      size_t total_len = 0;      //create a list of all buffers to copy @@ -149,7 +152,7 @@ send_pkt(      //copy each buffer into the out buffer      for (size_t i = 0; i < sizeof(buffs)/sizeof(buffs[0]); i++){          total_len += lens[i]; //use full length (not clipped) -        size_t bytes_remaining = out_buff_size - (size_t)(p - (uint8_t*)buf_out); +        size_t bytes_remaining = out_buff_size - (size_t)(p - (uint8_t*)eth_buf);          if (lens[i] > bytes_remaining) lens[i] = bytes_remaining;          memcpy(p, buffs[i], lens[i]);          p += lens[i]; @@ -161,7 +164,7 @@ send_pkt(      //For some reason, the ENC28J60 won't send the CRC      //if you don't tell it to send another byte after      //the given packet -    enc28j60PacketSend(total_len+1, buf_out, 0, 0); +    enc28j60_send(eth_buf, total_len);  }  static void @@ -333,15 +336,15 @@ handle_arp_packet(struct arp_eth_ipv4 *p, size_t size)  void  handle_eth_packet(size_t recv_len)  { -    eth_hdr_t *eth_hdr = (eth_hdr_t *)buf_in; +    eth_hdr_t *eth_hdr = (eth_hdr_t *)eth_buf;      uint16_t ethertype = htons(eth_hdr->ethertype);      if (ethertype == ETHERTYPE_ARP){ -        struct arp_eth_ipv4 *arp = (struct arp_eth_ipv4 *)(buf_in + sizeof(eth_hdr_t)); +        struct arp_eth_ipv4 *arp = (struct arp_eth_ipv4 *)(eth_buf + sizeof(eth_hdr_t));          handle_arp_packet(arp, recv_len-ETH_HLEN);      }      else if (ethertype == ETHERTYPE_IPV4){ -        struct ip_hdr *ip = (struct ip_hdr *)(buf_in + sizeof(eth_hdr_t)); +        struct ip_hdr *ip = (struct ip_hdr *)(eth_buf + sizeof(eth_hdr_t));          if (_IPH_V(ip) != 4 || _IPH_HL(ip) != 5)	// ignore pkts w/ bad version or options              return; @@ -357,7 +360,7 @@ handle_eth_packet(size_t recv_len)          bool is_my_ip = memcmp(&ip->dest, &htonl_local_ip_addr, sizeof(_local_ip_addr)) == 0;          if (!is_bcast && !is_my_ip) return; -        arp_cache_update(&ip->src, (eth_mac_addr_t *)(((char *)buf_in)+6)); +        arp_cache_update(&ip->src, (eth_mac_addr_t *)(((char *)eth_buf)+6));          switch (_IPH_PROTO(ip)){              case IP_PROTO_UDP: @@ -381,7 +384,6 @@ handle_eth_packet(size_t recv_len)   **********************************************************************/  static bool send_garp = false; -static bool sent_initial_garp = false;  static uint32_t num_overflows = 0;  // Six overflows is the closest overflow count to one minute. @@ -390,6 +392,7 @@ ISR(TIMER1_OVF_vect){      if(!(num_overflows % 6)) send_garp = true;  } +// Send a GARP packet once per minute.  static void  send_gratuitous_arp(){      send_garp = false; @@ -415,18 +418,9 @@ send_gratuitous_arp(){  // Executed every loop  void network_check(void){ -    size_t recv_len = enc28j60PacketReceive(512, buf_in); +    size_t recv_len = enc28j60_recv(eth_buf, ETH_BUF_SIZE);      if(recv_len > 0) handle_eth_packet(recv_len); -    /* -     * Send a gratuitous ARP packet two seconds after Ethernet -     * initialization. -     */ -    if(!sent_initial_garp && (num_overflows == 0 && TCNT1 > (TIMER1_ONE_SECOND*2))){ -        sent_initial_garp = true; -        send_garp = true; -    } -      if(send_garp) send_gratuitous_arp();  } @@ -435,9 +429,10 @@ void network_init(void){       * Read MAC address from EEPROM and initialize Ethernet driver. If EEPROM is blank,       * use default MAC address instead.       */ +    eeprom_busy_wait();      if(eeprom_read_byte(0) == 0xFF){          _MAC_ADDR(_local_mac_addr.addr, 0x00,0x80,0x2F,0x11,0x22,0x33); -        _local_ip_addr.addr = default_ip; +        _local_ip_addr.addr = _IP(192,168,10,3);          using_network_defaults = true;      }      else{ @@ -446,5 +441,8 @@ void network_init(void){          using_network_defaults = false;      } -    enc28j60Init((uint8_t*)&_local_mac_addr); +    enc28j60_init((uint8_t*)&_local_mac_addr); +    init_udp_listeners(); + +    send_garp = true;  } diff --git a/firmware/octoclock/lib/state.c b/firmware/octoclock/lib/state.c index 0dbcc6ece..26e1b783c 100644 --- a/firmware/octoclock/lib/state.c +++ b/firmware/octoclock/lib/state.c @@ -15,110 +15,80 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ -#include <avr/interrupt.h>  #include <avr/io.h> +#include <avrlibdefs.h>  #include <debug.h>  #include <octoclock.h>  #include <clkdist.h>  #include <state.h> -void led(LEDs which, int turn_it_on) { +// Global state variables +volatile bool g_ext_ref_present    = false; +volatile bool g_gps_present        = false; +volatile switch_pos_t g_switch_pos = PREFER_INTERNAL; +volatile ref_t g_ref               = NO_REF; +void led(led_t which, bool on){      // selects the proper bit      uint8_t LED = 0x20 << which; -    if(turn_it_on) +    if(on)          PORTC |= LED;      else          PORTC &= ~LED;  } -void LEDs_Off(void){ -    led(Top,false); -    led(Middle,false); -    led(Bottom,false); +void leds_off(void){ +    led(LED_TOP,    false); +    led(LED_MIDDLE, false); +    led(LED_BOTTOM, false);  } -void force_internal(void){ -    led(Top,true); -    led(Middle,false); -    led(Bottom,true); +static void force_internal(void){ +    led(LED_TOP,    true); +    led(LED_MIDDLE, false); +    led(LED_BOTTOM, true); +    // Tell ClkDist chip to use internal signals +    cli();      setup_TI_CDCE18005(Primary_GPS); +    sei(); -    // Set PPS to Primary (1) n.b.:  "1" in general means "Internal" for all -    // such signals +    // Set PPS to internal      PORTA |= (1<<PA6);  } -void force_external(void){ -    led(Top, false); -    led(Middle, true); -    led(Bottom, true); +static void force_external(void){ +    led(LED_TOP,    false); +    led(LED_MIDDLE, true); +    led(LED_BOTTOM, true); +    // Tell Clkdist chip to use external signals +    cli();      setup_TI_CDCE18005(Secondary_Ext); +    sei(); -    // Set PPS to External +    // Set PPS to external      PORTA &= ~(1<<PA6);  }  void prefer_internal(void){ -    // if internal is NOT OK, then force external -    if(global_gps_present) +    // If internal is NOT OK, then force external +    if(g_gps_present)          force_internal(); -    else if(global_ext_ref_is_present) +    else if(g_ext_ref_present)          force_external();      else -        LEDs_Off(); +        leds_off();  }  void prefer_external(void){ -    // if external is NOT OK, then force internal -    if(global_ext_ref_is_present) +    // If external is NOT OK, then force internal +    if(g_ext_ref_present)          force_external(); -    else if(global_gps_present) +    else if(g_gps_present)          force_internal();      else -        LEDs_Off(); -} - -static uint8_t prev_PE7 = 0; -static uint32_t timer0_num_overflows = 0; - -ISR(TIMER0_OVF_vect){ -    global_gps_present = (PIND & (1<<DDD4)); - -    // Every ~1/10 second -    if(!(timer0_num_overflows % 610)){ -        prev_PE7 = (PINE & (1<<DDE7)); - -        if(get_switch_pos() == UP) prefer_internal(); -        else prefer_external(); - -        global_ext_ref_is_present = false; -    } - -    if(!global_ext_ref_is_present){ -        global_ext_ref_is_present = (prev_PE7 != (PINE & (1<<DDE7))); -    } - -    timer0_num_overflows++; -} - -ref_t which_ref(void){ -    if(!global_gps_present && !global_ext_ref_is_present) global_which_ref = NO_REF; -    else if(global_gps_present && !global_ext_ref_is_present) global_which_ref = INTERNAL; -    else if(!global_gps_present && global_ext_ref_is_present) global_which_ref = EXTERNAL; -    else global_which_ref = (get_switch_pos() == UP) ? INTERNAL : EXTERNAL; - -    return global_which_ref; -} - -switch_pos_t get_switch_pos(void){ -    uint8_t portC = PINC; - -    // UP is prefer internal, -    // DOWN is prefer external -    return (portC & (1<<DDC1)) ? DOWN : UP; +        leds_off();  } diff --git a/firmware/octoclock/lib/udp_handlers.c b/firmware/octoclock/lib/udp_handlers.c index 1f20112c9..49b9b8023 100644 --- a/firmware/octoclock/lib/udp_handlers.c +++ b/firmware/octoclock/lib/udp_handlers.c @@ -36,7 +36,7 @@ void handle_udp_ctrl_packet(      pkt_out.proto_ver = OCTOCLOCK_FW_COMPAT_NUM;      pkt_out.sequence = pkt_in->sequence; -    //If the firmware is incompatible, only respond to queries +    // If the firmware is incompatible, only respond to queries      if(pkt_in->code == OCTOCLOCK_QUERY_CMD){          pkt_out.code = OCTOCLOCK_QUERY_ACK;          pkt_out.len = 0; @@ -50,44 +50,43 @@ void handle_udp_ctrl_packet(                  octoclock_fw_eeprom_t *eeprom_info = (octoclock_fw_eeprom_t*)pkt_out.data; -                //Read values from EEPROM into packet +                // Read values from EEPROM into packet +                eeprom_busy_wait();                  eeprom_read_block(eeprom_info, 0, sizeof(octoclock_fw_eeprom_t)); -                //If EEPROM network fields are not fully populated, copy defaults +                // If EEPROM network fields are not fully populated, copy defaults                  if(using_network_defaults){                      _MAC_ADDR(eeprom_info->mac_addr, 0x00,0x80,0x2F,0x11,0x22,0x33); -                    eeprom_info->ip_addr = default_ip; -                    eeprom_info->dr_addr = default_dr; -                    eeprom_info->netmask = default_netmask; +                    eeprom_info->ip_addr = _IP(192,168,10,3); +                    eeprom_info->dr_addr = _IP(192,168,10,1); +                    eeprom_info->netmask = _IP(255,255,255,0);                  } -                //Check if strings or revision is empty -                if(eeprom_info->serial[0] == 0xFF) memset(eeprom_info->serial, 0, 10); -                if(eeprom_info->name[0] == 0xFF) memset(eeprom_info->name, 0, 10); +                // Check if strings or revision is empty                  if(eeprom_info->revision == 0xFF) eeprom_info->revision = 0;                  break;              case BURN_EEPROM_CMD:{ -                //Confirm length of data +                // Confirm length of data                  if(pkt_in->len != sizeof(octoclock_fw_eeprom_t)){                      pkt_out.code = BURN_EEPROM_FAILURE_ACK;                      break;                  }                  /* -                 * In all cases, a full octoclock_fw_eeprom_t is written to lower the overall -                 * number of writes due to this EEPROM's smaller amount of safe writes.                   * It is up to the host to make sure that the values that should be                   * preserved are present in the octoclock_fw_eeprom_t struct.                   */                  const octoclock_fw_eeprom_t *eeprom_pkt = (octoclock_fw_eeprom_t*)pkt_in->data;                  pkt_out.len = 0; -                //Write EEPROM data from packet +                // Write EEPROM data from packet +                eeprom_busy_wait();                  eeprom_write_block(eeprom_pkt, 0, sizeof(octoclock_fw_eeprom_t)); -                //Read back and compare to packet to confirm successful write +                // Read back and compare to packet to confirm successful write                  uint8_t eeprom_contents[sizeof(octoclock_fw_eeprom_t)]; +                eeprom_busy_wait();                  eeprom_read_block(eeprom_contents, 0, sizeof(octoclock_fw_eeprom_t));                  uint8_t n = memcmp(eeprom_contents, eeprom_pkt, sizeof(octoclock_fw_eeprom_t));                  pkt_out.code = n ? BURN_EEPROM_FAILURE_ACK @@ -99,12 +98,12 @@ void handle_udp_ctrl_packet(                  pkt_out.code = SEND_STATE_ACK;                  pkt_out.len = sizeof(octoclock_state_t); -                //Populate octoclock_state_t fields +                // Populate octoclock_state_t fields                  octoclock_state_t *state = (octoclock_state_t*)pkt_out.data; -                state->external_detected = global_ext_ref_is_present ? 1 : 0; -                state->gps_detected = (PIND & _BV(DDD4)) ? 1 : 0; -                state->which_ref = (uint8_t)which_ref(); -                state->switch_pos = (uint8_t)get_switch_pos(); +                state->external_detected = g_ext_ref_present ? 1 : 0; +                state->gps_detected      = g_gps_present     ? 1 : 0; +                state->which_ref         = (uint8_t)g_ref; +                state->switch_pos        = (uint8_t)g_switch_pos;                  break;              case RESET_CMD: diff --git a/firmware/octoclock/lib/usart.c b/firmware/octoclock/lib/usart.c index 3620ac5e9..a267e43c6 100644 --- a/firmware/octoclock/lib/usart.c +++ b/firmware/octoclock/lib/usart.c @@ -34,16 +34,8 @@ char usart_getc(void){      return UDR1;  } -char usart_getc_noblock(void){ -    return ((UCSR1A & (1 << RXC))) ? UDR1 : -1; -} -  void usart_putc(char ch){      while((UCSR1A & (1 << UDRE1)) == 0);      UDR1 = ch;  } - -void usart_putc_nowait(char ch){ -    if((UCSR1A & (1 << UDRE1)) != 0) UDR1 = ch; -}  | 
