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/bootloader/CMakeLists.txt | 2 +- firmware/octoclock/bootloader/main.c | 40 +++++----- firmware/octoclock/include/debug.h | 23 ++++-- firmware/octoclock/include/net/eth_mac_addr.h | 2 + firmware/octoclock/include/network.h | 20 +++-- firmware/octoclock/include/octoclock.h | 21 ++++-- firmware/octoclock/include/state.h | 8 -- 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 ++++--- firmware/octoclock/octoclock_r4/CMakeLists.txt | 3 + .../octoclock/octoclock_r4/octoclock_r4_main.c | 71 +++--------------- 14 files changed, 193 insertions(+), 165 deletions(-) (limited to 'firmware') diff --git a/firmware/octoclock/bootloader/CMakeLists.txt b/firmware/octoclock/bootloader/CMakeLists.txt index eecb87002..04bcfc492 100644 --- a/firmware/octoclock/bootloader/CMakeLists.txt +++ b/firmware/octoclock/bootloader/CMakeLists.txt @@ -31,7 +31,7 @@ add_executable(octoclock_bootloader.elf ${bootloader_sources}) # Details: http://savannah.nongnu.org/bugs/?36410 # set_target_properties(octoclock_bootloader.elf PROPERTIES - COMPILE_FLAGS "${CMAKE_C_FLAGS} -Os -D__AVR_LIBC_DEPRECATED_ENABLE__ -Wall" + COMPILE_FLAGS "${CMAKE_C_FLAGS} -Os -D__AVR_LIBC_DEPRECATED_ENABLE__ -D__BOOTLOADER__ -Wall" LINK_FLAGS "-Os -Wl,--relax,--section-start=.text=0x1E000,-Map=octoclock_bootloader.map,--cref" ) diff --git a/firmware/octoclock/bootloader/main.c b/firmware/octoclock/bootloader/main.c index b2f6730ac..5e2e6f17e 100644 --- a/firmware/octoclock/bootloader/main.c +++ b/firmware/octoclock/bootloader/main.c @@ -28,12 +28,17 @@ #include #include +#include + #include #include "octoclock/common.h" -#define wait() for(uint16_t u=4000; u; u--) asm("nop"); -#define TIME_PASSED (TCNT1 > FIVE_SECONDS || (TIFR & _BV(TOV1))) +/* + * The number for 5 seconds is close enough to 65535 that the + * timer may have overflowed before the main loop queries it. + */ +#define TIME_PASSED (TCNT1 > (TIMER1_ONE_SECOND*5) || (TIFR & _BV(TOV1))) //States static bool received_cmd = false; @@ -83,8 +88,7 @@ void handle_udp_query_packet( const octoclock_packet_t *pkt_in = (octoclock_packet_t*)payload; //Respond to query packets - if(pkt_in->proto_ver == OCTOCLOCK_FW_COMPAT_NUM && - pkt_in->code == OCTOCLOCK_QUERY_CMD){ + 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; @@ -134,7 +138,6 @@ void handle_udp_fw_packet( case FILE_TRANSFER_CMD: boot_program_page(pkt_in->data, pkt_in->addr); - wait(); //Necessary for some reason pkt_out.code = FILE_TRANSFER_ACK; break; @@ -144,7 +147,7 @@ void handle_udp_fw_packet( break; case FINALIZE_BURNING_CMD: - //With stuff verified, burn values into EEPROM + //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); @@ -196,17 +199,12 @@ int main(void){ register_udp_listener(OCTOCLOCK_UDP_FW_PORT, handle_udp_fw_packet); register_udp_listener(OCTOCLOCK_UDP_EEPROM_PORT, handle_udp_eeprom_packet); - /* - * Initialize AVR timer. This will be used to detect how much time has gone - * by with no contact from the octoclock_burn_firmware utility. If 5 seconds go by - * with no PREPARE_FW_BURN_CMD packet, the bootloader will jump into the - * existing application. - * - * This timer is polled by the main loop with each iteration instead of using - * an interrupt. - */ - TCCR1B = (1 << CS12) | (1 << CS10); - TCNT1 = 0; + //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; while(true){ @@ -219,10 +217,14 @@ int main(void){ if(valid_app()) break; } - size_t recv_len = enc28j60PacketReceive(512, buf_in); - if(recv_len > 0) handle_eth_packet(recv_len); + network_check(); } + //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 diff --git a/firmware/octoclock/include/debug.h b/firmware/octoclock/include/debug.h index 3b89140f6..ee0618bc6 100644 --- a/firmware/octoclock/include/debug.h +++ b/firmware/octoclock/include/debug.h @@ -18,7 +18,8 @@ #ifndef _DEBUG_H_ #define _DEBUG_H_ -#if DEBUG +//Only expose these macros to the firmware, and only if specified +#if defined(DEBUG) && !defined(__BOOTLOADER__) #include #include @@ -41,17 +42,17 @@ #define DEBUG_LOG_CHAR_ARR(arr,len) DEBUG_LOG_CHAR_ARR_NNL(arr,len); \ DEBUG_LOG(" ") -#define DEBUG_LOG_MAC(mac_addr) DEBUG_LOG_HEX_NNL(mac_addr.addr[0]); \ +#define DEBUG_LOG_MAC(mac_addr) DEBUG_LOG_HEX_NNL(mac_addr[0]); \ DEBUG_LOG_NNL(":"); \ - DEBUG_LOG_HEX_NNL(mac_addr.addr[1]); \ + DEBUG_LOG_HEX_NNL(mac_addr[1]); \ DEBUG_LOG_NNL(":"); \ - DEBUG_LOG_HEX_NNL(mac_addr.addr[2]); \ + DEBUG_LOG_HEX_NNL(mac_addr[2]); \ DEBUG_LOG_NNL(":"); \ - DEBUG_LOG_HEX_NNL(mac_addr.addr[3]); \ + DEBUG_LOG_HEX_NNL(mac_addr[3]); \ DEBUG_LOG_NNL(":"); \ - DEBUG_LOG_HEX_NNL(mac_addr.addr[4]); \ + DEBUG_LOG_HEX_NNL(mac_addr[4]); \ DEBUG_LOG_NNL(":"); \ - DEBUG_LOG_HEX(mac_addr.addr[5]); + DEBUG_LOG_HEX(mac_addr[5]); #define DEBUG_LOG_IP(ip_addr) DEBUG_LOG_BYTE_NNL(ip4_addr1(&ip_addr)); \ DEBUG_LOG_NNL("."); \ @@ -62,7 +63,12 @@ DEBUG_LOG_BYTE(ip4_addr4(&ip_addr)); #define DEBUG_LOG_SHORT(num) DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[1]); \ - DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[0]); + 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(((uint8_t*)&num)[0]); #else @@ -81,6 +87,7 @@ #define DEBUG_LOG_MAC(mac_addr) #define DEBUG_LOG_IP(ip_addr) #define DEBUG_LOG_SHORT(num) +#define DEBUG_LOG_INT(num) #endif diff --git a/firmware/octoclock/include/net/eth_mac_addr.h b/firmware/octoclock/include/net/eth_mac_addr.h index cb6fb234b..0c790aa4f 100644 --- a/firmware/octoclock/include/net/eth_mac_addr.h +++ b/firmware/octoclock/include/net/eth_mac_addr.h @@ -22,8 +22,10 @@ // Ethernet MAC address +#pragma pack(push,1) typedef struct { uint8_t addr[6]; } eth_mac_addr_t; +#pragma pack(pop) #endif /* INCLUDED_ETH_MAC_ADDR_H */ diff --git a/firmware/octoclock/include/network.h b/firmware/octoclock/include/network.h index 69c1dcf42..83e398bc5 100644 --- a/firmware/octoclock/include/network.h +++ b/firmware/octoclock/include/network.h @@ -41,6 +41,15 @@ #define ntohl(n) htonl(n) +#define _MAC_ADDR(mac_addr,a,b,c,d,e,f) mac_addr[0] = a; \ + mac_addr[1] = b; \ + mac_addr[2] = c; \ + mac_addr[3] = d; \ + mac_addr[4] = e; \ + mac_addr[5] = f; + +#define _MAC_SET_EQUAL(mac_addr1,mac_addr2) for(uint8_t i = 0; i < 6; i++) mac_addr1[i] = mac_addr2[i]; + #define _IP(a,b,c,d) (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | ((uint32_t)d << 0)) #define _IPH_V(hdr) (ntohs((hdr)->_v_hl_tos) >> 12) #define _IPH_HL(hdr) ((ntohs((hdr)->_v_hl_tos) >> 8) & 0x0f) @@ -60,11 +69,7 @@ #define _IPH_PROTO_SET(hdr, proto) (hdr)->_ttl_proto = (htons((proto) | (_IPH_TTL(hdr) << 8))) #define _IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) -// Global network values -eth_mac_addr_t octoclock_mac_addr; -struct ip_addr octoclock_ip_addr; -struct ip_addr octoclock_dr_addr; -struct ip_addr octoclock_netmask; +bool using_network_defaults; // Ethernet I/O buffers uint8_t buf_in[512]; @@ -76,9 +81,6 @@ 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); -static const uint8_t blank_eeprom_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -static const uint8_t default_mac[6] = {0x00, 0x80, 0x2F, 0x11, 0x22, 0x33}; - typedef void (*udp_receiver_t)(struct socket_address src, struct socket_address dst, unsigned char *payload, int payload_len); @@ -93,6 +95,8 @@ void send_udp_pkt(int src_port, struct socket_address dst, void handle_eth_packet(size_t recv_len); +void network_check(void); + void network_init(void); #endif /* INCLUDED_NETWORK_H */ diff --git a/firmware/octoclock/include/octoclock.h b/firmware/octoclock/include/octoclock.h index 3c6254eba..849ab7f96 100644 --- a/firmware/octoclock/include/octoclock.h +++ b/firmware/octoclock/include/octoclock.h @@ -27,14 +27,25 @@ #define F_CPU 12500000UL /* - * Working with the AVR timer + * Timer 0 (8-bit) + * * Set prescaler to 8 + * * Enable overflow interrupt + * * Set timer to 0 */ -// Prescaler: 1024, Timer: 0 -#define TIMER_INIT() TCCR1B = (1 << CS12) | (1 << CS10); \ +#define TIMER0_INIT() TCCR0 = (1 << CS01); \ + TIMSK |= (1 << TOIE0); \ + TCNT0 = 0; +/* + * Timer 1 (16-bit) + * * Set prescaler to 1024 + * * Enable overflow interrupt + * * Set timer to 0 + */ +#define TIMER1_INIT() TCCR1B = (1 << CS12) | (1 << CS10); \ + TIMSK |= (1< #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(); diff --git a/firmware/octoclock/octoclock_r4/CMakeLists.txt b/firmware/octoclock/octoclock_r4/CMakeLists.txt index a030d3249..c3559d8d4 100644 --- a/firmware/octoclock/octoclock_r4/CMakeLists.txt +++ b/firmware/octoclock/octoclock_r4/CMakeLists.txt @@ -17,6 +17,9 @@ add_executable(octoclock_r4_fw.elf octoclock_r4_main.c) target_link_libraries(octoclock_r4_fw.elf octoclock) +set_target_properties(octoclock_r4_fw.elf PROPERTIES + LINK_FLAGS "-Wl,--relax,-Map=octoclock_r4_fw.map,--cref" +) add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/octoclock_r4_fw.bin diff --git a/firmware/octoclock/octoclock_r4/octoclock_r4_main.c b/firmware/octoclock/octoclock_r4/octoclock_r4_main.c index f80c7d188..5e8e6d09b 100644 --- a/firmware/octoclock/octoclock_r4/octoclock_r4_main.c +++ b/firmware/octoclock/octoclock_r4/octoclock_r4_main.c @@ -72,23 +72,21 @@ int main(void){ - #ifdef DEBUG asm("cli"); - #else - asm("sei"); - #endif - - bool old_global_ext_ref_is_present = false; - switch_pos_t old_switch_pos, current_switch_pos; setup_atmel_io_ports(); - network_init(); + + #ifndef DEBUG + asm("sei"); + #endif + 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 usart_init(); //Set initial ClkDist and front panel settings @@ -98,61 +96,10 @@ int main(void){ led(Top,true); PORTA |= (1< 0) handle_eth_packet(recv_len); + network_check(); } } -- cgit v1.2.3