#include #include #include #include #include "F5xx_F6xx_Core_Lib/HAL_PMM.h" #include "F5xx_F6xx_Core_Lib/HAL_UCS.h" #include "mw_main.h" #include "mw_uart.h" #include "mw_lcd.h" #include "mw_bt.h" #include "mw_adc.h" #include "mw_bt.h" #include "mw_acc.h" #include "bt_hci.h" #include "bt_l2cap.h" #include "oswald_main.h" #include "oswald_hal.h" #include "bluetooth_init_cc256x.h" uint16_t _event_src = 0; #define HARDWARE_REVISION_ADDRESS (0x1a07) uint8_t GetMsp430HardwareRevision(void) { uint8_t *pDeviceType = (uint8_t *)(uint8_t *)HARDWARE_REVISION_ADDRESS; return pDeviceType[0]+'1'; } uint8_t DetermineErrata(void) { uint8_t Revision = GetMsp430HardwareRevision(); switch (Revision) { case 'F': case 'G': case 'H': return 0; break; default: return 1; break; } } static void set16mhz(void) { UCSCTL0 = 0x00; // Set lowest possible DCOx, MODx UCSCTL1 = DCORSEL_5; // Select suitable range UCSCTL2 = 488 + FLLD_1; // Set DCO Multiplier UCSCTL4 = SELA__XT1CLK | SELS__DCOCLKDIV | SELM__DCOCLKDIV ; // Worst-case settling time for the DCO when the DCO range bits have been // changed is n x 32 x 32 x f_FLL_reference. See UCS chapter in 5xx UG // for optimization. // 32 x 32 x / f_FLL_reference (32,768 Hz) = .03125 = t_DCO_settle // t_DCO_settle / (1 / 18 MHz) = 562500 = counts_DCO_settle // __delay_cycles(562500); int i; for (i=0;i<10;i++){ __delay_cycles(56250); } } static unsigned char PMM15Check(void) { // First check if SVSL/SVML is configured for fast wake-up if ((!(SVSMLCTL & SVSLE)) || ((SVSMLCTL & SVSLE) && (SVSMLCTL & SVSLFP)) || (!(SVSMLCTL & SVMLE)) || ((SVSMLCTL & SVMLE) && (SVSMLCTL & SVMLFP))) { // Next Check SVSH/SVMH settings to see if settings are affected by PMM15 if ((SVSMHCTL & SVSHE) && (!(SVSMHCTL & SVSHFP))) { if ( (!(SVSMHCTL & SVSHMD)) || ((SVSMHCTL & SVSHMD) && (SVSMHCTL & SVSMHACE)) ) return 1; // SVSH affected configurations } if ((SVSMHCTL & SVMHE) && (!(SVSMHCTL & SVMHFP)) && (SVSMHCTL & SVSMHACE)) return 1; // SVMH affected configurations } return 0; // SVS/M settings not affected by PMM15 } #define configCPU_CLOCK_HZ ((unsigned long) 16777216) /* 512*32768 */ #define configTICK_RATE_HZ ((unsigned int)1024) #define ACLK_MULTIPLIER ((unsigned int)512) static void setup_clocks(void) { unsigned long i; SetVCore(PMMCOREV_2); /* use external oscillator */ P7SEL |= BIT0 + BIT1; #if 1 if ((UCSCTL6 & XT1DRIVE_3) != XT1DRIVE_3) { UCSCTL6_L |= XT1DRIVE1_L + XT1DRIVE0_L; } i = 50000; while ((SFRIFG1 & OFIFG) && i--) { UCSCTL7 &= ~(DCOFFG + XT1LFOFFG + XT1HFOFFG + XT2OFFG); SFRIFG1 &= ~OFIFG; } UCSCTL6 = (UCSCTL6 & ~(XT1DRIVE_3)) |(XT1DRIVE_0); set16mhz(); UCSCTL8 |= SMCLKREQEN; #else // Startup LFXT1 32 kHz crystal while (LFXT_Start_Timeout(XT1DRIVE_0, 50000) == UCS_STATUS_ERROR) nop(); // select the sources for the FLL reference and ACLK SELECT_ACLK(SELA__XT1CLK); SELECT_FLLREF(SELREF__XT1CLK); // 512 * 32768 = 16777216 / 1024 Init_FLL_Settle(configCPU_CLOCK_HZ/configTICK_RATE_HZ, ACLK_MULTIPLIER); // Disable FLL loop control // __bis_SR_register(SCG0); #endif // setup for quick wake up from interrupt and // minimal power consumption in sleep mode DISABLE_SVSL(); // SVS Low side is turned off DISABLE_SVSL_RESET(); DISABLE_SVML(); // Monitor low side is turned off DISABLE_SVML_INTERRUPT(); DISABLE_SVMH(); // Monitor high side is turned off DISABLE_SVMH_INTERRUPT(); ENABLE_SVSH(); // SVS High side is turned on ENABLE_SVSH_RESET(); // Enable POR on SVS Event SVSH_ENABLED_IN_LPM_FULL_PERF(); // SVS high side Full perf mode, // stays on in LPM3,enhanced protect SVSL_ENABLED_IN_LPM_FAST_WAKE(); // Wait until high side, low side settled while ((PMMIFG & SVSMLDLYIFG) == 0 && (PMMIFG & SVSMHDLYIFG) == 0) nop(); CLEAR_PMM_IFGS(); while (PMM15Check()); // Errata PMM17 if (DetermineErrata()) { *(unsigned int*)(0x0110) = 0x9602; *(unsigned int*)(0x0112) |= 0x0800; } /* enable oscillator fault NMI IRQ */ // SFRIE1 = OFIE; } #if 0 #pragma vector=PWR_PORT_VECTOR __interrupt void PWR_ISR (void) { /* clear all possible sources */ PWR_PORT_IFG &= ~(BAT_CHARGE_STAT1 | BAT_CHARGE_STAT2 | BAT_CHARGE_PWR_BIT); _event_src |= POWER_SRC_EVENT; LPM3_EXIT(); nop(); } #endif static void mw_init_vibrate_pwm(void) { #ifdef MW_DIGITAL_V2 // Start with P7.3 as an output P7OUT &= ~BIT3; // Low when a digital output P7SEL &= ~BIT3; // P7 option select = false P7DIR |= BIT3; // P7 outputs TA1CTL = 0; // No expansion divide TA1EX0 = 0; // do a PWM with 64 total steps. This gives a count up of 32 and // a count down of 32 TA1CCR0 = 31; // Compare channel 2 is used as output TA1CCTL2 = OUTMOD_6; // PWM output mode: 6 - toggle/set TA1CCR2 = 10; // 10 is a 2/3 duty cycle #endif } static void setup_pins(void) { CONFIG_SRAM_PINS(); CONFIGURE_BUTTON_PINS(); #ifdef MW_DEVBOARD_V2 CONFIG_LED_PINS(); // debug LEDs on devboard #endif DISABLE_LCD_LED(); // frontlight CONFIG_DEBUG_PINS(); CONFIG_ACCELEROMETER_PINS(); //DISABLE_ACCELEROMETER_POWER(); // starts from config 5 and later ENABLE_ACCELEROMETER_POWER(); // starts from config 5 and later HARDWARE_CFG_SENSE_INIT(); APPLE_CONFIG(); APPLE_POWER_DISABLE(); CONFIG_BT_PINS(); // BT_CLK_REQ_CONFIG_AS_OUTPUT_LOW(); // BT_IO1_CONFIG_AS_OUTPUT_LOW(); // BT_IO2_CONFIG_AS_OUTPUT_LOW(); BT_CLK_REQ_CONFIG_AS_INPUT(); BT_IO1_CONFIG_AS_INPUT(); BT_IO2_CONFIG_AS_INPUT(); mw_disable_bt(); LIGHT_SENSE_INIT(); LIGHT_SENSOR_SHUTDOWN(); BATTERY_SENSE_INIT(); BATTERY_SENSE_DISABLE(); BAT_CHARGE_DIR &= ~(BAT_CHARGE_STAT1 | BAT_CHARGE_STAT2 | BAT_CHARGE_PWR_BIT); BAT_CHARGE_OUT |= BAT_CHARGE_PWR_BIT | BAT_CHARGE_STAT1 | BAT_CHARGE_STAT2; // pull-up BAT_CHARGE_REN |= BAT_CHARGE_PWR_BIT; // enable resistors // BAT_CHARGE_IE |= BAT_CHARGE_PWR_BIT; BAT_CHARGE_OUT |= BAT_CHARGE_ENABLE_PIN; // !CE, negative logic BAT_CHARGE_DIR |= BAT_CHARGE_ENABLE_PIN; /* disable reset function, enable NMI, do not enable NMI IRQ */ /* to avoid accidential reset on charger clip connect */ #ifndef MW_DEVBOARD_V2 // but only on real watch SFRRPCR &= ~SYSRSTRE; SFRRPCR |= SYSNMI; #endif /* allow debug UART */ /* P10SEL &= ~(BIT6 | BIT7); P10DIR |= BIT6 | BIT7; P10OUT &= ~(BIT6 | BIT7); */ #ifndef MW_DEVBOARD_V2 ENABLE_MUX_OUTPUT_CONTROL(); #ifdef MW_DEBUG_UART MUX_OUTPUT_SELECTS_SERIAL(); #else MUX_OUTPUT_OFF(); #endif #endif } #pragma vector=WDT_VECTOR __interrupt void WDT_ISR (void) { /* eventually we will do something here, not for now */ _event_src |= WATCHDOG_EVENT; nop(); } static void setup_wdt(void) { #if 1 WDTCTL = WDTPW + WDTHOLD; // disable watchdog #else WDTCTL = WDT_ADLY_1000; // 1 second timeout SFRIE1 |= WDTIE; // Enable WDT interrupt #endif } #if 1 #pragma vector=UNMI_VECTOR __interrupt void NMI_ISR (void) { #if defined MW_DEVBOARD_V2 LED7_TOGGLE(); debug_uart_tx_char('n'); #endif while ((SFRIFG1 & OFIFG)) { UCSCTL7 &= ~(DCOFFG + XT1LFOFFG + XT1HFOFFG + XT2OFFG); SFRIFG1 &= ~OFIFG; } } #endif #pragma vector=RTC_VECTOR __interrupt void RTC_ISR (void) { switch (RTCIV) { case RTCIV_NONE: debug_uart_tx("RTC none IRQ\n"); break; case RTCIV_RTCRDYIFG: case RTCIV_RTCTEVIFG: case RTCIV_RTCAIFG: case RTCIV_RT0PSIFG: debug_uart_tx("RTC misc IRQ\n"); break; case RTCIV_RT1PSIFG: RTCPS1CTL &= ~RT1PSIFG; _event_src |= RTC_1HZ_EVENT; // LPM3_EXIT; LPM3_EXIT_ISR(); #if defined MW_DEVBOARD_V2 LED7_TOGGLE(); #endif break; default: break; }; } void setup_rtc(void) { // stop it RTCCTL01 = RTCHOLD; // calibration RTCCTL2 = 0x00 & 0x3f; RTCCTL2 |= RTCCALS; // Set the counter for RTC mode RTCCTL01 |= RTCMODE; // set 128 Hz rate for prescale 0 interrupt RTCPS0CTL |= RT0IP_7; // enable 1 pulse per second interrupt using prescale 1 RTCPS1CTL |= RT1IP_6 | RT1PSIE; // 1 Hz calibration output RTCCTL23 |= RTCCALF_3; // setting the peripheral selection bit makes the other I/O control a don't care // P2.4 = 1 Hz RTC calibration output // Direction needs to be set as output RTC_1HZ_PORT_SEL |= RTC_1HZ_BIT; RTC_1HZ_PORT_DIR |= RTC_1HZ_BIT; RTCYEAR = (unsigned int) 2013; RTCMON = (unsigned int) 1; RTCDAY = (unsigned int) 1; RTCDOW = (unsigned int) 2; RTCHOUR = (unsigned int) 01; RTCMIN = (unsigned int) 0; RTCSEC = (unsigned int) 0; // Enable the RTC RTCCTL01 &= ~RTCHOLD; nop(); } #if defined MW_DEVBOARD_V2 || MW_DEBUG_UART static void dbg_out_rtc(void) { char clk_str[16]; snprintf(clk_str, 16, "%02d:%02d.%02d %d\n", RTCHOUR, RTCMIN, RTCSEC, RTCDOW); debug_uart_tx(clk_str); } #endif static void handle_button_event(void) { unsigned char _button_state = 0; #if 0 char clk_str[16]; snprintf(clk_str, 16, "0x%02x\n", _button_state); debug_uart_tx(clk_str); #endif while (_button_state != (BUTTON_PORT_IN & ALL_BUTTONS)) { __delay_cycles(562500); _button_state = (BUTTON_PORT_IN & ALL_BUTTONS); __delay_cycles(562500); } // BUTTON_PORT_IE |= INT_EDGE_SEL_BUTTONS; if (_button_state & SW_A) { debug_uart_tx("switch A\n"); oswald_handle_button_press(BUTTON_A); } if (_button_state & SW_B) { debug_uart_tx("switch B\n"); oswald_handle_button_press(BUTTON_B); } if (_button_state & SW_C) { debug_uart_tx("switch C\n"); oswald_handle_button_press(BUTTON_C); } if (_button_state & SW_D) { debug_uart_tx("switch D\n"); oswald_handle_button_press(BUTTON_D); } if (_button_state & SW_E) { debug_uart_tx("switch E\n"); oswald_handle_button_press(BUTTON_E); } if (_button_state & SW_F) { debug_uart_tx("switch F\n"); oswald_handle_button_press(BUTTON_F); } } void check_pwr_state(void) { if (BAT_CHARGE_IN & BAT_CHARGE_PWR_BIT) { BAT_CHARGE_OUT |= BAT_CHARGE_ENABLE_PIN; BAT_CHARGE_REN &= ~(BAT_CHARGE_STAT1 | BAT_CHARGE_STAT2); // disable pull-up } else { BAT_CHARGE_OUT &= ~BAT_CHARGE_ENABLE_PIN; BAT_CHARGE_REN |= BAT_CHARGE_STAT1 | BAT_CHARGE_STAT2; // enable pull-up } } static void handle_bt_uart_rx_event(void) { const unsigned char *rx; unsigned char len, *rp, p; rx = mw_bt_get_rx_buf(&rp, &len); p = 0; while (p < len) { p += bt_feed_packet_data(rx[(*rp+p)%BT_RX_MAX_SIZE]); } // all consumed *rp = (*rp + len) % BT_RX_MAX_SIZE; } #if defined MW_DEVBOARD_V2 || MW_DEBUG_UART static void handle_uart_rx_event(void) { char c; #ifndef CC256x_TRANSP char tstr[255]; if (debug_uart_rx_char(&c)) { debug_uart_tx_char(c); if (c == 'a') { debug_uart_tx("\nenabling ACC\n"); mw_acc_enable(); } else if (c == 'A') { debug_uart_tx("\ndisabling ACC\n"); mw_acc_disable(); } else if (c == 'r') { int16_t x,y,z; debug_uart_tx("\nread ACC: "); mw_acc_read(&x, &y, &z); snprintf(tstr, 64, "x:%d y:%d z:%d\n", x,y,z); debug_uart_tx(tstr); } else if (c =='R') { int16_t al; al = mw_get_amblight_adc_val(); snprintf(tstr, 64, "light: %d\n", al); debug_uart_tx(tstr); } else if (c == 'b') { debug_uart_tx("\nenabling BT\n"); mw_enable_bt(); } else if (c == 'B') { debug_uart_tx("\ndisabling BT\n"); mw_disable_bt(); } else if (c == 'c') { debug_uart_tx("\nCharger status: "); snprintf(tstr, 16, "0x%04x 0x%04x ", BAT_CHARGE_IN, (BAT_CHARGE_IN & BAT_CHARGE_PWR_BIT)); debug_uart_tx(tstr); if (BAT_CHARGE_IN & BAT_CHARGE_PWR_BIT) debug_uart_tx("no ext pwr, "); else debug_uart_tx("ext pwr connected, "); switch (BAT_CHARGE_IN & (BAT_CHARGE_STAT1 | BAT_CHARGE_STAT2)) { case BAT_CHARGE_STAT1: debug_uart_tx("charge done\n"); break; case BAT_CHARGE_STAT2: debug_uart_tx("fast charge\n"); break; case (BAT_CHARGE_STAT1 | BAT_CHARGE_STAT2): debug_uart_tx("suspend, sleep or fault\n"); break; default: debug_uart_tx("precharge\n"); break; } if (BAT_CHARGE_IN & BAT_CHARGE_ENABLE_PIN) debug_uart_tx(" !charge\n"); else debug_uart_tx(" charge\n"); } else if (c == 'd') { debug_uart_tx("charging disabled\n"); BAT_CHARGE_OUT |= BAT_CHARGE_ENABLE_PIN; } else if (c == 'e') { debug_uart_tx("charging enabled\n"); BAT_CHARGE_OUT &= ~BAT_CHARGE_ENABLE_PIN; } else if (c == 'l') { debug_uart_tx("backlight LED on\n"); hal_lcd_set_backlight(TRUE); } else if (c == 'L') { debug_uart_tx("backlight LED off\n"); hal_lcd_set_backlight(FALSE); } else if (c == 'u') { mw_lcd_update_screen(); } else if (c == '+') { nop(); } else if (c == '-') { nop(); } else if (c == 'H') { uint8_t dclass[3]; dclass[0] = BT_MW_DEVICE_CLASS & 0xff; dclass[1] = (BT_MW_DEVICE_CLASS & 0xff00) >> 8; dclass[2] = (BT_MW_DEVICE_CLASS & 0xff0000) >> 16; debug_uart_tx("HCI reset\n"); bt_hci_cmd(HCI_HC_BB_OGF, HCI_RESET_OCF, 0, NULL); bt_hci_cmd(HCI_HC_BB_OGF, HCI_W_COD_OCF, 3, dclass); } else if (c == 'i') { debug_uart_tx("Information:\n"); debug_uart_tx("Oswald "); debug_uart_tx(MW_MAIN_VERSION); debug_uart_tx("\n"); debug_uart_tx("Build #"); debug_uart_tx(BUILDNO); debug_uart_tx("\n"); debug_uart_tx("BT V "); debug_uart_tx(cc256x_version); debug_uart_tx("\n"); debug_uart_tx("MCU Rev "); debug_uart_tx_char(GetMsp430HardwareRevision()); debug_uart_tx("\n"); } else if (c == 'S') { debug_uart_tx("Scan enable\n"); tstr[0] = HCI_BB_SCAN_INQUIRY | HCI_BB_SCAN_PAGE; bt_hci_cmd(HCI_HC_BB_OGF, HCI_W_SCAN_EN_OCF, 1, (uint8_t *)tstr); } else if (c == 'h') { RTCHOUR++; if (RTCHOUR > 23) RTCHOUR = 0; } else if (c == 'm') { RTCMIN++; if (RTCMIN > 59) RTCMIN = 0; } else if (c == 'N') { debug_uart_tx("Set name\n"); tstr[0] = 'O'; tstr[1] = 's'; tstr[2] = 'w'; tstr[3] = 'a'; tstr[4] = 'l'; tstr[5] = 'd'; tstr[6] = 0x00; bt_hci_cmd(HCI_HC_BB_OGF, HCI_W_LOCAL_NAME_OCF, 0x07, (uint8_t *)tstr); } else if (c == 'R') { bt_hci_cmd(HCI_INFO_PARAM_OGF, HCI_R_BD_ADDR_OCF, 0, NULL); } } #endif } #endif void start_timer(int cycles) { TA0EX0 = TAIDEX_0; TA0CTL = TASSEL_1 | TACLR | MC__STOP; // SMCLK, clear TAR TA0CCTL0 = CCIE; // CCR0 interrupt enabled TA0CCR0 = cycles; TA0CTL |= MC_1; // Start Timer_A in continuous mode } void stop_timer(void) { TA0CCTL0 &= ~CCIE; // CCR0 interrupt enabled TA0CTL = MC__STOP; // Start Timer_A in continuous mode } // Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void TIMER0_A0_ISR (void) { TA0CTL &= ~(TAIFG); #if defined xMW_DEVBOARD_V2 LED6_TOGGLE(); #endif _event_src |= TIMER_500MS_EVENT | TIMER_100MS_EVENT; // LPM3_EXIT; LPM3_EXIT_ISR(); } uint8_t handle_event(void) { #if defined MW_DEVBOARD_V2 || MW_DEBUG_UART char tstr[64]; #endif if (_event_src == 0) return 1; while (_event_src != 0) { if (_event_src & WATCHDOG_EVENT) { _event_src &= ~WATCHDOG_EVENT; debug_uart_tx_char('w'); } else if (_event_src & RTC_1HZ_EVENT) { _event_src &= ~RTC_1HZ_EVENT; check_pwr_state(); oswald_one_second_tick(); #if defined MW_DEVBOARD_V2 || MW_DEBUG_UART dbg_out_rtc(); #endif } else if (_event_src & BT_UART_RCV_EVENT) { _event_src &= ~BT_UART_RCV_EVENT; handle_bt_uart_rx_event(); } else if (_event_src & BT_UART_WAKEUP_EVENT) { _event_src &= ~BT_UART_WAKEUP_EVENT; bt_hci_ehcill_wake(); } else if (_event_src & DBG_UART_RCV_EVENT) { _event_src &= ~DBG_UART_RCV_EVENT; #if defined MW_DEVBOARD_V2 || MW_DEBUG_UART handle_uart_rx_event(); #endif } else if (_event_src & BUTTON_EVENT) { _event_src &= ~BUTTON_EVENT; handle_button_event(); } else if (_event_src & TIMER_500MS_EVENT) { _event_src &= ~TIMER_500MS_EVENT; oswald_halfsecond_tick(); } else if (_event_src & TIMER_100MS_EVENT) { _event_src &= ~TIMER_100MS_EVENT; oswald_centisecond_tick(); } else if (_event_src & ACCEL_EVENT) { _event_src &= ~ACCEL_EVENT; mw_acc_handle_irq(); } else { #if defined MW_DEVBOARD_V2 || MW_DEBUG_UART snprintf(tstr, 64, "unhandled event in 0x%04x\n", _event_src); debug_uart_tx(tstr); #endif } } return 0; } #pragma vector=BUTTON_PORT_VECTOR __interrupt void BUTTON_ISR (void) { // LPM3_EXIT; LPM3_EXIT_ISR(); BUTTON_PORT_IFG &= ~ALL_BUTTONS; // BUTTON_PORT_IE &= ~INT_EDGE_SEL_BUTTONS; _event_src |= BUTTON_EVENT; //_button_state = (BUTTON_PORT_IN & ALL_BUTTONS); } #pragma vector=PORT1_VECTOR __interrupt void PORT1_GPIO_ISR (void) { if (P1IFG & BT_IO_CTS) { //LPM3_EXIT; LPM3_EXIT_ISR(); P1IE &= ~BT_IO_CTS; P1IFG &= ~BT_IO_CTS; debug_uart_tx("BT CTS irq\n"); _event_src |= BT_UART_WAKEUP_EVENT; // bt_hci_ehcill_wake(); } else if (P1IFG & ACCELEROMETER_INT_PIN) { //LPM3_EXIT; LPM3_EXIT_ISR(); P1IFG &= ~ACCELEROMETER_INT_PIN; // debug_uart_tx("ACC irq\n"); _event_src |= ACCEL_EVENT; } } #if 0 #pragma vector=NOVECTOR __interrupt void UNEXP_ISR (void) { debug_uart_tx("unexpected IRQ occured\n"); } #endif int main(void) { setup_wdt(); setup_pins(); setup_clocks(); setup_rtc(); /* enable interrupts, we will need them! */ __enable_interrupt(); #if defined MW_DEVBOARD_V2 || MW_DEBUG_UART init_debug_uart(); debug_uart_tx("\nOswald on MetaWatch\n"); #endif mw_lcd_init(); mw_lcd_clear(); mw_lcd_update_screen(); mw_init_adc(); mw_init_vibrate_pwm(); oswald_set_time(RTCHOUR, RTCMIN, RTCSEC, TRUE); oswald_set_date(RTCDAY, RTCMON, RTCYEAR, TRUE); oswald_init(); while (1) { /* handle pending events */ handle_event(); /* enter LPM3 sleep mode waiting for interrupt */ /* errata PMM11 + PMM12 - divide MCLK before going to sleep */ if (DetermineErrata()) { MCLK_DIV(2); nop(); } LPM3; nop(); if (DetermineErrata()) { __delay_cycles(100); MCLK_DIV(1); } }; return 0; }