Merge pull request #1047 from g-oikonomou/cc26xx/contrib/lpm

CC26xx Ultra Low Power Operation
This commit is contained in:
Antonio Lignan 2015-05-16 10:27:22 +02:00
commit 32afadea77
37 changed files with 1406 additions and 742 deletions

View file

@ -81,7 +81,7 @@ CONTIKI_CPU_SOURCEFILES += clock.c rtimer-arch.c cc26xx-rtc.c uart.c
CONTIKI_CPU_SOURCEFILES += cc26xx-rf.c contiki-watchdog.c CONTIKI_CPU_SOURCEFILES += cc26xx-rf.c contiki-watchdog.c
CONTIKI_CPU_SOURCEFILES += putchar.c ieee-addr.c batmon-sensor.c CONTIKI_CPU_SOURCEFILES += putchar.c ieee-addr.c batmon-sensor.c
CONTIKI_CPU_SOURCEFILES += slip-arch.c slip.c cc26xx-uart.c lpm.c CONTIKI_CPU_SOURCEFILES += slip-arch.c slip.c cc26xx-uart.c lpm.c
CONTIKI_CPU_SOURCEFILES += gpio-interrupt.c CONTIKI_CPU_SOURCEFILES += gpio-interrupt.c oscillators.c
DEBUG_IO_SOURCEFILES += dbg-printf.c dbg-snprintf.c dbg-sprintf.c strformat.c DEBUG_IO_SOURCEFILES += dbg-printf.c dbg-snprintf.c dbg-sprintf.c strformat.c

View file

@ -79,8 +79,14 @@ clock_init(void)
* Here, we configure GPT0 Timer A, which we subsequently use in * Here, we configure GPT0 Timer A, which we subsequently use in
* clock_delay_usec * clock_delay_usec
* *
* First, enable GPT0 in run mode. We don't need it in sleep mode * We need to access registers, so firstly power up the PD and then enable
* the clock to GPT0.
*/ */
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_PERIPH) !=
PRCM_DOMAIN_POWER_ON) {
power_domain_on();
}
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_TIMER0); ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_load_set(); ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get()); while(!ti_lib_prcm_load_get());
@ -155,11 +161,19 @@ clock_wait(clock_time_t i)
void void
clock_delay_usec(uint16_t len) clock_delay_usec(uint16_t len)
{ {
uint32_t clock_status;
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_PERIPH) != if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_PERIPH) !=
PRCM_DOMAIN_POWER_ON) { PRCM_DOMAIN_POWER_ON) {
power_domain_on(); power_domain_on();
} }
clock_status = HWREG(PRCM_BASE + PRCM_O_GPTCLKGR) & PRCM_GPIOCLKGR_CLK_EN;
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
ti_lib_timer_load_set(GPT0_BASE, TIMER_B, len); ti_lib_timer_load_set(GPT0_BASE, TIMER_B, len);
ti_lib_timer_enable(GPT0_BASE, TIMER_B); ti_lib_timer_enable(GPT0_BASE, TIMER_B);
@ -168,6 +182,12 @@ clock_delay_usec(uint16_t len)
* function, hence the direct register access here * function, hence the direct register access here
*/ */
while(HWREG(GPT0_BASE + GPT_O_CTL) & GPT_CTL_TBEN); while(HWREG(GPT0_BASE + GPT_O_CTL) & GPT_CTL_TBEN);
if(clock_status == 0) {
ti_lib_prcm_peripheral_run_disable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
}
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** /**

View file

@ -39,6 +39,7 @@
#include "contiki.h" #include "contiki.h"
#include "dev/radio.h" #include "dev/radio.h"
#include "dev/cc26xx-rf.h" #include "dev/cc26xx-rf.h"
#include "dev/oscillators.h"
#include "net/packetbuf.h" #include "net/packetbuf.h"
#include "net/rime/rimestats.h" #include "net/rime/rimestats.h"
#include "net/linkaddr.h" #include "net/linkaddr.h"
@ -68,6 +69,7 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define BUSYWAIT_UNTIL(cond, max_time) \ #define BUSYWAIT_UNTIL(cond, max_time) \
do { \ do { \
@ -393,74 +395,6 @@ static int on(void);
static int off(void); static int off(void);
static void setup_interrupts(void); static void setup_interrupts(void);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Select the HF XOSC as the source for the HF clock, but don't switch yet */
static void
request_hf_xosc(void)
{
/* Enable OSC DIG interface to change clock sources */
ti_lib_osc_interface_enable();
/* Make sure the SMPH clock within AUX is enabled */
ti_lib_aux_wuc_clock_enable(AUX_WUC_SMPH_CLOCK);
while(ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY);
if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_HF) != OSC_XOSC_HF) {
/*
* Request to switch to the crystal to enable radio operation. It takes a
* while for the XTAL to be ready so instead of performing the actual
* switch, we return and we do other stuff while the XOSC is getting ready.
*/
ti_lib_osc_clock_source_set(OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_XOSC_HF);
}
/* Disable OSC DIG interface */
ti_lib_osc_interface_disable();
}
/*---------------------------------------------------------------------------*/
/*
* Switch to the XOSC. This will block until the XOSC is ready, so this must
* be preceded by a call to select_hf_xosc()
*/
static void
switch_to_hf_xosc(void)
{
/* Enable OSC DIG interface to change clock sources */
ti_lib_osc_interface_enable();
/* Make sure the SMPH clock within AUX is enabled */
ti_lib_aux_wuc_clock_enable(AUX_WUC_SMPH_CLOCK);
while(ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY);
if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_HF) != OSC_XOSC_HF) {
/* Switch the HF clock source (cc26xxware executes this from ROM) */
ti_lib_osc_hf_source_switch();
}
/* Disable OSC DIG interface */
ti_lib_osc_interface_disable();
}
/*---------------------------------------------------------------------------*/
static void
switch_to_hf_rc_osc(void)
{
/* Enable OSC DIG interface to change clock sources */
ti_lib_osc_interface_enable();
/* Make sure the SMPH clock within AUX is enabled */
ti_lib_aux_wuc_clock_enable(AUX_WUC_SMPH_CLOCK);
while(ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY);
/* Set all clock sources to the HF RC Osc */
ti_lib_osc_clock_source_set(OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_RCOSC_HF);
/* Check to not enable HF RC oscillator if already enabled */
if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_HF) != OSC_RCOSC_HF) {
/* Switch the HF clock source (cc26xxware executes this from ROM) */
ti_lib_osc_hf_source_switch();
}
ti_lib_osc_interface_disable();
}
/*---------------------------------------------------------------------------*/
static uint8_t static uint8_t
rf_is_accessible(void) rf_is_accessible(void)
{ {
@ -498,16 +432,19 @@ static uint_fast8_t
rf_send_cmd(uint32_t cmd, uint32_t *status) rf_send_cmd(uint32_t cmd, uint32_t *status)
{ {
uint32_t timeout_count = 0; uint32_t timeout_count = 0;
bool interrupts_disabled;
/* /*
* Make sure ContikiMAC doesn't turn us off from within an interrupt while * Make sure ContikiMAC doesn't turn us off from within an interrupt while
* we are accessing RF Core registers * we are accessing RF Core registers
*/ */
ti_lib_int_master_disable(); interrupts_disabled = ti_lib_int_master_disable();
if(!rf_is_accessible()) { if(!rf_is_accessible()) {
PRINTF("rf_send_cmd: RF was off\n"); PRINTF("rf_send_cmd: RF was off\n");
ti_lib_int_master_enable(); if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
return RF_CMD_ERROR; return RF_CMD_ERROR;
} }
@ -516,12 +453,16 @@ rf_send_cmd(uint32_t cmd, uint32_t *status)
*status = HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDSTA); *status = HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDSTA);
if(++timeout_count > 50000) { if(++timeout_count > 50000) {
PRINTF("rf_send_cmd: Timeout\n"); PRINTF("rf_send_cmd: Timeout\n");
ti_lib_int_master_enable(); if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
return RF_CMD_ERROR; return RF_CMD_ERROR;
} }
} while(*status == RF_CMD_STATUS_PENDING); } while(*status == RF_CMD_STATUS_PENDING);
ti_lib_int_master_enable(); if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/* /*
* If we reach here the command is no longer pending. It is either completed * If we reach here the command is no longer pending. It is either completed
@ -978,8 +919,8 @@ static int
power_up(void) power_up(void)
{ {
uint32_t cmd_status; uint32_t cmd_status;
bool interrupts_disabled = ti_lib_int_master_disable();
ti_lib_int_master_disable();
ti_lib_int_pend_clear(INT_RF_CPE0); ti_lib_int_pend_clear(INT_RF_CPE0);
ti_lib_int_pend_clear(INT_RF_CPE1); ti_lib_int_pend_clear(INT_RF_CPE1);
ti_lib_int_disable(INT_RF_CPE0); ti_lib_int_disable(INT_RF_CPE0);
@ -1002,7 +943,10 @@ power_up(void)
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = 0x0; HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = 0x0;
ti_lib_int_enable(INT_RF_CPE0); ti_lib_int_enable(INT_RF_CPE0);
ti_lib_int_enable(INT_RF_CPE1); ti_lib_int_enable(INT_RF_CPE1);
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/* Let CPE boot */ /* Let CPE boot */
HWREG(RFC_PWR_NONBUF_BASE + RFC_PWR_O_PWMCLKEN) = RF_CORE_CLOCKS_MASK; HWREG(RFC_PWR_NONBUF_BASE + RFC_PWR_O_PWMCLKEN) = RF_CORE_CLOCKS_MASK;
@ -1022,7 +966,7 @@ power_up(void)
static void static void
power_down(void) power_down(void)
{ {
ti_lib_int_master_disable(); bool interrupts_disabled = ti_lib_int_master_disable();
ti_lib_int_disable(INT_RF_CPE0); ti_lib_int_disable(INT_RF_CPE0);
ti_lib_int_disable(INT_RF_CPE1); ti_lib_int_disable(INT_RF_CPE1);
@ -1045,7 +989,9 @@ power_down(void)
ti_lib_int_pend_clear(INT_RF_CPE1); ti_lib_int_pend_clear(INT_RF_CPE1);
ti_lib_int_enable(INT_RF_CPE0); ti_lib_int_enable(INT_RF_CPE0);
ti_lib_int_enable(INT_RF_CPE1); ti_lib_int_enable(INT_RF_CPE1);
ti_lib_int_master_enable(); if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static int static int
@ -1160,6 +1106,8 @@ cc26xx_rf_cpe0_isr(void)
static void static void
setup_interrupts(void) setup_interrupts(void)
{ {
bool interrupts_disabled;
/* We are already turned on by the caller, so this should not happen */ /* We are already turned on by the caller, so this should not happen */
if(!rf_is_accessible()) { if(!rf_is_accessible()) {
PRINTF("setup_interrupts: No access\n"); PRINTF("setup_interrupts: No access\n");
@ -1167,7 +1115,7 @@ setup_interrupts(void)
} }
/* Disable interrupts */ /* Disable interrupts */
ti_lib_int_master_disable(); interrupts_disabled = ti_lib_int_master_disable();
/* Set all interrupt channels to CPE0 channel, error to CPE1 */ /* Set all interrupt channels to CPE0 channel, error to CPE1 */
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEISL) = ERROR_IRQ; HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEISL) = ERROR_IRQ;
@ -1182,7 +1130,10 @@ setup_interrupts(void)
ti_lib_int_pend_clear(INT_RF_CPE1); ti_lib_int_pend_clear(INT_RF_CPE1);
ti_lib_int_enable(INT_RF_CPE0); ti_lib_int_enable(INT_RF_CPE0);
ti_lib_int_enable(INT_RF_CPE1); ti_lib_int_enable(INT_RF_CPE1);
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static uint8_t static uint8_t
@ -1199,7 +1150,7 @@ request(void)
return LPM_MODE_MAX_SUPPORTED; return LPM_MODE_MAX_SUPPORTED;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
LPM_MODULE(cc26xx_rf_lpm_module, request, NULL, NULL); LPM_MODULE(cc26xx_rf_lpm_module, request, NULL, NULL, LPM_DOMAIN_NONE);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static int static int
init(void) init(void)
@ -1530,7 +1481,7 @@ on(void)
* Request the HF XOSC as the source for the HF clock. Needed before we can * Request the HF XOSC as the source for the HF clock. Needed before we can
* use the FS. This will only request, it will _not_ perform the switch. * use the FS. This will only request, it will _not_ perform the switch.
*/ */
request_hf_xosc(); oscillators_request_hf_xosc();
/* /*
* If we are in the middle of a BLE operation, we got called by ContikiMAC * If we are in the middle of a BLE operation, we got called by ContikiMAC
@ -1568,7 +1519,7 @@ on(void)
* This will block until the XOSC is actually ready, but give how we * This will block until the XOSC is actually ready, but give how we
* requested it early on, this won't be too long a wait/ * requested it early on, this won't be too long a wait/
*/ */
switch_to_hf_xosc(); oscillators_switch_to_hf_xosc();
if(rf_radio_setup(RF_MODE_IEEE) != RF_CMD_OK) { if(rf_radio_setup(RF_MODE_IEEE) != RF_CMD_OK) {
PRINTF("on: radio_setup() failed\n"); PRINTF("on: radio_setup() failed\n");
@ -1597,7 +1548,7 @@ off(void)
power_down(); power_down();
/* Switch HF clock source to the RCOSC to preserve power */ /* Switch HF clock source to the RCOSC to preserve power */
switch_to_hf_rc_osc(); oscillators_switch_to_hf_rc();
/* We pulled the plug, so we need to restore the status manually */ /* We pulled the plug, so we need to restore the status manually */
GET_FIELD(cmd_ieee_rx_buf, radioOp, status) = IDLE; GET_FIELD(cmd_ieee_rx_buf, radioOp, status) = IDLE;
@ -1993,6 +1944,7 @@ PROCESS_THREAD(cc26xx_rf_ble_beacon_process, ev, data)
uint8_t was_on; uint8_t was_on;
int j; int j;
uint32_t cmd_status; uint32_t cmd_status;
bool interrupts_disabled;
PROCESS_BEGIN(); PROCESS_BEGIN();
@ -2023,9 +1975,11 @@ PROCESS_THREAD(cc26xx_rf_ble_beacon_process, ev, data)
* Under ContikiMAC, some IEEE-related operations will be called from an * Under ContikiMAC, some IEEE-related operations will be called from an
* interrupt context. We need those to see that we are in BLE mode. * interrupt context. We need those to see that we are in BLE mode.
*/ */
ti_lib_int_master_disable(); interrupts_disabled = ti_lib_int_master_disable();
ble_mode_on = 1; ble_mode_on = 1;
ti_lib_int_master_enable(); if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/* /*
* Send BLE_ADV_MESSAGES beacon bursts. Each burst on all three * Send BLE_ADV_MESSAGES beacon bursts. Each burst on all three
@ -2061,7 +2015,7 @@ PROCESS_THREAD(cc26xx_rf_ble_beacon_process, ev, data)
} }
} else { } else {
/* Request the HF XOSC to source the HF clock. */ /* Request the HF XOSC to source the HF clock. */
request_hf_xosc(); oscillators_request_hf_xosc();
/* We were off: Boot the CPE */ /* We were off: Boot the CPE */
if(power_up() != RF_CMD_OK) { if(power_up() != RF_CMD_OK) {
@ -2079,7 +2033,7 @@ PROCESS_THREAD(cc26xx_rf_ble_beacon_process, ev, data)
} }
/* Trigger a switch to the XOSC, so that we can use the FS */ /* Trigger a switch to the XOSC, so that we can use the FS */
switch_to_hf_xosc(); oscillators_switch_to_hf_xosc();
} }
/* Enter BLE mode */ /* Enter BLE mode */
@ -2118,13 +2072,17 @@ PROCESS_THREAD(cc26xx_rf_ble_beacon_process, ev, data)
power_down(); power_down();
/* Switch HF clock source to the RCOSC to preserve power */ /* Switch HF clock source to the RCOSC to preserve power */
switch_to_hf_rc_osc(); oscillators_switch_to_hf_rc();
} }
etimer_set(&ble_adv_et, BLE_ADV_DUTY_CYCLE); etimer_set(&ble_adv_et, BLE_ADV_DUTY_CYCLE);
ti_lib_int_master_disable(); interrupts_disabled = ti_lib_int_master_disable();
ble_mode_on = 0; ble_mode_on = 0;
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/* Wait unless this is the last burst */ /* Wait unless this is the last burst */
if(i < BLE_ADV_MESSAGES - 1) { if(i < BLE_ADV_MESSAGES - 1) {
@ -2132,9 +2090,13 @@ PROCESS_THREAD(cc26xx_rf_ble_beacon_process, ev, data)
} }
} }
ti_lib_int_master_disable(); interrupts_disabled = ti_lib_int_master_disable();
ble_mode_on = 0; ble_mode_on = 0;
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
} }
PROCESS_END(); PROCESS_END();
} }

View file

@ -44,6 +44,7 @@
#include "ti-lib.h" #include "ti-lib.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define cc26xx_rtc_isr(...) AONRTCIntHandler(__VA_ARGS__) #define cc26xx_rtc_isr(...) AONRTCIntHandler(__VA_ARGS__)
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -54,9 +55,11 @@ void
cc26xx_rtc_init(void) cc26xx_rtc_init(void)
{ {
uint32_t compare_value; uint32_t compare_value;
bool interrupts_disabled;
/* Disable and clear interrupts */ /* Disable and clear interrupts */
ti_lib_int_master_disable(); interrupts_disabled = ti_lib_int_master_disable();
ti_lib_aon_rtc_disable(); ti_lib_aon_rtc_disable();
ti_lib_aon_rtc_event_clear(AON_RTC_CH0); ti_lib_aon_rtc_event_clear(AON_RTC_CH0);
@ -65,22 +68,25 @@ cc26xx_rtc_init(void)
/* Setup the wakeup event */ /* Setup the wakeup event */
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU0, AON_EVENT_RTC0); ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU0, AON_EVENT_RTC0);
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU1, AON_EVENT_RTC2); ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU1, AON_EVENT_RTC2);
ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH2);
/* Configure channel 2 in continuous compare, 128 ticks / sec */ /* Configure channel 2 in continuous compare, 128 ticks / sec */
ti_lib_aon_rtc_inc_value_ch2_set(RTIMER_SECOND / CLOCK_SECOND);
ti_lib_aon_rtc_mode_ch2_set(AON_RTC_MODE_CH2_CONTINUOUS);
compare_value = (RTIMER_SECOND / CLOCK_SECOND) + compare_value = (RTIMER_SECOND / CLOCK_SECOND) +
ti_lib_aon_rtc_current_compare_value_get(); ti_lib_aon_rtc_current_compare_value_get();
ti_lib_aon_rtc_compare_value_set(AON_RTC_CH2, compare_value); ti_lib_aon_rtc_compare_value_set(AON_RTC_CH2, compare_value);
ti_lib_aon_rtc_inc_value_ch2_set(RTIMER_SECOND / CLOCK_SECOND);
ti_lib_aon_rtc_mode_ch2_set(AON_RTC_MODE_CH2_CONTINUOUS);
/* Enable event generation for channels 0 and 2 and enable the RTC */ /* Enable channel 2 and the RTC */
ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH2);
ti_lib_aon_rtc_channel_enable(AON_RTC_CH2); ti_lib_aon_rtc_channel_enable(AON_RTC_CH2);
ti_lib_aon_rtc_enable(); ti_lib_aon_rtc_enable();
ti_lib_int_enable(INT_AON_RTC); ti_lib_int_enable(INT_AON_RTC);
ti_lib_int_master_enable();
/* Re-enable interrupts */
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
rtimer_clock_t rtimer_clock_t

View file

@ -40,6 +40,7 @@
#include "sys/energest.h" #include "sys/energest.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Which events to trigger a UART interrupt */ /* Which events to trigger a UART interrupt */
#define CC26XX_UART_RX_INTERRUPT_TRIGGERS (UART_INT_RX | UART_INT_RT) #define CC26XX_UART_RX_INTERRUPT_TRIGGERS (UART_INT_RX | UART_INT_RT)
@ -53,35 +54,98 @@
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static int (*input_handler)(unsigned char c); static int (*input_handler)(unsigned char c);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static bool
power_domain_on(void) usable(void)
{ {
if(BOARD_IOID_UART_RX == IOID_UNUSED ||
BOARD_IOID_UART_TX == IOID_UNUSED ||
CC26XX_UART_CONF_ENABLE == 0) {
return false;
}
return true;
}
/*---------------------------------------------------------------------------*/
static void
power_and_clock(void)
{
/* Power on the SERIAL PD */
ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL); ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL);
while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL) while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON); != PRCM_DOMAIN_POWER_ON);
/* Enable UART clock in active mode */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_UART0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
}
/*---------------------------------------------------------------------------*/
/*
* Returns 0 if either the SERIAL PD is off, or the PD is on but the run mode
* clock is gated. If this function would return 0, accessing UART registers
* will return a precise bus fault. If this function returns 1, it is safe to
* access UART registers.
*
* This function only checks the 'run mode' clock gate, since it can only ever
* be called with the MCU in run mode.
*/
static bool
accessible(void)
{
/* First, check the PD */
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON) {
return false;
}
/* Then check the 'run mode' clock gate */
if(!(HWREG(PRCM_BASE + PRCM_O_UARTCLKGR) & PRCM_UARTCLKGR_CLK_EN)) {
return false;
}
return true;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
configure_baud_rate(void) disable_interrupts(void)
{ {
/* Acknowledge UART interrupts */
ti_lib_int_disable(INT_UART0);
/* Disable all UART module interrupts */
ti_lib_uart_int_disable(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
/* Clear all UART interrupts */
ti_lib_uart_int_clear(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
}
/*---------------------------------------------------------------------------*/
static void
enable_interrupts(void)
{
/* Clear all UART interrupts */
ti_lib_uart_int_clear(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
/* Enable RX-related interrupts only if we have an input handler */
if(input_handler) {
/* Configure which interrupts to generate: FIFO level or after RX timeout */
ti_lib_uart_int_enable(UART0_BASE, CC26XX_UART_RX_INTERRUPT_TRIGGERS);
/* Acknowledge UART interrupts */
ti_lib_int_enable(INT_UART0);
}
}
/*---------------------------------------------------------------------------*/
static void
configure(void)
{
uint32_t ctl_val = UART_CTL_UARTEN | UART_CTL_TXE;
/* /*
* Configure the UART for 115,200, 8-N-1 operation. * Make sure the TX pin is output / high before assigning it to UART control
* This function uses SysCtrlClockGet() to get the system clock * to avoid falling edge glitches
* frequency. This could be also be a variable or hard coded value
* instead of a function call.
*/ */
ti_lib_uart_config_set_exp_clk(UART0_BASE, ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_UART_TX);
ti_lib_sys_ctrl_peripheral_clock_get( ti_lib_gpio_pin_write(BOARD_UART_TX, 1);
PRCM_PERIPH_UART0,
SYSCTRL_SYSBUS_ON),
CC26XX_UART_CONF_BAUD_RATE,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
UART_CONFIG_PAR_NONE));
}
/*---------------------------------------------------------------------------*/
static void
configure_registers(void)
{
/* /*
* Map UART signals to the correct GPIO pins and configure them as * Map UART signals to the correct GPIO pins and configure them as
* hardware controlled. * hardware controlled.
@ -89,7 +153,14 @@ configure_registers(void)
ti_lib_ioc_pin_type_uart(UART0_BASE, BOARD_IOID_UART_RX, BOARD_IOID_UART_TX, ti_lib_ioc_pin_type_uart(UART0_BASE, BOARD_IOID_UART_RX, BOARD_IOID_UART_TX,
BOARD_IOID_UART_CTS, BOARD_IOID_UART_RTS); BOARD_IOID_UART_CTS, BOARD_IOID_UART_RTS);
configure_baud_rate(); /* Configure the UART for 115,200, 8-N-1 operation. */
ti_lib_uart_config_set_exp_clk(UART0_BASE,
ti_lib_sys_ctrl_peripheral_clock_get(
PRCM_PERIPH_UART0,
SYSCTRL_SYSBUS_ON),
CC26XX_UART_CONF_BAUD_RATE,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
UART_CONFIG_PAR_NONE));
/* /*
* Generate an RX interrupt at FIFO 1/2 full. * Generate an RX interrupt at FIFO 1/2 full.
@ -97,116 +168,138 @@ configure_registers(void)
*/ */
ti_lib_uart_fifo_level_set(UART0_BASE, UART_FIFO_TX7_8, UART_FIFO_RX4_8); ti_lib_uart_fifo_level_set(UART0_BASE, UART_FIFO_TX7_8, UART_FIFO_RX4_8);
/* Configure which interrupts to generate: FIFO level or after RX timeout */ /* Enable FIFOs */
ti_lib_uart_int_enable(UART0_BASE, CC26XX_UART_RX_INTERRUPT_TRIGGERS); HWREG(UART0_BASE + UART_O_LCRH) |= UART_LCRH_FEN;
}
/*---------------------------------------------------------------------------*/
static void
uart_on(void)
{
power_domain_on();
/* Configure baud rate and enable */ if(input_handler) {
if((HWREG(UART0_BASE + UART_O_CTL) & UART_CTL_UARTEN) == 0) { ctl_val += UART_CTL_RXE;
configure_registers();
/* Enable UART */
ti_lib_uart_enable(UART0_BASE);
} }
}
/*---------------------------------------------------------------------------*/ /* Enable TX, RX (conditionally), and the UART. */
static uint8_t HWREG(UART0_BASE + UART_O_CTL) = ctl_val;
lpm_permit_max_pm_handler(void)
{
return LPM_MODE_MAX_SUPPORTED;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
lpm_drop_handler(uint8_t mode) lpm_drop_handler(uint8_t mode)
{ {
/* Do nothing if the PD is off */ /*
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL) * First, wait for any outstanding TX to complete. If we have an input
!= PRCM_DOMAIN_POWER_ON) { * handler, the SERIAL PD will be kept on and the UART module clock will
return; * be enabled under sleep as well as deep sleep. In theory, this means that
* we shouldn't lose any outgoing bytes, but we actually do on occasion.
* This byte loss may (or may not) be related to the freezing of IO latches
* between MCU and AON when we drop to deep sleep. This here is essentially a
* workaround
*/
if(accessible() == true) {
while(ti_lib_uart_busy(UART0_BASE));
} }
/* Wait for outstanding TX to complete */
while(ti_lib_uart_busy(UART0_BASE));
/* /*
* Check our clock gate under Deep Sleep. If it's off, we can shut down. If * If we have a registered input_handler then we need to retain RX
* it's on, this means that some other code module wants UART functionality * capability. Thus, if this is not a shutdown notification and we have an
* during deep sleep, so we stay enabled * input handler, we do nothing
*/ */
if((HWREG(PRCM_BASE + PRCM_O_UARTCLKGDS) & 1) == 0) { if((mode != LPM_MODE_SHUTDOWN) && (input_handler != NULL)) {
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_UART_RX);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_UART_TX);
ti_lib_uart_disable(UART0_BASE);
}
}
/*---------------------------------------------------------------------------*/
static void
lpm_wakeup_handler(void)
{
uart_on();
}
/*---------------------------------------------------------------------------*/
/* Declare a data structure to register with LPM. */
LPM_MODULE(uart_module, lpm_permit_max_pm_handler,
lpm_drop_handler, lpm_wakeup_handler);
/*---------------------------------------------------------------------------*/
void
cc26xx_uart_init()
{
/* Exit without initialising if ports are misconfigured */
if(BOARD_IOID_UART_RX == IOID_UNUSED ||
BOARD_IOID_UART_TX == IOID_UNUSED) {
return; return;
} }
/* Enable the serial domain and wait for domain to be on */ /*
power_domain_on(); * If we reach here, we either don't care about staying awake or we have
* received a shutdown notification
*
* Only touch UART registers if the module is powered and clocked
*/
if(accessible() == true) {
/* Disable the module */
ti_lib_uart_disable(UART0_BASE);
/* Enable the UART clock when running and sleeping */ /* Disable all UART interrupts and clear all flags */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_UART0); disable_interrupts();
}
/* Apply clock settings and wait for them to take effect */ /*
* Always stop the clock in run mode. Also stop in Sleep and Deep Sleep if
* this is a request for full shutdown
*/
ti_lib_prcm_peripheral_run_disable(PRCM_PERIPH_UART0);
if(mode == LPM_MODE_SHUTDOWN) {
ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_UART0);
ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_UART0);
}
ti_lib_prcm_load_set(); ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get()); while(!ti_lib_prcm_load_get());
/* Disable Interrupts */ /* Set pins to low leakage configuration in preparation for deep sleep */
ti_lib_int_master_disable(); lpm_pin_set_default_state(BOARD_IOID_UART_TX);
lpm_pin_set_default_state(BOARD_IOID_UART_RX);
lpm_pin_set_default_state(BOARD_IOID_UART_CTS);
lpm_pin_set_default_state(BOARD_IOID_UART_RTS);
}
/*---------------------------------------------------------------------------*/
/* Declare a data structure to register with LPM. */
LPM_MODULE(uart_module, NULL, lpm_drop_handler, NULL, LPM_DOMAIN_NONE);
/*---------------------------------------------------------------------------*/
static void
enable(void)
{
power_and_clock();
/* Make sure the peripheral is disabled */ /* Make sure the peripheral is disabled */
ti_lib_uart_disable(UART0_BASE); ti_lib_uart_disable(UART0_BASE);
/* Disable all UART module interrupts */ /* Disable all UART interrupts and clear all flags */
ti_lib_uart_int_disable(UART0_BASE, CC26XX_UART_INTERRUPT_ALL); disable_interrupts();
configure_registers(); /* Setup pins, Baud rate and FIFO levels */
configure();
/* Acknowledge UART interrupts */ /* Enable UART interrupts */
ti_lib_int_enable(INT_UART0); enable_interrupts();
}
/*---------------------------------------------------------------------------*/
void
cc26xx_uart_init()
{
bool interrupts_disabled;
/* Re-enable processor interrupts */ /* Return early if disabled by user conf or if ports are misconfigured */
ti_lib_int_master_enable(); if(usable() == false) {
return;
}
/* Enable UART */ /* Disable Interrupts */
ti_lib_uart_enable(UART0_BASE); interrupts_disabled = ti_lib_int_master_disable();
/* Register ourselves with the LPM module */ /* Register ourselves with the LPM module */
lpm_register_module(&uart_module); lpm_register_module(&uart_module);
/* Only TX and EN to start with. RX will be enabled only if needed */
input_handler = NULL;
/*
* init() won't actually fire up the UART. We turn it on only when (and if)
* it gets requested, either to enable input or to send out a character
*
* Thus, we simply re-enable processor interrupts here
*/
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
cc26xx_uart_write_byte(uint8_t c) cc26xx_uart_write_byte(uint8_t c)
{ {
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL) /* Return early if disabled by user conf or if ports are misconfigured */
!= PRCM_DOMAIN_POWER_ON) { if(usable() == false) {
return; return;
} }
if(accessible() == false) {
enable();
}
ti_lib_uart_char_put(UART0_BASE, c); ti_lib_uart_char_put(UART0_BASE, c);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -214,9 +307,52 @@ void
cc26xx_uart_set_input(int (*input)(unsigned char c)) cc26xx_uart_set_input(int (*input)(unsigned char c))
{ {
input_handler = input; input_handler = input;
/* Return early if disabled by user conf or if ports are misconfigured */
if(usable() == false) {
return;
}
if(input == NULL) {
/* Let the SERIAL PD power down */
uart_module.domain_lock = LPM_DOMAIN_NONE;
/* Disable module clocks under sleep and deep sleep */
ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_UART0);
ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_UART0);
} else {
/* Request the SERIAL PD to stay on during deep sleep */
uart_module.domain_lock = LPM_DOMAIN_SERIAL;
/* Enable module clocks under sleep and deep sleep */
ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_UART0);
ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_UART0);
}
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
enable();
return; return;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uint8_t
cc26xx_uart_busy(void)
{
/* Return early if disabled by user conf or if ports are misconfigured */
if(usable() == false) {
return UART_IDLE;
}
/* If the UART is not accessible, it is not busy */
if(accessible() == false) {
return UART_IDLE;
}
return ti_lib_uart_busy(UART0_BASE);
}
/*---------------------------------------------------------------------------*/
void void
cc26xx_uart_isr(void) cc26xx_uart_isr(void)
{ {
@ -225,6 +361,8 @@ cc26xx_uart_isr(void)
ENERGEST_ON(ENERGEST_TYPE_IRQ); ENERGEST_ON(ENERGEST_TYPE_IRQ);
power_and_clock();
/* Read out the masked interrupt status */ /* Read out the masked interrupt status */
flags = ti_lib_uart_int_status(UART0_BASE, true); flags = ti_lib_uart_int_status(UART0_BASE, true);

View file

@ -63,9 +63,32 @@ void cc26xx_uart_write_byte(uint8_t b);
/** /**
* \brief Assigns a callback to be called when the UART receives a byte * \brief Assigns a callback to be called when the UART receives a byte
* \param input A pointer to the function * \param input A pointer to the function
*
* If \e input is NULL, the UART driver will assume that RX functionality is
* not required and it will be disabled. It will also disable the module's
* clocks under sleep and deep sleep and allow the SERIAL PD to be powered off.
*
* If \e input is not NULL, the UART driver will assume that RX is in fact
* required and it will be enabled. The module's clocks will be enabled under
* sleep and deep sleep and the driver will not allow the SERIAL PD to turn
* off during deep sleep, so that the UART can still receive bytes.
*
* \note This has a significant impact on overall energy consumption, so you
* should only enabled UART RX input when it's actually required.
*/ */
void cc26xx_uart_set_input(int (*input)(unsigned char c)); void cc26xx_uart_set_input(int (*input)(unsigned char c));
/**
* \brief Returns the UART busy status
* \return UART_IDLE or UART_BUSY
*
* ti_lib_uart_busy() will access UART registers. It is our responsibility
* to first make sure the UART is accessible before calling it. Hence this
* wrapper.
*
* Return values are defined in CC26xxware's uart.h
*/
uint8_t cc26xx_uart_busy(void);
/** @} */ /** @} */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#endif /* CC26XX_UART_H_ */ #endif /* CC26XX_UART_H_ */

View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 2015, Texas Instruments Incorporated - http://www.ti.com/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*---------------------------------------------------------------------------*/
/**
* \addtogroup cc26xx-oscillators
* @{
*
* \file
* Implementation of CC26xxware oscillator control wrappers.
*/
/*---------------------------------------------------------------------------*/
#include "ti-lib.h"
#include <stdint.h>
/*---------------------------------------------------------------------------*/
static uint32_t
osc_interface_en(void)
{
uint32_t smph_clk_state;
/* Enable OSC DIG interface to change clock sources */
ti_lib_osc_interface_enable();
/* Save the state of the SMPH clock within AUX */
smph_clk_state = ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK);
/* Make sure the SMPH clock within AUX is enabled */
ti_lib_aux_wuc_clock_enable(AUX_WUC_SMPH_CLOCK);
while(ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY);
return smph_clk_state;
}
/*---------------------------------------------------------------------------*/
static void
osc_interface_dis(uint32_t smph_clk_state)
{
/* If the SMPH clock was off, turn it back off */
if(smph_clk_state == AUX_WUC_CLOCK_OFF) {
ti_lib_aux_wuc_clock_disable(AUX_WUC_SMPH_CLOCK);
}
/* Disable OSC DIG interface */
ti_lib_osc_interface_disable();
}
/*---------------------------------------------------------------------------*/
void
oscillators_select_lf_xosc(void)
{
/* Enable the Osc interface and remember the state of the SMPH clock */
uint32_t smph_clk_state = osc_interface_en();
/* Switch LF clock source to the LF XOSC if required */
if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_LF) != OSC_XOSC_LF) {
ti_lib_osc_clock_source_set(OSC_SRC_CLK_LF, OSC_XOSC_LF);
/* Wait for LF clock source to become XOSC_LF */
while(ti_lib_osc_clock_source_get(OSC_SRC_CLK_LF) != OSC_XOSC_LF);
/* Disable the LF clock qualifiers */
ti_lib_ddi_16_bit_field_write(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
DDI_0_OSC_CTL0_BYPASS_XOSC_LF_CLK_QUAL_M |
DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_M,
DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_S,
0x3);
}
/* Restore the SMPH clock and disable the OSC interface */
osc_interface_dis(smph_clk_state);
}
/*---------------------------------------------------------------------------*/
void
oscillators_select_lf_rcosc(void)
{
/* Enable the Osc interface and remember the state of the SMPH clock */
uint32_t smph_clk_state = osc_interface_en();
/* Switch LF clock source to the LF XOSC if required */
if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_LF) != OSC_RCOSC_LF) {
ti_lib_osc_clock_source_set(OSC_SRC_CLK_LF, OSC_RCOSC_LF);
/* Wait for LF clock source to become XOSC_LF */
while(ti_lib_osc_clock_source_get(OSC_SRC_CLK_LF) != OSC_RCOSC_LF);
}
/* Restore the SMPH clock and disable the OSC interface */
osc_interface_dis(smph_clk_state);
}
/*---------------------------------------------------------------------------*/
void
oscillators_request_hf_xosc(void)
{
/* Enable the Osc interface and remember the state of the SMPH clock */
uint32_t smph_clk_state = osc_interface_en();
if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_HF) != OSC_XOSC_HF) {
/*
* Request to switch to the crystal to enable radio operation. It takes a
* while for the XTAL to be ready so instead of performing the actual
* switch, we return and we do other stuff while the XOSC is getting ready.
*/
ti_lib_osc_clock_source_set(OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_XOSC_HF);
}
/* Restore the SMPH clock and disable the OSC interface */
osc_interface_dis(smph_clk_state);
}
/*---------------------------------------------------------------------------*/
void
oscillators_switch_to_hf_xosc(void)
{
/* Enable the Osc interface and remember the state of the SMPH clock */
uint32_t smph_clk_state = osc_interface_en();
if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_HF) != OSC_XOSC_HF) {
/* Switch the HF clock source (cc26xxware executes this from ROM) */
ti_lib_osc_hf_source_switch();
}
/* Restore the SMPH clock and disable the OSC interface */
osc_interface_dis(smph_clk_state);
}
/*---------------------------------------------------------------------------*/
void
oscillators_switch_to_hf_rc(void)
{
/* Enable the Osc interface and remember the state of the SMPH clock */
uint32_t smph_clk_state = osc_interface_en();
/* Set all clock sources to the HF RC Osc */
ti_lib_osc_clock_source_set(OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_RCOSC_HF);
/* Check to not enable HF RC oscillator if already enabled */
if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_HF) != OSC_RCOSC_HF) {
/* Switch the HF clock source (cc26xxware executes this from ROM) */
ti_lib_osc_hf_source_switch();
}
/* Restore the SMPH clock and disable the OSC interface */
osc_interface_dis(smph_clk_state);
}
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2015, Texas Instruments Incorporated - http://www.ti.com/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*---------------------------------------------------------------------------*/
/**
* \addtogroup cc26xx
* @{
*
* \defgroup cc26xx-oscillators CC26XX oscillator control
*
* Wrapper around those CC26xxware OSC functions that we need in Contiki.
*
* All CC26xxware OSC control requires access to the semaphore module within
* AUX. Thus, in addition to enabling the oscillator interface, we need to
* start the clock to SMPH and restore it to its previous state when we are
* done.
* @{
*
* \file
* Header file for the CC26XX oscillator control
*/
/*---------------------------------------------------------------------------*/
#ifndef OSCILLATORS_H_
#define OSCILLATORS_H_
/*---------------------------------------------------------------------------*/
/**
* \brief Set the LF clock source to be the LF XOSC
*
* This function is only called once as soon as the system starts.
*
* Do not switch the LF clock source to the RC OSC for normal system operation
* See CC26xx Errata (swrz058)
*/
void oscillators_select_lf_xosc(void);
/**
* \brief Set the LF clock source to be the LF RCOSC
*
* This function is only called once, when the systen transitions to a full
* shutdown
*
* Do not switch the LF clock source to the RC OSC for normal system operation
* See CC26xx Errata (swrz058)
*/
void oscillators_select_lf_rcosc(void);
/**
* \brief Requests the HF XOSC as the source for the HF clock, but does not
* perform the actual switch.
*
* This triggers the startup sequence of the HF XOSC and returns so the CPU
* can perform other tasks while the XOSC is starting.
*
* The XOSC is requested as the source for the HF as well as the MF clock.
*/
void oscillators_request_hf_xosc(void);
/**
* \brief Performs the switch to the XOSC
*
* This function must be preceded by a call to oscillators_request_hf_xosc()
*/
void oscillators_switch_to_hf_xosc(void);
/**
* \brief Switches MF and HF clock source to be the HF RC OSC
*/
void oscillators_switch_to_hf_rc(void);
/*---------------------------------------------------------------------------*/
#endif /* OSCILLATORS_H_ */
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/

View file

@ -49,6 +49,7 @@
#include "dev/leds.h" #include "dev/leds.h"
#include "dev/watchdog.h" #include "dev/watchdog.h"
#include "dev/cc26xx-rtc.h" #include "dev/cc26xx-rtc.h"
#include "dev/oscillators.h"
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#if ENERGEST_CONF_ON #if ENERGEST_CONF_ON
static unsigned long irq_energest = 0; static unsigned long irq_energest = 0;
@ -64,9 +65,6 @@ static unsigned long irq_energest = 0;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
LIST(modules_list); LIST(modules_list);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Control what power domains we are allow to run under what mode */
LIST(power_domain_locks_list);
/* PDs that may stay on in deep sleep */ /* PDs that may stay on in deep sleep */
#define LOCKABLE_DOMAINS ((uint32_t)(PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH)) #define LOCKABLE_DOMAINS ((uint32_t)(PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH))
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -76,49 +74,48 @@ LIST(power_domain_locks_list);
*/ */
#define STANDBY_MIN_DURATION (RTIMER_SECOND >> 8) #define STANDBY_MIN_DURATION (RTIMER_SECOND >> 8)
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Variables used by the power on/off (Power mode: SHUTDOWN) functionality */
static uint8_t shutdown_requested;
static uint32_t pin;
/*---------------------------------------------------------------------------*/
void void
lpm_pd_lock_obtain(lpm_power_domain_lock_t *lock, uint32_t domains) lpm_shutdown(uint32_t wakeup_pin, uint32_t io_pull, uint32_t wake_on)
{
/* We only accept locks for specific PDs */
domains &= LOCKABLE_DOMAINS;
if(domains == 0) {
return;
}
lock->domains = domains;
list_add(power_domain_locks_list, lock);
}
/*---------------------------------------------------------------------------*/
void
lpm_pd_lock_release(lpm_power_domain_lock_t *lock)
{
lock->domains = 0;
list_remove(power_domain_locks_list, lock);
}
/*---------------------------------------------------------------------------*/
void
lpm_shutdown(uint32_t wakeup_pin)
{
shutdown_requested = 1;
pin = wakeup_pin;
}
/*---------------------------------------------------------------------------*/
static void
shutdown_now(void)
{ {
lpm_registered_module_t *module; lpm_registered_module_t *module;
int i; int i, j;
rtimer_clock_t t0; uint32_t io_cfg = (IOC_STD_INPUT & ~IOC_IOPULL_M) | io_pull |
uint32_t io_cfg = (IOC_STD_INPUT & ~IOC_IOPULL_M) | IOC_IOPULL_UP | wake_on;
IOC_WAKE_ON_LOW;
/* This procedure may not be interrupted */
ti_lib_int_master_disable();
/* Disable the RTC */
ti_lib_aon_rtc_disable();
ti_lib_aon_rtc_event_clear(AON_RTC_CH0);
ti_lib_aon_rtc_event_clear(AON_RTC_CH1);
ti_lib_aon_rtc_event_clear(AON_RTC_CH2);
/* Reset AON even fabric to default wakeup sources */
for(i = AON_EVENT_MCU_WU0; i <= AON_EVENT_MCU_WU3; i++) {
ti_lib_aon_event_mcu_wake_up_set(i, AON_EVENT_NULL);
}
for(i = AON_EVENT_AUX_WU0; i <= AON_EVENT_AUX_WU2; i++) {
ti_lib_aon_event_aux_wake_up_set(i, AON_EVENT_NULL);
}
ti_lib_sys_ctrl_aon_sync();
watchdog_periodic();
/* fade away....... */
j = 1000;
for(i = j; i > 0; --i) {
leds_on(LEDS_ALL);
clock_delay_usec(i);
leds_off(LEDS_ALL);
clock_delay_usec(j - i);
}
leds_off(LEDS_ALL);
/* Notify all modules that we're shutting down */
for(module = list_head(modules_list); module != NULL; for(module = list_head(modules_list); module != NULL;
module = module->next) { module = module->next) {
if(module->shutdown) { if(module->shutdown) {
@ -126,34 +123,76 @@ shutdown_now(void)
} }
} }
leds_off(LEDS_ALL); /* Configure the wakeup trigger */
ti_lib_gpio_dir_mode_set((1 << wakeup_pin), GPIO_DIR_MODE_IN);
ti_lib_ioc_port_configure_set(wakeup_pin, IOC_PORT_GPIO, io_cfg);
for(i = 0; i < 5; i++) { /* Freeze I/O latches in AON */
t0 = RTIMER_NOW(); ti_lib_aon_ioc_freeze_enable();
leds_toggle(LEDS_ALL);
while(RTIMER_CLOCK_LT(RTIMER_NOW(), (t0 + (RTIMER_SECOND >> 3))));
}
leds_off(LEDS_ALL); /* Turn off RFCORE, SERIAL and PERIPH PDs. This will happen immediately */
ti_lib_prcm_power_domain_off(PRCM_DOMAIN_RFCORE | PRCM_DOMAIN_SERIAL |
PRCM_DOMAIN_PERIPH);
ti_lib_gpio_dir_mode_set((1 << pin), GPIO_DIR_MODE_IN); oscillators_switch_to_hf_rc();
ti_lib_ioc_port_configure_set(pin, IOC_PORT_GPIO, io_cfg); oscillators_select_lf_rcosc();
ti_lib_pwr_ctrl_state_set(LPM_MODE_SHUTDOWN); /* Configure clock sources for MCU and AUX: No clock */
ti_lib_aon_wuc_mcu_power_down_config(AONWUC_NO_CLOCK);
ti_lib_aon_wuc_aux_power_down_config(AONWUC_NO_CLOCK);
/* Disable retentions: SRAM, CPU, AUX, RFCORE - possibly not required */
ti_lib_aon_wuc_mcu_sram_config(0);
ti_lib_prcm_retention_disable(PRCM_DOMAIN_CPU);
ti_lib_aon_wuc_aux_sram_config(false);
ti_lib_prcm_retention_disable(PRCM_DOMAIN_RFCORE);
/*
* Request CPU, SYSBYS and VIMS PD off.
* This will only happen when the CM3 enters deep sleep
*/
ti_lib_prcm_power_domain_off(PRCM_DOMAIN_CPU | PRCM_DOMAIN_VIMS |
PRCM_DOMAIN_SYSBUS);
/* Request JTAG domain power off */
ti_lib_aon_wuc_jtag_power_off();
/* Turn off AUX */
ti_lib_aux_wuc_power_ctrl(AUX_WUC_POWER_OFF);
ti_lib_aon_wuc_domain_power_down_enable();
while(ti_lib_aon_wuc_power_status() & AONWUC_AUX_POWER_ON);
/*
* Request MCU VD power off.
* This will only happen when the CM3 enters deep sleep
*/
ti_lib_prcm_mcu_power_off();
/* Set MCU wakeup to immediate and disable virtual power off */
ti_lib_aon_wuc_mcu_wake_up_config(MCU_IMM_WAKE_UP);
ti_lib_aon_wuc_mcu_power_off_config(MCU_VIRT_PWOFF_DISABLE);
/* Latch the IOs in the padring and enable I/O pad sleep mode */
ti_lib_pwr_ctrl_io_freeze_enable();
/* Turn off VIMS cache, CRAM and TRAM - possibly not required */
ti_lib_prcm_retention_disable(PRCM_DOMAIN_VIMS);
ti_lib_vims_mode_set(VIMS_BASE, VIMS_MODE_OFF);
/* Enable shutdown and sync AON */
ti_lib_aon_wuc_shut_down_enable();
ti_lib_sys_ctrl_aon_sync();
/* Deep Sleep */
ti_lib_prcm_deep_sleep();
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
* We'll get called on three occasions: * Notify all modules that we're back on and rely on them to restore clocks
* - While running
* - While sleeping
* - While deep sleeping
*
* For the former two, we don't need to do anything. For the latter, we
* notify all modules that we're back on and rely on them to restore clocks
* and power domains as required. * and power domains as required.
*/ */
void static void
lpm_wake_up() wake_up(void)
{ {
lpm_registered_module_t *module; lpm_registered_module_t *module;
@ -182,8 +221,8 @@ lpm_wake_up()
ti_lib_aon_ioc_freeze_disable(); ti_lib_aon_ioc_freeze_disable();
ti_lib_sys_ctrl_aon_sync(); ti_lib_sys_ctrl_aon_sync();
/* Power up AUX and allow it to go to sleep */ /* Check operating conditions, optimally choose DCDC versus GLDO */
ti_lib_aon_wuc_aux_wakeup_event(AONWUC_AUX_ALLOW_SLEEP); ti_lib_sys_ctrl_dcdc_voltage_conditional_control();
/* Notify all registered modules that we've just woken up */ /* Notify all registered modules that we've just woken up */
for(module = list_head(modules_list); module != NULL; for(module = list_head(modules_list); module != NULL;
@ -198,16 +237,11 @@ void
lpm_drop() lpm_drop()
{ {
lpm_registered_module_t *module; lpm_registered_module_t *module;
lpm_power_domain_lock_t *lock;
uint8_t max_pm = LPM_MODE_MAX_SUPPORTED; uint8_t max_pm = LPM_MODE_MAX_SUPPORTED;
uint8_t module_pm; uint8_t module_pm;
uint32_t domains = LOCKABLE_DOMAINS; uint32_t domains = LOCKABLE_DOMAINS;
if(shutdown_requested) {
shutdown_now();
}
if(RTIMER_CLOCK_LT(cc26xx_rtc_get_next_trigger(), if(RTIMER_CLOCK_LT(cc26xx_rtc_get_next_trigger(),
RTIMER_NOW() + STANDBY_MIN_DURATION)) { RTIMER_NOW() + STANDBY_MIN_DURATION)) {
lpm_sleep(); lpm_sleep();
@ -244,26 +278,21 @@ lpm_drop()
* This is a chance for modules to delay us a little bit until an ongoing * This is a chance for modules to delay us a little bit until an ongoing
* operation has finished (e.g. uart TX) or to configure themselves for * operation has finished (e.g. uart TX) or to configure themselves for
* deep sleep. * deep sleep.
*
* At this stage, we also collect power domain locks, if any.
* The argument to PRCMPowerDomainOff() is a bitwise OR, so every time
* we encounter a lock we just clear the respective bits in the 'domains'
* variable as required by the lock. In the end the domains variable will
* just hold whatever has not been cleared
*/ */
for(module = list_head(modules_list); module != NULL; for(module = list_head(modules_list); module != NULL;
module = module->next) { module = module->next) {
if(module->shutdown) { if(module->shutdown) {
module->shutdown(max_pm); module->shutdown(max_pm);
} }
}
/*
* Iterate PD locks to see what we can and cannot turn off.
*
* The argument to PRCMPowerDomainOff() is a bitwise OR, so every time
* we encounter a lock we just clear the respective bits in the 'domains'
* variable as required by the lock. In the end the domains variable will
* just hold whatever has not been cleared
*/
for(lock = list_head(power_domain_locks_list); lock != NULL;
lock = lock->next) {
/* Clear the bits specified in the lock */ /* Clear the bits specified in the lock */
domains &= ~lock->domains; domains &= ~module->domain_lock;
} }
/* Pat the dog: We don't want it to shout right after we wake up */ /* Pat the dog: We don't want it to shout right after we wake up */
@ -289,6 +318,20 @@ lpm_drop()
ti_lib_prcm_power_domain_off(domains); ti_lib_prcm_power_domain_off(domains);
} }
/*
* Before entering Deep Sleep, we must switch off the HF XOSC. The HF XOSC
* is predominantly controlled by the RF driver. In a build with radio
* cycling (e.g. ContikiMAC), the RF driver will request the XOSC before
* using the Freq. Synth, and switch back to the RC when it is about to
* turn back off.
*
* If the radio is on, we won't even reach here, and if it's off the HF
* clock source should already be the HF RC.
*
* Nevertheless, request the switch to the HF RC explicitly here.
*/
oscillators_switch_to_hf_rc();
/* Configure clock sources for MCU and AUX: No clock */ /* Configure clock sources for MCU and AUX: No clock */
ti_lib_aon_wuc_mcu_power_down_config(AONWUC_NO_CLOCK); ti_lib_aon_wuc_mcu_power_down_config(AONWUC_NO_CLOCK);
ti_lib_aon_wuc_aux_power_down_config(AONWUC_NO_CLOCK); ti_lib_aon_wuc_aux_power_down_config(AONWUC_NO_CLOCK);
@ -304,11 +347,7 @@ lpm_drop()
ti_lib_aon_wuc_aux_sram_config(false); ti_lib_aon_wuc_aux_sram_config(false);
/* Disable retention in the RFCORE RAM */ /* Disable retention in the RFCORE RAM */
HWREG(PRCM_BASE + PRCM_O_RAMRETEN) &= ~PRCM_RAMRETEN_RFC; ti_lib_prcm_retention_disable(PRCM_DOMAIN_RFCORE);
/* Disable retention of VIMS RAM (TRAM and CRAM) */
//TODO: This can probably be removed, we are calling ti_lib_prcm_retention_disable(PRCM_DOMAIN_VIMS); further down
HWREG(PRCM_BASE + PRCM_O_RAMRETEN) &= ~PRCM_RAMRETEN_VIMS_M;
/* /*
* Always turn off RFCORE, CPU, SYSBUS and VIMS. RFCORE should be off * Always turn off RFCORE, CPU, SYSBUS and VIMS. RFCORE should be off
@ -329,7 +368,7 @@ lpm_drop()
ti_lib_sys_ctrl_set_recharge_before_power_down(false); ti_lib_sys_ctrl_set_recharge_before_power_down(false);
/* /*
* If both PERIPH and SERIAL PDs are off, request the uLDO for as the power * If both PERIPH and SERIAL PDs are off, request the uLDO as the power
* source while in deep sleep. * source while in deep sleep.
*/ */
if(domains == LOCKABLE_DOMAINS) { if(domains == LOCKABLE_DOMAINS) {
@ -362,7 +401,7 @@ lpm_drop()
* the chip properly, and then we will enable the global interrupt without * the chip properly, and then we will enable the global interrupt without
* unpending events so the handlers can fire * unpending events so the handlers can fire
*/ */
lpm_wake_up(); wake_up();
ti_lib_int_master_enable(); ti_lib_int_master_enable();
} }
@ -396,10 +435,26 @@ lpm_register_module(lpm_registered_module_t *module)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
lpm_unregister_module(lpm_registered_module_t *module)
{
list_remove(modules_list, module);
}
/*---------------------------------------------------------------------------*/
void
lpm_init() lpm_init()
{ {
list_init(modules_list); list_init(modules_list);
list_init(power_domain_locks_list); }
/*---------------------------------------------------------------------------*/
void
lpm_pin_set_default_state(uint32_t ioid)
{
if(ioid == IOID_UNUSED) {
return;
}
ti_lib_ioc_port_configure_set(ioid, IOC_PORT_GPIO, IOC_STD_OUTPUT);
ti_lib_gpio_dir_mode_set((1 << ioid), GPIO_DIR_MODE_IN);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** /**

View file

@ -49,17 +49,22 @@
#include <stdint.h> #include <stdint.h>
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define LPM_MODE_SLEEP PWRCTRL_ACTIVE #define LPM_MODE_SLEEP 1
#define LPM_MODE_DEEP_SLEEP PWRCTRL_POWER_DOWN #define LPM_MODE_DEEP_SLEEP 2
#define LPM_MODE_SHUTDOWN PWRCTRL_SHUTDOWN #define LPM_MODE_SHUTDOWN 3
#define LPM_MODE_MAX_SUPPORTED LPM_MODE_DEEP_SLEEP #define LPM_MODE_MAX_SUPPORTED LPM_MODE_DEEP_SLEEP
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define LPM_DOMAIN_NONE 0
#define LPM_DOMAIN_SERIAL PRCM_DOMAIN_SERIAL
#define LPM_DOMAIN_PERIPH PRCM_DOMAIN_PERIPH
/*---------------------------------------------------------------------------*/
typedef struct lpm_registered_module { typedef struct lpm_registered_module {
struct lpm_registered_module *next; struct lpm_registered_module *next;
uint8_t (*request_max_pm)(void); uint8_t (*request_max_pm)(void);
void (*shutdown)(uint8_t mode); void (*shutdown)(uint8_t mode);
void (*wakeup)(void); void (*wakeup)(void);
uint32_t domain_lock;
} lpm_registered_module_t; } lpm_registered_module_t;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** /**
@ -78,46 +83,14 @@ typedef struct lpm_registered_module {
* woken up. This can be used to e.g. turn a peripheral back on. This * woken up. This can be used to e.g. turn a peripheral back on. This
* function is in charge of turning power domains back on. This * function is in charge of turning power domains back on. This
* function will normally be called within an interrupt context. * function will normally be called within an interrupt context.
* \param l Power domain locks, if any are required. The module can request
* that the SERIAL or PERIPH PD be kept powered up at the transition
* to deep sleep. This field can be a bitwise OR of LPM_DOMAIN_x, so
* if required multiple domains can be kept powered.
*/ */
#define LPM_MODULE(n, m, s, w) static lpm_registered_module_t n = \ #define LPM_MODULE(n, m, s, w, l) static lpm_registered_module_t n = \
{ NULL, m, s, w } { NULL, m, s, w, l }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/**
*
* \brief Data type used to control whether a PD will get shut down when the
* CM3 drops to deep sleep
*
* Modules using these facilities must allocate a variable of this type, but
* they must not try to manipulate it directly. Instead, the respective
* functions must be used
*
* \sa lpm_pd_lock_obtain(), lpm_pd_lock_release()
*/
typedef struct lpm_power_domain_lock {
struct lpm_power_domain_lock *next;
uint32_t domains;
} lpm_power_domain_lock_t;
/*---------------------------------------------------------------------------*/
/**
* \brief Prohibit a PD from turning off in standby mode
* \param lock A pointer to a lpm_power_domain_lock_t
* \param domains Bitwise OR from PRCM_DOMAIN_PERIPH, PRCM_DOMAIN_SERIAL
*
* The caller is responsible for allocating lpm_power_domain_lock_t
*
* Only the domains listed above can be locked / released, but a single lock
* can be used for multiple domains
*/
void lpm_pd_lock_obtain(lpm_power_domain_lock_t *lock, uint32_t domains);
/**
* \brief Permit a PD to turn off in standby mode
* \param pd A pointer to a previously used lock
*
* \sa lpm_pd_lock_obtain()
*/
void lpm_pd_lock_release(lpm_power_domain_lock_t *pd);
/** /**
* \brief Drop the cortex to sleep / deep sleep and shut down peripherals * \brief Drop the cortex to sleep / deep sleep and shut down peripherals
* *
@ -134,17 +107,11 @@ void lpm_sleep(void);
/** /**
* \brief Put the chip in shutdown power mode * \brief Put the chip in shutdown power mode
* \param wakeup_pin The GPIO pin which will wake us up. Must be IOID_0 etc... * \param wakeup_pin The GPIO pin which will wake us up. Must be IOID_0 etc...
* \param io_pull Pull configuration for the shutdown pin: IOC_NO_IOPULL,
* IOC_IOPULL_UP or IOC_IOPULL_DOWN
* \param wake_on High or Low (IOC_WAKE_ON_LOW or IOC_WAKE_ON_HIGH)
*/ */
void lpm_shutdown(uint32_t wakeup_pin); void lpm_shutdown(uint32_t wakeup_pin, uint32_t io_pull, uint32_t wake_on);
/**
* \brief Wake up from sleep mode
*
* This function must be called at the start of any interrupt context which
* may bring us out of sleep. Current interrupts do this already, but make sure
* to do the same when adding new ISRs
*/
void lpm_wake_up(void);
/** /**
* \brief Register a module for LPM notifications. * \brief Register a module for LPM notifications.
@ -159,10 +126,31 @@ void lpm_wake_up(void);
*/ */
void lpm_register_module(lpm_registered_module_t *module); void lpm_register_module(lpm_registered_module_t *module);
/**
* \brief Unregister a module from LPM notifications.
* \param module A pointer to the data structure with the module definition
*
* When a previously registered module is no longer interested in LPM
* notifications, this function can be used to unregister it.
*/
void lpm_unregister_module(lpm_registered_module_t *module);
/** /**
* \brief Initialise the low-power mode management module * \brief Initialise the low-power mode management module
*/ */
void lpm_init(void); void lpm_init(void);
/**
* \brief Sets an IOID to a default state
* \param ioid IOID_0...
*
* This will set ioid to sw control, input, no pull. Input buffer and output
* driver will both be disabled
*
* The function will do nothing if ioid == IOID_UNUSED, so the caller does not
* have to check board configuration before calling this.
*/
void lpm_pin_set_default_state(uint32_t ioid);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#endif /* LPM_H_ */ #endif /* LPM_H_ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/

View file

@ -29,6 +29,7 @@
*/ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#include "cc26xx-uart.h" #include "cc26xx-uart.h"
#include "ti-lib.h"
#include <string.h> #include <string.h>
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -47,9 +48,16 @@ puts(const char *str)
return 0; return 0;
} }
for(i = 0; i < strlen(str); i++) { for(i = 0; i < strlen(str); i++) {
putchar(str[i]); cc26xx_uart_write_byte(str[i]);
} }
putchar('\n'); cc26xx_uart_write_byte('\n');
/*
* Wait for the line to go out. This is to prevent garbage when used between
* UART on/off cycles
*/
while(cc26xx_uart_busy() == UART_BUSY);
return i; return i;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -62,9 +70,16 @@ dbg_send_bytes(const unsigned char *s, unsigned int len)
if(i >= len) { if(i >= len) {
break; break;
} }
putchar(*s++); cc26xx_uart_write_byte(*s++);
i++; i++;
} }
/*
* Wait for the buffer to go out. This is to prevent garbage when used
* between UART on/off cycles
*/
while(cc26xx_uart_busy() == UART_BUSY);
return i; return i;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/

View file

@ -58,6 +58,10 @@ slip_arch_writeb(unsigned char c)
void void
slip_arch_init(unsigned long ubr) slip_arch_init(unsigned long ubr)
{ {
/*
* Enable an input handler. In doing so, the driver will make sure that UART
* RX stays operational during deep sleep
*/
cc26xx_uart_set_input(slip_input_byte); cc26xx_uart_set_input(slip_input_byte);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/

View file

@ -148,7 +148,7 @@
#include "driverlib/aux_wuc.h" #include "driverlib/aux_wuc.h"
#define ti_lib_aux_wuc_clock_enable(...) AUXWUCClockEnable(__VA_ARGS__) #define ti_lib_aux_wuc_clock_enable(...) AUXWUCClockEnable(__VA_ARGS__)
#define ti_lib_aux_wuc_clock_disble(...) AUXWUCClockDisable(__VA_ARGS__) #define ti_lib_aux_wuc_clock_disable(...) AUXWUCClockDisable(__VA_ARGS__)
#define ti_lib_aux_wuc_clock_status(...) AUXWUCClockStatus(__VA_ARGS__) #define ti_lib_aux_wuc_clock_status(...) AUXWUCClockStatus(__VA_ARGS__)
#define ti_lib_aux_wuc_clock_freq_req(...) AUXWUCClockFreqReq(__VA_ARGS__) #define ti_lib_aux_wuc_clock_freq_req(...) AUXWUCClockFreqReq(__VA_ARGS__)
#define ti_lib_aux_wuc_power_ctrl(...) AUXWUCPowerCtrl(__VA_ARGS__) #define ti_lib_aux_wuc_power_ctrl(...) AUXWUCPowerCtrl(__VA_ARGS__)
@ -531,6 +531,8 @@
#define ti_lib_sys_ctrl_aon_update(...) SysCtrlAonUpdate(__VA_ARGS__) #define ti_lib_sys_ctrl_aon_update(...) SysCtrlAonUpdate(__VA_ARGS__)
#define ti_lib_sys_ctrl_set_recharge_before_power_down(...) SysCtrlSetRechargeBeforePowerDown(__VA_ARGS__) #define ti_lib_sys_ctrl_set_recharge_before_power_down(...) SysCtrlSetRechargeBeforePowerDown(__VA_ARGS__)
#define ti_lib_sys_ctrl_adjust_recharge_after_power_down(...) SysCtrlAdjustRechargeAfterPowerDown(__VA_ARGS__) #define ti_lib_sys_ctrl_adjust_recharge_after_power_down(...) SysCtrlAdjustRechargeAfterPowerDown(__VA_ARGS__)
#define ti_lib_sys_ctrl_dcdc_voltage_conditional_control(...) SysCtrl_DCDC_VoltageConditionalControl(__VA_ARGS__)
#define ti_lib_sys_ctrl_reset_source_get(...) SysCtrlResetSourceGet(__VA_ARGS__)
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* ssi.h */ /* ssi.h */
#include "driverlib/ssi.h" #include "driverlib/ssi.h"

View file

@ -6,8 +6,6 @@ boards. More specifically, the example demonstrates:
* How to take sensor readings * How to take sensor readings
* How to use buttons and the reed relay (triggered by holding a magnet near S3 * How to use buttons and the reed relay (triggered by holding a magnet near S3
on the SensorTag). on the SensorTag).
* How to keep a power domain powered and a peripheral clocked under low power
operation
* How to send out BLE advertisements. The device will periodically send out BLE * How to send out BLE advertisements. The device will periodically send out BLE
beacons with the platform name as payload. Those beacons/BLE ADV packets can beacons with the platform name as payload. Those beacons/BLE ADV packets can
be captured with any BLE capable device. Two such applications for iOS are the be captured with any BLE capable device. Two such applications for iOS are the

View file

@ -77,12 +77,6 @@
* - The example also shows how to retrieve the duration of a * - The example also shows how to retrieve the duration of a
* button press (in ticks). The driver will generate a * button press (in ticks). The driver will generate a
* sensors_changed event upon button release * sensors_changed event upon button release
* - UART : Receiving an entire line of text over UART (ending
* in \\r) will cause CC26XX_DEMO_LEDS_SERIAL_IN to toggle
* This also demonstrates how a code module can influence
* low-power operation: In this example we keep the UART on
* and capable to RX even with the chip in deep sleep.
* see keep_uart_on() and the UART driver
* - Reed Relay : Will toggle the sensortag buzzer on/off * - Reed Relay : Will toggle the sensortag buzzer on/off
* *
* @{ * @{
@ -100,7 +94,6 @@
#include "button-sensor.h" #include "button-sensor.h"
#include "batmon-sensor.h" #include "batmon-sensor.h"
#include "board-peripherals.h" #include "board-peripherals.h"
#include "lpm.h"
#include "cc26xx-rf.h" #include "cc26xx-rf.h"
#include "ti-lib.h" #include "ti-lib.h"
@ -251,8 +244,7 @@ get_light_reading()
printf("OPT: Light Read Error\n"); printf("OPT: Light Read Error\n");
} }
SENSORS_DEACTIVATE(opt_3001_sensor); /* The OPT will turn itself off, so we don't need to call its DEACTIVATE */
ctimer_set(&opt_timer, next, init_opt_reading, NULL); ctimer_set(&opt_timer, next, init_opt_reading, NULL);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -369,26 +361,6 @@ init_sensor_readings(void)
#endif #endif
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static lpm_power_domain_lock_t lock;
/*---------------------------------------------------------------------------*/
/*
* In order to maintain UART input operation:
* - Keep the uart clocked in sleep and deep sleep
* - Keep the serial PD on in deep sleep
*/
static void
keep_uart_on(void)
{
/* Keep the serial PD on */
lpm_pd_lock_obtain(&lock, PRCM_DOMAIN_SERIAL);
/* Keep the UART clock on during Sleep and Deep Sleep */
ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_UART0);
ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_UART0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(cc26xx_demo_process, ev, data) PROCESS_THREAD(cc26xx_demo_process, ev, data)
{ {
@ -406,8 +378,6 @@ PROCESS_THREAD(cc26xx_demo_process, ev, data)
get_sync_sensor_readings(); get_sync_sensor_readings();
init_sensor_readings(); init_sensor_readings();
keep_uart_on();
while(1) { while(1) {
PROCESS_YIELD(); PROCESS_YIELD();
@ -462,8 +432,6 @@ PROCESS_THREAD(cc26xx_demo_process, ev, data)
button_select_sensor.value(BUTTON_SENSOR_VALUE_DURATION)); button_select_sensor.value(BUTTON_SENSOR_VALUE_DURATION));
#endif #endif
} }
} else if(ev == serial_line_event_message) {
leds_toggle(CC26XX_DEMO_LEDS_SERIAL_IN);
} }
} }

View file

@ -574,8 +574,6 @@ get_light_reading()
value = opt_3001_sensor.value(0); value = opt_3001_sensor.value(0);
SENSORS_DEACTIVATE(opt_3001_sensor);
if(value != CC26XX_SENSOR_READING_ERROR) { if(value != CC26XX_SENSOR_READING_ERROR) {
opt_reading.raw = value; opt_reading.raw = value;
@ -587,6 +585,7 @@ get_light_reading()
value % 100); value % 100);
} }
/* The OPT will turn itself off, so we don't need to call its DEACTIVATE */
ctimer_set(&opt_timer, next, init_light_reading, NULL); ctimer_set(&opt_timer, next, init_light_reading, NULL);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/

View file

@ -59,10 +59,10 @@
#include "contiki-conf.h" #include "contiki-conf.h"
#include "sys/process.h" #include "sys/process.h"
#include "dev/serial-line.h" #include "dev/serial-line.h"
#include "dev/cc26xx-uart.h"
#include "net/ip/uip.h" #include "net/ip/uip.h"
#include "net/ip/uip-udp-packet.h" #include "net/ip/uip-udp-packet.h"
#include "net/ip/uiplib.h" #include "net/ip/uiplib.h"
#include "lpm.h"
#include "net-uart.h" #include "net-uart.h"
#include "httpd-simple.h" #include "httpd-simple.h"
@ -148,37 +148,16 @@ net_input(void)
return; return;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/*
* In order to maintain UART input operation:
* - Keep the uart clocked in sleep and deep sleep
* - Keep the serial PD on in deep sleep
*/
static lpm_power_domain_lock_t lock;
/*---------------------------------------------------------------------------*/
static void static void
release_uart(void) release_uart(void)
{ {
/* Release serial PD lock */ cc26xx_uart_set_input(NULL);
lpm_pd_lock_release(&lock);
/* Let the UART turn off during Sleep and Deep Sleep */
ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_UART0);
ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_UART0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
keep_uart_on(void) keep_uart_on(void)
{ {
/* Keep the serial PD on */ cc26xx_uart_set_input(serial_line_input_byte);
lpm_pd_lock_obtain(&lock, PRCM_DOMAIN_SERIAL);
/* Keep the UART clock on during Sleep and Deep Sleep */
ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_UART0);
ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_UART0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static int static int

View file

@ -46,6 +46,7 @@
#include "lpm.h" #include "lpm.h"
#include "gpio-interrupt.h" #include "gpio-interrupt.h"
#include "dev/watchdog.h" #include "dev/watchdog.h"
#include "dev/oscillators.h"
#include "ieee-addr.h" #include "ieee-addr.h"
#include "vims.h" #include "vims.h"
#include "cc26xx-model.h" #include "cc26xx-model.h"
@ -119,23 +120,6 @@ set_rf_params(void)
#endif #endif
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void
select_lf_xosc(void)
{
ti_lib_osc_interface_enable();
/* Make sure the SMPH clock within AUX is enabled */
ti_lib_aux_wuc_clock_enable(AUX_WUC_SMPH_CLOCK);
while(ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY);
/* Switch LF clock source to the LF RCOSC if required */
if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_LF) != OSC_XOSC_LF) {
ti_lib_osc_clock_source_set(OSC_SRC_CLK_LF, OSC_XOSC_LF);
}
ti_lib_osc_interface_disable();
}
/*---------------------------------------------------------------------------*/
/** /**
* \brief Main function for CC26xx-based platforms * \brief Main function for CC26xx-based platforms
* *
@ -144,41 +128,39 @@ select_lf_xosc(void)
int int
main(void) main(void)
{ {
/* Enable flash cache and prefetch. */
ti_lib_vims_mode_set(VIMS_BASE, VIMS_MODE_ENABLED);
ti_lib_vims_configure(VIMS_BASE, true, true);
ti_lib_int_master_disable();
/* Set the LF XOSC as the LF system clock source */ /* Set the LF XOSC as the LF system clock source */
select_lf_xosc(); oscillators_select_lf_xosc();
/*
* Make sure to open the latches - this will be important when returning
* from shutdown
*/
ti_lib_pwr_ctrl_io_freeze_disable();
/* Use DCDC instead of LDO to save current */
ti_lib_pwr_ctrl_source_set(PWRCTRL_PWRSRC_DCDC);
lpm_init(); lpm_init();
board_init(); board_init();
/* Enable flash cache and prefetch. */
ti_lib_vims_mode_set(VIMS_BASE, VIMS_MODE_ENABLED);
ti_lib_vims_configure(VIMS_BASE, true, true);
gpio_interrupt_init(); gpio_interrupt_init();
/* Clock must always be enabled for the semaphore module */
HWREG(AUX_WUC_BASE + AUX_WUC_O_MODCLKEN1) = AUX_WUC_MODCLKEN1_SMPH;
leds_init(); leds_init();
/*
* Disable I/O pad sleep mode and open I/O latches in the AON IOC interface
* This is only relevant when returning from shutdown (which is what froze
* latches in the first place. Before doing these things though, we should
* allow software to first regain control of pins
*/
ti_lib_pwr_ctrl_io_freeze_disable();
fade(LEDS_RED); fade(LEDS_RED);
ti_lib_int_master_enable();
cc26xx_rtc_init(); cc26xx_rtc_init();
clock_init(); clock_init();
rtimer_init(); rtimer_init();
board_init();
watchdog_init(); watchdog_init();
process_init(); process_init();
@ -187,7 +169,6 @@ main(void)
/* Character I/O Initialisation */ /* Character I/O Initialisation */
#if CC26XX_UART_CONF_ENABLE #if CC26XX_UART_CONF_ENABLE
cc26xx_uart_init(); cc26xx_uart_init();
cc26xx_uart_set_input(serial_line_input_byte);
#endif #endif
serial_line_init(); serial_line_init();

View file

@ -136,7 +136,7 @@ static int enabled = SENSOR_STATUS_DISABLED;
static uint8_t sensor_value[SENSOR_DATA_BUF_SIZE]; static uint8_t sensor_value[SENSOR_DATA_BUF_SIZE];
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Wait SENSOR_STARTUP_DELAY clock ticks for the sensor to be ready - ~80ms */ /* Wait SENSOR_STARTUP_DELAY clock ticks for the sensor to be ready - ~80ms */
#define SENSOR_STARTUP_DELAY 11 #define SENSOR_STARTUP_DELAY 3
static struct ctimer startup_timer; static struct ctimer startup_timer;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -187,7 +187,7 @@ enable_sensor(bool enable)
if(enable) { if(enable) {
/* Enable forced mode */ /* Enable forced mode */
val = PM_NORMAL | OSRSP(1) | OSRST(1); val = PM_FORCED | OSRSP(1) | OSRST(1);
} else { } else {
val = PM_OFF; val = PM_OFF;
} }
@ -345,6 +345,7 @@ configure(int type, int enable)
case SENSORS_HW_INIT: case SENSORS_HW_INIT:
enabled = SENSOR_STATUS_INITIALISED; enabled = SENSOR_STATUS_INITIALISED;
init(); init();
enable_sensor(0);
break; break;
case SENSORS_ACTIVE: case SENSORS_ACTIVE:
/* Must be initialised first */ /* Must be initialised first */

View file

@ -42,12 +42,10 @@
* *
* Once the sensor is stable, the driver will generate a sensors_changed event. * Once the sensor is stable, the driver will generate a sensors_changed event.
* *
* Once a reading has been taken, the caller has two options: * We take readings in "Forced" mode. In this mode, the BMP will take a single
* - Turn the sensor off by calling SENSORS_DEACTIVATE, but in order to take * measurement and it will then automatically go to sleep.
* subsequent readings SENSORS_ACTIVATE must be called again *
* - Leave the sensor on. In this scenario, the caller can simply keep calling * SENSORS_ACTIVATE must be called again to trigger a new reading cycle
* value() for subsequent readings, but having the sensor on will consume
* energy
* @{ * @{
* *
* \file * \file

View file

@ -39,9 +39,55 @@
#include "contiki-conf.h" #include "contiki-conf.h"
#include "ti-lib.h" #include "ti-lib.h"
#include "board-i2c.h" #include "board-i2c.h"
#include "lpm.h"
#include <string.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/
#define NO_INTERFACE 0xFF
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static uint8_t slave_addr = 0x00; static uint8_t slave_addr = 0x00;
static uint8_t interface = 0xFF; static uint8_t interface = NO_INTERFACE;
/*---------------------------------------------------------------------------*/
static bool
accessible(void)
{
/* First, check the PD */
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON) {
return false;
}
/* Then check the 'run mode' clock gate */
if(!(HWREG(PRCM_BASE + PRCM_O_I2CCLKGR) & PRCM_I2CCLKGR_CLK_EN)) {
return false;
}
return true;
}
/*---------------------------------------------------------------------------*/
void
board_i2c_wakeup()
{
/* First, make sure the SERIAL PD is on */
ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL);
while((ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON));
/* Enable the clock to I2C */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_I2C0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
/* Reset the I2C controller */
HWREG(PRCM_BASE + PRCM_O_RESETI2C) = PRCM_RESETI2C_I2C;
/* Enable and initialize the I2C master module */
ti_lib_i2c_master_init_exp_clk(I2C0_BASE,
ti_lib_sys_ctrl_peripheral_clock_get(
PRCM_PERIPH_I2C0, SYSCTRL_SYSBUS_ON),
true);
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static bool static bool
i2c_status() i2c_status()
@ -57,21 +103,34 @@ i2c_status()
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
board_i2c_init() board_i2c_shutdown()
{ {
/* The I2C peripheral must be enabled */ interface = NO_INTERFACE;
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_I2C0);
if(accessible()) {
ti_lib_i2c_master_disable(I2C0_BASE);
}
ti_lib_prcm_peripheral_run_disable(PRCM_PERIPH_I2C0);
ti_lib_prcm_load_set(); ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get()); while(!ti_lib_prcm_load_get());
/* Reset the I2C controller */ /*
HWREG(PRCM_BASE + PRCM_O_RESETI2C) = PRCM_RESETI2C_I2C; * Set all pins to GPIO Input and disable the output driver. Set internal
* pull to match external pull
*
* SDA and SCL: external PU resistor
* SDA HP and SCL HP: MPU PWR low
*/
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA_HP);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA_HP, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL_HP);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL_HP, IOC_IOPULL_DOWN);
/* Enable and initialize the I2C master module */ ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA);
ti_lib_i2c_master_init_exp_clk(I2C0_BASE, ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_IOPULL_UP);
ti_lib_sys_ctrl_peripheral_clock_get( ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL);
PRCM_PERIPH_I2C0, SYSCTRL_SYSBUS_ON), ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_IOPULL_UP);
true);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
bool bool
@ -248,8 +307,15 @@ board_i2c_select(uint8_t new_interface, uint8_t address)
{ {
slave_addr = address; slave_addr = address;
if(accessible() == false) {
board_i2c_wakeup();
}
if(new_interface != interface) { if(new_interface != interface) {
interface = new_interface; interface = new_interface;
ti_lib_i2c_master_disable(I2C0_BASE);
if(interface == BOARD_I2C_INTERFACE_0) { if(interface == BOARD_I2C_INTERFACE_0) {
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_NO_IOPULL); ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_NO_IOPULL);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_NO_IOPULL); ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_NO_IOPULL);
@ -263,6 +329,12 @@ board_i2c_select(uint8_t new_interface, uint8_t address)
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA); ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL); ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL);
} }
/* Enable and initialize the I2C master module */
ti_lib_i2c_master_init_exp_clk(I2C0_BASE,
ti_lib_sys_ctrl_peripheral_clock_get(
PRCM_PERIPH_I2C0, SYSCTRL_SYSBUS_ON),
true);
} }
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/

View file

@ -48,13 +48,15 @@
#define BOARD_I2C_INTERFACE_0 0 #define BOARD_I2C_INTERFACE_0 0
#define BOARD_I2C_INTERFACE_1 1 #define BOARD_I2C_INTERFACE_1 1
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define board_i2c_deselect(...)
/*---------------------------------------------------------------------------*/
/** /**
* \brief Initialise the I2C controller with defaults for the sensortag * \brief Put the I2C controller in a known state
*
* In this state, pins SDA and SCL will be under i2c control and pins SDA HP
* and SCL HP will be configured as gpio inputs. This is equal to selecting
* BOARD_I2C_INTERFACE_0, but without selecting a slave device address
*/ */
void board_i2c_init(void); #define board_i2c_deselect() board_i2c_select(BOARD_I2C_INTERFACE_0, 0)
/*---------------------------------------------------------------------------*/
/** /**
* \brief Select an I2C slave * \brief Select an I2C slave
* \param interface The I2C interface to be used (BOARD_I2C_INTERFACE_0 or _1) * \param interface The I2C interface to be used (BOARD_I2C_INTERFACE_0 or _1)
@ -99,6 +101,27 @@ bool board_i2c_write_single(uint8_t data);
*/ */
bool board_i2c_write_read(uint8_t *wdata, uint8_t wlen, uint8_t *rdata, bool board_i2c_write_read(uint8_t *wdata, uint8_t wlen, uint8_t *rdata,
uint8_t rlen); uint8_t rlen);
/**
* \brief Enables the I2C peripheral with defaults
*
* This function is called to wakeup and initialise the I2C.
*
* This function can be called explicitly, but it will also be called
* automatically by board_i2c_select() when required. One of those two
* functions MUST be called before any other I2C operation after a chip
* sleep / wakeup cycle or after a call to board_i2c_shutdown(). Failing to do
* so will lead to a bus fault.
*/
void board_i2c_wakeup(void);
/**
* \brief Stops the I2C peripheral and restores pins to s/w control
*
* This function is called automatically by the board's LPM logic, but it
* can also be called explicitly.
*/
void board_i2c_shutdown(void);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#endif /* BOARD_I2C_H_ */ #endif /* BOARD_I2C_H_ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/

View file

@ -40,12 +40,35 @@
#include "ti-lib.h" #include "ti-lib.h"
#include "board-spi.h" #include "board-spi.h"
#include "board.h" #include "board.h"
#include <stdbool.h>
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define CPU_FREQ 48000000ul #define CPU_FREQ 48000000ul
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static bool
accessible(void)
{
/* First, check the PD */
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON) {
return false;
}
/* Then check the 'run mode' clock gate */
if(!(HWREG(PRCM_BASE + PRCM_O_SSICLKGR) & PRCM_SSICLKGR_CLK_EN_SSI0)) {
return false;
}
return true;
}
/*---------------------------------------------------------------------------*/
int int
board_spi_write(const uint8_t *buf, size_t len) board_spi_write(const uint8_t *buf, size_t len)
{ {
if(accessible() == false) {
return 0;
}
while(len > 0) { while(len > 0) {
uint32_t ul; uint32_t ul;
@ -61,6 +84,10 @@ board_spi_write(const uint8_t *buf, size_t len)
int int
board_spi_read(uint8_t *buf, size_t len) board_spi_read(uint8_t *buf, size_t len)
{ {
if(accessible() == false) {
return 0;
}
while(len > 0) { while(len > 0) {
uint32_t ul; uint32_t ul;
@ -79,6 +106,10 @@ board_spi_read(uint8_t *buf, size_t len)
void void
board_spi_flush() board_spi_flush()
{ {
if(accessible() == false) {
return;
}
uint32_t ul; uint32_t ul;
while(ti_lib_rom_ssi_data_get_non_blocking(SSI0_BASE, &ul)); while(ti_lib_rom_ssi_data_get_non_blocking(SSI0_BASE, &ul));
} }
@ -88,7 +119,12 @@ board_spi_open(uint32_t bit_rate, uint32_t clk_pin)
{ {
uint32_t buf; uint32_t buf;
/* SPI power */ /* First, make sure the SERIAL PD is on */
ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL);
while((ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON));
/* Enable clock in active mode */
ti_lib_rom_prcm_peripheral_run_enable(PRCM_PERIPH_SSI0); ti_lib_rom_prcm_peripheral_run_enable(PRCM_PERIPH_SSI0);
ti_lib_prcm_load_set(); ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get()); while(!ti_lib_prcm_load_get());
@ -113,6 +149,16 @@ board_spi_close()
ti_lib_rom_prcm_peripheral_run_disable(PRCM_PERIPH_SSI0); ti_lib_rom_prcm_peripheral_run_disable(PRCM_PERIPH_SSI0);
ti_lib_prcm_load_set(); ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get()); while(!ti_lib_prcm_load_get());
/* Restore pins to a low-consumption state */
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SPI_MISO);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SPI_MISO, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SPI_MOSI);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SPI_MOSI, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SPI_CLK_FLASH);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SPI_CLK_FLASH, IOC_IOPULL_DOWN);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** @} */ /** @} */

View file

@ -46,17 +46,16 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
/*---------------------------------------------------------------------------*/ #include <stdbool.h>
#define PRCM_DOMAINS (PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH)
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
power_domains_on(void) power_domains_on(void)
{ {
/* Turn on relevant power domains */ /* Turn on the PERIPH PD */
ti_lib_prcm_power_domain_on(PRCM_DOMAINS); ti_lib_prcm_power_domain_on(PRCM_DOMAIN_PERIPH);
/* Wait for domains to power on */ /* Wait for domains to power on */
while((ti_lib_prcm_power_domain_status(PRCM_DOMAINS) while((ti_lib_prcm_power_domain_status(PRCM_DOMAIN_PERIPH)
!= PRCM_DOMAIN_POWER_ON)); != PRCM_DOMAIN_POWER_ON));
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -64,8 +63,6 @@ static void
lpm_wakeup_handler(void) lpm_wakeup_handler(void)
{ {
power_domains_on(); power_domains_on();
board_i2c_init();
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
@ -77,8 +74,12 @@ shutdown_handler(uint8_t mode)
SENSORS_DEACTIVATE(opt_3001_sensor); SENSORS_DEACTIVATE(opt_3001_sensor);
SENSORS_DEACTIVATE(tmp_007_sensor); SENSORS_DEACTIVATE(tmp_007_sensor);
SENSORS_DEACTIVATE(hdc_1000_sensor); SENSORS_DEACTIVATE(hdc_1000_sensor);
mpu_9250_sensor.configure(MPU_9250_SENSOR_SHUTDOWN, 0); SENSORS_DEACTIVATE(mpu_9250_sensor);
ti_lib_gpio_pin_clear(BOARD_MPU_POWER);
} }
/* In all cases, stop the I2C */
board_i2c_shutdown();
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
@ -88,13 +89,47 @@ shutdown_handler(uint8_t mode)
* wake up so we can turn power domains back on for I2C and SSI, and to make * wake up so we can turn power domains back on for I2C and SSI, and to make
* sure everything on the board is off before CM3 shutdown. * sure everything on the board is off before CM3 shutdown.
*/ */
LPM_MODULE(sensortag_module, NULL, shutdown_handler, lpm_wakeup_handler); LPM_MODULE(sensortag_module, NULL, shutdown_handler, lpm_wakeup_handler,
LPM_DOMAIN_NONE);
/*---------------------------------------------------------------------------*/
static void
configure_unused_pins(void)
{
/* DP[0..3] */
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_DP0);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_DP0, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_DP1);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_DP1, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_DP2);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_DP2, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_DP3);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_DP3, IOC_IOPULL_DOWN);
/* Devpack ID */
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_DEVPK_ID);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_DEVPK_ID, IOC_IOPULL_UP);
/* Digital Microphone */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_MIC_POWER);
ti_lib_gpio_pin_clear((1 << BOARD_IOID_MIC_POWER));
ti_lib_ioc_io_drv_strength_set(BOARD_IOID_MIC_POWER, IOC_CURRENT_2MA,
IOC_STRENGTH_MIN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_AUDIO_DI);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_AUDIO_DI, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_AUDIO_CLK);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_AUDIO_CLK, IOC_IOPULL_DOWN);
/* UART over Devpack - TX only (ToDo: Map all UART pins to Debugger) */
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_DP5_UARTTX);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_DP5_UARTTX, IOC_IOPULL_DOWN);
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
board_init() board_init()
{ {
/* Disable global interrupts */ /* Disable global interrupts */
uint8_t int_disabled = ti_lib_int_master_disable(); bool int_disabled = ti_lib_int_master_disable();
power_domains_on(); power_domains_on();
@ -112,37 +147,19 @@ board_init()
ti_lib_prcm_load_set(); ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get()); while(!ti_lib_prcm_load_get());
/* Enable GPT0 module - Wait for the clock to be enabled */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
/* Keys (input pullup) */
ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_KEY_LEFT);
ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_KEY_RIGHT);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_KEY_LEFT, IOC_IOPULL_UP);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_KEY_RIGHT, IOC_IOPULL_UP);
/* I2C controller */ /* I2C controller */
board_i2c_init(); board_i2c_wakeup();
/* Sensor interface */
ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_MPU_INT);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_MPU_INT, IOC_IOPULL_DOWN);
ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_REED_RELAY);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_REED_RELAY, IOC_IOPULL_DOWN);
ti_lib_rom_ioc_pin_type_gpio_output(BOARD_IOID_MPU_POWER);
/* Flash interface */
ti_lib_rom_ioc_pin_type_gpio_output(BOARD_IOID_FLASH_CS);
ti_lib_gpio_pin_write(BOARD_FLASH_CS, 1);
buzzer_init(); buzzer_init();
/* Make sure the external flash is in the lower power mode */
ext_flash_init();
lpm_register_module(&sensortag_module); lpm_register_module(&sensortag_module);
/* For unsupported peripherals, select a default pin configuration */
configure_unused_pins();
/* Re-enable interrupt if initially enabled. */ /* Re-enable interrupt if initially enabled. */
if(!int_disabled) { if(!int_disabled) {
ti_lib_int_master_enable(); ti_lib_int_master_enable();

View file

@ -100,12 +100,16 @@
* Those values are not meant to be modified by the user * Those values are not meant to be modified by the user
* @{ * @{
*/ */
#define BOARD_IOID_UART_RX IOID_17 #define BOARD_IOID_DP4_UARTRX IOID_28
#define BOARD_IOID_DP5_UARTTX IOID_29
#define BOARD_IOID_UART_RX BOARD_IOID_DP4_UARTRX
#define BOARD_IOID_UART_TX IOID_16 #define BOARD_IOID_UART_TX IOID_16
#define BOARD_IOID_UART_CTS IOID_UNUSED #define BOARD_IOID_UART_CTS IOID_UNUSED
#define BOARD_IOID_UART_RTS IOID_UNUSED #define BOARD_IOID_UART_RTS IOID_UNUSED
#define BOARD_UART_RXD (1 << BOARD_IOID_UART_RXD) #define BOARD_UART_RX (1 << BOARD_IOID_UART_RX)
#define BOARD_UART_TXD (1 << BOARD_IOID_UART_TXD) #define BOARD_UART_TX (1 << BOARD_IOID_UART_TX)
#define BOARD_UART_CTS (1 << BOARD_IOID_UART_CTS) #define BOARD_UART_CTS (1 << BOARD_IOID_UART_CTS)
#define BOARD_UART_RTS (1 << BOARD_IOID_UART_RTS) #define BOARD_UART_RTS (1 << BOARD_IOID_UART_RTS)
/** @} */ /** @} */
@ -156,7 +160,7 @@
*/ */
#define BOARD_IOID_FLASH_CS IOID_14 #define BOARD_IOID_FLASH_CS IOID_14
#define BOARD_FLASH_CS (1 << BOARD_IOID_FLASH_CS) #define BOARD_FLASH_CS (1 << BOARD_IOID_FLASH_CS)
#define BOARD_SPI_CLK_FLASH IOID_11 #define BOARD_IOID_SPI_CLK_FLASH IOID_17
/** @} */ /** @} */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** /**
@ -183,6 +187,44 @@
#define BOARD_MPU_POWER (1 << BOARD_IOID_MPU_POWER) #define BOARD_MPU_POWER (1 << BOARD_IOID_MPU_POWER)
/** @} */ /** @} */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/**
* \brief Board devpack IOID mappings (LCD etc.)
*
* Those values are not meant to be modified by the user
* @{
*/
#define BOARD_IOID_AUDIOFS_TDO IOID_16
#define BOARD_IOID_DEVPACK_CS IOID_20
#define BOARD_IOID_DEVPK_LCD_EXTCOMIN IOID_22
#define BOARD_IOID_AUDIODO IOID_22
#define BOARD_IOID_DP2 IOID_23
#define BOARD_IOID_DP1 IOID_24
#define BOARD_IOID_DP0 IOID_25
#define BOARD_IOID_DP3 IOID_27
#define BOARD_IOID_DEVPK_ID IOID_30
#define BOARD_DEVPACK_CS (1 << BOARD_IOID_DEVPACK_CS)
/** @} */
/*---------------------------------------------------------------------------*/
/**
* \brief TMP Sensor
*
* Those values are not meant to be modified by the user
* @{
*/
#define BOARD_IOID_TMP_RDY IOID_1
/** @} */
/*---------------------------------------------------------------------------*/
/**
* \brief Digital Microphone
*
* Those values are not meant to be modified by the user
* @{
*/
#define BOARD_IOID_MIC_POWER IOID_13
#define BOARD_IOID_AUDIO_DI IOID_2
#define BOARD_IOID_AUDIO_CLK IOID_11
/** @} */
/*---------------------------------------------------------------------------*/
/** /**
* \name Device string used on startup * \name Device string used on startup
* @{ * @{

View file

@ -55,7 +55,7 @@
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define BUTTON_GPIO_CFG (IOC_CURRENT_2MA | IOC_STRENGTH_AUTO | \ #define BUTTON_GPIO_CFG (IOC_CURRENT_2MA | IOC_STRENGTH_AUTO | \
IOC_IOPULL_UP | IOC_SLEW_DISABLE | \ IOC_IOPULL_UP | IOC_SLEW_DISABLE | \
IOC_HYST_DISABLE | IOC_BOTH_EDGES | \ IOC_HYST_ENABLE | IOC_BOTH_EDGES | \
IOC_INT_ENABLE | IOC_IOMODE_NORMAL | \ IOC_INT_ENABLE | IOC_IOMODE_NORMAL | \
IOC_NO_WAKE_UP | IOC_INPUT_ENABLE | \ IOC_NO_WAKE_UP | IOC_INPUT_ENABLE | \
IOC_JTAG_DISABLE) IOC_JTAG_DISABLE)
@ -116,7 +116,7 @@ button_press_handler(uint8_t ioid)
sensors_changed(&button_right_sensor); sensors_changed(&button_right_sensor);
} }
} else { } else {
lpm_shutdown(BOARD_IOID_KEY_RIGHT); lpm_shutdown(BOARD_IOID_KEY_RIGHT, IOC_IOPULL_UP, IOC_WAKE_ON_LOW);
} }
} }
} }

View file

@ -46,19 +46,12 @@
#include <stdio.h> #include <stdio.h>
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static uint8_t buzzer_on; static uint8_t buzzer_on;
static lpm_power_domain_lock_t lock; LPM_MODULE(buzzer_module, NULL, NULL, NULL, LPM_DOMAIN_PERIPH);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
buzzer_init() buzzer_init()
{ {
buzzer_on = 0; buzzer_on = 0;
/* Drive the I/O ID with GPT0 / Timer A */
ti_lib_ioc_port_configure_set(BOARD_IOID_BUZZER, IOC_PORT_MCU_PORT_EVENT0,
IOC_STD_OUTPUT);
/* GPT0 / Timer A: PWM, Interrupt Enable */
HWREG(GPT0_BASE + GPT_O_TAMR) = (TIMER_CFG_A_PWM & 0xFF) | GPT_TAMR_TAPWMIE;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uint8_t uint8_t
@ -72,9 +65,28 @@ buzzer_start(int freq)
{ {
uint32_t load; uint32_t load;
/* Enable GPT0 clocks under active, sleep, deep sleep */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
/* Drive the I/O ID with GPT0 / Timer A */
ti_lib_ioc_port_configure_set(BOARD_IOID_BUZZER, IOC_PORT_MCU_PORT_EVENT0,
IOC_STD_OUTPUT);
/* GPT0 / Timer A: PWM, Interrupt Enable */
HWREG(GPT0_BASE + GPT_O_TAMR) = (TIMER_CFG_A_PWM & 0xFF) | GPT_TAMR_TAPWMIE;
buzzer_on = 1; buzzer_on = 1;
lpm_pd_lock_obtain(&lock, PRCM_DOMAIN_PERIPH); /*
* Register ourself with LPM. This will keep the PERIPH PD powered on
* during deep sleep, allowing the buzzer to keep working while the chip is
* being power-cycled
*/
lpm_register_module(&buzzer_module);
/* Stop the timer */ /* Stop the timer */
ti_lib_timer_disable(GPT0_BASE, TIMER_A); ti_lib_timer_disable(GPT0_BASE, TIMER_A);
@ -88,12 +100,6 @@ buzzer_start(int freq)
/* Start */ /* Start */
ti_lib_timer_enable(GPT0_BASE, TIMER_A); ti_lib_timer_enable(GPT0_BASE, TIMER_A);
} }
/* Run in sleep mode */
ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
@ -101,24 +107,37 @@ buzzer_stop()
{ {
buzzer_on = 0; buzzer_on = 0;
lpm_pd_lock_release(&lock); /*
* Unregister the buzzer module from LPM. This will effectively release our
* lock for the PERIPH PD allowing it to be powered down (unless some other
* module keeps it on)
*/
lpm_unregister_module(&buzzer_module);
/* Stop the timer */ /* Stop the timer */
ti_lib_timer_disable(GPT0_BASE, TIMER_A); ti_lib_timer_disable(GPT0_BASE, TIMER_A);
/* /*
* Stop running in sleep mode. * Stop the module clock:
* ToDo: Currently GPT0 is in use by clock_delay_usec (GPT0/TB) and by this *
* module here (GPT0/TA). clock_delay_usec will never need GPT0/TB in sleep * Currently GPT0 is in use by clock_delay_usec (GPT0/TB) and by this
* mode and we control TA here. Thus, with the current setup, it's OK to * module here (GPT0/TA).
* control whether GPT0 runs in sleep mode in this module here. However, if *
* some other module at some point starts using GPT0, we should change this * clock_delay_usec
* to happen through an umbrella module * - is definitely not running when we enter here and
* - handles the module clock internally
*
* Thus, we can safely change the state of module clocks here.
*/ */
ti_lib_prcm_peripheral_run_disable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_TIMER0); ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_TIMER0); ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_load_set(); ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get()); while(!ti_lib_prcm_load_get());
/* Un-configure the pin */
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_BUZZER);
ti_lib_ioc_io_input_set(BOARD_IOID_BUZZER, IOC_INPUT_DISABLE);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** @} */ /** @} */

View file

@ -72,7 +72,7 @@
/* Part specific constants */ /* Part specific constants */
#define BLS_MANUFACTURER_ID 0xEF #define BLS_MANUFACTURER_ID 0xEF
#define BLS_DEVICE_ID 0x11 #define BLS_DEVICE_ID 0x12
#define BLS_PROGRAM_PAGE_SIZE 256 #define BLS_PROGRAM_PAGE_SIZE 256
#define BLS_ERASE_SECTOR_SIZE 4096 #define BLS_ERASE_SECTOR_SIZE 4096
@ -140,50 +140,8 @@ wait_ready(void)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** /**
* \brief Put the device in power save mode. No access to data; only * \brief Verify the flash part.
* the status register is accessible. * \return True when successful.
* \return True when SPI transactions succeed
*/
static bool
power_down(void)
{
uint8_t cmd;
bool success;
cmd = BLS_CODE_DP;
select();
success = board_spi_write(&cmd, sizeof(cmd));
deselect();
return success;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Take device out of power save mode and prepare it for normal operation
* \return True if the command was written successfully
*/
static bool
power_standby(void)
{
uint8_t cmd;
bool success;
cmd = BLS_CODE_RDP;
select();
success = board_spi_write(&cmd, sizeof(cmd));
if(success) {
success = wait_ready() == 0;
}
deselect();
return success;
}
/*---------------------------------------------------------------------------*/
/**
* Verify the flash part.
* @return True when successful.
*/ */
static bool static bool
verify_part(void) verify_part(void)
@ -210,6 +168,57 @@ verify_part(void)
return true; return true;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/**
* \brief Put the device in power save mode. No access to data; only
* the status register is accessible.
*/
static void
power_down(void)
{
uint8_t cmd;
uint8_t i;
cmd = BLS_CODE_DP;
select();
board_spi_write(&cmd, sizeof(cmd));
deselect();
i = 0;
while(i < 10) {
if(!verify_part()) {
/* Verify Part failed: Device is powered down */
return;
}
i++;
}
/* Should not be required */
deselect();
}
/*---------------------------------------------------------------------------*/
/**
* \brief Take device out of power save mode and prepare it for normal operation
* \return True if the command was written successfully
*/
static bool
power_standby(void)
{
uint8_t cmd;
bool success;
cmd = BLS_CODE_RDP;
select();
success = board_spi_write(&cmd, sizeof(cmd));
if(success) {
success = wait_ready() == 0;
}
deselect();
return success;
}
/*---------------------------------------------------------------------------*/
/** /**
* \brief Enable write. * \brief Enable write.
* \return Zero when successful. * \return Zero when successful.
@ -232,7 +241,7 @@ write_enable(void)
bool bool
ext_flash_open() ext_flash_open()
{ {
board_spi_open(4000000, BOARD_SPI_CLK_FLASH); board_spi_open(4000000, BOARD_IOID_SPI_CLK_FLASH);
/* GPIO pin configuration */ /* GPIO pin configuration */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_FLASH_CS); ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_FLASH_CS);
@ -406,4 +415,11 @@ ext_flash_test(void)
return ret; return ret;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void
ext_flash_init()
{
ext_flash_open();
ext_flash_close();
}
/*---------------------------------------------------------------------------*/
/** @} */ /** @} */

View file

@ -54,6 +54,8 @@ bool ext_flash_open(void);
/** /**
* \brief Close the storage driver * \brief Close the storage driver
*
* This call will put the device in its lower power mode (power down).
*/ */
void ext_flash_close(void); void ext_flash_close(void);
@ -94,6 +96,17 @@ bool ext_flash_write(size_t offset, size_t length, const uint8_t *buf);
* \return True when successful. * \return True when successful.
*/ */
bool ext_flash_test(void); bool ext_flash_test(void);
/**
* \brief Initialise the external flash
*
* This function will explicitly put the part in its lowest power mode
* (power-down).
*
* In order to perform any operation, the caller must first wake the device
* up by calling ext_flash_open()
*/
void ext_flash_init(void);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#endif /* EXT_FLASH_H_ */ #endif /* EXT_FLASH_H_ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/

View file

@ -212,12 +212,12 @@ static uint8_t acc_range_reg;
static uint8_t val; static uint8_t val;
static uint8_t interrupt_status; static uint8_t interrupt_status;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define SENSOR_STATUS_DISABLED 0 #define SENSOR_STATE_DISABLED 0
#define SENSOR_STATUS_BOOTING 1 #define SENSOR_STATE_BOOTING 1
#define SENSOR_STATUS_ENABLED 2 #define SENSOR_STATE_ENABLED 2
static int enabled = SENSOR_STATUS_DISABLED; static int state = SENSOR_STATE_DISABLED;
static int elements; static int elements = MPU_9250_SENSOR_TYPE_NONE;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* 3 16-byte words for all sensor readings */ /* 3 16-byte words for all sensor readings */
#define SENSOR_DATA_BUF_SIZE 3 #define SENSOR_DATA_BUF_SIZE 3
@ -289,7 +289,9 @@ static void
select_axes(void) select_axes(void)
{ {
val = ~mpu_config; val = ~mpu_config;
SENSOR_SELECT();
sensor_common_write_reg(PWR_MGMT_2, &val, 1); sensor_common_write_reg(PWR_MGMT_2, &val, 1);
SENSOR_DESELECT();
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
@ -334,37 +336,6 @@ acc_set_range(uint8_t new_range)
return success; return success;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/**
* \brief Initialise the MPU
* \return True if success
*/
static bool
init_sensor(void)
{
bool ret;
interrupt_status = false;
acc_range = ACC_RANGE_INVALID;
mpu_config = 0; /* All axes off */
/* Device reset */
val = 0x80;
SENSOR_SELECT();
ret = sensor_common_write_reg(PWR_MGMT_1, &val, 1);
SENSOR_DESELECT();
if(ret) {
delay_ms(200);
/* Initial configuration */
acc_set_range(ACC_RANGE_8G);
/* Power save */
sensor_sleep();
}
return ret;
}
/*---------------------------------------------------------------------------*/
/** /**
* \brief Check whether a data or wake on motion interrupt has occurred * \brief Check whether a data or wake on motion interrupt has occurred
* \return Return the interrupt status * \return Return the interrupt status
@ -450,13 +421,13 @@ gyro_read(uint16_t *data)
/* Burst read of all gyroscope values */ /* Burst read of all gyroscope values */
success = sensor_common_read_reg(GYRO_XOUT_H, (uint8_t *)data, DATA_SIZE); success = sensor_common_read_reg(GYRO_XOUT_H, (uint8_t *)data, DATA_SIZE);
SENSOR_DESELECT();
if(success) { if(success) {
convert_to_le((uint8_t *)data, DATA_SIZE); convert_to_le((uint8_t *)data, DATA_SIZE);
} else { } else {
sensor_common_set_error_data((uint8_t *)data, DATA_SIZE); sensor_common_set_error_data((uint8_t *)data, DATA_SIZE);
} }
SENSOR_DESELECT();
} else { } else {
success = false; success = false;
} }
@ -514,15 +485,13 @@ gyro_convert(int16_t raw_data)
static void static void
notify_ready(void *not_used) notify_ready(void *not_used)
{ {
enabled = SENSOR_STATUS_ENABLED; state = SENSOR_STATE_ENABLED;
sensors_changed(&mpu_9250_sensor); sensors_changed(&mpu_9250_sensor);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
initialise(void *not_used) initialise(void *not_used)
{ {
init_sensor();
/* Configure the accelerometer range */ /* Configure the accelerometer range */
if((elements & MPU_9250_SENSOR_TYPE_ACC) != 0) { if((elements & MPU_9250_SENSOR_TYPE_ACC) != 0) {
acc_set_range(MPU_9250_SENSOR_ACC_RANGE); acc_set_range(MPU_9250_SENSOR_ACC_RANGE);
@ -537,7 +506,7 @@ static void
power_up(void) power_up(void)
{ {
ti_lib_gpio_pin_write(BOARD_MPU_POWER, 1); ti_lib_gpio_pin_write(BOARD_MPU_POWER, 1);
enabled = SENSOR_STATUS_BOOTING; state = SENSOR_STATE_BOOTING;
ctimer_set(&startup_timer, SENSOR_BOOT_DELAY, initialise, NULL); ctimer_set(&startup_timer, SENSOR_BOOT_DELAY, initialise, NULL);
} }
@ -553,7 +522,7 @@ value(int type)
int rv; int rv;
float converted_val = 0; float converted_val = 0;
if(enabled == SENSOR_STATUS_DISABLED) { if(state == SENSOR_STATE_DISABLED) {
PRINTF("MPU: Sensor Disabled\n"); PRINTF("MPU: Sensor Disabled\n");
return CC26XX_SENSOR_READING_ERROR; return CC26XX_SENSOR_READING_ERROR;
} }
@ -632,33 +601,42 @@ configure(int type, int enable)
{ {
switch(type) { switch(type) {
case SENSORS_HW_INIT: case SENSORS_HW_INIT:
ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_MPU_INT);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_MPU_INT, IOC_IOPULL_DOWN);
ti_lib_ioc_io_hyst_set(BOARD_IOID_MPU_INT, IOC_HYST_ENABLE);
ti_lib_rom_ioc_pin_type_gpio_output(BOARD_IOID_MPU_POWER);
ti_lib_ioc_io_drv_strength_set(BOARD_IOID_MPU_POWER, IOC_CURRENT_4MA,
IOC_STRENGTH_MAX);
ti_lib_gpio_pin_clear(BOARD_MPU_POWER);
elements = MPU_9250_SENSOR_TYPE_NONE; elements = MPU_9250_SENSOR_TYPE_NONE;
break; break;
case SENSORS_ACTIVE: case SENSORS_ACTIVE:
if((enable & MPU_9250_SENSOR_TYPE_ACC) != 0 || if(((enable & MPU_9250_SENSOR_TYPE_ACC) != 0) ||
(enable & MPU_9250_SENSOR_TYPE_GYRO) != 0) { ((enable & MPU_9250_SENSOR_TYPE_GYRO) != 0)) {
PRINTF("MPU: Enabling\n"); PRINTF("MPU: Enabling\n");
elements = enable & MPU_9250_SENSOR_TYPE_ALL; elements = enable & MPU_9250_SENSOR_TYPE_ALL;
power_up(); power_up();
enabled = SENSOR_STATUS_BOOTING; state = SENSOR_STATE_BOOTING;
} else { } else {
PRINTF("MPU: Disabling\n"); PRINTF("MPU: Disabling\n");
ctimer_stop(&startup_timer); if(HWREG(GPIO_BASE + GPIO_O_DOUT31_0) & BOARD_MPU_POWER) {
elements = MPU_9250_SENSOR_TYPE_NONE; /* Then check our state */
enable_sensor(0); elements = MPU_9250_SENSOR_TYPE_NONE;
while(ti_lib_i2c_master_busy(I2C0_BASE)); ctimer_stop(&startup_timer);
enabled = SENSOR_STATUS_DISABLED; sensor_sleep();
while(ti_lib_i2c_master_busy(I2C0_BASE));
state = SENSOR_STATE_DISABLED;
ti_lib_gpio_pin_clear(BOARD_MPU_POWER);
}
} }
break; break;
case MPU_9250_SENSOR_SHUTDOWN:
ti_lib_gpio_pin_write(BOARD_MPU_POWER, 0);
break;
default: default:
break; break;
} }
return enabled; return state;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** /**
@ -672,12 +650,12 @@ status(int type)
switch(type) { switch(type) {
case SENSORS_ACTIVE: case SENSORS_ACTIVE:
case SENSORS_READY: case SENSORS_READY:
return enabled; return state;
break; break;
default: default:
break; break;
} }
return SENSOR_STATUS_DISABLED; return SENSOR_STATE_DISABLED;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
SENSORS_SENSOR(mpu_9250_sensor, "MPU9250", value, configure, status); SENSORS_SENSOR(mpu_9250_sensor, "MPU9250", value, configure, status);

View file

@ -86,8 +86,6 @@
#define MPU_9250_SENSOR_TYPE_NONE 0 #define MPU_9250_SENSOR_TYPE_NONE 0
#define MPU_9250_SENSOR_TYPE_ALL (MPU_9250_SENSOR_TYPE_ACC | \ #define MPU_9250_SENSOR_TYPE_ALL (MPU_9250_SENSOR_TYPE_ACC | \
MPU_9250_SENSOR_TYPE_GYRO) MPU_9250_SENSOR_TYPE_GYRO)
#define MPU_9250_SENSOR_SHUTDOWN 0xFF
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Accelerometer range */ /* Accelerometer range */
#define MPU_9250_SENSOR_ACC_RANGE_2G 0 #define MPU_9250_SENSOR_ACC_RANGE_2G 0

View file

@ -69,16 +69,42 @@
#define REG_MANUFACTURER_ID 0x7E #define REG_MANUFACTURER_ID 0x7E
#define REG_DEVICE_ID 0x7F #define REG_DEVICE_ID 0x7F
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Register values */ /*
#define MANUFACTURER_ID 0x5449 /* TI */ * Configuration Register Bits and Masks.
#define DEVICE_ID 0x3001 /* Opt 3001 */ * We use uint16_t to read from / write to registers, meaning that the
#define CONFIG_RESET 0xC810 * register's MSB is the variable's LSB.
#define CONFIG_TEST 0xCC10 */
#define CONFIG_ENABLE 0x10CC /* 0xCC10 */ #define CONFIG_RN 0x00F0 /* [15..12] Range Number */
#define CONFIG_DISABLE 0x108C /* 0xC810 */ #define CONFIG_CT 0x0008 /* [11] Conversion Time */
/*---------------------------------------------------------------------------*/ #define CONFIG_M 0x0006 /* [10..9] Mode of Conversion */
/* Bit values */ #define CONFIG_OVF 0x0001 /* [8] Overflow */
#define DATA_RDY_BIT 0x0080 /* Data ready */ #define CONFIG_CRF 0x8000 /* [7] Conversion Ready Field */
#define CONFIG_FH 0x4000 /* [6] Flag High */
#define CONFIG_FL 0x2000 /* [5] Flag Low */
#define CONFIG_L 0x1000 /* [4] Latch */
#define CONFIG_POL 0x0800 /* [3] Polarity */
#define CONFIG_ME 0x0400 /* [2] Mask Exponent */
#define CONFIG_FC 0x0300 /* [1..0] Fault Count */
/* Possible Values for CT */
#define CONFIG_CT_100 0x0000
#define CONFIG_CT_800 CONFIG_CT
/* Possible Values for M */
#define CONFIG_M_CONTI 0x0004
#define CONFIG_M_SINGLE 0x0002
#define CONFIG_M_SHUTDOWN 0x0000
/* Reset Value for the register 0xC810. All zeros except: */
#define CONFIG_RN_RESET 0x00C0
#define CONFIG_CT_RESET CONFIG_CT_800
#define CONFIG_L_RESET 0x1000
#define CONFIG_DEFAULTS (CONFIG_RN_RESET | CONFIG_CT_100 | CONFIG_L_RESET)
/* Enable / Disable */
#define CONFIG_ENABLE_CONTINUOUS (CONFIG_M_CONTI | CONFIG_DEFAULTS)
#define CONFIG_ENABLE_SINGLE_SHOT (CONFIG_M_SINGLE | CONFIG_DEFAULTS)
#define CONFIG_DISABLE CONFIG_DEFAULTS
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Register length */ /* Register length */
#define REGISTER_LENGTH 2 #define REGISTER_LENGTH 2
@ -86,24 +112,22 @@
/* Sensor data size */ /* Sensor data size */
#define DATA_LENGTH 2 #define DATA_LENGTH 2
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define SENSOR_STATUS_DISABLED 0 /*
#define SENSOR_STATUS_NOT_READY 1 * SENSOR_STATE_SLEEPING and SENSOR_STATE_ACTIVE are mutually exclusive.
#define SENSOR_STATUS_ENABLED 2 * SENSOR_STATE_DATA_READY can be ORd with both of the above. For example the
* sensor may be sleeping but with a conversion ready to read out.
*/
#define SENSOR_STATE_SLEEPING 0
#define SENSOR_STATE_ACTIVE 1
#define SENSOR_STATE_DATA_READY 2
static int enabled = SENSOR_STATUS_DISABLED; static int state = SENSOR_STATE_SLEEPING;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Wait SENSOR_STARTUP_DELAY for the sensor to be ready - 125ms */ /* Wait SENSOR_STARTUP_DELAY for the sensor to be ready - 125ms */
#define SENSOR_STARTUP_DELAY (CLOCK_SECOND >> 3) #define SENSOR_STARTUP_DELAY (CLOCK_SECOND >> 3)
static struct ctimer startup_timer; static struct ctimer startup_timer;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void
notify_ready(void *not_used)
{
enabled = SENSOR_STATUS_ENABLED;
sensors_changed(&opt_3001_sensor);
}
/*---------------------------------------------------------------------------*/
/** /**
* \brief Select the sensor on the I2C bus * \brief Select the sensor on the I2C bus
*/ */
@ -114,6 +138,28 @@ select(void)
board_i2c_select(BOARD_I2C_INTERFACE_0, OPT3001_I2C_ADDRESS); board_i2c_select(BOARD_I2C_INTERFACE_0, OPT3001_I2C_ADDRESS);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void
notify_ready(void *not_used)
{
/*
* Depending on the CONFIGURATION.CONVERSION_TIME bits, a conversion will
* take either 100 or 800 ms. Here we inspect the CONVERSION_READY bit and
* if the reading is ready we notify, otherwise we just reschedule ourselves
*/
uint16_t val;
select();
sensor_common_read_reg(REG_CONFIGURATION, (uint8_t *)&val, REGISTER_LENGTH);
if(val & CONFIG_CRF) {
sensors_changed(&opt_3001_sensor);
state = SENSOR_STATE_DATA_READY;
} else {
ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL);
}
}
/*---------------------------------------------------------------------------*/
/** /**
* \brief Turn the sensor on/off * \brief Turn the sensor on/off
* \param enable TRUE: on, FALSE: off * \param enable TRUE: on, FALSE: off
@ -122,13 +168,20 @@ static void
enable_sensor(bool enable) enable_sensor(bool enable)
{ {
uint16_t val; uint16_t val;
uint16_t had_data_ready = state & SENSOR_STATE_DATA_READY;
select(); select();
if(enable) { if(enable) {
val = CONFIG_ENABLE; val = CONFIG_ENABLE_SINGLE_SHOT;
/* Writing CONFIG_ENABLE_SINGLE_SHOT to M bits will clear CRF bits */
state = SENSOR_STATE_ACTIVE;
} else { } else {
val = CONFIG_DISABLE; val = CONFIG_DISABLE;
/* Writing CONFIG_DISABLE to M bits will not clear CRF bits */
state = SENSOR_STATE_SLEEPING | had_data_ready;
} }
sensor_common_write_reg(REG_CONFIGURATION, (uint8_t *)&val, REGISTER_LENGTH); sensor_common_write_reg(REG_CONFIGURATION, (uint8_t *)&val, REGISTER_LENGTH);
@ -145,15 +198,15 @@ read_data(uint16_t *raw_data)
bool success; bool success;
uint16_t val; uint16_t val;
if((state & SENSOR_STATE_DATA_READY) != SENSOR_STATE_DATA_READY) {
return false;
}
select(); select();
success = sensor_common_read_reg(REG_CONFIGURATION, (uint8_t *)&val, success = sensor_common_read_reg(REG_CONFIGURATION, (uint8_t *)&val,
REGISTER_LENGTH); REGISTER_LENGTH);
if(success) {
success = (val & DATA_RDY_BIT) == DATA_RDY_BIT;
}
if(success) { if(success) {
success = sensor_common_read_reg(REG_RESULT, (uint8_t *)&val, DATA_LENGTH); success = sensor_common_read_reg(REG_RESULT, (uint8_t *)&val, DATA_LENGTH);
} }
@ -196,14 +249,9 @@ value(int type)
uint16_t raw_val; uint16_t raw_val;
float converted_val; float converted_val;
if(enabled != SENSOR_STATUS_ENABLED) {
PRINTF("Sensor disabled or starting up (%d)\n", enabled);
return CC26XX_SENSOR_READING_ERROR;
}
rv = read_data(&raw_val); rv = read_data(&raw_val);
if(rv == 0) { if(rv == false) {
return CC26XX_SENSOR_READING_ERROR; return CC26XX_SENSOR_READING_ERROR;
} }
@ -229,30 +277,38 @@ value(int type)
static int static int
configure(int type, int enable) configure(int type, int enable)
{ {
int rv = 0;
switch(type) { switch(type) {
case SENSORS_HW_INIT: case SENSORS_HW_INIT:
/*
* Device reset won't reset the sensor, so we put it to sleep here
* explicitly
*/
enable_sensor(0);
rv = 0;
break; break;
case SENSORS_ACTIVE: case SENSORS_ACTIVE:
if(enable) { if(enable) {
enable_sensor(1); enable_sensor(1);
ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL); ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL);
enabled = SENSOR_STATUS_NOT_READY; rv = 1;
} else { } else {
ctimer_stop(&startup_timer); ctimer_stop(&startup_timer);
enable_sensor(0); enable_sensor(0);
enabled = SENSOR_STATUS_DISABLED; rv = 0;
} }
break; break;
default: default:
break; break;
} }
return enabled; return rv;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** /**
* \brief Returns the status of the sensor * \brief Returns the status of the sensor
* \param type SENSORS_ACTIVE or SENSORS_READY * \param type ignored
* \return 1 if the sensor is enabled * \return The state of the sensor SENSOR_STATE_xyz
*/ */
static int static int
status(int type) status(int type)
@ -260,12 +316,10 @@ status(int type)
switch(type) { switch(type) {
case SENSORS_ACTIVE: case SENSORS_ACTIVE:
case SENSORS_READY: case SENSORS_READY:
return enabled;
break;
default: default:
break; break;
} }
return SENSOR_STATUS_DISABLED; return state;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
SENSORS_SENSOR(opt_3001_sensor, "OPT3001", value, configure, status); SENSORS_SENSOR(opt_3001_sensor, "OPT3001", value, configure, status);

View file

@ -40,14 +40,16 @@
* sequence, but the call will not wait for it to complete so that the CPU can * sequence, but the call will not wait for it to complete so that the CPU can
* perform other tasks or drop to a low power mode. * perform other tasks or drop to a low power mode.
* *
* Once the sensor is stable, the driver will generate a sensors_changed event. * Once the reading and conversion are complete, the driver will generate a
* sensors_changed event.
* *
* Once a reading has been taken, the caller has two options: * We use single-shot readings. In this mode, the hardware automatically goes
* - Turn the sensor off by calling SENSORS_DEACTIVATE, but in order to take * back to its shutdown mode after the conversion is finished. However, it will
* subsequent readings SENSORS_ACTIVATE must be called again * still respond to I2C operations, so the last conversion can still be read
* - Leave the sensor on. In this scenario, the caller can simply keep calling * out.
* value() for subsequent readings, but having the sensor on will consume *
* energy * In order to take a new reading, the caller has to use SENSORS_ACTIVATE
* again.
* @{ * @{
* *
* \file * \file

View file

@ -88,7 +88,7 @@
#define SWAP(v) ((LO_UINT16(v) << 8) | HI_UINT16(v)) #define SWAP(v) ((LO_UINT16(v) << 8) | HI_UINT16(v))
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define SELECT() board_i2c_select(BOARD_I2C_INTERFACE_0, SENSOR_I2C_ADDRESS); #define SELECT() board_i2c_select(BOARD_I2C_INTERFACE_0, SENSOR_I2C_ADDRESS)
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static uint8_t buf[DATA_SIZE]; static uint8_t buf[DATA_SIZE];
static uint16_t val; static uint16_t val;
@ -124,7 +124,7 @@ enable_sensor(bool enable)
{ {
bool success; bool success;
SELECT() SELECT();
if(enable) { if(enable) {
val = TMP007_VAL_CONFIG_ON; val = TMP007_VAL_CONFIG_ON;
@ -267,6 +267,10 @@ configure(int type, int enable)
{ {
switch(type) { switch(type) {
case SENSORS_HW_INIT: case SENSORS_HW_INIT:
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_TMP_RDY);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_TMP_RDY, IOC_IOPULL_UP);
ti_lib_ioc_io_hyst_set(BOARD_IOID_TMP_RDY, IOC_HYST_ENABLE);
enable_sensor(false); enable_sensor(false);
enabled = SENSOR_STATUS_INITIALISED; enabled = SENSOR_STATUS_INITIALISED;
break; break;

View file

@ -45,95 +45,65 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define PRCM_DOMAINS (PRCM_DOMAIN_RFCORE | PRCM_DOMAIN_SERIAL | \
PRCM_DOMAIN_PERIPH | PRCM_DOMAIN_CPU | \
PRCM_DOMAIN_SYSBUS | PRCM_DOMAIN_VIMS)
/*---------------------------------------------------------------------------*/
#define LPM_DOMAINS (PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH)
/*---------------------------------------------------------------------------*/
static void static void
power_domains_on(void) wakeup_handler(void)
{ {
/* Turn on relevant power domains */ /* Turn on the PERIPH PD */
ti_lib_prcm_power_domain_on(LPM_DOMAINS); ti_lib_prcm_power_domain_on(PRCM_DOMAIN_PERIPH);
while((ti_lib_prcm_power_domain_status(PRCM_DOMAIN_PERIPH)
/* Wait for domains to power on */
while((ti_lib_prcm_power_domain_status(LPM_DOMAINS)
!= PRCM_DOMAIN_POWER_ON)); != PRCM_DOMAIN_POWER_ON));
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void
lpm_wakeup_handler(void)
{
power_domains_on();
}
/*---------------------------------------------------------------------------*/
/* /*
* Declare a data structure to register with LPM. * Declare a data structure to register with LPM.
* We don't care about what power mode we'll drop to, we don't care about * We don't care about what power mode we'll drop to, we don't care about
* getting notified before deep sleep. All we need is to be notified when we * getting notified before deep sleep. All we need is to be notified when we
* wake up so we can turn power domains back on * wake up so we can turn power domains back on
*/ */
LPM_MODULE(srf_module, NULL, NULL, lpm_wakeup_handler); LPM_MODULE(srf_module, NULL, NULL, wakeup_handler, LPM_DOMAIN_NONE);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void static void
board_init() configure_unused_pins(void)
{ {
uint8_t int_disabled = ti_lib_int_master_disable(); /* Turn off 3.3-V domain (lcd/sdcard power, output low) */
/* Turn on all power domains */
ti_lib_prcm_power_domain_on(PRCM_DOMAINS);
/* Wait for power on domains */
while((ti_lib_prcm_power_domain_status(PRCM_DOMAINS)
!= PRCM_DOMAIN_POWER_ON));
/* Configure all clock domains to run at full speed */
ti_lib_prcm_clock_configure_set(PRCM_DOMAIN_SYSBUS | PRCM_DOMAIN_CPU |
PRCM_DOMAIN_CPU | PRCM_DOMAIN_TIMER |
PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH,
PRCM_CLOCK_DIV_1);
/* Enable GPIO peripheral */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_GPIO);
/* Apply settings and wait for them to take effect */
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get()) ;
/* Keys (input pullup) */
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_UP);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_LEFT);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_RIGHT);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_SELECT);
/* Turn off 3.3V domain (Powers the LCD and SD card reader): Output, low */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_3V3_EN); ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_3V3_EN);
ti_lib_gpio_pin_write(BOARD_3V3_EN, 0); ti_lib_gpio_pin_write(BOARD_3V3_EN, 0);
/* LCD CSn (output high) */ /* Accelerometer (PWR output low, CSn output, high) */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_LCD_CS);
ti_lib_gpio_pin_write(BOARD_LCD_CS, 1);
/* SD Card reader CSn (output high) */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_SDCARD_CS);
ti_lib_gpio_pin_write(BOARD_SDCARD_CS, 1);
/* Accelerometer (PWR output low, CSn output high) */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ACC_PWR); ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ACC_PWR);
ti_lib_gpio_pin_write(BOARD_ACC_PWR, 0); ti_lib_gpio_pin_write(BOARD_ACC_PWR, 0);
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ACC_CS);
ti_lib_gpio_pin_write(BOARD_IOID_ACC_CS, 1);
/* Ambient light sensor (off, output low) */ /* Ambient light sensor (off, output low) */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ALS_PWR); ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ALS_PWR);
ti_lib_gpio_pin_write(BOARD_ALS_PWR, 0); ti_lib_gpio_pin_write(BOARD_ALS_PWR, 0);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_ALS_OUT); ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_ALS_OUT);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_ALS_OUT, IOC_NO_IOPULL); ti_lib_ioc_io_port_pull_set(BOARD_IOID_ALS_OUT, IOC_NO_IOPULL);
}
/*---------------------------------------------------------------------------*/
void
board_init()
{
uint8_t int_disabled = ti_lib_int_master_disable();
/* Turn on relevant PDs */
wakeup_handler();
/* Configure all clock domains to run at full speed */
ti_lib_prcm_clock_configure_set(PRCM_DOMAIN_SYSBUS | PRCM_DOMAIN_CPU |
PRCM_DOMAIN_TIMER | PRCM_DOMAIN_SERIAL |
PRCM_DOMAIN_PERIPH, PRCM_CLOCK_DIV_1);
/* Enable GPIO peripheral */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_GPIO);
/* Apply settings and wait for them to take effect */
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
lpm_register_module(&srf_module); lpm_register_module(&srf_module);
configure_unused_pins();
/* Re-enable interrupt if initially enabled. */ /* Re-enable interrupt if initially enabled. */
if(!int_disabled) { if(!int_disabled) {
ti_lib_int_master_enable(); ti_lib_int_master_enable();

View file

@ -107,8 +107,8 @@
*/ */
#define BOARD_IOID_UART_RX IOID_2 #define BOARD_IOID_UART_RX IOID_2
#define BOARD_IOID_UART_TX IOID_3 #define BOARD_IOID_UART_TX IOID_3
#define BOARD_IOID_UART_CTS IOID_0 #define BOARD_IOID_UART_CTS IOID_UNUSED
#define BOARD_IOID_UART_RTS IOID_21 #define BOARD_IOID_UART_RTS IOID_UNUSED
#define BOARD_UART_RX (1 << BOARD_IOID_UART_RX) #define BOARD_UART_RX (1 << BOARD_IOID_UART_RX)
#define BOARD_UART_TX (1 << BOARD_IOID_UART_TX) #define BOARD_UART_TX (1 << BOARD_IOID_UART_TX)
#define BOARD_UART_CTS (1 << BOARD_IOID_UART_CTS) #define BOARD_UART_CTS (1 << BOARD_IOID_UART_CTS)

View file

@ -55,7 +55,7 @@
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#define BUTTON_GPIO_CFG (IOC_CURRENT_2MA | IOC_STRENGTH_AUTO | \ #define BUTTON_GPIO_CFG (IOC_CURRENT_2MA | IOC_STRENGTH_AUTO | \
IOC_IOPULL_UP | IOC_SLEW_DISABLE | \ IOC_IOPULL_UP | IOC_SLEW_DISABLE | \
IOC_HYST_DISABLE | IOC_BOTH_EDGES | \ IOC_HYST_ENABLE | IOC_BOTH_EDGES | \
IOC_INT_ENABLE | IOC_IOMODE_NORMAL | \ IOC_INT_ENABLE | IOC_IOMODE_NORMAL | \
IOC_NO_WAKE_UP | IOC_INPUT_ENABLE | \ IOC_NO_WAKE_UP | IOC_INPUT_ENABLE | \
IOC_JTAG_DISABLE) IOC_JTAG_DISABLE)
@ -137,7 +137,7 @@ button_press_handler(uint8_t ioid)
sensors_changed(&button_right_sensor); sensors_changed(&button_right_sensor);
} }
} else { } else {
lpm_shutdown(BOARD_IOID_KEY_RIGHT); lpm_shutdown(BOARD_IOID_KEY_RIGHT, IOC_IOPULL_UP, IOC_WAKE_ON_LOW);
} }
} }