OctoClock bugfixes

* Bumped compatibility version to 3
* firmware: Ethernet, clkdist bugfixes
* lib: fixed invalid rev detection
This commit is contained in:
Nicholas Corgan 2015-06-30 13:36:15 -07:00 committed by Martin Braun
parent 1449687bb4
commit d9656de88f
26 changed files with 859 additions and 925 deletions

View File

@ -31,7 +31,7 @@ if(NOT DEFINED PROGRAMMER)
endif(NOT DEFINED PROGRAMMER)
if(OCTOCLOCK_DEBUG)
message(STATUS "Debug enabled. Interrupts will be disabled.")
message(STATUS "Debug enabled.")
add_definitions(-DDEBUG)
endif(OCTOCLOCK_DEBUG)

View File

@ -49,7 +49,7 @@ add_custom_target(
add_custom_target(
upload_bootloader
${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xFF:m -U flash:w:octoclock_bootloader.hex:i
${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xEF:m -U flash:w:octoclock_bootloader.hex:i
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS octoclock_bootloader_hex
COMMENT "Uploading OctoClock bootloader to device with ${PROGRAMMER}"

View File

@ -23,13 +23,13 @@
#include <avr/eeprom.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <avrlibdefs.h>
#include <octoclock.h>
#include <debug.h>
#include <network.h>
#include <util/delay.h>
#include <net/enc28j60.h>
#include "octoclock/common.h"
@ -40,10 +40,19 @@
*/
#define TIME_PASSED (TCNT1 > (TIMER1_ONE_SECOND*5) || (TIFR & _BV(TOV1)))
//States
static bool received_cmd = false;
static bool done_burning = false;
/*
* States
*/
static bool received_cmd = false; // Received "PREPARE_FW_BURN_CMD" signal
static bool done_burning = false; // Received "FINALIZE_BURNING_CMD" signal
static bool app_checked = false; // Ran validation check on firmware
/*
* After new firmware is burned onto the device, the bootloader calculates its
* CRC and burns it into the EEPROM. When the device boots, this CRC is used
* to validate the firmware before loading it. This struct represents how the
* information is stored in the EEPROM.
*/
typedef struct {
uint16_t fw_len;
uint16_t fw_crc;
@ -51,15 +60,22 @@ typedef struct {
static crc_info_t crc_info;
/*
* What actually burns the firmware onto the device.
*
* Source: http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__boot.html
*/
static void boot_program_page(uint8_t *buf, uint16_t page){
uint16_t i;
// Disable interrupts
uint8_t sreg = SREG;
cli();
eeprom_busy_wait();
boot_page_erase(page);
boot_spm_busy_wait(); // Wait until the memory is erased.
for(i = 0; i < SPM_PAGESIZE; i += 2){
for(uint16_t i = 0; i < SPM_PAGESIZE; i += 2){
// Set up little-endian word.
uint16_t w = *buf++;
w += ((*buf++) << 8);
@ -68,36 +84,31 @@ static void boot_program_page(uint8_t *buf, uint16_t page){
}
boot_page_write(page); // Store buffer in flash page.
boot_spm_busy_wait(); // Wait until the memory is written.
boot_spm_busy_wait(); // Wait until the memory is written.
// Reenable RWW-section again. We need this if we want to jump back
// to the application after bootloading.
boot_rww_enable();
// Restore interrupt state
SREG = sreg;
sei();
}
/*
* Load firmware at given address into packet to send to host.
*/
static void read_firmware(uint16_t addr, octoclock_packet_t *pkt_out){
for(size_t i = 0; i < SPM_PAGESIZE; i++){
pkt_out->data[i] = pgm_read_byte(addr+i);
}
}
void handle_udp_query_packet(
struct socket_address src, struct socket_address dst,
unsigned char *payload, int payload_len
){
const octoclock_packet_t *pkt_in = (octoclock_packet_t*)payload;
//Respond to query packets
if(pkt_in->code == OCTOCLOCK_QUERY_CMD){
octoclock_packet_t pkt_out;
pkt_out.proto_ver = OCTOCLOCK_BOOTLOADER_PROTO_VER;
pkt_out.sequence = pkt_in->sequence;
pkt_out.code = OCTOCLOCK_QUERY_ACK;
pkt_out.len = 0;
send_udp_pkt(OCTOCLOCK_UDP_CTRL_PORT, src, (void*)&pkt_out, sizeof(octoclock_packet_t));
}
}
/*
* Calculate the CRC of the current firmware.
*
* Adapted from _crc16_update in <util/crc16.h>.
*/
static void calculate_crc(uint16_t *crc, uint16_t len){
*crc = 0xFFFF;
@ -110,6 +121,11 @@ static void calculate_crc(uint16_t *crc, uint16_t len){
}
}
/*
* Calculate the CRC of the current firmware. If it matches the
* CRC burned into the EEPROM, the firmware is considered valid,
* and the bootloader can load it.
*/
static bool valid_app(){
crc_info_t crc_eeprom_info;
eeprom_read_block(&crc_eeprom_info, (void*)OCTOCLOCK_EEPROM_APP_LEN, 4);
@ -118,6 +134,26 @@ static bool valid_app(){
return (crc_info.fw_crc == crc_eeprom_info.fw_crc);
}
/*
* UDP handlers
*/
void handle_udp_query_packet(
struct socket_address src, struct socket_address dst,
unsigned char *payload, int payload_len
){
const octoclock_packet_t *pkt_in = (octoclock_packet_t*)payload;
// Respond to uhd::device::find(), identify as bootloader
if(pkt_in->code == OCTOCLOCK_QUERY_CMD){
octoclock_packet_t pkt_out;
pkt_out.proto_ver = OCTOCLOCK_BOOTLOADER_PROTO_VER;
pkt_out.sequence = pkt_in->sequence;
pkt_out.code = OCTOCLOCK_QUERY_ACK;
pkt_out.len = 0;
send_udp_pkt(OCTOCLOCK_UDP_CTRL_PORT, src, (void*)&pkt_out, sizeof(octoclock_packet_t));
}
}
void handle_udp_fw_packet(
struct socket_address src, struct socket_address dst,
unsigned char *payload, int payload_len
@ -132,24 +168,27 @@ void handle_udp_fw_packet(
case PREPARE_FW_BURN_CMD:
received_cmd = true;
done_burning = false;
crc_info.fw_crc = pkt_in->crc;
crc_info.fw_len = pkt_in->len;
pkt_out.code = FW_BURN_READY_ACK;
break;
// Burn firmware sent from the host
case FILE_TRANSFER_CMD:
boot_program_page(pkt_in->data, pkt_in->addr);
pkt_out.code = FILE_TRANSFER_ACK;
pkt_out.addr = pkt_in->addr;
break;
// Send firmware back to the host for verification
case READ_FW_CMD:
pkt_out.code = READ_FW_ACK;
read_firmware(pkt_in->addr, &pkt_out);
break;
// Calculate the CRC of the new firmware and finish
case FINALIZE_BURNING_CMD:
//With stuff verified, burn CRC info into EEPROM
done_burning = true;
calculate_crc(&(crc_info.fw_crc), crc_info.fw_len);
eeprom_write_block(&crc_info, (void*)OCTOCLOCK_EEPROM_APP_LEN, 4);
pkt_out.code = FINALIZE_BURNING_ACK;
break;
@ -170,11 +209,12 @@ void handle_udp_eeprom_packet(
pkt_out.sequence = pkt_in->sequence;
pkt_out.len = 0;
// Restore OctoClock's EEPROM to factory state
if(pkt_in->proto_ver == OCTOCLOCK_FW_COMPAT_NUM){
switch(pkt_in->code){
case CLEAR_EEPROM_CMD:
received_cmd = true;
uint8_t blank_eeprom[103];
uint8_t blank_eeprom[103]; // 103 is offset of CRC info
memset(blank_eeprom, 0xFF, 103);
eeprom_write_block(blank_eeprom, 0, 103);
pkt_out.code = CLEAR_EEPROM_ACK;
@ -189,28 +229,45 @@ void handle_udp_eeprom_packet(
int main(void){
asm("cli");
// Disable watchdog timer
wdt_disable();
//Initialization
// Give interrupts to bootloader
MCUCR = (1<<IVCE);
MCUCR = (1<<IVSEL);
cli();
// Atmega128
setup_atmel_io_ports();
// Start timer
TIMER1_INIT();
// Ethernet stack
network_init();
init_udp_listeners();
register_udp_listener(OCTOCLOCK_UDP_CTRL_PORT, handle_udp_query_packet);
register_udp_listener(OCTOCLOCK_UDP_FW_PORT, handle_udp_fw_packet);
register_udp_listener(OCTOCLOCK_UDP_EEPROM_PORT, handle_udp_eeprom_packet);
//Turn LED's on to show we're in the bootloader
// Turn LED's on to show we're in the bootloader
PORTC |= 0x20;
PORTC |= (0x20<<1);
PORTC |= (0x20<<2);
TIMER1_INIT();
bool app_checked = false;
/*
* This loop determines whether the OctoClock will remain in its bootloader
* state or if it will load the main firmware. After five seconds, it will
* check to see if valid firmware is installed. If so, it will immediately
* load it. Otherwise, it will remain here until firmware is installed.
*
* This process can be stopped by an instruction from the firmware burner
* utility, at which point the OctoClock will remain in bootloader state until
* instructed by the utility to exit the loop and load the new firmware.
*/
while(true){
if(done_burning){
if(valid_app()) break;
else done_burning = false; //Burning somehow failed and wasn't caught
else done_burning = false; // Burning somehow failed and wasn't caught
}
if(!app_checked && !received_cmd && TIME_PASSED){
app_checked = true;
@ -220,16 +277,18 @@ int main(void){
network_check();
}
//Turn LED's off before moving to application
// Turn LED's off before moving to application
PORTC &= ~0x20;
PORTC &= ~(0x20<<1);
PORTC &= ~(0x20<<2);
/*
* Whether the bootloader reaches here through five seconds of inactivity
* or after a firmware burn just finished, it can be assumed that the application
* is valid.
* At this point, the bootloader has determined that there is valid
* firmware installed on the device and that it is OK to load it.
*/
TIMER1_DISABLE();
MCUCR = (1<<IVCE);
MCUCR = 0;
cli();
asm("jmp 0000");
return 0; //Will never get here, but AVR-GCC needs it
}

View File

@ -23,7 +23,7 @@
#include <octoclock.h>
typedef enum {
Reg0, Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7,
Reg0=0, Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7,
Reg8_Status_Control,
Read_Command=0xE,
RAM_EEPROM_Unlock=0x1F,
@ -46,6 +46,8 @@ void reset_TI_CDCE18005(void);
uint32_t get_TI_CDCE18005(CDCE18005 which_register);
void set_TI_CDCE18005(CDCE18005 which_register, uint32_t bits);
bool check_TI_CDCE18005(TI_Input_10_MHz which_input, CDCE18005 which_register);
#endif /* _CLKDIST_H_ */

View File

@ -66,8 +66,8 @@
DEBUG_LOG_HEX(((uint8_t*)&num)[0]);
#define DEBUG_LOG_INT(num) DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[3]); \
DEBUG_LOG_HEX(((uint8_t*)&num)[2]); \
DEBUG_LOG_HEX(((uint8_t*)&num)[1]); \
DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[2]); \
DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[1]); \
DEBUG_LOG_HEX(((uint8_t*)&num)[0]);
#else

View File

@ -27,6 +27,4 @@ gpsdo_cache_state_t gpsdo_state;
void send_gpsdo_cmd(char* buf, uint8_t size);
void process_gpsdo_output(void);
#endif /* _GPSDO_H_ */

View File

@ -1,299 +1,276 @@
/*! \file enc28j60.h \brief Microchip ENC28J60 Ethernet Interface Driver. */
//*****************************************************************************
//
// File Name : 'enc28j60.h'
// Title : Microchip ENC28J60 Ethernet Interface Driver
// Author : Pascal Stang (c)2005
// Created : 9/22/2005
// Revised : 9/22/2005
// Version : 0.1
// Target MCU : Atmel AVR series
// Editor Tabs : 4
//
/// \ingroup network
/// \defgroup enc28j60 Microchip ENC28J60 Ethernet Interface Driver (enc28j60.c)
/// \code #include "net/enc28j60.h" \endcode
/// \par Overview
/// 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/>.
*/
#ifndef ENC28J60_H
#define ENC28J60_H
#ifndef _NET_ENC28J60_H_
#define _NET_ENC28J60_H_
#include <avrlibdefs.h>
#include <avr/io.h>
#include "enc28j60conf.h"
#define SPI_DDR DDRB
#define SPI_PORT PORTB
#define SPI_CS 0
#define SPI_MOSI 2
#define SPI_MISO 3
#define SPI_SCK 1
// ENC28J60 Control Registers
// Control register definitions are a combination of address,
// bank number, and Ethernet/MAC/PHY indicator bits.
// - Register address (bits 0-4)
// - Bank number (bits 5-6)
// - MAC/PHY indicator (bit 7)
#define ADDR_MASK 0x1F
#define BANK_MASK 0x60
#define SPRD_MASK 0x80
// All-bank registers
#define EIE 0x1B
#define EIR 0x1C
#define ESTAT 0x1D
#define ECON2 0x1E
#define ECON1 0x1F
// Bank 0 registers
#define ERDPTL (0x00|0x00)
#define ERDPTH (0x01|0x00)
#define EWRPTL (0x02|0x00)
#define EWRPTH (0x03|0x00)
#define ETXSTL (0x04|0x00)
#define ETXSTH (0x05|0x00)
#define ETXNDL (0x06|0x00)
#define ETXNDH (0x07|0x00)
#define ERXSTL (0x08|0x00)
#define ERXSTH (0x09|0x00)
#define ERXNDL (0x0A|0x00)
#define ERXNDH (0x0B|0x00)
#define ERXRDPTL (0x0C|0x00)
#define ERXRDPTH (0x0D|0x00)
#define ERXWRPTL (0x0E|0x00)
#define ERXWRPTH (0x0F|0x00)
#define EDMASTL (0x10|0x00)
#define EDMASTH (0x11|0x00)
#define EDMANDL (0x12|0x00)
#define EDMANDH (0x13|0x00)
#define EDMADSTL (0x14|0x00)
#define EDMADSTH (0x15|0x00)
#define EDMACSL (0x16|0x00)
#define EDMACSH (0x17|0x00)
// Bank 1 registers
#define EHT0 (0x00|0x20)
#define EHT1 (0x01|0x20)
#define EHT2 (0x02|0x20)
#define EHT3 (0x03|0x20)
#define EHT4 (0x04|0x20)
#define EHT5 (0x05|0x20)
#define EHT6 (0x06|0x20)
#define EHT7 (0x07|0x20)
#define EPMM0 (0x08|0x20)
#define EPMM1 (0x09|0x20)
#define EPMM2 (0x0A|0x20)
#define EPMM3 (0x0B|0x20)
#define EPMM4 (0x0C|0x20)
#define EPMM5 (0x0D|0x20)
#define EPMM6 (0x0E|0x20)
#define EPMM7 (0x0F|0x20)
#define EPMCSL (0x10|0x20)
#define EPMCSH (0x11|0x20)
#define EPMOL (0x14|0x20)
#define EPMOH (0x15|0x20)
#define EWOLIE (0x16|0x20)
#define EWOLIR (0x17|0x20)
#define ERXFCON (0x18|0x20)
#define EPKTCNT (0x19|0x20)
// Bank 2 registers
#define MACON1 (0x00|0x40|0x80)
#define MACON2 (0x01|0x40|0x80)
#define MACON3 (0x02|0x40|0x80)
#define MACON4 (0x03|0x40|0x80)
#define MABBIPG (0x04|0x40|0x80)
#define MAIPGL (0x06|0x40|0x80)
#define MAIPGH (0x07|0x40|0x80)
#define MACLCON1 (0x08|0x40|0x80)
#define MACLCON2 (0x09|0x40|0x80)
#define MAMXFLL (0x0A|0x40|0x80)
#define MAMXFLH (0x0B|0x40|0x80)
#define MAPHSUP (0x0D|0x40|0x80)
#define MICON (0x11|0x40|0x80)
#define MICMD (0x12|0x40|0x80)
#define MIREGADR (0x14|0x40|0x80)
#define MIWRL (0x16|0x40|0x80)
#define MIWRH (0x17|0x40|0x80)
#define MIRDL (0x18|0x40|0x80)
#define MIRDH (0x19|0x40|0x80)
// Bank 3 registers
#define MAADR1 (0x00|0x60|0x80)
#define MAADR0 (0x01|0x60|0x80)
#define MAADR3 (0x02|0x60|0x80)
#define MAADR2 (0x03|0x60|0x80)
#define MAADR5 (0x04|0x60|0x80)
#define MAADR4 (0x05|0x60|0x80)
#define EBSTSD (0x06|0x60)
#define EBSTCON (0x07|0x60)
#define EBSTCSL (0x08|0x60)
#define EBSTCSH (0x09|0x60)
#define MISTAT (0x0A|0x60|0x80)
#define EREVID (0x12|0x60)
#define ECOCON (0x15|0x60)
#define EFLOCON (0x17|0x60)
#define EPAUSL (0x18|0x60)
#define EPAUSH (0x19|0x60)
// PHY registers
#define PHCON1 0x00
#define PHSTAT1 0x01
#define PHHID1 0x02
#define PHHID2 0x03
#define PHCON2 0x10
#define PHSTAT2 0x11
#define PHIE 0x12
#define PHIR 0x13
#define PHLCON 0x14
// Register Masks
#define ADDR_MASK 0x1F
#define BANK_MASK 0x60
#define SPRD_MASK 0x80
// ENC28J60 EIE Register Bit Definitions
#define EIE_INTIE 0x80
#define EIE_PKTIE 0x40
#define EIE_DMAIE 0x20
#define EIE_LINKIE 0x10
#define EIE_TXIE 0x08
#define EIE_WOLIE 0x04
#define EIE_TXERIE 0x02
#define EIE_RXERIE 0x01
// ENC28J60 EIR Register Bit Definitions
#define EIR_PKTIF 0x40
#define EIR_DMAIF 0x20
#define EIR_LINKIF 0x10
#define EIR_TXIF 0x08
#define EIR_WOLIF 0x04
#define EIR_TXERIF 0x02
#define EIR_RXERIF 0x01
// ENC28J60 ESTAT Register Bit Definitions
#define ESTAT_INT 0x80
#define ESTAT_LATECOL 0x10
#define ESTAT_RXBUSY 0x04
#define ESTAT_TXABRT 0x02
#define ESTAT_CLKRDY 0x01
// ENC28J60 ECON2 Register Bit Definitions
#define ECON2_AUTOINC 0x80
#define ECON2_PKTDEC 0x40
#define ECON2_PWRSV 0x20
#define ECON2_VRPS 0x08
// ENC28J60 ECON1 Register Bit Definitions
#define ECON1_TXRST 0x80
#define ECON1_RXRST 0x40
#define ECON1_DMAST 0x20
#define ECON1_CSUMEN 0x10
#define ECON1_TXRTS 0x08
#define ECON1_RXEN 0x04
#define ECON1_BSEL1 0x02
#define ECON1_BSEL0 0x01
// ENC28J60 MACON1 Register Bit Definitions
#define MACON1_LOOPBK 0x10
#define MACON1_TXPAUS 0x08
#define MACON1_RXPAUS 0x04
#define MACON1_PASSALL 0x02
#define MACON1_MARXEN 0x01
// ENC28J60 MACON2 Register Bit Definitions
#define MACON2_MARST 0x80
#define MACON2_RNDRST 0x40
#define MACON2_MARXRST 0x08
#define MACON2_RFUNRST 0x04
#define MACON2_MATXRST 0x02
#define MACON2_TFUNRST 0x01
// ENC28J60 MACON3 Register Bit Definitions
#define MACON3_PADCFG2 0x80
#define MACON3_PADCFG1 0x40
#define MACON3_PADCFG0 0x20
#define MACON3_TXCRCEN 0x10
#define MACON3_PHDRLEN 0x08
#define MACON3_HFRMLEN 0x04
#define MACON3_FRMLNEN 0x02
#define MACON3_FULDPX 0x01
// ENC28J60 MICMD Register Bit Definitions
#define MICMD_MIISCAN 0x02
#define MICMD_MIIRD 0x01
// ENC28J60 MISTAT Register Bit Definitions
#define MISTAT_NVALID 0x04
#define MISTAT_SCAN 0x02
#define MISTAT_BUSY 0x01
// ENC28J60 PHY PHCON1 Register Bit Definitions
#define PHCON1_PRST 0x8000
#define PHCON1_PLOOPBK 0x4000
#define PHCON1_PPWRSV 0x0800
#define PHCON1_PDPXMD 0x0100
// ENC28J60 PHY PHSTAT1 Register Bit Definitions
#define PHSTAT1_PFDPX 0x1000
#define PHSTAT1_PHDPX 0x0800
#define PHSTAT1_LLSTAT 0x0004
#define PHSTAT1_JBSTAT 0x0002
// ENC28J60 PHY PHCON2 Register Bit Definitions
#define PHCON2_FRCLINK 0x4000
#define PHCON2_TXDIS 0x2000
#define PHCON2_JABBER 0x0400
#define PHCON2_HDLDIS 0x0100
// All Banks Registers
#define EIE 0x1B
#define EIR 0x1C
#define ESTAT 0x1D
#define ECON2 0x1E
#define ECON1 0x1F
// ENC28J60 Packet Control Byte Bit Definitions
#define PKTCTRL_PHUGEEN 0x08
#define PKTCTRL_PPADEN 0x04
#define PKTCTRL_PCRCEN 0x02
#define PKTCTRL_POVERRIDE 0x01
// Bank 0 Registers
#define ERDPTL 0x00
#define ERDPTH 0x01
#define EWRPTL 0x02
#define EWRPTH 0x03
#define ETXSTL 0x04
#define ETXSTH 0x05
#define ETXNDL 0x06
#define ETXNDH 0x07
#define ERXSTL 0x08
#define ERXSTH 0x09
#define ERXNDL 0x0A
#define ERXNDH 0x0B
#define ERXRDPTL 0x0C
#define ERXRDPTH 0x0D
#define ERXWRPTL 0x0E
#define ERXWRPTH 0x0F
#define EDMASTL 0x10
#define EDMASTH 0x11
#define EDMANDL 0x12
#define EDMANDH 0x13
#define EDMADSTL 0x14
#define EDMADSTH 0x15
#define EDMACSL 0x16
#define EDMACSH 0x17
// SPI operation codes
#define ENC28J60_READ_CTRL_REG 0x00
#define ENC28J60_READ_BUF_MEM 0x3A
#define ENC28J60_WRITE_CTRL_REG 0x40
#define ENC28J60_WRITE_BUF_MEM 0x7A
#define ENC28J60_BIT_FIELD_SET 0x80
#define ENC28J60_BIT_FIELD_CLR 0xA0
#define ENC28J60_SOFT_RESET 0xFF
// Bank 1 Registers
#define EHT0 0x20
#define EHT1 0x21
#define EHT2 0x22
#define EHT3 0x23
#define EHT4 0x24
#define EHT5 0x25
#define EHT6 0x26
#define EHT7 0x27
#define EPMM0 0x28
#define EPMM1 0x29
#define EPMM2 0x2A
#define EPMM3 0x2B
#define EPMM4 0x2C
#define EPMM5 0x2D
#define EPMM6 0x2E
#define EPMM7 0x2F
#define EPMCSL 0x30
#define EPMCSH 0x31
#define EPMOL 0x34
#define EPMOH 0x35
#define EWOLIE 0x36
#define EWOLIR 0x37
#define ERXFCON 0x38
#define EPKTCNT 0x39
// Bank 2 Register
#define MACON1 0xC0
#define MACON2 0xC1
#define MACON3 0xC2
#define MACON4 0xC3
#define MABBIPG 0xC4
#define MAIPGL 0xC6
#define MAIPGH 0xC7
#define MACLCON1 0xC8
#define MACLCON2 0xC9
#define MAMXFLL 0xCA
#define MAMXFLH 0xCB
#define MAPHSUP 0xCD
#define MICON 0xD1
#define MICMD 0xD2
#define MIREGADR 0xD4
#define MIWRL 0xD6
#define MIWRH 0xD7
#define MIRDL 0xD8
#define MIRDH 0xD9
// buffer boundaries applied to internal 8K ram
// entire available packet buffer space is allocated
#define TXSTART_INIT 0x0000 // start TX buffer at 0
#define RXSTART_INIT 0x0600 // give TX buffer space for one full ethernet frame (~1500 bytes)
#define RXSTOP_INIT 0x1FFF // receive buffer gets the rest
// Bank 3 Registers
#define MAADR1 0xE0
#define MAADR0 0xE1
#define MAADR3 0xE2
#define MAADR2 0xE3
#define MAADR5 0xE4
#define MAADR4 0xE5
#define EBSTSD 0x66
#define EBSTCON 0x67
#define EBSTCSL 0x68
#define EBSTCSH 0x69
#define MISTAT 0xEA
#define EREVID 0x72
#define ECOCON 0x75
#define EFLOCON 0x77
#define EPAUSL 0x78
#define EPAUSH 0x79
#define MAX_FRAMELEN 1518 // maximum ethernet frame length
// PHY Registers
#define PHCON1 0x00
#define PHSTAT1 0x01
#define PHHID1 0x02
#define PHHID2 0x03
#define PHCON2 0x10
#define PHSTAT2 0x11
#define PHIE 0x12
#define PHIR 0x13
#define PHLCON 0x14
// Ethernet constants
#define ETHERNET_MIN_PACKET_LENGTH 0x3C
//#define ETHERNET_HEADER_LENGTH 0x0E
// ERXFCON bit definitions
#define UCEN 0x80
#define ANDOR 0x40
#define CRCEN 0x20
#define PMEN 0x10
#define MPEN 0x08
#define HTEN 0x04
#define MCEN 0x02
#define BCEN 0x01
// setup ports for I/O
//void ax88796SetupPorts(void);
// EIE bit definitions
#define INTIE 0x80
#define PKTIE 0x40
#define DMAIE 0x20
#define LINKIE 0x10
#define TXIE 0x08
#define WOLIE 0x04
#define TXERIE 0x02
#define RXERIE 0x01
//! do a ENC28J60 read operation
u08 enc28j60ReadOp(u08 op, u08 address);
//! do a ENC28J60 write operation
void enc28j60WriteOp(u08 op, u08 address, u08 data);
//! read the packet buffer memory
void enc28j60ReadBuffer(u16 len, u08* data);
//! write the packet buffer memory
void enc28j60WriteBuffer(u16 len, u08* data);
//! set the register bank for register at address
void enc28j60SetBank(u08 address);
//! read ax88796 register
u08 enc28j60Read(u08 address);
//! write ax88796 register
void enc28j60Write(u08 address, u08 data);
//! read a PHY register
u16 enc28j60PhyRead(u08 address);
//! write a PHY register
void enc28j60PhyWrite(u08 address, u16 data);
// EIR bit definitions
#define PKTIF 0x40
#define DMAIF 0x20
#define LINKIF 0x10
#define TXIF 0x08
#define WOLIF 0x04
#define TXERIF 0x02
#define RXERIF 0x01
//! initialize the ethernet interface for transmit/receive
void enc28j60Init(u08* macaddr);
// ESTAT bit definitions
#define INT 0x80
#define LATECOL 0x10
#define RXBUSY 0x04
#define TXABRT 0x02
#define CLKRDY 0x01
//! Packet transmit function.
/// Sends a packet on the network. It is assumed that the packet is headed by a valid ethernet header.
/// \param len Length of packet in bytes.
/// \param packet Pointer to packet data.
/// \param len2 Length of the secound packet in bytes, can be 0.
/// \param packet2 Pointer to the secound packet data, can be NULL.
void enc28j60PacketSend(unsigned int len1, unsigned char* packet1, unsigned int len2, unsigned char* packet2);
// ECON2 bit definitions
#define AUTOINC 0x80
#define PKTDEC 0x40
#define PWRSV 0x20
#define VRPS 0x08
//! Packet receive function.
/// Gets a packet from the network receive buffer, if one is available.
/// The packet will by headed by an ethernet header.
/// \param maxlen The maximum acceptable length of a retrieved packet.
/// \param buf Pointer to buffer.
/// \return Packet length in bytes if a packet was retrieved, zero otherwise.
unsigned int enc28j60PacketReceive(unsigned int maxlen, u08* buf);
// ECON1 bit definitions
#define TXRST 0x80
#define RXRST 0x40
#define DMAST 0x20
#define CSUMEN 0x10
#define TXRTS 0x08
#define ENCRXEN 0x04
#define BSEL1 0x02
#define BSEL0 0x01
#endif
//@}
// MACON1 bit definitions
#define LOOPBK 0x10
#define TXPAUS 0x08
#define RXPAUS 0x04
#define PASSALL 0x02
#define MARXEN 0x01
// MACON2 bit definitions
#define MARST 0x80
#define RNDRST 0x40
#define MARXRST 0x08
#define RFUNRST 0x04
#define MATXRST 0x02
#define TFUNRST 0x01
// MACON3 bit definitions
#define PADCFG2 0x80
#define PADCFG1 0x40
#define PADCFG0 0x20
#define TXCRCEN 0x10
#define PHDRLEN 0x08
#define HFRMLEN 0x04
#define FRMLNEN 0x02
#define FULDPX 0x01
// MICMD bit definitions
#define MIISCAN 0x02
#define MIIRD 0x01
// MISTAT bit definitions
#define NVALID 0x04
#define SCAN 0x02
#define BUSY 0x01
// PHCON1 bit definitions
#define PRST 0x8000
#define PLOOPBK 0x4000
#define PPWRSV 0x0800
#define PDPXMD 0x0100
// PHSTAT1 bit definitions
#define PFDPX 0x1000
#define PHDPX 0x0800
#define LLSTAT 0x0004
#define JBSTAT 0x0002
// PHCON2 bit definitions
#define FRCLINK 0x4000
#define TXDIS 0x2000
#define JABBER 0x0400
#define HDLDIS 0x0100
// Packet Control bit Definitions
#define PHUGEEN 0x08
#define PPADEN 0x04
#define PCRCEN 0x02
#define POVERRIDE 0x01
// SPI Instruction Set
#define RCR 0x00 // Read Control Register
#define RBM 0x3A // Read Buffer Memory
#define WCR 0x40 // Write Control Register
#define WBM 0x7A // Write Buffer Memory
#define BFS 0x80 // Bit Field Set
#define BFC 0xA0 // Bit Field Clear
#define SC 0xFF // Soft Reset
// Buffer
#define RXSTART_INIT 0x0000
#define RXSTOP_INIT (0x1FFF-0x0600-1)
#define TXSTART_INIT (0x1FFF-0x0600)
#define TXSTOP_INIT 0x1FFF
#define MAX_FRAMELEN 1500
void enc28j60_init(uint8_t* mac_addr);
uint16_t enc28j60_recv(uint8_t* buffer, uint16_t max_len);
void enc28j60_send(uint8_t* buffer, uint16_t len);
#endif /* _NET_ENC28J60_H_ */

View File

@ -1,49 +0,0 @@
/*! \file enc28j60conf.h \brief Microchip ENC28J60 Ethernet Interface Driver Configuration. */
//*****************************************************************************
//
// File Name : 'enc28j60conf.h'
// Title : Microchip ENC28J60 Ethernet Interface Driver Configuration
// Author : Pascal Stang
// Created : 10/5/2004
// Revised : 8/22/2005
// Version : 0.1
// Target MCU : Atmel AVR series
// Editor Tabs : 4
//
// Description : This driver provides initialization and transmit/receive
// functions for the ENC28J60 10Mb Ethernet Controller and PHY.
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************
#ifndef ENC28J60CONF_H
#define ENC28J60CONF_H
#include <stdint.h>
typedef uint8_t u08;
typedef uint16_t u16;
typedef uint32_t u32;
// ENC28J60 SPI port
#define ENC28J60_SPI_PORT PORTB
#define ENC28J60_SPI_DDR DDRB
#define ENC28J60_SPI_SCK PORTB1
#define ENC28J60_SPI_MOSI PORTB2
#define ENC28J60_SPI_MISO PORTB3
#define ENC28J60_SPI_SS PORTB0
// ENC28J60 control port
#define ENC28J60_CONTROL_PORT PORTB
#define ENC28J60_CONTROL_DDR DDRB
#define ENC28J60_CONTROL_CS PORTB0
// MAC address for this interface
#define ENC28J60_MAC0 '0'
#define ENC28J60_MAC1 'F'
#define ENC28J60_MAC2 'F'
#define ENC28J60_MAC3 'I'
#define ENC28J60_MAC4 'C'
#define ENC28J60_MAC5 'E'
#endif /* ENC28J60CONF_H */

View File

@ -21,11 +21,8 @@
#include <stdint.h>
// Ethernet MAC address
#pragma pack(push,1)
typedef struct {
uint8_t addr[6];
} eth_mac_addr_t;
#pragma pack(pop)
} eth_mac_addr_t __attribute__((aligned(1)));
#endif /* INCLUDED_ETH_MAC_ADDR_H */

View File

@ -69,17 +69,11 @@
#define _IPH_PROTO_SET(hdr, proto) (hdr)->_ttl_proto = (htons((proto) | (_IPH_TTL(hdr) << 8)))
#define _IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
bool using_network_defaults;
volatile bool using_network_defaults;
// Ethernet I/O buffers
uint8_t buf_in[512];
uint8_t buf_out[512];
// Default values loaded if EEPROM is incomplete
static const uint32_t blank_eeprom_ip = _IP(255,255,255,255);
static const uint32_t default_ip = _IP(192,168,10,3);
static const uint32_t default_dr = _IP(192,168,10,1);
static const uint32_t default_netmask = _IP(255,255,255,0);
#define ETH_BUF_SIZE 512
uint8_t eth_buf[ETH_BUF_SIZE];
typedef void (*udp_receiver_t)(struct socket_address src, struct socket_address dst,
unsigned char *payload, int payload_len);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014 Ettus Research LLC
* Copyright 2014-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
@ -24,17 +24,8 @@
#include <stdint.h>
// Define frequency
#define F_CPU 12500000UL
#define F_CPU 7372800UL
/*
* Timer 0 (8-bit)
* * Set prescaler to 8
* * Enable overflow interrupt
* * Set timer to 0
*/
#define TIMER0_INIT() TCCR0 = (1 << CS01); \
TIMSK |= (1 << TOIE0); \
TCNT0 = 0;
/*
* Timer 1 (16-bit)
* * Set prescaler to 1024
@ -42,8 +33,12 @@
* * Set timer to 0
*/
#define TIMER1_INIT() TCCR1B = (1 << CS12) | (1 << CS10); \
TIMSK |= (1<<TOIE1); \
TCNT1 = 0;
TIMSK |= (1<<TOIE1); \
TCNT1 = 0;
#define TIMER1_DISABLE() TCCR1B = 0; \
TIMSK = 0; \
TCNT1 = 0;
#define TIMER1_ONE_SECOND ((uint32_t)(12207))
@ -94,12 +89,6 @@
* Bits_32(10000000,11111111,10101010,01010101) = 2164238933
*/
typedef enum {
Top,
Middle,
Bottom
} LEDs;
void setup_atmel_io_ports(void);
#endif /* _OCTOCLOCK_H_ */

View File

@ -22,23 +22,24 @@
#include <octoclock.h>
// NOT PRESENT unless proven so...
static ref_t global_which_ref = NO_REF;
static bool global_gps_present = false;
static bool global_ext_ref_is_present = false;
// Global state variables
extern volatile bool g_ext_ref_present;
extern volatile bool g_gps_present;
extern volatile switch_pos_t g_switch_pos;
extern volatile ref_t g_ref;
void led(LEDs which, int turn_it_on);
typedef enum {
LED_TOP, // Internal
LED_MIDDLE, // External
LED_BOTTOM // Status
} led_t;
void LEDs_off(void);
void led(led_t which, bool on);
void force_internal(void);
void leds_off(void);
void prefer_internal(void);
void prefer_external(void);
ref_t which_ref(void);
switch_pos_t get_switch_pos(void);
#endif /* _STATE_H_ */

View File

@ -25,10 +25,6 @@ void usart_init(void);
char usart_getc(void);
char usart_getc_noblock(void);
void usart_putc(char ch);
void usart_putc_nowait(char ch);
#endif /* _USART_H_ */

View File

@ -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) {

View File

@ -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;
static uint8_t current_bank;
static uint16_t next_pkt_ptr;
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);
#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)));
return data;
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();
if(addr & 0x80){
SPDR = 0x00;
SPI_WAIT();
}
SET_CS_PASSIVE();
return SPDR;
}
void enc28j60WriteOp(u08 op, u08 address, u08 data)
{
// assert 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();
// issue write command
SPDR = op | (address & ADDR_MASK);
while(!(SPSR & (1<<SPIF)));
// write data
SPDR = data;
while(!(SPSR & (1<<SPIF)));
SPDR = (op | (addr & ADDR_MASK));
SPI_WAIT();
SPDR = value;
SPI_WAIT();
// release CS
ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS);
SET_CS_PASSIVE();
}
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);
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();
}
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_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 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_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);
}
}
u08 enc28j60Read(u08 address)
{
// set the bank
enc28j60SetBank(address);
// do the read
return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address);
static uint8_t enc28j60_read(uint8_t addr){
enc28j60_set_bank(addr);
return enc28j60_read_op(RCR, addr);
}
void enc28j60Write(u08 address, u08 data)
{
// set the bank
enc28j60SetBank(address);
// do the write
enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data);
static void enc28j60_write(uint8_t addr, uint16_t value){
enc28j60_set_bank(addr);
enc28j60_write_op(WCR, addr, value);
}
u16 enc28j60PhyRead(u08 address)
{
u16 data;
void enc28j60_init(uint8_t* mac_addr){
SPI_DDR |= (1 << SPI_CS);
SET_CS_PASSIVE();
// Set the right address and start the register read operation
enc28j60Write(MIREGADR, address);
enc28j60Write(MICMD, MICMD_MIIRD);
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;
// wait until the PHY read completes
while(enc28j60Read(MISTAT) & MISTAT_BUSY);
// 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));
// quit reading
enc28j60Write(MICMD, 0x00);
// get data value
data = enc28j60Read(MIRDL);
data |= enc28j60Read(MIRDH);
// return the data
return data;
// 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 enc28j60PhyWrite(u08 address, u16 data)
{
// set the PHY register address
enc28j60Write(MIREGADR, address);
// write the PHY data
enc28j60Write(MIWRL, data);
enc28j60Write(MIWRH, data>>8);
uint16_t enc28j60_recv(uint8_t* buf, uint16_t max_len){
uint16_t rxstat, len;
// wait until the PHY write completes
while(enc28j60Read(MISTAT) & MISTAT_BUSY);
// 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 enc28j60Init(u08* macaddr)
{
// initialize I/O
sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_CS);
sbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS);
void enc28j60_send(uint8_t* buf, uint16_t len){
// 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);
// 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);
}
}
// 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 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);
}
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;
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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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:

View File

@ -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;
}

View File

@ -43,7 +43,7 @@ add_custom_target(
)
add_custom_target(
upload_r4
${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x81:m -U lfuse:w:0xFF:m -U flash:w:octoclock_r4_fw.hex:i
${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x81:m -U lfuse:w:0xEF:m -U flash:w:octoclock_r4_fw.hex:i
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS octoclock_r4_fw_hex
COMMENT "Uploading OctoClock firmware to device with ${PROGRAMMER}"

View File

@ -36,7 +36,7 @@
* JTAGEN = [X]
* SPIEN = [X]
* EESAVE = [X]
* BOOTSZ = 4096W_F000
* BOOTSZ = 4095W_F000
* BOOTRST = [ ]
* CKOPT = [X]
* BODLEVEL = 2V7
@ -49,57 +49,165 @@
*
*/
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avrlibdefs.h>
#include <octoclock.h>
#include <clkdist.h>
#include <debug.h>
#include <clkdist.h>
#include <state.h>
#include <network.h>
#include <usart.h>
#include <gpsdo.h>
#include <net/enc28j60.h>
#include <net/udp_handlers.h>
/*
* Timer 3 (16-bit)
* * Set input capture trigger to rising edge
* * Set prescaler to 1
* * Enable overflow interrupt
* * Set timer to 0
*/
#define TIMER3_INIT() TCCR3B = (1 << ICES3) | (1 << CS30); \
ETIMSK |= (1 << TOIE3); \
TCNT3 = 0; \
ICR3 = 0;
/*
* We use TIMER3 as a watchdog timer for external reference
* detection. Once a signal is detected, we allow for five
* timer overflows (~26 ms) without another signal before
* deciding that there is no external reference connected.
*/
#define EXT_REF_TIMEOUT 5
static volatile uint16_t num_overflows = 0;
static uint16_t current_num_overflows = 0;
static uint16_t prev_num_overflows = 0;
static uint16_t current_ICR3 = 0;
static uint16_t prev_ICR3 = 0;
static ref_t prev_ref = NO_REF;
static switch_pos_t prev_switch_pos = PREFER_EXTERNAL;
bool top = false;
ISR(TIMER3_OVF_vect){
num_overflows++;
}
/*******************************************************************************
* Main Routine
*******************************************************************************/
int main(void){
asm("cli");
/*
* Initializations
*/
cli();
// Make sure interrupts belong to us
MCUCR = (1<<IVCE);
MCUCR = 0;
// Initialize global variables
g_ext_ref_present = false;
g_gps_present = false;
g_switch_pos = PREFER_INTERNAL;
g_ref = NO_REF;
// Atmega128
setup_atmel_io_ports();
network_init();
#ifndef DEBUG
asm("sei");
#endif
// Reset ClkDist chip
reset_TI_CDCE18005();
init_udp_listeners();
register_udp_listener(OCTOCLOCK_UDP_CTRL_PORT, handle_udp_ctrl_packet);
register_udp_listener(OCTOCLOCK_UDP_GPSDO_PORT, handle_udp_gpsdo_packet);
DEBUG_INIT(); // Does nothing when not in debug mode
DEBUG_LOG(" "); //Force a newline between runs
// GPSDO communication
usart_init();
//Set initial ClkDist and front panel settings
led(Middle,true);
setup_TI_CDCE18005(Primary_GPS); // 10 MHz from Internal Source
// Ethernet stack
network_init();
register_udp_listener(OCTOCLOCK_UDP_CTRL_PORT, handle_udp_ctrl_packet);
register_udp_listener(OCTOCLOCK_UDP_GPSDO_PORT, handle_udp_gpsdo_packet);
led(Top,true);
PORTA |= (1<<PA6); // PPS from Internal source
// Timers
TIMER1_INIT(); // Network
TIMER3_INIT(); // External reference check
TIMER0_INIT();
TIMER1_INIT();
// Debug (does nothing when not in debug mode)
DEBUG_INIT();
DEBUG_LOG(" "); // Force a newline between runs
while(true) {
leds_off();
sei();
// Check if GPS present (should only need to happen once)
g_gps_present = (PIND & (1<<DDD4));
// State of previous iteration
prev_ref = NO_REF;
prev_switch_pos = PREFER_EXTERNAL;
cli();
prev_ICR3 = ICR3;
sei();
prev_num_overflows = 0;
/*
* Main loop
*/
while(true){
// Check switch position
g_switch_pos = (PINC & (1<<DDC1)) ? PREFER_EXTERNAL : PREFER_INTERNAL;
/*
* Check input capture pin for external reference detection.
*
* 16-bit reads could be corrupted during an interrupt, so
* disable interrupts for safety.
*/
cli();
current_ICR3 = ICR3;
current_num_overflows = num_overflows;
sei();
// Signal detected, reset timer
if(current_ICR3 != prev_ICR3){
cli();
TCNT3 = 0;
num_overflows = 0;
sei();
g_ext_ref_present = true;
}
// Timeout, no external reference detected
if(current_num_overflows >= EXT_REF_TIMEOUT){
g_ext_ref_present = false;
}
// Determine and set reference based on state
if(!g_gps_present && !g_ext_ref_present) g_ref = NO_REF;
else if(g_gps_present && !g_ext_ref_present) g_ref = INTERNAL;
else if(!g_gps_present && g_ext_ref_present) g_ref = EXTERNAL;
else g_ref = (g_switch_pos == PREFER_INTERNAL) ? INTERNAL : EXTERNAL;
if((g_ref != prev_ref) || (g_switch_pos != prev_switch_pos)){
if(g_switch_pos == PREFER_INTERNAL) prefer_internal();
else prefer_external();
}
// Record this iteration's state
prev_ref = g_ref;
prev_switch_pos = g_switch_pos;
prev_ICR3 = current_ICR3;
prev_num_overflows = current_num_overflows;
// Handle incoming Ethernet packets
network_check();
}
}

View File

@ -7,41 +7,24 @@
- Hardware Capabilities:
- Fully integrated timing source with 8-Way distribution (10 MHz and 1 PPS)
- User selection between internal GPSDO (when present) or external 10 MHz/1 PPS source
- Ethernet bootloader for easy firmware upgrade
- Source detection with automatic switch over in case of failure or disconnect
- Streaming GPS time and NMEA strings over Ethernet (OctoClock-G only)
\section octoclock_load Loading Firmware onto the Octoclock
\subsection bootloader OctoClock bootloader
If you purchased your OctoClock device before Ethernet functionality was introduced, or if your unit's
bootloader has somehow become corrupted, you must burn the bootloader onto the device before you can load
the primary firmware.
To load the bootloader onto the OctoClock, two things are needed:
- AVR programmer
- AVRdude software
Connect the AVR programmer to J108, as specified on the <a href="http://files.ettus.com/schematics/octoclock/octoclock.pdf">
schematics</a>. Once you verify that the programmer is properly connected, run the following commands to burn the firmware:
First, the OctoClock's bootloader needs to be loaded onto the device. Connect the AVR programmer to J108, as
specified on the <a href="http://files.ettus.com/schematics/octoclock/octoclock.pdf">schematics</a>. Once you
verify that the programmer is properly connected, run the following commands to load the bootloader:
cd <install path>/share/uhd/images
avrdude -p atmega128 -c <programmer name> -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xFF:m -U flash:w:octoclock_bootloader.hex:i
avrdude -p atmega128 -c <programmer name> -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xEF:m -U flash:w:octoclock_r4_fw.hex:i
When the bootloader is loaded, it will have a default IP address of `192.168.10.3`.
\b Note:
On Linux, `sudo avrdude ...` might be necessary to gain access to the programmer.
Once the bootloader has been burned, power-cycle your OctoClock device and refer to the below instructions on burning the OctoClock's
primary firmware.
\subsection application Primary Octoclock firmware
To load firmware onto the OctoClock, you must use the `octoclock_firmware_burner` utility, specifying the IP
address of the OctoClock device, as follows:
octoclock_firmware_burner --addr=192.168.10.3
\section octoclock_network Setting Up Networking
\subsection host_interface Setting up the host interface

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014 Ettus Research LLC
* Copyright 2014-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
@ -42,11 +42,11 @@ extern "C" {
* only valid C code should go in this section.
*/
//These values are placed in the octoclock_packet_t.proto_ver field
// These values are placed in the octoclock_packet_t.proto_ver field
#define OCTOCLOCK_BOOTLOADER_PROTO_VER 1234
#define OCTOCLOCK_FW_COMPAT_NUM 2
#define OCTOCLOCK_FW_COMPAT_NUM 3
//UDP ports assigned for different tasks
// UDP ports assigned for different tasks
#define OCTOCLOCK_UDP_CTRL_PORT 50000
#define OCTOCLOCK_UDP_GPSDO_PORT 50001
#define OCTOCLOCK_UDP_FW_PORT 50002
@ -98,11 +98,21 @@ typedef enum {
} ref_t;
typedef enum {
UP,
DOWN
PREFER_INTERNAL,
PREFER_EXTERNAL
} switch_pos_t;
/*
* Some versions of AVR-GCC ignore #pragma pack, so
* if AVR-GCC is being used, use __attribute__
* instead.
*/
#ifdef AVR
#define __AVR_ALIGNED__ __attribute__((aligned(1)))
#else
#define __AVR_ALIGNED__
#pragma pack(push,1)
#endif
// Structure of values in EEPROM, starting in location 0
typedef struct {
@ -113,34 +123,37 @@ typedef struct {
uint8_t serial[10];
uint8_t name[10];
uint8_t revision;
} octoclock_fw_eeprom_t;
} octoclock_fw_eeprom_t __AVR_ALIGNED__;
typedef struct {
uint8_t external_detected;
uint8_t gps_detected;
uint8_t which_ref;
uint8_t switch_pos;
} octoclock_state_t;
} octoclock_state_t __AVR_ALIGNED__;
typedef struct {
uint8_t num_wraps;
uint8_t pos;
} gpsdo_cache_state_t;
} gpsdo_cache_state_t __AVR_ALIGNED__;
typedef struct {
uint32_t proto_ver;
uint32_t sequence;
uint8_t code;
union {
uint16_t len;
uint16_t crc;
gpsdo_cache_state_t state;
uint16_t poolsize;
uint16_t addr;
};
uint8_t data[256];
} octoclock_packet_t;
uint16_t len;
} octoclock_packet_t __AVR_ALIGNED__;
#ifndef AVR
#pragma pack(pop)
#endif
#ifdef __cplusplus
}

View File

@ -357,14 +357,14 @@ void octoclock_impl::_get_state(const std::string &oc){
}
uhd::dict<ref_t, std::string> _ref_strings = boost::assign::map_list_of
(NO_REF, "none")
(NO_REF, "none")
(INTERNAL, "internal")
(EXTERNAL, "external")
;
uhd::dict<switch_pos_t, std::string> _switch_pos_strings = boost::assign::map_list_of
(UP, "Prefer internal")
(DOWN, "Prefer external")
(PREFER_INTERNAL, "Prefer internal")
(PREFER_EXTERNAL, "Prefer external")
;
sensor_value_t octoclock_impl::_ext_ref_detected(const std::string &oc){
@ -410,7 +410,7 @@ boost::uint32_t octoclock_impl::_get_time(const std::string &oc){
}
std::string octoclock_impl::_get_images_help_message(const std::string &addr){
const std::string image_name = "octoclock_r4_fw.bin";
const std::string image_name = "octoclock_r4_fw.hex";
//Check to see if image is in default location
std::string image_location;

View File

@ -1,5 +1,5 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2014-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
@ -74,8 +74,23 @@ boost::uint8_t firmware_image[MAX_FIRMWARE_SIZE];
size_t firmware_size = 0;
boost::uint8_t octoclock_data[udp_simple::mtu];
octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t *>(octoclock_data);
std::string firmware_path;
std::string firmware_path, actual_firmware_path;
size_t num_blocks = 0;
bool hex = true;
static uint16_t calculate_crc(boost::uint8_t* buffer, boost::uint16_t len){
boost::uint16_t crc = 0xFFFF;
for(size_t i = 0; i < len; i++){
crc ^= buffer[i];
for(boost::uint8_t j = 0; j < 8; ++j){
if(crc & 1) crc = (crc >> 1) ^ 0xA001;
else crc = (crc >> 1);
}
}
return crc;
}
/*
* Functions
@ -121,26 +136,25 @@ device_addrs_t bootloader_find(const std::string &ip_addr){
}
void read_firmware(){
std::ifstream firmware_file(firmware_path.c_str(), std::ios::binary);
firmware_file.seekg(0, std::ios::end);
firmware_size = size_t(firmware_file.tellg());
std::ifstream firmware_file(actual_firmware_path.c_str(), std::ios::binary);
firmware_size = size_t(fs::file_size(actual_firmware_path));
if(firmware_size > MAX_FIRMWARE_SIZE){
firmware_file.close();
throw uhd::runtime_error(str(boost::format("Firmware file too large: %d > %d")
% firmware_size % (MAX_FIRMWARE_SIZE)));
}
firmware_file.seekg(0, std::ios::beg);
firmware_file.read((char*)firmware_image, firmware_size);
firmware_file.close();
num_blocks = (firmware_size % BLOCK_SIZE) ? (firmware_size / BLOCK_SIZE)
: ((firmware_size / BLOCK_SIZE) + 1);
num_blocks = (firmware_size % BLOCK_SIZE) ? ((firmware_size / BLOCK_SIZE) + 1)
: (firmware_size / BLOCK_SIZE);
}
void burn_firmware(udp_simple::sptr udp_transport){
octoclock_packet_t pkt_out;
pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand());
pkt_out.len = uhd::htonx<boost::uint16_t>((boost::uint16_t)firmware_size);
pkt_out.len = (boost::uint16_t)firmware_size;
pkt_out.crc = calculate_crc(firmware_image, firmware_size);
size_t len = 0, current_pos = 0;
//Tell OctoClock not to jump to application, wait for us instead
@ -149,6 +163,7 @@ void burn_firmware(udp_simple::sptr udp_transport){
if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)) std::cout << "ready." << std::endl;
else{
std::cout << std::endl;
if(hex) fs::remove(actual_firmware_path);
throw uhd::runtime_error("Could not get OctoClock in valid state for firmware download.");
}
@ -165,7 +180,7 @@ void burn_firmware(udp_simple::sptr udp_transport){
<< "% (" << (i+1) << "/" << num_blocks << " blocks)" << std::flush;
memset(pkt_out.data, 0, BLOCK_SIZE);
memcpy((void*)(pkt_out.data), &firmware_image[i*BLOCK_SIZE], std::min(int(firmware_size-current_pos), BLOCK_SIZE));
memcpy((void*)(pkt_out.data), &firmware_image[i*BLOCK_SIZE], BLOCK_SIZE);
bool success = false;
while(num_tries <= 5){
@ -181,6 +196,7 @@ void burn_firmware(udp_simple::sptr udp_transport){
}
if(not success){
std::cout << std::endl;
if(hex) fs::remove(actual_firmware_path);
throw uhd::runtime_error("Failed to burn firmware to OctoClock!");
}
@ -196,7 +212,6 @@ void verify_firmware(udp_simple::sptr udp_transport){
pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand());
size_t len = 0, current_pos = 0;
for(size_t i = 0; i < num_blocks; i++){
pkt_out.sequence++;
pkt_out.addr = i*BLOCK_SIZE;
@ -208,11 +223,13 @@ void verify_firmware(udp_simple::sptr udp_transport){
if(memcmp((void*)(pkt_in->data), &firmware_image[i*BLOCK_SIZE],
std::min(int(firmware_size-current_pos), BLOCK_SIZE))){
std::cout << std::endl;
if(hex) fs::remove(actual_firmware_path);
throw uhd::runtime_error("Failed to verify OctoClock firmware!");
}
}
else{
std::cout << std::endl;
if(hex) fs::remove(actual_firmware_path);
throw uhd::runtime_error("Failed to verify OctoClock firmware!");
}
}
@ -230,6 +247,7 @@ bool reset_octoclock(const std::string &ip_addr){
UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, RESET_CMD, pkt_out, len, octoclock_data);
if(not UHD_OCTOCLOCK_PACKET_MATCHES(RESET_ACK, pkt_out, pkt_in, len)){
std::cout << std::endl;
if(hex) fs::remove(actual_firmware_path);
throw uhd::runtime_error("Failed to place device in state to receive firmware.");
}
@ -246,11 +264,13 @@ void finalize(udp_simple::sptr udp_transport){
UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, FINALIZE_BURNING_CMD, pkt_out, len, octoclock_data);
if(not UHD_OCTOCLOCK_PACKET_MATCHES(FINALIZE_BURNING_ACK, pkt_out, pkt_in, len)){
std::cout << std::endl;
if(hex) fs::remove(actual_firmware_path);
std::cout << "no ACK. Bootloader may not have loaded application." << std::endl;
}
}
int UHD_SAFE_MAIN(int argc, char *argv[]){
int UHD_SAFE_MAIN(UHD_UNUSED(int argc), UHD_UNUSED(char *argv[])){
std::string ip_addr;
po::options_description desc("Allowed options");
desc.add_options()
@ -300,7 +320,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
throw uhd::runtime_error(str(boost::format("This filepath does not exist: %s") % firmware_path));
}
}
else firmware_path = find_image_path("octoclock_r4_fw.bin");
else firmware_path = find_image_path("octoclock_r4_fw.hex");
//If Intel hex file detected, convert to binary
std::string ext = fs::extension(firmware_path);
@ -312,9 +332,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
% time_spec_t::get_system_time().get_full_secs()));
Hex2Bin(firmware_path.c_str(), temp_bin.string().c_str(), false);
firmware_path = temp_bin.string();
actual_firmware_path = temp_bin.string();
}
else if(ext == ".bin"){
hex = false;
actual_firmware_path = firmware_path;
std::cout << "Found firmware at path: " << firmware_path << std::endl;
}
else throw uhd::runtime_error("The firmware file has in improper extension (must be .hex or .bin).");
@ -327,6 +349,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
if(reset_octoclock(ip_addr)) std::cout << "successful." << std::endl;
else{
std::cout << "failed." << std::endl;
if(hex) fs::remove(actual_firmware_path);
throw uhd::runtime_error("Failed to reset OctoClock device into its bootloader.");
}
}
@ -334,6 +357,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
}
else{
std::cout << "failed." << std::endl;
if(hex) fs::remove(actual_firmware_path);
throw uhd::runtime_error("Could not find OctoClock with given IP address!");
}
@ -354,7 +378,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
if(octoclocks.size() == 1){
if(octoclocks[0]["type"] == "octoclock-bootloader"){
std::cout << std::endl;
throw uhd::runtime_error("OctoClock failed to leave bootloader state.");
if(hex) fs::remove(actual_firmware_path);
throw uhd::runtime_error("Firmware did not load properly.");
}
else{
std::cout << "found." << std::endl << std::endl
@ -363,8 +388,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
}
else{
std::cout << std::endl;
if(hex) fs::remove(actual_firmware_path);
throw uhd::runtime_error("Failed to reinitialize OctoClock.");
}
if(hex) fs::remove(actual_firmware_path);
return EXIT_SUCCESS;
}