aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/octoclock/lib
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/octoclock/lib')
-rw-r--r--firmware/octoclock/lib/clkdist.c14
-rw-r--r--firmware/octoclock/lib/enc28j60.c488
-rw-r--r--firmware/octoclock/lib/init.c6
-rw-r--r--firmware/octoclock/lib/network.c40
-rw-r--r--firmware/octoclock/lib/state.c102
-rw-r--r--firmware/octoclock/lib/udp_handlers.c37
-rw-r--r--firmware/octoclock/lib/usart.c8
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;
-}