aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/octoclock/lib
diff options
context:
space:
mode:
authorNicholas Corgan <nick.corgan@ettus.com>2014-08-15 12:57:10 -0700
committerNicholas Corgan <nick.corgan@ettus.com>2014-08-20 09:42:26 -0700
commit2de96cd57c3f19bfa778ccad280ad19170af0967 (patch)
tree88e5235bbbc1df0dd5d6ce1883203a09f3e07ea3 /firmware/octoclock/lib
parent9fb6c2919ad9e7e736c837186861b362ba80cdfa (diff)
downloaduhd-2de96cd57c3f19bfa778ccad280ad19170af0967.tar.gz
uhd-2de96cd57c3f19bfa778ccad280ad19170af0967.tar.bz2
uhd-2de96cd57c3f19bfa778ccad280ad19170af0967.zip
OctoClock: bugfixes/improvements
* Fixed Ethernet initialization problem * Improved external reference detection * Added gratuitous ARP, sent upon power-up * Tweaked host-side timing for initialization and firmware burning * Fixed logic for dealing with firmware incompatibility * Misc efficiency/reliability improvements to firmware's network code
Diffstat (limited to 'firmware/octoclock/lib')
-rw-r--r--firmware/octoclock/lib/CMakeLists.txt2
-rw-r--r--firmware/octoclock/lib/enc28j60.c19
-rw-r--r--firmware/octoclock/lib/network.c87
-rw-r--r--firmware/octoclock/lib/state.c32
-rw-r--r--firmware/octoclock/lib/udp_handlers.c28
5 files changed, 114 insertions, 54 deletions
diff --git a/firmware/octoclock/lib/CMakeLists.txt b/firmware/octoclock/lib/CMakeLists.txt
index 006af287a..3c992399e 100644
--- a/firmware/octoclock/lib/CMakeLists.txt
+++ b/firmware/octoclock/lib/CMakeLists.txt
@@ -33,5 +33,5 @@ ENDIF(OCTOCLOCK_DEBUG)
ADD_LIBRARY(octoclock ${lib_files})
SET_TARGET_PROPERTIES(octoclock
- PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} -O2"
+ PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} -O2 -g"
)
diff --git a/firmware/octoclock/lib/enc28j60.c b/firmware/octoclock/lib/enc28j60.c
index 0e8c1fa3c..f0bbee0e7 100644
--- a/firmware/octoclock/lib/enc28j60.c
+++ b/firmware/octoclock/lib/enc28j60.c
@@ -198,8 +198,23 @@ void enc28j60Init(u08* macaddr)
// perform system reset
enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
- // check CLKRDY bit to see if reset is complete
- _delay_us(51);
+
+ /*
+ * "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
diff --git a/firmware/octoclock/lib/network.c b/firmware/octoclock/lib/network.c
index b7de18f3d..bb49de4f6 100644
--- a/firmware/octoclock/lib/network.c
+++ b/firmware/octoclock/lib/network.c
@@ -19,11 +19,13 @@
#include <string.h>
#include <avr/eeprom.h>
+#include <avr/interrupt.h>
#include <lwip/ip.h>
#include <lwip/udp.h>
#include <lwip/icmp.h>
+#include <debug.h>
#include <octoclock.h>
#include <network.h>
@@ -61,10 +63,6 @@ static uint32_t chksum_buffer(
**********************************************************************/
static eth_mac_addr_t _local_mac_addr;
static struct ip_addr _local_ip_addr;
-void register_addrs(const eth_mac_addr_t *mac_addr, const struct ip_addr *ip_addr){
- _local_mac_addr = *mac_addr;
- _local_ip_addr = *ip_addr;
-}
struct listener_entry {
unsigned short port;
@@ -378,28 +376,75 @@ handle_eth_packet(size_t recv_len)
return; // Not ARP or IPV4, ignore
}
-void network_init(void){
+/***********************************************************************
+ * Timer+GARP stuff
+ **********************************************************************/
+
+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.
+ISR(TIMER1_OVF_vect){
+ num_overflows++;
+ if(!(num_overflows % 6)) send_garp = true;
+}
+
+static void
+send_gratuitous_arp(){
+ send_garp = false;
+
+ //Need to htonl IP address
+ struct ip_addr htonl_ip_addr;
+ htonl_ip_addr.addr = htonl(_local_ip_addr.addr);
+
+ struct arp_eth_ipv4 req _AL4;
+ req.ar_hrd = htons(ARPHRD_ETHER);
+ req.ar_pro = htons(ETHERTYPE_IPV4);
+ req.ar_hln = sizeof(eth_mac_addr_t);
+ req.ar_pln = sizeof(struct ip_addr);
+ req.ar_op = htons(ARPOP_REQUEST);
+ memcpy(req.ar_sha, &_local_mac_addr, sizeof(eth_mac_addr_t));
+ memcpy(req.ar_sip, &htonl_ip_addr, sizeof(struct ip_addr));
+ memset(req.ar_tha, 0x00, sizeof(eth_mac_addr_t));
+ memcpy(req.ar_tip, &htonl_ip_addr, sizeof(struct ip_addr));
+
+ //Send the request with the broadcast MAC address
+ send_pkt(BCAST_MAC_ADDR, htons(ETHERTYPE_ARP), &req, sizeof(req), 0, 0, 0, 0);
+}
+
+// Executed every loop
+void network_check(void){
+ size_t recv_len = enc28j60PacketReceive(512, buf_in);
+ if(recv_len > 0) handle_eth_packet(recv_len);
/*
- * Read MAC address from EEPROM and initialize Ethernet driver. If EEPROM is blank,
- * use default MAC address instead.
+ * Send a gratuitous ARP packet two seconds after Ethernet
+ * initialization.
*/
- eeprom_read_block((void*)octoclock_mac_addr.addr, (void*)OCTOCLOCK_EEPROM_MAC_ADDR, 6);
- if(!memcmp(&octoclock_mac_addr, blank_eeprom_mac, 6)) memcpy(octoclock_mac_addr.addr, default_mac, 6);
-
- enc28j60Init((uint8_t*)&octoclock_mac_addr);
+ if(!sent_initial_garp && (num_overflows == 0 && TCNT1 > (TIMER1_ONE_SECOND*2))){
+ sent_initial_garp = true;
+ send_garp = true;
+ }
- eeprom_read_block((void*)&octoclock_ip_addr, (void*)OCTOCLOCK_EEPROM_IP_ADDR, 4);
- eeprom_read_block((void*)&octoclock_dr_addr, (void*)OCTOCLOCK_EEPROM_DR_ADDR, 4);
- eeprom_read_block((void*)&octoclock_netmask, (void*)OCTOCLOCK_EEPROM_NETMASK, 4);
+ if(send_garp) send_gratuitous_arp();
+}
- //In case of blank EEPROM, load default values
- if(octoclock_ip_addr.addr == blank_eeprom_ip || octoclock_dr_addr.addr == blank_eeprom_ip ||
- octoclock_netmask.addr == blank_eeprom_ip){
- octoclock_ip_addr.addr = default_ip;
- octoclock_dr_addr.addr = default_dr;
- octoclock_netmask.addr = default_netmask;
+void network_init(void){
+ /*
+ * Read MAC address from EEPROM and initialize Ethernet driver. If EEPROM is blank,
+ * use default MAC address instead.
+ */
+ if(eeprom_read_byte(0) == 0xFF){
+ _MAC_ADDR(_local_mac_addr.addr, 0x00,0x80,0x2F,0x11,0x22,0x33);
+ _local_ip_addr.addr = default_ip;
+ using_network_defaults = true;
+ }
+ else{
+ eeprom_read_block((void*)&_local_mac_addr, (void*)OCTOCLOCK_EEPROM_MAC_ADDR, 6);
+ eeprom_read_block((void*)&_local_ip_addr, (void*)OCTOCLOCK_EEPROM_IP_ADDR, 4);
+ using_network_defaults = false;
}
- register_addrs(&octoclock_mac_addr, &octoclock_ip_addr);
+ enc28j60Init((uint8_t*)&_local_mac_addr);
}
diff --git a/firmware/octoclock/lib/state.c b/firmware/octoclock/lib/state.c
index 2adab6fc4..0dbcc6ece 100644
--- a/firmware/octoclock/lib/state.c
+++ b/firmware/octoclock/lib/state.c
@@ -83,20 +83,27 @@ void prefer_external(void){
LEDs_Off();
}
-bool is_ext_ref_present(void){
- volatile uint8_t prev = (PINE & (1<<DDE7));
- volatile uint8_t now;
+static uint8_t prev_PE7 = 0;
+static uint32_t timer0_num_overflows = 0;
- for(uint16_t i = 1; i < 512; i++){
- now = (PINE & (1<<DDE7));
- if(prev != now) return true;
+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;
}
- return false;
-}
+ if(!global_ext_ref_is_present){
+ global_ext_ref_is_present = (prev_PE7 != (PINE & (1<<DDE7)));
+ }
-bool is_gps_present(void){
- return (PIND & (1<<DDD4));
+ timer0_num_overflows++;
}
ref_t which_ref(void){
@@ -108,11 +115,6 @@ ref_t which_ref(void){
return global_which_ref;
}
-void check_what_is_present(void){
- global_ext_ref_is_present = is_ext_ref_present();
- global_gps_present = is_gps_present();
-}
-
switch_pos_t get_switch_pos(void){
uint8_t portC = PINC;
diff --git a/firmware/octoclock/lib/udp_handlers.c b/firmware/octoclock/lib/udp_handlers.c
index daf2a7612..1f20112c9 100644
--- a/firmware/octoclock/lib/udp_handlers.c
+++ b/firmware/octoclock/lib/udp_handlers.c
@@ -36,13 +36,14 @@ void handle_udp_ctrl_packet(
pkt_out.proto_ver = OCTOCLOCK_FW_COMPAT_NUM;
pkt_out.sequence = pkt_in->sequence;
- if(pkt_in->proto_ver == OCTOCLOCK_FW_COMPAT_NUM){
+ //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;
+ send_udp_pkt(OCTOCLOCK_UDP_CTRL_PORT, src, (void*)&pkt_out, sizeof(octoclock_packet_t));
+ }
+ else if(pkt_in->proto_ver == OCTOCLOCK_FW_COMPAT_NUM){
switch(pkt_in->code){
- case OCTOCLOCK_QUERY_CMD:
- pkt_out.code = OCTOCLOCK_QUERY_ACK;
- pkt_out.len = 0;
- break;
-
case SEND_EEPROM_CMD:
pkt_out.code = SEND_EEPROM_ACK;
pkt_out.len = sizeof(octoclock_fw_eeprom_t);
@@ -50,14 +51,11 @@ void handle_udp_ctrl_packet(
octoclock_fw_eeprom_t *eeprom_info = (octoclock_fw_eeprom_t*)pkt_out.data;
//Read values from EEPROM into packet
- eeprom_read_block(eeprom_info, (void*)0, sizeof(octoclock_fw_eeprom_t));
+ eeprom_read_block(eeprom_info, 0, sizeof(octoclock_fw_eeprom_t));
//If EEPROM network fields are not fully populated, copy defaults
- if(eeprom_read_byte((uint8_t*)OCTOCLOCK_EEPROM_IP_ADDR) == 0xFF ||
- eeprom_read_byte((uint8_t*)OCTOCLOCK_EEPROM_DR_ADDR) == 0xFF ||
- eeprom_read_byte((uint8_t*)OCTOCLOCK_EEPROM_MAC_ADDR) == 0xFF){
-
- memcpy(eeprom_info->mac_addr, default_mac, 6);
+ 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;
@@ -86,11 +84,11 @@ void handle_udp_ctrl_packet(
pkt_out.len = 0;
//Write EEPROM data from packet
- eeprom_write_block(eeprom_pkt, (void*)0, sizeof(octoclock_fw_eeprom_t));
+ eeprom_write_block(eeprom_pkt, 0, sizeof(octoclock_fw_eeprom_t));
//Read back and compare to packet to confirm successful write
uint8_t eeprom_contents[sizeof(octoclock_fw_eeprom_t)];
- eeprom_read_block(eeprom_contents, (void*)0, sizeof(octoclock_fw_eeprom_t));
+ 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
: BURN_EEPROM_SUCCESS_ACK;
@@ -103,7 +101,7 @@ void handle_udp_ctrl_packet(
//Populate octoclock_state_t fields
octoclock_state_t *state = (octoclock_state_t*)pkt_out.data;
- state->external_detected = (is_ext_ref_present()) ? 1 : 0;
+ 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();