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 += putchar.c ieee-addr.c batmon-sensor.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

View file

@ -79,8 +79,14 @@ clock_init(void)
* Here, we configure GPT0 Timer A, which we subsequently use in
* 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_load_set();
while(!ti_lib_prcm_load_get());
@ -155,11 +161,19 @@ clock_wait(clock_time_t i)
void
clock_delay_usec(uint16_t len)
{
uint32_t clock_status;
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_PERIPH) !=
PRCM_DOMAIN_POWER_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_enable(GPT0_BASE, TIMER_B);
@ -168,6 +182,12 @@ clock_delay_usec(uint16_t len)
* function, hence the direct register access here
*/
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 "dev/radio.h"
#include "dev/cc26xx-rf.h"
#include "dev/oscillators.h"
#include "net/packetbuf.h"
#include "net/rime/rimestats.h"
#include "net/linkaddr.h"
@ -68,6 +69,7 @@
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/
#define BUSYWAIT_UNTIL(cond, max_time) \
do { \
@ -393,74 +395,6 @@ static int on(void);
static int off(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
rf_is_accessible(void)
{
@ -498,16 +432,19 @@ static uint_fast8_t
rf_send_cmd(uint32_t cmd, uint32_t *status)
{
uint32_t timeout_count = 0;
bool interrupts_disabled;
/*
* Make sure ContikiMAC doesn't turn us off from within an interrupt while
* we are accessing RF Core registers
*/
ti_lib_int_master_disable();
interrupts_disabled = ti_lib_int_master_disable();
if(!rf_is_accessible()) {
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;
}
@ -516,12 +453,16 @@ rf_send_cmd(uint32_t cmd, uint32_t *status)
*status = HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDSTA);
if(++timeout_count > 50000) {
PRINTF("rf_send_cmd: Timeout\n");
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
return RF_CMD_ERROR;
}
} 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
@ -978,8 +919,8 @@ static int
power_up(void)
{
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_CPE1);
ti_lib_int_disable(INT_RF_CPE0);
@ -1002,7 +943,10 @@ power_up(void)
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = 0x0;
ti_lib_int_enable(INT_RF_CPE0);
ti_lib_int_enable(INT_RF_CPE1);
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/* Let CPE boot */
HWREG(RFC_PWR_NONBUF_BASE + RFC_PWR_O_PWMCLKEN) = RF_CORE_CLOCKS_MASK;
@ -1022,7 +966,7 @@ power_up(void)
static 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_CPE1);
@ -1045,7 +989,9 @@ power_down(void)
ti_lib_int_pend_clear(INT_RF_CPE1);
ti_lib_int_enable(INT_RF_CPE0);
ti_lib_int_enable(INT_RF_CPE1);
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
}
/*---------------------------------------------------------------------------*/
static int
@ -1160,6 +1106,8 @@ cc26xx_rf_cpe0_isr(void)
static void
setup_interrupts(void)
{
bool interrupts_disabled;
/* We are already turned on by the caller, so this should not happen */
if(!rf_is_accessible()) {
PRINTF("setup_interrupts: No access\n");
@ -1167,7 +1115,7 @@ setup_interrupts(void)
}
/* Disable interrupts */
ti_lib_int_master_disable();
interrupts_disabled = ti_lib_int_master_disable();
/* Set all interrupt channels to CPE0 channel, error to CPE1 */
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_enable(INT_RF_CPE0);
ti_lib_int_enable(INT_RF_CPE1);
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
}
/*---------------------------------------------------------------------------*/
static uint8_t
@ -1199,7 +1150,7 @@ request(void)
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
init(void)
@ -1530,7 +1481,7 @@ on(void)
* 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.
*/
request_hf_xosc();
oscillators_request_hf_xosc();
/*
* 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
* 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) {
PRINTF("on: radio_setup() failed\n");
@ -1597,7 +1548,7 @@ off(void)
power_down();
/* 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 */
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;
int j;
uint32_t cmd_status;
bool interrupts_disabled;
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
* 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;
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/*
* 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 {
/* Request the HF XOSC to source the HF clock. */
request_hf_xosc();
oscillators_request_hf_xosc();
/* We were off: Boot the CPE */
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 */
switch_to_hf_xosc();
oscillators_switch_to_hf_xosc();
}
/* Enter BLE mode */
@ -2118,13 +2072,17 @@ PROCESS_THREAD(cc26xx_rf_ble_beacon_process, ev, data)
power_down();
/* 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);
ti_lib_int_master_disable();
interrupts_disabled = ti_lib_int_master_disable();
ble_mode_on = 0;
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/* Wait unless this is the last burst */
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;
ti_lib_int_master_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
}
PROCESS_END();
}

View file

@ -44,6 +44,7 @@
#include "ti-lib.h"
#include <stdint.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/
#define cc26xx_rtc_isr(...) AONRTCIntHandler(__VA_ARGS__)
/*---------------------------------------------------------------------------*/
@ -54,9 +55,11 @@ void
cc26xx_rtc_init(void)
{
uint32_t compare_value;
bool interrupts_disabled;
/* 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_event_clear(AON_RTC_CH0);
@ -65,22 +68,25 @@ cc26xx_rtc_init(void)
/* 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_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 */
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) +
ti_lib_aon_rtc_current_compare_value_get();
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 */
ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH2);
/* Enable channel 2 and the RTC */
ti_lib_aon_rtc_channel_enable(AON_RTC_CH2);
ti_lib_aon_rtc_enable();
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

View file

@ -40,6 +40,7 @@
#include "sys/energest.h"
#include <stdint.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/
/* Which events to trigger a UART interrupt */
#define CC26XX_UART_RX_INTERRUPT_TRIGGERS (UART_INT_RX | UART_INT_RT)
@ -53,35 +54,98 @@
/*---------------------------------------------------------------------------*/
static int (*input_handler)(unsigned char c);
/*---------------------------------------------------------------------------*/
static void
power_domain_on(void)
static bool
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);
while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= 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
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.
* This function uses SysCtrlClockGet() to get the system clock
* frequency. This could be also be a variable or hard coded value
* instead of a function call.
* Make sure the TX pin is output / high before assigning it to UART control
* to avoid falling edge glitches
*/
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));
}
/*---------------------------------------------------------------------------*/
static void
configure_registers(void)
{
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_UART_TX);
ti_lib_gpio_pin_write(BOARD_UART_TX, 1);
/*
* Map UART signals to the correct GPIO pins and configure them as
* 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,
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.
@ -97,116 +168,138 @@ configure_registers(void)
*/
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 */
ti_lib_uart_int_enable(UART0_BASE, CC26XX_UART_RX_INTERRUPT_TRIGGERS);
}
/*---------------------------------------------------------------------------*/
static void
uart_on(void)
{
power_domain_on();
/* Enable FIFOs */
HWREG(UART0_BASE + UART_O_LCRH) |= UART_LCRH_FEN;
/* Configure baud rate and enable */
if((HWREG(UART0_BASE + UART_O_CTL) & UART_CTL_UARTEN) == 0) {
configure_registers();
/* Enable UART */
ti_lib_uart_enable(UART0_BASE);
if(input_handler) {
ctl_val += UART_CTL_RXE;
}
}
/*---------------------------------------------------------------------------*/
static uint8_t
lpm_permit_max_pm_handler(void)
{
return LPM_MODE_MAX_SUPPORTED;
/* Enable TX, RX (conditionally), and the UART. */
HWREG(UART0_BASE + UART_O_CTL) = ctl_val;
}
/*---------------------------------------------------------------------------*/
static void
lpm_drop_handler(uint8_t mode)
{
/* Do nothing if the PD is off */
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON) {
return;
/*
* First, wait for any outstanding TX to complete. If we have an input
* handler, the SERIAL PD will be kept on and the UART module clock will
* 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
* it's on, this means that some other code module wants UART functionality
* during deep sleep, so we stay enabled
* If we have a registered input_handler then we need to retain RX
* capability. Thus, if this is not a shutdown notification and we have an
* input handler, we do nothing
*/
if((HWREG(PRCM_BASE + PRCM_O_UARTCLKGDS) & 1) == 0) {
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) {
if((mode != LPM_MODE_SHUTDOWN) && (input_handler != NULL)) {
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 */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_UART0);
/* Disable all UART interrupts and clear all flags */
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();
while(!ti_lib_prcm_load_get());
/* Disable Interrupts */
ti_lib_int_master_disable();
/* Set pins to low leakage configuration in preparation for deep sleep */
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 */
ti_lib_uart_disable(UART0_BASE);
/* Disable all UART module interrupts */
ti_lib_uart_int_disable(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
/* Disable all UART interrupts and clear all flags */
disable_interrupts();
configure_registers();
/* Setup pins, Baud rate and FIFO levels */
configure();
/* Acknowledge UART interrupts */
ti_lib_int_enable(INT_UART0);
/* Enable UART interrupts */
enable_interrupts();
}
/*---------------------------------------------------------------------------*/
void
cc26xx_uart_init()
{
bool interrupts_disabled;
/* Re-enable processor interrupts */
ti_lib_int_master_enable();
/* Return early if disabled by user conf or if ports are misconfigured */
if(usable() == false) {
return;
}
/* Enable UART */
ti_lib_uart_enable(UART0_BASE);
/* Disable Interrupts */
interrupts_disabled = ti_lib_int_master_disable();
/* Register ourselves with the LPM 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
cc26xx_uart_write_byte(uint8_t c)
{
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON) {
/* Return early if disabled by user conf or if ports are misconfigured */
if(usable() == false) {
return;
}
if(accessible() == false) {
enable();
}
ti_lib_uart_char_put(UART0_BASE, c);
}
/*---------------------------------------------------------------------------*/
@ -214,9 +307,52 @@ void
cc26xx_uart_set_input(int (*input)(unsigned char c))
{
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;
}
/*---------------------------------------------------------------------------*/
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
cc26xx_uart_isr(void)
{
@ -225,6 +361,8 @@ cc26xx_uart_isr(void)
ENERGEST_ON(ENERGEST_TYPE_IRQ);
power_and_clock();
/* Read out the masked interrupt status */
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
* \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));
/**
* \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_ */

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/watchdog.h"
#include "dev/cc26xx-rtc.h"
#include "dev/oscillators.h"
/*---------------------------------------------------------------------------*/
#if ENERGEST_CONF_ON
static unsigned long irq_energest = 0;
@ -64,9 +65,6 @@ static unsigned long irq_energest = 0;
/*---------------------------------------------------------------------------*/
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 */
#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)
/*---------------------------------------------------------------------------*/
/* Variables used by the power on/off (Power mode: SHUTDOWN) functionality */
static uint8_t shutdown_requested;
static uint32_t pin;
/*---------------------------------------------------------------------------*/
void
lpm_pd_lock_obtain(lpm_power_domain_lock_t *lock, uint32_t domains)
{
/* 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_shutdown(uint32_t wakeup_pin, uint32_t io_pull, uint32_t wake_on)
{
lpm_registered_module_t *module;
int i;
rtimer_clock_t t0;
uint32_t io_cfg = (IOC_STD_INPUT & ~IOC_IOPULL_M) | IOC_IOPULL_UP |
IOC_WAKE_ON_LOW;
int i, j;
uint32_t io_cfg = (IOC_STD_INPUT & ~IOC_IOPULL_M) | io_pull |
wake_on;
/* 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;
module = module->next) {
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++) {
t0 = RTIMER_NOW();
leds_toggle(LEDS_ALL);
while(RTIMER_CLOCK_LT(RTIMER_NOW(), (t0 + (RTIMER_SECOND >> 3))));
}
/* Freeze I/O latches in AON */
ti_lib_aon_ioc_freeze_enable();
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);
ti_lib_ioc_port_configure_set(pin, IOC_PORT_GPIO, io_cfg);
oscillators_switch_to_hf_rc();
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:
* - 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
* Notify all modules that we're back on and rely on them to restore clocks
* and power domains as required.
*/
void
lpm_wake_up()
static void
wake_up(void)
{
lpm_registered_module_t *module;
@ -182,8 +221,8 @@ lpm_wake_up()
ti_lib_aon_ioc_freeze_disable();
ti_lib_sys_ctrl_aon_sync();
/* Power up AUX and allow it to go to sleep */
ti_lib_aon_wuc_aux_wakeup_event(AONWUC_AUX_ALLOW_SLEEP);
/* Check operating conditions, optimally choose DCDC versus GLDO */
ti_lib_sys_ctrl_dcdc_voltage_conditional_control();
/* Notify all registered modules that we've just woken up */
for(module = list_head(modules_list); module != NULL;
@ -198,16 +237,11 @@ void
lpm_drop()
{
lpm_registered_module_t *module;
lpm_power_domain_lock_t *lock;
uint8_t max_pm = LPM_MODE_MAX_SUPPORTED;
uint8_t module_pm;
uint32_t domains = LOCKABLE_DOMAINS;
if(shutdown_requested) {
shutdown_now();
}
if(RTIMER_CLOCK_LT(cc26xx_rtc_get_next_trigger(),
RTIMER_NOW() + STANDBY_MIN_DURATION)) {
lpm_sleep();
@ -244,26 +278,21 @@ lpm_drop()
* 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
* 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;
module = module->next) {
if(module->shutdown) {
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 */
domains &= ~lock->domains;
domains &= ~module->domain_lock;
}
/* 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);
}
/*
* 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 */
ti_lib_aon_wuc_mcu_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);
/* Disable retention in the RFCORE RAM */
HWREG(PRCM_BASE + PRCM_O_RAMRETEN) &= ~PRCM_RAMRETEN_RFC;
/* 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;
ti_lib_prcm_retention_disable(PRCM_DOMAIN_RFCORE);
/*
* 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);
/*
* 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.
*/
if(domains == LOCKABLE_DOMAINS) {
@ -362,7 +401,7 @@ lpm_drop()
* the chip properly, and then we will enable the global interrupt without
* unpending events so the handlers can fire
*/
lpm_wake_up();
wake_up();
ti_lib_int_master_enable();
}
@ -396,10 +435,26 @@ lpm_register_module(lpm_registered_module_t *module)
}
/*---------------------------------------------------------------------------*/
void
lpm_unregister_module(lpm_registered_module_t *module)
{
list_remove(modules_list, module);
}
/*---------------------------------------------------------------------------*/
void
lpm_init()
{
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>
/*---------------------------------------------------------------------------*/
#define LPM_MODE_SLEEP PWRCTRL_ACTIVE
#define LPM_MODE_DEEP_SLEEP PWRCTRL_POWER_DOWN
#define LPM_MODE_SHUTDOWN PWRCTRL_SHUTDOWN
#define LPM_MODE_SLEEP 1
#define LPM_MODE_DEEP_SLEEP 2
#define LPM_MODE_SHUTDOWN 3
#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 {
struct lpm_registered_module *next;
uint8_t (*request_max_pm)(void);
void (*shutdown)(uint8_t mode);
void (*wakeup)(void);
uint32_t domain_lock;
} 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
* function is in charge of turning power domains back on. This
* 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 = \
{ NULL, m, s, w }
#define LPM_MODULE(n, m, s, w, l) static lpm_registered_module_t n = \
{ 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
*
@ -134,17 +107,11 @@ void lpm_sleep(void);
/**
* \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 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);
/**
* \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);
void lpm_shutdown(uint32_t wakeup_pin, uint32_t io_pull, uint32_t wake_on);
/**
* \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);
/**
* \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
*/
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_ */
/*---------------------------------------------------------------------------*/

View file

@ -29,6 +29,7 @@
*/
/*---------------------------------------------------------------------------*/
#include "cc26xx-uart.h"
#include "ti-lib.h"
#include <string.h>
/*---------------------------------------------------------------------------*/
@ -47,9 +48,16 @@ puts(const char *str)
return 0;
}
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;
}
/*---------------------------------------------------------------------------*/
@ -62,9 +70,16 @@ dbg_send_bytes(const unsigned char *s, unsigned int len)
if(i >= len) {
break;
}
putchar(*s++);
cc26xx_uart_write_byte(*s++);
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;
}
/*---------------------------------------------------------------------------*/

View file

@ -58,6 +58,10 @@ slip_arch_writeb(unsigned char c)
void
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);
}
/*---------------------------------------------------------------------------*/

View file

@ -148,7 +148,7 @@
#include "driverlib/aux_wuc.h"
#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_freq_req(...) AUXWUCClockFreqReq(__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_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_dcdc_voltage_conditional_control(...) SysCtrl_DCDC_VoltageConditionalControl(__VA_ARGS__)
#define ti_lib_sys_ctrl_reset_source_get(...) SysCtrlResetSourceGet(__VA_ARGS__)
/*---------------------------------------------------------------------------*/
/* 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 use buttons and the reed relay (triggered by holding a magnet near S3
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
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

View file

@ -77,12 +77,6 @@
* - The example also shows how to retrieve the duration of a
* button press (in ticks). The driver will generate a
* 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
*
* @{
@ -100,7 +94,6 @@
#include "button-sensor.h"
#include "batmon-sensor.h"
#include "board-peripherals.h"
#include "lpm.h"
#include "cc26xx-rf.h"
#include "ti-lib.h"
@ -251,8 +244,7 @@ get_light_reading()
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);
}
/*---------------------------------------------------------------------------*/
@ -369,26 +361,6 @@ init_sensor_readings(void)
#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)
{
@ -406,8 +378,6 @@ PROCESS_THREAD(cc26xx_demo_process, ev, data)
get_sync_sensor_readings();
init_sensor_readings();
keep_uart_on();
while(1) {
PROCESS_YIELD();
@ -462,8 +432,6 @@ PROCESS_THREAD(cc26xx_demo_process, ev, data)
button_select_sensor.value(BUTTON_SENSOR_VALUE_DURATION));
#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);
SENSORS_DEACTIVATE(opt_3001_sensor);
if(value != CC26XX_SENSOR_READING_ERROR) {
opt_reading.raw = value;
@ -587,6 +585,7 @@ get_light_reading()
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);
}
/*---------------------------------------------------------------------------*/

View file

@ -59,10 +59,10 @@
#include "contiki-conf.h"
#include "sys/process.h"
#include "dev/serial-line.h"
#include "dev/cc26xx-uart.h"
#include "net/ip/uip.h"
#include "net/ip/uip-udp-packet.h"
#include "net/ip/uiplib.h"
#include "lpm.h"
#include "net-uart.h"
#include "httpd-simple.h"
@ -148,37 +148,16 @@ net_input(void)
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
release_uart(void)
{
/* Release serial PD lock */
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());
cc26xx_uart_set_input(NULL);
}
/*---------------------------------------------------------------------------*/
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());
cc26xx_uart_set_input(serial_line_input_byte);
}
/*---------------------------------------------------------------------------*/
static int

View file

@ -46,6 +46,7 @@
#include "lpm.h"
#include "gpio-interrupt.h"
#include "dev/watchdog.h"
#include "dev/oscillators.h"
#include "ieee-addr.h"
#include "vims.h"
#include "cc26xx-model.h"
@ -119,23 +120,6 @@ set_rf_params(void)
#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
*
@ -144,41 +128,39 @@ select_lf_xosc(void)
int
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 */
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);
oscillators_select_lf_xosc();
lpm_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();
/* Clock must always be enabled for the semaphore module */
HWREG(AUX_WUC_BASE + AUX_WUC_O_MODCLKEN1) = AUX_WUC_MODCLKEN1_SMPH;
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);
ti_lib_int_master_enable();
cc26xx_rtc_init();
clock_init();
rtimer_init();
board_init();
watchdog_init();
process_init();
@ -187,7 +169,6 @@ main(void)
/* Character I/O Initialisation */
#if CC26XX_UART_CONF_ENABLE
cc26xx_uart_init();
cc26xx_uart_set_input(serial_line_input_byte);
#endif
serial_line_init();

View file

@ -136,7 +136,7 @@ static int enabled = SENSOR_STATUS_DISABLED;
static uint8_t sensor_value[SENSOR_DATA_BUF_SIZE];
/*---------------------------------------------------------------------------*/
/* 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;
/*---------------------------------------------------------------------------*/
@ -187,7 +187,7 @@ enable_sensor(bool enable)
if(enable) {
/* Enable forced mode */
val = PM_NORMAL | OSRSP(1) | OSRST(1);
val = PM_FORCED | OSRSP(1) | OSRST(1);
} else {
val = PM_OFF;
}
@ -345,6 +345,7 @@ configure(int type, int enable)
case SENSORS_HW_INIT:
enabled = SENSOR_STATUS_INITIALISED;
init();
enable_sensor(0);
break;
case SENSORS_ACTIVE:
/* Must be initialised first */

View file

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

View file

@ -39,9 +39,55 @@
#include "contiki-conf.h"
#include "ti-lib.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 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
i2c_status()
@ -57,21 +103,34 @@ i2c_status()
}
/*---------------------------------------------------------------------------*/
void
board_i2c_init()
board_i2c_shutdown()
{
/* The I2C peripheral must be enabled */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_I2C0);
interface = NO_INTERFACE;
if(accessible()) {
ti_lib_i2c_master_disable(I2C0_BASE);
}
ti_lib_prcm_peripheral_run_disable(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;
/*
* 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_i2c_master_init_exp_clk(I2C0_BASE,
ti_lib_sys_ctrl_peripheral_clock_get(
PRCM_PERIPH_I2C0, SYSCTRL_SYSBUS_ON),
true);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_IOPULL_UP);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_IOPULL_UP);
}
/*---------------------------------------------------------------------------*/
bool
@ -248,8 +307,15 @@ board_i2c_select(uint8_t new_interface, uint8_t address)
{
slave_addr = address;
if(accessible() == false) {
board_i2c_wakeup();
}
if(new_interface != interface) {
interface = new_interface;
ti_lib_i2c_master_disable(I2C0_BASE);
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_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_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_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
* \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,
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_ */
/*---------------------------------------------------------------------------*/

View file

@ -40,12 +40,35 @@
#include "ti-lib.h"
#include "board-spi.h"
#include "board.h"
#include <stdbool.h>
/*---------------------------------------------------------------------------*/
#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
board_spi_write(const uint8_t *buf, size_t len)
{
if(accessible() == false) {
return 0;
}
while(len > 0) {
uint32_t ul;
@ -61,6 +84,10 @@ board_spi_write(const uint8_t *buf, size_t len)
int
board_spi_read(uint8_t *buf, size_t len)
{
if(accessible() == false) {
return 0;
}
while(len > 0) {
uint32_t ul;
@ -79,6 +106,10 @@ board_spi_read(uint8_t *buf, size_t len)
void
board_spi_flush()
{
if(accessible() == false) {
return;
}
uint32_t 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;
/* 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_prcm_load_set();
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_prcm_load_set();
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 <string.h>
/*---------------------------------------------------------------------------*/
#define PRCM_DOMAINS (PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH)
#include <stdbool.h>
/*---------------------------------------------------------------------------*/
static void
power_domains_on(void)
{
/* Turn on relevant power domains */
ti_lib_prcm_power_domain_on(PRCM_DOMAINS);
/* Turn on the PERIPH PD */
ti_lib_prcm_power_domain_on(PRCM_DOMAIN_PERIPH);
/* 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));
}
/*---------------------------------------------------------------------------*/
@ -64,8 +63,6 @@ static void
lpm_wakeup_handler(void)
{
power_domains_on();
board_i2c_init();
}
/*---------------------------------------------------------------------------*/
static void
@ -77,8 +74,12 @@ shutdown_handler(uint8_t mode)
SENSORS_DEACTIVATE(opt_3001_sensor);
SENSORS_DEACTIVATE(tmp_007_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
* 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
board_init()
{
/* Disable global interrupts */
uint8_t int_disabled = ti_lib_int_master_disable();
bool int_disabled = ti_lib_int_master_disable();
power_domains_on();
@ -112,37 +147,19 @@ board_init()
ti_lib_prcm_load_set();
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 */
board_i2c_init();
/* 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);
board_i2c_wakeup();
buzzer_init();
/* Make sure the external flash is in the lower power mode */
ext_flash_init();
lpm_register_module(&sensortag_module);
/* For unsupported peripherals, select a default pin configuration */
configure_unused_pins();
/* Re-enable interrupt if initially enabled. */
if(!int_disabled) {
ti_lib_int_master_enable();

View file

@ -100,12 +100,16 @@
* 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_CTS IOID_UNUSED
#define BOARD_IOID_UART_RTS IOID_UNUSED
#define BOARD_UART_RXD (1 << BOARD_IOID_UART_RXD)
#define BOARD_UART_TXD (1 << BOARD_IOID_UART_TXD)
#define BOARD_UART_RX (1 << BOARD_IOID_UART_RX)
#define BOARD_UART_TX (1 << BOARD_IOID_UART_TX)
#define BOARD_UART_CTS (1 << BOARD_IOID_UART_CTS)
#define BOARD_UART_RTS (1 << BOARD_IOID_UART_RTS)
/** @} */
@ -156,7 +160,7 @@
*/
#define BOARD_IOID_FLASH_CS IOID_14
#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)
/** @} */
/*---------------------------------------------------------------------------*/
/**
* \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
* @{

View file

@ -55,7 +55,7 @@
/*---------------------------------------------------------------------------*/
#define BUTTON_GPIO_CFG (IOC_CURRENT_2MA | IOC_STRENGTH_AUTO | \
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_NO_WAKE_UP | IOC_INPUT_ENABLE | \
IOC_JTAG_DISABLE)
@ -116,7 +116,7 @@ button_press_handler(uint8_t ioid)
sensors_changed(&button_right_sensor);
}
} 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>
/*---------------------------------------------------------------------------*/
static uint8_t buzzer_on;
static lpm_power_domain_lock_t lock;
LPM_MODULE(buzzer_module, NULL, NULL, NULL, LPM_DOMAIN_PERIPH);
/*---------------------------------------------------------------------------*/
void
buzzer_init()
{
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
@ -72,9 +65,28 @@ buzzer_start(int freq)
{
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;
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 */
ti_lib_timer_disable(GPT0_BASE, TIMER_A);
@ -88,12 +100,6 @@ buzzer_start(int freq)
/* Start */
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
@ -101,24 +107,37 @@ buzzer_stop()
{
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 */
ti_lib_timer_disable(GPT0_BASE, TIMER_A);
/*
* Stop running in sleep mode.
* 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
* mode and we control TA here. Thus, with the current setup, it's OK to
* 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
* to happen through an umbrella module
* Stop the module clock:
*
* Currently GPT0 is in use by clock_delay_usec (GPT0/TB) and by this
* module here (GPT0/TA).
*
* clock_delay_usec
* - 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_deep_sleep_disable(PRCM_PERIPH_TIMER0);
ti_lib_prcm_load_set();
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 */
#define BLS_MANUFACTURER_ID 0xEF
#define BLS_DEVICE_ID 0x11
#define BLS_DEVICE_ID 0x12
#define BLS_PROGRAM_PAGE_SIZE 256
#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
* the status register is accessible.
* \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.
* \brief Verify the flash part.
* \return True when successful.
*/
static bool
verify_part(void)
@ -210,6 +168,57 @@ verify_part(void)
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.
* \return Zero when successful.
@ -232,7 +241,7 @@ write_enable(void)
bool
ext_flash_open()
{
board_spi_open(4000000, BOARD_SPI_CLK_FLASH);
board_spi_open(4000000, BOARD_IOID_SPI_CLK_FLASH);
/* GPIO pin configuration */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_FLASH_CS);
@ -406,4 +415,11 @@ ext_flash_test(void)
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
*
* This call will put the device in its lower power mode (power down).
*/
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.
*/
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_ */
/*---------------------------------------------------------------------------*/

View file

@ -212,12 +212,12 @@ static uint8_t acc_range_reg;
static uint8_t val;
static uint8_t interrupt_status;
/*---------------------------------------------------------------------------*/
#define SENSOR_STATUS_DISABLED 0
#define SENSOR_STATUS_BOOTING 1
#define SENSOR_STATUS_ENABLED 2
#define SENSOR_STATE_DISABLED 0
#define SENSOR_STATE_BOOTING 1
#define SENSOR_STATE_ENABLED 2
static int enabled = SENSOR_STATUS_DISABLED;
static int elements;
static int state = SENSOR_STATE_DISABLED;
static int elements = MPU_9250_SENSOR_TYPE_NONE;
/*---------------------------------------------------------------------------*/
/* 3 16-byte words for all sensor readings */
#define SENSOR_DATA_BUF_SIZE 3
@ -289,7 +289,9 @@ static void
select_axes(void)
{
val = ~mpu_config;
SENSOR_SELECT();
sensor_common_write_reg(PWR_MGMT_2, &val, 1);
SENSOR_DESELECT();
}
/*---------------------------------------------------------------------------*/
static void
@ -334,37 +336,6 @@ acc_set_range(uint8_t new_range)
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
* \return Return the interrupt status
@ -450,13 +421,13 @@ gyro_read(uint16_t *data)
/* Burst read of all gyroscope values */
success = sensor_common_read_reg(GYRO_XOUT_H, (uint8_t *)data, DATA_SIZE);
SENSOR_DESELECT();
if(success) {
convert_to_le((uint8_t *)data, DATA_SIZE);
} else {
sensor_common_set_error_data((uint8_t *)data, DATA_SIZE);
}
SENSOR_DESELECT();
} else {
success = false;
}
@ -514,15 +485,13 @@ gyro_convert(int16_t raw_data)
static void
notify_ready(void *not_used)
{
enabled = SENSOR_STATUS_ENABLED;
state = SENSOR_STATE_ENABLED;
sensors_changed(&mpu_9250_sensor);
}
/*---------------------------------------------------------------------------*/
static void
initialise(void *not_used)
{
init_sensor();
/* Configure the accelerometer range */
if((elements & MPU_9250_SENSOR_TYPE_ACC) != 0) {
acc_set_range(MPU_9250_SENSOR_ACC_RANGE);
@ -537,7 +506,7 @@ static void
power_up(void)
{
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);
}
@ -553,7 +522,7 @@ value(int type)
int rv;
float converted_val = 0;
if(enabled == SENSOR_STATUS_DISABLED) {
if(state == SENSOR_STATE_DISABLED) {
PRINTF("MPU: Sensor Disabled\n");
return CC26XX_SENSOR_READING_ERROR;
}
@ -632,33 +601,42 @@ configure(int type, int enable)
{
switch(type) {
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;
break;
case SENSORS_ACTIVE:
if((enable & MPU_9250_SENSOR_TYPE_ACC) != 0 ||
(enable & MPU_9250_SENSOR_TYPE_GYRO) != 0) {
if(((enable & MPU_9250_SENSOR_TYPE_ACC) != 0) ||
((enable & MPU_9250_SENSOR_TYPE_GYRO) != 0)) {
PRINTF("MPU: Enabling\n");
elements = enable & MPU_9250_SENSOR_TYPE_ALL;
power_up();
enabled = SENSOR_STATUS_BOOTING;
state = SENSOR_STATE_BOOTING;
} else {
PRINTF("MPU: Disabling\n");
ctimer_stop(&startup_timer);
elements = MPU_9250_SENSOR_TYPE_NONE;
enable_sensor(0);
while(ti_lib_i2c_master_busy(I2C0_BASE));
enabled = SENSOR_STATUS_DISABLED;
if(HWREG(GPIO_BASE + GPIO_O_DOUT31_0) & BOARD_MPU_POWER) {
/* Then check our state */
elements = MPU_9250_SENSOR_TYPE_NONE;
ctimer_stop(&startup_timer);
sensor_sleep();
while(ti_lib_i2c_master_busy(I2C0_BASE));
state = SENSOR_STATE_DISABLED;
ti_lib_gpio_pin_clear(BOARD_MPU_POWER);
}
}
break;
case MPU_9250_SENSOR_SHUTDOWN:
ti_lib_gpio_pin_write(BOARD_MPU_POWER, 0);
break;
default:
break;
}
return enabled;
return state;
}
/*---------------------------------------------------------------------------*/
/**
@ -672,12 +650,12 @@ status(int type)
switch(type) {
case SENSORS_ACTIVE:
case SENSORS_READY:
return enabled;
return state;
break;
default:
break;
}
return SENSOR_STATUS_DISABLED;
return SENSOR_STATE_DISABLED;
}
/*---------------------------------------------------------------------------*/
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_ALL (MPU_9250_SENSOR_TYPE_ACC | \
MPU_9250_SENSOR_TYPE_GYRO)
#define MPU_9250_SENSOR_SHUTDOWN 0xFF
/*---------------------------------------------------------------------------*/
/* Accelerometer range */
#define MPU_9250_SENSOR_ACC_RANGE_2G 0

View file

@ -69,16 +69,42 @@
#define REG_MANUFACTURER_ID 0x7E
#define REG_DEVICE_ID 0x7F
/*---------------------------------------------------------------------------*/
/* Register values */
#define MANUFACTURER_ID 0x5449 /* TI */
#define DEVICE_ID 0x3001 /* Opt 3001 */
#define CONFIG_RESET 0xC810
#define CONFIG_TEST 0xCC10
#define CONFIG_ENABLE 0x10CC /* 0xCC10 */
#define CONFIG_DISABLE 0x108C /* 0xC810 */
/*---------------------------------------------------------------------------*/
/* Bit values */
#define DATA_RDY_BIT 0x0080 /* Data ready */
/*
* Configuration Register Bits and Masks.
* We use uint16_t to read from / write to registers, meaning that the
* register's MSB is the variable's LSB.
*/
#define CONFIG_RN 0x00F0 /* [15..12] Range Number */
#define CONFIG_CT 0x0008 /* [11] Conversion Time */
#define CONFIG_M 0x0006 /* [10..9] Mode of Conversion */
#define CONFIG_OVF 0x0001 /* [8] Overflow */
#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 */
#define REGISTER_LENGTH 2
@ -86,24 +112,22 @@
/* Sensor data size */
#define DATA_LENGTH 2
/*---------------------------------------------------------------------------*/
#define SENSOR_STATUS_DISABLED 0
#define SENSOR_STATUS_NOT_READY 1
#define SENSOR_STATUS_ENABLED 2
/*
* SENSOR_STATE_SLEEPING and SENSOR_STATE_ACTIVE are mutually exclusive.
* 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 */
#define SENSOR_STARTUP_DELAY (CLOCK_SECOND >> 3)
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
*/
@ -114,6 +138,28 @@ select(void)
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
* \param enable TRUE: on, FALSE: off
@ -122,13 +168,20 @@ static void
enable_sensor(bool enable)
{
uint16_t val;
uint16_t had_data_ready = state & SENSOR_STATE_DATA_READY;
select();
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 {
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);
@ -145,15 +198,15 @@ read_data(uint16_t *raw_data)
bool success;
uint16_t val;
if((state & SENSOR_STATE_DATA_READY) != SENSOR_STATE_DATA_READY) {
return false;
}
select();
success = sensor_common_read_reg(REG_CONFIGURATION, (uint8_t *)&val,
REGISTER_LENGTH);
if(success) {
success = (val & DATA_RDY_BIT) == DATA_RDY_BIT;
}
if(success) {
success = sensor_common_read_reg(REG_RESULT, (uint8_t *)&val, DATA_LENGTH);
}
@ -196,14 +249,9 @@ value(int type)
uint16_t raw_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);
if(rv == 0) {
if(rv == false) {
return CC26XX_SENSOR_READING_ERROR;
}
@ -229,30 +277,38 @@ value(int type)
static int
configure(int type, int enable)
{
int rv = 0;
switch(type) {
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;
case SENSORS_ACTIVE:
if(enable) {
enable_sensor(1);
ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL);
enabled = SENSOR_STATUS_NOT_READY;
rv = 1;
} else {
ctimer_stop(&startup_timer);
enable_sensor(0);
enabled = SENSOR_STATUS_DISABLED;
rv = 0;
}
break;
default:
break;
}
return enabled;
return rv;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Returns the status of the sensor
* \param type SENSORS_ACTIVE or SENSORS_READY
* \return 1 if the sensor is enabled
* \param type ignored
* \return The state of the sensor SENSOR_STATE_xyz
*/
static int
status(int type)
@ -260,12 +316,10 @@ status(int type)
switch(type) {
case SENSORS_ACTIVE:
case SENSORS_READY:
return enabled;
break;
default:
break;
}
return SENSOR_STATUS_DISABLED;
return state;
}
/*---------------------------------------------------------------------------*/
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
* 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:
* - Turn the sensor off by calling SENSORS_DEACTIVATE, but in order to take
* subsequent readings SENSORS_ACTIVATE must be called again
* - Leave the sensor on. In this scenario, the caller can simply keep calling
* value() for subsequent readings, but having the sensor on will consume
* energy
* We use single-shot readings. In this mode, the hardware automatically goes
* back to its shutdown mode after the conversion is finished. However, it will
* still respond to I2C operations, so the last conversion can still be read
* out.
*
* In order to take a new reading, the caller has to use SENSORS_ACTIVATE
* again.
* @{
*
* \file

View file

@ -88,7 +88,7 @@
#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 uint16_t val;
@ -124,7 +124,7 @@ enable_sensor(bool enable)
{
bool success;
SELECT()
SELECT();
if(enable) {
val = TMP007_VAL_CONFIG_ON;
@ -267,6 +267,10 @@ configure(int type, int enable)
{
switch(type) {
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);
enabled = SENSOR_STATUS_INITIALISED;
break;

View file

@ -45,95 +45,65 @@
#include <stdint.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
power_domains_on(void)
wakeup_handler(void)
{
/* Turn on relevant power domains */
ti_lib_prcm_power_domain_on(LPM_DOMAINS);
/* Wait for domains to power on */
while((ti_lib_prcm_power_domain_status(LPM_DOMAINS)
/* Turn on the PERIPH PD */
ti_lib_prcm_power_domain_on(PRCM_DOMAIN_PERIPH);
while((ti_lib_prcm_power_domain_status(PRCM_DOMAIN_PERIPH)
!= PRCM_DOMAIN_POWER_ON));
}
/*---------------------------------------------------------------------------*/
static void
lpm_wakeup_handler(void)
{
power_domains_on();
}
/*---------------------------------------------------------------------------*/
/*
* 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
* getting notified before deep sleep. All we need is to be notified when we
* 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
board_init()
static void
configure_unused_pins(void)
{
uint8_t int_disabled = ti_lib_int_master_disable();
/* 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 */
/* Turn off 3.3-V domain (lcd/sdcard power, output low) */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_3V3_EN);
ti_lib_gpio_pin_write(BOARD_3V3_EN, 0);
/* LCD 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) */
/* Accelerometer (PWR output low, CSn output, high) */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ACC_PWR);
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) */
ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ALS_PWR);
ti_lib_gpio_pin_write(BOARD_ALS_PWR, 0);
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);
}
/*---------------------------------------------------------------------------*/
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);
configure_unused_pins();
/* Re-enable interrupt if initially enabled. */
if(!int_disabled) {
ti_lib_int_master_enable();

View file

@ -107,8 +107,8 @@
*/
#define BOARD_IOID_UART_RX IOID_2
#define BOARD_IOID_UART_TX IOID_3
#define BOARD_IOID_UART_CTS IOID_0
#define BOARD_IOID_UART_RTS IOID_21
#define BOARD_IOID_UART_CTS IOID_UNUSED
#define BOARD_IOID_UART_RTS IOID_UNUSED
#define BOARD_UART_RX (1 << BOARD_IOID_UART_RX)
#define BOARD_UART_TX (1 << BOARD_IOID_UART_TX)
#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 | \
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_NO_WAKE_UP | IOC_INPUT_ENABLE | \
IOC_JTAG_DISABLE)
@ -137,7 +137,7 @@ button_press_handler(uint8_t ioid)
sensors_changed(&button_right_sensor);
}
} else {
lpm_shutdown(BOARD_IOID_KEY_RIGHT);
lpm_shutdown(BOARD_IOID_KEY_RIGHT, IOC_IOPULL_UP, IOC_WAKE_ON_LOW);
}
}