From 2de96cd57c3f19bfa778ccad280ad19170af0967 Mon Sep 17 00:00:00 2001 From: Nicholas Corgan Date: Fri, 15 Aug 2014 12:57:10 -0700 Subject: 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 --- firmware/octoclock/lib/CMakeLists.txt | 2 +- firmware/octoclock/lib/enc28j60.c | 19 +++++++- firmware/octoclock/lib/network.c | 87 ++++++++++++++++++++++++++--------- firmware/octoclock/lib/state.c | 32 +++++++------ firmware/octoclock/lib/udp_handlers.c | 28 ++++++----- 5 files changed, 114 insertions(+), 54 deletions(-) (limited to 'firmware/octoclock/lib') 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 #include +#include #include #include #include +#include #include #include @@ -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<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(); -- cgit v1.2.3