From 4085ae3ddfbbf10c8ccbd3dccd43452c40a1fe40 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 13 Mar 2015 12:24:52 +0200 Subject: Add bootloader, nfclib and sensorlib --- boot_loader/bl_autobaud.c | 279 +++ boot_loader/bl_can.c | 1403 ++++++++++++ boot_loader/bl_can.h | 64 + boot_loader/bl_can_timing.h | 241 +++ boot_loader/bl_check.c | 266 +++ boot_loader/bl_check.h | 39 + boot_loader/bl_commands.h | 242 +++ boot_loader/bl_config.c | 164 ++ boot_loader/bl_config.h.tmpl | 950 +++++++++ boot_loader/bl_crc32.c | 267 +++ boot_loader/bl_crc32.h | 49 + boot_loader/bl_crystal.h | 79 + boot_loader/bl_decrypt.c | 64 + boot_loader/bl_decrypt.h | 35 + boot_loader/bl_emac.c | 1914 +++++++++++++++++ boot_loader/bl_flash.c | 218 ++ boot_loader/bl_flash.h | 127 ++ boot_loader/bl_hooks.h | 72 + boot_loader/bl_i2c.c | 149 ++ boot_loader/bl_i2c.h | 70 + boot_loader/bl_link.icf | 83 + boot_loader/bl_link.ld | 51 + boot_loader/bl_link.sct | 45 + boot_loader/bl_link_ccs.cmd | 63 + boot_loader/bl_main.c | 913 ++++++++ boot_loader/bl_packet.c | 295 +++ boot_loader/bl_packet.h | 37 + boot_loader/bl_ssi.c | 161 ++ boot_loader/bl_ssi.h | 92 + boot_loader/bl_startup_ccs.s | 645 ++++++ boot_loader/bl_startup_ewarm.S | 612 ++++++ boot_loader/bl_startup_gcc.S | 630 ++++++ boot_loader/bl_startup_rvmdk.S | 653 ++++++ boot_loader/bl_startup_sourcerygxx.S | 630 ++++++ boot_loader/bl_uart.c | 156 ++ boot_loader/bl_uart.h | 81 + boot_loader/bl_usb.c | 2166 +++++++++++++++++++ boot_loader/bl_usbfuncs.c | 1915 +++++++++++++++++ boot_loader/bl_usbfuncs.h | 505 +++++ boot_loader/readme.txt | 28 + boot_loader/uip-conf.h | 108 + boot_loader/usbdfu.h | 420 ++++ nfclib/debug.h | 74 + nfclib/directmode.c | 1059 +++++++++ nfclib/directmode.h | 78 + nfclib/iso14443-4.c | 164 ++ nfclib/iso14443-4.h | 33 + nfclib/iso14443a.c | 1141 ++++++++++ nfclib/iso14443a.h | 65 + nfclib/iso14443b.c | 339 +++ nfclib/iso14443b.h | 51 + nfclib/iso15693.c | 694 ++++++ nfclib/iso15693.h | 62 + nfclib/llcp.c | 1051 +++++++++ nfclib/llcp.h | 248 +++ nfclib/nfc.c | 241 +++ nfclib/nfc.h | 42 + nfclib/nfc_dep.c | 597 ++++++ nfclib/nfc_dep.h | 109 + nfclib/nfc_f.c | 166 ++ nfclib/nfc_f.h | 48 + nfclib/nfc_p2p.c | 1993 +++++++++++++++++ nfclib/nfc_p2p.h | 1536 +++++++++++++ nfclib/snep.c | 760 +++++++ nfclib/snep.h | 206 ++ nfclib/ssitrf79x0.c | 826 +++++++ nfclib/ssitrf79x0.h | 62 + nfclib/trf79x0.c | 1961 +++++++++++++++++ nfclib/trf79x0.h | 400 ++++ nfclib/trf79x0_hw_example.h | 489 +++++ nfclib/types.h | 36 + sensorlib/Makefile | 88 + sensorlib/ak8963.c | 666 ++++++ sensorlib/ak8963.h | 160 ++ sensorlib/ak8975.c | 558 +++++ sensorlib/ak8975.h | 148 ++ sensorlib/bmp180.c | 919 ++++++++ sensorlib/bmp180.h | 207 ++ sensorlib/bq27510g3.c | 1398 ++++++++++++ sensorlib/bq27510g3.h | 210 ++ sensorlib/ccs/.ccsproject | 9 + sensorlib/ccs/.cproject | 147 ++ sensorlib/ccs/.project | 140 ++ .../ccs/.settings/org.eclipse.cdt.codan.core.prefs | 3 + sensorlib/ccs/Debug/sensorlib.lib | Bin 0 -> 953848 bytes sensorlib/ccs/macros.ini_initial | 1 + sensorlib/cm3218.c | 468 ++++ sensorlib/cm3218.h | 149 ++ sensorlib/comp_dcm.c | 634 ++++++ sensorlib/comp_dcm.h | 120 ++ sensorlib/ewarm/Exe/sensorlib.a | Bin 0 -> 740558 bytes sensorlib/gcc/libsensor.a | Bin 0 -> 87204 bytes sensorlib/hw_ak8963.h | 209 ++ sensorlib/hw_ak8975.h | 177 ++ sensorlib/hw_bmp180.h | 349 +++ sensorlib/hw_bq27510g3.h | 148 ++ sensorlib/hw_cm3218.h | 96 + sensorlib/hw_isl29023.h | 128 ++ sensorlib/hw_kxti9.h | 619 ++++++ sensorlib/hw_l3gd20h.h | 468 ++++ sensorlib/hw_lsm303d.h | 1017 +++++++++ sensorlib/hw_lsm303dlhc.h | 1034 +++++++++ sensorlib/hw_mpu6050.h | 1313 ++++++++++++ sensorlib/hw_mpu9150.h | 1454 +++++++++++++ sensorlib/hw_sht21.h | 76 + sensorlib/hw_tmp006.h | 75 + sensorlib/hw_tmp100.h | 93 + sensorlib/i2cm_drv.c | 2256 ++++++++++++++++++++ sensorlib/i2cm_drv.h | 544 +++++ sensorlib/isl29023.c | 668 ++++++ sensorlib/isl29023.h | 166 ++ sensorlib/kxti9.c | 777 +++++++ sensorlib/kxti9.h | 170 ++ sensorlib/l3gd20h.c | 722 +++++++ sensorlib/l3gd20h.h | 158 ++ sensorlib/lsm303d.c | 835 ++++++++ sensorlib/lsm303d.h | 186 ++ sensorlib/lsm303dlhc_accel.c | 662 ++++++ sensorlib/lsm303dlhc_accel.h | 163 ++ sensorlib/lsm303dlhc_mag.c | 615 ++++++ sensorlib/lsm303dlhc_mag.h | 164 ++ sensorlib/magneto.c | 248 +++ sensorlib/magneto.h | 92 + sensorlib/mpu6050.c | 879 ++++++++ sensorlib/mpu6050.h | 174 ++ sensorlib/mpu9150.c | 1180 ++++++++++ sensorlib/mpu9150.h | 187 ++ sensorlib/quaternion.c | 286 +++ sensorlib/quaternion.h | 70 + sensorlib/readme.txt | 21 + sensorlib/rvmdk/sensorlib.lib | Bin 0 -> 879928 bytes sensorlib/sensorlib.ewp | 833 ++++++++ sensorlib/sensorlib.uvopt | 524 +++++ sensorlib/sensorlib.uvproj | 510 +++++ sensorlib/sht21.c | 564 +++++ sensorlib/sht21.h | 156 ++ sensorlib/tmp006.c | 606 ++++++ sensorlib/tmp006.h | 157 ++ sensorlib/tmp100.c | 582 +++++ sensorlib/tmp100.h | 156 ++ sensorlib/vector.c | 139 ++ sensorlib/vector.h | 61 + 142 files changed, 60304 insertions(+) create mode 100644 boot_loader/bl_autobaud.c create mode 100644 boot_loader/bl_can.c create mode 100644 boot_loader/bl_can.h create mode 100644 boot_loader/bl_can_timing.h create mode 100644 boot_loader/bl_check.c create mode 100644 boot_loader/bl_check.h create mode 100644 boot_loader/bl_commands.h create mode 100644 boot_loader/bl_config.c create mode 100644 boot_loader/bl_config.h.tmpl create mode 100644 boot_loader/bl_crc32.c create mode 100644 boot_loader/bl_crc32.h create mode 100644 boot_loader/bl_crystal.h create mode 100644 boot_loader/bl_decrypt.c create mode 100644 boot_loader/bl_decrypt.h create mode 100644 boot_loader/bl_emac.c create mode 100644 boot_loader/bl_flash.c create mode 100644 boot_loader/bl_flash.h create mode 100644 boot_loader/bl_hooks.h create mode 100644 boot_loader/bl_i2c.c create mode 100644 boot_loader/bl_i2c.h create mode 100644 boot_loader/bl_link.icf create mode 100644 boot_loader/bl_link.ld create mode 100644 boot_loader/bl_link.sct create mode 100644 boot_loader/bl_link_ccs.cmd create mode 100644 boot_loader/bl_main.c create mode 100644 boot_loader/bl_packet.c create mode 100644 boot_loader/bl_packet.h create mode 100644 boot_loader/bl_ssi.c create mode 100644 boot_loader/bl_ssi.h create mode 100644 boot_loader/bl_startup_ccs.s create mode 100644 boot_loader/bl_startup_ewarm.S create mode 100644 boot_loader/bl_startup_gcc.S create mode 100644 boot_loader/bl_startup_rvmdk.S create mode 100644 boot_loader/bl_startup_sourcerygxx.S create mode 100644 boot_loader/bl_uart.c create mode 100644 boot_loader/bl_uart.h create mode 100644 boot_loader/bl_usb.c create mode 100644 boot_loader/bl_usbfuncs.c create mode 100644 boot_loader/bl_usbfuncs.h create mode 100644 boot_loader/readme.txt create mode 100644 boot_loader/uip-conf.h create mode 100644 boot_loader/usbdfu.h create mode 100644 nfclib/debug.h create mode 100644 nfclib/directmode.c create mode 100644 nfclib/directmode.h create mode 100644 nfclib/iso14443-4.c create mode 100644 nfclib/iso14443-4.h create mode 100644 nfclib/iso14443a.c create mode 100644 nfclib/iso14443a.h create mode 100644 nfclib/iso14443b.c create mode 100644 nfclib/iso14443b.h create mode 100644 nfclib/iso15693.c create mode 100644 nfclib/iso15693.h create mode 100644 nfclib/llcp.c create mode 100644 nfclib/llcp.h create mode 100644 nfclib/nfc.c create mode 100644 nfclib/nfc.h create mode 100644 nfclib/nfc_dep.c create mode 100644 nfclib/nfc_dep.h create mode 100644 nfclib/nfc_f.c create mode 100644 nfclib/nfc_f.h create mode 100644 nfclib/nfc_p2p.c create mode 100644 nfclib/nfc_p2p.h create mode 100644 nfclib/snep.c create mode 100644 nfclib/snep.h create mode 100644 nfclib/ssitrf79x0.c create mode 100644 nfclib/ssitrf79x0.h create mode 100644 nfclib/trf79x0.c create mode 100644 nfclib/trf79x0.h create mode 100644 nfclib/trf79x0_hw_example.h create mode 100644 nfclib/types.h create mode 100644 sensorlib/Makefile create mode 100644 sensorlib/ak8963.c create mode 100644 sensorlib/ak8963.h create mode 100644 sensorlib/ak8975.c create mode 100644 sensorlib/ak8975.h create mode 100644 sensorlib/bmp180.c create mode 100644 sensorlib/bmp180.h create mode 100644 sensorlib/bq27510g3.c create mode 100644 sensorlib/bq27510g3.h create mode 100644 sensorlib/ccs/.ccsproject create mode 100644 sensorlib/ccs/.cproject create mode 100644 sensorlib/ccs/.project create mode 100644 sensorlib/ccs/.settings/org.eclipse.cdt.codan.core.prefs create mode 100644 sensorlib/ccs/Debug/sensorlib.lib create mode 100644 sensorlib/ccs/macros.ini_initial create mode 100644 sensorlib/cm3218.c create mode 100644 sensorlib/cm3218.h create mode 100644 sensorlib/comp_dcm.c create mode 100644 sensorlib/comp_dcm.h create mode 100644 sensorlib/ewarm/Exe/sensorlib.a create mode 100644 sensorlib/gcc/libsensor.a create mode 100644 sensorlib/hw_ak8963.h create mode 100644 sensorlib/hw_ak8975.h create mode 100644 sensorlib/hw_bmp180.h create mode 100644 sensorlib/hw_bq27510g3.h create mode 100644 sensorlib/hw_cm3218.h create mode 100644 sensorlib/hw_isl29023.h create mode 100644 sensorlib/hw_kxti9.h create mode 100644 sensorlib/hw_l3gd20h.h create mode 100644 sensorlib/hw_lsm303d.h create mode 100644 sensorlib/hw_lsm303dlhc.h create mode 100644 sensorlib/hw_mpu6050.h create mode 100644 sensorlib/hw_mpu9150.h create mode 100644 sensorlib/hw_sht21.h create mode 100644 sensorlib/hw_tmp006.h create mode 100644 sensorlib/hw_tmp100.h create mode 100644 sensorlib/i2cm_drv.c create mode 100644 sensorlib/i2cm_drv.h create mode 100644 sensorlib/isl29023.c create mode 100644 sensorlib/isl29023.h create mode 100644 sensorlib/kxti9.c create mode 100644 sensorlib/kxti9.h create mode 100644 sensorlib/l3gd20h.c create mode 100644 sensorlib/l3gd20h.h create mode 100644 sensorlib/lsm303d.c create mode 100644 sensorlib/lsm303d.h create mode 100644 sensorlib/lsm303dlhc_accel.c create mode 100644 sensorlib/lsm303dlhc_accel.h create mode 100644 sensorlib/lsm303dlhc_mag.c create mode 100644 sensorlib/lsm303dlhc_mag.h create mode 100644 sensorlib/magneto.c create mode 100644 sensorlib/magneto.h create mode 100644 sensorlib/mpu6050.c create mode 100644 sensorlib/mpu6050.h create mode 100644 sensorlib/mpu9150.c create mode 100644 sensorlib/mpu9150.h create mode 100644 sensorlib/quaternion.c create mode 100644 sensorlib/quaternion.h create mode 100644 sensorlib/readme.txt create mode 100644 sensorlib/rvmdk/sensorlib.lib create mode 100644 sensorlib/sensorlib.ewp create mode 100644 sensorlib/sensorlib.uvopt create mode 100644 sensorlib/sensorlib.uvproj create mode 100644 sensorlib/sht21.c create mode 100644 sensorlib/sht21.h create mode 100644 sensorlib/tmp006.c create mode 100644 sensorlib/tmp006.h create mode 100644 sensorlib/tmp100.c create mode 100644 sensorlib/tmp100.h create mode 100644 sensorlib/vector.c create mode 100644 sensorlib/vector.h diff --git a/boot_loader/bl_autobaud.c b/boot_loader/bl_autobaud.c new file mode 100644 index 0000000..1600c71 --- /dev/null +++ b/boot_loader/bl_autobaud.c @@ -0,0 +1,279 @@ +//***************************************************************************** +// +// bl_autobaud.c - Automatic baud rate detection code. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "inc/hw_gpio.h" +#include "inc/hw_memmap.h" +#include "inc/hw_nvic.h" +#include "inc/hw_types.h" +#include "bl_config.h" +#include "boot_loader/bl_uart.h" + +//***************************************************************************** +// +// If using auto-baud, make sure that the data buffer is large enough. +// +//***************************************************************************** +#if defined(UART_ENABLE_UPDATE) && defined(UART_AUTOBAUD) && (BUFFER_SIZE < 20) +#error ERROR: BUFFER_SIZE must be >= 20! +#endif + +//***************************************************************************** +// +//! \addtogroup bl_autobaud_api +//! @{ +// +//***************************************************************************** +#if defined(UART_ENABLE_UPDATE) && defined(UART_AUTOBAUD) || defined(DOXYGEN) + +//***************************************************************************** +// +// This define holds the multiplier for the pulse detection algorithm. The +// value is used to generate a fractional difference detection of +// 1 / PULSE_DETECTION_MULT. +// +//***************************************************************************** +#define PULSE_DETECTION_MULT 3 + +//***************************************************************************** +// +// This define holds the minimum number of edges to successfully sync to a +// pattern of 2 bytes. +// +//***************************************************************************** +#define MIN_EDGE_COUNT 18 + +//***************************************************************************** +// +// This global holds the number of edges that have been stored in the global +// buffer g_pui32DataBuffer. +// +//***************************************************************************** +static volatile uint32_t g_ui32TickIndex; + +//***************************************************************************** +// +// The data buffer that is used for receiving packets is used to hold the edge +// times during auto-baud. The buffer is not used for receiving packets while +// auto-baud is in progress, so this does not present problems. +// +//***************************************************************************** +extern uint32_t g_pui32DataBuffer[]; + +//***************************************************************************** +// +//! Handles the UART Rx GPIO interrupt. +//! +//! When an edge is detected on the UART Rx pin, this function is called to +//! save the time of the edge. These times are later used to determine the +//! ratio of the UART baud rate to the processor clock rate. +//! +//! \return None. +// +//***************************************************************************** +void +GPIOIntHandler(void) +{ + uint32_t ui32Temp; + + // + // Clear the GPIO interrupt source. + // + HWREG(GPIO_PORTA_BASE + GPIO_O_ICR) = UART_RX; + + // + // While we still have space in our buffer, store the current system tick + // count and return from interrupt. + // + if(g_ui32TickIndex < 20) + { + ui32Temp = HWREG(NVIC_ST_CURRENT); + g_pui32DataBuffer[g_ui32TickIndex++] = ui32Temp; + } +} + +//***************************************************************************** +// +//! Performs auto-baud on the UART port. +//! +//! \param pui32Ratio is the ratio of the processor's crystal frequency to the +//! baud rate being used by the UART port for communications. +//! +//! This function attempts to synchronize to the updater program that is trying +//! to communicate with the boot loader. The UART port is monitored for edges +//! using interrupts. Once enough edges are detected, the boot loader +//! determines the ratio of baud rate and crystal frequency needed to program +//! the UART. +//! +//! \return Returns a value of 0 to indicate that this call successfully +//! synchronized with the other device communicating over the UART, and a +//! negative value to indicate that this function did not successfully +//! synchronize with the other UART device. +// +//***************************************************************************** +int +UARTAutoBaud(uint32_t *pui32Ratio) +{ + int32_t i32Pulse, i32ValidPulses, i32Temp, i32Total; + volatile int32_t i32Delay; + + // + // Configure and enable SysTick. Set the reload value to the maximum; + // there are only 24 bits in the register but loading 32 bits of ones is + // more efficient. + // + HWREG(NVIC_ST_RELOAD) = 0xffffffff; + HWREG(NVIC_ST_CTRL) = NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_ENABLE; + + // + // Reset the counters that control the pulse detection. + // + i32ValidPulses = 0; + i32Total = 0; + g_ui32TickIndex = 0; + + // + // Set the pad(s) for standard push-pull operation. + // + HWREG(GPIO_PORTA_BASE + GPIO_O_PUR) |= UART_RX; + HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= UART_RX; + + // + // Interrupt on both edges. + // + HWREG(GPIO_PORTA_BASE + GPIO_O_IBE) = UART_RX; + + // + // Clear out all of the gpio interrupts in this register. + // + HWREG(GPIO_PORTA_BASE + GPIO_O_ICR) = UART_RX; + + // + // Enable the GPIO pin corresponding to the UART RX pin. + // + HWREG(GPIO_PORTA_BASE + GPIO_O_IM) = UART_RX; + + // + // Enable GPIOA Interrupt. + // + HWREG(NVIC_EN0) = 1; + + // + // Wait for MIN_EDGE_COUNT to pass to collect enough edges. + // + while(g_ui32TickIndex < MIN_EDGE_COUNT) + { + } + + // + // Disable GPIOA Interrupt. + // + HWREG(NVIC_DIS0) = 1; + + // + // Calculate the pulse widths from the array of tick times. + // + for(i32Pulse = 0; i32Pulse < (MIN_EDGE_COUNT - 1); i32Pulse++) + { + i32Temp = (((int32_t)g_pui32DataBuffer[i32Pulse] - + (int32_t)g_pui32DataBuffer[i32Pulse + 1]) & 0x00ffffff); + g_pui32DataBuffer[i32Pulse] = i32Temp; + } + + // + // This loops handles checking for consecutive pulses that have pulse + // widths that are within an acceptable margin. + // + for(i32Pulse = 0; i32Pulse < (MIN_EDGE_COUNT - 1); i32Pulse++) + { + // + // Calculate the absolute difference between two consecutive pulses. + // + i32Temp = (int32_t)g_pui32DataBuffer[i32Pulse]; + i32Temp -= (int32_t)g_pui32DataBuffer[i32Pulse + 1]; + if(i32Temp < 0) + { + i32Temp *= -1; + } + + // + // This pulse detection code uses the following algorithm: + // If the following is true then we have consecutive acceptable pulses + // abs(Pulse[n] - Pulse[n + 1]) < Pulse[n + 1] / PULSE_DETECTION_MULT + // or + // PULSE_DETECTION_MULT * abs(Pulse[n] - Pulse[n + 1]) < Pulse[n + 1] + // + if((i32Temp * PULSE_DETECTION_MULT) < + (int32_t)g_pui32DataBuffer[i32Pulse + 1]) + { + i32Total += (int32_t)g_pui32DataBuffer[i32Pulse]; + i32ValidPulses++; + } + else + { + i32ValidPulses = 0; + i32Total = 0; + } + + // + // Once we have 7 pulses calculate the ratio needed to program the + // UART. + // + if(i32ValidPulses == 7) + { + // + // Add in the last pulse and calculate the ratio. + // + i32Total += (int32_t)g_pui32DataBuffer[i32Pulse]; + *pui32Ratio = i32Total >> 1; + + // + // Wait for at least 2 UART clocks since we only wait for 18 of 20 + // that are coming from the host. If we don't wait, we can turn + // on the UART while the last two pulses come down. + // + for(i32Delay = i32Total; i32Delay; i32Delay--) + { + } + + // + // Indicate a successful auto baud operation. + // + return(0); + } + } + + // + // Automatic baud rate detection failed. + // + return(-1); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif diff --git a/boot_loader/bl_can.c b/boot_loader/bl_can.c new file mode 100644 index 0000000..9cf5c0b --- /dev/null +++ b/boot_loader/bl_can.c @@ -0,0 +1,1403 @@ +//***************************************************************************** +// +// bl_can.c - Functions to transfer data via the CAN port. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "inc/hw_can.h" +#include "inc/hw_gpio.h" +#include "inc/hw_memmap.h" +#include "inc/hw_nvic.h" +#include "inc/hw_flash.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "inc/hw_uart.h" +#include "bl_config.h" +#include "boot_loader/bl_can.h" +#include "boot_loader/bl_can_timing.h" +#include "boot_loader/bl_check.h" +#include "boot_loader/bl_crystal.h" +#include "boot_loader/bl_flash.h" +#include "boot_loader/bl_hooks.h" +#include "boot_loader/bl_uart.h" + +//***************************************************************************** +// +//! \addtogroup bl_can_api +//! @{ +// +//***************************************************************************** +#if defined(CAN_ENABLE_UPDATE) || defined(DOXYGEN) + +//***************************************************************************** +// +// The results that can be returned by the CAN APIs. +// +//***************************************************************************** +#define CAN_CMD_SUCCESS 0x00 +#define CAN_CMD_FAIL 0x01 + +//***************************************************************************** +// +// Macros used to generate correct pin definitions. +// +//***************************************************************************** +#define CAN_RX_PIN_M (1 << CAN_RX_PIN) +#define CAN_TX_PIN_M (1 << CAN_TX_PIN) + +//***************************************************************************** +// +// Convenience macros for accessing CAN registers. +// +//***************************************************************************** +#define CANRegWrite(ui32Address, ui32Value) \ + HWREG(ui32Address) = ui32Value + +#define CANRegRead(ui32Address) \ + HWREG(ui32Address) + +//***************************************************************************** +// +// The message object number and index to the local message object memory to +// use when accessing the messages. +// +//***************************************************************************** +#define MSG_OBJ_BCAST_RX_ID 1 +#define MSG_OBJ_BCAST_TX_ID 2 + +//***************************************************************************** +// +// A prototype for the function (in the startup code) for calling the +// application. +// +//***************************************************************************** +extern void StartApplication(void); + +//***************************************************************************** +// +// A prototype for the function (in the startup code) for a predictable length +// delay. +// +//***************************************************************************** +extern void Delay(uint32_t ui32Count); + +//***************************************************************************** +// +// Holds the current address to write to when data is received via the Send +// Data Command. +// +//***************************************************************************** +static uint32_t g_ui32TransferAddress; + +//***************************************************************************** +// +// Holds the remaining bytes expected to be received. +// +//***************************************************************************** +static uint32_t g_ui32TransferSize; + +//***************************************************************************** +// +// The buffer used to receive data from the update. +// +//***************************************************************************** +static uint8_t g_pui8CommandBuffer[8]; + +//***************************************************************************** +// +// These globals are used to store the first two words to prevent a partial +// image from being booted. +// +//***************************************************************************** +static uint32_t g_ui32StartValues[2]; +static uint32_t g_ui32StartSize; +static uint32_t g_ui32StartAddress; + +//***************************************************************************** +// +// The active interface when the UART bridge is enabled. +// +//***************************************************************************** +#ifdef CAN_UART_BRIDGE +static uint32_t g_ui32Interface; +#define IFACE_UNKNOWN 0 +#define IFACE_CAN 1 +#define IFACE_UART 2 +#endif + +//***************************************************************************** +// +//! Initializes the CAN controller after reset. +//! +//! After reset, the CAN controller is left in the disabled state. However, +//! the memory used for message objects contains undefined values and must be +//! cleared prior to enabling the CAN controller the first time. This prevents +//! unwanted transmission or reception of data before the message objects are +//! configured. This function must be called before enabling the controller +//! the first time. +//! +//! \return None. +// +//***************************************************************************** +static void +CANInit(void) +{ + int iMsg; + + // + // Place CAN controller in init state, regardless of previous state. This + // will put the controller in idle, and allow the message object RAM to be + // programmed. + // + CANRegWrite(CAN0_BASE + CAN_O_CTL, CAN_CTL_INIT | CAN_CTL_CCE); + + // + // Loop through to program all 32 message objects + // + for(iMsg = 1; iMsg <= 32; iMsg++) + { + // + // Wait for busy bit to clear. + // + while(CANRegRead(CAN0_BASE + CAN_O_IF1CRQ) & CAN_IF1CRQ_BUSY) + { + } + + // + // Clear the message value bit in the arbitration register. This + // indicates the message is not valid and is a "safe" condition to + // leave the message object. + // + CANRegWrite(CAN0_BASE + CAN_O_IF1CMSK, + CAN_IF1CMSK_WRNRD | CAN_IF1CMSK_ARB | CAN_IF1CMSK_CONTROL); + CANRegWrite(CAN0_BASE + CAN_O_IF1ARB2, 0); + CANRegWrite(CAN0_BASE + CAN_O_IF1MCTL, 0); + + // + // Initiate programming of the message object + // + CANRegWrite(CAN0_BASE + CAN_O_IF1CRQ, iMsg); + } + + // + // Acknowledge any pending status interrupts. + // + CANRegRead(CAN0_BASE + CAN_O_STS); +} + +//***************************************************************************** +// +//! This function configures the message object used to receive commands. +//! +//! This function configures the message object used to receive all firmware +//! update messages. This will not actually read the data from the message it +//! is used to prepare the message object to receive the data when it is sent. +//! +//! \return None. +// +//***************************************************************************** +static void +CANMessageSetRx(void) +{ + uint16_t ui16CmdMaskReg; + uint16_t ui16MaskReg[2]; + uint16_t ui16ArbReg[2]; + uint16_t ui16MsgCtrl; + + // + // Wait for busy bit to clear + // + while(CANRegRead(CAN0_BASE + CAN_O_IF1CRQ) & CAN_IF1CRQ_BUSY) + { + } + + // + // This is always a write to the Message object as this call is setting a + // message object. This call will also always set all size bits so it sets + // both data bits. The call will use the CONTROL register to set control + // bits so this bit needs to be set as well. + // + // Set the MASK bit so that this gets transferred to the Message Object. + // Set the Arb bit so that this gets transferred to the Message object. + // + ui16CmdMaskReg = (CAN_IF1CMSK_WRNRD | CAN_IF1CMSK_DATAA | + CAN_IF1CMSK_DATAB | CAN_IF1CMSK_CONTROL | + CAN_IF1CMSK_MASK | CAN_IF1CMSK_ARB); + + // + // Set the UMASK bit to enable using the mask register. + // Set the data length since this is set for all transfers. This is also a + // single transfer and not a FIFO transfer so set EOB bit. + // + ui16MsgCtrl = CAN_IF1MCTL_UMASK | CAN_IF1MCTL_EOB; + + // + // Configure the Mask Registers. + // + // + // Set the 29 bits of Identifier mask that were requested. + // + ui16MaskReg[0] = (uint16_t)LM_API_UPD; + + // + // If the caller wants to filter on the extended ID bit then set it. + // + ui16MaskReg[1] = + (uint16_t)(CAN_IF1MSK2_MXTD | (LM_API_UPD >> 16)); + + // + // Set the 29 bit version of the Identifier for this message object. + // Mark the message as valid and set the extended ID bit. + // + ui16ArbReg[0] = LM_API_UPD & CAN_IF1ARB1_ID_M; + ui16ArbReg[1] = (((LM_API_UPD >> 16) & CAN_IF1ARB2_ID_M) | + (CAN_IF1ARB2_MSGVAL | CAN_IF1ARB2_XTD)); + + // + // Write out the registers to program the message object. + // + CANRegWrite(CAN0_BASE + CAN_O_IF1CMSK, ui16CmdMaskReg); + CANRegWrite(CAN0_BASE + CAN_O_IF1MSK1, ui16MaskReg[0]); + CANRegWrite(CAN0_BASE + CAN_O_IF1MSK2, ui16MaskReg[1]); + CANRegWrite(CAN0_BASE + CAN_O_IF1ARB1, ui16ArbReg[0]); + CANRegWrite(CAN0_BASE + CAN_O_IF1ARB2, ui16ArbReg[1]); + CANRegWrite(CAN0_BASE + CAN_O_IF1MCTL, ui16MsgCtrl); + + // + // Transfer the message object to the message object specific by + // MSG_OBJ_BCAST_RX_ID. + // + CANRegWrite(CAN0_BASE + CAN_O_IF1CRQ, + MSG_OBJ_BCAST_RX_ID & CAN_IF1CRQ_MNUM_M); +} + +//***************************************************************************** +// +//! This function reads data from the receive message object. +//! +//! \param pui8Data is a pointer to the buffer to store the data read from the +//! CAN controller. +//! \param pui32MsgID is a pointer to the ID that was received with the data. +//! +//! This function will reads and acknowledges the data read from the message +//! object used to receive all CAN firmware update messages. It will also +//! return the message identifier as this holds the API number that was +//! attached to the data. This message identifier should be one of the +//! LM_API_UPD_* definitions. +//! +//! \return The number of valid bytes returned in the \e pui8Data buffer or +//! 0xffffffff if data was overwritten in the buffer. +// +//***************************************************************************** +static uint32_t +CANMessageGetRx(uint8_t *pui8Data, uint32_t *pui32MsgID) +{ + uint16_t ui16CmdMaskReg; + uint16_t ui16ArbReg0, ui16ArbReg1; + uint16_t ui16MsgCtrl; + uint32_t ui32Bytes; + uint16_t *pui16Data; + + // + // This is always a read to the Message object as this call is setting a + // message object. + // Clear a pending interrupt and new data in a message object. + // + ui16CmdMaskReg = (CAN_IF2CMSK_DATAA | CAN_IF2CMSK_DATAB | + CAN_IF1CMSK_CONTROL | CAN_IF2CMSK_CLRINTPND | + CAN_IF2CMSK_ARB); + + // + // Set up the request for data from the message object. + // + CANRegWrite(CAN0_BASE + CAN_O_IF2CMSK, ui16CmdMaskReg); + + // + // Transfer the message object to the message object specific by + // MSG_OBJ_BCAST_RX_ID. + // + CANRegWrite(CAN0_BASE + CAN_O_IF2CRQ, + MSG_OBJ_BCAST_RX_ID & CAN_IF1CRQ_MNUM_M); + + // + // Wait for busy bit to clear + // + while(CANRegRead(CAN0_BASE + CAN_O_IF2CRQ) & CAN_IF1CRQ_BUSY) + { + } + + // + // Read out the IF Registers. + // + ui16ArbReg0 = CANRegRead(CAN0_BASE + CAN_O_IF2ARB1); + ui16ArbReg1 = CANRegRead(CAN0_BASE + CAN_O_IF2ARB2); + ui16MsgCtrl = CANRegRead(CAN0_BASE + CAN_O_IF2MCTL); + + // + // Set the 29 bit version of the Identifier for this message object. + // + *pui32MsgID = ((ui16ArbReg1 & CAN_IF1ARB2_ID_M) << 16) | ui16ArbReg0; + + // + // See if there is new data available. + // + if((ui16MsgCtrl & (CAN_IF1MCTL_NEWDAT | CAN_IF1MCTL_MSGLST)) == + CAN_IF1MCTL_NEWDAT) + { + // + // Get the amount of data needed to be read. + // + ui32Bytes = ui16MsgCtrl & CAN_IF1MCTL_DLC_M; + + // + // Read out the data from the CAN registers 16 bits at a time. + // + pui16Data = (uint16_t *)pui8Data; + + pui16Data[0] = CANRegRead(CAN0_BASE + CAN_O_IF2DA1); + pui16Data[1] = CANRegRead(CAN0_BASE + CAN_O_IF2DA2); + pui16Data[2] = CANRegRead(CAN0_BASE + CAN_O_IF2DB1); + pui16Data[3] = CANRegRead(CAN0_BASE + CAN_O_IF2DB2); + + // + // Now clear out the new data flag. + // + CANRegWrite(CAN0_BASE + CAN_O_IF2CMSK, CAN_IF1CMSK_NEWDAT); + + // + // Transfer the message object to the message object specific by + // MSG_OBJ_BCAST_RX_ID. + // + CANRegWrite(CAN0_BASE + CAN_O_IF2CRQ, MSG_OBJ_BCAST_RX_ID); + + // + // Wait for busy bit to clear + // + while(CANRegRead(CAN0_BASE + CAN_O_IF2CRQ) & CAN_IF2CRQ_BUSY) + { + } + } + else + { + // + // Data was lost so inform the caller. + // + ui32Bytes = 0xffffffff; + } + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function sends data using the transmit message object. +//! +//! \param ui32Id is the ID to use with this message. +//! \param pui8Data is a pointer to the buffer with the data to be sent. +//! \param ui32Size is the number of bytes to send and should not be more than +//! 8 bytes. +//! +//! This function will reads and acknowledges the data read from the message +//! object used to receive all CAN firmware update messages. It will also +//! return the message identifier as this holds the API number that was +//! attached to the data. This message identifier should be one of the +//! LM_API_UPD_* definitions. +//! +//! \return None. +// +//***************************************************************************** +static void +CANMessageSetTx(uint32_t ui32Id, const uint8_t *pui8Data, uint32_t ui32Size) +{ + uint16_t ui16CmdMaskReg; + uint16_t ui16ArbReg0, ui16ArbReg1; + uint16_t ui16MsgCtrl; + uint16_t *pui16Data; + + // + // Wait for busy bit to clear + // + while(CANRegRead(CAN0_BASE + CAN_O_IF1CRQ) & CAN_IF1CRQ_BUSY) + { + } + + // + // This is always a write to the Message object as this call is setting a + // message object. This call will also always set all size bits so it sets + // both data bits. The call will use the CONTROL register to set control + // bits so this bit needs to be set as well. + // + ui16CmdMaskReg = (CAN_IF1CMSK_WRNRD | CAN_IF1CMSK_DATAA | + CAN_IF1CMSK_DATAB | CAN_IF1CMSK_CONTROL | + CAN_IF1CMSK_ARB); + + // + // Set the 29 bit version of the Identifier for this message object. + // + ui16ArbReg0 = ui32Id & CAN_IF1ARB1_ID_M; + + // + // Mark the message as valid and set the extended ID bit. + // + ui16ArbReg1 = (((ui32Id >> 16) & CAN_IF1ARB2_ID_M) | + (CAN_IF1ARB2_DIR | CAN_IF1ARB2_MSGVAL | CAN_IF1ARB2_XTD)); + + // + // Set the TXRQST bit and the reset the rest of the register. + // Set the data length since this is set for all transfers. This is also a + // single transfer and not a FIFO transfer so set EOB bit. + // + // + ui16MsgCtrl = (CAN_IF1MCTL_TXRQST | CAN_IF1MCTL_EOB | + (ui32Size & CAN_IF1MCTL_DLC_M)); + + pui16Data = (uint16_t *)pui8Data; + + // + // Write the data out to the CAN Data registers if needed. + // + CANRegWrite(CAN0_BASE + CAN_O_IF1DA1, pui16Data[0]); + CANRegWrite(CAN0_BASE + CAN_O_IF1DA2, pui16Data[1]); + CANRegWrite(CAN0_BASE + CAN_O_IF1DB1, pui16Data[2]); + CANRegWrite(CAN0_BASE + CAN_O_IF1DB2, pui16Data[3]); + + // + // Write out the registers to program the message object. + // + CANRegWrite(CAN0_BASE + CAN_O_IF1CMSK, ui16CmdMaskReg); + CANRegWrite(CAN0_BASE + CAN_O_IF1ARB1, ui16ArbReg0); + CANRegWrite(CAN0_BASE + CAN_O_IF1ARB2, ui16ArbReg1); + CANRegWrite(CAN0_BASE + CAN_O_IF1MCTL, ui16MsgCtrl); + + // + // Transfer the message object to the message object specifiec by + // MSG_OBJ_BCAST_RX_ID. + // + CANRegWrite(CAN0_BASE + CAN_O_IF1CRQ, + (MSG_OBJ_BCAST_TX_ID) & CAN_IF1CRQ_MNUM_M); +} + +//***************************************************************************** +// +//! Configures the CAN interface. +//! +//! \param ui32SetTiming determines if the CAN bit timing should be configured. +//! +//! This function configures the CAN controller, preparing it for use by +//! the boot loader. If the \e ui32SetTiming parameter is 0, the bit timing +//! for the CAN bus will be left alone. This occurs when the boot loader was +//! entered from a running application that already has configured the timing +//! for the system. When \e ui32SetTiming is non-zero the bit timing will be +//! set to the defaults defined in the bl_config.h file in the +//! project. +//! +//! \return None. +// +//***************************************************************************** +static void +ConfigureCANInterface(uint32_t ui32SetTiming) +{ + // + // Reset the state of all the message object and the state of the CAN + // module to a known state. + // + CANInit(); + + // + // If a device identifier was specified then this was due to an update from + // a running CAN application so don't change the CAN bit timing. + // + if(ui32SetTiming != 0) + { + // + // Set the bit fields of the bit timing register according to the + // parms. + // + CANRegWrite(CAN0_BASE + CAN_O_BIT, CAN_BIT_TIMING); + + // + // Set the divider upper bits in the extension register. + // + CANRegWrite(CAN0_BASE + CAN_O_BRPE, 0); + } + + // + // Take the CAN0 device out of INIT state. + // + CANRegWrite(CAN0_BASE + CAN_O_CTL, 0); + + // + // Configure the broadcast receive message object. + // + CANMessageSetRx(); +} + +//***************************************************************************** +// +// Reads the next packet that is sent to the boot loader. +// +//***************************************************************************** +static uint32_t +PacketRead(uint8_t *pui8Data, uint32_t *pui32Size) +{ + uint32_t ui32MsgID; + +#ifdef CAN_UART_BRIDGE + uint32_t ui32Size, ui32Length, ui32Mode, ui32Char; + uint8_t pui8Buffer[12]; + + // + // Initialize the size and length of the packet. + // + ui32Length = 0; + ui32Size = 0; + + // + // If no interface has been determined then wait for either CAN or UART + // data until either responds. + // + if(g_ui32Interface == IFACE_UNKNOWN) + { + // + // Wait for CAN or UART data. + // + while((CANRegRead(CAN0_BASE + CAN_O_NWDA1) == 0) && + ((HWREG(UART0_BASE + UART_O_FR) & UART_FR_RXFE) == UART_FR_RXFE)) + { + } + + // + // If the UART FIFO was empty then the loop exited due to a CAN + // message. + // + if((HWREG(UART0_BASE + UART_O_FR) & UART_FR_RXFE) == UART_FR_RXFE) + { + g_ui32Interface = IFACE_CAN; + } + else + { + // + // The UART FIFO was not empty so the UART interface was used. + // + g_ui32Interface = IFACE_UART; + } + } + + // + // Read a data packet from the CAN controller. + // + if(g_ui32Interface == IFACE_CAN) + { +#endif + // + // Wait until a packet has been received. + // + while(CANRegRead(CAN0_BASE + CAN_O_NWDA1) == 0) + { + } + + // + // Read the packet. + // + *pui32Size = CANMessageGetRx(pui8Data, &ui32MsgID); +#ifdef CAN_UART_BRIDGE + } + else + { + // + // Read a data packet from the UART controller. + // + ui32Mode = 0; + + while(1) + { + // + // Wait until a char is available. + // + while(HWREG(UART0_BASE + UART_O_FR) & UART_FR_RXFE) + { + } + + // + // Now get the char. + // + ui32Char = HWREG(UART0_BASE + UART_O_DR); + + if(ui32Char == 0xff) + { + ui32Mode = 1; + ui32Length = 0; + } + else if(ui32Mode == 1) + { + if(ui32Char > 12) + { + ui32Mode = 0; + } + else + { + ui32Size = ui32Char; + ui32Mode = 2; + } + } + else if(ui32Mode == 3) + { + if(ui32Char == 0xfe) + { + pui8Buffer[ui32Length++] = 0xff; + ui32Mode = 2; + } + else if(ui32Char == 0xfd) + { + pui8Buffer[ui32Length++] = 0xfe; + ui32Mode = 2; + } + else + { + ui32Mode = 0; + } + } + else if(ui32Mode == 2) + { + if(ui32Char == 0xfe) + { + ui32Mode = 3; + } + else + { + pui8Buffer[ui32Length++] = ui32Char; + } + } + + if((ui32Length == ui32Size) && (ui32Mode == 2)) + { + ui32MsgID = *(uint32_t *)pui8Buffer; + + if((ui32MsgID & (CAN_MSGID_MFR_M | CAN_MSGID_DTYPE_M)) == + LM_API_UPD) + { + *(uint32_t *)pui8Data = + *(uint32_t *)(pui8Buffer + 4); + *(uint32_t *)(pui8Data + 4) = + *(uint32_t *)(pui8Buffer + 8); + *pui32Size = ui32Size - 4; + break; + } + } + } + } +#endif + + // + // Return the message ID of the packet that was received. + // + return(ui32MsgID); +} + +//***************************************************************************** +// +// This function writes out an individual character over the UART and +// handles sending out special sequences for handling 0xff and 0xfe values. +// +//***************************************************************************** +#ifdef CAN_UART_BRIDGE +static void +UARTBridgeWrite(uint32_t ui32Char) +{ + // + // See if the character being sent is 0xff. + // + if(ui32Char == 0xff) + { + // + // Send 0xfe 0xfe, the escaped version of 0xff. A sign extended + // version of 0xfe is used to avoid the check below for 0xfe, thereby + // avoiding an infinite loop. Only the lower 8 bits are actually sent, + // so 0xfe is what is actually transmitted. + // + UARTBridgeWrite(0xfffffffe); + UARTBridgeWrite(0xfffffffe); + } + + // + // Otherwise, see if the character being sent is 0xfe. + // + else if(ui32Char == 0xfe) + { + // + // Send 0xfe 0xfd, the escaped version of 0xfe. A sign extended + // version of 0xfe is used to avoid the check above for 0xfe, thereby + // avoiding an infinite loop. Only the lower 8 bits are actually sent, + // so 0xfe is what is actually transmitted. + // + UARTBridgeWrite(0xfffffffe); + UARTBridgeWrite(0xfd); + } + + // + // Otherwise, simply send this character. + // + else + { + // + // Wait until space is available in the UART transmit FIFO. + // + while(HWREG(UART0_BASE + UART_O_FR) & UART_FR_TXFF) + { + } + + // + // Send the char. + // + HWREG(UART0_BASE + UART_O_DR) = ui32Char & 0xff; + } +} +#endif + +//***************************************************************************** +// +// Sends a packet to the controller that is communicating with the boot loader. +// +//***************************************************************************** +static void +PacketWrite(uint32_t ui32Id, const uint8_t *pui8Data, uint32_t ui32Size) +{ + uint32_t ui32Idx; + +#ifdef CAN_UART_BRIDGE + // + // Check if the boot loader is in CAN mode. + // + if(g_ui32Interface == IFACE_CAN) + { +#endif + // + // Wait until the previous packet has been sent, providing a time out so + // that the boot loader does not hang here. + // + for(ui32Idx = 1000; + (ui32Idx != 0) && (CANRegRead(CAN0_BASE + CAN_O_TXRQ1) != 0); + ui32Idx--) + { + } + + // + // If the previous packet was sent, then send this packet. + // + if(ui32Idx != 0) + { + CANMessageSetTx(ui32Id, pui8Data, ui32Size); + } +#ifdef CAN_UART_BRIDGE + } + else + { + // + // The boot loader is in UART modes so write the packet using the UART + // functions. Write the start pattern followed by the size, and the ID. + // + UARTBridgeWrite(0xffffffff); + UARTBridgeWrite(ui32Size + 4); + UARTBridgeWrite(ui32Id & 0xff); + UARTBridgeWrite((ui32Id >> 8) & 0xff); + UARTBridgeWrite((ui32Id >> 16) & 0xff); + UARTBridgeWrite((ui32Id >> 24) & 0xff); + + // + // Now write out the remaining data bytes. + // + while(ui32Size--) + { + UARTBridgeWrite(*pui8Data++); + } + } +#endif +} + +//***************************************************************************** +// +//! This is the main routine for handling updating over CAN. +//! +//! This function accepts boot loader commands over CAN to perform a firmware +//! update over the CAN bus. This function assumes that the CAN bus timing +//! and message objects have been configured elsewhere. +//! +//! \return None. +// +//***************************************************************************** +void +UpdaterCAN(void) +{ + uint32_t ui32Bytes; + uint32_t ui32Cmd; + uint32_t ui32FlashSize; + uint32_t ui32Temp; + uint8_t ui8Status; + +#ifdef ENABLE_UPDATE_CHECK + // + // Check the application is valid and check the pin to see if an update is + // being requested. + // + if(g_ui32Forced == 1) + { + // + // Send out the CAN request. + // +#ifdef CAN_UART_BRIDGE + g_ui32Interface = IFACE_CAN; +#endif + PacketWrite(LM_API_UPD_REQUEST, 0, 0); + + // + // Send out the UART request. + // +#ifdef CAN_UART_BRIDGE + g_ui32Interface = IFACE_UART; + PacketWrite(LM_API_UPD_REQUEST, 0, 0); + g_ui32Interface = IFACE_UNKNOWN; +#endif + + // + // Wait only 50ms for the response and move on otherwise. + // + Delay(CRYSTAL_FREQ / 20); + + // + // Wait until a packet has been received. + // +#ifdef CAN_UART_BRIDGE + if((CANRegRead(CAN0_BASE + CAN_O_NWDA1) == 0) && + ((HWREG(UART0_BASE + UART_O_FR) & UART_FR_RXFE) == UART_FR_RXFE)) +#else + if(CANRegRead(CAN0_BASE + CAN_O_NWDA1) == 0) +#endif + { + // + // Call the application. + // + StartApplication(); + } + } +#endif + + // + // Loop forever processing packets. + // + while(1) + { + // + // Read the next packet. + // + ui32Bytes = 0; + ui32Cmd = PacketRead(g_pui8CommandBuffer, &ui32Bytes); + + // + // Handle this packet. + // + ui8Status = CAN_CMD_SUCCESS; + switch(ui32Cmd) + { + // + // This is an update request packet. + // + case LM_API_UPD_REQUEST: + { + // + // This packet is ignored (other than generating an ACK). + // + break; + } + + // + // This is a ping packet. + // + case LM_API_UPD_PING: + { + // + // This packet is ignored (other than generating an ACK). + // + break; + } + + // + // This is a reset packet. + // + case LM_API_UPD_RESET: + { + // + // Perform a software reset request. This will cause the + // microcontroller to reset; no further code will be executed. + // + HWREG(NVIC_APINT) = (NVIC_APINT_VECTKEY | + NVIC_APINT_SYSRESETREQ); + + // + // The microcontroller should have reset, so this should never + // be reached. Just in case, loop forever. + // + while(1) + { + } + } + + // + // This is a data packet. + // + case LM_API_UPD_SEND_DATA: + { + // + // If this is overwriting the boot loader then the application + // has already been erased so now erase the boot loader. + // + if(g_ui32TransferAddress == 0) + { + // + // Clear the flash access interrupt. + // + BL_FLASH_CL_ERR_FN_HOOK(); + + // + // Erase the application before the boot loader. + // + for(ui32Temp = 0; ui32Temp < APP_START_ADDRESS; + ui32Temp += FLASH_PAGE_SIZE) + { + // + // Erase this block. + // + BL_FLASH_ERASE_FN_HOOK(ui32Temp); + } + + // + // Return an error if an access violation occurred. + // + if(BL_FLASH_ERROR_FN_HOOK()) + { + // + // Setting g_ui32TransferSize to zero makes + // COMMAND_SEND_DATA fail to accept any more data. + // + g_ui32TransferSize = 0; + + // + // Indicate that the flash erase failed. + // + ui8Status = CAN_CMD_FAIL; + } + } + + // + // Check if there are any more bytes to receive. + // + if(g_ui32TransferSize >= ui32Bytes) + { + // + // Decrypt the data if required. + // +#ifdef BL_DECRYPT_FN_HOOK + BL_DECRYPT_FN_HOOK(g_pui8CommandBuffer, ui32Bytes); +#endif + + // + // Clear the flash access interrupt. + // + BL_FLASH_CL_ERR_FN_HOOK(); + + // + // Skip the first transfer. + // + if(g_ui32StartSize == g_ui32TransferSize) + { + g_ui32StartValues[0] = + *((uint32_t *)&g_pui8CommandBuffer[0]); + g_ui32StartValues[1] = + *((uint32_t *)&g_pui8CommandBuffer[4]); + } + else + { + // + // Loop over the words to program. + // + BL_FLASH_PROGRAM_FN_HOOK(g_ui32TransferAddress, + g_pui8CommandBuffer, + ui32Bytes); + } + + // + // Return an error if an access violation occurred. + // + if(BL_FLASH_ERROR_FN_HOOK()) + { + // + // Indicate that the flash programming failed. + // + ui8Status = CAN_CMD_FAIL; + } + else + { + // + // Now update the address to program. + // + g_ui32TransferSize -= ui32Bytes; + g_ui32TransferAddress += ui32Bytes; + + // + // If a progress hook function has been provided, call + // it here. + // +#ifdef BL_PROGRESS_FN_HOOK + BL_PROGRESS_FN_HOOK(g_ui32StartSize - + g_ui32TransferSize, + g_ui32StartSize); +#endif + } + } + else + { + // + // This indicates that too much data is being sent to the + // device. + // + ui8Status = CAN_CMD_FAIL; + } + + // + // If the last expected bytes were received then write out the + // first two words of the image to allow it to boot. + // + if(g_ui32TransferSize == 0) + { + // + // Loop over the words to program. + // + BL_FLASH_PROGRAM_FN_HOOK(g_ui32StartAddress, + (uint8_t *)&g_ui32StartValues, + 8); + + // + // If an end signal hook function has been provided, call + // it here since we have finished a download. + // +#ifdef BL_END_FN_HOOK + BL_END_FN_HOOK(); +#endif + } + break; + } + + // + // This is a start download packet. + // + case LM_API_UPD_DOWNLOAD: + { + // + // Get the application address and size from the packet data. + // + g_ui32TransferAddress = + *((uint32_t *)&g_pui8CommandBuffer[0]); + g_ui32TransferSize = *((uint32_t *)&g_pui8CommandBuffer[4]); + g_ui32StartSize = g_ui32TransferSize; + g_ui32StartAddress = g_ui32TransferAddress; + + // + // Check for a valid starting address and image size. + // + if(!BL_FLASH_AD_CHECK_FN_HOOK(g_ui32TransferAddress, + g_ui32TransferSize)) + { + // + // Set the code to an error to indicate that the last + // command failed. This informs the updater program + // that the download command failed. + // + ui8Status = CAN_CMD_FAIL; + + // + // This packet has been handled. + // + break; + } + + // + // Only erase the space that we need if we are not protecting + // the code, otherwise erase the entire flash. + // +#ifdef FLASH_CODE_PROTECTION + ui32FlashSize = BL_FLASH_SIZE_FN_HOOK(); +#ifdef FLASH_RSVD_SPACE + if((ui32FlashSize - FLASH_RSVD_SPACE) != g_ui32TransferAddress) + { + ui32FlashSize -= FLASH_RSVD_SPACE; + } +#endif +#else + ui32FlashSize = g_ui32TransferAddress + g_ui32TransferSize; +#endif + + // + // Clear the flash access interrupt. + // + BL_FLASH_CL_ERR_FN_HOOK(); + + // + // Leave the boot loader present until we start getting an + // image. + // + for(ui32Temp = g_ui32TransferAddress; ui32Temp < ui32FlashSize; + ui32Temp += FLASH_PAGE_SIZE) + { + // + // Erase this block. + // + BL_FLASH_ERASE_FN_HOOK(ui32Temp); + } + + // + // Return an error if an access violation occurred. + // + if(BL_FLASH_ERROR_FN_HOOK()) + { + ui8Status = CAN_CMD_FAIL; + } + + // + // See if the command was successful. + // + if(ui8Status != CAN_CMD_SUCCESS) + { + // + // Setting g_ui32TransferSize to zero makes + // COMMAND_SEND_DATA fail to accept any data. + // + g_ui32TransferSize = 0; + } +#ifdef BL_START_FN_HOOK + else + { + // + // If a start signal hook function has been provided, call + // it here since we are about to start a new download. + // + BL_START_FN_HOOK(); + } +#endif + + break; + } + + // + // This is an unknown packet. + // + default: + { + // + // Set the status to indicate a failure. + // + ui8Status = CAN_CMD_FAIL; + break; + } + } + + // + // Send an ACK packet in response to indicate that the packet was + // received. The status in the ACK data indicates if the command was + // successfully processed. + // + PacketWrite(LM_API_UPD_ACK, &ui8Status, 1); + } +} + +//***************************************************************************** +// +// Configures the UART used for CAN traffic bridging. +// +//***************************************************************************** +#ifdef CAN_UART_BRIDGE +void +ConfigureBridge(void) +{ + // + // Enable the GPIO module if necessary. + // +#if (CAN_RX_PERIPH != SYSCTL_RCGC2_GPIOA) && \ + (CAN_TX_PERIPH != SYSCTL_RCGC2_GPIOA) + HWREG(SYSCTL_RCGC2) |= SYSCTL_RCGC2_GPIOA; +#endif + + // + // Enable the UART module. + // + HWREG(SYSCTL_RCGC1) |= SYSCTL_RCGC1_UART0; + + // + // Enable the GPIO pins used for the UART. + // + HWREG(GPIO_PORTA_BASE + GPIO_O_AFSEL) |= 0x3; + HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= 0x03; + + // + // Configure the UART. + // + HWREG(UART0_BASE + UART_O_IBRD) = UART_BAUD_RATIO(115200) >> 6; + HWREG(UART0_BASE + UART_O_FBRD) = (UART_BAUD_RATIO(115200) & + UART_FBRD_DIVFRAC_M); + HWREG(UART0_BASE + UART_O_LCRH) = UART_LCRH_WLEN_8 | UART_LCRH_FEN; + HWREG(UART0_BASE + UART_O_CTL) = (UART_CTL_UARTEN | UART_CTL_TXE | + UART_CTL_RXE); +} +#endif + +//***************************************************************************** +// +//! This is the application entry point to the CAN updater. +//! +//! This function should only be entered from a running application and not +//! when running the boot loader with no application present. +//! +//! \return None. +// +//***************************************************************************** +void +AppUpdaterCAN(void) +{ + // + // If the boot loader is being called from the application the UART needs + // to be configured. + // +#ifdef CAN_UART_BRIDGE + ConfigureBridge(); +#endif + + // + // Configure the CAN controller but don't change the bit timing. + // + ConfigureCANInterface(0); + + // + // Call the main update routine. + // + UpdaterCAN(); +} + +//***************************************************************************** +// +//! Generic configuration is handled in this function. +//! +//! This function is called by the start up code to perform any configuration +//! necessary before calling the update routine. +//! +//! \return None. +// +//***************************************************************************** +void +ConfigureCAN(void) +{ +#ifdef CRYSTAL_FREQ + // + // Since the crystal frequency was specified, enable the main oscillator + // and clock the processor from it. + // + HWREG(SYSCTL_RCC) &= ~(SYSCTL_RCC_MOSCDIS); + + // + // Delay while the main oscillator starts up. + // + Delay(524288); + + // + // Set the crystal frequency and switch to the main oscillator. + // + HWREG(SYSCTL_RCC) = ((HWREG(SYSCTL_RCC) & + ~(SYSCTL_RCC_XTAL_M | SYSCTL_RCC_OSCSRC_M)) | + XTAL_VALUE | SYSCTL_RCC_OSCSRC_MAIN); +#endif + + // + // Enable the CAN controller. + // + HWREG(SYSCTL_RCGC0) |= SYSCTL_RCGC0_CAN0; + +#if CAN_RX_PERIPH == CAN_TX_PERIPH + // + // Enable the GPIO associated with CAN0 + // + HWREG(SYSCTL_RCGC2) |= CAN_RX_PERIPH; + + // + // Wait a while before accessing the peripheral. + // + Delay(3); + + // + // Set the alternate function selects. + // + HWREG(CAN_RX_PORT + GPIO_O_AFSEL) |= CAN_RX_PIN_M | CAN_TX_PIN_M; + + // + // Set the pin type to it's digital function. + // + HWREG(CAN_RX_PORT + GPIO_O_DEN) |= CAN_RX_PIN_M | CAN_TX_PIN_M; + +#else + // + // Enable the GPIO associated with CAN0 + // + HWREG(SYSCTL_RCGC2) |= CAN_RX_PERIPH | CAN_TX_PERIPH; + + // + // Wait a while before accessing the peripheral. + // + Delay(3); + + // + // Set the alternate function selects. + // + HWREG(CAN_RX_PORT + GPIO_O_AFSEL) |= CAN_RX_PIN_M; + HWREG(CAN_TX_PORT + GPIO_O_AFSEL) |= CAN_TX_PIN_M; + + // + // Set the pin type to it's digital function. + // + HWREG(CAN_RX_PORT + GPIO_O_DEN) |= CAN_RX_PIN_M; + HWREG(CAN_TX_PORT + GPIO_O_DEN) |= CAN_TX_PIN_M; +#endif + + // + // Configure the UART used for bridging. + // +#ifdef CAN_UART_BRIDGE + ConfigureBridge(); +#endif + + // + // Configure the CAN interface. + // + ConfigureCANInterface(1); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif diff --git a/boot_loader/bl_can.h b/boot_loader/bl_can.h new file mode 100644 index 0000000..504a410 --- /dev/null +++ b/boot_loader/bl_can.h @@ -0,0 +1,64 @@ +//***************************************************************************** +// +// bl_can.h - Definitions for the CAN transport functions. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_CAN_H__ +#define __BL_CAN_H__ + +//***************************************************************************** +// +// These defines are used to define the range of values that are used for +// the CAN update protocol. +// +//***************************************************************************** +#define CAN_MSGID_DTYPE_UPDATE 0x1f000000 +#define CAN_MSGID_MFR_LM 0x00020000 + +//***************************************************************************** +// +// The masks of the fields that are used in the message identifier. +// +//***************************************************************************** +#define CAN_MSGID_DEVNO_M 0x0000003f +#define CAN_MSGID_API_M 0x0000ffc0 +#define CAN_MSGID_MFR_M 0x00ff0000 +#define CAN_MSGID_DTYPE_M 0x1f000000 +#define CAN_MSGID_DEVNO_S 0 +#define CAN_MSGID_API_S 6 +#define CAN_MSGID_MFR_S 16 +#define CAN_MSGID_DTYPE_S 24 + +//***************************************************************************** +// +// Firmware Update API definitions. +// +//***************************************************************************** +#define LM_API_UPD (CAN_MSGID_MFR_LM | CAN_MSGID_DTYPE_UPDATE) +#define LM_API_UPD_PING (LM_API_UPD | (0 << CAN_MSGID_API_S)) +#define LM_API_UPD_DOWNLOAD (LM_API_UPD | (1 << CAN_MSGID_API_S)) +#define LM_API_UPD_SEND_DATA (LM_API_UPD | (2 << CAN_MSGID_API_S)) +#define LM_API_UPD_RESET (LM_API_UPD | (3 << CAN_MSGID_API_S)) +#define LM_API_UPD_ACK (LM_API_UPD | (4 << CAN_MSGID_API_S)) +#define LM_API_UPD_REQUEST (LM_API_UPD | (6 << CAN_MSGID_API_S)) + +#endif // __BL_CAN_H__ diff --git a/boot_loader/bl_can_timing.h b/boot_loader/bl_can_timing.h new file mode 100644 index 0000000..f0aaa0c --- /dev/null +++ b/boot_loader/bl_can_timing.h @@ -0,0 +1,241 @@ +//***************************************************************************** +// +// bl_can_timing.h - Timing definitions for the CAN controller. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_CAN_TIMING_H__ +#define __BL_CAN_TIMING_H__ + +#ifdef CAN_ENABLE_UPDATE + +//***************************************************************************** +// +// This macro is used to generate the proper value for CAN_BIT_TIMING. The +// values selected for each crystal/bit rate combination assumes a propagation +// delay of 300ns (which will always be rounded up to the next integer multiple +// of the CAN time quanta). +// +//***************************************************************************** +#define CAN_BIT_REG(seg1, seg2, sjw, brp) \ + ((((seg2 - 1) << CAN_BIT_TSEG2_S) & \ + CAN_BIT_TSEG2_M) | \ + (((seg1 - 1) << CAN_BIT_TSEG1_S) & \ + CAN_BIT_TSEG1_M) | \ + (((sjw - 1) << CAN_BIT_SJW_S) & \ + CAN_BIT_SJW_M) | \ + (((brp - 1) << CAN_BIT_BRP_S) & \ + CAN_BIT_BRP_M)) + +//***************************************************************************** +// +// The settings for a 16MHz crystal frequency. +// +//***************************************************************************** +#if CRYSTAL_FREQ == 16000000 +#if CAN_BIT_RATE == 1000000 +#define CAN_BIT_TIMING CAN_BIT_REG(10, 5, 4, 1) // tProp = 312ns +#elif CAN_BIT_RATE == 500000 +#define CAN_BIT_TIMING CAN_BIT_REG(9, 6, 4, 2) // tProp = 375ns +#elif CAN_BIT_RATE == 250000 +#define CAN_BIT_TIMING CAN_BIT_REG(4, 3, 3, 8) // tProp = 500ns +#elif CAN_BIT_RATE == 125000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 8) // tProp = 500ns +#elif CAN_BIT_RATE == 50000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 20) // tProp = 1250ns +#elif CAN_BIT_RATE == 20000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 50) // tProp = 3125ns +#else +#error Invalid CAN_BIT_RATE value used with a 16MHz crystal. +#endif + +//***************************************************************************** +// +// The settings for a 12MHz crystal frequency. +// +//***************************************************************************** +#elif CRYSTAL_FREQ == 12000000 +#if CAN_BIT_RATE == 1000000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 3, 3, 1) // tProp = 416ns +#elif CAN_BIT_RATE == 500000 +#define CAN_BIT_TIMING CAN_BIT_REG(7, 4, 4, 2) // tProp = 500ns +#elif CAN_BIT_RATE == 250000 +#define CAN_BIT_TIMING CAN_BIT_REG(6, 5, 4, 4) // tProp = 333ns +#elif CAN_BIT_RATE == 125000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 6) // tProp = 500ns +#elif CAN_BIT_RATE == 50000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 15) // tProp = 1250ns +#elif CAN_BIT_RATE == 20000 +#define CAN_BIT_TIMING CAN_BIT_REG(6, 5, 4, 50) // tProp = 4166ns +#else +#error Invalid CAN_BIT_RATE value used with a 12MHz crystal. +#endif + +//***************************************************************************** +// +// The settings for a 10MHz crystal frequency. +// +//***************************************************************************** +#elif CRYSTAL_FREQ == 10000000 +#if CAN_BIT_RATE == 1000000 +#define CAN_BIT_TIMING CAN_BIT_REG(6, 3, 3, 1) // tProp = 300ns +#elif CAN_BIT_RATE == 500000 +#define CAN_BIT_TIMING CAN_BIT_REG(11, 8, 4, 1) // tProp = 300ns +#elif CAN_BIT_RATE == 250000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 4) // tProp = 400ns +#elif CAN_BIT_RATE == 125000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 5) // tProp = 500ns +#elif CAN_BIT_RATE == 50000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 20) // tProp = 2000ns +#elif CAN_BIT_RATE == 20000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 50) // tProp = 5000ns +#else +#error Invalid CAN_BIT_RATE value used with a 10MHz crystal. +#endif + +//***************************************************************************** +// +// The settings for a 8MHz crystal frequency. +// +//***************************************************************************** +#elif CRYSTAL_FREQ == 8000000 +#if CAN_BIT_RATE == 1000000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 2, 2, 1) // tProp = 375ns +#elif CAN_BIT_RATE == 500000 +#define CAN_BIT_TIMING CAN_BIT_REG(9, 6, 4, 1) // tProp = 375ns +#elif CAN_BIT_RATE == 250000 +#define CAN_BIT_TIMING CAN_BIT_REG(4, 3, 3, 4) // tProp = 500ns +#elif CAN_BIT_RATE == 125000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 4) // tProp = 500ns +#elif CAN_BIT_RATE == 50000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 10) // tProp = 1250ns +#elif CAN_BIT_RATE == 20000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 25) // tProp = 3125ns +#else +#error Invalid CAN_BIT_RATE value used with a 8MHz crystal. +#endif + +//***************************************************************************** +// +// The settings for a 6MHz crystal frequency. +// +//***************************************************************************** +#elif CRYSTAL_FREQ == 6000000 +#if CAN_BIT_RATE == 500000 +#define CAN_BIT_TIMING CAN_BIT_REG(7, 4, 4, 1) // tProp = 500ns +#elif CAN_BIT_RATE == 250000 +#define CAN_BIT_TIMING CAN_BIT_REG(6, 5, 4, 2) // tProp = 333ns +#elif CAN_BIT_RATE == 125000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 3) // tProp = 500ns +#elif CAN_BIT_RATE == 50000 +#define CAN_BIT_TIMING CAN_BIT_REG(6, 5, 4, 10) // tProp = 1666ns +#elif CAN_BIT_RATE == 20000 +#define CAN_BIT_TIMING CAN_BIT_REG(6, 5, 4, 25) // tProp = 4166ns +#else +#error Invalid CAN_BIT_RATE value used with a 6MHz crystal. +#endif + +//***************************************************************************** +// +// The settings for a 5MHz crystal frequency. +// +//***************************************************************************** +#elif CRYSTAL_FREQ == 5000000 +#if CAN_BIT_RATE == 500000 +#define CAN_BIT_TIMING CAN_BIT_REG(6, 3, 3, 1) // tProp = 600ns +#elif CAN_BIT_RATE == 250000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 2) // tProp = 400ns +#elif CAN_BIT_RATE == 125000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 4) // tProp = 800ns +#elif CAN_BIT_RATE == 50000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 10) // tProp = 2000ns +#elif CAN_BIT_RATE == 20000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 25) // tProp = 5000ns +#else +#error Invalid CAN_BIT_RATE value used with a 5MHz crystal. +#endif + +//***************************************************************************** +// +// The settings for a 4MHz crystal frequency. +// +//***************************************************************************** +#elif CRYSTAL_FREQ == 4000000 +#if CAN_BIT_RATE == 500000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 2, 2, 1) // tProp = 750ns +#elif CAN_BIT_RATE == 250000 +#define CAN_BIT_TIMING CAN_BIT_REG(4, 3, 3, 2) // tProp = 500ns +#elif CAN_BIT_RATE == 125000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 2) // tProp = 500ns +#elif CAN_BIT_RATE == 50000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 5) // tProp = 1250ns +#elif CAN_BIT_RATE == 20000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 20) // tProp = 5000ns +#else +#error Invalid CAN_BIT_RATE value used with a 4MHz crystal. +#endif + +//***************************************************************************** +// +// The settings for a 2MHz crystal frequency. +// +//***************************************************************************** +#elif CRYSTAL_FREQ == 2000000 +#if CAN_BIT_RATE == 250000 +#define CAN_BIT_TIMING CAN_BIT_REG(4, 3, 3, 1) // tProp = 500ns +#elif CAN_BIT_RATE == 125000 +#define CAN_BIT_TIMING CAN_BIT_REG(8, 7, 4, 1) // tProp = 500ns +#elif CAN_BIT_RATE == 50000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 4) // tProp = 2000ns +#elif CAN_BIT_RATE == 20000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 10) // tProp = 5000ns +#else +#error Invalid CAN_BIT_RATE value used with a 2MHz crystal. +#endif + +//***************************************************************************** +// +// The settings for a 1MHz crystal frequency. +// +//***************************************************************************** +#elif CRYSTAL_FREQ == 1000000 +#if CAN_BIT_RATE == 125000 +#define CAN_BIT_TIMING CAN_BIT_REG(4, 3, 3, 1) // tProp = 1000ns +#elif CAN_BIT_RATE == 50000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 2) // tProp = 2000ns +#elif CAN_BIT_RATE == 20000 +#define CAN_BIT_TIMING CAN_BIT_REG(5, 4, 4, 5) // tProp = 5000ns +#else +#error Invalid CAN_BIT_RATE value used with a 1MHz crystal. +#endif + +//***************************************************************************** +// +// An unsupported crystal frequency was specified. +// +//***************************************************************************** +#else +#error The CRYSTAL_FREQ value is not supported by the CAN controller. +#endif + +#endif + +#endif // __BL_CAN_TIMING_H__ diff --git a/boot_loader/bl_check.c b/boot_loader/bl_check.c new file mode 100644 index 0000000..b481013 --- /dev/null +++ b/boot_loader/bl_check.c @@ -0,0 +1,266 @@ +//***************************************************************************** +// +// bl_check.c - Code to check for a forced update. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_gpio.h" +#include "inc/hw_memmap.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "bl_config.h" +#include "boot_loader/bl_check.h" +#include "boot_loader/bl_hooks.h" +#ifdef CHECK_CRC +#include "boot_loader/bl_crc32.h" +#endif + +//***************************************************************************** +// +//! \addtogroup bl_check_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This global is used to remember if a forced update occurred. +// +//***************************************************************************** +#ifdef ENABLE_UPDATE_CHECK +uint32_t g_ui32Forced; +#endif + +//***************************************************************************** +// +// A prototype for the function (in the startup code) for a predictable length +// delay. +// +//***************************************************************************** +extern void Delay(uint32_t ui32Count); + +//***************************************************************************** +// +//! Checks a GPIO for a forced update. +//! +//! This function checks the state of a GPIO to determine if a update is being +//! requested. +//! +//! \return Returns a non-zero value if an update is being requested and zero +//! otherwise. +// +//***************************************************************************** +#ifdef ENABLE_UPDATE_CHECK +uint32_t +CheckGPIOForceUpdate(void) +{ + // + // Enable the required GPIO module. + // + HWREG(SYSCTL_RCGC2) |= FORCED_UPDATE_PERIPH; + + // + // Wait a while before accessing the peripheral. + // + Delay(3); + +#ifdef FORCED_UPDATE_KEY + // + // Unlock the GPIO Access. + // + HWREG(FORCED_UPDATE_PORT + GPIO_O_LOCK) = FORCED_UPDATE_KEY; + HWREG(FORCED_UPDATE_PORT + GPIO_O_CR) = 1 << FORCED_UPDATE_PIN; +#endif + + // + // Enable the pin used to see if an update is being requested. + // + HWREG(FORCED_UPDATE_PORT + GPIO_O_DEN) |= 1 << FORCED_UPDATE_PIN; +#ifdef FORCED_UPDATE_WPU + // + // Set the output drive strength. + // + HWREG(FORCED_UPDATE_PORT + GPIO_O_DR2R) |= 1 << FORCED_UPDATE_PIN; + + // + // Enable the weak pull up. + // + HWREG(FORCED_UPDATE_PORT + GPIO_O_PUR) |= 1 << FORCED_UPDATE_PIN; + + // + // Make sure that the analog mode select register is clear for this pin. + // + HWREG(FORCED_UPDATE_PORT + GPIO_O_AMSEL) &= ~(1 << FORCED_UPDATE_PIN); +#endif +#ifdef FORCED_UPDATE_WPD + // + // Set the output drive strength. + // + HWREG(FORCED_UPDATE_PORT + GPIO_O_DR2R) |= 1 << FORCED_UPDATE_PIN; + + // + // Enable the weak pull down. + // + HWREG(FORCED_UPDATE_PORT + GPIO_O_PDR) |= 1 << FORCED_UPDATE_PIN; + + // + // Make sure that the analog mode select register is clear for this pin. + // This register only appears in DustDevil-class (and later) devices, but + // is a harmless write on Sandstorm- and Fury-class devices. + // + HWREG(FORCED_UPDATE_PORT + GPIO_O_AMSEL) &= ~(1 << FORCED_UPDATE_PIN); +#endif + +#ifdef FORCED_UPDATE_KEY + // + // Unlock the GPIO Access. + // + HWREG(FORCED_UPDATE_PORT + GPIO_O_LOCK) = FORCED_UPDATE_KEY; + HWREG(FORCED_UPDATE_PORT + GPIO_O_CR) = 0; +#endif + + // + // Wait a while before reading the pin. + // + Delay(1000); + + // + // Check the pin to see if an update is being requested. + // + if(HWREG(FORCED_UPDATE_PORT + (1 << (FORCED_UPDATE_PIN + 2))) == + (FORCED_UPDATE_POLARITY << FORCED_UPDATE_PIN)) + { + // + // Remember that this was a forced update. + // + g_ui32Forced = 1; + + return(1); + } + + // + // No update is being requested so return 0. + // + return(0); +} +#endif + +//***************************************************************************** +// +//! Checks if an update is needed or is being requested. +//! +//! This function detects if an update is being requested or if there is no +//! valid code presently located on the microcontroller. This is used to tell +//! whether or not to enter update mode. +//! +//! \return Returns a non-zero value if an update is needed or is being +//! requested and zero otherwise. +// +//***************************************************************************** +uint32_t +CheckForceUpdate(void) +{ +#ifdef CHECK_CRC + uint32_t ui32Retcode; +#endif + +#ifdef BL_CHECK_UPDATE_FN_HOOK + // + // If the update check function is hooked, call the application to determine + // how to proceed. + // + return(BL_CHECK_UPDATE_FN_HOOK()); +#else + uint32_t *pui32App; + +#ifdef ENABLE_UPDATE_CHECK + g_ui32Forced = 0; +#endif + + // + // See if the first location is 0xfffffffff or something that does not + // look like a stack pointer, or if the second location is 0xffffffff or + // something that does not look like a reset vector. + // + pui32App = (uint32_t *)APP_START_ADDRESS; + if((pui32App[0] == 0xffffffff) || + ((pui32App[0] & 0xfff00000) != 0x20000000) || + (pui32App[1] == 0xffffffff) || + ((pui32App[1] & 0xfff00001) != 0x00000001)) + { + return(1); + } + + // + // If required, scan the image for an embedded CRC and ensure that it + // matches the current CRC of the image. + // +#ifdef CHECK_CRC + InitCRC32Table(); + ui32Retcode = CheckImageCRC32(pui32App); + + // + // If ENFORCE_CRC is defined, we only boot the image if the CRC is + // present in the image information header and the value calculated + // matches the value in the header. If ENFORCE_CRC is not defined, we + // the image if the CRC is good but also if the length field of the header + // is zero (which typically indicates that the post-build step of running + // binpack to add the length and CRC to the header was not run). + // +#ifdef ENFORCE_CRC + if(ui32Retcode != CHECK_CRC_OK) +#else + if((ui32Retcode != CHECK_CRC_OK) && (ui32Retcode != CHECK_CRC_NO_LENGTH)) +#endif + { + // + // The CRC32 image check failed indicating that the image is + // corrupt (or doesn't have the CRC embedded correctly). Either way, + // fail the update check and force the boot loader to retain control. + // + return(2); + } +#endif + +#ifdef ENABLE_UPDATE_CHECK + // + // If simple GPIO checking is configured, determine whether or not to force + // an update. + // + return(CheckGPIOForceUpdate()); +#else + // + // GPIO checking is not required so, if we get here, a valid image exists + // and no update is needed. + // + return(0); +#endif +#endif +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/boot_loader/bl_check.h b/boot_loader/bl_check.h new file mode 100644 index 0000000..a3e0519 --- /dev/null +++ b/boot_loader/bl_check.h @@ -0,0 +1,39 @@ +//***************************************************************************** +// +// bl_check.h - Definitions for the forced update check function. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_CHECK_H__ +#define __BL_CHECK_H__ + +//***************************************************************************** +// +// Prototype for the forced update check function. +// +//***************************************************************************** +extern uint32_t CheckForceUpdate(void); +#ifdef ENABLE_UPDATE_CHECK +extern uint32_t CheckGPIOForceUpdate(void); +extern uint32_t g_ui32Forced; +#endif + +#endif // __BL_CHECK_H__ diff --git a/boot_loader/bl_commands.h b/boot_loader/bl_commands.h new file mode 100644 index 0000000..f666e15 --- /dev/null +++ b/boot_loader/bl_commands.h @@ -0,0 +1,242 @@ +//***************************************************************************** +// +// bl_commands.h - The list of commands and return messages supported by the +// boot loader. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_COMMANDS_H__ +#define __BL_COMMANDS_H__ + +//***************************************************************************** +// +// This command is used to receive an acknowledge from the the boot loader +// proving that communication has been established. This command is a single +// byte. +// +// The format of the command is as follows: +// +// uint8_t ui8Command[1]; +// +// ui8Command[0] = COMMAND_PING; +// +//***************************************************************************** +#define COMMAND_PING 0x20 + +//***************************************************************************** +// +// This command is sent to the boot loader to indicate where to store data and +// how many bytes will be sent by the COMMAND_SEND_DATA commands that follow. +// The command consists of two 32-bit values that are both transferred MSB +// first. The first 32-bit value is the address to start programming data +// into, while the second is the 32-bit size of the data that will be sent. +// This command also triggers an erasure of the full application area in the +// flash or possibly the entire flash depending on the address used. This +// causes the command to take longer to send the ACK/NAK in response to the +// command. This command should be followed by a COMMAND_GET_STATUS to ensure +// that the program address and program size were valid for the microcontroller +// running the boot loader. +// +// The format of the command is as follows: +// +// uint8_t ui8Command[9]; +// +// ui8Command[0] = COMMAND_DOWNLOAD; +// ui8Command[1] = Program Address [31:24]; +// ui8Command[2] = Program Address [23:16]; +// ui8Command[3] = Program Address [15:8]; +// ui8Command[4] = Program Address [7:0]; +// ui8Command[5] = Program Size [31:24]; +// ui8Command[6] = Program Size [23:16]; +// ui8Command[7] = Program Size [15:8]; +// ui8Command[8] = Program Size [7:0]; +// +//***************************************************************************** +#define COMMAND_DOWNLOAD 0x21 + +//***************************************************************************** +// +// This command is sent to the boot loader to transfer execution control to the +// specified address. The command is followed by a 32-bit value, transferred +// MSB first, that is the address to which execution control is transferred. +// +// The format of the command is as follows: +// +// uint8_t ui8Command[5]; +// +// ui8Command[0] = COMMAND_RUN; +// ui8Command[1] = Run Address [31:24]; +// ui8Command[2] = Run Address [23:16]; +// ui8Command[3] = Run Address [15:8]; +// ui8Command[4] = Run Address [7:0]; +// +//***************************************************************************** +#define COMMAND_RUN 0x22 + +//***************************************************************************** +// +// This command returns the status of the last command that was issued. +// Typically this command should be received after every command is sent to +// ensure that the previous command was successful or, if unsuccessful, to +// properly respond to a failure. The command requires one byte in the data of +// the packet and the boot loader should respond by sending a packet with one +// byte of data that contains the current status code. +// +// The format of the command is as follows: +// +// uint8_t ui8Command[1]; +// +// ui8Command[0] = COMMAND_GET_STATUS; +// +// The following are the definitions for the possible status values that can be +// returned from the boot loader when COMMAND_GET_STATUS is sent to +// the microcontroller. +// +// COMMAND_RET_SUCCESS +// COMMAND_RET_UNKNOWN_CMD +// COMMAND_RET_INVALID_CMD +// COMMAND_RET_INVALID_ADD +// COMMAND_RET_FLASH_FAIL +// COMMAND_RET_CRC_FAIL +// +//***************************************************************************** +#define COMMAND_GET_STATUS 0x23 + +//***************************************************************************** +// +// This command should only follow a COMMAND_DOWNLOAD command or another +// COMMAND_SEND_DATA command, if more data is needed. Consecutive send data +// commands automatically increment the address and continue programming from +// the previous location. The transfer size is limited by the size of the +// receive buffer in the boot loader (as configured by the BUFFER_SIZE +// parameter). The command terminates programming once the number of bytes +// indicated by the COMMAND_DOWNLOAD command has been received. Each time this +// function is called, it should be followed by a COMMAND_GET_STATUS command to +// ensure that the data was successfully programmed into the flash. If the +// boot loader sends a NAK to this command, the boot loader will not increment +// the current address to allow retransmission of the previous data. +// +// The format of the command is as follows: +// +// uint8_t ui8Command[9]; +// +// ui8Command[0] = COMMAND_SEND_DATA; +// ui8Command[1] = Data[0]; +// ui8Command[2] = Data[1]; +// ui8Command[3] = Data[2]; +// ui8Command[4] = Data[3]; +// ui8Command[5] = Data[4]; +// ui8Command[6] = Data[5]; +// ui8Command[7] = Data[6]; +// ui8Command[8] = Data[7]; +// +//***************************************************************************** +#define COMMAND_SEND_DATA 0x24 + +//***************************************************************************** +// +// This command is used to tell the boot loader to reset. This is used after +// downloading a new image to the microcontroller to cause the new application +// or the new boot loader to start from a reset. The normal boot sequence +// occurs and the image runs as if from a hardware reset. It can also be used +// to reset the boot loader if a critical error occurs and the host device +// wants to restart communication with the boot loader. +// +// The format of the command is as follows: +// +// uint8_t ui8Command[1]; +// +// ui8Command[0] = COMMAND_RESET; +// +// The boot loader responds with an ACK signal to the host device before +// actually executing the software reset on the microcontroller running the +// boot loader. This informs the updater application that the command was +// received successfully and the part will be reset. +// +//***************************************************************************** +#define COMMAND_RESET 0x25 + +//***************************************************************************** +// +// This is returned in response to a COMMAND_GET_STATUS command and indicates +// that the previous command completed successful. +// +//***************************************************************************** +#define COMMAND_RET_SUCCESS 0x40 + +//***************************************************************************** +// +// This is returned in response to a COMMAND_GET_STATUS command and indicates +// that the command sent was an unknown command. +// +//***************************************************************************** +#define COMMAND_RET_UNKNOWN_CMD 0x41 + +//***************************************************************************** +// +// This is returned in response to a COMMAND_GET_STATUS command and indicates +// that the previous command was formatted incorrectly. +// +//***************************************************************************** +#define COMMAND_RET_INVALID_CMD 0x42 + +//***************************************************************************** +// +// This is returned in response to a COMMAND_GET_STATUS command and indicates +// that the previous download command contained an invalid address value. +// +//***************************************************************************** +#define COMMAND_RET_INVALID_ADR 0x43 + +//***************************************************************************** +// +// This is returned in response to a COMMAND_GET_STATUS command and indicates +// that an attempt to program or erase the flash has failed. +// +//***************************************************************************** +#define COMMAND_RET_FLASH_FAIL 0x44 + +//***************************************************************************** +// +// This is returned in response to a COMMAND_GET_STATUS command and indicates +// that the boot loader is configured to check the embedded CRC32 in the +// downloaded image but the check failed. This status can only be returned +// after the last COMMAND_SEND_DATA has been received and processed, and only +// if CHECK_CRC is defined in the boot loader configuration. +// +//***************************************************************************** +#define COMMAND_RET_CRC_FAIL 0x45 + +//***************************************************************************** +// +// This is the value that is sent to acknowledge a packet. +// +//***************************************************************************** +#define COMMAND_ACK 0xcc + +//***************************************************************************** +// +// This is the value that is sent to not-acknowledge a packet. +// +//***************************************************************************** +#define COMMAND_NAK 0x33 + +#endif // __BL_COMMANDS_H__ diff --git a/boot_loader/bl_config.c b/boot_loader/bl_config.c new file mode 100644 index 0000000..9c26068 --- /dev/null +++ b/boot_loader/bl_config.c @@ -0,0 +1,164 @@ +//***************************************************************************** +// +// bl_config.c - A dummy C file to generate bl_config.in from bl_config.h. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include "bl_config.h" + +//***************************************************************************** +// +// Since the RV-MDK assembler is not able to run assembly code through the C +// preprocessor, the relevant contents of bl_config.h need to be converted to +// assembly format for inclusion into the RV-MDK startup code. This file +// performs this conversion when manually run through the C preprocessor via: +// +// armcc --device DLM -o bl_config.inc -E bl_config.c +// +// This file does not contain valid C code and will fail to compile (-E tells +// the compiler to preprocess but not attempt to compile the code). +// +//***************************************************************************** + +//***************************************************************************** +// +// Define an assembler symbol for the stack size. +// +//***************************************************************************** +_STACK_SIZE equ STACK_SIZE + +//***************************************************************************** +// +// Define an assembler symbol for the application starting address. +// +//***************************************************************************** +_APP_START_ADDRESS equ APP_START_ADDRESS + +//***************************************************************************** +// +// Define an assembler symbol for the application vector table address. +// +//***************************************************************************** +_VTABLE_START_ADDRESS equ VTABLE_START_ADDRESS + +//***************************************************************************** +// +// Define an assembler symbol if the MOSCFAIL handler is enabled. +// +//***************************************************************************** +#ifdef ENABLE_MOSCFAIL_HANDLER +_ENABLE_MOSCFAIL_HANDLER equ 1 +#endif + +//***************************************************************************** +// +// Define an assembler symbol if update via the UART is enabled. +// +//***************************************************************************** +#ifdef UART_ENABLE_UPDATE +_UART_ENABLE_UPDATE equ 1 +#endif + +//***************************************************************************** +// +// Define an assember symbol if UART autobauding is enabled. +// +//***************************************************************************** +#ifdef UART_AUTOBAUD +_UART_AUTOBAUD equ 1 +#endif + +//***************************************************************************** +// +// Define an assembler symbol if update via Ethernet is enabled. +// +//***************************************************************************** +#ifdef ENET_ENABLE_UPDATE +_ENET_ENABLE_UPDATE equ 1 +#endif + +//***************************************************************************** +// +// Define an assembler symbol if update via CAN is enabled. +// +//***************************************************************************** +#ifdef CAN_ENABLE_UPDATE +_CAN_ENABLE_UPDATE equ 1 +#endif + +//***************************************************************************** +// +// Define an assembler symbol if update via USB is enabled. +// +//***************************************************************************** +#ifdef USB_ENABLE_UPDATE +_USB_ENABLE_UPDATE equ 1 +#endif + +//***************************************************************************** +// +// Define an assembler symbol if a hardware initialization hook is provided. +// +//***************************************************************************** +#ifdef BL_HW_INIT_FN_HOOK +#define _quote(x) #x +#define quote(x) _quote(x) + gbls _BL_HW_INIT_FN_HOOK +_BL_HW_INIT_FN_HOOK sets quote(BL_HW_INIT_FN_HOOK) +#undef _quote +#undef quote +#endif + +//***************************************************************************** +// +// Define an assembler symbol if an initialization hook is provided. +// +//***************************************************************************** +#ifdef BL_INIT_FN_HOOK +#define _quote(x) #x +#define quote(x) _quote(x) + gbls _BL_INIT_FN_HOOK +_BL_INIT_FN_HOOK sets quote(BL_INIT_FN_HOOK) +#undef _quote +#undef quote +#endif + +//***************************************************************************** +// +// Define an assembler symbol if a re-initialization hook is provided. +// +//***************************************************************************** +#ifdef BL_REINIT_FN_HOOK +#define _quote(x) #x +#define quote(x) _quote(x) + gbls _BL_REINIT_FN_HOOK +_BL_REINIT_FN_HOOK sets quote(BL_REINIT_FN_HOOK) +#undef _quote +#undef quote +#endif + +//***************************************************************************** +// +// The assembler will require an end statement at the end of the output +// bl_config.inc file. +// +//***************************************************************************** + end diff --git a/boot_loader/bl_config.h.tmpl b/boot_loader/bl_config.h.tmpl new file mode 100644 index 0000000..99ab978 --- /dev/null +++ b/boot_loader/bl_config.h.tmpl @@ -0,0 +1,950 @@ +//***************************************************************************** +// +// bl_config.h - The configurable parameters of the boot loader. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_CONFIG_H__ +#define __BL_CONFIG_H__ + +//***************************************************************************** +// +// The following defines are used to configure the operation of the boot +// loader. For each define, its interactions with other defines are described. +// First is the dependencies (i.e. the defines that must also be defined if it +// is defined), next are the exclusives (i.e. the defines that can not be +// defined if it is defined), and finally are the requirements (i.e. the +// defines that must be defined if it is defined). +// +// The following defines must be defined in order for the boot loader to +// operate: +// +// One of CAN_ENABLE_UPDATE, ENET_ENABLE_UPDATE, I2C_ENABLE_UPDATE, +// SSI_ENABLE_UPDATE, UART_ENABLE_UPDATE, or USB_ENABLE_UPDATE +// APP_START_ADDRESS +// STACK_SIZE +// BUFFER_SIZE +// +//***************************************************************************** + +//***************************************************************************** +// +// The frequency of the crystal used to clock the microcontroller. +// +// This defines the crystal frequency used by the microcontroller running the +// boot loader. If this is unknown at the time of production, then use the +// UART_AUTOBAUD feature to properly configure the UART. +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +#define CRYSTAL_FREQ 8000000 + +//***************************************************************************** +// +// This enables the boosting of the LDO voltage to 2.75V. For boot loader +// configurations that enable the PLL (for example, using the Ethernet port) +// on a part that has the PLL errata, this should be enabled. This applies to +// revision A2 of Fury-class devices. +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define BOOST_LDO_VOLTAGE + +//***************************************************************************** +// +// The starting address of the application. This must be a multiple of 1024 +// bytes (making it aligned to a page boundary). A vector table is expected at +// this location, and the perceived validity of the vector table (stack located +// in SRAM, reset vector located in flash) is used as an indication of the +// validity of the application image. +// +// The flash image of the boot loader must not be larger than this value. +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +#define APP_START_ADDRESS 0x00001000 + +//***************************************************************************** +// +// The address at which the application locates its exception vector table. +// This must be a multiple of 1KB (making it aligned to a page boundary). +// Typically, an application will start with its vector table and this value +// will default to APP_START_ADDRESS. This option is provided to cater for +// applications which run from external memory which may not be accessible by +// the NVIC (the vector table offset register is only 30 bits long). +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +#define VTABLE_START_ADDRESS 0x00001000 + +//***************************************************************************** +// +// The size of a single, erasable page in the flash. This must be a power +// of 2. The default value of 1KB represents the page size for the internal +// flash on all Tiva MCUs and this value should only be overridden if +// configuring a boot loader to access external flash devices with a page size +// different from this. +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +#define FLASH_PAGE_SIZE 0x00000400 + +//***************************************************************************** +// +// The amount of space at the end of flash to reserved. This must be a +// multiple of 1024 bytes (making it aligned to a page boundary). This +// reserved space is not erased when the application is updated, providing +// non-volatile storage that can be used for parameters. +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define FLASH_RSVD_SPACE 0x00000800 + +//***************************************************************************** +// +// The number of words of stack space to reserve for the boot loader. +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +#define STACK_SIZE 64 + +//***************************************************************************** +// +// The number of words in the data buffer used for receiving packets. This +// value must be at least 3. If using autobauding on the UART, this must be at +// least 20. The maximum usable value is 65 (larger values will result in +// unused space in the buffer). +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +#define BUFFER_SIZE 20 + +//***************************************************************************** +// +// Enables updates to the boot loader. Updating the boot loader is an unsafe +// operation since it is not fully fault tolerant (losing power to the device +// part way though could result in the boot loader no longer being present in +// flash). +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define ENABLE_BL_UPDATE + +//***************************************************************************** +// +// This definition will cause the the boot loader to erase the entire flash on +// updates to the boot loader or to erase the entire application area when the +// application is updated. This erases any unused sections in the flash before +// the firmware is updated. +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define FLASH_CODE_PROTECTION + +//***************************************************************************** +// +// Enables the call to decrypt the downloaded data before writing it into +// flash. The decryption routine is empty in the reference boot loader source, +// which simply provides a placeholder for adding an actual decrypter. +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define ENABLE_DECRYPTION + +//***************************************************************************** +// +// Enables support for the MOSCFAIL handler in the NMI interrupt. +// Note: Sandstorm or Fury devices do not provide the MOSCFAIL reset, so this +// feature should not be enabled for these devices. +// +// Depends on: None +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define ENABLE_MOSCFAIL_HANDLER + +//***************************************************************************** +// +// Enables the pin-based forced update check. When enabled, the boot loader +// will go into update mode instead of calling the application if a pin is read +// at a particular polarity, forcing an update operation. In either case, the +// application is still able to return control to the boot loader in order to +// start an update. +// +// Depends on: None +// Exclusive of: None +// Requires: FORCED_UPDATE_PERIPH, FORCED_UPDATE_PORT, FORCED_UPDATE_PIN, +// FORCED_UPDATE_POLARITY +// +//***************************************************************************** +//#define ENABLE_UPDATE_CHECK + +//***************************************************************************** +// +// The GPIO module to enable in order to check for a forced update. This will +// be one of the SYSCTL_RCGC2_GPIOx values, where "x" is replaced with the port +// name (such as B). The value of "x" should match the value of "x" for +// FORCED_UPDATE_PORT. +// +// Depends on: ENABLE_UPDATE_CHECK +// Exclusive of: None +// Requries: None +// +//***************************************************************************** +//#define FORCED_UPDATE_PERIPH SYSCTL_RCGC2_GPIOB + +//***************************************************************************** +// +// The GPIO port to check for a forced update. This will be one of the +// GPIO_PORTx_BASE values, where "x" is replaced with the port name (such as +// B). The value of "x" should match the value of "x" for +// FORCED_UPDATE_PERIPH. +// +// Depends on: ENABLE_UPDATE_CHECK +// Exclusive of: None +// Requries: None +// +//***************************************************************************** +//#define FORCED_UPDATE_PORT GPIO_PORTB_BASE + +//***************************************************************************** +// +// The pin to check for a forced update. This is a value between 0 and 7. +// +// Depends on: ENABLE_UPDATE_CHECK +// Exclusive of: None +// Requries: None +// +//***************************************************************************** +//#define FORCED_UPDATE_PIN 4 + +//***************************************************************************** +// +// The polarity of the GPIO pin that results in a forced update. This value +// should be 0 if the pin should be low and 1 if the pin should be high. +// +// Depends on: ENABLE_UPDATE_CHECK +// Exclusive of: None +// Requries: None +// +//***************************************************************************** +//#define FORCED_UPDATE_POLARITY 0 + +//***************************************************************************** +// +// This enables a weak pull up for the GPIO pin used in a forced update. This +// value should be 0 if the pin should be have an internal weak pull down and +// 1 if the pin should have an interal weak pull up. +// Only FORCED_UPDATE_WPU or FORCED_UPDATE_WPD or neither should be defined. +// +// Depends on: ENABLE_UPDATE_CHECK +// Exclusive of: None +// Requries: None +// +//***************************************************************************** +//#define FORCED_UPDATE_WPU +//#define FORCED_UPDATE_WPD + +//***************************************************************************** +// +// This enables the use of the GPIO_LOCK mechanism for configuration of +// protected GPIO pins (for example JTAG pins). If this value is not defined, +// the locking mechanism will not be used. The only legal values for this +// feature are GPIO_LOCK_KEY for Fury devices and GPIO_LOCK_KEY_DD for all +// other devices except Sandstorm devices, which do not support this feature. +// +// Depends on: ENABLE_UPDATE_CHECK +// Exclusive of: None +// Requries: None +// +//***************************************************************************** +//#define FORCED_UPDATE_KEY GPIO_LOCK_KEY +//#define FORCED_UPDATE_KEY GPIO_LOCK_KEY_DD + +//***************************************************************************** +// +// Selects the UART as the port for communicating with the boot loader. +// +// Depends on: None +// Exclusive of: CAN_ENABLE_UPDATE, ENET_ENABLE_UPDATE, I2C_ENABLE_UPDATE, +// SSI_ENABLE_UPDATE, USB_ENABLE_UPDATE +// Requires: UART_AUTOBAUD or UART_FIXED_BAUDRATE +// +//***************************************************************************** +//#define UART_ENABLE_UPDATE + +//***************************************************************************** +// +// Enables automatic baud rate detection. This can be used if the crystal +// frequency is unknown, or if operation at different baud rates is desired. +// +// Depends on: UART_ENABLE_UPDATE +// Exclusive of: UART_FIXED_BAUDRATE +// Requires: None +// +//***************************************************************************** +//#define UART_AUTOBAUD + +//***************************************************************************** +// +// Selects the baud rate to be used for the UART. +// +// Depends on: UART_ENABLE_UPDATE, CRYSTAL_FREQ +// Exclusive of: UART_AUTOBAUD +// Requires: None +// +//***************************************************************************** +//#define UART_FIXED_BAUDRATE 115200 + +//***************************************************************************** +// +// Selects the SSI port as the port for communicating with the boot loader. +// +// Depends on: None +// Exclusive of: CAN_ENABLE_UPDATE, ENET_ENABLE_UPDATE, I2C_ENABLE_UPDATE, +// UART_ENABLE_UPDATE, USB_ENABLE_UPDATE +// Requires: None +// +//***************************************************************************** +//#define SSI_ENABLE_UPDATE + +//***************************************************************************** +// +// Selects the I2C port as the port for communicating with the boot loader. +// +// Depends on: None +// Exclusive of: CAN_ENABLE_UPDATE, ENET_ENABLE_UPDATE, SSI_ENABLE_UPDATE, +// UART_ENABLE_UPDATE, USB_ENABLE_UPDATE +// Requires: I2C_SLAVE_ADDR +// +//***************************************************************************** +//#define I2C_ENABLE_UPDATE + +//***************************************************************************** +// +// Specifies the I2C address of the boot loader. +// +// Depends on: I2C_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define I2C_SLAVE_ADDR 0x42 + +//***************************************************************************** +// +// Selects Ethernet update via the BOOTP/TFTP protocol. +// +// Depends on: None +// Exclusive of: CAN_ENABLE_UPDATE, I2C_ENABLE_UPDATE, SSI_ENABLE_UPDATE, +// UART_ENABLE_UPDATE, USB_ENABLE_UPDATE +// Requires: CRYSTAL_FREQ +// +//***************************************************************************** +//#define ENET_ENABLE_UPDATE + +//***************************************************************************** +// +// Selects if the Ethernet LEDs should be enabled. +// +// Depends on: ENET_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define ENET_ENABLE_LEDS + +//***************************************************************************** +// +// Selects the Ethernet MAC address. If not specified, the MAC address is +// taken from the user registers. +// +// Depends on: ENET_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define ENET_MAC_ADDR0 0x00 +//#define ENET_MAC_ADDR1 0x00 +//#define ENET_MAC_ADDR2 0x00 +//#define ENET_MAC_ADDR3 0x00 +//#define ENET_MAC_ADDR4 0x00 +//#define ENET_MAC_ADDR5 0x00 + +//***************************************************************************** +// +// Sets the name of the BOOTP server to use. This can be used to request that +// a particular BOOTP server respond to our request; the value will be either +// the server's name, or a nickname used by that server. If not defined then +// any BOOTP server is allowed to respond. +// +// Depends on: ENET_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define ENET_BOOTP_SERVER "tiva" + +//***************************************************************************** +// +// Selects USB update via Device Firmware Update class. +// +// Depends on: None +// Exclusive of: CAN_ENABLE_UPDATE, ENET_ENABLE_UPDATE, I2C_ENABLE_UPDATE, +// SSI_ENABLE_UPDATE, UART_ENABLE_UPDATE, +// Requires: CRYSTAL_FREQ, USB_VENDOR_ID, USB_PRODUCT_ID +// +//***************************************************************************** +//#define USB_ENABLE_UPDATE + +//***************************************************************************** +// +// The USB vendor ID published by the DFU device. This value is the TI +// Tiva vendor ID. Change this to the vendor ID you have been assigned by +// USB-IF. +// +// Depends on: USB_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define USB_VENDOR_ID 0x1cbe + +//***************************************************************************** +// +// The USB device ID published by the DFU device. If you are using your own +// vendor ID, chose a device ID that is different from the ID you use in +// non-update operation. If you have sublicensed TI's vendor ID, you must +// use an assigned product ID here. +// +// Depends on: USB_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define USB_PRODUCT_ID 0x00ff + +//***************************************************************************** +// +// Selects the BCD USB device release number published in the device +// descriptor. +// +// Depends on: USB_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define USB_DEVICE_ID 0x0001 + +//***************************************************************************** +// +// Sets the maximum power consumption that the DFU device will report to the +// USB host in the configuration descriptor. Units are milliamps. +// +// Depends on: USB_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define USB_MAX_POWER 150 + +//***************************************************************************** +// +// Determines whether the DFU device reports to the host that it is self +// powered (defined as 0) or bus powered (defined as 1). +// +// Depends on: USB_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define USB_BUS_POWERED 1 + +//***************************************************************************** +// +// Specifies the GPIO peripheral associated with the USB host/device mux. +// +// Depends on: USB_ENABLE_UPDATE +// Exclusive of: None +// Requires: USB_MUX_PERIPH, USB_MUX_PORT, USB_MUX_PIN, USB_MUX_DEVICE +// +//***************************************************************************** +//#define USB_HAS_MUX + +//***************************************************************************** +// +// Specifies the GPIO peripheral associated with the USB host/device mux. +// +// Depends on: USB_ENABLE_UPDATE, USB_HAS_MUX +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define USB_MUX_PERIPH SYSCTL_RCGC2_GPIOH + +//***************************************************************************** +// +// Specifies the GPIO port associated with the USB host/device mux. +// +// Depends on: USB_ENABLE_UPDATE, USB_HAS_MUX +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define USB_MUX_PORT GPIO_PORTH_BASE + +//***************************************************************************** +// +// Specifies the GPIO pin number used to switch the USB host/device mux. Valid +// values are 0 through 7. +// +// Depends on: USB_ENABLE_UPDATE, USB_HAS_MUX +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define USB_MUX_PIN 2 + +//***************************************************************************** +// +// Specifies the state to set the GPIO pin to to select USB device mode via +// the USB host/device mux. Valid values are 1 (high) or 0 (low). +// +// Depends on: USB_ENABLE_UPDATE, USB_HAS_MUX +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define USB_MUX_DEVICE 1 + +//***************************************************************************** +// +// Selects the CAN port as the port for communicating with the boot loader. +// +// Depends on: None +// Exclusive of: ENET_ENABLE_UPDATE, I2C_ENABLE_UPDATE, SSI_ENABLE_UPDATE, +// UART_ENABLE_UPDATE, USB_ENABLE_UPDATE +// Requires: CAN_RX_PERIPH, CAN_RX_PORT, CAN_RX_PIN, CAN_TX_PERIPH, +// CAN_TX_PORT, CAN_TX_PIN, CAN_BIT_RATE, CRYSTAL_FREQ. +// +//***************************************************************************** +//#define CAN_ENABLE_UPDATE + +//***************************************************************************** +// +// Enables the UART to CAN bridging for use when the CAN port is selected for +// communicating with the boot loader. +// +// Depends on: CAN_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define CAN_UART_BRIDGE + +//***************************************************************************** +// +// Specifies the GPIO peripheral associated with CAN0 RX pin used by the boot +// loader. +// +// Depends on: CAN_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define CAN_RX_PERIPH SYSCTL_RCGC2_GPIOA + +//***************************************************************************** +// +// Specifies the GPIO port associated with CAN0 RX pin used by the boot loader. +// +// Depends on: CAN_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define CAN_RX_PORT GPIO_PORTA_BASE + +//***************************************************************************** +// +// Specifies the GPIO pin number associated with CAN0 RX pin used by the boot +// loader. +// +// Depends on: CAN_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define CAN_RX_PIN 4 + +//***************************************************************************** +// +// Specifies the GPIO peripheral associated with CAN0 TX pin used by the boot +// loader. +// +// Depends on: CAN_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define CAN_TX_PERIPH SYSCTL_RCGC2_GPIOA + +//***************************************************************************** +// +// Specifies the GPIO port associated with CAN0 TX pin used by the boot loader. +// +// Depends on: CAN_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define CAN_TX_PORT GPIO_PORTA_BASE + +//***************************************************************************** +// +// Specifies the GPIO pin number associated with CAN0 TX pin used by the boot +// loader. +// +// Depends on: CAN_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define CAN_TX_PIN 5 + +//***************************************************************************** +// +// Specifies the bit rate for CAN0 used by the boot loader. +// +// Depends on: CAN_ENABLE_UPDATE +// Exclusive of: None +// Requires: None +// +//***************************************************************************** +//#define CAN_BIT_RATE 1000000 + +//***************************************************************************** +// +// Boot loader hook functions. +// +// The following defines allow you to add application-specific function which +// are called at various points during boot loader execution. +// +//***************************************************************************** + +//***************************************************************************** +// +// Performs application-specific low level hardware initialization on system +// reset. +// +// If hooked, this function will be called immediately after the boot loader +// code relocation completes. An application may perform any required low +// hardware initialization during this function. Note that the system clock +// has not been set when this function is called. Initialization that assumes +// the system clock is set may be performed in the BL_INIT_FN_HOOK function +// instead. +// +// void MyHwInitFunc(void); +// +//***************************************************************************** +//#define BL_HW_INIT_FN_HOOK MyHwInitFunc + +//***************************************************************************** +// +// Performs application-specific initialization on system reset. +// +// If hooked, this function will be called immediately after the boot loader +// sets the system clock. An application may perform any additional +// initialization during this function. +// +// void MyInitFunc(void); +// +//***************************************************************************** +//#define BL_INIT_FN_HOOK MyInitFunc + +//***************************************************************************** +// +// Performs application-specific reinitialization on boot loader entry via SVC. +// +// If hooked, this function will be called immediately after the boot loader +// reinitializes the system clock when it is entered from an application +// via the SVC mechanism rather than as a result of a system reset. An +// application may perform any additional reinitialization in this function. +// +// void MyReinitFunc(void); +// +//***************************************************************************** +//#define BL_REINIT_FN_HOOK MyReinitFunc + +//***************************************************************************** +// +// Informs an application that a download is starting. +// +// If hooked, this function will be called when a new firmware download is +// about to start. The application may use this signal to initialize any +// progress display. +// +// void MyStartFunc(void); +// +//***************************************************************************** +//#define BL_START_FN_HOOK MyStartFunc + +//***************************************************************************** +// +// Informs an application of download progress. +// +// If hooked, this function will be called periodically during firmware +// download. The application may use this to update its user interface. +// When using a protocol which does not inform the client of the final size of +// the download in advance (e.g. TFTP), the ulTotal parameter will be 0, +// otherwise it indicates the expected size of the complete download. +// +// void MyProgressFunc(unsigned long ulCompleted, unsigned long ulTotal); +// +// where: +// +// - ulCompleted indicates the number of bytes already downloaded. +// - ulTotal indicates the number of bytes expected or 0 if this is not known. +// +//***************************************************************************** +//#define BL_PROGRESS_FN_HOOK MyProgressFunc + +//***************************************************************************** +// +// Informs an application that a download has completed. +// +// If hooked, this function will be called when a firmware download ends. +// The application may use this signal to update its user interface. Typically +// a system reset will occur shortly after this function returns as the boot +// loader attempts to boot the new image. +// +// void MyEndFunc(void); +// +//***************************************************************************** +//#define BL_END_FN_HOOK MyEndFunc + +//***************************************************************************** +// +// Allows an application to perform in-place data decryption during download. +// +// If hooked, this function will be called on receipt of any new block of +// downloaded firmware image data. The application must decrypt this data +// in place then return at which point the boot loader will write the data to +// flash. +// +// void MyDecryptionFunc(unsigned char *pucBuffer, unsigned long ulSize); +// +// where: +// +// - pucBuffer points to the first byte of data to be decrypted. +// - ulSize indicates the number of bytes of data at pucBuffer. +// +//***************************************************************************** +//#define BL_DECRYPT_FN_HOOK MyDecryptionFunc + +//***************************************************************************** +// +// Allows an application to force a new firmware download. +// +// If hooked, this function will be called after a system reset (following +// basic initialization and the initialization hook function) to give the +// application an opportunity to force a new firmware download. Depending upon +// the return code, the boot loader will either boot the existing firmware +// image or wait for a new download to be started. +// +// Note that this hook takes precedence over ENABLE_UPDATE_CHECK settings. If +// the hook function is defined, the basic GPIO check offered by +// ENABLE_UPDATE_CHECK does not take place. +// +// unsigned long MyCheckUpdateFunc(void); +// +// where the return code is 0 if the boot loader should boot the existing +// image (if found) or non-zero to indicate that the boot loader should retain +// control and wait for a new firmware image to be downloaded. +// +//***************************************************************************** +//#define BL_CHECK_UPDATE_FN_HOOK MyCheckUpdateFunc + +//***************************************************************************** +// +// Allows an application to replace the flash block erase function. +// +// If hooked, this function will be called whenever a block of flash is to +// be erased. The function must erase the block and block until the operation +// has completed. The size of the block which will be erased is defined by +// FLASH_BLOCK_SIZE. +// +// void MyFlashEraseFunc(unsigned long ulBlockAddr); +// +// where: +// +// - ulBlockAddr is the address of the flash block to be erased. +// +//***************************************************************************** +//#define BL_FLASH_ERASE_FN_HOOK MyFlashEraseFunc + +//***************************************************************************** +// +// Allows an application to replace the flash programming function. +// +// If hooked, this function will be called whenever a block of data is to be +// be written to flash. The function must program the supplied data and block +// until the operation has has completed. +// +// void MyFlashProgramFunc(unsigned long ulDstAddr, +// unsigned char *pucSrcData, +// unsigned long ulLength); +// +// where: +// +// - ulDstAddr is the address in flash at which the data is to be programmed. +// This must be a multiple of 4. +// - pucSrcData points to the first byte of the data to program. +// - ulLength is the number of bytes of data to program. This must be a +// multiple of 4. +// +//***************************************************************************** +//#define BL_FLASH_PROGRAM_FN_HOOK MyFlashProgramFunc + +//***************************************************************************** +// +// Allows an application to replace the flash error clear function. +// +// If hooked, this function will be called before each flash erase or program +// operation. The function must clear any flash error indicators and prepare +// to detect access violations that may occur in a future erase or program +// operation. +// +// void MyFlashClearErrorFunc(void); +// +//***************************************************************************** +//#define BL_FLASH_CL_ERR_FN_HOOK MyFlashClearErrorFunc + +//***************************************************************************** +// +// Reports whether or not a flash access violation error has occurred. +// +// If hooked, this function will be called after flash erase or program +// operations. The return code indicates to the caller whether or not +// an access violation error has occurred since the last call to the function +// defined by BL_FLASH_CL_ERR_FN_HOOK. +// +// unsigned long MyFlashErrorFunc(void); +// +// where the return code is 0 if no error has occurred or non-zero if an +// error was detected. +// +//***************************************************************************** +//#define BL_FLASH_ERROR_FN_HOOK MyFlashErrorFunc + +//***************************************************************************** +// +// Reports the total size of the device flash. +// +// If hooked, this function will be called to determine the size of the flash +// device. +// +// unsigned long MyFlashSizeFunc(void); +// +// where the return code is the total number of bytes of flash supported by the +// device. Note that this does not take into account any reserved space +// defined via the FLASH_RSVD_SPACE value in this header file. +// +//***************************************************************************** +//#define BL_FLASH_SIZE_FN_HOOK MyFlashSizeFunc + +//***************************************************************************** +// +// Reports the address of the first byte after the end of the device flash. +// +// If hooked, this function will be called to determine the address of the end +// of valid flash. +// +// unsigned long MyFlashEndFunc(void); +// +// where the return code is the address of the first byte after the end of flash. +// Note that this does not take into account any reserved space defined via +// the FLASH_RSVD_SPACE value in this header file. +// +//***************************************************************************** +//#define BL_FLASH_END_FN_HOOK MyFlashEndFunc + +//***************************************************************************** +// +// Checks whether the start address and size of an image are valid. +// +// If hooked, this function will be called whenever a new download is to be +// started. It determines whether or not an image of a particular size may be +// flashed at a given address. Valid addresses are: +// +// 1. APP_START_ADDRESS in all cases. +// 2. 0x00000000 if ENABLE_BL_UPDATE is defined. +// 3. The start of the reserved space if FLASH_RSVD_SPACE is defined. +// +// unsigned long MyFlashAddrCheckFunc(unsigned long ulAddr, +// unsigned long ulSize); +// +// where: +// +// - ulAddr is the address in flash at which the image is to be programmed. +// - ulSize is the total size of the image if known or 0 otherwise. +// +// The return code will be 0 if the address or size is invalid or a non-zero +// value if valid. +// +//***************************************************************************** +//#define BL_FLASH_AD_CHECK_FN_HOOK MyFlashAddrCheckFunc + +#endif // __BL_CONFIG_H__ diff --git a/boot_loader/bl_crc32.c b/boot_loader/bl_crc32.c new file mode 100644 index 0000000..9413987 --- /dev/null +++ b/boot_loader/bl_crc32.c @@ -0,0 +1,267 @@ +//***************************************************************************** +// +// bl_crc32.c - CRC32 calculation functions used in the boot loader. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#include +#include +#include "inc/hw_types.h" +#include "inc/hw_flash.h" +#include "inc/hw_sysctl.h" +#include "bl_config.h" +#include "boot_loader/bl_crc32.h" + +//***************************************************************************** +// +// Storage for the CRC32 calculation lookup table. +// +//***************************************************************************** +static uint32_t g_pui32CRC32Table[256]; + +//***************************************************************************** +// +// Initialize the CRC32 calculation table for the polynomial used. We pick +// the commonly used ANSI X 3.66 polymonial. This code was informed by an +// example found at http://www.createwindow.com/programming/crc32/index.htm. +// +//***************************************************************************** +static uint32_t +Reflect(uint32_t ui32Ref, uint8_t ui8Ch) +{ + uint_fast32_t ui32Value; + int_fast16_t i16Loop; + + // + // Clear our accumulator variable. + // + ui32Value = 0; + + // + // Swap bit 0 for bit 7, bit 1 for bit 6, etc. + // + for(i16Loop = 1; i16Loop < (ui8Ch + 1); i16Loop++) + { + if(ui32Ref & 1) + { + ui32Value |= 1 << (ui8Ch - i16Loop); + } + ui32Ref >>= 1; + } + + // + // Return the reflected value. + // + return(ui32Value); +} + +//***************************************************************************** +// +// Initialize the lookup table used in calculating the CRC32 value. +// +//***************************************************************************** +void +InitCRC32Table(void) +{ + uint_fast32_t ui32Polynomial; + int_fast16_t i16Loop, i16Bit; + + // + // This is the ANSI X 3.66 polynomial as required by the DFU + // specification. + // + ui32Polynomial = 0x04c11db7; + + for(i16Loop = 0; i16Loop <= 0xFF; i16Loop++) + { + g_pui32CRC32Table[i16Loop]=Reflect(i16Loop, 8) << 24; + for (i16Bit = 0; i16Bit < 8; i16Bit++) + { + g_pui32CRC32Table[i16Loop] = ((g_pui32CRC32Table[i16Loop] << 1) ^ + (g_pui32CRC32Table[i16Loop] & + ((uint32_t)1 << 31) ? + ui32Polynomial : 0)); + } + g_pui32CRC32Table[i16Loop] = Reflect(g_pui32CRC32Table[i16Loop], 32); + } +} + +//***************************************************************************** +// +// Calculate the CRC for the supplied block of data. +// +//***************************************************************************** +uint32_t +CalculateCRC32(uint8_t *pui8Data, uint32_t ui32Length, uint32_t ui32CRC) +{ + uint32_t ui32Count; + uint8_t *pui8Buffer; + uint8_t ui8Char; + + // + // Get a pointer to the start of the data and the number of bytes to + // process. + // + pui8Buffer = pui8Data; + ui32Count = ui32Length; + + // + // Perform the algorithm on each byte in the supplied buffer using the + // lookup table values calculated in InitCRC32Table(). + // + while(ui32Count--) + { + ui8Char = *pui8Buffer++; + ui32CRC = (ui32CRC >> 8) ^ g_pui32CRC32Table[(ui32CRC & 0xFF) ^ + ui8Char]; + } + + // + // Return the result. + // + return(ui32CRC); +} + +//***************************************************************************** +// +//! Checks that the embedded CRC in the image matches the expected value. +//! +//! \param pui32Image points to the start of the firmware image in memory. +//! +//! This function finds the firmware image information header and verifies that +//! the embedded CRC32 matches one calculated over the image. +//! +//! \return Returns \b CHECK_CRC_OK if the CRC calculated matches the value +//! embedded in the image, \b CHECK_CRC_NO_HEADER if no image information +//! header was found at the top of the vector table, \b CHECK_CRC_BAD_CRC if +//! an embedded CRC was found but did not match the calculated value or \b +//! CHECK_CRC_ZERO_LENGTH if the length field of the image information header +//! contains 0 (likely indicating that the image had not been run through the +//! binpack tool which inserts the length and CRC values into the header). +// +//***************************************************************************** +uint32_t +CheckImageCRC32(uint32_t *pui32Image) +{ + uint32_t ui32Loop, ui32FlashSize, ui32CRC; + + // + // Determine the size of flash (giving an upper bound for the image + // size). + // + if(CLASS_IS_TM4C129) + { + // + // Get the flash size from the FLASH_PP register. + // + ui32FlashSize = ((2048 * ((HWREG(FLASH_PP) & FLASH_PP_SIZE_M) + 1)) - + APP_START_ADDRESS); + } + else + { + // + // Compute the size of the flash. + // + ui32FlashSize = (((HWREG(SYSCTL_DC0) & SYSCTL_DC0_FLASHSZ_M) << 11) + + 0x800 - APP_START_ADDRESS); + } + + // + // Scan for the image information header marker bytes. Given that the + // largest possible vector table includes 16 system exceptions and 240 + // IC-specific vectors, we only need to search 257 words into memory before + // giving up. + // + for(ui32Loop = 0; ui32Loop < 257; ui32Loop++) + { + // + // Have we found the header marker words? + // + if((pui32Image[ui32Loop] == 0xFF01FF02) && + (pui32Image[ui32Loop + 1] == 0xFF03FF04)) + { + // + // Yes. Check to see if the length field is 0xFFFFFFFF. This + // likely indicates that the image has not been processed by the + // binpack tool which adds the length and CRC information to the + // image header. + // + if(pui32Image[ui32Loop + 2] == 0xFFFFFFFF) + { + // + // The header reports an image size of 0 so we can't go on and + // check the CRC. + // + return(CHECK_CRC_NO_LENGTH); + } + + // + // Extract the image length and ensure that it is sensible + // given the flash size. We assume the length is invalid if it + // is larger than the available flash size or smaller than the + // space taken up by the vector table and header we've already + // scanned through. + // + if((pui32Image[ui32Loop + 2] > ui32FlashSize) || + (pui32Image[ui32Loop + 2] < + ((ui32Loop + 4) * sizeof(uint32_t)))) + { + // + // The header reports an image size that is larger than the + // available flash so this is obviously incorrect. Fail the + // check. + // + return(CHECK_CRC_BAD_LENGTH); + } + + // + // Calculate the CRC32 value for the image. Note that we skip the + // 4 bytes that hold the check CRC. + // + ui32CRC = CalculateCRC32((uint8_t *)pui32Image, + (ui32Loop + 3) * sizeof(uint32_t), + 0xffffffff); + ui32CRC = CalculateCRC32((uint8_t *)&pui32Image[ui32Loop + 4], + (pui32Image[ui32Loop + 2] - + ((ui32Loop + 4) * sizeof(uint32_t))), + ui32CRC); + ui32CRC ^= 0xffffffff; + + // + // Determine whether the calculated CRC matches the value stored + // in the image information header. + // + if(ui32CRC == pui32Image[ui32Loop + 3]) + { + return(CHECK_CRC_OK); + } + else + { + return(CHECK_CRC_BAD_CRC); + } + } + } + + // + // If we drop out the loop, there was no image information header so + // fail the call. + // + return(CHECK_CRC_NO_HEADER); +} diff --git a/boot_loader/bl_crc32.h b/boot_loader/bl_crc32.h new file mode 100644 index 0000000..ab8848d --- /dev/null +++ b/boot_loader/bl_crc32.h @@ -0,0 +1,49 @@ +//***************************************************************************** +// +// bl_crc32.h - Public header for the boot loader CRC32 functions. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_CRC32_H__ +#define __BL_CRC32_H__ + +//***************************************************************************** +// +// Return codes generated by CheckImageCRC32(). +// +//***************************************************************************** +#define CHECK_CRC_OK 0 +#define CHECK_CRC_NO_HEADER 1 +#define CHECK_CRC_NO_LENGTH 2 +#define CHECK_CRC_BAD_LENGTH 3 +#define CHECK_CRC_BAD_CRC 4 + +//***************************************************************************** +// +// Exported function prototypes. +// +//***************************************************************************** +extern void InitCRC32Table(void); +extern uint32_t CheckImageCRC32(uint32_t *pui32Image); +extern uint32_t CalculateCRC32(uint8_t *pui8Data, uint32_t ui32Length, + uint32_t ui32CRC); + +#endif diff --git a/boot_loader/bl_crystal.h b/boot_loader/bl_crystal.h new file mode 100644 index 0000000..268d102 --- /dev/null +++ b/boot_loader/bl_crystal.h @@ -0,0 +1,79 @@ +//***************************************************************************** +// +// bl_crystal.h - Macros to convert a CRYSTAL_FREQ value into the appropriate +// RCC XTAL field define. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_CRYSTAL_H__ +#define __BL_CRYSTAL_H__ + +//***************************************************************************** +// +// Convert the CRYSTAL_FREQ value into the corresponding SYSCTL_RCC_XTAL_??? +// value. +// +//***************************************************************************** +#if CRYSTAL_FREQ == 3579545 +#define XTAL_VALUE SYSCTL_RCC_XTAL_3_57MHZ +#elif CRYSTAL_FREQ == 3686400 +#define XTAL_VALUE SYSCTL_RCC_XTAL_3_68MHZ +#elif CRYSTAL_FREQ == 4000000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_4MHZ +#elif CRYSTAL_FREQ == 4096000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_4_09MHZ +#elif CRYSTAL_FREQ == 4915200 +#define XTAL_VALUE SYSCTL_RCC_XTAL_4_91MHZ +#elif CRYSTAL_FREQ == 5000000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_5MHZ +#elif CRYSTAL_FREQ == 5120000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_5_12MHZ +#elif CRYSTAL_FREQ == 6000000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_6MHZ +#elif CRYSTAL_FREQ == 6144000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_6_14MHZ +#elif CRYSTAL_FREQ == 7372800 +#define XTAL_VALUE SYSCTL_RCC_XTAL_7_37MHZ +#elif CRYSTAL_FREQ == 8000000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_8MHZ +#elif CRYSTAL_FREQ == 8192000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_8_19MHZ +#elif CRYSTAL_FREQ == 10000000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_10MHZ +#elif CRYSTAL_FREQ == 12000000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_12MHZ +#elif CRYSTAL_FREQ == 12288000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_12_2MHZ +#elif CRYSTAL_FREQ == 13560000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_13_5MHZ +#elif CRYSTAL_FREQ == 14318180 +#define XTAL_VALUE SYSCTL_RCC_XTAL_14_3MHZ +#elif CRYSTAL_FREQ == 16000000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_16MHZ +#elif CRYSTAL_FREQ == 16384000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_16_3MHZ +#elif CRYSTAL_FREQ == 25000000 +#define XTAL_VALUE SYSCTL_RCC_XTAL_25MHZ +#else +#error ERROR: Unknown CRYSTAL_FREQ value specified! +#endif + +#endif // __BL_CRYSTAL_H__ diff --git a/boot_loader/bl_decrypt.c b/boot_loader/bl_decrypt.c new file mode 100644 index 0000000..6db64da --- /dev/null +++ b/boot_loader/bl_decrypt.c @@ -0,0 +1,64 @@ +//***************************************************************************** +// +// bl_decrypt.c - Code for performing an in-place decryption of the firmware +// image as it is downloaded. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "bl_config.h" +#include "boot_loader/bl_decrypt.h" + +//***************************************************************************** +// +//! \addtogroup bl_decrypt_api +//! @{ +// +//***************************************************************************** +#if defined(ENABLE_DECRYPTION) || defined(DOXYGEN) + +//***************************************************************************** +// +//! Performs an in-place decryption of downloaded data. +//! +//! \param pui8Buffer is the buffer that holds the data to decrypt. +//! \param ui32Size is the size, in bytes, of the buffer that was passed in via +//! the \e pui8Buffer parameter. +//! +//! This function is a stub that could provide in-place decryption of the data +//! that is being downloaded to the device. +//! +//! \return None. +// +//***************************************************************************** +void +DecryptData(uint8_t *pui8Buffer, uint32_t ui32Size) +{ +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif + diff --git a/boot_loader/bl_decrypt.h b/boot_loader/bl_decrypt.h new file mode 100644 index 0000000..c8eed0c --- /dev/null +++ b/boot_loader/bl_decrypt.h @@ -0,0 +1,35 @@ +//***************************************************************************** +// +// bl_decrypt.h - Definitions for the decryption function. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_DECRYPT_H__ +#define __BL_DECRYPT_H__ + +//***************************************************************************** +// +// Prototype for the decryption function. +// +//***************************************************************************** +extern void DecryptData(uint8_t *pui8Buffer, uint32_t ui32Size); + +#endif // __BL_DECRYPT_H__ diff --git a/boot_loader/bl_emac.c b/boot_loader/bl_emac.c new file mode 100644 index 0000000..c098da0 --- /dev/null +++ b/boot_loader/bl_emac.c @@ -0,0 +1,1914 @@ +//***************************************************************************** +// +// bl_emac.c - Functions to update via Ethernet. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include +#include "bl_config.h" +#include "inc/hw_emac.h" +#include "inc/hw_flash.h" +#include "inc/hw_gpio.h" +#include "inc/hw_memmap.h" +#include "inc/hw_nvic.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/pin_map.h" +#include "driverlib/emac.h" +#include "driverlib/sysctl.h" +#include "boot_loader/bl_decrypt.h" +#include "boot_loader/bl_flash.h" +#include "boot_loader/bl_hooks.h" +#include "driverlib/rom.h" +// +// Define ROM_SysCtlClockFreqSet() for snowflake RA0. Even though this function +// is deprecated in RA0 ROM, the function operates correctly when +// SYSCTL_MOSCCTL register is configured correctly prior to calling this +// function. +// +#if defined(TARGET_IS_TM4C129_RA0) +#define ROM_SysCtlClockFreqSet \ + ((uint32_t (*)(uint32_t ui32Config, \ + uint32_t ui32SysClock))ROM_SYSCTLTABLE[48]) +#endif + +// +// Define MAP_GPIOPadConfigSet() for the Boot Loader for Snowflake. +// This function fails in Snowflake for higher drive strengths, it will work +// properly for the instances where it is used here in the boot loader. +// +#if defined(TARGET_IS_TM4C129_RA0) || \ + defined(TARGET_IS_TM4C129_RA1) +#define ROM_GPIOPadConfigSet \ + ((void (*)(uint32_t ui32Port, \ + uint8_t ui8Pins, \ + uint32_t ui32Strength, \ + uint32_t ui32PadType))ROM_GPIOTABLE[5]) +#endif + +#include "driverlib/rom_map.h" + +//***************************************************************************** +// +//! \addtogroup bl_emac_api +//! @{ +// +//***************************************************************************** + +#if defined(ENET_ENABLE_UPDATE) || defined(DOXYGEN) +//***************************************************************************** +// +// Make sure that the crystal frequency is defined. +// +//***************************************************************************** +#if !defined(CRYSTAL_FREQ) +#error ERROR: CRYSTAL_FREQ must be defined for Ethernet update! +#endif + +//***************************************************************************** +// +// Make sure that boot loader update is not enabled (it is not supported via +// BOOTP given that there is no way to distinguish between a normal firmware +// image and a boot loader update image). +// +//***************************************************************************** +#if defined(ENABLE_BL_UPDATE) +#error ERROR: Updating the boot loader is not supported over Ethernet! +#endif + +//***************************************************************************** +// +// TFTP packets contain 512 bytes of data and a packet shorter than this +// indicates the end of the transfer. +// +//***************************************************************************** +#define TFTP_BLOCK_SIZE 512 + +//***************************************************************************** +// +// uIP uses memset, so a simple one is provided here. This is not as efficient +// as the one in the C library (from an execution time perspective), but it is +// much smaller. +// +//***************************************************************************** +void * +my_memset(void *pvDest, int iChar, size_t i32Length) +{ + int8_t *pi8Buf = (int8_t *)pvDest; + + // + // Fill the buffer with the given character. + // + while(i32Length--) + { + *pi8Buf++ = iChar; + } + + // + // Return a pointer to the beginning of the buffer. + // + return(pvDest); +} + +//***************************************************************************** +// +// uIP uses memcpy, so a simple one is provided here. This is not as efficient +// as the one in the C library (from an execution time perspective), but it is +// much smaller. +// +//***************************************************************************** +void * +my_memcpy(void *pvDest, const void *pvSrc, size_t i32Length) +{ + const int8_t *pi8Src = (const int8_t *)pvSrc; + int8_t *pi8Dest = (int8_t *)pvDest; + + // + // Copy bytes from the source buffer to the destination buffer. + // + while(i32Length--) + { + *pi8Dest++ = *pi8Src++; + } + + // + // Return a pointer to the beginning of the destination buffer. + // + return(pvDest); +} + +//***************************************************************************** +// +// Directly include the uIP code if using Ethernet for the update. This allows +// non-Ethernet boot loader builds to not have to supply the uip-conf.h file +// that would otherwise be required. +// +//***************************************************************************** +#define memcpy my_memcpy +#define memset my_memset +#undef htonl +#undef ntohl +#undef htons +#undef ntohs +#include "third_party/uip-1.0/uip/pt.h" +#include "third_party/uip-1.0/uip/uip_arp.c" +#undef BUF +#include "third_party/uip-1.0/uip/uip.c" + +//***************************************************************************** +// +// A prototype for the function (in the startup code) for a predictable length +// delay. +// +//***************************************************************************** +extern void Delay(uint32_t ui32Count); + +//***************************************************************************** +// +// Defines for setting up the system clock. +// +//***************************************************************************** +#define SYSTICKHZ 100 +#define SYSTICKMS (1000 / SYSTICKHZ) + +//***************************************************************************** +// +// UIP Timers (in ms) +// +//***************************************************************************** +#define UIP_PERIODIC_TIMER_MS 50 +#define UIP_ARP_TIMER_MS 10000 + +//***************************************************************************** +// +// This structure defines the fields in a BOOTP request/reply packet. +// +//***************************************************************************** +typedef struct +{ + // + // The operation; 1 is a request, 2 is a reply. + // + uint8_t ui8Op; + + // + // The hardware type; 1 is Ethernet. + // + uint8_t ui8HType; + + // + // The hardware address length; for Ethernet this will be 6, the length of + // the MAC address. + // + uint8_t ui8HLen; + + // + // Hop count, used by gateways for cross-gateway booting. + // + uint8_t ui8Hops; + + // + // The transaction ID. + // + uint32_t ui32XID; + + // + // The number of seconds elapsed since the client started trying to boot. + // + uint16_t ui16Secs; + + // + // The BOOTP flags. + // + uint16_t ui16Flags; + + // + // The client's IP address, if it knows it. + // + uint32_t ui32CIAddr; + + // + // The client's IP address, as assigned by the BOOTP server. + // + uint32_t ui32YIAddr; + + // + // The TFTP server's IP address. + // + uint32_t ui32SIAddr; + + // + // The gateway IP address, if booting cross-gateway. + // + uint32_t ui32GIAddr; + + // + // The hardware address; for Ethernet this is the MAC address. + // + uint8_t pui8CHAddr[16]; + + // + // The name, or nickname, of the server that should handle this BOOTP + // request. + // + char pcSName[64]; + + // + // The name of the boot file to be loaded via TFTP. + // + char pcFile[128]; + + // + // Optional vendor-specific area; not used for BOOTP. + // + uint8_t pui8Vend[64]; +} +tBOOTPPacket; + +//***************************************************************************** +// +// The BOOTP commands. +// +//***************************************************************************** +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + +//***************************************************************************** +// +// The TFTP commands. +// +//***************************************************************************** +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + +//***************************************************************************** +// +// The UDP ports used by the BOOTP protocol. +// +//***************************************************************************** +#define BOOTP_SERVER_PORT 67 +#define BOOTP_CLIENT_PORT 68 + +//***************************************************************************** +// +// The UDP port for the TFTP server. +// +//***************************************************************************** +#define TFTP_PORT 69 + +//***************************************************************************** +// +// The MAC address of the Ethernet interface. +// +//***************************************************************************** +#ifdef ENET_MAC_ADDR0 +static struct uip_eth_addr g_sMACAddr = +{ + { + ENET_MAC_ADDR0, + ENET_MAC_ADDR1, + ENET_MAC_ADDR2, + ENET_MAC_ADDR3, + ENET_MAC_ADDR4, + ENET_MAC_ADDR5 + } +}; +#else +static struct uip_eth_addr g_sMACAddr; +#endif + +//***************************************************************************** +// +// The number of SysTick interrupts since the start of the boot loader. +// +//***************************************************************************** +static uint32_t g_ui32Ticks; + +//***************************************************************************** +// +// The seed for the random number generator. +// +//***************************************************************************** +static uint32_t g_ui32RandomSeed; + +//***************************************************************************** +// +// The number of milliseconds since the last call to uip_udp_periodic(). +// +//***************************************************************************** +static volatile uint32_t g_ui32PeriodicTimer; + +//***************************************************************************** +// +// The number of milliseconds since the last call to uip_arp_timer(). +// +//***************************************************************************** +static volatile uint32_t g_ui32ARPTimer; + +//***************************************************************************** +// +// The transaction ID of the most recently sent out BOOTP request. +// +//***************************************************************************** +static uint32_t g_ui32XID; + +//***************************************************************************** +// +// The state for the proto-thread that handles the BOOTP process. +// +//***************************************************************************** +static struct pt g_sThread; + +//***************************************************************************** +// +// The amount of time to wait for a BOOTP reply before sending out a new BOOTP +// request. +// +//***************************************************************************** +static uint32_t g_ui32Delay; + +//***************************************************************************** +// +// The target time (relative to g_ui32Ticks) when the next timeout occurs. +// +//***************************************************************************** +static uint32_t g_ui32Target; + +//***************************************************************************** +// +// The IP address of the TFTP server. +// +//***************************************************************************** +static uip_ipaddr_t g_sServerAddr; + +//***************************************************************************** +// +// The name of the file to be read from the TFTP server. +// +//***************************************************************************** +static char g_pcFilename[128]; + +//***************************************************************************** +// +// The end of flash. If there is not a reserved block at the end of flash, +// this is the real end of flash. If there is a reserved block, this is the +// start of the reserved block (i.e. the virtual end of flash). +// +//***************************************************************************** +static uint32_t g_ui32FlashEnd; + +//***************************************************************************** +// +// The current block being read from the TFTP server. +// +//***************************************************************************** +static uint32_t g_ui32TFTPBlock; + +//***************************************************************************** +// +// The number of TFTP retries. +// +//***************************************************************************** +static uint32_t g_ui32TFTPRetries; + +//***************************************************************************** +// +// The UDP socket used to communicate with the BOOTP and TFTP servers (in +// sequence). +// +//***************************************************************************** +struct uip_udp_conn *g_pConn; + +//***************************************************************************** +// +// The current link status. +// +//***************************************************************************** +static uint32_t g_ui32Link; + +//***************************************************************************** +// +// Ethernet DMA descriptors. +// +// Although uIP uses a single buffer, the MAC hardware needs a minimum of +// 3 receive descriptors to operate. +// +//***************************************************************************** +#define NUM_TX_DESCRIPTORS 3 +#define NUM_RX_DESCRIPTORS 3 +tEMACDMADescriptor g_psRxDescriptor[NUM_TX_DESCRIPTORS]; +tEMACDMADescriptor g_psTxDescriptor[NUM_RX_DESCRIPTORS]; +uint32_t g_ui32RxDescIndex; +uint32_t g_ui32TxDescIndex; + +//***************************************************************************** +// +// Transmit and receive buffers. +// +//***************************************************************************** +#define RX_BUFFER_SIZE 1536 +#define TX_BUFFER_SIZE 1536 +uint8_t g_pui8RxBuffer[RX_BUFFER_SIZE]; +uint8_t g_pui8TxBuffer[TX_BUFFER_SIZE]; + +//***************************************************************************** +// +//! Handles the SysTick interrupt. +//! +//! This function is called when the SysTick interrupt occurs. It simply +//! keeps a running count of interrupts, used as a time basis for the BOOTP and +//! TFTP protocols. +//! +//! \return None. +// +//***************************************************************************** +void +SysTickIntHandler(void) +{ + // + // Increment the tick count. + // + g_ui32Ticks++; + g_ui32PeriodicTimer += SYSTICKMS; + g_ui32ARPTimer += SYSTICKMS; +} + +//***************************************************************************** +// +//! Computes a new random number. +//! +//! This function computes a new pseudo-random number, using a linear +//! congruence random number generator. Note that if the entire 32-bits of the +//! produced random number are not being used, the upper N bits should be used +//! instead of the lower N bits as they are much more random (for example, use +//! ``RandomNumber() >> 28'' instead of ``RandomNumber() & 15''). +//! +//! \return Returns a 32-bit pseudo-random number. +// +//***************************************************************************** +static uint32_t +RandomNumber(void) +{ + // + // Generate a new pseudo-random number with a linear congruence random + // number generator. This new random number becomes the seed for the next + // random number. + // + g_ui32RandomSeed = (g_ui32RandomSeed * 1664525) + 1013904223; + + // + // Return the new random number. + // + return(g_ui32RandomSeed); +} + +//***************************************************************************** +// +// Read a packet from the DMA receive buffer into the uIP packet buffer. +// +//***************************************************************************** +static int32_t +PacketReceive(uint8_t *pui8Buf, int32_t i32BufLen) +{ + int_fast32_t i32FrameLen, i32Loop; + + // + // By default, we assume we got a bad frame. + // + i32FrameLen = 0; + + // + // See if the receive descriptor contains a valid frame. Look for a + // descriptor error, indicating that the incoming packet was truncated or, + // if this is the last frame in a packet, the receive error bit. + // + if(!(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & + DES0_RX_STAT_ERR)) + { + // + // We have a valid frame so copy the content to the supplied buffer. + // First check that the "last descriptor" flag is set. We sized the + // receive buffer such that it can always hold a valid frame so this + // flag should never be clear at this point but... + // + if(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & + DES0_RX_STAT_LAST_DESC) + { + i32FrameLen = + ((g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & + DES0_RX_STAT_FRAME_LENGTH_M) >> + DES0_RX_STAT_FRAME_LENGTH_S); + + // + // Sanity check. This shouldn't be required since we sized the uIP + // buffer such that it's the same size as the DMA receive buffer + // but, just in case... + // + if(i32FrameLen > i32BufLen) + { + i32FrameLen = i32BufLen; + } + + // + // Copy the data from the DMA receive buffer into the provided + // frame buffer. + // + for(i32Loop = 0; i32Loop < i32FrameLen; i32Loop++) + { + pui8Buf[i32Loop] = g_pui8RxBuffer[i32Loop]; + } + } + } + + // + // Move on to the next descriptor in the chain. + // + g_ui32RxDescIndex++; + if(g_ui32RxDescIndex == NUM_RX_DESCRIPTORS) + { + g_ui32RxDescIndex = 0; + } + + // + // Mark the next descriptor in the ring as available for the receiver to + // write into. + // + g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus = DES0_RX_CTRL_OWN; + + // + // Return the Frame Length + // + return(i32FrameLen); +} + +//***************************************************************************** +// +// Transmit a packet from the supplied buffer. +// +//***************************************************************************** +static int32_t +PacketTransmit(uint8_t *pui8Buf, int32_t i32BufLen) +{ + int_fast32_t i32Loop; + + // + // Wait for the previous packet to be transmitted. + // + while(g_psTxDescriptor[g_ui32TxDescIndex].ui32CtrlStatus & + DES0_TX_CTRL_OWN) + { + } + + // + // Check that we're not going to overflow the transmit buffer. This + // shouldn't be necessary since the uIP buffer is smaller than our DMA + // transmit buffer but, just in case... + // + if(i32BufLen > TX_BUFFER_SIZE) + { + i32BufLen = TX_BUFFER_SIZE; + } + + // + // Copy the packet data into the transmit buffer. + // + for(i32Loop = 0; i32Loop < i32BufLen; i32Loop++) + { + g_pui8TxBuffer[i32Loop] = pui8Buf[i32Loop]; + } + + // + // Move to the next descriptor. + // + g_ui32TxDescIndex++; + if(g_ui32TxDescIndex == NUM_TX_DESCRIPTORS) + { + g_ui32TxDescIndex = 0; + } + + // + // Fill in the packet size and tell the transmitter to start work. + // + g_psTxDescriptor[g_ui32TxDescIndex].ui32Count = (uint32_t)i32BufLen; + g_psTxDescriptor[g_ui32TxDescIndex].ui32CtrlStatus = + (DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG | + DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_IP_ALL_CKHSUMS | + DES0_TX_CTRL_CHAINED | DES0_TX_CTRL_OWN); + + // + // Tell the DMA to reacquire the descriptor now that we've filled it in. + // + ROM_EMACTxDMAPollDemand(EMAC0_BASE); + + // + // Return the number of bytes sent. + // + return(i32BufLen); +} + +//***************************************************************************** +// +//! Constructs and sends a BOOTP request packet. +//! +//! This function constructs a BOOTP request packet and sends it as a broadcast +//! message to the network. +//! +//! \return None. +// +//***************************************************************************** +static void +SendBOOTPRequest(void) +{ + uint8_t *pui8Packet = (uint8_t *)uip_appdata; + tBOOTPPacket *psBOOTP = (tBOOTPPacket *)uip_appdata; + uint32_t ui32Idx; + + // + // Zero fill the BOOTP request packet. + // + for(ui32Idx = 0; ui32Idx < sizeof(tBOOTPPacket); ui32Idx++) + { + pui8Packet[ui32Idx] = 0; + } + + // + // Construct a BOOTP request. + // + psBOOTP->ui8Op = BOOTP_REQUEST; + + // + // Set the hardware type to Ethernet. + // + psBOOTP->ui8HType = 0x01; + + // + // Set the hardware address length to 6. + // + psBOOTP->ui8HLen = 0x06; + + // + // Choose a random number for the transaction ID. + // + psBOOTP->ui32XID = g_ui32XID = RandomNumber(); + + // + // Set the number of seconds since we started. + // + psBOOTP->ui16Secs = HTONS(g_ui32Ticks / SYSTICKHZ); + + // + // Fill in the Ethernet MAC address. + // + for(ui32Idx = 0; ui32Idx < 6; ui32Idx++) + { + psBOOTP->pui8CHAddr[ui32Idx] = g_sMACAddr.addr[ui32Idx]; + } + + // + // Set the server name if defined. + // +#ifdef ENET_BOOTP_SERVER + for(ui32Idx = 0; + (psBOOTP->pcSName[ui32Idx] = ENET_BOOTP_SERVER[ui32Idx]) != 0; + ui32Idx++) + { + } +#endif + + // + // Send the BOOTP request packet. + // + uip_udp_send(sizeof(tBOOTPPacket)); +} + +//***************************************************************************** +// +//! Parses a packet checking for a BOOTP reply message. +//! +//! This function parses a packet to determine if it is a BOOTP reply to our +//! currently outstanding BOOTP request. If a valid reply is found, the +//! appropriate information from the packet is extracted and saved. +//! +//! \return Returns 1 if a valid BOOTP reply message was found and 0 otherwise. +// +//***************************************************************************** +static uint32_t +ParseBOOTPReply(void) +{ + tBOOTPPacket *psBOOTP = (tBOOTPPacket *)uip_appdata; + uint32_t ui32Idx; + + // + // See if this is a reply for our current BOOTP request. + // + if((psBOOTP->ui8Op != BOOTP_REPLY) || + (psBOOTP->ui32XID != g_ui32XID) || + (*(uint32_t *)psBOOTP->pui8CHAddr != *(uint32_t *)g_sMACAddr.addr) || + (*(uint16_t *)(psBOOTP->pui8CHAddr + 4) != + *(uint16_t *)(g_sMACAddr.addr + 4))) + { + return(0); + } + + // + // Extract our IP address from the response. + // + *((uint32_t *)(void *)(&uip_hostaddr)) = psBOOTP->ui32YIAddr; + + // + // Extract the server address from the response. + // + *((uint32_t *)(void *)(&g_sServerAddr)) = psBOOTP->ui32SIAddr; + + // + // Save the boot file name. + // + for(ui32Idx = 0; + ((g_pcFilename[ui32Idx] = psBOOTP->pcFile[ui32Idx]) != 0) && + (ui32Idx < (sizeof(g_pcFilename) - 1)); + ui32Idx++) + { + } + g_pcFilename[ui32Idx] = 0; + + // + // A valid BOOTP reply was found and decoded. + // + return(1); +} + + +//***************************************************************************** +// +//! Constructs and sends a TFTP error packet. +//! +//! This function constructs a TFTP read request packet (RRQ) and sends it to +//! the server. +//! +//! \return None. +// +//***************************************************************************** +static void +SendTFTPError(uint16_t ui16Error, char *pcString) +{ + uint8_t *pui8Packet = (uint8_t *)uip_appdata; + int32_t i32Len; + + pui8Packet[0] = (TFTP_ERROR >> 8) & 0xff; + pui8Packet[1] = TFTP_ERROR & 0xff; + pui8Packet[2] = (ui16Error >> 8) & 0xFF; + pui8Packet[3] = ui16Error & 0xFF; + + // + // Get ready to copy the error string. + // + i32Len = 4; + pui8Packet += 4; + + // + // Copy as much of the string as we can fit. + // + while((i32Len < (UIP_APPDATA_SIZE - 1)) && *pcString) + { + *pui8Packet++ = *pcString++; + i32Len++; + } + + // + // Write the terminating 0. + // + *pui8Packet = (uint8_t)0; + + // + // Send the error packet. + // + uip_udp_send(i32Len + 1); +} + +//***************************************************************************** +// +//! Constructs and sends a TFTP read packet. +//! +//! This function constructs a TFTP read request packet (RRQ) and sends it to +//! the server. +//! +//! \return None. +// +//***************************************************************************** +static void +SendTFTPGet(void) +{ + uint8_t *pui8Packet = (uint8_t *)uip_appdata; + uint32_t ui32Idx; + char *pcFilename; + + // + // The TFTP RRQ packet should be sent to the TFTP server port. + // + g_pConn->rport = HTONS(TFTP_PORT); + + // + // Set the TFTP packet opcode to RRQ. + // + pui8Packet[0] = (TFTP_RRQ >> 8) & 0xff; + pui8Packet[1] = TFTP_RRQ & 0xff; + + // + // Copy the file name into the RRQ packet. + // + for(ui32Idx = 2, pcFilename = g_pcFilename; + (pui8Packet[ui32Idx++] = *pcFilename++) != 0; ) + { + } + + // + // Set the transfer mode to binary. + // + for(pcFilename = "octet"; (pui8Packet[ui32Idx++] = *pcFilename++) != 0; ) + { + } + + // + // Send the TFTP read packet. + // + uip_udp_send(ui32Idx); +} + +//***************************************************************************** +// +//! Parses a packet checking for a TFTP data packet. +//! +//! This function parses a packet to determine if it is a TFTP data packet for +//! out current TFTP transfer. If a valid packet is found, the contents of the +//! packet are programmed into flash. +//! +//! \return Returns 1 if this packet was the last packet of the TFTP data +//! transfer and 0 otherwise. +// +//***************************************************************************** +static uint32_t +ParseTFTPData(void) +{ + uint8_t *pui8Packet = (uint8_t *)uip_appdata; + uint32_t ui32FlashAddr; + uint32_t ui32Idx; + + // + // See if this is a TFTP data packet. + // + if((pui8Packet[0] != ((TFTP_DATA >> 8) && 0xff)) || + (pui8Packet[1] != (TFTP_DATA & 0xff))) + { + return(0); + } + + // + // If the remote port on our connection is still the TFTP server port (i.e. + // this is the first data packet), then copy the transaction ID for the + // TFTP data connection into our connection. This will ensure that our + // response will be sent to the correct port. + // + if(g_pConn->rport == HTONS(TFTP_PORT)) + { + g_pConn->rport = + ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])->srcport; + } + + // + // See if this is the correct data packet. + // + if((pui8Packet[2] != ((g_ui32TFTPBlock >> 8) & 0xff)) || + (pui8Packet[3] != (g_ui32TFTPBlock & 0xff))) + { + // + // Since the wrong data packet was sent, resend the ACK for it since + // we've already processed it. + // + pui8Packet[0] = (TFTP_ACK >> 8) & 0xff; + pui8Packet[1] = TFTP_ACK & 0xff; + uip_udp_send(4); + + // + // Ignore this packet. + // + return(0); + } + + // + // What address are we about to program to? + // + ui32FlashAddr = + ((g_ui32TFTPBlock - 1) * TFTP_BLOCK_SIZE) + APP_START_ADDRESS; + + // + // Do not program this data into flash if it is beyond the end of flash. + // + if(ui32FlashAddr < g_ui32FlashEnd) + { + // + // If this is the first block and we have been provided with a start + // hook function, call it here to indicate that we are about to begin + // flashing a new image. + // +#ifdef BL_START_FN_HOOK + if(g_ui32TFTPBlock == 1) + { + BL_START_FN_HOOK(); + } +#endif + + // + // Clear any flash error indicator. + // + BL_FLASH_CL_ERR_FN_HOOK(); + + // + // If this is the first data packet and code protection is enabled, + // then erase the entire flash. + // +#ifdef FLASH_CODE_PROTECTION + if(g_ui32TFTPBlock == 1) + { + // + // Loop through the pages in the flash, excluding the pages that + // contain the boot loader and the optional reserved space. + // + for(ui32Idx = APP_START_ADDRESS; ui32Idx < g_ui32FlashEnd; + ui32Idx += FLASH_PAGE_SIZE) + { + // + // Erase this block of the flash. + // + BL_FLASH_ERASE_FN_HOOK((ui32Idx); + } + } +#else + // + // Flash code protection is not enabled, so see if the data in this + // packet will be programmed to the beginning of a flash block. We + // assume that the flash block size is always a multiple of 1KB so, + // since each TFTP packet is 512 bytes and that the start must always + // be on a flash page boundary, we can be sure that we will hit the + // start of each page as we receive packets. + // + if(!(ui32FlashAddr & (FLASH_PAGE_SIZE - 1))) + { + // + // Erase this block of the flash. + // + BL_FLASH_ERASE_FN_HOOK(ui32FlashAddr); + } +#endif + + // + // Decrypt the data if required. + // +#ifdef BL_DECRYPT_FN_HOOK + BL_DECRYPT_FN_HOOK(pui8Packet + 4, uip_len - 4); +#endif + + // + // Program this block of data into flash. + // + BL_FLASH_PROGRAM_FN_HOOK(ui32FlashAddr, (pui8Packet + 4), + (uip_len - 4)); + + // + // If a progress reporting hook function has been provided, call it + // here. The TFTP protocol doesn't let us know how large the image is + // before it starts the transfer so we pass 0 as the ui32Total + // parameter to indicate this. + // +#ifdef BL_PROGRESS_FN_HOOK + BL_PROGRESS_FN_HOOK(((ui32FlashAddr - APP_START_ADDRESS) + + (uip_len - 4)), 0); +#endif + } + + // + // Increment to the next block. + // + g_ui32TFTPBlock++; + + // + // Save the packet length. + // + ui32Idx = uip_len; + + // + // Did we see any error? + // + if(BL_FLASH_ERROR_FN_HOOK()) + { + // + // Yes - send back an error packet. + // + SendTFTPError(2, "Error programming flash."); + } + else + { + // + // No errors reported so construct an ACK packet. The block number + // field is already correct, so it does not need to be set. + // + pui8Packet[0] = (TFTP_ACK >> 8) & 0xff; + pui8Packet[1] = TFTP_ACK & 0xff; + + // + // Send the ACK packet to the TFTP server. + // + uip_udp_send(4); + } + + // + // If the packet was shorter than TFTP_BLOCK_SIZE bytes then this was the + // last packet in the file. + // + if(ui32Idx != (TFTP_BLOCK_SIZE + 4)) + { + // + // If an end signal hook function has been provided, call it here. + // +#ifdef BL_END_FN_HOOK + BL_END_FN_HOOK(); +#endif + return(1); + } + // + // There is more data to be read. + // + return(0); +} + +uint16_t +LOCAL_EMACPHYRead(uint32_t ui32Base, uint8_t ui8PhyAddr, uint8_t ui8RegAddr) +{ + + // + // Make sure the MII is idle. + // + while(HWREG(ui32Base + EMAC_O_MIIADDR) & EMAC_MIIADDR_MIIB) + { + } + + // + // Tell the MAC to read the given PHY register. + // + HWREG(ui32Base + EMAC_O_MIIADDR) = + ((HWREG(ui32Base + EMAC_O_MIIADDR) & EMAC_MIIADDR_CR_M) | + (ui8RegAddr << EMAC_MIIADDR_MII_S) | + (ui8PhyAddr << EMAC_MIIADDR_PLA_S) | EMAC_MIIADDR_MIIB); + + // + // Wait for the read to complete. + // + while(HWREG(ui32Base + EMAC_O_MIIADDR) & EMAC_MIIADDR_MIIB) + { + } + + // + // Return the result. + // + return(HWREG(ui32Base + EMAC_O_MIIDATA) & EMAC_MIIDATA_DATA_M); +} + +//***************************************************************************** +// +//! Handles the BOOTP process. +//! +//! This function contains the proto-thread for handling the BOOTP process. It +//! first communicates with the BOOTP server to get its boot parameters (IP +//! address, server address, and file name), then it communicates with the TFTP +//! server on the specified server to read the firmware image file. +//! +//! \return None. +// +//***************************************************************************** +#ifdef DOXYGEN +char +BOOTPThread(void) +#else +PT_THREAD(BOOTPThread(void)) +#endif +{ + // + // Begin the proto-thread. + // + PT_BEGIN(&g_sThread); + +wait_for_link: + PT_WAIT_UNTIL(&g_sThread, + (LOCAL_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) & + EPHY_BMSR_LINKSTAT) != 0); + + // + // Reset the host address. + // + *((uint32_t *)(void *)(&uip_hostaddr)) = 0; + + // + // Re-bind the UDP socket for sending requests to the BOOTP server. + // + uip_udp_remove(g_pConn); + *((uint32_t *)(void *)(&g_sServerAddr)) = 0xffffffff; + uip_udp_new(&g_sServerAddr, HTONS(BOOTP_SERVER_PORT)); + uip_udp_bind(g_pConn, HTONS(BOOTP_CLIENT_PORT)); + + // + // Set the initial delay between BOOTP requests to 1 second. + // + g_ui32Delay = SYSTICKHZ; + + // + // Loop forever. This loop is explicitly exited when a valid BOOTP reply + // is received. + // + while(1) + { + // + // Send a BOOTP request. + // + SendBOOTPRequest(); + + // + // Set the amount of time to wait for the BOOTP reply message. + // + g_ui32Target = g_ui32Ticks + g_ui32Delay; + + // + // Wait until a packet is received or the timeout has occurred. + // +wait_for_bootp_reply: + PT_WAIT_UNTIL(&g_sThread, + ((g_ui32Link = (LOCAL_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) & + EPHY_BMSR_LINKSTAT)) == 0) || + uip_newdata() || (g_ui32Ticks > g_ui32Target)); + + // + // If the link has been lost, go back to waiting for a link. + // + if(g_ui32Link == 0) + { + goto wait_for_link; + } + + // + // See if a packet has been received. + // + if(uip_newdata()) + { + // + // Clear the new data flag so that this packet will only be + // examined one time. + // + uip_flags &= ~(UIP_NEWDATA); + + // + // See if this is a BOOTP reply. + // + if(ParseBOOTPReply() == 1) + { + break; + } + + // + // This was not a BOOTP reply packet, so go back to waiting. + // + goto wait_for_bootp_reply; + } + + // + // If the delay between BOOTP requests is less than 60 seconds, double + // the delay time. This avoids constantly slamming the network with + // requests. + // + if(g_ui32Delay < (60 * SYSTICKHZ)) + { + g_ui32Delay *= 2; + } + } + + // + // Reconfigure the UDP socket to target the TFTP port on the server. + // + uip_ipaddr_copy(&g_pConn->ripaddr, g_sServerAddr); + uip_udp_bind(g_pConn, HTONS(13633)); + + // + // Send a TFTP read request. + // + SendTFTPGet(); + + // + // Since the first TFTP read request will result in an ARP request, delay + // for just a bit and then re-issue the TFTP read request. + // + PT_YIELD(&g_sThread); + + // + // Resend the TFTP read request. If the ARP request has already been + // answered, this will go out as is and avoid the two second timeout below. + // + SendTFTPGet(); + + // + // Start the TFTP transfer from block one. + // + g_ui32TFTPBlock = 1; + + // + // Set the number of TFTP retries to zero. + // + g_ui32TFTPRetries = 0; + + // + // Loop forever. This loop is explicitly exited when the TFTP transfer has + // completed. + // + while(1) + { + // + // Set the amount of time to wait for the TFTP data packet. + // + g_ui32Target = g_ui32Ticks + (SYSTICKHZ * 4); + + // + // Wait until a packet is received or the timeout has occurred. + // + PT_WAIT_UNTIL(&g_sThread, + ((g_ui32Link = (LOCAL_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) & + EPHY_BMSR_LINKSTAT)) == 0) || + uip_newdata() || (g_ui32Ticks > g_ui32Target)); + + // + // If the link has been lost, go back to waiting for a link. + // + if(g_ui32Link == 0) + { + goto wait_for_link; + } + + // + // See if a packet has been received. + // + if(uip_newdata()) + { + // + // Clear the new data flag so that this packet will only be + // examined one time. + // + uip_flags &= ~(UIP_NEWDATA); + + // + // See if this is a TFTP data packet. + // + if(ParseTFTPData() == 1) + { + break; + } + } + else if(g_ui32TFTPRetries < 3) + { + // + // The transfer timed out, so send a new TFTP read request. + // + SendTFTPGet(); + + // + // Start the TFTP transfer from block one. + // + g_ui32TFTPBlock = 1; + + // + // Increment the count of TFTP retries. + // + g_ui32TFTPRetries++; + } + else + { + // + // The TFTP transfer failed after three retries, so start over. + // + goto wait_for_link; + } + } + // + // Wait for the last packet to be transmitted. + // + while(g_psTxDescriptor[g_ui32TxDescIndex].ui32CtrlStatus & + DES0_TX_CTRL_OWN) + { + } + + // + // Wait for a bit to make sure that the final ACK packet is transmitted. + // + g_ui32Target = g_ui32Ticks + (SYSTICKHZ / 4); + while(g_ui32Ticks < g_ui32Target) + { + PT_YIELD(&g_sThread); + } + + // + // Perform a software reset request. This will cause the microcontroller + // to reset; no further code will be executed. + // + HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ; + + // + // The microcontroller should have reset, so this should never be reached. + // Just in case, loop forever. + // + while(1) + { + } + + // + // End the proto-thread. + // + PT_END(&g_sThread); +} + +static void +LOCAL_EMACPHYConfigSet(uint32_t ui32Base, uint32_t ui32Config) +{ + // + // Write the Ethernet PHY configuration to the peripheral configuration + // register. + // + HWREG(ui32Base + EMAC_O_PC) = ui32Config; + + // + // If using the internal PHY, reset it to ensure that new configuration is + // latched there. + // + if((ui32Config & EMAC_PHY_TYPE_MASK) == EMAC_PHY_TYPE_INTERNAL) + { + ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0); + while(!ROM_SysCtlPeripheralReady(SYSCTL_PERIPH_EPHY0)) + { + // + // Wait for the PHY reset to complete. + // + } + + // + // Delay a bit longer to ensure that the PHY reset has completed. + // + ROM_SysCtlDelay(1000); + } + + // + // If using an external RMII PHY, we must set 2 bits in the Ethernet MAC + // Clock Configuration Register. + // + if((ui32Config & EMAC_PHY_TYPE_MASK) == EMAC_PHY_TYPE_EXTERNAL_RMII) + { + // + // Select and enable the external clock from the RMII PHY. + // + HWREG(EMAC0_BASE + EMAC_O_CC) |= EMAC_CC_CLKEN; + } + else + { + // + // Disable the external clock. + // + HWREG(EMAC0_BASE + EMAC_O_CC) &= ~EMAC_CC_CLKEN; + } + + // + // Reset the MAC regardless of whether the PHY connection changed or not. + // + ROM_EMACReset(EMAC0_BASE); + + ROM_SysCtlDelay(1000); +} + +//***************************************************************************** +// +//! Reconfigures the Ethernet controller. +//! +//! \param ui32Clock is the system clock frequency. +//! +//! This function reconfigures the Ethernet controller, preparing it for use by +//! the boot loader. This performs the steps common between the direct +//! invocation of the boot loader and the application invocation of the boot +//! loader. +//! +//! \return None. +// +//***************************************************************************** +void +EnetReconfig(uint32_t ui32Clock) +{ + uip_ipaddr_t sAddr; + uint32_t ui32Loop; + uint32_t ui32User0, ui32User1; + + // + // Configure for use with the internal PHY. + // + LOCAL_EMACPHYConfigSet(EMAC0_BASE, + (EMAC_PHY_TYPE_INTERNAL | EMAC_PHY_INT_MDIX_EN | + EMAC_PHY_AN_100B_T_FULL_DUPLEX)); + + + // + // Reset the MAC. + // + ROM_EMACReset(EMAC0_BASE); + + // + // Initialize the MAC and set the DMA mode. + // + ROM_EMACInit(EMAC0_BASE, ui32Clock, + EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, 4, 4, 0); + + // + // Get the MAC address from the flash user registers. If it has not been + // programmed, then use the boot loader default MAC address. + // + ROM_FlashUserGet(&ui32User0, &ui32User1); + if((ui32User0 == 0xffffffff) || (ui32User1 == 0xffffffff)) + { + // + // MAC address has not been programmed, use default. + // + g_sMACAddr.addr[0] = 0x00; + g_sMACAddr.addr[1] = 0x1a; + g_sMACAddr.addr[2] = 0xb6; + g_sMACAddr.addr[3] = 0x00; + g_sMACAddr.addr[4] = 0x64; + g_sMACAddr.addr[5] = 0x00; + } + else + { + g_sMACAddr.addr[0] = ui32User0 & 0xff; + g_sMACAddr.addr[1] = (ui32User0 >> 8) & 0xff; + g_sMACAddr.addr[2] = (ui32User0 >> 16) & 0xff; + g_sMACAddr.addr[3] = ui32User1 & 0xff; + g_sMACAddr.addr[4] = (ui32User1 >> 8) & 0xff; + g_sMACAddr.addr[5] = (ui32User1 >> 16) & 0xff; + } + + // + // Set MAC configuration options. + // + ROM_EMACConfigSet(EMAC0_BASE, + (EMAC_CONFIG_FULL_DUPLEX | EMAC_CONFIG_CHECKSUM_OFFLOAD | + EMAC_CONFIG_7BYTE_PREAMBLE | EMAC_CONFIG_IF_GAP_96BITS | + EMAC_CONFIG_USE_MACADDR0 | EMAC_CONFIG_SA_FROM_DESCRIPTOR | + EMAC_CONFIG_BO_LIMIT_1024), + (EMAC_MODE_RX_STORE_FORWARD | EMAC_MODE_TX_STORE_FORWARD | + EMAC_MODE_TX_THRESHOLD_64_BYTES | + EMAC_MODE_RX_THRESHOLD_64_BYTES), 0); + + // + // Initialize each of the transmit descriptors. Note that we leave the OWN + // bit clear here since we have not set up any transmissions yet. + // + for(ui32Loop = 0; ui32Loop < NUM_TX_DESCRIPTORS; ui32Loop++) + { + g_psTxDescriptor[ui32Loop].ui32Count = + (DES1_TX_CTRL_SADDR_INSERT | + (TX_BUFFER_SIZE << DES1_TX_CTRL_BUFF1_SIZE_S)); + g_psTxDescriptor[ui32Loop].pvBuffer1 = g_pui8TxBuffer; + g_psTxDescriptor[ui32Loop].DES3.pLink = + (ui32Loop == (NUM_TX_DESCRIPTORS - 1)) ? + g_psTxDescriptor : &g_psTxDescriptor[ui32Loop + 1]; + g_psTxDescriptor[ui32Loop].ui32CtrlStatus = + (DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG | + DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_CHAINED | + DES0_TX_CTRL_IP_ALL_CKHSUMS); + } + + // + // Initialize each of the receive descriptors. We clear the OWN bit here + // to make sure that the receiver doesn't start writing anything + // immediately. + // + for(ui32Loop = 0; ui32Loop < NUM_RX_DESCRIPTORS; ui32Loop++) + { + g_psRxDescriptor[ui32Loop].ui32CtrlStatus = 0; + g_psRxDescriptor[ui32Loop].ui32Count = + (DES1_RX_CTRL_CHAINED | + (RX_BUFFER_SIZE << DES1_RX_CTRL_BUFF1_SIZE_S)); + g_psRxDescriptor[ui32Loop].pvBuffer1 = g_pui8RxBuffer; + g_psRxDescriptor[ui32Loop].DES3.pLink = + (ui32Loop == (NUM_RX_DESCRIPTORS - 1)) ? + g_psRxDescriptor : &g_psRxDescriptor[ui32Loop + 1]; + } + + // + // Set the descriptor pointers in the hardware. + // + ROM_EMACRxDMADescriptorListSet(EMAC0_BASE, g_psRxDescriptor); + ROM_EMACTxDMADescriptorListSet(EMAC0_BASE, g_psTxDescriptor); + + // + // Start from the beginning of both descriptor chains. We actually set + // the transmit descriptor index to the last descriptor in the chain + // since it will be incremented before use and this means the first + // transmission we perform will use the correct descriptor. + // + g_ui32RxDescIndex = 0; + g_ui32TxDescIndex = NUM_TX_DESCRIPTORS - 1; + + // + // Program the MAC address. + // + ROM_EMACAddrSet(EMAC0_BASE, 0, g_sMACAddr.addr); + + // + // Wait for the link to become active. + // + while((ROM_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) & + EPHY_BMSR_LINKSTAT) == 0) + { + } + + // + // Set MAC filtering options. We receive all broadcast and multicast + // packets along with those addressed specifically for us. + // + ROM_EMACFrameFilterSet(EMAC0_BASE, (EMAC_FRMFILTER_SADDR | + EMAC_FRMFILTER_PASS_MULTICAST | + EMAC_FRMFILTER_PASS_NO_CTRL)); + + // + // Seed the random number generator from the MAC address. + // + g_ui32RandomSeed = *(uint32_t *)(g_sMACAddr.addr + 2); + + // + // Initialize the uIP stack. + // + uip_init(); + uip_arp_init(); + + // + // Set the MAC address. + // + uip_setethaddr(g_sMACAddr); + + // + // Initialize the proto-thread used by the BOOTP protocol handler. + // + PT_INIT(&g_sThread); + + // + // Create a UDP socket for sending requests to the BOOTP server. After the + // BOOTP portion of the protocol has been handled, this socket will be + // reused to communicate with the TFTP server. + // + *((uint32_t *)(void *)(&sAddr)) = 0xffffffff; + g_pConn = uip_udp_new(&sAddr, HTONS(BOOTP_SERVER_PORT)); + uip_udp_bind(g_pConn, HTONS(BOOTP_CLIENT_PORT)); + + // + // Enable the Ethernet MAC transmitter and receiver. + // + ROM_EMACTxEnable(EMAC0_BASE); + ROM_EMACRxEnable(EMAC0_BASE); + + // + // Mark the first receive descriptor as available to the DMA to start + // the receive processing. + // + g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus |= DES0_RX_CTRL_OWN; + + // + // Reset the counters that are incremented by SysTick. + // + g_ui32Ticks = 0; + g_ui32PeriodicTimer = 0; + g_ui32ARPTimer = 0; + + // + // Setup SysTick. + // + HWREG(NVIC_ST_RELOAD) = (ui32Clock / SYSTICKHZ) - 1; + HWREG(NVIC_ST_CTRL) = (NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_INTEN | + NVIC_ST_CTRL_ENABLE); +} +//***************************************************************************** +// +//! Configures the Ethernet controller. +//! +//! This function configures the Ethernet controller, preparing it for use by +//! the boot loader. +//! +//! \return None. +// +//***************************************************************************** +void +ConfigureEnet(void) +{ + // + // Make sure the main oscillator is enabled because this is required by + // the PHY. The system must have a 25MHz crystal attached to the OSC + // pins. The SYSCTL_MOSC_HIGHFREQ parameter is used when the crystal + // frequency is 10MHz or higher. + // + HWREG(SYSCTL_MOSCCTL) = SYSCTL_MOSC_HIGHFREQ; + + // + // Delay while the main oscillator starts up. + // + Delay(5242880); + + MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | + SYSCTL_OSC_MAIN | + SYSCTL_USE_PLL | + SYSCTL_CFG_VCO_480), 120000000); + + +#ifdef ENET_ENABLE_LEDS + // + // PF1/PK4/PK6 are used for Ethernet LEDs. + // + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK); + ROM_GPIOPinConfigure(GPIO_PF1_EN0LED2); + ROM_GPIOPinConfigure(GPIO_PK4_EN0LED0); + ROM_GPIOPinConfigure(GPIO_PK6_EN0LED1); + + // + // Make the pin(s) be peripheral controlled. + // + ROM_GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_DIR_MODE_HW); + ROM_GPIODirModeSet(GPIO_PORTK_BASE, GPIO_PIN_4|GPIO_PIN_6, GPIO_DIR_MODE_HW); + + // + // Set the pad(s) for standard push-pull operation. + // + ROM_GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD); + ROM_GPIOPadConfigSet(GPIO_PORTK_BASE, GPIO_PIN_4|GPIO_PIN_6, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD); +#endif + + // + // Enable and reset the Ethernet modules. + // + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EMAC0); + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EPHY0); + ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_EMAC0); + ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0); + + while(!ROM_SysCtlPeripheralReady(SYSCTL_PERIPH_EMAC0)) + { + } + +} + +//***************************************************************************** +// +//! Starts the update process via BOOTP. +//! +//! This function starts the Ethernet firmware update process. The BOOTP +//! (as defined by RFC951 at http://tools.ietf.org/html/rfc951) and TFTP (as +//! defined by RFC1350 at http://tools.ietf.org/html/rfc1350) protocols are +//! used to transfer the firmware image over Ethernet. +//! +//! \return Never returns. +// +//***************************************************************************** +void +UpdateBOOTP(void) +{ + // + // Get the size of flash. + // + g_ui32FlashEnd = ROM_SysCtlFlashSizeGet(); +#ifdef FLASH_RSVD_SPACE + g_ui32FlashEnd -= FLASH_RSVD_SPACE; +#endif + + // + // Perform the common Ethernet configuration. The frequency should + // match whatever the application sets the system clock. + // + EnetReconfig(120000000); + + // + // Main Application Loop. + // + while(1) + { + uint32_t ui32Temp; + + // + // See if there is a packet waiting to be read. + // + if(!(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & + DES0_RX_CTRL_OWN)) + { + // + // Read the packet from the Ethernet controller. + // + uip_len = PacketReceive(uip_buf, UIP_CONF_BUFFER_SIZE); + + // + // See if this is an IP packet. + // + if((uip_len != 0) && + (((struct uip_eth_hdr *)&uip_buf[0])->type == + HTONS(UIP_ETHTYPE_IP))) + { + // + // Update the ARP tables based on this packet. + // + uip_arp_ipin(); + + // + // Process this packet. + // + uip_input(); + + // + // See if the processing of this packet resulted in a packet to be + // sent. + // + if(uip_len > 0) + { + // + // Update the ARP tables based on the packet to be sent. + // + uip_arp_out(); + + // + // Send the packet. + // + PacketTransmit(uip_buf, uip_len); + + // + // Indicate that the packet has been sent. + // + uip_len = 0; + } + } + + // + // See if this is an ARP packet. + // + else if((uip_len != 0) && + (((struct uip_eth_hdr *)&uip_buf[0])->type == + HTONS(UIP_ETHTYPE_ARP))) + { + // + // Process this packet. + // + uip_arp_arpin(); + + // + // See if the processing of this packet resulted in a packet to be + // sent. + // + if(uip_len > 0) + { + // + // Send the packet. + // + PacketTransmit(uip_buf, uip_len); + + // + // Indicate that the packet has been sent. + // + uip_len = 0; + } + } + } + + // + // See if the periodic timer has expired. + // + if(g_ui32PeriodicTimer > UIP_PERIODIC_TIMER_MS) + { + // + // Reset the periodic timer. + // + g_ui32PeriodicTimer = 0; + + // + // Loop through the UDP connections. + // + for(ui32Temp = 0; ui32Temp < UIP_UDP_CONNS; ui32Temp++) + { + // + // Perform the periodic processing on this UDP connection. + // + uip_udp_periodic(ui32Temp); + + // + // See if the periodic processing of this connection resulted in a + // packet to be sent. + // + if(uip_len > 0) + { + // + // Update the ARP tables based on the packet to be sent. + // + uip_arp_out(); + + // + // Send the packet. + // + PacketTransmit(uip_buf, uip_len); + + // + // Indicate that the packet has been sent. + // + uip_len = 0; + } + } + } + + // + // See if the ARP timer has expired. + // + if(g_ui32ARPTimer > UIP_ARP_TIMER_MS) + { + // + // Reset the ARP timer. + // + g_ui32ARPTimer = 0; + + // + // Perform periodic processing on the ARP table. + // + uip_arp_timer(); + } + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif diff --git a/boot_loader/bl_flash.c b/boot_loader/bl_flash.c new file mode 100644 index 0000000..3bbb0ee --- /dev/null +++ b/boot_loader/bl_flash.c @@ -0,0 +1,218 @@ +//***************************************************************************** +// +// bl_flash.c - Flash programming functions used by the boot loader. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "inc/hw_types.h" +#include "inc/hw_flash.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_memmap.h" +#include "bl_config.h" +#include "boot_loader/bl_flash.h" + +//***************************************************************************** +// +//! Erases a single 1KB block of internal flash. +//! +//! \param ui32Address is the address of the block of flash to erase. +//! +//! This function erases a single 1KB block of the internal flash, blocking +//! until the erase has completed. +//! +//! \return None +// +//***************************************************************************** +void +BLInternalFlashErase(uint32_t ui32Address) +{ + // + // Erase this block of the flash. + // + HWREG(FLASH_FMA) = ui32Address; + HWREG(FLASH_FMC) = FLASH_FMC_WRKEY | FLASH_FMC_ERASE; + + // + // Wait until the flash has been erased. + // + while(HWREG(FLASH_FMC) & FLASH_FMC_ERASE) + { + } +} + +//***************************************************************************** +// +//! Programs a block of data at a given address in the internal flash. +//! +//! \param ui32DstAddr is the address of the first word to be programmed in +//! flash. +//! \param pui8SrcData is a pointer to the first byte to be programmed. +//! \param ui32Length is the number of bytes to program. This must be a +//! multiple of 4. +//! +//! This function writes a block of data to the internal flash at a given +//! address. Since the flash is written a word at a time, the data must be a +//! multiple of 4 bytes and the destination address, ui32DstAddr, must be on a +//! word boundary. +//! +//! \return None +// +//***************************************************************************** +void +BLInternalFlashProgram(uint32_t ui32DstAddr, uint8_t *pui8SrcData, + uint32_t ui32Length) +{ + uint32_t ui32Loop; + + for(ui32Loop = 0; ui32Loop < ui32Length; ui32Loop += 4) + { + // + // Program this word into flash. + // + HWREG(FLASH_FMA) = ui32DstAddr + ui32Loop; + HWREG(FLASH_FMD) = *(uint32_t *)(pui8SrcData + ui32Loop); + HWREG(FLASH_FMC) = FLASH_FMC_WRKEY | FLASH_FMC_WRITE; + + // + // Wait until the flash has been programmed. + // + while(HWREG(FLASH_FMC) & FLASH_FMC_WRITE) + { + } + } +} + +//***************************************************************************** +// +//! Returns the size of the internal flash in bytes. +//! +//! This function returns the total number of bytes of internal flash in the +//! current part. No adjustment is made for any sections reserved via +//! options defined in bl_config.h. +//! +//! \return Returns the total number of bytes of internal flash. +// +//***************************************************************************** +uint32_t +BLInternalFlashSizeGet(void) +{ + return(((HWREG(SYSCTL_DC0) & SYSCTL_DC0_FLASHSZ_M) + 1) << 11); +} + +//***************************************************************************** +// +//! Checks whether a given start address is valid for a download. +//! +//! This function checks to determine whether the given address is a valid +//! download image start address given the options defined in bl_config.h. +//! +//! \return Returns non-zero if the address is valid or 0 otherwise. +// +//***************************************************************************** +uint32_t +BLInternalFlashStartAddrCheck(uint32_t ui32Addr, uint32_t ui32ImgSize) +{ + uint32_t ui32FlashSize; + + // + // Determine the size of the flash available on the part in use. + // + ui32FlashSize = ((HWREG(SYSCTL_DC0) & SYSCTL_DC0_FLASHSZ_M) + 1) << 11; + + // + // If we are reserving space at the top of flash then this space is not + // available for application download but it is availble to be updated + // directly. + // +#ifdef FLASH_RSVD_SPACE + if((ui32FlashSize - FLASH_RSVD_SPACE) != ui32Addr) + { + ui32FlashSize -= FLASH_RSVD_SPACE; + } +#endif + + // + // Is the address we were passed a valid start address? We allow: + // + // 1. Address 0 if configured to update the boot loader. + // 2. The start of the reserved block if parameter space is reserved (to + // allow a download of the parameter block contents). + // 3. The application start address specified in bl_config.h. + // + // The function fails if the address is not one of these, if the image + // size is larger than the available space or if the address is not word + // aligned. + // + if(( +#ifdef ENABLE_BL_UPDATE + (ui32Addr != 0) && +#endif +#ifdef FLASH_RSVD_SPACE + (ui32Addr != (ui32FlashSize - FLASH_RSVD_SPACE)) && +#endif + (ui32Addr != APP_START_ADDRESS)) || + ((ui32Addr + ui32ImgSize) > ui32FlashSize) || ((ui32Addr & 3) != 0)) + { + return(0); + } + else + { + return(1); + } +} + +//***************************************************************************** +// +//! Checks whether a flash access violation occurred. +//! +//! This function checks whether an access violation error occurred during +//! the previous program or erase operation. +//! +//! \return Returns 0 if no error occurred or a non-zero value if an error was +//! reported. +// +//***************************************************************************** +void +BLInternalFlashErrorClear(void) +{ + // + // Clear the flash controller access interrupt. + // + HWREG(FLASH_FCMISC) = FLASH_FCMISC_AMISC; +} + +//***************************************************************************** +// +//! Checks whether a flash access violation occurred. +//! +//! This function checks whether an access violation error occurred since the +//! last call to BLInternalFlashErrorClear(). +//! +//! \return Returns 0 if no error occurred or a non-zero value if an error was +//! reported. +// +//***************************************************************************** +uint32_t +BLInternalFlashErrorCheck(void) +{ + return(HWREG(FLASH_FCRIS) & FLASH_FCRIS_ARIS); +} diff --git a/boot_loader/bl_flash.h b/boot_loader/bl_flash.h new file mode 100644 index 0000000..4a23f00 --- /dev/null +++ b/boot_loader/bl_flash.h @@ -0,0 +1,127 @@ +//***************************************************************************** +// +// bl_flash.h - Flash programming functions used by the boot loader. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_FLASH_H__ +#define __BL_FLASH_H__ + +#include "driverlib/rom.h" + +//***************************************************************************** +// +// Basic functions for erasing and programming internal flash. +// +//***************************************************************************** +extern void BLInternalFlashErase(uint32_t ui32Address); +extern void BLInternalFlashProgram(uint32_t ui32DstAddr, uint8_t *pui8SrcData, + uint32_t ui32Length); +extern uint32_t BLInternalFlashSizeGet(void); +extern uint32_t BLInternalFlashStartAddrCheck(uint32_t ui32Addr, + uint32_t ui32ImgSize); +extern uint32_t BLInternalFlashErrorCheck(void); +extern void BLInternalFlashErrorClear(void); + +//***************************************************************************** +// +// If the user has not specified which flash programming functions to use, +// default to the basic, internal flash functions on Sandstorm, Fury and +// DustDevil parts or the ROM-resident function for Tempest-class parts. +// +//***************************************************************************** +#ifndef BL_FLASH_ERASE_FN_HOOK +#define BL_FLASH_ERASE_FN_HOOK(ui32Address) \ + { \ + HWREG(FLASH_FMA) = (ui32Address); \ + HWREG(FLASH_FMC) = FLASH_FMC_WRKEY | FLASH_FMC_ERASE; \ + while(HWREG(FLASH_FMC) & FLASH_FMC_ERASE) \ + { \ + } \ + } +#else +extern void BL_FLASH_ERASE_FN_HOOK(uint32_t ui32Address); +#endif + +#ifndef BL_FLASH_PROGRAM_FN_HOOK +#ifdef ROM_FlashProgram +#define BL_FLASH_PROGRAM_FN_HOOK(ui32DstAddr, pui8SrcData, ui32Length) \ + ROM_FlashProgram((uint32_t *)pui8SrcData, ui32DstAddr, \ + (((ui32Length) + 3) & ~3)) +#else +#define BL_FLASH_PROGRAM_FN_HOOK(ui32DstAddr, pui8SrcData, ui32Length) \ + { \ + uint32_t ui32FlashProgLoop; \ + \ + for(ui32FlashProgLoop = 0; ui32FlashProgLoop < (ui32Length); \ + ui32FlashProgLoop += 4) \ + { \ + HWREG(FLASH_FMA) = (ui32DstAddr) + ui32FlashProgLoop; \ + HWREG(FLASH_FMD) = *(uint32_t *)((pui8SrcData) + \ + ui32FlashProgLoop); \ + HWREG(FLASH_FMC) = FLASH_FMC_WRKEY | FLASH_FMC_WRITE; \ + while(HWREG(FLASH_FMC) & FLASH_FMC_WRITE) \ + { \ + } \ + } \ + } +#endif +#else +extern uint32_t BL_FLASH_PROGRAM_FN_HOOK(uint32_t ui32DstAddr, + uint8_t *pui8SrcData, + uint32_t ui32Length); +#endif + +#ifndef BL_FLASH_CL_ERR_FN_HOOK +#define BL_FLASH_CL_ERR_FN_HOOK() HWREG(FLASH_FCMISC) = FLASH_FCMISC_AMISC +#else +extern void BL_FLASH_CL_ERR_FN_HOOK(void); +#endif + +#ifndef BL_FLASH_ERROR_FN_HOOK +#define BL_FLASH_ERROR_FN_HOOK() (HWREG(FLASH_FCRIS) & FLASH_FCRIS_ARIS) +#else +extern uint32_t BL_FLASH_ERROR_FN_HOOK(void); +#endif + +#ifndef BL_FLASH_SIZE_FN_HOOK +#define BL_FLASH_SIZE_FN_HOOK() \ + (((HWREG(SYSCTL_DC0) & SYSCTL_DC0_FLASHSZ_M) + 1) << 11) +#else +extern uint32_t BL_FLASH_SIZE_FN_HOOK(void); +#endif + +#ifndef BL_FLASH_END_FN_HOOK +#define BL_FLASH_END_FN_HOOK() \ + (((HWREG(SYSCTL_DC0) & SYSCTL_DC0_FLASHSZ_M) + 1) << 11) +#else +extern uint32_t BL_FLASH_END_FN_HOOK(void); +#endif + +#ifndef BL_FLASH_AD_CHECK_FN_HOOK +#define BL_FLASH_AD_CHECK_FN_HOOK(ui32Addr, ui32Size) \ + BLInternalFlashStartAddrCheck((ui32Addr), (ui32Size)) +#else +extern uint32_t BL_FLASH_AD_CHECK_FN_HOOK(uint32_t ui32Address, + uint32_t ui32Length); +#endif + +#endif // __BL_FLASH_H__ diff --git a/boot_loader/bl_hooks.h b/boot_loader/bl_hooks.h new file mode 100644 index 0000000..1a174e3 --- /dev/null +++ b/boot_loader/bl_hooks.h @@ -0,0 +1,72 @@ +//***************************************************************************** +// +// bl_hooks.h - Definitions for the application-specific hook function. +// +// Copyright (c) 2009-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_HOOKS_H__ +#define __BL_HOOKS_H__ + +//***************************************************************************** +// +// Prototypes for any application-specific hook functions that are defined in +// bl_config.h. Note that the low level flash programming hooks are handled +// in bl_flash.h to allow us to define macros for internal flash programming +// in the normal case where no override functions are provided. +// +//***************************************************************************** +#ifdef BL_HW_INIT_FN_HOOK +extern void BL_HW_INIT_FN_HOOK(void); +#endif +#ifdef BL_INIT_FN_HOOK +extern void BL_INIT_FN_HOOK(void); +#endif +#ifdef BL_REINIT_FN_HOOK +extern void BL_REINIT_FN_HOOK(void); +#endif +#ifdef BL_START_FN_HOOK +extern void BL_START_FN_HOOK(void); +#endif +#ifdef BL_PROGRESS_FN_HOOK +extern void BL_PROGRESS_FN_HOOK(uint32_t ui32Completed, uint32_t ui32Total); +#endif +#ifdef BL_END_FN_HOOK +extern void BL_END_FN_HOOK(void); +#endif +#ifdef BL_DECRYPT_FN_HOOK +extern void BL_DECRYPT_FN_HOOK(uint8_t *pui8Buffer, uint32_t ui32Size); +#endif +#ifdef BL_CHECK_UPDATE_FN_HOOK +extern uint32_t BL_CHECK_UPDATE_FN_HOOK(void); +#endif + +//***************************************************************************** +// +// If ENABLE_DECRYPTION is defined but we don't have a hook function set for +// decryption, default to the previous behavior which calls the stub function +// DecryptData. +// +//***************************************************************************** +#if (defined ENABLE_DECRYPTION) && !(defined BL_DECRYPT_FN_HOOK) +#define BL_DECRYPT_FN_HOOK DecryptData +#endif + +#endif // __BL_HOOKS_H__ diff --git a/boot_loader/bl_i2c.c b/boot_loader/bl_i2c.c new file mode 100644 index 0000000..bc2b62c --- /dev/null +++ b/boot_loader/bl_i2c.c @@ -0,0 +1,149 @@ +//***************************************************************************** +// +// bl_i2c.c - This file contains the function used to transfer data via the I2C +// port. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "inc/hw_gpio.h" +#include "inc/hw_i2c.h" +#include "inc/hw_memmap.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "bl_config.h" +#include "boot_loader/bl_i2c.h" + +//***************************************************************************** +// +//! \addtogroup bl_i2c_api +//! @{ +// +//***************************************************************************** +#if defined(I2C_ENABLE_UPDATE) || defined(DOXYGEN) + +//***************************************************************************** +// +//! Sends data over the I2C port. +//! +//! \param pui8Data is the buffer containing the data to write out to the I2C +//! port. +//! \param ui32Size is the number of bytes provided in \e pui8Data buffer that +//! will be written out to the I2C port. +//! +//! This function sends \e ui32Size bytes of data from the buffer pointed to by +//! \e pui8Data via the I2C port. The function will wait till the I2C Slave +//! port has been properly addressed by the I2C Master device before sending +//! the first byte. +//! +//! \return None. +// +//***************************************************************************** +void +I2CSend(const uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Transmit the number of bytes requested on the UART port. + // + while(ui32Size--) + { + // + // Wait for request to come in at slave. + // + while(!(HWREG(I2C0_BASE + I2C_O_SCSR) & I2C_SCSR_TREQ)) + { + } + + // + // Send out the next byte. + // + HWREG(I2C0_BASE + I2C_O_SDR) = *pui8Data++; + } +} + +//***************************************************************************** +// +//! Waits until all data has been transmitted by the I2C port. +//! +//! This function waits until all data written to the I2C port has been read by +//! the master. +//! +//! \return None. +// +//***************************************************************************** +void +I2CFlush(void) +{ + // + // Wait until the I2C bus is no longer busy, meaning that the last byte has + // been sent. + // + while(HWREG(I2C0_BASE + I2C_O_MCS) & I2C_MCS_BUSBSY) + { + } +} + +//***************************************************************************** +// +//! Receives data over the I2C port. +//! +//! \param pui8Data is the buffer to read data into from the I2C port. +//! \param ui32Size is the number of bytes provided in the \e pui8Data buffer +//! that should be written with data from the I2C port. +//! +//! This function reads back \e ui32Size bytes of data from the I2C port, into +//! the buffer that is pointed to by \e pui8Data. This function will not +//! return until \e ui32Size number of bytes have been received. This function +//! will wait till the I2C Slave port has been properly addressed by the I2C +//! Master before reading the first byte of data from the I2C port. +//! +//! \return None. +// +//***************************************************************************** +void +I2CReceive(uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Send out the number of bytes requested. + // + while(ui32Size--) + { + // + // Wait until the slave has received the character. + // + while(!(HWREG(I2C0_BASE + I2C_O_SCSR) & I2C_SCSR_RREQ)) + { + } + + // + // Receive a byte from the I2C. + // + *pui8Data++ = HWREG(I2C0_BASE + I2C_O_SDR); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif diff --git a/boot_loader/bl_i2c.h b/boot_loader/bl_i2c.h new file mode 100644 index 0000000..4b7a01a --- /dev/null +++ b/boot_loader/bl_i2c.h @@ -0,0 +1,70 @@ +//***************************************************************************** +// +// bl_i2c.h - Definitions for the I2C transport functions. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_I2C_H__ +#define __BL_I2C_H__ + +//***************************************************************************** +// +// This defines the I2C clock pin that is being used by the boot loader. +// +//***************************************************************************** +#define I2C_CLK (1 << 2) + +//***************************************************************************** +// +// This defines the I2C data pin that is being used by the boot loader. +// +//***************************************************************************** +#define I2C_DATA (1 << 3) + +//***************************************************************************** +// +// This defines the combination of pins used to implement the I2C port used by +// the boot loader. +// +//***************************************************************************** +#define I2C_PINS (I2C_CLK | I2C_DATA) + +//***************************************************************************** +// +// I2C Transport APIs +// +//***************************************************************************** +extern void I2CSend(const uint8_t *pui8Data, uint32_t ui32Size); +extern void I2CReceive(uint8_t *pui8Data, uint32_t ui32Size); +extern void I2CFlush(void); + +//***************************************************************************** +// +// Define the transport functions if the I2C port is being used. +// +//***************************************************************************** +#ifdef I2C_ENABLE_UPDATE +#define SendData I2CSend +#define FlushData I2CFlush +#define ReceiveData I2CReceive +#endif + +#endif // __BL_I2C_H__ diff --git a/boot_loader/bl_link.icf b/boot_loader/bl_link.icf new file mode 100644 index 0000000..3e7af89 --- /dev/null +++ b/boot_loader/bl_link.icf @@ -0,0 +1,83 @@ +//***************************************************************************** +// +// bl_link.icf - Linker script for EW-ARM. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +// +// Define a memory region that covers the entire 4 GB addressible space of the +// processor. +// +define memory mem with size = 4G; + +// +// Define a region for the on-chip flash. +// +define region FLASH = mem:[from 0x00000000 to 0x0000ffff]; + +// +// Define a region for the on-chip SRAM. +// +define region SRAM = mem:[from 0x20000000 to 0x2000ffff]; + +// +// Indicate that the sections containing the boot loader code should be +// initialized by copying. +// +initialize manually with packing = none { section INTVEC }; +initialize manually with packing = none { section CODE }; +initialize manually with packing = none { section .text }; +initialize manually with packing = none { section .rodata }; +initialize manually with packing = none { section .data }; + +keep { section INTVEC }; +keep { section INTVEC_init }; + +// +// Indicate that the noinit values should be left alone. This includes the +// stack, which if initialized will destroy the return address from the +// initialization code, causing the processor to branch to zero and fault. +// +do not initialize { section .noinit }; + +// +// Place the interrupt vectors at the start of flash/SRAM. +// +place at start of FLASH { readonly section INTVEC_init }; +place at start of SRAM { readwrite section INTVEC }; + +// +// Place the remainder of the read-only items into flash/SRAM. +// +place in FLASH { readonly section CODE_init }; +place in SRAM { readwrite section CODE }; +place in FLASH { readonly section .text_init }; +place in SRAM { readwrite section .text }; +place in FLASH { readonly section .rodata_init }; +place in SRAM { readwrite section .rodata }; +place in FLASH { readonly section .data_init }; +place in SRAM { readwrite section .data }; +place in FLASH { readonly }; + +// +// Place all read/write items into SRAM. +// +place in SRAM { readwrite }; diff --git a/boot_loader/bl_link.ld b/boot_loader/bl_link.ld new file mode 100644 index 0000000..aae5f00 --- /dev/null +++ b/boot_loader/bl_link.ld @@ -0,0 +1,51 @@ +/****************************************************************************** + * + * bl_link.ld - Scatter file for Gnu tools + * + * Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. + * Software License Agreement + * + * Texas Instruments (TI) is supplying this software for use solely and + * exclusively on TI's microcontroller products. The software is owned by + * TI and/or its suppliers, and is protected under applicable copyright + * laws. You may not combine this software with "viral" open-source + * software in order to form a larger program. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. + * NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT + * NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY + * CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL + * DAMAGES, FOR ANY REASON WHATSOEVER. + * + * This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. + * + *****************************************************************************/ + +SECTIONS +{ + .text 0x20000000 : AT (0x00000000) + { + _text = .; + KEEP(*(.isr_vector)) + *(.text*) + *(.rodata*) + _etext = .; + } + + .data 0x20000000 + SIZEOF(.text) : AT (SIZEOF(.text)) + { + _data = .; + *(.data*) + _edata = .; + } + + .bss 0x20000000 + SIZEOF(.text) + SIZEOF(.data) : + AT (ADDR(.data) + SIZEOF(.data)) + { + _bss = .; + *(.bss*) + *(COMMON) + _ebss = .; + } +} diff --git a/boot_loader/bl_link.sct b/boot_loader/bl_link.sct new file mode 100644 index 0000000..fe22bd1 --- /dev/null +++ b/boot_loader/bl_link.sct @@ -0,0 +1,45 @@ +;****************************************************************************** +; +; bl_link.sct - Scatter file for RV-MDK. +; +; Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +; Software License Agreement +; +; Texas Instruments (TI) is supplying this software for use solely and +; exclusively on TI's microcontroller products. The software is owned by +; TI and/or its suppliers, and is protected under applicable copyright +; laws. You may not combine this software with "viral" open-source +; software in order to form a larger program. +; +; THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +; NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +; NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +; CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +; DAMAGES, FOR ANY REASON WHATSOEVER. +; +; This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +; +;****************************************************************************** + +; +; The contents of this application reside in flash. +; +FLASH 0x00000000 0x00010000 +{ + ; + ; Place the vector table and reset handlers into flash. + ; + RESET 0x00000000 0x00010000 + { + *.o (RESET, +First) + } + + ; + ; Place everything else remaining into SRAM (RO, RW, and ZI) + ; + SRAM +0x20000000 0x00010000 + { + * (+RO, +RW, +ZI) + } +} diff --git a/boot_loader/bl_link_ccs.cmd b/boot_loader/bl_link_ccs.cmd new file mode 100644 index 0000000..0e09b6e --- /dev/null +++ b/boot_loader/bl_link_ccs.cmd @@ -0,0 +1,63 @@ +/****************************************************************************** + * + * bl_link_ccs.cmd - CCS linker configuration file for boot loader. + * + * Copyright (c) 2009-2014 Texas Instruments Incorporated. All rights reserved. + * Software License Agreement + * + * Texas Instruments (TI) is supplying this software for use solely and + * exclusively on TI's microcontroller products. The software is owned by + * TI and/or its suppliers, and is protected under applicable copyright + * laws. You may not combine this software with "viral" open-source + * software in order to form a larger program. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. + * NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT + * NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY + * CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL + * DAMAGES, FOR ANY REASON WHATSOEVER. + * + * This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. + * + *****************************************************************************/ + +--retain=Vectors + +/* The following command line options are set as part of the CCS project. */ +/* If you are building using the command line, or for some reason want to */ +/* define them here, you can uncomment and modify these lines as needed. */ +/* If you are using CCS for building, it is probably better to make any such */ +/* modifications in your CCS project and leave this file alone. */ +/* */ +/* --heap_size=0 */ +/* --stack_size=256 */ +/* --library=rtsv7M3_T_le_eabi.lib */ + +/* System memory map */ + +MEMORY +{ + FLASH (RX) : origin = 0x00000000, length = 0x00010000 + SRAM (RWX) : origin = 0x20000000, length = 0x00010000 +} + +/* Section allocation in memory */ + +SECTIONS +{ + GROUP + { + .intvecs + .text + .const + .data + } load = FLASH, run = 0x20000000, LOAD_START(init_load), RUN_START(init_run), SIZE(init_size) + + GROUP + { + .bss + .stack + } run = SRAM, RUN_START(bss_run), RUN_END(bss_end), SIZE(bss_size), RUN_END(__STACK_TOP) + +} diff --git a/boot_loader/bl_main.c b/boot_loader/bl_main.c new file mode 100644 index 0000000..fbb7275 --- /dev/null +++ b/boot_loader/bl_main.c @@ -0,0 +1,913 @@ +//***************************************************************************** +// +// bl_main.c - The file holds the main control loop of the boot loader. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_gpio.h" +#include "inc/hw_flash.h" +#include "inc/hw_i2c.h" +#include "inc/hw_memmap.h" +#include "inc/hw_nvic.h" +#include "inc/hw_ssi.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "inc/hw_uart.h" +#include "bl_config.h" +#include "boot_loader/bl_commands.h" +#include "boot_loader/bl_decrypt.h" +#include "boot_loader/bl_flash.h" +#include "boot_loader/bl_hooks.h" +#include "boot_loader/bl_i2c.h" +#include "boot_loader/bl_packet.h" +#include "boot_loader/bl_ssi.h" +#include "boot_loader/bl_uart.h" +#ifdef CHECK_CRC +#include "boot_loader/bl_crc32.h" +#endif + +//***************************************************************************** +// +// Make sure that the application start address falls on a flash page boundary +// +//***************************************************************************** +#if (APP_START_ADDRESS & (FLASH_PAGE_SIZE - 1)) +#error ERROR: APP_START_ADDRESS must be a multiple of FLASH_PAGE_SIZE bytes! +#endif + +//***************************************************************************** +// +// Make sure that the flash reserved space is a multiple of flash pages. +// +//***************************************************************************** +#if (FLASH_RSVD_SPACE & (FLASH_PAGE_SIZE - 1)) +#error ERROR: FLASH_RSVD_SPACE must be a multiple of FLASH_PAGE_SIZE bytes! +#endif + +//***************************************************************************** +// +//! \addtogroup bl_main_api +//! @{ +// +//***************************************************************************** +#if defined(I2C_ENABLE_UPDATE) || defined(SSI_ENABLE_UPDATE) || \ + defined(UART_ENABLE_UPDATE) || defined(DOXYGEN) + +//***************************************************************************** +// +// A prototype for the function (in the startup code) for calling the +// application. +// +//***************************************************************************** +extern void CallApplication(uint32_t ui32Base); + +//***************************************************************************** +// +// A prototype for the function (in the startup code) for a predictable length +// delay. +// +//***************************************************************************** +extern void Delay(uint32_t ui32Count); + +//***************************************************************************** +// +// Holds the current status of the last command that was issued to the boot +// loader. +// +//***************************************************************************** +uint8_t g_ui8Status; + +//***************************************************************************** +// +// This holds the current remaining size in bytes to be downloaded. +// +//***************************************************************************** +uint32_t g_ui32TransferSize; + +//***************************************************************************** +// +// This holds the total size of the firmware image being downloaded (if the +// protocol in use provides this). +// +//***************************************************************************** +#if (defined BL_PROGRESS_FN_HOOK) || (defined CHECK_CRC) +uint32_t g_ui32ImageSize; +#endif + +//***************************************************************************** +// +// This holds the current address that is being written to during a download +// command. +// +//***************************************************************************** +uint32_t g_ui32TransferAddress; +#ifdef CHECK_CRC +uint32_t g_ui32ImageAddress; +#endif + +//***************************************************************************** +// +// This is the data buffer used during transfers to the boot loader. +// +//***************************************************************************** +uint32_t g_pui32DataBuffer[BUFFER_SIZE]; + +//***************************************************************************** +// +// This is an specially aligned buffer pointer to g_pui32DataBuffer to make +// copying to the buffer simpler. It must be offset to end on an address that +// ends with 3. +// +//***************************************************************************** +uint8_t *g_pui8DataBuffer; + +//***************************************************************************** +// +// Converts a word from big endian to little endian. This macro uses compiler- +// specific constructs to perform an inline insertion of the "rev" instruction, +// which performs the byte swap directly. +// +//***************************************************************************** +#if defined(ewarm) +#include +#define SwapWord(x) __REV(x) +#endif +#if defined(codered) || defined(gcc) || defined(sourcerygxx) +#define SwapWord(x) __extension__ \ + ({ \ + register uint32_t __ret, __inp = x; \ + __asm__("rev %0, %1" : "=r" (__ret) : "r" (__inp)); \ + __ret; \ + }) +#endif +#if defined(rvmdk) || defined(__ARMCC_VERSION) +#define SwapWord(x) __rev(x) +#endif +#if defined(ccs) +uint32_t +SwapWord(uint32_t x) +{ + __asm(" rev r0, r0\n" + " bx lr\n"); // need this to make sure r0 is returned + return(x + 1); // return makes compiler happy - ignored +} +#endif + +//***************************************************************************** +// +//! Configures the microcontroller. +//! +//! This function configures the peripherals and GPIOs of the microcontroller, +//! preparing it for use by the boot loader. The interface that has been +//! selected as the update port will be configured, and auto-baud will be +//! performed if required. +//! +//! \return None. +// +//***************************************************************************** +void +ConfigureDevice(void) +{ +#ifdef UART_ENABLE_UPDATE + uint32_t ui32ProcRatio; +#endif + +#ifdef CRYSTAL_FREQ + // + // Since the crystal frequency was specified, enable the main oscillator + // and clock the processor from it. + // + HWREG(SYSCTL_RCC) &= ~(SYSCTL_RCC_MOSCDIS); + Delay(524288); + HWREG(SYSCTL_RCC) = ((HWREG(SYSCTL_RCC) & ~(SYSCTL_RCC_OSCSRC_M)) | + SYSCTL_RCC_OSCSRC_MAIN); +#endif + +#ifdef I2C_ENABLE_UPDATE + // + // Enable the clocks to the I2C and GPIO modules. + // + HWREG(SYSCTL_RCGC2) |= SYSCTL_RCGC2_GPIOB; + HWREG(SYSCTL_RCGC1) |= SYSCTL_RCGC1_I2C0; + + // + // Configure the GPIO pins for hardware control, open drain with pull-up, + // and enable them. + // + HWREG(GPIO_PORTB_BASE + GPIO_O_AFSEL) |= (1 << 7) | I2C_PINS; + HWREG(GPIO_PORTB_BASE + GPIO_O_DEN) |= (1 << 7) | I2C_PINS; + HWREG(GPIO_PORTB_BASE + GPIO_O_ODR) |= I2C_PINS; + + // + // Enable the I2C Slave Mode. + // + HWREG(I2C0_BASE + I2C_O_MCR) = I2C_MCR_MFE | I2C_MCR_SFE; + + // + // Setup the I2C Slave Address. + // + HWREG(I2C0_BASE + I2C_O_SOAR) = I2C_SLAVE_ADDR; + + // + // Enable the I2C Slave Device on the I2C bus. + // + HWREG(I2C0_BASE + I2C_O_SCSR) = I2C_SCSR_DA; +#endif + +#ifdef SSI_ENABLE_UPDATE + // + // Enable the clocks to the SSI and GPIO modules. + // + HWREG(SYSCTL_RCGC2) |= SYSCTL_RCGC2_GPIOA; + HWREG(SYSCTL_RCGC1) |= SYSCTL_RCGC1_SSI0; + + // + // Make the pin be peripheral controlled. + // + HWREG(GPIO_PORTA_BASE + GPIO_O_AFSEL) |= SSI_PINS; + HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= SSI_PINS; + + // + // Set the SSI protocol to Motorola with default clock high and data + // valid on the rising edge. + // + HWREG(SSI0_BASE + SSI_O_CR0) = (SSI_CR0_SPH | SSI_CR0_SPO | + (DATA_BITS_SSI - 1)); + + // + // Enable the SSI interface in slave mode. + // + HWREG(SSI0_BASE + SSI_O_CR1) = SSI_CR1_MS | SSI_CR1_SSE; +#endif + +#ifdef UART_ENABLE_UPDATE + // + // Enable the the clocks to the UART and GPIO modules. + // + HWREG(SYSCTL_RCGC2) |= SYSCTL_RCGC2_GPIOA; + HWREG(SYSCTL_RCGC1) |= SYSCTL_RCGC1_UART0; + + // + // Keep attempting to sync until we are successful. + // +#ifdef UART_AUTOBAUD + while(UARTAutoBaud(&ui32ProcRatio) < 0) + { + } +#else + ui32ProcRatio = UART_BAUD_RATIO(UART_FIXED_BAUDRATE); +#endif + + // + // Set GPIO A0 and A1 as UART pins. + // + HWREG(GPIO_PORTA_BASE + GPIO_O_AFSEL) |= UART_PINS; + + // + // Set the pin type. + // + HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= UART_PINS; + + // + // Set the baud rate. + // + HWREG(UART0_BASE + UART_O_IBRD) = ui32ProcRatio >> 6; + HWREG(UART0_BASE + UART_O_FBRD) = ui32ProcRatio & UART_FBRD_DIVFRAC_M; + + // + // Set data length, parity, and number of stop bits to 8-N-1. + // + HWREG(UART0_BASE + UART_O_LCRH) = UART_LCRH_WLEN_8 | UART_LCRH_FEN; + + // + // Enable RX, TX, and the UART. + // + HWREG(UART0_BASE + UART_O_CTL) = (UART_CTL_UARTEN | UART_CTL_TXE | + UART_CTL_RXE); + +#ifdef UART_AUTOBAUD + // + // Need to ack in the UART case to hold it up while we get things set up. + // + AckPacket(); +#endif +#endif +} + +//***************************************************************************** +// +//! This function performs the update on the selected port. +//! +//! This function is called directly by the boot loader or it is called as a +//! result of an update request from the application. +//! +//! \return Never returns. +// +//***************************************************************************** +void +Updater(void) +{ + uint32_t ui32Size, ui32Temp, ui32FlashSize; +#ifdef CHECK_CRC + uint32_t ui32Retcode; +#endif + + // + // This ensures proper alignment of the global buffer so that the one byte + // size parameter used by the packetized format is easily skipped for data + // transfers. + // + g_pui8DataBuffer = ((uint8_t *)g_pui32DataBuffer) + 3; + + // + // Insure that the COMMAND_SEND_DATA cannot be sent to erase the boot + // loader before the application is erased. + // + g_ui32TransferAddress = 0xffffffff; + + // + // Read any data from the serial port in use. + // + while(1) + { + // + // Receive a packet from the port in use. + // + ui32Size = sizeof(g_pui32DataBuffer) - 3; + if(ReceivePacket(g_pui8DataBuffer, &ui32Size) != 0) + { + continue; + } + + // + // The first byte of the data buffer has the command and determines + // the format of the rest of the bytes. + // + switch(g_pui8DataBuffer[0]) + { + // + // This was a simple ping command. + // + case COMMAND_PING: + { + // + // This command always sets the status to COMMAND_RET_SUCCESS. + // + g_ui8Status = COMMAND_RET_SUCCESS; + + // + // Just acknowledge that the command was received. + // + AckPacket(); + + // + // Go back and wait for a new command. + // + break; + } + + // + // This command indicates the start of a download sequence. + // + case COMMAND_DOWNLOAD: + { + // + // Until determined otherwise, the command status is success. + // + g_ui8Status = COMMAND_RET_SUCCESS; + + // + // A simple do/while(0) control loop to make error exits + // easier. + // + do + { + // + // See if a full packet was received. + // + if(ui32Size != 9) + { + // + // Indicate that an invalid command was received. + // + g_ui8Status = COMMAND_RET_INVALID_CMD; + + // + // This packet has been handled. + // + break; + } + + // + // Get the address and size from the command. + // + g_ui32TransferAddress = SwapWord(g_pui32DataBuffer[1]); + g_ui32TransferSize = SwapWord(g_pui32DataBuffer[2]); + + // + // Depending upon the build options set, keep a copy of + // the original size and start address because we will need + // these later. + // +#if (defined BL_PROGRESS_FN_HOOK) || (defined CHECK_CRC) + g_ui32ImageSize = g_ui32TransferSize; +#endif +#ifdef CHECK_CRC + g_ui32ImageAddress = g_ui32TransferAddress; +#endif + + // + // Check for a valid starting address and image size. + // + if(!BL_FLASH_AD_CHECK_FN_HOOK(g_ui32TransferAddress, + g_ui32TransferSize)) + { + // + // Set the code to an error to indicate that the last + // command failed. This informs the updater program + // that the download command failed. + // + g_ui8Status = COMMAND_RET_INVALID_ADR; + + // + // This packet has been handled. + // + break; + } + + + // + // Only erase the space that we need if we are not + // protecting the code, otherwise erase the entire flash. + // +#ifdef FLASH_CODE_PROTECTION + ui32FlashSize = BL_FLASH_SIZE_FN_HOOK(); +#ifdef FLASH_RSVD_SPACE + if((ui32FlashSize - FLASH_RSVD_SPACE) != + g_ui32TransferAddress) + { + ui32FlashSize -= FLASH_RSVD_SPACE; + } +#endif +#else + ui32FlashSize = g_ui32TransferAddress + g_ui32TransferSize; +#endif + + // + // Clear the flash access interrupt. + // + BL_FLASH_CL_ERR_FN_HOOK(); + + // + // Leave the boot loader present until we start getting an + // image. + // + for(ui32Temp = g_ui32TransferAddress; + ui32Temp < ui32FlashSize; ui32Temp += FLASH_PAGE_SIZE) + { + // + // Erase this block. + // + BL_FLASH_ERASE_FN_HOOK(ui32Temp); + } + + // + // Return an error if an access violation occurred. + // + if(BL_FLASH_ERROR_FN_HOOK()) + { + g_ui8Status = COMMAND_RET_FLASH_FAIL; + } + } + while(0); + + // + // See if the command was successful. + // + if(g_ui8Status != COMMAND_RET_SUCCESS) + { + // + // Setting g_ui32TransferSize to zero makes + // COMMAND_SEND_DATA fail to accept any data. + // + g_ui32TransferSize = 0; + } + + // + // Acknowledge that this command was received correctly. This + // does not indicate success, just that the command was + // received. + // + AckPacket(); + + // + // If we have a start notification hook function, call it + // now if everything is OK. + // +#ifdef BL_START_FN_HOOK + if(g_ui32TransferSize) + { + BL_START_FN_HOOK(); + } +#endif + + // + // Go back and wait for a new command. + // + break; + } + + // + // This command indicates that control should be transferred to + // the specified address. + // + case COMMAND_RUN: + { + // + // Acknowledge that this command was received correctly. This + // does not indicate success, just that the command was + // received. + // + AckPacket(); + + // + // See if a full packet was received. + // + if(ui32Size != 5) + { + // + // Indicate that an invalid command was received. + // + g_ui8Status = COMMAND_RET_INVALID_CMD; + + // + // This packet has been handled. + // + break; + } + + // + // Get the address to which control should be transferred. + // + g_ui32TransferAddress = SwapWord(g_pui32DataBuffer[1]); + + // + // This determines the size of the flash available on the + // device in use. + // + ui32FlashSize = BL_FLASH_SIZE_FN_HOOK(); + + // + // Test if the transfer address is valid for this device. + // + if(g_ui32TransferAddress >= ui32FlashSize) + { + // + // Indicate that an invalid address was specified. + // + g_ui8Status = COMMAND_RET_INVALID_ADR; + + // + // This packet has been handled. + // + break; + } + + // + // Make sure that the ACK packet has been sent. + // + FlushData(); + + // + // Reset and disable the peripherals used by the boot loader. + // +#ifdef I2C_ENABLE_UPDATE + HWREG(SYSCTL_RCGC1) &= ~SYSCTL_RCGC1_I2C0; + HWREG(SYSCTL_SRCR1) = SYSCTL_SRCR1_I2C0; +#endif +#ifdef UART_ENABLE_UPDATE + HWREG(SYSCTL_RCGC1) &= ~SYSCTL_RCGC1_UART0; + HWREG(SYSCTL_SRCR1) = SYSCTL_SRCR1_UART0; +#endif +#ifdef SSI_ENABLE_UPDATE + HWREG(SYSCTL_RCGC1) &= ~SYSCTL_RCGC1_SSI0; + HWREG(SYSCTL_SRCR1) = SYSCTL_SRCR1_SSI0; +#endif + HWREG(SYSCTL_SRCR1) = 0; + + // + // Branch to the specified address. This should never return. + // If it does, very bad things will likely happen since it is + // likely that the copy of the boot loader in SRAM will have + // been overwritten. + // + ((void (*)(void))g_ui32TransferAddress)(); + + // + // In case this ever does return and the boot loader is still + // intact, simply reset the device. + // + HWREG(NVIC_APINT) = (NVIC_APINT_VECTKEY | + NVIC_APINT_SYSRESETREQ); + + // + // The microcontroller should have reset, so this should + // never be reached. Just in case, loop forever. + // + while(1) + { + } + } + + // + // This command just returns the status of the last command that + // was sent. + // + case COMMAND_GET_STATUS: + { + // + // Acknowledge that this command was received correctly. This + // does not indicate success, just that the command was + // received. + // + AckPacket(); + + // + // Return the status to the updater. + // + SendPacket(&g_ui8Status, 1); + + // + // Go back and wait for a new command. + // + break; + } + + // + // This command is sent to transfer data to the device following + // a download command. + // + case COMMAND_SEND_DATA: + { + // + // Until determined otherwise, the command status is success. + // + g_ui8Status = COMMAND_RET_SUCCESS; + + // + // If this is overwriting the boot loader then the application + // has already been erased so now erase the boot loader. + // + if(g_ui32TransferAddress == 0) + { + // + // Clear the flash access interrupt. + // + BL_FLASH_CL_ERR_FN_HOOK(); + + // + // Erase the boot loader. + // + for(ui32Temp = 0; ui32Temp < APP_START_ADDRESS; + ui32Temp += FLASH_PAGE_SIZE) + { + // + // Erase this block. + // + BL_FLASH_ERASE_FN_HOOK(ui32Temp); + } + + // + // Return an error if an access violation occurred. + // + if(BL_FLASH_ERROR_FN_HOOK()) + { + // + // Setting g_ui32TransferSize to zero makes + // COMMAND_SEND_DATA fail to accept any more data. + // + g_ui32TransferSize = 0; + + // + // Indicate that the flash erase failed. + // + g_ui8Status = COMMAND_RET_FLASH_FAIL; + } + } + + // + // Take one byte off for the command. + // + ui32Size = ui32Size - 1; + + // + // Check if there are any more bytes to receive. + // + if(g_ui32TransferSize >= ui32Size) + { + // + // If we have been provided with a decryption hook function + // call it here. + // +#ifdef BL_DECRYPT_FN_HOOK + BL_DECRYPT_FN_HOOK(g_pui8DataBuffer + 1, ui32Size); +#endif + + // + // Write this block of data to the flash + // + BL_FLASH_PROGRAM_FN_HOOK(g_ui32TransferAddress, + (uint8_t *) &g_pui32DataBuffer[1], + ((ui32Size + 3) & ~3)); + + // + // Return an error if an access violation occurred. + // + if(BL_FLASH_ERROR_FN_HOOK()) + { + // + // Indicate that the flash programming failed. + // + g_ui8Status = COMMAND_RET_FLASH_FAIL; + } + else + { + // + // Now update the address to program. + // + g_ui32TransferSize -= ui32Size; + g_ui32TransferAddress += ui32Size; + + // + // If a progress hook function has been provided, call + // it here. + // +#ifdef BL_PROGRESS_FN_HOOK + BL_PROGRESS_FN_HOOK(g_ui32ImageSize - + g_ui32TransferSize, + g_ui32ImageSize); +#endif + +#ifdef CHECK_CRC + // + // If we've reached the end, check the CRC in the + // image to determine whether or not we report an error + // back to the host. + // + if(g_ui32TransferSize == 0) + { + InitCRC32Table(); + ui32Retcode = CheckImageCRC32( + (uint32_t *)g_ui32ImageAddress); + + // + // Was the CRC good? We consider the CRC good if + // the header is found and the embedded CRC matches + // the calculated value or, if ENFORCE_CRC is not + // defined, if the header exists but is unpopulated. + // +#ifdef ENFORCE_CRC + if(ui32Retcode == CHECK_CRC_OK) +#else + if((ui32Retcode == CHECK_CRC_OK) || + (ui32Retcode == CHECK_CRC_NO_LENGTH)) +#endif + { + // + // The calculated CRC didn't match the expected + // value or the image didn't contain an embedded + // CRC. + // + g_ui8Status = COMMAND_RET_SUCCESS; + } + else + { + // + // The calculated CRC agreed with the embedded + // value. + // + g_ui8Status = COMMAND_RET_CRC_FAIL; + } + } +#endif + } + } + else + { + // + // This indicates that too much data is being sent to the + // device. + // + g_ui8Status = COMMAND_RET_INVALID_ADR; + } + + // + // Acknowledge that this command was received correctly. This + // does not indicate success, just that the command was + // received. + // + AckPacket(); + + // + // If we have an end notification hook function, and we've + // reached the end, call it now. + // +#ifdef BL_END_FN_HOOK + if(g_ui32TransferSize == 0) + { + BL_END_FN_HOOK(); + } +#endif + + // + // Go back and wait for a new command. + // + break; + } + + // + // This command is used to reset the device. + // + case COMMAND_RESET: + { + // + // Send out a one-byte ACK to ensure the byte goes back to the + // host before we reset everything. + // + AckPacket(); + + // + // Make sure that the ACK packet has been sent. + // + FlushData(); + + // + // Perform a software reset request. This will cause the + // microcontroller to reset; no further code will be executed. + // + HWREG(NVIC_APINT) = (NVIC_APINT_VECTKEY | + NVIC_APINT_SYSRESETREQ); + + // + // The microcontroller should have reset, so this should never + // be reached. Just in case, loop forever. + // + while(1) + { + } + } + + // + // Just acknowledge the command and set the error to indicate that + // a bad command was sent. + // + default: + { + // + // Acknowledge that this command was received correctly. This + // does not indicate success, just that the command was + // received. + // + AckPacket(); + + // + // Indicate that a bad comand was sent. + // + g_ui8Status = COMMAND_RET_UNKNOWN_CMD; + + // + // Go back and wait for a new command. + // + break; + } + } + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif diff --git a/boot_loader/bl_packet.c b/boot_loader/bl_packet.c new file mode 100644 index 0000000..f54495c --- /dev/null +++ b/boot_loader/bl_packet.c @@ -0,0 +1,295 @@ +//***************************************************************************** +// +// bl_packet.c - Packet handler functions used by the boot loader. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "bl_config.h" +#include "boot_loader/bl_commands.h" +#include "boot_loader/bl_i2c.h" +#include "boot_loader/bl_packet.h" +#include "boot_loader/bl_ssi.h" +#include "boot_loader/bl_uart.h" + +//***************************************************************************** +// +//! \addtogroup bl_packet_api +//! @{ +// +//***************************************************************************** +#if defined(I2C_ENABLE_UPDATE) || defined(SSI_ENABLE_UPDATE) || \ + defined(UART_ENABLE_UPDATE) || defined(DOXYGEN) + +//***************************************************************************** +// +// The packet that is sent to acknowledge a received packet. +// +//***************************************************************************** +static const uint8_t g_pui8ACK[2] = { 0, COMMAND_ACK }; + +//***************************************************************************** +// +// The packet that is sent to not-acknowledge a received packet. +// +//***************************************************************************** +static const uint8_t g_pui8NAK[2] = { 0, COMMAND_NAK }; + +//***************************************************************************** +// +//! Calculates an 8-bit checksum +//! +//! \param pui8Data is a pointer to an array of 8-bit data of size ui32Size. +//! \param ui32Size is the size of the array that will run through the checksum +//! algorithm. +//! +//! This function simply calculates an 8-bit checksum on the data passed in. +//! +//! \return Returns the calculated checksum. +// +//***************************************************************************** +uint32_t +CheckSum(const uint8_t *pui8Data, uint32_t ui32Size) +{ + uint32_t ui32CheckSum; + + // + // Initialize the checksum to zero. + // + ui32CheckSum = 0; + + // + // Add up all the bytes, do not do anything for an overflow. + // + while(ui32Size--) + { + ui32CheckSum += *pui8Data++; + } + + // + // Return the caculated check sum. + // + return(ui32CheckSum & 0xff); +} + +//***************************************************************************** +// +//! Sends an Acknowledge packet. +//! +//! This function is called to acknowledge that a packet has been received by +//! the microcontroller. +//! +//! \return None. +// +//***************************************************************************** +void +AckPacket(void) +{ + // + // ACK/NAK packets are the only ones with no size. + // + SendData(g_pui8ACK, 2); +} + +//***************************************************************************** +// +//! Sends a no-acknowledge packet. +//! +//! This function is called when an invalid packet has been received by the +//! microcontroller, indicating that it should be retransmitted. +//! +//! \return None. +// +//***************************************************************************** +void +NakPacket(void) +{ + // + // ACK/NAK packets are the only ones with no size. + // + SendData(g_pui8NAK, 2); +} + +//***************************************************************************** +// +//! Receives a data packet. +//! +//! \param pui8Data is the location to store the data that is sent to the boot +//! loader. +//! \param pui32Size is the number of bytes returned in the pui8Data buffer +//! that was provided. +//! +//! This function receives a packet of data from specified transfer function. +//! +//! \return Returns zero to indicate success while any non-zero value indicates +//! a failure. +// +//***************************************************************************** +int +ReceivePacket(uint8_t *pui8Data, uint32_t *pui32Size) +{ + uint32_t ui32Size, ui32CheckSum; + + // + // Wait for non-zero data before getting the first byte that holds the + // size of the packet we are receiving. + // + ui32Size = 0; + while(ui32Size == 0) + { + ReceiveData((uint8_t *)&ui32Size, 1); + } + + // + // Subtract off the size and checksum bytes. + // + ui32Size -= 2; + + // + // Receive the checksum followed by the actual data. + // + ReceiveData((uint8_t *)&ui32CheckSum, 1); + + // + // If there is room in the buffer then receive the requested data. + // + if(*pui32Size >= ui32Size) + { + // + // Receive the actual data in the packet. + // + ReceiveData(pui8Data, ui32Size); + + // + // Send a no acknowledge if the checksum does not match, otherwise send + // an acknowledge to the packet later. + // + if(CheckSum(pui8Data, ui32Size) != (ui32CheckSum & 0xff)) + { + // + // Indicate tha the packet was not received correctly. + // + NakPacket(); + + // + // Packet was not received, there is no valid data in the buffer. + // + return(-1); + } + } + else + { + // + // If the caller allocated a buffer that was too small for the received + // data packet, receive it but don't fill the buffer. + // Then inform the caller that the packet was not received correctly. + // + while(ui32Size--) + { + ReceiveData(pui8Data, 1); + } + + // + // Packet was not received, there is no valid data in the buffer. + // + return(-1); + } + + // + // Make sure to return the number of bytes received. + // + *pui32Size = ui32Size; + + // + // Packet was received successfully. + // + return(0); +} + +//***************************************************************************** +// +//! Sends a data packet. +//! +//! \param pui8Data is the location of the data to be sent. +//! \param ui32Size is the number of bytes to send. +//! +//! This function sends the data provided in the \e pui8Data parameter in the +//! packet format used by the boot loader. The caller only needs to specify +//! the buffer with the data that needs to be transferred. This function +//! addresses all other packet formatting issues. +//! +//! \return Returns zero to indicate success while any non-zero value indicates +//! a failure. +// +//***************************************************************************** +int +SendPacket(uint8_t *pui8Data, uint32_t ui32Size) +{ + uint32_t ui32Temp; + + // + // Caculate the checksum to be sent out with the data. + // + ui32Temp = CheckSum(pui8Data, ui32Size); + + // + // Need to include the size and checksum bytes in the packet. + // + ui32Size += 2; + + // + // Send out the size followed by the data. + // + SendData((uint8_t *)&ui32Size, 1); + SendData((uint8_t *)&ui32Temp, 1); + SendData(pui8Data, ui32Size - 2); + + // + // Wait for a non zero byte. + // + ui32Temp = 0; + while(ui32Temp == 0) + { + ReceiveData((uint8_t *)&ui32Temp, 1); + } + + // + // Check if the byte was a valid ACK and return a negative value if it was + // not and aknowledge. + // + if(ui32Temp != COMMAND_ACK) + { + return(-1); + } + + // + // This packet was sent and received successfully. + // + return(0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif diff --git a/boot_loader/bl_packet.h b/boot_loader/bl_packet.h new file mode 100644 index 0000000..3c4f78d --- /dev/null +++ b/boot_loader/bl_packet.h @@ -0,0 +1,37 @@ +//***************************************************************************** +// +// bl_packet.h - The global variables and definitions of the boot loader. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_PACKET_H__ +#define __BL_PACKET_H__ + +//***************************************************************************** +// +// Packet Handling APIs +// +//***************************************************************************** +extern int ReceivePacket(uint8_t *pui8Data, uint32_t *pui32Size); +extern int SendPacket(uint8_t *pui8Data, uint32_t ui32Size); +extern void AckPacket(void); + +#endif // __BL_PACKET_H__ diff --git a/boot_loader/bl_ssi.c b/boot_loader/bl_ssi.c new file mode 100644 index 0000000..ca10517 --- /dev/null +++ b/boot_loader/bl_ssi.c @@ -0,0 +1,161 @@ +//***************************************************************************** +// +// bl_ssi.c - Functions used to transfer data via the SSI port. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "inc/hw_gpio.h" +#include "inc/hw_memmap.h" +#include "inc/hw_ssi.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "bl_config.h" +#include "boot_loader/bl_ssi.h" + +//***************************************************************************** +// +//! \addtogroup bl_ssi_api +//! @{ +// +//***************************************************************************** +#if defined(SSI_ENABLE_UPDATE) || defined(DOXYGEN) + +//***************************************************************************** +// +//! Sends data via the SSI port in slave mode. +//! +//! \param pui8Data is the location of the data to send through the SSI port. +//! \param ui32Size is the number of bytes of data to send. +//! +//! This function sends data through the SSI port in slave mode. This function +//! will not return until all bytes are sent. +//! +//! \return None. +// +//***************************************************************************** +void +SSISend(const uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Send the requested number of bytes over the SSI port. + // + while(ui32Size--) + { + // + // Wait until there is space in the SSI FIFO. + // + while(!(HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_TNF)) + { + } + + // + // Write the next byte to the SSI port. + // + HWREG(SSI0_BASE + SSI_O_DR) = *pui8Data++; + } + + // + // Empty the receive FIFO. + // + while(HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_RNE) + { + HWREG(SSI0_BASE + SSI_O_DR); + } +} + +//***************************************************************************** +// +//! Waits until all data has been transmitted by the SSI port. +//! +//! This function waits until all data written to the SSI port has been read by +//! the master. +//! +//! \return None. +// +//***************************************************************************** +void +SSIFlush(void) +{ + // + // Wait until the transmit FIFO is empty. + // + while(!(HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_TFE)) + { + } + + // + // Wait until the interface is not busy. + // + while(HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_BSY) + { + } +} + +//***************************************************************************** +// +//! Receives data from the SSI port in slave mode. +//! +//! \param pui8Data is the location to store the data received from the SSI +//! port. +//! \param ui32Size is the number of bytes of data to receive. +//! +//! This function receives data from the SSI port in slave mode. The function +//! will not return until \e ui32Size number of bytes have been received. +//! +//! \return None. +// +//***************************************************************************** +void +SSIReceive(uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Ensure that we are sending out zeros so that we don't confuse the host. + // + HWREG(SSI0_BASE + SSI_O_DR) = 0; + + // + // Wait for the requested number of bytes. + // + while(ui32Size--) + { + // + // Wait until there is data in the FIFO. + // + while(!(HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_RNE)) + { + } + + // + // Read the next byte from the FIFO. + // + *pui8Data++ = HWREG(SSI0_BASE + SSI_O_DR); + HWREG(SSI0_BASE + SSI_O_DR) = 0; + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif diff --git a/boot_loader/bl_ssi.h b/boot_loader/bl_ssi.h new file mode 100644 index 0000000..05ec3bb --- /dev/null +++ b/boot_loader/bl_ssi.h @@ -0,0 +1,92 @@ +//***************************************************************************** +// +// bl_ssi.h - Definitions for the SSI transport functions. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_SSI_H__ +#define __BL_SSI_H__ + +//***************************************************************************** +// +// This is the number of bits per transfer for SSI. This is a constant and +// cannot be changed without corresponding code changes. +// +//***************************************************************************** +#define DATA_BITS_SSI 8 + +//***************************************************************************** +// +// This defines the SSI chip select pin that is being used by the boot loader. +// +//***************************************************************************** +#define SSI_CS (1 << 3) + +//***************************************************************************** +// +// This defines the SSI clock pin that is being used by the boot loader. +// +//***************************************************************************** +#define SSI_CLK (1 << 2) + +//***************************************************************************** +// +// This defines the SSI transmit pin that is being used by the boot loader. +// +//***************************************************************************** +#define SSI_TX (1 << 5) + +//***************************************************************************** +// +// This defines the SSI receive pin that is being used by the boot loader. +// +//***************************************************************************** +#define SSI_RX (1 << 4) + +//***************************************************************************** +// +// This defines the combination of pins used to implement the SSI port used by +// the boot loader. +// +//***************************************************************************** +#define SSI_PINS (SSI_CLK | SSI_TX | SSI_RX | SSI_CS) + +//***************************************************************************** +// +// SSI Transport APIs +// +//***************************************************************************** +extern void SSISend(const uint8_t *pui8Data, uint32_t ui32Size); +extern void SSIReceive(uint8_t *pui8Data, uint32_t ui32Size); +extern void SSIFlush(void); + +//***************************************************************************** +// +// Define the transport functions if the SSI port is being used. +// +//***************************************************************************** +#ifdef SSI_ENABLE_UPDATE +#define SendData SSISend +#define FlushData SSIFlush +#define ReceiveData SSIReceive +#endif + +#endif // __BL_SSI_H__ diff --git a/boot_loader/bl_startup_ccs.s b/boot_loader/bl_startup_ccs.s new file mode 100644 index 0000000..8bfa96b --- /dev/null +++ b/boot_loader/bl_startup_ccs.s @@ -0,0 +1,645 @@ +;;***************************************************************************** +;; +;; bl_startup_ccs.s - Boot loader startup code for Code Composer Studio +;; +;; Copyright (c) 2009-2014 Texas Instruments Incorporated. All rights reserved. +;; Software License Agreement +;; +;; Texas Instruments (TI) is supplying this software for use solely and +;; exclusively on TI's microcontroller products. The software is owned by +;; TI and/or its suppliers, and is protected under applicable copyright +;; laws. You may not combine this software with "viral" open-source +;; software in order to form a larger program. +;; +;; THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +;; NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +;; NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +;; A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +;; CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +;; DAMAGES, FOR ANY REASON WHATSOEVER. +;; +;; This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +;; +;;***************************************************************************** + +;;***************************************************************************** +;; +;; Include the boot loader configuration options. +;; +;;***************************************************************************** + .cdecls C, NOLIST, WARN + %{ + #include "inc/hw_nvic.h" + #include "inc/hw_sysctl.h" + #include "bl_config.h" + %} + +;;***************************************************************************** +;; +;; Export symbols from this file that are used elsewhere +;; +;;***************************************************************************** + .global ResetISR, Delay, Vectors + +;;***************************************************************************** +;; +;; Create the stack and put it in a section +;; +;;***************************************************************************** + .global __stack +__stack:.usect ".stack", STACK_SIZE * 4, 8 + +;;***************************************************************************** +;; +;; Put the assembler into the correct configuration. +;; +;;***************************************************************************** + .thumb + +;;***************************************************************************** +;; +;; This portion of the file goes into interrupt vectors section +;; +;;***************************************************************************** + .sect ".intvecs" + +;;***************************************************************************** +;; +;; The minimal vector table for a Cortex-M3 processor. +;; +;;***************************************************************************** +Vectors: + .ref __STACK_TOP + .word __STACK_TOP ;; Offset 00: Initial stack pointer + .word ResetISR - 0x20000000 ;; Offset 04: Reset handler + .word NmiSR - 0x20000000 ;; Offset 08: NMI handler + .word FaultISR - 0x20000000 ;; Offset 0C: Hard fault handler + .word IntDefaultHandler ;; Offset 10: MPU fault handler + .word IntDefaultHandler ;; Offset 14: Bus fault handler + .word IntDefaultHandler ;; Offset 18: Usage fault handler + .word 0 ;; Offset 1C: Reserved + .word 0 ;; Offset 20: Reserved + .word 0 ;; Offset 24: Reserved + .word 0 ;; Offset 28: Reserved + .word UpdateHandler - 0x20000000 ;; Offset 2C: SVCall handler + .word IntDefaultHandler ;; Offset 30: Debug monitor handler + .word 0 ;; Offset 34: Reserved + .word IntDefaultHandler ;; Offset 38: PendSV handler + .if $$defined(ENET_ENABLE_UPDATE) + .ref SysTickIntHandler + .word SysTickIntHandler ;; Offset 3C: SysTick handler + .else + .word IntDefaultHandler ;; Offset 3C: SysTick handler + .endif + .if $$defined(UART_ENABLE_UPDATE) & $$defined(UART_AUTOBAUD) + .ref GPIOIntHandler + .word GPIOIntHandler ;; Offset 40: GPIO port A handler + .else + .word IntDefaultHandler ;; Offset 40: GPIO port A handler + .endif + .if ($$defined(USB_ENABLE_UPDATE) | (APP_START_ADDRESS != VTABLE_START_ADDRESS)) + .word IntDefaultHandler ;; Offset 44: GPIO Port B + .word IntDefaultHandler ;; Offset 48: GPIO Port C + .word IntDefaultHandler ;; Offset 4C: GPIO Port D + .word IntDefaultHandler ;; Offset 50: GPIO Port E + .word IntDefaultHandler ;; Offset 54: UART0 Rx and Tx + .word IntDefaultHandler ;; Offset 58: UART1 Rx and Tx + .word IntDefaultHandler ;; Offset 5C: SSI0 Rx and Tx + .word IntDefaultHandler ;; Offset 60: I2C0 Master and Slave + .word IntDefaultHandler ;; Offset 64: PWM Fault + .word IntDefaultHandler ;; Offset 68: PWM Generator 0 + .word IntDefaultHandler ;; Offset 6C: PWM Generator 1 + .word IntDefaultHandler ;; Offset 70: PWM Generator 2 + .word IntDefaultHandler ;; Offset 74: Quadrature Encoder 0 + .word IntDefaultHandler ;; Offset 78: ADC Sequence 0 + .word IntDefaultHandler ;; Offset 7C: ADC Sequence 1 + .word IntDefaultHandler ;; Offset 80: ADC Sequence 2 + .word IntDefaultHandler ;; Offset 84: ADC Sequence 3 + .word IntDefaultHandler ;; Offset 88: Watchdog timer + .word IntDefaultHandler ;; Offset 8C: Timer 0 subtimer A + .word IntDefaultHandler ;; Offset 90: Timer 0 subtimer B + .word IntDefaultHandler ;; Offset 94: Timer 1 subtimer A + .word IntDefaultHandler ;; Offset 98: Timer 1 subtimer B + .word IntDefaultHandler ;; Offset 9C: Timer 2 subtimer A + .word IntDefaultHandler ;; Offset A0: Timer 2 subtimer B + .word IntDefaultHandler ;; Offset A4: Analog Comparator 0 + .word IntDefaultHandler ;; Offset A8: Analog Comparator 1 + .word IntDefaultHandler ;; Offset AC: Analog Comparator 2 + .word IntDefaultHandler ;; Offset B0: System Control + .word IntDefaultHandler ;; Offset B4: FLASH Control + .endif + .if ($$defined(USB_ENABLE_UPDATE) | (APP_START_ADDRESS != VTABLE_START_ADDRESS)) + .word IntDefaultHandler ;; Offset B8: GPIO Port F + .word IntDefaultHandler ;; Offset BC: GPIO Port G + .word IntDefaultHandler ;; Offset C0: GPIO Port H + .word IntDefaultHandler ;; Offset C4: UART2 Rx and Tx + .word IntDefaultHandler ;; Offset C8: SSI1 Rx and Tx + .word IntDefaultHandler ;; Offset CC: Timer 3 subtimer A + .word IntDefaultHandler ;; Offset D0: Timer 3 subtimer B + .word IntDefaultHandler ;; Offset D4: I2C1 Master and Slave + .word IntDefaultHandler ;; Offset D8: Quadrature Encoder 1 + .word IntDefaultHandler ;; Offset DC: CAN0 + .word IntDefaultHandler ;; Offset E0: CAN1 + .word IntDefaultHandler ;; Offset E4: CAN2 + .word IntDefaultHandler ;; Offset E8: Ethernet + .word IntDefaultHandler ;; Offset EC: Hibernation module + .if $$defined(USB_ENABLE_UPDATE) + .ref USB0DeviceIntHandler + .word USB0DeviceIntHandler ;; Offset F0: USB 0 Controller + .else + .word IntDefaultHandler ;; Offset F0: USB 0 Controller + .endif + .endif + +;;***************************************************************************** +;; +;; This portion of the file goes into the text section. +;; +;;***************************************************************************** + .text + +;;***************************************************************************** +;; +;; Initialize the processor by copying the boot loader from flash to SRAM, zero +;; filling the .bss section, and moving the vector table to the beginning of +;; SRAM. The return address is modified to point to the SRAM copy of the boot +;; loader instead of the flash copy, resulting in a branch to the copy now in +;; SRAM. +;; +;;***************************************************************************** + .ref bss_run +bss_start .word bss_run + .ref __STACK_TOP +bss_end .word __STACK_TOP + + .thumbfunc ProcessorInit +ProcessorInit: .asmfunc + ;; + ;; Copy the code image from flash to SRAM. + ;; + movs r0, #0x0000 + movs r1, #0x0000 + movt r1, #0x2000 + ldr r2, bss_start +copy_loop: + ldr r3, [r0], #4 + str r3, [r1], #4 + cmp r1, r2 + blt copy_loop + + ;; + ;; Zero fill the .bss section. + ;; + movs r0, #0x0000 + ldr r2, bss_end +zero_loop: + str r0, [r1], #4 + cmp r1, r2 + blt zero_loop + + ;; + ;; Set the vector table pointer to the beginning of SRAM. + ;; + movw r0, #(NVIC_VTABLE & 0xffff) + movt r0, #(NVIC_VTABLE >> 16) + movs r1, #0x0000 + movt r1, #0x2000 + str r1, [r0] + + ;; + ;; Set the return address to the code just copied into SRAM. + ;; + orr lr, lr, #0x20000000 + + ;; + ;; Return to the caller. + ;; + bx lr + .endasmfunc + +;;***************************************************************************** +;; +;; The reset handler, which gets called when the processor starts. +;; +;;***************************************************************************** + .thumbfunc ResetISR +ResetISR: .asmfunc + ;; + ;; Enable the floating-point unit. This must be done here in case any + ;; later C functions use floating point. Note that some toolchains will + ;; use the FPU registers for general workspace even if no explicit floating + ;; point data types are in use. + ;; + movw r0, #0xED88 + movt r0, #0xE000 + ldr r1, [r0] + orr r1, r1, #0x00F00000 + str r1, [r0] + + ;; + ;; Initialize the processor. + ;; + bl ProcessorInit + + ;; + ;; Call the user-supplied low level hardware initialization function + ;; if provided. + ;; + .if $$defined(BL_HW_INIT_FN_HOOK) + .ref BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK + .endif + + ;; + ;; See if an update should be performed. + ;; + .ref CheckForceUpdate + bl CheckForceUpdate + cbz r0, CallApplication + + ;; + ;; Configure the microcontroller. + ;; + .thumbfunc EnterBootLoader +EnterBootLoader: + .if $$defined(ENET_ENABLE_UPDATE) + .ref ConfigureEnet + bl ConfigureEnet + .elseif $$defined(CAN_ENABLE_UPDATE) + .ref ConfigureCAN + bl ConfigureCAN + .elseif $$defined(USB_ENABLE_UPDATE) + .ref ConfigureUSB + bl ConfigureUSB + .else + .ref ConfigureDevice + bl ConfigureDevice + .endif + + ;; + ;; Call the user-supplied initialization function if provided. + ;; + .if $$defined(BL_INIT_FN_HOOK) + .ref BL_INIT_FN_HOOK + bl BL_INIT_FN_HOOK + .endif + + ;; + ;; Branch to the update handler. + ;; + .if $$defined(ENET_ENABLE_UPDATE) + .ref UpdateBOOTP + b UpdateBOOTP + .elseif $$defined(CAN_ENABLE_UPDATE) + .ref UpdaterCAN + b UpdaterCAN + .elseif $$defined(USB_ENABLE_UPDATE) + .ref UpdaterUSB + b UpdaterUSB + .else + .ref Updater + b Updater + .endif + .endasmfunc + + ;; + ;; This is a second symbol to allow starting the application from the boot + ;; loader the linker may not like the perceived jump. + ;; + .global StartApplication + .thumbfunc StartApplication +StartApplication: + ;; + ;; Call the application via the reset handler in its vector table. Load + ;; the address of the application vector table. + ;; + .thumbfunc CallApplication +CallApplication: .asmfunc + ;; + ;; Copy the application's vector table to the target address if necessary. + ;; Note that incorrect boot loader configuration could cause this to + ;; corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + ;; of SRAM) is safe since this will use the same memory that the boot loader + ;; already uses for its vector table. Great care will have to be taken if + ;; other addresses are to be used. + ;; + .if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(VTABLE_START_ADDRESS & 0xffff) + .if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) + .endif + movw r1, #(APP_START_ADDRESS & 0xffff) + .if (APP_START_ADDRESS > 0xffff) + movt r1, #(APP_START_ADDRESS >> 16) + .endif + + ;; + ;; Calculate the end address of the vector table assuming that it has the + ;; maximum possible number of vectors. We don't know how many the app has + ;; populated so this is the safest approach though it may copy some non + ;; vector data if the app table is smaller than the maximum. + ;; + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop: + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop + .endif + + ;; + ;; Set the application's vector table start address. Typically this is the + ;; application start address but in some cases an application may relocate + ;; this so we can't assume that these two addresses are equal. + ;; + movw r0, #(VTABLE_START_ADDRESS & 0xffff) + .if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) + .endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + ;; + ;; Load the stack pointer from the application's vector table. + ;; + .if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(APP_START_ADDRESS & 0xffff) + .if (APP_START_ADDRESS > 0xffff) + movt r0, #(APP_START_ADDRESS >> 16) + .endif + .endif + ldr sp, [r0] + + ;; + ;; Load the initial PC from the application's vector table and branch to + ;; the application's entry point. + ;; + ldr r0, [r0, #4] + bx r0 + .endasmfunc + +;;***************************************************************************** +;; +;; The update handler, which gets called when the application would like to +;; start an update. +;; +;;***************************************************************************** + .thumbfunc UpdateHandler +UpdateHandler: .asmfunc + ;; + ;; Initialize the processor. + ;; + bl ProcessorInit + + ;; + ;; Load the stack pointer from the vector table. + ;; + movs r0, #0x0000 + ldr sp, [r0] + + ;; + ;; Call the user-supplied low level hardware initialization function + ;; if provided. + ;; + .if $$defined(BL_HW_INIT_FN_HOOK) + bl BL_HW_INIT_FN_HOOK + .endif + + ;; + ;; Call the user-supplied re-initialization function if provided. + ;; + .if $$defined(BL_REINIT_FN_HOOK) + .ref BL_REINIT_FN_HOOK + bl BL_REINIT_FN_HOOK + .endif + + ;; + ;; Branch to the update handler. + ;; + .if $$defined(ENET_ENABLE_UPDATE) + b UpdateBOOTP + .elseif $$defined(CAN_ENABLE_UPDATE) + .ref AppUpdaterCAN + b AppUpdaterCAN + .elseif $$defined(USB_ENABLE_UPDATE) + .ref AppUpdaterUSB + b AppUpdaterUSB + .else + b Updater + .endif + .endasmfunc + +;;***************************************************************************** +;; +;; The NMI handler. +;; +;;***************************************************************************** + .thumbfunc NmiSR +NmiSR: .asmfunc + .if $$defined(ENABLE_MOSCFAIL_HANDLER) + ;; + ;; Grab the fault frame from the stack (the stack will be cleared by the + ;; processor initialization that follows). + ;; + ldm sp, {r4-r11} + mov r12, lr + + ;; + ;; Initialize the processor. + ;; + bl ProcessorInit + + ;; + ;; Restore the stack frame. + ;; + mov lr, r12 + stm sp, {r4-r11} + + ;; + ;; Save the link register. + ;; + mov r9, lr + + ;; + ;; Call the user-supplied low level hardware initialization function + ;; if provided. + ;; + .if $$defined(BL_HW_INIT_FN_HOOK) + bl BL_HW_INIT_FN_HOOK + .endif + + ;; + ;; See if an update should be performed. + ;; + bl CheckForceUpdate + cbz r0, EnterApplication + + ;; + ;; Clear the MOSCFAIL bit in RESC. + ;; + movw r0, #(SYSCTL_RESC & 0xffff) + movt r0, #(SYSCTL_RESC >> 16) + ldr r1, [r0] + bic r1, r1, #SYSCTL_RESC_MOSCFAIL + str r1, [r0] + + ;; + ;; Fix up the PC on the stack so that the boot pin check is bypassed + ;; (since it has already been performed). + ;; + ldr r0, =EnterBootLoader + bic r0, #0x00000001 + str r0, [sp, #0x18] + + ;; + ;; Return from the NMI handler. This will then start execution of the + ;; boot loader. + ;; + bx r9 + + ;; + ;; Restore the link register. + ;; +EnterApplication: + mov lr, r9 + + ;; + ;; Copy the application's vector table to the target address if necessary. + ;; Note that incorrect boot loader configuration could cause this to + ;; corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + ;; of SRAM) is safe since this will use the same memory that the boot loader + ;; already uses for its vector table. Great care will have to be taken if + ;; other addresses are to be used. + ;; + .if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(VTABLE_START_ADDRESS & 0xffff) + .if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) + .endif + movw r1, #(APP_START_ADDRESS & 0xffff) + .if (APP_START_ADDRESS > 0xffff) + movt r1, #(APP_START_ADDRESS >> 16) + .endif + + ;; + ;; Calculate the end address of the vector table assuming that it has the + ;; maximum possible number of vectors. We don't know how many the app has + ;; populated so this is the safest approach though it may copy some non + ;; vector data if the app table is smaller than the maximum. + ;; + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop2: + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop2 + .endif + + ;; + ;; Set the application's vector table start address. Typically this is the + ;; application start address but in some cases an application may relocate + ;; this so we can't assume that these two addresses are equal. + ;; + movw r0, #(VTABLE_START_ADDRESS & 0xffff) + .if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) + .endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + ;; + ;; Remove the NMI stack frame from the boot loader's stack. + ;; + ldmia sp, {r4-r11} + + ;; + ;; Get the application's stack pointer. + ;; + .if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(APP_START_ADDRESS & 0xffff) + .if (APP_START_ADDRESS > 0xffff) + movt r0, #(APP_START_ADDRESS >> 16) + .endif + .endif + ldr sp, [r0, #0x00] + + ;; + ;; Fix up the NMI stack frame's return address to be the reset handler of + ;; the application. + ;; + ldr r10, [r0, #0x04] + bic r10, #0x00000001 + + ;; + ;; Store the NMI stack frame onto the application's stack. + ;; + stmdb sp!, {r4-r11} + + ;; + ;; Branch to the application's NMI handler. + ;; + ldr r0, [r0, #0x08] + bx r0 + .else + ;; + ;; Loop forever since there is nothing that we can do about a NMI. + ;; + b NmiSR + .endif + .endasmfunc + +;;***************************************************************************** +;; +;; The hard fault handler. +;; +;;***************************************************************************** + .thumbfunc FaultISR +FaultISR: .asmfunc + ;; + ;; Loop forever since there is nothing that we can do about a hard fault. + ;; + b FaultISR + .endasmfunc + +;;***************************************************************************** +;; +;; The default interrupt handler. +;; +;;***************************************************************************** + .thumbfunc IntDefaultHandler +IntDefaultHandler: .asmfunc + ;; + ;; Loop forever since there is nothing that we can do about an unexpected + ;; interrupt. + ;; + b IntDefaultHandler + .endasmfunc + +;;***************************************************************************** +;; +;; Provides a small delay. The loop below takes 3 cycles/loop. +;; +;;***************************************************************************** +; .globl Delay + .thumbfunc Delay +Delay: .asmfunc + subs r0, #1 + bne Delay + bx lr + .endasmfunc + + .thumbfunc _c_int00 + .global _c_int00 +_c_int00: .asmfunc + b ResetISR + +;;***************************************************************************** +;; +;; This is the end of the file. +;; +;;***************************************************************************** + .end diff --git a/boot_loader/bl_startup_ewarm.S b/boot_loader/bl_startup_ewarm.S new file mode 100644 index 0000000..2100b96 --- /dev/null +++ b/boot_loader/bl_startup_ewarm.S @@ -0,0 +1,612 @@ +//***************************************************************************** +// +// bl_startup_ewarm.S - Startup code for EWARM. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +//***************************************************************************** +// +// Include the assember definitions used to make this code compiler +// independent. +// +//***************************************************************************** +#include "inc/hw_nvic.h" +#include "inc/hw_sysctl.h" +#include "bl_config.h" + +//***************************************************************************** +// +// The stack gets placed into the zero-init section. +// +//***************************************************************************** + rseg .bss:DATA(2) + +//***************************************************************************** +// +// Allocate storage for the stack. +// +//***************************************************************************** + export g_pulStack +g_pulStack ds8 STACK_SIZE * 4 + +//***************************************************************************** +// +// This portion of the file goes into the vector section. +// +//***************************************************************************** + rseg INTVEC:CONST(2) + +//***************************************************************************** +// +// The minimal vector table for a Cortex-M3 processor. +// +//***************************************************************************** + export __vector_table +__vector_table + dcd g_pulStack + (STACK_SIZE * 4) // Offset 00: Initial stack pointer + dcd ResetISR - 0x20000000 // Offset 04: Reset handler + dcd NmiSR - 0x20000000 // Offset 08: NMI handler + dcd FaultISR - 0x20000000 // Offset 0C: Hard fault handler + dcd IntDefaultHandler // Offset 10: MPU fault handler + dcd IntDefaultHandler // Offset 14: Bus fault handler + dcd IntDefaultHandler // Offset 18: Usage fault handler + dcd 0 // Offset 1C: Reserved + dcd 0 // Offset 20: Reserved + dcd 0 // Offset 24: Reserved + dcd 0 // Offset 28: Reserved + dcd UpdateHandler - 0x20000000 // Offset 2C: SVCall handler + dcd IntDefaultHandler // Offset 30: Debug monitor handler + dcd 0 // Offset 34: Reserved + dcd IntDefaultHandler // Offset 38: PendSV handler +#if defined(ENET_ENABLE_UPDATE) + import SysTickIntHandler + dcd SysTickIntHandler // Offset 3C: SysTick handler +#else + dcd IntDefaultHandler // Offset 3C: SysTick handler +#endif +#if defined(UART_ENABLE_UPDATE) && defined(UART_AUTOBAUD) + import GPIOIntHandler + dcd GPIOIntHandler // Offset 40: GPIO port A handler +#else + dcd IntDefaultHandler // Offset 40: GPIO port A handler +#endif +#if (defined(USB_ENABLE_UPDATE) || \ + (APP_START_ADDRESS != VTABLE_START_ADDRESS)) + dcd IntDefaultHandler // Offset 44: GPIO Port B + dcd IntDefaultHandler // Offset 48: GPIO Port C + dcd IntDefaultHandler // Offset 4C: GPIO Port D + dcd IntDefaultHandler // Offset 50: GPIO Port E + dcd IntDefaultHandler // Offset 54: UART0 Rx and Tx + dcd IntDefaultHandler // Offset 58: UART1 Rx and Tx + dcd IntDefaultHandler // Offset 5C: SSI0 Rx and Tx + dcd IntDefaultHandler // Offset 60: I2C0 Master and Slave + dcd IntDefaultHandler // Offset 64: PWM Fault + dcd IntDefaultHandler // Offset 68: PWM Generator 0 + dcd IntDefaultHandler // Offset 6C: PWM Generator 1 + dcd IntDefaultHandler // Offset 70: PWM Generator 2 + dcd IntDefaultHandler // Offset 74: Quadrature Encoder 0 + dcd IntDefaultHandler // Offset 78: ADC Sequence 0 + dcd IntDefaultHandler // Offset 7C: ADC Sequence 1 + dcd IntDefaultHandler // Offset 80: ADC Sequence 2 + dcd IntDefaultHandler // Offset 84: ADC Sequence 3 + dcd IntDefaultHandler // Offset 88: Watchdog timer + dcd IntDefaultHandler // Offset 8C: Timer 0 subtimer A + dcd IntDefaultHandler // Offset 90: Timer 0 subtimer B + dcd IntDefaultHandler // Offset 94: Timer 1 subtimer A + dcd IntDefaultHandler // Offset 98: Timer 1 subtimer B + dcd IntDefaultHandler // Offset 9C: Timer 2 subtimer A + dcd IntDefaultHandler // Offset A0: Timer 2 subtimer B + dcd IntDefaultHandler // Offset A4: Analog Comparator 0 + dcd IntDefaultHandler // Offset A8: Analog Comparator 1 + dcd IntDefaultHandler // Offset AC: Analog Comparator 2 + dcd IntDefaultHandler // Offset B0: System Control + dcd IntDefaultHandler // Offset B4: FLASH Control +#endif +#if (defined(USB_ENABLE_UPDATE) || (APP_START_ADDRESS != VTABLE_START_ADDRESS)) + dcd IntDefaultHandler // Offset B8: GPIO Port F + dcd IntDefaultHandler // Offset BC: GPIO Port G + dcd IntDefaultHandler // Offset C0: GPIO Port H + dcd IntDefaultHandler // Offset C4: UART2 Rx and Tx + dcd IntDefaultHandler // Offset C8: SSI1 Rx and Tx + dcd IntDefaultHandler // Offset CC: Timer 3 subtimer A + dcd IntDefaultHandler // Offset D0: Timer 3 subtimer B + dcd IntDefaultHandler // Offset D4: I2C1 Master and Slave + dcd IntDefaultHandler // Offset D8: Quadrature Encoder 1 + dcd IntDefaultHandler // Offset DC: CAN0 + dcd IntDefaultHandler // Offset E0: CAN1 + dcd IntDefaultHandler // Offset E4: CAN2 + dcd IntDefaultHandler // Offset E8: Ethernet + dcd IntDefaultHandler // Offset EC: Hibernation module +#if defined(USB_ENABLE_UPDATE) + import USB0DeviceIntHandler + dcd USB0DeviceIntHandler // Offset F0: USB 0 Controller +#else + dcd IntDefaultHandler // Offset F0: USB 0 Controller +#endif +#endif + +//***************************************************************************** +// +// This portion of the file goes into the text section. +// +//***************************************************************************** + rseg CODE:CODE(2) + thumb + +//***************************************************************************** +// +// Initialize the processor by copying the boot loader from flash to SRAM, zero +// filling the .bss section, and moving the vector table to the beginning of +// SRAM. The return address is modified to point to the SRAM copy of the boot +// loader instead of the flash copy, resulting in a branch to the copy now in +// SRAM. +// +//***************************************************************************** +ProcessorInit + // + // Copy the code image from flash to SRAM. + // + movs r0, #0x0000 + movs r1, #0x0000 + movt r1, #0x2000 + ldr r2, =SFB(.bss) +copy_loop + ldr r3, [r0], #4 + str r3, [r1], #4 + cmp r1, r2 + blt copy_loop + + // + // Zero fill the .bss section. + // + movs r0, #0x0000 + ldr r2, =SFE(.bss) +zero_loop + str r0, [r1], #4 + cmp r1, r2 + blt zero_loop + + // + // Set the vector table pointer to the beginning of SRAM. + // + movw r0, #(NVIC_VTABLE & 0xffff) + movt r0, #(NVIC_VTABLE >> 16) + movs r1, #0x0000 + movt r1, #0x2000 + str r1, [r0] + + // + // Set the return address to the code just copied into SRAM. + // + orr lr, lr, #0x20000000 + + // + // Return to the caller. + // + bx lr + +//***************************************************************************** +// +// The reset handler, which gets called when the processor starts. +// +//***************************************************************************** + export ResetISR +ResetISR + // + // Enable the floating-point unit. This must be done here in case any + // later C functions use floating point. Note that some toolchains will + // use the FPU registers for general workspace even if no explicit floating + // point data types are in use. + // + movw r0, #0xED88 + movt r0, #0xE000 + ldr r1, [r0] + orr r1, r1, #0x00F00000 + str r1, [r0] + + // + // Initialize the processor. + // + bl ProcessorInit + + // + // Call the user-supplied low level hardware initialization function + // if provided. + // +#ifdef BL_HW_INIT_FN_HOOK + import BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK +#endif + + // + // See if an update should be performed. + // + import CheckForceUpdate + bl CheckForceUpdate + cbz r0, CallApplication + + // + // Configure the microcontroller. + // +EnterBootLoader +#ifdef ENET_ENABLE_UPDATE + import ConfigureEnet + bl ConfigureEnet +#elif defined(CAN_ENABLE_UPDATE) + import ConfigureCAN + bl ConfigureCAN +#elif defined(USB_ENABLE_UPDATE) + import ConfigureUSB + bl ConfigureUSB +#else + import ConfigureDevice + bl ConfigureDevice +#endif + + // + // Call the user-supplied initialization function if provided. + // +#ifdef BL_INIT_FN_HOOK + import BL_INIT_FN_HOOK + bl BL_INIT_FN_HOOK +#endif + + // + // Branch to the update handler. + // +#ifdef ENET_ENABLE_UPDATE + import UpdateBOOTP + b UpdateBOOTP +#elif defined(CAN_ENABLE_UPDATE) + import UpdaterCAN + b UpdaterCAN +#elif defined(USB_ENABLE_UPDATE) + import UpdaterUSB + b UpdaterUSB +#else + import Updater + b Updater +#endif + + // + // This is a second symbol to allow starting the application from the boot + // loader the linker may not like the perceived jump. + // + export StartApplication +StartApplication + // + // Call the application via the reset handler in its vector table. Load + // the address of the application's vector table first. + // +CallApplication + // + // Copy the application's vector table to the target address if necessary. + // Note that incorrect boot loader configuration could cause this to + // corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + // of SRAM) is safe since this will use the same memory that the boot loader + // already uses for its vector table. Great care will have to be taken if + // other addresses are to be used. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r1, #(APP_START_ADDRESS >> 16) +#endif + + // + // Calculate the end address of the vector table assuming that it has the + // maximum possible number of vectors. We don't know how many the app has + // populated so this is the safest approach though it may copy some non + // vector data if the app table is smaller than the maximum. + // + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop +#endif + + // + // Set the application's vector table start address. Typically this is the + // application start address but in some cases an application may relocate + // this so we can't assume that these two addresses are equal. + // + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + // + // Load the stack pointer from the application's vector table at the + // beginning of the image. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r0, #(APP_START_ADDRESS >> 16) +#endif +#endif + ldr sp, [r0] + + // + // Load the initial PC from the application's vector table and branch to + // the application's entry point. + // + ldr r0, [r0, #4] + bx r0 + +//***************************************************************************** +// +// The update handler, which gets called when the application would like to +// start an update. +// +//***************************************************************************** +UpdateHandler + // + // Initialize the processor. + // + bl ProcessorInit + + // + // Load the stack pointer from the vector table. + // + movs r0, #0x0000 + ldr sp, [r0] + + // + // Call the user-supplied low level hardware initialization function + // if provided. + // +#ifdef BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK +#endif + + // + // Call the user-supplied re-initialization function if provided. + // +#ifdef BL_REINIT_FN_HOOK + import BL_REINIT_FN_HOOK + bl BL_REINIT_FN_HOOK +#endif + + // + // Branch to the update handler. + // +#ifdef ENET_ENABLE_UPDATE + b UpdateBOOTP +#elif defined(CAN_ENABLE_UPDATE) + import AppUpdaterCAN + b AppUpdaterCAN +#elif defined(USB_ENABLE_UPDATE) + import AppUpdaterUSB + b AppUpdaterUSB +#else + b Updater +#endif + +//***************************************************************************** +// +// The NMI handler. +// +//***************************************************************************** +NmiSR +#ifdef ENABLE_MOSCFAIL_HANDLER + // + // Grab the fault frame from the stack (the stack will be cleared by the + // processor initialization that follows). + // + ldm sp, {r4-r11} + mov r12, lr + + // + // Initialize the processor. + // + bl ProcessorInit + + // + // Restore the stack frame. + // + mov lr, r12 + stm sp, {r4-r11} + + // + // Save the link register. + // + mov r9, lr + + // + // Call the user-supplied low level hardware initialization function + // if provided. + // +#ifdef BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK +#endif + + // + // See if an update should be performed. + // + bl CheckForceUpdate + cbz r0, EnterApplication + + // + // Clear the MOSCFAIL bit in RESC. + // + movw r0, #(SYSCTL_RESC & 0xffff) + movt r0, #(SYSCTL_RESC >> 16) + ldr r1, [r0] + bic r1, r1, #SYSCTL_RESC_MOSCFAIL + str r1, [r0] + + // + // Fix up the PC on the stack so that the boot pin check is bypassed + // (since it has already been performed). + // + ldr r0, =EnterBootLoader + bic r0, #0x00000001 + str r0, [sp, #0x18] + + // + // Return from the NMI handler. This will then start execution of the + // boot loader. + // + bx r9 + + // + // Restore the link register. + // +EnterApplication: + mov lr, r9 + + // + // Copy the application's vector table to the target address if necessary. + // Note that incorrect boot loader configuration could cause this to + // corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + // of SRAM) is safe since this will use the same memory that the boot loader + // already uses for its vector table. Great care will have to be taken if + // other addresses are to be used. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r1, #(APP_START_ADDRESS >> 16) +#endif + + // + // Calculate the end address of the vector table assuming that it has the + // maximum possible number of vectors. We don't know how many the app has + // populated so this is the safest approach though it may copy some non + // vector data if the app table is smaller than the maximum. + // + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop2: + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop2 +#endif + + // + // Set the application's vector table start address. Typically this is the + // application start address but in some cases an application may relocate + // this so we can't assume that these two addresses are equal. + // + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + // + // Remove the NMI stack frame from the boot loader's stack. + // + ldmia sp, {r4-r11} + + // + // Get the application's stack pointer. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r0, #(APP_START_ADDRESS >> 16) +#endif +#endif + ldr sp, [r0, #0x00] + + // + // Fix up the NMI stack frame's return address to be the reset handler of + // the application. + // + ldr r10, [r0, #0x04] + bic r10, #0x00000001 + + // + // Store the NMI stack frame onto the application's stack. + // + stmdb sp!, {r4-r11} + + // + // Branch to the application's NMI handler. + // + ldr r0, [r0, #0x08] + bx r0 +#else + // + // Loop forever since there is nothing that we can do about a NMI. + // + b . +#endif + +//***************************************************************************** +// +// The hard fault handler. +// +//***************************************************************************** +FaultISR + // + // Loop forever since there is nothing that we can do about a hard fault. + // + b . + +//***************************************************************************** +// +// The default interrupt handler. +// +//***************************************************************************** +IntDefaultHandler + // + // Loop forever since there is nothing that we can do about an unexpected + // interrupt. + // + b . + +//***************************************************************************** +// +// Provides a small delay. The loop below takes 3 cycles/loop. +// +//***************************************************************************** + export Delay +Delay + subs r0, #1 + bne Delay + bx lr + +//***************************************************************************** +// +// This is the end of the file. +// +//***************************************************************************** + end diff --git a/boot_loader/bl_startup_gcc.S b/boot_loader/bl_startup_gcc.S new file mode 100644 index 0000000..53f28d1 --- /dev/null +++ b/boot_loader/bl_startup_gcc.S @@ -0,0 +1,630 @@ +//***************************************************************************** +// +// bl_startup_gcc.S - Startup code for GNU. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +//***************************************************************************** +// +// Include the assember definitions used to make this code compiler +// independent. +// +//***************************************************************************** +#include "inc/hw_nvic.h" +#include "inc/hw_sysctl.h" +#include "bl_config.h" + +//***************************************************************************** +// +// Put the assembler into the correct configuration. +// +//***************************************************************************** + .syntax unified + .thumb + +//***************************************************************************** +// +// The stack gets placed into the zero-init section. +// +//***************************************************************************** + .bss + +//***************************************************************************** +// +// Allocate storage for the stack. +// +//***************************************************************************** +g_pulStack: + .space STACK_SIZE * 4 + +//***************************************************************************** +// +// This portion of the file goes into the text section. +// +//***************************************************************************** + .section .isr_vector + +//***************************************************************************** +// +// The minimal vector table for a Cortex-M3 processor. +// +//***************************************************************************** +Vectors: + .word g_pulStack + (STACK_SIZE * 4) // Offset 00: Initial stack pointer + .word ResetISR - 0x20000000 // Offset 04: Reset handler + .word NmiSR - 0x20000000 // Offset 08: NMI handler + .word FaultISR - 0x20000000 // Offset 0C: Hard fault handler + .word IntDefaultHandler // Offset 10: MPU fault handler + .word IntDefaultHandler // Offset 14: Bus fault handler + .word IntDefaultHandler // Offset 18: Usage fault handler + .word 0 // Offset 1C: Reserved + .word 0 // Offset 20: Reserved + .word 0 // Offset 24: Reserved + .word 0 // Offset 28: Reserved + .word UpdateHandler - 0x20000000 // Offset 2C: SVCall handler + .word IntDefaultHandler // Offset 30: Debug monitor handler + .word 0 // Offset 34: Reserved + .word IntDefaultHandler // Offset 38: PendSV handler +#if defined(ENET_ENABLE_UPDATE) + .extern SysTickIntHandler + .word SysTickIntHandler // Offset 3C: SysTick handler +#else + .word IntDefaultHandler // Offset 3C: SysTick handler +#endif +#if defined(UART_ENABLE_UPDATE) && defined(UART_AUTOBAUD) + .extern GPIOIntHandler + .word GPIOIntHandler // Offset 40: GPIO port A handler +#else + .word IntDefaultHandler // Offset 40: GPIO port A handler +#endif +#if (defined(USB_ENABLE_UPDATE) || \ + (APP_START_ADDRESS != VTABLE_START_ADDRESS)) + .word IntDefaultHandler // Offset 44: GPIO Port B + .word IntDefaultHandler // Offset 48: GPIO Port C + .word IntDefaultHandler // Offset 4C: GPIO Port D + .word IntDefaultHandler // Offset 50: GPIO Port E + .word IntDefaultHandler // Offset 54: UART0 Rx and Tx + .word IntDefaultHandler // Offset 58: UART1 Rx and Tx + .word IntDefaultHandler // Offset 5C: SSI0 Rx and Tx + .word IntDefaultHandler // Offset 60: I2C0 Master and Slave + .word IntDefaultHandler // Offset 64: PWM Fault + .word IntDefaultHandler // Offset 68: PWM Generator 0 + .word IntDefaultHandler // Offset 6C: PWM Generator 1 + .word IntDefaultHandler // Offset 70: PWM Generator 2 + .word IntDefaultHandler // Offset 74: Quadrature Encoder 0 + .word IntDefaultHandler // Offset 78: ADC Sequence 0 + .word IntDefaultHandler // Offset 7C: ADC Sequence 1 + .word IntDefaultHandler // Offset 80: ADC Sequence 2 + .word IntDefaultHandler // Offset 84: ADC Sequence 3 + .word IntDefaultHandler // Offset 88: Watchdog timer + .word IntDefaultHandler // Offset 8C: Timer 0 subtimer A + .word IntDefaultHandler // Offset 90: Timer 0 subtimer B + .word IntDefaultHandler // Offset 94: Timer 1 subtimer A + .word IntDefaultHandler // Offset 98: Timer 1 subtimer B + .word IntDefaultHandler // Offset 9C: Timer 2 subtimer A + .word IntDefaultHandler // Offset A0: Timer 2 subtimer B + .word IntDefaultHandler // Offset A4: Analog Comparator 0 + .word IntDefaultHandler // Offset A8: Analog Comparator 1 + .word IntDefaultHandler // Offset AC: Analog Comparator 2 + .word IntDefaultHandler // Offset B0: System Control + .word IntDefaultHandler // Offset B4: FLASH Control +#endif +#if (defined(USB_ENABLE_UPDATE) || (APP_START_ADDRESS != VTABLE_START_ADDRESS)) + .word IntDefaultHandler // Offset B8: GPIO Port F + .word IntDefaultHandler // Offset BC: GPIO Port G + .word IntDefaultHandler // Offset C0: GPIO Port H + .word IntDefaultHandler // Offset C4: UART2 Rx and Tx + .word IntDefaultHandler // Offset C8: SSI1 Rx and Tx + .word IntDefaultHandler // Offset CC: Timer 3 subtimer A + .word IntDefaultHandler // Offset D0: Timer 3 subtimer B + .word IntDefaultHandler // Offset D4: I2C1 Master and Slave + .word IntDefaultHandler // Offset D8: Quadrature Encoder 1 + .word IntDefaultHandler // Offset DC: CAN0 + .word IntDefaultHandler // Offset E0: CAN1 + .word IntDefaultHandler // Offset E4: CAN2 + .word IntDefaultHandler // Offset E8: Ethernet + .word IntDefaultHandler // Offset EC: Hibernation module +#if defined(USB_ENABLE_UPDATE) + .extern USB0DeviceIntHandler + .word USB0DeviceIntHandler // Offset F0: USB 0 Controller +#else + .word IntDefaultHandler // Offset F0: USB 0 Controller +#endif +#endif + +//***************************************************************************** +// +// This portion of the file goes into the text section. +// +//***************************************************************************** + .text + +//***************************************************************************** +// +// Initialize the processor by copying the boot loader from flash to SRAM, zero +// filling the .bss section, and moving the vector table to the beginning of +// SRAM. The return address is modified to point to the SRAM copy of the boot +// loader instead of the flash copy, resulting in a branch to the copy now in +// SRAM. +// +//***************************************************************************** + .thumb_func +ProcessorInit: + // + // Copy the code image from flash to SRAM. + // + movs r0, #0x0000 + movs r1, #0x0000 + movt r1, #0x2000 + .extern _bss + ldr r2, =_bss +copy_loop: + ldr r3, [r0], #4 + str r3, [r1], #4 + cmp r1, r2 + blt copy_loop + + // + // Zero fill the .bss section. + // + movs r0, #0x0000 + .extern _ebss + ldr r2, =_ebss +zero_loop: + str r0, [r1], #4 + cmp r1, r2 + blt zero_loop + + // + // Set the vector table pointer to the beginning of SRAM. + // + movw r0, #(NVIC_VTABLE & 0xffff) + movt r0, #(NVIC_VTABLE >> 16) + movs r1, #0x0000 + movt r1, #0x2000 + str r1, [r0] + + // + // Set the return address to the code just copied into SRAM. + // + orr lr, lr, #0x20000000 + + // + // Return to the caller. + // + bx lr + +//***************************************************************************** +// +// The reset handler, which gets called when the processor starts. +// +//***************************************************************************** + .globl ResetISR + .thumb_func +ResetISR: + // + // Enable the floating-point unit. This must be done here in case any + // later C functions use floating point. Note that some toolchains will + // use the FPU registers for general workspace even if no explicit floating + // point data types are in use. + // + movw r0, #0xED88 + movt r0, #0xE000 + ldr r1, [r0] + orr r1, r1, #0x00F00000 + str r1, [r0] + + // + // Initialize the processor. + // + bl ProcessorInit + + // + // Call the user-supplied low level hardware initialization function + // if provided. + // +#ifdef BL_HW_INIT_FN_HOOK + .extern BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK +#endif + + // + // See if an update should be performed. + // + .extern CheckForceUpdate + bl CheckForceUpdate + cbz r0, CallApplication + + // + // Configure the microcontroller. + // + .thumb_func +EnterBootLoader: +#ifdef ENET_ENABLE_UPDATE + .extern ConfigureEnet + bl ConfigureEnet +#elif defined(CAN_ENABLE_UPDATE) + .extern ConfigureCAN + bl ConfigureCAN +#elif defined(USB_ENABLE_UPDATE) + .extern ConfigureUSB + bl ConfigureUSB +#else + .extern ConfigureDevice + bl ConfigureDevice +#endif + + // + // Call the user-supplied initialization function if provided. + // +#ifdef BL_INIT_FN_HOOK + .extern BL_INIT_FN_HOOK + bl BL_INIT_FN_HOOK +#endif + + // + // Branch to the update handler. + // +#ifdef ENET_ENABLE_UPDATE + .extern UpdateBOOTP + b UpdateBOOTP +#elif defined(CAN_ENABLE_UPDATE) + .extern UpdaterCAN + b UpdaterCAN +#elif defined(USB_ENABLE_UPDATE) + .extern UpdaterUSB + b UpdaterUSB +#else + .extern Updater + b Updater +#endif + + // + // This is a second symbol to allow starting the application from the boot + // loader the linker may not like the perceived jump. + // + .globl StartApplication + .thumb_func +StartApplication: + // + // Call the application via the reset handler in its vector table. Load + // the address of the application vector table. + // + .thumb_func +CallApplication: + // + // Copy the application's vector table to the target address if necessary. + // Note that incorrect boot loader configuration could cause this to + // corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + // of SRAM) is safe since this will use the same memory that the boot loader + // already uses for its vector table. Great care will have to be taken if + // other addresses are to be used. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r1, #(APP_START_ADDRESS >> 16) +#endif + + // + // Calculate the end address of the vector table assuming that it has the + // maximum possible number of vectors. We don't know how many the app has + // populated so this is the safest approach though it may copy some non + // vector data if the app table is smaller than the maximum. + // + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop: + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop +#endif + + // + // Set the application's vector table start address. Typically this is the + // application start address but in some cases an application may relocate + // this so we can't assume that these two addresses are equal. + // + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + // + // Load the stack pointer from the application's vector table. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r0, #(APP_START_ADDRESS >> 16) +#endif +#endif + ldr sp, [r0] + + // + // Load the initial PC from the application's vector table and branch to + // the application's entry point. + // + ldr r0, [r0, #4] + bx r0 + +//***************************************************************************** +// +// The update handler, which gets called when the application would like to +// start an update. +// +//***************************************************************************** + .thumb_func +UpdateHandler: + // + // Initialize the processor. + // + bl ProcessorInit + + // + // Load the stack pointer from the vector table. + // + movs r0, #0x0000 + ldr sp, [r0] + + // + // Call the user-supplied low level hardware initialization function + // if provided. + // +#ifdef BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK +#endif + + // + // Call the user-supplied re-initialization function if provided. + // +#ifdef BL_REINIT_FN_HOOK + .extern BL_REINIT_FN_HOOK + bl BL_REINIT_FN_HOOK +#endif + + // + // Branch to the update handler. + // +#ifdef ENET_ENABLE_UPDATE + b UpdateBOOTP +#elif defined(CAN_ENABLE_UPDATE) + .extern AppUpdaterCAN + b AppUpdaterCAN +#elif defined(USB_ENABLE_UPDATE) + .extern AppUpdaterUSB + b AppUpdaterUSB +#else + b Updater +#endif + +//***************************************************************************** +// +// The NMI handler. +// +//***************************************************************************** + .thumb_func +NmiSR: +#ifdef ENABLE_MOSCFAIL_HANDLER + // + // Grab the fault frame from the stack (the stack will be cleared by the + // processor initialization that follows). + // + ldm sp, {r4-r11} + mov r12, lr + + // + // Initialize the processor. + // + bl ProcessorInit + + // + // Restore the stack frame. + // + mov lr, r12 + stm sp, {r4-r11} + + // + // Save the link register. + // + mov r9, lr + + // + // Call the user-supplied low level hardware initialization function + // if provided. + // +#ifdef BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK +#endif + + // + // See if an update should be performed. + // + bl CheckForceUpdate + cbz r0, EnterApplication + + // + // Clear the MOSCFAIL bit in RESC. + // + movw r0, #(SYSCTL_RESC & 0xffff) + movt r0, #(SYSCTL_RESC >> 16) + ldr r1, [r0] + bic r1, r1, #SYSCTL_RESC_MOSCFAIL + str r1, [r0] + + // + // Fix up the PC on the stack so that the boot pin check is bypassed + // (since it has already been performed). + // + ldr r0, =EnterBootLoader + bic r0, #0x00000001 + str r0, [sp, #0x18] + + // + // Return from the NMI handler. This will then start execution of the + // boot loader. + // + bx r9 + + // + // Restore the link register. + // + .thumb_func +EnterApplication: + mov lr, r9 + + // + // Copy the application's vector table to the target address if necessary. + // Note that incorrect boot loader configuration could cause this to + // corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + // of SRAM) is safe since this will use the same memory that the boot loader + // already uses for its vector table. Great care will have to be taken if + // other addresses are to be used. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r1, #(APP_START_ADDRESS >> 16) +#endif + + // + // Calculate the end address of the vector table assuming that it has the + // maximum possible number of vectors. We don't know how many the app has + // populated so this is the safest approach though it may copy some non + // vector data if the app table is smaller than the maximum. + // + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop2: + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop2 +#endif + + // + // Set the application's vector table start address. Typically this is the + // application start address but in some cases an application may relocate + // this so we can't assume that these two addresses are equal. + // + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + // + // Remove the NMI stack frame from the boot loader's stack. + // + ldmia sp, {r4-r11} + + // + // Get the application's stack pointer. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r0, #(APP_START_ADDRESS >> 16) +#endif +#endif + ldr sp, [r0, #0x00] + + // + // Fix up the NMI stack frame's return address to be the reset handler of + // the application. + // + ldr r10, [r0, #0x04] + bic r10, #0x00000001 + + // + // Store the NMI stack frame onto the application's stack. + // + stmdb sp!, {r4-r11} + + // + // Branch to the application's NMI handler. + // + ldr r0, [r0, #0x08] + bx r0 +#else + // + // Loop forever since there is nothing that we can do about a NMI. + // + b . +#endif + +//***************************************************************************** +// +// The hard fault handler. +// +//***************************************************************************** + .thumb_func +FaultISR: + // + // Loop forever since there is nothing that we can do about a hard fault. + // + b . + +//***************************************************************************** +// +// The default interrupt handler. +// +//***************************************************************************** + .thumb_func +IntDefaultHandler: + // + // Loop forever since there is nothing that we can do about an unexpected + // interrupt. + // + b . + +//***************************************************************************** +// +// Provides a small delay. The loop below takes 3 cycles/loop. +// +//***************************************************************************** + .globl Delay + .thumb_func +Delay: + subs r0, #1 + bne Delay + bx lr + +//***************************************************************************** +// +// This is the end of the file. +// +//***************************************************************************** + .end diff --git a/boot_loader/bl_startup_rvmdk.S b/boot_loader/bl_startup_rvmdk.S new file mode 100644 index 0000000..435c325 --- /dev/null +++ b/boot_loader/bl_startup_rvmdk.S @@ -0,0 +1,653 @@ +;****************************************************************************** +; +; bl_startup_rvmdk.S - Startup code for RV-MDK. +; +; Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +; Software License Agreement +; +; Texas Instruments (TI) is supplying this software for use solely and +; exclusively on TI's microcontroller products. The software is owned by +; TI and/or its suppliers, and is protected under applicable copyright +; laws. You may not combine this software with "viral" open-source +; software in order to form a larger program. +; +; THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +; NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +; NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +; CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +; DAMAGES, FOR ANY REASON WHATSOEVER. +; +; This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +; +;****************************************************************************** + + include bl_config.inc + +;****************************************************************************** +; +; A couple of defines that would normally be obtained from the appropriate C +; header file, but must be manually provided here since the Keil compiler does +; not have a mechanism for passing assembly source through the C preprocessor. +; +;****************************************************************************** +SYSCTL_RESC equ 0x400fe05c +SYSCTL_RESC_MOSCFAIL equ 0x00010000 +NVIC_VTABLE equ 0xe000ed08 + +;****************************************************************************** +; +; Put the assembler into the correct configuration. +; +;****************************************************************************** + thumb + require8 + preserve8 + +;****************************************************************************** +; +; The stack gets placed into the zero-init section. +; +;****************************************************************************** + area ||.bss||, noinit, align=2 + +;****************************************************************************** +; +; Allocate storage for the stack. +; +;****************************************************************************** +g_pulStack + space _STACK_SIZE * 4 + +;****************************************************************************** +; +; This portion of the file goes into the reset section. +; +;****************************************************************************** + area RESET, code, readonly, align=3 + +;****************************************************************************** +; +; The minimal vector table for a Cortex-M3 processor. +; +;****************************************************************************** + export __Vectors +__Vectors + dcd g_pulStack + (_STACK_SIZE * 4) ; Offset 00: Initial stack pointer + dcd Reset_Handler ; Offset 04: Reset handler + dcd NmiSR ; Offset 08: NMI handler + dcd FaultISR ; Offset 0C: Hard fault handler + dcd IntDefaultHandler ; Offset 10: MPU fault handler + dcd IntDefaultHandler ; Offset 14: Bus fault handler + dcd IntDefaultHandler ; Offset 18: Usage fault handler + dcd 0 ; Offset 1C: Reserved + dcd 0 ; Offset 20: Reserved + dcd 0 ; Offset 24: Reserved + dcd 0 ; Offset 28: Reserved + dcd UpdateHandler ; Offset 2C: SVCall handler + dcd IntDefaultHandler ; Offset 30: Debug monitor handler + dcd 0 ; Offset 34: Reserved + dcd IntDefaultHandler ; Offset 38: PendSV handler + if :def:_ENET_ENABLE_UPDATE + import SysTickIntHandler + dcd SysTickIntHandler ; Offset 3C: SysTick handler + else + dcd IntDefaultHandler ; Offset 3C: SysTick handler + endif + if :def:_UART_ENABLE_UPDATE :land: :def:_UART_AUTOBAUD + import GPIOIntHandler + dcd GPIOIntHandler ; Offset 40: GPIO port A handler + else + dcd IntDefaultHandler ; Offset 40: GPIO port A handler + endif + if :def:_USB_ENABLE_UPDATE :lor: \ + (_APP_START_ADDRESS != _VTABLE_START_ADDRESS) + dcd IntDefaultHandler ; Offset 44: GPIO Port B + dcd IntDefaultHandler ; Offset 48: GPIO Port C + dcd IntDefaultHandler ; Offset 4C: GPIO Port D + dcd IntDefaultHandler ; Offset 50: GPIO Port E + dcd IntDefaultHandler ; Offset 54: UART0 Rx and Tx + dcd IntDefaultHandler ; Offset 58: UART1 Rx and Tx + dcd IntDefaultHandler ; Offset 5C: SSI0 Rx and Tx + dcd IntDefaultHandler ; Offset 60: I2C0 Master and Slave + dcd IntDefaultHandler ; Offset 64: PWM Fault + dcd IntDefaultHandler ; Offset 68: PWM Generator 0 + dcd IntDefaultHandler ; Offset 6C: PWM Generator 1 + dcd IntDefaultHandler ; Offset 70: PWM Generator 2 + dcd IntDefaultHandler ; Offset 74: Quadrature Encoder 0 + dcd IntDefaultHandler ; Offset 78: ADC Sequence 0 + dcd IntDefaultHandler ; Offset 7C: ADC Sequence 1 + dcd IntDefaultHandler ; Offset 80: ADC Sequence 2 + dcd IntDefaultHandler ; Offset 84: ADC Sequence 3 + dcd IntDefaultHandler ; Offset 88: Watchdog timer + dcd IntDefaultHandler ; Offset 8C: Timer 0 subtimer A + dcd IntDefaultHandler ; Offset 90: Timer 0 subtimer B + dcd IntDefaultHandler ; Offset 94: Timer 1 subtimer A + dcd IntDefaultHandler ; Offset 98: Timer 1 subtimer B + dcd IntDefaultHandler ; Offset 9C: Timer 2 subtimer A + dcd IntDefaultHandler ; Offset A0: Timer 2 subtimer B + dcd IntDefaultHandler ; Offset A4: Analog Comparator 0 + dcd IntDefaultHandler ; Offset A8: Analog Comparator 1 + dcd IntDefaultHandler ; Offset AC: Analog Comparator 2 + dcd IntDefaultHandler ; Offset B0: System Control + dcd IntDefaultHandler ; Offset B4: FLASH Control + endif + if :def:_USB_ENABLE_UPDATE :lor: \ + (_APP_START_ADDRESS != _VTABLE_START_ADDRESS) + dcd IntDefaultHandler ; Offset B8: GPIO Port F + dcd IntDefaultHandler ; Offset BC: GPIO Port G + dcd IntDefaultHandler ; Offset C0: GPIO Port H + dcd IntDefaultHandler ; Offset C4: UART2 Rx and Tx + dcd IntDefaultHandler ; Offset C8: SSI1 Rx and Tx + dcd IntDefaultHandler ; Offset CC: Timer 3 subtimer A + dcd IntDefaultHandler ; Offset D0: Timer 3 subtimer B + dcd IntDefaultHandler ; Offset D4: I2C1 Master and Slave + dcd IntDefaultHandler ; Offset D8: Quadrature Encoder 1 + dcd IntDefaultHandler ; Offset DC: CAN0 + dcd IntDefaultHandler ; Offset E0: CAN1 + dcd IntDefaultHandler ; Offset E4: CAN2 + dcd IntDefaultHandler ; Offset E8: Ethernet + dcd IntDefaultHandler ; Offset EC: Hibernation module + if :def: _USB_ENABLE_UPDATE + import USB0DeviceIntHandler + dcd USB0DeviceIntHandler ; Offset F0: USB 0 Controller + else + dcd IntDefaultHandler ; Offset F0: USB 0 Controller + endif + endif + +;****************************************************************************** +; +; Initialize the processor by copying the boot loader from flash to SRAM, zero +; filling the .bss section, and moving the vector table to the beginning of +; SRAM. The return address is modified to point to the SRAM copy of the boot +; loader instead of the flash copy, resulting in a branch to the copy now in +; SRAM. +; +;****************************************************************************** + export ProcessorInit +ProcessorInit + ; + ; Copy the code image from flash to SRAM. + ; + movs r0, #0x0000 + movs r1, #0x0000 + movt r1, #0x2000 + import ||Image$$SRAM$$ZI$$Base|| + ldr r2, =||Image$$SRAM$$ZI$$Base|| +copy_loop + ldr r3, [r0], #4 + str r3, [r1], #4 + cmp r1, r2 + blt copy_loop + + ; + ; Zero fill the .bss section. + ; + movs r0, #0x0000 + import ||Image$$SRAM$$ZI$$Limit|| + ldr r2, =||Image$$SRAM$$ZI$$Limit|| +zero_loop + str r0, [r1], #4 + cmp r1, r2 + blt zero_loop + + ; + ; Set the vector table pointer to the beginning of SRAM. + ; + movw r0, #(NVIC_VTABLE & 0xffff) + movt r0, #(NVIC_VTABLE >> 16) + movs r1, #0x0000 + movt r1, #0x2000 + str r1, [r0] + + ; + ; Return to the caller. + ; + bx lr + +;****************************************************************************** +; +; The reset handler, which gets called when the processor starts. +; +;****************************************************************************** + export Reset_Handler +Reset_Handler + + ; + ; Enable the floating-point unit. This must be done here in case any + ; later C functions use floating point. Note that some toolchains will + ; use the FPU registers for general workspace even if no explicit floating + ; point data types are in use. + ; + movw r0, #0xED88 + movt r0, #0xE000 + ldr r1, [r0] + orr r1, #0x00F00000 + str r1, [r0] + + ; + ; Initialize the processor. + ; + bl ProcessorInit + + ; + ; Branch to the SRAM copy of the reset handler. + ; + ldr pc, =Reset_Handler_In_SRAM + +;****************************************************************************** +; +; The NMI handler. +; +;****************************************************************************** +NmiSR + if :def:_ENABLE_MOSCFAIL_HANDLER + ; + ; Grab the fault frame from the stack (the stack will be cleared by the + ; processor initialization that follows). + ; + ldm sp, {r4-r11} + mov r12, lr + + ; + ; Initialize the processor. + ; + bl ProcessorInit + + ; + ; Branch to the SRAM copy of the NMI handler. + ; + ldr pc, =NmiSR_In_SRAM + else + ; + ; Loop forever since there is nothing that we can do about a NMI. + ; + b . + endif + +;****************************************************************************** +; +; The hard fault handler. +; +;****************************************************************************** +FaultISR + ; + ; Loop forever since there is nothing that we can do about a hard fault. + ; + b . + +;****************************************************************************** +; +; The update handler, which gets called when the application would like to +; start an update. +; +;****************************************************************************** +UpdateHandler + ; + ; Initialize the processor. + ; + bl ProcessorInit + + ; + ; Branch to the SRAM copy of the update handler. + ; + ldr pc, =UpdateHandler_In_SRAM + +;****************************************************************************** +; +; This portion of the file goes into the text section. +; +;****************************************************************************** + align 4 + area ||.text||, code, readonly, align=2 + +Reset_Handler_In_SRAM + ; + ; Call the user-supplied low level hardware initialization function + ; if provided. + ; + if :def:_BL_HW_INIT_FN_HOOK + import $_BL_HW_INIT_FN_HOOK + bl $_BL_HW_INIT_FN_HOOK + endif + + ; + ; See if an update should be performed. + ; + import CheckForceUpdate + bl CheckForceUpdate + cbz r0, CallApplication + + ; + ; Configure the microcontroller. + ; +EnterBootLoader + if :def:_ENET_ENABLE_UPDATE + import ConfigureEnet + bl ConfigureEnet + elif :def:_CAN_ENABLE_UPDATE + import ConfigureCAN + bl ConfigureCAN + elif :def:_USB_ENABLE_UPDATE + import ConfigureUSB + bl ConfigureUSB + else + import ConfigureDevice + bl ConfigureDevice + endif + + ; + ; Call the user-supplied initialization function if provided. + ; + if :def:_BL_INIT_FN_HOOK + import $_BL_INIT_FN_HOOK + bl $_BL_INIT_FN_HOOK + endif + + ; + ; Branch to the update handler. + ; + if :def:_ENET_ENABLE_UPDATE + import UpdateBOOTP + b UpdateBOOTP + elif :def:_CAN_ENABLE_UPDATE + import UpdaterCAN + b UpdaterCAN + elif :def:_USB_ENABLE_UPDATE + import UpdaterUSB + b UpdaterUSB + else + import Updater + b Updater + endif + + ; + ; This is a second symbol to allow starting the application from the boot + ; loader the linker may not like the perceived jump. + ; + export StartApplication +StartApplication + ; + ; Call the application via the reset handler in its vector table. Load the + ; address of the application vector table. + ; +CallApplication + ; + ; Copy the application's vector table to the target address if necessary. + ; Note that incorrect boot loader configuration could cause this to + ; corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + ; of SRAM) is safe since this will use the same memory that the boot loader + ; already uses for its vector table. Great care will have to be taken if + ; other addresses are to be used. + ; + if (_APP_START_ADDRESS != _VTABLE_START_ADDRESS) + movw r0, #(_VTABLE_START_ADDRESS & 0xffff) + if (_VTABLE_START_ADDRESS > 0xffff) + movt r0, #(_VTABLE_START_ADDRESS >> 16) + endif + movw r1, #(_APP_START_ADDRESS & 0xffff) + if (_APP_START_ADDRESS > 0xffff) + movt r1, #(_APP_START_ADDRESS >> 16) + endif + + ; + ; Calculate the end address of the vector table assuming that it has the + ; maximum possible number of vectors. We don't know how many the app has + ; populated so this is the safest approach though it may copy some non + ; vector data if the app table is smaller than the maximum. + ; + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop + endif + + ; + ; Set the vector table address to the beginning of the application. + ; + movw r0, #(_VTABLE_START_ADDRESS & 0xffff) + if (_VTABLE_START_ADDRESS > 0xffff) + movt r0, #(_VTABLE_START_ADDRESS >> 16) + endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + ; + ; Load the stack pointer from the application's vector table. + ; + if (_APP_START_ADDRESS != _VTABLE_START_ADDRESS) + movw r0, #(_APP_START_ADDRESS & 0xffff) + if (_APP_START_ADDRESS > 0xffff) + movt r0, #(_APP_START_ADDRESS >> 16) + endif + endif + ldr sp, [r0] + + ; + ; Load the initial PC from the application's vector table and branch to + ; the application's entry point. + ; + ldr r0, [r0, #4] + bx r0 + +;****************************************************************************** +; +; The update handler, which gets called when the application would like to +; start an update. +; +;****************************************************************************** +UpdateHandler_In_SRAM + ; + ; Load the stack pointer from the vector table. + ; + movs r0, #0x0000 + ldr sp, [r0] + + ; + ; Call the user-supplied low level hardware initialization function + ; if provided. + ; + if :def:_BL_HW_INIT_FN_HOOK + bl $_BL_HW_INIT_FN_HOOK + endif + + ; + ; Call the user-supplied re-initialization function if provided. + ; + if :def:_BL_REINIT_FN_HOOK + import $_BL_REINIT_FN_HOOK + bl $_BL_REINIT_FN_HOOK + endif + + ; + ; Branch to the update handler. + ; + if :def:_ENET_ENABLE_UPDATE + b UpdateBOOTP + elif :def:_CAN_ENABLE_UPDATE + import AppUpdaterCAN + b AppUpdaterCAN + elif :def:_USB_ENABLE_UPDATE + import AppUpdaterUSB + b AppUpdaterUSB + else + b Updater + endif + +;****************************************************************************** +; +; The NMI handler. +; +;****************************************************************************** + if :def:_ENABLE_MOSCFAIL_HANDLER +NmiSR_In_SRAM + ; + ; Restore the stack frame. + ; + mov lr, r12 + stm sp, {r4-r11} + + ; + ; Save the link register. + ; + mov r9, lr + + ; + ; Call the user-supplied low level hardware initialization function + ; if provided. + ; + if :def:_BL_HW_INIT_FN_HOOK + bl _BL_HW_INIT_FN_HOOK + endif + + ; + ; See if an update should be performed. + ; + bl CheckForceUpdate + cbz r0, EnterApplication + + ; + ; Clear the MOSCFAIL bit in RESC. + ; + movw r0, #(SYSCTL_RESC & 0xffff) + movt r0, #(SYSCTL_RESC >> 16) + ldr r1, [r0] + bic r1, r1, #SYSCTL_RESC_MOSCFAIL + str r1, [r0] + + ; + ; Fix up the PC on the stack so that the boot pin check is bypassed + ; (since it has already been performed). + ; + ldr r0, =EnterBootLoader + bic r0, #0x00000001 + str r0, [sp, #0x18] + + ; + ; Return from the NMI handler. This will then start execution of the + ; boot loader. + ; + bx r9 + + ; + ; Restore the link register. + ; +EnterApplication + mov lr, r9 + + ; + ; Copy the application's vector table to the target address if necessary. + ; Note that incorrect boot loader configuration could cause this to + ; corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + ; of SRAM) is safe since this will use the same memory that the boot loader + ; already uses for its vector table. Great care will have to be taken if + ; other addresses are to be used. + ; + if (_APP_START_ADDRESS != _VTABLE_START_ADDRESS) + movw r0, #(_VTABLE_START_ADDRESS & 0xffff) + if (_VTABLE_START_ADDRESS > 0xffff) + movt r0, #(_VTABLE_START_ADDRESS >> 16) + endif + movw r1, #(_APP_START_ADDRESS & 0xffff) + if (_APP_START_ADDRESS > 0xffff) + movt r1, #(_APP_START_ADDRESS >> 16) + endif + + ; + ; Calculate the end address of the vector table assuming that it has the + ; maximum possible number of vectors. We don't know how many the app has + ; populated so this is the safest approach though it may copy some non + ; vector data if the app table is smaller than the maximum. + ; + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop2 + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop2 + endif + + ; + ; Set the application's vector table start address. Typically this is the + ; application start address but in some cases an application may relocate + ; this so we can't assume that these two addresses are equal. + ; + movw r0, #(_VTABLE_START_ADDRESS & 0xffff) + if (_VTABLE_START_ADDRESS > 0xffff) + movt r0, #(_VTABLE_START_ADDRESS >> 16) + endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + ; + ; Remove the NMI stack frame from the boot loader's stack. + ; + ldmia sp, {r4-r11} + + ; + ; Get the application's stack pointer. + ; + if (_APP_START_ADDRESS != _VTABLE_START_ADDRESS) + movw r0, #(_APP_START_ADDRESS & 0xffff) + if (_APP_START_ADDRESS > 0xffff) + movt r0, #(_APP_START_ADDRESS >> 16) + endif + endif + ldr sp, [r0, #0x00] + + ; + ; Fix up the NMI stack frame's return address to be the reset handler of + ; the application. + ; + ldr r10, [r0, #0x04] + bic r10, #0x00000001 + + ; + ; Store the NMI stack frame onto the application's stack. + ; + stmdb sp!, {r4-r11} + + ; + ; Branch to the application's NMI handler. + ; + ldr r0, [r0, #0x08] + bx r0 + endif + +;****************************************************************************** +; +; The default interrupt handler. +; +;****************************************************************************** +IntDefaultHandler + ; + ; Loop forever since there is nothing that we can do about an unexpected + ; interrupt. + ; + b . + +;****************************************************************************** +; +; Provides a small delay. The loop below takes 3 cycles/loop. +; +;****************************************************************************** + export Delay +Delay + subs r0, #1 + bne Delay + bx lr + +;****************************************************************************** +; +; This is the end of the file. +; +;****************************************************************************** + align 4 + end diff --git a/boot_loader/bl_startup_sourcerygxx.S b/boot_loader/bl_startup_sourcerygxx.S new file mode 100644 index 0000000..ded9231 --- /dev/null +++ b/boot_loader/bl_startup_sourcerygxx.S @@ -0,0 +1,630 @@ +//***************************************************************************** +// +// bl_startup_sourcerygxx.S - Startup code for Sourcery G++. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +//***************************************************************************** +// +// Include the assember definitions used to make this code compiler +// independent. +// +//***************************************************************************** +#include "inc/hw_nvic.h" +#include "inc/hw_sysctl.h" +#include "bl_config.h" + +//***************************************************************************** +// +// Put the assembler into the correct configuration. +// +//***************************************************************************** + .syntax unified + .thumb + +//***************************************************************************** +// +// The stack gets placed into the zero-init section. +// +//***************************************************************************** + .bss + +//***************************************************************************** +// +// Allocate storage for the stack. +// +//***************************************************************************** +g_pulStack: + .space STACK_SIZE * 4 + +//***************************************************************************** +// +// This portion of the file goes into the text section. +// +//***************************************************************************** + .section .isr_vector + +//***************************************************************************** +// +// The minimal vector table for a Cortex-M3 processor. +// +//***************************************************************************** +Vectors: + .word g_pulStack + (STACK_SIZE * 4) // Offset 00: Initial stack pointer + .word ResetISR - 0x20000000 // Offset 04: Reset handler + .word NmiSR - 0x20000000 // Offset 08: NMI handler + .word FaultISR - 0x20000000 // Offset 0C: Hard fault handler + .word IntDefaultHandler // Offset 10: MPU fault handler + .word IntDefaultHandler // Offset 14: Bus fault handler + .word IntDefaultHandler // Offset 18: Usage fault handler + .word 0 // Offset 1C: Reserved + .word 0 // Offset 20: Reserved + .word 0 // Offset 24: Reserved + .word 0 // Offset 28: Reserved + .word UpdateHandler - 0x20000000 // Offset 2C: SVCall handler + .word IntDefaultHandler // Offset 30: Debug monitor handler + .word 0 // Offset 34: Reserved + .word IntDefaultHandler // Offset 38: PendSV handler +#if defined(ENET_ENABLE_UPDATE) + .extern SysTickIntHandler + .word SysTickIntHandler // Offset 3C: SysTick handler +#else + .word IntDefaultHandler // Offset 3C: SysTick handler +#endif +#if defined(UART_ENABLE_UPDATE) && defined(UART_AUTOBAUD) + .extern GPIOIntHandler + .word GPIOIntHandler // Offset 40: GPIO port A handler +#else + .word IntDefaultHandler // Offset 40: GPIO port A handler +#endif +#if (defined(USB_ENABLE_UPDATE) || \ + (APP_START_ADDRESS != VTABLE_START_ADDRESS)) + .word IntDefaultHandler // Offset 44: GPIO Port B + .word IntDefaultHandler // Offset 48: GPIO Port C + .word IntDefaultHandler // Offset 4C: GPIO Port D + .word IntDefaultHandler // Offset 50: GPIO Port E + .word IntDefaultHandler // Offset 54: UART0 Rx and Tx + .word IntDefaultHandler // Offset 58: UART1 Rx and Tx + .word IntDefaultHandler // Offset 5C: SSI0 Rx and Tx + .word IntDefaultHandler // Offset 60: I2C0 Master and Slave + .word IntDefaultHandler // Offset 64: PWM Fault + .word IntDefaultHandler // Offset 68: PWM Generator 0 + .word IntDefaultHandler // Offset 6C: PWM Generator 1 + .word IntDefaultHandler // Offset 70: PWM Generator 2 + .word IntDefaultHandler // Offset 74: Quadrature Encoder 0 + .word IntDefaultHandler // Offset 78: ADC Sequence 0 + .word IntDefaultHandler // Offset 7C: ADC Sequence 1 + .word IntDefaultHandler // Offset 80: ADC Sequence 2 + .word IntDefaultHandler // Offset 84: ADC Sequence 3 + .word IntDefaultHandler // Offset 88: Watchdog timer + .word IntDefaultHandler // Offset 8C: Timer 0 subtimer A + .word IntDefaultHandler // Offset 90: Timer 0 subtimer B + .word IntDefaultHandler // Offset 94: Timer 1 subtimer A + .word IntDefaultHandler // Offset 98: Timer 1 subtimer B + .word IntDefaultHandler // Offset 9C: Timer 2 subtimer A + .word IntDefaultHandler // Offset A0: Timer 2 subtimer B + .word IntDefaultHandler // Offset A4: Analog Comparator 0 + .word IntDefaultHandler // Offset A8: Analog Comparator 1 + .word IntDefaultHandler // Offset AC: Analog Comparator 2 + .word IntDefaultHandler // Offset B0: System Control + .word IntDefaultHandler // Offset B4: FLASH Control +#endif +#if (defined(USB_ENABLE_UPDATE) || (APP_START_ADDRESS != VTABLE_START_ADDRESS)) + .word IntDefaultHandler // Offset B8: GPIO Port F + .word IntDefaultHandler // Offset BC: GPIO Port G + .word IntDefaultHandler // Offset C0: GPIO Port H + .word IntDefaultHandler // Offset C4: UART2 Rx and Tx + .word IntDefaultHandler // Offset C8: SSI1 Rx and Tx + .word IntDefaultHandler // Offset CC: Timer 3 subtimer A + .word IntDefaultHandler // Offset D0: Timer 3 subtimer B + .word IntDefaultHandler // Offset D4: I2C1 Master and Slave + .word IntDefaultHandler // Offset D8: Quadrature Encoder 1 + .word IntDefaultHandler // Offset DC: CAN0 + .word IntDefaultHandler // Offset E0: CAN1 + .word IntDefaultHandler // Offset E4: CAN2 + .word IntDefaultHandler // Offset E8: Ethernet + .word IntDefaultHandler // Offset EC: Hibernation module +#if defined(USB_ENABLE_UPDATE) + .extern USB0DeviceIntHandler + .word USB0DeviceIntHandler // Offset F0: USB 0 Controller +#else + .word IntDefaultHandler // Offset F0: USB 0 Controller +#endif +#endif + +//***************************************************************************** +// +// This portion of the file goes into the text section. +// +//***************************************************************************** + .text + +//***************************************************************************** +// +// Initialize the processor by copying the boot loader from flash to SRAM, zero +// filling the .bss section, and moving the vector table to the beginning of +// SRAM. The return address is modified to point to the SRAM copy of the boot +// loader instead of the flash copy, resulting in a branch to the copy now in +// SRAM. +// +//***************************************************************************** + .thumb_func +ProcessorInit: + // + // Copy the code image from flash to SRAM. + // + movs r0, #0x0000 + movs r1, #0x0000 + movt r1, #0x2000 + .extern _bss + ldr r2, =_bss +copy_loop: + ldr r3, [r0], #4 + str r3, [r1], #4 + cmp r1, r2 + blt copy_loop + + // + // Zero fill the .bss section. + // + movs r0, #0x0000 + .extern _ebss + ldr r2, =_ebss +zero_loop: + str r0, [r1], #4 + cmp r1, r2 + blt zero_loop + + // + // Set the vector table pointer to the beginning of SRAM. + // + movw r0, #(NVIC_VTABLE & 0xffff) + movt r0, #(NVIC_VTABLE >> 16) + movs r1, #0x0000 + movt r1, #0x2000 + str r1, [r0] + + // + // Set the return address to the code just copied into SRAM. + // + orr lr, lr, #0x20000000 + + // + // Return to the caller. + // + bx lr + +//***************************************************************************** +// +// The reset handler, which gets called when the processor starts. +// +//***************************************************************************** + .globl ResetISR + .thumb_func +ResetISR: + // + // Enable the floating-point unit. This must be done here in case any + // later C functions use floating point. Note that some toolchains will + // use the FPU registers for general workspace even if no explicit floating + // point data types are in use. + // + movw r0, #0xED88 + movt r0, #0xE000 + ldr r1, [r0] + orr r1, r1, #0x00F00000 + str r1, [r0] + + // + // Initialize the processor. + // + bl ProcessorInit + + // + // Call the user-supplied low level hardware initialization function + // if provided. + // +#ifdef BL_HW_INIT_FN_HOOK + .extern BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK +#endif + + // + // See if an update should be performed. + // + .extern CheckForceUpdate + bl CheckForceUpdate + cbz r0, CallApplication + + // + // Configure the microcontroller. + // + .thumb_func +EnterBootLoader: +#ifdef ENET_ENABLE_UPDATE + .extern ConfigureEnet + bl ConfigureEnet +#elif defined(CAN_ENABLE_UPDATE) + .extern ConfigureCAN + bl ConfigureCAN +#elif defined(USB_ENABLE_UPDATE) + .extern ConfigureUSB + bl ConfigureUSB +#else + .extern ConfigureDevice + bl ConfigureDevice +#endif + + // + // Call the user-supplied initialization function if provided. + // +#ifdef BL_INIT_FN_HOOK + .extern BL_INIT_FN_HOOK + bl BL_INIT_FN_HOOK +#endif + + // + // Branch to the update handler. + // +#ifdef ENET_ENABLE_UPDATE + .extern UpdateBOOTP + b UpdateBOOTP +#elif defined(CAN_ENABLE_UPDATE) + .extern UpdaterCAN + b UpdaterCAN +#elif defined(USB_ENABLE_UPDATE) + .extern UpdaterUSB + b UpdaterUSB +#else + .extern Updater + b Updater +#endif + + // + // This is a second symbol to allow starting the application from the boot + // loader the linker may not like the perceived jump. + // + .globl StartApplication + .thumb_func +StartApplication: + // + // Call the application via the reset handler in its vector table. Load + // the address of the application vector table. + // + .thumb_func +CallApplication: + // + // Copy the application's vector table to the target address if necessary. + // Note that incorrect boot loader configuration could cause this to + // corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + // of SRAM) is safe since this will use the same memory that the boot loader + // already uses for its vector table. Great care will have to be taken if + // other addresses are to be used. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r1, #(APP_START_ADDRESS >> 16) +#endif + + // + // Calculate the end address of the vector table assuming that it has the + // maximum possible number of vectors. We don't know how many the app has + // populated so this is the safest approach though it may copy some non + // vector data if the app table is smaller than the maximum. + // + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop: + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop +#endif + + // + // Set the application's vector table start address. Typically this is the + // application start address but in some cases an application may relocate + // this so we can't assume that these two addresses are equal. + // + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + // + // Load the stack pointer from the application's vector table. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r0, #(APP_START_ADDRESS >> 16) +#endif +#endif + ldr sp, [r0] + + // + // Load the initial PC from the application's vector table and branch to + // the application's entry point. + // + ldr r0, [r0, #4] + bx r0 + +//***************************************************************************** +// +// The update handler, which gets called when the application would like to +// start an update. +// +//***************************************************************************** + .thumb_func +UpdateHandler: + // + // Initialize the processor. + // + bl ProcessorInit + + // + // Load the stack pointer from the vector table. + // + movs r0, #0x0000 + ldr sp, [r0] + + // + // Call the user-supplied low level hardware initialization function + // if provided. + // +#ifdef BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK +#endif + + // + // Call the user-supplied re-initialization function if provided. + // +#ifdef BL_REINIT_FN_HOOK + .extern BL_REINIT_FN_HOOK + bl BL_REINIT_FN_HOOK +#endif + + // + // Branch to the update handler. + // +#ifdef ENET_ENABLE_UPDATE + b UpdateBOOTP +#elif defined(CAN_ENABLE_UPDATE) + .extern AppUpdaterCAN + b AppUpdaterCAN +#elif defined(USB_ENABLE_UPDATE) + .extern AppUpdaterUSB + b AppUpdaterUSB +#else + b Updater +#endif + +//***************************************************************************** +// +// The NMI handler. +// +//***************************************************************************** + .thumb_func +NmiSR: +#ifdef ENABLE_MOSCFAIL_HANDLER + // + // Grab the fault frame from the stack (the stack will be cleared by the + // processor initialization that follows). + // + ldm sp, {r4-r11} + mov r12, lr + + // + // Initialize the processor. + // + bl ProcessorInit + + // + // Restore the stack frame. + // + mov lr, r12 + stm sp, {r4-r11} + + // + // Save the link register. + // + mov r9, lr + + // + // Call the user-supplied low level hardware initialization function + // if provided. + // +#ifdef BL_HW_INIT_FN_HOOK + bl BL_HW_INIT_FN_HOOK +#endif + + // + // See if an update should be performed. + // + bl CheckForceUpdate + cbz r0, EnterApplication + + // + // Clear the MOSCFAIL bit in RESC. + // + movw r0, #(SYSCTL_RESC & 0xffff) + movt r0, #(SYSCTL_RESC >> 16) + ldr r1, [r0] + bic r1, r1, #SYSCTL_RESC_MOSCFAIL + str r1, [r0] + + // + // Fix up the PC on the stack so that the boot pin check is bypassed + // (since it has already been performed). + // + ldr r0, =EnterBootLoader + bic r0, #0x00000001 + str r0, [sp, #0x18] + + // + // Return from the NMI handler. This will then start execution of the + // boot loader. + // + bx r9 + + // + // Restore the link register. + // + .thumb_func +EnterApplication: + mov lr, r9 + + // + // Copy the application's vector table to the target address if necessary. + // Note that incorrect boot loader configuration could cause this to + // corrupt the code! Setting VTABLE_START_ADDRESS to 0x20000000 (the start + // of SRAM) is safe since this will use the same memory that the boot loader + // already uses for its vector table. Great care will have to be taken if + // other addresses are to be used. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r1, #(APP_START_ADDRESS >> 16) +#endif + + // + // Calculate the end address of the vector table assuming that it has the + // maximum possible number of vectors. We don't know how many the app has + // populated so this is the safest approach though it may copy some non + // vector data if the app table is smaller than the maximum. + // + movw r2, #(70 * 4) + adds r2, r2, r0 +VectorCopyLoop2: + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r0, r2 + blt VectorCopyLoop2 +#endif + + // + // Set the application's vector table start address. Typically this is the + // application start address but in some cases an application may relocate + // this so we can't assume that these two addresses are equal. + // + movw r0, #(VTABLE_START_ADDRESS & 0xffff) +#if (VTABLE_START_ADDRESS > 0xffff) + movt r0, #(VTABLE_START_ADDRESS >> 16) +#endif + movw r1, #(NVIC_VTABLE & 0xffff) + movt r1, #(NVIC_VTABLE >> 16) + str r0, [r1] + + // + // Remove the NMI stack frame from the boot loader's stack. + // + ldmia sp, {r4-r11} + + // + // Get the application's stack pointer. + // +#if (APP_START_ADDRESS != VTABLE_START_ADDRESS) + movw r0, #(APP_START_ADDRESS & 0xffff) +#if (APP_START_ADDRESS > 0xffff) + movt r0, #(APP_START_ADDRESS >> 16) +#endif +#endif + ldr sp, [r0, #0x00] + + // + // Fix up the NMI stack frame's return address to be the reset handler of + // the application. + // + ldr r10, [r0, #0x04] + bic r10, #0x00000001 + + // + // Store the NMI stack frame onto the application's stack. + // + stmdb sp!, {r4-r11} + + // + // Branch to the application's NMI handler. + // + ldr r0, [r0, #0x08] + bx r0 +#else + // + // Loop forever since there is nothing that we can do about a NMI. + // + b . +#endif + +//***************************************************************************** +// +// The hard fault handler. +// +//***************************************************************************** + .thumb_func +FaultISR: + // + // Loop forever since there is nothing that we can do about a hard fault. + // + b . + +//***************************************************************************** +// +// The default interrupt handler. +// +//***************************************************************************** + .thumb_func +IntDefaultHandler: + // + // Loop forever since there is nothing that we can do about an unexpected + // interrupt. + // + b . + +//***************************************************************************** +// +// Provides a small delay. The loop below takes 3 cycles/loop. +// +//***************************************************************************** + .globl Delay + .thumb_func +Delay: + subs r0, #1 + bne Delay + bx lr + +//***************************************************************************** +// +// This is the end of the file. +// +//***************************************************************************** + .end diff --git a/boot_loader/bl_uart.c b/boot_loader/bl_uart.c new file mode 100644 index 0000000..71df5c3 --- /dev/null +++ b/boot_loader/bl_uart.c @@ -0,0 +1,156 @@ +//***************************************************************************** +// +// bl_uart.c - Functions to transfer data via the UART port. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "inc/hw_gpio.h" +#include "inc/hw_memmap.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "inc/hw_uart.h" +#include "bl_config.h" +#include "boot_loader/bl_uart.h" + +//***************************************************************************** +// +//! \addtogroup bl_uart_api +//! @{ +// +//***************************************************************************** +#if defined(UART_ENABLE_UPDATE) || defined(DOXYGEN) + +//***************************************************************************** +// +//! Sends data over the UART port. +//! +//! \param pui8Data is the buffer containing the data to write out to the UART +//! port. +//! \param ui32Size is the number of bytes provided in \e pui8Data buffer that +//! will be written out to the UART port. +//! +//! This function sends \e ui32Size bytes of data from the buffer pointed to by +//! \e pui8Data via the UART port. +//! +//! \return None. +// +//***************************************************************************** +void +UARTSend(const uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Transmit the number of bytes requested on the UART port. + // + while(ui32Size--) + { + // + // Make sure that the transmit FIFO is not full. + // + while((HWREG(UART0_BASE + UART_O_FR) & UART_FR_TXFF)) + { + } + + // + // Send out the next byte. + // + HWREG(UART0_BASE + UART_O_DR) = *pui8Data++; + } + + // + // Wait until the UART is done transmitting. + // + UARTFlush(); +} + +//***************************************************************************** +// +//! Waits until all data has been transmitted by the UART port. +//! +//! This function waits until all data written to the UART port has been +//! transmitted. +//! +//! \return None. +// +//***************************************************************************** +void +UARTFlush(void) +{ + // + // Wait for the UART FIFO to empty and then wait for the shifter to get the + // bytes out the port. + // + while(!(HWREG(UART0_BASE + UART_O_FR) & UART_FR_TXFE)) + { + } + + // + // Wait for the FIFO to not be busy so that the shifter completes. + // + while((HWREG(UART0_BASE + UART_O_FR) & UART_FR_BUSY)) + { + } +} + +//***************************************************************************** +// +//! Receives data over the UART port. +//! +//! \param pui8Data is the buffer to read data into from the UART port. +//! \param ui32Size is the number of bytes provided in the \e pui8Data buffer +//! that should be written with data from the UART port. +//! +//! This function reads back \e ui32Size bytes of data from the UART port, into +//! the buffer that is pointed to by \e pui8Data. This function will not +//! return until \e ui32Size number of bytes have been received. +//! +//! \return None. +// +//***************************************************************************** +void +UARTReceive(uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Send out the number of bytes requested. + // + while(ui32Size--) + { + // + // Wait for the FIFO to not be empty. + // + while((HWREG(UART0_BASE + UART_O_FR) & UART_FR_RXFE)) + { + } + + // + // Receive a byte from the UART. + // + *pui8Data++ = HWREG(UART0_BASE + UART_O_DR); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif diff --git a/boot_loader/bl_uart.h b/boot_loader/bl_uart.h new file mode 100644 index 0000000..8148995 --- /dev/null +++ b/boot_loader/bl_uart.h @@ -0,0 +1,81 @@ +//***************************************************************************** +// +// bl_uart.h - Definitions for the UART transport functions. +// +// Copyright (c) 2006-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_UART_H__ +#define __BL_UART_H__ + +//***************************************************************************** +// +// This macro is used to generate a constant to represent the UART baud rate to +// processor clock rate ratio. This prevents the need for run-time calculation +// of the ratio of baud rate to processor clock rate ratio. +// +//***************************************************************************** +#define UART_BAUD_RATIO(ui32Baud) \ + ((((CRYSTAL_FREQ * 8) / ui32Baud) + 1) / 2) + +//***************************************************************************** +// +// This defines the UART receive pin that is being used by the boot loader. +// +//***************************************************************************** +#define UART_RX (1 << 0) + +//***************************************************************************** +// +// This defines the UART transmit pin that is being used by the boot loader. +// +//***************************************************************************** +#define UART_TX (1 << 1) + +//***************************************************************************** +// +// This defines the combination of pins used to implement the UART port used by +// the boot loader. +// +//***************************************************************************** +#define UART_PINS (UART_RX | UART_TX) + +//***************************************************************************** +// +// UART Transport APIs +// +//***************************************************************************** +extern void UARTSend(const uint8_t *pui8Data, uint32_t ui32Size); +extern void UARTReceive(uint8_t *pui8Data, uint32_t ui32Size); +extern void UARTFlush(void); +extern int UARTAutoBaud(uint32_t *pui32Ratio); + +//***************************************************************************** +// +// Define the transport functions if the UART is being used. +// +//***************************************************************************** +#ifdef UART_ENABLE_UPDATE +#define SendData UARTSend +#define FlushData UARTFlush +#define ReceiveData UARTReceive +#endif + +#endif // __BL_UART_H__ diff --git a/boot_loader/bl_usb.c b/boot_loader/bl_usb.c new file mode 100644 index 0000000..00ccd7a --- /dev/null +++ b/boot_loader/bl_usb.c @@ -0,0 +1,2166 @@ +//***************************************************************************** +// +// bl_usb.c - Functions to transfer data via the USB port. +// +// Copyright (c) 2009-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_gpio.h" +#include "inc/hw_memmap.h" +#include "inc/hw_flash.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "inc/hw_nvic.h" +#include "inc/hw_usb.h" +#include "bl_config.h" +#include "boot_loader/bl_crystal.h" +#include "boot_loader/bl_flash.h" +#include "boot_loader/bl_hooks.h" +#include "boot_loader/bl_usbfuncs.h" +#include "boot_loader/usbdfu.h" + +//***************************************************************************** +// +// DFU Notes: +// +// 1. This implementation is manifestation-tolerant and doesn't time out +// waiting for a reset after a download completes. As a result, the detach +// timeout in the DFU functional descriptor is set to the maximum possible +// value representing a timeout of 65.536 seconds. +// +// 2. This implementation does not support the BUSY state. By skipping this +// and remaining in DNLOAD_SYNC when we are waiting for a programming or +// erase operation to complete, we save the overhead of having to support a +// timeout mechanism. Host-side implementations don't seem to rely upon +// the busy state so this does not appear to be a problem. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup bl_usb_api +//! @{ +// +//***************************************************************************** +#if defined(USB_ENABLE_UPDATE) || defined(DOXYGEN) + +//***************************************************************************** +// +// Make sure that the crystal frequency is defined. +// +//***************************************************************************** +#if !defined(CRYSTAL_FREQ) +#error ERROR: CRYSTAL_FREQ must be defined for USB update! +#endif + +//***************************************************************************** +// +// Make sure that the crystal frequency is one of the ones that support USB +// operation. +// +//***************************************************************************** +#if CRYSTAL_FREQ != 4000000 && \ + CRYSTAL_FREQ != 5000000 && \ + CRYSTAL_FREQ != 6000000 && \ + CRYSTAL_FREQ != 8000000 && \ + CRYSTAL_FREQ != 10000000 && \ + CRYSTAL_FREQ != 12000000 && \ + CRYSTAL_FREQ != 16000000 +#error ERROR: Invalid CRYSTAL_FREQ specified for USB update! +#endif + +//***************************************************************************** +// +// The DFU device information structure was developed assuming flash block +// sizes in the 1KB to 32KB range but large external flash devices may have +// 64KB or larger blocks. If the configuration options indicate a target +// device with large pages, we fake the size at 32KB to keep the client happy. +// The other option would be to redefine this field as an uint32_t but that +// would break existing applications using the interface. +// +// For normal operation, this is unlikely to cause a problem since we will not +// allow a flash operation to start anywhere other than at APP_START_ADDRESS +// (which must fall on a real flash page boundary) or the start of the +// reserved +// +//***************************************************************************** +#if (FLASH_PAGE_SIZE > 0x10000) +#define DFU_REPORTED_PAGE_SIZE 0x8000 +#else +#define DFU_REPORTED_PAGE_SIZE FLASH_PAGE_SIZE +#endif + +//***************************************************************************** +// +// This holds the total size of the firmware image being downloaded (which is +// needed if we have a progress reporting hook function provided). +// +//***************************************************************************** +#ifdef BL_PROGRESS_FN_HOOK +uint32_t g_ui32ImageSize; +#endif + +//***************************************************************************** +// +// The structure used to define a block of memory. +// +//***************************************************************************** +typedef struct +{ + uint8_t *pui8Start; + uint32_t ui32Length; +} +tMemoryBlock; + +//***************************************************************************** +// +// The block of memory that is to be sent back in response to the next upload +// request. +// +//***************************************************************************** +tMemoryBlock g_sNextUpload; + +//***************************************************************************** +// +// The block of memory into which the next programming operation will write. +// +//***************************************************************************** +volatile tMemoryBlock g_sNextDownload; + +//***************************************************************************** +// +// The block of flash to be erased. +// +//***************************************************************************** +volatile tMemoryBlock g_sErase; + +//***************************************************************************** +// +// Information on the device we are running on. This will be returned to the +// host after a download request containing command DFU_CMD_INFO. +// +//***************************************************************************** +tDFUDeviceInfo g_sDFUDeviceInfo; + +//***************************************************************************** +// +// This variable keeps track of the last software-specific command received +// from the host via a download request. +// +//***************************************************************************** +uint8_t g_ui8LastCommand; + +//***************************************************************************** +// +// The current status of the DFU device as reported to the host in response to +// USBD_DFU_REQUEST_GETSTATUS. +// +//***************************************************************************** +tDFUGetStatusResponse g_sDFUStatus = +{ + 0, { 5, 0, 0 }, (uint8_t)STATE_IDLE, 0 +}; + +//***************************************************************************** +// +// The structure sent in response to a valid USBD_DFU_REQUEST_TIVA. +// +//***************************************************************************** +tDFUQueryTIVAProtocol g_sDFUProtocol = +{ + DFU_PROTOCOL_USBLIB_MARKER, + DFU_PROTOCOL_USBLIB_VERSION_1 +}; + +//***************************************************************************** +// +// The current state of the device. +// +//***************************************************************************** +volatile tDFUState g_eDFUState = STATE_IDLE; + +//***************************************************************************** +// +// The current status of the device. +// +//***************************************************************************** +volatile tDFUStatus g_eDFUStatus = STATUS_OK; + +//***************************************************************************** +// +// The buffer used to hold download data from the host prior to writing it to +// flash or image data in the process of being uploaded to the host. +// +//***************************************************************************** +uint8_t g_pui8DFUBuffer[DFU_TRANSFER_SIZE]; + +//***************************************************************************** +// +// The start of the image data within g_pui8DFUBuffer. +// +//***************************************************************************** +uint8_t *g_pui8DFUWrite; + +//***************************************************************************** +// +// The number of bytes of valid data in the DFU buffer. +// +//***************************************************************************** +volatile uint16_t g_ui16DFUBufferUsed; + +//***************************************************************************** +// +// Flags used to indicate that the main thread is being asked to do something. +// +//***************************************************************************** +volatile uint32_t g_ui32CommandFlags; +#define CMD_FLAG_ERASE 0 +#define CMD_FLAG_WRITE 1 +#define CMD_FLAG_RESET 2 + +//***************************************************************************** +// +// This global determines whether or not we add a DFU header to any uploaded +// image data. If true, the binary image is sent without the header. If false +// the header is included. This is a DFU requirement since uploaded images +// must be able to be downloaded again and hence must have the header in place +// so that the destination address is available. +// +//***************************************************************************** +bool g_bUploadBinary = false; + +//***************************************************************************** +// +// If the upload format includes the header, we need to be able to suppress +// this when replying to TIVA-specific commands such as CMD_DFU_INFO. This +// global determines whether we need to suppress the header that would +// otherwise be send in response to the first USBD_DFU_REQUEST_UPLOAD received +// while in STATE_IDLE. +// +//***************************************************************************** +bool g_bSuppressUploadHeader = false; + +//***************************************************************************** +// +// A flag we use to indicate when the device has been enumerated. +// +//***************************************************************************** +bool g_bAddressSet = false; + +//***************************************************************************** +// +// The languages supported by this device. +// +//***************************************************************************** +const uint8_t g_pui8LangDescriptor[] = +{ + 4, + USB_DTYPE_STRING, + USBShort(USB_LANG_EN_US) +}; + +//***************************************************************************** +// +// The jump table used to implement request handling in the DFU state machine. +// +//***************************************************************************** +typedef void (* tHandleRequests)(tUSBRequest *psUSBRequest); + +extern void HandleRequestIdle(tUSBRequest *psUSBRequest); +extern void HandleRequestDnloadSync(tUSBRequest *psUSBRequest); +extern void HandleRequestDnloadIdle(tUSBRequest *psUSBRequest); +extern void HandleRequestManifestSync(tUSBRequest *psUSBRequest); +extern void HandleRequestUploadIdle(tUSBRequest *psUSBRequest); +extern void HandleRequestError(tUSBRequest *psUSBRequest); + +tHandleRequests g_pfnRequestHandlers[] = +{ + 0, // STATE_APP_IDLE + 0, // STATE_APP_DETACH + HandleRequestIdle, // STATE_IDLE + HandleRequestDnloadSync, // STATE_DNLOAD_SYNC + HandleRequestDnloadSync, // STATE_DNBUSY + HandleRequestDnloadIdle, // STATE_DNLOAD_IDLE + HandleRequestManifestSync, // STATE_MANIFEST_SYNC + 0, // STATE_MANIFEST + 0, // STATE_MANIFEST_WAIT_RESET + HandleRequestUploadIdle, // STATE_UPLOAD_IDLE + HandleRequestError // STATE_ERROR +}; + +//***************************************************************************** +// +// The manufacturer string. +// +//***************************************************************************** +const uint8_t g_pui8ManufacturerString[] = +{ + (17 + 1) * 2, + USB_DTYPE_STRING, + 'T', 0, 'e', 0, 'x', 0, 'a', 0, 's', 0, ' ', 0, 'I', 0, 'n', 0, + 's', 0, 't', 0, 'r', 0, 'u', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, + 's', 0 +}; + +//***************************************************************************** +// +// The product string. +// +//***************************************************************************** +const uint8_t g_pui8ProductString[] = +{ + (23 + 1) * 2, + USB_DTYPE_STRING, + 'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, ' ', 0, 'F', 0, 'i', 0, + 'r', 0, 'm', 0, 'w', 0, 'a', 0, 'r', 0, 'e', 0, ' ', 0, 'U', 0, 'p', 0, + 'g', 0, 'r', 0, 'a', 0, 'd', 0, 'e', 0 +}; + +//***************************************************************************** +// +// The serial number string. +// +//***************************************************************************** +const uint8_t g_pui8SerialNumberString[] = +{ + (3 + 1) * 2, + USB_DTYPE_STRING, + '0', 0, '.', 0, '1', 0 +}; + +//***************************************************************************** +// +// The descriptor string table. +// +//***************************************************************************** +const uint8_t *const g_ppui8StringDescriptors[] = +{ + g_pui8LangDescriptor, + g_pui8ManufacturerString, + g_pui8ProductString, + g_pui8SerialNumberString +}; + +//***************************************************************************** +// +// DFU Device Descriptor. +// +//***************************************************************************** +const uint8_t g_pui8DFUDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + USB_CLASS_VEND_SPECIFIC, // USB Device Class + 0, // USB Device Sub-class + 0, // USB Device protocol + 64, // Maximum packet size for default pipe. + USBShort(USB_VENDOR_ID), // Vendor ID (VID). + USBShort(USB_PRODUCT_ID), // Product ID (PID). + USBShort(USB_DEVICE_ID), // Device Release Number BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// DFU device configuration descriptor. +// +//***************************************************************************** +const uint8_t g_pui8DFUConfigDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(27), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 0, // The string identifier that describes this + // configuration. +#if USB_BUS_POWERED + USB_CONF_ATTR_BUS_PWR, // Bus Powered +#else + USB_CONF_ATTR_SELF_PWR, // Self Powered +#endif + (USB_MAX_POWER / 2), // The maximum power in 2mA increments. + + // + // Interface descriptor. + // + 9, // Length of this descriptor. + USB_DTYPE_INTERFACE, // This is an interface descriptor. + 0, // Interface number . + 0, // Alternate setting number. + 0, // Number of endpoints (only endpoint 0 used) + USB_CLASS_APP_SPECIFIC, // Application specific interface class + USB_DFU_SUBCLASS, // Device Firmware Upgrade subclass + USB_DFU_PROTOCOL, // DFU protocol + 0, // No interface description string present. + + // + // Device Firmware Upgrade functional descriptor. + // + 9, // Length of this descriptor. + 0x21, // DFU Functional descriptor type + (DFU_ATTR_CAN_DOWNLOAD | // DFU attributes. + DFU_ATTR_CAN_UPLOAD | + DFU_ATTR_MANIFEST_TOLERANT), + USBShort(0xFFFF), // Detach timeout (set to maximum). + USBShort(DFU_TRANSFER_SIZE),// Transfer size 1KB. + USBShort(0x0110) // DFU Version 1.1 +}; + +//***************************************************************************** +// +// The USB device interrupt handler. +// +// This function is called to process USB interrupts when in device mode. +// This handler will branch the interrupt off to the appropriate application or +// stack handlers depending on the current status of the USB controller. +// +// \return None. +// +//***************************************************************************** +void +USB0DeviceIntHandler(void) +{ + uint32_t ui32TxStatus, ui32GenStatus; + + // + // Get the current full USB interrupt status. + // + ui32TxStatus = HWREGH(USB0_BASE + USB_O_TXIS); + ui32GenStatus = HWREGB(USB0_BASE + USB_O_IS); + + // + // Received a reset from the host. + // + if(ui32GenStatus & USB_IS_RESET) + { + USBDeviceEnumResetHandler(); + } + + // + // USB device was disconnected. + // + if(ui32GenStatus & USB_IS_DISCON) + { + HandleDisconnect(); + } + + // + // Handle end point 0 interrupts. + // + if(ui32TxStatus & USB_TXIE_EP0) + { + USBDeviceEnumHandler(); + } +} + +//***************************************************************************** +// +// A prototype for the function (in the startup code) for a predictable length +// delay. +// +//***************************************************************************** +extern void Delay(uint32_t ui32Count); + +//***************************************************************************** +// +// Send the current state or status structure back to the host. This function +// also acknowledges the request which causes us to send back this data. +// +//***************************************************************************** +void +SendDFUStatus(void) +{ + // + // Acknowledge the original request. + // + USBDevEndpoint0DataAck(false); + + // + // Copy the current state into the status structure we will return. + // + g_sDFUStatus.bState = (uint8_t)g_eDFUState; + g_sDFUStatus.bStatus = (uint8_t)g_eDFUStatus; + + // + // Send the status structure back to the host. + // + USBBLSendDataEP0((uint8_t *)&g_sDFUStatus, sizeof(tDFUGetStatusResponse)); +} + +//***************************************************************************** +// +// Send the next block of upload data back to the host assuming data remains +// to be sent. +// +// \param ui16Length is the requested amount of data. +// \param bAppendHeader is \b true to append a tDFUDownloadProgHeader at the +// start of the uploaded data or \b false if no header is required. +// +// Returns \b true if a full packet containing DFU_TRANSFER_SIZE bytes +// was sent and data remains to be sent following this transaction, or \b +// false if no more data remains to be sent following this transaction. +// +//***************************************************************************** +bool +SendUploadData(uint16_t ui16Length, bool bAppendHeader) +{ + uint16_t ui16ToSend; + uint32_t ui32Available; + + // + // Acknowledge the original request. + // + USBDevEndpoint0DataAck(false); + + // + // How much data is available to be sent? + // + ui32Available = (g_sNextUpload.ui32Length + + (bAppendHeader ? sizeof(tDFUDownloadProgHeader) : 0)); + + // + // How much data can we send? This is the smallest of the maximum transfer + // size, the requested length or the available data. + // + ui16ToSend = + (ui16Length > DFU_TRANSFER_SIZE) ? DFU_TRANSFER_SIZE : ui16Length; + ui16ToSend = + ((uint32_t)ui16ToSend > ui32Available) ? ui32Available : ui16ToSend; + + // + // If we have been asked to send a header, we need to copy some of the data + // into a buffer and send from there. If we don't do this, we run the risk + // of sending a long packet prematurely and ending the upload before it is + // complete. + // + if(bAppendHeader) + { + tDFUDownloadProgHeader *psHdr; + uint8_t *pui8From; + uint8_t *pui8To; + uint32_t ui32Loop; + + // + // We are appending a header so write the header information into a + // buffer then copy the first chunk of data from its original position + // into the same buffer. + // + psHdr = (tDFUDownloadProgHeader *)g_pui8DFUBuffer; + + // + // Build the header. + // + psHdr->ui8Command = DFU_CMD_PROG; + psHdr->ui8Reserved = 0; + psHdr->ui16StartAddr = ((uint32_t)(g_sNextUpload.pui8Start) / 1024); + psHdr->ui32Length = g_sNextUpload.ui32Length; + + // + // Copy the remainder of the first transfer's data from its original + // position. + // + pui8From = g_sNextUpload.pui8Start; + pui8To = (uint8_t *)(psHdr + 1); + for(ui32Loop = (ui16ToSend - sizeof(tDFUDownloadProgHeader)); ui32Loop; + ui32Loop--) + { + *pui8To++ = *pui8From++; + } + + // + // Send the data. + // + USBBLSendDataEP0((uint8_t *)psHdr, ui16ToSend); + + // + // Update our upload pointer and length. + // + g_sNextUpload.pui8Start += ui16ToSend - sizeof(tDFUDownloadProgHeader); + g_sNextUpload.ui32Length -= + ui16ToSend - sizeof(tDFUDownloadProgHeader); + } + else + { + // + // We are not sending a header so send the requested upload data back + // to the host directly from its original position. + // + USBBLSendDataEP0(g_sNextUpload.pui8Start, ui16ToSend); + + // + // Update our upload pointer and length. + // + g_sNextUpload.pui8Start += ui16ToSend; + g_sNextUpload.ui32Length -= ui16ToSend; + } + + // + // We return true if we sent a full packet (containing the maximum transfer + // size bytes) or false to indicate that a long packet was sent or no more + // data remains. + // + return(((ui16ToSend == DFU_TRANSFER_SIZE) && g_sNextUpload.ui32Length) ? + true : false); +} + +//***************************************************************************** +// +// Send the current state back to the host. +// +//***************************************************************************** +void +SendDFUState(void) +{ + // + // Acknowledge the original request. + // + USBDevEndpoint0DataAck(false); + + // + // Update the status structure with the current state. + // + g_sDFUStatus.bState = (uint8_t)g_eDFUState; + + // + // Send the state from the status structure back to the host. + // + USBBLSendDataEP0((uint8_t *)&g_sDFUStatus.bState, 1); +} + +//***************************************************************************** +// +//! Handle USB requests sent to the DFU device. +//! +//! \param psUSBRequest is a pointer to the USB request that the device has +//! been sent. +//! +//! This function is called to handle all non-standard requests received +//! by the device. This will include all the DFU endpoint 0 commands along +//! with the TIVA-specific request we use to query whether the device +//! supports our flavor of the DFU binary format. Incoming DFU requests are +//! processed by request handlers specific to the particular state of the DFU +//! connection. This state machine implementation is chosen to keep the +//! software as close as possible to the USB DFU class documentation. +//! +//! \return None. +// +//***************************************************************************** +void +HandleRequests(tUSBRequest *psUSBRequest) +{ + // + // This request is used by the host to determine whether the connected + // device supports the TIVA protocol extensions to DFU (our + // DFU_CMD_xxxx command headers passed alongside DNLOAD requests). We + // check the parameters and, if they are as expected, we respond with + // a 4 byte structure providing a marker and the protocol version + // number. + // + if(psUSBRequest->bRequest == USBD_DFU_REQUEST_TIVA) + { + // + // Check that the request parameters are all as expected. We are + // using the wValue value merely as a way of making it less likely + // that we respond to another vendor's device-specific request. + // + if((psUSBRequest->wLength == sizeof(tDFUQueryTIVAProtocol)) && + (psUSBRequest->wValue == REQUEST_TIVA_VALUE)) + { + // + // Acknowledge the original request. + // + USBDevEndpoint0DataAck(false); + + // + // Send the status structure back to the host. + // + USBBLSendDataEP0((uint8_t *)&g_sDFUProtocol, + sizeof(tDFUQueryTIVAProtocol)); + } + else + { + // + // The request parameters were not as expected so we assume + // that this is not our request and stall the endpoint to + // indicate an error. + // + USBBLStallEP0(); + } + + return; + } + + // + // Pass the request to the relevant handler depending upon our current + // state. If no handler is configured, we stall the endpoint since this + // implies that requests can't be handled in this state. + // + if(g_pfnRequestHandlers[g_eDFUState]) + { + // + // Dispatch the request to the relevant handler depending upon the + // current state. + // + (g_pfnRequestHandlers[g_eDFUState])(psUSBRequest); + } + else + { + USBBLStallEP0(); + } +} + +//***************************************************************************** +// +// Handle all incoming DFU requests while in state STATE_IDLE. +// +//***************************************************************************** +void +HandleRequestIdle(tUSBRequest *psUSBRequest) +{ + switch(psUSBRequest->bRequest) + { + // + // This is a download request. We need to request the transaction + // payload unless this is a zero length request in which case we mark + // the error by stalling the endpoint. + // + case USBD_DFU_REQUEST_DNLOAD: + { + if(psUSBRequest->wLength) + { + USBBLRequestDataEP0(g_pui8DFUBuffer, psUSBRequest->wLength); + } + else + { + USBBLStallEP0(); + return; + } + break; + } + + // + // This is an upload request. We send back a block of data + // corresponding to the current upload pointer as held in + // g_sNextUpload. + // + case USBD_DFU_REQUEST_UPLOAD: + { + // + // If we have any upload data to send, send it. Make sure we append + // a header if required. + // + if(SendUploadData(psUSBRequest->wLength, + g_bSuppressUploadHeader ? false : + !g_bUploadBinary)) + { + // + // We sent a full (max packet size) frame to the host so + // transition to UPLOAD_IDLE state since we expect another + // upload request to continue the process. + // + g_eDFUState = STATE_UPLOAD_IDLE; + } + + // + // Clear the flag we use to suppress sending the DFU header. + // + g_bSuppressUploadHeader = false; + + return; + } + + // + // Return the current device status structure. + // + case USBD_DFU_REQUEST_GETSTATUS: + { + SendDFUStatus(); + return; + } + + // + // Return the current device state. + // + case USBD_DFU_REQUEST_GETSTATE: + { + SendDFUState(); + return; + } + + // + // Ignore the ABORT request. This returns us to IDLE state but we're + // there already. + // + case USBD_DFU_REQUEST_ABORT: + { + break; + } + + // + // All other requests are illegal in this state so signal the error + // by stalling the endpoint. + // + case USBD_DFU_REQUEST_CLRSTATUS: + case USBD_DFU_REQUEST_DETACH: + default: + { + USBBLStallEP0(); + return; + } + } + + // + // If we drop out of the switch, we need to ACK the received request. + // + USBDevEndpoint0DataAck(false); +} + +//***************************************************************************** +// +// Handle all incoming DFU requests while in state STATE_DNLOAD_SYNC or +// STATE_DNBUSY. +// +//***************************************************************************** +void +HandleRequestDnloadSync(tUSBRequest *psUSBRequest) +{ + // + // In this state, we have received a block of the download and are waiting + // for a USBD_DFU_REQUEST_GETSTATUS which will trigger a return + // to STATE_DNLOAD_IDLE assuming we have finished programming the block. + // If the last command we received was not DFU_CMD_PROG, we transition + // directly from this state back to STATE_IDLE once the last operation has + // completed since we need to be able to accept a new command. + // + switch(psUSBRequest->bRequest) + { + // + // The host is requesting the current device status. Return this and + // revert to STATE_IDLE. + // + case USBD_DFU_REQUEST_GETSTATUS: + { + // + // Are we finished processing whatever the last flash-operation + // was? Note that we don't support DNLOAD_BUSY state in this + // implementation, we merely continue to report DNLOAD_SYNC state + // until we are finished with the command. + // + if(!g_ui32CommandFlags) + { + // + // If we are in the middle of a programming operation, + // transition back to DNLOAD_IDLE state to wait for the + // next block. If not, go back to idle since we expect a + // new command. + // + g_eDFUState = ((g_ui8LastCommand == DFU_CMD_PROG) ? + STATE_DNLOAD_IDLE : STATE_IDLE); + } + + // + // Send the latest status back to the host. + // + SendDFUStatus(); + + // + // Return here since we've already ACKed the request. + // + return; + } + + // + // The host is requesting the current device state. + // + case USBD_DFU_REQUEST_GETSTATE: + { + // + // Are we currently in DNLOAD_SYNC state? + // + if(g_eDFUState == STATE_DNLOAD_SYNC) + { + // + // Yes - send back the state. + // + SendDFUState(); + } + else + { + // + // In STATE_BUSY, we can't respond to any requests so stall + // the endpoint. + // + USBBLStallEP0(); + } + + // + // Return here since the incoming request has already been either + // ACKed or stalled by the processing above. + // + return; + } + + // + // Any other request is ignored and causes us to stall the control + // endpoint and remain in STATE_ERROR. + // + default: + { + USBBLStallEP0(); + return; + } + } +} + +//***************************************************************************** +// +// Handle all incoming DFU requests while in state STATE_DNLOAD_IDLE. +// +//***************************************************************************** +void +HandleRequestDnloadIdle(tUSBRequest *psUSBRequest) +{ + switch(psUSBRequest->bRequest) + { + // + // This is a download request. We need to request the transaction + // payload unless this is a zero length request in which case we mark + // the error by stalling the endpoint. + // + case USBD_DFU_REQUEST_DNLOAD: + { + // + // Are we being passed data to program? + // + if(psUSBRequest->wLength) + { + // + // Yes - request the data. + // + USBBLRequestDataEP0(g_pui8DFUBuffer, psUSBRequest->wLength); + } + else + { + // + // No - this is the signal that a download operation is + // complete. Do we agree? + // + if(g_sNextDownload.ui32Length) + { + // + // We think there should still be some data to be received + // so mark this as an error. + // + g_eDFUState = STATE_ERROR; + g_eDFUStatus = STATUS_ERR_NOTDONE; + } + else + { + // + // We agree that the download has completed. Enter state + // STATE_MANIFEST_SYNC. + // + g_eDFUState = STATE_MANIFEST_SYNC; + } + } + break; + } + + // + // Return the current device status structure. + // + case USBD_DFU_REQUEST_GETSTATUS: + { + SendDFUStatus(); + return; + } + + // + // Return the current device state. + // + case USBD_DFU_REQUEST_GETSTATE: + { + SendDFUState(); + return; + } + + // + // An ABORT request causes us to abort the current transfer and + // return the the idle state regardless of the state of the previous + // programming operation. + // + case USBD_DFU_REQUEST_ABORT: + { + // + // Default to downloading the main code image. + // + g_sNextDownload.pui8Start = + (uint8_t *)g_sDFUDeviceInfo.ui32AppStartAddr; + g_sNextDownload.ui32Length = (g_sDFUDeviceInfo.ui32FlashTop - + g_sDFUDeviceInfo.ui32AppStartAddr); + g_eDFUState = STATE_IDLE; + break; + } + + // + // All other requests are illegal in this state so signal the error + // by stalling the endpoint. + // + case USBD_DFU_REQUEST_CLRSTATUS: + case USBD_DFU_REQUEST_DETACH: + case USBD_DFU_REQUEST_UPLOAD: + default: + { + USBBLStallEP0(); + return; + } + } + + // + // If we drop out of the switch, we need to ACK the received request. + // + USBDevEndpoint0DataAck(false); +} + +//***************************************************************************** +// +// Handle all incoming DFU requests while in state STATE_MANIFEST_SYNC. +// +//***************************************************************************** +void +HandleRequestManifestSync(tUSBRequest *psUSBRequest) +{ + // + // In this state, we have received the last block of a download and are + // waiting for a USBD_DFU_REQUEST_GETSTATUS which will trigger a return + // to STATE_IDLE. + // + switch(psUSBRequest->bRequest) + { + // + // The host is requesting the current device status. Return this and + // revert to STATE_IDLE. + // + case USBD_DFU_REQUEST_GETSTATUS: + { + g_eDFUState = STATE_IDLE; + SendDFUStatus(); + break; + } + + // + // The host is requesting the current device state. + // + case USBD_DFU_REQUEST_GETSTATE: + { + SendDFUState(); + break; + } + + // + // Any other request is ignored and causes us to stall the control + // endpoint and remain in STATE_MANIFEST_SYNC. + // + default: + { + USBBLStallEP0(); + break; + } + } +} + +//***************************************************************************** +// +// Handle all incoming DFU requests while in state STATE_UPLOAD_IDLE. +// +//***************************************************************************** +void +HandleRequestUploadIdle(tUSBRequest *psUSBRequest) +{ + // + // In this state, we have already received the first upload request. What + // are we being asked to do now? + // + switch(psUSBRequest->bRequest) + { + // + // The host is requesting more upload data. + // + case USBD_DFU_REQUEST_UPLOAD: + { + // + // See if there is any more data to transfer and, if there is, + // send it back to the host. + // + if(!SendUploadData(psUSBRequest->wLength, false)) + { + // + // We sent less than a full packet of data so the transfer is + // complete. Revert to idle state and ensure that we reset + // our upload pointer and size to the default flash region. + // + g_eDFUState = STATE_IDLE; + g_sNextUpload.pui8Start = + (uint8_t *)g_sDFUDeviceInfo.ui32AppStartAddr; + g_sNextUpload.ui32Length = (g_sDFUDeviceInfo.ui32FlashTop - + g_sDFUDeviceInfo.ui32AppStartAddr); + } + break; + } + + // + // The host is requesting the current device status. + // + case USBD_DFU_REQUEST_GETSTATUS: + { + SendDFUStatus(); + break; + } + + // + // The host is requesting the current device state. + // + case USBD_DFU_REQUEST_GETSTATE: + { + SendDFUState(); + break; + } + + // + // The host is requesting that we abort the current upload. + // + case USBD_DFU_REQUEST_ABORT: + { + // + // Default to sending the main application image for the next + // upload. + // + g_sNextUpload.pui8Start = + (uint8_t *)g_sDFUDeviceInfo.ui32AppStartAddr; + g_sNextUpload.ui32Length = (g_sDFUDeviceInfo.ui32FlashTop - + g_sDFUDeviceInfo.ui32AppStartAddr); + g_eDFUState = STATE_IDLE; + break; + } + + // + // Any other request is ignored and causes us to stall the control + // endpoint and remain in STATE_ERROR. + // + default: + { + USBBLStallEP0(); + break; + } + } +} + +//***************************************************************************** +// +// Handle all incoming DFU requests while in state STATE_ERROR. +// +//***************************************************************************** +void +HandleRequestError(tUSBRequest *psUSBRequest) +{ + // + // In this state, we respond to state and status requests and also to + // USBD_DFU_REQUEST_CLRSTATUS which clears the previous error condition. + // + switch(psUSBRequest->bRequest) + { + // + // The host is requesting the current device status. + // + case USBD_DFU_REQUEST_GETSTATUS: + { + SendDFUStatus(); + break; + } + + // + // The host is requesting the current device state. + // + case USBD_DFU_REQUEST_GETSTATE: + { + SendDFUState(); + break; + } + + // + // The host is asking us to clear our previous error condition and + // revert to idle state in preparation to receive new commands. + // + case USBD_DFU_REQUEST_CLRSTATUS: + { + g_eDFUState = STATE_IDLE; + g_eDFUStatus = STATUS_OK; + USBDevEndpoint0DataAck(false); + break; + } + + // + // Any other request is ignored and causes us to stall the control + // endpoint and remain in STATE_ERROR. + // + default: + { + USBBLStallEP0(); + break; + } + } +} + +//***************************************************************************** +// +// Handle cases where the host sets a new USB configuration. +// +//***************************************************************************** +void +HandleConfigChange(uint32_t ui32Info) +{ + // + // Revert to idle state. + // + g_eDFUState = STATE_IDLE; + g_eDFUStatus = STATUS_OK; +} + +//***************************************************************************** +// +// Setting the device address indicates that we are now connected to the host +// and can expect some DFU communication so we use this opportunity to clean +// out our state just in case we were not idle last time the host disconnected. +// +//***************************************************************************** +void +HandleSetAddress(void) +{ + g_eDFUState = STATE_IDLE; + g_eDFUStatus = STATUS_OK; + g_bAddressSet = true; + + // + // Default the download address to the app start address and valid length + // to the whole of the programmable flash area. + // + g_sNextDownload.pui8Start = + (uint8_t *)g_sDFUDeviceInfo.ui32AppStartAddr; + g_sNextDownload.ui32Length = (g_sDFUDeviceInfo.ui32FlashTop - + g_sDFUDeviceInfo.ui32AppStartAddr); + + // + // Default the upload address to the app start address and valid length + // to the whole of the programmable flash area. + // + g_sNextUpload.pui8Start = + (uint8_t *)g_sDFUDeviceInfo.ui32AppStartAddr; + g_sNextUpload.ui32Length = (g_sDFUDeviceInfo.ui32FlashTop - + g_sDFUDeviceInfo.ui32AppStartAddr); +} + +//***************************************************************************** +// +// Check that a range of addresses passed is within the region of flash that +// the boot loader is allowed to access. +// +// Returns true if the address range is accessible or false otherwise. +// +//***************************************************************************** +bool +FlashRangeCheck(uint32_t ui32Start, uint32_t ui32Length) +{ +#ifdef ENABLE_BL_UPDATE + if((ui32Length <= + (g_sDFUDeviceInfo.ui32FlashTop - g_sDFUDeviceInfo.ui32AppStartAddr)) && + ((ui32Start + ui32Length) <= g_sDFUDeviceInfo.ui32FlashTop)) +#else + if((ui32Start >= g_sDFUDeviceInfo.ui32AppStartAddr) && + (ui32Length <= + (g_sDFUDeviceInfo.ui32FlashTop - g_sDFUDeviceInfo.ui32AppStartAddr)) && + ((ui32Start + ui32Length) <= g_sDFUDeviceInfo.ui32FlashTop)) +#endif + { + // + // The block passed lies wholly within the flash address range of + // this device. + // + return(true); + } + else + { + // + // We were passed an address that is out of range so set the + // appropriate status code. + // + g_eDFUStatus = STATUS_ERR_ADDRESS; + return(false); + } +} + +//***************************************************************************** +// +//! Process TIVA-specific commands passed via DFU download requests. +//! +//! \param psCmd is a pointer to the first byte of the \b DFU_DNLOAD payload +//! that is expected to hold a command. +//! \param ui32Size is the number of bytes of data pointed to by \e psCmd. +//! This function is called when a DFU download command is received while in +//! \b STATE_IDLE. New downloads are assumed to contain a prefix structure +//! containing one of several TIVA-specific commands and this function +//! is responsible for parsing the download data and processing whichever +//! command is contained within it. +//! +//! \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +bool +ProcessDFUDnloadCommand(tDFUDownloadHeader *psCmd, uint32_t ui32Size) +{ + // + // Make sure we got enough data to contain a valid command header. + // + if(ui32Size < sizeof(tDFUDownloadHeader)) + { + return(false); + } + + // + // Remember the command that we have been passed since we will need thi + // to determine which state to transition to on exit from STATE_DNLOAD_SYNC. + // + g_ui8LastCommand = psCmd->ui8Command; + + // + // Which command have we been passed? + // + switch(psCmd->ui8Command) + { + // + // We are being asked to start a programming operation. + // + case DFU_CMD_PROG: + { + tDFUDownloadProgHeader *psHdr; + + // + // Extract the address and size from the command header. + // + psHdr = (tDFUDownloadProgHeader *)psCmd; + + // + // Is the passed address range valid? + // + if(BL_FLASH_AD_CHECK_FN_HOOK(psHdr->ui16StartAddr * 1024, + psHdr->ui32Length)) + { + // + // Yes - remember the range passed so that we will write the + // passed data to the correct place. + // + g_sNextDownload.pui8Start = + (uint8_t *)(psHdr->ui16StartAddr * 1024); + g_sNextDownload.ui32Length = psHdr->ui32Length; + + // + // If we have been provided with a progress reporting hook + // function, remember the total length of the image so that + // we can report this later. + // +#ifdef BL_PROGRESS_FN_HOOK + g_ui32ImageSize = psHdr->ui32Length; +#endif + + // + // Also set the upload address and size to match this download + // so that, by default, the host will get back what it just + // wrote if it performs an upload without an intermediate + // DFU_CMD_READ to set the address and size. + // + g_sNextUpload.pui8Start = + (uint8_t *)(psHdr->ui16StartAddr * 1024); + g_sNextUpload.ui32Length = psHdr->ui32Length; + + // + // Also remember that we have data in this packet to write. + // + g_pui8DFUWrite = (uint8_t *)(psHdr + 1); + g_ui16DFUBufferUsed = ui32Size - sizeof(tDFUDownloadHeader); + + // + // If a start signal hook function has been provided, call it + // here since we are about to start a new download. + // +#ifdef BL_START_FN_HOOK + BL_START_FN_HOOK(); +#endif + + // + // If FLASH_CODE_PROTECTION is defined in bl_config.h we + // erase the whole application area at this point before we + // start to flash the new image. + // +#ifdef FLASH_CODE_PROTECTION + g_sErase.pui8Start = + (uint8_t *)g_sDFUDeviceInfo.ui32AppStartAddr; + + g_sErase.ui32Length = (g_sDFUDeviceInfo.ui32FlashTop - + g_sDFUDeviceInfo.ui32AppStartAddr); + HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_ERASE) = 1; +#endif + + // + // Tell the main thread to write the data we just received it. + // + HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_WRITE) = 1; + } + else + { + // + // The flash range was invalid so switch to error state. + // + return(false); + } + break; + } + + // + // We are being passed the position and size of a block of flash to + // return in a following upload operation. + // + case DFU_CMD_READ: + { + tDFUDownloadReadCheckHeader *psHdr; + + // + // Extract the address and size from the command header. + // + psHdr = (tDFUDownloadReadCheckHeader *)psCmd; + + // + // Is the passed address range valid? + // + if(FlashRangeCheck(psHdr->ui16StartAddr * 1024, psHdr->ui32Length)) + { + // + // Yes - remember the range passed so that we will return + // this block of flash on the next upload request. + // + g_sNextUpload.pui8Start = + (uint8_t *)(psHdr->ui16StartAddr * 1024); + g_sNextUpload.ui32Length = psHdr->ui32Length; + } + else + { + // + // The flash range was invalid so switch to error state. + // + return(false); + } + break; + } + + // + // We are being passed the position and size of a block of flash which + // we will check to ensure that it is erased. + // + case DFU_CMD_CHECK: + { + tDFUDownloadReadCheckHeader *psHdr; + uint32_t *pui32Check; + uint32_t ui32Loop; + + // + // Extract the address and size from the command header. + // + psHdr = (tDFUDownloadReadCheckHeader *)psCmd; + + // + // Make sure the range we have been passed is within the area of + // flash that we are allowed to look at. + // + if(FlashRangeCheck(psHdr->ui16StartAddr * 1024, psHdr->ui32Length)) + { + // + // The range is valid so perform the check here. + // + pui32Check = (uint32_t *)(psHdr->ui16StartAddr * 1024); + + // + // Check each word in the range to ensure that it is erased. If + // not, set the error status and return. + // + for(ui32Loop = 0; ui32Loop < (psHdr->ui32Length / 4); + ui32Loop++) + { + if(*pui32Check != 0xFFFFFFFF) + { + g_eDFUStatus = STATUS_ERR_CHECK_ERASED; + return(false); + } + pui32Check++; + } + + // + // If we get here, the check passed so set the status to + // indicate this. + // + g_eDFUStatus = STATUS_OK; + } + else + { + // + // The flash range was invalid so switch to error state. + // + return(false); + } + break; + } + + // + // We are being asked to erase a block of flash. + // + case DFU_CMD_ERASE: + { + tDFUDownloadEraseHeader *psHdr; + + // + // Extract the address and size from the command header. + // + psHdr = (tDFUDownloadEraseHeader *)psCmd; + + // + // Make sure the range we have been passed is within the area of + // flash that we are allowed to look at. + // + if(FlashRangeCheck((uint32_t)psHdr->ui16StartAddr * 1024, + ((uint32_t)psHdr->ui16NumBlocks * + DFU_REPORTED_PAGE_SIZE ))) + { + // + // The range is valid so tell the main loop to erase the + // block. + // + g_sErase.pui8Start = (uint8_t *) + ((uint32_t)psHdr->ui16StartAddr * 1024); + g_sErase.ui32Length = ((uint32_t)psHdr->ui16NumBlocks * + DFU_REPORTED_PAGE_SIZE); + HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_ERASE) = 1; + } + else + { + // + // The flash range was invalid so switch to error state. + // + return(false); + } + break; + } + + // + // We are being asked to send back device information on the next + // upload request. + // + case DFU_CMD_INFO: + { + // + // Register that we need to send the device info structure on the + // next upload request. + // + g_sNextUpload.pui8Start = (uint8_t *)&g_sDFUDeviceInfo; + g_sNextUpload.ui32Length = sizeof(tDFUDeviceInfo); + + // + // Make sure we don't append the DFU_CMD_PROG header when we send + // back the data. + // + g_bSuppressUploadHeader = true; + break; + } + + // + // We are being asked to set the format of uploaded images. + // + case DFU_CMD_BIN: + { + tDFUDownloadBinHeader *psHdr; + + // + // Extract the required format the command header. + // + psHdr = (tDFUDownloadBinHeader *)psCmd; + + // + // Set the global format appropriately. + // + g_bUploadBinary = psHdr->bBinary ? true : false; + break; + } + + // + // We are being asked to prepare to reset the board and, as a result, + // run the main application image. + // + case DFU_CMD_RESET: + { + // + // Tell the main thread that it's time to go bye-bye... + // + HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_RESET) = 1; + + break; + } + + // + // We have been passed an unrecognized command identifier so report an + // error. + // + default: + { + g_eDFUStatus = STATUS_ERR_VENDOR; + return(false); + } + } + + return(true); +} + +//***************************************************************************** +// +// This callback function is called when data is received for the DATA phase +// of an EP0 OUT transaction. This data will either be a block of download +// data (if we are in STATE_DNLOAD_IDLE) or a new command (if we are in +// STATE_IDLE). +// +//***************************************************************************** +void +HandleEP0Data(uint32_t ui32Size) +{ + bool bRetcode; + + if(g_eDFUState == STATE_IDLE) + { + // + // This must be a new DFU download command header so parse it and + // determine what to do next. + // + bRetcode = + ProcessDFUDnloadCommand((tDFUDownloadHeader *)g_pui8DFUBuffer, + ui32Size); + + // + // Did we receive a recognized and valid command? + // + if(!bRetcode) + { + // + // No - set the error state. The status is set within the + // ProcessDFUDnloadCommand() function. + // + g_eDFUState = STATE_ERROR; + return; + } + } + else + { + // + // If we are not in STATE_IDLE, this must be a block of data for an + // ongoing download so signal the main thread to write it to flash. + // + g_ui16DFUBufferUsed = (uint16_t)ui32Size; + g_pui8DFUWrite = g_pui8DFUBuffer; + + // + // Tell the main thread to write the new data. + // + HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_WRITE) = 1; + } + + // + // Move to STATE_DNLOAD_SYNC since we now expect USBD_DFU_REQUEST_GETSTATUS + // before the next USBD_DFU_REQUEST_DNLOAD. + // + g_eDFUState = STATE_DNLOAD_SYNC; +} + +//***************************************************************************** +// +// Handle bus resets +// +// This function is called if the USB controller detects a reset condition on +// the bus. If we are not in the process of downloading a new image, we use +// this as a signal to reboot and run the main application image. +// +//***************************************************************************** +void +HandleReset(void) +{ + // + // Are we currently in the middle of a download operation? + // + if((g_eDFUState != STATE_DNLOAD_IDLE) && + (g_eDFUState != STATE_DNLOAD_SYNC) && (g_eDFUState != STATE_IDLE)) + { + // + // No - tell the main thread that it should reboot the system assuming + // that we are already configured. If we don't check that we are + // already configured, this will cause a reset during initial + // enumeration and that wouldn't be very helpful. + // + if(g_bAddressSet) + { + HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_RESET) = 1; + } + } +} + +//***************************************************************************** +// +// Handle cases where the USB host disconnects. +// +//***************************************************************************** +void +HandleDisconnect(void) +{ + // + // For error resilience, it may be desireable to note if the host + // disconnects and, if partway through a main image download, clear the + // first block of the flash to ensure that the image is not considered + // valid on the next boot. For now, however, we merely wait for the host + // to connect again, remaining in DFU mode. + // + + // + // Remember that we are waiting for enumeration. + // + g_bAddressSet = false; +} + +//***************************************************************************** +// +// Erase a single block of flash +// +// This function erases a single, 1KB block of flash, returning once the +// operation has completed. +// +// \return None. +// +//***************************************************************************** +static void +EraseFlashBlock(uint32_t ui32Addr) +{ + BL_FLASH_ERASE_FN_HOOK(ui32Addr); +} + +//***************************************************************************** +// +//! This is the main routine for handling updating over USB. +//! +//! This function forms the main loop of the USB DFU updater. It polls for +//! commands sent from the USB request handlers and is responsible for +//! erasing flash blocks, programming data into erased blocks and resetting +//! the device. +//! +//! \return None. +// +//***************************************************************************** +void +UpdaterUSB(void) +{ + uint32_t ui32Idx, ui32Start, ui32Temp; + uint16_t ui16Used; +#ifndef FLASH_CODE_PROTECTION + uint32_t ui32End; +#endif + + // + // Loop forever waiting for the USB interrupt handlers to tell us to do + // something. + // + while(1) + { + while(g_ui32CommandFlags == 0) + { + // + // Wait for something to do. + // + } + + // + // Are we being asked to perform a system reset? + // + if(HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_RESET)) + { + // + // Time to go bye-bye... This will cause the microcontroller + // to reset; no further code will be executed. + // + HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ; + + // + // The microcontroller should have reset, so this should never be + // reached. Just in case, loop forever. + // + while(1) + { + } + } + + // + // Are we being asked to erase a range of blocks in flash? + // + if(HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_ERASE)) + { + // + // Loop through the pages in the block of flash we have been asked + // to erase and clear each one. + // + ui32Temp = g_sErase.ui32Length; + for(ui32Idx = (uint32_t)g_sErase.pui8Start; + ui32Idx < (uint32_t)(g_sErase.pui8Start + ui32Temp); + ui32Idx += FLASH_PAGE_SIZE) + { + EraseFlashBlock(ui32Idx); + } + + // + // Clear the command flag to indicate that we are done. + // + HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_ERASE) = 0; + } + + // + // Are we being asked to program a block of flash? + // + if(HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_WRITE)) + { + // + // Decrypt the data if required. + // +#ifdef BL_DECRYPT_FN_HOOK + BL_DECRYPT_FN_HOOK(g_pui8DFUWrite, g_ui16DFUBufferUsed); +#endif + + // + // Where will the new block be written? + // + ui32Start = (uint32_t)(g_sNextDownload.pui8Start); + +#ifndef FLASH_CODE_PROTECTION + // + // What is the address of the last byte we will write in this + // block of data? We copy g_ui16DFUBufferUsed to prevent warnings + // about "undefined order of volatile accesses" from some + // compilers. + // + ui16Used = g_ui16DFUBufferUsed; + + ui32End = (uint32_t)g_sNextDownload.pui8Start + ui16Used - 1; + + // + // Are we writing data at the start of a new flash block? If so, + // we need to erase the content of the block first. + // + if((ui32Start & (FLASH_PAGE_SIZE - 1)) == 0) + { + // + // We are writing to the start of a block so erase it. + // + EraseFlashBlock(ui32Start & ~(FLASH_PAGE_SIZE - 1)); + } + else + { + // + // Will this block of data straddle two flash blocks? If so, + // we need to erase the following block. + // + if((ui32Start & ~(FLASH_PAGE_SIZE - 1)) != + (ui32End & ~(FLASH_PAGE_SIZE - 1))) + { + EraseFlashBlock(ui32End & ~(FLASH_PAGE_SIZE - 1)); + } + } +#endif + + // + // Write the new block of data to the flash + // + BL_FLASH_PROGRAM_FN_HOOK(ui32Start, g_pui8DFUWrite, ui16Used); + + // + // Update our position and remaining size. + // + g_sNextDownload.pui8Start += ui16Used; + g_sNextDownload.ui32Length -= ui16Used; + + // + // Clear the command flag to indicate that we are done. + // + HWREGBITW(&g_ui32CommandFlags, CMD_FLAG_WRITE) = 0; + + // + // If a progress hook function has been provided, call + // it here. + // +#ifdef BL_PROGRESS_FN_HOOK + BL_PROGRESS_FN_HOOK(g_ui32ImageSize - g_sNextDownload.ui32Length, + g_ui32ImageSize); +#endif + + // + // If we just finished the download and an end signal hook function + // has been provided, call it too. + // +#ifdef BL_END_FN_HOOK + if(g_sNextDownload.ui32Length == 0) + { + BL_END_FN_HOOK(); + } +#endif + } + } +} + +//***************************************************************************** +// +//! Configure the USB controller and place the DFU device on the bus. +//! +//! This function configures the USB controller for DFU device operation, +//! initializes the state machines required to control the firmware update and +//! places the device on the bus in preparation for requests from the host. It +//! is assumed that the main system clock has been configured at this point. +//! +//! \return None. +// +//***************************************************************************** +void +ConfigureUSBInterface(void) +{ + uint32_t ui32FlashSize; + + // + // Initialize our device information structure. + // + ui32FlashSize = BL_FLASH_SIZE_FN_HOOK(); + + g_sDFUDeviceInfo.ui16FlashBlockSize = DFU_REPORTED_PAGE_SIZE; + g_sDFUDeviceInfo.ui16NumFlashBlocks = + ui32FlashSize / DFU_REPORTED_PAGE_SIZE; + g_sDFUDeviceInfo.ui32ClassInfo = HWREG(SYSCTL_DID0); + g_sDFUDeviceInfo.ui32PartInfo = HWREG(SYSCTL_DID1); + g_sDFUDeviceInfo.ui32AppStartAddr = APP_START_ADDRESS; +#ifdef FLASH_RSVD_SPACE + g_sDFUDeviceInfo.ui32FlashTop = ui32FlashSize - FLASH_RSVD_SPACE; +#else + g_sDFUDeviceInfo.ui32FlashTop = ui32FlashSize; +#endif + + // + // Publish our DFU device descriptors and place the device on the bus. + // + USBBLInit(); +} + +#if (defined USB_HAS_MUX) || (defined DOXYGEN) +//***************************************************************************** +// +//! Configures and set the mux selecting USB device-mode operation. +//! +//! On target boards which use a multiplexer to switch between USB host and +//! device operation, this function is used to configure the relevant GPIO +//! pin and drive it such that the mux selects USB device-mode operation. +//! If \b USB_HAS_MUX is not defined in bl_config.h, this function is compiled +//! out. +//! +//! \return None. +// +//***************************************************************************** +static void +SetUSBMux(void) +{ + // + // Enable the GPIO peripheral that contains the mux control pin. + // + HWREG(SYSCTL_RCGC2) |= USB_MUX_PERIPH; + + // + // Delay a very short period before we access the newly-enabled peripheral. + // + Delay(1); + + // + // Make the pin be an output. + // + HWREG(USB_MUX_PORT + GPIO_O_DIR) |= (1 << USB_MUX_PIN); + HWREG(USB_MUX_PORT + GPIO_O_AFSEL) &= ~(1 << USB_MUX_PIN); + + // + // Set the output drive strength to 2mA. + // + HWREG(USB_MUX_PORT + GPIO_O_DR2R) |= (1 << USB_MUX_PIN); + HWREG(USB_MUX_PORT + GPIO_O_DR4R) &= ~(1 << USB_MUX_PIN); + HWREG(USB_MUX_PORT + GPIO_O_DR8R) &= ~(1 << USB_MUX_PIN); + HWREG(USB_MUX_PORT + GPIO_O_SLR) &= ~(1 << USB_MUX_PIN); + + // + // Set the pin type to a normal, GPIO output. + // + HWREG(USB_MUX_PORT + GPIO_O_ODR) &= ~(1 << USB_MUX_PIN); + HWREG(USB_MUX_PORT + GPIO_O_PUR) &= ~(1 << USB_MUX_PIN); + HWREG(USB_MUX_PORT + GPIO_O_PDR) &= ~(1 << USB_MUX_PIN); + HWREG(USB_MUX_PORT + GPIO_O_DEN) |= (1 << USB_MUX_PIN); + + // + // Clear this pin's bit in the analog mode select register. + // + HWREG(USB_MUX_PORT + GPIO_O_AMSEL) &= ~(1 << USB_MUX_PIN); + + // + // Write the pin to the appropriate level to select USB device mode. + // + HWREG(USB_MUX_PORT + (GPIO_O_DATA + ((1 << USB_MUX_PIN) << 2))) = + (USB_MUX_DEVICE ? (1 << USB_MUX_PIN) : 0); +} +#endif + +//***************************************************************************** +// +//! Generic configuration is handled in this function. +//! +//! This function is called by the start up code to perform any configuration +//! necessary before calling the update routine. It is responsible for setting +//! the system clock to the expected rate and setting flash programming +//! parameters prior to calling ConfigureUSBInterface() to set up the USB +//! hardware and place the DFU device on the bus. +//! +//! \return None. +// +//***************************************************************************** +void +ConfigureUSB(void) +{ + // + // Enable the main oscillator. + // + HWREG(SYSCTL_RCC) &= ~(SYSCTL_RCC_MOSCDIS); + + // + // Delay while the main oscillator starts up. + // + Delay(524288); + + // + // Set the crystal frequency, switch to the main oscillator, and enable the + // PLL. + // + HWREG(SYSCTL_RCC) = ((HWREG(SYSCTL_RCC) & + ~(SYSCTL_RCC_PWRDN | SYSCTL_RCC_XTAL_M | + SYSCTL_RCC_OSCSRC_M)) | + XTAL_VALUE | SYSCTL_RCC_OSCSRC_MAIN); + + // + // Delay while the PLL locks. + // + Delay(524288); + + // + // Disable the PLL bypass so that the part is clocked from the PLL, and set + // sysdiv to 8. This yields a system clock of 25MHz. + // + HWREG(SYSCTL_RCC) = ((HWREG(SYSCTL_RCC) & ~(SYSCTL_RCC_BYPASS | + SYSCTL_RCC_SYSDIV_M)) | + ((8 - 1) << SYSCTL_RCC_SYSDIV_S) | + SYSCTL_RCC_USESYSDIV); + + // + // If the target device has a mux to allow selection of USB host or + // device mode, make sure this is set to device mode. + // +#ifdef USB_HAS_MUX + SetUSBMux(); +#endif + + // + // Configure the USB interface and put the device on the bus. + // + ConfigureUSBInterface(); +} + +//***************************************************************************** +// +//! This is the application entry point to the USB updater. +//! +//! This function should only be entered from a running application and not +//! when running the boot loader with no application present. If the +//! calling application supports any USB device function, it must remove +//! itself from the USB bus prior to calling this function. This function +//! assumes that the calling application has already configured the system +//! clock to run from the PLL. +//! +//! \return None. +// +//***************************************************************************** +void +AppUpdaterUSB(void) +{ + // + // Set sysdiv to 8. This yields a system clock of 25MHz. + // + HWREG(SYSCTL_RCC) = ((HWREG(SYSCTL_RCC) & ~(SYSCTL_RCC_SYSDIV_M)) | + ((8 - 1) << SYSCTL_RCC_SYSDIV_S)); + + // + // If the target device has a mux to allow selection of USB host or + // device mode, make sure this is set to device mode. + // +#ifdef USB_HAS_MUX + SetUSBMux(); +#endif + + // + // Configure the USB interface and put the device on the bus. + // + ConfigureUSBInterface(); + + // + // Call the main update routine. + // + UpdaterUSB(); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif diff --git a/boot_loader/bl_usbfuncs.c b/boot_loader/bl_usbfuncs.c new file mode 100644 index 0000000..552fabc --- /dev/null +++ b/boot_loader/bl_usbfuncs.c @@ -0,0 +1,1915 @@ +//***************************************************************************** +// +// bl_usbfuncs.c - The subset of USB library functions required by the USB DFU +// boot loader. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "inc/hw_memmap.h" +#include "inc/hw_usb.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_nvic.h" +#include "inc/hw_ints.h" +#include "inc/hw_gpio.h" +#include "bl_config.h" +#include "boot_loader/bl_usbfuncs.h" + +//***************************************************************************** +// +//! \addtogroup bl_usb_api +//! @{ +// +//***************************************************************************** +#if defined(USB_ENABLE_UPDATE) || defined(DOXYGEN) + +//***************************************************************************** +// +// Local functions prototypes. +// +//***************************************************************************** +static void USBDGetStatus(tUSBRequest *pUSBRequest); +static void USBDClearFeature(tUSBRequest *pUSBRequest); +static void USBDSetFeature(tUSBRequest *pUSBRequest); +static void USBDSetAddress(tUSBRequest *pUSBRequest); +static void USBDGetDescriptor(tUSBRequest *pUSBRequest); +static void USBDSetDescriptor(tUSBRequest *pUSBRequest); +static void USBDGetConfiguration(tUSBRequest *pUSBRequest); +static void USBDSetConfiguration(tUSBRequest *pUSBRequest); +static void USBDGetInterface(tUSBRequest *pUSBRequest); +static void USBDSetInterface(tUSBRequest *pUSBRequest); +static void USBDEP0StateTx(void); +static int32_t USBDStringIndexFromRequest(uint16_t ui16Lang, + uint16_t ui16Index); + +//***************************************************************************** +// +// This structure holds the full state for the device enumeration. +// +//***************************************************************************** +typedef struct +{ + // + // The devices current address, this also has a change pending bit in the + // MSB of this value specified by DEV_ADDR_PENDING. + // + volatile uint32_t ui32DevAddress; + + // + // This holds the current active configuration for this device. + // + uint32_t ui32Configuration; + + // + // This holds the current alternate interface for this device. We only have + // 1 interface so only need to hold 1 setting. + // + uint8_t ui8AltSetting; + + // + // This is the pointer to the current data being sent out or received + // on endpoint zero. + // + uint8_t *pui8EP0Data; + + // + // This is the number of bytes that remain to be sent from or received + // into the g_sUSBDeviceState.pui8EP0Data data buffer. + // + volatile uint32_t ui32EP0DataRemain; + + // + // The amount of data being sent/received due to a custom request. + // + uint32_t ui32OUTDataSize; + + // + // Holds the current device status. + // + uint8_t ui8Status; + + // + // This flag indicates whether or not remote wakeup signalling is in + // progress. + // + bool bRemoteWakeup; + + // + // During remote wakeup signalling, this counter is used to track the + // number of milliseconds since the signalling was initiated. + // + uint8_t ui8RemoteWakeupCount; +} +tDeviceState; + +//***************************************************************************** +// +// The states for endpoint zero during enumeration. +// +//***************************************************************************** +typedef enum +{ + // + // The USB device is waiting on a request from the host controller on + // endpoint zero. + // + USB_STATE_IDLE, + + // + // The USB device is sending data back to the host due to an IN request. + // + USB_STATE_TX, + + // + // The USB device is receiving data from the host due to an OUT + // request from the host. + // + USB_STATE_RX, + + // + // The USB device has completed the IN or OUT request and is now waiting + // for the host to acknowledge the end of the IN/OUT transaction. This + // is the status phase for a USB control transaction. + // + USB_STATE_STATUS, + + // + // This endpoint has signaled a stall condition and is waiting for the + // stall to be acknowledged by the host controller. + // + USB_STATE_STALL +} +tEP0State; + +//***************************************************************************** +// +// Define the max packet size for endpoint zero. +// +//***************************************************************************** +#define EP0_MAX_PACKET_SIZE 64 + +//***************************************************************************** +// +// This is a flag used with g_sUSBDeviceState.ui32DevAddress to indicate that a +// device address change is pending. +// +//***************************************************************************** +#define DEV_ADDR_PENDING 0x80000000 + +//***************************************************************************** +// +// This label defines the default configuration number to use after a bus +// reset. +// +//***************************************************************************** +#define DEFAULT_CONFIG_ID 1 + +//***************************************************************************** +// +// This label defines the number of milliseconds that the remote wakeup signal +// must remain asserted before removing it. Section 7.1.7.7 of the USB 2.0 spec +// states that "the remote wakeup device must hold the resume signaling for at +// least 1ms but for no more than 15ms" so 10mS seems a reasonable choice. +// +//***************************************************************************** +#define REMOTE_WAKEUP_PULSE_MS 10 + +//***************************************************************************** +// +// This label defines the number of milliseconds between the point where we +// assert the remote wakeup signal and calling the client back to tell it that +// bus operation has been resumed. This value is based on the timings provided +// in section 7.1.7.7 of the USB 2.0 specification which indicates that the host +// (which takes over resume signalling when the device's initial signal is +// detected) must hold the resume signalling for at least 20mS. +// +//***************************************************************************** +#define REMOTE_WAKEUP_READY_MS 20 + +//***************************************************************************** +// +// The buffer for reading data coming into EP0 +// +//***************************************************************************** +static uint8_t g_pui8DataBufferIn[EP0_MAX_PACKET_SIZE]; + +//***************************************************************************** +// +// This global holds the current state information for the USB device. +// +//***************************************************************************** +static volatile tDeviceState g_sUSBDeviceState; + +//***************************************************************************** +// +// This global holds the current state of endpoint zero. +// +//***************************************************************************** +static volatile tEP0State g_eUSBDEP0State = USB_STATE_IDLE; + +//***************************************************************************** +// +// Function table to handle standard requests. +// +//***************************************************************************** +static const tStdRequest g_ppfnUSBDStdRequests[] = +{ + USBDGetStatus, + USBDClearFeature, + 0, + USBDSetFeature, + 0, + USBDSetAddress, + USBDGetDescriptor, + USBDSetDescriptor, + USBDGetConfiguration, + USBDSetConfiguration, + USBDGetInterface, + USBDSetInterface, +}; + +//***************************************************************************** +// +// Amount to shift the RX interrupt sources by in the flags used in the +// interrupt calls. +// +//***************************************************************************** +#define USB_INT_RX_SHIFT 8 + +//***************************************************************************** +// +// Amount to shift the status interrupt sources by in the flags used in the +// interrupt calls. +// +//***************************************************************************** +#define USB_INT_STATUS_SHIFT 24 + +//***************************************************************************** +// +// Amount to shift the RX endpoint status sources by in the flags used in the +// calls. +// +//***************************************************************************** +#define USB_RX_EPSTATUS_SHIFT 16 + +//***************************************************************************** +// +// Converts from an endpoint specifier to the offset of the endpoint's +// control/status registers. +// +//***************************************************************************** +#define EP_OFFSET(Endpoint) (Endpoint - 0x10) + +//***************************************************************************** +// +// Retrieves data from endpoint 0's FIFO. +// +// \param pui8Data is a pointer to the data area used to return the data from +// the FIFO. +// \param pui32Size is initially the size of the buffer passed into this call +// via the \e pui8Data parameter. It will be set to the amount of data +// returned in the buffer. +// +// This function will return the data from the FIFO for endpoint 0. +// The \e pui32Size parameter should indicate the size of the buffer passed in +// the \e pui32Data parameter. The data in the \e pui32Size parameter will be +// changed to match the amount of data returned in the \e pui8Data parameter. +// If a zero byte packet was received this call will not return a error but +// will instead just return a zero in the \e pui32Size parameter. The only +// error case occurs when there is no data packet available. +// +// \return This call will return 0, or -1 if no packet was received. +// +//***************************************************************************** +int32_t +USBEndpoint0DataGet(uint8_t *pui8Data, uint32_t *pui32Size) +{ + uint32_t ui32ByteCount; + + // + // Don't allow reading of data if the RxPktRdy bit is not set. + // + if((HWREGH(USB0_BASE + USB_O_CSRL0) & USB_CSRL0_RXRDY) == 0) + { + // + // Can't read the data because none is available. + // + *pui32Size = 0; + + // + // Return a failure since there is no data to read. + // + return(-1); + } + + // + // Get the byte count in the FIFO. + // + ui32ByteCount = HWREGH(USB0_BASE + USB_O_COUNT0 + USB_EP_0); + + // + // Determine how many bytes we will actually copy. + // + ui32ByteCount = (ui32ByteCount < *pui32Size) ? ui32ByteCount : *pui32Size; + + // + // Return the number of bytes we are going to read. + // + *pui32Size = ui32ByteCount; + + // + // Read the data out of the FIFO. + // + for(; ui32ByteCount > 0; ui32ByteCount--) + { + // + // Read a byte at a time from the FIFO. + // + *pui8Data++ = HWREGB(USB0_BASE + USB_O_FIFO0 + (USB_EP_0 >> 2)); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +// Acknowledge that data was read from endpoint 0's FIFO. +// +// \param bIsLastPacket indicates if this is the last packet. +// +// This function acknowledges that the data was read from the endpoint 0's +// FIFO. The \e bIsLastPacket parameter is set to a \b true value if this is +// the last in a series of data packets. This call can be used if processing +// is required between reading the data and acknowledging that the data has +// been read. +// +// \return None. +// +//***************************************************************************** +void +USBDevEndpoint0DataAck(bool bIsLastPacket) +{ + // + // Clear RxPktRdy, and optionally DataEnd, on endpoint zero. + // + HWREGB(USB0_BASE + USB_O_CSRL0) = + USB_CSRL0_RXRDYC | (bIsLastPacket ? USB_CSRL0_DATAEND : 0); + +} + +//***************************************************************************** +// +// Puts data into endpoint 0's FIFO. +// +// \param pui8Data is a pointer to the data area used as the source for the +// data to put into the FIFO. +// \param ui32Size is the amount of data to put into the FIFO. +// +// This function will put the data from the \e pui8Data parameter into the FIFO +// for endpoint 0. If a packet is already pending for transmission then +// this call will not put any of the data into the FIFO and will return -1. +// +// \return This call will return 0 on success, or -1 to indicate that the FIFO +// is in use and cannot be written. +// +//***************************************************************************** +int32_t +USBEndpoint0DataPut(uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Don't allow transmit of data if the TxPktRdy bit is already set. + // + if(HWREGB(USB0_BASE + USB_O_CSRL0 + USB_EP_0) & USB_CSRL0_TXRDY) + { + return(-1); + } + + // + // Write the data to the FIFO. + // + for(; ui32Size > 0; ui32Size--) + { + HWREGB(USB0_BASE + USB_O_FIFO0 + (USB_EP_0 >> 2)) = *pui8Data++; + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +// Starts the transfer of data from endpoint 0's FIFO. +// +// \param ui32TransType is set to indicate what type of data is being sent. +// +// This function will start the transfer of data from the FIFO for +// endpoint 0. This is necessary if the \b USB_EP_AUTO_SET bit was not enabled +// for the endpoint. Setting the \e ui32TransType parameter will allow the +// appropriate signaling on the USB bus for the type of transaction being +// requested. The \e ui32TransType parameter should be one of the following: +// +// - USB_TRANS_OUT for OUT transaction on any endpoint in host mode. +// - USB_TRANS_IN for IN transaction on any endpoint in device mode. +// - USB_TRANS_IN_LAST for the last IN transactions on endpoint zero in a +// sequence of IN transactions. +// - USB_TRANS_SETUP for setup transactions on endpoint zero. +// - USB_TRANS_STATUS for status results on endpoint zero. +// +// \return This call will return 0 on success, or -1 if a transmission is +// already in progress. +// +//***************************************************************************** +int32_t +USBEndpoint0DataSend(uint32_t ui32TransType) +{ + // + // Don't allow transmit of data if the TxPktRdy bit is already set. + // + if(HWREGB(USB0_BASE + USB_O_CSRL0 + USB_EP_0) & USB_CSRL0_TXRDY) + { + return(-1); + } + + // + // Set TxPktRdy in order to send the data. + // + HWREGB(USB0_BASE + USB_O_CSRL0 + USB_EP_0) = ui32TransType & 0xff; + + // + // Success. + // + return(0); +} + +#if defined(USB_VBUS_CONFIG) || defined(USB_ID_CONFIG) || \ + defined(USB_DP_CONFIG) || defined(USB_DM_CONFIG) || defined(DOXYGEN) +//***************************************************************************** +// +//! Initialize the pins used by USB functions. +//! +//! This function configures the pins for USB functions depending on defines +//! from the bl_config.h file. +//! +//! \return None. +// +//***************************************************************************** +void +USBConfigurePins(void) +{ + // + // Enable the clocks to the GPIOs. + // + HWREG(SYSCTL_RCGCGPIO) |= (0x0 +#if defined(USB_VBUS_CONFIG) + | USB_VBUS_PERIPH +#endif +#if defined(USB_ID_CONFIG) + | USB_ID_PERIPH +#endif +#if defined(USB_DP_CONFIG) + | USB_DP_PERIPH +#endif +#if defined(USB_DM_CONFIG) + | USB_DM_PERIPH +#endif + ); + + // + // Setup the pins based on bl_config.h + // +#if defined(USB_VBUS_CONFIG) + // + // Set the VBUS pin to be an analog input. + // + HWREG(USB_VBUS_PORT + GPIO_O_DIR) &= ~(1 << USB_VBUS_PIN); + HWREG(USB_VBUS_PORT + GPIO_O_AMSEL) |= (1 << USB_VBUS_PIN); +#endif + +#if defined(USB_ID_CONFIG) + // + // Set the ID pin to be an analog input. + // + HWREG(USB_ID_PORT + GPIO_O_DIR) &= ~(1 << USB_ID_PIN); + HWREG(USB_ID_PORT + GPIO_O_AMSEL) |= (1 << USB_ID_PIN); +#endif + +#if defined(USB_DP_CONFIG) + // + // Set the DP pin to be an analog input. + // + HWREG(USB_DP_PORT + GPIO_O_DIR) &= ~(1 << USB_DP_PIN); + HWREG(USB_DP_PORT + GPIO_O_AMSEL) |= (1 << USB_DP_PIN); +#endif + +#if defined(USB_DM_CONFIG) + // + // Set the DM pin to be an analog input. + // + HWREG(USB_DM_PORT + GPIO_O_DIR) &= ~(1 << USB_DM_PIN); + HWREG(USB_DM_PORT + GPIO_O_AMSEL) |= (1 << USB_DM_PIN); +#endif + +} +#endif + +//***************************************************************************** +// +//! Initialize the boot loader USB functions. +//! +//! This function initializes the boot loader USB functions and places the DFU +//! device onto the USB bus. +//! +//! \return None. +// +//***************************************************************************** +void +USBBLInit(void) +{ + // + // Configure the USB Pins based on the bl_config.h settings. + // +#if defined(USB_VBUS_CONFIG) || defined(USB_ID_CONFIG) || \ + defined(USB_DP_CONFIG) || defined(USB_DM_CONFIG) + USBConfigurePins(); +#endif + + // + // Initialize a couple of fields in the device state structure. + // + g_sUSBDeviceState.ui32Configuration = DEFAULT_CONFIG_ID; + + // + // Enable the USB controller. + // + HWREG(SYSCTL_RCGC2) |= 0x10000; + + // + // Turn on USB Phy clock. + // + HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_USBPWRDN; + + // + // Clear any pending interrupts. + // + HWREGH(USB0_BASE + USB_O_TXIS); + HWREGB(USB0_BASE + USB_O_IS); + + // + // Enable USB Interrupts. + // + HWREGH(USB0_BASE + USB_O_TXIE) = USB_TXIS_EP0; + HWREGB(USB0_BASE + USB_O_IE) = (USB_IS_DISCON | USB_IS_RESET); + + // + // Default to the state where remote wakeup is disabled. + // + g_sUSBDeviceState.ui8Status = 0; + g_sUSBDeviceState.bRemoteWakeup = false; + + // + // Determine the self- or bus-powered state based on bl_config.h setting. + // +#if USB_BUS_POWERED + g_sUSBDeviceState.ui8Status &= ~USB_STATUS_SELF_PWR; +#else + g_sUSBDeviceState.ui8Status |= USB_STATUS_SELF_PWR; +#endif + + // + // Attach the device using the soft connect. + // + HWREGB(USB0_BASE + USB_O_POWER) |= USB_POWER_SOFTCONN; + + // + // Enable the USB interrupt. + // + HWREG(NVIC_EN1) = 1 << (INT_USB0 - 48); +} + +//***************************************************************************** +// +// This function starts the request for data from the host on endpoint zero. +// +// \param pui8Data is a pointer to the buffer to fill with data from the USB +// host. +// \param ui32Size is the size of the buffer or data to return from the USB +// host. +// +// This function handles retrieving data from the host when a custom command +// has been issued on endpoint zero. When the requested data is received, +// the function HandleEP0Data() will be called. +// +// \return None. +// +//***************************************************************************** +void +USBBLRequestDataEP0(uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Enter the RX state on end point 0. + // + g_eUSBDEP0State = USB_STATE_RX; + + // + // Save the pointer to the data. + // + g_sUSBDeviceState.pui8EP0Data = pui8Data; + + // + // Location to save the current number of bytes received. + // + g_sUSBDeviceState.ui32OUTDataSize = ui32Size; + + // + // Bytes remaining to be received. + // + g_sUSBDeviceState.ui32EP0DataRemain = ui32Size; +} + +//***************************************************************************** +// +//! This function requests transfer of data to the host on endpoint zero. +//! +//! \param pui8Data is a pointer to the buffer to send via endpoint zero. +//! \param ui32Size is the amount of data to send in bytes. +//! +//! This function handles sending data to the host when a custom command is +//! issued or non-standard descriptor has been requested on endpoint zero. +//! +//! \return None. +// +//***************************************************************************** +void +USBBLSendDataEP0(uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Return the externally provided device descriptor. + // + g_sUSBDeviceState.pui8EP0Data = pui8Data; + + // + // The size of the device descriptor is in the first byte. + // + g_sUSBDeviceState.ui32EP0DataRemain = ui32Size; + + // + // Save the total size of the data sent. + // + g_sUSBDeviceState.ui32OUTDataSize = ui32Size; + + // + // Now in the transmit data state. + // + USBDEP0StateTx(); +} + +//***************************************************************************** +// +//! This function generates a stall condition on endpoint zero. +//! +//! This function is typically called to signal an error condition to the host +//! when an unsupported request is received by the device. It should be +//! called from within the callback itself (in interrupt context) and not +//! deferred until later since it affects the operation of the endpoint zero +//! state machine. +//! +//! \return None. +// +//***************************************************************************** +void +USBBLStallEP0(void) +{ + // + // Perform a stall on endpoint zero. + // + HWREGB(USB0_BASE + USB_O_CSRL0) |= (USB_CSRL0_STALL | USB_CSRL0_RXRDYC); + + // + // Enter the stalled state. + // + g_eUSBDEP0State = USB_STATE_STALL; +} + +//***************************************************************************** +// +// This internal function reads a request data packet and dispatches it to +// either a standard request handler or the registered device request +// callback depending upon the request type. +// +// \return None. +// +//***************************************************************************** +static void +USBDReadAndDispatchRequest(void) +{ + uint32_t ui32Size; + tUSBRequest *pRequest; + + // + // Cast the buffer to a request structure. + // + pRequest = (tUSBRequest *)g_pui8DataBufferIn; + + // + // Set the buffer size. + // + ui32Size = EP0_MAX_PACKET_SIZE; + + // + // Get the data from the USB controller end point 0. + // + USBEndpoint0DataGet(g_pui8DataBufferIn, &ui32Size); + + if(!ui32Size) + { + return; + } + + // + // See if this is a standard request or not. + // + if((pRequest->bmRequestType & USB_RTYPE_TYPE_M) != USB_RTYPE_STANDARD) + { + // + // Pass this non-standard request on to the DFU handler + // + HandleRequests(pRequest); + } + else + { + // + // Assure that the jump table is not out of bounds. + // + if((pRequest->bRequest < + (sizeof(g_ppfnUSBDStdRequests) / sizeof(tStdRequest))) && + (g_ppfnUSBDStdRequests[pRequest->bRequest] != 0)) + { + // + // Jump table to the appropriate handler. + // + g_ppfnUSBDStdRequests[pRequest->bRequest](pRequest); + } + else + { + // + // If there is no handler then stall this request. + // + USBBLStallEP0(); + } + } +} + +//***************************************************************************** +// +// This is the low level interrupt handler for endpoint zero. +// +// This function handles all interrupts on endpoint zero in order to maintain +// the state needed for the control endpoint on endpoint zero. In order to +// successfully enumerate and handle all USB standard requests, all requests +// on endpoint zero must pass through this function. The endpoint has the +// following states: \b USB_STATE_IDLE, \b USB_STATE_TX, \b USB_STATE_RX, +// \b USB_STATE_STALL, and \b USB_STATE_STATUS. In the \b USB_STATE_IDLE +// state the USB controller has not received the start of a request, and once +// it does receive the data for the request it will either enter the +// \b USB_STATE_TX, \b USB_STATE_RX, or \b USB_STATE_STALL depending on the +// command. If the controller enters the \b USB_STATE_TX or \b USB_STATE_RX +// then once all data has been sent or received, it must pass through the +// \b USB_STATE_STATUS state to allow the host to acknowledge completion of +// the request. The \b USB_STATE_STALL is entered from \b USB_STATE_IDLE in +// the event that the USB request was not valid. Both the \b USB_STATE_STALL +// and \b USB_STATE_STATUS are transitional states that return to the +// \b USB_STATE_IDLE state. +// +// \return None. +// +// USB_STATE_IDLE -*--> USB_STATE_TX -*-> USB_STATE_STATUS -*->USB_STATE_IDLE +// | | | +// |--> USB_STATE_RX - | +// | | +// |--> USB_STATE_STALL ---------->--------- +// +// ---------------------------------------------------------------- +// | Current State | State 0 | State 1 | +// | --------------------|-------------------|---------------------- +// | USB_STATE_IDLE | USB_STATE_TX/RX | USB_STATE_STALL | +// | USB_STATE_TX | USB_STATE_STATUS | | +// | USB_STATE_RX | USB_STATE_STATUS | | +// | USB_STATE_STATUS | USB_STATE_IDLE | | +// | USB_STATE_STALL | USB_STATE_IDLE | | +// ---------------------------------------------------------------- +// +//***************************************************************************** +void +USBDeviceEnumHandler(void) +{ + uint32_t ui32EPStatus; + + // + // Get the TX portion of the endpoint status. + // + ui32EPStatus = HWREGH(USB0_BASE + EP_OFFSET(USB_EP_0) + USB_O_TXCSRL1); + + // + // Get the RX portion of the endpoint status. + // + ui32EPStatus |= + ((HWREGH(USB0_BASE + EP_OFFSET(USB_EP_0) + USB_O_RXCSRL1)) << + USB_RX_EPSTATUS_SHIFT); + + // + // What state are we currently in? + // + switch(g_eUSBDEP0State) + { + // + // Handle the status state, this is a transitory state from + // USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE. + // + case USB_STATE_STATUS: + { + // + // Just go back to the idle state. + // + g_eUSBDEP0State = USB_STATE_IDLE; + + // + // If there is a pending address change then set the address. + // + if(g_sUSBDeviceState.ui32DevAddress & DEV_ADDR_PENDING) + { + // + // Clear the pending address change and set the address. + // + g_sUSBDeviceState.ui32DevAddress &= ~DEV_ADDR_PENDING; + HWREGB(USB0_BASE + USB_O_FADDR) = + (uint8_t)g_sUSBDeviceState.ui32DevAddress; + } + + // + // If a new packet is already pending, we need to read it + // and handle whatever request it contains. + // + if(ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY) + { + // + // Process the newly arrived packet. + // + USBDReadAndDispatchRequest(); + } + break; + } + + // + // In the IDLE state the code is waiting to receive data from the host. + // + case USB_STATE_IDLE: + { + // + // Is there a packet waiting for us? + // + if(ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY) + { + // + // Yes - process it. + // + USBDReadAndDispatchRequest(); + } + break; + } + + // + // Data is still being sent to the host so handle this in the + // EP0StateTx() function. + // + case USB_STATE_TX: + { + USBDEP0StateTx(); + break; + } + + // + // Handle the receive state for commands that are receiving data on + // endpoint zero. + // + case USB_STATE_RX: + { + uint32_t ui32DataSize; + + // + // Set the number of bytes to get out of this next packet. + // + if(g_sUSBDeviceState.ui32EP0DataRemain > EP0_MAX_PACKET_SIZE) + { + // + // Don't send more than EP0_MAX_PACKET_SIZE bytes. + // + ui32DataSize = EP0_MAX_PACKET_SIZE; + } + else + { + // + // There was space so send the remaining bytes. + // + ui32DataSize = g_sUSBDeviceState.ui32EP0DataRemain; + } + + // + // Get the data from the USB controller end point 0. + // + USBEndpoint0DataGet(g_sUSBDeviceState.pui8EP0Data, &ui32DataSize); + + // + // If there we not more that EP0_MAX_PACKET_SIZE or more bytes + // remaining then this transfer is complete. If there were exactly + // EP0_MAX_PACKET_SIZE remaining then there still needs to be + // null packet sent before this is complete. + // + if(g_sUSBDeviceState.ui32EP0DataRemain < EP0_MAX_PACKET_SIZE) + { + // + // Need to ack the data on end point 0 in this case + // without setting data end. + // + USBDevEndpoint0DataAck(true); + + // + // Return to the idle state. + // + g_eUSBDEP0State = USB_STATE_IDLE; + + // + // If there is a receive callback then call it. + // + if(g_sUSBDeviceState.ui32OUTDataSize != 0) + { + // + // Call the receive handler to handle the data + // that was received. + // + HandleEP0Data(g_sUSBDeviceState.ui32OUTDataSize); + + // + // Indicate that there is no longer any data being waited + // on. + // + g_sUSBDeviceState.ui32OUTDataSize = 0; + } + } + else + { + // + // Need to ack the data on end point 0 in this case + // without setting data end. + // + USBDevEndpoint0DataAck(false); + } + + // + // Advance the pointer. + // + g_sUSBDeviceState.pui8EP0Data += ui32DataSize; + + // + // Decrement the number of bytes that are being waited on. + // + g_sUSBDeviceState.ui32EP0DataRemain -= ui32DataSize; + + break; + } + // + // The device stalled endpoint zero so check if the stall needs to be + // cleared once it has been successfully sent. + // + case USB_STATE_STALL: + { + // + // If we sent a stall then acknowledge this interrupt. + // + if(ui32EPStatus & USB_DEV_EP0_SENT_STALL) + { + // + // Clear the stall condition. + // + HWREGB(USB0_BASE + USB_O_CSRL0) &= ~(USB_DEV_EP0_SENT_STALL); + + // + // Reset the global end point 0 state to IDLE. + // + g_eUSBDEP0State = USB_STATE_IDLE; + + } + break; + } + // + // Halt on an unknown state, but only in DEBUG mode builds. + // + default: + { +#ifdef DEBUG + while(1); +#endif + break; + } + } +} + +//***************************************************************************** +// +// This function handles bus reset notifications. +// +// This function is called from the low level USB interrupt handler whenever +// a bus reset is detected. It performs tidy-up as required and resets the +// configuration back to defaults in preparation for descriptor queries from +// the host. +// +// \return None. +// +//***************************************************************************** +void +USBDeviceEnumResetHandler(void) +{ + // + // Disable remote wakeup signalling (as per USB 2.0 spec 9.1.1.6). + // + g_sUSBDeviceState.ui8Status &= ~USB_STATUS_REMOTE_WAKE; + g_sUSBDeviceState.bRemoteWakeup = false; + + // + // Call the device dependent code to indicate a bus reset has occurred. + // + HandleReset(); + + // + // Reset the default configuration identifier and alternate function + // selections. + // + g_sUSBDeviceState.ui32Configuration = DEFAULT_CONFIG_ID; + g_sUSBDeviceState.ui8AltSetting = 0; +} + +//***************************************************************************** +// +// This function handles the GET_STATUS standard USB request. +// +// \param pUSBRequest holds the request type and endpoint number if endpoint +// status is requested. +// +// This function handles responses to a Get Status request from the host +// controller. A status request can be for the device, an interface or an +// endpoint. If any other type of request is made this function will cause +// a stall condition to indicate that the command is not supported. The +// \e pUSBRequest structure holds the type of the request in the +// bmRequestType field. If the type indicates that this is a request for an +// endpoint's status, then the wIndex field holds the endpoint number. +// +// \return None. +// +//***************************************************************************** +static void +USBDGetStatus(tUSBRequest *pUSBRequest) +{ + uint16_t ui16Data; + + // + // Determine what type of status was requested. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This was a Device Status request. + // + case USB_RTYPE_DEVICE: + { + // + // Return the current status for the device. + // + ui16Data = g_sUSBDeviceState.ui8Status; + + break; + } + + // + // This was a Interface status request. + // + case USB_RTYPE_INTERFACE: + { + // + // Interface status always returns 0. + // + ui16Data = 0; + + break; + } + + // + // This was an unknown request or a request for an endpoint (of which + // we have none) so set a stall. + // + case USB_RTYPE_ENDPOINT: + default: + { + // + // Anything else causes a stall condition to indicate that the + // command was not supported. + // + USBBLStallEP0(); + return; + } + } + + // + // Send the two byte status response. + // + g_sUSBDeviceState.ui32EP0DataRemain = 2; + g_sUSBDeviceState.pui8EP0Data = (uint8_t *)&ui16Data; + + // + // Send the response. + // + USBDEP0StateTx(); +} + +//***************************************************************************** +// +// This function handles the CLEAR_FEATURE standard USB request. +// +// \param pUSBRequest holds the options for the Clear Feature USB request. +// +// This function handles device or endpoint clear feature requests. The +// \e pUSBRequest structure holds the type of the request in the bmRequestType +// field and the feature is held in the wValue field. For device, the only +// clearable feature is the Remote Wake feature. This device request +// should only be made if the descriptor indicates that Remote Wake is +// implemented by the device. For endpoint requests the only clearable +// feature is the ability to clear a halt on a given endpoint. If any other +// requests are made, then the device will stall the request to indicate to +// the host that the command was not supported. +// +// \return None. +// +//***************************************************************************** +static void +USBDClearFeature(tUSBRequest *pUSBRequest) +{ + // + // Determine what type of status was requested. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This is a clear feature request at the device level. + // + case USB_RTYPE_DEVICE: + { + // + // Only remote wake is clearable by this function. + // + if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue) + { + // + // Clear the remote wake up state. + // + g_sUSBDeviceState.ui8Status &= ~USB_STATUS_REMOTE_WAKE; + + // + // Need to ack the data on end point 0. + // + USBDevEndpoint0DataAck(true); + } + else + { + USBBLStallEP0(); + } + break; + } + + // + // This is an unknown request or one destined for an invalid endpoint. + // + case USB_RTYPE_ENDPOINT: + default: + { + USBBLStallEP0(); + return; + } + } +} + +//***************************************************************************** +// +// This function handles the SET_FEATURE standard USB request. +// +// \param pUSBRequest holds the feature in the wValue field of the USB +// request. +// +// This function handles device or endpoint set feature requests. The +// \e pUSBRequest structure holds the type of the request in the bmRequestType +// field and the feature is held in the wValue field. For device, the only +// settable feature is the Remote Wake feature. This device request +// should only be made if the descriptor indicates that Remote Wake is +// implemented by the device. For endpoint requests the only settable feature +// is the ability to issue a halt on a given endpoint. If any other requests +// are made, then the device will stall the request to indicate to the host +// that the command was not supported. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetFeature(tUSBRequest *pUSBRequest) +{ + // + // Determine what type of status was requested. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This is a set feature request at the device level. + // + case USB_RTYPE_DEVICE: + { + // + // Only remote wake is setable by this function. + // + if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue) + { + // + // Set the remote wakeup state. + // + g_sUSBDeviceState.ui8Status |= USB_STATUS_REMOTE_WAKE; + + // + // Need to ack the data on end point 0. + // + USBDevEndpoint0DataAck(true); + } + else + { + USBBLStallEP0(); + } + break; + } + + // + // This is an unknown request or one destined for an invalid endpoint. + // + case USB_RTYPE_ENDPOINT: + default: + { + USBBLStallEP0(); + return; + } + } +} + +//***************************************************************************** +// +// This function handles the SET_ADDRESS standard USB request. +// +// \param pUSBRequest holds the new address to use in the wValue field of the +// USB request. +// +// This function is called to handle the change of address request from the +// host controller. This can only start the sequence as the host must +// acknowledge that the device has changed address. Thus this function sets +// the address change as pending until the status phase of the request has +// been completed successfully. This prevents the devices address from +// changing and not properly responding to the status phase. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetAddress(tUSBRequest *pUSBRequest) +{ + // + // The data needs to be acknowledged on end point 0 without setting data + // end because there is no data coming. + // + USBDevEndpoint0DataAck(true); + + // + // Save the device address as we cannot change address until the status + // phase is complete. + // + g_sUSBDeviceState.ui32DevAddress = pUSBRequest->wValue | DEV_ADDR_PENDING; + + // + // Transition directly to the status state since there is no data phase + // for this request. + // + g_eUSBDEP0State = USB_STATE_STATUS; + + // + // Clear the DFU status just in case we were in an error state last time + // the device was accessed and we were unplugged and replugged (for a self- + // powered implementation, of course). + // + HandleSetAddress(); +} + +//***************************************************************************** +// +// This function handles the GET_DESCRIPTOR standard USB request. +// +// \param pUSBRequest holds the data for this request. +// +// This function will return all configured standard USB descriptors to the +// host - device, config and string descriptors. Any request for a descriptor +// which is not available will result in endpoint 0 being stalled. +// +// \return None. +// +//***************************************************************************** +static void +USBDGetDescriptor(tUSBRequest *pUSBRequest) +{ + uint32_t ui32Stall; + + // + // Default to no stall. + // + ui32Stall = 0; + + // + // Which descriptor are we being asked for? + // + switch(pUSBRequest->wValue >> 8) + { + // + // This request was for a device descriptor. + // + case USB_DTYPE_DEVICE: + { + // + // Return the externally provided device descriptor. + // + g_sUSBDeviceState.pui8EP0Data = + (uint8_t *)g_pui8DFUDeviceDescriptor; + + // + // The size of the device descriptor is in the first byte. + // + g_sUSBDeviceState.ui32EP0DataRemain = + g_pui8DFUDeviceDescriptor[0]; + break; + } + + // + // This request was for a configuration descriptor. + // + case USB_DTYPE_CONFIGURATION: + { + uint8_t ui8Index; + + // + // Which configuration are we being asked for? + // + ui8Index = (uint8_t)(pUSBRequest->wValue & 0xFF); + + // + // Is this valid? + // + if(ui8Index != 0) + { + // + // This is an invalid configuration index. Stall EP0 to + // indicate a request error. + // + USBBLStallEP0(); + g_sUSBDeviceState.pui8EP0Data = 0; + g_sUSBDeviceState.ui32EP0DataRemain = 0; + } + else + { + // + // Start by sending data from the beginning of the first + // descriptor. + // + + g_sUSBDeviceState.pui8EP0Data = + (uint8_t *)g_pui8DFUConfigDescriptor; + + // + // Get the size of the config descriptor (remembering that in + // this case, we only have a single section) + // + g_sUSBDeviceState.ui32EP0DataRemain = + *(uint16_t *)&(g_pui8DFUConfigDescriptor[2]); + } + break; + } + + // + // This request was for a string descriptor. + // + case USB_DTYPE_STRING: + { + int32_t i32Index; + + // + // Determine the correct descriptor index based on the requested + // language ID and index. + // + i32Index = USBDStringIndexFromRequest(pUSBRequest->wIndex, + pUSBRequest->wValue & 0xFF); + + // + // If the mapping function returned -1 then stall the request to + // indicate that the request was not valid. + // + if(i32Index == -1) + { + USBBLStallEP0(); + break; + } + + // + // Return the externally specified configuration descriptor. + // + g_sUSBDeviceState.pui8EP0Data = + (uint8_t *)g_ppui8StringDescriptors[i32Index]; + + // + // The total size of a string descriptor is in byte 0. + // + g_sUSBDeviceState.ui32EP0DataRemain = + g_ppui8StringDescriptors[i32Index][0]; + + break; + } + + // + // Any other request is not handled by the default enumeration handler + // so see if it needs to be passed on to another handler. + // + default: + { + // + // All other requests are not handled. + // + USBBLStallEP0(); + ui32Stall = 1; + break; + } + } + + // + // If there was no stall, ACK the data and see if data needs to be sent. + // + if(ui32Stall == 0) + { + // + // Need to ack the data on end point 0 in this case without + // setting data end. + // + USBDevEndpoint0DataAck(false); + + // + // If this request has data to send, then send it. + // + if(g_sUSBDeviceState.pui8EP0Data) + { + // + // If there is more data to send than is requested then just + // send the requested amount of data. + // + if(g_sUSBDeviceState.ui32EP0DataRemain > pUSBRequest->wLength) + { + g_sUSBDeviceState.ui32EP0DataRemain = pUSBRequest->wLength; + } + + // + // Now in the transmit data state. Be careful to call the correct + // function since we need to handle the config descriptor + // differently from the others. + // + USBDEP0StateTx(); + } + } +} + +//***************************************************************************** +// +// This function determines which string descriptor to send to satisfy a +// request for a given index and language. +// +// \param ui16Lang is the requested string language ID. +// \param ui16Index is the requested string descriptor index. +// +// When a string descriptor is requested, the host provides a language ID and +// index to identify the string ("give me string number 5 in French"). This +// function maps these two parameters to an index within our device's string +// descriptor array which is arranged as multiple groups of strings with +// one group for each language advertised via string descriptor 0. +// +// We assume that there are an equal number of strings per language and +// that the first descriptor is the language descriptor and use this fact to +// perform the mapping. +// +// \return The index of the string descriptor to return or -1 if the string +// could not be found. +// +//***************************************************************************** +static int32_t +USBDStringIndexFromRequest(uint16_t ui16Lang, uint16_t ui16Index) +{ + tString0Descriptor *pLang; + uint32_t ui32NumLangs; + uint32_t ui32NumStringsPerLang; + uint32_t ui32Loop; + + // + // First look for the trivial case where descriptor 0 is being + // requested. This is the special case since descriptor 0 contains the + // language codes supported by the device. + // + if(ui16Index == 0) + { + return(0); + } + + // + // How many languages does this device support? This is determined by + // looking at the length of the first descriptor in the string table, + // subtracting 2 for the header and dividing by two (the size of each + // language code). + // + ui32NumLangs = (g_ppui8StringDescriptors[0][0] - 2) / 2; + + // + // We assume that the table includes the same number of strings for each + // supported language. We know the number of entries in the string table, + // so how many are there for each language? This may seem an odd way to + // do this (why not just have the application tell us in the device info + // structure?) but it's needed since we didn't want to change the API + // after the first release which did not support multiple languages. + // + ui32NumStringsPerLang = ((NUM_STRING_DESCRIPTORS - 1) / ui32NumLangs); + + // + // Just to be sure, make sure that the calculation indicates an equal + // number of strings per language. We expect the string table to contain + // (1 + (strings_per_language * languages)) entries. + // + if((1 + (ui32NumStringsPerLang * ui32NumLangs)) != NUM_STRING_DESCRIPTORS) + { + return(-1); + } + + // + // Now determine which language we are looking for. It is assumed that + // the order of the groups of strings per language in the table is the + // same as the order of the language IDs listed in the first descriptor. + // + pLang = (tString0Descriptor *)(g_ppui8StringDescriptors[0]); + + // + // Look through the supported languages looking for the one we were asked + // for. + // + for(ui32Loop = 0; ui32Loop < ui32NumLangs; ui32Loop++) + { + // + // Have we found the requested language? + // + if(pLang->wLANGID[ui32Loop] == ui16Lang) + { + // + // Yes - calculate the index of the descriptor to send. + // + return((ui32NumStringsPerLang * ui32Loop) + ui16Index); + } + } + + // + // If we drop out of the loop, the requested language was not found so + // return -1 to indicate the error. + // + return(-1); +} + +//***************************************************************************** +// +// This function handles the SET_DESCRIPTOR standard USB request. +// +// \param pUSBRequest holds the data for this request. +// +// This function currently is not supported and will respond with a Stall +// to indicate that this command is not supported by the device. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetDescriptor(tUSBRequest *pUSBRequest) +{ + // + // This function is not handled by default. + // + USBBLStallEP0(); +} + +//***************************************************************************** +// +// This function handles the GET_CONFIGURATION standard USB request. +// +// \param pUSBRequest holds the data for this request. +// +// This function responds to a host request to return the current +// configuration of the USB device. The function will send the configuration +// response to the host and return. This value will either be 0 or the last +// value received from a call to SetConfiguration(). +// +// \return None. +// +//***************************************************************************** +static void +USBDGetConfiguration(tUSBRequest *pUSBRequest) +{ + uint8_t ui8Value; + + // + // If we still have an address pending then the device is still not + // configured. + // + if(g_sUSBDeviceState.ui32DevAddress & DEV_ADDR_PENDING) + { + ui8Value = 0; + } + else + { + ui8Value = (uint8_t)g_sUSBDeviceState.ui32Configuration; + } + + g_sUSBDeviceState.ui32EP0DataRemain = 1; + g_sUSBDeviceState.pui8EP0Data = &ui8Value; + + // + // Send the single byte response. + // + USBDEP0StateTx(); +} + +//***************************************************************************** +// +// This function handles the SET_CONFIGURATION standard USB request. +// +// \param pUSBRequest holds the data for this request. +// +// This function responds to a host request to change the current +// configuration of the USB device. The actual configuration number is taken +// from the structure passed in via \e pUSBRequest. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetConfiguration(tUSBRequest *pUSBRequest) +{ + // + // Cannot set the configuration to one that does not exist so check the + // enumeration structure to see how many valid configurations are present. + // + if(pUSBRequest->wValue > 1) + { + // + // The passed configuration number is not valid. Stall the endpoint to + // signal the error to the host. + // + USBBLStallEP0(); + } + else + { + // + // Need to ack the data on end point 0. + // + USBDevEndpoint0DataAck(true); + + // + // Save the configuration. + // + g_sUSBDeviceState.ui32Configuration = pUSBRequest->wValue; + + // + // If passed a configuration other than 0 (which tells us that we are + // not currently configured), configure the endpoints (other than EP0) + // appropriately. + // + if(g_sUSBDeviceState.ui32Configuration) + { + // + // Set the power state + // +#if USB_BUS_POWERED + g_sUSBDeviceState.ui8Status &= ~USB_STATUS_SELF_PWR; +#else + g_sUSBDeviceState.ui8Status |= USB_STATUS_SELF_PWR; +#endif + } + + // + // Do whatever needs to be done as a result of the config change. + // + HandleConfigChange(g_sUSBDeviceState.ui32Configuration); + } +} + +//***************************************************************************** +// +// This function handles the GET_INTERFACE standard USB request. +// +// \param pUSBRequest holds the data for this request. +// +// This function is called when the host controller request the current +// interface that is in use by the device. This simply returns the value set +// by the last call to SetInterface(). +// +// \return None. +// +//***************************************************************************** +static void +USBDGetInterface(tUSBRequest *pUSBRequest) +{ + uint8_t ui8Value; + + // + // If we still have an address pending then the device is still not + // configured. + // + if(g_sUSBDeviceState.ui32DevAddress & DEV_ADDR_PENDING) + { + ui8Value = 0; + } + else + { + // + // Is the interface number valid? + // + if(pUSBRequest->wIndex == 0) + { + // + // Read the current alternate setting for the required interface. + // + ui8Value = g_sUSBDeviceState.ui8AltSetting; + } + else + { + // + // An invalid interface number was specified. + // + USBBLStallEP0(); + return; + } + } + + // + // Send the single byte response. + // + g_sUSBDeviceState.ui32EP0DataRemain = 1; + g_sUSBDeviceState.pui8EP0Data = &ui8Value; + + // + // Send the single byte response. + // + USBDEP0StateTx(); +} + +//***************************************************************************** +// +// This function handles the SET_INTERFACE standard USB request. +// +// \param pUSBRequest holds the data for this request. +// +// The DFU device supports a single interface with no alternate settings so +// this handler is hardcoded assuming this configuration. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetInterface(tUSBRequest *pUSBRequest) +{ + if((pUSBRequest->wIndex == 0) && (pUSBRequest->wValue == 0)) + { + // + // We were passed a valid interface number so acknowledge the request. + // + USBDevEndpoint0DataAck(true); + } + else + { + // + // The values passed were not valid so stall endpoint 0. + // + USBBLStallEP0(); + } +} + +//***************************************************************************** +// +// This internal function handles sending data on endpoint zero. +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTx(void) +{ + uint32_t ui32NumBytes; + uint8_t *pui8Data; + + // + // In the TX state on endpoint zero. + // + g_eUSBDEP0State = USB_STATE_TX; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = g_sUSBDeviceState.ui32EP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ui32NumBytes > EP0_MAX_PACKET_SIZE) + { + ui32NumBytes = EP0_MAX_PACKET_SIZE; + } + + // + // Save the pointer so that it can be passed to the USBEndpointDataPut() + // function. + // + pui8Data = g_sUSBDeviceState.pui8EP0Data; + + // + // Advance the data pointer and counter to the next data to be sent. + // + g_sUSBDeviceState.ui32EP0DataRemain -= ui32NumBytes; + g_sUSBDeviceState.pui8EP0Data += ui32NumBytes; + + // + // Put the data in the correct FIFO. + // + USBEndpoint0DataPut(pui8Data, ui32NumBytes); + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ui32NumBytes == EP0_MAX_PACKET_SIZE) + { + // + // There is more data to send or exactly 64 bytes were sent, this + // means that there is either more data coming or a null packet needs + // to be sent to complete the transaction. + // + USBEndpoint0DataSend(USB_TRANS_IN); + } + else + { + // + // Now go to the status state and wait for the transmit to complete. + // + g_eUSBDEP0State = USB_STATE_STATUS; + + // + // Send the last bit of data. + // + USBEndpoint0DataSend(USB_TRANS_IN_LAST); + g_sUSBDeviceState.ui32OUTDataSize = 0; + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** +#endif // USB_ENABLE_UPDATE diff --git a/boot_loader/bl_usbfuncs.h b/boot_loader/bl_usbfuncs.h new file mode 100644 index 0000000..7c3868c --- /dev/null +++ b/boot_loader/bl_usbfuncs.h @@ -0,0 +1,505 @@ +//***************************************************************************** +// +// bl_usbfuncs.h - Prototypes for the subset of USB library functions used in +// the USB DFU boot loader. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __BL_USBFUNCS_H__ +#define __BL_USBFUNCS_H__ + +//***************************************************************************** +// +//! \addtogroup bl_usb_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The following macro allows compiler-independent syntax to be used to +// define packed structures. A typical structure definition using these +// macros will look similar to the following example: +// +// #ifdef ewarm +// #pragma pack(1) +// #endif +// +// typedef struct _PackedStructName +// { +// uint32_t ui32FirstField; +// int8_t i8CharMember; +// uint16_t ui16Short; +// } +// PACKED tPackedStructName; +// +// #ifdef ewarm +// #pragma pack() +// #endif +// +// The conditional blocks related to ewarm include the #pragma pack() lines +// only if the IAR Embedded Workbench compiler is being used. Unfortunately, +// it is not possible to emit a #pragma from within a macro definition so this +// must be done explicitly. +// +//***************************************************************************** +#if defined(ccs) || \ + defined(codered) || \ + defined(gcc) || \ + defined(rvmdk) || \ + defined(__ARMCC_VERSION) || \ + defined(sourcerygxx) +#define PACKED __attribute__ ((packed)) +#elif defined(ewarm) +#define PACKED +#else +#error Unrecognized COMPILER! +#endif + +//***************************************************************************** +// +// Standard USB descriptor types. These values are passed in the upper bytes +// of tUSBRequest.wValue on USBREQ_GET_DESCRIPTOR and also appear in the +// bDescriptorType field of standard USB descriptors. +// +//***************************************************************************** +#define USB_DTYPE_DEVICE 1 +#define USB_DTYPE_CONFIGURATION 2 +#define USB_DTYPE_STRING 3 +#define USB_DTYPE_INTERFACE 4 +#define USB_DTYPE_ENDPOINT 5 +#define USB_DTYPE_DEVICE_QUAL 6 +#define USB_DTYPE_OSPEED_CONF 7 +#define USB_DTYPE_INTERFACE_PWR 8 + +#define USBShort(ui16Value) (ui16Value & 0xff), (ui16Value >> 8) + +#define USB_LANG_EN_US 0x0409 // English (United States) + +#define USB_EP_DEV_IN 0x00002000 // Device IN endpoint +#define USB_EP_DEV_OUT 0x00001000 // Device OUT endpoint + +//***************************************************************************** +// +// All structures defined in this section of the header require byte packing of +// fields. This is usually accomplished using the PACKED macro but, for IAR +// Embedded Workbench, this requires a pragma. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +// Definitions related to standard USB device requests (sections 9.3 & 9.4) +// +//***************************************************************************** + +//***************************************************************************** +// +//! The standard USB request header as defined in section 9.3 of the USB 2.0 +//! specification. +// +//***************************************************************************** +typedef struct +{ + // + //! Determines the type and direction of the request. + // + uint8_t bmRequestType; + + // + //! Identifies the specific request being made. + // + uint8_t bRequest; + + // + //! Word-sized field that varies according to the request. + // + uint16_t wValue; + + // + //! Word-sized field that varies according to the request; typically used + //! to pass an index or offset. + // + uint16_t wIndex; + + // + //! The number of bytes to transfer if there is a data stage to the + //! request. + // + uint16_t wLength; +} +PACKED tUSBRequest; + +//***************************************************************************** +// +// The following defines are used with the bmRequestType member of tUSBRequest. +// +// Request types have 3 bit fields: +// 4:0 - Is the recipient type. +// 6:5 - Is the request type. +// 7 - Is the direction of the request. +// +//***************************************************************************** +#define USB_RTYPE_DIR_IN 0x80 +#define USB_RTYPE_DIR_OUT 0x00 + +#define USB_RTYPE_TYPE_M 0x60 +#define USB_RTYPE_VENDOR 0x40 +#define USB_RTYPE_CLASS 0x20 +#define USB_RTYPE_STANDARD 0x00 + +#define USB_RTYPE_RECIPIENT_M 0x1f +#define USB_RTYPE_OTHER 0x03 +#define USB_RTYPE_ENDPOINT 0x02 +#define USB_RTYPE_INTERFACE 0x01 +#define USB_RTYPE_DEVICE 0x00 + +//***************************************************************************** +// +// Standard USB requests IDs used in the bRequest field of tUSBRequest. +// +//***************************************************************************** +#define USBREQ_GET_STATUS 0x00 +#define USBREQ_CLEAR_FEATURE 0x01 +#define USBREQ_SET_FEATURE 0x03 +#define USBREQ_SET_ADDRESS 0x05 +#define USBREQ_GET_DESCRIPTOR 0x06 +#define USBREQ_SET_DESCRIPTOR 0x07 +#define USBREQ_GET_CONFIG 0x08 +#define USBREQ_SET_CONFIG 0x09 +#define USBREQ_GET_INTERFACE 0x0a +#define USBREQ_SET_INTERFACE 0x0b +#define USBREQ_SYNC_FRAME 0x0c + +//***************************************************************************** +// +// Data returned from a USBREQ_GET_STATUS request to a device. +// +//***************************************************************************** +#define USB_STATUS_SELF_PWR 0x0001 // Currently self powered. +#define USB_STATUS_BUS_PWR 0x0000 // Currently bus-powered. +#define USB_STATUS_PWR_M 0x0001 // Mask for power mode. +#define USB_STATUS_REMOTE_WAKE 0x0002 // Remote wake-up is currently enabled. + + +//***************************************************************************** +// +//! This structure describes the USB configuration descriptor as defined in +//! USB 2.0 specification section 9.6.3. This structure also applies to the +//! USB other speed configuration descriptor defined in section 9.6.4. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. All configuration descriptors + //! are 9 bytes long. + // + uint8_t bLength; + + // + //! The type of the descriptor. For a configuration descriptor, this will + //! be USB_DTYPE_CONFIGURATION (2). + // + uint8_t bDescriptorType; + + // + //! The total length of data returned for this configuration. This + //! includes the combined length of all descriptors (configuration, + //! interface, endpoint and class- or vendor-specific) returned for this + //! configuration. + // + uint16_t wTotalLength; + + // + //! The number of interface supported by this configuration. + // + uint8_t bNumInterfaces; + + // + //! The value used as an argument to the SetConfiguration standard request + //! to select this configuration. + // + uint8_t bConfigurationValue; + + // + //! The index of a string descriptor describing this configuration. + // + uint8_t iConfiguration; + + // + //! Attributes of this configuration. + // + uint8_t bmAttributes; + + // + //! The maximum power consumption of the USB device from the bus in this + //! configuration when the device is fully operational. This is expressed + //! in units of 2mA so, for example, 100 represents 200mA. + // + uint8_t bMaxPower; +} +PACKED tConfigDescriptor; + +//***************************************************************************** +// +// Flags used in constructing the value assigned to the field +// tConfigDescriptor.bmAttributes. Note that bit 7 is reserved and must be set +// to 1. +// +//***************************************************************************** +#define USB_CONF_ATTR_PWR_M 0xC0 + +#define USB_CONF_ATTR_SELF_PWR 0xC0 +#define USB_CONF_ATTR_BUS_PWR 0x80 +#define USB_CONF_ATTR_RWAKE 0xA0 + +//***************************************************************************** +// +// Feature Selectors (tUSBRequest.wValue) passed on USBREQ_CLEAR_FEATURE and +// USBREQ_SET_FEATURE. +// +//***************************************************************************** +#define USB_FEATURE_EP_HALT 0x0000 // Endpoint halt feature. +#define USB_FEATURE_REMOTE_WAKE 0x0001 // Remote wake feature, device only. +#define USB_FEATURE_TEST_MODE 0x0002 // Test mode + +//***************************************************************************** +// +//! This structure describes the USB string descriptor for index 0 as defined +//! in USB 2.0 specification section 9.6.7. Note that the number of language +//! IDs is variable and can be determined by examining bLength. The number of +//! language IDs present in the descriptor is given by ((bLength - 2) / 2). +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. This value will vary + //! depending upon the number of language codes provided in the descriptor. + // + uint8_t bLength; + + // + //! The type of the descriptor. For a string descriptor, this will be + //! USB_DTYPE_STRING (3). + // + uint8_t bDescriptorType; + + // + //! The language code (LANGID) for the first supported language. Note that + //! this descriptor may support multiple languages, in which case, the + //! number of elements in the wLANGID array will increase and bLength will + //! be updated accordingly. + // + uint16_t wLANGID[1]; +} +PACKED tString0Descriptor; + +//***************************************************************************** +// +//! This structure describes the USB string descriptor for all string indexes +//! other than 0 as defined in USB 2.0 specification section 9.6.7. +// +//***************************************************************************** +typedef struct +{ + // + //! The length of this descriptor in bytes. This value will be 2 greater + //! than the number of bytes comprising the UNICODE string that the + //! descriptor contains. + // + uint8_t bLength; + + // + //! The type of the descriptor. For a string descriptor, this will be + //! USB_DTYPE_STRING (3). + // + uint8_t bDescriptorType; + + // + //! The first byte of the UNICODE string. This string is not NULL + //! terminated. Its length (in bytes) can be computed by subtracting 2 + //! from the value in the bLength field. + // + uint8_t bString; +} +PACKED tStringDescriptor; + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// Some standard USB class definitions. +// +//***************************************************************************** +#define USB_CLASS_APP_SPECIFIC 0xfe +#define USB_CLASS_VEND_SPECIFIC 0xff + +//***************************************************************************** +// +// The following are values that are returned from USBEndpointStatus(). The +// USB_HOST_* values are used when the USB controller is in host mode and the +// USB_DEV_* values are used when the USB controller is in device mode. +// +//***************************************************************************** +#define USB_DEV_RX_SENT_STALL 0x00400000 // Stall was sent on this endpoint +#define USB_DEV_RX_DATA_ERROR 0x00080000 // CRC error on the data +#define USB_DEV_RX_OVERRUN 0x00040000 // OUT packet was not loaded due to + // a full FIFO +#define USB_DEV_RX_FIFO_FULL 0x00020000 // RX FIFO full +#define USB_DEV_RX_PKT_RDY 0x00010000 // Data packet ready +#define USB_DEV_TX_NOT_COMP 0x00000080 // Large packet split up, more data + // to come +#define USB_DEV_TX_SENT_STALL 0x00000020 // Stall was sent on this endpoint +#define USB_DEV_TX_UNDERRUN 0x00000004 // IN received with no data ready +#define USB_DEV_TX_FIFO_NE 0x00000002 // The TX FIFO is not empty +#define USB_DEV_TX_TXPKTRDY 0x00000001 // Transmit still being transmitted +#define USB_DEV_EP0_SETUP_END 0x00000010 // Control transaction ended before + // Data End seen +#define USB_DEV_EP0_SENT_STALL 0x00000004 // Stall was sent on this endpoint +#define USB_DEV_EP0_IN_PKTPEND 0x00000002 // Transmit data packet pending +#define USB_DEV_EP0_OUT_PKTRDY 0x00000001 // Receive data packet ready + +//***************************************************************************** +// +// This value specifies the maximum size of transfers on endpoint 0 as 64 +// bytes. This value is fixed in hardware as the FIFO size for endpoint 0. +// +//***************************************************************************** +#define MAX_PACKET_SIZE_EP0 64 + +//***************************************************************************** +// +// These values are used to indicate which endpoint to access. +// +//***************************************************************************** +#define USB_EP_0 0x00000000 // Endpoint 0 +#define NUM_USB_EP 4 // Number of supported endpoints + +//***************************************************************************** +// +// These macros allow conversion between 0-based endpoint indices and the +// USB_EP_x values required when calling various USB APIs. +// +//***************************************************************************** +#define INDEX_TO_USB_EP(x) ((x) << 4) +#define USB_EP_TO_INDEX(x) ((x) >> 4) + +//***************************************************************************** +// +// The following are values that can be passed to USBEndpointDataSend() as the +// ui32TransType parameter. +// +//***************************************************************************** +#define USB_TRANS_OUT 0x00000102 // Normal OUT transaction +#define USB_TRANS_IN 0x00000102 // Normal IN transaction +#define USB_TRANS_IN_LAST 0x0000010a // Final IN transaction (for + // endpoint 0 in device mode) +#define USB_TRANS_SETUP 0x0000110a // Setup transaction (for endpoint + // 0) +#define USB_TRANS_STATUS 0x00000142 // Status transaction (for endpoint + // 0) + +//***************************************************************************** +// +// Function prototype for any standard USB request. +// +//***************************************************************************** +typedef void (* tStdRequest)(tUSBRequest *psUSBRequest); + +//***************************************************************************** +// +// Data structures defined in bl_usb.c but referenced elsewhere. +// +//***************************************************************************** +extern const uint8_t g_pui8DFUConfigDescriptor[]; +extern const uint8_t g_pui8DFUDeviceDescriptor[]; +extern const uint8_t * const g_ppui8StringDescriptors[]; + +#define NUM_STRING_DESCRIPTORS 4 + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes of the various USB handler functions. +// +//***************************************************************************** +extern void HandleRequests(tUSBRequest *psUSBRequest); +extern void HandleConfigChange(uint32_t ui32Info); +extern void HandleEP0Data(uint32_t ui32Info); +extern void HandleReset(void); +extern void HandleDisconnect(void); +extern void HandleSetAddress(void); + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern void USBDevEndpoint0DataAck(bool bIsLastPacket); +extern int32_t USBEndpoint0DataGet(uint8_t *pui8Data, uint32_t *pui32Size); +extern int32_t USBEndpoint0DataPut(uint8_t *pui8Data, uint32_t ui32Size); +extern int32_t USBEndpoint0DataSend(uint32_t ui32TransType); +extern void USBBLInit(void); +extern void USBBLStallEP0(void); +extern void USBBLRequestDataEP0(uint8_t *pui8Data, uint32_t ui32Size); +extern void USBBLSendDataEP0(uint8_t *pui8Data, uint32_t ui32Size); +extern void USBDeviceEnumHandler(void); +extern void USBDeviceEnumResetHandler(void); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __BL_USBFUNCS_H__ diff --git a/boot_loader/readme.txt b/boot_loader/readme.txt new file mode 100644 index 0000000..6c37d28 --- /dev/null +++ b/boot_loader/readme.txt @@ -0,0 +1,28 @@ +Boot Loader + +The boot loader is a small piece of code that can be programmed at the +beginning of flash to act as an application loader as well as an update +mechanism for an application running on a Tiva microcontroller, utilizing +either UART0, I2C0, SSI0, Ethernet or USB. The capabilities of the boot loader +are configured via the bl_config.h include file (which is located in the +application directory, not in the boot loader source directory). + +------------------------------------------------------------------------------- + +Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +Software License Agreement + +Texas Instruments (TI) is supplying this software for use solely and +exclusively on TI's microcontroller products. The software is owned by +TI and/or its suppliers, and is protected under applicable copyright +laws. You may not combine this software with "viral" open-source +software in order to form a larger program. + +THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, FOR ANY REASON WHATSOEVER. + +This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. diff --git a/boot_loader/uip-conf.h b/boot_loader/uip-conf.h new file mode 100644 index 0000000..5c1d770 --- /dev/null +++ b/boot_loader/uip-conf.h @@ -0,0 +1,108 @@ +//***************************************************************************** +// +// uip-conf.h - uIP configuration for the boot loader. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __UIP_CONF_H__ +#define __UIP_CONF_H__ + +//***************************************************************************** +// +// This typedef defines the 8-bit type used throughout uIP. +// +//***************************************************************************** +typedef uint8_t u8_t; + +//***************************************************************************** +// +// This typedef defines the 16-bit type used throughout uIP. +// +//***************************************************************************** +typedef uint16_t u16_t; + +//***************************************************************************** +// +// This typedef defines the dataype used for keeping statistics in uIP. +// +//***************************************************************************** +typedef uint16_t uip_stats_t; + +//***************************************************************************** +// +// Turn off TCP support. +// +//***************************************************************************** +#define UIP_CONF_TCP 0 + +//***************************************************************************** +// +// Turn on UDP support. +// +//***************************************************************************** +#define UIP_CONF_UDP 1 + +//***************************************************************************** +// +// Only support a single UDP connection. +// +//***************************************************************************** +#define UIP_CONF_UDP_CONNS 1 + +//***************************************************************************** +// +// Only support a single entry in the ARP table. +// +//***************************************************************************** +#define UIP_CONF_ARPTAB_SIZE 1 + +//***************************************************************************** +// +// Set the size of the uIP packet data buffer. +// +//***************************************************************************** +#define UIP_CONF_BUFFER_SIZE 1600//700 + +//***************************************************************************** +// +// Enable UDP broadcast support. +// +//***************************************************************************** +#define UIP_CONF_BROADCAST 1 + +//***************************************************************************** +// +// Define a data type for the UDP application state. This is not used, but +// must be defined for uIP. +// +//***************************************************************************** +typedef uint32_t uip_udp_appstate_t; + +//***************************************************************************** +// +// The name of the function to be called when UDP packets arrive, or when the +// UDP periodic timer expires. +// +//***************************************************************************** +extern char BOOTPThread(void); +#define UIP_UDP_APPCALL BOOTPThread + +#endif // __UIP_CONF_H__ diff --git a/boot_loader/usbdfu.h b/boot_loader/usbdfu.h new file mode 100644 index 0000000..3ae00e1 --- /dev/null +++ b/boot_loader/usbdfu.h @@ -0,0 +1,420 @@ +//***************************************************************************** +// +// usbdfu.h - Definitions related to the USB Device Firmware Upgrade class. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __USBDFU_H__ +#define __USBDFU_H__ + +//***************************************************************************** +// +// DFU attributes as published in the functional descriptor. +// +//***************************************************************************** +#define DFU_ATTR_WILL_DETACH 0x08 +#define DFU_ATTR_MANIFEST_TOLERANT \ + 0x04 +#define DFU_ATTR_CAN_UPLOAD 0x02 +#define DFU_ATTR_CAN_DOWNLOAD 0x01 + +//***************************************************************************** +// +// The states that the DFU device can be in. These values are reported to +// the host in response to a USBD_DFU_REQUEST_GETSTATE request. +// +//***************************************************************************** +typedef enum +{ + STATE_APP_IDLE = 0, + STATE_APP_DETACH, + STATE_IDLE, + STATE_DNLOAD_SYNC, + STATE_DNBUSY, + STATE_DNLOAD_IDLE, + STATE_MANIFEST_SYNC, + STATE_MANIFEST, + STATE_MANIFEST_WAIT_RESET, + STATE_UPLOAD_IDLE, + STATE_ERROR +} +tDFUState; + +//***************************************************************************** +// +// The current error status of the DFU device. These values are reported to +// the host in response to a USBD_DFU_REQUEST_GETSTATUS request. +// +//***************************************************************************** +typedef enum +{ + STATUS_OK = 0, + STATUS_ERR_TARGET, + STATUS_ERR_FILE, + STATUS_ERR_WRITE, + STATUS_ERR_ERASE, + STATUS_ERR_CHECK_ERASED, + STATUS_ERR_PROG, + STATUS_ERR_VERIFY, + STATUS_ERR_ADDRESS, + STATUS_ERR_NOTDONE, + STATUS_ERR_FIRMWARE, + STATUS_ERR_VENDOR, + STATUS_ERR_USBR, + STATUS_ERR_POR, + STATUS_ERR_UNKNOWN, + STATUS_ERR_STALLEDPKT +} +tDFUStatus; + +//***************************************************************************** +// +// The descriptor type for the DFU functional descriptor. +// +//***************************************************************************** +#define USB_DFU_FUNC_DESCRIPTOR_TYPE 0x21 + +//***************************************************************************** +// +// The subclass identifier for DFU as reported to the host in the +// bInterfaceSubClass field of the DFU interface descriptor. +// +//***************************************************************************** +#define USB_DFU_SUBCLASS 0x01 + +//***************************************************************************** +// +// The protocol identifier for DFU as reported to the host in the +// bInterfaceProtocol field of the DFU interface descriptor. +// +//***************************************************************************** +#define USB_DFU_PROTOCOL 0x02 +#define USB_DFU_RUNTIME_PROTOCOL 0x01 + +//***************************************************************************** +// +// DFU class-specific request identifiers. +// +//***************************************************************************** +#define USBD_DFU_REQUEST_DETACH 0 +#define USBD_DFU_REQUEST_DNLOAD 1 +#define USBD_DFU_REQUEST_UPLOAD 2 +#define USBD_DFU_REQUEST_GETSTATUS 3 +#define USBD_DFU_REQUEST_CLRSTATUS 4 +#define USBD_DFU_REQUEST_GETSTATE 5 +#define USBD_DFU_REQUEST_ABORT 6 + +//***************************************************************************** +// +// Request 1KB blocks from the host. This value is published in the USB +// functional descriptor. +// +//***************************************************************************** +#define DFU_TRANSFER_SIZE 1024 + +//***************************************************************************** +// +// TIVA-specific request identifier. This is used to determine whether +// the target device supports our DFU command protocol. It is expected that +// a device not supporting our extensions will stall this request. This +// request is only supported while the DFU device is in STATE_IDLE. +// +// An IN request containing the following parameters will result in the device +// sending back a tDFUQueryTIVAProtocol structure indicating that +// TIVA extensions are supported. The actual values in wValue and wIndex +// have no meaning other than to act as markers in the unlikely event that +// another DFU device also chooses to use request ID 0x42 for some other +// purpose. +// +// wValue - 0x23(REQUEST_TIVA_VALUE) +// wIndex - Interface number +// wLength - sizeof(tDFUQueryTIVAProtocol) +// +//***************************************************************************** +#define USBD_DFU_REQUEST_TIVA 0x42 +#define REQUEST_TIVA_VALUE 0x23 + +#define DFU_PROTOCOL_USBLIB_MARKER \ + 0x4C4D +#define DFU_PROTOCOL_USBLIB_VERSION_1 \ + 0x0001 + +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +// The structure sent to the host when a valid USBD_DFU_REQUEST_TIVA is +// received while the DFU device is in idle state. +// +//***************************************************************************** +typedef struct +{ + uint16_t ui16Marker; // DFU_PROTOCOL_USBLIB_MARKER + uint16_t ui16Version; // DFU_PROTOCOL_USBLIB_VERSION_1 +} +PACKED tDFUQueryTIVAProtocol; + +//***************************************************************************** +// +// Structure sent to the host in response to USBD_DFU_REQUEST_GETSTATUS. +// +//***************************************************************************** +typedef struct +{ + uint8_t bStatus; + uint8_t bwPollTimeout[3]; + uint8_t bState; + uint8_t iString; +} +PACKED tDFUGetStatusResponse; + +//***************************************************************************** +// +// Firmware Download Commands +// +// The data passed on a USBD_DFU_REQUEST_DNLOAD request is comprised of a +// header which instructs the boot loader how to interpret the block and +// block-specific data. The following definitions relate to the download +// block headers. +// +//***************************************************************************** + +//***************************************************************************** +// +// Supported command identifiers +// +//***************************************************************************** +#define DFU_CMD_PROG 0x01 +#define DFU_CMD_READ 0x02 +#define DFU_CMD_CHECK 0x03 +#define DFU_CMD_ERASE 0x04 +#define DFU_CMD_INFO 0x05 +#define DFU_CMD_BIN 0x06 +#define DFU_CMD_RESET 0x07 + +//***************************************************************************** +// +// Generic download command header. +// +//***************************************************************************** +typedef struct +{ + uint8_t ui8Command; // Command identifier. + uint8_t pui8Data[7]; // Command-specific data elements. +} +PACKED tDFUDownloadHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_PROG command. +// +// This command is used to program a section of the flash with the binary data +// which immediately follows the header. The start address of the data is +// expressed as a 1KB block number so 0 would represent the bottom of flash +// (which, incidentally, the USB boot loader will not let you program) and 0x10 +// would represent address 16KB or 16384 (0x4000). The ui32Length field +// contains the total number of bytes of data in the following programming +// operation. The DFU device will not look for any command header on following +// USBD_DFU_REQUEST_DNLOAD requests until the operation is completed or +// aborted. +// +// By using this protocol, the DFU_CMD_PROG command header may be used as a +// simple header on the binary files to be sent to the DFU device for +// programming. If we enforce the requirement that the DFU_CMD_PROG header is +// applied to each USBD_DFU_REQUEST_DNLOAD (one per block), this means that the +// host-side DFU application must be aware of the underlying protocol and +// insert these headers dynamically during programming operations. This could +// be handled by post processing the binary to insert the headers at the +// appropriate points but this would then tie the binary structure to the +// chosen transfer size and break the operation if the transfer size were to +// change in the future. +// +//***************************************************************************** +typedef struct +{ + uint8_t ui8Command; // DFU_CMD_PROG + uint8_t ui8Reserved; // Reserved - set to 0x00. + uint16_t ui16StartAddr; // Block start address / 1024 + uint32_t ui32Length; // Total length, in bytes, of following data + // for the complete download operation. +} +PACKED tDFUDownloadProgHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_READ and DFU_CMD_CHECK commands. +// +// This command may be used to set the address range whose content will be +// returned on subsequent USBD_DFU_REQUEST_UPLOAD requests from the host. +// +// To read back a the contents of a region of flash, the host should send +// USBD_DFU_REQUEST_DNLOAD with ui8Command DFU_CMD_READ, ui16StartAddr set to +// the 1KB block start address and ui32Length set to the number of bytes to +// read. The host should then send one or more USBD_DFU_REQUEST_UPLOAD +// requests to receive the current flash contents from the configured +// addresses. Data returned will include an 8 byte DFU_CMD_PROG prefix +// structure unless the prefix has been disabled by sending a DFU_CMD_BIN +// command with the bBinary parameter set to 1. +// +// To check that a region of flash is erased, the DFU_CMD_CHECK command should +// be sent with ui16StartAddr and ui32Length set to describe the region to +// check. The host should then send a USBD_DFU_REQUEST_GETSTATUS. If the +// erase check was successful, the returned bStatus value will be STATUS_OK, +// otherwise it will be STATUS_ERR_CHECK_ERASED. Note that ui32Length passed +// must be a multiple of 4. If this is not the case, the value will be +// truncated before the check is performed. +// +//***************************************************************************** +typedef struct +{ + uint8_t ui8Command; // DFU_CMD_READ or DFU_CMD_CHECK + uint8_t ui8Reserved; // Reserved - write to 0 + uint16_t ui16StartAddr; // Block start address / 1024 + uint32_t ui32Length; // The number of bytes of data to read back or + // check. +} +PACKED tDFUDownloadReadCheckHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_ERASE command. +// +// This command may be used to erase a number of flash blocks. The address of +// the first block to be erased is passed in ui16StartAddr with ui16NumBlocks +// containing the number of blocks to be erased from this address. The block +// size of the device may be determined using the DFU_CMD_INFO command. +// +//***************************************************************************** +typedef struct +{ + uint8_t ui8Command; // DFU_CMD_ERASE + uint8_t ui8Reserved; // Reserved - set to 0 + uint16_t ui16StartAddr; // Block start address / 1024 + uint16_t ui16NumBlocks; // The number of blocks to erase + uint8_t pui8Reserved2[2]; // Reserved - set to 0 +} +PACKED tDFUDownloadEraseHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_INFO command. +// +// This command may be used to query information about the connected device. +// After sending the command, the information is returned on the next +// USBD_DFU_REQUEST_UPLOAD request. +// +//***************************************************************************** +typedef struct +{ + uint8_t ui8Command; // DFU_CMD_INFO + uint8_t pui8Reserved[7]; // Reserved - set to 0 +} +PACKED tDFUDownloadInfoHeader; + +//***************************************************************************** +// +// Header for the DFU_CMD_BIN command. +// +// This command may be used to set the format of uploaded data. By default, +// images read using USBD_DFU_REQUEST_UPLOAD are formatted with the appropriate +// header to allow the same image to be flashed back to the device and have it +// located at the address from which it originated. This is a requirement of +// the DFU class specification (section 6.2 "the uploaded image must be +// usable in a subsequent download") but may not be helpful in some cases where +// the application wishes to receive only the binary image from flash. To +// instruct the DFU device to omit the position and size header, send this +// command with the bBinary field set to \b true prior to issuing a +// USBD_DFU_REQUEST_UPLOAD for image data. The format choice remains in effect +// until the command is sent once again with bBinary set to \b false. +// +// Note that the format choice affects only image data sent and not responses +// read via USBD_DFU_REQUEST_UPLOAD following software-specific commands such +// as DFU_CMD_INFO. +// +//***************************************************************************** +typedef struct +{ + uint8_t ui8Command; // DFU_CMD_BIN + uint8_t bBinary; // Set to true to omit image header or false + // to include it (the default) + uint8_t pui8Reserved[6]; // Reserved - set to 0 +} +PACKED tDFUDownloadBinHeader; + +//***************************************************************************** +// +// The DFU_CMD_RESET command uses a tDFUDownloadHeader structure since +// only the ui8Command field is important. This command causes an immediate +// reset of the the target board. +// +//***************************************************************************** + +//***************************************************************************** +// +//! Payload returned in response to the DFU_CMD_INFO command. +//! +//! This is structure is returned in response to the first +//! USBD_DFU_REQUEST_UPLOAD request following a DFU_CMD_INFO command. +// +//***************************************************************************** +typedef struct +{ + // + //! The size of a flash block in bytes. + // + uint16_t ui16FlashBlockSize; + + // + //! The number of blocks of flash in the device. Total flash size is + //! ui16NumFlashBlocks * ui16FlashBlockSize. + // + uint16_t ui16NumFlashBlocks; + + // + //! Information on the part number, family, version and package as read + //! from SYSCTL register DID1. + // + uint32_t ui32PartInfo; + + // + //! Information on the part class and revision as read from SYSCTL DID0. + // + uint32_t ui32ClassInfo; + + // + //! Address 1 byte above the highest location the boot loader can access. + // + uint32_t ui32FlashTop; + + // + //! Lowest address the boot loader can write or erase. + // + uint32_t ui32AppStartAddr; +} +PACKED tDFUDeviceInfo; + +#ifdef ewarm +#pragma pack() +#endif + +#endif // __USBDFU_H__ diff --git a/nfclib/debug.h b/nfclib/debug.h new file mode 100644 index 0000000..ad0b20c --- /dev/null +++ b/nfclib/debug.h @@ -0,0 +1,74 @@ +//***************************************************************************** +// +// debug.h - macro for debug output to terminal. +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +//***************************************************************************** +// +// Debugging Macros from debug.h in NFCLib. These provide extra debug support. +// comment out the undef's if you want to use them. +// +// DEBUG_PRINTF enables UART messages +// DEBUG enables ASSERT statements for line / file specific information. +// +//***************************************************************************** +//#define DEBUG_PRINT +//#define DEBUG + +#ifdef DEBUG_PRINT +#include "utils/uartstdio.h" +#define DebugPrintf(...) UARTprintf(__VA_ARGS__) +#else +#define DebugPrintf(...) +#endif + +//***************************************************************************** +// +// Prototype for the function that is called when an invalid argument is passed +// to an API. This is only used when doing a DEBUG build. +// +//***************************************************************************** +extern void __error__(char *pcFilename, uint32_t ui32Line); + +//***************************************************************************** +// +// The ASSERT macro, which does the actual assertion checking. Typically, this +// will be for procedure arguments. +// +//***************************************************************************** +#ifdef DEBUG +#define ASSERT(expr) do \ + { \ + if(!(expr)) \ + { \ + __error__(__FILE__, __LINE__); \ + } \ + } \ + while(0) +#else +#define ASSERT(expr) +#endif + +#endif //__DEBUG_H__ diff --git a/nfclib/directmode.c b/nfclib/directmode.c new file mode 100644 index 0000000..4aef39e --- /dev/null +++ b/nfclib/directmode.c @@ -0,0 +1,1059 @@ +//***************************************************************************** +// +// directmode.h - Direct mode communications. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_timer.h" +#include "inc/hw_gpio.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/timer.h" +#include "driverlib/sysctl.h" +#include "driverlib/ssi.h" +#include "driverlib/interrupt.h" +#include "ssitrf79x0.h" +#include "trf79x0_hw.h" +#include "directmode.h" +#include "trf79x0.h" +#include "iso14443a.h" + +#if defined(rvmdk) +#define inline __inline +#endif + +//***************************************************************************** +// +// Direct mode 0 implementation for ISO 14443 A. +// +// This file implements transmission of raw ISO 14443-2 modulation type A +// formatted bit streams at ~106kbit/s on the TRF79x0 in direct mode 0. +// The functionality will generate and receive the correct SOF and EOF markers +// but everything else (parity and CRC) is the responsibility of the calling +// code. iso14443.c has functions ISO14443ACalculateParity()/ +// ISO14443ACheckParity()/ ISO14443ACalculateCRC()/ ISO14443ACheckCRC() for +// this purpose. Since it transmits and receives raw bit streams it can also +// be used for MIFARE Classic communication which needs incorrect parity bits. +// +// The implementation uses one timer (TIMER 0) for timing, so this can not +// be used by anything else, or at least must be set up again before each +// use, with DirectModeInit(). +// +// DirectModeEnable() and DirectModeDisable() keep track of state and will +// not re-enable the mode if it was already active. DirectModeIsEnabled() +// can be used to query the state. While direct mode is active no other +// functionality on the TRF79x0 should be accessed and its IRQ is disabled. +// +// \note DirectModeDisable() implements a workaround for an apparent bug in +// the TRF7960 which will perform a soft reset of the TRF7960. In order to +// not leave the chip in an entirely unexpected state it will then call +// ISO14443ASetupRegisters() to prepare the chip for ISO 14443 A operation +// (which is most likely what you'll be using together with this code). If +// you do not want ISO 14443 A operation you need to restore the necessary +// settings yourself. +// +//***************************************************************************** + +// +// Keep track whether direct mode is enabled. +// +static int g_iDirectModeEnabled = 0; + +// +// Receive timeout. This is a loop count, not as reliable as SysCtlDelay(), +// but not really critical. +// +#define DIRECTMODE_RECEIVE_TIMEOUT 30000 + +// +// Use timer 0 for direct mode timing. +// +#define DIRECTMODE_TIMER_PORT TIMER0_BASE +#define DIRECTMODE_TIMER_SYSCTL SYSCTL_PERIPH_TIMER0 + +//***************************************************************************** +// +// The macros below do exactly the same as GPIOPinWrite() and GPIOPinRead() +// from gpio.c and TimerIntStatus() and TimerIntClear() from timer.c, just +// without the function call and with compile time argument optimization. +// +//***************************************************************************** +#define GPIOPinWrite(ulPort, ucPins, ucVal) \ + (HWREG((ulPort) + (GPIO_O_DATA + ((ucPins) << 2))) = (ucVal)) + +#define GPIOPinRead(ulPort, ucPins) \ + (HWREG((ulPort) + (GPIO_O_DATA + ((ucPins) << 2)))) + +#define TimerIntStatus(ulBase, bMasked) \ + ((bMasked) ? HWREG((ulBase) + TIMER_O_MIS) : \ + HWREG((ulBase) + TIMER_O_RIS)) + +//***************************************************************************** +// +// Shortcut to mimic TimerIntClear() function in DriverLib without the call +// overhead. +// +//***************************************************************************** +#define TimerIntClear(ulBase, ulIntFlags) \ + HWREG((ulBase) + TIMER_O_ICR) = (ulIntFlags) + +//***************************************************************************** +// +// Set timer value. +// This function is missing from the StellarisWare timer API, so here it is +// as a macro +// +//***************************************************************************** +#define TimerValueSet(ulBase, ulTimer, ulValue) \ + HWREG((ulBase) + ((ulTimer)==TIMER_A ? TIMER_O_TAV : TIMER_O_TBV)) = \ + (ulValue) + +//***************************************************************************** +// +// This macro enables modulation/disables the field. +// +//***************************************************************************** +#define MODOn() \ + GPIOPinWrite(TRF79X0_MOD_BASE, TRF79X0_MOD_PIN, \ + TRF79X0_MOD_PIN) + +//***************************************************************************** +// +// This macro disables modulation/enables the field. +// +//***************************************************************************** +#define MODOff() \ + GPIOPinWrite(TRF79X0_MOD_BASE, TRF79X0_MOD_PIN, 0) + +//***************************************************************************** +// +// Waits for the next one-eighth bit interval, depends on timer B being set up +// for one-eighth bit intervals. +// +//***************************************************************************** +#define WaitEighthBit() \ +{ \ + while(!(TimerIntStatus(DIRECTMODE_TIMER_PORT, 0) & TIMER_TIMB_TIMEOUT)) \ + { \ + }; \ + \ + TimerIntClear(DIRECTMODE_TIMER_PORT, TIMER_TIMB_TIMEOUT); \ +} + +//***************************************************************************** +// +// Waits for the next quarter bit interval, depends on timer A being set up +// for quarter bit intervals. +// +//***************************************************************************** +#define WaitQuarterBit() \ +{ \ + while(!(TimerIntStatus(DIRECTMODE_TIMER_PORT, 0) & TIMER_TIMA_TIMEOUT)) \ + { \ + } \ + TimerIntClear(DIRECTMODE_TIMER_PORT, TIMER_TIMA_TIMEOUT); \ +} + +//***************************************************************************** +// +// Modulation sequences, names from ISO 14443-2. +// +// All these sequences end at 0.75 bit period and start +// somewhere before 1 bit period. This way they can be freely combined +// for an overall rate of one sequence per bit period, and give less than +// 0.25 bit periods for computation. +// +//***************************************************************************** + +//***************************************************************************** +// +// X: pulse after half-bit. +// +// - Wait 1/4 bit period for previous sequence to get to the start of this bit. +// - Wait 1/4 bit period. +// - Wait 1/4 bit period to get to 1/2 bit period. +// - Set MOD bit active. +// - Wait 1/4 bit period to get to 3/4 bit period. +// - Set MOD bit inactive. +// +//***************************************************************************** +#define SequenceX() \ + WaitQuarterBit(); \ + WaitQuarterBit(); \ + WaitQuarterBit(); \ + MODOn(); \ + WaitQuarterBit(); \ + MODOff(); + +//***************************************************************************** +// +// Y: This sequence just waits out a full bit period with no other toggle. +// +// - Wait 1/4 bit period for previous sequence to get to the start of this bit. +// - Wait 1/4 bit period. +// - Wait 1/4 bit period to get to 1/2 bit period. +// - Wait 1/4 bit period to get to 3/4 bit period. +// +//***************************************************************************** +#define SequenceY() \ + WaitQuarterBit(); \ + WaitQuarterBit(); \ + WaitQuarterBit(); \ + WaitQuarterBit(); + +//***************************************************************************** +// +// Z: Mode pulse at start of bit period. +// +// - Wait 1/4 bit period for previous sequence to get to the start of this bit. +// - Set MOD bit active. +// - Wait 1/4 bit period. +// - Set MOD bit inactive. +// - Wait 1/4 bit period to get to 1/2 bit period. +// - Wait 1/4 bit period to get to 3/4 bit period. +// +//***************************************************************************** +#define SequenceZ() \ + WaitQuarterBit(); \ + MODOn(); \ + WaitQuarterBit(); \ + MODOff(); \ + WaitQuarterBit(); \ + WaitQuarterBit(); + +//***************************************************************************** +// +// Set up timers and GPIO port for direct mode operation. +// +// This sets up GPTM 0 timer A for quarter bit periods (used in sending) +// and timer B for one-eighth bit periods (used in receiving). +// +//***************************************************************************** +void +DirectModeInit(void) +{ + // + // Enable GPIO port A for bit-banging receive. + // + SysCtlPeripheralEnable(TRF79X0_RX_PERIPH); + SysCtlPeripheralEnable(TRF79X0_EN_PERIPH); + SysCtlPeripheralEnable(TRF79X0_MOD_PERIPH); + SysCtlPeripheralEnable(TRF79X0_IRQ_PERIPH); + + // + // Enable and configure timer in periodic up mode + // + SysCtlPeripheralEnable(DIRECTMODE_TIMER_SYSCTL); + TimerConfigure(DIRECTMODE_TIMER_PORT, + TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PERIODIC_UP | + TIMER_CFG_B_PERIODIC_UP); + + // + // Configure timer max value for an fc/32 = 13.56MHz/32 = quarter bit + // at ~106kHz. This means that the timer must count up to + // SysClk/(13.56MHz/32) = (32*SysClk)/13.56MHz. This comes down to 117.99 + // at 50MHz. The error at 50MHz is negligible, but at other frequencies or + // in the general case a fractional logic might be needed. + // Note that the argument for TimerLoadSet is actually the desired divisor + // minus 1. 117.99 would round to 118, so the argument must be 117. + // However since integer calculation is truncating and not rounding this is + // directly the result of the division. Should a different frequency be + // used where the result of the division is not also the rounded result of + // the division minus 1 then proper rounding logic must be added. + // + TimerLoadSet(DIRECTMODE_TIMER_PORT, TIMER_A, + ((SysCtlClockGet() * 32) / 13560000)); + + // + // Configure Timer B for fc/16 = one eighth bit at ~106kHz. Same + // considerations as above apply. + // + TimerLoadSet(DIRECTMODE_TIMER_PORT, TIMER_B, + ((SysCtlClockGet() * 16) / 13560000)); +} + +//***************************************************************************** +// +// Dual use send code for direct mode. Can either accept an opaque bit stream +// ( (iMode && DIRECT_MODE_SEND_MASK) == DIRECT_MODE_SEND_OPAQUE ) or +// structured bytes with parity (... DIRECT_MODE_SEND_PARITY), e.q. as +// parity_data_t. In the first case uiBytes gives the number of opaque 8 +// bit units to send (e.g. sizeof(*pvBuffer) == uiBytes + (uiBits > 0 ? +// 1 : 0) ), in the second case it's the number of logical bytes (since each +// logical byte is encoded as a 16bit word the buffer size must be twice as +// big). In both cases uiBits gives the number of least significant bits +// that should additionally be sent. +// +//***************************************************************************** +static inline void +DirectModeSend(int iMode, void const *pvBuffer, unsigned int uiBytes, + unsigned int uiBits) +{ + // + // We'll keep the current pointer as an 8-bit value and the current byte as + // an 16-bit value in any case. In parity mode we'll arrange the pointer + // movement and uiCurrentByte assignment specially. + // + unsigned char const *pucCurrent; + unsigned short usPos, usCurrentByte; + unsigned char ucLastBit, ucCurrentBit, ucBitsRemain; + + // + // Initialize the byte and bit position. + // + usPos = 0; + ucLastBit = 0; + + iMode = iMode & DIRECT_MODE_SEND_MASK; + + // + // Create a byte pointer to use with the rest of this function. + // + pucCurrent = pvBuffer; + + // + // Set the MOD pin inactive. + // + MODOff(); + + // + // Start the timer. + // + TimerEnable(DIRECTMODE_TIMER_PORT, TIMER_A); + + // + // SOF. + // + SequenceZ(); + + while(usPos++ < uiBytes) + { + // + // Prepare the bit counter and value for this byte for either + // 8 bits per byte or 9 bits per 16 bit word. + // + if(iMode == DIRECT_MODE_SEND_OPAQUE) + { + ucBitsRemain = 8; + usCurrentByte = *pucCurrent; + } + else + { + ucBitsRemain = 9; + usCurrentByte = pucCurrent[0] | (pucCurrent[1] << 8); + } + + // + // Send the bits of this byte. + // + do + { + ucCurrentBit = usCurrentByte & 0x1; + + if(ucCurrentBit) + { + // + // Transfer a 1 Bit. + // + SequenceX(); + } + else + { + // + // Transfer a 0-Bit, encoded differently depending on if this + // was the last bit. + // + if(ucLastBit) + { + SequenceY(); + } + else + { + SequenceZ(); + } + } + + // + // Shift to next bit. + // + usCurrentByte >>= 1; + + ucLastBit = ucCurrentBit; + } + while(--ucBitsRemain > 0); + + // + // Increment the data pointer by either a byte or one 16 bit word. + // + pucCurrent += (iMode == DIRECT_MODE_SEND_OPAQUE) ? 1 : 2; + } + + // + // This is the same as above for the possibly remaining fractional byte. + // + if(uiBits > 0) + { + ucBitsRemain = uiBits; + usCurrentByte = *pucCurrent; + + // + // If sending parity then or in the parity. + // + if(iMode == DIRECT_MODE_SEND_PARITY) + { + usCurrentByte |= pucCurrent[-1] << 8; + } + + do + { + ucCurrentBit = usCurrentByte & 0x1; + + // + // Transfer a 1 Bit. + // + if(ucCurrentBit) + { + SequenceX(); + } + else + { + // + // Transfer a 0-Bit, encoded differently depending on if this + // was the last bit. + // + if(ucLastBit) + { + SequenceY(); + } + else + { + SequenceZ(); + } + } + + // + // Shift to next bit. + // + usCurrentByte >>= 1; + ucLastBit = ucCurrentBit; + } + while(--ucBitsRemain > 0); + } + + // + // EOF is either a 0 or a Y. + // + if(ucLastBit) + { + SequenceY(); + } + else + { + SequenceZ(); + } + + SequenceY(); + + // + // Disable the timer and return. + // + TimerDisable(DIRECTMODE_TIMER_PORT, TIMER_A); +} + +//***************************************************************************** +// +// Dual-use receive code for direct mode 0. Similar to the send code can +// either output an opaque bitstream (DIRECT_MODE_RECV_OPAQUE), or bytes with +// associated parity bits (DIRECT_MODE_RECV_PARITY). +// +//***************************************************************************** +static void +DirectModeReceive(int iMode, void *pvBuffer, unsigned int *puiBytes, + unsigned int *puiBits) +{ + unsigned int uiMaxBytes, uiCountBytes, uiCountBits; + int iCurrentBitVal, iLastBitVal, iCount, iHaveSOF; + unsigned char *pucCurrent; + unsigned int uiCurrentByte; + unsigned int uiBitsRemain; + int iTimeout; + + // + // Signal description: The input on MISO will start out low + // and then change to the sub carrier data stream which is either + // high, or high-low-high with a frequency of 848kHz. Exactly one + // half bit will be all high and one half bit will be alternating. + // + + // Reception methodology: Use the IRQ logic as an edge detector. + // Configure the GPIO pin for edge triggered interrupts (the interrupt + // will not actually be enabled, so no handler will be called). Clear + // the interrupt before each sampling interval and check its unmasked + // status afterwards. + // + // The reception may not be perfectly aligned to the bit clock, in that + // case the edges will dominate the high signal, e.g. even if there is + // just one edge in a sampling period the complete period will read as + // "edges present". Look for changes in the sampling result to decode the + // manchester encoded stream: there will be a change in the middle of each + // bit (and the direction of that change signifies the bit value) and there + // might be change at the start/end of a bit. One bit is 8 sampling + // periods, so expected is a change every 8 periods. If a change occurs + // after 4 periods this is at the start/end of a bit and should be ignored + // (and the counter kept incrementing). When keeping in mind that the + // subcarier edges may dominate the steady signal that means that there + // must have been at least 7 periods since a recognized edge to recognize a + // subcarrier-steady edge as a data edge, or 6 periods since a recognized + // edge to recognize a steady-subcarrier edge as a data edge. + // + + // + // Pointer to the next storage location. + // + pucCurrent = pvBuffer; + + // + // Currently sampled data unit (either 8 or 9 bits). + // + uiCurrentByte = 0; + + // + // Set up edge detection. + // + GPIOIntTypeSet(TRF79X0_RX_BASE, TRF79X0_RX_PIN, GPIO_BOTH_EDGES); + + // + // Make sure that data parameters are correct before using them. + // + if((pvBuffer == NULL) || (puiBytes == NULL) || (*puiBytes == 0)) + { + return; + } + + // + // Maximal number of bytes to receive, and count of bytes and count of bits + // received so far. + // + uiMaxBytes = *puiBytes; + uiCountBytes = 0; + uiCountBits = 0; + + // + // iCurrentBitVal contains the sampling result for the most recently ended + // sampling interval, while iLastBitVal is for the interval before that. + // Edges are detected by having iCurrentBitVal != iLastBitVal. + // + iCurrentBitVal = 0; + iLastBitVal = 0; + + // + // iCount contains the number of quarter bit intervals since the last + // recognized data edge. It is initialized with a half bit period + // at the start to immediately detect the data edge in the middle of + // the SOF bit, and afterwards incremented for each sampling period and + // reset to 0 when a data edge is detected. When an edge is ignored count + // will also be set to exactly a half bit period in order to guarantee + // that the next edge will be detected as a data edge. + // + iCount = 4; + + iMode = iMode & DIRECT_MODE_RECV_MASK; + + // + // Initialized the number of bits left in the data unit. + // + if(iMode == DIRECT_MODE_RECV_OPAQUE) + { + uiBitsRemain = 8; + } + else + { + uiBitsRemain = 9; + } + + // + // Ignore the first bit which is a start-of-frame indicator. + // + iHaveSOF = 0; + + // + // The signal starts out low, so wait for the rising edge. + // + GPIOIntClear(TRF79X0_RX_BASE, TRF79X0_RX_PIN); + { + // + // Initialize the timeout. + // + iTimeout = DIRECTMODE_RECEIVE_TIMEOUT; + + while(!(GPIOIntStatus(TRF79X0_RX_BASE, 0) & + TRF79X0_RX_PIN) && (iTimeout-- > 0)) + { + } + } + + // + // Set the timer to 0 and start it. + // + TimerValueSet(DIRECTMODE_TIMER_PORT, TIMER_B, 0); + TimerEnable(DIRECTMODE_TIMER_PORT, TIMER_B); + + // + // Reset edge detector. + // + GPIOIntClear(TRF79X0_RX_BASE, TRF79X0_RX_PIN); + + do + { + // + // Wait until the end of the current sampling interval. + // + WaitEighthBit(); + + // + // Copy over the sampling result to be processed, reset edge detector. + // + iCurrentBitVal = (GPIOIntStatus(TRF79X0_RX_BASE, 0) & + TRF79X0_RX_PIN); + + GPIOIntClear(TRF79X0_RX_BASE, TRF79X0_RX_PIN); + + // + // Check for a change in bit polarity. + // + if(iLastBitVal != iCurrentBitVal) + { + if(iLastBitVal) + { + // + // may be overly long. + // + if(iCount <= 6) + { + // + // ignore, but force iCount to sane value. + // + iCount = 4; + } + else + { + if(iHaveSOF) + { + // + // This edge is a 1 bit, add it to the current data + // unit. + // + uiBitsRemain--; + + uiCurrentByte |= 1 << uiCountBits; + + uiCountBits++; + } + else + { + iHaveSOF = 1; + } + + // + // Reset iCount. + // + iCount = 0; + } + } + else + { + // + // may be overly short + // + if(iCount <= 5) + { + // + // ignore, but force iCount to sane value. + // + iCount = 4; + } + else + { + if(iHaveSOF) + { + // + // This edge is a 0 bit, add it to the current data + // unit. + // + uiBitsRemain--; + uiCountBits++; + } + else + { + iHaveSOF = 1; + } + + // + // Reset iCount. + // + iCount = 0; + } + } + } + + // + // Increment number of one eighth bit periods since last recognized + // edge. + // + iCount++; + iLastBitVal = iCurrentBitVal; + + if(uiBitsRemain == 0) + { + // + // Store received data unit, advance pointer. + // + if(iMode == DIRECT_MODE_RECV_OPAQUE) + { + uiBitsRemain = 8; + *pucCurrent = uiCurrentByte; + pucCurrent += 1; + } + else + { + uiBitsRemain = 9; + pucCurrent[0] = uiCurrentByte & 0xff; + pucCurrent[1] = uiCurrentByte >> 8; + pucCurrent += 2; + } + + // + // Clear temporary store. + // + uiCurrentByte = 0; + uiCountBits = 0; + + // + // Increment counter, abort when the receive buffer is full. + // + uiCountBytes++; + if((uiCountBytes + 1) >= uiMaxBytes) + { + break; + } + } + + // + // More than 2 bit periods (16 eighth bit periods) since the last edge + // signify a time out, end of reception. + // + } + while(iCount < 16); + + // + // Stop timer. + // + TimerDisable(DIRECTMODE_TIMER_PORT, TIMER_B); + + // + // Store length. + // + *puiBytes = uiCountBytes; + + if(puiBits != NULL) + { + if(uiCountBits > 0) + { + // + // Store incomplete byte. + // + if(iMode == DIRECT_MODE_RECV_OPAQUE) + { + *pucCurrent = uiCurrentByte; + } + else + { + pucCurrent[0] = uiCurrentByte & 0xff; + pucCurrent[1] = uiCurrentByte >> 8; + } + } + + // + // Store length of incomplete byte. + // + *puiBits = uiCountBits; + } +} + +//***************************************************************************** +// +// Transmits and receives an ISO 14443-2 type A frame in direct mode 0. +// +// \param iMode is a flag field to specify the format of the input and output +// parameters. Should be a combination of (either \b DIRECT_MODE_SEND_OPAQUE +// or \b DIRECT_MODE_SEND_PARITY) and (either \b DIRECT_MODE_RECV_OPAQUE or +// \b DIRECT_MODE_RECV_PARITY). See discussion below. +// \param pvSendBuf is the data buffer to send. +// \param uiSendBytes determines the number of full data units to be sent (8 +// or 9 bits each). For a discussion of data unit sizes see below. +// \param uiSendBits determines how many bits from an additional, fractional +// data unit should be sent. Setting this to a value other than 0 means that +// \e pvSendBuf has space for an \e uiSendBytes + 1 data units. +// \param pvRecvBuf is the data buffer for receiving. +// \param puiRecvBytes inputs the space available in \e pvRecvBuf (in logical +// data units) and outputs the number of full data units actually received +// \param puiRecvBits outputs the number of additional bits received after the +// last full data unit indicated in \e puiRecvBytes +// +// Both input and output can be in one of two formats: OPAQUE and PARITY. +// +// - \b OPAQUE specifies an opaque bit stream, where each byte in the input +// corresponds to 8 bits sent on the radio interface and 8 bits received on +// the radio interface correspond to 1 byte in the output. +// - \b PARITY has for each byte in the input/output an associated parity bit. +// These are stored as a 16 bit word: the payload byte is in the lower 8 bits +// and the parity bit is the least significant bit of the higher byte. +// +// The principal data unit size for OPAQUE is 8 bits, and the principal data +// unit size for PARITY is 9 bits (stored as a 16 bit word). All inputs +// and outputs are in terms of data units, which means that the actual storage +// size, in bytes, for PARITY mode is twice the number of data units. +// +// In both modes additional bits can be sent or received after the last +// full data unit. PARITY mode is best suited for ISO 14443 operation +// since it conveniently associates each byte with its parity bit, and +// allows for direct access to the payload byte of each data unit through +// simple masking, and not requiring shifts and masks over two bytes. +// +// Direct mode needs to have been enabled with DirectModeEnable() (with +// argument \e iMode = 0) before calling this function. This function will +// disable the master processor interrupt while it is running. +// +//***************************************************************************** +void +DirectModeTransceive(int iMode, void const *pvSendBuf, unsigned int uiSendBytes, + unsigned int uiSendBits, void *pvRecvBuf, + unsigned int *puiRecvBytes, unsigned int *puiRecvBits) +{ + int iDisabled; + + // + // Disable interrupts. + // + iDisabled = IntMasterDisable(); + + // + // Send and receive + // + DirectModeSend(iMode, pvSendBuf, uiSendBytes, uiSendBits); + DirectModeReceive(iMode, pvRecvBuf, puiRecvBytes, puiRecvBits); + + // + // Enable interrupts if necessary. + // + if(iDisabled == 0) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +// Starts direct mode. +// +// \param iMode is the direct mode to enable and must be 0 for now. +// +// This function sets the desired direct mode type on the TRF79x0 and then +// enables direct mode. This also has the effect of disabling the +// TRF79x0 IRQ. No TRF79x0 operation can be performed while direct mode is +// active (and none should be attempted). +// The function sets an internal flag and does nothing if direct mode has +// already been enabled by this function and not been disabled with +// DirectModeDisable(). +// +//***************************************************************************** +void +DirectModeEnable(unsigned int iMode) +{ + unsigned char pucRegs[3]; + + // + // Check to see if direct mode is already enabled, and if so, do nothing + // + if(g_iDirectModeEnabled) + { + return; + } + + // + // Read chip status control and ISO registers. + // + TRF79x0ReadRegisterContinuous(TRF79X0_CHIP_STATUS_CTRL_REG, pucRegs, 2); + + // + // Set direct mode type to bitstream. + // + if(iMode) + { + pucRegs[TRF79X0_ISO_CONTROL_REG] |= TRF79X0_ISO_CONTROL_DIR_MODE; + + } + else + { + pucRegs[TRF79X0_ISO_CONTROL_REG] &= ~TRF79X0_ISO_CONTROL_DIR_MODE; + } + + // + // Enable direct mode in saved registers. + // + pucRegs[0] |= TRF79X0_STATUS_CTRL_DIRECT; + + // + // Write direct mode type to TRF79x0. + // + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, pucRegs[1]); + + // + // Clear pucRegs[2] + // + pucRegs[2] = 0; + + // + // Start direct mode + // This write will not finish (which would end direct mode) but instead + // must be finished with TRF79x0DirectModeDisable(). Also the IRQ handler + // has been deactivated while the chip select is asserted since it can't + // use the SPI anyway. + // + SSITRF79x0WriteContinuousStart(TRF79X0_CHIP_STATUS_CTRL_REG); + SSITRF79x0WriteContinuousData(pucRegs, 1); + + // + // Delay 8 dummy clock cycles + // + SSITRF79x0DummyWrite(&pucRegs[2], 1); + + // + // Set up GPIO configuration: Use the input (normally MISO) as a GPIO to + // bit-bang the reception of the sub-carrier signal + // + GPIOPinTypeGPIOInput(TRF79X0_RX_BASE, TRF79X0_RX_PIN); + + // + // Set flag + // + g_iDirectModeEnabled = 1; +} + +//***************************************************************************** +// +// Stops direct mode. +// +// This stops the direct mode and releases the communication interface. +// It checks an internal flag and does nothing if direct mode has not been +// enabled with DirectModeEnable() or has been disabled with +// DirectModeDisable() before. +// +// \note There seems to be a bug in the TRF7960 which makes the chip unusable +// for some time after exiting direct mode due to the MISO line not +// working properly. Currently the required workaround is to send a +// \b TRF79X0_SOFT_INIT_CMD command and then reinitialize the chip, with +// ISO14443ASetupRegisters(). This is done by this function, so you'll +// find the TRF79x0 configured for ISO 14443-A even if it wasn't before. +// +//***************************************************************************** +void +DirectModeDisable(void) +{ + int iDisabled; + + // + // Check to see if direct mode is enabled, and if not, do nothing. + // + if(!g_iDirectModeEnabled) + { + return; + } + + // + // Kludge: We want to prevent the IRQ handler from going off + // before we have reinitialized the interface. The call to + // SSITRF79x0WriteContinuousStop(), and by extension all the + // calls to TRF79x0DirectCommand or TRF79x0Read*, will enable the + // IRQ, so we disable the processor IRQ for the time being. + // + iDisabled = IntMasterDisable(); + + // + // Restore SSI pin settings. + // + GPIOPinTypeSSI(TRF79X0_RX_BASE, TRF79X0_RX_PIN); + + // + // Disable direct mode. + // + SSITRF79x0WriteContinuousStop(); + + // + // For good measure: Discard bytes from FIFO. + // + TRF79x0DirectCommand(TRF79X0_RESET_FIFO_CMD); + + // + // Clear flag. + // + g_iDirectModeEnabled = 0; + + // + // Re-enable processor IRQ if necessary. + // + if(iDisabled == 0) + { + IntMasterEnable(); + } + + // + // Enable TRF IRQ. + // + TRF79x0InterruptEnable(); + + // + // This code should be removed if a better solution is found since + // the direct mode code should not directly depend on ISO 14443-A + // and there might, hypothetically, be other protocols that the user + // might want to use. + // + TRF79x0DirectCommand(TRF79X0_SOFT_INIT_CMD); + ISO14443ASetupRegisters(); + ISO14443APowerOn(); +} + +//***************************************************************************** +// +// Queries whether direct mode is enabled. +// +// \return A non-zero value indicates that direct mode is enabled and a zero +// value indicates that direct mode is disabled. +// +//***************************************************************************** +int +DirectModeIsEnabled(void) +{ + return(g_iDirectModeEnabled); +} diff --git a/nfclib/directmode.h b/nfclib/directmode.h new file mode 100644 index 0000000..6124859 --- /dev/null +++ b/nfclib/directmode.h @@ -0,0 +1,78 @@ +//***************************************************************************** +// +// directmode.h - Direct mode communications. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __DIRECTMODE_H__ +#define __DIRECTMODE_H__ + +//***************************************************************************** +// +// Define NULL, if not already defined. +// +//***************************************************************************** +#ifndef NULL +#define NULL ((void *)0) +#endif + +// +// Input to DirectModeTransceive is an opaque stream of bits, grouped as 8 +// bits into one byte. LSBit should be sent first. +// +#define DIRECT_MODE_SEND_OPAQUE 0 + +// +// Input to DirectModeTransceive is an array of bytes with associated parity +// bit, stored as 16 bit words. The lower 8 bits in each word are the byte +// (LSBit should be sent first), the lowest bit of the upper 8 bits is the +// parity bit. The remaining 7 bits are ignored. +// +// +#define DIRECT_MODE_SEND_PARITY 1 + +// +// Output from DirectModeTransceive should be an opaque bit stream, grouped as +// 8 bits into one byte, LSBit was received first. +// +#define DIRECT_MODE_RECV_OPAQUE 0 + +// +// Output from DirectModeTransceive should be an array of bytes with +// associated parity bit. +// +#define DIRECT_MODE_RECV_PARITY 2 + +#define DIRECT_MODE_SEND_MASK 1 +#define DIRECT_MODE_RECV_MASK 2 + +extern void DirectModeInit(void); +extern void DirectModeTransceive(int iMode, void const *pvSendBuf, + unsigned int iSendBytes, + unsigned int iSendBits, void *pvRecvBuf, + unsigned int *piRecvBytes, + unsigned int *piRecvBits); + +extern void DirectModeEnable(unsigned int iMode); +extern void DirectModeDisable(void); +extern int DirectModeIsEnabled(void); + +#endif diff --git a/nfclib/iso14443-4.c b/nfclib/iso14443-4.c new file mode 100644 index 0000000..4bb39af --- /dev/null +++ b/nfclib/iso14443-4.c @@ -0,0 +1,164 @@ +//***************************************************************************** +// +// iso14443-4.c - ISO 14443-4 implementation. +// +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include + +#include "inc/hw_types.h" +#include "driverlib/sysctl.h" +#include "trf7960.h" + +//***************************************************************************** +// +// Transceive ISO 14443-4 RATS command. +// +//***************************************************************************** +int +ISO14443RATS(unsigned char ucFSDI, unsigned char ucCID, unsigned char *pucATS) +{ + unsigned char pucResponse[16]; + unsigned int uiRxSize; + unsigned char pucRATS[2]; + int i; + + uiRxSize = sizeof(pucResponse); + + // + // RATS command. + // + pucRATS[0] = 0xE0; + pucRATS[1] = (ucFSDI << 4) || ucCID; + + // + // Transmit RATS, receive ATS. + // + TRF7960Transceive(pucRATS, sizeof(pucRATS), 0, pucResponse, &uiRxSize, NULL, + TRF7960_TRANSCEIVE_CRC); + + if(uiRxSize >= 3) + { + // + // Valid ATS received, return it as an char buffer. Was transmitted LSByte first. + // pucResponse[0] is the length of the transmitted ATS, including TL byte, NOT including + // two CRC bytes + // + for(i = 0; i < pucResponse[0]; i++) + pucATS[i] = pucResponse[i]; + + return(uiRxSize); + } + else + { + return(0); + } +} + +//***************************************************************************** +// +// Transceive ISO 14443-4 PPS command. +// ucCID must between 0~14, ucDRI & ucDSI must between 0~3 +// +//***************************************************************************** +int +ISO14443PPS(unsigned char ucCID, unsigned char ucDRI, unsigned char ucDSI) +{ + unsigned char pucResponse[3]; + unsigned int uiRxSize; + unsigned char pucPPS[3]; + + uiRxSize = sizeof(pucResponse); + + // + // PPS command. + // + pucPPS[0] = (0xD << 4) | ucCID; + pucPPS[1] = 0x11; // PPS1 is transmitted + pucPPS[2] = (ucDSI << 2) | ucDRI; + + // + // Transmit PPS, receive PPS response. + // + TRF7960Transceive(pucPPS, sizeof(pucPPS), 0, pucResponse, &uiRxSize, NULL, + TRF7960_TRANSCEIVE_CRC); + + // + // check if receive the first byte of the response is PPSS + // + if(pucResponse[0] == pucPPS[0]) + return(1); + else + { + // + // Not valid response + // + return(0); + } +} + +//***************************************************************************** +// +// Transceive ISO 14443-4 DESELECT command. +// ucCID must between 0~14, ucDRI & ucDSI must between 0~3 +// +//***************************************************************************** +int +ISO14443DESELECT(unsigned char ucCID) +{ + unsigned char pucResponse[2]; + unsigned int uiRxSize; + unsigned char pucDESELECT[2]; + + uiRxSize = sizeof(pucResponse); + + pucResponse[0] = 0; + pucResponse[1] = 0; + + // + // DESELECT command. + // + pucDESELECT[0] = 0xCA; // S-block with DESELECT set and CID following + pucDESELECT[1] = ucCID & 0x0F; + + // + // Transmit DESELECT, receive DESELECT response. + // + TRF7960Transceive(pucDESELECT, sizeof(pucDESELECT), 0, pucResponse, &uiRxSize, NULL, + TRF7960_TRANSCEIVE_CRC); + + // + // check if receive the first byte of the response is DESELECT + // check if the second byte contains the same CID + // + if(pucResponse[0] == pucDESELECT[0]) + { + return(1); + } + else + { + // + // Not valid response + // + return(0); + } +} diff --git a/nfclib/iso14443-4.h b/nfclib/iso14443-4.h new file mode 100644 index 0000000..b2f9b8b --- /dev/null +++ b/nfclib/iso14443-4.h @@ -0,0 +1,33 @@ +//***************************************************************************** +// +// iso14443a.h - ISO 14443A implementation. +// +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __ISO14443-4_H__ +#define __ISO14443-4_H__ + +extern int ISO14443RATS(unsigned char ucFSDI, unsigned char ucCID, unsigned char *pucATS); +extern int ISO14443PPS(unsigned char ucCID, unsigned char ucDRI, unsigned char ucDSI); +extern int ISO14443DESELECT(unsigned char ucCID); + +#endif diff --git a/nfclib/iso14443a.c b/nfclib/iso14443a.c new file mode 100644 index 0000000..10cd6b2 --- /dev/null +++ b/nfclib/iso14443a.c @@ -0,0 +1,1141 @@ +//***************************************************************************** +// +// iso14443a.c - ISO 14443A implementation. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_types.h" +#include "driverlib/sysctl.h" +#include "trf79x0.h" +#include "iso14443a.h" + +//***************************************************************************** +// +// Global anti-collision state for use by ISO14443ASelectFirst() and +// ISO14443ASelectNext(). +// +//***************************************************************************** +static struct ISO14443AAnticolState g_sAnticolState; + +//***************************************************************************** +// +// ISO14443-A Anti-collision implementation, iterative depth-first tree search +// with optional backtracking. +// +// Usage: +// In ISO 14443 A there are two types of resting states for cards: IDLE and +// HALT. A card enters IDLE state after powering up and performing all the +// necessary internal initialization. The specification states that the card +// must be in IDLE state and ready to accept commands 5ms after being put into +// an unmodulated (e.g. no commands sent) field of the necessary strength. +// +// This pause is guaranteed by ISO14443APowerOn(). +// +// During the selection and anti-collision phase cards will be in intermediary +// states (READY and READY*) but then always return to the original state +// (IDLE and HALT). +// +// After a card has been selected by any of the ISO14443ASelect* functions +// of this module it can be sent to the HALT state with ISO14443AHalt(), +// and must be sent to HALT (or deactivated in another way) before calling +// another ISO14443ASelect* function. +// +// Cards in the IDLE state react to both WUPA and REQA commands, cards in +// HALT state react only to WUPA commands. Cards in HALT state can not +// return to IDLE state except through completely powering off the card +// and powering it up again, but the specification makes no claims as to how +// long the field must be off in order for the card to power off and this time +// will vary between card types. +// +// The ISO14443ASelectFirst/Next functions take one parameter (\e ucCmd) that +// must be \b ISO14443A_REQA or \b ISO14443A_WUPA to specify which wake up +// method to use. ISO14443ASelect will always use WUPA. +// +// This leads to two main usage protocols: +//

A: Detect only new cards

    +//
  • Keep field enabled at all times +//
  • Use ISO14443ASelectFirst() with ISO14443A_REQA to find new cards that +// entered the field. Note: Ensure a pause of 5ms before each call to +// ISO14443ASelectFirst(), e.g. with ISO14443APowerOn(). +//
  • If a card was found by SelectFirst, operate on that card and +// deactivate it with ISO14443AHalt(). Note: all successful calls to any +// ISO14443ASelect* function should always be paired with a call to +// ISO14443AHalt() before the next call to any ISO14443ASelect* function. +//
  • Repeatedly call ISO14443ASelectFirst() with \b ISO14443A_REQA in a +// loop. It will only find new cards and not relist the cards that were +// already handled and halted +//
+// Pseudo C:
+//  while(1) {
+//      ISO14443APowerOn();
+//      if(ISO14443ASelectFirst(ISO14443A_REQA, ...)) {
+//
+//          Do something with the card
+//
+//          ISO14443AHalt();
+//      }
+//
+//      Do NOT power off the field
+//  }
+// 
+// +//

B: List all cards in the field

    +//
  • Optionally disable the field or do other things, but enable the +// field at least for 5ms (e.g. with ISO14443APowerOn()) +//
  • Call ISO14443ASelectFirst() with \b ISO14443A_WUPA to find the first +// card, handle it, call ISO14443AHalt(). If at least one card was +// found, use ISO14443ASelectNext() with \b ISO14443A_WUPA in a loop to +// find more cards, handle them and call ISO14443AHalt() on them. +//
  • You may disable the field and restart the procedure at any time with +// ISO14443APowerOn() and ISO14443ASelectFirst(). It will always list +// all cards in the field, not only new cards. +//
+// Pseudo C:
+//  while(1) {
+//      ISO14443APowerOn();
+//      if(ISO14443ASelectFirst(ISO14443A_WUPA, ...)) {
+//          do {
+//
+//              Do something with the card
+//
+//              ISO14443AHalt();
+//          } while(ISO14443ASelectNext(ISO14443A_WUPA, ...));
+//      }
+//
+//      You may power off the field here
+//  }
+// 
+// +// In both cases ISO14443ASelect() can be used at any time (after halting a +// previously selected card) to select a card by known UID. +// +//***************************************************************************** + +//***************************************************************************** +// +// This structure stores the UID that we're currently working on. +// +//***************************************************************************** +struct ISO14443AAnticolState +{ + // + // This field stores the raw responses that the anti-collision is actually + // performed over, e.g. 3 times 5 bytes. Same goes for \e ucCollisions. + // Before returning the UID to the calling code this must be cleaned, + // that is remove cascade tag and BCC. + // + unsigned char ucUID[15]; + // + // Stores the collision positions discovered so far. It is a bit field + // with the same indices as \e ucUID. + // + unsigned char ucCollisions[15]; + // + // Stores the number of bits that we've successfully received or + // disambiguated. Note: Real count for the \e ucUID field of this + // structure, not NVB format. For example 8 means 1 byte and 0 bits, 40 + // means full cascade level 1, 41 means full cascade level 1 plus 1 bit in + // cascade level 2. + // + unsigned int iBitPos; +}; + +//***************************************************************************** +// +// Set up registers for ISO 14443 A 106Kbit/s operation. This function must +// be called after initializing the TRF79x0 (for example with TRF79x0Init() +// or TRF79x0DirectCommand() with argument \b TRF79X0_SOFT_INIT_CMD) and before +// calling any of the other ISO14443A functions. +// +//***************************************************************************** +void +ISO14443ASetupRegisters(void) +{ + // + // Set the ISO format to ISO1443A 106Kbps. + // + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, + TRF79X0_ISO_CONTROL_14443A_106K); + + // + // Set the TX pulse to 106ns (0x20 * 73.7ns). + // + TRF79x0WriteRegister(TRF79X0_TX_PULSE_LENGTH_CTRL_REG, 0x20); + + // + // Set the RX No response wait time to 529us (0xe * 37.76us). + // + TRF79x0WriteRegister(TRF79X0_RX_NO_RESPONSE_WAIT_REG, 0x0e); + + // + // Set the RX wait time to 66us (7 * 9.44us). + // + TRF79x0WriteRegister(TRF79X0_RX_WAIT_TIME_REG, 0x07); + + // + // Set the SYSCLK to 6.78MHz and the Modulation Depth to OOK. + // + TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG, + (TRF79X0_MOD_CTRL_SYS_CLK_6_78MHZ | + TRF79X0_MOD_CTRL_MOD_OOK_100)); + + // + // Configure the Special Settings Register. + // + TRF79x0WriteRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG, + (TRF79x0ReadRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG) & 0x0f) | + TRF79X0_RX_SP_SET_M848); + + // + // Configure the Test Settings Register. + // + TRF79x0WriteRegister(TRF79X0_TEST_SETTING1_REG, 0x20); + + // + // Set the regulator voltage to be automatic. + // + TRF79x0WriteRegister(TRF79X0_REGULATOR_CONTROL_REG, + TRF79X0_REGULATOR_CTRL_AUTO_REG); +} + +//***************************************************************************** +// +// Power on the field and wait for a time that is long enough to guarantee +// that all cards in the field will be initialized. +// +//***************************************************************************** +void +ISO14443APowerOn(void) +{ + unsigned char ucReg; + + // + // Enable RF field and receiver. + // + ucReg = TRF79x0ReadRegister(TRF79X0_CHIP_STATUS_CTRL_REG); + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, + ucReg | TRF79X0_STATUS_CTRL_RF_ON); + + // + // Wait 5ms (as per ISO 14443-3 clause 5). + // + SysCtlDelay(((SysCtlClockGet() / 3) * 5) / 1000); +} + +//***************************************************************************** +// +// Power off the field and wait for some time. +// +//***************************************************************************** +void +ISO14443APowerOff(void) +{ + unsigned char ucReg; + + // + // Disable RF field and receiver. + // + ucReg = TRF79x0ReadRegister(TRF79X0_CHIP_STATUS_CTRL_REG); + + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, + ucReg & ~TRF79X0_STATUS_CTRL_RF_ON); + + // + // Wait 5ms. + // + SysCtlDelay(((SysCtlClockGet() / 3) * 5) / 1000); +} + +//***************************************************************************** +// +// Transmit a HLTA command that should HALT the currently selected card. You +// should always call this function after a successful call to either +// ISO14443ASelect(), ISO14443ASelectFirst() or ISO14443ASelectNext() and +// before any other call to any of those functions. +// +//***************************************************************************** +void +ISO14443AHalt(void) +{ + // + // HLTA command. + // + const unsigned char pucHLTA[2] = {0x50, 0x00}; + + TRF79x0Transceive(pucHLTA, sizeof(pucHLTA), 0, NULL, NULL, NULL, + TRF79X0_TRANSCEIVE_CRC); +} + +//***************************************************************************** +// +// Transceive ISO 14443-A REQA type command. +// +// \param ucCmd is the command, either \b ISO14443A_REQA or \b ISO14443A_WUPA +// \param piATQA is a pointer to an integer to store the received ATQA and +// will be set to -1 if a collision occurred. +// +// \return true if at least one card responded that is capable of bit-frame +// anti-collision (e.g. no collision and one of the lower 5 bits of response +// set, or collision within the first 5 bits, or collision not in the first +// 5 bits but at least one of the first 5 bits is a 1-bit) and false +// otherwise. +// +// \note User code usually does not need to call this function since it is +// implicitly called in ISO14443ASelect(), ISO14443ASelectFirst() or +// ISO14443ASelectNext(). +// +//***************************************************************************** +int +ISO14443AREQA(unsigned char ucCmd, int *piATQA) +{ + unsigned char pucResponse[2]; + unsigned int uiRxSize; + int iColPos; + + uiRxSize = sizeof(pucResponse); + + // + // Transmit WUPA/REQA, receive ATQA. + // + TRF79x0Transceive(&ucCmd, 0, 7, pucResponse, &uiRxSize, 0, + TRF79X0_TRANSCEIVE_NO_CRC); + + if(uiRxSize == 2) + { + // + // Valid ATQA received, return it as an integer. Was transmitted + // LSByte first. + // + if(piATQA != NULL) + { + *piATQA = pucResponse[0] | (pucResponse[1] << 8); + } + + // + // Return true if one of the lower 5 bits was set. + // + return((pucResponse[0] & 0x1F) != 0); + } + else + { + // + // No valid ATQA received. + // + if(piATQA != NULL) + { + *piATQA = -1; + } + + if(uiRxSize == 0) + { + // + // No response at all -> no card with bit-frame anti-collision. + // + return(0); + } + else + { + // + // Probably some collision. + // + iColPos = TRF79x0GetCollisionPosition(); + + if(iColPos > 5) + { + // + // Collision not within the first 5 bits, return true if one of + // the lower 5 bits was set. + // + return((pucResponse[0] & 0x1F) != 0); + } + else if(iColPos > 0 && iColPos <= 5) + { + // + // Collision within the first 5 bits, so at least one of them + // was 1. + // + return(1); + } + else + { + // + // No collision, but only 1 byte sent? That card's not right. + // + return(0); + } + } + } +} + +//***************************************************************************** +// +// Find one card through the anti-collision procedure with given \e psState. +// +// \param psState is the anti-collision state to start from. If this state +// already specifies a full UID then it will be selected, otherwise +// anti-collision will be tried to complete that starting state, with no +// backtracking. +// \param pucUID is an output buffer to write the selected UID and may be +// \b NULL in which case the UID will not be returned. +// \param puiUIDSize inputs the available space in bytes in \e pucUID and +// returns with the actual length that has been stored there. +// \param pucSAK is an output parameter that stores the received SAK value. +// May be \b NULL in which case the SAK will not be returned +// +// This is a depth first search in a binary tree over the UID space. On each +// attempt we can learn up to 4 bytes of the UID of the card(s) currently in +// the field. If the UIDs of two cards differ we will learn that too and get +// the collision position: the position of the bit where the UID of at least +// two cards differs. We will mark this position in the appropriate field in +// the structure ISO14443AnticolState and then branch first in the direction of +// 0 and increase iBitPos to include this bit. +// +// \return This function returns 1 if a card was selected and 0 otherwise. +// +//***************************************************************************** +static int +ISO14443ADoAnticol(struct ISO14443AAnticolState *psState, unsigned char *pucUID, + unsigned int *puiUIDSize, unsigned char *pucSAK) +{ + int iCascadeLevel, iPos; + unsigned char pucCmd[7], pucResponse[5]; + unsigned int uiRxSize; + int iIdx, iMaskPosition, iCollPosition, iValidBits, iMaxLength, iNVB; + + iCascadeLevel = 1; + + while(iCascadeLevel < 4) + { + // + // Already known bits for this cascade level, e.g. not including + // the possible 5 bytes * 8 bits/byte for the lower levels. + // + iValidBits = psState->iBitPos - (iCascadeLevel - 1) * 5 * 8; + + // + // Clamp to a full cascade level. + // + if(iValidBits > 40) + { + iValidBits = 40; + } + + // + // NVB format: bytes. + // + iNVB = (iValidBits / 8) << 4; + + // + // NVB format: bits. + // + iNVB |= (iValidBits % 8); + + // + // Also count the command byte and the NVB byte itself. + // + iNVB += 0x20; + + // + // Prepare command for this level: ANTICOLLISION if less than a full 5 + // bytes for the current cascade level, SELECT otherwise. + // + switch (iCascadeLevel) + { + case 1: + { + pucCmd[0] = 0x93; + break; + } + case 2: + { + pucCmd[0] = 0x95; + break; + } + case 3: + { + pucCmd[0] = 0x97; + break; + } + default: + { + break; + } + } + + pucCmd[1] = iNVB; + + // + // Copy over known bytes (number of bits for this level divided by 8, + // rounded up). + // + memcpy(pucCmd + 2, psState->ucUID + (iCascadeLevel - 1) * 5, + (iValidBits + 7) / 8); + + // + // Enforce a small delay of ~600us before each anti-collision frame. + // + SysCtlDelay(((SysCtlClockGet() / 3) * 6) / 10000); + + // + // Maximal expected response length. + // + uiRxSize = 5; + + if(iNVB != 0x70) + { + // + // Anti-collision command. + // + TRF79x0Transceive(pucCmd, pucCmd[1] >> 4, pucCmd[1] & 0xf, + pucResponse, &uiRxSize, NULL, + TRF79X0_TRANSCEIVE_NO_CRC); + + if(uiRxSize == 0) + { + return(0); + } + + iCollPosition = TRF79x0GetCollisionPosition(); + + if(iCollPosition < 0) + { + // + // No collision occurred, add full response data to known bits. + // + iCollPosition = 40; + } + else + { + // + // Collision occurred, only add the part that was received + // correctly. + // + // TF7960 Collision position register is in NVB format, + // convert to straight bit position. This will be the number + // of bits that were the same in all responding cards. + // + iCollPosition -= 0x20; + iCollPosition = ((iCollPosition >> 4) * 8) + (iCollPosition & 0xf); + } + + // + // Bounds check the results and return 0 if it was invalid. + // + if(iCollPosition < 0 || iCollPosition > 40) + { + return(0); + } + + // + // Mask out the invalid bits in the last byte of the response, if + // any. + // + // Graphic: + // UID bytes: | first || second || third || fourth || fifth | + // | iValidBits | + // | iCollPosition | + // In this graphic the first byte is fully valid. The second byte + // was sent partially invalid, but should have been masked on a + // previous run. The third byte is received partially invalid and + // needs to be masked. Response will only contain the second and + // third byte (although both are received properly byte-aligned). + // + // + // This many bits in response are valid or at least compatible + // with the UID. + // + iMaskPosition = iCollPosition - (iValidBits / 8) * 8; + + if(iMaskPosition % 8) + { + // + // Need to construct a mask for iMaskPosition%8 bits and + // apply it at iMaskPosition/8. + // + pucResponse[iMaskPosition / 8] &= ~((~0) << (iMaskPosition % 8)); + } + + // + // Merge in up to iMaskPosition/8 (rounded up) byte into response + // at index iBitPos/8 (rounded down). + // + for(iIdx = 0; iIdx < (iMaskPosition + 7) / 8; iIdx++) + { + psState->ucUID[(psState->iBitPos / 8) + iIdx] |= + pucResponse[iIdx]; + } + + psState->iBitPos += iCollPosition - iValidBits; + + // + // Only within this cascade level: + // + if(psState->iBitPos % 40 != 0) + { + // + // Mark backtracking point. + // + psState->ucCollisions[psState->iBitPos / 8] |= + 1 << (psState->iBitPos % 8); + + // + // Walk into the 0 direction. + // + psState->iBitPos += 1; + } + } + else + { + // + // Select command. + // + TRF79x0Transceive(pucCmd, pucCmd[1] >> 4, pucCmd[1] & 0xf, + pucResponse, &uiRxSize, NULL, + TRF79X0_TRANSCEIVE_CRC); + + if(uiRxSize == 1) + { + // + // SAK received. + // + if(pucResponse[0] & 0x04) + { + // + // UID not complete, increase cascade level. + // + iCascadeLevel++; + + if(iCascadeLevel > 3) + { + break; + } + } + else + { + // + // UID complete, return. + // + break; + } + } + else + { + // + // Some error, card not selected. + // + memset(psState->ucUID, 0, sizeof(psState->ucUID)); + psState->iBitPos = 0; + break; + } + } + } + + // + // Some error, not fully selected. + // + if(((psState->iBitPos % 40) != 0) || (psState->iBitPos == 0)) + { + return(0); + } + + // + // Fully selected a card. pucResponse[0] should be from the last + // transaction, of a SELECT command, and therefore contain the SAK + // + if(pucSAK != NULL) + { + *pucSAK = pucResponse[0]; + } + + // + // If requested, return the UID, without cascade tag and BCC. + // + if(pucUID != NULL && puiUIDSize != NULL) + { + iMaxLength = *puiUIDSize; + iPos = 0; + *puiUIDSize = 0; + + // + // From the 5 bytes in each cascade level the 3 middle bytes need to be + // copied for each level except for the last, where the first 4 bytes + // need to be copied. + // + for(iPos = 0; iPos < psState->iBitPos / 8; iPos += 5) + { + if(iPos + 5 < psState->iBitPos / 8) + { + // + // Not the last cascade level. + // + if(*puiUIDSize + 3 > iMaxLength) + { + // + // Not enough space + // + *puiUIDSize = 0; + break; + } + + // + // Copy 3 bytes (e.g. don't copy cascade tag and BCC). + // + memcpy(pucUID + *puiUIDSize, psState->ucUID + iPos + 1, 3); + + *puiUIDSize += 3; + } + else + { + // + // Last cascade level. + // + if(*puiUIDSize + 4 > iMaxLength) + { + // + // Not enough space. + // + *puiUIDSize = 0; + + break; + } + + // + // Copy 4 bytes (e.g. don't copy BCC). + // + memcpy(pucUID + *puiUIDSize, psState->ucUID + iPos, 4); + + *puiUIDSize += 4; + } + } + } + return(1); +} + +//***************************************************************************** +// +// Selects the first (or only) card and returns its UID, UID length and +// SAK bytes. +// +// \param ucCmd must be ISO14443A_REQA or ISO14443A_WUPA. +// \param pucUID will store UID of the card that was selected. May be NULL +// in which case the UID will not be returned. +// \param puiUIDSize must be initialized with the length of the buffer in +// \e pucUID and will return the number of bytes actually stored. +// \param pucSAK will store the SAK byte of the card that was selected and may +// be NULL in which case the SAK byte will not be returned. +// +// The function call initializes and updates a static internal state that +// marks the position in the anti-collision procedure. ISO14443ASelectNext() +// can be used to continue with the anti-collision from that starting point. +// +// \note You should call ISO14443AHalt() if this function returned true and +// you are done operating on the card. +// +// \return Function returns 1 if a card was selected, 0 otherwise. +// +//***************************************************************************** +int +ISO14443ASelectFirst(unsigned char ucCmd, unsigned char *pucUID, + unsigned int *puiUIDSize, unsigned char *pucSAK) +{ + // + // Initialize/clear static state. + // + memset(&g_sAnticolState, 0, sizeof(g_sAnticolState)); + + // + // Wake up all or only new tags. + // + if(ISO14443AREQA(ucCmd, NULL) == 0) + { + // + // No tag with support for bit frame anti-collision found. + // + return(0); + } + + return(ISO14443ADoAnticol(&g_sAnticolState, pucUID, puiUIDSize, pucSAK)); +} + +//***************************************************************************** +// +// Selects the next card and returns its UID, UID length and SAK bytes. +// +// \param ucCmd must be ISO14443A_REQA or ISO14443A_WUPA. +// \param UID will store UID of the card that was selected and may be NULL +// in which case the UID will not be returned. +// \param puiUIDSize must be initialized with the length of the buffer in +// \e UID and will return the number of bytes actually stored. +// \param pucSAK will store the SAK byte of the card that was selected and may +// be NULL in which case the SAK byte will not be returned. +// +// Uses the state that was initialized by ISO14443SelectFirst() and tries +// to find more cards in the field. +// +// \note You should call ISO14443AHalt() if this function returned true and +// you are done operating on the card. +// +// \return This function returns 1 if a card was selected and 0 otherwise. +// +//***************************************************************************** +int +ISO14443ASelectNext(unsigned char ucCmd, unsigned char *pucUID, + unsigned int *puiUIDSize, unsigned char *pucSAK) +{ + // + // Backtrack through static state: starting at iBitPos and going reverse, + // find the first bit that's set in collisions, walk into the 1 direction, + // clear the collision indicator and set iBitPos to that position. + // + while(--g_sAnticolState.iBitPos > 0) + { + // + // Clear UID bit at this position to clean the state. + // + g_sAnticolState.ucUID[g_sAnticolState.iBitPos / 8] &= + ~(1 << (g_sAnticolState.iBitPos % 8)); + + if(g_sAnticolState.ucCollisions[g_sAnticolState.iBitPos / 8] & + (1 << (g_sAnticolState.iBitPos % 8))) + { + // + // This is our new starting point, set UID bit to walk into the + // 1 direction. + // + g_sAnticolState.ucUID[g_sAnticolState.iBitPos / 8] |= + 1 << (g_sAnticolState.iBitPos % 8); + + // + // Remove backtracking marker. + // + g_sAnticolState.ucCollisions[g_sAnticolState.iBitPos / 8] &= + ~(1 << (g_sAnticolState.iBitPos % 8)); + + // + // Increment bit position to account for the bit that we just + // added, then break loop to perform anti-collision with the new + // partial UID. + // + g_sAnticolState.iBitPos++; + + break; + } + + // + // Not a backtracking point, go further back. + // + } + + // + // No further backtracking points -> no other cards. + // + if(g_sAnticolState.iBitPos <= 0) + { + return(0); + } + + // + // Wake up all or only new tags. + // + if(!ISO14443AREQA(ucCmd, NULL)) + { + // + // No tag with support for bit frame anti-collision found. + // + return(0); + } + + return(ISO14443ADoAnticol(&g_sAnticolState, pucUID, puiUIDSize, pucSAK)); +} + +//***************************************************************************** +// +// Selects a card with given UID and return its SAK byte. +// +// \param pucUID must point to the UID of the card that should be selected and +// may not be NULL. +// \param uiUIDSize must be the length in bytes of the UID stored in \e pucUID. +// \param pucSAK will store the SAK byte of the card that was selected. May be +// \b NULL in which case the SAK byte will not be returned. +// +// \note You should call ISO14443AHalt() if this function returned true and +// you are done operating on the card. +// +// \return This function will return 1 if a card was selected and 0 otherwise. +// +//***************************************************************************** +int +ISO14443ASelect(unsigned char const *pucUID, unsigned int uiUIDSize, + unsigned char *pucSAK) +{ + int iIdx, iPos; + struct ISO14443AAnticolState sState; + + // + // Check if the given UID size is supported. + // + if((uiUIDSize != 4) && (uiUIDSize != 7) && (uiUIDSize != 10)) + { + return(0); + } + + // + // Prepare a state for the given UID. + // + sState.iBitPos = 0; + + for(iPos = 0; iPos < uiUIDSize;) + { + // + // Check if this is the final cascade level. + // + if(iPos + 4 < uiUIDSize) + { + // + // If this was not the final cascade level then add a cascade tag. + // + sState.ucUID[sState.iBitPos / 8] = 0x88; + + // + // Copy three bytes of UID. + // + memcpy(sState.ucUID + (sState.iBitPos / 8) + 1, pucUID + iPos, 3); + + // + // Increment position in UID. + // + iPos += 3; + } + else + { + // + // For the final cascade level just copy four bytes of UID. + // + memcpy(sState.ucUID + (sState.iBitPos / 8), pucUID + iPos, 4); + + // + // Increment position in UID. + // + iPos += 4; + } + + // + // Calculate BCC. + // + sState.ucUID[sState.iBitPos / 8 + 4] = 0; + + for(iIdx = 0; iIdx < 4; iIdx++) + { + sState.ucUID[sState.iBitPos / 8 + 4] ^= + sState.ucUID[sState.iBitPos / 8 + iIdx]; + } + + // + // Increment position in state. + // + sState.iBitPos += 40; + } + + // + // Always wake up all cards. + // + if(!ISO14443AREQA(ISO14443A_WUPA, NULL)) + { + // + // No tag with support for bit frame anti-collision found. + // + return(0); + } + + return(ISO14443ADoAnticol(&sState, NULL, NULL, pucSAK)); +} + +//***************************************************************************** +// +// Helper functions for ISO 14443-A frames to be sent or received in Direct +// Mode. +// +//***************************************************************************** + +//***************************************************************************** +// +// Calculates odd parity for one byte. +// +//***************************************************************************** +static unsigned char +ParityByte(unsigned char ucByte) +{ + ucByte ^= ucByte >> 1; + ucByte ^= ucByte >> 1; + ucByte ^= ucByte >> 1; + ucByte ^= ucByte >> 1; + ucByte ^= ucByte >> 1; + ucByte ^= ucByte >> 1; + ucByte ^= ucByte >> 1; + return((ucByte & 1) ^ 1); +} + +//***************************************************************************** +// +// Checks that data has correct (odd) parity +// +// \param pusData is the data buffer to check and must store 16 bits per one +// logical byte: the lower 8 bits are the data byte, the LSBit in the upper +// byte is the parity. +// \param lSize is the number of logical bytes/16 bit words in \e pusData. +// +// \return This function returns 1 if the parity was correct and 0 otherwise. +// +//***************************************************************************** +int +ISO14443ACheckParity(const unsigned short * const pusData, const long lSize) +{ + int iFailed, iIdx; + + iFailed = 0; + + for(iIdx = 0; iIdx < lSize; iIdx++) + { + iFailed |= (pusData[iIdx] >> 8) ^ ParityByte(pusData[iIdx] & 0xff); + } + + return(!iFailed); +} + +//***************************************************************************** +// +// Sets data to correct (odd) parity +// +// \param pusData is the data buffer to update and must store 16 bits per one +// logical byte: the lower 8 bits are the data byte, the LSBit in the upper +// byte is the parity. +// \param lSize is the number of logical bytes/16 bit words in \e data +// +//***************************************************************************** +void +ISO14443ACalculateParity(unsigned short * const pusData, const long lSize) +{ + int iIdx; + + for(iIdx = 0; iIdx < lSize; iIdx++) + { + pusData[iIdx] = (pusData[iIdx] & 0xff) | + (ParityByte(pusData[iIdx] & 0xff) << 8); + } +} + +//***************************************************************************** +// +// Calculate CRC-A and return it. +// +//***************************************************************************** +static unsigned short +CalculateCRC(const unsigned short * const pusData, const long lSize) +{ + unsigned short usCrc; + int iIdx, iBit; + unsigned char ucByte, ucBit; + + usCrc = 0x6363; + + for(iIdx = 0; iIdx < lSize; iIdx++) + { + ucByte = pusData[iIdx] & 0xff; + + for(iBit = 0; iBit < 8; iBit++) + { + ucBit = (usCrc ^ ucByte) & 1; + + ucByte >>= 1; + usCrc >>= 1; + + if(ucBit) + { + usCrc ^= 0x8408; + } + } + } + return(usCrc); +} + +//***************************************************************************** +// +// Check that data has correct CRC in last two bytes. +// +// \param pusData is the data buffer to check and must store 16 bits per one +// logical byte: the lower 8 bits are the data byte, the LSBit in the upper +// byte is the parity. +// \param lSize is the number of logical bytes/16 bit words in \e pusData. +// Must be at least 2, since the CRC consists of two bytes. +// +// \return This function returns 1 if the CRC was correct and 0 otherwise. +// +//***************************************************************************** +int +ISO14443ACheckCRC(const unsigned short * const pusData, const long lSize) +{ + unsigned short usCrc; + + if(lSize < 2) + { + return(0); + } + + usCrc = CalculateCRC(pusData, lSize - 2); + + if(((usCrc & 0xff) == (pusData[lSize - 2] & 0xff)) && + (((usCrc >> 8) & 0xff) == (pusData[lSize - 1] & 0xff))) + { + return(1); + } + return(0); +} + +//***************************************************************************** +// +// Appends correct CRC to the data +// +// \param pusData is the data buffer to update and must store 16 bits per one +// logical byte: the lower 8 bits are the data byte, the LSBit in the upper +// byte is the parity. +// \param lSize is the number of logical bytes/16 bit words in \e pusData. The +// buffer in \e pusData must have room for an additional two logical bytes. +// +// \return This function returns the new length to correctly append the CRC. +// +//***************************************************************************** +long +ISO14443ACalculateCRC(unsigned short * const pusData, const long lSize) +{ + unsigned short usCrc; + + usCrc = CalculateCRC(pusData, lSize); + + pusData[lSize] = usCrc & 0xff; + pusData[lSize + 1] = (usCrc >> 8) & 0xff; + + ISO14443ACalculateParity(pusData + lSize, 2); + + return(lSize + 2); +} diff --git a/nfclib/iso14443a.h b/nfclib/iso14443a.h new file mode 100644 index 0000000..274ada7 --- /dev/null +++ b/nfclib/iso14443a.h @@ -0,0 +1,65 @@ +//***************************************************************************** +// +// iso14443a.h - ISO 14443A implementation. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __ISO14443A_H__ +#define __ISO14443A_H__ + +// +// REQA command, which wakes cards from IDLE state and puts them into READY +// state. One of the two possible parameters for \e ucCmd in +// ISO14443ASelectFirst() and ISO14443ASelectNext(). +// +#define ISO14443A_REQA 0x26 +// +// WUPA command, which wakes cards from IDLE or HALT state and puts them +// into READY or READY* state. One of the two possible parameters for +// \e ucCmd in ISO14443ASelectFirst() and ISO14443ASelectNext(). +// +#define ISO14443A_WUPA 0x52 + +extern void ISO14443ASetupRegisters(void); +extern void ISO14443APowerOn(void); +extern void ISO14443APowerOff(void); +extern void ISO14443AHalt(void); +extern int ISO14443AREQA(unsigned char ucCmd, int *piATQA); +extern int ISO14443ASelect(unsigned char const *pucUID, + unsigned int uiUIDLength, unsigned char *pucSAK); +extern int ISO14443ASelectFirst(unsigned char ucCmd, unsigned char *pucUID, + unsigned int *puiUIDLength, + unsigned char *pucSAK); +extern int ISO14443ASelectNext(unsigned char ucCmd, unsigned char *pucUID, + unsigned int *puiUIDLength, + unsigned char *pucSAK); +extern int ISO14443ACheckParity(const unsigned short * const pusData, + const long lLen); +extern void ISO14443ACalculateParity(unsigned short * const pusData, + const long lLen); +extern int ISO14443ACheckCRC(const unsigned short * const pusData, + const long lLen); +extern long ISO14443ACalculateCRC(unsigned short * const pusData, const long lSize); + +extern int ISO14443RATS(unsigned char ucFSDI, unsigned char ucCID, unsigned char *pucATS); +extern int ISO14443PPS(unsigned char ucCID, unsigned char ucDRI, unsigned char ucDSI); +extern int ISO14443DESELECT(unsigned char ucCID); +#endif diff --git a/nfclib/iso14443b.c b/nfclib/iso14443b.c new file mode 100644 index 0000000..a7cd7fb --- /dev/null +++ b/nfclib/iso14443b.c @@ -0,0 +1,339 @@ +//***************************************************************************** +// +// iso14443B.c - ISO 14443B implementation. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_types.h" +#include "driverlib/sysctl.h" +#include "trf79x0.h" +#include "iso14443b.h" + +//***************************************************************************** +// +// Set up registers for ISO 14443 B 106Kbit/s operation. This function must +// be called after initializing the TRF79x0 (for example with TRF79x0Init() +// or TRF79x0Command() with argument \b TRF79X0_SOFT_INIT_CMD) and before +// calling any of the other ISO14443B functions. +// +//***************************************************************************** +void +ISO14443BSetupRegisters(void) +{ + // + // Set the ISO format to ISO1443B 106Kbps. + // + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, + TRF79X0_ISO_CONTROL_14443B_106K); + + // + // Set the TX pulse to 106ns (0x20 * 73.7ns). + // + TRF79x0WriteRegister(TRF79X0_TX_PULSE_LENGTH_CTRL_REG, 0x20); + + // + // Set the RX No response wait time to 529us (0xe * 37.76us). + // + TRF79x0WriteRegister(TRF79X0_RX_NO_RESPONSE_WAIT_REG, 0x0e); + + // + // Set the RX wait time to 66us (7 * 9.44us). + // + TRF79x0WriteRegister(TRF79X0_RX_WAIT_TIME_REG, 0x07); + + // + // Set the SYSCLK to 6.78MHz and the Modulation 10% ASK. + // + TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG, TRF79X0_MOD_CTRL_SYS_CLK_6_78MHZ); + + // + // Configure the Special Settings Register. + // + TRF79x0WriteRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG, + (TRF79x0ReadRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG) & 0x0f) | + TRF79X0_RX_SP_SET_M848); + + // + // Configure the Test Settings Register. + // + TRF79x0WriteRegister(TRF79X0_TEST_SETTING1_REG, 0x20); + + + // + // Set the regulator voltage to be automatic. + // + TRF79x0WriteRegister(TRF79X0_REGULATOR_CONTROL_REG, + TRF79X0_REGULATOR_CTRL_AUTO_REG); +} + +//***************************************************************************** +// +// Power on the field and wait for a time that is long enough to guarantee +// that all cards in the field will be initialized. +// +//***************************************************************************** +void +ISO14443BPowerOn(void) +{ + unsigned char ucReg; + + // + // Enable RF field and receiver. + // + ucReg = TRF79x0ReadRegister(TRF79X0_CHIP_STATUS_CTRL_REG); + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, + ucReg | TRF79X0_STATUS_CTRL_RF_ON); + + // + // Wait 5ms (as per ISO 14443-3 clause 5). + // + SysCtlDelay(((SysCtlClockGet() / 3) * 5) / 1000); +} + +//***************************************************************************** +// +// Power off the field and wait for some time. +// +//***************************************************************************** +void +ISO14443BPowerOff(void) +{ + unsigned char ucReg; + + // + // Disable RF field and receiver. + // + ucReg = TRF79x0ReadRegister(TRF79X0_CHIP_STATUS_CTRL_REG); + + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, + ucReg & ~TRF79X0_STATUS_CTRL_RF_ON); + + // + // Wait 5ms. + // + SysCtlDelay(((SysCtlClockGet() / 3) * 5) / 1000); +} + +int +ISO14443BHalt(unsigned char *pucPUPI) +{ + unsigned char ucResponse; + unsigned int uiRxSize; + + // + // HLTA command. + // + unsigned char pucHLTA[5]; + + pucHLTA[0] = 0x50; + pucHLTA[1] = pucPUPI[0]; + pucHLTA[2] = pucPUPI[1]; + pucHLTA[3] = pucPUPI[2]; + pucHLTA[4] = pucPUPI[3]; + + TRF79x0Transceive(pucHLTA, sizeof(pucHLTA), 0, &ucResponse, &uiRxSize, NULL, + TRF79X0_TRANSCEIVE_CRC); + + // + // Valid answer to HLTB received. + if((uiRxSize == 1) && (ucResponse == 0)) + return(uiRxSize); + else + return(0); +} + +//***************************************************************************** +// +// Transceive ISO 14443-B SlotMARKER command. +// +// \param ucSlot is the slot number for the following operations, must between 1~15 +// +//***************************************************************************** +void +ISO14443BSlotMARKER(unsigned char ucSlot) +{ + unsigned char ucAPn; + + ucAPn = (ucSlot << 4) | 0x05; + + TRF79x0Transceive(&ucAPn, 1, 0, 0, 0, 0, TRF79X0_TRANSCEIVE_CRC); +// TRF79x0Send(&ucAPn, 1, 0, TRF79X0_TRANSCEIVE_TX_CRC); +} + +//***************************************************************************** +// +// Transceive ISO 14443-B REQB type command. +// +// \param ucCmd is the command, either \b ISO14443B_REQB or \b ISO14443B_WUPB +// \param ucAFI is the application family identifier +// \param ucN is the number of the time slot that was used in anticollision process, +// must between 0 ~ 4 +// \param piATQB is a pointer to an integer to store the received ATQB and +// will be set to -1 if a collision occurred. +// +// \return true if at least one card responded and false +// otherwise. +// +// \note User code usually does not need to call this function since it is +// implicitly called in ISO14443BSelect(), ISO14443bSelectFirst() or +// ISO14443BSelectNext(). +// +//***************************************************************************** +int +ISO14443BREQB(unsigned char ucCmd, unsigned char ucAFI, unsigned char ucN, + unsigned char *pucATQB, unsigned int *puiATQBSize) +{ + unsigned char pucREQB[3]; + unsigned char pucResponse[12]; + unsigned int uiRxSize; + int i, slotTotal, slot; + + switch(ucN) + { + case 0: slotTotal = 1; break; + case 1: slotTotal = 2; break; + case 2: slotTotal = 4; break; + case 3: slotTotal = 8; break; + case 4: slotTotal = 16; break; + default: slotTotal = 1; break; + } + + uiRxSize = sizeof(pucResponse); + pucResponse[0] = 0; + + // + // Transmit WUPB/REQB, receive ATQB. + // + pucREQB[0] = 0x05; + pucREQB[1] = ucAFI; + pucREQB[2] = ucCmd | ucN; + + // + // the first byte of ATQB is 0x50 + // + TRF79x0Transceive(pucREQB, sizeof(pucREQB), 0, pucResponse, &uiRxSize, 0, + TRF79X0_TRANSCEIVE_CRC); + + // + // check if needing to scan slot + // + slot = 1; + while((pucResponse[0] != 0x50) && (slot < slotTotal)) + { + uiRxSize = 12; + pucResponse[0] = 0; + + TRF79x0IRQClearCauses(TRF79X0_WAIT_RXEND); + // + // the order of the two function must not reversed, because the TRF79x0ReceiveAgain() called + // TRF79x0Command(TRF79X0_RESET_FIFO_CMD); to reset receive FIFO + // the most important action TRF79x0ReceiveAgain() done is to set g_sRXState.uiMaxLength as uiRxSize + // in order to enable the TRF7970A interrupt to continue receive data + // + ISO14443BSlotMARKER(slot++); + TRF79x0ReceiveAgain(pucResponse, &uiRxSize); + }; + + // + // Valid ATQB received. Was transmitted LSByte first. + // + if(pucResponse[0] == 0x50) + { + if(pucATQB != NULL) + { + for(i = 0; i < uiRxSize; i++) + pucATQB[i] = pucResponse[i]; + } + *puiATQBSize = uiRxSize; + + // + // Return true + // + return(1); + } + else + { + // + // No response at all + // + return(0); + } +} + +//***************************************************************************** +// +// Transceive ISO 14443-3 ATTRIB command. +// EOF_SOF indicate the PCD capability to support suppression of the EOF and/or SOF +// from PICC to PCD, which may reduce communication overhead. +// The suppression of EOF and/or SOF is optional for the PICC. +// SOF/EOF suppression applies only for communications at fc / 128 (~ 106 kbit/s). +// For bit rates higher than fc / 128 (~ 106 kbit/s) the PICC shall always provide SOF and EOF +// EOF_SOF set 0 indicate SOF&EOF required. +// +// ucTR1 & ucTR0 must between 0~2 +// +//***************************************************************************** +int +ISO14443BATTRIB(unsigned char *pucPUPI, unsigned char ucTR0, unsigned char ucTR1, unsigned char ucEOF_SOF, + unsigned char ucMaxFrameSize, unsigned char ucBitRateD2C, unsigned char ucBitRateC2D, + unsigned char ucProtocolType, unsigned char ucCID, unsigned char *A2ATTRIB) +{ + unsigned char pucResponse[3]; + unsigned int uiRxSize; + unsigned char pucATTRIB[9]; + + uiRxSize = sizeof(pucResponse); + + // + // ATTRIB command. + // + pucATTRIB[0] = 0x1D; + pucATTRIB[1] = pucPUPI[0]; + pucATTRIB[2] = pucPUPI[1]; + pucATTRIB[3] = pucPUPI[2]; + pucATTRIB[4] = pucPUPI[3]; + pucATTRIB[5] = (ucTR0 << 6) | (ucTR1 << 4) | (ucEOF_SOF << 3) | (ucEOF_SOF << 2); + pucATTRIB[6] = (ucBitRateC2D << 6) | (ucBitRateD2C << 4) | ucMaxFrameSize; + pucATTRIB[7] = ucProtocolType & 0x0F; + pucATTRIB[8] = ucCID & 0x0F; + + // + // Transmit ATTRIB without higher layer INF, receive answer to ATTRIB command without higher layer response + // + TRF79x0Transceive(pucATTRIB, sizeof(pucATTRIB), 0, pucResponse, &uiRxSize, NULL, + TRF79X0_TRANSCEIVE_CRC); + + if(uiRxSize == 1 ) + { + // + // Valid answer to ATTRIB command received, return it as an char, uiRxSize NOT including two CRC bytes + // + if(A2ATTRIB != NULL) + *A2ATTRIB = pucResponse[0]; + + return(uiRxSize); + } + else + { + return(0); + } +} diff --git a/nfclib/iso14443b.h b/nfclib/iso14443b.h new file mode 100644 index 0000000..16b2e3c --- /dev/null +++ b/nfclib/iso14443b.h @@ -0,0 +1,51 @@ +//***************************************************************************** +// +// iso14443b.h - ISO 14443B implementation. +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __ISO14443B_H__ +#define __ISO14443B_H__ + +// +// REQB command, which wakes cards from IDLE state and puts them into READY +// state. One of the two possible parameters for \e ucCmd in +// ISO14443BSelectFirst() and ISO14443BSelectNext(). +// +#define ISO14443B_REQB 0x00 +// +// WUPB command, which wakes cards from IDLE or HALT state and puts them +// into READY or READY* state. One of the two possible parameters for +// \e ucCmd in ISO14443BSelectFirst() and ISO14443BSelectNext(). +// +#define ISO14443B_WUPB 0x08 + +extern void ISO14443BSetupRegisters(void); +extern void ISO14443BPowerOn(void); +extern void ISO14443BPowerOff(void); +extern int ISO14443BHalt(unsigned char *pucPUPI); +extern int ISO14443BREQB(unsigned char ucCmd, unsigned char ucAFI, unsigned char ucN, + unsigned char *pucATQB, unsigned int *puiATQBSize ); +extern int ISO14443BATTRIB(unsigned char *pucPUPI, unsigned char ucTR0, unsigned char ucTR1, unsigned char ucEOF_SOF, + unsigned char ucMaxFrameSize, unsigned char ucBitRateD2C, unsigned char ucBitRateC2D, + unsigned char ucProtocolType, unsigned char ucCID, unsigned char *A2ATTRIB); + +#endif diff --git a/nfclib/iso15693.c b/nfclib/iso15693.c new file mode 100644 index 0000000..dc32578 --- /dev/null +++ b/nfclib/iso15693.c @@ -0,0 +1,694 @@ +//***************************************************************************** +// +// iso15693.c - The top level API used to communicate with ISO15063 cards. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +//***************************************************************************** + +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/sysctl.h" +#include "trf79x0.h" +#include "nfclib/iso15693.h" + +extern struct +{ + // + // The actual string of bytes in the UID. + // + unsigned char pucUID[UID_SIZE]; + + // + // The number of valid bytes in the pucUID variable. + // + unsigned long ulUIDSize; + + // + // The ASCII string that is used to display the UID on the screen. + // + char pcUIDStr[CARD_LABEL_SIZE]; + + unsigned char ucSlot; +}g_sCard_15693[16]; + +//***************************************************************************** +// +// Command/Response and transmit/receive buffer. +// +//***************************************************************************** +static unsigned char g_pucCmd[16]; + +//***************************************************************************** +// +// The value that is written to the block if the "Erase" button is pressed. +// This will invalidate the block. +// +//***************************************************************************** +static const unsigned char g_pucValueEmpty[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char ucCardFound = 0; + +//***************************************************************************** +// +// Set up registers for ISO 15693 operation. This function must +// be called after initializing the TRF79x0 (for example with TRF79x0Init() +// or TRF79x0Command() with argument \b TRF79X0_SOFT_INIT_CMD) and before +// calling any of the other ISO15963 functions. +// +//***************************************************************************** +void +ISO15693SetupRegisters(void) +{ + // actually, we can just use the default setting +#if 0 + // + // Set the TX pulse to 9.44us (0x80 * 73.7ns). + // + TRF79x0WriteRegister(TRF79X0_TX_PULSE_LENGTH_CTRL_REG, 0x80); + + // + // Set the RX No response wait time to 529us (0xe * 37.76us). + // + TRF79x0WriteRegister(TRF79X0_RX_NO_RESPONSE_WAIT_REG, 0x0e); + + // + // Set the RX wait time to 293us (0x20 * 9.44us). + // + TRF79x0WriteRegister(TRF79X0_RX_WAIT_TIME_REG, 0x20); + + // + // Configure the Special Settings Register. + // + TRF79x0WriteRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG, + (TRF79x0ReadRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG) & 0x0f) | + TRF79X0_RX_SP_SET_C424); + + // + // Configure the Test Settings Register. + // + TRF79x0WriteRegister(TRF79X0_TEST_SETTING1_REG, 0x20); +#endif + + // + // Set the SYSCLK to 6.78MHz and the Modulation Depth to OOK. + // + TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG, + (TRF79X0_MOD_CTRL_SYS_CLK_6_78MHZ | + TRF79X0_MOD_CTRL_MOD_ASK_10)); + + // + // Set the regulator voltage to be automatic. + // + TRF79x0WriteRegister(TRF79X0_REGULATOR_CONTROL_REG, + TRF79X0_REGULATOR_CTRL_AUTO_REG); + + // + // Set the regulator voltage to be automatic. + // + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, + TRF79X0_STATUS_CTRL_RF_ON | TRF79X0_STATUS_CTRL_RF_PWR_FULL + | TRF79X0_STATUS_CTRL_5V_OPERATION); + + // + // Set the ISO format to ISO15693 high bit rate, 26.48 kbps, one subcarrier, 1 out of 4 + // + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, + TRF79X0_ISO_CONTROL_15693_HIGH_1SUB_1OUT4); +} + +//***************************************************************************** +// +//! Initializes the ISO15693 utility functions. +//! +//! This function prepares the ISO1593 utility functions so that they are +//! prepared for the remaining ISO1593 calls. This function must be called once +//! before calling any other ISO1593 functions. +//! +//! \return None. +// +//***************************************************************************** +void +ISO15693Init(void) +{ + // + // Initialize RFID hardware. + // + TRF79x0Init(); + + // + // Set up ISO 15693 operation. + // + ISO15693SetupRegisters(); +} + +void +ISO15693NextSlot(void) +{ + TRF79x0Command(TRF79X0_STOP_DECODERS_CMD); + TRF79x0Command(TRF79X0_RUN_DECODERS_CMD); + TRF79x0Command(TRF79X0_RESET_FIFO_CMD); + TRF79x0Command(TRF79X0_TRANSMIT_NEXT_SLOT_CMD); +} + +// +// \ucSubCarrier, +// 0 A single sub-carrier frequency shall be used by the VICC +// 1 Two sub-carriers shall be used by the VICC +// \ucDataRate +// 0 Low data rate shall be used +// 1 High data rate shall be used +// \ucNbSlots +// 0 16 slots +// 1 1 slot +// +int +ISO15693InventoryAFI(unsigned char ucSubCarrier, unsigned char ucDataRate, + unsigned char ucAfi, unsigned char ucNbSlots, + unsigned char *pucMask, unsigned char ucMaskLen) +{ + unsigned char pucResponse[10]; + unsigned int uiRxSize, i, slot; + + uiRxSize = 10; + + // + // Prepare Inventory command. + // + // b5 AFI_flag + // 0 AFI Field is not present + // 1 AFI Field is present + g_pucCmd[0] = (ucNbSlots << 5) | (0x1 << 4) | (0x1 << 2) | (ucDataRate << 1) | ucSubCarrier; + g_pucCmd[1] = 0x01; // Command Code = 0x01 ---> Inventory + g_pucCmd[2] = ucAfi; + g_pucCmd[3] = ucMaskLen; + + // + // Transmit Inventory, receive response + // + TRF79x0Transceive(g_pucCmd, 4, 0, pucResponse, &uiRxSize, 0, TRF79X0_TRANSCEIVE_CRC); + + // + // check if needing to scan slot + // + slot = 1; + while((uiRxSize != 10) && (slot < 16)) + { + uiRxSize = sizeof(pucResponse); + + TRF79x0IRQClearCauses(TRF79X0_WAIT_RXEND); + // + // the order of the two function must not reversed, because the TRF79x0ReceiveAgain() called + // TRF79x0Command(TRF79X0_RESET_FIFO_CMD); to reset receive FIFO + // the most important action TRF79x0ReceiveAgain() done is to set g_sRXState.uiMaxLength as uiRxSize + // in order to enable the TRF7970A interrupt to continue receive data + // + ISO15693NextSlot(); + TRF79x0ReceiveAgain(pucResponse, &uiRxSize); + slot++; + }; + + if(uiRxSize == 10 ) + { + // + // Valid answer to Inventory command received, return it as an char, uiRxSize NOT including two CRC bytes + // the first 2 byte in pucResponse is Flags & DSFI, the last 8 bytes is UID + // + if(pucMask != NULL) + { + for(i = 0; i < 8; i++) + pucMask[i] = pucResponse[2 + i]; + } + return(uiRxSize); + } + else + { + return(0); + } +} + + +// +// \ucSubCarrier, +// 0 A single sub-carrier frequency shall be used by the VICC +// 1 Two sub-carriers shall be used by the VICC +// \ucDataRate +// 0 Low data rate shall be used +// 1 High data rate shall be used +// \ucNbSlots +// 0 16 slots +// 1 1 slot +// +int +ISO15693Inventory(unsigned char ucSubCarrier, unsigned char ucDataRate, + unsigned char ucNbSlots, unsigned char *pucMask, unsigned char ucMaskLen) +{ + unsigned char pucResponse[10]; + unsigned char uiRxSize, i, slot; + + uiRxSize = sizeof(pucResponse); + + // + // Prepare Inventory command. + // + // b5 AFI_flag + // 0 AFI Field is not present + // 1 AFI Field is present + g_pucCmd[0] = (ucNbSlots << 5) | (0x1 << 2) | (ucDataRate << 1) | ucSubCarrier; + g_pucCmd[1] = 0x01; // Command Code = 0x01 ---> Inventory + g_pucCmd[3] = ucMaskLen; + + // + // Transmit Inventory, receive response + // + TRF79x0Transceive(g_pucCmd, 3, 0, pucResponse, &uiRxSize, 0, TRF79X0_TRANSCEIVE_CRC); + + // + // check if needing to scan slot + // + slot = 1; + while((uiRxSize != 10) && (slot < 16)) + { + uiRxSize = sizeof(pucResponse); + + TRF79x0IRQClearCauses(TRF79X0_WAIT_RXEND); + // + // the order of the two function must not reversed, because the TRF79x0ReceiveAgain() called + // TRF79x0Command(TRF79X0_RESET_FIFO_CMD); to reset receive FIFO + // the most important action TRF79x0ReceiveAgain() done is to set g_sRXState.uiMaxLength as uiRxSize + // in order to enable the TRF7970A interrupt to continue receive data + // + ISO15693NextSlot(); + TRF79x0ReceiveAgain(pucResponse, &uiRxSize); + slot++; + }; + + if(uiRxSize == 10 ) + { + // + // Valid answer to Inventory command received, return it as an char, uiRxSize NOT including two CRC bytes + // + if(pucMask != NULL) + { + for(i = 0; i < uiRxSize; i++) + pucMask[i] = pucResponse[i]; + } + return(uiRxSize); + } + else + { + return(0); + } +} + +int +ISO15693Anticollision16Slots(unsigned char ucSubCarrier, unsigned char ucDataRate, + unsigned char *pucMask, unsigned char ucMaskLen) +{ + unsigned char pucResponse[10], ucMaskNew[8]; + unsigned int uiRxSize, uiTxSize, i, slot = 0; + unsigned int uiFlagCollision = 0, uiSlotCollision = 0; + + uiRxSize = sizeof(pucResponse); + + // + // Prepare Inventory command. + // + // b5 AFI_flag + // 0 AFI Field is not present + // 1 AFI Field is present + g_pucCmd[0] = (0x1 << 2) | (ucDataRate << 1) | ucSubCarrier; + g_pucCmd[1] = 0x01; // Command Code = 0x01 ---> Inventory + g_pucCmd[2] = ucMaskLen; + + uiTxSize = 3 + (((ucMaskLen >> 2) + 1) >> 1); + + if(uiTxSize > 3) + { + for(i = 0; i < (uiTxSize - 3); i++) + g_pucCmd[3 + i] = pucMask[i]; + } + + // + // Transmit Inventory, receive response + // + TRF79x0Transceive(g_pucCmd, uiTxSize, 0, pucResponse, &uiRxSize, 0, TRF79X0_TRANSCEIVE_CRC); + + // + // check if needing to scan slot + // + while(slot < 16) + { + if(TRF79x0IsCollision() == 1) + { + uiFlagCollision = 1; + uiSlotCollision = slot -1; + } + else if(uiRxSize == 10) + { + for(i = 0; i < 8; i++) + g_sCard_15693[ucCardFound + 1].pucUID[i] = pucResponse[2 + i]; + g_sCard_15693[ucCardFound + 1].ucSlot = slot; + ucCardFound++; + } + + uiRxSize = sizeof(pucResponse); + + TRF79x0IRQClearCauses(TRF79X0_WAIT_RXEND); + + // + // the order of the two function must not reversed, because the TRF79x0ReceiveAgain() called + // TRF79x0Command(TRF79X0_RESET_FIFO_CMD); to reset receive FIFO + // the most important action TRF79x0ReceiveAgain() done is to set g_sRXState.uiMaxLength as uiRxSize + // in order to enable the TRF7970A interrupt to continue receive data + // + ISO15693NextSlot(); + TRF79x0ReceiveAgain(pucResponse, &uiRxSize); + slot++; + }; + + // + // only do ones cascade + // TODO: NEED refer to the msp430 version to make more cascade anticollision function + // + if(uiFlagCollision && (ucMaskLen < 4)) + { + uiFlagCollision = 0; + ucMaskNew[0] = uiSlotCollision; + ISO15693Anticollision16Slots(0, 1, &ucMaskNew[0], ucMaskLen + 4); + } + + if(ucCardFound) + { + ucCardFound = 0; + return(1); + } + else + { + return(0); + } +} + +int +ISO15693StayQuiet(unsigned char *pucUID) +{ + int i; + + // + // Prepare Stay Quiet command. + // + // b6 Address_flag the bit sequence start from b1 not b0! + // 1 Request is addressed. UID field is included. It shall be executed only + // by the VICC whose UID matches the UID specified in the request. + g_pucCmd[0] = (1 << 5) | (1 << 1); + // Command Code = 0x02 ---> Stay Quiet + g_pucCmd[1] = 0x02; + for(i = 0; i < 8; i++) + g_pucCmd[2 + i] = pucUID[i]; + + // + // Transmit Stay Quiet command, receive response + // + TRF79x0Transceive(g_pucCmd, 10, 0, 0, 0, 0, TRF79X0_TRANSCEIVE_TX_CRC); +} + +//***************************************************************************** +// +// ! Reads a single block data from the selected card. +// ! +// ! \param uiBlock is the address of the block to read. +// ! \param pucBuf is the output buffer to store the raw block contents into. +// ! This buffer must be able to store at least 32 bytes. +// ! +// ! This function reads a ISO15693 block and returns the full contents +// ! of the block with no interpretation of the bytes. The function will +// ! return the number of valid bytes stored in the \e pucBuf parameter. +// ! +// +//***************************************************************************** +int +BlockReadSingleUID(unsigned char *pucUID, unsigned int uiBlock, unsigned char *pucBuf) +{ + unsigned char pucCmd[11]; + unsigned int uiRxBytes; + unsigned int uiRxBits; + int i; + + // + // Reading 32 bytes and 0 bits. + // + uiRxBytes = 32; + uiRxBits = 0; + + // + // Prepare Read Single Block command. + // + // b6 Address_flag the bit sequence start from b1 not b0! + // 1 Request is addressed. UID field is included. It shall be executed only + // by the VICC whose UID matches the UID specified in the request. + // b7 Option_flag + // 1 Meaning is defined by the command description + pucCmd[0] = (1 << 6) |(1 << 5) | (1 << 1); + // Command Code = 0x20 ---> Read Single Block + pucCmd[1] = 0x20; + for(i = 0; i < 8; i++) + pucCmd[2 + i] = pucUID[i]; + pucCmd[10] = uiBlock; + + // + // Transmit Read Single Block, receive response + // + TRF79x0Transceive(pucCmd, sizeof(pucCmd), 0, pucBuf, &uiRxBytes, &uiRxBits, TRF79X0_TRANSCEIVE_CRC); + if(uiRxBytes == 0) + { + return(0); + } + + return(uiRxBytes); +} + +int +BlockReadSingle(unsigned int uiBlock, unsigned char *pucBuf) +{ + unsigned char pucCmd[3]; + unsigned int uiRxBytes; + unsigned int uiRxBits; + int i; + + // + // Reading 32 bytes and 0 bits. + // + uiRxBytes = 32; + uiRxBits = 0; + + // + // Prepare Read Single Block command. + // + // b7 Option_flag + // 1 Meaning is defined by the command description + pucCmd[0] = (1 << 6) | (1 << 1); + // Command Code = 0x20 ---> Read Single Block + pucCmd[1] = 0x20; + pucCmd[2] = uiBlock; + + // + // Transmit Read Single Block, receive response + // + TRF79x0Transceive(pucCmd, sizeof(pucCmd), 0, pucBuf, &uiRxBytes, &uiRxBits, TRF79X0_TRANSCEIVE_CRC); + if(uiRxBytes == 0) + { + return(0); + } + + return(uiRxBytes); +} +//***************************************************************************** +// +// ! Write a single block data to the selected card. +// ! +// ! \param uiBlock is the address of the block to write. +// ! \param pucBuf is the input buffer to store the raw block contents into. +// ! This buffer must be able to store at least 32 bytes. +// ! +// ! This function write a ISO15693 block +// ! +// +//***************************************************************************** +int +BlockWriteSingleUID(unsigned char *pucUID, unsigned int uiBlock, unsigned char ucValueLen, unsigned char *pucBuf) +{ + unsigned char pucCmd[43]; + unsigned char pucResponse[2]; + unsigned int uiRxBytes; + int i; + + // + // transmit bytes as most + // + uiRxBytes = 2; + + // + // Prepare Write Single Block command. + // + // b6 Address_flag the bit sequence start from b1 not b0! + // 1 Request is addressed. UID field is included. It shall be executed only + // by the VICC whose UID matches the UID specified in the request. + + // b7 Option_flag must be set for Write & Lock command + // 1 Meaning is defined by the command description + pucCmd[0] = (1 << 6) | (1 << 5) | (1 << 1); + // Command Code = 0x21 ---> Write Single Block + pucCmd[1] = 0x21; + for(i = 0; i < 8; i++) + pucCmd[2 + i] = pucUID[i]; + pucCmd[10] = uiBlock; + for(i = 0; i < ucValueLen; i++) + pucCmd[11 + i] = pucBuf[i]; + + // + // Transmit Read Single Block, receive response + // + TRF79x0TransceiveISO15693(pucCmd, 11 + ucValueLen, 0, pucResponse, &uiRxBytes, 0, TRF79X0_TRANSCEIVE_CRC); + if(uiRxBytes == 0) + { + return(0); + } + + return(uiRxBytes); +} + +int +BlockWriteSingle(unsigned int uiBlock, unsigned char ucValueLen, unsigned char *pucBuf) +{ + unsigned char pucCmd[7]; + unsigned char pucResponse[2]; + unsigned int uiRxBytes; + int i; + + // + // transmit bytes as most + // + uiRxBytes = 2; + + // + // Prepare Write Single Block command. + // + // b6 Address_flag the bit sequence start from b1 not b0! + // 1 Request is addressed. UID field is included. It shall be executed only + // by the VICC whose UID matches the UID specified in the request. + + // b7 Option_flag must be set for Write & Lock command + // 1 Meaning is defined by the command description + pucCmd[0] = (1 << 6) | (1 << 1); + // Command Code = 0x21 ---> Write Single Block + pucCmd[1] = 0x21; + pucCmd[2] = uiBlock; + for(i = 0; i < ucValueLen; i++) + pucCmd[3 + i] = pucBuf[i]; + + // + // Transmit Read Single Block, receive response + // + TRF79x0TransceiveISO15693(pucCmd, 3 + ucValueLen, 0, pucResponse, &uiRxBytes, 0, TRF79X0_TRANSCEIVE_CRC); + if(uiRxBytes == 0) + { + return(0); + } + + return(uiRxBytes); +} + +int +BlockLockSingleUID(unsigned char *pucUID, unsigned int uiBlock, unsigned char *pucResponse) +{ + unsigned char pucCmd[11]; + unsigned int uiRxBytes; + unsigned int uiRxBits; + int i; + + // + // Reading 32 bytes and 0 bits. + // + uiRxBytes = 2; + uiRxBits = 0; + + // + // Prepare Read Single Block command. + // + // b6 Address_flag the bit sequence start from b1 not b0! + // 1 Request is addressed. UID field is included. It shall be executed only + // by the VICC whose UID matches the UID specified in the request. + // b7 Option_flag must be set for Write & Lock command + // 1 Meaning is defined by the command description + pucCmd[0] = (1 << 6) | (1 << 5) | (1 << 1); + // Command Code = 0x22 ---> Lock Single Block + pucCmd[1] = 0x22; + for(i = 0; i < 8; i++) + pucCmd[2 + i] = pucUID[i]; + pucCmd[10] = uiBlock; + + // + // Transmit Read Single Block, receive response + // + TRF79x0TransceiveISO15693(pucCmd, sizeof(pucCmd), 0, pucResponse, &uiRxBytes, &uiRxBits, TRF79X0_TRANSCEIVE_CRC); + if(uiRxBytes == 0) + { + return(0); + } + + return(uiRxBytes); +} + +int +BlockLockSingle(unsigned int uiBlock, unsigned char *pucResponse) +{ + unsigned char pucCmd[3]; + unsigned int uiRxBytes; + unsigned int uiRxBits; + int i; + + // + // Reading 32 bytes and 0 bits. + // + uiRxBytes = 2; + uiRxBits = 0; + + // + // Prepare Read Single Block command. + // + + pucCmd[0] = (1 << 6) | (1 << 1); + // Command Code = 0x22 ---> Lock Single Block + pucCmd[1] = 0x22; + pucCmd[2] = uiBlock; + + // + // Transmit Read Single Block, receive response + // + TRF79x0TransceiveISO15693(pucCmd, sizeof(pucCmd), 0, pucResponse, &uiRxBytes, &uiRxBits, TRF79X0_TRANSCEIVE_CRC); + if(uiRxBytes == 0) + { + return(0); + } + + return(uiRxBytes); +} diff --git a/nfclib/iso15693.h b/nfclib/iso15693.h new file mode 100644 index 0000000..574b199 --- /dev/null +++ b/nfclib/iso15693.h @@ -0,0 +1,62 @@ +//***************************************************************************** +// +// iso15693.h - The top level API used to communicate with MIFARE cards. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +//***************************************************************************** + +#ifndef __ISO15693_H__ +#define __ISO15693_H__ + +//***************************************************************************** +// +// The maximum size in bytes of a card's UID in ASCII. +// +//***************************************************************************** +#define UID_SIZE 8 + + +//***************************************************************************** +// +// The size in card label string for the card's UID. Two characters per byte +// and a spot for a null terminator. +// +//***************************************************************************** +#define CARD_LABEL_SIZE (6 + (UID_SIZE * 2) + 1) + +extern void ISO15693Init(void); +extern int ISO15693InventoryAFI(unsigned char ucSubCarrier, unsigned char ucDataRate, + unsigned char ucAfi, unsigned char ucNbSlots, + unsigned char *pucMask, unsigned char ucMaskLen); +extern int ISO15693Inventory(unsigned char ucSubCarrier, unsigned char ucDataRate, + unsigned char ucNbSlots, unsigned char *pucMask, unsigned char ucMaskLen); +extern int ISO15693Anticollision16Slots(unsigned char ucSubCarrier, unsigned char ucDataRate, + unsigned char *pucMask, unsigned char ucMaskLen); + +extern int BlockReadSingleUID(unsigned char *pucUID, unsigned int uiBlock, unsigned char *pucBuf); +extern int BlockWriteSingleUID(unsigned char *pucUID, unsigned int uiBlock, unsigned char ucValueLen, unsigned char *pucBuf); +extern int BlockLockSingleUID(unsigned char *pucUID, unsigned int uiBlock, unsigned char *pucResponse); + +extern int BlockReadSingle(unsigned int uiBlock, unsigned char *pucBuf); +extern int BlockWriteSingle(unsigned int uiBlock, unsigned char ucValueLen, unsigned char *pucBuf); +extern int BlockLockSingle(unsigned int uiBlock, unsigned char *pucResponse); + +extern int ISO15693StayQuiet(unsigned char *pucUID); + +#endif + diff --git a/nfclib/llcp.c b/nfclib/llcp.c new file mode 100644 index 0000000..9a878ea --- /dev/null +++ b/nfclib/llcp.c @@ -0,0 +1,1051 @@ +//***************************************************************************** +// +// llcp.c - Logic Link Control Protocol : used to send packets via NPP or SNEP +// NPP: NDEF Push Protocol +// SNEP: Simple NDEF Exchange protocol +// NOTE: currently only SNEP is Supported +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#include +#include +#include +#include "utils/uartstdio.h" +#include "nfclib/llcp.h" +#include "nfclib/snep.h" + +//***************************************************************************** +// +//! \addtogroup nfc_llcp_api NFC LLCP API Functions +//! @{ +//! Logical Link Control Protocol is the NFC transport layer used to open and +//! close a virtual link used to transfer NDEFs between two devices in +//! peer-to-peer mode via the Simple NDEF Exchange Protocol. +//! For more information on LLCP, please read the Logical Link Control Protocol +//! Specification Version 1.1. +// +//***************************************************************************** + +//***************************************************************************** +// +// The next PDU to be sent to the destination device - updated based on +// incoming packets or by LLCPSetNextPDU(). +// +//***************************************************************************** +tLLCPPduPtype g_eNextPduQueue = LLCP_SYMM_PDU; + +//***************************************************************************** +// +// Connection Status +// +//***************************************************************************** +tLLCPConnectionStatus g_eLLCPConnectionStatus = LLCP_CONNECTION_IDLE; + +//***************************************************************************** +// +// Destination Service Access Point Address +// +//***************************************************************************** +uint8_t g_ui8dsapValue; + +//***************************************************************************** +// +// Source Service Access Point Address +// +//***************************************************************************** +uint8_t g_ui8ssapValue; + +//***************************************************************************** +// +// Service Name - SNEP by default +// +//***************************************************************************** +tServiceName g_eCurrentServiceEnabled = SNEP_SERVICE; + +//***************************************************************************** +// +// Acknowledged packets +// +//***************************************************************************** +uint8_t g_ui8NSNR = 0x00; + +//***************************************************************************** +// +// Disconnected Mode Reason +// +//***************************************************************************** +tDisconnectModeReason g_eDMReason; + +//***************************************************************************** +// +// LLCP Link Time Out +// +//***************************************************************************** +uint16_t g_ui16LLCPlto = 0x00; + +//***************************************************************************** +// +// LLCP MIUX of the initiator / target communicating with the TRF7970A. +// 248 by default. +// +//***************************************************************************** +uint8_t g_ui8LLCPmiu = 248; + +//***************************************************************************** +// +//! Initializes the Logical Link Control Protocol layer. +//! +//! This function must be called prior to any other function offer by the LLCP +//! driver. This function initializes the acknowledge packets, the current +//! service enabled, and the next PDU for the LLCP_stateMachine(), and also +//! initializes the SNEP layer with SNEP_init(). +//! +//! \return None +//! +// +//***************************************************************************** +void LLCP_init(void) +{ + // + // Reset NS and NR + // + g_ui8NSNR = 0x00; + g_ui8LLCPmiu = 128; + g_eLLCPConnectionStatus = LLCP_CONNECTION_IDLE; + g_eCurrentServiceEnabled = SNEP_SERVICE; + g_eNextPduQueue = LLCP_SYMM_PDU; + SNEP_init(); + SNEP_setMaxPayload(g_ui8LLCPmiu); +} + +//***************************************************************************** +// +//! Gets link timeout +//! +//! This function returns the Link Timeout, which may be modified if the +//! LLCP_processTLV() function processes a LLCP_LTO TLV. +//! +//! \return \e \b g_ui16LLCPlto the link timeout. +// +//***************************************************************************** +uint16_t LLCP_getLinkTimeOut(void) +{ + return g_ui16LLCPlto; +} + +//***************************************************************************** +// +//! Adds a LLCP parameter to the LLCP PDU with the Type Length +//! Value (TLV) format. +//! +//! \param eLLCPparam is the LLCP type that will be added. +//! \param pui8TLVBufferPtr is the pointer where the TLV is written +//! +//! The \e \b eLLCPparam parameter can be any of the following: +//! +//! - \b LLCP_VERSION - Version Number +//! - \b LLCP_MIUX - Maximum Information Unit Extension +//! - \b LLCP_WKS - Well-Known Service List +//! - \b LLCP_LTO - Link Timeout +//! - \b LLCP_RW - Receive Window Size +//! - \b LLCP_SN - Service Name +//! - \b LLCP_OPT - Option +//! - \b LLCP_SDREQ - Service Discovery Request +//! - \b LLCP_SDRES - Service Discovery Response +//! - \b LLCP_ERROR - Reserved (used ro return length of 0) +//! +//! This function is used to add a LLCP Parameter to the LLCP PDU to include +//! more information about the LLCP layer. This function must be called inside +//! LLCP_sendCONNECT(), LLCP_sendCC(), NFCDEP_sendATR_REQ() and +//! NFCDEP_sendATR_RES(). +//! +//! \return ui8PacketLength Length of the LLCP Parameter added to the LLCP. +// +//***************************************************************************** +uint8_t LLCP_addTLV(tLLCPParamaeter eLLCPparam, uint8_t * pui8TLVBufferPtr) +{ + uint8_t ui8PacketLength = 0; + + switch(eLLCPparam) + { + case LLCP_VERSION: + // Type + pui8TLVBufferPtr[0] = (uint8_t) LLCP_VERSION; + // Length + pui8TLVBufferPtr[1] = 0x01; + // Value + pui8TLVBufferPtr[2] = 0x11; // Version 1.1 + break; + case LLCP_MIUX: + // Type + pui8TLVBufferPtr[0] = (uint8_t) LLCP_MIUX; + // Length + pui8TLVBufferPtr[1] = 0x02; + // Value + // 128 + MIUX (120) = MIU (248) + pui8TLVBufferPtr[2] = (LLCP_MIUX_SIZE >> 8) & 0xFF; // MIUX 15:8 + pui8TLVBufferPtr[3] = (uint8_t) LLCP_MIUX_SIZE; // MIUX 7:0 + break; + case LLCP_WKS: + // Type + pui8TLVBufferPtr[0] = (uint8_t) LLCP_WKS; + // Length + pui8TLVBufferPtr[1] = 0x02; + // Value + pui8TLVBufferPtr[2] = 0x00; + pui8TLVBufferPtr[3] = 0x03; + break; + case LLCP_LTO: + // Type + pui8TLVBufferPtr[0] = (uint8_t) LLCP_LTO; + // Length + pui8TLVBufferPtr[1] = 0x01; + // Value + pui8TLVBufferPtr[2] = 0x64; // (100 (0x64) * 10 mS = 1000 mS timeout, Figure 22, LLP) + break; + case LLCP_RW: + // Type + pui8TLVBufferPtr[0] = (uint8_t) LLCP_RW; + // Length + pui8TLVBufferPtr[1] = 0x01; + // Value + // Section 5.6.2.2 LLP + // A receive window size of zero indicates that the local LLC will not + // accept I PDUs on that data link connection. A receive window size of + // one indicates that the local LLC will acknowledge every I PDU before + // accepting additional I PDUs. + // + pui8TLVBufferPtr[2] = 0x04; + break; + case LLCP_SN: + // Type + pui8TLVBufferPtr[0] = (uint8_t) LLCP_SN; + if(g_eCurrentServiceEnabled == NPP_SERVICE) + { + // Length + pui8TLVBufferPtr[1] = 0x0F; + // Value + pui8TLVBufferPtr[2] = 'c'; + pui8TLVBufferPtr[3] = 'o'; + pui8TLVBufferPtr[4] = 'm'; + pui8TLVBufferPtr[5] = '.'; + pui8TLVBufferPtr[6] = 'a'; + pui8TLVBufferPtr[7] = 'n'; + pui8TLVBufferPtr[8] = 'd'; + pui8TLVBufferPtr[9] = 'r'; + pui8TLVBufferPtr[10] = 'o'; + pui8TLVBufferPtr[11] = 'i'; + pui8TLVBufferPtr[12] = 'd'; + pui8TLVBufferPtr[13] = '.'; + pui8TLVBufferPtr[14] = 'n'; + pui8TLVBufferPtr[15] = 'p'; + pui8TLVBufferPtr[16] = 'p'; + } + else if(g_eCurrentServiceEnabled == SNEP_SERVICE) + { + // Length + pui8TLVBufferPtr[1] = 0x0F; + // Value + pui8TLVBufferPtr[2] = 'u'; + pui8TLVBufferPtr[3] = 'r'; + pui8TLVBufferPtr[4] = 'n'; + pui8TLVBufferPtr[5] = ':'; + pui8TLVBufferPtr[6] = 'n'; + pui8TLVBufferPtr[7] = 'f'; + pui8TLVBufferPtr[8] = 'c'; + pui8TLVBufferPtr[9] = ':'; + pui8TLVBufferPtr[10] = 's'; + pui8TLVBufferPtr[11] = 'n'; + pui8TLVBufferPtr[12] = ':'; + pui8TLVBufferPtr[13] = 's'; + pui8TLVBufferPtr[14] = 'n'; + pui8TLVBufferPtr[15] = 'e'; + pui8TLVBufferPtr[16] = 'p'; + + } + break; + case LLCP_OPT: + // Type + pui8TLVBufferPtr[0] = (uint8_t) LLCP_OPT; + // Length + pui8TLVBufferPtr[1] = 0x01; + // Value + pui8TLVBufferPtr[2] = 0x03; // (Class 3) (Table 7, LLP) + break; + case LLCP_SDREQ: + break; + case LLCP_SDRES: + break; + default: + pui8TLVBufferPtr[0] = LLCP_ERROR; + break; + } + + if(pui8TLVBufferPtr[0] == LLCP_ERROR) + ui8PacketLength = 0x00; + else + ui8PacketLength = pui8TLVBufferPtr[1] + 2; + + return ui8PacketLength; +} + +//***************************************************************************** +// +//! Processes the LLCP Parameter TLV. +//! +//! \param pui8TLVBufferPtr is the pointer to the Type value of the TLV. +//! +//! This function processes the LLCP Parameters included in the ATR_RES. This +//! function must be called inside the NFCDEP_processReceivedData(), to +//! initialize the g_ui8LLCPmiu and g_ui16LLCPlto if they are included as part +//! of ATR_RES. +//! +//! \return None +// +//***************************************************************************** +void LLCP_processTLV(uint8_t * pui8TLVBufferPtr) +{ + uint16_t ui16Miu; + switch(pui8TLVBufferPtr[0]) + { + case LLCP_VERSION: + break; + case LLCP_MIUX: + // MIU = 128 + MIUX + ui16Miu = (pui8TLVBufferPtr[2] << 8)+pui8TLVBufferPtr[3]+128; + // Check if the received MIU is less than 248, the modify the current MIU to it + if(ui16Miu < 248) + { + // Modify MIU to be less + g_ui8LLCPmiu = (uint8_t) ui16Miu; + + } + else + { + // Maximum supported MIU is 248 + g_ui8LLCPmiu = 248; + } + + SNEP_setMaxPayload(g_ui8LLCPmiu); + break; + case LLCP_WKS: + break; + case LLCP_LTO: + g_ui16LLCPlto = pui8TLVBufferPtr[2] * 10; + break; + case LLCP_RW: + break; + case LLCP_SN: + break; + case LLCP_OPT: + break; + case LLCP_SDREQ: + break; + case LLCP_SDRES: + break; + default: + break; + } +} + +//***************************************************************************** +// +//! Prepares the LLCP packet to be transmitted. +//! +//! \param pui8PduBufferPtr is the start pointer to add the LLCP PDU. +//! +//! This function is used to add the LLCP portion of the DEP_REQ / DEP_RES PDU. +//! This function must be called inside NFCDEP_sendDEP_REQ() and +//! NFCDEP_sendDEP_RES(). It currently does not support sending the following +//! PDUs : LLCP_PAX_PDU, LLCP_AGF_PDU, LLCP_UI_PDU, LLCP_FRMR_PDU, LLCP_SNL_PDU, +//! LLCP_RNR_PDU, and LLCP_RESERVED_PDU. +//! +//! \return ui8PacketLength is the length of the LLCP PDU added to the +//! pui8PduBufferPtr. +// +//***************************************************************************** +uint8_t LLCP_stateMachine(uint8_t * pui8PduBufferPtr) +{ + uint8_t ui8PacketLength=0; + + switch(g_eNextPduQueue) + { + case LLCP_SYMM_PDU: + { + //UARTprintf("TX: SYMM\n"); + ui8PacketLength = LLCP_sendSYMM(pui8PduBufferPtr); + break; + } + case LLCP_PAX_PDU: + { + break; + } + case LLCP_AGF_PDU: + { + break; + } + case LLCP_UI_PDU: + { + break; + } + case LLCP_CONNECT_PDU: + { + //UARTprintf("TX: CONNECT\n"); + ui8PacketLength = LLCP_sendCONNECT(pui8PduBufferPtr); + g_eNextPduQueue = LLCP_SYMM_PDU; + break; + } + case LLCP_DISC_PDU: + { + if(g_eCurrentServiceEnabled == HANDOVER_SERVICE) + { + g_eCurrentServiceEnabled = SNEP_SERVICE; + } + //UARTprintf("TX: DISC\n"); + ui8PacketLength = LLCP_sendDISC(pui8PduBufferPtr); + break; + } + case LLCP_CC_PDU: + { + //UARTprintf("TX: CC\n"); + ui8PacketLength = LLCP_sendCC(pui8PduBufferPtr); + break; + } + case LLCP_DM_PDU: + { + //UARTprintf("TX: DM\n"); + g_eLLCPConnectionStatus = LLCP_CONNECTION_IDLE; + ui8PacketLength = LLCP_sendDM(pui8PduBufferPtr,g_eDMReason); + g_eNextPduQueue = LLCP_SYMM_PDU; + break; + } + case LLCP_FRMR_PDU: + { + break; + } + case LLCP_SNL_PDU: + { + break; + } + case LLCP_I_PDU: + { + //UARTprintf("TX: I\n"); + ui8PacketLength = LLCP_sendI(pui8PduBufferPtr); + break; + } + case LLCP_RR_PDU: + { + //UARTprintf("TX: RR\n"); + if(g_eCurrentServiceEnabled == HANDOVER_SERVICE) + { + g_eLLCPConnectionStatus = LLCP_CONNECTION_IDLE; + } + ui8PacketLength = LLCP_sendRR(pui8PduBufferPtr); + break; + } + case LLCP_RNR_PDU: + { + break; + } + case LLCP_RESERVED_PDU: + { + break; + } + default: + { + break; + } + } + + return ui8PacketLength; +} + +//***************************************************************************** +// +//! Processes LLCP Data Received. +//! +//! \param pui8RxBuffer is the start pointer of the LLCP data received. +//! \param ui8PduLength is the length of the LLCP PDU received. +//! +//! This function is used to handle the LLCP portion of the DEP_REQ / DEP_RES PDU. +//! This function must be called inside NFCDEP_processReceivedRequest() and +//! NFCDEP_processReceivedData().It currently does not support to handle the +//! following PDUs : LLCP_PAX_PDU, LLCP_AGF_PDU, LLCP_UI_PDU, LLCP_FRMR_PDU, +//! LLCP_SNL_PDU, LLCP_RNR_PDU, and LLCP_RESERVED_PDU. +//! +//! \return \b eLLCPStatus is the boolean status if the command was processed +//! (1) or not (0). +// +//***************************************************************************** +tStatus LLCP_processReceivedData(uint8_t * pui8RxBuffer, uint8_t ui8PduLength) +{ + tLLCPPduPtype ePduType; + tStatus eLLCPStatus = STATUS_SUCCESS; + tSNEPConnectionStatus eSnepProtocolStatus; + + ePduType = (tLLCPPduPtype) ( ((pui8RxBuffer[0] & 0x03) << 2) + + ((pui8RxBuffer[1] & 0xC0) >> 6)); + + switch(ePduType) + { + case LLCP_SYMM_PDU: + if(g_eCurrentServiceEnabled == SNEP_SERVICE) + { + eSnepProtocolStatus = SNEP_getProtocolStatus(); + if((g_eNextPduQueue == LLCP_CONNECT_PDU) || + (g_eNextPduQueue == LLCP_I_PDU)) + { + // + // Do no modify the next PDU + // + } + else if(eSnepProtocolStatus == SNEP_CONNECTION_SEND_COMPLETE) + { + SNEP_setProtocolStatus(SNEP_CONNECTION_IDLE); + //UARTprintf("RX: SYMM "); + g_eNextPduQueue = LLCP_DISC_PDU; + } + else if((eSnepProtocolStatus == + SNEP_CONNECTION_RECEIVED_FIRST_PACKET) || + (eSnepProtocolStatus == + SNEP_CONNECTION_RECEIVE_COMPLETE) || + (eSnepProtocolStatus == + SNEP_CONNECTION_EXCESS_SIZE) || + (eSnepProtocolStatus == + SNEP_CONNECTION_SENDING_N_FRAGMENTS)) + { + //UARTprintf("RX: SYMM "); + g_eNextPduQueue = LLCP_I_PDU; + } + else + { + if(g_eLLCPConnectionStatus != LLCP_CONNECTION_IDLE) + { + g_eNextPduQueue = LLCP_SYMM_PDU; + } + } + } + else if(g_eCurrentServiceEnabled == HANDOVER_SERVICE) + { + if(g_eLLCPConnectionStatus == LLCP_CONNECTION_IDLE) + { + g_eNextPduQueue = LLCP_DISC_PDU; + } + else + g_eNextPduQueue = LLCP_SYMM_PDU; + } + else + { + if(g_eLLCPConnectionStatus != LLCP_CONNECTION_IDLE) + { + //UARTprintf("RX: SYMM "); + g_eNextPduQueue = LLCP_SYMM_PDU; + } + } + break; + case LLCP_PAX_PDU: + // Not Supported + break; + case LLCP_AGF_PDU: + // Not Supported + break; + case LLCP_UI_PDU: + // Not Supported + break; + case LLCP_CONNECT_PDU: + g_ui8dsapValue = (pui8RxBuffer[1] & 0x3F); + + // Check Service Name TLV + if(pui8RxBuffer[2] == 0x06) + { + if (pui8RxBuffer[3] == 0x0F && pui8RxBuffer[4] == 'u' + && pui8RxBuffer[5] == 'r' && pui8RxBuffer[6] == 'n' + && pui8RxBuffer[7] == ':' && pui8RxBuffer[8] == 'n' + && pui8RxBuffer[9] == 'f' && pui8RxBuffer[10] == 'c' + && pui8RxBuffer[11] == ':' && pui8RxBuffer[12] == 's' + && pui8RxBuffer[13] == 'n' && pui8RxBuffer[14] == ':' + && pui8RxBuffer[15] == 's' && pui8RxBuffer[16] == 'n' + && pui8RxBuffer[17] == 'e' && pui8RxBuffer[18] == 'p') + { + // SNEP + g_eCurrentServiceEnabled = SNEP_SERVICE; + } + else if (pui8RxBuffer[3] == 0x0F && pui8RxBuffer[4] == 'c' + && pui8RxBuffer[5] == 'o' && pui8RxBuffer[6] == 'm' + && pui8RxBuffer[7] == '.' && pui8RxBuffer[8] == 'a' + && pui8RxBuffer[9] == 'n' && pui8RxBuffer[10] == 'd' + && pui8RxBuffer[11] == 'r' && pui8RxBuffer[12] == 'o' + && pui8RxBuffer[13] == 'i' && pui8RxBuffer[14] == 'd' + && pui8RxBuffer[15] == '.' && pui8RxBuffer[16] == 'n' + && pui8RxBuffer[17] == 'p' && pui8RxBuffer[18] == 'p') + { + // NPP + g_eCurrentServiceEnabled = NPP_SERVICE; + } + else if (pui8RxBuffer[3] == 0x13 && pui8RxBuffer[4] == 'u' + && pui8RxBuffer[5] == 'r' && pui8RxBuffer[6] == 'n' + && pui8RxBuffer[7] == ':' && pui8RxBuffer[8] == 'n' + && pui8RxBuffer[9] == 'f' && pui8RxBuffer[10] == 'c' + && pui8RxBuffer[11] == ':' && pui8RxBuffer[12] == 's' + && pui8RxBuffer[13] == 'n' && pui8RxBuffer[14] == ':' + && pui8RxBuffer[15] == 'h' && pui8RxBuffer[16] == 'a' + && pui8RxBuffer[17] == 'n' && pui8RxBuffer[18] == 'd' + && pui8RxBuffer[19] == 'o' && pui8RxBuffer[20] == 'v' + && pui8RxBuffer[21] == 'e' && pui8RxBuffer[22] == 'r') + { + // Handover + g_eCurrentServiceEnabled = HANDOVER_SERVICE; + } + else if(ui8PduLength == 2) + { + // SNEP + g_eCurrentServiceEnabled = SNEP_SERVICE; + } + else + { + // // Debug Incoming Request + // while(1); + // Ignore the command + g_eNextPduQueue = LLCP_SYMM_PDU; + break; + } + } + else + g_eCurrentServiceEnabled = SNEP_SERVICE; + + g_eNextPduQueue = LLCP_CC_PDU; + break; + case LLCP_DISC_PDU: + //UARTprintf("RX: DISC "); + g_eDMReason = DM_REASON_LLCP_RECEIVED_DISC_PDU; + g_eNextPduQueue = LLCP_DM_PDU; + break; + case LLCP_CC_PDU: + //UARTprintf("RX: CC "); + g_ui8dsapValue = (pui8RxBuffer[1] & 0x3F); + g_eNextPduQueue = LLCP_I_PDU; + break; + case LLCP_DM_PDU: + //UARTprintf("RX: DM "); + g_eLLCPConnectionStatus = LLCP_CONNECTION_IDLE; + // Reset the snep communication + g_eNextPduQueue = LLCP_SYMM_PDU; + break; + case LLCP_FRMR_PDU: + //UARTprintf("RX: FRMR "); + break; + case LLCP_SNL_PDU: + //UARTprintf("RX: SNL "); + break; + case LLCP_I_PDU: + //UARTprintf("RX: I "); + if(g_eCurrentServiceEnabled == SNEP_SERVICE) + SNEP_processReceivedData(&pui8RxBuffer[3],ui8PduLength-3); + else if(g_eCurrentServiceEnabled == NPP_SERVICE) + { + // Not Supported + } + else if(g_eCurrentServiceEnabled == HANDOVER_SERVICE) + { + // Not Supported + } + if(g_eLLCPConnectionStatus == LLCP_CONNECTION_ESTABLISHED) + { + g_eLLCPConnectionStatus = LLCP_CONNECTION_RECEIVING; + } + g_eNextPduQueue = LLCP_RR_PDU; + break; + case LLCP_RR_PDU: + //UARTprintf("RX: RR \n"); + g_eNextPduQueue = LLCP_SYMM_PDU; + eSnepProtocolStatus = SNEP_getProtocolStatus(); + if(g_eLLCPConnectionStatus == LLCP_CONNECTION_SENDING) + { + if( + (eSnepProtocolStatus == + SNEP_CONNECTION_WAITING_FOR_CONTINUE) || + (eSnepProtocolStatus == + SNEP_CONNECTION_WAITING_FOR_SUCCESS)) + { + g_eNextPduQueue = LLCP_SYMM_PDU; + } + else if(eSnepProtocolStatus == + SNEP_CONNECTION_SENDING_N_FRAGMENTS) + { + g_eNextPduQueue = LLCP_I_PDU; + } + else if(eSnepProtocolStatus == SNEP_CONNECTION_SEND_COMPLETE) + { + g_eNextPduQueue = LLCP_DISC_PDU; + } + } + else + { + // + // Used for debugging + // + g_eNextPduQueue = LLCP_SYMM_PDU; + } + break; + case LLCP_RNR_PDU: + //UARTprintf("RX: RNR "); + break; + case LLCP_RESERVED_PDU: + //UARTprintf("RX: RESERVED "); + break; + default: + //UARTprintf("RX: UNKNOWN LLCP "); + eLLCPStatus = STATUS_FAIL; + break; + } + + return eLLCPStatus; +} + +//***************************************************************************** +// +//! Set next PDU, return SUCCESS or FAIL +//! +//! \param eNextPdu is the LLCP PDU to set next. +//! +//! The \e eNextPdu parameter can be any of the following: +//! +//! - \b LLCP_SYMM_PDU - See LLCP standard document section 4.3.1 +//! - \b LLCP_PAX_PDU - See LLCP standard document section 4.3.2 +//! - \b LLCP_AGF_PDU - See LLCP standard document section 4.3.3 +//! - \b LLCP_UI_PDU - See LLCP standard document section 4.3.4 +//! - \b LLCP_CONNECT_PDU - See LLCP standard document section 4.3.5 +//! - \b LLCP_DISC_PDU - See LLCP standard document section 4.3.6 +//! - \b LLCP_CC_PDU - See LLCP standard document section 4.3.7 +//! - \b LLCP_DM_PDU - See LLCP standard document section 4.3.8 +//! - \b LLCP_FRMR_PDU - See LLCP standard document section 4.3.9 +//! - \b LLCP_SNL_PDU - See LLCP standard document section 4.3.10 +//! - \b LLCP_I_PDU - See LLCP standard document section 4.3.11 +//! - \b LLCP_RR_PDU - See LLCP standard document section 4.3.12 +//! - \b LLCP_RNR_PDU - See LLCP standard document section 4.3.13 +//! - \b LLCP_RESERVED_PDU - See LLCP standard document section 4.3.14 +//! - \b LLCP_ERROR_PDU - Unknown PDU +//! +//! This function is used to modify the next LLCP PDU. For example +//! when we need to set the next PDU to be LLCP_CONNECT_PDU, to initiate +//! a transfer. For more information please see the LLCP document from the +//! NFC Forum. +//! +//! \return eSetNextPduStatus SUCCESS if g_eNextPduQueue was modified, else +//! return FAIL +// +//***************************************************************************** +tStatus LLCP_setNextPDU(tLLCPPduPtype eNextPdu) +{ + tStatus eSetNextPduStatus; + if(g_eLLCPConnectionStatus == LLCP_CONNECTION_IDLE || + g_eLLCPConnectionStatus == LLCP_CONNECTION_ESTABLISHED) + { + g_eNextPduQueue = eNextPdu; + if(eNextPdu == LLCP_CONNECT_PDU) + g_eCurrentServiceEnabled = SNEP_SERVICE; + eSetNextPduStatus = STATUS_SUCCESS; + } + else + { + eSetNextPduStatus = STATUS_FAIL; + } + return eSetNextPduStatus; +} + +//***************************************************************************** +// +//! Send SYMM message +//! +//! \param pui8PduBufferPtr is the start pointer to store the SYMM PDU. +//! +//! This function adds a SYMM PDU starting at pui8PduBufferPtr.For more +//! details on this PDU read LLCP V1.1 Section 4.3.1. +//! +//! \return ui8IndexTemp is the length of the SYMM PDU. +// +//***************************************************************************** +uint8_t LLCP_sendSYMM(uint8_t * pui8PduBufferPtr) +{ + uint8_t ui8IndexTemp = 0; + // DSAP (6 bits) PTYPE (4 bits) SSAP (6 bits) + pui8PduBufferPtr[ui8IndexTemp++] = ( (LLCP_SYMM_PDU & 0xFC) >> 2); + pui8PduBufferPtr[ui8IndexTemp++] = ( (LLCP_SYMM_PDU & 0x03) << 6); + return ui8IndexTemp; +} + +//***************************************************************************** +// +//! Send CONNECT message +//! +//! \param pui8PduBufferPtr is the start pointer to store the CONNECT PDU. +//! +//! This function adds a CONNECT PDU starting at pui8PduBufferPtr.For more +//! details on this PDU read LLCP V1.1 Section 4.3.5. +//! +//! \return ui8IndexTemp is the length of the CONNECT PDU. +// +//***************************************************************************** +uint8_t LLCP_sendCONNECT(uint8_t * pui8PduBufferPtr) +{ + uint8_t ui8IndexTemp = 0; + + g_eCurrentServiceEnabled = SNEP_SERVICE; + + // + // Reset NR and NS + // + g_ui8NSNR = 0x00; + + g_eLLCPConnectionStatus = LLCP_CONNECTION_SENDING; + + g_ui8ssapValue = LLCP_SSAP_CONNECT_SEND; + g_ui8dsapValue = DSAP_SERVICE_DISCOVERY_PROTOCOL; + + // DSAP (6 bits) PTYPE (4 bits) SSAP (6 bits) + pui8PduBufferPtr[ui8IndexTemp++] = (g_ui8dsapValue << 2) | + ( (LLCP_CONNECT_PDU & 0xFC) >> 2); + pui8PduBufferPtr[ui8IndexTemp++] = ( (LLCP_CONNECT_PDU & 0x03) << 6) | + g_ui8ssapValue; + + // + // TLV Fields + // + ui8IndexTemp = ui8IndexTemp + + LLCP_addTLV(LLCP_SN, &pui8PduBufferPtr[ui8IndexTemp]); + ui8IndexTemp = ui8IndexTemp + + LLCP_addTLV(LLCP_MIUX, &pui8PduBufferPtr[ui8IndexTemp]); + ui8IndexTemp = ui8IndexTemp + + LLCP_addTLV(LLCP_RW, &pui8PduBufferPtr[ui8IndexTemp]); + + return ui8IndexTemp; +} + +//***************************************************************************** +// +//! Send DISC message +//! +//! \param pui8PduBufferPtr is the start pointer to store the DISC PDU. +//! +//! This function adds a DISC PDU starting at pui8PduBufferPtr.For more details +//! on this PDU read LLCP V1.1 Section 4.3.6. +//! +//! \return ui8IndexTemp is the length of the DISC PDU. +// +//***************************************************************************** +uint8_t LLCP_sendDISC(uint8_t * pui8PduBufferPtr) +{ + uint8_t ui8IndexTemp = 0; + + // + // DSAP (6 bits) PTYPE (4 bits) SSAP (6 bits) + // + pui8PduBufferPtr[ui8IndexTemp++] = (g_ui8dsapValue << 2) | + ( (LLCP_DISC_PDU & 0xFC) >> 2); + pui8PduBufferPtr[ui8IndexTemp++] = ( (LLCP_DISC_PDU & 0x03) << 6) | + g_ui8ssapValue; + + return ui8IndexTemp; +} + +//***************************************************************************** +// +//! Send CC message +//! +//! \param pui8PduBufferPtr is the start pointer to store the CC PDU. +//! +//! This function adds a CC PDU starting at pui8PduBufferPtr. For more details +//! on this PDU, read LLCP V1.1 Section 4.3.7. +//! +//! \return \b ui8IndexTemp is the length of the CC PDU. +// +//***************************************************************************** +uint8_t LLCP_sendCC(uint8_t * pui8PduBufferPtr) +{ + uint8_t ui8IndexTemp = 0; + + g_eLLCPConnectionStatus = LLCP_CONNECTION_ESTABLISHED; + + // + // Reset NR and NS + // + g_ui8NSNR = 0x00; + + g_ui8ssapValue = LLCP_SSAP_CONNECT_RECEIVED; + + // DSAP (6 bits) PTYPE (4 bits) SSAP (6 bits) + pui8PduBufferPtr[ui8IndexTemp++] = (g_ui8dsapValue << 2) | + ( (LLCP_CC_PDU & 0xFC) >> 2); + pui8PduBufferPtr[ui8IndexTemp++] = ( (LLCP_CC_PDU & 0x03) << 6) | + g_ui8ssapValue; + + // + // TLV Fields + // + ui8IndexTemp = ui8IndexTemp + + LLCP_addTLV(LLCP_MIUX, &pui8PduBufferPtr[ui8IndexTemp]); + ui8IndexTemp = ui8IndexTemp + + LLCP_addTLV(LLCP_RW, &pui8PduBufferPtr[ui8IndexTemp]); + + return ui8IndexTemp; +} + +//***************************************************************************** +// +//! Send DM message +//! +//! \param pui8PduBufferPtr is the start pointer to store the DM PDU. +//! \param eDmReason is the enumeration of the disconnection reason. +//! +//! The \e eDmReason parameter can be any of the following: +//! +//! - \b DM_REASON_LLCP_RECEIVED_DISC_PDU +//! - \b DM_REASON_LLCP_RECEIVED_CONNECTION_ORIENTED_PDU +//! - \b DM_REASON_LLCP_RECEIVED_CONNECT_PDU_NO_SERVICE +//! - \b DM_REASON_LLCP_PROCESSED_CONNECT_PDU_REQ_REJECTED +//! - \b DM_REASON_LLCP_PERMNANTLY_NOT_ACCEPT_CONNECT_WITH_SAME_SSAP +//! - \b DM_REASON_LLCP_PERMNANTLY_NOT_ACCEPT_CONNECT_WITH_ANY_SSAP +//! - \b DM_REASON_LLCP_TEMMPORARILY_NOT_ACCEPT_PDU_WITH_SAME_SSSAPT +//! - \b DM_REASON_LLCP_TEMMPORARILY_NOT_ACCEPT_PDU_WITH_ANY_SSSAPT +//! +//! This function adds a DM PDU starting at pui8PduBufferPtr with a dm_reason. +//! For more details on this PDU read LLCP V1.1 Section 4.3.8. +//! +//! \return ui8IndexTemp is the length of the DM PDU. +// +//***************************************************************************** +uint8_t LLCP_sendDM(uint8_t * pui8PduBufferPtr,tDisconnectModeReason eDmReason) +{ + uint8_t ui8IndexTemp = 0; + + // DSAP (6 bits) PTYPE (4 bits) SSAP (6 bits) + pui8PduBufferPtr[ui8IndexTemp++] = (g_ui8dsapValue << 2) | + ( (LLCP_DM_PDU & 0xFC) >> 2); + pui8PduBufferPtr[ui8IndexTemp++] = ( (LLCP_DM_PDU & 0x03) << 6) | + g_ui8ssapValue; + + pui8PduBufferPtr[ui8IndexTemp++] = (uint8_t) eDmReason; + + return ui8IndexTemp; +} + +//***************************************************************************** +// +//! Send I message +//! +//! \param pui8PduBufferPtr is the start pointer to store the I PDU. +//! +//! This function adds a I PDU starting at pui8PduBufferPtr.For more details +//! on this PDU read LLCP V1.1 Section 4.3.10. +//! +//! \return ui8IndexTemp is the length of the I PDU. +// +//***************************************************************************** +uint8_t LLCP_sendI(uint8_t * pui8PduBufferPtr) +{ + uint8_t ui8IndexTemp = 0; + tSNEPConnectionStatus eSnepProtocolStatus; + + // DSAP (6 bits) PTYPE (4 bits) SSAP (6 bits) + pui8PduBufferPtr[ui8IndexTemp++] = (g_ui8dsapValue << 2) | ( (LLCP_I_PDU & 0xFC) >> 2); + pui8PduBufferPtr[ui8IndexTemp++] = ( (LLCP_I_PDU & 0x03) << 6) | g_ui8ssapValue; + + pui8PduBufferPtr[ui8IndexTemp++] = g_ui8NSNR; + + g_ui8NSNR = (g_ui8NSNR & 0x0F) | (((g_ui8NSNR >> 4) + 0x01) << 4); // Increment N(S) + + if(g_eLLCPConnectionStatus == LLCP_CONNECTION_ESTABLISHED) + { + g_eLLCPConnectionStatus = LLCP_CONNECTION_SENDING; + } + if(g_eCurrentServiceEnabled == SNEP_SERVICE) + { + if(g_eLLCPConnectionStatus == LLCP_CONNECTION_SENDING) + { + ui8IndexTemp = ui8IndexTemp + + SNEP_sendRequest(&pui8PduBufferPtr[ui8IndexTemp],SNEP_REQUEST_PUT); + } + else if(g_eLLCPConnectionStatus == LLCP_CONNECTION_RECEIVING) + { + eSnepProtocolStatus = SNEP_getProtocolStatus(); + if(eSnepProtocolStatus == SNEP_CONNECTION_RECEIVED_FIRST_PACKET) + { + ui8IndexTemp = ui8IndexTemp + + SNEP_sendResponse(&pui8PduBufferPtr[ui8IndexTemp], + SNEP_RESPONSE_CONTINUE); + } + else if(eSnepProtocolStatus == SNEP_CONNECTION_RECEIVE_COMPLETE) + { + ui8IndexTemp = ui8IndexTemp + + SNEP_sendResponse(&pui8PduBufferPtr[ui8IndexTemp], + SNEP_RESPONSE_SUCCESS); + } + else if(eSnepProtocolStatus == SNEP_CONNECTION_EXCESS_SIZE) + { + ui8IndexTemp = ui8IndexTemp + + SNEP_sendResponse(&pui8PduBufferPtr[ui8IndexTemp], + SNEP_RESPONSE_REJECT); + } + } + } + else if(g_eCurrentServiceEnabled == NPP_SERVICE) + { + // RFU + } + + return ui8IndexTemp; +} + +//***************************************************************************** +// +//! Send RR message +//! +//! \param pui8PduBufferPtr is the start pointer to store the RR PDU. +//! +//! This function adds a RR PDU starting at pui8PduBufferPtr.For more details +//! on this PDU read LLCP V1.1 Section 4.3.11. +//! +//! \return ui8IndexTemp is the length of the RR PDU. +// +//***************************************************************************** +uint8_t LLCP_sendRR(uint8_t * pui8PduBufferPtr) +{ + uint8_t ui8IndexTemp = 0; + + // DSAP (6 bits) PTYPE (4 bits) SSAP (6 bits) + pui8PduBufferPtr[ui8IndexTemp++] = (g_ui8dsapValue << 2) | \ + ((LLCP_RR_PDU & 0xFC) >> 2); + pui8PduBufferPtr[ui8IndexTemp++] = ( (LLCP_RR_PDU & 0x03) << 6) | \ + g_ui8ssapValue; + + // Increment N(R) + g_ui8NSNR = (g_ui8NSNR & 0xF0) | ((g_ui8NSNR + 0x01) & 0x0F); + + pui8PduBufferPtr[ui8IndexTemp++] = (g_ui8NSNR & 0x0F); + + return ui8IndexTemp; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/nfclib/llcp.h b/nfclib/llcp.h new file mode 100644 index 0000000..7e419e0 --- /dev/null +++ b/nfclib/llcp.h @@ -0,0 +1,248 @@ +//***************************************************************************** +// +// llcp.h - Logic Link Control Protocol header file +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#ifndef __NFC_LLCP_H__ +#define __NFC_LLCP_H__ + +#include "types.h" + +//***************************************************************************** +// +//! \addtogroup nfc_llcp_api NFC LLCP API Functions +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// List of Commands +// +//***************************************************************************** + +// +// ! LLCP Magic Number is constant 0x46666D +// +#define LLCP_MAGIC_NUMBER_HIGH 0x46 +#define LLCP_MAGIC_NUMBER_MIDDLE 0x66 +#define LLCP_MAGIC_NUMBER_LOW 0x6D + +//***************************************************************************** +// +// Service in local service Environment and is NOT advertised by local SDP +// +//***************************************************************************** + +// +//! Source Service Access Point when sending +// +#define LLCP_SSAP_CONNECT_SEND 0x20 + +// +//! Source Service Access Point when receiving +// +#define LLCP_SSAP_CONNECT_RECEIVED 0x04 + +// +//! Destination Service Access Point for discovery +// +#define DSAP_SERVICE_DISCOVERY_PROTOCOL 0x01 + +// +//! The LLCP_MIU is the maximum information unit supported by the LLCP layer. +//! This information unit may be included in each LLCP packet depending on the +//! PDU type. The minimum must be 128. +// +#define LLCP_MIU 248 + +// +//! The LLCP_MIUX_SIZE is the value for the LLCP_MIUX TLV used in LLCP_addTLV(). +// +#define LLCP_MIUX_SIZE (LLCP_MIU - 128) + +//***************************************************************************** +// +//! LLCP Parameter Enumerations. +// +//***************************************************************************** +typedef enum +{ + //! See LLCP V1.1 Section 4.5.1 + LLCP_VERSION = 0x01, + //! See LLCP V1.1 Section 4.5.2 + LLCP_MIUX = 0x02, + //! See LLCP V1.1 Section 4.5.3 + LLCP_WKS = 0x03, + //! See LLCP V1.1 Section 4.5.4 + LLCP_LTO = 0x04, + //! See LLCP V1.1 Section 4.5.5 + LLCP_RW = 0x05, + //! See LLCP V1.1 Section 4.5.6 + LLCP_SN = 0x06, + //! See LLCP V1.1 Section 4.5.7 + LLCP_OPT = 0x07, + //! See LLCP V1.1 Section 4.5.8 + LLCP_SDREQ = 0x08, + //! See LLCP V1.1 Section 4.5.9 + LLCP_SDRES = 0x09, + LLCP_ERROR +}tLLCPParamaeter; + +//***************************************************************************** +// +//! PDU Type Enumerations. +// +//***************************************************************************** +typedef enum +{ + //! See LLCP V1.1 Section 4.3.1 + LLCP_SYMM_PDU = 0x00, + //! See LLCP V1.1 Section 4.3.2 + LLCP_PAX_PDU= 0x01, + //! See LLCP V1.1 Section 4.3.3 + LLCP_AGF_PDU= 0x02, + //! See LLCP V1.1 Section 4.3.4 + LLCP_UI_PDU = 0x03, + //! See LLCP V1.1 Section 4.3.5 + LLCP_CONNECT_PDU = 0x04, + //! See LLCP V1.1 Section 4.3.6 + LLCP_DISC_PDU = 0x05, + //! See LLCP V1.1 Section 4.3.7 + LLCP_CC_PDU = 0x06, + //! See LLCP V1.1 Section 4.3.8 + LLCP_DM_PDU = 0x07, + //! See LLCP V1.1 Section 4.3.9 + LLCP_FRMR_PDU = 0x08, + //! See LLCP V1.1 Section 4.3.10 + LLCP_SNL_PDU = 0x09, + //! See LLCP V1.1 Section 4.3.11 + LLCP_I_PDU = 0x0C, + //! See LLCP V1.1 Section 4.3.12 + LLCP_RR_PDU = 0x0D, + //! See LLCP V1.1 Section 4.3.13 + LLCP_RNR_PDU = 0x0E, + //! See LLCP V1.1 Section 4.3.14 + LLCP_RESERVED_PDU = 0x0F +}tLLCPPduPtype; + +//***************************************************************************** +// +//! LLCP Connection Status Enumeration. +// +//***************************************************************************** +typedef enum +{ + //! No Tx/Rx ongoing. + LLCP_CONNECTION_IDLE = 0x00, + + //! When a virtual link is created either when we send a CONNECT PDU and + //! receive a CC PDU, or when we receive a CONNECT PDU and respond a CC PDU. + LLCP_CONNECTION_ESTABLISHED, + + //! When sending data via SNEP + LLCP_CONNECTION_SENDING, + + //! When receiving data via SNEP + LLCP_CONNECTION_RECEIVING + +}tLLCPConnectionStatus; + +//***************************************************************************** +// +//! Service Name Enumerations - Only support SNEP_SERVICE +// +//***************************************************************************** +typedef enum +{ + NPP_SERVICE = 0, + SNEP_SERVICE, + HANDOVER_SERVICE +}tServiceName; + + +//***************************************************************************** +// +//! Disconnected Mode Reasons Enumerations. +// +//***************************************************************************** +typedef enum +{ + //! See LLCP Section 4.3.8. + DM_REASON_LLCP_RECEIVED_DISC_PDU = 0x00, + + //! See LLCP Section 4.3.8. + DM_REASON_LLCP_RECEIVED_CONNECTION_ORIENTED_PDU = 0x01, + + //! See LLCP Section 4.3.8. + DM_REASON_LLCP_RECEIVED_CONNECT_PDU_NO_SERVICE = 0x02, + + //! See LLCP Section 4.3.8. + DM_REASON_LLCP_PROCESSED_CONNECT_PDU_REQ_REJECTED = 0x03, + + //! See LLCP Section 4.3.8. + DM_REASON_LLCP_PERMNANTLY_NOT_ACCEPT_CONNECT_WITH_SAME_SSAP = 0x10, + + //! See LLCP Section 4.3.8. + DM_REASON_LLCP_PERMNANTLY_NOT_ACCEPT_CONNECT_WITH_ANY_SSAP = 0x11, + + //! See LLCP Section 4.3.8. + DM_REASON_LLCP_TEMMPORARILY_NOT_ACCEPT_PDU_WITH_SAME_SSSAPT = 0x20, + + //! See LLCP Section 4.3.8. + DM_REASON_LLCP_TEMMPORARILY_NOT_ACCEPT_PDU_WITH_ANY_SSSAPT = 0x21 +}tDisconnectModeReason; + + + +//***************************************************************************** +// +// Function Prototypes +// +//***************************************************************************** +void LLCP_init(void); + +uint8_t LLCP_stateMachine(uint8_t * pui8PduBufferPtr); + +tStatus LLCP_processReceivedData(uint8_t * pui8RxBuffer, uint8_t ui8PduLength); + +uint16_t LLCP_getLinkTimeOut(void); +uint8_t LLCP_addTLV(tLLCPParamaeter eLLCPparam, uint8_t * pui8TLVBufferPtr); +void LLCP_processTLV(uint8_t * pui8TLVBufferPtr); + +tStatus LLCP_setNextPDU(tLLCPPduPtype eNextPdu); +void LLCP_setConnectionStatus(tLLCPConnectionStatus eConnectionState); + +uint8_t LLCP_sendSYMM(uint8_t * pui8PduBufferPtr); +uint8_t LLCP_sendCONNECT(uint8_t * pui8PduBufferPtr); +uint8_t LLCP_sendDISC(uint8_t * pui8PduBufferPtr); +uint8_t LLCP_sendCC(uint8_t * pui8PduBufferPtr); +uint8_t LLCP_sendDM(uint8_t * pui8PduBufferPtr,tDisconnectModeReason eDmReason); +uint8_t LLCP_sendI(uint8_t * pui8PduBufferPtr); +uint8_t LLCP_sendRR(uint8_t * pui8PduBufferPtr); +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +#endif //__NFC_LLCP_H__ diff --git a/nfclib/nfc.c b/nfclib/nfc.c new file mode 100644 index 0000000..30bc104 --- /dev/null +++ b/nfclib/nfc.c @@ -0,0 +1,241 @@ +//***************************************************************************** +// +// nfc.c - NFC implementation. +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_types.h" +#include "driverlib/sysctl.h" +#include "trf79x0.h" +#include "iso14443b.h" + +unsigned char g_ucNFCID[11] = "\x80\x12\x34\x56"; //NFC ID (PUPI = 80123456) + +//***************************************************************************** +// +// Set up registers for ISO 14443 B 106Kbit/s operation. This function must +// be called after initializing the TRF79x0 (for example with TRF79x0Init() +// or TRF79x0DirectCommand() with argument \b TRF79X0_SOFT_INIT_CMD) and before +// calling any of the other ISO14443B functions. +// +//***************************************************************************** +void +NfcTagType4BSetupRegisters(void) +{ + TRF79x0DirectCommand(TRF79X0_SOFT_INIT_CMD); + TRF79x0DirectCommand(TRF79X0_IDLE_CMD); + + // + // TODO:check why have to set as this? + // + TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG, + TRF79X0_MOD_CTRL_MOD_OOK_100); + + // + // Set the ISO format to NFC Card Emulation, Type B + // + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x25); + + // + // Set the regulator voltage to be automatic. + // + TRF79x0WriteRegister(TRF79X0_REGULATOR_CONTROL_REG, + TRF79X0_REGULATOR_CTRL_VRS_2_8V); + + // + // RX Special Settings for ISO14443B + // + TRF79x0WriteRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG, 0x3C); + + // + // Set the Target Detection Level to Max; use SDD + // +// TRF79x0WriteRegister(TRF79X0_NFC_TARGET_LEVEL_REG, 0x27); + TRF79x0WriteRegister(TRF79X0_NFC_TARGET_LEVEL_REG, 0x07); + + // + // Set the NFCID to be sent during SDD + // + TRF79x0WriteRegisterContinuous(TRF79X0_NFC_ID_REG, g_ucNFCID, 4); + + TRF79x0WriteRegister(TRF79X0_NFC_LO_FIELD_LEVEL_REG, 0x03); + + // + // ISO14443B TX Options + // + TRF79x0WriteRegister(TRF79X0_ISO14443B_OPTIONS_REG, 0x00); + + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, 0x21); + + TRF79x0ResetFifoCommand(); + TRF79x0DirectCommand(TRF79X0_STOP_DECODERS_CMD); + TRF79x0DirectCommand(TRF79X0_RUN_DECODERS_CMD); +} + +//***************************************************************************** +// +// Set up registers for ISO 14443 A 106Kbit/s operation. This function must +// be called after initializing the TRF79x0 (for example with TRF79x0Init() +// or TRF79x0DirectCommand() with argument \b TRF79X0_SOFT_INIT_CMD) and before +// calling any of the other ISO14443B functions. +// +//***************************************************************************** +void +NfcTagType4ASetupRegisters(void) +{ + unsigned char Data[11] = "\x08\x12\x34\x56"; + + //Examples of start byte of Type A UID Values and MFGs seen by Nexus S. + //These are just a few found by using the TagInfo app + //0x01, 0x05, 0x07, 0x09, 0x19 = Infineon + //0x02, 0x03, 0x04, 0x06, 0x0A = NXP + //0x08 = Unknown, considered to be for random ID + //0x1C, 0xC2, 0x3E, 0x80 = NXP + + TRF79x0DirectCommand(TRF79X0_SOFT_INIT_CMD); + TRF79x0DirectCommand(TRF79X0_IDLE_CMD); + + // + // TODO:check why have to set as this? + // + TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG, + TRF79X0_MOD_CTRL_MOD_OOK_100); + + // + // Set the ISO format to NFC Card Emulation, Type A + // + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x24); + + // + // Set the regulator voltage to be automatic. + // + TRF79x0WriteRegister(TRF79X0_REGULATOR_CONTROL_REG, + TRF79X0_REGULATOR_CTRL_AUTO_REG); + + // + // RX Special Settings for ISO14443A + // + TRF79x0WriteRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG, 0x30); + + // + // Set the Target Detection Level to Max; use SDD + // + TRF79x0WriteRegister(TRF79X0_NFC_TARGET_LEVEL_REG, 0x27); + + // + // Set the NFCID to be sent during SDD + // + TRF79x0WriteRegisterContinuous(TRF79X0_NFC_ID_REG, Data, 4); + + TRF79x0WriteRegister(TRF79X0_NFC_LO_FIELD_LEVEL_REG, 0x83); + + // SDD need this + TRF79x0WriteRegister(TRF79X0_ISO14443B_OPTIONS_REG, 0x01); + + // + // ISO14443B TX Options + // +// TRF79x0WriteRegister(TRF79X0_ISO14443B_OPTIONS_REG, 0x01); + TRF79x0WriteRegister(TRF79X0_ISO14443A_OPTIONS_REG, 0x00); + +// // +// // Set the TX pulse to 106ns (0x20 * 73.7ns). +// // +// TRF79x0WriteRegister(TRF79X0_TX_PULSE_LENGTH_CTRL_REG, 0x20); +// +// // +// // Set the RX No response wait time to 529us (0xe * 37.76us). +// // +// TRF79x0WriteRegister(TRF79X0_RX_NO_RESPONSE_WAIT_REG, 0x0e); +// +// // +// // Set the RX wait time to 66us (7 * 9.44us). +// // +// TRF79x0WriteRegister(TRF79X0_RX_WAIT_TIME_REG, 0x07); + TRF79x0WriteRegister(TRF79X0_TEST_SETTING1_REG, 0x40); + + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, 0x21); + + TRF79x0ResetFifoCommand(); + TRF79x0DirectCommand(TRF79X0_STOP_DECODERS_CMD); + TRF79x0DirectCommand(TRF79X0_RUN_DECODERS_CMD); + + // + // Delay 5ms before initializing the TRF79x0. + // + SysCtlDelay((SysCtlClockGet()/3000) * 2); +} + +//***************************************************************************** +// +// +// +//***************************************************************************** +int +ISO14443BATQB(unsigned char *pucPUPI,unsigned char ucAFI, unsigned char ucBitRate, + unsigned char ucMaxFrameSize, unsigned char ucProtocolType, + unsigned char ucFWI, unsigned char ucADC, unsigned char ucFO) +{ + unsigned char pucATQB[12]; + +// +// // Protocol Info Bytes +// buffer[10] = 0x80; // Date Rate Capability ( Only Support 106 kbps) +// // Max Frame/Protocol type (128 bytes / PICC compliant to -4) +// buffer[11] = 0x71; +// // (FWI/ADC/FO) ( FWT = 77.3mSec, ADC = coded according to AFI, CID supported) +// buffer[12] = 0x85; + + // + // ATQB response. + // + pucATQB[0] = 0x50; + pucATQB[1] = pucPUPI[0]; + pucATQB[2] = pucPUPI[1]; + pucATQB[3] = pucPUPI[2]; + pucATQB[4] = pucPUPI[3]; + pucATQB[5] = ucAFI; + pucATQB[6] = 0xE2; // CRC_B + pucATQB[7] = 0xAF; // CRC_B + pucATQB[8] = 0x11; // # of applications (1) + pucATQB[9] = ucBitRate; + pucATQB[10] = (ucMaxFrameSize << 4) | ucProtocolType; + pucATQB[11] = (ucFWI << 4) | (ucADC << 2) | ucFO; + + // + // Transmit w/o receive + // + TRF79x0Transceive(pucATQB, sizeof(pucATQB), 0, 0, 0, 0, TRF79X0_TRANSCEIVE_TX_CRC); + + // + // Return true + // + return(1); +} + +//***************************************************************************** +// +// NFC P2P Functions +// +//***************************************************************************** diff --git a/nfclib/nfc.h b/nfclib/nfc.h new file mode 100644 index 0000000..6f349ce --- /dev/null +++ b/nfclib/nfc.h @@ -0,0 +1,42 @@ +//***************************************************************************** +// +// nfc.h - NFC implementation. +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __NFC_H__ +#define __NFC_H__ + +//***************************************************************************** +// +// General NFC Function Prototypes +// +//***************************************************************************** +extern unsigned char g_ucNFCID[4]; + +extern void NfcTagType4BSetupRegisters(void); +extern void NfcTagType4ASetupRegisters(void); +extern int ISO14443BATQB(unsigned char *pucPUPI,unsigned char ucAFI, + unsigned char ucBitRate,unsigned char ucMaxFrameSize, + unsigned char ucProtocolType,unsigned char ucFWI, + unsigned char ucADC, unsigned char ucFO); + +#endif //__NFC_H__ diff --git a/nfclib/nfc_dep.c b/nfclib/nfc_dep.c new file mode 100644 index 0000000..3c30910 --- /dev/null +++ b/nfclib/nfc_dep.c @@ -0,0 +1,597 @@ +//***************************************************************************** +// +// nfc_dep.c - used to send packets of P2P +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#include +#include +#include +#include "nfclib/nfc_dep.h" +#include "nfclib/llcp.h" +#include "nfclib/trf79x0.h" + +//***************************************************************************** +// +// Globals +// +//***************************************************************************** + +uint8_t g_pui8NFCID3t[10] = {0x01, 0xFE, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09}; + +uint8_t g_ui8NfcDepPni = 0x00; + +uint8_t g_ui8RtoxTransportData; + +tPDUBlock tNextPduType = INFORMATION_PDU; + +uint8_t * g_pui8DEPBufferPtr; + +//***************************************************************************** +// +// NFCDEP_SendATR_REQ - +// +//***************************************************************************** +void NFCDEP_SendATR_REQ(uint8_t * pui8NFCID2_Ptr) +{ + uint8_t ui8Counter = 0; + uint8_t ui8Offset = 0; + + // + // Length + // + g_pui8DEPBufferPtr[0] = 0x25; + + // + // Command + // + g_pui8DEPBufferPtr[1] = (uint8_t) ((ATR_REQ_CMD & 0xFF00) >> 8); + g_pui8DEPBufferPtr[2] = (uint8_t) (ATR_REQ_CMD & 0x00FF); + + // + // NFCID3i + // + for(ui8Counter=0;ui8Counter<8;ui8Counter++) + { + g_pui8DEPBufferPtr[3+ui8Counter] = pui8NFCID2_Ptr[ui8Counter]; + } + g_pui8DEPBufferPtr[11] = 0x00; + g_pui8DEPBufferPtr[12] = 0x00; + + g_pui8DEPBufferPtr[13] = DIDi; + g_pui8DEPBufferPtr[14] = BSi; + g_pui8DEPBufferPtr[15] = BRi; + g_pui8DEPBufferPtr[16] = PPi; // Max Payload 64 bytes + + // + // LLCP Magic Number + // + g_pui8DEPBufferPtr[17] = LLCP_MAGIC_NUMBER_HIGH; + g_pui8DEPBufferPtr[18] = LLCP_MAGIC_NUMBER_MIDDLE; + g_pui8DEPBufferPtr[19] = LLCP_MAGIC_NUMBER_LOW; + + ui8Offset = 20; + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_VERSION, + &g_pui8DEPBufferPtr[ui8Offset]); + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_MIUX,&g_pui8DEPBufferPtr[ui8Offset]); + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_WKS,&g_pui8DEPBufferPtr[ui8Offset]); + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_LTO,&g_pui8DEPBufferPtr[ui8Offset]); + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_OPT,&g_pui8DEPBufferPtr[ui8Offset]); + + TRF79x0WriteFIFO(g_pui8DEPBufferPtr,CRC_BIT_ENABLE,ui8Offset); +} + +//***************************************************************************** +// +// NFCDEP_SendPSL_REQ - +// +//***************************************************************************** +void NFCDEP_SendPSL_REQ(void) +{ + uint8_t ui8Offset = 1; + + // + // Command + // + g_pui8DEPBufferPtr[ui8Offset++] = (uint8_t) ((PSL_REQ_CMD & 0xFF00) >> 8); + g_pui8DEPBufferPtr[ui8Offset++] = (uint8_t) (PSL_REQ_CMD & 0x00FF); + + // + // DID + // + g_pui8DEPBufferPtr[ui8Offset++] = 0x00; + + // + // BRS - + // B5 B4 B3 (DSI) Initiator to Target + // B2 B1 B0 (DRI) Target to Initiator + // 0 0 0 106kbaud + // 0 0 1 212kbaud + // 0 1 0 424kbaud (default) + // 0 1 1 848kbaud + // + g_pui8DEPBufferPtr[ui8Offset++] = 0x12; + + // + // FSL + // B1-B0 Max Payload Size (11b: Max payload size is 254 bytes) + // + g_pui8DEPBufferPtr[ui8Offset++] = 0x03; + + // + // Length + // + g_pui8DEPBufferPtr[0] = ui8Offset; + + TRF79x0WriteFIFO(g_pui8DEPBufferPtr,CRC_BIT_ENABLE,ui8Offset); +} + +//***************************************************************************** +// +// NFCDEP_SendATR_RES - +// +//***************************************************************************** +void NFCDEP_SendATR_RES(void) +{ + uint8_t ui8Counter = 0; + uint8_t ui8Offset = 1; + + // + // Command + // + g_pui8DEPBufferPtr[ui8Offset++] = (uint8_t) ((ATR_RES_CMD & 0xFF00) >> 8); + g_pui8DEPBufferPtr[ui8Offset++] = (uint8_t) (ATR_RES_CMD & 0x00FF); + + // + // NFCID3t + // + for(ui8Counter=0;ui8Counter<10;ui8Counter++) + { + g_pui8DEPBufferPtr[ui8Offset++] = g_pui8NFCID3t[ui8Counter]; + } + + g_pui8DEPBufferPtr[ui8Offset++] = DIDt; + g_pui8DEPBufferPtr[ui8Offset++] = BSt; + g_pui8DEPBufferPtr[ui8Offset++] = BRt; + g_pui8DEPBufferPtr[ui8Offset++] = TO; + g_pui8DEPBufferPtr[ui8Offset++] = PPt; // Max Payload 64 bytes + + // + // LLCP Magic Number + // + g_pui8DEPBufferPtr[ui8Offset++] = LLCP_MAGIC_NUMBER_HIGH; + g_pui8DEPBufferPtr[ui8Offset++] = LLCP_MAGIC_NUMBER_MIDDLE; + g_pui8DEPBufferPtr[ui8Offset++] = LLCP_MAGIC_NUMBER_LOW; + + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_VERSION, + &g_pui8DEPBufferPtr[ui8Offset]); + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_MIUX,&g_pui8DEPBufferPtr[ui8Offset]); + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_WKS,&g_pui8DEPBufferPtr[ui8Offset]); + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_LTO,&g_pui8DEPBufferPtr[ui8Offset]); + ui8Offset = ui8Offset + LLCP_addTLV(LLCP_OPT,&g_pui8DEPBufferPtr[ui8Offset]); + + // + // Length + // + g_pui8DEPBufferPtr[0] = ui8Offset; + + TRF79x0WriteFIFO(g_pui8DEPBufferPtr,CRC_BIT_ENABLE,ui8Offset); +} + +//***************************************************************************** +// +// NFCDEP_SendRSL_RES - +// +//***************************************************************************** +void NFCDEP_SendRSL_RES(void) +{ + uint8_t ui8Offset = 1; + + // + // Command + // + g_pui8DEPBufferPtr[ui8Offset++] = (uint8_t) ((RSL_RES_CMD & 0xFF00) >> 8); + g_pui8DEPBufferPtr[ui8Offset++] = (uint8_t) (RSL_RES_CMD & 0x00FF); + + // + // Length + // + g_pui8DEPBufferPtr[0] = ui8Offset; + + TRF79x0WriteFIFO(g_pui8DEPBufferPtr,CRC_BIT_ENABLE,ui8Offset); +} + +//***************************************************************************** +// +// NFCDEP_SendPSL_RES - +// +//***************************************************************************** +void NFCDEP_SendPSL_RES(uint8_t did_value) +{ + uint8_t ui8Offset = 1; + + // + // Command + // + g_pui8DEPBufferPtr[ui8Offset++] = (uint8_t) ((PSL_RES_CMD & 0xFF00) >> 8); + g_pui8DEPBufferPtr[ui8Offset++] = (uint8_t) (PSL_RES_CMD & 0x00FF); + + g_pui8DEPBufferPtr[ui8Offset++] = 0x00; + + // + // Length + // + g_pui8DEPBufferPtr[0] = ui8Offset; + + TRF79x0WriteFIFO(g_pui8DEPBufferPtr,CRC_BIT_ENABLE,ui8Offset); +} + +//***************************************************************************** +// +// NFCDEP_ProcessReceivedRequest - +// +//***************************************************************************** +tStatus NFCDEP_ProcessReceivedRequest(uint8_t * pui8RxBuffer , \ + uint8_t * pui8NFCID2_Ptr, + bool bActiveResponse) +{ + volatile uint8_t ui8CommandLength; + uint16_t ui16Command; + tStatus eNfcDepStatus = STATUS_SUCCESS; + uint8_t ui8PFBValue; + uint8_t ui8Counter; + + ui8CommandLength = pui8RxBuffer[0]; + ui16Command = pui8RxBuffer[2] + (pui8RxBuffer[1] << 8); + + ui8PFBValue = pui8RxBuffer[3]; + + // Check if chaining is enabled + if((ui8PFBValue & 0xF0) == 0x00) + { + tNextPduType = INFORMATION_PDU; + } + else if((ui8PFBValue & 0xF0) == 0x10) + { + tNextPduType = ACK_PDU; + } + else if((ui8PFBValue & 0xF0) == 0x90) + { + tNextPduType = RTOX_REQ_PDU; + } + else if((ui8PFBValue & 0xF0) == 0x80) + { + tNextPduType = ATN_PDU; + } + + + if(ui16Command == ATR_REQ_CMD) + { + if((pui8NFCID2_Ptr[0] == pui8RxBuffer[3] && \ + pui8NFCID2_Ptr[1] == pui8RxBuffer[4] && \ + pui8NFCID2_Ptr[2] == pui8RxBuffer[5] && \ + pui8NFCID2_Ptr[3] == pui8RxBuffer[6] && \ + pui8NFCID2_Ptr[4] == pui8RxBuffer[7] && \ + pui8NFCID2_Ptr[5] == pui8RxBuffer[8] && \ + pui8NFCID2_Ptr[6] == pui8RxBuffer[9] && \ + pui8NFCID2_Ptr[7] == pui8RxBuffer[10]) || bActiveResponse == true) + { + ui8Counter = 0; + while(ui8CommandLength > (ui8Counter+20)) + { + // + // Process the TLV - pass the starting address of the TLV + // + LLCP_processTLV(&pui8RxBuffer[ui8Counter+20]); + + // + // Increment ui8Counter by the length+ 2 (type and length) of + // the current TLV + // + ui8Counter = ui8Counter+ pui8RxBuffer[ui8Counter+21] + 2; + } + NFCDEP_SendATR_RES(); + // Reset the PNI + g_ui8NfcDepPni = 0x00; + //UARTprintf("CMD : D400\n"); + } + else + eNfcDepStatus = STATUS_FAIL; + } + else if(ui16Command == PSL_REQ_CMD) + { + // Check if the DSI (Bits 5-3) == 010b => 424kbaud (Init. to Target) + // if the DRI (2-0) == 010b => 424kbaud (Target to Initiator) + if(((pui8RxBuffer[4] & 0x38) == 0x10) && \ + ((pui8RxBuffer[4] & 0x07) == 0x02)) + { + NFCDEP_SendPSL_RES(pui8RxBuffer[3]); + TRF79x0SetMode(P2P_PASSIVE_TARGET_MODE,FREQ_424_KBPS); + } + + } + else if(ui16Command == DEP_REQ_CMD) + { + // + // LLCP Packet Handler + // + if(tNextPduType == INFORMATION_PDU) + { + LLCP_processReceivedData(&pui8RxBuffer[4], (ui8CommandLength-4)); + } + + NFCDEP_SendDEP_RES(); + } + else if(ui16Command == DSL_REQ_CMD) + { + // + // Debug + // + while(1); + } + else if(ui16Command == RSL_REQ_CMD) + { + //UARTprintf("CMD : D40A\n"); + if(ui8CommandLength == 0x03) + NFCDEP_SendRSL_RES(); + } + else + { + eNfcDepStatus = STATUS_FAIL; + + } + return eNfcDepStatus; +} + +//***************************************************************************** +// +// NFCDEP_ProcessReceivedData - +// +//***************************************************************************** +tStatus NFCDEP_ProcessReceivedData(uint8_t * pui8RxBuffer) +{ + volatile uint8_t ui8CommandLength; + uint16_t ui16Command; + uint8_t ui8Counter; + tStatus eNfcDepStatus = STATUS_SUCCESS; + uint8_t ui8PFBValue; + + ui8CommandLength = pui8RxBuffer[0]; + ui16Command = pui8RxBuffer[2] + (pui8RxBuffer[1] << 8); + + if(ui16Command == ATR_RES_CMD) + { + // + // Store the g_pui8NFCID3t + // + for(ui8Counter = 0; ui8Counter < 10; ui8Counter++) + { + g_pui8NFCID3t[ui8Counter] = pui8RxBuffer[3+ui8Counter]; + } + // + // LLCP Decoding - RFU + // + if(pui8RxBuffer[18] == LLCP_MAGIC_NUMBER_HIGH && \ + pui8RxBuffer[19] == LLCP_MAGIC_NUMBER_MIDDLE && \ + pui8RxBuffer[20] == LLCP_MAGIC_NUMBER_LOW) + { + ui8Counter = 0; + while(ui8CommandLength > (ui8Counter+21)) + { + // + // Process the TLV - pass the starting address of the TLV + // + LLCP_processTLV(&pui8RxBuffer[ui8Counter+21]); + + // + // Increment ui8Counter by the length+ 2 (type and length) of + // the current TLV + // + ui8Counter = ui8Counter+ pui8RxBuffer[ui8Counter+22] + 2; + } + // + // Set the next PDU for LLCP - SYMM PDU + // + LLCP_setNextPDU(LLCP_SYMM_PDU); + + // + // Reset the PNI + // + g_ui8NfcDepPni = 0x00; + tNextPduType = INFORMATION_PDU; + } + else + { + eNfcDepStatus = STATUS_FAIL; + } + } + else if(ui16Command == PSL_RES_CMD) + { + // + // Check if DID is correct + // + if(pui8RxBuffer[3] != 0x00) + eNfcDepStatus = STATUS_FAIL; + + } + else if(ui16Command == DEP_RES_CMD) + { + ui8PFBValue = pui8RxBuffer[3]; + + if((ui8PFBValue & 0xF0) == 0x00) + { + tNextPduType = INFORMATION_PDU; + } + else if((ui8PFBValue & 0xF0) == 0x10) + { + // + // Check if chaining is enabled + // + tNextPduType = ACK_PDU; + } + else if((ui8PFBValue & 0xF0) == 0x90) + { + tNextPduType = RTOX_REQ_PDU; + g_ui8RtoxTransportData = (0x3F & pui8RxBuffer[4]); + } + + if(tNextPduType == INFORMATION_PDU) + // + // LLCP Packet Handler + // + eNfcDepStatus = LLCP_processReceivedData(&pui8RxBuffer[4], + (ui8CommandLength-4)); + + } + else if(ui16Command == DSL_RES_CMD) + { + + } + else if(ui16Command == RSL_RES_CMD) + { + + } + else + { + eNfcDepStatus = STATUS_FAIL; + } + + return eNfcDepStatus; +} + +//***************************************************************************** +// +// NFCDEP_SendDEP_REQ - Send DEP_REQ to TRF79x0 +// +//***************************************************************************** +void NFCDEP_SendDEP_REQ(uint8_t * pui8RxBuffer) +{ + uint8_t ui8TotalLength = 0; + + if(tNextPduType == INFORMATION_PDU) + { + // + // Total = 1 byte Length + 2 bytes Command + 1 byte PFB + n PDU + // + ui8TotalLength = 4 + LLCP_stateMachine(&g_pui8DEPBufferPtr[4]); + + // + // PFB Byte + // + g_pui8DEPBufferPtr[3] = ((tNextPduType | (g_ui8NfcDepPni++)) & 0x03); + } + else if(tNextPduType == RTOX_REQ_PDU) + { + // + // PFB Byte + // + g_pui8DEPBufferPtr[3] = (tNextPduType); + + g_pui8DEPBufferPtr[4] = g_ui8RtoxTransportData; + ui8TotalLength = 5; + } + else if(tNextPduType == ACK_PDU) + { + // + // PFB Byte + // + g_pui8DEPBufferPtr[3] = ((tNextPduType | (g_ui8NfcDepPni++)) & 0x03); + + ui8TotalLength = 4; + } + + // + // Length + // + g_pui8DEPBufferPtr[0] = ui8TotalLength; + + // + // Command + // + g_pui8DEPBufferPtr[1] = (uint8_t) ((DEP_REQ_CMD & 0xFF00) >> 8); + g_pui8DEPBufferPtr[2] = (uint8_t) (DEP_REQ_CMD & 0x00FF); + + TRF79x0WriteFIFO(g_pui8DEPBufferPtr,CRC_BIT_ENABLE,ui8TotalLength); + + if(tNextPduType == RTOX_REQ_PDU) + if(TRF79x0IRQHandler(((2<> 8); + g_pui8DEPBufferPtr[2] = (uint8_t) (DEP_RES_CMD & 0x00FF); + + TRF79x0WriteFIFO(g_pui8DEPBufferPtr,CRC_BIT_ENABLE,ui8TotalLength); +} + +//***************************************************************************** +// +// NFCDEP_SetBufferPtr - set global buffer pointer to input pointer value +// +//***************************************************************************** +void NFCDEP_SetBufferPtr(uint8_t * buffer_ptr) +{ + g_pui8DEPBufferPtr = buffer_ptr; +} + diff --git a/nfclib/nfc_dep.h b/nfclib/nfc_dep.h new file mode 100644 index 0000000..4581ebe --- /dev/null +++ b/nfclib/nfc_dep.h @@ -0,0 +1,109 @@ +//***************************************************************************** +// +// nfc_dep.h - Defines for sending packets of P2P +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#ifndef __NFC_DEP_H__ +#define __NFC_DEP_H__ +#include "types.h" + +//***************************************************************************** +// +// List of Commands +// +//***************************************************************************** +// REQUESTS // +#define ATR_REQ_CMD 0xD400 +#define PSL_REQ_CMD 0xD404 +#define DEP_REQ_CMD 0xD406 +#define DSL_REQ_CMD 0xD408 +#define RSL_REQ_CMD 0xD40A + +// RESPONSES // +#define ATR_RES_CMD 0xD501 +#define PSL_RES_CMD 0xD505 +#define DEP_RES_CMD 0xD507 +#define DSL_RES_CMD 0xD509 +#define RSL_RES_CMD 0xD50B + + +#define DIDi 0x00 +#define BSi 0x00 +#define BRi 0x00 +//***************************************************************************** +// +// Initiator Maximum payload size + General bytes available (BIT1) +// B6 B5 - '00' Max Payload 64 bytes +// B6 B5 - '01' Max Payload 128 bytes +// B6 B5 - '10' Max Payload 192 bytes +// B6 B5 - '11' Max Payload 254 bytes (default) +// +//***************************************************************************** +#define PPi 0x32 + +#define DIDt 0x00 +#define BSt 0x00 +#define BRt 0x00 +#define TO 0x07 +//***************************************************************************** +// +// Target Maximum payload size + General bytes available (BIT1) +// B6 B5 - '00' Max Payload 64 bytes +// B6 B5 - '01' Max Payload 128 bytes +// B6 B5 - '10' Max Payload 192 bytes +// B6 B5 - '11' Max Payload 254 bytes (default) +// +//***************************************************************************** +#define PPt 0x32 + +//***************************************************************************** +// +// +// +//***************************************************************************** +typedef enum +{ + ACK_PDU = 0x40, + INFORMATION_PDU = 0x00, + NACK_PDU = 0x50, + ATN_PDU = 0x80, + RTOX_REQ_PDU = 0x90, + +}tPDUBlock; + +//***************************************************************************** +// +// Function Prototypes +// +//***************************************************************************** +void NFCDEP_SendATR_REQ(uint8_t * pui8NFCID2_Ptr); +void NFCDEP_SendPSL_REQ(void); +void NFCDEP_SendATR_RES(void); +void NFCDEP_SendRSL_RES(void); +void NFCDEP_SendPSL_RES(uint8_t did_value); + +tStatus NFCDEP_ProcessReceivedRequest(uint8_t * rx_buffer ,uint8_t * pui8NFCID2_Ptr, bool bActiveResponse); +tStatus NFCDEP_ProcessReceivedData(uint8_t * rx_buffer); +void NFCDEP_SendDEP_REQ(uint8_t * rx_buffer); +void NFCDEP_SendDEP_RES(void); +void NFCDEP_SetBufferPtr(uint8_t * buffer_ptr); + +#endif //__NFC_DEP_H__ diff --git a/nfclib/nfc_f.c b/nfclib/nfc_f.c new file mode 100644 index 0000000..c27ef22 --- /dev/null +++ b/nfclib/nfc_f.c @@ -0,0 +1,166 @@ +//***************************************************************************** +// +// nfc_f.c - contains implementation of NFC Type F (Felica) protocol +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "nfclib/nfc_f.h" +#include "nfclib/trf79x0.h" + +//***************************************************************************** +// +// NFC ID for TYPE F cards +// +//***************************************************************************** +uint8_t g_ui8NFCID2[8] = {0x01 , 0xFE, 0x88 , 0x77, 0x66 , 0x55, 0x44 , 0x33}; + +//***************************************************************************** +// +// Pointer to buffer +// +//***************************************************************************** +uint8_t * g_pui8NFC_F_BufferPtr; + +//***************************************************************************** +// +// Sens SENSF_REQ (request) +// +//***************************************************************************** +void NFCTypeF_SendSENSF_REQ(void) +{ + uint8_t ui8NFC_F_Packet[6]; + // + // Length + // + ui8NFC_F_Packet[0] = 0x06; + // + // Command + // + ui8NFC_F_Packet[1] = SENSF_REQ_CMD; + + ui8NFC_F_Packet[2] = 0xFF; // System Code (SC) 7:0 + ui8NFC_F_Packet[3] = 0xFF; // System Code (SC) 15:8 + + ui8NFC_F_Packet[4] = 0x00; // Request Code (RC) + + ui8NFC_F_Packet[5] = 0x03; // Time Slot Number (TSN) (DP, Table 42, 4 time slots) + TRF79x0WriteFIFO(ui8NFC_F_Packet,CRC_BIT_ENABLE,6); +} + +//***************************************************************************** +// +// Send SENSF_RES (response) +// +//***************************************************************************** +void NFCTypeF_SendSENSF_RES(void) +{ + uint8_t ui8NFC_F_Packet[18]; + uint8_t ui8Offset = 0; + uint8_t ui8Counter = 0; + // + // Length + // + ui8NFC_F_Packet[ui8Offset++] = 0x12; + // + // Command + // + ui8NFC_F_Packet[ui8Offset++] = SENSF_RES_CMD; + + for(ui8Counter = 0; ui8Counter < 8; ui8Counter++) + { + ui8NFC_F_Packet[ui8Offset++] = g_ui8NFCID2[ui8Counter]; + } + + // PAD 0 + ui8NFC_F_Packet[ui8Offset++] = 0xC0; + ui8NFC_F_Packet[ui8Offset++] = 0xC1; + // PAD 1 + ui8NFC_F_Packet[ui8Offset++] = 0xC2; + ui8NFC_F_Packet[ui8Offset++] = 0xC3; + ui8NFC_F_Packet[ui8Offset++] = 0xC4; + // MRTI CHECK + ui8NFC_F_Packet[ui8Offset++] = 0xC5; + // MRTI UPDATE + ui8NFC_F_Packet[ui8Offset++] = 0xC6; + // PAD2 + ui8NFC_F_Packet[ui8Offset++] = 0xC7; + + TRF79x0WriteFIFO(ui8NFC_F_Packet,CRC_BIT_ENABLE,ui8Offset); +} +//***************************************************************************** +// +// Process data received in buffer +// +//***************************************************************************** +tStatus NFCTypeF_ProcessReceivedData(uint8_t * pui8RxBuffer) +{ + volatile uint8_t ui8CommandLength; + uint8_t ui8Command; + uint8_t ui8Counter; + tStatus eNFCFStatus = STATUS_SUCCESS; + + ui8CommandLength = pui8RxBuffer[0]; + ui8Command = pui8RxBuffer[1]; + +// //UARTprintf("NFC_F CMD: %d \n",ui8Command); + + if(ui8Command == SENSF_RES_CMD) + { + // + // Store the g_ui8NFCID2 + // + for(ui8Counter = 0; ui8Counter < 8; ui8Counter++) + { + g_ui8NFCID2[ui8Counter] = pui8RxBuffer[2+ui8Counter]; + } + } + else if(ui8Command == SENSF_REQ_CMD && ui8CommandLength == 0x06 ) + { + if(pui8RxBuffer[2] == 0xFF && pui8RxBuffer[3] == 0xFF) + { + // Valid SENSF_REQ received - thus send a SENSF Response + NFCTypeF_SendSENSF_RES(); + } + else + eNFCFStatus = STATUS_FAIL; + } + else + { + eNFCFStatus = STATUS_FAIL; + } + return eNFCFStatus; +} + +//***************************************************************************** +// +// Return the NFCID +// +//***************************************************************************** +uint8_t * NFCTypeF_GetNFCID2(void) +{ + return g_ui8NFCID2; +} + + + + diff --git a/nfclib/nfc_f.h b/nfclib/nfc_f.h new file mode 100644 index 0000000..9292731 --- /dev/null +++ b/nfclib/nfc_f.h @@ -0,0 +1,48 @@ +//***************************************************************************** +// +// nfc_f.h - Type F (Felica) NFC Header +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#ifndef __NFC_F_H__ +#define __NFC_F_H__ + +#include "types.h" + +//***************************************************************************** +// +// List of Commands +// +//***************************************************************************** +#define SENSF_REQ_CMD 0x00 +#define SENSF_RES_CMD 0x01 + +//***************************************************************************** +// +// Function Prototypes +// +//***************************************************************************** +void NFCTypeF_SendSENSF_REQ(void); +void NFCTypeF_SendSENSF_RES(void); +tStatus NFCTypeF_ProcessReceivedData(uint8_t * pui8RxBuffer); +uint8_t * NFCTypeF_GetNFCID2(void); +void NFCTypeF_SetBufferPtr(uint8_t * buffer_ptr); + +#endif //__NFC_F_H__ diff --git a/nfclib/nfc_p2p.c b/nfclib/nfc_p2p.c new file mode 100644 index 0000000..7f1638a --- /dev/null +++ b/nfclib/nfc_p2p.c @@ -0,0 +1,1993 @@ +//***************************************************************************** +// +// nfc_p2p.c - contains implementation of p2p over NFC +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#include +#include +#include "nfclib/nfc_p2p.h" +#include "nfclib/nfc_f.h" +#include "nfclib/nfc_dep.h" +#include "nfclib/llcp.h" +#include "nfclib/snep.h" +#include "nfclib/debug.h" + +//***************************************************************************** +//! \addtogroup nfc_p2p_api NFC P2P API Functions +//! @{ +//! This module implements the encoding and decoding of NFC P2P messages +//! and records. +//! +//! It is assumed that users of this module have a functional knowledge of NFC +//! P2P messages and record types as defined by the NFC specification at +//! +//! http://www.nfc-forum.org/specs/spec_list . +//! +//! The functions in this module assume that the NFCP2P_proccessStateMachine() +//! is being called every 77ms or less as defined by the Digital +//! Protocol Technical Specification requirement 197. Before any of the +//! functions in this module are called, TRF79x0Init() and NFCP2P_init() must be +//! called to initialize the transceiver and the NFCP2P state machine. +// +//***************************************************************************** + +//***************************************************************************** +// +// Globals +// +//***************************************************************************** + +// +// Global pointer to recieve data, used by NFCP2PStateMachine(). +// +uint8_t *g_ui8RxDataPtr; + +// +// Flag to keep track of when to transmit data. Used by NFCP2PStateMachine(). +// +bool g_bTxDataAvailable = false; + +// +// Timout value aquired from lower level in NFC Stack, used by +// NFCP2PStateMachine() +// +uint16_t g_ui16TargetTimeout = 0; + +//***************************************************************************** +// +// State used by NFCP2PStateMachine. +// +// Options are: +// - NFC_P2P_PROTOCOL_ACTIVATION +// - NFC_P2P_PARAMETER_SELECTION +// - NFC_P2P_DATA_EXCHANGE_PROTOCOL +// - NFC_P2P_DEACTIVATION +// +//***************************************************************************** +tNFCP2PState g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + +//***************************************************************************** +// +// Global for what mode the TRF79x0 operates in. +// +// Options are: +// - BOARD_INIT +// - P2P_INITATIOR_MODE +// - P2P_PASSIVE_TARGET_MODE +// - P2P_ACTIVE_TARGET_MODE +// - CARD_EMULATION_TYPE_A +// - CARD_EMULATION_TYPE_B +// +//***************************************************************************** +tTRF79x0TRFMode g_eP2PMode; + +//***************************************************************************** +// +// Global for TRF79x0 operating frequency. +// +// Options are: +// - FREQ_STAND_BY +// - FREQ_106_KBPS +// - FREQ_212_KBPS +// - FREQ_424_KBPS +// +//***************************************************************************** +tTRF79x0Frequency g_eP2PFrequency; + +//***************************************************************************** +//! Initialize the variables used by the NFC Stack. +//! +//! \param eMode is the mode which to initialize the TRF79x0 +//! \param eFrequency is the frequency which to initialize the TRF79x0 +//! +//! This function must be called before any other NFCP2P function is called. +//! It can be called at any point to change the mode or frequency of the +//! TRF79x0 transceiver. This function initializes either the initiator or the +//! target mode. +//! +//! The \e eMode parameter can be any of the following: +//! +//! - \b BOARD_INIT - Initial Mode. +//! - \b P2P_INITATIOR_MODE - P2P Initiator Mode. +//! - \b P2P_PASSIVE_TARGET_MODE - P2P Passive Target Mode. +//! - \b P2P_ACTIVE_TARGET_MODE - P2P Active Target Mode. +//! - \b CARD_EMULATION_TYPE_A - Card Emulation for Type A cards. +//! - \b CARD_EMULATION_TYPE_B - Card Emulation for Type B cards. +//! +//! The \e eFrequency parameter can be any of the following: +//! +//! - \b FREQ_STAND_BY - Used for Board Initialization. +//! - \b FREQ_106_KBPS - Frequency of 106 kB per second. +//! - \b FREQ_212_KBPS - Frequency of 212 kB per second. +//! - \b FREQ_424_KBPS - Frequency of 424 kB per second. +//! +//! \return None. +// +//***************************************************************************** +void +NFCP2P_init(tTRF79x0TRFMode eMode,tTRF79x0Frequency eFrequency) +{ + // + // Reset Default Values + // + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + g_eP2PMode = eMode; + g_eP2PFrequency = eFrequency; + g_bTxDataAvailable = false; + g_ui16TargetTimeout = 0; + + // + // Store the nfc_buffer ptr in g_ui8RxDataPtr + // + g_ui8RxDataPtr = TRF79x0GetNFCBuffer(); + + // + // Initialize NFC DEP Global Pointer to use the g_ui8RxDataPtr pointer - + // the pointer is used to send responses/commands to the other Peer to + // Peer device. This implementation allows to reduce the RAM consumption. + // + NFCDEP_SetBufferPtr(g_ui8RxDataPtr); +} + +//***************************************************************************** +// +//! +//! Processes low level stack. +//! +//! \return This function returns the current NFCP2P state. +//! +//! The \e \b tNFCP2PState return parameter can be any of the following +//! - \b NFC_P2P_PROTOCOL_ACTIVATION - Polling/Listening for SENSF_REQ / SENSF_RES. +//! - \b NFC_P2P_PARAMETER_SELECTION - Setting the NFCIDs and bit rate +//! - \b NFC_P2P_DATA_EXCHANGE_PROTOCOL - Data exchange using the LLCP layer +//! - \b NFC_P2P_DEACTIVATION - Technology deactivation. +//! +//! This function must be executed every 77 ms or less as +//! defined by requirement 197 inside the Digital Protocol Technical +//! Specification. When the g_eP2PMode is set to P2P_INITATIOR_MODE, this +//! function sends a SENSF_REQ to check if there is a Target in the field, +//! while blocking the main application. If there is no target in the field, +//! it exits. When the g_eP2PMode is set to P2P_PASSIVE_TARGET_MODE, this +//! function waits for command for 495 ms, while blocking the main +//! application. If no commands are received or if any errors occurred, this +//! function exits. Once a technology is activated for either +//! P2P_INITATIOR_MODE or P2P_PASSIVE_TARGET_MODE, the main application can use +//! g_eNFCP2PState when equal to NFC_P2P_DATA_EXCHANGE_PROTOCOL, to then call +//! NFCP2P_sendPacket() to send data from the TRF7970A to a target/initiator. +//! Furthermore when g_eNFCP2PState is NFC_P2P_DATA_EXCHANGE_PROTOCOL, +//! the main application must check the receive state with the function +//! NFCP2P_getReceiveState() each time NFCP2P_proccessStateMachine() is +//! executed to ensure it handles the data as it is received. +//! +//! \return g_eNFCP2PState, which is the current P2P state. +// +//***************************************************************************** +tNFCP2PState +NFCP2P_proccessStateMachine(void) +{ + uint8_t *pui8NFCID2_Ptr=0; + + tTRF79x0IRQFlag eIRQStatus = IRQ_STATUS_IDLE; + + switch(g_eNFCP2PState) + { + case NFC_P2P_PROTOCOL_ACTIVATION: + { + if (g_eP2PMode == P2P_INITATIOR_MODE) + { + // + // Initialize the TRF7970A Registers for P2P Initiator Mode - + // in the case there is an external field enabled, the function + // will return STATUS_FAIL, the TRF7970 field will be disabled, + // and the program should switch to Target Mode. + // + if(TRF79x0Init2(P2P_INITATIOR_MODE, g_eP2PFrequency) == + STATUS_FAIL) + break; + + // + // Send SENSF_REQ + // + NFCTypeF_SendSENSF_REQ(); + + // + // Check if IRQ is triggered - timeout of 20 mS + // + if(TRF79x0IRQHandler(20) == IRQ_STATUS_RX_COMPLETE) + { + // + // Process the received data - check for valid SENSF_RES + // + if (NFCTypeF_ProcessReceivedData(g_ui8RxDataPtr) == + STATUS_SUCCESS) + { + g_eNFCP2PState = NFC_P2P_PARAMETER_SELECTION; + #ifdef DEBUG_PRINT + //UARTprintf("\nInitiator Activated \n"); + //UARTprintf("Exit PROT ACT \n"); + #endif + + break; + } + else + { + TRF79x0DisableTransmitter(); + break; + } + } + else + { + TRF79x0DisableTransmitter(); + break; + } + + } + else if (g_eP2PMode == P2P_PASSIVE_TARGET_MODE) + { + TRF79x0Init2(P2P_PASSIVE_TARGET_MODE, g_eP2PFrequency); + + // + // Poll the IRQ flag for 495 mS. + // + while(eIRQStatus != IRQ_STATUS_TIME_OUT) + { + eIRQStatus = TRF79x0IRQHandler(495); + + // + // Process the received data - check for valid SENSF_REQ + // + if((eIRQStatus == IRQ_STATUS_RX_COMPLETE) && + (NFCTypeF_ProcessReceivedData(g_ui8RxDataPtr) == + STATUS_SUCCESS)) + { + g_eNFCP2PState = NFC_P2P_PARAMETER_SELECTION; + break; + #ifdef DEBUG_PRINT + //UARTprintf("\nTarget Activated \n"); + //UARTprintf("Exit PROT ACT \n"); + #endif + + } + } + break; + + } + else if (g_eP2PMode == P2P_ACTIVE_TARGET_MODE) + { + TRF79x0Init2(P2P_ACTIVE_TARGET_MODE, g_eP2PFrequency); + + // + // Poll the IRQ flag for 495 mS. + // + while(eIRQStatus != IRQ_STATUS_TIME_OUT) + { + eIRQStatus = TRF79x0IRQHandler(495); + + // + // Process the received data - check for valid ATR_REQ + // + if((eIRQStatus == IRQ_STATUS_RX_COMPLETE) && + (NFCDEP_ProcessReceivedRequest(g_ui8RxDataPtr,0,true) == + STATUS_SUCCESS)) + { + g_eNFCP2PState = NFC_P2P_DATA_EXCHANGE_PROTOCOL; + break; + #ifdef DEBUG_PRINT + //UARTprintf("\nTarget Activated \n"); + //UARTprintf("Exit PROT ACT \n"); + #endif + + + } + } + break; + } + } + case NFC_P2P_PARAMETER_SELECTION: + { + // + // Reset the LLCP Parameters + // + LLCP_init(); + if (g_eP2PMode == P2P_INITATIOR_MODE) + { + pui8NFCID2_Ptr = NFCTypeF_GetNFCID2(); + NFCDEP_SendATR_REQ(pui8NFCID2_Ptr); + // + // Check if IRQ is triggered - timeout of 100 mS + // + if (TRF79x0IRQHandler(1000) == IRQ_STATUS_RX_COMPLETE) + { + // + // Process the received data - check for valid ATR_RES + // + if (NFCDEP_ProcessReceivedData(g_ui8RxDataPtr) + == STATUS_SUCCESS) + { + // + // If Current Frequency is 212 request to go to a higher + // baud rate + // + if(g_eP2PFrequency == FREQ_212_KBPS) + { + NFCDEP_SendPSL_REQ(); + + if (TRF79x0IRQHandler(1000) == + IRQ_STATUS_RX_COMPLETE) + { + if (NFCDEP_ProcessReceivedData(g_ui8RxDataPtr)== + STATUS_SUCCESS) + { + // + // If the function returns successful then + // the returned DID was correct. + // + TRF79x0SetMode(g_eP2PMode,FREQ_424_KBPS); + } + } + else + { + #ifdef DEBUG_PRINT + //UARTprintf("\nMCU Timed Out\n"); + //UARTprintf("Exit PARAM SEL\n"); + #endif + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + TRF79x0DisableTransmitter(); + break; + } + } + + g_eNFCP2PState = NFC_P2P_DATA_EXCHANGE_PROTOCOL; + #ifdef DEBUG_PRINT + //UARTprintf("Exit P2P PARM SEL\n"); + #endif + + g_ui16TargetTimeout = LLCP_getLinkTimeOut(); + + #ifdef DEBUG_PRINT + //UARTprintf("Time out is: %d",g_ui16TargetTimeout); + #endif + } + else + { + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + TRF79x0DisableTransmitter(); + break; + } + } + else + { + #ifdef DEBUG_PRINT + //UARTprintf("\nMCU Timed Out\n"); + //UARTprintf("Exit PARAM SEL\n"); + #endif + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + TRF79x0DisableTransmitter(); + break; + } + } + else if (g_eP2PMode == P2P_PASSIVE_TARGET_MODE) + { + // + // Check if IRQ is triggered - timeout of 100 mS + // + if (TRF79x0IRQHandler(1000) == IRQ_STATUS_RX_COMPLETE) + { + pui8NFCID2_Ptr = NFCTypeF_GetNFCID2(); + // + // Process the received data - check for valid ATR_REQ + // + if (NFCDEP_ProcessReceivedRequest(g_ui8RxDataPtr, + pui8NFCID2_Ptr,false) + == STATUS_SUCCESS) + { + g_eNFCP2PState = NFC_P2P_DATA_EXCHANGE_PROTOCOL; + #ifdef DEBUG_PRINT + //UARTprintf("Exit P2P PARM SEL\n"); + #endif + } + else + { + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + #ifdef DEBUG_PRINT + //UARTprintf("\nMCU Invalid ATR REQ\n"); + //UARTprintf("Exit P2P PARM SEL\n"); + #endif + break; + } + } + else + { + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + #ifdef DEBUG_PRINT + //UARTprintf("\nMCU Timed Out\n"); + //UARTprintf("Exit PARAM SEL\n"); + #endif + break; + //TRF79x0DisableTransmitter(); + } + } + else if (g_eP2PMode == P2P_ACTIVE_TARGET_MODE) + { + //TODO + break; + } + } + case NFC_P2P_DATA_EXCHANGE_PROTOCOL: + { + if (g_eP2PMode == P2P_INITATIOR_MODE) + { + NFCDEP_SendDEP_REQ(g_ui8RxDataPtr); + // + // Check if IRQ is triggered - timeout of 100 mS + // + if (TRF79x0IRQHandler(g_ui16TargetTimeout) == + IRQ_STATUS_RX_COMPLETE) + { + // + // Process the received data - check for valid DEP_RES + // + if (NFCDEP_ProcessReceivedData(g_ui8RxDataPtr) == + STATUS_FAIL) + { + //DebugPrintf("Exit DATA EXCHANGE\n"); + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + break; + } + + // + // Check if there is data to send to the Target. + // + if (g_bTxDataAvailable == true) + { + // + // Set the Connect PDU as the next command to the Target + // + if (LLCP_setNextPDU(LLCP_CONNECT_PDU) == STATUS_SUCCESS) + { + // + // If there was no ongoing connection, then clear + // the g_data_available flag + // + g_bTxDataAvailable = false; + } + } + } + else + { + #ifdef DEBUG_PRINT + //UARTprintf("\nMCU Timed Out \n"); + //UARTprintf("Exit DATA EXCHANGE\n"); + #endif + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + TRF79x0DisableTransmitter(); + break; + } + } + else if ((g_eP2PMode == P2P_PASSIVE_TARGET_MODE) || + (g_eP2PMode == P2P_ACTIVE_TARGET_MODE)) + { + // + // Check if IRQ is triggered - timeout of 100 mS + // + eIRQStatus = IRQ_STATUS_IDLE; + while((eIRQStatus == IRQ_STATUS_IDLE) || + (eIRQStatus == IRQ_STATUS_RF_FIELD_CHANGE) ) + { + eIRQStatus = TRF79x0IRQHandler(1000); + } + + if (eIRQStatus == IRQ_STATUS_RX_COMPLETE) + { + // + // Check if there is data to send to the Target. + // + if (g_bTxDataAvailable == true) + { + // + // Set the Connect PDU as the next command to the Target + // + if (LLCP_setNextPDU(LLCP_CONNECT_PDU) == STATUS_SUCCESS) + { + // + // If there was no ongoing connection, then clear + //the g_data_available flag + // + g_bTxDataAvailable = false; + } + } + + // + // Process the received data - check for valid DEP_REQ + // + if (NFCDEP_ProcessReceivedRequest(g_ui8RxDataPtr, + pui8NFCID2_Ptr,false) + == STATUS_FAIL) + { + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + #ifdef DEBUG_PRINT + //UARTprintf("Exit DATA EXCHANGE\n"); + #endif + break; + } + } + else if (eIRQStatus + == (IRQ_STATUS_RX_COMPLETE | IRQ_STATUS_FIFO_HIGH_OR_LOW)) + { + // Wait to receive the complete payload + } + else + { + g_eNFCP2PState = NFC_P2P_PROTOCOL_ACTIVATION; + #ifdef DEBUG_PRINT + //UARTprintf("\nMCU Timed Out \n"); + //UARTprintf("Exit DATA EXCHANGE\n"); + #endif + + break; + } + } + else if (g_eP2PMode == P2P_ACTIVE_TARGET_MODE) + { + //TODO + break; + } + } + case NFC_P2P_DEACTIVATION: + { + break; + } + } + + return g_eNFCP2PState; + +} + +//***************************************************************************** +// +//! Sends a raw buffer of data to the SNEP stack to be transmitted. +//! +//! \param pui8DataPtr is a pointer to the raw data to be sent. +//! \param ui32DataLength is the length of the raw data. +//! +//! This function is used to send a data stream over NFC. The buffer resulting +//! from a call to NFCP2P_NDEFMessageEncoder() should be fed to this function. +//! +//! \return Status of sent packet. +//! +//! The \e \b tStatus parameter can be any of the following: +//! +//! - \b STATUS_FAIL - The function exited with a failure. +//! - \b STATUS_SUCCESS - The function ended in succes. +// +//***************************************************************************** +tStatus +NFCP2P_sendPacket(uint8_t *pui8DataPtr, uint32_t ui32DataLength) +{ + g_bTxDataAvailable = true; + return SNEP_setupPacket(pui8DataPtr,ui32DataLength); +} + +//***************************************************************************** +// +//! NFCP2P_getReceiveState - Gets the receive state from the low level SNEP +//! stack. +//! +//! Description: This function is used to get the receive payload status +//! from the SNEP layer. +//! +//! \return This function returns the receive state. +// +//***************************************************************************** +sNFCP2PRxStatus +NFCP2P_getReceiveState(void) +{ + sNFCP2PRxStatus eReceiveStatus; + + SNEP_getReceiveStatus(&eReceiveStatus.eDataReceivedStatus, + &eReceiveStatus.ui8DataReceivedLength, + &eReceiveStatus.pui8RxDataPtr); + + return eReceiveStatus; +} + +//***************************************************************************** +// +//! Encodes NFC Message meta-data and payload information. +//! +//! \param sNDEFDataToSend is a sNDEFMessageData structure filled out with the +//! NDEF message to send. +//! \param pui8Buffer is a pointer to the buffer where the raw encoded data will +//! be stored +//! \param ui16BufferMaxLength is the maximum number of bytes the buffer +//! can hold. This parameter is used to prevent writing past the end of the +//! buffer. +//! \param pui32BufferLength is a pointer to an integer that is filled with +//! the length of the raw data encoded to the \b pui8Buffer. +//! +//! This function takes a filled sNDEFMessageData structure and encodes it to +//! the provided buffer. The length, in bytes, of the data encoded to the buffer +//! is stored into the integer pointer provided. +//! +//! \return This function returns \b STATUS_SUCCESS (1) or \b STATUS_FAIL (0). +//! +// +// Note: for an explanation of the fields please see the Programmers Note in +// nfc_p2p.h +//***************************************************************************** +bool +NFCP2P_NDEFMessageEncoder(sNDEFMessageData sNDEFDataToSend, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t *pui32BufferLength) +{ + uint32_t ui32HeaderSize = 0; + uint32_t x; + sNDEFMessageData sMessage = sNDEFDataToSend; + + // + // Check Arguements, ASSERT / return STATUS_FAIL as appropriate + // + ASSERT(ui16BufferMaxLength > 0); + ASSERT(pui8Buffer != 0); + ASSERT(sNDEFDataToSend.ui8TypeLength > 0); + ASSERT(sNDEFDataToSend.ui32PayloadLength > 0); + ASSERT(sNDEFDataToSend.pui8PayloadPtr != 0); + ASSERT(sNDEFDataToSend.ui32PayloadLength < ui16BufferMaxLength); + if( + (ui16BufferMaxLength == 0) || + (pui8Buffer == 0) || + (sNDEFDataToSend.ui8TypeLength == 0) || + (sNDEFDataToSend.ui32PayloadLength == 0 ) || + (sNDEFDataToSend.pui8PayloadPtr == 0) || + (sNDEFDataToSend.ui32PayloadLength > ui16BufferMaxLength) + ) + { + DebugPrintf(" ERR: NDEFMessageEncoder: Invalid Input\n"); + return STATUS_FAIL; + } + if(ui16BufferMaxLength < 25) + { + DebugPrintf("Warning: NDEFMessageEncoder : You need a bigger buffer\n"); + } + + // + // Fill STATUS_BYTE field + // + pui8Buffer[ui32HeaderSize] = ( + NDEF_STATUSBYTE_SET_MB(sMessage.sStatusByte.MB) | + NDEF_STATUSBYTE_SET_ME(sMessage.sStatusByte.ME) | + NDEF_STATUSBYTE_SET_CF(sMessage.sStatusByte.CF) | + NDEF_STATUSBYTE_SET_SR(sMessage.sStatusByte.SR) | + NDEF_STATUSBYTE_SET_IL(sMessage.sStatusByte.IL) | + NDEF_STATUSBYTE_SET_TNF(sMessage.sStatusByte.TNF) + ); + ui32HeaderSize++; + + // + // Fill TYPE_LENGTH field + // + pui8Buffer[ui32HeaderSize] = sMessage.ui8TypeLength; + ui32HeaderSize++; + + // + // Fill PAYLOAD_LENGTH field. + // based on StatusByte.SR field. May truncate if improperly set. + // + switch(sMessage.sStatusByte.SR) + { + // + // PAYLOAD_LENGTH is 1 byte long + // + case NDEF_STATUSBYTE_SR_1BYTEPAYLOADSIZE: + { + pui8Buffer[ui32HeaderSize] = (sMessage.ui32PayloadLength & 0xFF); + ui32HeaderSize++; + break; + } + + // + // PAYLOAD_LENGTH is 4 bytes long, inverted order (NFC Standard) + // + case NDEF_STATUSBYTE_SR_4BYTEPAYLOADSIZE: + { + pui8Buffer[ui32HeaderSize+0] = ((sMessage.ui32PayloadLength >> 3*8) + & 0xFF); + pui8Buffer[ui32HeaderSize+1] = ((sMessage.ui32PayloadLength >> 2*8) + & 0xFF); + pui8Buffer[ui32HeaderSize+2] = ((sMessage.ui32PayloadLength >> 1*8) + & 0xFF); + pui8Buffer[ui32HeaderSize+3] = ((sMessage.ui32PayloadLength >> 0*8) + & 0xFF); + ui32HeaderSize = ui32HeaderSize + 4; + break; + } + + // + // default case, should never get here, if you do its an error + // + default: + { + DebugPrintf("ERR: NFC Header Encoder fn PAYLOAD_LENGTH field\n"); + return STATUS_FAIL; + break; + } + } + + // + // Fill ID_LENGTH field. + // depends on Statusbyte.IL, if IL not set but data given in ui8IDLength + // the data will be ignored. + // + switch(sMessage.sStatusByte.IL) + { + // + // No ID_LENGTH field included + // + case NDEF_STATUSBYTE_IL_IDLENGTHABSENT: + { + // do nothing + break; + } + + // + // ID_LENGTH field present, fill data, incriment buffer pointer + // + case NDEF_STATUSBYTE_IL_IDLENGTHPRESENT: + { + pui8Buffer[ui32HeaderSize] = sMessage.ui8IDLength; + ui32HeaderSize++; + break; + } + + // + // default case, should never get here, if you do its an error. + // + default: + { + DebugPrintf("ERR: NFC Header Encoder fn ID_LENGTH field\n"); + return STATUS_FAIL; + break; + } + } + + // + // Fill TYPE field. If TYPE_LENGTH > NDEF_TYPE_MAXSIZE then TYPE will be + // truncated to MAXSIZE + // + if(0 == sMessage.ui8TypeLength) + { + // + // do nothing + // TYPE_LENGTH = 0, so there is nothing to put in the TYPE field + // + } + else + { + for(x = 0;(x < sMessage.ui8TypeLength) && (x < NDEF_TYPE_MAXSIZE); x++) + { + pui8Buffer[ui32HeaderSize] = sMessage.pui8Type[x]; + ui32HeaderSize++; + } + } + + // + // Fill ID field. If ID_LENGTH > NDEF_ID_MAXSIZE then ID will be truncated + // to MAXSIZE. + // + switch(sMessage.sStatusByte.IL) + { + // + // StatusByte.IL says no ID_LENGTH field, thus no ID field. + // + case NDEF_STATUSBYTE_IL_IDLENGTHABSENT:{ + //do nothing. + break; + } + + // + // StatusByte.IL says ID_LENGTH Exists, so add the ID. + // + case NDEF_STATUSBYTE_IL_IDLENGTHPRESENT: + { + if(0 == sMessage.ui8IDLength) + { + // + // Do nothing. ID_LENGTH = 0 so there is no ID to add. + // + } + else + { + for(x = 0;(x < sMessage.ui8IDLength) && (x < NDEF_ID_MAXSIZE); + x++) + { + pui8Buffer[ui32HeaderSize] = sMessage.pui8ID[x]; + ui32HeaderSize++; + } + } + break; + } + } + + // + // Make sure we wont overflow the buffer with the payload in the next step. + // + if((ui32HeaderSize + sMessage.ui32PayloadLength) > ui16BufferMaxLength) + { + ASSERT(0); + DebugPrintf("ERR:NDEFMessageEncoder: BufferOverflow Payload too big\n"); + return STATUS_FAIL; + } + + // + // Fill PAYLOAD buffer. + // + if(sMessage.sStatusByte.SR == NDEF_STATUSBYTE_SR_1BYTEPAYLOADSIZE) + { + // + // 1 byte PAYLOAD_LENGTH. + // + for(x = 0;x < (sMessage.ui32PayloadLength & 0xFF);x++) + { + pui8Buffer[ui32HeaderSize] = sMessage.pui8PayloadPtr[x]; + ui32HeaderSize++; + } + } + else + { + // + // 4 byte PAYLOAD_LENGTH. + // (treat Payload length as a 32bit number) + // + for(x = 0;x < sMessage.ui32PayloadLength; x++) + { + pui8Buffer[ui32HeaderSize] = sMessage.pui8PayloadPtr[x]; + ui32HeaderSize++; + } + } + + // + // Fill BufferLength variable. + // + *pui32BufferLength = ui32HeaderSize; + + + return STATUS_SUCCESS; + +} + +//***************************************************************************** +// +//! Decodes NFC Message meta-data and payload information. +//! +//! \param psNDEFDataDecoded is a pointer to the sNDEFMessageData structure to +//! be filled. +//! \param pui8Buffer is a pointer to the raw NFC data buffer from which to +//! decode the data. +//! \param ui16BufferMaxLength is the maximum number of bytes the buffer +//! can hold. This parameter is used to prevent reading past the end of the +//! buffer. +//! +//! This function takes in a buffer of raw NFC data and fills up an +//! sNDEFMessageData structure. This function is the first step to decoding an +//! NFC Message. The next step is to decode the Message Payload, which is the record. +//! The decoded sNDEFMessageData structure has a field named \b pui8Type. The +//! \b pui8Type field defines the record type and therefore indicates which +//! RecordDecoder function to use on the Message Payload. +//! +//! \return This function returns \b STATUS_SUCCESS (1) or \b STATUS_FAIL (0). +// +// Note: local variables are used to break out fields from the header for +// clarity. ui32HeaderSize is used to keep track of how large the header +// is in bytes. It is used to computer the size of the payload at the end. +// (length of Buffer - HeaderSize = Payload length) +// +//***************************************************************************** +bool +NFCP2P_NDEFMessageDecoder(sNDEFMessageData *psNDEFDataDecoded, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength) +{ + sNDEFMessageData *psMessage; + uint8_t ui8StatusByte,ui8TypeLength,ui8IDLength; + uint8_t *pui8PayloadPtr; + uint32_t ui32HeaderSize = 0; + uint32_t ui32PayloadLength=0; + uint32_t x; + + // + // Check Input for Validity + // + ASSERT(pui8Buffer != 0); + ASSERT(ui16BufferMaxLength > 0); + + // + // Minimum length of header is 5 bytes. + // + if(ui16BufferMaxLength <= 5) + { + DebugPrintf("ERR: NDEFMessageDecoder: Invalid Input\n"); + return STATUS_FAIL; + } + + psMessage = psNDEFDataDecoded; + + // + // Load Status Byte into NDEF Structure. + // + ui8StatusByte = pui8Buffer[ui32HeaderSize]; + psMessage->sStatusByte.MB = NDEF_STATUSBYTE_GET_MB(ui8StatusByte); + psMessage->sStatusByte.ME = NDEF_STATUSBYTE_GET_ME(ui8StatusByte); + psMessage->sStatusByte.CF = NDEF_STATUSBYTE_GET_CF(ui8StatusByte); + psMessage->sStatusByte.SR = NDEF_STATUSBYTE_GET_SR(ui8StatusByte); + psMessage->sStatusByte.IL = NDEF_STATUSBYTE_GET_IL(ui8StatusByte); + psMessage->sStatusByte.TNF = NDEF_STATUSBYTE_GET_TNF(ui8StatusByte); + + // + // Increment size of header (+1 for the size of the Status Byte). + // + ui32HeaderSize++; + + // + // Load TypeLength byte into NDEF Structure. + // + ui8TypeLength = pui8Buffer[ui32HeaderSize]; + psMessage->ui8TypeLength = ui8TypeLength; + + // + // Increment size of header (+1 for the size of the Status Byte). + // + ui32HeaderSize++; + + // + // Determine the payload size based upon the SR field in the header. + // + switch (psMessage->sStatusByte.SR) + { + // + // Short Record (PAYLOAD_LENGTH field is 1 byte). + // + case NDEF_STATUSBYTE_SR_1BYTEPAYLOADSIZE: + { + ui32PayloadLength = pui8Buffer[ui32HeaderSize]; + + // + // Validate Data + // + if((ui32HeaderSize + ui32PayloadLength) > ui16BufferMaxLength) + { + ASSERT(0); + DebugPrintf( + "ERR: NFCP2P_NDEFMessageDecoder: ui32PayloadLength > ui16BufferMaxLength\n"); + DebugPrintf("\tYou Need a bigger buffer to hold this message.\n"); + return STATUS_FAIL; + } + else + { + // + // Set Payload Length + // + psMessage->ui32PayloadLength = ui32PayloadLength; + ui32HeaderSize++; + } + break; + } + + // + // Normal Record (PAYLOAD_LENGTH field is 4 bytes). + // + case NDEF_STATUSBYTE_SR_4BYTEPAYLOADSIZE: + { + ui32PayloadLength = + ( + (pui8Buffer[ui32HeaderSize + 3] << 0*8) | + (pui8Buffer[ui32HeaderSize + 2] << 1*8) | + (pui8Buffer[ui32HeaderSize + 1] << 2*8) | + (pui8Buffer[ui32HeaderSize + 0] << 3*8) + ); + + // + // Validate Data + // + if((ui32HeaderSize + ui32PayloadLength) > ui16BufferMaxLength) + { + ASSERT(0); + DebugPrintf( + "ERR: NFCP2P_NDEFMessageDecoder: ui32PayloadLength > ui16BufferMaxLength\n"); + DebugPrintf("\tYou Need a bigger buffer to hold this message.\n"); + return STATUS_FAIL; + } + else + { + // + // Set Payload Length + // + psMessage->ui32PayloadLength = ui32PayloadLength; + ui32HeaderSize = ui32HeaderSize + 4; + } + break; + } + + // + // This should never happen. return error. + // + default: + { + DebugPrintf("NDEFMessageDecoder: ERR decoding SR bit \n"); + ASSERT(0); + return STATUS_FAIL; + break; + } + } + + // + // Load ID_LENGTH field, if it exists. Depends on StatusByte.IL. + // + switch (psMessage->sStatusByte.IL) + { + // + // ID_LENGTH field exists. Load it to the NDEF structure. + // + case NDEF_STATUSBYTE_IL_IDLENGTHPRESENT: + { + ui8IDLength = pui8Buffer[ui32HeaderSize]; + psMessage->ui8IDLength = ui8IDLength; + ui32HeaderSize++; + break; + } + + // + // ID_LENGTH field does not exist and thus the ID field doesnt exists. + // Load 0 to NDEF structure to express this + // + case NDEF_STATUSBYTE_IL_IDLENGTHABSENT: + { + psMessage->ui8IDLength = 0; + break; + } + + // + // This should never happen. return error. + // + default: + { + DebugPrintf( + "ERR: Invalid ID_LENGTH field Detected in NDEFMessageDecoder\n"); + ASSERT(0); + return STATUS_FAIL; + break; + } + } + + // + // Load TYPE field based on length in TYPE_LENGTH field + // + // If TYPE_LENGTH value is larger than NDEF_TYPE_MAXSIZE truncate to MAXSIZE + // and adjust index in buffer to end of TYPE so as to not lose data / skew + // pointer. + // + if(psMessage->ui8TypeLength > NDEF_TYPE_MAXSIZE) + { + #ifdef DEBUG_PRINT + ASSERT(0); + UARTprintf("ERR: MessageDecode: TYPE > NDEF_TYPE_MAXSIZE, truncating to %d bytes\n", + NDEF_TYPE_MAXSIZE); + UARTprintf(" Orig Type = "); + for(x = 0;x < psMessage->ui8TypeLength;x++) + { + UARTprintf("%c",pui8Buffer[ui32HeaderSize + x]); + } + UARTprintf("\n"); + #endif + + // + // Copy across truncated TYPE + // + for(x = 0;x < NDEF_TYPE_MAXSIZE;x++) + { + psMessage->pui8Type[x] = pui8Buffer[ui32HeaderSize]; + ui32HeaderSize++; + } + + // + // Adjust index appropriately. + // + ui32HeaderSize = ui32HeaderSize + + (psMessage->ui8TypeLength - NDEF_TYPE_MAXSIZE); + psMessage->ui8TypeLength = NDEF_TYPE_MAXSIZE; + } + else + { + // + // No problem + // Load Type field into NDEF structure + // + for(x = 0;x < psMessage->ui8TypeLength;x++) + { + psMessage->pui8Type[x] = pui8Buffer[ui32HeaderSize]; + ui32HeaderSize++; + } + } + + // + // Load ID field into NDEF structure. Depends on length in ID_LENGTH field. + // if ID field is > NDEF_ID_MAXSIZE truncate to MAXSIZE + // + if(psMessage->ui8IDLength > NDEF_ID_MAXSIZE) + { + #ifdef DEBUG_PRINT + ASSERT(0); + UARTprintf("ERR: ID_LENGTH > NDEF_ID_MAXSIZE, trucating to %d bytes\n", + NDEF_ID_MAXSIZE); + UARTprintf(" Orig ID = "); + for(x = 0;x < psMessage->ui8IDLength;x++) + { + UARTprintf("%c",pui8Buffer[ui32HeaderSize + x]); + } + UARTprintf("\n"); + #endif + + // + // Copy across truncated ID + // + for(x = 0;x < NDEF_ID_MAXSIZE;x++) + { + psMessage->pui8ID[x] = pui8Buffer[ui32HeaderSize]; + ui32HeaderSize++; + } + + // + // adjust index appropriately + // + ui32HeaderSize = ui32HeaderSize + (psMessage->ui8IDLength - + NDEF_ID_MAXSIZE); + psMessage->ui8IDLength = NDEF_ID_MAXSIZE; + } + else + { + // + // No problem + // Load ID field into NDEF structure + // + for(x = 0;x < psMessage->ui8IDLength;x++) + { + psMessage->pui8ID[x] = pui8Buffer[ui32HeaderSize]; + ui32HeaderSize++; + } + } + + // + // Error Check + // Check to make sure we didnt overrun the buffer / read beyond its bounds. + // + if((ui32HeaderSize + psMessage->ui32PayloadLength) > ui16BufferMaxLength) + { + ASSERT(0); + DebugPrintf("ERR: NDEFMessageDecode: Buffer OverRun / OverRead\n"); + + // + // Clear all data out of datastrucutre, dont return invalid data. + // + psMessage->ui32PayloadLength=0; + psMessage->pui8PayloadPtr=0; + + return STATUS_FAIL; + } + + // + // Calculate Payload Pointer (payload is located after the header) + // + pui8PayloadPtr = pui8Buffer + ui32HeaderSize; + + // + // Set the Message Payload Pointer + // + psMessage->pui8PayloadPtr = pui8PayloadPtr; + + return STATUS_SUCCESS; +} + +//***************************************************************************** +// +//! Encode NDEF Text Records. +//! +//! \param sTextRecord is the Text Record Structure to be encoded. +//! \param pui8Buffer is a pointer to the buffer to fill with the raw NFC data. +//! \param ui16BufferMaxLength is the maximum number of bytes the buffer +//! can hold. This parameter is used to prevent writing past the end of the +//! buffer. +//! \param pui32BufferLength is a pointer to the integer to hold the length of +//! the raw NFC data buffer. +//! +//! This function takes a TextRecord structure and encodes it into a provided +//! buffer in the raw NFC data format. The length of the data stored in the +//! buffer is stored in \e ui32BufferLength. +//! +//! \return This function returns \b STATUS_SUCCESS (1) or \b STATUS_FAIL (0). +// +//***************************************************************************** +bool +NFCP2P_NDEFTextRecordEncoder(sNDEFTextRecord sTextRecord, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t *pui32BufferLength) +{ + uint8_t x; + uint32_t ui32RecordIndex = 0; + + // + // Validate Input + // + ASSERT(pui8Buffer != 0); + ASSERT(ui16BufferMaxLength > 0); + ASSERT(pui32BufferLength != 0); + ASSERT(sTextRecord.pui8Text != 0); + ASSERT(sTextRecord.ui32TextLength > 0); + ASSERT(sTextRecord.ui32TextLength < ui16BufferMaxLength); + if( (pui8Buffer == 0) || + (ui16BufferMaxLength == 0) || + (pui32BufferLength == 0) || + (sTextRecord.pui8Text == 0) || + (sTextRecord.ui32TextLength == 0) || + (sTextRecord.ui32TextLength > ui16BufferMaxLength)) + { + DebugPrintf("ERR: NDEFTextRecordEncoder: Invalid Input\n"); + return STATUS_FAIL; + } + + // + // Fill StatusByte in buffer + // + pui8Buffer[ui32RecordIndex] = + ( + NDEF_TEXTRECORD_STATUSBYTE_SET_UTF(sTextRecord.sStatusByte.bUTFcode) | + NDEF_TEXTRECORD_STATUSBYTE_SET_RFU(sTextRecord.sStatusByte.bRFU ) | + NDEF_TEXTRECORD_STATUSBYTE_SET_LENGTHLANGCODE( + sTextRecord.sStatusByte.ui5LengthLangCode) + ); + ui32RecordIndex++; + + // + // Validate LanguageCode Length + // + if(sTextRecord.sStatusByte.ui5LengthLangCode > + NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE) + { + ASSERT(0); + DebugPrintf("Err: TextRecordEncoder: ui5LengthLanguageCode > "); + DebugPrintf("NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE\n"); + DebugPrintf("\t Truncating from %d to MaxSize of %d.\n", + sTextRecord.sStatusByte.ui5LengthLangCode, + NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE); + } + + // + // Fill LanguageCode in buffer + // + for(x = 0;x < sTextRecord.sStatusByte.ui5LengthLangCode;x++) + { + pui8Buffer[ui32RecordIndex] = sTextRecord.pui8LanguageCode[x]; + ui32RecordIndex++; + } + + // + // Error Check + // + if((ui32RecordIndex + sTextRecord.ui32TextLength) > ui16BufferMaxLength) + { + ASSERT(0); + DebugPrintf("ERR: NDEFTextRecordEncode: Buffer Overflow Immenant\n"); + return STATUS_FAIL; + } + + // + // Fill Text in buffer + // + for(x = 0;x < sTextRecord.ui32TextLength;x++) + { + pui8Buffer[ui32RecordIndex] = sTextRecord.pui8Text[x]; + ui32RecordIndex++; + } + + // + // Set buffer length + // + *pui32BufferLength = ui32RecordIndex; + + return STATUS_SUCCESS; +} + +//***************************************************************************** +// +//! Decode NDEF Text Records. +//! +//! \param psTextDataDecoded is a pointer to the TextRecord structure to decode +//! the data into. +//! \param pui8Buffer is a pointer to the raw NFC data buffer to be decoded. +//! \param ui32BufferLength is the length of the raw NFC data buffer. +//! +//! This function takes a raw NFC data buffer and decodes the data into a Text +//! record data structure. It is assumed that the raw data buffer contains a +//! text record. +//! +//! \return This function returns \b STATUS_SUCCESS (1) or \b STATUS_FAIL (0). +// +//***************************************************************************** +bool +NFCP2P_NDEFTextRecordDecoder(sNDEFTextRecord *psTextDataDecoded, + uint8_t *pui8Buffer, + uint32_t ui32BufferLength) +{ + sNDEFTextRecord *psTextRecord; + uint8_t ui8StatusByte, ui8LengthLangCode, x = 0; + uint32_t ui32RecordIndex = 0; + + // + // Validate Input + // + ASSERT(pui8Buffer != 0); + ASSERT(psTextDataDecoded != 0); + if( + (pui8Buffer == 0) || + (psTextDataDecoded == 0) + ) + { + DebugPrintf("ERR: TextRecordDecoder: Invalid Input\n"); + return STATUS_FAIL; + } + + // + // Initialize (done to insure 0 as sentinel in language Code) + // + psTextRecord = psTextDataDecoded; + for(x = 0;x < NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE;x++) + { + psTextRecord->pui8LanguageCode[x] = 0; + } + psTextRecord->ui32TextLength = 0; + + // + // Load STATUSBYTE field + // + ui8StatusByte = pui8Buffer[ui32RecordIndex]; + psTextRecord->sStatusByte.bUTFcode = + NDEF_TEXTRECORD_STATUSBYTE_GET_UTF(ui8StatusByte); + psTextRecord->sStatusByte.bRFU = + NDEF_TEXTRECORD_STATUSBYTE_GET_RFU(ui8StatusByte); + ui8LengthLangCode = + NDEF_TEXTRECORD_STATUSBYTE_GET_LENGTHLANGCODE(ui8StatusByte); + psTextRecord->sStatusByte.ui5LengthLangCode = ui8LengthLangCode; + ui32RecordIndex++; + + // + // The StatusByte.RFU should always be 0, if this is not the case return + // failure + // + if(psTextRecord->sStatusByte.bRFU != 0) + { + ASSERT(0); + DebugPrintf("Err: NDEF TextRecord Decoder: StatusByte.RFU !=0\n"); + return STATUS_FAIL; + } + + // + // LengthLangCode must be > 0 + // + if(ui8LengthLangCode <= 0) + { + ASSERT(0); + DebugPrintf("ERR: NDEFTextRecordDecoder: LengthLangCode <= 0\n"); + return STATUS_FAIL; + + } + + // + // Load LANGUAGE_CODE field + // + for(x = 0;x < ui8LengthLangCode;x++) + { + // + // If space left in LanguageCode field put character in, otherwise + // truncate. (dont copy across, but do incriment through raw buffer) + // + if(x < NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE) + { + psTextRecord->pui8LanguageCode[x] = pui8Buffer[ui32RecordIndex]; + ui32RecordIndex++; + } + else + { + ui32RecordIndex++; + } + } + + // + // Validate Data + // + if(ui8LengthLangCode > NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE) + { + DebugPrintf("ERR: TextRecordDecoder: LengthLangCode > "); + DebugPrintf("NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE, truncating %d to %d", + ui8LengthLangCode,NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE); + DebugPrintf("\n"); + psTextRecord->sStatusByte.ui5LengthLangCode = + NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE; + } + + // + // Load pointer to Text + // + psTextRecord->pui8Text = pui8Buffer + ui32RecordIndex; + + // + // Validate Data - make sure we dont overrun the buffer + // + if(ui32RecordIndex > ui32BufferLength) + { + ASSERT(0); + DebugPrintf("ERR: TextRecordDecoder: Text Length longer than payload."); + DebugPrintf("\n"); + return STATUS_FAIL; + } + else + { + // + // Calculate Length of Text + // Length of text = Length of Record - RecordIndex to this point. + // + psTextRecord->ui32TextLength = ui32BufferLength-ui32RecordIndex; + } + + return STATUS_SUCCESS; +} + +//***************************************************************************** +// +//! Encode NDEF URI Records. +//! +//! \param sURIRecord is the URI Record Structure to be encoded. +//! \param pui8Buffer is a pointer to the buffer to fill with the raw NFC data. +//! \param ui16BufferMaxLength is the maximum number of bytes the buffer +//! can hold. This parameter is used to prevent writing past the end of the +//! buffer. +//! \param pui32BufferLength is a pointer to the integer to hold the length of +//! the raw NFC data buffer. +//! +//! This function takes a URI Record structure and encodes it into a provided +//! buffer in a raw NFC data format. The length of the data stored in the buffer +//! is stored in \e \b pui32BufferLength. +//! +//! \return This function returns \b STATUS_SUCCESS (1) or \b STATUS_FAIL (0). +// +//***************************************************************************** +bool +NFCP2P_NDEFURIRecordEncoder(sNDEFURIRecord sURIRecord, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t *pui32BufferLength) +{ + uint32_t ui32RecordIndex = 0; + uint8_t x = 0; + + // + // Validate Input + // + ASSERT(pui8Buffer != 0); + ASSERT(ui16BufferMaxLength !=0); + ASSERT((sURIRecord.ui32URILength +1) < ui16BufferMaxLength); + if( + (pui8Buffer == 0) || + (ui16BufferMaxLength ==0) || + ((sURIRecord.ui32URILength +1) > ui16BufferMaxLength) + ) + { + ASSERT(0); + DebugPrintf("ERR: URIRecordEncoder: Invalid Input\n"); + return STATUS_FAIL; + } + + // + // Fill IDCode field in buffer + // + pui8Buffer[ui32RecordIndex] = sURIRecord.eIDCode; + ui32RecordIndex++; + + // + // Fill UTF8 string into buffer + // + for(x = 0;x < sURIRecord.ui32URILength;x++) + { + pui8Buffer[ui32RecordIndex] = sURIRecord.puiUTF8String[x]; + ui32RecordIndex++; + } + + // + // Set Buffer Length + // + *pui32BufferLength = ui32RecordIndex; + + return STATUS_SUCCESS; + +} + +//***************************************************************************** +// +//! Decode NDEF URI Records. +//! +//! \param sURIRecord is a pointer to the URIRecord structure into which to +//! decode the data. +//! \param pui8Buffer is a pointer to the raw NFC data buffer to be decoded. +//! \param ui32BufferLength is the length of the raw NFC data buffer. +//! +//! This function takes a raw NFC data buffer and decodes the data into a URI +//! record data structure. It is assumed that the raw data buffer contains a +//! URI record. +//! +//! \return This function returns \b STATUS_SUCCESS (1) or \b STATUS_FAIL (0). +// +//***************************************************************************** +bool +NFCP2P_NDEFURIRecordDecoder(sNDEFURIRecord *sURIRecord, + uint8_t *pui8Buffer, + uint32_t ui32BufferLength) +{ + uint32_t ui32RecordIndex = 0; + + // + // Validate Input + // + ASSERT(pui8Buffer != 0); + ASSERT(sURIRecord != 0); + if( + (pui8Buffer == 0) || + (sURIRecord == 0) + ) + { + DebugPrintf("ERR: URIRecordDecoder: Invalid Input\n"); + return STATUS_FAIL; + } + + // + // Load eIDCode field into struct + // error check that the ID code is valid. + // + if(pui8Buffer[ui32RecordIndex] >= NDEF_URIRECORD_IDCODE_RFU) + { + // + // IDCode not recognized, skip it. + // (can add codes in nfc_p2p.h eNDEF_URIRecord_IDCode enumeration) + // + DebugPrintf("ERR: URI Record Decoder: URI ID Code Not Recognized: 0x%x\n" + ,pui8Buffer[ui32RecordIndex]); + sURIRecord->eIDCode = RFU; + ui32RecordIndex++; + //return STATUS_FAIL; + } + else + { + // + // ID Code is Valid, set it. + // + sURIRecord->eIDCode = pui8Buffer[ui32RecordIndex]; + ui32RecordIndex++; + } + + // + // Load UTF8 String Pointer into struct + // + sURIRecord->puiUTF8String = pui8Buffer + ui32RecordIndex; + + // + // Load URI string Length into struct + // + sURIRecord->ui32URILength = ui32BufferLength-ui32RecordIndex; + + return STATUS_SUCCESS; +} + +//***************************************************************************** +// +//! Encode NDEF SmartPoster Records. +//! +//! \param sSmartPoster is the SmartPoster Record Structure to be encoded. +//! \param pui8Buffer is a pointer to the buffer to fill with the raw NFC data. +//! \param ui16BufferMaxLength is the maximum number of bytes the buffer +//! can hold. This parameter is used to prevent writing past the end of the +//! buffer. +//! \param pui32BufferLength is a pointer to the integer to hold the length of +//! the raw NFC data buffer. +//! +//! This function takes a SmartPoster record structure and encodes it into a +//! provided buffer in a raw NFC data format. The length of the data stored in +//! the buffer is stored in \e \b pui32BufferLength. +//! +//! \note It is assumed that all smart poster messages have a Text record and a +//! URI record. +//! +//! \return This function returns \b STATUS_SUCCESS (1) or \b STATUS_FAIL (0). +// +// Note: This function works by first encoding the Record, then the Header. +// The Header comes before the Record. Thus space is allocated in the +// buffer for the Header before the buffer is passed to the encoder. The +// extra space will be taken care of by the Header encoder function +// (aka NDEFMessageEncoder). +// +// +//***************************************************************************** +bool +NFCP2P_NDEFSmartPosterRecordEncoder(sNDEFSmartPosterRecord sSmartPoster, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t *pui32BufferLength) +{ + // + // RECORD_OFFSET is the max size of the header. The magic number 7 comes + // from the size of the Statusbyte[1]+PayloadLength[4]+IDLength[1]+ + // TypeLength[1]. This is done to ensure that there is space + // left in the buffer for the header while the record is encoding. + // + #define RECORD_OFFSET (NDEF_TYPE_MAXSIZE+NDEF_ID_MAXSIZE+7) + + bool bStatus = STATUS_SUCCESS; + + uint32_t ui32TotalLength = 0; + uint32_t ui32RecordLength = 0; + + uint8_t *pui8CurrHeaderPt = pui8Buffer; + uint8_t *pui8CurrRecordPt = pui8CurrHeaderPt + RECORD_OFFSET; + + // + // Validate Data + // + ASSERT(ui16BufferMaxLength != 0); + ASSERT(pui8Buffer != 0); + + // + // Encode TextMessage, Update Payload Ptr and Payload Length in Header, + // Encode TextHeader (included TextPayload) + // + bStatus = NFCP2P_NDEFTextRecordEncoder(sSmartPoster.sTextPayload, + pui8CurrRecordPt, + (ui16BufferMaxLength - + (pui8CurrRecordPt - pui8Buffer)), + &ui32RecordLength); + sSmartPoster.sTextHeader.ui32PayloadLength = ui32RecordLength; + sSmartPoster.sTextHeader.pui8PayloadPtr = pui8CurrRecordPt; + if(STATUS_FAIL == bStatus) + { + DebugPrintf(" ERR: SmartPoster TextRecord Encode FAIL.\n"); + return bStatus; + } + + bStatus = NFCP2P_NDEFMessageEncoder(sSmartPoster.sTextHeader, + pui8CurrHeaderPt, + (ui16BufferMaxLength - + (pui8CurrHeaderPt - pui8Buffer)), + &ui32RecordLength); + pui8CurrHeaderPt = pui8CurrHeaderPt + ui32RecordLength; + pui8CurrRecordPt = pui8CurrHeaderPt + RECORD_OFFSET; + if(STATUS_FAIL == bStatus) + { + DebugPrintf(" ERR: SmartPoster TextRecord Header Encode FAIL.\n"); + return bStatus; + } + ui32TotalLength += ui32RecordLength; + + // + // Encode URIMessage, Update Payload Ptr and Payload Length in Header, + // Encode URIHeader (included URIPayload) + // + bStatus = NFCP2P_NDEFURIRecordEncoder(sSmartPoster.sURIPayload, + pui8CurrRecordPt, + (ui16BufferMaxLength - + (pui8CurrRecordPt - pui8Buffer)), + &ui32RecordLength); + sSmartPoster.sURIHeader.ui32PayloadLength = ui32RecordLength; + sSmartPoster.sURIHeader.pui8PayloadPtr = pui8CurrRecordPt; + if(STATUS_FAIL == bStatus) + { + DebugPrintf(" ERR: SmartPoster URIRecord Encode FAIL.\n"); + return bStatus; + } + bStatus = NFCP2P_NDEFMessageEncoder(sSmartPoster.sURIHeader, + pui8CurrHeaderPt, + (ui16BufferMaxLength - + (pui8CurrHeaderPt - pui8Buffer)), + &ui32RecordLength); + pui8CurrHeaderPt = pui8CurrHeaderPt + ui32RecordLength; + pui8CurrRecordPt = pui8CurrHeaderPt + RECORD_OFFSET; + ui32TotalLength += ui32RecordLength; + if(STATUS_FAIL == bStatus) + { + DebugPrintf(" ERR: SmartPoster URIRecord Header Encode FAIL.\n"); + return bStatus; + } + + // + // Encode ActionMessage, Update Payload Ptr and Payload Length in Header, + // Encode ActionHeader (included ActionPayload) + // + if(sSmartPoster.bActionExists) + { + // + // The Action Record has no Encoder / Decoder because it is just 1 byte + // of data. So it is hard coded into the Smart Poster Encoder / Decoder + // + pui8CurrRecordPt[0] = sSmartPoster.sActionPayload.eAction; + sSmartPoster.sActionHeader.ui32PayloadLength = 1; + sSmartPoster.sActionHeader.pui8PayloadPtr = pui8CurrRecordPt; + bStatus = NFCP2P_NDEFMessageEncoder(sSmartPoster.sActionHeader, + pui8CurrHeaderPt, + (ui16BufferMaxLength - + (pui8CurrHeaderPt - pui8Buffer)), + &ui32RecordLength); + pui8CurrHeaderPt = pui8CurrHeaderPt + ui32RecordLength; + pui8CurrRecordPt = pui8CurrHeaderPt + RECORD_OFFSET; + ui32TotalLength += ui32RecordLength; + if(STATUS_FAIL == bStatus) + { + DebugPrintf(" ERR: SmartPoster ActionRecord Encode FAIL.\n"); + return bStatus; + } + } + + // + // Check for buffer overflow. This should be caught in the lower level + // encode functions, but just to be safe we check for it here as well. + // + if(ui32TotalLength > ui16BufferMaxLength) + { + DebugPrintf(" ERR: SmartPosterRecordEncoder : Buffer Overflow.\n"); + return STATUS_FAIL; + } + + // + // Return Buffer Length + // + *pui32BufferLength = ui32TotalLength; + + return STATUS_SUCCESS; +} + +//***************************************************************************** +// +//! Decode NDEF SmartPoster Records. +//! +//! \param sSmartPoster is a pointer to the SmartPosterRecord structure into +//! which to decode the data. +//! \param pui8Buffer is a pointer to the raw NFC data buffer to be decoded. +//! \param ui16BufferMaxLength is the maximum number of bytes the buffer +//! can hold. This parameter is used to prevent reading past the end of the +//! buffer. +//! \param ui32BufferLength is the length of the raw NFC data buffer. +//! +//! This function takes a raw NFC data buffer and decodes the data into a +//! SmartPoster record data structure. It is assumed that the raw data buffer +//! contains a SmartPoster record. +//! +//! \return This function returns \b STATUS_SUCCESS (1) or \b STATUS_FAIL (0). +//! +//! \note Currently only Title, Action and URI records are supported. +//! Other records are skipped and ignored. +// +//***************************************************************************** +bool +NFCP2P_NDEFSmartPosterRecordDecoder(sNDEFSmartPosterRecord *sSmartPoster, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t ui32BufferLength) +{ + sNDEFMessageData sCurrentHeader; //temp Header Info + uint32_t ui32RecordIndex = 0; + uint8_t *pui8CurrHeaderPt; + uint8_t x = 0; + bool bCheck = STATUS_SUCCESS; + uint64_t TypeID = 0; + + // + // Initialize + // + sSmartPoster->bActionExists = false; + + // + // Process through Payload for Smart Poster. + // Assume first header at pui8Buffer[0] + // Process and fill + // + while(ui32RecordIndex < ui32BufferLength) + { + // + // Pointer to Header + // + pui8CurrHeaderPt = pui8Buffer+ui32RecordIndex; + + // + // Decode Current Header, in this case the + // + bCheck = NFCP2P_NDEFMessageDecoder(&sCurrentHeader, + pui8CurrHeaderPt, + (ui16BufferMaxLength - + (pui8CurrHeaderPt - pui8Buffer)) + ); + if(STATUS_FAIL == bCheck) + { + DebugPrintf("ERR: SPDecoder: SP NDEFMessageDecoder Failed\n"); + return STATUS_FAIL; + } + // + // Check for buffer read overrun. This would be caused by bad data. + // This goes off when you try to read past the end of the buffer. + // + if((sCurrentHeader.ui32PayloadLength + + (sCurrentHeader.pui8PayloadPtr - pui8Buffer)) + > ui16BufferMaxLength) + { + DebugPrintf("ERR: SPDecoder: BufferRead Overrun. Bad Data."); + return STATUS_FAIL; + } + + // + // Calculate Record Type + // + for(x = 0,TypeID = 0;x < sCurrentHeader.ui8TypeLength;x++) + { + TypeID = (TypeID << 8) + sCurrentHeader.pui8Type[x]; + } + + // + // Decode Header into appropriate part of SmartPoster struct + // Call decoder function for each header type + // + switch(TypeID) + { + // + // Text Record + // + case NDEF_TYPE_TEXT: + { + bCheck = NFCP2P_NDEFMessageDecoder(&sSmartPoster->sTextHeader, + pui8CurrHeaderPt, + (ui16BufferMaxLength - + (pui8CurrHeaderPt - pui8Buffer)) + ); + if(STATUS_FAIL == bCheck) + { + DebugPrintf( + " ERR: SPDecoder: Text NDEFMessageDecoder Failed\n"); + return STATUS_FAIL; + } + bCheck = NFCP2P_NDEFTextRecordDecoder( + &sSmartPoster->sTextPayload, + sSmartPoster->sTextHeader.pui8PayloadPtr, + sSmartPoster->sTextHeader.ui32PayloadLength + ); + if(STATUS_FAIL == bCheck) + { + DebugPrintf( + " ERR: SPDecoder: NDEFTextRecordDecoder Failed\n"); + return STATUS_FAIL; + } + break; + } + + // + // URI Record + // + case NDEF_TYPE_URI: + { + bCheck = NFCP2P_NDEFMessageDecoder(&sSmartPoster->sURIHeader, + pui8CurrHeaderPt, + (ui16BufferMaxLength - + (pui8CurrHeaderPt - pui8Buffer)) + ); + if(STATUS_FAIL == bCheck) + { + DebugPrintf( + " ERR: SPDecoder: URI NDEFMessageDecoder Failed\n"); + return STATUS_FAIL; + } + bCheck = NFCP2P_NDEFURIRecordDecoder( + &sSmartPoster->sURIPayload, + sSmartPoster->sURIHeader.pui8PayloadPtr, + sSmartPoster->sURIHeader.ui32PayloadLength + ); + if(STATUS_FAIL == bCheck) + { + DebugPrintf(\ + " ERR: SPDecoder: NDEFURIMessageDecoder Failed\n"); + return STATUS_FAIL; + } + break; + } + + // + // Action Record (built in type to SmartPoster, no need for external + // functions) + // + case NDEF_TYPE_ACTION: + { + sSmartPoster->bActionExists = true; + bCheck = NFCP2P_NDEFMessageDecoder(&sSmartPoster->sActionHeader, + pui8CurrHeaderPt, + (ui16BufferMaxLength - + (pui8CurrHeaderPt - pui8Buffer)) + ); + if(STATUS_FAIL == bCheck) + { + DebugPrintf( + " ERR: SPDecoder: Action NDEFMessageDecoder Failed\n"); + return STATUS_FAIL; + } + sSmartPoster->sActionPayload.eAction = + sSmartPoster->sActionHeader.pui8PayloadPtr[0]; + break; + } + + // + // Other record type, not supported, so skip it. + // + default: + { + DebugPrintf("NDEFSmartPosterDecode: Record Not recognized: 0x%x\n" + ,TypeID); + break; + } + } + + // + // Incriment ui32RecordIndex + // (sCurrentHeader.pui8PayloadPtr-pui8CurrHeaderPt) = size of header + // when added to payload length this gives the total record size + // + ui32RecordIndex += (sCurrentHeader.pui8PayloadPtr-pui8CurrHeaderPt) + + sCurrentHeader.ui32PayloadLength; + } + + return STATUS_SUCCESS; +} + + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/nfclib/nfc_p2p.h b/nfclib/nfc_p2p.h new file mode 100644 index 0000000..d0a7136 --- /dev/null +++ b/nfclib/nfc_p2p.h @@ -0,0 +1,1536 @@ +//***************************************************************************** +// +// nfc_p2p.h - contains P2P State Machine NDEF P2P Record Type Structures +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#ifndef __NFC_P2P_H__ +#define __NFC_P2P_H__ + +//***************************************************************************** +// +//! \addtogroup nfc_p2p_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// NFC Protocol Headers +// +//***************************************************************************** +#include "nfc_f.h" +#include "nfc_dep.h" +#include "llcp.h" +#include "snep.h" + +//***************************************************************************** +// +// TRF7970 Header +// +//***************************************************************************** +#include "trf79x0.h" + +//***************************************************************************** +// +//! Enumeration for 4 possible states for NFC P2P State Machine. +// +//***************************************************************************** +typedef enum { + // + //! Polling/Listening for SENSF_REQ / SENSF_RES. + // + NFC_P2P_PROTOCOL_ACTIVATION = 0, + + // + //! Setting the NFCIDs and bit rate + // + NFC_P2P_PARAMETER_SELECTION, + + // + //! Data exchange using the LLCP layer + // + NFC_P2P_DATA_EXCHANGE_PROTOCOL, + + // + //! Technology deactivation + // + NFC_P2P_DEACTIVATION + +} tNFCP2PState; + +//***************************************************************************** +// +//! This structure defines the status of the received payload. +// +//***************************************************************************** +typedef struct{ + + // + //! SNEP RX Packet Status + // + tPacketStatus eDataReceivedStatus; + + // + //! SNEP Number of bytes received + // + uint8_t ui8DataReceivedLength; + + // + //! Pointer to data received + // + uint8_t *pui8RxDataPtr; + +}sNFCP2PRxStatus; + +//***************************************************************************** +// +// Programmers Note: NDEF message layout +// +// The fields in an NDEF header are as follows: +// ______________________________ +// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0| Notes: +// |------------------------------| +// | MB| ME| CF| SR| IL| TNF | NDEF StatusByte +// |------------------------------| +// | TYPE_LENGTH | 1 byte, hex value +// |------------------------------| +// | PAYLOAD_LENGTH | 1 or 4 bytes (determined by SR) (LSB first) +// |------------------------------| +// | ID_LENGTH | 0 or 1 bytes (determined by IL) +// |------------------------------| +// | TYPE | 2 or 5 bytes (determined by TYPE_LENGTH) +// |------------------------------| +// | ID | 0 or 1 byte (determined by IL & ID_LENGTH) +// |------------------------------| +// | PAYLOAD | X bytes (determined by PAYLOAD_LENGTH) +// |------------------------------| +// NDEF messages NDEF messages can be considered as two parts: +// The Header (everything except the last field), and the Payload. +// +// ********** +// HEADER +// ********** +// The Header encompases Everything in the above diagram except the PAYLOAD +// The Header can vary in length from 5-13 bytes. +// The PAYLOAD_LENGTH, ID_LENGTH, ID, and TYPE fields can all very in length. +// +// Field Name | Length Depends On | Length +// ------------------------------------------------ +// PAYLOAD LENGTH | SR | SR = 1 => 1 byte , SR = 0 => 4 bytes +// ID_LENGTH | IL | IL = 0 (if IL = 0 Both ID_LENGTH and +// ID fields are excluded. +// If IL = 1 then ID_LENGTH +// exists. If ID_LENGTH = 0x0 +// then the ID field is +// not included) +// TYPE | TYPE_LENGTH | 2-5 bytes (hex value of TYPE_LENGTH) +// (In special cases there can be +// a TYPE_LENGTH of 0, in which +// case there is no TYPE field.) +// +// Note: PAYLOAD_LENGTH only gives the length of the PAYLOAD in its message. +// The PAYLOAD_LENGTH does NOT give the length of the record across +// multiple messages.. +// +// *********** +// PAYLOAD +// *********** +// The Payload can have a wide range of formats depending on the TNF and TYPE +// specified. (IE a TNF of 0x01 aka WELL_KNOWN_TYPE and a TYPE of 'T' would +// indicate a plain text payload, which has its own syntax). The user can even +// implement their own PAYLOAD type, providing handlers are provided on both the +// sending and receiving devices. +// +//***************************************************************************** + +//***************************************************************************** +// +// NDEF message header definitions +// SET macros are used to set the bit (encoder) +// GET macros are used to read the bit (decoder) +// +//***************************************************************************** + +// +//! This macro is used to set the MB field in the StatusByte of the NFC message header by +//! shifting a bit into position. This define should be ORed together with other StatusByte +//! Fields. +//! +//! \param ui8x is the binary value to be shifted into place +//! +//! \b Example: Set the MB field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte | +//! NDEF_STATUSBYTE_SET_MB(0x1) +//! +//! \b Example: Clear the MB field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte & +//! NDEF_STATUSBYTE_SET_MB(0x0) +//! +// +#define NDEF_STATUSBYTE_SET_MB(ui8x) ((ui8x & 0x01) << 7) + +// +//! This macro is used to set the ME field in the StatusByte of the NFC message +//! header by shifting a bit into position. This define should be ORed together +//! with other StatusByte Fields. +//! +//! \param ui8x is the binary value to be shifted into place +//! +//! \b Example: Set the ME field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte | +//! NDEF_STATUSBYTE_SET_ME(0x1) +//! +//! \b Example: Clear the ME field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte & +//! NDEF_STATUSBYTE_SET_ME(0x0) +//! +// +#define NDEF_STATUSBYTE_SET_ME(ui8x) ((ui8x & 0x01) << 6) + +// +//! This Macro is used to set the CF field in the StatusByte of the NFC message +//! header by shifting a bit into position. This define should be ORed together +//! with other StatusByte Fields. +//! +//! \param ui8x is the binary value to be shifted into place +//! +//! \b Example: Set the CF field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte | +//! NDEF_STATUSBYTE_SET_CF(0x1) +//! +//! \b Example: Clear the CF field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte & +//! NDEF_STATUSBYTE_SET_CF(0x0) +//! +// +#define NDEF_STATUSBYTE_SET_CF(ui8x) ((ui8x & 0x01) << 5) + +// +//! This macro is used to set the SR field in the StatusByte of the NFC message +//! header by shifting a bit into position. This define should be ORed together +//! with other StatusByte Fields. +//! +//! \param ui8x is the binary value to be shifted into place +//! +//! \b Example: Set the SR field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte | +//! NDEF_STATUSBYTE_SET_SR(0x1) +//! +//! \b Example: Clear the SR field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte & +//! NDEF_STATUSBYTE_SET_SR(0x0) +//! +// +#define NDEF_STATUSBYTE_SET_SR(ui8x) ((ui8x & 0x01) << 4) + +// +//! This macro is used to set the IL field in the StatusByte of the NFC message header by +//! shifting a bit into position. This define should be ORed together with other StatusByte +//! Fields. +//! +//! \param ui8x is the binary value to be shifted into place +//! +//! \b Example: Set the IL field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte | +//! NDEF_STATUSBYTE_SET_IL(0x1) +//! +//! \b Example: Clear the IL field in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte & +//! NDEF_STATUSBYTE_SET_IL(0x0) +//! +// +#define NDEF_STATUSBYTE_SET_IL(ui8x) ((ui8x & 0x01) << 3) + +// +//! This macro is used to set the TNF field in the StatusByte of the NFC message +//! header by shifting a bit into position. This define should be ORed together +//! with other StatusByte Fields. +//! +//! \param ui8x is the 3-bit value to be shifted into place +//! +//! \b Example: Set the TNF field to Well Known Type in a StatusByte +//! +//! NsNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte | +//! NDEF_STATUSBYTE_SET_TNF(0x1) +//! +//! \b Example: Set the TNF field to Unknown Type in a StatusByte +//! +//! sNDEFMessage.sStatusByte = sNDEFMessage.sStatusByte & +//! NDEF_STATUSBYTE_SET_TNF(0x5) +//! +// +#define NDEF_STATUSBYTE_SET_TNF(ui8x) ((ui8x & 0x07) << 0) + +// +//! Macro used to get the MB field value from the StatusByte of the NFC message +//! header. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Get the MB field from the StatusByte into variable x +//! +//! x = NDEF_STATUSBYTE_GET_MB(sNDEFMessageData.sStatusByte) +//! +// +#define NDEF_STATUSBYTE_GET_MB(ui8x) ((ui8x >> 7) & 0x01) + +// +//! Macro used to get the ME field value from the StatusByte of the NFC message +//! header. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Get the ME field from the StatusByte into variable x +//! +//! x = NDEF_STATUSBYTE_GET_ME(sNDEFMessageData.sStatusByte) +//! +// +#define NDEF_STATUSBYTE_GET_ME(ui8x) ((ui8x >> 6) & 0x01) + +// +//! Macro used to get the CF field value from the StatusByte of the NFC message +//! header. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Get the CF field from the StatusByte into variable x +//! +//! x = NDEF_STATUSBYTE_GET_CF(sNDEFMessageData.sStatusByte) +//! +// +#define NDEF_STATUSBYTE_GET_CF(ui8x) ((ui8x >> 5) & 0x01) + +// +//! Macro used to get the SR field value from the StatusByte of the NFC message +//! header. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Get the SR field from the StatusByte into variable x +//! +//! x = NDEF_STATUSBYTE_GET_SR(sNDEFMessageData.sStatusByte) +//! +// +#define NDEF_STATUSBYTE_GET_SR(ui8x) ((ui8x >> 4) & 0x01) + +// +//! Macro used to get the IL field value from the StatusByte of the NFC message +//! header. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Get the IL field from the StatusByte into variable x +//! +//! x = NDEF_STATUSBYTE_GET_IL(sNDEFMessageData.sStatusByte) +//! +// +#define NDEF_STATUSBYTE_GET_IL(ui8x) ((ui8x >> 3) & 0x01) + +// +//! Macro used to get the TNF field value from the StatusByte of the NFC message +//! header. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Get the TNF field from the StatusByte into variable x +//! +//! x = NDEF_STATUSBYTE_GET_TNF(sNDEFMessageData.sStatusByte) +//! +// +#define NDEF_STATUSBYTE_GET_TNF(ui8x) ((ui8x >> 0) & 0x07) + +//***************************************************************************** +// +// Defines to check Header StatusByte field meaning. Some cases left out +// because they are irrelevant (ie only care when MB is 1 or not 1, dont care +// about 0) +// +//***************************************************************************** + +// +//! Flag used to check the MB field in the StatusByte. If MB is set then this is +//! the first Record. +//! +//! \b Example: Check for Message Begin flag +//! +//! if(NDEF_STATUSBYTE_GET_MB(ui8StatusByte) == +//! NDEF_STATUSBYTE_MB_FIRSTBYTE){} +// +#define NDEF_STATUSBYTE_MB_FIRSTBYTE 1 + +// +//! Flag used to check the ME field in the StatusByte. If ME is set, then this +//! is the last Record. +//! +//! \b Example: Check for Message End flag +//! +//! if(NDEF_STATUSBYTE_GET_ME(ui8StatusByte) == NDEF_STATUSBYTE_ME_LASTBYTE) +//! {} +// +#define NDEF_STATUSBYTE_ME_LASTBYTE 1 + +// +//! Flag used to check the CF field in the StatusByte. If CF is set, then the +//! message is a chunked message spread out across multiple transactions. +//! +//! \b Example: Check for Chunked Flag +//! +//! if(NDEF_STATUSBYTE_GET_CF(ui8StatusByte) == NDEF_STATUSBYTE_CF_CHUNK) +//! {} +// +#define NDEF_STATUSBYTE_CF_CHUNK 1 + +// +//! Flag used to check the SR field in the StatusByte. If SR is set, then +//! the message is a short record with a payload length field of 1 byte instead +//! of 4 bytes. +//! +//! \b Example: Check the Short Record flag +//! +//! if(NDEF_STATUSBYTE_GET_SR(ui8StatusByte) == +//! NDEF_STATUSBYTE_SR_1BYTEPAYLOADSIZE){} +// +#define NDEF_STATUSBYTE_SR_1BYTEPAYLOADSIZE 1 + +// +//! Flag used to check the SR field in the StatusByte. If SR is not set, then +//! the message is a normal record with a payload length field of 4 bytes +//! instead of 1 byte. +//! +//! \b Example: Check the Short Record flag +//! +//! if(NDEF_STATUSBYTE_GET_SR(ui8StatusByte) == +//! NDEF_STATUSBYTE_SR_4BYTEPAYLOADSIZE){} +// +#define NDEF_STATUSBYTE_SR_4BYTEPAYLOADSIZE 0 + +// +//! Flag used to check the IL field in the StatusByte. If IL is set, then the ID +//! and IDLength fields are present in the message. +//! +//! \b Example: Check for the presence of the ID Length and ID name field +//! +//! if(NDEF_STATUSBYTE_GET_IL(ui8StatusByte) == +//! NDEF_STATUSBYTE_IL_IDLENGTHPRESENT) {} +// +#define NDEF_STATUSBYTE_IL_IDLENGTHPRESENT 1 + +// +//! Flag used to check the IL field in the StatusByte. If IL is not set, then +//! there is no ID or IDLength fields included in the message. +//! +//! \b Example: Check for the presence of the ID Length and ID name field +//! +//! if(NDEF_STATUSBYTE_GET_IL(ui8StatusByte) == +//! NDEF_STATUSBYTE_IL_IDLENGTHABSENT) {} +// +#define NDEF_STATUSBYTE_IL_IDLENGTHABSENT 0 + +//***************************************************************************** +// +// Defines to set maximum field lengths in bytes +// +//***************************************************************************** + +// +//! Maximum size of Type field in StatusByte. This define is used to declare the +//! length of the buffer in the structure and thus can be changed to allow +//! larger Type names. +//! +//! \b Example: Copy the Type from raw buffer to structure using +//! NDEF_TYPE_MAXSIZE to prevent overflowing buffer in the +//! structure +//! +//! +//! \verbatim +//! // +//! // Assume that TypeLength is already decoded from the raw buffer and is +//! // stored in sNDEFMessageData.ui8TypeLength. Assume ui8RawBuffer is a +//! // pointer to the beginning of the Type field in the raw data stream. +//! // +//! int x = 0; +//! for(x = 0; (x +// +#define NDEF_TYPE_MAXSIZE 10 // can be changed + +// +//! Maximum size of the ID field in StatusByte, which can be modified to support +//! larger ID names. +//! +//! \b Example: Copy the ID from the raw buffer to the structure using +//! NDEF_ID_MAXSIZE to prevent overflowing buffer in the structure +//! +//! +//! \verbatim +//! // +//! // Assume IDLength already decoded from the raw buffer is stored in +//! // sNDEFMessageData.ui8IDLength. Assume ui8RawBuffer is a pointer to +//! // the beginning of the ID field in the raw data stream. +//! // +//! int x = 0; +//! for(x = 0; (x +// +#define NDEF_ID_MAXSIZE 10 // can be changed + +//***************************************************************************** +// +// Defines to check NDEF ID type +// +//***************************************************************************** + +// +//! NFC Message TypeID hex representation for TEXT records. +//! 0x54 == 'T' in UTF-8 +//! +//! \b Example: Check if tag type is TEXT +//! +//! +//! \verbatim +//! // +//! // Assume the Tag Type has already decoded into sNDEFMessageData.pui8Type +//! // +//! if(sNDEFMessageData.pui8Type == NDEF_TYPE_TEXT) +//! { +//! // The Tag is a TEXT record, handle it appropriately +//! } +//! \endverbatim +//! +// +#define NDEF_TYPE_TEXT 0x54 // 'T' in UTF-8 + +// +//! NFC Message TypeID hex representation for URI records. +//! 0x55 == 'U' in UTF-8 +//! +//! \b Example: Check if tag type is URI +//! +//! +//! \verbatim +//! // +//! // Assume the Tag Type has already decoded into sNDEFMessageData.pui8Type +//! // +//! if(sNDEFMessageData.pui8Type == NDEF_TYPE_URI) +//! { +//! // The Tag is a URI record, handle it appropriately +//! } +//! \endverbatim +//! +// +#define NDEF_TYPE_URI 0x55 // 'U' in UTF-8 + +// +//! NFC Message TypeID hex representation for SMARTPOSTER records. +//! 0x5370 == "Sp" in UTF-8 +//! +//! \b Example: Check if tag type is SMARTPOSTER +//! +//! +//! \verbatim +//! // +//! // Assume the Tag Type has already decoded into sNDEFMessageData.pui8Type +//! // +//! if(sNDEFMessageData.pui8Type == NDEF_TYPE_SMARTPOSTER) +//! { +//! // The Tag is a SMARTPOSTER record, handle it appropriately +//! } +//! \endverbatim +//! +// +#define NDEF_TYPE_SMARTPOSTER 0x5370 //"Sp" in UTF-8 + +// +//! NFC Message TypeID hex representation for SIGNATURE records. +//! 0x536967 == "Sig" in UTF-8 +//! +//! \b Example: Check if tag type is SIGNATURE +//! +//! +//! \verbatim +//! // +//! // Assume the Tag Type has already decoded into sNDEFMessageData.pui8Type +//! // +//! if(sNDEFMessageData.pui8Type == NDEF_TYPE_SIGNATURE) +//! { +//! // The Tag is a SIGNATURE record, handle it appropriately +//! } +//! \endverbatim +//! +// +#define NDEF_TYPE_SIGNATURE 0x536967 //"Sig" in UTF-8 + +// +//! NFC Message TypeID hex representation for SIZE records. +//! 0x73 == 's' in UTF-8 +//! +//! \b Example: Check if tag type is SIZE +//! +//! +//! \verbatim +//! // +//! // Assume the Tag Type has already decoded into sNDEFMessageData.pui8Type +//! // +//! if(sNDEFMessageData.pui8Type == NDEF_TYPE_SIZE) +//! { +//! // The Tag is a SIZE record. Handle it appropriately +//! } +//! \endverbatim +//! +// +#define NDEF_TYPE_SIZE 0x73 // 's' in UTF-8 + +// +//! NFC Message TypeID hex representation for ACTION records. +//! 0x616374 == "act" in UTF-8 +//! +//! \b Example: Check if tag type is ACTION +//! +//! +//! \verbatim +//! // +//! // Assume the Tag Type has already decoded into sNDEFMessageData.pui8Type +//! // +//! if(sNDEFMessageData.pui8Type == NDEF_TYPE_ACTION) +//! { +//! // The Tag is a ACTION record, handle it appropriately +//! } +//! \endverbatim +//! +// +#define NDEF_TYPE_ACTION 0x616374 //"act" in UTF-8 + + + +//***************************************************************************** +// +//! Enumeration for Type Name Format (TNF) field in NDEF header StatusByte. +//! TNF values are 3 bits. Most records are of the Well Known Type format +//! (0x01). +// +// TNF = Type Name Format: 3bit field, indicates structure of TYPE field +// Acceptable Values are: +// 0x00 Empty +// 0x01 NFC Forum well-known type [NFC RTD] +// NDEF Record Type Description Full URI Reference +// 'Sp' Smart Poster urn:nfc:wkt:Sp +// 'T' Text urn:nfc:wkt:T +// 'U' URI urn:nfc:wkt:U +// 'Hr' Handover Request urn:nfc:wkt:Hr +// 'Hs' Handover Select urn:nfc:wkt:Hs +// 'Hc' Handover Carrier urn:nfc:wkt:Hc +// 'Sig' Signature urn:nfc:wkt:Sig +// 0x02 Media-type as defined in RFC 2046 [RFC 2046] +// 0x03 Absolute URI as defined in RFC 3986 [RFC 3986] +// 0x04 NFC Forum external type [NFC RTD] +// 0x05 Unknown +// 0x06 Unchanged (used with single message across multiple chunks) +// 0x07 Reserved +// +//***************************************************************************** +typedef enum +{ + // + //! Empty Format + // + TNF_EMPTY = 0x00, + + // + //! NFC Forum Well Known Type [NFC RTD] + // + TNF_WELLKNOWNTYPE = 0x01, + + // + //! Media-type as defined in RFC 2046 [RFC 2046] + // + TNF_MEDIA_TYPE = 0x02, + + // + //! Absolute URI as defined in RFC 3986 [RFC 3986] + // + TNF_ABSOLUTE_URI = 0x03, + + // + //! NFC Forum external type [NFC RTD] + // + TNF_EXTERNAL_TYPE = 0x04, + + // + //! Unknown + // + TNF_UNKNOWN = 0x05, + + // + //! Unchanged (used with single message across multiple chunks) + // + TNF_UNCHANGED = 0x06, + + // + //! Reserved + // + TNF_RESERVED = 0x07 +} tTNF; + +//***************************************************************************** +// +//! NFC NDEF message header StatusByte structure. Included in this structure +//! are fields for Message Begin (MB), Message End (ME), Chunk Flag (CF), Short +//! Record (SR), IDLength (IL) and Type Name Format (TNF). The purpose of this +//! structure is to make the fields readily available for message processing. +// ______________________________ +// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0| +// |------------------------------| +// | MB| ME| CF| SR| IL| TNF | +// |------------------------------| +// +// MB = Message Begin : marks start of NDEF message +// ME = Message End : marks end of NDEF message +// CF = Chunk Flag : indicate first or middle record chunk of chunked payload +// SR = Short Record : if == 1 PAYLOAD_LENGTH is 1 byte, else it is 4 bytes +// IL = ID Length : indicate presence of ID_LENGTH byte +// (1 =included, 0 = not) +// TNF = Type Name Format: 3bit field, indicates structure of TYPE field +// +// Note: for a record that only takes up 1 NDEF message both the MB and ME +// fields would be set on the same message. It is likely that the SR +// field would be set as well to save space, but not required. +// +//***************************************************************************** +typedef struct +{ + // + //! Message Begin flag + // + bool MB; + + // + //! Message End flag + // + bool ME; + + // + //! Chunk Flag + // + bool CF; + + // + //! Short Record flag + // + bool SR; + + // + //! ID Length flag + // + bool IL; + + // + //! Type Name Field. An enumeration specifying the general tag type. + // + tTNF TNF; + +} sNDEFStatusByte; + +//***************************************************************************** +// +//! Structure to hold NDEF Message header data. The message header encapsulates +//! and contains metadata about the payload message. This structure is used +//! with the NFCP2P_NDEFMessageEncoder and NFCP2P_NDEFMessageDecoder functions. +//! For detailed information on the NDEF message header data, please see the NFC +//! specification. +// +// NDEF Record Layout +// _________________ +// | StatusByte | 1 byte +// |-----------------| +// | TYPE_LENGTH | 1 byte, hex value +// |---------------- | +// | PAYLOAD_LENGTH | 1 or 4 bytes +// |---------------- | +// | ID_LENGTH | 0 or 1 bytes +// |---------------- | +// | TYPE | 2 or 5 bytes +// |---------------- | +// | ID | 0 or 1 byte +// |---------------- | +// | | +// | PAYLOAD | Multiple Bytes +// | | +// |-----------------| +// +// Note: The Type and ID field lengths are arbitrarily set and can be expanded +// if desired. The PayloadLength field is set to the standard maximum. +// The Payload is set as a pointer into the received buffer. +// +//***************************************************************************** +typedef struct +{ + + // + //! Metadata about the message + // + sNDEFStatusByte sStatusByte; + + // + //! Length of the Type field in bytes + // + uint8_t ui8TypeLength; + + // + //! Length of the payload in bytes + // + uint32_t ui32PayloadLength; + + // + //! Length of ID field in bytes. Optional field + // + uint8_t ui8IDLength; + + // + //! Contains message type + // + uint8_t pui8Type[NDEF_TYPE_MAXSIZE]; + + // + //! Contains message ID. Optional field + // + uint8_t pui8ID[NDEF_ID_MAXSIZE]; + + // + //! Pointer to the encoded payload buffer + // + uint8_t *pui8PayloadPtr; + +} sNDEFMessageData; + +//***************************************************************************** +// +// General defines used to interpret data / set limits on buffer sizes +// +//***************************************************************************** +// +//! Check text record bit in the StatusByte to determine if text record is UTF8 +//! format. +//! +//! \b Example: Check Text Record for UTF8 format +//! +//! if(sNDEFTextRecord.bUTFcode == NDEF_TEXTRECORD_STATUSBYTE_UTF8){} +// +#define NDEF_TEXTRECORD_STATUSBYTE_UTF8 0 // DO NOT CHANGE + +// +//! Check text record bit in the StatusByte to determine if text record is UTF16 +//! format. +//! +//! \b Example: Check Text Record for UTF16 format +//! +//! if(sNDEFTextRecord.bUTFcode == NDEF_TEXTRECORD_STATUSBYTE_UTF16){} +// +#define NDEF_TEXTRECORD_STATUSBYTE_UTF16 1 // DO NOT CHANGE + +// +//! Define the size of the Text Record Language Code Buffer. This can be changed +//! by the user to fit larger language codes that may develop in the future. +//! Current language codes are 2 or 5 bits, but users can use larger sizes +//! if they are adopted in the future. +// +#define NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE 5 // can be changed + +//***************************************************************************** +// +// Set Values into Raw StatusByte by | together +// +//***************************************************************************** + +// +//! Set UTF bit field in TextRecord StatusByte field. This define should be ORed +//! together with other StatusByte fields and set into StatusByte. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Set UTF bit field to UTF8 +//! +//! ui8StatusByte = (NDEF_TEXTRECORD_STATUSBYTE_SET_UTF( +//! NDEF_TEXTRECORD_STATUSBYTE_UTF8) | +//! NDEF_TEXTRECORD_STATUSBYTE_SET_LENGTHLANGCODE(...)) +//! +//! +// +#define NDEF_TEXTRECORD_STATUSBYTE_SET_UTF(ui8x) ((ui8x & 0x01) << 7) + +// +//! Set the RFU bit field in the TextRecord StatusByte field. Should be ORed +//! together with other StatusByte fields and set into StatusByte. The RFU field +//! is reserved for future use by the NFC specification and should not be used +//! by normal applications. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! ui8StatusByte = (NDEF_TEXTRECORD_STATUSBYTE_SET_RFU(0)| +//! (NDEF_TEXTRECORD_STATUSBYTE_SET_LENGTHLANGCODE(...))) +//! +//! +// +#define NDEF_TEXTRECORD_STATUSBYTE_SET_RFU(ui8x) ((ui8x & 0x01) << 6) + +// +//! Set the Language Code Length field in the TextRecord StatusByte field. +//! This define should be ORed together with other StatusByte fields and set +//! into StatusByte. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! ui8StatusByte = (NDEF_TEXTRECORD_STATUSBYTE_SET_LENGTHLANGCODE(5) | +//! NDEF_TEXTRECORD_STATUSBYTE_SET_LENGTHLANGCODE(...)) +//! +//! +// +#define NDEF_TEXTRECORD_STATUSBYTE_SET_LENGTHLANGCODE(ui8x) ((ui8x & 0x3F) << 0) + +//***************************************************************************** +// +// Get values from Raw StatusByte +// +//***************************************************************************** + +// +//! This macro extracts the UTF bit value from the raw StatusByte. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Fill the UTF boolean value in the data structure from the raw +//! buffer byte +//! +//! sNDEFTextRecord.bUTFcode = NDEF_TEXTRECORD_STATUSBYTE_GET_UTF( +//! ui8StatusByte) +//! +//! +// +#define NDEF_TEXTRECORD_STATUSBYTE_GET_UTF(ui8x) ((ui8x >> 7) & 0x01) + +// +//! This macro extracts the RFU bit value from raw StatusByte. According to the +//! NFC specification, this value must be zero. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Fill the RFU boolean value in the data structure from the raw +//! buffer byte +//! +//! sNDEFTextRecord.bRFU = NDEF_TEXTRECORD_STATUSBYTE_GET_RFU( +//! ui8StatusByte) +//! +//! +// +#define NDEF_TEXTRECORD_STATUSBYTE_GET_RFU(ui8x) ((ui8x >> 6) & 0x01) + +// +//! This macro extracts the Language Code Length field from the raw StatusByte. +//! +//! \param ui8x is the 8-bit StatusByte +//! +//! \b Example: Fill the Language Code Length field in the data structure from +//! the raw buffer byte +//! +//! +//! sNDEFTextRecord.ui5LengthLangCode = +//! NDEF_TEXTRECORD_STATUSBYTE_GET_LENGTHLANGCODE(ui8StatusByte) +//! +//! +// +#define NDEF_TEXTRECORD_STATUSBYTE_GET_LENGTHLANGCODE(ui8x) ((ui8x >> 0) & 0x3F) + +//***************************************************************************** +// +//! This structure defines the text record status byte. +//! bUTFcode determines if the Text Record is encoded with UTF8 (0) or +//! UTF16 (1). bRFU is reserved for future use by the NFC specification. +//! ui5LengthLangCode holds the length of the language code. Currently +//! language code lengths are either 2 or 5 bytes. +// ______________________________ +// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0| +// |------------------------------| +// |UTF|RFU| Length of Lang Code | = StatusByte +// |------------------------------| +// +// UTF = UTF8 or UTF16 text string formatting (0 = UTF8, 1 = UTF16) +// RFU = 0, no exceptions, its reserved for future use +// LenLangCode = 6 bytes to determine the Length of Language Code (next field) +// +//***************************************************************************** +typedef struct +{ + // + //! Flag for UTF Code. 0 = UTF8, 1 = UTF16 + // + bool bUTFcode; + + // + //! Reserved for future use by NFC specification + // + bool bRFU; + + // + //! Length of Text Record language code + // + uint8_t ui5LengthLangCode; + +} sNDEFTextRecordStatusByte; + +//***************************************************************************** +// +//! This structure defines the text record. sStatusByte contains the length of +//! the language code and the formatting for the Text (UTF8/UTF16). +//! pui8LanguageCode is a buffer that contains the language code; the buffer +//! size can be changed at compile time by modifying the +//! NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE define. pui8Text is a pointer to the +//! text payload of the Text Record. These three fields are defined in the +//! NFC specification. In addition, ui32TextLength has been added for +//! convenience to keep track of the Text buffer length. For example, a +//! text record with the Text "hello world" would have a StatusByte of 0x02 +//! (UTF = 0 (UTF8), LenLangCode = 0x2), a Language Code of "en" +//! (for English, note that it is 2 bytes long just as the ui5LengthLangCode +//! field of the Text Record StatusByte denoted), pui8Text points to +//! a buffer holding "hello world", and ui32TextLength has a value of 11, +//! which is the number of chars in "hello world". +// +// NDEF message Text Record Payload Layout +// _________________ +// | StatusByte | = 1 byte +// |-----------------| +// | Language Code | = 2-5 bytes +// |-----------------| +// | | +// | Text | = Multiple Bytes +// | | +// |-----------------| +// +// Note: the contents of a text record are freeform plain text in either +// UTF8 or UTF16 format. +// +// Note: the TextRecordLength is used in lieu of a terminiating sentinel on +// the puiText buffer. +// +//***************************************************************************** +typedef struct +{ + + // + //! Structure to hold StatusByte information + // + sNDEFTextRecordStatusByte sStatusByte; + + // + //! Buffer that holds the Language Code + // + uint8_t pui8LanguageCode[NDEF_TEXTRECORD_LANGUAGECODE_MAXSIZE]; + + // + //! Pointer to the Text Buffer + // + uint8_t *pui8Text; + + // + //! Length of text in Text Buffer + // + uint32_t ui32TextLength; + +} sNDEFTextRecord; + +//***************************************************************************** +// +//! Define used to mark end of well-defined URI Record ID Codes. Any code +//! greater than this value is not defined by the NFC specification. +//! +//! \b Example: Check if ID Code of Tag is known defined value +//! +//! +//! \verbatim +//! if(sNDEFURIRecord.eIDCode < NDEF_URIRECORD_IDCODE_RFU) +//! { +//! //process tag +//! } +//! \endverbatim +//! +// +//***************************************************************************** +#define NDEF_URIRECORD_IDCODE_RFU 0x24 + +//***************************************************************************** +// +//! Enumeration of all possible URI Record ID Codes defined by the NFC +//! specification. +//! For the complete list, please see the enumeration definition in nfc_p2p.h. +//! Defined values range from 0x00 (no prepending) to 0x23 ('urn:nfc:'). +//! Values 0x24 and above are reserved for future use. +// +// Acceptable Prepending values are: +// 0x00 N/A. No prepending is done +// 0x01 http://www. +// 0x02 https://www. +// 0x03 http:// +// 0x04 https:// +// 0x05 tel: +// 0x06 mailto: +// 0x07 ftp://anonymous:anonymous@ +// 0x08 ftp://ftp. +// 0x09 ftps:// +// 0x0A sftp:// +// 0x0B smb:// +// 0x0C nfs:// +// 0x0D ftp:// +// 0x0E dav:// +// 0x0F news: +// 0x10 telnet:// +// 0x11 imap: +// 0x12 rtsp:// +// 0x13 urn: +// 0x14 pop: +// 0x15 sip: +// 0x16 sips: +// 0x17 tftp: +// 0x18 btspp:// +// 0x19 btl2cap:// +// 0x1A btgoep:// +// 0x1B tcpobex:// +// 0x1C irdaobex:// +// 0x1D file:// +// 0x1E urn:epc:id: +// 0x1F urn:epc:tag: +// 0x20 urn:epc:pat: +// 0x21 urn:epc:raw: +// 0x22 urn:epc: +// 0x23 urn:nfc: +// +// 0x24-0xFF RFU Reserved for Future Use, Not Valid Inputs +// +//***************************************************************************** +typedef enum +{ + + // + //! Nothing is prepended to puiUTF8String + // + unabridged = 0x00, + + // + //! 'http://www.' is prepended to puiUTF8String + // + http_www = 0x01, + + // + //! 'https://www.' is prepended to puiUTF8String + // + https_www = 0x02, + + // + //! 'http://' is prepended to puiUTF8String + // + http = 0x03, + + // + //! 'https://' is prepended to puiUTF8String + // + https = 0x04, + + // + //! 'tel:' is prepended to puiUTF8String + // + tel = 0x05, + + // + //! 'mailto:' is prepended to puiUTF8String + // + mailto = 0x06, + + // + //! 'ftp://anonymous:anonymous@' is prepended to puiUTF8String + // + ftp_anonymous = 0x07, + + // + //! 'ftp://ftp.' is prepended to puiUTF8String + // + ftp_ftp = 0x08, + + // + //! 'ftps://' is prepended to puiUTF8String + // + ftps = 0x09, + + // + //! 'sftp://' is prepended to puiUTF8String + // + sftp = 0x0A, + + // + //! 'smb://' is prepended to puiUTF8String + // + smb = 0x0B, + + // + //! 'nfs://' is prepended to puiUTF8String + // + nfs = 0x0C, + + // + //! 'ftp://' is prepended to puiUTF8String + // + ftp = 0x0D, + + // + //! 'dav://' is prepended to puiUTF8String + // + dav = 0x0E, + + // + //! 'news:' is prepended to puiUTF8String + // + news = 0x0F, + + // + //! 'telnet://' is prepended to puiUTF8String + // + telnet = 0x10, + + // + //! 'imap:' is prepended to puiUTF8String + // + imap = 0x11, + + // + //! 'rtsp://' is prepended to puiUTF8String + // + rtsp = 0x12, + + // + //! 'urn:' is prepended to puiUTF8String + // + urn = 0x13, + + // + //! 'pop:' is prepended to puiUTF8String + // + pop = 0x14, + + // + //! 'sip:' is prepended to puiUTF8String + // + sip = 0x15, + + // + //! 'sips:' is prepended to puiUTF8String + // + sips = 0x16, + + // + //! 'tftp:' is prepended to puiUTF8String + // + tftp = 0x17, + + // + //! 'btspp://' is prepended to puiUTF8String + // + btspp = 0x18, + + // + //! 'btl2cap://' is prepended to puiUTF8String + // + btl2cap = 0x19, + + // + //! 'btgoep://' is prepended to puiUTF8String + // + btgoep = 0x1A, + + // + //! 'tcpobex://' is prepended to puiUTF8String + // + tcpobex = 0x1B, + + // + //! 'irdaobex://' is prepended to puiUTF8String + // + irdaobex = 0x1C, + + // + //! 'file://' is prepended to puiUTF8String + // + file = 0x1D, + + // + //! 'urn:epc:id:' is prepended to puiUTF8String + // + urn_epc_id = 0x1E, + + // + //! 'urn:epc:tag:' is prepended to puiUTF8String + // + urn_epc_tag = 0x1F, + + // + //! 'urn:epc:pat:' is prepended to puiUTF8String + // + urn_epc_pat = 0x20, + + // + //! 'urn:epc:raw:' is prepended to puiUTF8String + // + urn_epc_raw = 0x21, + + // + //! 'urn:epc:' is prepended to puiUTF8String + // + urn_epc = 0x22, + + // + //! 'urn:nfc:' is prepended to puiUTF8String + // + urn_nfc = 0x23, + + // + //! Values equal to and above this are reserved for future use (RFU) + // + RFU = 0x24 + +} eNDEF_URIRecord_IDCode; + +//***************************************************************************** +// +//! This structure defines the URI record type. The URI Record Type has two +//! fields; the ID code and the UTF8 URI string. The IDCode is used to +//! determine the URI type. For example, IDcode of 0x06 is 'mailto:' +//! and usually triggers an email event. IDcode 0x01 is 'http://www.' and +//! usually triggers a webpage to open. The IDcode values are prepended to +//! the UTF8 string. ui32URILength is used to determine the length of the +//! puiUTF8String buffer. For example, to direct a user to 'http://www.ti.com' +//! the IDcode is 0x01, the UTF8 string is 'ti.com', and the ui32URILength is +//! 0x6. +// +// NDEF message URI Record Payload Layout +// _________________ +// | ID Code | 1 byte +// |-----------------| +// | | +// | UTF8 String | Multiple Bytes +// | | +// |-----------------| +// +// The URI string is multiple bytes of UTF8 format text with a possible +// prepended value depending on the ID Code +// +//***************************************************************************** +typedef struct +{ + // + //! Enumeration of all possible ID codes + // + eNDEF_URIRecord_IDCode eIDCode; + + // + //! Buffer that holds the URI character string + // + uint8_t *puiUTF8String; + + // + //! Length of URI Character String + // + uint32_t ui32URILength; + +} sNDEFURIRecord; + +//***************************************************************************** +// +//! Enumeration of the three actions that can be associated with an Action +//! Record. +// +//***************************************************************************** +typedef enum +{ + + // + //! Do Action on Record + // + DO_ACTION = 0x00, + + // + //! Save Record for Later + // + SAVE_FOR_LATER = 0x01, + + // + //! Open Record for Editing + // + OPEN_FOR_EDITING = 0x02 + +} tAction; + +//***************************************************************************** +// +//! This structure defines an Action Record +// +//***************************************************************************** +typedef struct +{ + + // + //! Action Record type enumeration + // + tAction eAction; + +} sNDEFActionRecord; + +//***************************************************************************** +// +//! This structure defines the SmartPoster record type. +//! The SmartPoster Record is essentially +//! a URI Record with other records included for metadata. Thus +//! the SmartPoster must include at least a URI Record and may also include a +//! Text Record for a Title record, an Action record to do actions on the URI, +//! an Icon Record with a small icon, a Size record that holds the size of the +//! externally referenced entity, and a Type record that denotes the type of the +//! externally referenced entity. It should be noted that while the SmartPoster +//! specification can include all these records, this library only provides +//! support for Title, URI and Action records. All other records are ignored +//! by the default handler. +// +// NDEF message SmartPoster Record Payload consists of multiple fully wrapped +// NDEF records. The basic layout is a URI record with subsequent records as +// metadata on size, type, icon, title, and action associated with record. +// +// The possible record types are : +// Title Record : multiple possible in different languages (Text Record) +// URI Record : 1 and only 1, core of Smart Poster record +// Action Record : how to treat the URI (Do, Save for later, Open for edit) +// Icon Record : MIME type image record [optional] +// Size Record : size of external referenced entity (web link) [optional] +// Type Record : MIME type of external referenced entity [optional +// +// +// Note: Currently only Title,URI, and Action records are supported. +// Image, Type and size records are not implemented. +// +//***************************************************************************** +typedef struct +{ + + // + //! message header for Text Record + // + sNDEFMessageData sTextHeader; + + // + //! Text Record payload structure + // + sNDEFTextRecord sTextPayload; + + // + //! message header for URI Record + // + sNDEFMessageData sURIHeader; + + // + //! URI Record payload strucutre + // + sNDEFURIRecord sURIPayload; + + // + //! Flag to signal if Action Record is part of Smart Poster + // + bool bActionExists; + + // + //! message header for Action Record + // + sNDEFMessageData sActionHeader; + + // + //! Action Record payload strucutre + // + sNDEFActionRecord sActionPayload; + +} sNDEFSmartPosterRecord; + +//***************************************************************************** +// +// Function Prototypes +// +//***************************************************************************** +void NFCP2P_init(tTRF79x0TRFMode eMode,tTRF79x0Frequency eFrequency); +tNFCP2PState NFCP2P_proccessStateMachine(void); +tStatus NFCP2P_sendPacket(uint8_t *pui8DataPtr, uint32_t ui32DataLength); +sNFCP2PRxStatus NFCP2P_getReceiveState(void); + +bool NFCP2P_NDEFMessageEncoder(sNDEFMessageData sNDEFDataToSend, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t *pui32BufferLength); +bool NFCP2P_NDEFMessageDecoder(sNDEFMessageData *psNDEFDataDecoded, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength); +bool NFCP2P_NDEFTextRecordEncoder(sNDEFTextRecord sTextRecord, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t *ui32BufferLength); +bool NFCP2P_NDEFTextRecordDecoder(sNDEFTextRecord *sTextRecord, + uint8_t *pui8Buffer, + uint32_t ui32BufferLength); +bool NFCP2P_NDEFURIRecordEncoder(sNDEFURIRecord sURIRecord, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t *ui32BufferLength); +bool NFCP2P_NDEFURIRecordDecoder(sNDEFURIRecord *sURIRecord, + uint8_t *pui8Buffer, + uint32_t ui32BufferLength); +bool NFCP2P_NDEFSmartPosterRecordEncoder(sNDEFSmartPosterRecord sSmartPoster, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t *ui32BufferLength); +bool NFCP2P_NDEFSmartPosterRecordDecoder(sNDEFSmartPosterRecord *sSmartPoster, + uint8_t *pui8Buffer, + uint16_t ui16BufferMaxLength, + uint32_t ui32BufferLength); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +#endif //__NFC_P2P_H__ diff --git a/nfclib/snep.c b/nfclib/snep.c new file mode 100644 index 0000000..dc0bd95 --- /dev/null +++ b/nfclib/snep.c @@ -0,0 +1,760 @@ +//***************************************************************************** +// +// snep.c - implementation of Simple NDEF Exchange Protocol, uses LLCP +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#include +#include +#include +#include "nfclib/snep.h" +#include "nfclib/debug.h" + +//***************************************************************************** +// +//! \addtogroup nfc_snep_api NFC SNEP API Functions +//! @{ +//! Simple NDEF Exchange Protocol is an application protocol used by the LLCP +//! layer to send / receive NDEFs between two NFC Forum Devices operating +//! in Peer-to-Peer Mode (1 Target and 1 Initiator). For more information +//! on SNEP, please read the NFC Simple NDEF Exchange Protocol Specification +//! Version 1.0. +// +//***************************************************************************** + +//***************************************************************************** +// +// Stores the length of the Tx/Rx packet. +// +//**************************************************************************** +uint32_t g_ui32SNEPPacketLength; + +//***************************************************************************** +// +// g_pui8SNEPTxPacketPtr points to the first location of the data to be +// transferred +// +//***************************************************************************** +uint8_t * g_pui8SNEPTxPacketPtr; +uint8_t * g_pui8SNEPRxPacketPtr; + +//***************************************************************************** +// +// Stores the remaining rx byte count +// +//***************************************************************************** +uint32_t g_ui32SNEPRemainingRxPayloadBytes = 0; + +//***************************************************************************** +// +// Stores the bytes received in the current I-PDU transaction +// +//***************************************************************************** +uint8_t g_ui8SNEPReceivedBytes = 0; + +//***************************************************************************** +// +// Stores the status of the incoming packet +// +//***************************************************************************** +tPacketStatus g_eRxPacketStatus = RECEIVED_NO_FRAGMENT; + +//***************************************************************************** +// +// Stores the status of the SNEP communication +// +//***************************************************************************** +tSNEPConnectionStatus g_eSNEPConnectionStatus = SNEP_CONNECTION_IDLE; +//***************************************************************************** +// +// Stores the maximum size of each SNEP packet. +// +//***************************************************************************** +uint8_t g_ui8MaxPayload = SNEP_MAX_BUFFER; + +//***************************************************************************** +// +// Stores the index of the current transaction +// +//***************************************************************************** +uint32_t g_ui32TxIndex = 0; + +//***************************************************************************** +// +//! Initialize the Simple NDEF Exchange Protocol driver. +//! +//! This function must be called prior to any other function offered by the +//! SNEP driver. This function initializes the SNEP status, Tx/Rx packet length +//! and maximum payload size. This function must be called by the LLCP_init(). +//! +//! \return None. +// +//***************************************************************************** +void SNEP_init(void) +{ + g_eSNEPConnectionStatus = SNEP_CONNECTION_IDLE; + g_eRxPacketStatus = RECEIVED_NO_FRAGMENT; + g_ui32SNEPRemainingRxPayloadBytes = 0; + g_ui8SNEPReceivedBytes = 0; + g_ui8MaxPayload = SNEP_MAX_BUFFER; + g_ui32TxIndex = 0; +} + +//***************************************************************************** +// +//! Set the Maximum size of each fragment. +//! +//! \param ui8MaxPayload is the maximum size of each fragment. +//! +//! This function must be called inside LLCP_processTLV() to define the maxium +//! size of each fragment based on the Maximum Information Unit (MIU) supported +//! by the target/initiator. +//! +//! \return None. +// +//***************************************************************************** +void SNEP_setMaxPayload(uint8_t ui8MaxPayload) +{ + if(ui8MaxPayload <= SNEP_MAX_BUFFER) + { + g_ui8MaxPayload = ui8MaxPayload; + if(g_ui8MaxPayload == 0x80) + { + ui8MaxPayload = 0; + } + } +} + +//***************************************************************************** +// +//! Set the global SNEP Packet Pointer and Length +//! +//! \param pui8PacketPtr is the pointer to the first payload to be transmitted. +//! \param ui32PacketLength is the length of the total packet. +//! +//! This function must be called by the main application to initialize the +//! packet to be sent to the SNEP server. +//! +//! \return This function returns \b STATUS_SUCCESS (1) if the packet was +//! queued, else it returns \b STATUS_FAIL (0). +// +//***************************************************************************** +tStatus SNEP_setupPacket(uint8_t * pui8PacketPtr, uint32_t ui32PacketLength) +{ + tStatus ePacketSetupStatus; + + if(g_eSNEPConnectionStatus == SNEP_CONNECTION_IDLE ) + { + g_pui8SNEPTxPacketPtr = pui8PacketPtr; + // Reset TX Index + g_ui32TxIndex = 0; + g_ui32SNEPPacketLength = ui32PacketLength; + g_eSNEPConnectionStatus = SNEP_CONNECTION_IDLE; + + ePacketSetupStatus = STATUS_SUCCESS; + } + else + ePacketSetupStatus = STATUS_FAIL; + + return ePacketSetupStatus; +} + + +//***************************************************************************** +// +//! Sends request to the server. +//! +//! \param pui8DataPtr is the start pointer where the request is written. +//! \param eRequestCmd is the request command to be sent. +//! +//! The \e eRequestCmd parameter can be any of the following: +//! +//! - \b SNEP_REQUEST_CONTINUE - Send remaining fragments +//! - \b SNEP_REQUEST_GET - Return an NDEF message +//! - \b SNEP_REQUEST_PUT - Accept an NDEF message +//! - \b SNEP_REQUEST_REJECT - Do not send remaining fragments +//! +//! This function sends an SNEP request from the SNEP client to an SNEP server. +//! It must be called from the LLCP_sendI() function. +//! +//! \return \b ui8offset, which is the length of the request written starting at +//! \b pui8DataPtr. +// +//***************************************************************************** +uint8_t SNEP_sendRequest(uint8_t * pui8DataPtr, tSNEPCommands eRequestCmd) +{ + uint8_t ui8PacketLength; + uint8_t ui8offset = 0; + static uint8_t * pui8SNEPPacketPtr; + volatile uint8_t ui8counter; + + switch(eRequestCmd) + { + case SNEP_REQUEST_CONTINUE: + { + if(g_eSNEPConnectionStatus == SNEP_CONNECTION_IDLE) + break; + } + case SNEP_REQUEST_GET: + { + break; + } + case SNEP_REQUEST_PUT: + { + if(g_eSNEPConnectionStatus == SNEP_CONNECTION_IDLE) + { + // + // Set sneP_packet_ptr to first address + // + pui8SNEPPacketPtr = g_pui8SNEPTxPacketPtr; + + // + // SNEP Protocol Version + // + pui8DataPtr[ui8offset++] = SNEP_VERSION; + + // + // Request Field + // + pui8DataPtr[ui8offset++] = (uint8_t) SNEP_REQUEST_PUT; + + // + // Length (4 bytes) + // + pui8DataPtr[ui8offset++] = + (uint8_t) ((g_ui32SNEPPacketLength & 0xFF000000) >> 24); + pui8DataPtr[ui8offset++] = + (uint8_t) ((g_ui32SNEPPacketLength & 0x00FF0000) >> 16); + pui8DataPtr[ui8offset++] = + (uint8_t) ((g_ui32SNEPPacketLength & 0x0000FF00) >> 8); + pui8DataPtr[ui8offset++] = + (uint8_t) (g_ui32SNEPPacketLength & 0x000000FF); + + // + // The PUT Request has 6 bytes of overhead (Version (1) Request + // Field (1) Length (4)). + // + if( g_ui32SNEPPacketLength > (g_ui8MaxPayload - 6)) + { + // + // Remaining bytes = Total Length - (SNEP_MAX_BUFFER - 13) + // + g_ui32SNEPPacketLength = g_ui32SNEPPacketLength - + (g_ui8MaxPayload - 6); + ui8PacketLength = (g_ui8MaxPayload - 6); + + // + // Change connection status to waiting for continue + // + g_eSNEPConnectionStatus = + SNEP_CONNECTION_WAITING_FOR_CONTINUE; + } + else + { + ui8PacketLength = g_ui32SNEPPacketLength; + g_ui32SNEPPacketLength = 0; + + // + // Change connection status to waiting for success + // + g_eSNEPConnectionStatus = + SNEP_CONNECTION_WAITING_FOR_SUCCESS; + } + + // + // Copy the snep_packet buffer into the pui8DataPtr + // + for(ui8counter = 0; ui8counter < ui8PacketLength; ui8counter++) + { + pui8DataPtr[ui8offset++] = pui8SNEPPacketPtr[g_ui32TxIndex++]; + + } + } + else if(g_eSNEPConnectionStatus == + SNEP_CONNECTION_SENDING_N_FRAGMENTS) + { + if( g_ui32SNEPPacketLength > g_ui8MaxPayload) + { + // + // Remaining bytes = Total Length - SNEP_MAX_BUFFER + // + g_ui32SNEPPacketLength = g_ui32SNEPPacketLength - + g_ui8MaxPayload; + ui8PacketLength = g_ui8MaxPayload; + } + else + { + ui8PacketLength = g_ui32SNEPPacketLength; + // + // Remaining bytes = 0 + // + g_ui32SNEPPacketLength = 0; + g_eSNEPConnectionStatus = SNEP_CONNECTION_WAITING_FOR_SUCCESS; + } + + // + // Copy the snep_packet buffer into the pui8DataPtr + // + for(ui8counter = 0; ui8counter < ui8PacketLength; ui8counter++) + { + pui8DataPtr[ui8offset++] = pui8SNEPPacketPtr[g_ui32TxIndex++]; + } + + } + break; + } + case SNEP_REQUEST_REJECT: + { + break; + } + default: + { + break; + } + } + + return ui8offset; +} + +//***************************************************************************** +// +//! Sends response to the client. +//! +//! \param pui8DataPtr is the start pointer where the response is written. +//! \param eResponseCmd is the response command to be sent. +//! +//! The \e eResponseCmd parameter can be any of the following: +//! +//! - \b SNEP_RESPONSE_CONTINUE - Continue send remaining fragments +//! - \b SNEP_RESPONSE_SUCCESS - Operation succeeded +//! - \b SNEP_RESPONSE_NOT_FOUND - Resource not found +//! - \b SNEP_RESPONSE_EXCESS_DATA - Resource exceeds data size limit +//! - \b SNEP_RESPONSE_BAD_REQUEST - Malformed request not understood +//! - \b SNEP_RESPONSE_NOT_IMPLEMENTED - Unsupported functionality requested +//! - \b SNEP_RESPONSE_UNSUPPORTED_VER - Unsupported protocol version +//! - \b SNEP_RESPONSE_REJECT - Do not send remaining fragments +//! +//! This function sends an SNEP response from the SNEP server to an SNEP client. +//! It must be called from the LLCP_sendI() function. +//! +//! \return \b ui8offset is the length of the response written starting at +//! \b pui8DataPtr. +// +//***************************************************************************** +uint8_t SNEP_sendResponse(uint8_t * pui8DataPtr, tSNEPCommands eResponseCmd) +{ + uint8_t ui8offset = 0; + + switch(eResponseCmd) + { + case SNEP_RESPONSE_CONTINUE: + { + if(g_eSNEPConnectionStatus == SNEP_CONNECTION_RECEIVED_FIRST_PACKET) + { + // + // SNEP Protocol Version + // + pui8DataPtr[ui8offset++] = SNEP_VERSION; + + // + // Response Field + // + pui8DataPtr[ui8offset++] = (uint8_t) SNEP_RESPONSE_CONTINUE; + + // + // Length (4 bytes) + // + pui8DataPtr[ui8offset++] = 0x00; + pui8DataPtr[ui8offset++] = 0x00; + pui8DataPtr[ui8offset++] = 0x00; + pui8DataPtr[ui8offset++] = 0x00; + g_eSNEPConnectionStatus = SNEP_CONNECTION_RECEIVING_N_FRAGMENTS; + } + break; + } + case SNEP_RESPONSE_SUCCESS: + { + if(g_eSNEPConnectionStatus == SNEP_CONNECTION_RECEIVE_COMPLETE) + { + // + // SNEP Protocol Version + // + pui8DataPtr[ui8offset++] = SNEP_VERSION; + + // + // Response Field + // + pui8DataPtr[ui8offset++] = (uint8_t) SNEP_RESPONSE_SUCCESS; + + // + // Length (4 bytes) + // + pui8DataPtr[ui8offset++] = 0x00; + pui8DataPtr[ui8offset++] = 0x00; + pui8DataPtr[ui8offset++] = 0x00; + pui8DataPtr[ui8offset++] = 0x00; + g_eSNEPConnectionStatus = SNEP_CONNECTION_IDLE; + } + break; + } + case SNEP_RESPONSE_NOT_FOUND: + { + break; + } + case SNEP_RESPONSE_EXCESS_DATA: + { + break; + } + case SNEP_RESPONSE_BAD_REQUEST: + { + break; + } + case SNEP_RESPONSE_NOT_IMPLEMENTED: + { + break; + } + case SNEP_RESPONSE_UNSUPPORTED_VER: + { + break; + } + case SNEP_RESPONSE_REJECT: + { + if(g_eSNEPConnectionStatus == SNEP_CONNECTION_EXCESS_SIZE) + { + // + // SNEP Protocol Version + // + pui8DataPtr[ui8offset++] = SNEP_VERSION; + + // + // Response Field + // + pui8DataPtr[ui8offset++] = (uint8_t) SNEP_RESPONSE_REJECT; + + // + // Length (4 bytes) + // + pui8DataPtr[ui8offset++] = 0x00; + pui8DataPtr[ui8offset++] = 0x00; + pui8DataPtr[ui8offset++] = 0x00; + pui8DataPtr[ui8offset++] = 0x00; + g_eSNEPConnectionStatus = SNEP_CONNECTION_IDLE; + } + break; + } + default: + { + break; + } + } + return ui8offset; +} + +//***************************************************************************** +// +//! Processes the data received from a client/server. +//! +//! \param pui8RxBuffer is the starting pointer of the SNEP request/response +//! received. +//! \param ui8RxLength is the length of the SNEP request/response received. +//! +//! This function handles the requests/responses received inside an I-PDU +//! in the LLCP layer. This function must be called inside +//! LLCP_processReceivedData(). +//! +//! \return None +// +//***************************************************************************** +void SNEP_processReceivedData(uint8_t * pui8RxBuffer, uint8_t ui8RxLength) +{ + volatile uint8_t ui8SNEPversion; + tSNEPCommands eCommandField; + + eCommandField = (tSNEPCommands) pui8RxBuffer[1]; + + if((g_eSNEPConnectionStatus == SNEP_CONNECTION_RECEIVED_FIRST_PACKET) || + (g_eSNEPConnectionStatus == SNEP_CONNECTION_RECEIVING_N_FRAGMENTS)) + { + if(g_ui32SNEPRemainingRxPayloadBytes > ui8RxLength) + { + g_ui8SNEPReceivedBytes = ui8RxLength; + g_eSNEPConnectionStatus = SNEP_CONNECTION_RECEIVING_N_FRAGMENTS; + g_eRxPacketStatus = RECEIVED_N_FRAGMENT; + } + else + { + g_ui8SNEPReceivedBytes = (uint8_t)g_ui32SNEPRemainingRxPayloadBytes; + g_eSNEPConnectionStatus = SNEP_CONNECTION_RECEIVE_COMPLETE; + g_eRxPacketStatus = RECEIVED_FRAGMENT_COMPLETED; + } + g_ui32SNEPRemainingRxPayloadBytes = g_ui32SNEPRemainingRxPayloadBytes - + g_ui8SNEPReceivedBytes; + g_pui8SNEPRxPacketPtr = &pui8RxBuffer[0]; + } + else if(eCommandField >= 0x80) + { + // + // Process Responses + // + switch(eCommandField) + { + case SNEP_RESPONSE_CONTINUE: + { + if(g_eSNEPConnectionStatus == + SNEP_CONNECTION_WAITING_FOR_CONTINUE) + { + g_eSNEPConnectionStatus = + SNEP_CONNECTION_SENDING_N_FRAGMENTS; + } + break; + } + case SNEP_RESPONSE_SUCCESS: + { + if(g_eSNEPConnectionStatus == + SNEP_CONNECTION_WAITING_FOR_SUCCESS) + { + g_eSNEPConnectionStatus = SNEP_CONNECTION_SEND_COMPLETE; + } + break; + } + case SNEP_RESPONSE_NOT_FOUND: + { + break; + } + case SNEP_RESPONSE_EXCESS_DATA: + { + break; + } + case SNEP_RESPONSE_BAD_REQUEST: + { + break; + } + case SNEP_RESPONSE_NOT_IMPLEMENTED: + { + break; + } + case SNEP_RESPONSE_UNSUPPORTED_VER: + { + break; + } + case SNEP_RESPONSE_REJECT: + { + break; + } + default : + { + break; + } + } + } + else + { + // + // Process Requests + // + switch(eCommandField) + { + case SNEP_REQUEST_CONTINUE: + { + break; + } + case SNEP_REQUEST_GET: + { + break; + } + case SNEP_REQUEST_PUT: + { + ui8SNEPversion = pui8RxBuffer[0]; + if(ui8SNEPversion == SNEP_VERSION) + { + // + // Update remaining payload bytes + // + g_ui32SNEPRemainingRxPayloadBytes = + ( + (uint32_t) (pui8RxBuffer[5] & 0xFF) + + ((uint32_t) (pui8RxBuffer[4] & 0xFF) << 8) + + ((uint32_t) (pui8RxBuffer[3] & 0xFF) << 16) + + ((uint32_t) (pui8RxBuffer[2] & 0xFF) << 24) + ); + if(g_ui32SNEPRemainingRxPayloadBytes > SNEP_MAX_PAYLOAD) + { + g_eSNEPConnectionStatus = SNEP_CONNECTION_EXCESS_SIZE; + } + else + { + if (g_ui32SNEPRemainingRxPayloadBytes + > (ui8RxLength - 6)) { + g_ui8SNEPReceivedBytes = (ui8RxLength - 6); + g_eSNEPConnectionStatus = + SNEP_CONNECTION_RECEIVED_FIRST_PACKET; + g_eRxPacketStatus = RECEIVED_FIRST_FRAGMENT; + } + else + { + // + // Packet Length + // + g_ui8SNEPReceivedBytes = + (uint8_t) g_ui32SNEPRemainingRxPayloadBytes; + g_eSNEPConnectionStatus = + SNEP_CONNECTION_RECEIVE_COMPLETE; + g_eRxPacketStatus = RECEIVED_FRAGMENT_COMPLETED; + } + // + // Update remaining payload bytes + // + g_ui32SNEPRemainingRxPayloadBytes = + g_ui32SNEPRemainingRxPayloadBytes + - g_ui8SNEPReceivedBytes; + + // + // Set the g_pui8SNEPRxPacketPtr to the start of payload + // + g_pui8SNEPRxPacketPtr = &pui8RxBuffer[6]; + + } + } + else + { + g_eSNEPConnectionStatus = SNEP_WRONG_VERSION_RECEIVED; + } + break; + } + case SNEP_REQUEST_REJECT: + { + break; + } + default : + { + break; + } + } + } +} + +//***************************************************************************** +// +//! Get RxStatus flag, Clear packet status flag,retrieve length of data and +//! retrieve data +//! +//! \param peReceiveFlag is a pointer to store the RX state status. +//! \param pui8length is a pointer to store the number of received bytes. +//! \param pui8DataPtr is a double pointer to store the pointer of data +//! received. +//! +//! The \e peReceiveFlag parameter can be any of the following: +//! +//! - \b RECEIVED_NO_FRAGMENT - No Fragment has been received +//! - \b RECEIVED_FIRST_FRAGMENT - First fragment has been received. +//! - \b RECEIVED_N_FRAGMENT - N Fragment has been received. +//! - \b RECEIVED_FRAGMENT_COMPLETED - End of the fragment has been received. +//! +//! This function must be called in the main application after the +//! NFCP2P_proccessStateMachine() is called to ensure the data received is moved +//! to another buffer and handled when a fragment is received. +//! +//! \return None +// +//***************************************************************************** +void SNEP_getReceiveStatus(tPacketStatus * peReceiveFlag, uint8_t * pui8length, + uint8_t ** pui8DataPtr) +{ + // + // Save RX Packet Status Flag + // + *peReceiveFlag = g_eRxPacketStatus; + + // + // Clear the packet status flag + // + g_eRxPacketStatus = RECEIVED_NO_FRAGMENT; + + // + // Save Number of Byted received + // + *pui8length = g_ui8SNEPReceivedBytes; + + // + // Set Data = ReceivedPacket + // + *pui8DataPtr = g_pui8SNEPRxPacketPtr; + + return; +} + +//***************************************************************************** +// +//! Returns current SNEP Connection Status enumeration +//! +//! This function returns the current SNEP status flag. It must be called inside +//! LLCP_processReceivedData() to determine if further I-PDUs are required, +//! which is when there are requests/responses queued. +//! +//! \return g_eSNEPConnectionStatus the current connection status flag. +// +//***************************************************************************** +tSNEPConnectionStatus SNEP_getProtocolStatus(void) +{ + return g_eSNEPConnectionStatus; +} + +//***************************************************************************** +// +//! Sets current SNEP Connection Status enumeration +//! +//! \param eProtocolStatus is the status flag used by the SNEP state machine +//! SNEP_processReceivedData() to send request/response. +//! New sent transactions are allowed only when eProtocolStatus is set +//! to \b SNEP_CONNECTION_IDLE. +//! +//! The \e eProtocolStatus parameter can be any of the following: +//! +//! - \b SNEP_CONNECTION_IDLE - No ongoing Tx/Rx +//! - \b SNEP_WRONG_VERSION_RECEIVED - Wrong Version Received +//! - \b SNEP_CONNECTION_RECEIVED_FIRST_PACKET - Received First Fragment +//! - \b SNEP_CONNECTION_RECEIVING_N_FRAGMENTS - Received N Fragment +//! - \b SNEP_CONNECTION_WAITING_FOR_CONTINUE - Waiting for Continue response +//! - \b SNEP_CONNECTION_WAITING_FOR_SUCCESS - Waiting for Success response +//! - \b SNEP_CONNECTION_SENDING_N_FRAGMENTS - Sending N Fragment +//! - \b SNEP_CONNECTION_SEND_COMPLETE - Send Completed +//! - \b SNEP_CONNECTION_RECEIVE_COMPLETE - Receive Completed +//! - \b SNEP_CONNECTION_EXCESS_SIZE - Received Excess Size request +//! +//! This function is called inside LLCP_processReceivedData(), to set the +//! \e g_eSNEPConnectionStatus flag to \b SNEP_CONNECTION_IDLE after +//! a send transaction is completed to allow for further send transactions. +//! +//! \return None +// +//***************************************************************************** +void SNEP_setProtocolStatus(tSNEPConnectionStatus eProtocolStatus) +{ + g_eSNEPConnectionStatus = eProtocolStatus; + return; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + diff --git a/nfclib/snep.h b/nfclib/snep.h new file mode 100644 index 0000000..561c688 --- /dev/null +++ b/nfclib/snep.h @@ -0,0 +1,206 @@ +//***************************************************************************** +// +// snep.h - Simple NDEF Exchange Protocol deffinitions +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#ifndef __NFC_SNEP_H__ +#define __NFC_SNEP_H__ + +#include "types.h" + +//***************************************************************************** +// +//! \addtogroup nfc_snep_api NFC SNEP API Functions +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This size is used to limit the maximum size of the incoming NDEF message. +// The maximum is dependent on the Maximum Information Units (MIU) defined in +// the LLCP layer. +// For example for MIU = 248, SNEP_MAX_BUFFER = 248 +// +//***************************************************************************** +// +//! This is the maximum size of a fragment that is sent/received. +// +#define SNEP_MAX_BUFFER 248 + +// +//! Maximum size of the incoming payload. +// +#define SNEP_MAX_PAYLOAD 20000 + +// +//! Simple NDEF protocol version specified in the specification. +// +#define SNEP_VERSION 0x10 + +//***************************************************************************** +// +// List of SNEP Commands +// +//***************************************************************************** + +//***************************************************************************** +// +//! SNEPCommand request / responses enumeration. +// +//***************************************************************************** +typedef enum +{ + // + // SNEP request field value + // + + //! See SNEP V1.0 Section 4.1 + SNEP_REQUEST_CONTINUE = 0x00, + + //! See SNEP V1.0 Section 4.2 + SNEP_REQUEST_GET = 0x01, + + //! See SNEP V1.0 Section 4.3 + SNEP_REQUEST_PUT = 0x02, + // 03h-7Eh Reserved for future use + + //! See SNEP V1.0 Section 4.4 + SNEP_REQUEST_REJECT = 0x7F, + // 80h-FFh Reserved for response field values + + // + // See SNEP Response Field Values + // + // 00h-7Fh Reserved for request field values + + //! See SNEP V1.0 Section 5.1 + SNEP_RESPONSE_CONTINUE = 0x80, + + //! See SNEP V1.0 Section 5.2 + SNEP_RESPONSE_SUCCESS = 0x81, + + //! See SNEP V1.0 Section 5.3 + SNEP_RESPONSE_NOT_FOUND = 0xC0, + + //! See SNEP V1.0 Section 5.4 + SNEP_RESPONSE_EXCESS_DATA = 0xC1, + + //! See SNEP V1.0 Section 5.5 + SNEP_RESPONSE_BAD_REQUEST = 0xC2, + + //! See SNEP V1.0 Section 5.6 + SNEP_RESPONSE_NOT_IMPLEMENTED = 0xE0, + + //! See SNEP V1.0 Section 5.7 + SNEP_RESPONSE_UNSUPPORTED_VER = 0xE1, + + //! See SNEP V1.0 Section 5.8 + SNEP_RESPONSE_REJECT = 0xFF +}tSNEPCommands; + +//***************************************************************************** +// +//! SNEP Connection Status Enumeration. +// +//***************************************************************************** +typedef enum +{ + //! No ongoing transaction to/from the client + SNEP_CONNECTION_IDLE = 0x00, + + //! Wrong version received + SNEP_WRONG_VERSION_RECEIVED, + + //! Received first fragment + SNEP_CONNECTION_RECEIVED_FIRST_PACKET, + + //! Received n fragment + SNEP_CONNECTION_RECEIVING_N_FRAGMENTS, + + //! Waiting for continue response + SNEP_CONNECTION_WAITING_FOR_CONTINUE, + + //! Waiting for success response + SNEP_CONNECTION_WAITING_FOR_SUCCESS, + + //! Sending n fragment + SNEP_CONNECTION_SENDING_N_FRAGMENTS, + + //! Send completed + SNEP_CONNECTION_SEND_COMPLETE, + + //! Receive completed + SNEP_CONNECTION_RECEIVE_COMPLETE, + + //! Received excess size request. + SNEP_CONNECTION_EXCESS_SIZE +}tSNEPConnectionStatus; + +//***************************************************************************** +// +//! RX packet status enumeration. +// +//***************************************************************************** +typedef enum +{ + // + //! No pending received data + // + RECEIVED_NO_FRAGMENT = 0, + + // + //! First fragment received from the client + // + RECEIVED_FIRST_FRAGMENT, + + // + //! N fragment received from the client + RECEIVED_N_FRAGMENT, + + //! Last fragment received from the client - packet completed + RECEIVED_FRAGMENT_COMPLETED +}tPacketStatus; + +//***************************************************************************** +// +// Function Prototypes +// +//***************************************************************************** +void SNEP_init(void); +void SNEP_setMaxPayload(uint8_t ui8MaxPayload); +tStatus SNEP_setupPacket(uint8_t * pui8PacketPtr, uint32_t ui32PacketLength); +uint8_t SNEP_sendRequest(uint8_t * pui8DataPtr, tSNEPCommands eRequestCmd); +uint8_t SNEP_sendResponse(uint8_t * pui8DataPtr, tSNEPCommands eResponseCmd); +void SNEP_processReceivedData(uint8_t * pui8RxBuffer, uint8_t ui8RxLength); +void SNEP_getReceiveStatus(tPacketStatus * peReceiveFlag, uint8_t * length, + uint8_t ** pui8DataPtr); +tSNEPConnectionStatus SNEP_getProtocolStatus(void); +void SNEP_setProtocolStatus(tSNEPConnectionStatus eProtocolStatus); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +#endif // __NFC_SNEP_H__ diff --git a/nfclib/ssitrf79x0.c b/nfclib/ssitrf79x0.c new file mode 100644 index 0000000..b14a393 --- /dev/null +++ b/nfclib/ssitrf79x0.c @@ -0,0 +1,826 @@ +//***************************************************************************** +// +// ssitrf79x0.c - SSI Driver for the TI TRF79x0 on the dk-lm3s9b96 board. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#include +#include +#include "inc/hw_gpio.h" +#include "inc/hw_memmap.h" +#include "inc/hw_ssi.h" +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/pin_map.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/ssi.h" +#include "driverlib/sysctl.h" +#include "ssitrf79x0.h" +#include "trf79x0.h" +#include "trf79x0_hw.h" + +//***************************************************************************** +// +// Raw SPI through SSI access API for the TRF79x0. Most user code will not and +// should not call these functions but instead use the provided higher level +// functions in trf79x0.c, directmode.c and iso14443a.c. +// +//***************************************************************************** +//***************************************************************************** +// +// Global that holds the clock speed of the MicroController in Hz. +// +//***************************************************************************** +extern uint32_t g_ui32SysClk; + +//***************************************************************************** +// +// The rate of the SSI clock and derived values. +// +//***************************************************************************** +#define SSI_CLKS_PER_MS (SSI_CLK_RATE / 1000) +#define STATUS_READS_PER_MS (SSI_CLKS_PER_MS / 16) +#define SSI_NO_DATA 0 + +//***************************************************************************** +// +// Internal helper function that sends a buffer of data to the TRF79x0, used +// by all the functions that need to send bytes. +// +//***************************************************************************** +void +SSITRF79x0GenericWrite(unsigned char const *pucBuffer, unsigned int uiLength) +{ + uint32_t ulDummyData; + + while(uiLength > 0) + { + // + // Write address/command/data and clear SSI register of dummy data. + // + MAP_SSIDataPut(TRF79X0_SSI_BASE, (unsigned long)*pucBuffer); + // + // Wait until the SSI Module is completed sending uiLength bytes to the SSI module. + // + while(SSIBusy(TRF79X0_SSI_BASE) == true); + MAP_SSIDataGet(TRF79X0_SSI_BASE, &ulDummyData); + + // + // Post increment counters. + // + pucBuffer++; + uiLength--; + } + + +} + +//***************************************************************************** +// +// Internal helper function used by all the functions that need to send bytes. +// +//***************************************************************************** +void +SSITRF79x0DummyWrite(unsigned char const *pucBuffer, unsigned int uiLength) +{ + uint32_t ulDummyData; + + while(uiLength > 0) + { + // + // Write address/command/data and clear SSI register of dummy data. + // + SSIDataPut(TRF79X0_SSI_BASE, (unsigned long)*pucBuffer); + SSIDataGet(TRF79X0_SSI_BASE, &ulDummyData); + + // + // Post increment counters. + // + pucBuffer++; + uiLength--; + } +} + +//***************************************************************************** +// +// Internal helper function that receives a buffer of data from the TRF79x0, +// used by all the functions that need to read bytes. +// +//***************************************************************************** +static void +SSITRF79x0GenericRead(unsigned char *pucBuffer, unsigned int uiLength) +{ + uint32_t ulData; + + while(uiLength > 0) + { + // + // Write dummy data for SSI clock and read data from SSI register. + // + MAP_SSIDataPut(TRF79X0_SSI_BASE, (unsigned long)SSI_NO_DATA); + // + // Wait until the SSI Module is completed sending uiLength bytes to the SSI module. + // + while(SSIBusy(TRF79X0_SSI_BASE) == true); + MAP_SSIDataGet(TRF79X0_SSI_BASE, &ulData); +// SSIDataGet(TRF79X0_SSI_BASE, &ulData); + + // + // Read data into buffers and post increment counters. + // + *pucBuffer++ = (unsigned char)ulData; + + uiLength--; + } + +} + +//***************************************************************************** +// +// Asserts the chip select for the TRF79x0. +// +//***************************************************************************** +void +SSITRF79x0ChipSelectAssert(void) +{ + // + // Disable the interrupt associated with the TRF79x0. + // + TRF79x0InterruptDisable(); + + // + // Assert the chip select for TRF79x0. + // + MAP_GPIOPinWrite(TRF79X0_CS_BASE, TRF79X0_CS_PIN, 0); +} + +//***************************************************************************** +// +// Deasserts the chip select for the TRF79x0 +// +//***************************************************************************** +void +SSITRF79x0ChipSelectDeAssert(void) +{ + // + // Deassert the chip select for the TRF79x0. + // + MAP_GPIOPinWrite(TRF79X0_CS_BASE, TRF79X0_CS_PIN, TRF79X0_CS_PIN); + + // + // Enable interrupt associated with the TRF79x0. + // + TRF79x0InterruptEnable(); +} + +//***************************************************************************** +// +// Initializes the SSI port and determines if the TRF79x0 is available. +// +// This function must be called prior to any other function offered by the +// TRF79x0. It configures the SSI port to run in Motorola/Freescale +// mode. +// +// \return None. +// +//***************************************************************************** +void +SSITRF79x0Init(void) +{ + // + // Enable the peripherals used to drive the TRF79x0 on SSI. + // + MAP_SysCtlPeripheralEnable(TRF79X0_SSI_PERIPH); + + // + // Enable the GPIO peripherals associated with the SSI. + // + MAP_SysCtlPeripheralEnable(TRF79X0_CLK_PERIPH); + MAP_SysCtlPeripheralEnable(TRF79X0_RX_PERIPH); + MAP_SysCtlPeripheralEnable(TRF79X0_TX_PERIPH); + MAP_SysCtlPeripheralEnable(TRF79X0_CS_PERIPH); + + // + // Configure the appropriate pins to be SSI instead of GPIO. The CS + // is configured as GPIO to support TRF79x0 SPI requirements for R/W + // access. + // + MAP_GPIOPinConfigure(TRF79X0_CLK_CONFIG); + MAP_GPIOPinConfigure(TRF79X0_RX_CONFIG); + MAP_GPIOPinConfigure(TRF79X0_TX_CONFIG); + MAP_GPIOPinTypeSSI(TRF79X0_CLK_BASE, TRF79X0_CLK_PIN); + MAP_GPIOPinTypeSSI(TRF79X0_RX_BASE, TRF79X0_RX_PIN); + MAP_GPIOPinTypeSSI(TRF79X0_TX_BASE, TRF79X0_TX_PIN); + MAP_GPIOPinTypeGPIOOutput(TRF79X0_CS_BASE, TRF79X0_CS_PIN); + + MAP_GPIOPadConfigSet(TRF79X0_CLK_BASE, TRF79X0_CLK_PIN, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU); + MAP_GPIOPadConfigSet(TRF79X0_RX_BASE, TRF79X0_RX_PIN, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU); + MAP_GPIOPadConfigSet(TRF79X0_TX_BASE, TRF79X0_TX_PIN, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU); + + // + // Deassert the SSI chip selects TRF79x0. + // + MAP_GPIOPinWrite(TRF79X0_CS_BASE, TRF79X0_CS_PIN, TRF79X0_CS_PIN); + + // + // Configure the SSI port for 2MHz operation. + // + MAP_SSIConfigSetExpClk(TRF79X0_SSI_BASE, g_ui32SysClk, + SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, SSI_CLK_RATE, + 8); + + if(RF_DAUGHTER_TRF7970) + { + // + // Switch from SPH=0 to SPH=1. Required for TRF7970. + // + HWREG(TRF79X0_SSI_BASE + SSI_O_CR0) |= SSI_CR0_SPH; + } + + // + // Enable the SSI controller. + // + MAP_SSIEnable(TRF79X0_SSI_BASE); +} + +//***************************************************************************** +// +// Writes a single value to TRF79x0 for address provided. +// +// \param ucAddress is the register address to write and must be between 0 +// and 0x1f, inclusive. +// \param ucData is the data byte to write. +// +// This function asserts the TRF79x0 chip select, sends a write command, +// the single data value and then deasserts the chip select. +// +// \return None. +// +//***************************************************************************** +void +SSITRF79x0WriteRegister(unsigned char ucAddress, unsigned char ucData) +{ + unsigned char pucCommand[2]; + + // + // Assert the chip select for TRF79x0. + // + SSITRF79x0ChipSelectAssert(); + + // + // Isolate register address. + // + ucAddress = ucAddress & TRF79X0_ADDRESS_MASK; + + // + // Add TRF79x0 write single command. + // + ucAddress |= TRF79X0_CONTROL_REG_WRITE | TRF79X0_REG_MODE_SINGLE; + + // + // Put the address and data into the buffer. + // + pucCommand[0] = ucAddress; + pucCommand[1] = ucData; + + // + // Start the write. + // + SSITRF79x0GenericWrite(pucCommand, sizeof(pucCommand)); + + // + // Deassert the chip select for the TRF79x0. + // + SSITRF79x0ChipSelectDeAssert(); +} + +//***************************************************************************** +// +// Starts a continuous write operation to the given address. +// +// \param ucAddress is the register address to start this write command and +// must be between 0 and 0x1f, inclusive. +// +// This function asserts the TRF79x0 chip select and sends a write continuous +// command. The chip select stays asserted when the function returns and must +// be released with SSITRF79x0WriteContinuousStop(). +// +// Typical usage for a write to multiple registers at once is: one call to +// SSITRF79x0WriteContinuousStart(), one or more calls to +// SSITRF79x0WriteContinuousData() and one call to +// SSITRF79x0WriteContinuousStop(). +// +// \sa SSITRF79x0WriteContinuousData() +// +// \return None. +// +//***************************************************************************** +void +SSITRF79x0WriteContinuousStart(unsigned char ucAddress) +{ + // + // Assert the chip select for TRF79x0. + // + SSITRF79x0ChipSelectAssert(); + + // + // Isolate register address. + // + ucAddress = ucAddress & TRF79X0_ADDRESS_MASK; + + // + // Add TRF79x0 write continuous command. + // + ucAddress |= TRF79X0_CONTROL_REG_WRITE | TRF79X0_REG_MODE_CONTINUOUS; + + SSITRF79x0GenericWrite(&ucAddress, 1); + + // + // Keep chip select asserted for follow-up calls to + // SSITRF79x0WriteContinuousData(). Calling code must ensure to finish + // with SSITRF79x0WriteContinuousStop(). + // +} + +//***************************************************************************** +// +// Starts a direct continous write operation +// +// This function asserts the chip select for the TRF79x0. +// +// \return None +// +//***************************************************************************** +void +SSITRF79x0WriteDirectContinuousStart(void) +{ + // + // Assert the chip select for TRF79x0. + // + SSITRF79x0ChipSelectAssert(); +} + +//***************************************************************************** +// +// Sends data in continuous write mode. +// +// \param pucBuffer is a pointer to the data buffer to write. +// \param uiLength is the length of the data to write in bytes. +// +// This function sends data from the buffer to the TRF79x0. The write must +// have been previously set up with SSITRF79x0WriteContinuousStart(). +// +// \return None. +// +//***************************************************************************** +void +SSITRF79x0WriteContinuousData(unsigned char const *pucBuffer, + unsigned int uiLength) +{ + SSITRF79x0GenericWrite(pucBuffer, uiLength); +} + +//***************************************************************************** +// +// Stops a continuous write operation. +// +// This function deasserts the TRF79x0 chip select. +// +// \return None. +// +//***************************************************************************** +void +SSITRF79x0WriteContinuousStop(void) +{ + // + // Deassert the chip select for the TRF79x0. + // + SSITRF79x0ChipSelectDeAssert(); +} + +//***************************************************************************** +// +// Reads a single value from TRF79x0 at the address provided. +// +// \param ucAddress is the register address to read and must be between 0 +// and 0x1f, inclusive. +// +// This function asserts the TRF79x0 chip select, sends a read command, +// reads a single byte and then deasserts the chip select. +// +// \return This function returns the value that was stored in the given +// register. +// +//***************************************************************************** +unsigned char +SSITRF79x0ReadRegister(unsigned char ucAddress) +{ + unsigned char ucData = 0; + + // + // Assert the chip select for TRF79x0. + // + SSITRF79x0ChipSelectAssert(); + + // + // Isolate register address. + // + ucAddress = ucAddress & TRF79X0_ADDRESS_MASK; + + // + // Add TRF79x0 read single command. + // + ucAddress |= TRF79X0_CONTROL_REG_READ | TRF79X0_REG_MODE_SINGLE; + + SSITRF79x0GenericWrite(&ucAddress, 1); + + if(RF_DAUGHTER_TRF7960) + { + // + // Switch from SPH=0 to SPH=1. + // + HWREG(TRF79X0_SSI_BASE + SSI_O_CR0) |= SSI_CR0_SPH; + } + + // + // Get the data. + // + SSITRF79x0GenericRead(&ucData, 1); + + if(RF_DAUGHTER_TRF7960) + { + // + // Switch from SPH=1 to SPH=0. + // + HWREG(TRF79X0_SSI_BASE + SSI_O_CR0) &= ~SSI_CR0_SPH; + } + + // + // Deassert the chip select for the TRF79x0. + // + SSITRF79x0ChipSelectDeAssert(); + + return(ucData); +} + +//***************************************************************************** +// +// Starts a continuous read operation from the given address. +// +// \param ucAddress is the register address to start this read command and must +// be between 0 and 0x1f, inclusive. +// +// This function asserts the TRF79x0 chip select and sends a read continuous +// command. The chip select stays asserted when the function returns and must +// be released with SSITRF79x0ReadContinuousStop(). +// +// Typical usage for a read from multiple registers at once is: one call to +// SSITRF79x0ReadContinuousStart(), one or more calls to +// SSITRF79x0ReadContinuousData() and one call to +// SSITRF79x0ReadContinuousStop(). +// +// \sa SSITRF79x0ReadContinuousData() +// +// \return None. +// +//***************************************************************************** +void +SSITRF79x0ReadContinuousStart(unsigned char ucAddress) +{ + // + // Assert the chip select for TRF79x0. + // + SSITRF79x0ChipSelectAssert(); + + // + // Isolate register address. + // + ucAddress = ucAddress & TRF79X0_ADDRESS_MASK; + + // + // Add TRF79x0 read continuous command. + // + ucAddress |= TRF79X0_CONTROL_REG_READ | TRF79X0_REG_MODE_CONTINUOUS; + + SSITRF79x0GenericWrite(&ucAddress, 1); + + if(RF_DAUGHTER_TRF7960) + { + // + // Switch from SPH=0 to SPH=1. + // + HWREG(TRF79X0_SSI_BASE + SSI_O_CR0) |= SSI_CR0_SPH; + } +} + +//***************************************************************************** +// +// Receives data in continuous read mode. +// +// \param pucBuffer is a pointer to the data buffer to receive data. +// \param uiLength is the length of the data to read in bytes. +// +// This function reads data from the the TRF79x0 into the buffer. The read +// must have been previously set up with SSITRF79x0ReadContinuousStart(). +// +// \return None. +// +//***************************************************************************** +void +SSITRF79x0ReadContinuousData(unsigned char *pucBuffer, unsigned int uiLength) +{ + SSITRF79x0GenericRead(pucBuffer, uiLength); +} + +//***************************************************************************** +// +// Stop a continuous read operation. +// +// This function deasserts the TRF79x0 chip select. +// +// \return None. +// +//***************************************************************************** +void +SSITRF79x0ReadContinuousStop(void) +{ + if(RF_DAUGHTER_TRF7960) + { + // + // Switch from SPH=1 to SPH=0. + // + HWREG(TRF79X0_SSI_BASE + SSI_O_CR0) &= ~SSI_CR0_SPH; + } + + // + // Deassert the chip select for the TRF79x0. + // + SSITRF79x0ChipSelectDeAssert(); +} + +//***************************************************************************** +// +// Reads IRQ status value from TRF79x0. +// +// This function reads the TRF79x0 IRQ status register 0x0c and returns its +// contents. This will make the TRF79x0 release its interrupt request. +// +// \note You should use this function instead of a direct read from register +// 0x0c if you want to retrieve the IRQ status since this function applies +// a special workaround as indicated in SLOA140. +// +// \return Returns the IRQ status +// +//***************************************************************************** +unsigned char +SSITRF79x0ReadIRQStatus(void) +{ + unsigned char pucData[2]; + + // + // Workaround as per SLOA140: When reading the IRQ status register, do a + // continuous read with an additional register to ensure at least one + // additional SPI clock after reading the IRQ status. Ignore the second + // read result. + // + + SSITRF79x0ReadContinuousStart(TRF79X0_IRQ_STATUS_REG); + SSITRF79x0ReadContinuousData(pucData, sizeof(pucData)); + SSITRF79x0ReadContinuousStop(); + + return(pucData[0]); +} + +//***************************************************************************** +// +// Executes a direct command on the TRF79x0. +// +// \param ucCommand is the command to be executed and must be a valid command +// code between 0 and 0x1f. Definitions for command codes are given in +// trf79x0.h. +// +// \note This function applies a special workaround as indicated in SLOA140. +// +// \return Returns void. +// +//***************************************************************************** +void +SSITRF79x0WriteDirectCommand(unsigned char ucCommand) +{ + unsigned char pucCommand[2]; + + // + // Assert the chip select for TRF79x0. + // + SSITRF79x0ChipSelectAssert(); + + // + // Add TRF79x0 direct command. + // + ucCommand = ucCommand | TRF79X0_CONTROL_CMD; + + // + // Workaround as per SLOA140: When sending a command, add a dummy cycle. + // + pucCommand[0] = ucCommand; + pucCommand[1] = SSI_NO_DATA; + + if(ucCommand == TRF79X0_RESET_FIFO_CMD) + { + SSITRF79x0GenericWrite(pucCommand, sizeof(pucCommand)); + } + else + { + SSITRF79x0GenericWrite(pucCommand, 1); + } + + // + // Deassert the chip select for the TRF79x0. + // + SSITRF79x0ChipSelectDeAssert(); +} + +//***************************************************************************** +// +// Write Direct Command Tailored for 7970 chip. Ported for redundancy +// +// \param ucCommand is the command to be executed and must be a valid command +// code between 0 and 0x1f. Definitions for command codes are given in +// trf79x0.h. A dummy command is sent after the direct command to handle +// issues with the last command somtimes not processing. +// +//***************************************************************************** +void +SSITRF79x0WriteDirectCommandWithDummy(unsigned char ucCommand) +{ + unsigned char pucCommand[2]; + + // + // Assert the chip select for TRF7970. + // + SSITRF79x0ChipSelectAssert(); + + // + // Add TRF7970 direct command. + // + ucCommand = ucCommand | TRF79X0_CONTROL_CMD; + + // + // Workaround as per SLOA140: When sending a command, add a dummy cycle. + // + pucCommand[0] = ucCommand; + pucCommand[1] = SSI_NO_DATA; + + SSITRF79x0GenericWrite(pucCommand, sizeof(pucCommand)); + + // + // Deassert the chip select for the TRF7970. + // + SSITRF79x0ChipSelectDeAssert(); +} + +//***************************************************************************** +// +// Executes a Reset direct command on the TRF79x0. +// +// \param ucCommand is the command to be executed and must be a valid command +// code between 0 and 0x1f. Definitions for command codes are given in +// trf79x0.h. +// +// \note This function applies a special workaround as indicated in SLOA140. +// +// \return Returns void. +// +//***************************************************************************** +void +SSITRF79x0WriteResetFifoDirectCommand(unsigned char ucCommand) +{ + unsigned char pucCommand[1]; + + // + // Assert the chip select for TRF79x0. + // + SSITRF79x0ChipSelectAssert(); + + // + // Add TRF79x0 direct command. + // + ucCommand = ucCommand | TRF79X0_CONTROL_CMD; + + // + // Workaround as per SLOA140: When sending a command, add a dummy cycle. + // + pucCommand[0] = ucCommand; + + SSITRF79x0GenericWrite(pucCommand, sizeof(pucCommand)); + + // + // Deassert the chip select for the TRF79x0. + // + SSITRF79x0ChipSelectDeAssert(); +} + +//***************************************************************************** +// +// Executes: writes a packet to the TRF79x0 +// +// \param pui8Buffer +// \param ui8CRCBit +// \param ui8TotalLength +// \param ui8PayloadLength +// \param bHeaderEnable +// +// \note +// +// \return Returns void. +// +//***************************************************************************** +void SSITRF79x0WritePacket(uint8_t *pui8Buffer, uint8_t ui8CRCBit, \ + uint8_t ui8TotalLength, uint8_t ui8PayloadLength, bool bHeaderEnable) +{ + uint8_t ui8LengthLowerNibble = (ui8TotalLength & 0x0F) << 4; + uint8_t ui8LengthHigherNibble = (ui8TotalLength & 0xF0) >> 4; + uint8_t pui8HeaderData[2]; + + // + // Assert the chip select for TRF79x0. + // + SSITRF79x0ChipSelectAssert(); + + if(bHeaderEnable == true) + { + // RESET FIFO + //while (!(IFG2 & UCB0TXIFG)); // USCI_B0 TX buffer ready? + pui8HeaderData[0] = 0x8F; // Previous data to TX, RX + SSITRF79x0GenericWrite(pui8HeaderData,1); + //while(UCB0STAT & UCBUSY); + + // CRC COMMAND + //while (!(IFG2 & UCB0TXIFG)); // USCI_B0 TX buffer ready? + pui8HeaderData[0] = 0x90 | (ui8CRCBit & 0x01); // Previous data to TX, RX + SSITRF79x0GenericWrite(pui8HeaderData,1); + //while(UCB0STAT & UCBUSY); + + // WRITE TO LENGTH REG + //while (!(IFG2 & UCB0TXIFG)); // USCI_B0 TX buffer ready? + pui8HeaderData[0] = 0x3D; + SSITRF79x0GenericWrite(pui8HeaderData,1); + //while(UCB0STAT & UCBUSY); + + // LENGTH HIGH Nibble + //while (!(IFG2 & UCB0TXIFG)); // USCI_B0 TX buffer ready? + pui8HeaderData[0] = ui8LengthHigherNibble; // Previous data to TX, RX + SSITRF79x0GenericWrite(pui8HeaderData,1); + //while(UCB0STAT & UCBUSY); + + // LENGTH LOW Nibble + //while (!(IFG2 & UCB0TXIFG)); // USCI_B0 TX buffer ready? + pui8HeaderData[0] = ui8LengthLowerNibble; // Previous data to TX, RX + SSITRF79x0GenericWrite(pui8HeaderData,1); + //while(UCB0STAT & UCBUSY); + } + else + { + //while (!(IFG2 & UCB0TXIFG)); // USCI_B0 TX buffer ready? + pui8HeaderData[0] = 0x3F; + SSITRF79x0GenericWrite(pui8HeaderData,1); + //while(UCB0STAT & UCBUSY); + } + + + SSITRF79x0GenericWrite(pui8Buffer,ui8PayloadLength); + //while(ui8PayloadLength > 0) + //{ + // while (!(IFG2 & UCB0TXIFG)); // USCI_B0 TX buffer ready? + // UCB0TXBUF = *pui8Buffer; // Previous data to TX, RX + // while(UCB0STAT & UCBUSY); + // pui8Buffer++; + // ui8PayloadLength--; + //} + + // + // Deassert the chip select for the TRF79x0. + // + SSITRF79x0ChipSelectDeAssert(); +} diff --git a/nfclib/ssitrf79x0.h b/nfclib/ssitrf79x0.h new file mode 100644 index 0000000..5b9f8f3 --- /dev/null +++ b/nfclib/ssitrf79x0.h @@ -0,0 +1,62 @@ +//***************************************************************************** +// +// ssitrf79x0.h - Header file for the TI TRF79x0 SSI driver for the +// dk-lm3s9b96 boards. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SSITRF79X0_H__ +#define __SSITRF79X0_H__ + +//***************************************************************************** +// +// Exported function prototypes. +// +//***************************************************************************** +extern void SSITRF79x0Init(void); +extern void SSITRF79x0WriteRegister(unsigned char ucAddress, + unsigned char ucData); +extern void SSITRF79x0WriteContinuousStart(unsigned char ucAddress); +extern void SSITRF79x0WriteContinuousData(unsigned char const *pucBuffer, + unsigned int uiLength); +extern void SSITRF79x0WriteContinuousStop(void); +extern unsigned char SSITRF79x0ReadRegister(unsigned char ucAddress); +extern void SSITRF79x0ReadContinuousStart(unsigned char ucAddress); +extern void SSITRF79x0ReadContinuousData(unsigned char *pucBuffer, + unsigned int uiLength); +extern void SSITRF79x0ReadContinuousStop(void); +extern unsigned char SSITRF79x0ReadIRQStatus(void); +extern void SSITRF79x0WriteDirectCommand(unsigned char ucCommand); +extern void SSITRF79x0WriteDirectContinuousStart(void); +extern void SSITRF79x0WriteResetFifoDirectCommand(unsigned char ucCommand); +extern void SSITRF79x0DummyWrite(unsigned char const *pucBuffer, + unsigned int uiLength); +extern void SSITRF79x0WriteDirectCommandWithDummy(unsigned char ucCommand); +extern void SSITRF79x0WritePacket(uint8_t *pui8Buffer, uint8_t ui8CRCBit, + uint8_t ui8TotalLength, uint8_t ui8PayloadLength, + bool eHeaderEnable); + +extern void SSITRF79x0ChipSelectAssert(void); +extern void SSITRF79x0GenericWrite(unsigned char const *pucBuffer, unsigned int uiLength); +extern void SSITRF79x0ChipSelectDeAssert(void); + + +#endif // __SSITRF79X0_H__ diff --git a/nfclib/trf79x0.c b/nfclib/trf79x0.c new file mode 100644 index 0000000..f0405e4 --- /dev/null +++ b/nfclib/trf79x0.c @@ -0,0 +1,1961 @@ +//***************************************************************************** +// +// trf79x0.c - Driver for the TI TRF79x0 on the dk-lm3s9b96 board. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_ssi.h" +#include "inc/hw_gpio.h" +#include "inc/hw_ints.h" +#include "driverlib/gpio.h" +#include "driverlib/ssi.h" +#include "driverlib/sysctl.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/timer.h" +#include "utils/uartstdio.h" +#include "ssitrf79x0.h" +#include "trf79x0_hw.h" +#include "trf79x0.h" +#include "nfc.h" +#include "nfclib/debug.h" + +//extern unsigned char g_ucNfcWorkMode = NFC_NONE; + +//***************************************************************************** +// +// Global Defines +// +//***************************************************************************** +#define NFC_FIFO_SIZE 255 +// Fifo size depends on the maximum payload size defined in LLCP.h +uint8_t g_fifo_buffer[NFC_FIFO_SIZE]; +uint8_t g_fifo_bytes_received = 0; +volatile uint8_t g_irq_flag = 0x00; +volatile uint8_t g_time_out_flag = 0x00; + +tTRF79x0TRFMode g_selected_mode = BOARD_INIT; +tTRF79x0Frequency g_selected_frequency = FREQ_STAND_BY; + +// Used for debugging +#define OUTPUT_FIFO_ENABLE 0 + +#define TRF7970A_5V_OPERATION 0x01 + +//***************************************************************************** +// +// A global variable indicating which RF daughter board, if any, is currently +// connected to the development board. +// +//***************************************************************************** +tRFDaughterBoard g_eRFDaughterType = RF_DAUGHTER_NONE; + +//***************************************************************************** +// +// API for the TRF79x0. Provides register read/write access, command +// execution, abstracted access to IRQ results and comprehensive transceiver +// functionality for higher-layer frame transmission and reception. +// +// Most user code will only need TRF79x0Init() from this module to set up +// and initialize the TRF79x0 and will then use the functions defined by +// some higher layer protocol, such as from iso14443a.c. +// +//***************************************************************************** +//***************************************************************************** +// +// The number of counts to pass to SysCtlDelay() to get approximately 1ms +// delay. +// +//***************************************************************************** +static unsigned long g_ulDelayms; + +//***************************************************************************** +// +// Global that holds the clock speed of the MicroController in Hz. +// +//***************************************************************************** +extern uint32_t g_ui32SysClk; + +//***************************************************************************** +// +// This structure holds information about encountered IRQs. The collision +// position can be queried by TRF79x0GetCollisionPosition(). +// TRF79x0IRQWait() and TRF79x0IRQWaitTimeout() can be used to wait for +// an interrupt cause to be asserted. TRF79x0IRQClearAll() and +// TRF79x0IRQClearCauses() can be used to clear indicated causes from this +// structure, since TRF79x0IRQWait()/TRF79x0IRQWaitTimeout() do not do +// that. +// +//***************************************************************************** +static volatile struct +{ + // + // This stores the contents of the IRQ status register at the most recent + // IRQ. However, the contents of this field are not reliable since IRQs + // may occur shortly after one another and a loop that simply queries state + // might miss all but the last of these. + // + unsigned char ucState; + + // + // Indicates whether a collision was detected since the last call to + // TRF79x0GetCollisionPosition(). + // + unsigned char ucCollisionDetected; + + // + // Stores the last collision position as returned in registers + // 0xd and 0xe. + // + unsigned int uiCollisionPosition; + + // + // Bitfield tracking the occurrence of abstract interrupt causes. The + // values of enum TRF79x0WaitCondition are used as indices into the + // bitfield, e.g. for a TRF79X0_WAIT_TXEND interrupt the bit at + // (1< is set. + // + unsigned int uiIrqCauses; +} +g_sIRQState; + +//***************************************************************************** +// +// Definitions for different interrupt status bits. +// +//***************************************************************************** +#define TX_FIFO_ALMOST_EMPTY 0xA0 +#define TX_COMPLETE 0x80 +#define RX_FIFO_ALMOST_FULL 0x60 +#define RX_COMPLETE 0x40 +#define COLLISION_DETECTED 0x02 + +//***************************************************************************** +// +// Timeout to apply while waiting for reception, this is expressed in +// milliseconds. +// +// For a more accurate timeout indication you can program the no-response +// timer in the TRF79x0 and must enable the no-response interrupt. +// +//***************************************************************************** +#define TRF79X0_RX_TIMEOUT 10 + +//***************************************************************************** +// +// This structure holds information about the transmission state for use by +// the FIFO refill algorithm in the IRQ handler. It is set up by +// TRF79x0FIFOWrite(). +// +//***************************************************************************** +static volatile struct +{ + // + // Pointer to the next byte to be transmitted + // + unsigned char const *pucBuffer; + + // + // Number of bytes left that need to be transmitted + // + unsigned int uiBytesRemaining; +} g_sTXState; + +//***************************************************************************** +// +// This structure holds information about the reception state for use by the +// FIFO read algorithm in the IRQ handler. It is set up by TRF79x0Receive(). +// +//***************************************************************************** +static volatile struct +{ + // + // Pointer to write the next received byte to. + // + unsigned char *pucBuffer; + + // + // Pointer to the received length counter. This is the counter that is + // passed in to TRF79x0Receive(). The integer that this pointer points + // to contains the number of bytes that were received (and stored in + // pucBuffer). + // + unsigned int *puiLength; + + // + // Length of the buffer that pucBuffer pointed to at the start of + // reception. No more bytes are received when *puiLength equals this + // value. + // + unsigned int uiMaxLength; +} g_sRXState; + +//***************************************************************************** +// +// Initializes the TRF79x0 and its communication interface. +// +// This function must be called prior to any other function offered by the +// TRF79x0. This function initializes the GPIO and pin settings, sets up the +// communication interface by calling SSITRF79x0Init() and sets up the +// interrupt handler by calling TRF79x0InterruptInit(). +// +// \return None. +// +//***************************************************************************** +void +TRF79x0Init(void) +{ + // + // Set up GPIO resources for bit-banging output access to EN and MOD + // and input for IRQ. + // + SysCtlPeripheralEnable(TRF79X0_EN_PERIPH); + SysCtlPeripheralEnable(TRF79X0_IRQ_PERIPH); + if(g_eRFDaughterType != RF_DAUGHTER_TRF7970ABP) + { + SysCtlPeripheralEnable(TRF79X0_MOD_PERIPH); + SysCtlPeripheralEnable(TRF79X0_EN2_PERIPH); + SysCtlPeripheralEnable(TRF79X0_ASKOK_PERIPH); + } + + // + // Set the IRQ pin as an input. + // + GPIOPinTypeGPIOInput(TRF79X0_IRQ_BASE, TRF79X0_IRQ_PIN); + + // + // Set the EN, EN2, MOD, and ASKOK pins as outputs. + // + GPIOPinTypeGPIOOutput(TRF79X0_EN_BASE, TRF79X0_EN_PIN); + if(g_eRFDaughterType != RF_DAUGHTER_TRF7970ABP) + { + GPIOPinTypeGPIOOutput(TRF79X0_EN2_BASE, TRF79X0_EN2_PIN); + GPIOPinTypeGPIOOutput(TRF79X0_MOD_BASE, TRF79X0_MOD_PIN); + GPIOPinTypeGPIOOutput(TRF79X0_ASKOK_BASE, TRF79X0_ASKOK_PIN); + } + + // + // Set the MOD and ASKOK pins to start with a low value. + // + if(g_eRFDaughterType != RF_DAUGHTER_TRF7970ABP) + { + GPIOPinWrite(TRF79X0_MOD_BASE, TRF79X0_MOD_PIN, 0); + GPIOPinWrite(TRF79X0_ASKOK_BASE, TRF79X0_ASKOK_PIN, 0); + } + + // + // Set up the SSI communication interface. + // + SSITRF79x0Init(); + + // + // Calculate the number of units for a 1ms delay using SysCtlDelay(). + // + // NOTE: the ifdef is necessary because of an API change + // +#ifdef TARGET_IS_TM4C123_RA1 + // + // Blizzard Silicon (and before) + // + g_ulDelayms=(SysCtlClockGet()/3000); +#else + // + // Snowflake Silicon (and after) + // + g_ulDelayms = (g_ui32SysClk / 3000); +#endif + + // + // Force a toggle on the EN and EN2 pins. + // + GPIOPinWrite(TRF79X0_EN_BASE, TRF79X0_EN_PIN, 0); + GPIOPinWrite(TRF79X0_EN_BASE, TRF79X0_EN_PIN, + TRF79X0_EN_PIN); + +// // +// // Delay 2ms between ENABLE. +// // +// SysCtlDelay(g_ulDelayms * 2); +// +// GPIOPinWrite(TRF79X0_EN2_BASE, TRF79X0_EN2_PIN, 0); +// GPIOPinWrite(TRF79X0_EN2_BASE, TRF79X0_EN2_PIN, +// TRF79X0_EN2_PIN); + + // + // Delay 2ms before initializing the TRF79x0. + // + SysCtlDelay(g_ulDelayms * 2); + + // + // Initialize the TRF7970 with a software initialization command, idle + // command, and set the modulator control register to + // + if(RF_DAUGHTER_TRF7970) + { + TRF79x0DirectCommand(TRF79X0_SOFT_INIT_CMD); + TRF79x0DirectCommand(TRF79X0_IDLE_CMD); + } + + // + // Get RF Daughter Board ID TRF7960/TRF7970 ATB + // + TRF79x0ReadRegister(TRF79X0_MODULATOR_CONTROL_REG); + + TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG, 0x01); + + // + // Set up the interrupt handler and enable the RX timeout IRQ. + // + TRF79x0InterruptInit(); + TRF79x0WriteRegister(TRF79X0_IRQ_MASK_REG, + TRF79x0ReadRegister(TRF79X0_IRQ_MASK_REG) | 1); + + // + // Delay 4ms before leaving the initialization function. + // + SysCtlDelay(g_ulDelayms * 4); +} + +//***************************************************************************** +// +// Set the Operating mode for the TRF79x0 +// +// Set bits in ISO_CONTROL_REG based on mode given +// Supported modes: +// NFC_P2P_PASSIVE_TARGET_MODE +// NFC_P2P_INITIATOR_MODE +// +// \return None. +// +//***************************************************************************** +void +TRF79x0SetMode(tTRF79x0TRFMode eMode, tTRF79x0Frequency eFrequency) +{ + g_selected_mode = eMode; + g_selected_frequency = eFrequency; + + if(g_selected_mode == P2P_PASSIVE_TARGET_MODE) + { + // + // Register 01h. ISO Control Register + // + if (eFrequency == FREQ_106_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x21); + } else if (eFrequency == FREQ_212_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x22); + } else if (eFrequency == FREQ_424_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x23); + } + } + else if(g_selected_mode == P2P_INITATIOR_MODE) + { + if (eFrequency == FREQ_106_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x31); + } else if (eFrequency == FREQ_212_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x32); + } else if (eFrequency == FREQ_424_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x33); + } + } + +} + +//***************************************************************************** +// +// Prepare the TRF79x0 interrupt handler. +// +// Sets up the GPIO for a level triggered interrupt on the TRF79x0 IRQ line +// and calls TRF79x0InterruptEnable(). Processor interrupts need to be +// enabled (IntMasterEnable() from DriverLib) for the interrupt handler to +// to be actually called. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0InterruptInit(void) +{ + // + // Set GPIO Interrupt to level triggered active high. + // + GPIOIntTypeSet(TRF79X0_IRQ_BASE, TRF79X0_IRQ_PIN, GPIO_RISING_EDGE); + + // + // Clear out any pending interrupt. + // + GPIOIntClear(TRF79X0_IRQ_BASE, TRF79X0_IRQ_PIN); + + // + // Set GPIO Interrupt Enable. + // + TRF79x0InterruptEnable(); + + // + // Enable the GPIO interrupt. + // + IntEnable(TRF79X0_IRQ_INT); +} + +//***************************************************************************** +// +// IRQ pin Interrupt Handler. This function is triggered by the IRQ pin going +// high. The g_irq_flag flag is set as a result. +// +//***************************************************************************** +void TRF79x0IRQPinInterruptHandler(void) +{ + uint32_t ui32IRQGPIOBankIntStatus; + + // + // Get the masked interrupt status. + // + ui32IRQGPIOBankIntStatus=GPIOIntStatus(TRF79X0_IRQ_BASE,true); + + + // + // check if IRQ pin is high + // + if(ui32IRQGPIOBankIntStatus & TRF79X0_IRQ_PIN) + { + // + // Clear the asserted interrupts. + // + GPIOIntClear(TRF79X0_IRQ_BASE, TRF79X0_IRQ_PIN); + + // + // Set flag appropriately. + // + g_irq_flag = 0x01; + } + +} + +//***************************************************************************** +// +// Internal helper function to transmit up to uiMaxLength bytes from g_sTXState +// to the FIFO. +// +//***************************************************************************** +static void +FIFOTransmitSomeBytes(unsigned int uiMaxLength) +{ + unsigned int uiLength; + + if(g_sTXState.uiBytesRemaining > 0) + { + // + // There are some bytes in g_sTXState that still need to + // be sent. + // + uiLength = g_sTXState.uiBytesRemaining; + + if(uiLength > uiMaxLength) + { + // + // Clamp number of bytes to be sent to parameter uiMaxLength, + // which is 12 for the initial call with an empty FIFO and + // 9 for subsequent calls from the IRQ. + // + uiLength = uiMaxLength; + } + + // + // Send the data in a continuous write to the FIFO "register". + // + if(RF_DAUGHTER_TRF7960) + { + SSITRF79x0WriteContinuousStart(TRF79X0_FIFO_REG); + SSITRF79x0WriteContinuousData(g_sTXState.pucBuffer, uiLength); + SSITRF79x0WriteContinuousStop(); + } + + if(RF_DAUGHTER_TRF7970) + { + SSITRF79x0WriteContinuousData(g_sTXState.pucBuffer, uiLength); + SSITRF79x0WriteContinuousStop(); + } + + // + // Update g_sTXState to reflect what we just sent. + // + g_sTXState.pucBuffer += uiLength; + g_sTXState.uiBytesRemaining -= uiLength; + } +} + + + + +//***************************************************************************** +// +// Clears all IRQ causes from g_sIRQState. +// +// You will need to call either this function or TRF79x0IRQClearCauses() +// before a call to TRF79x0IRQWait() or TRF79x0IRQWaitTimeout() in order to +// clear sticky causes from the interrupt state. If a cause has been indicated +// before and is not cleared from the state then the wait functions will +// return immediately. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0IRQClearAll(void) +{ + // + // Clear the interrupt causes flags. + // + g_sIRQState.uiIrqCauses = 0; +} + +//***************************************************************************** +// +// Clears all given IRQ causes from g_sIRQState. +// +// \param causes is a bitfield of clauses to clear. This is a logical or of +// one or more terms of the form (1<<x) where x +// is a value from enumeration TRF79x0WaitCondition. +// +// You will need to call either this function or TRF79x0IRQClearAll() +// before a call to TRF79x0IRQWait() or TRF79x0IRQWaitTimeout(). +// +// \return None. +// +//***************************************************************************** +void +TRF79x0IRQClearCauses(unsigned int uiCauses) +{ + // + // Clear the requested interrupt causes. + // + g_sIRQState.uiIrqCauses &= ~uiCauses; +} + +//***************************************************************************** +// +// Returns the last indicated collision position and clears the collision +// position indicator. +// +// \return This function returns the collision position as returned by the +// TRF79x0 in registers 0xd and 0xe, or -1 if no collision was indicated since +// the last call to this function. +// +//***************************************************************************** +int +TRF79x0GetCollisionPosition(void) +{ + // + // If there were no collisions detected then just return. + // + if(!g_sIRQState.ucCollisionDetected) + { + return(-1); + } + + // + // Clear the collisions detected flag and return the number of collisions + // detected. + // + g_sIRQState.ucCollisionDetected = 0; + + return(g_sIRQState.uiCollisionPosition); +} + +//***************************************************************************** +// +// Enables the TRF79x0 IRQ handler. +// +// The interrupt handler needs and the processor interrupt to be enabled +// (IntMasterEnable() from DriverLib) in order for transmission and +// reception to work. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0InterruptEnable(void) +{ + // + // Enable interrupts on the pin assigned to the IRQ signal. + // + GPIOIntEnable(TRF79X0_IRQ_BASE, TRF79X0_IRQ_PIN); +} + +//***************************************************************************** +// +// Disables the TRF79x0 IRQ handler. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0InterruptDisable(void) +{ + // + // Disable interrupts on the pin assigned to the IRQ signal. + // + GPIOIntDisable(TRF79X0_IRQ_BASE, TRF79X0_IRQ_PIN); +} + +//***************************************************************************** +// +// TRF79x0DisableTransmitter - Disable the TRF79x0 Transmitter and Reset Fifo +// +//***************************************************************************** +void TRF79x0DisableTransmitter(void) +{ + // + // Register 00h. Chip Status Control + // + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG,0x00 | TRF7970A_5V_OPERATION); + + // + // Reset FIFO CMD + Dummy byte + // + TRF79x0ResetFifoCommand(); +} + +//***************************************************************************** +// +// stop, then start the decoders +// +//***************************************************************************** +void TRF797x0ResetDecoders(void) +{ + TRF79x0DirectCommand(TRF79X0_STOP_DECODERS_CMD); + TRF79x0DirectCommand(TRF79X0_RUN_DECODERS_CMD); + +} + +//***************************************************************************** +// +// +// +//***************************************************************************** +uint8_t* TRF79x0GetNFCBuffer(void) +{ + return g_fifo_buffer; +} + +//***************************************************************************** +// +// Waits for an abstract IRQ cause. +// +// \param eCondition is the IRQ cause to wait for. +// +// Waits until the IRQ handler indicates that the given abstract IRQ cause +// has been met. +// +// \return Returns 1. +// +//***************************************************************************** +int +TRF79x0IRQWait(unsigned long ulCondition) +{ + // + // Wait with no timeout. + // + return(TRF79x0IRQWaitTimeout(ulCondition, 0)); +} + +//***************************************************************************** +// +// Waits for an abstract IRQ cause or timeout. +// +// \param ulCondition is the IRQ cause to wait for. +// \param ulTimeout is the number of milliseconds to wait before a timeout +// occurs. +// +// Waits until the IRQ handler indicates that the given abstract IRQ cause +// has been met or the timeout occurs. If ulTimeout is 0 then this function +// will wait forever. +// +// \return This function returns 1 if the condition was reached or 0 if the +// function aborted due to the timeout being met. +// +//***************************************************************************** +int +TRF79x0IRQWaitTimeout(unsigned long ulCondition, unsigned long ulTimeout) +{ + unsigned long ulTime; + + // + // If timeout was not set or not reached, return true. + // + if(ulTimeout == 0) + { + return(1); + } + + ulTime = 0; + + while((g_sIRQState.uiIrqCauses & ulCondition) == 0) + { + if(ulTimeout == ulTime) + { + // + // Abort if timeout is set and reached. + // + break; + } + + // + // Delay 1ms and check again. + // + SysCtlDelay(g_ulDelayms); + + // + // Increment the loop count. + // + ulTime++; + } + + // + // If timeout was set and reached: return false. + // + if(ulTimeout == ulTime) + { + return 1; + } + else + { + return 0; + } +} + +//***************************************************************************** +// +// Issues a direct command on the TRF79x0. +// +// \param ucCommand is the command to be executed. Must be a valid command +// code between 0 and 0x1f. Definitions for command codes are given in +// trf79x0.h. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0DirectCommand(unsigned char ucCommand) +{ + SSITRF79x0WriteDirectCommand(ucCommand); +} + +//***************************************************************************** +// +// Issues a direct Reset FIFO command on the TRF79x0. +// +// \param ucCommand is the command to be executed. Must be a valid command +// code between 0 and 0x1f. Definitions for command codes are given in +// trf79x0.h. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0ResetFifoCommand(void) +{ + SSITRF79x0WriteResetFifoDirectCommand(TRF79X0_RESET_FIFO_CMD); +} + +//***************************************************************************** +// +//! Writes a single value to the TRF79x0 for address provided. +//! +//! \param ucAddress is the register address to write to. Must be between 0 +//! and 0x1f, inclusive. +//! \param ucData is the data byte to be written. +//! +//! \return None. +// +//***************************************************************************** +void +TRF79x0WriteRegister(unsigned char ucAddress, unsigned char ucData) +{ + SSITRF79x0WriteRegister(ucAddress, ucData); +} + + +//***************************************************************************** +// +// Initialize the mode and frequecy for the TRF79x0. +// Useful for hot switching modes +// +// \param eMode is the mode the TRF79x0 is operating in. +// Implemented: Future Implementation: +// BOARD_INIT P2P_ACTIVE_TARGET_MODE +// P2P_INITATIOR_MODE CARD_EMULATION_TYPE_A +// P2P_PASSIVE_TARGET_MODE CARD_EMULATION_TYPE_B +// +// \param eFrequency is the frequency to set the board to. +// Valid values are: +// FREQ_STAND_BY +// FREQ_106_KBPS +// FREQ_212_KBPS +// FREQ_424_KBPS +// +//***************************************************************************** +tStatus TRF79x0Init2(tTRF79x0TRFMode eMode, tTRF79x0Frequency eFrequency) +{ + uint8_t ui8RxVal; + uint8_t ui8RxValCont[2]; + + g_selected_mode = eMode; + g_selected_frequency = eFrequency; + + if (eMode == BOARD_INIT) { + + do { + // + // Soft Init Command + // + TRF79x0DirectCommand(TRF79X0_SOFT_INIT_CMD); + + // + // Idle Command + // + TRF79x0DirectCommand(TRF79X0_IDLE_CMD); + + // + // Delay 1ms + // NOTE: Sysctl delay takes 3 clock ticks to complete, + // thus 1ms = (clock/1000)/3 or clock/3000 + // + SysCtlDelay(g_ulDelayms * 1 ); + + // + // Register 09h. Modulator Control + // + ui8RxVal=TRF79x0ReadRegister(TRF79X0_MODULATOR_CONTROL_REG); + + } while (ui8RxVal != 0x91); + + // + // Register 09h. Modulator Control + // + // SYS_CLK (in this case 13.56 MHz) out optional, based on system req. + TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG, 0x00); + + // + // Register 0Bh. Regulator Control + // + TRF79x0WriteRegister(TRF79X0_REGULATOR_CONTROL_REG, 0x87); + + // + // Reset FIFO CMD + Dummy byte + // + TRF79x0ResetFifoCommand(); + + // + // Register 00h. Chip Status Control + // + // +5 V operation + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, 0x00 | TRF7970A_5V_OPERATION); + + // + // Register 0Dh. Interrupt Mask Register + // +// TRF79x0WriteRegister(TRF79X0_IRQ_MASK_REG, 0x3F);//NO Response IRQEnable + TRF79x0WriteRegister(TRF79X0_IRQ_MASK_REG, 0x3E); + + // + // Register 14h. FIFO IRQ Level + // + // RX High = 96 bytes , TX Low = 32 bytes + TRF79x0WriteRegister(TRF79X0_FIFO_IRQ_LEVEL_REG, 0x0F); + } else if (eMode == P2P_INITATIOR_MODE) { + // TODO - Understand why the SOFT Init at start up, does + // not allow to send packets to reader + // + // Soft Init Command + // + TRF79x0DirectCommand(TRF79X0_SOFT_INIT_CMD); + + // + // Idle Command + // + TRF79x0DirectCommand(TRF79X0_IDLE_CMD); + + // Register 00h. Chip Status Control + // RF output active, +5 V operation + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, 0x02 | TRF7970A_5V_OPERATION); + + // Check if there an external RF Field + TRF79x0DirectCommand(TRF79X0_TEST_EXTERNAL_RF_CMD); + + // + // Delay 50uS + // + SysCtlDelay((g_ulDelayms/1000) * 50); + + ui8RxVal=TRF79x0ReadRegister(TRF79X0_RSSI_LEVEL_REG); + + // If the External RF Field is 0x00, we continue else we return fail + if ((ui8RxVal & 0x3F) != 0x00) { + //UARTprintf("Initiator Mode field disabled. RSSI: 0x%x \n", + //ui8RxVal); + + // Register 00h. Chip Status Control + // RF output de-activated, +5 V operation + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, 0x00 | TRF7970A_5V_OPERATION); + return STATUS_FAIL; + } + + // + // Register 09h. Modulator Control + // + // SYS_CLK (in this case 13.56 MHz) out optional, based on system req. + TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG, 0x00); + + // + // Register 0Bh. Regulator Control + // + TRF79x0WriteRegister(TRF79X0_REGULATOR_CONTROL_REG, 0x01); + + // + // Register 14h. FIFO IRQ Level + // + // RX High = 96 bytes , TX Low = 32 bytes + TRF79x0WriteRegister(TRF79X0_FIFO_IRQ_LEVEL_REG, 0x0F); + + // + // Register 01h. Chip Status Control + // + if (eFrequency == FREQ_106_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x31); + } else if (eFrequency == FREQ_212_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x1A); + } else if (eFrequency == FREQ_424_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x1B); + } + + // + // Register 0Ah. RX Special Settings + // + // Turn off transmitter, +5 V operation + TRF79x0WriteRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG, 0x2F); + + // + // Register 16h. NFC Low Detection Level + // + TRF79x0WriteRegister(TRF79X0_NFC_LO_FIELD_LEVEL_REG, 0x83); + + // + // Register 18h. NFC Target level + // +// TRF79x0WriteRegister(TRF79X0_NFC_TARGET_LEVEL_REG, 0x07); + + // + // Register 00h. Chip Status Control + // + // Turn off transmitter, +5 V operation + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, 0x20 |TRF7970A_5V_OPERATION); + + // + // Guard Time Delay (GT_F) - 20 mS - Incremented to 30 mS due to the GS3. + // + SysCtlDelay(g_ulDelayms * 30); + + } else if (eMode == P2P_PASSIVE_TARGET_MODE || eMode == P2P_ACTIVE_TARGET_MODE) { + // + // Soft Init Command + // + TRF79x0DirectCommand(TRF79X0_SOFT_INIT_CMD); + + // + // Idle Command + // + TRF79x0DirectCommand(TRF79X0_IDLE_CMD); + + // + // Disable Decoder Command + // + TRF79x0DirectCommand(TRF79X0_STOP_DECODERS_CMD); + + // + // Register 01h. ISO Control Register + // + if (eFrequency == FREQ_106_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x21); + } else if (eFrequency == FREQ_212_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x22); + } else if (eFrequency == FREQ_424_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x23); + } + + // + // Register 09h. Modulator Control + // + // SYS_CLK Disabled, based on system req. + TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG, 0x00); + + // + // Register 0Ah. RX Special Settings + // +// TRF79x0WriteRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG, 0x30); + + // + // Register 0Bh. Regulator Control + // + TRF79x0WriteRegister(TRF79X0_REGULATOR_CONTROL_REG, 0x01); + + // + // Register 14h. FIFO IRQ Level + // + // RX High = 96 bytes , TX Low = 32 bytes + TRF79x0WriteRegister(TRF79X0_FIFO_IRQ_LEVEL_REG, 0x0F); + + // + // Register 16h. NFC Low Detection Level + // + TRF79x0WriteRegister(TRF79X0_NFC_LO_FIELD_LEVEL_REG, 0x83); + + // + // Register 18h. NFC Target level + // + TRF79x0WriteRegister(TRF79X0_NFC_TARGET_LEVEL_REG, 0x07); + + // + // Register 00h. Chip Status Control + // + // RF output active, +5 V operation + TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG, 0x20 | TRF7970A_5V_OPERATION); + + // + // Read IRQ Register & Collision Register to clear data. + // + TRF79x0ReadRegisterContinuous(TRF79X0_IRQ_STATUS_REG, ui8RxValCont, 2); + + // + // Enable Decoder Command + // + TRF79x0DirectCommand(TRF79X0_RUN_DECODERS_CMD); + } + + return STATUS_SUCCESS; +} + +//***************************************************************************** +// +// Write Fifo - used for NFC +// +//***************************************************************************** +tStatus TRF79x0WriteFIFO(uint8_t *pui8Buffer, tTRF79x0CRC eCRCBit, + uint8_t ui8Length) +{ + tStatus eStatus; + tTRF79x0IRQFlag irq_flag = IRQ_STATUS_IDLE; + uint8_t remaining_bytes = 0; + uint8_t ui8FifoStatusLength = 0; + uint8_t ui8PayloadLength = 0; + uint8_t pui8IRQBuffer[2]; + + if (ui8Length > 127) { + ui8PayloadLength = 127; + } else { + ui8PayloadLength = ui8Length; + } + + remaining_bytes = ui8Length - ui8PayloadLength; + + if(g_selected_mode == P2P_ACTIVE_TARGET_MODE) + { + // + // Register 01h. ISO Control Register + // + if (g_selected_frequency == FREQ_106_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x31); + } else if (g_selected_frequency == FREQ_212_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x32); + } else if (g_selected_frequency == FREQ_424_KBPS) { + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x33); + } + } + + if (IRQ_IS_SET()) + { + // + // Read IRQ Register + // + TRF79x0ReadRegisterContinuous(TRF79X0_IRQ_STATUS_REG, pui8IRQBuffer, 2); + } + + SSITRF79x0WritePacket(pui8Buffer, eCRCBit, ui8Length, ui8PayloadLength, \ + true); + + while (irq_flag != IRQ_STATUS_TX_COMPLETE) { + // Workaround for Type A commands - check the IRQ within 10 mS to + // refill FIFO + if(g_selected_mode == CARD_EMULATION_TYPE_A) + irq_flag = TRF79x0IRQHandler(10); + else + { + // No workaround needed, implement a longer timeout, allowing for + // FIFO IRQ to handle the FIFO levels + irq_flag = TRF79x0IRQHandler(100); + } + + if (irq_flag == IRQ_STATUS_PROTOCOL_ERROR) { + eStatus = STATUS_FAIL; + break; + } else if (irq_flag == IRQ_STATUS_TX_COMPLETE) { + if(g_selected_mode == P2P_ACTIVE_TARGET_MODE) + { + // + // Delay 1uS + // + SysCtlDelay((g_ulDelayms/1000) * 1); + + // + // Register 01h. ISO Control Register + // + if(g_selected_frequency == FREQ_106_KBPS) + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x21); + else if(g_selected_frequency == FREQ_212_KBPS) + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x22); + else if(g_selected_frequency == FREQ_424_KBPS) + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, 0x23); + } + eStatus = STATUS_SUCCESS; + } else if ((irq_flag == IRQ_STATUS_FIFO_HIGH_OR_LOW + || irq_flag == IRQ_STATUS_TIME_OUT) && remaining_bytes > 0) { + // Modify the pointer to point to the next address of data for + // payload larger than 127 bytes + pui8Buffer = pui8Buffer + ui8PayloadLength; + + ui8FifoStatusLength=TRF79x0ReadRegister(TRF79X0_FIFO_STATUS_REG); + + // Check if there are more remaining bytes than available spots on + // the TRF7970 + if (remaining_bytes > (127 - ui8FifoStatusLength)) { + // If there are more bytes than available then payload length + //is the (127 - ui8FifoStatusLength) + ui8PayloadLength = (127 - ui8FifoStatusLength); + } else { + ui8PayloadLength = remaining_bytes; + } + + remaining_bytes = remaining_bytes - ui8PayloadLength; + + SSITRF79x0WritePacket(pui8Buffer, eCRCBit, ui8Length, \ + ui8PayloadLength, false); + } + } + + return eStatus; +} + +//***************************************************************************** +// +// IRQ Handler +// +// NOTE: currently TimerSet, TimerDisable, and TimerInteruptHandler must be +// implemented by the user. +// +//***************************************************************************** +extern void TimerSet(uint16_t timeout_ms, uint8_t * timeout_flag); + +tTRF79x0IRQFlag +TRF79x0IRQHandler(uint16_t ui16TimeOut) +{ + tTRF79x0IRQFlag eIRQStatus = IRQ_STATUS_IDLE; + uint8_t pui8IRQBuffer[2]; + uint8_t pui8TargetProtocol[2]; + uint8_t ui8FifoStatusLength; + uint8_t ui8FifoIndex = 0; + uint8_t ui8PacketLength = 0; + + //volatile uint8_t x; + + if (IRQ_IS_SET()) + { + g_irq_flag = 0x01; + } + else + { + g_irq_flag = 0x00; + // + // Initialize a ui16TimeOut timeout + // + TimerSet(ui16TimeOut, (uint8_t*) &g_time_out_flag); + + } + + // + // Check if the IRQ flag has been set + // + while (g_irq_flag == 0x00 && g_time_out_flag == 0x00) { + ; + // + // Enable Low Power Mode 0 + // + //__bis_SR_register(LPM0_bits); + } + + // + // Disable Timer + // + TimerDisable(TIMER0_BASE, TIMER_A); + + if (g_time_out_flag == 0x01) { + //MCU_rssiDisplay(0); + eIRQStatus = IRQ_STATUS_TIME_OUT; + } else { + + TRF79x0ReadRegisterContinuous(TRF79X0_NFC_TARGET_PROTOCOL_REG, \ + pui8TargetProtocol, 2); + + // + // Read IRQ Register + // + TRF79x0ReadRegisterContinuous(TRF79X0_IRQ_STATUS_REG, pui8IRQBuffer, 2); + + if (pui8IRQBuffer[0] & IRQ_STATUS_FIFO_HIGH_OR_LOW) { + if (pui8IRQBuffer[0] & IRQ_STATUS_RX_COMPLETE) { + g_fifo_bytes_received = 0; + // + // Read the FIFO status and FIFO into g_nfc_buffer + // + ui8FifoStatusLength=TRF79x0ReadRegister(TRF79X0_FIFO_STATUS_REG); + + ui8FifoIndex = 0; + + while ((ui8FifoStatusLength > 0) && + (g_fifo_bytes_received < NFC_FIFO_SIZE)) + { + + // + // Update the received bytes + // + g_fifo_bytes_received += ui8FifoStatusLength; + #ifdef DEBUG + //DebugPrintf("%d\n",g_fifo_bytes_received); + #endif + + // + // Read the FIFO Data + // + TRF79x0ReadRegisterContinuous(TRF79X0_FIFO_REG, + &g_fifo_buffer[ui8FifoIndex], ui8FifoStatusLength); + + ui8PacketLength = g_fifo_buffer[0]; + + // + // Update ui8FifoIndex + // + ui8FifoIndex = ui8FifoIndex + ui8FifoStatusLength; + + if (!IRQ_IS_SET()) + { + g_irq_flag = 0; + } + + // + // Type F - P2P Workaround + // + if((g_selected_mode == P2P_PASSIVE_TARGET_MODE) || + (g_selected_mode == P2P_INITATIOR_MODE)) + { + // + // Check if we have received all the bytes defined in + // the first packet. + // + if(g_fifo_buffer[0] == g_fifo_bytes_received) + { + eIRQStatus = IRQ_STATUS_RX_COMPLETE; + break; + } + // + // If we have not read all the bytes, then every 1 mS + // go read out the FIFO status register to ensure we do + // not get an overflow flag. + // + else + { + // + // Initialize a 1 mS timeout + // + ui16TimeOut = 0x01; + TimerSet(ui16TimeOut, (uint8_t*) &g_time_out_flag); + + while(g_irq_flag == 0x00 && g_time_out_flag == 0x00) + { + // + // Enable Low Power Mode 0 + // + // __bis_SR_register(LPM0_bits); + } + + // + // Disable Timer + // + TimerDisable(TIMER0_BASE, TIMER_A); + } + + } + else + { + while ((g_irq_flag == 0) && ( + (uint8_t) g_fifo_bytes_received != + ui8PacketLength)) + { + // + // Enable Low Power Mode 0 + // + //__bis_SR_register(LPM0_bits); + } + } + + TRF79x0ReadRegisterContinuous(TRF79X0_IRQ_STATUS_REG, + pui8IRQBuffer, 2); + + // + // Read the FIFO status and FIFO into g_nfc_buffer + // + ui8FifoStatusLength = + TRF79x0ReadRegister(TRF79X0_FIFO_STATUS_REG); + // + // Mask off the lower 7 bits. + // + ui8FifoStatusLength &= 0x7F; + } + + //TRF79x0ResetFifoCommand(); + + eIRQStatus = IRQ_STATUS_RX_COMPLETE; + } + else if (pui8IRQBuffer[0] & IRQ_STATUS_TX_COMPLETE) + { + eIRQStatus = IRQ_STATUS_FIFO_HIGH_OR_LOW; + } + } + else if (pui8IRQBuffer[0] == IRQ_STATUS_RX_COMPLETE) + { + + // + // Read the FIFO status and FIFO into g_nfc_buffer + // + ui8FifoStatusLength=TRF79x0ReadRegister(TRF79X0_FIFO_STATUS_REG); + + if (ui8FifoStatusLength != 0) { + // + // Read the FIFO Data + // + TRF79x0ReadRegisterContinuous(TRF79X0_FIFO_REG, g_fifo_buffer, + ui8FifoStatusLength); + + g_fifo_bytes_received = ui8FifoStatusLength; + } else { + TRF79x0Init2(g_selected_mode, g_selected_frequency); + return IRQ_STATUS_IDLE; + } + + // Check if the selected_mode corresponds to the command read in + // the command + if ((pui8TargetProtocol[0] == 0xC9 + && g_selected_mode == CARD_EMULATION_TYPE_A) + || (pui8TargetProtocol[0] == 0xC5 + && g_selected_mode == CARD_EMULATION_TYPE_B) + || (pui8TargetProtocol[0] == 0xD2 + && g_selected_mode == P2P_PASSIVE_TARGET_MODE + && g_selected_frequency == FREQ_212_KBPS) + || (pui8TargetProtocol[0] == 0xD3 + && g_selected_mode == P2P_PASSIVE_TARGET_MODE + && g_selected_frequency == FREQ_424_KBPS) + || (pui8TargetProtocol[0] == 0xD2 + && g_selected_mode == P2P_ACTIVE_TARGET_MODE + && g_selected_frequency == FREQ_212_KBPS) + || (pui8TargetProtocol[0] == 0xD3 + && g_selected_mode == P2P_ACTIVE_TARGET_MODE + && g_selected_frequency == FREQ_424_KBPS) + || (g_selected_mode == P2P_INITATIOR_MODE)) + { + eIRQStatus = IRQ_STATUS_RX_COMPLETE; + if(g_selected_mode == P2P_INITATIOR_MODE || + g_selected_mode == P2P_PASSIVE_TARGET_MODE) + // + // 500 microsecond // TR0 + // + SysCtlDelay(g_ulDelayms / 2); + } + else + TRF79x0Init2(g_selected_mode, g_selected_frequency); + + } else if (pui8IRQBuffer[0] & IRQ_STATUS_COLLISION_AVOID_FINISHED) { + eIRQStatus = IRQ_STATUS_COLLISION_AVOID_FINISHED; + } else if (pui8IRQBuffer[0] & IRQ_STATUS_RX_COMPLETE) { + // Handle the case for P2P Initiator Mode where IRQ is triggered + // with value 0xC0 - TODO + if(pui8IRQBuffer[0] & IRQ_STATUS_TX_COMPLETE) + { + + } + else if(pui8IRQBuffer[0] & IRQ_STATUS_PROTOCOL_ERROR) + { + TRF79x0Init2(g_selected_mode, g_selected_frequency); + } + else + { + // + // Read the FIFO status and FIFO into g_nfc_buffer + // + ui8FifoStatusLength = + TRF79x0ReadRegister(TRF79X0_FIFO_STATUS_REG); + + TRF79x0ResetFifoCommand(); + } + } + else if (pui8IRQBuffer[0] & IRQ_STATUS_PROTOCOL_ERROR + || pui8IRQBuffer[0] & IRQ_STATUS_COLLISION_ERROR) + { + eIRQStatus = IRQ_STATUS_PROTOCOL_ERROR; + TRF79x0Init2(g_selected_mode, g_selected_frequency); + } + else if (pui8IRQBuffer[0] & IRQ_STATUS_TX_COMPLETE) + { + + // Reset FIFO CMD + Dummy byte + TRF79x0ResetFifoCommand(); + + eIRQStatus = IRQ_STATUS_TX_COMPLETE; + } + else if (pui8IRQBuffer[0] & IRQ_STATUS_RF_FIELD_CHANGE) + { + + eIRQStatus = IRQ_STATUS_RF_FIELD_CHANGE; + } + + } + + // + // Reset Global Flags + // + g_irq_flag = 0x00; + g_time_out_flag = 0x00; + + return eIRQStatus; +} + +//***************************************************************************** +// +// Writes a sequence of values to the TRF79x0 starting at the address +// provided. +// +// \param ucAddress is the register address to start the write at. Must be +// between 0 and 0x1f, inclusive. +// \param pucData is a pointer to the data buffer to be written. +// \param uiLength is the length of the buffer and number of bytes to write. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0WriteRegisterContinuous(unsigned char ucAddress, unsigned char *pucData, + unsigned int uiLength) +{ + SSITRF79x0WriteContinuousStart(ucAddress); + SSITRF79x0WriteContinuousData(pucData, uiLength); + SSITRF79x0WriteContinuousStop(); +} + +//***************************************************************************** +// +// Reads IRQ status value from TRF79x0. +// +// This function reads the TRF79x0 IRQ status register 0x0c and returns its +// contents. This will make the TRF79x0 release its interrupt request. +// +// \return Returns the IRQ status +// +//***************************************************************************** +unsigned char +TRF79x0ReadIRQStatus(void) +{ + return(SSITRF79x0ReadIRQStatus()); +} + +//***************************************************************************** +// +// Reads a single value from TRF79x0 at the address provided. +// +// \param ucAddress is the register address to read from. Must be between 0 +// and 0x1f, inclusive. +// +// \return Returns the value that was stored in the given register. +// +//***************************************************************************** +unsigned char +TRF79x0ReadRegister(unsigned char ucAddress) +{ + return(SSITRF79x0ReadRegister(ucAddress)); +} + +//***************************************************************************** +// +// Reads a sequence of values from the TRF79x0 starting at the address +// provided. +// +// \param ucAddress is the register address to start the read at. Must be +// between 0 and 0x1f, inclusive. +// \param pucData is a pointer to the data buffer to store the read bytes into. +// \param uiLength is the length of the buffer and number of bytes to read. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0ReadRegisterContinuous(unsigned char ucAddress, unsigned char *pucData, + unsigned int uiLength) +{ + SSITRF79x0ReadContinuousStart(ucAddress); + SSITRF79x0ReadContinuousData(pucData, uiLength); + SSITRF79x0ReadContinuousStop(); +} + +//***************************************************************************** +// +// Writes a sequence of values to the FIFO of the TRF79x0. +// +// \param pucData is a pointer to the data buffer to be written. +// \param length is the length of the buffer and number of bytes to write. +// +// This function sets up g_sTXState for the write operation to the FIFO and +// sends the first chunk of up to 12 bytes. If more bytes need to be written +// this will be handled by the IRQ handler, which therefore must be enabled. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0FIFOWrite(unsigned char const *pucData, unsigned int uiLength) +{ + // + // Set up TX state to send the buffer. + // + g_sTXState.pucBuffer = pucData; + g_sTXState.uiBytesRemaining = uiLength; + + // + // This will start transmission and write the first couple byte (12 at + // most) to the FIFO. If more bytes are to be written then the IRQ handler + // will pick up and send the remainder. + // + FIFOTransmitSomeBytes(12); + return; +} + +//***************************************************************************** +// +// Writes to the FIFO, starting a transmission by the RF front end. +// +// \param pucData is a pointer to the data buffer to be written. +// \param uiLength is the number of bytes to send. +// \param uiBits is the additional number of bits to send. +// +// This function sets up the TX length byte registers 0x1D and 0x1E with +// the given bytes and bits and then calls TRF79x0FIFOWrite() to initiate the +// write to the FIFO. +// If the RF front end has been enabled for transmission with +// TRF79x0DirectCommand() with parameter \b TRF79X0_TRANSMIT_NO_CRC_CMD or +// \b TRF79X0_TRANSMIT_CRC_CMD this function call will start the radio +// transmission. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0Transmit(unsigned char const *pucData, unsigned int uiLength, + unsigned int uiBits) +{ + unsigned char pucLengthRegs[2]; + + // + // Prepare the length to be written into the FIFO for registers 0x1D and + // 0x1E. + // + pucLengthRegs[0] = (uiLength >> 4) & 0xff; + pucLengthRegs[1] = (uiLength & 0xf) << 4; + + if(uiBits > 0) + { + // + // Last byte is incomplete. + // + pucLengthRegs[1] |= ((uiBits & 0x7) << 1) | 1; + + // + // This is an additional byte, so increase the number of bytes for the + // purpose of SPI transmission below by 1. + // + uiLength++; + } + + // + // The data from pucLengthRegs is written to registers 0x1D and 0x1E + // in continuous mode. In principle the continuous mode could simply + // be kept active in order to write to the FIFO (starts at 0x1F). However + // there is a necessary workaround when only one byte needs to be + // transmitted (see SLOA140). Also stopping the continuous write here and + // separately enabling it in TRF79x0WriteFIFO makes for more logical + // function separation. + // + if(RF_DAUGHTER_TRF7960) + { + SSITRF79x0WriteContinuousStart(TRF79X0_TX_LENGTH_BYTE1_REG); + SSITRF79x0WriteContinuousData(pucLengthRegs, sizeof(pucLengthRegs)); + SSITRF79x0WriteContinuousStop(); + } + + if(RF_DAUGHTER_TRF7970) + { + SSITRF79x0WriteContinuousData(pucLengthRegs, sizeof(pucLengthRegs)); + } + + TRF79x0FIFOWrite(pucData, uiLength); +} + +//***************************************************************************** +// +// Sets up reception from the FIFO +// +// \param pucData is a pointer to the data buffer to receive the data. +// \param puiLength is a pointer to the length of the \e pucData buffer in +// bytes. +// +// This function sets up g_sRXState for the read operation from the FIFO. The +// actual reading will be handled by the IRQ handler, which therefore must +// be enabled. When the function returns the \e puiLength parameter will +// contain the number of bytes that were actually received. These values are +// updated asynchronously by the IRQ handler. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0Receive(unsigned char *pucData, unsigned int *puiLength) +{ + unsigned int uiMaxLength; + + uiMaxLength = *puiLength; + + // + // Already received: 0 bytes. + // + *puiLength = 0; + + // + // The uiMaxLength member is the ultimate deciding factor on whether the + // IRQ receiver is enabled. So set it to 0 first and only set it to its + // final value when the other members are set. + // + g_sRXState.uiMaxLength = 0; + + g_sRXState.pucBuffer = pucData; + g_sRXState.puiLength = puiLength; + g_sRXState.uiMaxLength = uiMaxLength; +} + +//***************************************************************************** +// +// Sets up reception from the FIFO with wait time out feature +// +// \param pucData is a pointer to the data buffer to receive the data. +// \param puiLength is a pointer to the length of the \e pucData buffer in +// bytes. +// +// This function sets up g_sRXState for the read operation from the FIFO. The +// actual reading will be handled by the IRQ handler, which therefore must +// be enabled. When the function returns the \e puiLength parameter will +// contain the number of bytes that were actually received. These values are +// updated asynchronously by the IRQ handler. +// +// \return None. +// +//***************************************************************************** +void +TRF79x0ReceiveAgain(unsigned char *pucRXBuf, unsigned int *puiRXLen) +{ + if((pucRXBuf != 0) && (puiRXLen != 0) && (*puiRXLen > 0)) + TRF79x0Receive(pucRXBuf, puiRXLen); + + TRF79x0IRQWaitTimeout(TRF79X0_WAIT_RXEND, TRF79X0_RX_TIMEOUT); + + // + // Abort receive job, e.g. if timeout reached. + // + g_sRXState.uiMaxLength = 0; +} + +//***************************************************************************** +// +// +// +//***************************************************************************** +void +TRF79x0ReceiveEnd(void) +{ + TRF79x0IRQClearCauses(TRF79X0_WAIT_RXEND); + + // + // Abort receive job, e.g. if timeout reached. + // + g_sRXState.uiMaxLength = 0; + + TRF79x0ResetFifoCommand(); +} + +//***************************************************************************** +// +// Coordinated transmission and reception function. +// +// \param pucTXBuf is a pointer to the data buffer. +// \param uiTXLen is the number of full bytes to send. +// \param uiTXBits is the number of additional bits to send +// \param pucRXBuf is a pointer to a data buffer to receive data. If this is +// \b 0 then no reception will take place. +// \param puiRXLen is pointer that inputs the length of \e pucRXBuf and outputs +// the number of bytes that were actually received. +// \param puiRXBits is unused. +// \param uiFlags is a bitfield of uiFlags to modify the transceiver operation. +// Should contain at least \b TRF79X0_TRANSCEIVE_NO_CRC, +// \b TRF79X0_TRANSCEIVE_RX_CRC, \b TRF79X0_TRANSCEIVE_TX_CRC or +// \b TRF79X0_TRANSCEIVE_CRC. These values indicate whether a CRC should be +// added when transmitting (\b TRF79X0_TRANSCEIVE_TX_CRC or +// \b TRF79X0_TRANSCEIVE_CRC) and whether it should be checked when receiving +// (\b TRF79X0_TRANSCEIVE_RX_CRC or \b TRF79X0_TRANSCEIVE_CRC). +// +// This function calls, in order: +// +// - TRF79x0WriteRegister() to set up reception with/without CRC (in +// register 0x1), +// - TRF79x0DirectCommand() with \b TRF79X0_RESET_FIFO_CMD to clear the FIFO, +// - TRF79x0DirectCommand() with \b TRF79X0_TRANSMIT_CRC_CMD or +// \b TRF79X0_TRANSMIT_NO_CRC_CMD to prepare transmission with/without CRC, +// - TRF79x0IRQClearAll() to clear the IRQ state, +// - TRF79x0GetCollisionPosition() to clear the stored collision position, +// - TRF79x0Receive() to set up reception (if enabled), +// - TRF79x0Transmit() to set up transmission, +// - TRF79x0IRQWaitTimeout() with \b TRF79X0_WAIT_TXEND to wait for the +// end of transmission and +// - TRF79x0IRQWaitTimeout() with \b TRF79X0_WAIT_RXEND to wait for the +// end of reception (if enabled). +// +// The uiFlags and puiRXBits parameters offer for future, source-compatible +// extensions such as integrated collision handling (which would result in +// incomplete byte reception). +// +// \return None. +// +//***************************************************************************** +void +TRF79x0Transceive(unsigned char const *pucTXBuf, unsigned int uiTXLen, + unsigned int uiTXBits, unsigned char *pucRXBuf, + unsigned int *puiRXLen, unsigned int *puiRXBits, + unsigned int uiFlags) +{ + int iRXEnabled; + unsigned char ucISOState; + unsigned char ucBuf[30]; + + ucISOState = TRF79x0ReadRegister(TRF79X0_ISO_CONTROL_REG); + + if(uiFlags & TRF79X0_TRANSCEIVE_RX_CRC) + { + // + // Receive with CRC. + // + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, + ucISOState & ~TRF79X0_ISO_CONTROL_RX_CRC_N); + } + else + { + // + // Receive without CRC. + // + TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG, + ucISOState | TRF79X0_ISO_CONTROL_RX_CRC_N); + } + + if(RF_DAUGHTER_TRF7960) + { + TRF79x0DirectCommand(TRF79X0_RESET_FIFO_CMD); + + if(uiFlags & TRF79X0_TRANSCEIVE_TX_CRC) + { + // + // Transmit with CRC. + // + TRF79x0DirectCommand(TRF79X0_TRANSMIT_CRC_CMD); + } + else + { + // + // Transmit without CRC. + // + TRF79x0DirectCommand(TRF79X0_TRANSMIT_NO_CRC_CMD); + } + + // + // Disable any possible old receive job. + // + g_sRXState.uiMaxLength = 0; + + // + // Clear all IRQ causes. + // + TRF79x0IRQClearAll(); + + // + // Clear stored collision position. + // + TRF79x0GetCollisionPosition(); + + // + // If receive is enabled, set up receive job. + // + iRXEnabled = 0; + + if((pucRXBuf != 0) && (puiRXLen != 0) && (*puiRXLen > 0)) + { + TRF79x0Receive(pucRXBuf, puiRXLen); + iRXEnabled = 1; + } + + // + // Writing the FIFO starts the transmission. This function will return + // after writing up to 12 bytes with the remaining bytes to be written + // by the interrupt handler. + // + TRF79x0Transmit(pucTXBuf, uiTXLen, uiTXBits); + + // + // Wait for the interrupt handler to signal the end of transmission + // with no further FIFO loading. This IRQ should always happen, so + // no timeout necessary. However, for robustness reasons: Use the RX + // timeout. + // + TRF79x0IRQWaitTimeout(TRF79X0_WAIT_TXEND, TRF79X0_RX_TIMEOUT); + + // + // If receive is enabled, wait for receive end. + // + if(iRXEnabled) + { + TRF79x0IRQWaitTimeout(TRF79X0_WAIT_RXEND, TRF79X0_RX_TIMEOUT); + + // + // Abort receive job, e.g. if timeout reached. + // + g_sRXState.uiMaxLength = 0; + } + } + + if(RF_DAUGHTER_TRF7970) + { + // + // Prepare SELECT command + // + ucBuf[0] = TRF79X0_CONTROL_CMD | TRF79X0_RESET_FIFO_CMD; + + if(uiFlags & TRF79X0_TRANSCEIVE_TX_CRC) + { + // + // Transmit with CRC. + // + ucBuf[1] = TRF79X0_CONTROL_CMD | TRF79X0_TRANSMIT_CRC_CMD; + } + else + { + // + // Transmit without CRC. + // + ucBuf[1] = TRF79X0_CONTROL_CMD | TRF79X0_TRANSMIT_NO_CRC_CMD; + } + + // + // Disable any possible old receive job. + // + g_sRXState.uiMaxLength = 0; + + // + // Clear all IRQ causes. + // + TRF79x0IRQClearAll(); + + // + // Clear stored collision position. + // + TRF79x0GetCollisionPosition(); + + // + // If receive is enabled, set up receive job. + // + iRXEnabled = 0; + + if((pucRXBuf != 0) && (puiRXLen != 0) && (*puiRXLen > 0)) + { + TRF79x0Receive(pucRXBuf, puiRXLen); + iRXEnabled = 1; + } + + // + // Writing the FIFO starts the transmission. This function will return + // after writing up to 12 bytes with the remaining bytes to be written + // by the interrupt handler. + // + + // + // Look into what is ucBuf being used for. + // + ucBuf[2] = 0x3D; + + // + // Send the data in a continuous write to the FIFO "register". + // + SSITRF79x0WriteDirectContinuousStart(); + SSITRF79x0WriteContinuousData(ucBuf, 3); + TRF79x0Transmit(pucTXBuf, uiTXLen, uiTXBits); + + // + // Wait for the interrupt handler to signal the end of transmission + // with no further FIFO loading. This IRQ should always happen, so + // no timeout necessary. However, for robustness reasons: Use the RX + // timeout. + // + TRF79x0IRQWaitTimeout(TRF79X0_WAIT_TXEND, TRF79X0_RX_TIMEOUT); + + // + // If receive is enabled, wait for receive end. + // + if(iRXEnabled) + { + TRF79x0IRQWaitTimeout(TRF79X0_WAIT_RXEND, TRF79X0_RX_TIMEOUT); + + // + // Abort receive job, e.g. if timeout reached. + // + g_sRXState.uiMaxLength = 0; + } + } +} diff --git a/nfclib/trf79x0.h b/nfclib/trf79x0.h new file mode 100644 index 0000000..159bec7 --- /dev/null +++ b/nfclib/trf79x0.h @@ -0,0 +1,400 @@ +//***************************************************************************** +// +// trf79x0.h - Header file for the TI TRF79X0 driver +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __TRF79X0_H__ +#define __TRF79X0_H__ + +#include "types.h" + +//***************************************************************************** +// +// Definitions for different interrupt status bits. +// +//***************************************************************************** +#define TX_FIFO_ALMOST_EMPTY 0xA0 +#define TX_COMPLETE 0x80 +#define RX_FIFO_ALMOST_FULL 0x60 +#define RX_COMPLETE 0x40 +#define COLLISION_DETECTED 0x02 + +//***************************************************************************** +// +// Timeout to apply while waiting for reception, this is expressed in +// milliseconds. +// +// For a more accurate timeout indication you can program the no-response +// timer in the TRF7960 and must enable the no-response interrupt. +// +//***************************************************************************** +#define TRF7960_RX_TIMEOUT 10 + +//***************************************************************************** +// +// An enum defining the various daughter boards that can be attached to the +// development board. +// +//***************************************************************************** +typedef enum +{ + RF_DAUGHTER_NONE = 0, + RF_DAUGHTER_TRF7960ATB = 1, + RF_DAUGHTER_TRF7970ATB = 2, + RF_DAUGHTER_TRF7970ABP = 3, + RF_DAUGHTER_UNKNOWN = 0xFFFF +} +tRFDaughterBoard; + +extern tRFDaughterBoard g_eRFDaughterType; +#define NFC_NONE 0 +#define NFC_CARD_EMU_TAG4A 1 +#define NFC_CARD_EMU_TAG4B 2 + +extern unsigned char g_ucNfcWorkMode; + +//***************************************************************************** +// +// IRQ Status Register (0x0C) for NFC and Card Emulation Operation +// +//***************************************************************************** +#define RF_FIELD_CHANGE 0x04 +#define SDD_COMPLETED 0x08 + +//***************************************************************************** +// +// TRF79X0 Register Definitions. +// +//***************************************************************************** +#define TRF79X0_CHIP_STATUS_CTRL_REG 0x00 +#define TRF79X0_ISO_CONTROL_REG 0x01 +#define TRF79X0_ISO14443B_OPTIONS_REG 0x02 +#define TRF79X0_ISO14443A_OPTIONS_REG 0x03 +#define TRF79X0_TX_TIMER_EPC_HIGH 0x04 +#define TRF79X0_TX_TIMER_EPC_LOW 0x05 +#define TRF79X0_TX_PULSE_LENGTH_CTRL_REG 0x06 +#define TRF79X0_RX_NO_RESPONSE_WAIT_REG 0x07 +#define TRF79X0_RX_WAIT_TIME_REG 0x08 +#define TRF79X0_MODULATOR_CONTROL_REG 0x09 +#define TRF79X0_RX_SPECIAL_SETTINGS_REG 0x0A +#define TRF79X0_REGULATOR_CONTROL_REG 0x0B +#define TRF79X0_IRQ_STATUS_REG 0x0C +#define TRF79X0_IRQ_MASK_REG 0x0D +#define TRF79X0_COLLISION_POSITION_REG 0x0E +#define TRF79X0_RSSI_LEVEL_REG 0x0F +#define TRF79X0_RAM_START_ADDRESS_REG 0x10 +#define TRF797X0_SPECIAL_FUNC_1_REG 0x10 +#define TRF797X0_SPECIAL_FUNC_2_REG 0x11 +#define TRF79X0_FIFO_IRQ_LEVEL_REG 0x14 +#define TRF79X0_NFC_LO_FIELD_LEVEL_REG 0x16 +#define TRF79X0_NFC_ID_REG 0x17 +#define TRF79X0_NFC_TARGET_LEVEL_REG 0x18 +#define TRF79X0_NFC_TARGET_PROTOCOL_REG 0x19 +#define TRF79X0_TEST_SETTING1_REG 0x1A +#define TRF79X0_TEST_SETTING2_REG 0x1B +#define TRF79X0_FIFO_STATUS_REG 0x1C +#define TRF79X0_TX_LENGTH_BYTE1_REG 0x1D +#define TRF79X0_TX_LENGTH_BYTE2_REG 0x1E +#define TRF79X0_FIFO_REG 0x1F + +//***************************************************************************** +// +// TRF79X0 TRF79X0_CHIP_STATUS_CTRL_REG Register Definitions. +// +//***************************************************************************** +#define TRF79X0_STATUS_CTRL_DIRECT 0x40 +#define TRF79X0_STATUS_CTRL_RF_ON 0x20 +#define TRF79X0_STATUS_CTRL_RF_PWR_HALF 0x10 +#define TRF79X0_STATUS_CTRL_RF_PWR_FULL 0x00 +#define TRF79X0_STATUS_CTRL_5V_OPERATION 0x01 + +//***************************************************************************** +// +// TRF79X0 TRF79X0_ISO_CONTROL Register Definitions. +// +//***************************************************************************** +#define TRF79X0_ISO_CONTROL_RX_CRC_N 0x80 +#define TRF79X0_ISO_CONTROL_DIR_MODE 0x40 +#define TRF79X0_ISO_NFC_TARGET 0x00 +#define TRF79X0_ISO_NFC_INITIATOR 0x10 +#define TRF79X0_NFC_PASSIVE_MODE 0x00 +#define TRF79X0_NFC_ACTIVE_MODE 0x08 +#define TRF79X0_NFC_NORMAL_MODE 0x00 +#define TRF79X0_NFC_CARD_EMULATION_MODE 0x40 +#define TRF79X0_ISO_CONTROL_14443A_106K 0x08 +#define TRF79X0_ISO_CONTROL_14443A_106K 0x08 +#define TRF79X0_ISO_CONTROL_14443B_106K 0x0C +#define TRF79X0_ISO_CONTROL_15693_LOW_1SUB_1OUT4 0x00 +#define TRF79X0_ISO_CONTROL_15693_HIGH_1SUB_1OUT4 0x02 +#define TRF79X0_ISO_CONTROL_15693_HIGH_1SUB_1OUT256 0x03 + +//***************************************************************************** +// +// TRF79X0 TRF79X0_MODULATOR_CONTROL_REG Register Definitions. +// +//***************************************************************************** +#define TRF79X0_MOD_CTRL_SYS_CLK_13_56MHZ 0x30 +#define TRF79X0_MOD_CTRL_SYS_CLK_6_78MHZ 0x20 +#define TRF79X0_MOD_CTRL_SYS_CLK_3_3MHZ 0x10 +#define TRF79X0_MOD_CTRL_SYS_CLK_DISABLE 0x00 +#define TRF79X0_MOD_CTRL_MOD_ASK_30 0x07 +#define TRF79X0_MOD_CTRL_MOD_ASK_22 0x06 +#define TRF79X0_MOD_CTRL_MOD_ASK_16 0x05 +#define TRF79X0_MOD_CTRL_MOD_ASK_13 0x04 +#define TRF79X0_MOD_CTRL_MOD_ASK_8_5 0x03 +#define TRF79X0_MOD_CTRL_MOD_ASK_7 0x02 +#define TRF79X0_MOD_CTRL_MOD_OOK_100 0x01 +#define TRF79X0_MOD_CTRL_MOD_ASK_10 0x00 + +//***************************************************************************** +// +// TRF79X0 TRF79X0_RX_SPECIAL_SETTINGS_REG Register Definitions. +// +//***************************************************************************** +#define TRF79X0_RX_SP_SET_M848 0x20 +#define TRF79X0_RX_SP_SET_C424 0x40 + +//***************************************************************************** +// +// TRF79X0 TRF79X0_REGULATOR_CONTROL_REG Register Definitions. +// +//***************************************************************************** +#define TRF79X0_REGULATOR_CTRL_AUTO_REG 0x80 +#define TRF79X0_REGULATOR_CTRL_VRS_2_7V 0x00 +#define TRF79X0_REGULATOR_CTRL_VRS_2_8V 0x01 +#define TRF79X0_REGULATOR_CTRL_VRS_2_9V 0x02 +#define TRF79X0_REGULATOR_CTRL_VRS_3_0V 0x03 +#define TRF79X0_REGULATOR_CTRL_VRS_3_1V 0x04 +#define TRF79X0_REGULATOR_CTRL_VRS_3_2V 0x05 +#define TRF79X0_REGULATOR_CTRL_VRS_3_3V 0x06 +#define TRF79X0_REGULATOR_CTRL_VRS_3_4V 0x07 + +//***************************************************************************** +// +// TRF79x0 Command Definitions. +// +//***************************************************************************** +#define TRF79X0_IDLE_CMD 0x00 +#define TRF79X0_SOFT_INIT_CMD 0x03 +#define TRF79X0_INITIAL_RF_COLLISION_AVOID_CMD 0x04 +#define TRF79X0_PERFORM_RES_RF_COLLISION_AVOID_CMD 0x05 +#define TRF79X0_PERFORM_RES_RF_COLLISION_AVOID_N0_CMD 0x06 +#define TRF79X0_RESET_FIFO_CMD 0x0F +#define TRF79X0_TRANSMIT_NO_CRC_CMD 0x10 +#define TRF79X0_TRANSMIT_CRC_CMD 0x11 +#define TRF79X0_DELAY_TRANSMIT_NO_CRC_CMD 0x12 +#define TRF79X0_DELAY_TRANSMIT_CRC_CMD 0x13 +#define TRF79X0_TRANSMIT_NEXT_SLOT_CMD 0x14 +#define TRF79X0_CLOSE_SLOT_SEQUENCE_CMD 0x15 +#define TRF79X0_STOP_DECODERS_CMD 0x16 +#define TRF79X0_RUN_DECODERS_CMD 0x17 +#define TRF79X0_TEST_INTERNAL_RF_CMD 0x18 +#define TRF79X0_TEST_EXTERNAL_RF_CMD 0x19 +#define TRF79X0_RX_ADJUST_GAIN_CMD 0x1A + +//***************************************************************************** +// +// TRF79x0 Command/Address mode definitions. +// +//***************************************************************************** +#define TRF79X0_ADDRESS_MASK 0x1F +#define TRF79X0_CONTROL_CMD 0x80 +#define TRF79X0_CONTROL_REG_READ 0x40 +#define TRF79X0_CONTROL_REG_WRITE 0x00 +#define TRF79X0_REG_MODE_SINGLE 0x00 +#define TRF79X0_REG_MODE_CONTINUOUS 0x20 + +//***************************************************************************** +// +// TRF7960/7970 Modulator control register mode default values to +// determine RF Daughter Board. +// +//***************************************************************************** +#define TRF7960_DEFAULT_ID 0x11 +#define TRF7970_DEFAULT_ID 0x91 + +//***************************************************************************** +// +// The following defines are used with the TRF79x0Transceive() function with +// the uiFlags parameter. +// +//***************************************************************************** + +// +// Transmit without CRC, receive without CRC check. +// +#define TRF79X0_TRANSCEIVE_NO_CRC 0 +// +// Transmit without CRC, receive with CRC check. +// +#define TRF79X0_TRANSCEIVE_RX_CRC 1 +// +// Transmit with CRC, receive without CRC check. +// +#define TRF79X0_TRANSCEIVE_TX_CRC 2 +// +// Transmit with CRC, receive with CRC check. +// +#define TRF79X0_TRANSCEIVE_CRC (TRF79X0_TRANSCEIVE_TX_CRC | \ + TRF79X0_TRANSCEIVE_RX_CRC) + +//***************************************************************************** +// +// These defines specify abstract IRQ causes to wait for. Since the IRQ +// state register does not lend itself to easy cumulative storage (for +// example just because bit 0x80 was set at least once does not mean that the +// transmission is complete) these are defined to have an abstract way to +// express certain conditions that one would want to wait for. +// +//***************************************************************************** + +// +// Wait for any IRQ to occur. +// +#define TRF79X0_WAIT_ANY 0x00000001 + +// +// Wait for an IRQ that signifies the end of transmission to occur. +// +#define TRF79X0_WAIT_TXEND 0x00000002 + +// +// Wait for an IRQ that signifies the end of reception to occur, this +// will either be 0x40 with no other flags set, or 0x01 for RX timeout. +// +#define TRF79X0_WAIT_RXEND 0x00000004 + +//***************************************************************************** +// +// These enumerations are used as part of the state machine layout for NFC P2P +// +//***************************************************************************** + +// +// States for the TRF79x0 State Machine +// +typedef enum +{ + BOARD_INIT = 0, + P2P_INITATIOR_MODE, + P2P_PASSIVE_TARGET_MODE, + P2P_ACTIVE_TARGET_MODE, + CARD_EMULATION_TYPE_A, + CARD_EMULATION_TYPE_B +} tTRF79x0TRFMode; + +// +// Frequency Settings for TRF79x0 +// +typedef enum +{ + FREQ_STAND_BY= 0, // Used for Board Initialization + FREQ_106_KBPS, + FREQ_212_KBPS, + FREQ_424_KBPS +} tTRF79x0Frequency; + +// +// CRC Settings for TRF79x0 +// +typedef enum +{ + CRC_BIT_DISABLE = 0, + CRC_BIT_ENABLE +} tTRF79x0CRC; + +// +// IRQ Flag deffinitions. Defined in datasheet, provided for ease of use +// +typedef enum +{ + IRQ_STATUS_IDLE = 0x00, + IRQ_STATUS_COLLISION_ERROR = 0x01, + IRQ_STATUS_COLLISION_AVOID_FINISHED = 0x02, + IRQ_STATUS_RF_FIELD_CHANGE = 0x04, + IRQ_STATUS_SDD_COMPLETE = 0x08, + IRQ_STATUS_PROTOCOL_ERROR = 0x10, + IRQ_STATUS_FIFO_HIGH_OR_LOW = 0x20, + IRQ_STATUS_RX_COMPLETE = 0x40, + IRQ_STATUS_TX_COMPLETE = 0x80, + IRQ_STATUS_TIME_OUT = 0xFF +} tTRF79x0IRQFlag; + +//***************************************************************************** +// +// Exported function prototypes. +// +//***************************************************************************** +extern void TRF79x0Init(void); +extern void TRF79x0SetMode(tTRF79x0TRFMode eMode, tTRF79x0Frequency eFrequency); +extern void TRF79x0Interrupt(void); +extern void TRF79x0InterruptInit(void); +extern void TRF79x0InterruptEnable(void); +extern void TRF79x0InterruptDisable(void); +extern void TRF79x0DisableTransmitter(void); +extern void TRF797x0ResetDecoders(void); +extern uint8_t* TRF79x0GetNFCBuffer(void); +extern void TRF79x0DirectCommand(uint8_t ucCommand); +extern void TRF79x0ResetFifoCommand(void); +extern tStatus TRF79x0Init2(tTRF79x0TRFMode eMode, + tTRF79x0Frequency eFrequency); +tStatus TRF79x0WriteFIFO(uint8_t *pui8Buffer, tTRF79x0CRC eCRCBit, + uint8_t ui8Length); +tTRF79x0IRQFlag TRF79x0IRQHandler(uint16_t ui16TimeOut); +extern void TRF79x0WriteRegister(unsigned char ucAddress, + unsigned char ucData); +extern void TRF79x0WriteRegisterContinuous(unsigned char ucAddress, + unsigned char *pucData, + unsigned int uiLength); +extern unsigned char TRF79x0ReadIRQStatus(void); +extern unsigned char TRF79x0ReadRegister(unsigned char ucAddress); +extern void TRF79x0ReadRegisterContinuous(unsigned char ucAddress, + unsigned char *pucData, + unsigned int uiLength); +extern void TRF79x0FIFOWrite(unsigned char const *pucData, + unsigned int uiLength); +extern void TRF79x0Receive(unsigned char *pucData, unsigned int *puiLength); +extern void TRF79x0Transmit(unsigned char const *pucData, + unsigned int uiLength, unsigned int uiBits); +extern void TRF79x0Transceive(unsigned char const *pucTXBuf, + unsigned int uiTXLen, unsigned int uiTXBits, + unsigned char *pucRXBuf, unsigned int *puiRXLen, + unsigned int *puiRXBits, unsigned int uiFlags); +extern void TRF79x0IRQClearAll(void); +extern void TRF79x0IRQClearCauses(unsigned int uiCauses); +extern int TRF79x0IRQWait(unsigned long ulCondition); +extern int TRF79x0IRQWaitTimeout(unsigned long ulCondition, + unsigned long ulTimeout); +extern int TRF79x0GetCollisionPosition(void); +extern int TRF79x0IsCollision(void); +extern void TRF79x0InitialSettings(void); +extern void TRF79x0ReceiveAgain(unsigned char *pucRXBuf, + unsigned int *puiRXLen); +extern void TRF79x0ReceiveEnd(void); +extern void TRF79x0TransceiveISO15693(unsigned char const *pucTXBuf, + unsigned int uiTXLen, + unsigned int uiTXBits, unsigned char *pucRXBuf, + unsigned int *puiRXLen, unsigned int *puiRXBits, + unsigned int uiFlags); +extern int SendResponse(int Something, int DataLength, char *DataPtr); +extern int SendResponse_w_o_CRC(int Something, int DataLength, char *DataPtr); +#endif diff --git a/nfclib/trf79x0_hw_example.h b/nfclib/trf79x0_hw_example.h new file mode 100644 index 0000000..c73e882 --- /dev/null +++ b/nfclib/trf79x0_hw_example.h @@ -0,0 +1,489 @@ +//***************************************************************************** +// +// trf79x0_hw_example.h - Hardware Pin configuration for TRF79x0 ATB on +// Tiva C Series Snowflake Class silicon. Tailored for DK-tm4c129x, but will +// work for any board with a Snowflake chip with RF Headers. +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __TRF79X0_HW_H__ +#define __TRF79X0_HW_H__ + +//***************************************************************************** +// +// Enable the TRF79x0 that will be used with the TM4C129X board +// Enabled = 1, Disabled = 0 +// +//***************************************************************************** +#define RF_DAUGHTER_TRF7960 0 +#define RF_DAUGHTER_TRF7970 1 + +//***************************************************************************** +// +// Check for correct definition of RF_DAUGTHER_TRF79X0 +// +//***************************************************************************** +#if (RF_DAUGHTER_TRF7960 && RF_DAUGHTER_TRF7970) +#error "Only one TRF79X0 can be defined at the same time." +#elif (!(RF_DAUGHTER_TRF7960 || RF_DAUGHTER_TRF7970)) +#error "Define the TRF79X0 to be used, none currently defined." +#endif + +//***************************************************************************** +// +// Pin definitions for the DK-TM4C129X development board connections to the +// BoosterPack board. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup nfc_hw NFC Hardware Definitions +//! @{ +//! This section covers the definitions that control which hardware is used to +//! communicate with the TRF79x0 EM module. These defines configure which SSI +//! peripheral is used as well as which pins are assigned to the other +//! connections to the TRF79x0 EM module. The \b TRF79X0_SSI_* defines are +//! used to specify the SSI peripheral that is used by the application. The +//! remaining defines specify the pins used by the NFC APIs. The TRF79x0 +//! EM module requires the following signal connections: CLK, RX, TX, CS, ASKOK, +//! EN, EN2, IRQ, MOD. To configure these signals, three defines must be set +//! for each. For example, for the CS signal, the \ref TRF79X0_CS_BASE, +//! \ref TRF79X0_CS_PERIPH and \ref TRF79X0_CS_PIN defines must be set. +//! +//! \b Example: CS pin is on GPIO port E pin 1. +//! \verbatim +//! +//! #define TRF79X0_CS_BASE GPIO_PORTA_BASE +//! #define TRF79X0_CS_PERIPH SYSCTL_PERIPH_GPIOA +//! #define TRF79X0_CS_PIN GPIO_PIN_4 +//! \endverbatim +//! +//***************************************************************************** + +//***************************************************************************** +// +//! The clock rate of the SSI clock specified in Hz. +//! +//! \b Example: 2-MHz SSI data clock. +//! +//! \#define SSI_CLK_RATE 2000000 +//! +//***************************************************************************** +#define SSI_CLK_RATE 2000000 +#define SSI_CLKS_PER_MS (SSI_CLK_RATE / 1000) +#define STATUS_READS_PER_MS (SSI_CLKS_PER_MS / 16) +#define SSI_NO_DATA 0 + +//***************************************************************************** +// +//! Specifies the SSI peripheral for the SSI port that is connected to the +//! TRF79x0 EM board. The value should be set to SYSCTL_PERIPH_SSIn, where n is +//! the number of the SSI port being used. +//! +//! \b Example: Uses SSI0 peripheral +//! +//! \#define TRF79X0_SSI_PERIPH SYSCTL_PERIPH_SSI0 +//! +//***************************************************************************** +#define TRF79X0_SSI_PERIPH SYSCTL_PERIPH_SSI0 + +//***************************************************************************** +// +//! Specifies the SSI @a base address for the SSI port that is connected to the +//! TRF79x0 EM board. The value should be set to SYSCTL_PERIPH_SSIn, where n is +//! the number of the SSI port being used. +//! +//! \b Example: Uses SSI0 peripheral +//! +//! \#define TRF79X0_SSI_BASE SSI0_BASE +//! +//***************************************************************************** +#define TRF79X0_SSI_BASE SSI0_BASE + +//***************************************************************************** +// +// GPIO pin deffinitions for TRF79x0 SSI signals +// +//***************************************************************************** + +// +//! Specifies the @a base address of the GPIO port that is connected to the SSI +//! Clock signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral CLK signal is on GPIO port A. +//! +//! \#define TRF79X0_CLK_BASE GPIO_PORTA_BASE +// +#define TRF79X0_CLK_BASE GPIO_PORTA_BASE + +// +//! Specifies the @a peripheral for the GPIO port that is connected to the SSI +//! Clock signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral CLK signal is on GPIO port A. +//! +//! \#define TRF79X0_CLK_PERIPH SYSCTL_PERIPH_GPIOA +// +#define TRF79X0_CLK_PERIPH SYSCTL_PERIPH_GPIOA + +// +//! Specifies the GPIO pin that is connected to the SSI +//! Clock signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral CLK signal is on GPIO pin 2. +//! +//! \#define TRF79X0_CLK_PIN GPIO_PIN_2 +// +#define TRF79X0_CLK_PIN GPIO_PIN_2 + +// +//! Specifies the GPIO pin that is connected to +//! the SSI Clock signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI Clock signal is on GPIO port A pin 2. +//! +//! \#define TRF79X0_CLK_CONFIG GPIO_PA2_SSI0CLK +// +#define TRF79X0_CLK_CONFIG GPIO_PA2_SSI0CLK + +// +//! Specifies the @a base address of the GPIO port that is connected to the SSI +//! TX signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral TX signal is on GPIO port A. +//! +//! \#define TRF79X0_TX_BASE GPIO_PORTA_BASE +// +#define TRF79X0_TX_BASE GPIO_PORTA_BASE + +// +//! Specifies the @a peripheral for the GPIO port that is connected to the SSI +//! TX signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral TX signal is on GPIO port A. +//! +//! \#define TRF79X0_TX_PERIPH SYSCTL_PERIPH_GPIOA +// +#define TRF79X0_TX_PERIPH SYSCTL_PERIPH_GPIOA + +// +//! Specifies the GPIO pin that is connected to the SSI +//! TX signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral TX signal is on GPIO pin 4. +//! +//! \#define TRF79X0_TX_PIN GPIO_PIN_4 +// +#define TRF79X0_TX_PIN GPIO_PIN_4 + +// +//! Specifies the GPIO pin that is connected to +//! the SSITX (DAT0) signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI 1 TX signal is on GPIO port A pin 4. +//! +//! \#define TRF79X0_TX_CONFIG GPIO_PA4_SSI0XDAT0 +// +#define TRF79X0_TX_CONFIG GPIO_PA4_SSI0XDAT0 + +// +//! Specifies the @a base address of the GPIO port that is connected to the SSI +//! RX signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral RX signal is on GPIO port A. +//! +//! \#define TRF79X0_RX_BASE GPIO_PORTA_BASE +// +#define TRF79X0_RX_BASE GPIO_PORTA_BASE + +// +//! Specifies the @a peripheral for the GPIO port that is connected to the SSI +//! RX signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral RX signal is on GPIO port A. +//! +//! \#define TRF79X0_RX_PERIPH SYSCTL_PERIPH_GPIOA +// +#define TRF79X0_RX_PERIPH SYSCTL_PERIPH_GPIOA + +// +//! Specifies the GPIO pin that is connected to the SSI +//! RX signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral RX signal is on GPIO pin 5. +//! +//! \#define TRF79X0_RX_PIN GPIO_PIN_5 +// +#define TRF79X0_RX_PIN GPIO_PIN_5 + +// +//! Specifies the GPIO pin that is connected to +//! the SSIRX (DAT1) signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI 1 RX signal is on GPIO port A pin 5. +//! +//! \#define TRF79X0_RX_CONFIG GPIO_PA5_SSI0XDAT1 +// +#define TRF79X0_RX_CONFIG GPIO_PA5_SSI0XDAT1 + +//***************************************************************************** +// +// Hardware connection definitions for the TRF79x0 board. +// +//***************************************************************************** + +// +//! Specifies the @a base address of the GPIO port that is connected to the SSI +//! CS signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI CS signal is on GPIO port A. +//! +//! \#define TRF79X0_CS_BASE GPIO_PORTA_BASE +// +#define TRF79X0_CS_BASE GPIO_PORTA_BASE + +// +//! Specifies the @a peripheral for the GPIO port that is connected to the SSI +//! CS signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI CS signal is on GPIO port A. +//! +//! \#define TRF79X0_CS_PERIPH SYSCTL_PERIPH_GPIOA +// +#define TRF79X0_CS_PERIPH SYSCTL_PERIPH_GPIOA + +// +//! Specifies the GPIO pin that is connected to the SSI +//! CS signal on the TRF79x0 EM board. +//! +//! \b Example: The SSI peripheral CS signal is on GPIO pin 4. +//! +//! \#define TRF79X0_CS_PIN GPIO_PIN_4 +// +#define TRF79X0_CS_PIN GPIO_PIN_3 + +// +//! Specifies the @a base address of the GPIO port that is connected to the EN +//! signal on the TRF79x0 EM board. +//! +//! \b Example: The EN signal is on GPIO port D. +//! +//! \#define TRF79X0_EN_BASE GPIO_PORTD_BASE +// +#define TRF79X0_EN_BASE GPIO_PORTD_BASE + +// +//! Specifies the @a peripheral for the GPIO port that is connected to the EN +//! signal on the TRF79x0 EM board. +//! +//! \b Example: The EN signal is on GPIO port D. +//! +//! \#define TRF79X0_EN_PERIPH SYSCTL_PERIPH_GPIOD +// +#define TRF79X0_EN_PERIPH SYSCTL_PERIPH_GPIOD + +// +//! Specifies the GPIO pin that is connected to the EN pin on the +//! TRF79x0 EM board. +//! +//! \b Example: The EN signal is on GPIO pin 2. +//! +//! \#define TRF79X0_EN_PIN GPIO_PIN_2 +// +#define TRF79X0_EN_PIN GPIO_PIN_2 + +// +//! Specifies the @a base address of the GPIO port that is connected to the EN2 +//! signal on the TRF79x0 EM board. +//! +//! \b Example: The EN2 signal is on GPIO port D. +//! +//! \#define TRF79X0_EN2_BASE GPIO_PORTD_BASE +// +#define TRF79X0_EN2_BASE GPIO_PORTD_BASE + +// +//! Specifies the @a peripheral for the GPIO port that is connected to the EN2 +//! signal on the TRF79x0 EM board. +//! +//! \b Example: The EN2 signal is on GPIO port D. +//! +//! \#define TRF79X0_EN2_PERIPH SYSCTL_PERIPH_GPIOD +// +#define TRF79X0_EN2_PERIPH SYSCTL_PERIPH_GPIOD + +// +//! Specifies the GPIO pin that is connected to the EN2 signal on the +//! TRF79x0 EM board. +//! +//! \b Example: The EN2 signal is on GPIO pin 3. +//! +//! \#define TRF79X0_EN2_PIN GPIO_PIN_3 +// +#define TRF79X0_EN2_PIN GPIO_PIN_3 + +// +//! Specifies the @a base address of the GPIO port that is connected to the +//! ASKOK signal on the TRF79x0 EM board. +//! +//! \b Example: The ASKOK signal is on GPIO port J. +//! +//! \#define TRF79X0_ASKOK_BASE GPIO_PORTJ_BASE +// +#define TRF79X0_ASKOK_BASE GPIO_PORTJ_BASE + +// +//! Specifies the @a peripheral for the GPIO port that is connected to the ASKOK +//! signal on the TRF79x0 EM board. +//! +//! \b Example: The ASKOK signal is on GPIO port J. +//! +//! \#define TRF79X0_ASKOK_PERIPH SYSCTL_PERIPH_GPIOJ +// +#define TRF79X0_ASKOK_PERIPH SYSCTL_PERIPH_GPIOJ + +// +//! Specifies the GPIO pin that is connected to the ASKOK signal on +//! the TRF79x0 EM board. +//! +//! \b Example: The ASKOK signal is on GPIO pin 5. +//! +//! \#define TRF79X0_ASKOK_PIN GPIO_PIN_5 +// +#define TRF79X0_ASKOK_PIN GPIO_PIN_5 + +// +//! Specifies the @a base address of the GPIO port that is connected to the MOD +//! signal on the TRF79x0 EM board. +//! +//! \b Example: The MOD signal is on GPIO port J. +//! +//! \#define TRF79X0_MOD_BASE GPIO_PORTJ_BASE +// +#define TRF79X0_MOD_BASE GPIO_PORTJ_BASE + +// +//! Specifies the @a peripheral for the GPIO port that is connected to the MOD +//! signal on the TRF79x0 EM board. +//! +//! \b Example: The MOD signal is on GPIO port J. +//! +//! \#define TRF79X0_MOD_PERIPH SYSCTL_PERIPH_GPIOJ +// +#define TRF79X0_MOD_PERIPH SYSCTL_PERIPH_GPIOJ + +// +//! Specifies the GPIO pin that is connected to the MOD signal on the +//! TRF79x0 EM board. +//! +//! \b Example: The MOD signal is on GPIO pin 4. +//! +//! \#define TRF79X0_MOD_PIN GPIO_PIN_4 +// +#define TRF79X0_MOD_PIN GPIO_PIN_4 + +// +//! Specifies the @a base address of the GPIO port that is connected to the IRQ +//! signal on the TRF79x0 EM board. +//! +//! \b Example: The IRQ signal is on GPIO port J. +//! +//! \#define TRF79X0_IRQ_BASE GPIO_PORTJ_BASE +// +#define TRF79X0_IRQ_BASE GPIO_PORTJ_BASE + +// +//! Specifies the @a peripheral for the GPIO port that is connected to the IRQ +//! signal on the TRF79x0 EM board. +//! +//! \b Example: The IRQ signal is on GPIO port J. +//! +//! \#define TRF79X0_IRQ_PERIPH SYSCTL_PERIPH_GPIOJ +// +#define TRF79X0_IRQ_PERIPH SYSCTL_PERIPH_GPIOJ + +// +//! Specifies the GPIO pin that is connected to the IRQ signal on the +//! TRF79x0 EM board. +//! +//! \b Example: The IRQ signal is on GPIO pin 1. +//! +//! \#define TRF79X0_IRQ_PIN GPIO_PIN_1 +// +#define TRF79X0_IRQ_PIN GPIO_PIN_1 + +// +//! Specifies GPIO interrupt that is tied to the GPIO port that the IRQ signal +//! is connected to TRF79x0 EM board. +//! +//! \b Example: SSI GPIO interrupt is on GPIO port C. +//! +//! \#define TRF79X0_IRQ_INT INT_GPIOC +// +#define TRF79X0_IRQ_INT INT_GPIOJ + +// +// Uses Blue LED part of RGB tricolor LED (arbitrary color choice) +// +#define ENABLE_LED_PERIPHERAL SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ); +#define SET_LED_DIRECTION GPIOPinTypeGPIOOutput(GPIO_PORTQ_BASE, GPIO_PIN_4 ); +#define TURN_ON_LED GPIOPinWrite(GPIO_PORTQ_BASE, GPIO_PIN_4, GPIO_PIN_4); +#define TURN_OFF_LED GPIOPinWrite(GPIO_PORTQ_BASE, GPIO_PIN_4, 0); + +//***************************************************************************** +// +// Optional LED Defines, useful for boards that have tricolor LED's +// +//***************************************************************************** +#define BOARD_HAS_TRICOLOR_LED 1 + +#define ENABLE_LED_TRICOLOR_RED_PERIPH SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION); +#define SET_LED_TRICOLOR_RED_DIRECTION GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_5 ); +#define TURN_ON_LED_TRICOLOR_RED GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_5, GPIO_PIN_5); +#define TURN_OFF_LED_TRICOLOR_RED GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_5, 0); + +#define ENABLE_LED_TRICOLOR_BLUE_PERIPH SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ); +#define SET_LED_TRICOLOR_BLUE_DIRECTION GPIOPinTypeGPIOOutput(GPIO_PORTQ_BASE, GPIO_PIN_4 ); +#define TURN_ON_LED_TRICOLOR_BLUE GPIOPinWrite(GPIO_PORTQ_BASE, GPIO_PIN_4, GPIO_PIN_4); +#define TURN_OFF_LED_TRICOLOR_BLUE GPIOPinWrite(GPIO_PORTQ_BASE, GPIO_PIN_4, 0); + +#define ENABLE_LED_TRICOLOR_GREEN_PERIPH SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ); +#define SET_LED_TRICOLOR_GREEN_DIRECTION GPIOPinTypeGPIOOutput(GPIO_PORTQ_BASE, GPIO_PIN_7 ); +#define TURN_ON_LED_TRICOLOR_GREEN GPIOPinWrite(GPIO_PORTQ_BASE, GPIO_PIN_7, GPIO_PIN_7); +#define TURN_OFF_LED_TRICOLOR_GREEN GPIOPinWrite(GPIO_PORTQ_BASE, GPIO_PIN_7, 0); + +//***************************************************************************** +// +// Macro for IRQ signal from TRF79x0 -> Board +// left in this format for cross platform compatibility. +// +//***************************************************************************** +#define IRQ_IS_SET() GPIOPinRead(TRF79X0_IRQ_BASE, TRF79X0_IRQ_PIN) + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +#endif // __TRF79X0_HW_H__ diff --git a/nfclib/types.h b/nfclib/types.h new file mode 100644 index 0000000..b1a655a --- /dev/null +++ b/nfclib/types.h @@ -0,0 +1,36 @@ +//***************************************************************************** +// types.h - typedefs used for cross architecture code porting / ease of use. +// +// Copyright (c) 2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** +#ifndef _TYPES_H_ +#define _TYPES_H_ + +// +// Boolean Status type. Provided for Cross Compatibility with other chipsets. +// Not necessary, but useful for porting code between architectures. +// +typedef enum +{ + STATUS_FAIL = 0, + STATUS_SUCCESS +}tStatus; + +#endif //_TYPES_H_ diff --git a/sensorlib/Makefile b/sensorlib/Makefile new file mode 100644 index 0000000..3b8f43c --- /dev/null +++ b/sensorlib/Makefile @@ -0,0 +1,88 @@ +#****************************************************************************** +# +# Makefile - Rules for building the sensor library. +# +# Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +# Software License Agreement +# +# Texas Instruments (TI) is supplying this software for use solely and +# exclusively on TI's microcontroller products. The software is owned by +# TI and/or its suppliers, and is protected under applicable copyright +# laws. You may not combine this software with "viral" open-source +# software in order to form a larger program. +# +# THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +# NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +# NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +# CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +# DAMAGES, FOR ANY REASON WHATSOEVER. +# +# This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +# +#****************************************************************************** + +# +# The base directory for TivaWare. +# +ROOT=.. + +# +# Include the common make definitions. +# +include ${ROOT}/makedefs + +# +# Where to find header files that do not live in the source directory. +# +IPATH=.. + +# +# The default rule, which causes the sensor library to be built. +# +all: ${COMPILER} +all: ${COMPILER}/libsensor.a + +# +# The rule to clean out all the build products. +# +clean: + @rm -rf ${COMPILER} ${wildcard *~} + +# +# The rule to create the target directory. +# +${COMPILER}: + @mkdir -p ${COMPILER} + +# +# Rules for building the sensor library. +# +${COMPILER}/libsensor.a: ${COMPILER}/ak8963.o +${COMPILER}/libsensor.a: ${COMPILER}/ak8975.o +${COMPILER}/libsensor.a: ${COMPILER}/bmp180.o +${COMPILER}/libsensor.a: ${COMPILER}/bq27510g3.o +${COMPILER}/libsensor.a: ${COMPILER}/cm3218.o +${COMPILER}/libsensor.a: ${COMPILER}/comp_dcm.o +${COMPILER}/libsensor.a: ${COMPILER}/i2cm_drv.o +${COMPILER}/libsensor.a: ${COMPILER}/isl29023.o +${COMPILER}/libsensor.a: ${COMPILER}/kxti9.o +${COMPILER}/libsensor.a: ${COMPILER}/l3gd20h.o +${COMPILER}/libsensor.a: ${COMPILER}/lsm303d.o +${COMPILER}/libsensor.a: ${COMPILER}/lsm303dlhc_accel.o +${COMPILER}/libsensor.a: ${COMPILER}/lsm303dlhc_mag.o +${COMPILER}/libsensor.a: ${COMPILER}/magneto.o +${COMPILER}/libsensor.a: ${COMPILER}/mpu6050.o +${COMPILER}/libsensor.a: ${COMPILER}/mpu9150.o +${COMPILER}/libsensor.a: ${COMPILER}/quaternion.o +${COMPILER}/libsensor.a: ${COMPILER}/sht21.o +${COMPILER}/libsensor.a: ${COMPILER}/tmp006.o +${COMPILER}/libsensor.a: ${COMPILER}/tmp100.o +${COMPILER}/libsensor.a: ${COMPILER}/vector.o + +# +# Include the automatically generated dependency files. +# +ifneq (${MAKECMDGOALS},clean) +-include ${wildcard ${COMPILER}/*.d} __dummy__ +endif diff --git a/sensorlib/ak8963.c b/sensorlib/ak8963.c new file mode 100644 index 0000000..5b605a6 --- /dev/null +++ b/sensorlib/ak8963.c @@ -0,0 +1,666 @@ +//***************************************************************************** +// +// ak8963.c - Driver for the AK8963 magnetometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_ak8963.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/ak8963.h" + +//***************************************************************************** +// +//! \addtogroup ak8963_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the AK8963 state machine. +// +//***************************************************************************** +#define AK8963_STATE_IDLE 0 // State machine is idle +#define AK8963_STATE_READ 1 // Waiting for read +#define AK8963_STATE_WRITE 2 // Waiting for write +#define AK8963_STATE_RMW 3 // Waiting for read-modify-write + +//***************************************************************************** +// +// The factors used to convert the magnetometer readings from the AK8963 into +// floating point values in tesla +// +//***************************************************************************** +static const float g_fAK8963Factors[] = +{ + 0.0000006, // 14-bit = .6 uT/LSB + 0.00000015, // 16-bit = .15 uT/LSB +}; + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// AK8963 have completed. +// +//***************************************************************************** +static void +AK8963Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tAK8963 *psInst; + + // + // Convert the instance data into a pointer to a tAK8963 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate + // the error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = AK8963_STATE_IDLE; + } + + // + // Determine the current state of the AK8963 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case AK8963_STATE_READ: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = AK8963_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A write has just completed. + // + case AK8963_STATE_WRITE: + { + // + // Set the bit width to the new value. If the register was not + // modified, the values will be the same so this has no effect. + // + psInst->ui8BitOutput = psInst->ui8NewBitOutput; + + // + // The state machine is now idle. + // + psInst->ui8State = AK8963_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A read-modify-write just completed + // + case AK8963_STATE_RMW: + { + // + // See if the AK8963_O_CNTL2 register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + AK8963_O_CNTL2) + { + // + // Extract the AK8963_CNTL2_SRST field + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + AK8963_CNTL2_SRST) + { + // + // A soft reset has happened. Reset the bitoutput + // tracking variable + // + psInst->ui8BitOutput = 0; + } + } + + // + // See if the AK8963_O_CNTL register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + AK8963_O_CNTL) + { + // + // Extract the BITM field + // + psInst->ui8BitOutput = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + AK8963_CNTL_BITM_M) >> AK8963_CNTL_BITM_S); + } + + // + // The state machine is now idle. + // + psInst->ui8State = AK8963_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == AK8963_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the AK8963 driver. +//! +//! \param psInst is a pointer to the AK8963 instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the AK8963 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the AK8963 driver, preparing it for operation. +//! +//! \return Returns 1 if the AK8963 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +AK8963Init(tAK8963 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Initialize the AK8963 instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = AK8963_STATE_IDLE; + psInst->ui8BitOutput = AK8963_CNTL_BITM_14BIT >> AK8963_CNTL_BITM_S; + psInst->ui8NewBitOutput = AK8963_CNTL_BITM_14BIT >> AK8963_CNTL_BITM_S; + + // + // The default settings are ok. Return success and call the callback. + // + if(pfnCallback) + { + pfnCallback(pvCallbackData, 0); + } + + return(1); +} + +//***************************************************************************** +// +//! Reads data from AK8963 registers. +//! +//! \param psInst is a pointer to the AK8963 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the AK8963. +//! +//! \return Returns 1 if the read was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +AK8963Read(tAK8963 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the AK8963 driver is not idle (in other words, there + // is already an outstanding request to the AK8963). + // + if(psInst->ui8State != AK8963_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = AK8963_STATE_READ; + + // + // Read the requested registers from the AK8963. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + AK8963Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = AK8963_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to AK8963 registers. +//! +//! \param psInst is a pointer to the AK8963 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the AK8963. The first byte of the \e pui8Data buffer contains the value to +//! be written into the \e ui8Reg register, the second value contains the data +//! to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +AK8963Write(tAK8963 *psInst, uint_fast8_t ui8Reg, const uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the AK8963 driver is not idle (in other words, there + // is already an outstanding request to the AK8963). + // + if(psInst->ui8State != AK8963_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if the CNTL2 register is being written. + // + if((ui8Reg <= AK8963_O_CNTL2) && ((ui8Reg + ui16Count) > AK8963_O_CNTL2)) + { + // + // See if a soft reset is being requested. + // + if(pui8Data[ui8Reg - AK8963_O_CNTL2] & AK8963_CNTL2_SRST) + { + // + // Update the bit width based on the soft reset. + // + psInst->ui8NewBitOutput = 0; + } + } + + // + // See if the CNTL register is being written. + // + if((ui8Reg <= AK8963_O_CNTL) && ((ui8Reg + ui16Count) > AK8963_O_CNTL)) + { + // + // Extract the new value of the BITM field from the CNTL register + // value. + // + psInst->ui8NewBitOutput = ((pui8Data[ui8Reg - AK8963_O_CNTL] & + AK8963_CNTL_BITM_M) >> AK8963_CNTL_BITM_S); + } + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = AK8963_STATE_WRITE; + + // + // Write the requested registers to the AK8963. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, AK8963Callback, + psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = AK8963_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of an AK8963 register. +//! +//! \param psInst is a pointer to the AK8963 instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the AK8963 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! AK8963. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +AK8963ReadModifyWrite(tAK8963 *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the AK8963 driver is not idle (in other words, there + // is already an outstanding request to the AK8963). + // + if(psInst->ui8State != AK8963_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = AK8963_STATE_RMW; + + // + // Submit the read-modify-write request to the AK8963. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, AK8963Callback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = AK8963_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the magnetometer data from the AK8963. +//! +//! \param psInst is a pointer to the AK8963 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the AK8963 data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - AK8963DataMagnetoGetRaw() +//! - AK8963DataMagnetoGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +AK8963DataRead(tAK8963 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the AK8963 driver is not idle (in other words, there + // is already an outstanding request to the AK8963). + // + if(psInst->ui8State != AK8963_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = AK8963_STATE_READ; + + // + // Read the data registers from the AK8963. + // + // ST1 + (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + ST2 = 8 bytes + // + psInst->pui8Data[0] = AK8963_O_ST1; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, 1, + psInst->pui8Data, 8, AK8963Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = AK8963_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw magnetometer data from the most recent data read. +//! +//! \param psInst is a pointer to the AK8963 instance data. +//! \param pui16MagnetoX is a pointer to the value into which the raw X-axis +//! magnetometer data is stored. +//! \param pui16MagnetoY is a pointer to the value into which the raw Y-axis +//! magnetometer data is stored. +//! \param pui16MagnetoZ is a pointer to the value into which the raw Z-axis +//! magnetometer data is stored. +//! +//! This function returns the raw magnetometer data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +AK8963DataMagnetoGetRaw(tAK8963 *psInst, uint_fast16_t *pui16MagnetoX, + uint_fast16_t *pui16MagnetoY, + uint_fast16_t *pui16MagnetoZ) +{ + // + // Return the raw magnetometer values. + // + if(pui16MagnetoX) + { + *pui16MagnetoX = (psInst->pui8Data[2] << 8) | psInst->pui8Data[1]; + } + if(pui16MagnetoY) + { + *pui16MagnetoY = (psInst->pui8Data[4] << 8) | psInst->pui8Data[3]; + } + if(pui16MagnetoZ) + { + *pui16MagnetoZ = (psInst->pui8Data[6] << 8) | psInst->pui8Data[5]; + } +} + +//***************************************************************************** +// +//! Gets the magnetometer data from the most recent data read. +//! +//! \param psInst is a pointer to the AK8963 instance data. +//! \param pfMagnetoX is a pointer to the value into which the X-axis +//! magnetometer data is stored. +//! \param pfMagnetoY is a pointer to the value into which the Y-axis +//! magnetometer data is stored. +//! \param pfMagnetoZ is a pointer to the value into which the Z-axis +//! magnetometer data is stored. +//! +//! This function returns the magnetometer data from the most recent data read, +//! converted into tesla. If any of the output data pointers are \b NULL, the +//! corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +AK8963DataMagnetoGetFloat(tAK8963 *psInst, float *pfMagnetoX, + float *pfMagnetoY, float *pfMagnetoZ) +{ + float fFactor; + + // + // Get the conversion factor for the current data format. + // + fFactor = g_fAK8963Factors[psInst->ui8BitOutput]; + + // + // Convert the magnetometer values into floating-point tesla values. + // + if(pfMagnetoX) + { + *pfMagnetoX = ((float)(int16_t)((psInst->pui8Data[2] << 8) | + psInst->pui8Data[1]) * fFactor); + } + if(pfMagnetoY) + { + *pfMagnetoY = ((float)(int16_t)((psInst->pui8Data[4] << 8) | + psInst->pui8Data[3]) * fFactor); + } + if(pfMagnetoZ) + { + *pfMagnetoZ = ((float)(int16_t)((psInst->pui8Data[6] << 8) | + psInst->pui8Data[5]) * fFactor); + } +} + +//***************************************************************************** +// +//! Gets the status registers from the most recent data read. +//! +//! \param psInst is a pointer to the AK8963 instance data. +//! \param pui8Status1 is a pointer to the value into which the ST1 data is +//! stored. +//! \param pui8Status2 is a pointer to the value into which the ST2 data is +//! stored. +//! +//! This function returns the magnetometer status registers from the most +//! recent data read. If any of the output data pointers are \b NULL, the +//! corresponding data is not be provided. +//! +//! Note that the AKM comp routines require ST1 and ST2, so we read them for +//! that reason. +//! +//! \return None. +// +//***************************************************************************** +void +AK8963DataGetStatus(tAK8963 *psInst, uint_fast8_t *pui8Status1, + uint_fast8_t *pui8Status2) +{ + // + // Return the status registers + // + if(pui8Status1) + { + *pui8Status1 = psInst->pui8Data[0]; + } + if(pui8Status2) + { + *pui8Status2 = psInst->pui8Data[7]; + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/ak8963.h b/sensorlib/ak8963.h new file mode 100644 index 0000000..4be56dd --- /dev/null +++ b/sensorlib/ak8963.h @@ -0,0 +1,160 @@ +//***************************************************************************** +// +// ak8963.h - Prototypes for the AK8963 magnetometer driver. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_AK8963_H__ +#define __SENSORLIB_AK8963_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the AK8963 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the AK8963. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the AK8963. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the AK8963. + // + uint8_t ui8State; + + // + // The data output bit width. + // + uint8_t ui8BitOutput; + + // + // The new data output bit width, which is used when a register write + // succeeds. + // + uint8_t ui8NewBitOutput; + + // + // The data buffer used for sending/receiving data to/from the AK8963. + // + uint8_t pui8Data[8]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The callback data provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tAK8963; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t AK8963Init(tAK8963 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t AK8963Read(tAK8963 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t AK8963Write(tAK8963 *psInst, uint_fast8_t ui8Reg, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t AK8963ReadModifyWrite(tAK8963 *psInst, + uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t AK8963DataRead(tAK8963 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void AK8963DataMagnetoGetRaw(tAK8963 *psInst, + uint_fast16_t *pui16MagnetoX, + uint_fast16_t *pui16MagnetoY, + uint_fast16_t *pui16MagnetoZ); +extern void AK8963DataMagnetoGetFloat(tAK8963 *psInst, float *pfMagnetoX, + float *pfMagnetoY, float *pfMagnetoZ); +extern void AK8963DataGetStatus(tAK8963 *psInst, uint_fast8_t *pui8Status1, + uint_fast8_t *pui8Status2); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_AK8963_H__ diff --git a/sensorlib/ak8975.c b/sensorlib/ak8975.c new file mode 100644 index 0000000..776229c --- /dev/null +++ b/sensorlib/ak8975.c @@ -0,0 +1,558 @@ +//***************************************************************************** +// +// ak8975.c - Driver for the AK8975 magnetometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_ak8975.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/ak8975.h" + +//***************************************************************************** +// +//! \addtogroup ak8975_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the AK8975 state machine. +// +//***************************************************************************** +#define AK8975_STATE_IDLE 0 // State machine is idle +#define AK8975_STATE_READ 1 // Waiting for read +#define AK8975_STATE_WRITE 2 // Waiting for write +#define AK8975_STATE_RMW 3 // Waiting for read-modify-write + +//***************************************************************************** +// +// Converting sensor data to tesla (0.3 uT per LSB) +// +//***************************************************************************** +#define CONVERT_TO_TESLA 0.0000003 + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// AK8975 have completed. +// +//***************************************************************************** +static void +AK8975Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tAK8975 *psInst; + + // + // Convert the instance data into a pointer to a tAK8975 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = AK8975_STATE_IDLE; + } + + // + // Determine the current state of the AK8975 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case AK8975_STATE_READ: + case AK8975_STATE_WRITE: + case AK8975_STATE_RMW: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = AK8975_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == AK8975_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the AK8975 driver. +//! +//! \param psInst is a pointer to the AK8975 instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the AK8975 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the AK8975 driver, preparing it for operation. +//! +//! \return Returns 1 if the AK8975 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +AK8975Init(tAK8975 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Initialize the AK8975 instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = AK8975_STATE_IDLE; + + // + // The default settings are ok. Return success and call the callback. + // + if(pfnCallback) + { + pfnCallback(pvCallbackData, 0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from AK8975 registers. +//! +//! \param psInst is a pointer to the AK8975 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the AK8975. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +AK8975Read(tAK8975 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the AK8975 driver is not idle (in other words, there + // is already an outstanding request to the AK8975). + // + if(psInst->ui8State != AK8975_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = AK8975_STATE_READ; + + // + // Read the requested registers from the AK8975. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + AK8975Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = AK8975_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to AK8975 registers. +//! +//! \param psInst is a pointer to the AK8975 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the AK8975. The first byte of the \e pui8Data buffer contains the value to +//! be written into the \e ui8Reg register, the second value contains the data +//! to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +AK8975Write(tAK8975 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the AK8975 driver is not idle (in other words, there + // is already an outstanding request to the AK8975). + // + if(psInst->ui8State != AK8975_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = AK8975_STATE_WRITE; + + // + // Write the requested registers to the AK8975. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, AK8975Callback, + psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = AK8975_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of an AK8975 register. +//! +//! \param psInst is a pointer to the AK8975 instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the AK8975 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! AK8975. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +AK8975ReadModifyWrite(tAK8975 *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the AK8975 driver is not idle (in other words, there + // is already an outstanding request to the AK8975). + // + if(psInst->ui8State != AK8975_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = AK8975_STATE_RMW; + + // + // Submit the read-modify-write request to the AK8975. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, AK8975Callback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = AK8975_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the magnetometer data from the AK8975. +//! +//! \param psInst is a pointer to the AK8975 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the AK8975 data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - AK8975DataMagnetoGetRaw() +//! - AK8975DataMagnetoGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +AK8975DataRead(tAK8975 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the AK8975 driver is not idle (in other words, there + // is already an outstanding request to the AK8975). + // + if(psInst->ui8State != AK8975_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = AK8975_STATE_READ; + + // + // Read the data registers from the AK8975. + // + // ST1 + (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + ST2 = 8 bytes + // + psInst->pui8Data[0] = AK8975_O_ST1; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, 1, + psInst->pui8Data, 8, AK8975Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = AK8975_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw magnetometer data from the most recent data read. +//! +//! \param psInst is a pointer to the AK8975 instance data. +//! \param pui16MagnetoX is a pointer to the value into which the raw X-axis +//! magnetometer data is stored. +//! \param pui16MagnetoY is a pointer to the value into which the raw Y-axis +//! magnetometer data is stored. +//! \param pui16MagnetoZ is a pointer to the value into which the raw Z-axis +//! magnetometer data is stored. +//! +//! This function returns the raw magnetometer data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +AK8975DataMagnetoGetRaw(tAK8975 *psInst, uint_fast16_t *pui16MagnetoX, + uint_fast16_t *pui16MagnetoY, + uint_fast16_t *pui16MagnetoZ) +{ + // + // Return the raw magnetometer values. + // + if(pui16MagnetoX) + { + *pui16MagnetoX = (psInst->pui8Data[2] << 8) | psInst->pui8Data[1]; + } + if(pui16MagnetoY) + { + *pui16MagnetoY = (psInst->pui8Data[4] << 8) | psInst->pui8Data[3]; + } + if(pui16MagnetoZ) + { + *pui16MagnetoZ = (psInst->pui8Data[6] << 8) | psInst->pui8Data[5]; + } +} + +//***************************************************************************** +// +//! Gets the magnetometer data from the most recent data read. +//! +//! \param psInst is a pointer to the AK8975 instance data. +//! \param pfMagnetoX is a pointer to the value into which the X-axis +//! magnetometer data is stored. +//! \param pfMagnetoY is a pointer to the value into which the Y-axis +//! magnetometer data is stored. +//! \param pfMagnetoZ is a pointer to the value into which the Z-axis +//! magnetometer data is stored. +//! +//! This function returns the magnetometer data from the most recent data read, +//! converted into tesla. If any of the output data pointers are +//! \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +AK8975DataMagnetoGetFloat(tAK8975 *psInst, float *pfMagnetoX, + float *pfMagnetoY, float *pfMagnetoZ) +{ + // + // Convert the magnetometer values into floating-point tesla values. + // + if(pfMagnetoX) + { + *pfMagnetoX = ((float)(int16_t)((psInst->pui8Data[2] << 8) | + psInst->pui8Data[1]) * + CONVERT_TO_TESLA); + } + if(pfMagnetoY) + { + *pfMagnetoY = ((float)(int16_t)((psInst->pui8Data[4] << 8) | + psInst->pui8Data[3]) * + CONVERT_TO_TESLA); + } + if(pfMagnetoZ) + { + *pfMagnetoZ = ((float)(int16_t)((psInst->pui8Data[6] << 8) | + psInst->pui8Data[5]) * + CONVERT_TO_TESLA); + } +} + +//***************************************************************************** +// +//! Gets the status registers from the most recent data read. +//! +//! \param psInst is a pointer to the AK8975 instance data. +//! \param pui8Status1 is a pointer to the value into which the ST1 data is +//! stored. +//! \param pui8Status2 is a pointer to the value into which the ST2 data is +//! stored. +//! +//! This function returns the magnetometer status registers from the most +//! recent data read. If any of the output data pointers are \b NULL, the +//! corresponding data is not provided. +//! +//! Note that the AKM comp routines require ST1 and ST2, so we read +//! them for that reason. +//! +//! \return None. +// +//***************************************************************************** +void +AK8975DataGetStatus(tAK8975 *psInst, uint_fast8_t *pui8Status1, + uint_fast8_t *pui8Status2) +{ + // + // Return the status registers + // + if(pui8Status1) + { + *pui8Status1 = psInst->pui8Data[0]; + } + if(pui8Status2) + { + *pui8Status2 = psInst->pui8Data[7]; + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/ak8975.h b/sensorlib/ak8975.h new file mode 100644 index 0000000..97c2893 --- /dev/null +++ b/sensorlib/ak8975.h @@ -0,0 +1,148 @@ +//***************************************************************************** +// +// ak8975.h - Prototypes for the AK8975 magnetometer driver. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_AK8975_H__ +#define __SENSORLIB_AK8975_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the AK8975 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the AK8975. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the AK8975. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the AK8975. + // + uint8_t ui8State; + + // + // The data buffer used for sending/receiving data to/from the AK8975. + // + uint8_t pui8Data[8]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The callback data provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tAK8975; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t AK8975Init(tAK8975 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t AK8975Read(tAK8975 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t AK8975Write(tAK8975 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t AK8975ReadModifyWrite(tAK8975 *psInst, + uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t AK8975DataRead(tAK8975 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void AK8975DataMagnetoGetRaw(tAK8975 *psInst, + uint_fast16_t *pui16MagnetoX, + uint_fast16_t *pui16MagnetoY, + uint_fast16_t *pui16MagnetoZ); +extern void AK8975DataMagnetoGetFloat(tAK8975 *psInst, float *pfMagnetoX, + float *pfMagnetoY, float *pfMagnetoZ); +extern void AK8975DataGetStatus(tAK8975 *psInst, uint_fast8_t *pui8Status1, + uint_fast8_t *pui8Status2); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_AK8975_H__ diff --git a/sensorlib/bmp180.c b/sensorlib/bmp180.c new file mode 100644 index 0000000..a25701e --- /dev/null +++ b/sensorlib/bmp180.c @@ -0,0 +1,919 @@ +//***************************************************************************** +// +// bmp180.c - Driver for the BMP180 pressure sensor. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "sensorlib/hw_bmp180.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/bmp180.h" + +//***************************************************************************** +// +//! \addtogroup bmp180_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the BMP180 state machine. +// +//***************************************************************************** +#define BMP180_STATE_IDLE 0 // State machine is idle +#define BMP180_STATE_INIT1 1 // Waiting for initialization 1 +#define BMP180_STATE_INIT2 2 // Waiting for initialization 2 +#define BMP180_STATE_READ 3 // Waiting for read +#define BMP180_STATE_WRITE 4 // Waiting for write +#define BMP180_STATE_RMW 5 // Waiting for read-modify-write +#define BMP180_STATE_REQ_TEMP 6 // Requested temperature +#define BMP180_STATE_WAIT_TEMP 7 // Waiting for temperature ready +#define BMP180_STATE_READ_TEMP 8 // Reading temperature value +#define BMP180_STATE_REQ_PRES 9 // Requested pressure +#define BMP180_STATE_WAIT_PRES 10 // Waiting for pressure ready +#define BMP180_STATE_READ_PRES 11 // Reading pressure value + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// BMP180 have completed. +// +//***************************************************************************** +static void +BMP180Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tBMP180 *psInst; + uint16_t ui16ReadVerify; + + // + // Convert the instance data into a pointer to a tBMP180 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = BMP180_STATE_IDLE; + } + + // + // Determine the current state of the BMP180 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case BMP180_STATE_READ: + case BMP180_STATE_READ_PRES: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = BMP180_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // The first step of initialization has just completed. + // + case BMP180_STATE_INIT1: + { + // + // Read the calibration data from the BMP180. + // + psInst->pui8Data[0] = BMP180_O_AC1_MSB; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, 1, + psInst->uCommand.pui8Buffer, 22, BMP180Callback, psInst); + + // + // Move to the wait for initialization step 2 state. + // + psInst->ui8State = BMP180_STATE_INIT2; + + // + // Done. + // + break; + } + + // + // The second step of initialization has just completed. + // + case BMP180_STATE_INIT2: + { + // + // Data communication is checked by verifying that the calibration + // data is neither 0 nor 0xFFFF. This is used to check that reset + // is complete and the part is ready. It also verifies that we + // have valid calibration data before proceeding. + // + ui16ReadVerify = psInst->uCommand.pui8Buffer[0]; + ui16ReadVerify <<= 8; + ui16ReadVerify |= psInst->uCommand.pui8Buffer[1]; + if((ui16ReadVerify == 0) || (ui16ReadVerify == 0xFFFF)) + { + // + // Reread the calibration data from the BMP180. + // + psInst->pui8Data[0] = BMP180_O_AC1_MSB; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, + 1, psInst->uCommand.pui8Buffer, 22, BMP180Callback, + psInst); + } + else + { + // + // Extract the calibration data from the data that was read. + // + psInst->i16AC1 = + (int16_t)((psInst->uCommand.pui8Buffer[0] << 8) | + psInst->uCommand.pui8Buffer[1]); + psInst->i16AC2 = + (int16_t)((psInst->uCommand.pui8Buffer[2] << 8) | + psInst->uCommand.pui8Buffer[3]); + psInst->i16AC3 = + (int16_t)((psInst->uCommand.pui8Buffer[4] << 8) | + psInst->uCommand.pui8Buffer[5]); + psInst->ui16AC4 = + (uint16_t)((psInst->uCommand.pui8Buffer[6] << 8) | + psInst->uCommand.pui8Buffer[7]); + psInst->ui16AC5 = + (uint16_t)((psInst->uCommand.pui8Buffer[8] << 8) | + psInst->uCommand.pui8Buffer[9]); + psInst->ui16AC6 = + (uint16_t)((psInst->uCommand.pui8Buffer[10] << 8) | + psInst->uCommand.pui8Buffer[11]); + psInst->i16B1 = + (int16_t)((psInst->uCommand.pui8Buffer[12] << 8) | + psInst->uCommand.pui8Buffer[13]); + psInst->i16B2 = + (int16_t)((psInst->uCommand.pui8Buffer[14] << 8) | + psInst->uCommand.pui8Buffer[15]); + psInst->i16MC = + (int16_t)((psInst->uCommand.pui8Buffer[18] << 8) | + psInst->uCommand.pui8Buffer[19]); + psInst->i16MD = + (int16_t)((psInst->uCommand.pui8Buffer[20] << 8) | + psInst->uCommand.pui8Buffer[21]); + + // + // The state machine is now idle. + // + psInst->ui8State = BMP180_STATE_IDLE; + } + + // + // Done. + // + break; + } + + // + // A write has just completed. + // + case BMP180_STATE_WRITE: + { + // + // Set the mode to the new mode. If the register was not modified, + // the values will be the same so this has no effect. + // + psInst->ui8Mode = psInst->ui8NewMode; + + // + // The state machine is now idle. + // + psInst->ui8State = BMP180_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A read-modify-write has just completed. + // + case BMP180_STATE_RMW: + { + // + // See if the CTRL_MEAS register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + BMP180_O_CTRL_MEAS) + { + // + // Extract the measurement mode from the CTRL_MEAS register + // value. + // + psInst->ui8Mode = + (psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + BMP180_CTRL_MEAS_OSS_M); + } + + // + // The state machine is now idle. + // + psInst->ui8State = BMP180_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // The temperature has been requested. + // + case BMP180_STATE_REQ_TEMP: + { + // + // Read the control register to see if the temperature reading is + // available. + // + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, + psInst->uCommand.pui8Buffer + 1, 1, BMP180Callback, + psInst); + + // + // Move to the wait for temperature state. + // + psInst->ui8State = BMP180_STATE_WAIT_TEMP; + + // + // Done. + // + break; + } + + // + // Waiting for the temperature reading to be available. + // + case BMP180_STATE_WAIT_TEMP: + { + // + // See if the temperature reading is available. + // + if(psInst->uCommand.pui8Buffer[1] & BMP180_CTRL_MEAS_SCO) + { + // + // The temperature reading is not ready yet, so read the + // control register again. + // + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, + psInst->uCommand.pui8Buffer + 1, 1, BMP180Callback, + psInst); + } + else + { + // + // The temperature reading is ready, so read it now. + // + psInst->uCommand.pui8Buffer[0] = BMP180_O_OUT_MSB; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 2, + BMP180Callback, psInst); + + // + // Move to the temperature reading state. + // + psInst->ui8State = BMP180_STATE_READ_TEMP; + } + + // + // Done. + // + break; + } + + // + // The temperature reading has been retrieved. + // + case BMP180_STATE_READ_TEMP: + { + // + // Request the pressure reading from the BMP180. + // + psInst->uCommand.pui8Buffer[0] = BMP180_O_CTRL_MEAS; + psInst->uCommand.pui8Buffer[1] = (BMP180_CTRL_MEAS_SCO | + BMP180_CTRL_MEAS_PRESSURE | + psInst->ui8Mode); + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, BMP180Callback, psInst); + + // + // Move to the pressure reading request state. + // + psInst->ui8State = BMP180_STATE_REQ_PRES; + + // + // Done. + // + break; + } + + // + // The pressure has been requested. + // + case BMP180_STATE_REQ_PRES: + { + // + // Read the control register to see if the pressure reading is + // available. + // + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, + psInst->uCommand.pui8Buffer + 1, 1, BMP180Callback, + psInst); + + // + // Move to the wait for pressure state. + // + psInst->ui8State = BMP180_STATE_WAIT_PRES; + + // + // Done. + // + break; + } + + // + // Waiting for the pressure reading to be available. + // + case BMP180_STATE_WAIT_PRES: + { + // + // See if the pressure reading is available. + // + if(psInst->uCommand.pui8Buffer[1] & BMP180_CTRL_MEAS_SCO) + { + // + // The pressure reading is not ready yet, so read the control + // register again. + // + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, + psInst->uCommand.pui8Buffer + 1, 1, BMP180Callback, + psInst); + } + else + { + // + // The pressure reading is ready, so read it now. + // + psInst->uCommand.pui8Buffer[0] = BMP180_O_OUT_MSB; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data + 2, + 3, BMP180Callback, psInst); + + // + // Move to the pressure reading state. + // + psInst->ui8State = BMP180_STATE_READ_PRES; + } + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == BMP180_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the BMP180 driver. +//! +//! \param psInst is a pointer to the BMP180 instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the BMP180 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the BMP180 driver, preparing it for operation. +//! +//! \return Returns 1 if the BMP180 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +BMP180Init(tBMP180 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Initialize the BMP180 instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = BMP180_STATE_INIT1; + psInst->ui8Mode = 0; + psInst->ui8NewMode = 0; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Perform a soft reset of the BMP180. + // + psInst->pui8Data[0] = BMP180_O_SOFT_RESET; + psInst->pui8Data[1] = BMP180_SOFT_RESET_VALUE; + if(I2CMWrite(psI2CInst, ui8I2CAddr, psInst->pui8Data, 2, BMP180Callback, + psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = BMP180_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from BMP180 registers. +//! +//! \param psInst is a pointer to the BMP180 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the BMP180. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +BMP180Read(tBMP180 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the BMP180 driver is not idle (in other words, there + // is already an outstanding request to the BMP180). + // + if(psInst->ui8State != BMP180_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = BMP180_STATE_READ; + + // + // Read the requested registers from the BMP180. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + BMP180Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = BMP180_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to BMP180 registers. +//! +//! \param psInst is a pointer to the BMP180 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the BMP180. The first byte of the \e pui8Data buffer contains the value to +//! be written into the \e ui8Reg register, the second value contains the data +//! to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +BMP180Write(tBMP180 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the BMP180 driver is not idle (in other words, there + // is already an outstanding request to the BMP180). + // + if(psInst->ui8State != BMP180_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if the CTRL_MEAS register is being written. + // + if((ui8Reg <= BMP180_O_CTRL_MEAS) && + ((ui8Reg + ui16Count) > BMP180_O_CTRL_MEAS)) + { + // + // Extract the measurement mode from the CTRL_MEAS register value. + // + psInst->ui8NewMode = (pui8Data[ui8Reg - BMP180_O_CTRL_MEAS] & + BMP180_CTRL_MEAS_OSS_M); + } + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = BMP180_STATE_WRITE; + + // + // Write the requested registers to the BMP180. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, BMP180Callback, + psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = BMP180_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a BMP180 register. +//! +//! \param psInst is a pointer to the BMP180 instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the BMP180 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! BMP180. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +BMP180ReadModifyWrite(tBMP180 *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the BMP180 driver is not idle (in other words, there + // is already an outstanding request to the BMP180). + // + if(psInst->ui8State != BMP180_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = BMP180_STATE_RMW; + + // + // Submit the read-modify-write request to the BMP180. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, BMP180Callback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = BMP180_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the pressure data from the BMP180. +//! +//! \param psInst is a pointer to the BMP180 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the BMP180 data registers. When the +//! read has completed (as indicated by calling the callback function), the +//! new temperature and pressure readings can be obtained via: +//! +//! - BMP180DataPressureGetRaw() +//! - BMP180DataPressureGetFloat() +//! - BMP180DataTemperatureGetRaw() +//! - BMP180DataTemperatureGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +BMP180DataRead(tBMP180 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the BMP180 driver is not idle (in other words, there + // is already an outstanding request to the BMP180). + // + if(psInst->ui8State != BMP180_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the temperature reading request state. + // + psInst->ui8State = BMP180_STATE_REQ_TEMP; + + // + // Request the temperature reading from the BMP180. + // + psInst->uCommand.pui8Buffer[0] = BMP180_O_CTRL_MEAS; + psInst->uCommand.pui8Buffer[1] = (BMP180_CTRL_MEAS_SCO | + BMP180_CTRL_MEAS_TEMPERATURE); + if(I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, BMP180Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = BMP180_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw pressure data from the most recent data read. +//! +//! \param psInst is a pointer to the BMP180 instance data. +//! \param pui32Pressure is a pointer to the value into which the raw pressure +//! data is stored. +//! +//! This function returns the raw pressure data from the most recent data read. +//! The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BMP180DataPressureGetRaw(tBMP180 *psInst, uint_fast32_t *pui32Pressure) +{ + // + // Return the raw pressure value. + // + *pui32Pressure = ((psInst->pui8Data[2] << 16) | + (psInst->pui8Data[3] << 8) | + (psInst->pui8Data[4] & BMP180_OUT_XLSB_M)); +} + +//***************************************************************************** +// +//! Gets the pressure data from the most recent data read. +//! +//! \param psInst is a pointer to the BMP180 instance data. +//! \param pfPressure is a pointer to the value into which the pressure data is +//! stored. +//! +//! This function returns the pressure data from the most recent data read, +//! converted into pascals. +//! +//! \return None. +// +//***************************************************************************** +void +BMP180DataPressureGetFloat(tBMP180 *psInst, float *pfPressure) +{ + float fUT, fUP, fX1, fX2, fX3, fB3, fB4, fB5, fB6, fB7, fP; + int_fast8_t i8Oss; + + // + // Get the oversampling ratio. + // + i8Oss = psInst->ui8Mode >> BMP180_CTRL_MEAS_OSS_S; + + // + // Retrieve the uncompensated temperature and pressure. + // + fUT = (float)(uint16_t)((psInst->pui8Data[0] << 8) | + psInst->pui8Data[1]); + fUP = ((float)(int32_t)((psInst->pui8Data[2] << 16) | + (psInst->pui8Data[3] << 8) | + (psInst->pui8Data[4] & BMP180_OUT_XLSB_M)) / + (1 << (8 - i8Oss))); + + // + // Calculate the true temperature. + // + fX1 = ((fUT - (float)psInst->ui16AC6) * (float)psInst->ui16AC5) / 32768.f; + fX2 = ((float)psInst->i16MC * 2048.f) / (fX1 + (float)psInst->i16MD); + fB5 = fX1 + fX2; + + // + // Calculate the true pressure. + // + fB6 = fB5 - 4000; + fX1 = ((float)psInst->i16B2 * ((fB6 * fB6) / 4096)) / 2048; + fX2 = ((float)psInst->i16AC2 * fB6) / 2048; + fX3 = fX1 + fX2; + fB3 = ((((float)psInst->i16AC1 * 4) + fX3) * (1 << i8Oss)) / 4; + fX1 = ((float)psInst->i16AC3 * fB6) / 8192; + fX2 = ((float)psInst->i16B1 * ((fB6 * fB6) / 4096)) / 65536; + fX3 = (fX1 + fX2) / 4; + fB4 = (float)psInst->ui16AC4 * ((fX3 / 32768) + 1); + fB7 = (fUP - fB3) * (50000 >> i8Oss); + fP = (fB7 * 2) / fB4; + fX1 = (fP / 256) * (fP / 256); + fX1 = (fX1 * 3038) / 65536; + fX2 = (fP * -7357) / 65536; + fP += (fX1 + fX2 + 3791) / 16; + *pfPressure = fP; +} + +//***************************************************************************** +// +//! Gets the raw temperature data from the most recent data read. +//! +//! \param psInst is a pointer to the BMP180 instance data. +//! \param pui16Temperature is a pointer to the value into which the raw +//! temperature data is stored. +//! +//! This function returns the raw temperature data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BMP180DataTemperatureGetRaw(tBMP180 *psInst, uint_fast16_t *pui16Temperature) +{ + // + // Return the raw temperature value. + // + *pui16Temperature = (psInst->pui8Data[0] << 8) | psInst->pui8Data[1]; +} + +//***************************************************************************** +// +//! Gets the temperature data from the most recent data read. +//! +//! \param psInst is a pointer to the BMP180 instance data. +//! \param pfTemperature is a pointer to the value into which the temperature +//! data is stored. +//! +//! This function returns the temperature data from the most recent data read, +//! converted into Celsius. +//! +//! \return None. +// +//***************************************************************************** +void +BMP180DataTemperatureGetFloat(tBMP180 *psInst, float *pfTemperature) +{ + float fUT, fX1, fX2, fB5; + + // + // Get the uncompensated temperature. + // + fUT = (float)(uint16_t)((psInst->pui8Data[0] << 8) | + psInst->pui8Data[1]); + + // + // Calculate the true temperature. + // + fX1 = ((fUT - (float)psInst->ui16AC6) * (float)psInst->ui16AC5) / 32768.f; + fX2 = ((float)psInst->i16MC * 2048.f) / (fX1 + (float)psInst->i16MD); + fB5 = fX1 + fX2; + *pfTemperature = fB5 / 160.f; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/bmp180.h b/sensorlib/bmp180.h new file mode 100644 index 0000000..c56b4df --- /dev/null +++ b/sensorlib/bmp180.h @@ -0,0 +1,207 @@ +//***************************************************************************** +// +// bmp180.h - Prototypes for the BMP180 pressure sensor driver. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_BMP180_H__ +#define __SENSORLIB_BMP180_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the BMP180 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the BMP180. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the BMP180. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the BMP180. + // + uint8_t ui8State; + + // + // The sampling mode to be used by the BMP180. + // + uint8_t ui8Mode; + + // + // The new sampling mode, which is used when a register write succeeds. + // + uint8_t ui8NewMode; + + // + // The AC1 calibration from the BMP180. + // + int16_t i16AC1; + + // + // The AC2 calibration from the BMP180. + // + int16_t i16AC2; + + // + // The AC3 calibration from the BMP180. + // + int16_t i16AC3; + + // + // The AC4 calibration from the BMP180. + // + uint16_t ui16AC4; + + // + // The AC5 calibration from the BMP180. + // + uint16_t ui16AC5; + + // + // The AC6 calibration from the BMP180. + // + uint16_t ui16AC6; + + // + // The B1 calibration from the BMP180. + // + int16_t i16B1; + + // + // The B2 calibration from the BMP180. + // + int16_t i16B2; + + // + // The MC calibration from the BMP180. + // + int16_t i16MC; + + // + // The MD calibration from the BMP180. + // + int16_t i16MD; + + // + // The data buffer used for sending/receiving data to/from the BMP180. + // + uint8_t pui8Data[5]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. This + // is also used to read back the calibration data from the device. + // + uint8_t pui8Buffer[22]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tBMP180; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t BMP180Init(tBMP180 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t BMP180Read(tBMP180 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t BMP180Write(tBMP180 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t BMP180ReadModifyWrite(tBMP180 *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t BMP180DataRead(tBMP180 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void BMP180DataPressureGetRaw(tBMP180 *psInst, + uint_fast32_t *pui32Pressure); +extern void BMP180DataPressureGetFloat(tBMP180 *psInst, float *pfPressure); +extern void BMP180DataTemperatureGetRaw(tBMP180 *psInst, + uint_fast16_t *pui16Temperature); +extern void BMP180DataTemperatureGetFloat(tBMP180 *psInst, + float *pfTemperature); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_BMP180_H__ diff --git a/sensorlib/bq27510g3.c b/sensorlib/bq27510g3.c new file mode 100644 index 0000000..b9453b3 --- /dev/null +++ b/sensorlib/bq27510g3.c @@ -0,0 +1,1398 @@ +//***************************************************************************** +// +// bq27510g3.c - Driver for the TI BQ27510G3 Battery Fuel Gauge +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "sensorlib/hw_bq27510g3.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/bq27510g3.h" + +//***************************************************************************** +// +//! \addtogroup bq27510g3_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the BQ27510G3 state machine. +// +//***************************************************************************** +#define BQ27510G3_STATE_IDLE 0 +#define BQ27510G3_STATE_INIT 1 +#define BQ27510G3_STATE_READ 2 +#define BQ27510G3_STATE_WRITE 3 +#define BQ27510G3_STATE_RMW 4 +#define BQ27510G3_STATE_READ_DATA_1 5 +#define BQ27510G3_STATE_READ_DATA_2 6 +#define BQ27510G3_STATE_READ_DATA_3 7 + +//***************************************************************************** +// +// The constants used to calculate object temperature. +// +//***************************************************************************** +#define T_REF 273 + +//***************************************************************************** +// +// The callback function that is called when I2C transactions to/from the +// BQ27510G3 have completed. +// +//***************************************************************************** +static void +BQ27510G3Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tBQ27510G3 *psInst; + + // + // Convert the instance data into a pointer to a tBQ27510G3 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = BQ27510G3_STATE_IDLE; + } + + // + // Determine the current state of the BQ27510G3 state machine. + // + switch(psInst->ui8State) + { + // + // The first data read state, has finished setup and trigger data read + // state 2. + // + case BQ27510G3_STATE_READ_DATA_1: + { + // + // Move the state machine to the next read state. + // + psInst->ui8State = BQ27510G3_STATE_READ_DATA_2; + + // + // Read the requested data from the BQ27510G3. + // + psInst->uCommand.pui8Buffer[0] = BQ27510G3_O_NOM_AV_CAP_LSB; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data + 6, + 24, BQ27510G3Callback, psInst); + + // + // break + // + break; + } + + // + // The 2nd data read state, has finished setup and trigger data read + // state 3. Read state 3 is the final state and when done will return + // to idle and trigger the application level callback. + // + case BQ27510G3_STATE_READ_DATA_2: + { + // + // Move the state machine to the next read state. + // + psInst->ui8State = BQ27510G3_STATE_READ_DATA_3; + + // + // Read the requested data from the BQ27510G3. + // + psInst->uCommand.pui8Buffer[0] = BQ27510G3_O_INT_TEMP_LSB; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data + 30, + 2, BQ27510G3Callback, psInst); + + // + // break + // + break; + } + + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case BQ27510G3_STATE_INIT: + case BQ27510G3_STATE_READ: + case BQ27510G3_STATE_WRITE: + case BQ27510G3_STATE_READ_DATA_3: + case BQ27510G3_STATE_RMW: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = BQ27510G3_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == BQ27510G3_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the BQ27510G3 driver. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param psI2CInst is a pointer to the I2C driver instance data. +//! \param ui8I2CAddr is the I2C address of the BQ27510G3 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the BQ27510G3 driver, preparing it for operation. +//! +//! \return Returns 1 if the BQ27510G3 driver was successfully initialized and +//! 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +BQ27510G3Init(tBQ27510G3 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Initialize the BQ27510G3 instance structure + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = BQ27510G3_STATE_IDLE; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // The default settings are ok. Return success and call the callback. + // + if(pfnCallback) + { + pfnCallback(pvCallbackData, 0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from BQ27510G3 registers. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui16Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count the number of register values to read. +//! \param pfnCallback is the function to be called when data read is complete +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the BQ27510G3. +//! +//! \note The BQ27510G3 does not auto-increment the register pointer, so reads +//! of more than one value returns garbage for the subsequent values. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +BQ27510G3Read(tBQ27510G3 *psInst, uint_fast8_t ui8Reg, uint16_t *pui16Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the BQ27510G3 driver is not idle (in other words, + // there is already an outstanding request to the BQ27510G3). + // + if(psInst->ui8State != BQ27510G3_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = BQ27510G3_STATE_READ; + + // + // Read the requested registers from the BQ27510G3. + // + if(I2CMRead16BE(&(psInst->uCommand.sReadState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui16Data, ui16Count, + BQ27510G3Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = BQ27510G3_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to BQ27510G3 registers. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui16Data is a pointer to the 16-bit register data to write. +//! \param ui16Count is the number of 16-bit registers to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the BQ27510G3. The first value in the \e pui16Data buffer contains the +//! data to be written into the \e ui8Reg register, the second value contains +//! the data to be written into the next register, and so on. +//! +//! \note The BQ27510G3 does not auto-increment the register pointer, so writes +//! of more than one register are rejected by the BQ27510G3. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +BQ27510G3Write(tBQ27510G3 *psInst, uint_fast8_t ui8Reg, + const uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the BQ27510G3 driver is not idle (in other words, + // there is already an outstanding request to the BQ27510G3). + // + if(psInst->ui8State != BQ27510G3_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = BQ27510G3_STATE_WRITE; + + // + // Write the requested registers to the BQ27510G3. + // + if(I2CMWrite16BE(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui16Data, ui16Count, + BQ27510G3Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = BQ27510G3_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a BQ27510G3 register. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param ui8Reg is the register offset to read modify and write +//! \param ui16Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui16Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the BQ27510G3 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui16Mask, ORed with \e ui16Value, and then written back to the +//! BQ27510G3. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +BQ27510G3ReadModifyWrite(tBQ27510G3 *psInst, uint_fast8_t ui8Reg, + uint_fast16_t ui16Mask, uint_fast16_t ui16Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the BQ27510G3 driver is not idle (in other words, + // there is already an outstanding request to the BQ27510G3). + // + if(psInst->ui8State != BQ27510G3_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = BQ27510G3_STATE_RMW; + + // + // Submit the read-modify-write request to the BQ27510G3. + // + if(I2CMReadModifyWrite16BE(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui16Mask, ui16Value, BQ27510G3Callback, + psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = BQ27510G3_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read of a BQ27510G3 data register. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the BQ27510G3 data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via functions like: +//! +//! - BQ27510G3DataTCurrentInstantaneousGetRaw() +//! - BQ27510G3DataTCurrentInstantaneousGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +BQ27510G3DataRead(tBQ27510G3 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the BQ27510G3 driver is not idle (in other words, + // there is already an outstanding request to the BQ27510G3). + // + if(psInst->ui8State != BQ27510G3_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the first read state. Reads are done in three + // parts based on address ranges of the information being read. + // + psInst->ui8State = BQ27510G3_STATE_READ_DATA_1; + + // + // Read the requested data from the BQ27510G3. + // + psInst->uCommand.pui8Buffer[0] = BQ27510G3_O_AT_RATE_TTE_LSB; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 6, + BQ27510G3Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = BQ27510G3_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw "at rate time to empty" data. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataAtRateTimeToEmptyGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[1] << 8) | psInst->pui8Data[0]; +} + +//***************************************************************************** +// +//! Gets the "at rate time to empty" data as a floating point value. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are minutes. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataAtRateTimeToEmptyGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataAtRateTimeToEmptyGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + +} + +//***************************************************************************** +// +//! Gets the raw battery temperature from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataTemperatureBatteryGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[3] << 8) | psInst->pui8Data[2]; +} + +//***************************************************************************** +// +//! Gets the battery temperature measurement data from the most recent data +//! read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are degrees Celsius. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataTemperatureBatteryGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataTemperatureBatteryGetRaw(psInst, &i16Data); + + // + // Device returns the units as 0.1 degrees K. Convert first to whole + // degrees then from K to C + // + *pfData = (float)(i16Data); + *pfData = *pfData / 10.0f; + *pfData -= 272.15f; + + +} + +//***************************************************************************** +// +//! Gets the raw battery voltage measurement data from the most recent data +//! read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataVoltageBatteryGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[5] << 8) | psInst->pui8Data[4]; +} + +//***************************************************************************** +// +//! Gets the battery voltage measurement from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are volts. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataVoltageBatteryGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataVoltageBatteryGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + *pfData = *pfData / 1000.0f; + +} + +//***************************************************************************** +// +//! Gets the raw nominal available capacity measurement from the most recent +//! data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCapacityNominalAvailableGetRaw(tBQ27510G3 *psInst, + int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[7] << 8) | psInst->pui8Data[6]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are amp-hours (Ah). +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCapacityNominalAvailableGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataCapacityNominalAvailableGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + *pfData = *pfData / 1000.0f; + +} + +//***************************************************************************** +// +//! Gets the raw available capacity of a new battery from the most recent data +//! read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCapacityFullAvailableGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[9] << 8) | psInst->pui8Data[8]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are amp-hours (Ah). +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCapacityFullAvailableGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataCapacityFullAvailableGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + *pfData = *pfData / 1000.0f; + +} + +//***************************************************************************** +// +//! Gets the raw remaining capacity of measurement from the most recent data +//! read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCapacityRemainingGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[11] << 8) | psInst->pui8Data[10]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are amp-hours (Ah). +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCapacityRemainingGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataCapacityRemainingGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + *pfData = *pfData / 1000.0f; + +} + +//***************************************************************************** +// +//! Gets the raw full charge capacity from the most recent data +//! read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCapacityFullChargeGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[13] << 8) | psInst->pui8Data[12]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are amp-hours (Ah). +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCapacityFullChargeGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataCapacityFullChargeGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + *pfData = *pfData / 1000.0f; + +} + + +//***************************************************************************** +// +//! Gets the raw average current measurement from the most recent data +//! read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCurrentAverageGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[15] << 8) | psInst->pui8Data[14]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are amps. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCurrentAverageGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataCurrentAverageGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + *pfData = *pfData / 1000.0f; + +} + + +//***************************************************************************** +// +//! Gets the raw time to empty estimate from the most recent data +//! read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataTimeToEmptyGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[17] << 8) | psInst->pui8Data[16]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are minutes. Value of 65,535 indicates +//! battery is not being discharged. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataTimeToEmptyGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataTimeToEmptyGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + +} + +//***************************************************************************** +// +//! Gets the raw standby current from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCurrentStandbyGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[19] << 8) | psInst->pui8Data[18]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are amps. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCurrentStandbyGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataCurrentStandbyGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + *pfData = *pfData / 1000.0f; + +} + +//***************************************************************************** +// +//! Gets the raw standby time to empty data from the most recent data +//! read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataTimeToEmptyStandbyGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[21] << 8) | psInst->pui8Data[20]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, +//! converted into float value. Units are minutes. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataTimeToEmptyStandbyGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataTimeToEmptyStandbyGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + +} + +//***************************************************************************** +// +//! Gets the raw cycle count data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCycleCountGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[25] << 8) | psInst->pui8Data[24]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the data from the most recent data read, converted +//! into float value. This data does not have units. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCycleCountGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataCycleCountGetRaw(psInst, &i16Data); + + // + // Covert to float. + // + *pfData = (float)(i16Data); + +} + +//***************************************************************************** +// +//! Gets the raw health data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataHealthGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[23] << 8) | psInst->pui8Data[22]; +} + +//***************************************************************************** +// +//! Gets the health data from the most recent health data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfHealth is a pointer to the value into which the battery +//! health data is stored as floating point ratio of current/design capacity. +//! +//! This function returns the health data from the most recent data read, +//! converted into percent health. The health status bits are dropped. These +//! can be obtained with BQ27510G3DataHealthGetRaw function. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataHealthGetFloat(tBQ27510G3 *psInst, float *pfHealth) +{ + int16_t i16Health; + + // + // Get the raw readings. + // + BQ27510G3DataHealthGetRaw(psInst, &i16Health); + + // + // Mask off health bit field + // + *pfHealth = (float)(i16Health & 0xFF); + +} + +//***************************************************************************** +// +//! Gets the raw charge state data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataChargeStateGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[27] << 8) | psInst->pui8Data[26]; +} + +//***************************************************************************** +// +//! Gets the charge state from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the charge state from the most recent data read, +//! converted into percent charged. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataChargeStateGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataChargeStateGetRaw(psInst, &i16Data); + + // + // Convert to floating point. + // + *pfData = (float)(i16Data); + +} + +//***************************************************************************** +// +//! Gets the instantaneous current data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCurrentInstantaneousGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[29] << 8) | psInst->pui8Data[28]; +} + +//***************************************************************************** +// +//! Gets the instantaneous current data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the current measurement from the most recent data +//! read, converted into floating point amps. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataCurrentInstantaneousGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataCurrentInstantaneousGetRaw(psInst, &i16Data); + + // + // Convert to floating point. + // + *pfData = (float)(i16Data); + *pfData /= 1000.0f; + +} + +//***************************************************************************** +// +//! Gets the raw internal temparature data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pi16Data is a pointer to the value into which the raw data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataTemperatureInternalGetRaw(tBQ27510G3 *psInst, int16_t *pi16Data) +{ + // + // Return the raw data value. + // + *pi16Data = ((int16_t)psInst->pui8Data[31] << 8) | psInst->pui8Data[30]; +} + +//***************************************************************************** +// +//! Gets the internal temperature data from the most recent data read. +//! +//! \param psInst is a pointer to the BQ27510G3 instance data. +//! \param pfData is a pointer to the value into which the data is stored as +//! floating point. +//! +//! This function returns the internal temperature from the most recent data +//! read. +//! +//! \return None. +// +//***************************************************************************** +void +BQ27510G3DataTemperatureInternalGetFloat(tBQ27510G3 *psInst, float *pfData) +{ + int16_t i16Data; + + // + // Get the raw readings. + // + BQ27510G3DataTemperatureInternalGetRaw(psInst, &i16Data); + + // + // Convert to floating point Kelvin, then Celsius. + // + *pfData = (float)(i16Data); + *pfData = *pfData / 10.0f; + *pfData -= 272.15f; + + + +} +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/bq27510g3.h b/sensorlib/bq27510g3.h new file mode 100644 index 0000000..928da94 --- /dev/null +++ b/sensorlib/bq27510g3.h @@ -0,0 +1,210 @@ +//***************************************************************************** +// +// bq27510g3.c - Prototypes for the TI BQ27510G3 Battery Fuel Guage +// driver. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_BQ27510G3_H__ +#define __SENSORLIB_BQ27510G3_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the BQ27510G3 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the BQ27510G3. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the BQ27510G3. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the BQ27510G3. + // + uint8_t ui8State; + + // + // The data buffer used for sending/receiving data to/from the BQ27510G3. + // + uint8_t pui8Data[32]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[4]; + + // + // The read state used to read register values. + // + tI2CMRead16BE sReadState; + + // + // The write state used to write register values. + // + tI2CMWrite16BE sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite16 sReadModifyWriteState; + } + uCommand; +} +tBQ27510G3; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t BQ27510G3Init(tBQ27510G3 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t BQ27510G3Read(tBQ27510G3 *psInst, uint_fast8_t ui8Reg, + uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t BQ27510G3Write(tBQ27510G3 *psInst, uint_fast8_t ui8Reg, + const uint16_t *pui16Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t BQ27510G3ReadModifyWrite(tBQ27510G3 *psInst, + uint_fast8_t ui8Reg, + uint_fast16_t ui16Mask, + uint_fast16_t ui16Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t BQ27510G3DataRead(tBQ27510G3 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); + +extern void BQ27510G3DataAtRateTimeToEmptyGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataAtRateTimeToEmptyGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataTemperatureBatteryGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataTemperatureBatteryGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataVoltageBatteryGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataVoltageBatteryGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataCapacityNominalAvailableGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataCapacityNominalAvailalbeGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataCapacityFullAvailableGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataCapacityFullAvailableGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataCapacityRemainingGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataCapacityRemainingGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataCapacityFullChargeGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataCapacityFullChargeGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataCurrentAverageGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataCurrentAverageGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataTimeToEmptyGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataTimeToEmptyGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataCurrentStandbyGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataCurrentStandbyGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataTimeToEmptyStandbyGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataTimeToEmptyStandbyGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataCycleCountGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataCycleCountGetFloat(tBQ27510G3 *psInst, float *pfData); +extern void BQ27510G3DataHealthGetRaw(tBQ27510G3 *psInst, int16_t *pui16Data); +extern void BQ27510G3DataHealthGetFloat(tBQ27510G3 *psInst, float *pfData); +extern void BQ27510G3DataChargeStateGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataChargeStateGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataCurrentInstantaneousGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataCurrentInstantaneousGetFloat(tBQ27510G3 *psInst, + float *pfData); +extern void BQ27510G3DataTemperatureInternalGetRaw(tBQ27510G3 *psInst, + int16_t *pui16Data); +extern void BQ27510G3DataTemperatureInternalGetFloat(tBQ27510G3 *psInst, + float *pfData); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_BQ27510G3_H__ + diff --git a/sensorlib/ccs/.ccsproject b/sensorlib/ccs/.ccsproject new file mode 100644 index 0000000..b4eaafa --- /dev/null +++ b/sensorlib/ccs/.ccsproject @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sensorlib/ccs/.cproject b/sensorlib/ccs/.cproject new file mode 100644 index 0000000..10acdbf --- /dev/null +++ b/sensorlib/ccs/.cproject @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sensorlib/ccs/.project b/sensorlib/ccs/.project new file mode 100644 index 0000000..250724d --- /dev/null +++ b/sensorlib/ccs/.project @@ -0,0 +1,140 @@ + + + sensorlib + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + com.ti.ccstudio.core.ccsNature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + ak8963.c + 1 + SW_ROOT/sensorlib/ak8963.c + + + ak8975.c + 1 + SW_ROOT/sensorlib/ak8975.c + + + bmp180.c + 1 + SW_ROOT/sensorlib/bmp180.c + + + bq27510g3.c + 1 + SW_ROOT/sensorlib/bq27510g3.c + + + cm3218.c + 1 + SW_ROOT/sensorlib/cm3218.c + + + comp_dcm.c + 1 + SW_ROOT/sensorlib/comp_dcm.c + + + i2cm_drv.c + 1 + SW_ROOT/sensorlib/i2cm_drv.c + + + isl29023.c + 1 + SW_ROOT/sensorlib/isl29023.c + + + kxti9.c + 1 + SW_ROOT/sensorlib/kxti9.c + + + l3gd20h.c + 1 + SW_ROOT/sensorlib/l3gd20h.c + + + lsm303d.c + 1 + SW_ROOT/sensorlib/lsm303d.c + + + lsm303dlhc_accel.c + 1 + SW_ROOT/sensorlib/lsm303dlhc_accel.c + + + lsm303dlhc_mag.c + 1 + SW_ROOT/sensorlib/lsm303dlhc_mag.c + + + magneto.c + 1 + SW_ROOT/sensorlib/magneto.c + + + mpu6050.c + 1 + SW_ROOT/sensorlib/mpu6050.c + + + mpu9150.c + 1 + SW_ROOT/sensorlib/mpu9150.c + + + quaternion.c + 1 + SW_ROOT/sensorlib/quaternion.c + + + sht21.c + 1 + SW_ROOT/sensorlib/sht21.c + + + tmp006.c + 1 + SW_ROOT/sensorlib/tmp006.c + + + tmp100.c + 1 + SW_ROOT/sensorlib/tmp100.c + + + vector.c + 1 + SW_ROOT/sensorlib/vector.c + + + + + SW_ROOT + $%7BPARENT-2-PROJECT_LOC%7D + + + diff --git a/sensorlib/ccs/.settings/org.eclipse.cdt.codan.core.prefs b/sensorlib/ccs/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 0000000..98b6350 --- /dev/null +++ b/sensorlib/ccs/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +inEditor=false +onBuild=false diff --git a/sensorlib/ccs/Debug/sensorlib.lib b/sensorlib/ccs/Debug/sensorlib.lib new file mode 100644 index 0000000..953d9c6 Binary files /dev/null and b/sensorlib/ccs/Debug/sensorlib.lib differ diff --git a/sensorlib/ccs/macros.ini_initial b/sensorlib/ccs/macros.ini_initial new file mode 100644 index 0000000..08b716d --- /dev/null +++ b/sensorlib/ccs/macros.ini_initial @@ -0,0 +1 @@ +SW_ROOT = ../.. diff --git a/sensorlib/cm3218.c b/sensorlib/cm3218.c new file mode 100644 index 0000000..bb2251f --- /dev/null +++ b/sensorlib/cm3218.c @@ -0,0 +1,468 @@ +//***************************************************************************** +// +// cm3218.c - Driver for the CM3218 light sensor +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "sensorlib/hw_cm3218.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/cm3218.h" + +//***************************************************************************** +// +//! \addtogroup cm3218_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the CM3218 state machine. +// +//***************************************************************************** +#define CM3218_STATE_IDLE 0 +#define CM3218_STATE_INIT 1 +#define CM3218_STATE_READ 2 +#define CM3218_STATE_WRITE 3 + +//***************************************************************************** +// +// Sensitivity setting to floating point range value lookup table. +// +//***************************************************************************** +const float g_pfSensitivityLookup[4] = +{ + 0.02857, + 0.01328, + 0.00714, + 0.003571 +}; + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the CM3218 +// have completed. +// +//***************************************************************************** +static void +CM3218Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tCM3218 *psInst; + + // + // Convert the instance data into a pointer to a tCM3218 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = CM3218_STATE_IDLE; + } + + // + // Determine the current state of the CM3218 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case CM3218_STATE_INIT: + case CM3218_STATE_READ: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = CM3218_STATE_IDLE; + break; + } + + // + // A write has just completed. + // + case CM3218_STATE_WRITE: + { + // + // Set the integration time to the new integration time. If the + // register was not modified, the values will be the same so this + // has no effect. + // + psInst->ui8IntTime = psInst->ui8NewIntTime; + + // + // The state machine is now idle. + // + psInst->ui8State = CM3218_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == CM3218_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the CM3218 driver. +//! +//! \param psInst is a pointer to the CM3218 instance data. +//! \param psI2CInst is a pointer to the I2C driver instance data. +//! \param ui8I2CAddr is the I2C address of the CM3218 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the CM3218 driver, preparing it for operation. +//! +//! \return Returns 1 if the CM3218 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +CM3218Init(tCM3218 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Initialize the CM3218 instance structure + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = CM3218_STATE_IDLE; + psInst->ui8IntTime = CM3218_CMD_CONFIG_IT_10 >> CM3218_CMD_CONFIG_IT_S; + psInst->ui8NewIntTime = CM3218_CMD_CONFIG_IT_10 >> CM3218_CMD_CONFIG_IT_S; + + // + // The default settings are ok. Call the callback function if provided. + // + if(pfnCallback) + { + pfnCallback(pvCallbackData, 0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from CM3218 registers. +//! +//! \param psInst is a pointer to the CM3218 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui16Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count the number of register values bytes to read. +//! \param pfnCallback is the function to be called when data read is complete +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the CM3218. +//! +//! \note The CM3218 does not auto-increment the register pointer, so reads of +//! more than one value returns the same data. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +CM3218Read(tCM3218 *psInst, uint_fast8_t ui8Reg, uint16_t *pui16Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the CM3218 driver is not idle (in other words, there + // is already an outstanding request to the CM3218). + // + if(psInst->ui8State != CM3218_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = CM3218_STATE_READ; + + // + // Read the requested registers from the CM3218. + // + if(I2CMRead16BE(&(psInst->uCommand.sReadState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui16Data, ui16Count, + CM3218Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = CM3218_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to CM3218 registers. +//! +//! \param psInst is a pointer to the CM3218 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui16Data is a pointer to the 16-bit register data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the CM3218. The first value in the \e pui16Data buffer contains the data +//! to be written into the \e ui8Reg register, the second value contains the +//! data to be written into the next register, and so on. +//! +//! \note The CM3218 does not auto-increment the register pointer, so writes of +//! more than one register are rejected by the CM3218. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +CM3218Write(tCM3218 *psInst, uint_fast8_t ui8Reg, const uint16_t *pui16Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the CM3218 driver is not idle (in other words, there + // is already an outstanding request to the CM3218). + // + if(psInst->ui8State != CM3218_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if the CMD_CONFIG register is being written. + // + if((ui8Reg <= CM3218_CMD_CONFIG) && + ((ui8Reg + ui16Count) > CM3218_CMD_CONFIG)) + { + // + // Extract the integration time from the CMD_CONFIG register value. + // + psInst->ui8NewIntTime = ((pui16Data[ui8Reg - CM3218_CMD_CONFIG] & + CM3218_CMD_CONFIG_IT_M) >> + CM3218_CMD_CONFIG_IT_S); + } + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = CM3218_STATE_WRITE; + + // + // Write the requested registers to the CM3218. + // + if(I2CMWrite16BE(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui16Data, ui16Count, + CM3218Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = CM3218_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the light data from the CM3218. +//! +//! \param psInst is a pointer to the CM3218 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the CM3218 data registers. When the read +//! has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - CM3218DataLightVisibleGetRaw() +//! - CM3218DataLightVisibleGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +CM3218DataRead(tCM3218 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the CM3218 driver is not idle (in other words, there + // is already an outstanding request to the CM3218). + // + if(psInst->ui8State != CM3218_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read + // + psInst->ui8State = CM3218_STATE_READ; + + // + // Read the ambient light data from the CM3218. + // + psInst->uCommand.pui8Buffer[0] = CM3218_CMD_ALS_DATA; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 2, + CM3218Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = CM3218_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the CM3218 instance data. +//! \param pui16Visible is a pointer to the value into which the raw visible +//! light data is stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +CM3218DataLightVisibleGetRaw(tCM3218 *psInst, uint16_t *pui16Visible) +{ + // + // Return the raw Light value. + // + *pui16Visible = (psInst->pui8Data[1] << 8) | psInst->pui8Data[0]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the CM3218 instance data. +//! \param pfVisibleLight is a pointer to the value into which the light +//! data is stored as floating point lux. +//! +//! This function returns the light data from the most recent data read, +//! converted into lux. +//! +//! \return None. +// +//***************************************************************************** +void +CM3218DataLightVisibleGetFloat(tCM3218 *psInst, float *pfVisibleLight) +{ + uint16_t ui16Light; + float fSensitivity; + + // + // Get the raw light data from the instance structure + // + CM3218DataLightVisibleGetRaw(psInst, &ui16Light); + + // + // Get the floating point values for sensitivity + // + fSensitivity = g_pfSensitivityLookup[psInst->ui8IntTime]; + + // + // Calculate light reading in lux. + // + *pfVisibleLight = ((float)ui16Light) * fSensitivity; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/cm3218.h b/sensorlib/cm3218.h new file mode 100644 index 0000000..fad9f11 --- /dev/null +++ b/sensorlib/cm3218.h @@ -0,0 +1,149 @@ +//***************************************************************************** +// +// CM3218.h - Prototypes for the CM3218 light sensor +// driver. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_CM3218_H__ +#define __SENSORLIB_CM3218_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the CM3218 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the CM3218. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the CM3218. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the CM3218. + // + uint8_t ui8State; + + // + // The data buffer used for sending/receiving data to/from the CM3218. + // + uint8_t pui8Data[4]; + + // + // The integration time, which determines the sensitivity. + // + uint8_t ui8IntTime; + + // + // The new integration time, which is used when a register write succeeds. + // + uint8_t ui8NewIntTime; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read and write operations. + // Since only one operation can be active at a time, it is safe to re-use + // the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[4]; + + // + // The read state used to read register values. + // + tI2CMRead16BE sReadState; + + // + // The write state used to write register values. + // + tI2CMWrite16BE sWriteState; + } + uCommand; +} +tCM3218; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t CM3218Init(tCM3218 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t CM3218Read(tCM3218 *psInst, uint_fast8_t ui8Reg, + uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t CM3218Write(tCM3218 *psInst, uint_fast8_t ui8Reg, + const uint16_t *pui16Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t CM3218DataRead(tCM3218 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void CM3218DataLightVisibleGetRaw(tCM3218 *psInst, + uint16_t *pui16Visible); +extern void CM3218DataLightVisibleGetFloat(tCM3218 *psInst, float *pfVisible); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_CM3218_H__ diff --git a/sensorlib/comp_dcm.c b/sensorlib/comp_dcm.c new file mode 100644 index 0000000..42db71d --- /dev/null +++ b/sensorlib/comp_dcm.c @@ -0,0 +1,634 @@ +//***************************************************************************** +// +// comp_dcm.c - Complementary filter algorithm on a Direction Cosine Matrix for +// fusing sensor data from an accelerometer, gyroscope, and +// magnetometer. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include +#include "driverlib/debug.h" +#include "sensorlib/comp_dcm.h" +#include "sensorlib/vector.h" + +//***************************************************************************** +// +//! \addtogroup comp_dcm_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// If M_PI has not been defined by the system headers, define it here. +// +//***************************************************************************** +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +//***************************************************************************** +// +//! Initializes the complementary filter DCM attitude estimation state. +//! +//! \param psDCM is a pointer to the DCM state structure. +//! \param fDeltaT is the amount of time between DCM updates, in seconds. +//! \param fScaleA is the weight of the accelerometer reading in determining +//! the updated attitude estimation. +//! \param fScaleG is the weight of the gyroscope reading in determining the +//! updated attitude estimation. +//! \param fScaleM is the weight of the magnetometer reading in determining the +//! updated attitude estimation. +//! +//! This function initializes the complementary filter DCM attitude estimation +//! state, and must be called prior to performing any attitude estimation. +//! +//! New readings must be supplied to the complementary filter DCM attitude +//! estimation algorithm at the rate specified by the \e fDeltaT parameter. +//! Failure to provide new readings at this rate results in inaccuracies in the +//! attitude estimation. +//! +//! The \e fScaleA, \e fScaleG, and \e fScaleM weights must sum to one. +//! +//! \return None. +// +//***************************************************************************** +void +CompDCMInit(tCompDCM *psDCM, float fDeltaT, float fScaleA, float fScaleG, + float fScaleM) +{ + // + // Initialize the DCM matrix to the identity matrix. + // + psDCM->ppfDCM[0][0] = 1.0; + psDCM->ppfDCM[0][1] = 0.0; + psDCM->ppfDCM[0][2] = 0.0; + psDCM->ppfDCM[1][0] = 0.0; + psDCM->ppfDCM[1][1] = 1.0; + psDCM->ppfDCM[1][2] = 0.0; + psDCM->ppfDCM[2][0] = 0.0; + psDCM->ppfDCM[2][1] = 0.0; + psDCM->ppfDCM[2][2] = 1.0; + + // + // Save the time delta between DCM updates. + // + psDCM->fDeltaT = fDeltaT; + + // + // Save the scaling factors that are applied to the accelerometer, + // gyroscope, and magnetometer readings. + // + psDCM->fScaleA = fScaleA; + psDCM->fScaleG = fScaleG; + psDCM->fScaleM = fScaleM; +} + +//***************************************************************************** +// +//! Updates the accelerometer reading used by the complementary filter DCM +//! algorithm. +//! +//! \param psDCM is a pointer to the DCM state structure. +//! \param fAccelX is the accelerometer reading in the X body axis. +//! \param fAccelY is the accelerometer reading in the Y body axis. +//! \param fAccelZ is the accelerometer reading in the Z body axis. +//! +//! This function updates the accelerometer reading used by the complementary +//! filter DCM algorithm. The accelerometer readings provided to this function +//! are used by subsequent calls to CompDCMStart() and CompDCMUpdate() to +//! compute the attitude estimate. +//! +//! \return None. +// +//***************************************************************************** +void +CompDCMAccelUpdate(tCompDCM *psDCM, float fAccelX, float fAccelY, + float fAccelZ) +{ + // + // The user should never pass in values that are not-a-number + // + ASSERT(!isnan(fAccelX)); + ASSERT(!isnan(fAccelY)); + ASSERT(!isnan(fAccelZ)); + + // + // Save the new accelerometer reading. + // + psDCM->pfAccel[0] = fAccelX; + psDCM->pfAccel[1] = fAccelY; + psDCM->pfAccel[2] = fAccelZ; +} + +//***************************************************************************** +// +//! Updates the gyroscope reading used by the complementary filter DCM +//! algorithm. +//! +//! \param psDCM is a pointer to the DCM state structure. +//! \param fGyroX is the gyroscope reading in the X body axis. +//! \param fGyroY is the gyroscope reading in the Y body axis. +//! \param fGyroZ is the gyroscope reading in the Z body axis. +//! +//! This function updates the gyroscope reading used by the complementary +//! filter DCM algorithm. The gyroscope readings provided to this function are +//! used by subsequent calls to CompDCMUpdate() to compute the attitude +//! estimate. +//! +//! \return None. +// +//***************************************************************************** +void +CompDCMGyroUpdate(tCompDCM *psDCM, float fGyroX, float fGyroY, float fGyroZ) +{ + // + // The user should never pass in values that are not-a-number + // + ASSERT(!isnan(fGyroX)); + ASSERT(!isnan(fGyroY)); + ASSERT(!isnan(fGyroZ)); + + // + // Save the new gyroscope reading. + // + psDCM->pfGyro[0] = fGyroX; + psDCM->pfGyro[1] = fGyroY; + psDCM->pfGyro[2] = fGyroZ; +} + +//***************************************************************************** +// +//! Updates the magnetometer reading used by the complementary filter DCM +//! algorithm. +//! +//! \param psDCM is a pointer to the DCM state structure. +//! \param fMagnetoX is the magnetometer reading in the X body axis. +//! \param fMagnetoY is the magnetometer reading in the Y body axis. +//! \param fMagnetoZ is the magnetometer reading in the Z body axis. +//! +//! This function updates the magnetometer reading used by the complementary +//! filter DCM algorithm. The magnetometer readings provided to this function +//! are used by subsequent calls to CompDCMStart() and CompDCMUpdate() to +//! compute the attitude estimate. +//! +//! \return None. +// +//***************************************************************************** +void +CompDCMMagnetoUpdate(tCompDCM *psDCM, float fMagnetoX, float fMagnetoY, + float fMagnetoZ) +{ + // + // The user should never pass in values that are not-a-number + // + ASSERT(!isnan(fMagnetoX)); + ASSERT(!isnan(fMagnetoY)); + ASSERT(!isnan(fMagnetoZ)); + + // + // Save the new magnetometer reading. + // + psDCM->pfMagneto[0] = fMagnetoX; + psDCM->pfMagneto[1] = fMagnetoY; + psDCM->pfMagneto[2] = fMagnetoZ; +} + +//***************************************************************************** +// +//! Starts the complementary filter DCM attitude estimation from an initial +//! sensor reading. +//! +//! \param psDCM is a pointer to the DCM state structure. +//! +//! This function computes the initial complementary filter DCM attitude +//! estimation state based on the initial accelerometer and magnetometer +//! reading. While not necessary for the attitude estimation to converge, +//! using an initial state based on sensor readings results in quicker +//! convergence. +//! +//! \return None. +// +//***************************************************************************** +void +CompDCMStart(tCompDCM *psDCM) +{ + float pfI[3], pfJ[3], pfK[3]; + + // + // The magnetometer reading forms the initial I vector, pointing north. + // + pfI[0] = psDCM->pfMagneto[0]; + pfI[1] = psDCM->pfMagneto[1]; + pfI[2] = psDCM->pfMagneto[2]; + + // + // The accelerometer reading forms the initial K vector, pointing down. + // + pfK[0] = psDCM->pfAccel[0]; + pfK[1] = psDCM->pfAccel[1]; + pfK[2] = psDCM->pfAccel[2]; + + // + // Compute the initial J vector, which is the cross product of the K and I + // vectors. + // + VectorCrossProduct(pfJ, pfK, pfI); + + // + // Recompute the I vector from the cross product of the J and K vectors. + // This makes it fully orthogonal, which it wasn't before since magnetic + // north points inside the Earth in many places. + // + VectorCrossProduct(pfI, pfJ, pfK); + + // + // Normalize the I, J, and K vectors. + // + VectorScale(pfI, pfI, 1 / sqrtf(VectorDotProduct(pfI, pfI))); + VectorScale(pfJ, pfJ, 1 / sqrtf(VectorDotProduct(pfJ, pfJ))); + VectorScale(pfK, pfK, 1 / sqrtf(VectorDotProduct(pfK, pfK))); + + // + // Initialize the DCM matrix from the I, J, and K vectors. + // + psDCM->ppfDCM[0][0] = pfI[0]; + psDCM->ppfDCM[0][1] = pfI[1]; + psDCM->ppfDCM[0][2] = pfI[2]; + psDCM->ppfDCM[1][0] = pfJ[0]; + psDCM->ppfDCM[1][1] = pfJ[1]; + psDCM->ppfDCM[1][2] = pfJ[2]; + psDCM->ppfDCM[2][0] = pfK[0]; + psDCM->ppfDCM[2][1] = pfK[1]; + psDCM->ppfDCM[2][2] = pfK[2]; +} + +//***************************************************************************** +// +//! Updates the complementary filter DCM attitude estimation based on an +//! updated set of sensor readings. +//! +//! \param psDCM is a pointer to the DCM state structure. +//! +//! This function updates the complementary filter DCM attitude estimation +//! state based on the current sensor readings. This function must be called +//! at the rate specified to CompDCMInit(), with new readings supplied at an +//! appropriate rate (for example, magnetometers typically sample at a much +//! slower rate than accelerometers and gyroscopes). +//! +//! \return None. +// +//***************************************************************************** +void +CompDCMUpdate(tCompDCM *psDCM) +{ + float pfI[3], pfJ[3], pfK[3], pfDelta[3], pfTemp[3], fError; + bool bNAN; + + // + // The magnetometer reading forms the new Im vector, pointing north. + // + pfI[0] = psDCM->pfMagneto[0]; + pfI[1] = psDCM->pfMagneto[1]; + pfI[2] = psDCM->pfMagneto[2]; + + // + // The accelerometer reading forms the new Ka vector, pointing down. + // + pfK[0] = psDCM->pfAccel[0]; + pfK[1] = psDCM->pfAccel[1]; + pfK[2] = psDCM->pfAccel[2]; + + // + // Compute the new J vector, which is the cross product of the Ka and Im + // vectors. + // + VectorCrossProduct(pfJ, pfK, pfI); + + // + // Recompute the Im vector from the cross product of the J and Ka vectors. + // This makes it fully orthogonal, which it wasn't before since magnetic + // north points inside the Earth in many places. + // + VectorCrossProduct(pfI, pfJ, pfK); + + // + // Normalize the Im and Ka vectors. + // + VectorScale(pfI, pfI, 1 / sqrtf(VectorDotProduct(pfI, pfI))); + VectorScale(pfK, pfK, 1 / sqrtf(VectorDotProduct(pfK, pfK))); + + // + // Compute and scale the rotation as inferred from the accelerometer, + // storing it in the rotation accumulator. + // + VectorCrossProduct(pfTemp, psDCM->ppfDCM[2], pfK); + VectorScale(pfDelta, pfTemp, psDCM->fScaleA); + + // + // Compute and scale the rotation as measured by the gyroscope, adding it + // to the rotation accumulator. + // + pfTemp[0] = psDCM->pfGyro[0] * psDCM->fDeltaT * psDCM->fScaleG; + pfTemp[1] = psDCM->pfGyro[1] * psDCM->fDeltaT * psDCM->fScaleG; + pfTemp[2] = psDCM->pfGyro[2] * psDCM->fDeltaT * psDCM->fScaleG; + VectorAdd(pfDelta, pfDelta, pfTemp); + + // + // Compute and scale the rotation as inferred from the magnetometer, adding + // it to the rotation accumulator. + // + VectorCrossProduct(pfTemp, psDCM->ppfDCM[0], pfI); + VectorScale(pfTemp, pfTemp, psDCM->fScaleM); + VectorAdd(pfDelta, pfDelta, pfTemp); + + // + // Rotate the I vector from the DCM matrix by the scaled rotation. + // + VectorCrossProduct(pfI, pfDelta, psDCM->ppfDCM[0]); + VectorAdd(psDCM->ppfDCM[0], psDCM->ppfDCM[0], pfI); + + // + // Rotate the K vector from the DCM matrix by the scaled rotation. + // + VectorCrossProduct(pfK, pfDelta, psDCM->ppfDCM[2]); + VectorAdd(psDCM->ppfDCM[2], psDCM->ppfDCM[2], pfK); + + // + // Compute the orthogonality error between the rotated I and K vectors and + // adjust each by half the error, bringing them closer to orthogonality. + // + fError = VectorDotProduct(psDCM->ppfDCM[0], psDCM->ppfDCM[2]) / -2.0; + VectorScale(pfI, psDCM->ppfDCM[0], fError); + VectorScale(pfK, psDCM->ppfDCM[2], fError); + VectorAdd(psDCM->ppfDCM[0], psDCM->ppfDCM[0], pfK); + VectorAdd(psDCM->ppfDCM[2], psDCM->ppfDCM[2], pfI); + + // + // Normalize the I and K vectors. + // + VectorScale(psDCM->ppfDCM[0], psDCM->ppfDCM[0], + 0.5 * (3.0 - VectorDotProduct(psDCM->ppfDCM[0], + psDCM->ppfDCM[0]))); + VectorScale(psDCM->ppfDCM[2], psDCM->ppfDCM[2], + 0.5 * (3.0 - VectorDotProduct(psDCM->ppfDCM[2], + psDCM->ppfDCM[2]))); + + // + // Compute the rotated J vector from the cross product of the rotated, + // corrected K and I vectors. + // + VectorCrossProduct(psDCM->ppfDCM[1], psDCM->ppfDCM[2], psDCM->ppfDCM[0]); + + // + // Determine if the newly updated DCM contains any invalid (in other words, + // NaN) values. + // + bNAN = (isnan(psDCM->ppfDCM[0][0]) || + isnan(psDCM->ppfDCM[0][1]) || + isnan(psDCM->ppfDCM[0][2]) || + isnan(psDCM->ppfDCM[1][0]) || + isnan(psDCM->ppfDCM[1][1]) || + isnan(psDCM->ppfDCM[1][2]) || + isnan(psDCM->ppfDCM[2][0]) || + isnan(psDCM->ppfDCM[2][1]) || + isnan(psDCM->ppfDCM[2][2])); + + // + // As a debug measure, we check for NaN in the DCM. The user can trap + // this event depending on their implementation of __error__. Should they + // choose to disable interrupts and loop forever then they will have + // preserved the stack and can analyze how they arrived at NaN. + // + ASSERT(!bNAN); + + // + // If any part of the matrix is not-a-number then reset the DCM back to the + // identity matrix. + // + if(bNAN) + { + psDCM->ppfDCM[0][0] = 1.0; + psDCM->ppfDCM[0][1] = 0.0; + psDCM->ppfDCM[0][2] = 0.0; + psDCM->ppfDCM[1][0] = 0.0; + psDCM->ppfDCM[1][1] = 1.0; + psDCM->ppfDCM[1][2] = 0.0; + psDCM->ppfDCM[2][0] = 0.0; + psDCM->ppfDCM[2][1] = 0.0; + psDCM->ppfDCM[2][2] = 1.0; + } +} + +//***************************************************************************** +// +//! Returns the current DCM attitude estimation matrix. +//! +//! \param psDCM is a pointer to the DCM state structure. +//! \param ppfDCM is a pointer to the array into which to store the DCM matrix +//! values. +//! +//! This function returns the current value of the DCM matrix. +//! +//! \return None. +// +//***************************************************************************** +void +CompDCMMatrixGet(tCompDCM *psDCM, float ppfDCM[3][3]) +{ + // + // Return the current DCM matrix. + // + ppfDCM[0][0] = psDCM->ppfDCM[0][0]; + ppfDCM[0][1] = psDCM->ppfDCM[0][1]; + ppfDCM[0][2] = psDCM->ppfDCM[0][2]; + ppfDCM[1][0] = psDCM->ppfDCM[1][0]; + ppfDCM[1][1] = psDCM->ppfDCM[1][1]; + ppfDCM[1][2] = psDCM->ppfDCM[1][2]; + ppfDCM[2][0] = psDCM->ppfDCM[2][0]; + ppfDCM[2][1] = psDCM->ppfDCM[2][1]; + ppfDCM[2][2] = psDCM->ppfDCM[2][2]; +} + +//***************************************************************************** +// +//! Computes the Euler angles from the DCM attitude estimation matrix. +//! +//! \param psDCM is a pointer to the DCM state structure. +//! \param pfRoll is a pointer to the value into which the roll is stored. +//! \param pfPitch is a pointer to the value into which the pitch is stored. +//! \param pfYaw is a pointer to the value into which the yaw is stored. +//! +//! This function computes the Euler angles that are represented by the DCM +//! attitude estimation matrix. If any of the Euler angles is not required, +//! the corresponding parameter can be \b NULL. +//! +//! \return None. +// +//***************************************************************************** +void +CompDCMComputeEulers(tCompDCM *psDCM, float *pfRoll, float *pfPitch, + float *pfYaw) +{ + // + // Compute the roll, pitch, and yaw as required. + // + if(pfRoll) + { + *pfRoll = atan2f(psDCM->ppfDCM[2][1], psDCM->ppfDCM[2][2]); + } + if(pfPitch) + { + *pfPitch = -asinf(psDCM->ppfDCM[2][0]); + } + if(pfYaw) + { + *pfYaw = atan2f(psDCM->ppfDCM[1][0], psDCM->ppfDCM[0][0]); + } +} + +//***************************************************************************** +// +//! Computes the quaternion from the DCM attitude estimation matrix. +//! +//! \param psDCM is a pointer to the DCM state structure. +//! \param pfQuaternion is an array into which the quaternion is stored. +//! +//! This function computes the quaternion that is represented by the DCM +//! attitude estimation matrix. +//! +//! \return None. +// +//***************************************************************************** +void +CompDCMComputeQuaternion(tCompDCM *psDCM, float pfQuaternion[4]) +{ + float fQs, fQx, fQy, fQz; + + // + // Partially compute Qs, Qx, Qy, and Qz based on the DCM diagonals. The + // square root, an expensive operation, is computed for only one of these + // as determined later. + // + fQs = 1 + psDCM->ppfDCM[0][0] + psDCM->ppfDCM[1][1] + psDCM->ppfDCM[2][2]; + fQx = 1 + psDCM->ppfDCM[0][0] - psDCM->ppfDCM[1][1] - psDCM->ppfDCM[2][2]; + fQy = 1 - psDCM->ppfDCM[0][0] + psDCM->ppfDCM[1][1] - psDCM->ppfDCM[2][2]; + fQz = 1 - psDCM->ppfDCM[0][0] - psDCM->ppfDCM[1][1] + psDCM->ppfDCM[2][2]; + + // + // See if Qs is the largest of the diagonal values. + // + if((fQs > fQx) && (fQs > fQy) && (fQs > fQz)) + { + // + // Finish the computation of Qs. + // + fQs = sqrtf(fQs) / 2; + + // + // Compute the values of the quaternion based on Qs. + // + pfQuaternion[0] = fQs; + pfQuaternion[1] = ((psDCM->ppfDCM[2][1] - psDCM->ppfDCM[1][2]) / + (4 * fQs)); + pfQuaternion[2] = ((psDCM->ppfDCM[0][2] - psDCM->ppfDCM[2][0]) / + (4 * fQs)); + pfQuaternion[3] = ((psDCM->ppfDCM[1][0] - psDCM->ppfDCM[0][1]) / + (4 * fQs)); + } + + // + // Qs is not the largest, so see if Qx is the largest remaining diagonal + // value. + // + else if((fQx > fQy) && (fQx > fQz)) + { + // + // Finish the computation of Qx. + // + fQx = sqrtf(fQx) / 2; + + // + // Compute the values of the quaternion based on Qx. + // + pfQuaternion[0] = ((psDCM->ppfDCM[2][1] - psDCM->ppfDCM[1][2]) / + (4 * fQx)); + pfQuaternion[1] = fQx; + pfQuaternion[2] = ((psDCM->ppfDCM[1][0] + psDCM->ppfDCM[0][1]) / + (4 * fQx)); + pfQuaternion[3] = ((psDCM->ppfDCM[0][2] + psDCM->ppfDCM[2][0]) / + (4 * fQx)); + } + + // + // Qs and Qx are not the largest, so see if Qy is the largest remaining + // diagonal value. + // + else if(fQy > fQz) + { + // + // Finish the computation of Qy. + // + fQy = sqrtf(fQy) / 2; + + // + // Compute the values of the quaternion based on Qy. + // + pfQuaternion[0] = ((psDCM->ppfDCM[0][2] - psDCM->ppfDCM[2][0]) / + (4 * fQy)); + pfQuaternion[1] = ((psDCM->ppfDCM[1][0] + psDCM->ppfDCM[0][1]) / + (4 * fQy)); + pfQuaternion[2] = fQy; + pfQuaternion[3] = ((psDCM->ppfDCM[2][1] + psDCM->ppfDCM[1][2]) / + (4 * fQy)); + } + + // + // Qz is the largest diagonal value. + // + else + { + // + // Finish the computation of Qz. + // + fQz = sqrtf(fQz) / 2; + + // + // Compute the values of the quaternion based on Qz. + // + pfQuaternion[0] = ((psDCM->ppfDCM[1][0] - psDCM->ppfDCM[0][1]) / + (4 * fQz)); + pfQuaternion[1] = ((psDCM->ppfDCM[0][2] + psDCM->ppfDCM[2][0]) / + (4 * fQz)); + pfQuaternion[2] = ((psDCM->ppfDCM[2][1] + psDCM->ppfDCM[1][2]) / + (4 * fQz)); + pfQuaternion[3] = fQz; + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/comp_dcm.h b/sensorlib/comp_dcm.h new file mode 100644 index 0000000..860bda9 --- /dev/null +++ b/sensorlib/comp_dcm.h @@ -0,0 +1,120 @@ +//***************************************************************************** +// +// comp_dcm.h - Prototypes for the complementary filter direction cosine matrix +// functions. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_COMP_DCM_H__ +#define __SENSORLIB_COMP_DCM_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the complementary filter +// DCM algorithm. +// +//***************************************************************************** +typedef struct +{ + // + // The state of the direction cosine matrix. + // + float ppfDCM[3][3]; + + // + // The time delta between updates to the DCM. + // + float fDeltaT; + + // + // The scaling factor for the DCM update based on the accelerometer + // reading. + // + float fScaleA; + + // + // The scaling factor for the DCM update based on the gyroscope reading. + // + float fScaleG; + + // + // The scaling factor for the DCM update based on the magnetometer reading. + // + float fScaleM; + + // + // The most recent accelerometer readings. + // + float pfAccel[3]; + + // + // The most recent gyroscope readings. + // + float pfGyro[3]; + + // + // The most recent magnetometer readings. + // + float pfMagneto[3]; +} +tCompDCM; + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern void CompDCMInit(tCompDCM *psDCM, float fDeltaT, float fScaleA, + float fScaleG, float fScaleM); +extern void CompDCMAccelUpdate(tCompDCM *psDCM, float fAccelX, float fAccelY, + float fAccelZ); +extern void CompDCMGyroUpdate(tCompDCM *psDCM, float fGyroX, float fGyroY, + float fGyroZ); +extern void CompDCMMagnetoUpdate(tCompDCM *psDCM, float fMagnetoX, + float fMagnetoY, float fMagnetoZ); +extern void CompDCMStart(tCompDCM *psDCM); +extern void CompDCMUpdate(tCompDCM *psDCM); +extern void CompDCMMatrixGet(tCompDCM *psDCM, float ppfDCM[3][3]); +extern void CompDCMComputeEulers(tCompDCM *psDCM, float *pfRoll, + float *pfPitch, float *pfYaw); +extern void CompDCMComputeQuaternion(tCompDCM *psDCM, float pfQuaternion[4]); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_COMP_DCM_H__ diff --git a/sensorlib/ewarm/Exe/sensorlib.a b/sensorlib/ewarm/Exe/sensorlib.a new file mode 100644 index 0000000..408cb46 Binary files /dev/null and b/sensorlib/ewarm/Exe/sensorlib.a differ diff --git a/sensorlib/gcc/libsensor.a b/sensorlib/gcc/libsensor.a new file mode 100644 index 0000000..ae22311 Binary files /dev/null and b/sensorlib/gcc/libsensor.a differ diff --git a/sensorlib/hw_ak8963.h b/sensorlib/hw_ak8963.h new file mode 100644 index 0000000..53e43f2 --- /dev/null +++ b/sensorlib/hw_ak8963.h @@ -0,0 +1,209 @@ +//***************************************************************************** +// +// hw_ak8963.h - Macros used when accessing the Asahi Kasei AK8963 +// magnetometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_AK8963_H__ +#define __SENSORLIB_HW_AK8963_H__ + +//***************************************************************************** +// +// The following are defines for the AK8963 register addresses. +// +//***************************************************************************** +#define AK8963_O_WIA 0x00 // Device ID register +#define AK8963_O_INFO 0x01 // Information register +#define AK8963_O_ST1 0x02 // Status 1 register +#define AK8963_O_HXL 0x03 // X-axis LSB output register +#define AK8963_O_HXH 0x04 // X-axis MSB output register +#define AK8963_O_HYL 0x05 // Y-axis LSB output register +#define AK8963_O_HYH 0x06 // Y-axis MSB output register +#define AK8963_O_HZL 0x07 // Z-axis LSB output register +#define AK8963_O_HZH 0x08 // Z-axis MSB output register +#define AK8963_O_ST2 0x09 // Status 2 register +#define AK8963_O_CNTL 0x0A // Control register +#define AK8963_O_CNTL2 0x0B // Control 2 register +#define AK8963_O_ASTC 0x0C // Self-test register +#define AK8963_O_I2CDIS 0x0F // Disable I2C bus interface +#define AK8963_O_ASAX 0x10 // X-axis sensitivity register +#define AK8963_O_ASAY 0x11 // Y-axis sensitivity register +#define AK8963_O_ASAZ 0x12 // Z-axis sensitivity register + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_WIA register. +// +//***************************************************************************** +#define AK8963_WIA_M 0xFF // Device ID +#define AK8963_WIA_AK8963 0x48 // AK8963 +#define AK8963_WIA_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_INFO register. +// +//***************************************************************************** +#define AK8963_INFO_M 0xFF // Device information value +#define AK8963_INFO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_ST1 register. +// +//***************************************************************************** +#define AK8963_ST1_DOR 0x02 // Data overrun +#define AK8963_ST1_DRDY 0x01 // Data ready + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_HXL register. +// +//***************************************************************************** +#define AK8963_HXL_M 0xFF // Output data +#define AK8963_HXL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_HXH register. +// +//***************************************************************************** +#define AK8963_HXH_M 0xFF // Output data +#define AK8963_HXH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_HYL register. +// +//***************************************************************************** +#define AK8963_HYL_M 0xFF // Output data +#define AK8963_HYL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_HYH register. +// +//***************************************************************************** +#define AK8963_HYH_M 0xFF // Output data +#define AK8963_HYH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_HZL register. +// +//***************************************************************************** +#define AK8963_HZL_M 0xFF // Output data +#define AK8963_HZL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_HZH register. +// +//***************************************************************************** +#define AK8963_HZH_M 0xFF // Output data +#define AK8963_HZH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_ST2 register. +// +//***************************************************************************** +#define AK8963_ST2_BITM_M 0x10 // Output bit setting +#define AK8963_ST2_BITM_14BIT 0x00 // 14-bit output +#define AK8963_ST2_BITM_16BIT 0x10 // 16-bit output +#define AK8963_ST2_HOFL 0x08 // Magnetic sensor overflow +#define AK8963_ST2_BITM_S 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_CNTL register. +// +//***************************************************************************** +#define AK8963_CNTL_BITM_M 0x10 // Output bit setting +#define AK8963_CNTL_BITM_14BIT 0x00 // 14-bit output +#define AK8963_CNTL_BITM_16BIT 0x10 // 16-bit output +#define AK8963_CNTL_MODE_M 0x0F // Operation mode +#define AK8963_CNTL_MODE_POWER_DOWN \ + 0x00 // Power-down mode +#define AK8963_CNTL_MODE_SINGLE 0x01 // Single measurement mode +#define AK8963_CNTL_MODE_CONT_1 0x02 // Continuous measurement mode 1 + // (8Hz) +#define AK8963_CNTL_MODE_EXT_TRIG \ + 0x04 // External trigger measurement + // mode +#define AK8963_CNTL_MODE_CONT_2 0x06 // Continuous measurement mode 2 + // (100Hz) +#define AK8963_CNTL_MODE_SELF_TEST \ + 0x08 // Self-test mode +#define AK8963_CNTL_MODE_FUSE_ROM \ + 0x0F // Fuse ROM access mode +#define AK8963_CNTL_BITM_S 4 +#define AK8963_CNTL_MODE_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_CNTL2 register. +// +//***************************************************************************** +#define AK8963_CNTL2_SRST 0x01 // Register reset + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_ASTC register. +// +//***************************************************************************** +#define AK8963_ASTC_SELF 0x40 // Generate magnetic field for + // self-test + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_I2CDIS +// register. +// +//***************************************************************************** +#define AK8963_I2CDIS_I2CDIS 0xFF // Disable I2C bus interface + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_ASAX register. +// +//***************************************************************************** +#define AK8963_ASAX_M 0xFF // X-axis sensitivity adjustment +#define AK8963_ASAX_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_ASAY register. +// +//***************************************************************************** +#define AK8963_ASAY_M 0xFF // Y-axis sensitivity adjustment +#define AK8963_ASAY_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8963_O_ASAZ register. +// +//***************************************************************************** +#define AK8963_ASAZ_M 0xFF // Z-axis sensitivity adjustment +#define AK8963_ASAZ_S 0 + +#endif // __SENSORLIB_HW_AK8963_H__ diff --git a/sensorlib/hw_ak8975.h b/sensorlib/hw_ak8975.h new file mode 100644 index 0000000..e1cb847 --- /dev/null +++ b/sensorlib/hw_ak8975.h @@ -0,0 +1,177 @@ +//***************************************************************************** +// +// hw_ak8975.h - Macros used when accessing the Asahi Kasei AK8975 +// magnetometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_AK8975_H__ +#define __SENSORLIB_HW_AK8975_H__ + +//***************************************************************************** +// +// The following are defines for the AK8975 register addresses. +// +//***************************************************************************** +#define AK8975_O_WIA 0x00 // Device ID register +#define AK8975_O_INFO 0x01 // Information register +#define AK8975_O_ST1 0x02 // Status 1 register +#define AK8975_O_HXL 0x03 // X-axis LSB output register +#define AK8975_O_HXH 0x04 // X-axis MSB output register +#define AK8975_O_HYL 0x05 // Y-axis LSB output register +#define AK8975_O_HYH 0x06 // Y-axis MSB output register +#define AK8975_O_HZL 0x07 // Z-axis LSB output register +#define AK8975_O_HZH 0x08 // Z-axis MSB output register +#define AK8975_O_ST2 0x09 // Status 2 register +#define AK8975_O_CNTL 0x0A // Control register +#define AK8975_O_ASTC 0x0C // Self-test register +#define AK8975_O_ASAX 0x10 // X-axis sensitivity register +#define AK8975_O_ASAY 0x11 // Y-axis sensitivity register +#define AK8975_O_ASAZ 0x12 // Z-axis sensitivity register + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_WIA register. +// +//***************************************************************************** +#define AK8975_WIA_M 0xFF // Device ID +#define AK8975_WIA_AK8975 0x48 // AK8975 +#define AK8975_WIA_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_INFO register. +// +//***************************************************************************** +#define AK8975_INFO_M 0xFF // Device information value +#define AK8975_INFO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_ST1 register. +// +//***************************************************************************** +#define AK8975_ST1_DRDY 0x01 // Data ready + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_HXL register. +// +//***************************************************************************** +#define AK8975_HXL_M 0xFF // Output data +#define AK8975_HXL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_HXH register. +// +//***************************************************************************** +#define AK8975_HXH_M 0xFF // Output data +#define AK8975_HXH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_HYL register. +// +//***************************************************************************** +#define AK8975_HYL_M 0xFF // Output data +#define AK8975_HYL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_HYH register. +// +//***************************************************************************** +#define AK8975_HYH_M 0xFF // Output data +#define AK8975_HYH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_HZL register. +// +//***************************************************************************** +#define AK8975_HZL_M 0xFF // Output data +#define AK8975_HZL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_HZH register. +// +//***************************************************************************** +#define AK8975_HZH_M 0xFF // Output data +#define AK8975_HZH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_ST2 register. +// +//***************************************************************************** +#define AK8975_ST2_HOFL 0x08 // Magnetic sensor overflow +#define AK8975_ST2_DERR 0x04 // Data error + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_CNTL register. +// +//***************************************************************************** +#define AK8975_CNTL_MODE_M 0x0F // Operation mode +#define AK8975_CNTL_MODE_POWER_DOWN \ + 0x00 // Power-down mode +#define AK8975_CNTL_MODE_SINGLE 0x01 // Single measurement mode +#define AK8975_CNTL_MODE_SELF_TEST \ + 0x08 // Self-test mode +#define AK8975_CNTL_MODE_FUSE_ROM \ + 0x0F // Fuse ROM access mode +#define AK8975_CNTL_MODE_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_ASTC register. +// +//***************************************************************************** +#define AK8975_ASTC_SELF 0x40 // Generate magnetic field for + // self-test + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_ASAX register. +// +//***************************************************************************** +#define AK8975_ASAX_M 0xFF // X-axis sensitivity adjustment +#define AK8975_ASAX_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_ASAY register. +// +//***************************************************************************** +#define AK8975_ASAY_M 0xFF // Y-axis sensitivity adjustment +#define AK8975_ASAY_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the AK8975_O_ASAZ register. +// +//***************************************************************************** +#define AK8975_ASAZ_M 0xFF // Z-axis sensitivity adjustment +#define AK8975_ASAZ_S 0 + +#endif // __SENSORLIB_HW_AK8975_H__ diff --git a/sensorlib/hw_bmp180.h b/sensorlib/hw_bmp180.h new file mode 100644 index 0000000..7f8e3fe --- /dev/null +++ b/sensorlib/hw_bmp180.h @@ -0,0 +1,349 @@ +//***************************************************************************** +// +// hw_bmp180.h - Macros used when accessing the Bosch BMP180 barometric +// pressure sensor. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_BMP180_H__ +#define __SENSORLIB_HW_BMP180_H__ + +//***************************************************************************** +// +// The following are defines for the BMP180 register addresses. +// +//***************************************************************************** +#define BMP180_O_AC1_MSB 0xAA // AC1 MSB register +#define BMP180_O_AC1_LSB 0xAB // AC1 LSB register +#define BMP180_O_AC2_MSB 0xAC // AC2 MSB register +#define BMP180_O_AC2_LSB 0xAD // AC2 LSB register +#define BMP180_O_AC3_MSB 0xAE // AC3 MSB register +#define BMP180_O_AC3_LSB 0xAF // AC3 LSB register +#define BMP180_O_AC4_MSB 0xB0 // AC4 MSB register +#define BMP180_O_AC4_LSB 0xB1 // AC4 LSB register +#define BMP180_O_AC5_MSB 0xB2 // AC5 MSB register +#define BMP180_O_AC5_LSB 0xB3 // AC5 LSB register +#define BMP180_O_AC6_MSB 0xB4 // AC6 MSB register +#define BMP180_O_AC6_LSB 0xB5 // AC6 LSB register +#define BMP180_O_B1_MSB 0xB6 // B1 MSB register +#define BMP180_O_B1_LSB 0xB7 // B1 LSB register +#define BMP180_O_B2_MSB 0xB8 // B2 MSB register +#define BMP180_O_B2_LSB 0xB9 // B2 LSB register +#define BMP180_O_MB_MSB 0xBA // MB MSB register +#define BMP180_O_MB_LSB 0xBB // MB LSB register +#define BMP180_O_MC_MSB 0xBC // MC MSB register +#define BMP180_O_MC_LSB 0xBD // MC LSB register +#define BMP180_O_MD_MSB 0xBE // MD MSB register +#define BMP180_O_MD_LSB 0xBF // MD LSB register +#define BMP180_O_ID 0xD0 // Device ID register +#define BMP180_O_SOFT_RESET 0xE0 // Soft reset register +#define BMP180_O_CTRL_MEAS 0xF4 // Measurement control register +#define BMP180_O_OUT_MSB 0xF6 // ADC data MSB register +#define BMP180_O_OUT_LSB 0xF7 // ADC data LSB register +#define BMP180_O_OUT_XLSB 0xF8 // ADC data XLSB register + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC1_MSB +// register. +// +//***************************************************************************** +#define BMP180_AC1_MSB_M 0xFF // MSB of AC1 calibration + // coefficient +#define BMP180_AC1_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC1_LSB +// register. +// +//***************************************************************************** +#define BMP180_AC1_LSB_M 0xFF // LSB of AC1 calibration + // coefficient +#define BMP180_AC1_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC2_MSB +// register. +// +//***************************************************************************** +#define BMP180_AC2_MSB_M 0xFF // MSB of AC2 calibration + // coefficient +#define BMP180_AC2_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC2_LSB +// register. +// +//***************************************************************************** +#define BMP180_AC2_LSB_M 0xFF // LSB of AC2 calibration + // coefficient +#define BMP180_AC2_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC3_MSB +// register. +// +//***************************************************************************** +#define BMP180_AC3_MSB_M 0xFF // MSB of AC3 calibration + // coefficient +#define BMP180_AC3_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC3_LSB +// register. +// +//***************************************************************************** +#define BMP180_AC3_LSB_M 0xFF // LSB of AC3 calibration + // coefficient +#define BMP180_AC3_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC4_MSB +// register. +// +//***************************************************************************** +#define BMP180_AC4_MSB_M 0xFF // MSB of AC4 calibration + // coefficient +#define BMP180_AC4_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC4_LSB +// register. +// +//***************************************************************************** +#define BMP180_AC4_LSB_M 0xFF // LSB of AC4 calibration + // coefficient +#define BMP180_AC4_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC5_MSB +// register. +// +//***************************************************************************** +#define BMP180_AC5_MSB_M 0xFF // MSB of AC5 calibration + // coefficient +#define BMP180_AC5_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC5_LSB +// register. +// +//***************************************************************************** +#define BMP180_AC5_LSB_M 0xFF // LSB of AC5 calibration + // coefficient +#define BMP180_AC5_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC6_MSB +// register. +// +//***************************************************************************** +#define BMP180_AC6_MSB_M 0xFF // MSB of AC6 calibration + // coefficient +#define BMP180_AC6_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_AC6_LSB +// register. +// +//***************************************************************************** +#define BMP180_AC6_LSB_M 0xFF // LSB of AC6 calibration + // coefficient +#define BMP180_AC6_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_B1_MSB +// register. +// +//***************************************************************************** +#define BMP180_B1_MSB_M 0xFF // MSB of B1 calibration + // coefficient +#define BMP180_B1_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_B1_LSB +// register. +// +//***************************************************************************** +#define BMP180_B1_LSB_M 0xFF // LSB of B1 calibration + // coefficient +#define BMP180_B1_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_B2_MSB +// register. +// +//***************************************************************************** +#define BMP180_B2_MSB_M 0xFF // MSB of B2 calibration + // coefficient +#define BMP180_B2_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_B2_LSB +// register. +// +//***************************************************************************** +#define BMP180_B2_LSB_M 0xFF // LSB of B2 calibration + // coefficient +#define BMP180_B2_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_MB_MSB +// register. +// +//***************************************************************************** +#define BMP180_MB_MSB_M 0xFF // MSB of MB calibration + // coefficient +#define BMP180_MB_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_MB_LSB +// register. +// +//***************************************************************************** +#define BMP180_MB_LSB_M 0xFF // LSB of MB calibration + // coefficient +#define BMP180_MB_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_MC_MSB +// register. +// +//***************************************************************************** +#define BMP180_MC_MSB_M 0xFF // MSB of MC calibration + // coefficient +#define BMP180_MC_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_MC_LSB +// register. +// +//***************************************************************************** +#define BMP180_MC_LSB_M 0xFF // LSB of MC calibration + // coefficient +#define BMP180_MC_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_MD_MSB +// register. +// +//***************************************************************************** +#define BMP180_MD_MSB_M 0xFF // MSB of MD calibration + // coefficient +#define BMP180_MD_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_MD_LSB +// register. +// +//***************************************************************************** +#define BMP180_MD_LSB_M 0xFF // LSB of MD calibration + // coefficient +#define BMP180_MD_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_ID register. +// +//***************************************************************************** +#define BMP180_ID_M 0xFF // Device ID +#define BMP180_ID_BMP180 0x55 // BMP180 +#define BMP180_ID_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_SOFT_RESET +// register. +// +//***************************************************************************** +#define BMP180_SOFT_RESET_M 0xFF // Soft reset value +#define BMP180_SOFT_RESET_VALUE 0xB6 // Request a soft reset +#define BMP180_SOFT_RESET_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_CTRL_MEAS +// register. +// +//***************************************************************************** +#define BMP180_CTRL_MEAS_OSS_M 0xC0 // Oversampling ratio +#define BMP180_CTRL_MEAS_OSS_1 0x00 // Single sampling +#define BMP180_CTRL_MEAS_OSS_2 0x40 // 2x oversampling +#define BMP180_CTRL_MEAS_OSS_4 0x80 // 4x oversampling +#define BMP180_CTRL_MEAS_OSS_8 0xC0 // 8x oversampling +#define BMP180_CTRL_MEAS_SCO 0x20 // Start of conversion +#define BMP180_CTRL_MEAS_M 0x1F // Measurement control +#define BMP180_CTRL_MEAS_TEMPERATURE \ + 0x0E // Temperature measurement +#define BMP180_CTRL_MEAS_PRESSURE \ + 0x14 // Pressure measurement +#define BMP180_CTRL_MEAS_OSS_S 6 +#define BMP180_CTRL_MEAS_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_OUT_MSB +// register. +// +//***************************************************************************** +#define BMP180_OUT_MSB_M 0xFF // Bits [20:13] of the ADC data +#define BMP180_OUT_MSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_OUT_LSB +// register. +// +//***************************************************************************** +#define BMP180_OUT_LSB_M 0xFF // Bits [12:5] of the ADC data +#define BMP180_OUT_LSB_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the BMP180_O_OUT_XLSB +// register. +// +//***************************************************************************** +#define BMP180_OUT_XLSB_M 0xF8 // Bits [4:0] of the ADC data +#define BMP180_OUT_XLSB_S 3 + +#endif // __SENSORLIB_HW_BMP180_H__ diff --git a/sensorlib/hw_bq27510g3.h b/sensorlib/hw_bq27510g3.h new file mode 100644 index 0000000..f0b3309 --- /dev/null +++ b/sensorlib/hw_bq27510g3.h @@ -0,0 +1,148 @@ +//***************************************************************************** +// +// hw_bq27510g3.h - Macros used when accessing the TI BQ27510-G3 Fuel Gauge +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_BQ27510G3_H__ +#define __SENSORLIB_HW_BQ27510G3_H__ + +//***************************************************************************** +// +// The following are defines for the BQ27510G3 register addresses +// +//***************************************************************************** +#define BQ27510G3_O_CNTL_LSB 0x00 // System Control Register LSB +#define BQ27510G3_O_CNTL_MSB 0x01 // System Control Register MSB +#define BQ27510G3_O_AT_RATE_LSB 0x02 // Discharge current value LSB +#define BQ27510G3_O_AT_RATE_MSB 0x03 // Discharge current value MSB +#define BQ27510G3_O_AT_RATE_TTE_LSB \ + 0x04 // Remaining operating time of + // battery with current AT_RATE LSB +#define BQ27510G3_O_AT_RATE_TTE_MSB \ + 0x05 // Remaining operating time of + // battery with current AT_RATE MSB +#define BQ27510G3_O_TEMP_LSB 0x06 // Battery temperature LSB +#define BQ27510G3_O_TEMP_MSB 0x07 // Battery temperature MSB +#define BQ27510G3_O_VOLT_LSB 0x08 // Battery cell-pack voltage LSB +#define BQ27510G3_O_VOLT_MSB 0x09 // Battery cell-pack voltage MSB +#define BQ27510G3_O_FLAGS_LSB 0x0A // Holds various operating status + // value of gas-guage LSB +#define BQ27510G3_O_FLAGS_MSB 0x0B // Holds various operating status + // value of gas-guage MSB +#define BQ27510G3_O_NOM_AV_CAP_LSB \ + 0x0C // Uncompensated battery capacity + // remaining LSB +#define BQ27510G3_O_NOM_AV_CAP_MSB \ + 0x0D // Uncompensated battery capacity + // remaining MSB +#define BQ27510G3_O_FULL_AV_CAP_LSB \ + 0x0E // Uncompensated capacity of fully + // charged battery LSB +#define BQ27510G3_O_FULL_AV_CAP_MSB \ + 0x0F // Uncompensated capacity of fully + // charged battery MSB +#define BQ27510G3_O_REM_CAP_LSB 0x10 // Compensated battery capacity + // remaining LSB +#define BQ27510G3_O_REM_CAP_MSB 0x11 // Compensated battery capacity + // remaining MSB +#define BQ27510G3_O_FULL_CHRG_CAP_LSB \ + 0x12 // Compensated battery capacity + // when fully charged LSB +#define BQ27510G3_O_FULL_CHRG_CAP_MSB \ + 0x13 // Compensated battery capacity + // when fully charged MSB +#define BQ27510G3_O_AVG_I_LSB 0x14 // Average current flow through + // sense resistor LSB +#define BQ27510G3_O_AVG_I_MSB 0x15 // Average current flow through + // sense resistor MSB +#define BQ27510G3_O_TTE_LSB 0x16 // Predicted remaining battery life + // (minutes) LSB +#define BQ27510G3_O_TTE_MSB 0x17 // Predicted remaining battery life + // (minutes) MSB +#define BQ27510G3_O_STBY_I_LSB 0x18 // Standbye current through sense + // resistor LSB +#define BQ27510G3_O_STBY_I_MSB 0x19 // Standbye current through sense + // resistor MSB +#define BQ27510G3_O_STBY_TTE_LSB \ + 0x1A // Predicted remaining standby + // battery life LSB +#define BQ27510G3_O_STBY_TTE_MSB \ + 0x1B // Predicted remaining standby + // battery life MSB +#define BQ27510G3_O_STATE_OF_HEALTH_LSB \ + 0x1C // State of health (percent) LSB +#define BQ27510G3_O_STATE_OF_HEALTH_MSB \ + 0x1D // State of health (percent) MSB +#define BQ27510G3_O_CYC_COUNT_LSB \ + 0x1E // Number of battery cycles + // experienced LSB +#define BQ27510G3_O_CYC_COUNT_MSB \ + 0x1F // Number of battery cycles + // experienced MSB +#define BQ27510G3_O_STATE_OF_CHRG_LSB \ + 0x20 // State of charge (percent) LSB +#define BQ27510G3_O_STATE_OF_CHRG_MSB \ + 0x21 // State of charge (percent) MSB +#define BQ27510G3_O_INST_I_LSB 0x22 // Instananeous current flow + // through sense resistor LSB +#define BQ27510G3_O_INST_I_MSB 0x23 // Instananeous current flow + // through sense resistor MSB +#define BQ27510G3_O_INT_TEMP_LSB \ + 0x28 // Internal tmeperature LSB +#define BQ27510G3_O_INT_TEMP_MSB \ + 0x29 // Internal tmeperature MSB +#define BQ27510G3_O_RES_SCALE_LSB \ + 0x2A // Resistance Scale LSB +#define BQ27510G3_O_RES_SCALE_MSB \ + 0x2B // Resistance Scale MSB +#define BQ27510G3_O_OP_CFG_LSB 0x2C // Operating Configuration LSB +#define BQ27510G3_O_OP_CFG_MSB 0x2D // Operating Configuration MSB +#define BQ27510G3_O_DCAP_LSB 0x2E // Designed capacity of battery LSB +#define BQ27510G3_O_DCAP_MSB 0x2F // Designed capacity of battery MSB + +//***************************************************************************** +// +// The following are defines for the bit fields in the BQ27510G3_O_CNTL_LSB +// register. +// +//***************************************************************************** +#define BQ27510G3_CNTL_LSB_FUNC_M \ + 0xFF // Functions +#define BQ27510G3_CNTL_LSB_FUNC_STATUS \ + 0x00 // reports DF checksum, hibernate, + // IT, etc +#define BQ27510G3_CNTL_LSB_FUNC_DEVTYPE \ + 0x01 // reports device type (for + // example: 0x0520) +#define BQ27510G3_CNTL_LSB_FUNC_FWVER \ + 0x02 // reports firmware version on the + // device type +#define BQ27510G3_CNTL_LSB_FUNC_PREVCMD \ + 0x07 // reports previous Control() + // subcommand code +#define BQ27510G3_CNTL_LSB_FUNC_RESET \ + 0x41 // forces a full reset of the fuel + // gauge +#define BQ27510G3_CNTL_LSB_FUNC_S \ + 0 + +#endif // __SENSORLIB_HW_BQ27510G3_H__ diff --git a/sensorlib/hw_cm3218.h b/sensorlib/hw_cm3218.h new file mode 100644 index 0000000..87f959b --- /dev/null +++ b/sensorlib/hw_cm3218.h @@ -0,0 +1,96 @@ +//***************************************************************************** +// +// hw_cm3218.h - Macros used for accessing the Capella CM3218 ambient light +// sensor +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_CM3218_H__ +#define __SENSORLIB_HW_CM3218_H__ + +//***************************************************************************** +// +// The following are defines for the CM3218 Commands and Registers +// +//***************************************************************************** +#define CM3218_CMD_CONFIG 0x00 // Configure sensitivity, + // integration time, persistence + // protect, interrupts, and power +#define CM3218_CMD_HIGH_THRESHOLD \ + 0x01 // High threshold window setting +#define CM3218_CMD_LOW_THRESHOLD \ + 0x02 // Low threshold window setting +#define CM3218_CMD_ALS_DATA 0x04 // Read ambient light data + +//***************************************************************************** +// +// The following are defines for the bit fields in the CM3218_CMD_CONFIG +// register. +// +//***************************************************************************** +#define CM3218_CMD_CONFIG_SM_M 0x1800 // ALS sensitivity mode selection +#define CM3218_CMD_CONFIG_SM_10 0x0000 // Sensitivty * 1.0 +#define CM3218_CMD_CONFIG_SM_20 0x0800 // Sensitivty * 2.0 +#define CM3218_CMD_CONFIG_SM_05 0x1000 // Sensitivty * 0.5 +#define CM3218_CMD_CONFIG_SM_RSVD \ + 0x1800 // Reserved +#define CM3218_CMD_CONFIG_IT_M 0x00C0 // ALS integration time setting +#define CM3218_CMD_CONFIG_IT_05 0x0000 // integration time 0.5T +#define CM3218_CMD_CONFIG_IT_10 0x0040 // integration time 1.0T (default) +#define CM3218_CMD_CONFIG_IT_20 0x0080 // integration time 2.0T +#define CM3218_CMD_CONFIG_IT_40 0x00C0 // integration time 4.0T +#define CM3218_CMD_CONFIG_PERS_M \ + 0x0030 // ALS persistence protect number + // setting +#define CM3218_CMD_CONFIG_PERS_1 \ + 0x0000 // Persistence setting of 1 +#define CM3218_CMD_CONFIG_PERS_2 \ + 0x0010 // Persistence setting of 2 +#define CM3218_CMD_CONFIG_PERS_4 \ + 0x0020 // Persistence setting of 4 +#define CM3218_CMD_CONFIG_PERS_8 \ + 0x0030 // Persistence setting of 8 +#define CM3218_CMD_CONFIG_RSVD_M \ + 0x00C // Reserved +#define CM3218_CMD_CONFIG_RSVD_DEFAULT \ + 0x0004 // Default setting +#define CM3218_CMD_CONFIG_INT_M 0x2 // Interrupt Control +#define CM3218_CMD_CONFIG_INT_DISABLE \ + 0x0 // Disable interrupts +#define CM3218_CMD_CONFIG_INT_ENABLE \ + 0x2 // Enable interrupts +#define CM3218_CMD_CONFIG_POWER_M \ + 0x1 // Power Control +#define CM3218_CMD_CONFIG_POWER_ON \ + 0x0 // Power On +#define CM3218_CMD_CONFIG_POWER_OFF \ + 0x1 // Power Off +#define CM3218_CMD_CONFIG_SM_S 11 +#define CM3218_CMD_CONFIG_IT_S 6 +#define CM3218_CMD_CONFIG_PERS_S \ + 4 +#define CM3218_CMD_CONFIG_RSVD_S \ + 2 +#define CM3218_CMD_CONFIG_INT_S 1 +#define CM3218_CMD_CONFIG_POWER_S \ + 0 + +#endif // __SENSORLIB_HW_CM3218_H__ diff --git a/sensorlib/hw_isl29023.h b/sensorlib/hw_isl29023.h new file mode 100644 index 0000000..66f65ea --- /dev/null +++ b/sensorlib/hw_isl29023.h @@ -0,0 +1,128 @@ +//***************************************************************************** +// +// hw_isl29023.h - Macros used when accessing the Intersil ISL29023 ambient +// light sensor +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_ISL29023_H__ +#define __SENSORLIB_HW_ISL29023_H__ + +//***************************************************************************** +// +// The following are defines for the ISL29023 Register Addresses +// +//***************************************************************************** +#define ISL29023_O_CMD_I 0x00 // ISL29023 command register one +#define ISL29023_O_CMD_II 0x01 +#define ISL29023_O_DATA_OUT_LSB 0x02 // Least significant byte of data +#define ISL29023_O_DATA_OUT_MSB 0x03 // Most significant byte of data +#define ISL29023_O_INT_LT_LSB 0x04 // Interrupt lower threshold least + // significant byte. +#define ISL29023_O_INT_LT_MSB 0x05 // Interrupt lower threshold most + // significant byte. +#define ISL29023_O_INT_HT_LSB 0x06 // Interrupt high threshold least + // significant byte. +#define ISL29023_O_INT_HT_MSB 0x07 // Interrupt high threshold most + // signficant byte + +//***************************************************************************** +// +// The following are defines for the bit fields in the ISL29023_O_CMD_I +// register. +// +//***************************************************************************** +#define ISL29023_CMD_I_OP_MODE_M \ + 0xE0 // Operation Mode +#define ISL29023_CMD_I_OP_MODE_POWER_DOWN \ + 0x00 // Power Down the device (Default) +#define ISL29023_CMD_I_OP_MODE_RESERVED_3 \ + 0x0E // RESERVED +#define ISL29023_CMD_I_OP_MODE_ALS_LOW \ + 0x20 // Measure ALS once per integration + // cyle +#define ISL29023_CMD_I_OP_MODE_IR_ONCE \ + 0x40 // Measure IR once +#define ISL29023_CMD_I_OP_MODE_RESERVED_1 \ + 0x60 // RESERVED +#define ISL29023_CMD_I_OP_MODE_RESERVED_2 \ + 0x80 // RESERVED +#define ISL29023_CMD_I_OP_MODE_ALS_CONT \ + 0xA0 // Measure ambient light sensor + // continuously. +#define ISL29023_CMD_I_OP_MODE_IR_CONT \ + 0xC // Measure infrared sensor + // continuously +#define ISL29023_CMD_I_INT_FLAG_M \ + 0x04 // Interrupt flag +#define ISL29023_CMD_I_INT_FLAG 0x04 // Interrupt flag +#define ISL29023_CMD_I_INT_PERSIST_M \ + 0x03 // Consecutive measurements outside + // threshold before interrupt +#define ISL29023_CMD_I_INT_PERSIST_1 \ + 0x00 // Interrupt on first cycle outside + // threshold +#define ISL29023_CMD_I_INT_PERSIST_4 \ + 0x01 // Interrupt on fourth cycle + // oustside threshold +#define ISL29023_CMD_I_INT_PERSIST_8 \ + 0x02 // Interrupt on eigth cycle outside + // threshold +#define ISL29023_CMD_I_INT_PERSIST_16 \ + 0x03 // Interrupt on sixteenth cycle + // outside threshold +#define ISL29023_CMD_I_OP_MODE_S \ + 5 +#define ISL29023_CMD_I_INT_FLAG_S \ + 2 +#define ISL_29023_CMD_I_INT_PERSIST_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the ISL29023_O_CMD_II +// register. +// +//***************************************************************************** +#define ISL29023_CMD_II_ADC_RES_M \ + 0x0C // ADC resolution setting +#define ISL29023_CMD_II_ADC_RES_16 \ + 0x00 // 16 bit resolution +#define ISL29023_CMD_II_ADC_RES_12 \ + 0x04 // 12 bit resolution +#define ISL29023_CMD_II_ADC_RES_8 \ + 0x08 // 8 bit resolution +#define ISL29023_CMD_II_ADC_RES_4 \ + 0x0C // 4 bit resolution +#define ISL29023_CMD_II_RANGE_M 0x03 // Sensor Range Setting in Lux +#define ISL29023_CMD_II_RANGE_1K \ + 0x00 // 1000 lux range +#define ISL29023_CMD_II_RANGE_4K \ + 0x01 // 4000 lux range +#define ISL29023_CMD_II_RANGE_16K \ + 0x02 // 16000 lux range +#define ISL29023_CMD_II_RANGE_64K \ + 0x03 // 64000 lux range +#define ISL29023_CMD_II_ADC_RES_S \ + 2 +#define ISL29023_CMD_II_RANGE_S 0 + +#endif // __SENSORLIB_HW_ISL29023_H__ diff --git a/sensorlib/hw_kxti9.h b/sensorlib/hw_kxti9.h new file mode 100644 index 0000000..4ffc53a --- /dev/null +++ b/sensorlib/hw_kxti9.h @@ -0,0 +1,619 @@ +//***************************************************************************** +// +// hw_kxti9.h - Macros used when accessing the Kionix KXTI9 accelerometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_KXTI9_H__ +#define __SENSORLIB_HW_KXTI9_H__ + +//***************************************************************************** +// +// The following are defines for the KXTI9 register addresses. +// +//***************************************************************************** +#define KXTI9_O_XOUT_HFP_L 0x00 // X-axis high-pass data LSB + // register +#define KXTI9_O_XOUT_HFP_H 0x01 // X-axis high-pass data MSB + // register +#define KXTI9_O_YOUT_HFP_L 0x02 // Y-axis high-pass data LSB + // register +#define KXTI9_O_YOUT_HFP_H 0x03 // Y-axis high-pass data MSB + // register +#define KXTI9_O_ZOUT_HFP_L 0x04 // Z-axis high-pass data LSB + // register +#define KXTI9_O_ZOUT_HFP_H 0x05 // Z-axis high-pass data MSB + // register +#define KXTI9_O_XOUT_L 0x06 // X-axis data LSB register +#define KXTI9_O_XOUT_H 0x07 // X-axis data MSB register +#define KXTI9_O_YOUT_L 0x08 // Y-axis data LSB register +#define KXTI9_O_YOUT_H 0x09 // Y-axis data MSB register +#define KXTI9_O_ZOUT_L 0x0A // Z-axis data LSB register +#define KXTI9_O_ZOUT_H 0x0B // Z-axis data MSB register +#define KXTI9_O_DCST_RESP 0x0C // Digital Control Status register +#define KXTI9_O_WHO_AM_I 0x0F // Who Am I register +#define KXTI9_O_TILT_POS_CUR 0x10 // Current tilt position register +#define KXTI9_O_TILT_POS_PRE 0x11 // Previous tilt position register +#define KXTI9_O_INT_SRC1 0x15 // Interrupt source register 1 +#define KXTI9_O_INT_SRC2 0x16 // Interrupt source register2 +#define KXTI9_O_STATUS 0x18 // Status register +#define KXTI9_O_INT_REL 0x1A // Interrupt clear/release register +#define KXTI9_O_CTRL1 0x1B // Control register 1 +#define KXTI9_O_CTRL2 0x1C // Control register 2 +#define KXTI9_O_CTRL3 0x1D // Control register 3 +#define KXTI9_O_INT_CTRL1 0x1E // Interrupt control register 1 +#define KXTI9_O_INT_CTRL2 0x1F // Interrupt control register 2 +#define KXTI9_O_INT_CTRL3 0x20 // Interrupt control register 3 +#define KXTI9_O_DATA_CTRL 0x21 // Data control register +#define KXTI9_O_TILT_TIMER 0x28 // Tilt timer register +#define KXTI9_O_WUF_TIMER 0x29 // Wake-up timer register +#define KXTI9_O_TDT_TIMER 0x2B // TDT timer register +#define KXTI9_O_TDT_H_THRESH 0x2C // Jerk threshold high register +#define KXTI9_O_TDT_L_THRESH 0x2D // Jerk threshold low register +#define KXTI9_O_TDT_TAP_TIMER 0x2E // TDT tap timer register +#define KXTI9_O_TDT_TOTAL_TIMER 0x2F // TDT double tap timer register +#define KXTI9_O_TDT_LATENCY_TIMER \ + 0x30 // TDT tap latency timer register +#define KXTI9_O_TDT_WINDOW_TIMER \ + 0x31 // TDT tap window timer register +#define KXTI9_O_BUF_CTRL1 0x32 // Buffer control 1 register +#define KXTI9_O_BUF_CTRL2 0x33 // Buffer control 2 register +#define KXTI9_O_STATUS1 0x34 // Buffer status 1 register +#define KXTI9_O_STATUS2 0x35 // Buffer status 2 register +#define KXTI9_O_BUF_CLEAR 0x36 // Buffer status clear register +#define KXTI9_O_SELF_TEST 0x3A // Self-test register +#define KXTI9_O_WUF_THRESH 0x5A // Wake-up threshold register +#define KXTI9_O_TILT_ANGLE 0x5C // Tilt angle register +#define KXTI9_O_HYST_SET 0x5F // Hysteresis set register +#define KXTI9_O_BUF_READ 0x7F // Buffer read register + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_XOUT_HPF_L +// register. +// +//***************************************************************************** +#define KXTI9_XOUT_HPF_L_M 0xF0 // Bits [3:0] of high-pass filtered + // X-axis data +#define KXTI9_XOUT_HPF_L_S 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_XOUT_HPF_H +// register. +// +//***************************************************************************** +#define KXTI9_XOUT_HPF_H_M 0xFF // Bits [11:4] of high-pass + // filtered X-axis data +#define KXTI9_XOUT_HPF_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_YOUT_HPF_L +// register. +// +//***************************************************************************** +#define KXTI9_YOUT_HPF_L_M 0xF0 // Bits [3:0] of high-pass filtered + // Y-axis data +#define KXTI9_YOUT_HPF_L_S 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_YOUT_HPF_H +// register. +// +//***************************************************************************** +#define KXTI9_YOUT_HPF_H_M 0xFF // Bits [11:4] of high-pass + // filtered Y-axis data +#define KXTI9_YOUT_HPF_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_ZOUT_HPF_L +// register. +// +//***************************************************************************** +#define KXTI9_ZOUT_HPF_L_M 0xF0 // Bits [3:0] of high-pass filtered + // Z-axis data +#define KXTI9_ZOUT_HPF_L_S 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_ZOUT_HPF_H +// register. +// +//***************************************************************************** +#define KXTI9_ZOUT_HPF_H_M 0xFF // Bits [11:4] of high-pass + // filtered Z-axis data +#define KXTI9_ZOUT_HPF_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_XOUT_L register. +// +//***************************************************************************** +#define KXTI9_XOUT_L_M 0xF0 // Bits [3:0] of X-axis data +#define KXTI9_XOUT_L_S 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_XOUT_H register. +// +//***************************************************************************** +#define KXTI9_XOUT_H_M 0xFF // Bits [11:4] of X-axis data +#define KXTI9_XOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_YOUT_L register. +// +//***************************************************************************** +#define KXTI9_YOUT_L_M 0xF0 // Bits [3:0] of Y-axis data +#define KXTI9_YOUT_L_S 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_YOUT_H register. +// +//***************************************************************************** +#define KXTI9_YOUT_H_M 0xFF // Bits [11:4] of Y-axis data +#define KXTI9_YOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_ZOUT_L register. +// +//***************************************************************************** +#define KXTI9_ZOUT_L_M 0xF0 // Bits [3:0] of Z-axis data +#define KXTI9_ZOUT_L_S 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_ZOUT_H register. +// +//***************************************************************************** +#define KXTI9_ZOUT_H_M 0xFF // Bits [11:4] of Z-axis data +#define KXTI9_ZOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_DCST_RESP +// register. +// +//***************************************************************************** +#define KXTI9_DCST_RESP_M 0xFF // Check field +#define KXTI9_DCST_RESP_DEF 0x55 // Default response +#define KXTI9_DCST_RESP_INIT 0xAA // Post-initialization response +#define KXTI9_DCST_RESP_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_WHO_AM_I +// register. +// +//***************************************************************************** +#define KXTI9_DCST_RESP_M 0xFF // Identification field +#define KXTI9_WHO_AM_I_KXTI9 0x04 // KXTI9 +#define KXTI9_WHO_AM_I_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TILT_POS_CUR +// register. +// +//***************************************************************************** +#define KXTI9_TILT_POS_CUR_LE 0x20 // Left state (X-) +#define KXTI9_TILT_POS_CUR_RI 0x10 // Right state (X+) +#define KXTI9_TILT_POS_CUR_DO 0x08 // Down state (Y-) +#define KXTI9_TILT_POS_CUR_UP 0x04 // Up state (Y+) +#define KXTI9_TILT_POS_CUR_FD 0x02 // Face-down state (Z-) +#define KXTI9_TILT_POS_CUR_FU 0x01 // Face-up state (Z+) + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TILT_POS_PRE +// register. +// +//***************************************************************************** +#define KXTI9_TILT_POS_PRE_LE 0x20 // Left state (X-) +#define KXTI9_TILT_POS_PRE_RI 0x10 // Right state (X+) +#define KXTI9_TILT_POS_PRE_DO 0x08 // Down state (Y-) +#define KXTI9_TILT_POS_PRE_UP 0x04 // Up state (Y+) +#define KXTI9_TILT_POS_PRE_FD 0x02 // Face-down state (Z-) +#define KXTI9_TILT_POS_PRE_FU 0x01 // Face-up state (Z+) + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_INT_SRC1 +// register. +// +//***************************************************************************** +#define KXTI9_INT_SRC1_TLE 0x20 // X negative (X-) reported +#define KXTI9_INT_SRC1_TRI 0x10 // X positive (X+) reported +#define KXTI9_INT_SRC1_TDO 0x08 // Y negative (Y-) reported +#define KXTI9_INT_SRC1_TUP 0x04 // Y positive (Y+) reported +#define KXTI9_INT_SRC1_TFD 0x02 // Z negative (Z-) reported +#define KXTI9_INT_SRC1_TFU 0x01 // Z positive (Z+) reported + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_INT_SRC2 +// register. +// +//***************************************************************************** +#define KXTI9_INT_SRC2_WMI 0x20 // Buffer sample threshold reached +#define KXTI9_INT_SRC2_DRDY 0x10 // New accel data ready +#define KXTI9_INT_SRC2_TDTS_M 0x0C // Tap event detected +#define KXTI9_INT_SRC2_TDTS_NONE \ + 0x00 // No tap event +#define KXTI9_INT_SRC2_TDTS_SINGLE \ + 0x01 // Single tap event +#define KXTI9_INT_SRC2_TDTS_DOUBLE \ + 0x02 // Double tap event +#define KXTI9_INT_SRC2_TDTS_DIRECTIONAL \ + 0x03 // Double tap event +#define KXTI9_INT_SRC2_WUFS 0x02 // Wake-up +#define KXTI9_INT_SRC2_TPS 0x01 // Tilt position change +#define KXTI9_INT_SRC2_S 2 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_STATUS register. +// +//***************************************************************************** +#define KXTI9_STATUS_INT 0x10 // Interrupt event + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_INT_REL +// register. +// +//***************************************************************************** +#define KXTI9_INT_REL_M 0xFF // Data is unpredictable +#define KXTI9_INT_REL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_CTRL1 register. +// +//***************************************************************************** +#define KXTI9_CTRL1_PC1 0x80 // Operating mode +#define KXTI9_CTRL1_RES 0x40 // Performance (resolution) mode +#define KXTI9_CTRL1_DRDYE 0x20 // New data interrupt enable +#define KXTI9_CTRL1_GSEL_M 0x18 // Acceleration range +#define KXTI9_CTRL1_GSEL_2G 0x00 // +/-2g +#define KXTI9_CTRL1_GSEL_4G 0x01 // +/-4g +#define KXTI9_CTRL1_GSEL_8G 0x02 // +/-8g +#define KXTI9_CTRL1_TDTE 0x04 // Directional tap enable +#define KXTI9_CTRL1_WUFE 0x02 // Wake-up enable +#define KXTI9_CTRL1_TPE 0x01 // Tilt position enable +#define KXTI9_CTRL1_GSEL_S 3 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_CTRL2 register. +// +//***************************************************************************** +#define KXTI9_CTRL2_OTDTH 0x80 // Output data rate selection for + // directional tap +#define KXTI9_CTRL2_LEM 0x20 // Left state tilt enable +#define KXTI9_CTRL2_RIM 0x10 // Right state tilt enable +#define KXTI9_CTRL2_DOM 0x08 // Down state tilt enable +#define KXTI9_CTRL2_UPM 0x04 // Up state tilt enable +#define KXTI9_CTRL2_FDM 0x02 // Face-down state tilt enable +#define KXTI9_CTRL2_FUM 0x01 // Face-up state tilt enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_CTRL3 register. +// +//***************************************************************************** +#define KXTI9_CTRL3_SRST 0x80 // Initiate software reset +#define KXTI9_CTRL3_OTP_M 0x60 // Output data rate for tilt + // position +#define KXTI9_CTRL3_OTP_1_6HZ 0x00 // Data rate is 1.6Hz +#define KXTI9_CTRL3_OTP_6_3HZ 0x01 // Data rate is 6.3Hz +#define KXTI9_CTRL3_OTP_12_5HZ 0x02 // Data rate is 12.5Hz +#define KXTI9_CTRL3_OTP_50HZ 0x03 // Data rate is 50Hz +#define KXTI9_CTRL3_OWUF_M 0x60 // Output data rate for motion + // detection and high-pass outputs +#define KXTI9_CTRL3_OWUF_25HZ 0x00 // Data rate is 25Hz +#define KXTI9_CTRL3_OWUF_50HZ 0x01 // Data rate is 50Hz +#define KXTI9_CTRL3_OWUF_100HZ 0x02 // Data rate is 100Hz +#define KXTI9_CTRL3_OWUF_200HZ 0x03 // Data rate is 200Hz +#define KXTI9_CTRL3_DCST 0x10 // Digital communication self-test +#define KXTI9_CTRL3_OTDT_M 0x0C // Encoding values change based on + // value of OTDTH bit +#define KXTI9_CTRL3_OTDT_0 0x00 // Encoding 0 is 50Hz or 12.5Hz +#define KXTI9_CTRL3_OTDT_1 0x01 // Encoding 1 is 100Hz or 25Hz +#define KXTI9_CTRL3_OTDT_2 0x02 // Encoding 2 is 200Hz or 800Hz +#define KXTI9_CTRL3_OTDT_3 0x03 // Encoding 3 is 400Hz or 1600Hz +#define KXTI9_CTRL3_OTP_S 5 +#define KXTI9_CTRL3_OTDT_S 2 +#define KXTI9_CTRL3_OWUF_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_INT_CTRL1 +// register. +// +//***************************************************************************** +#define KXTI9_INT_CTRL1_IEN 0x20 // Interrupt pin enable +#define KXTI9_INT_CTRL1_IEA 0x10 // Interrupt pin polarity +#define KXTI9_INT_CTRL1_IEL 0x08 // Interrupt pin response +#define KXTI9_INT_CTRL1_IEU 0x04 // Interrupt pin alternative + // response + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_INT_CTRL2 +// register. +// +//***************************************************************************** +#define KXTI9_INT_CTRL2_XBW 0x80 // X-axis motion interrupt enable +#define KXTI9_INT_CTRL2_YBW 0x40 // Y-axis motion interrupt enable +#define KXTI9_INT_CTRL2_ZBW 0x20 // Z-axis motion interrupt enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_INT_CTRL3 +// register. +// +//***************************************************************************** +#define KXTI9_INT_CTRL3_TMEN 0x40 // Tap masking scheme enable +#define KXTI9_INT_CTRL3_TLEM 0x20 // X negative interrupt enable +#define KXTI9_INT_CTRL3_TRIM 0x10 // X positive interrupt enable +#define KXTI9_INT_CTRL3_TDOM 0x08 // Y negative interrupt enable +#define KXTI9_INT_CTRL3_TUPM 0x04 // Y positive interrupt enable +#define KXTI9_INT_CTRL3_TFDM 0x02 // Z negative interrupt enable +#define KXTI9_INT_CTRL3_TFUM 0x01 // Z positive interrupt enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_DATA_CTRL +// register. +// +//***************************************************************************** +#define KXTI9_DATA_CTRL_HPFRO_M 0x30 // High-pass filter roll-off + // frequency +#define KXTI9_DATA_CTRL_HPFRO_50HZ \ + 0x00 // Roll-off at 50Hz +#define KXTI9_DATA_CTRL_HPFRO_100HZ \ + 0x01 // Roll-off at 100Hz +#define KXTI9_DATA_CTRL_HPFRO_200HZ \ + 0x02 // Roll-off at 200Hz +#define KXTI9_DATA_CTRL_HPFRO_400HZ \ + 0x03 // Roll-off at 400Hz +#define KXTI9_DATA_CTRL_OSA_M 0x07 // Output data rate and roll-off + // for low-pass filter outputs +#define KXTI9_DATA_CTRL_OSA_12_5HZ \ + 0x00 // 12.5Hz data rate, 6.25Hz LPF + // roll-off +#define KXTI9_DATA_CTRL_OSA_25HZ \ + 0x01 // 25Hz data rate, 12.5Hz LPF + // roll-off +#define KXTI9_DATA_CTRL_OSA_50HZ \ + 0x02 // 50Hz data rate, 25Hz LPF + // roll-off +#define KXTI9_DATA_CTRL_OSA_100HZ \ + 0x03 // 100Hz data rate, 50Hz LPF + // roll-off +#define KXTI9_DATA_CTRL_OSA_200HZ \ + 0x04 // 200Hz data rate, 100Hz LPF + // roll-off +#define KXTI9_DATA_CTRL_OSA_400HZ \ + 0x05 // 400Hz data rate, 200Hz LPF + // roll-off +#define KXTI9_DATA_CTRL_OSA_800HZ \ + 0x06 // 800Hz data rate, 400Hz LPF + // roll-off +#define KXTI9_DATA_CTRL_HPFRO_S 4 +#define KXTI9_DATA_CTRL_OSA_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TILT_TIMER +// register. +// +//***************************************************************************** +#define KXTI9_TILT_TIMER_M 0xFF // Initial timer count +#define KXTI9_TILT_TIMER_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_WUF_TIMER +// register. +// +//***************************************************************************** +#define KXTI9_WUF_TIMER_M 0xFF // Initial timer count +#define KXTI9_WUF_TIMER_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TDT_TIMER +// register. +// +//***************************************************************************** +#define KXTI9_TDT_TIMER_M 0xFF // Initial timer count +#define KXTI9_TDT_TIMER_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TDT_H_THRESH +// register. +// +//***************************************************************************** +#define KXTI9_TDT_H_THRESH_M 0xFF // Threshold high value +#define KXTI9_TDT_H_THRESH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TDT_L_THRESH +// register. +// +//***************************************************************************** +#define KXTI9_TDT_L_THRESH_M 0xFF // Threshold low value +#define KXTI9_TDT_L_THRESH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TDT_TAP_TIMER +// register. +// +//***************************************************************************** +#define KXTI9_TDT_TAP_TIMER_M 0xFF // Tap event counter +#define KXTI9_TDT_TAP_TIMER_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TDT_TOTAL_TIMER +// register. +// +//***************************************************************************** +#define KXTI9_TDT_TOTAL_TIMER_M 0xFF // Double tap event counter +#define KXTI9_TDT_TOTAL_TIMER_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// KXTI9_O_TDT_LATENCY_TIMER register. +// +//***************************************************************************** +#define KXTI9_TDT_LATENCY_TIMER_M \ + 0xFF // Tap event latency counter +#define KXTI9_TDT_LATENCY_TIMER_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TDT_WINDOW_TIMER +// register. +// +//***************************************************************************** +#define KXTI9_TDT_WINDOW_TIMER_M \ + 0xFF // Tap event window counter +#define KXTI9_TDT_WINDOW_TIMER_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_BUF_CTRL1 +// register. +// +//***************************************************************************** +#define KXTI9_BUF_CTRL1_M 0x7F // Buffer sample threshold +#define KXTI9_BUF_CTRL1_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_BUF_CTRL2 +// register. +// +//***************************************************************************** +#define KXTI9_BUF_CTRL2_BUFE 0x80 // Buffer enable +#define KXTI9_BUF_CTRL2_BUF_RES 0x40 // Buffer resolution +#define KXTI9_BUF_CTRL2_BUF_M_M 0x03 // Buffer mode +#define KXTI9_BUF_CTRL2_BUF_M_FIFO \ + 0x00 // FIFO mode +#define KXTI9_BUF_CTRL2_BUF_M_STREAM \ + 0x01 // Stream mode +#define KXTI9_BUF_CTRL2_BUF_M_TRIG \ + 0x02 // Trigger mode +#define KXTI9_BUF_CTRL2_BUF_M_FILO \ + 0x03 // FILO mode +#define KXTI9_BUF_CTRL2_BUF_M_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_STATUS1 +// register. +// +//***************************************************************************** +#define KXTI9_STATUS1_SMP_LEV_M 0xFF // Number of bytes in sample buffer +#define KXTI9_STATUS1_SMP_LEV_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_STATUS2 +// register. +// +//***************************************************************************** +#define KXTI9_STATUS2_BUF_TRIG 0x80 // Trigger mode status + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_BUF_CLEAR +// register. +// +//***************************************************************************** +#define KXTI9_BUF_CLEAR_M 0xFF // Data is unpredictable +#define KXTI9_BUF_CLEAR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_SELF_TEST +// register. +// +//***************************************************************************** +#define KXTI9_SELF_TEST_M 0xFF // Writing 0xCA enables MEMS + // self-test +#define KXTI9_SELF_TEST_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_WUF_THRESH +// register. +// +//***************************************************************************** +#define KXTI9_WUF_THRESH_M 0xFF // Acceleration wake-up threshold +#define KXTI9_WUF_THRESH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_TILT_ANGLE +// register. +// +//***************************************************************************** +#define KXTI9_TILT_ANGLE_M 0xFF // Tilt angle threshold +#define KXTI9_TILT_ANGLE_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_HYST_SET +// register. +// +//***************************************************************************** +#define KXTI9_HYST_SET_RES_M 0xE0 // Factory set value - do not + // change +#define KXTI9_HYST_SET_HYST_M 0x1F // Hysteresis angle +#define KXTI9_HYST_SET_RES_S 5 +#define KXTI9_HYST_SET_HYST_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the KXTI9_O_BUF_READ +// register. +// +//***************************************************************************** +#define KXTI9_BUF_READ_M 0xFF // Read data from buffer +#define KXTI9_BUF_READ_S 0 + +#endif // __SENSORLIB_HW_KXTI9_H__ diff --git a/sensorlib/hw_l3gd20h.h b/sensorlib/hw_l3gd20h.h new file mode 100644 index 0000000..4a9a459 --- /dev/null +++ b/sensorlib/hw_l3gd20h.h @@ -0,0 +1,468 @@ +//***************************************************************************** +// +// hw_l3gd20h.h - Macros used when accessing the ST L3GD20H gyroscope +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_L3GD20H_H__ +#define __SENSORLIB_HW_L3GD20H_H__ + +//***************************************************************************** +// +// The following are defines for the L3GD20H register addresses +// +//***************************************************************************** +#define L3GD20H_O_WHOAMI 0x0F // Device idenfitication register +#define L3GD20H_O_CTRL1 0x20 // Control 1 - power settings +#define L3GD20H_O_CTRL2 0x21 // control 2 +#define L3GD20H_O_CTRL3 0x22 // Control 3 +#define L3GD20H_O_CTRL4 0x23 // Control 4 +#define L3GD20H_O_CTRL5 0x24 // Control 5 +#define L3GD20H_O_REFERENCE 0x25 // Reference register +#define L3GD20H_O_OUT_TEMP 0x26 // Temperature data; -1LSB/deg, + // two's complement +#define L3GD20H_O_STATUS 0x27 // Status register +#define L3GD20H_O_OUT_X_LSB 0x28 // X-axis LSB +#define L3GD20H_O_OUT_X_MSB 0x29 // X-axis MSB +#define L3GD20H_O_OUT_Y_LSB 0x2A // Y-axis LSB +#define L3GD20H_O_OUT_Y_MSB 0x2B // Y-axis MSB +#define L3GD20H_O_OUT_Z_LSB 0x2C // Z-axis LSB +#define L3GD20H_O_OUT_Z_MSB 0x2D // Z-axis MSB +#define L3GD20H_O_FIFO_CTRL 0x2E // FIFO control +#define L3GD20H_O_FIFO_SRC 0x2F // FIFO_SRC register +#define L3GD20H_O_IG_CFG 0x30 // Interrupt generation control +#define L3GD20H_O_IG_SRC 0x31 // interrupt source register (read + // only) +#define L3GD20H_O_IG_THS_XH 0x32 // Hi-X threshold +#define L3GD20H_O_IG_THS_XL 0x33 // Lo-X threshold +#define L3GD20H_O_IG_THS_TH 0x34 // Hi-Y threshold +#define L3GD20H_O_IG_THS_TL 0x35 // Lo-Y threshold +#define L3GD20H_O_IG_THS_ZH 0x36 // Hi-Z threshold +#define L3GD20H_O_IG_THS_ZL 0x37 // Lo-X threshold +#define L3GD20H_O_IG_DURATION 0x38 // Interrupt generation duration + // register +#define L3GD20H_O_LOW_ODR 0x39 // Low-speed output data rate (ODR) + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_CTRL1 +// register. +// +//***************************************************************************** +#define L3GD20H_CTRL1_DR_M 0xC0 // ODR select, x8 if Low_ODR = 0 +#define L3GD20H_CTRL1_DR_12_5_HZ \ + 0x00 // 12.5Hz or 100Hz +#define L3GD20H_CTRL1_DR_25_HZ 0x40 // 25Hz or 200Hz +#define L3GD20H_CTRL1_DR_50_HZ 0x80 // 50Hz or 400Hz +#define L3GD20H_CTRL1_DR_800_HZ 0xC0 // 800Hz of Low_ODR=0, or 50Hz + // otherwise +#define L3GD20H_CTRL1_BW_M 0x30 // Bandwidth select +#define L3GD20H_CTRL1_POWER_M 0x08 // Power control +#define L3GD20H_CTRL1_POWER_LOWPOW \ + 0x00 +#define L3GD20H_CTRL1_POWER_NORMAL \ + 0x08 +#define L3GD20H_CTRL1_AXIS_M 0x7 // Axis power control +#define L3GD20H_CTRL1_AXIS_Y_EN 0x01 // Y-axis enable +#define L3GD20H_CTRL1_AXIS_X_EN 0x02 // X-axis enable +#define L3GD20H_CTRL1_AXIS_Z_EN 0x04 // Z-axis enable +#define L3GD20H_CTRL1_DR_S 6 +#define L3GD20H_CTRL1_BW_S 4 +#define L3GD20H_CTRL1_AXIS_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_CTRL2 +// register. +// +//***************************************************************************** +#define L3GD20H_CTRL2_EXTREN_M 0x80 // edge sensitive +#define L3GD20H_CTRL2_EXTREN_DIS \ + 0x00 +#define L3GD20H_CTRL2_EXTREN_EN 0x80 +#define L3GD20H_CTRL2_LVLEN_M 0x40 // level sensitive +#define L3GD20H_CTRL2_LVLEN_DIS 0x00 +#define L3GD20H_CTRL2_LVLEN_EN 0x40 +#define L3GD20H_CTRL2_HPM_M 0x30 // high pass filter mode selection +#define L3GD20H_CTRL2_HPCF_M 0x0F // high pass cutoff frequency + // selection +#define L3GD20H_CTRL2_HPM_S 4 +#define L3GD20H_CTRL2_HPCF_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_CTRL3 +// register. +// +//***************************************************************************** +#define L3GD20H_CTRL3_INT1_IG_M 0x80 // Interrupt enable on INT1 pin +#define L3GD20H_CTRL3_INT1_IG_DIS \ + 0x00 +#define L3GD20H_CTRL3_INT1_IG_EN \ + 0x80 +#define L3GD20H_CTRL3_INT1_BOOT_M \ + 0x40 // Boot status available on INT1 + // pin. +#define L3GD20H_CTRL3_INT1_BOOT_DIS \ + 0x00 +#define L3GD20H_CTRL3_INT1_BOOT_EN \ + 0x40 +#define L3GD20H_CTRL3_H_LACTIVE_M \ + 0x20 // Interrupt active configuration + // on INT; default value: 0 (0: + // high; 1:low) +#define L3GD20H_CTRL3_H_LACTIVE_HI \ + 0x00 +#define L3GD20H_CTRL3_H_LACTIVE_LOW \ + 0x20 +#define L3GD20H_CTRL3_DRIVE_TYPE_M \ + 0x10 // Push- Pull / Open drain; default + // value: 0 (0: push-pull; 1: open + // drain) +#define L3GD20H_CTRL3_DRIVE_TYPE_PP \ + 0x00 +#define L3GD20H_CTRL3_DRIVE_TYPE_OD \ + 0x10 +#define L3GD20H_CTRL3_INT2_DRDY_M \ + 0x08 // Date Ready on DRDY/INT2 pin; + // default value: 0 +#define L3GD20H_CTRL3_INT2_DRDY_DIS \ + 0x00 +#define L3GD20H_CTRL3_INT2_DRDY_EN \ + 0x08 +#define L3GD20H_CTRL3_INT2_FTH_M \ + 0x04 // FIFO Threshold interrupt on + // DRDY/INT2 pin; default value: 0 +#define L3GD20H_CTRL3_INT2_FTH_DIS \ + 0x00 +#define L3GD20H_CTRL3_INT2_FTH_EN \ + 0x04 +#define L3GD20H_CTRL3_INT2_ORUN_M \ + 0x02 // FIFO Overrun interrupt on + // DRDY/INT2 pin; default value: 0 +#define L3GD20H_CTRL3_INT2_ORUN_DIS \ + 0x00 +#define L3GD20H_CTRL3_INT2_ORUN_EN \ + 0x02 +#define L3GD20H_CTRL3_INT2_EMPTY_M \ + 0x01 // FIFO Empty interrupt on + // DRDY/INT2 pin; default value: 0 +#define L3GD20H_CTRL3_INT2_EMPTY_DIS \ + 0x00 +#define L3GD20H_CTRL3_INT2_EMPTY_EN \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_CTRL4 +// register. +// +//***************************************************************************** +#define L3GD20H_CTRL4_BDU_M 0x80 // Block data update; default + // value: 0 (0: continuous update; + // 1: output registers not updated + // until MSB and LSB reading) +#define L3GD20H_CTRL4_BDU_CONTINUOUS \ + 0x00 +#define L3GD20H_CTRL4_BDU_MSBLSB \ + 0x80 +#define L3GD20H_CTRL4_ENDIAN_M 0x40 // Endian selection +#define L3GD20H_CTRL4_ENDIAN_LITTLE \ + 0x00 +#define L3GD20H_CTRL4_ENDIAN_BIG \ + 0x40 +#define L3GD20H_CTRL4_FS_M 0x30 // full scale selection; default + // value: 0 +#define L3GD20H_CTRL4_FS_245DPS 0x00 // 245 degrees per second +#define L3GD20H_CTRL4_FS_500DPS 0x10 // 500 degrees per second +#define L3GD20H_CTRL4_FS_2000DPS \ + 0x30 // 2000 degrees per second +#define L3GD20H_CTRL4_IMPEN_M 0x08 // level sensitive latched enable; + // default value: 0 +#define L3GD20H_CTRL4_IMPEN_LVL_DIS \ + 0x00 +#define L3GD20H_CTRL4_IMPEN_LVL_EN \ + 0x08 +#define L3GD20H_CTRL4_SELFTEST_M \ + 0x06 // self-test mode +#define L3GD20H_CTRL4_SELFTEST_NORMAL \ + 0x00 // Normal mode (not self test, + // default) +#define L3GD20H_CTRL4_SELFTEST_MODE0 \ + 0x02 // Self-test mode 0 (+) +#define L3GD20H_CTRL4_SELFTEST_MODE1 \ + 0x06 // Self-test mode 1 (-) +#define L3GD20H_CTRL4_SIM_M 0x01 // SPI Serial Interface Mode + // selection (default = 0, 4-wire) +#define L3GD20H_CTRL4_SIM_4WIRE 0x00 +#define L3GD20H_CTRL4_SIM_3WIRE 0x01 +#define L3GD20H_CTRL4_FS_S 4 +#define L3GD20H_CTRL4_SELFTEST_S \ + 1 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_CTRL5 +// register. +// +//***************************************************************************** +#define L3GD20H_CTRL5_REBOOTCTL_M \ + 0x80 // Reboot memory conent +#define L3GD20H_CTRL5_REBOOTCTL_NORMAL \ + 0x00 +#define L3GD20H_CTRL5_REBOOTCTL_REBOOT \ + 0x80 +#define L3GD20H_CTRL5_FIFOCTL_M 0x40 // FIFO control +#define L3GD20H_CTRL5_FIFOCTL_DIS \ + 0x00 +#define L3GD20H_CTRL5_FIFOCTL_EN \ + 0x40 +#define L3GD20H_CTRL5_STOPONFTH_M \ + 0x20 // Sensing chain FIFO stop values + // memorization at FIFO Threshold; + // default value: 0 +#define L3GD20H_CTRL5_STOPONFTH_UNLIMITED \ + 0x00 +#define L3GD20H_CTRL5_STOPONFTH_THRESH_LIMITED \ + 0x20 +#define L3GD20H_CTRL5_HPEN_M 0x10 // high pass filtern enable + // (default 0) +#define L3GD20H_CTRL5_HPEN_DIS 0x00 +#define L3GD20H_CTRL5_HPEN_EN 0x10 +#define L3GD20H_CTRL5_IG_SEL_M 0x0C // interrupt generator section + // configuration +#define L3GD20H_CTRL5_OUT_SEL_M 0x03 // out selection configuration +#define L3GD20H_CTRL5_IG_SEL_S 2 +#define L3GD20H_CTRL5_OUT_SEL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_STATUS +// register. +// +//***************************************************************************** +#define L3GD20H_STATUS_OR_M 0xF0 // Axis data overrun +#define L3GD20H_STATUS_OR_X 0x10 // X-axis data overrun +#define L3GD20H_STATUS_OR_Y 0x20 // Y-axis data overrun +#define L3GD20H_STATUS_OR_Z 0x40 // Z-axis data overrun +#define L3GD20H_STATUS_OR_ZYX 0x80 // X, Y, and X data overrun +#define L3GD20H_STATUS_DA_M 0xF // Axis data available +#define L3GD20H_STATUS_DA_X 0x01 // X-axis data available +#define L3GD20H_STATUS_DA_Y 0x02 // Y-axis data available +#define L3GD20H_STATUS_DA_Z 0x04 // Z-axis data available +#define L3GD20H_STATUS_DA_ZYX 0x08 // X, Y, and X data available +#define L3GD20H_STATUS_OR_S 4 +#define L3GD20H_STATUS_DA_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_FIFO_CTRL +// register. +// +//***************************************************************************** +#define L3GD20H_FIFO_CTRL_THRESH_M \ + 0x1F // FIFO Threshold setting +#define L3GD20H_FIFO_CTRL_MODE_M \ + 0xE // FIFO mode setting +#define L3GD20H_FIFO_CTRL_MODE_S \ + 5 +#define L3GD20H_FIFO_CTRL_THRESH_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_FIFO_SRC +// register. +// +//***************************************************************************** +#define L3GD20H_FIFO_SRC_FTH_M 0x80 // FIFO threshold is greater than + // or equal to level or less than + // level +#define L3GD20H_FIFO_SRC_FTH_LT 0x00 +#define L3GD20H_FIFO_SRC_FTH_GEQ \ + 0x80 +#define L3GD20H_FIFO_SRC_OVRN_M 0x40 // overrun status bit +#define L3GD20H_FIFO_SRC_OVRN_FILLED \ + 0x40 +#define L3GD20H_FIFO_SRC_EMPTY_M \ + 0x20 // FIFO empty +#define L3GD20H_FIFO_SRC_EMPTY_EMPTY \ + 0x20 +#define L3GD20H_FIFO_SRC_STORE_SAMPLES_M \ + 0x1F // FIFO stored data level of the + // unread samples +#define L3GD20H_FIFO_SRC_STORE_SAMPLES_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_IG_CFG +// register. +// +//***************************************************************************** +#define L3GD20H_IG_CFG_ANDOR_M 0x80 // AND/OR combination of Interrupt + // events; default value: 0 +#define L3GD20H_IG_CFG_ANDOR_OR 0x00 +#define L3GD20H_IG_CFG_ANDOR_AND \ + 0x80 +#define L3GD20H_IG_CFG_LIR_M 0x40 // Latch Interrupt Request; default + // value: 0 +#define L3GD20H_IG_CFG_LIR_LATCHED \ + 0x40 +#define L3GD20H_IG_CFG_ZHI_M 0x20 // enable interrupt generation on + // Z-hi event +#define L3GD20H_IG_CFG_ZHI_EN 0x20 +#define L3GD20H_IG_CFG_ZLI_M 0x10 // enable interrupt generation on + // Z-low event +#define L3GD20H_IG_CFG_ZLI_EN 0x10 +#define L3GD20H_IG_CFG_YHI_M 0x08 // enable interrupt generation on + // Y-hi event +#define L3GD20H_IG_CFG_YHI_EN 0x08 +#define L3GD20H_IG_CFG_YLI_M 0x04 // enable interrupt generation on + // Y-low event +#define L3GD20H_IG_CFG_YLI_EN 0x04 +#define L3GD20H_IG_CFG_XHI_M 0x02 // enable interrupt generation on + // X-hi event +#define L3GD20H_IG_CFG_XHI_EN 0x02 +#define L3GD20H_IG_CFG_XLI_M 0x01 // enable interrupt generation on + // X-low event +#define L3GD20H_IG_CFG_XLI_EN 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_IG_SRC +// register. +// +//***************************************************************************** +#define L3GD20H_IG_SRC_IA_M 0x40 // interrupt active +#define L3GD20H_IG_SRC_IA_ACTIVE \ + 0x40 +#define L3GD20H_IG_SRC_ZH_M 0x20 // Z-Hi event occurred +#define L3GD20H_IG_SRC_ZH_OCCURRED \ + 0x20 +#define L3GD20H_IG_SRC_ZL_M 0x10 // Z-Low event occurred +#define L3GD20H_IG_SRC_ZL_OCCURRED \ + 0x10 +#define L3GD20H_IG_SRC_YH_M 0x08 // Y-Hi event occurred +#define L3GD20H_IG_SRC_YH_OCCURRED \ + 0x08 +#define L3GD20H_IG_SRC_YL_M 0x04 // Y-Low event occurred +#define L3GD20H_IG_SRC_YL_OCCURRED \ + 0x04 +#define L3GD20H_IG_SRC_XH_M 0x02 // X-Hi event occurred +#define L3GD20H_IG_SRC_XH_OCCURRED \ + 0x02 +#define L3GD20H_IG_SRC_XL_M 0x01 // X-Low event occurred +#define L3GD20H_IG_SRC_XL_OCCURRED \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_IG_THS_XH +// register. +// +//***************************************************************************** +#define L3GD20H_IG_THS_XH_DCRM_M \ + 0x80 // interrupt generation counter + // mode +#define L3GD20H_IG_THS_XH_DCRM_RESET \ + 0x00 +#define L3GD20H_IG_THS_XH_DCRM_DECREMENT \ + 0x80 +#define L3GD20H_IG_THS_XH_THSX_M \ + 0x7F // THSX[14-8], default 0x0 +#define L3GD20H_IG_THS_XH_THSX_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_IG_THS_TH +// register. +// +//***************************************************************************** +#define L3GD20H_IG_THS_TH_THSY_M \ + 0x7F // THSY[14-8], default 0x0 +#define L3GD20H_IG_THS_TH_THSY_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_IG_THS_ZH +// register. +// +//***************************************************************************** +#define L3GD20H_IG_THS_ZH_THSX_M \ + 0x7F // THSZ[14-8], default 0x0 +#define L3GD20H_IG_THS_ZH_THSX_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_IG_DURATION +// register. +// +//***************************************************************************** +#define L3GD20H_IG_DURATION_WAIT_M \ + 0x80 // If WAIT is enabled then DURATION + // samples must occur before + // asserting the interrupt. +#define L3GD20H_IG_DURATION_WAIT_DIS \ + 0x00 +#define L3GD20H_IG_DURATION_WAIT_EN \ + 0x80 +#define L3GD20H_IG_DURATION_DURATION_M \ + 0x7F // Duration D[6-0] +#define L3GD20H_IG_DURATION_DURATION_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the L3GD20H_O_LOW_ODR +// register. +// +//***************************************************************************** +#define L3GD20H_LOW_ODR_DRDY_HL_M \ + 0x20 // DRDY/INT2 active level +#define L3GD20H_LOW_ODR_DRDY_HL_ACTIVE_LOW \ + 0x00 +#define L3GD20H_LOW_ODR_DRDY_HL_ACTIVE_HIGH \ + 0x20 +#define L3GD20H_LOW_ODR_I2C_DISABLE_M \ + 0x08 // disable I2C interface +#define L3GD20H_LOW_ODR_I2C_DISABLE_BOTH \ + 0x00 +#define L3GD20H_LOW_ODR_I2C_DISABLE_SPI_ONLY \ + 0x08 +#define L3GD20H_LOW_ODR_SWRESET_M \ + 0x04 // software reset +#define L3GD20H_LOW_ODR_SWRESET_NORMAL \ + 0x00 +#define L3GD20H_LOW_ODR_SWRESET_RESET \ + 0x04 +#define L3GD20H_LOW_ODR_DATARATE_M \ + 0x01 // Low-speed data rate enable; + // default value: 0 +#define L3GD20H_LOW_ODR_DATARATE_HIGH \ + 0x00 +#define L3GD20H_LOW_ODR_DATARATE_LOW \ + 0x01 + +#endif // __SENSORLIB_HW_L3GD20H_H__ diff --git a/sensorlib/hw_lsm303d.h b/sensorlib/hw_lsm303d.h new file mode 100644 index 0000000..19ea89e --- /dev/null +++ b/sensorlib/hw_lsm303d.h @@ -0,0 +1,1017 @@ +//***************************************************************************** +// +// hw_lsm303d.h - Macros used when accessing the ST LSM303D +// accelerometer/magnetometer +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_LSM303D_H__ +#define __SENSORLIB_HW_LSM303D_H__ + +//***************************************************************************** +// +// The following are defines for the LSM303D register addresses +// +//***************************************************************************** +#define LSM303D_O_TEMP_OUT_LSB 0x5 // Temperature bits 7-0 +#define LSM303D_O_TEMP_OUT_MSB 0x6 // Temperature bits 11-8 +#define LSM303D_O_MAG_STATUS 0x7 // Magnetic status register +#define LSM303D_O_MAG_OUT_X_MSB 0x08 // X-axis MSB +#define LSM303D_O_MAG_OUT_X_LSB 0x09 // X-axis LSB +#define LSM303D_O_MAG_OUT_Y_MSB 0x0A // Y-axis MSB +#define LSM303D_O_MAG_OUT_Y_LSB 0x0B // Y-axis LSB +#define LSM303D_O_MAG_OUT_Z_MSB 0x0C // Z-axis MSB +#define LSM303D_O_MAG_OUT_Z_LSB 0x0D // Z-axis LSB +#define LSM303D_O_WHO_AM_I 0x0F // ID register, constant 0x49 +#define LSM303D_O_MAG_INT_CTRL 0x12 // magnetic control register +#define LSM303D_O_MAG_INT_SRC 0x13 // Interrupt source (status) for + // magnetometer interrupt +#define LSM303D_O_MAG_THS_MSB 0x14 // Threshold MSB +#define LSM303D_O_MAG_THS_LSB 0x15 // Threshold LSB. Even though the + // threshold is expressed in + // absolute value, the device + // detects both positive and + // negative thresholds. +#define LSM303D_O_MAG_OFFSET_X_MSB \ + 0x16 // MSB of Magnetic offset for + // X-axis. The value is expressed + // in 16-bit as 2s complement. +#define LSM303D_O_MAG_OFFSET_X_LSB \ + 0x17 // LSB of Magnetic offset for + // X-axis. The value is expressed + // in 16-bit as 2s complement. +#define LSM303D_O_MAG_OFFSET_Y_MSB \ + 0x18 // MSB of Magnetic offset for + // Y-axis. The value is expressed + // in 16-bit as 2s complement. +#define LSM303D_O_MAG_OFFSET_Y_LSB \ + 0x19 // LSB of Magnetic offset for + // Y-axis. The value is expressed + // in 16-bit as 2s complement. +#define LSM303D_O_MAG_OFFSET_Z_MSB \ + 0x1A // MSB of Magnetic offset for + // Z-axis. The value is expressed + // in 16-bit as 2s complement. +#define LSM303D_O_MAG_OFFSET_Z_LSB \ + 0x1B // LSB of Magnetic offset for + // Z-axis. The value is expressed + // in 16-bit as 2s complement. +#define LSM303D_O_ACCEL_REFERENCE_X \ + 0x1C // Reference value for high-pass + // filter for X-axis acceleration + // data. +#define LSM303D_O_ACCEL_REFERENCE_Y \ + 0x1D // Reference value for high-pass + // filter for Y-axis acceleration + // data. +#define LSM303D_O_ACCEL_REFERENCE_Z \ + 0x1E // Reference value for high-pass + // filter for Z-axis acceleration + // data. +#define LSM303D_O_CTRL0 0x1F // Accel control 0 +#define LSM303D_O_CTRL1 0x20 // Control 1 - power settings +#define LSM303D_O_CTRL2 0x21 // Control 2 +#define LSM303D_O_CTRL3 0x22 // Control 3 +#define LSM303D_O_CTRL4 0x23 // Control 4 +#define LSM303D_O_CTRL5 0x24 // Control 5 +#define LSM303D_O_CTRL6 0x25 // Control 6 +#define LSM303D_O_CTRL7 0x26 // Control 7 +#define LSM303D_O_STATUS 0x27 // Status register +#define LSM303D_O_OUT_X_LSB 0x28 // X-axis LSB +#define LSM303D_O_OUT_X_MSB 0x29 // X-axis MSB +#define LSM303D_O_OUT_Y_LSB 0x2A // Y-axis LSB +#define LSM303D_O_OUT_Y_MSB 0x2B // Y-axis MSB +#define LSM303D_O_OUT_Z_LSB 0x2C // Z-axis LSB +#define LSM303D_O_OUT_Z_MSB 0x2D // Z-axis MSB +#define LSM303D_O_FIFO_CTRL 0x2E // FIFO control +#define LSM303D_O_FIFO_SRC 0x2F // FIFO_SRC register +#define LSM303D_O_INT1_CFG 0x30 // INT1 interrupt generation; this + // register only writable after + // boot +#define LSM303D_O_INT1_SRC 0x31 // interrupt source register (read + // only) +#define LSM303D_O_INT1_THS 0x32 // Interrupt 1 threshold +#define LSM303D_O_INT1_DURATION 0x33 // INT1 duration register +#define LSM303D_O_INT2_CFG 0x34 // INT2 interrupt generation; this + // register only writable after + // boot +#define LSM303D_O_INT2_SRC 0x35 // INT2 source register (read only) +#define LSM303D_O_INT2_THS 0x36 // INT2 threshold +#define LSM303D_O_INT2_DURATION 0x37 // INT2 duration register +#define LSM303D_O_CLICK_CFG 0x38 // Click config A register +#define LSM303D_O_CLICK_SRC 0x39 // Click source A +#define LSM303D_O_CLICK_THS 0x3A // click-click threshold +#define LSM303D_O_TIME_LIMIT 0x3B // Time Limit A register +#define LSM303D_O_TIME_LATENCY 0x3C // Time Latency A register; 1 LSB = + // 1/ODR; TLA7 through TLA0 define + // the time interval that starts + // after the first click detection + // where the click detection + // procedure is disabled, in cases + // where the device is configured + // for double click detection +#define LSM303D_O_TIME_WINDOW 0x3D // Time Window A register; 1 LSB = + // 1/ODR; TW7 through TW0 define + // the maximum interval of time + // that can elapse after the end of + // the latency interval in which + // the click detection procedure + // can start, in cases where the + // device is configured for double + // click detection +#define LSM303D_O_ACT_THS 0x3E // Sleep to Wake, Return to Sleep + // activation threshold 1LSb = 16mg +#define LSM303D_O_ACT_DUR 0x3F // Sleep to Wake, Return to Sleep + // duration DUR = (Act_DUR + + // 1)*8/ODR + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_TEMP_OUT_LSB +// register. +// +//***************************************************************************** +#define LSM303D_TEMP_OUT_LSB_LSB_M \ + 0xFF +#define LSM303D_TEMP_OUT_LSB_LSB_S \ + 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_TEMP_OUT_MSB +// register. +// +//***************************************************************************** +#define LSM303D_TEMP_OUT_MSB_MSB_M \ + 0x0F +#define LSM303D_TEMP_OUT_MSB_MSB_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_MAG_STATUS +// register. +// +//***************************************************************************** +#define LSM303D_MAG_STATUS_ZYXTMOR_M \ + 0x80 // X,Y,Z axis and temp data overrun +#define LSM303D_MAG_STATUS_ZYXTMOR_TRUE \ + 0x80 +#define LSM303D_MAG_STATUS_ZMOR_M \ + 0x40 // Z-axis mag data overrun +#define LSM303D_MAG_STATUS_ZMOR_TRUE \ + 0x40 +#define LSM303D_MAG_STATUS_YMOR_M \ + 0x20 // Y-axis mag data overrun +#define LSM303D_MAG_STATUS_YMOR_TRUE \ + 0x20 +#define LSM303D_MAG_STATUS_XMOR_M \ + 0x10 // X-axis mag data overrun +#define LSM303D_MAG_STATUS_XMOR_TRUE \ + 0x10 +#define LSM303D_MAG_STATUS_ZYXTMD_M \ + 0x08 // X,Y,Z axis and temp data + // available +#define LSM303D_MAG_STATUS_ZYXTMD_AVAIL \ + 0x08 +#define LSM303D_MAG_STATUS_ZMD_M \ + 0x04 // New mag data available for Z + // axis +#define LSM303D_MAG_STATUS_ZMD_AVAIL \ + 0x04 +#define LSM303D_MAG_STATUS_YMD_M \ + 0x02 // New mag data available for Y + // axis +#define LSM303D_MAG_STATUS_YMD_AVAIL \ + 0x02 +#define LSM303D_MAG_STATUS_XMD_M \ + 0x01 // New mag data available for X + // axis +#define LSM303D_MAG_STATUS_XMD_AVAIL \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_MAG_INT_CTRL +// register. +// +//***************************************************************************** +#define LSM303D_MAG_INT_CTRL_XINT_M \ + 0x80 // Enable interrupt recognition on + // X-axis for magnetic data. +#define LSM303D_MAG_INT_CTRL_XINT_ENABLE \ + 0x80 +#define LSM303D_MAG_INT_CTRL_YINT_M \ + 0x40 // Enable interrupt recognition on + // Y-axis for magnetic data. +#define LSM303D_MAG_INT_CTRL_YINT_ENABLE \ + 0x40 +#define LSM303D_MAG_INT_CTRL_ZINT_M \ + 0x20 // Enable interrupt recognition on + // Z-axis for magnetic data. +#define LSM303D_MAG_INT_CTRL_ZINT_ENABLE \ + 0x20 +#define LSM303D_MAG_INT_CTRL_PINCFG_M \ + 0x10 // interrupt pin drive + // configuration +#define LSM303D_MAG_INT_CTRL_PINCFG_PUSHPULL \ + 0x00 +#define LSM303D_MAG_INT_CTRL_PINCFG_OPENDRAIN \ + 0x10 +#define LSM303D_MAG_INT_CTRL_POLARITY_M \ + 0x08 // interrupt polarity +#define LSM303D_MAG_INT_CTRL_POLARITY_LOW \ + 0x00 +#define LSM303D_MAG_INT_CTRL_POLARITY_HIGH \ + 0x08 +#define LSM303D_MAG_INT_CTRL_LATCH_M \ + 0x04 // latch interrupt request +#define LSM303D_MAG_INT_CTRL_LATCH_ENABLE \ + 0x04 +#define LSM303D_MAG_INT_CTRL_4D_M \ + 0x02 // 4D detection on acceleration + // data is enabled when 6D bit in + // IG_CFG1 +#define LSM303D_MAG_INT_CTRL_4D_ENABLE \ + 0x02 +#define LSM303D_MAG_INT_CTRL_MI_M \ + 0x01 // Interrupt generation for + // magnetic data +#define LSM303D_MAG_INT_CTRL_MI_ENABLE \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_MAG_INT_SRC +// register. +// +//***************************************************************************** +#define LSM303D_MAG_INT_SRC_M_PTH_X_M \ + 0x80 // Magnetic value on X-axis exceeds + // the threshold on the positive + // side. +#define LSM303D_MAG_INT_SRC_M_PTH_X_ACTIVE \ + 0x80 +#define LSM303D_MAG_INT_SRC_M_PTH_Y_M \ + 0x40 // Magnetic value on Y-axis exceeds + // the threshold on the positive + // side. +#define LSM303D_MAG_INT_SRC_M_PTH_Y_ACTIVE \ + 0x40 +#define LSM303D_MAG_INT_SRC_M_PTH_Z_M \ + 0x20 // Magnetic value on Z-axis exceeds + // the threshold on the positive + // side. +#define LSM303D_MAG_INT_SRC_M_PTH_Z_ACTIVE \ + 0x20 +#define LSM303D_MAG_INT_SRC_M_NTH_X_M \ + 0x10 // Magnetic value on X-axis exceeds + // the threshold on the negative + // side. +#define LSM303D_MAG_INT_SRC_M_NTH_X_ACTIVE \ + 0x10 +#define LSM303D_MAG_INT_SRC_M_NTH_Y_M \ + 0x08 // Magnetic value on Y-axis exceeds + // the threshold on the negative + // side. +#define LSM303D_MAG_INT_SRC_M_NTH_Y_ACTIVE \ + 0x08 +#define LSM303D_MAG_INT_SRC_M_NTH_Z_M \ + 0x04 // Magnetic value on Z-axis exceeds + // the threshold on the negative + // side. +#define LSM303D_MAG_INT_SRC_M_NTH_Z_ACTIVE \ + 0x04 +#define LSM303D_MAG_INT_SRC_MROI_M \ + 0x02 // Internal measurement range + // overflow on magnetic value. +#define LSM303D_MAG_INT_SRC_MROI_ACTIVE \ + 0x02 +#define LSM303D_MAG_INT_SRC_MINT_M \ + 0x01 // Magnetic interrupt event. The + // magnetic field value exceeds the + // threshold. +#define LSM303D_MAG_INT_SRC_MINT_ACTIVE \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CTRL0 +// register. +// +//***************************************************************************** +#define LSM303D_CTRL0_BOOT_M 0x80 // Reboot memory content. +#define LSM303D_CTRL0_BOOT_REBOOT \ + 0x80 +#define LSM303D_CTRL0_FIFO_M 0x40 // FIFO enable. +#define LSM303D_CTRL0_FIFO_ENABLE \ + 0x40 +#define LSM303D_CTRL0_FTH_M 0x20 // FIFO programmable threshold + // enable. +#define LSM303D_CTRL0_FTH_ENABLE \ + 0x20 +#define LSM303D_CTRL0_HPCLICK_M 0x04 // High-pass filter enabled for + // click function. +#define LSM303D_CTRL0_HPCLICK_ENABLE \ + 0x04 +#define LSM303D_CTRL0_HPIS1_M 0x02 // High-pass filter enabled for + // interrupt generator 1 +#define LSM303D_CTRL0_HPIS1_ENABLE \ + 0x02 +#define LSM303D_CTRL0_HPIS2_M 0x01 // High-pass filter enabled for + // interrupt generator 2 +#define LSM303D_CTRL0_HPIS2_ENABLE \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CTRL1 +// register. +// +//***************************************************************************** +#define LSM303D_CTRL1_AODR_M 0xF0 // accel output data rate selection +#define LSM303D_CTRL1_AODR_PD 0x00 // Power-down mode +#define LSM303D_CTRL1_AODR_3_125HZ \ + 0x10 +#define LSM303D_CTRL1_AODR_6_2HZ \ + 0x20 +#define LSM303D_CTRL1_AODR_12_5HZ \ + 0x30 +#define LSM303D_CTRL1_AODR_25HZ 0x40 +#define LSM303D_CTRL1_AODR_50HZ 0x50 +#define LSM303D_CTRL1_AODR_100HZ \ + 0x60 +#define LSM303D_CTRL1_AODR_200HZ \ + 0x70 +#define LSM303D_CTRL1_AODR_400HZ \ + 0x80 +#define LSM303D_CTRL1_AODR_800HZ \ + 0x90 +#define LSM303D_CTRL1_AODR_1600HZ \ + 0xA0 +#define LSM303D_CTRL1_BDU_M 0x08 // Block data update for + // acceleration and magnetic data. + // (0: continuous update; 1: output + // registers not updated until MSB + // and LSB have been read), default + // continuous +#define LSM303D_CTRL1_BDU_CONTINUOUS \ + 0x00 +#define LSM303D_CTRL1_BDU_BLOCK 0x08 +#define LSM303D_CTRL1_AXIS_M 0x7 // Axis power control +#define LSM303D_CTRL1_AXIS_Y_EN 0x01 // Y-axis enable +#define LSM303D_CTRL1_AXIS_X_EN 0x02 // X-axis enable +#define LSM303D_CTRL1_AXIS_Z_EN 0x04 // Z-axis enable +#define LSM303D_CTRL1_AODR_S 4 +#define LSM303D_CTRL1_AXIS_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CTRL2 +// register. +// +//***************************************************************************** +#define LSM303D_CTRL2_AFS_M 0x38 // Acceleration full-scale + // selection. +#define LSM303D_CTRL2_AFS_2G 0x00 // +/- 2G sensitivity +#define LSM303D_CTRL2_AFS_4G 0x08 // +/- 4G sensitivity +#define LSM303D_CTRL2_AFS_6G 0x10 // +/- 6G sensitivity +#define LSM303D_CTRL2_AFS_8G 0x18 // +/- 8G sensitivity +#define LSM303D_CTRL2_AFS_16G 0x40 // +/- 16G sensitivity +#define LSM303D_CTRL2_ABW_M 0xC // anti-alias filter bandwidth +#define LSM303D_CTRL2_ABW_773HZ 0x00 +#define LSM303D_CTRL2_ABW_194HZ 0x40 +#define LSM303D_CTRL2_ABW_362HZ 0x80 +#define LSM303D_CTRL2_ABW_50HZ 0xC0 +#define LSM303D_CTRL2_AST_M 0x02 // Acceleration self-test enable. +#define LSM303D_CTRL2_AST_ENABLE \ + 0x02 +#define LSM303D_CTRL2_SIM_M 0x01 // SPI Serial Interface mode + // selection. (default: 4 wire) +#define LSM303D_CTRL2_SIM_4WIRE 0x00 +#define LSM303D_CTRL2_SIM_3WIRE 0x01 +#define LSM303D_CTRL2_ABW_S 6 +#define LSM303D_CTRL2_AFS_S 3 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CTRL3 +// register. +// +//***************************************************************************** +#define LSM303D_CTRL3_INT1_BOOT_M \ + 0x80 // Boot on INT1 enable +#define LSM303D_CTRL3_INT1_BOOT_EN \ + 0x80 +#define LSM303D_CTRL3_INT1_CLICK_M \ + 0x40 // Click generator interrupt on + // INT1. +#define LSM303D_CTRL3_INT1_CLICK_EN \ + 0x40 +#define LSM303D_CTRL3_INT1_IG1_M \ + 0x20 // Inertial interrupt generator 1 + // on INT1 +#define LSM303D_CTRL3_INT1_IG1_EN \ + 0x20 +#define LSM303D_CTRL3_INT1_IG2_M \ + 0x10 // Inertial interrupt generator 2 + // on INT1 +#define LSM303D_CTRL3_INT1_IG2_EN \ + 0x10 +#define LSM303D_CTRL3_INT1_IGM_M \ + 0x08 // Magnetic interrupt generator on + // INT1 +#define LSM303D_CTRL3_INT1_IGM_EN \ + 0x08 +#define LSM303D_CTRL3_INT1_ACCEL_DRDY_M \ + 0x04 // Accelerometer data-ready signal + // on INT1 +#define LSM303D_CTRL3_INT1_ACCEL_DRDY_EN \ + 0x04 +#define LSM303D_CTRL3_INT1_MAG_DRDY_M \ + 0x02 // Magnetometer data-ready signal + // on INT1. +#define LSM303D_CTRL3_INT1_MAG_DRDY_EN \ + 0x02 +#define LSM303D_CTRL3_INT1_EMPTY_M \ + 0x01 // FIFO empty indication on INT1 +#define LSM303D_CTRL3_INT1_EMPTY_EN \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CTRL4 +// register. +// +//***************************************************************************** +#define LSM303D_CTRL4_INT2_CLICK_M \ + 0x80 // Click generator interrupt on + // INT2 +#define LSM303D_CTRL4_INT2_CLICK_EN \ + 0x80 +#define LSM303D_CTRL4_INT2_IG1_M \ + 0x40 // Inertial interrupt generator 1 + // on INT2 +#define LSM303D_CTRL4_INT2_IG1_EN \ + 0x40 +#define LSM303D_CTRL4_INT2_IG2_M \ + 0x20 // Inertial interrupt generator 2 + // on INT2 +#define LSM303D_CTRL4_INT2_IG2_EN \ + 0x20 +#define LSM303D_CTRL4_INT2_IGM_M \ + 0x10 // Magnetic interrupt generator on + // INT2 +#define LSM303D_CTRL4_INT2_IGM_EN \ + 0x10 +#define LSM303D_CTRL4_INT2_ACCEL_DRDY_M \ + 0x08 // Accelerometer data-ready signal + // on INT2 +#define LSM303D_CTRL4_INT2_ACCEL_DRDY_EN \ + 0x08 +#define LSM303D_CTRL4_INT2_MAG_DRDY_M \ + 0x04 // Magnetometer data-ready signal + // on INT2 +#define LSM303D_CTRL4_INT2_MAG_DRDY_EN \ + 0x04 +#define LSM303D_CTRL4_INT2_OVR_M \ + 0x02 // FIFO overrun interrupt on INT2 +#define LSM303D_CTRL4_INT2_OVR_EN \ + 0x02 +#define LSM303D_CTRL4_INT2_FTH_M \ + 0x01 // FIFO threshold interrupt on INT2 +#define LSM303D_CTRL4_INT2_FTH_EN \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CTRL5 +// register. +// +//***************************************************************************** +#define LSM303D_CTRL5_TEMP_M 0x80 // Temperature sensor enable. +#define LSM303D_CTRL5_TEMP_EN 0x80 +#define LSM303D_CTRL5_MRES_M 0x60 // Magnetic resolution selection +#define LSM303D_CTRL5_MRES_LOW 0x00 // low resolution +#define LSM303D_CTRL5_MRES_HIGH 0x60 // high resolution +#define LSM303D_CTRL5_MODR_M 0x1C // mag output data rate selection +#define LSM303D_CTRL5_MODR_3_125HZ \ + 0x00 +#define LSM303D_CTRL5_MODR_6_2HZ \ + 0x04 +#define LSM303D_CTRL5_MODR_12_5HZ \ + 0x08 +#define LSM303D_CTRL5_MODR_25HZ 0x0C +#define LSM303D_CTRL5_MODR_50HZ 0x10 +#define LSM303D_CTRL5_MODR_100HZ \ + 0x14 +#define LSM303D_CTRL5_LIR2_M 0x02 // latch interrupt request on int2 +#define LSM303D_CTRL5_LIR2_EN 0x02 +#define LSM303D_CTRL5_LIR1_M 0x01 // latch interrupt request on int1 +#define LSM303D_CTRL5_LIR1_EN 0x01 +#define LSM303D_CTRL5_MRES_S 5 +#define LSM303D_CTRL5_MODR_S 2 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CTRL6 +// register. +// +//***************************************************************************** +#define LSM303D_CTRL6_MFS_M 0x60 // magnetic full scale select +#define LSM303D_CTRL6_MFS_2G 0x00 // +/- 2 gauss +#define LSM303D_CTRL6_MFS_4G 0x20 // +/- 4 gauss +#define LSM303D_CTRL6_MFS_8G 0x40 // +/- 8 gauss +#define LSM303D_CTRL6_MFS_12G 0x60 // +/- 16 gauss +#define LSM303D_CTRL6_MFS_S 5 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CTRL7 +// register. +// +//***************************************************************************** +#define LSM303D_CTRL7_AHPM_M 0xC0 // High-pass filter mode selection + // for acceleration data +#define LSM303D_CTRL7_AHPM_NORMAL \ + 0x00 +#define LSM303D_CTRL7_AHPM_REFERENCE \ + 0x40 +#define LSM303D_CTRL7_AHPM_AUTORESET \ + 0xC0 +#define LSM303D_CTRL7_AFDS_M 0x20 // default: internal filter + // bypassed +#define LSM303D_CTRL7_AFDS_BYPASSED \ + 0x00 +#define LSM303D_CTRL7_AFDS_FILTERED \ + 0x20 +#define LSM303D_CTRL7_TEMP_ONLY_M \ + 0x10 // Temperature sensor only mode, + // mag off +#define LSM303D_CTRL7_TEMP_ONLY_EN \ + 0x10 +#define LSM303D_CTRL7_MLP_M 0x04 // Magnetic data low-power mode. If + // this bit is 1, the M_ODR [2:0] + // is set to 3.125 Hz independently + // from the MODR settings. +#define LSM303D_CTRL7_MLP_NORMAL \ + 0x00 +#define LSM303D_CTRL7_MLP_LOWPOWER \ + 0x04 +#define LSM303D_CTRL7_MD_M 0x3 // Magnetic sensor mode selection +#define LSM303D_CTRL7_MD_CONTINUOUS \ + 0x00 +#define LSM303D_CTRL7_MD_SINGLE 0x01 +#define LSM303D_CTRL7_MD_POWERDOWN \ + 0x02 +#define LSM303D_CTRL7_AHPM_S 6 +#define LSM303D_CTRL7_MD_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_STATUS +// register. +// +//***************************************************************************** +#define LSM303D_STATUS_OR_M 0xF0 // Accel Axis data overrun +#define LSM303D_STATUS_OR_X 0x10 // Accel X-axis data overrun +#define LSM303D_STATUS_OR_Y 0x20 // Accel Y-axis data overrun +#define LSM303D_STATUS_OR_Z 0x40 // Accel Z-axis data overrun +#define LSM303D_STATUS_OR_ZYX 0x80 // Accel X, Y, and X data overrun +#define LSM303D_STATUS_DA_M 0xF // Accel data available +#define LSM303D_STATUS_DA_X 0x01 // Accel X-axis data available +#define LSM303D_STATUS_DA_Y 0x02 // Accel Y-axis data available +#define LSM303D_STATUS_DA_Z 0x04 // Accel Z-axis data available +#define LSM303D_STATUS_DA_ZYX 0x08 // Accel X, Y, and X data available +#define LSM303D_STATUS_OR_S 4 +#define LSM303D_STATUS_DA_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_FIFO_CTRL +// register. +// +//***************************************************************************** +#define LSM303D_FIFO_CTRL_MODE_M \ + 0xE0 // FIFO mode setting +#define LSM303D_FIFO_CTRL_MODE_BYPASS \ + 0x00 // Bypass mode +#define LSM303D_FIFO_CTRL_MODE_FIFO \ + 0x20 // FIFO mode +#define LSM303D_FIFO_CTRL_MODE_STREAM \ + 0x40 // Stream mode +#define LSM303D_FIFO_CTRL_MODE_S2F \ + 0x60 // Stream-to-FIFO mode +#define LSM303D_FIFO_CTRL_MODE_B2S \ + 0x80 // Bypass-to-stream mode +#define LSM303D_FIFO_CTRL_THRESH_M \ + 0x1F // FIFO Threshold setting +#define LSM303D_FIFO_CTRL_MODE_S \ + 5 +#define LSM303D_FIFO_CTRL_THRESH_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_FIFO_SRC +// register. +// +//***************************************************************************** +#define LSM303D_FIFO_SRC_FTH_M 0x80 // FIFO threshold is greater than + // or equal to level or less than + // level +#define LSM303D_FIFO_SRC_FTH_LT 0x00 +#define LSM303D_FIFO_SRC_FTH_GEQ \ + 0x80 +#define LSM303D_FIFO_SRC_OVRN_M 0x40 // overrun status bit +#define LSM303D_FIFO_SRC_OVRN_FILLED \ + 0x40 +#define LSM303D_FIFO_SRC_EMPTY_M \ + 0x20 // FIFO empty +#define LSM303D_FIFO_SRC_EMPTY_EMPTY \ + 0x20 +#define LSM303D_FIFO_SRC_STORE_SAMPLES_M \ + 0x1F // FIFO stored data level of the + // unread samples +#define LSM303D_FIFO_SRC_STORE_SAMPLES_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_INT1_CFG +// register. +// +//***************************************************************************** +#define LSM303D_INT1_CFG_ANDOR_M \ + 0x80 // AND/OR combination of Interrupt + // events; default value: 0 +#define LSM303D_INT1_CFG_ANDOR_OR \ + 0x00 +#define LSM303D_INT1_CFG_ANDOR_AND \ + 0x80 +#define LSM303D_INT1_CFG_6D_M 0x40 // 6-direction function enabled +#define LSM303D_INT1_CFG_6D_EN 0x40 +#define LSM303D_INT1_CFG_ZHI_M 0x20 // enable interrupt generation on + // Z-hi event +#define LSM303D_INT1_CFG_ZHI_EN 0x20 +#define LSM303D_INT1_CFG_ZUPE_M 0x20 // enable interrupt generation on + // Z-hi event +#define LSM303D_INT1_CFG_ZUPE_EN \ + 0x20 +#define LSM303D_INT1_CFG_ZDOWNE_M \ + 0x10 // enable interrupt generation on + // Z-low event +#define LSM303D_INT1_CFG_ZDOWNE_EN \ + 0x10 +#define LSM303D_INT1_CFG_ZLI_M 0x10 // enable interrupt generation on + // Z-low event +#define LSM303D_INT1_CFG_ZLI_EN 0x10 +#define LSM303D_INT1_CFG_YUPE_M 0x08 // enable interrupt generation on + // Y-hi event +#define LSM303D_INT1_CFG_YUPE_EN \ + 0x08 +#define LSM303D_INT1_CFG_YHI_M 0x08 // enable interrupt generation on + // Y-hi event +#define LSM303D_INT1_CFG_YHI_EN 0x08 +#define LSM303D_INT1_CFG_YDOWNE_M \ + 0x04 // enable interrupt generation on + // Y-low event +#define LSM303D_INT1_CFG_YDOWNE_EN \ + 0x04 +#define LSM303D_INT1_CFG_YLI_M 0x04 // enable interrupt generation on + // Y-low event +#define LSM303D_INT1_CFG_YLI_EN 0x04 +#define LSM303D_INT1_CFG_XUPE_M 0x02 // enable interrupt generation on + // X-hi event +#define LSM303D_INT1_CFG_XUPE_EN \ + 0x02 +#define LSM303D_INT1_CFG_XHI_M 0x02 // enable interrupt generation on + // X-hi event +#define LSM303D_INT1_CFG_XHI_EN 0x02 +#define LSM303D_INT1_CFG_XDOWNE_M \ + 0x01 // enable interrupt generation on + // X-low event +#define LSM303D_INT1_CFG_XDOWNE_EN \ + 0x01 +#define LSM303D_INT1_CFG_XLI_M 0x01 // enable interrupt generation on + // X-low event +#define LSM303D_INT1_CFG_XLI_EN 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_INT1_SRC +// register. +// +//***************************************************************************** +#define LSM303D_INT1_SRC_ZERO_M 0x80 // This bit must be zero +#define LSM303D_INT1_SRC_ZERO_ZERO \ + 0x00 +#define LSM303D_INT1_SRC_ZERO_INVALID \ + 0x80 +#define LSM303D_INT1_SRC_IA_M 0x40 // interrupt active +#define LSM303D_INT1_SRC_IA_ACTIVE \ + 0x40 +#define LSM303D_INT1_SRC_ZH_M 0x20 // Z-Hi event occurred +#define LSM303D_INT1_SRC_ZH_OCCURRED \ + 0x20 +#define LSM303D_INT1_SRC_ZL_M 0x10 // Z-Low event occurred +#define LSM303D_INT1_SRC_ZL_OCCURRED \ + 0x10 +#define LSM303D_INT1_SRC_YH_M 0x08 // Y-Hi event occurred +#define LSM303D_INT1_SRC_YH_OCCURRED \ + 0x08 +#define LSM303D_INT1_SRC_YL_M 0x04 // Y-Low event occurred +#define LSM303D_INT1_SRC_YL_OCCURRED \ + 0x04 +#define LSM303D_INT1_SRC_XH_M 0x02 // X-Hi event occurred +#define LSM303D_INT1_SRC_XH_OCCURRED \ + 0x02 +#define LSM303D_INT1_SRC_XL_M 0x01 // X-Low event occurred +#define LSM303D_INT1_SRC_XL_OCCURRED \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_INT1_THS +// register. +// +//***************************************************************************** +#define LSM303D_INT1_THS_ZERO_M 0x80 // This bit must be zero +#define LSM303D_INT1_THS_ZERO_ZERO \ + 0x00 +#define LSM303D_INT1_THS_ZERO_INVALID \ + 0x80 +#define LSM303D_INT1_THS_THS_M 0x7F // THS[6-0] Interrupt threshold, + // default 0x0 +#define LSM303D_INT1_THS_THS_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_INT1_DURATION +// register. +// +//***************************************************************************** +#define LSM303D_INT1_DURATION_ZERO_M \ + 0x80 // This bit must be zero +#define LSM303D_INT1_DURATION_ZERO_ZERO \ + 0x00 +#define LSM303D_INT1_DURATION_ZERO_INVALID \ + 0x80 +#define LSM303D_INT1_DURATION_DURATION_M \ + 0x7F // Duration D[6-0] +#define LSM303D_INT1_DURATION_DURATION_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_INT2_CFG +// register. +// +//***************************************************************************** +#define LSM303D_INT2_CFG_ANDOR_M \ + 0x80 // AND/OR combination of Interrupt + // events; default value: 0 +#define LSM303D_INT2_CFG_ANDOR_OR \ + 0x00 +#define LSM303D_INT2_CFG_ANDOR_AND \ + 0x80 +#define LSM303D_INT2_CFG_6D_M 0x40 // 6-direction function enabled +#define LSM303D_INT2_CFG_6D_EN 0x40 +#define LSM303D_INT2_CFG_ZHI_M 0x20 // enable interrupt generation on + // Z-hi event +#define LSM303D_INT2_CFG_ZHI_EN 0x20 +#define LSM303D_INT2_CFG_ZLI_M 0x10 // enable interrupt generation on + // Z-low event +#define LSM303D_INT2_CFG_ZLI_EN 0x10 +#define LSM303D_INT2_CFG_YHI_M 0x08 // enable interrupt generation on + // Y-hi event +#define LSM303D_INT2_CFG_YHI_EN 0x08 +#define LSM303D_INT2_CFG_YLI_M 0x04 // enable interrupt generation on + // Y-low event +#define LSM303D_INT2_CFG_YLI_EN 0x04 +#define LSM303D_INT2_CFG_XHI_M 0x02 // enable interrupt generation on + // X-hi event +#define LSM303D_INT2_CFG_XHI_EN 0x02 +#define LSM303D_INT2_CFG_XLI_M 0x01 // enable interrupt generation on + // X-low event +#define LSM303D_INT2_CFG_XLI_EN 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_INT2_SRC +// register. +// +//***************************************************************************** +#define LSM303D_INT2_SRC_ZERO_M 0x80 // This bit must be zero +#define LSM303D_INT2_SRC_ZERO_ZERO \ + 0x00 +#define LSM303D_INT2_SRC_ZERO_INVALID \ + 0x80 +#define LSM303D_INT2_SRC_IA_M 0x40 // interrupt active +#define LSM303D_INT2_SRC_IA_ACTIVE \ + 0x40 +#define LSM303D_INT2_SRC_ZH_M 0x20 // Z-Hi event occurred +#define LSM303D_INT2_SRC_ZH_OCCURRED \ + 0x20 +#define LSM303D_INT2_SRC_ZL_M 0x10 // Z-Low event occurred +#define LSM303D_INT2_SRC_ZL_OCCURRED \ + 0x10 +#define LSM303D_INT2_SRC_YH_M 0x08 // Y-Hi event occurred +#define LSM303D_INT2_SRC_YH_OCCURRED \ + 0x08 +#define LSM303D_INT2_SRC_YL_M 0x04 // Y-Low event occurred +#define LSM303D_INT2_SRC_YL_OCCURRED \ + 0x04 +#define LSM303D_INT2_SRC_XH_M 0x02 // X-Hi event occurred +#define LSM303D_INT2_SRC_XH_OCCURRED \ + 0x02 +#define LSM303D_INT2_SRC_XL_M 0x01 // X-Low event occurred +#define LSM303D_INT2_SRC_XL_OCCURRED \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_INT2_THS +// register. +// +//***************************************************************************** +#define LSM303D_INT2_THS_ZERO_M 0x80 // This bit must be zero +#define LSM303D_INT2_THS_ZERO_ZERO \ + 0x00 +#define LSM303D_INT2_THS_ZERO_INVALID \ + 0x80 +#define LSM303D_INT2_THS_THS_M 0x7F // THS[6-0] Interrupt threshold, + // default 0x0 +#define LSM303D_INT2_THS_THS_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_INT2_DURATION +// register. +// +//***************************************************************************** +#define LSM303D_INT2_DURATION_ZERO_M \ + 0x80 // This bit must be zero +#define LSM303D_INT2_DURATION_ZERO_ZERO \ + 0x00 +#define LSM303D_INT2_DURATION_ZERO_INVALID \ + 0x80 +#define LSM303D_INT2_DURATION_DURATION_M \ + 0x7F // Duration D[6-0] +#define LSM303D_INT2_DURATION_DURATION_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CLICK_CFG +// register. +// +//***************************************************************************** +#define LSM303D_CLICK_CFG_ZD_M 0x20 // Enable interrupt double click on + // Z axis +#define LSM303D_CLICK_CFG_ZD_DIS \ + 0x00 +#define LSM303D_CLICK_CFG_ZD_EN 0x20 +#define LSM303D_CLICK_CFG_ZS_M 0x10 // Enable interrupt single click on + // Z axis +#define LSM303D_CLICK_CFG_ZS_DIS \ + 0x00 +#define LSM303D_CLICK_CFG_ZS_EN 0x10 +#define LSM303D_CLICK_CFG_YD_M 0x08 // Enable interrupt double click on + // Y axis +#define LSM303D_CLICK_CFG_YD_DIS \ + 0x00 +#define LSM303D_CLICK_CFG_YD_EN 0x08 +#define LSM303D_CLICK_CFG_YS_M 0x04 // Enable interrupt single click on + // Y axis +#define LSM303D_CLICK_CFG_YS_DIS \ + 0x00 +#define LSM303D_CLICK_CFG_YS_EN 0x04 +#define LSM303D_CLICK_CFG_XD_M 0x02 // Enable interrupt double click on + // X axis +#define LSM303D_CLICK_CFG_XD_DIS \ + 0x00 +#define LSM303D_CLICK_CFG_XD_EN 0x02 +#define LSM303D_CLICK_CFG_XS_M 0x01 // Enable interrupt single click on + // X axis +#define LSM303D_CLICK_CFG_XS_DIS \ + 0x00 +#define LSM303D_CLICK_CFG_XS_EN 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CLICK_SRC +// register. +// +//***************************************************************************** +#define LSM303D_CLICK_SRC_IA_M 0x40 // Interrupt pending +#define LSM303D_CLICK_SRC_IA_NONE \ + 0x00 +#define LSM303D_CLICK_SRC_IA_PENDING \ + 0x40 +#define LSM303D_CLICK_SRC_DCLICK_M \ + 0x20 // double click-click enable +#define LSM303D_CLICK_SRC_DCLICK_DIS \ + 0x00 +#define LSM303D_CLICK_SRC_DCLICK_EN \ + 0x20 +#define LSM303D_CLICK_SRC_SCLICK_M \ + 0x10 // single click-click enable +#define LSM303D_CLICK_SRC_SCLICK_DIS \ + 0x00 +#define LSM303D_CLICK_SRC_SCLICK_EN \ + 0x10 +#define LSM303D_CLICK_SRC_SIGN_M \ + 0x08 // click-click sign +#define LSM303D_CLICK_SRC_SIGN_POSITIVE \ + 0x00 +#define LSM303D_CLICK_SRC_SIGN_NEGATIVE \ + 0x08 +#define LSM303D_CLICK_SRC_AXIS_M \ + 0x7 // Axis click detection +#define LSM303D_CLICK_SRC_AXIS_X \ + 0x01 // X click-click detection +#define LSM303D_CLICK_SRC_AXIS_Y \ + 0x02 // Y click-click detection +#define LSM303D_CLICK_SRC_AXIS_Z \ + 0x04 // Z click-click detection +#define LSM303D_CLICK_SRC_AXIS_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_CLICK_THS +// register. +// +//***************************************************************************** +#define LSM303D_CLICK_THS_THS_M 0x7F // Threshold; 1 LSB = full-scale / + // 128; THS6 through THS0 define + // the threshold which is used by + // the system to start the click + // detection procedure; the + // threshold value is expressed + // over 7 bits as an unsigned + // number +#define LSM303D_CLICK_THS_THS_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_TIME_LIMIT +// register. +// +//***************************************************************************** +#define LSM303D_TIME_LIMIT_TLI_M \ + 0x7F // Time Limit; 1 LSB = 1/ODR; TLI7 + // through TLI0 define the maximum + // time interval that can elapse + // between the start of the click + // detection procedure (the + // acceleration on the selected + // channel exceeds the programmed + // threshold) and when the + // acceleration goes back below the + // threshold +#define LSM303D_TIME_LIMIT_TLI_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_ACT_THS +// register. +// +//***************************************************************************** +#define LSM303D_ACT_THS_THRESHOLD_M \ + 0x7F +#define LSM303D_ACT_THS_THRESHOLD_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303D_O_ACT_DUR +// register. +// +//***************************************************************************** +#define LSM303D_ACT_DUR_THRESHOLD_M \ + 0x7F +#define LSM303D_ACT_DUR_THRESHOLD_S \ + 0 + +#endif // __SENSORLIB_HW_LSM303D_H__ diff --git a/sensorlib/hw_lsm303dlhc.h b/sensorlib/hw_lsm303dlhc.h new file mode 100644 index 0000000..06cf9ba --- /dev/null +++ b/sensorlib/hw_lsm303dlhc.h @@ -0,0 +1,1034 @@ +//***************************************************************************** +// +// hw_lsm303dlhc.h - Macros used when accessing the ST LSM303DLHC accel/mag +// combo +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_LSM303DLHC_H__ +#define __SENSORLIB_HW_LSM303DLHC_H__ + +//***************************************************************************** +// +// The following are defines for the LSM303DLHC register addresses +// +//***************************************************************************** +#define LSM303DLHC_O_MAG_CRA 0x0 // Magnetometer control +#define LSM303DLHC_O_MAG_CRB 0x01 // Gain configuration +#define LSM303DLHC_O_MAG_MR 0x02 // Mode configuration +#define LSM303DLHC_O_MAG_OUT_X_MSB \ + 0x03 // X-axis MSB +#define LSM303DLHC_O_MAG_OUT_X_LSB \ + 0x04 // X-axis LSB +#define LSM303DLHC_O_MAG_OUT_Y_MSB \ + 0x05 // Y-axis MSB +#define LSM303DLHC_O_MAG_OUT_Y_LSB \ + 0x06 // Y-axis LSB +#define LSM303DLHC_O_MAG_OUT_Z_MSB \ + 0x07 // Z-axis MSB +#define LSM303DLHC_O_MAG_OUT_Z_LSB \ + 0x08 // Z-axis LSB +#define LSM303DLHC_O_MAG_SR 0x09 // Status register +#define LSM303DLHC_O_MAG_IRA 0x0A +#define LSM303DLHC_O_MAG_IRB 0x0B +#define LSM303DLHC_O_MAG_IRC 0x0C +#define LSM303DLHC_O_CTRL1 0x20 // Control 1 - power settings +#define LSM303DLHC_O_CTRL2 0x21 // Control 2 +#define LSM303DLHC_O_CTRL3 0x22 // Control 3 +#define LSM303DLHC_O_CTRL4 0x23 // Control 4 +#define LSM303DLHC_O_CTRL5 0x24 // Control 5 +#define LSM303DLHC_O_CTRL6 0x25 // Control 6 +#define LSM303DLHC_O_REFERENCE 0x26 // Reference/Datacapture_A +#define LSM303DLHC_O_STATUS 0x27 // Status register +#define LSM303DLHC_O_OUT_X_LSB 0x28 // X-axis LSB +#define LSM303DLHC_O_OUT_X_MSB 0x29 // X-axis MSB +#define LSM303DLHC_O_OUT_Y_LSB 0x2A // Y-axis LSB +#define LSM303DLHC_O_OUT_Y_MSB 0x2B // Y-axis MSB +#define LSM303DLHC_O_OUT_Z_LSB 0x2C // Z-axis LSB +#define LSM303DLHC_O_OUT_Z_MSB 0x2D // Z-axis MSB +#define LSM303DLHC_O_FIFO_CTRL 0x2E // FIFO control +#define LSM303DLHC_O_FIFO_SRC 0x2F // FIFO_SRC register +#define LSM303DLHC_O_INT1_CFG_A 0x30 // INT1 interrupt generation; this + // register only writable after + // boot +#define LSM303DLHC_O_INT1_SRC_A 0x31 // interrupt source register (read + // only) +#define LSM303DLHC_O_MAG_TEMP_OUT_MSB \ + 0x31 // Temperature bits 11-4 +#define LSM303DLHC_O_MAG_TEMP_OUT_LSB \ + 0x32 // Temperature bits 3-0 +#define LSM303DLHC_O_INT1_THS_A 0x32 // Interrupt 1 threshold +#define LSM303DLHC_O_INT1_DURATION_A \ + 0x33 // INT1 duration register +#define LSM303DLHC_O_INT2_CFG_A 0x34 // INT2 interrupt generation; this + // register only writable after + // boot +#define LSM303DLHC_O_INT2_SRC_A 0x35 // INT2 source register (read only) +#define LSM303DLHC_O_INT2_THS_A 0x36 // INT2 threshold +#define LSM303DLHC_O_INT2_DURATION_A \ + 0x37 // INT2 duration register +#define LSM303DLHC_O_CLICK_CFG_A \ + 0x38 // Click config A register +#define LSM303DLHC_O_CLICK_SRC_A \ + 0x39 // Click source A +#define LSM303DLHC_O_CLICK_THS_A \ + 0x3A // click-click threshold +#define LSM303DLHC_O_TIME_LIMIT_A \ + 0x3B // Time Limit A register +#define LSM303DLHC_O_TIME_LATENCY_A \ + 0x3C // Time Latency A register; 1 LSB = + // 1/ODR; TLA7 through TLA0 define + // the time interval that starts + // after the first click detection + // where the click detection + // procedure is disabled, in cases + // where the device is configured + // for double click detection +#define LSM303DLHC_O_TIME_WINDOW_A \ + 0x3D // Time Window A register; 1 LSB = + // 1/ODR; TW7 through TW0 define + // the maximum interval of time + // that can elapse after the end of + // the latency interval in which + // the click detection procedure + // can start, in cases where the + // device is configured for double + // click detection + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_MAG_CRA +// register. +// +//***************************************************************************** +#define LSM303DLHC_MAG_CRA_TEMP_M \ + 0x80 // Temperature sensor enable +#define LSM303DLHC_MAG_CRA_TEMP_DIS \ + 0x00 +#define LSM303DLHC_MAG_CRA_TEMP_EN \ + 0x80 +#define LSM303DLHC_MAG_CRA_DO_M 0x1C // Data output rate +#define LSM303DLHC_MAG_CRA_DO_0_75HZ \ + 0x00 // 0.75 Hz +#define LSM303DLHC_MAG_CRA_DO_1_5HZ \ + 0x04 // 1.5 Hz +#define LSM303DLHC_MAG_CRA_DO_3_0HZ \ + 0x08 // 3.0 Hz +#define LSM303DLHC_MAG_CRA_DO_7_5HZ \ + 0x0C // 7.5 Hz +#define LSM303DLHC_MAG_CRA_DO_15HZ \ + 0x10 // 15 Hz +#define LSM303DLHC_MAG_CRA_DO_30HZ \ + 0x14 // 30 Hz +#define LSM303DLHC_MAG_CRA_DO_75HZ \ + 0x18 // 75 Hz +#define LSM303DLHC_MAG_CRA_DO_220HZ \ + 0x1C // 220 Hz +#define LSM303DLHC_MAG_CRA_DO_S 2 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_MAG_CRB +// register. +// +//***************************************************************************** +#define LSM303DLHC_MAG_CRB_GAIN_M \ + 0xE0 // Gain selection +#define LSM303DLHC_MAG_CRB_GAIN_1_3GAUSS \ + 0x20 // +/- 1.3 gauss, 1100 LSB/gauss +#define LSM303DLHC_MAG_CRB_GAIN_1_9GAUSS \ + 0x40 // +/- 1.9 gauss, 855 LSB/gauss +#define LSM303DLHC_MAG_CRB_GAIN_2_5GAUSS \ + 0x60 // +/- 2.5 gauss, 670 LSB/gauss +#define LSM303DLHC_MAG_CRB_GAIN_4_0GAUSS \ + 0x80 // +/- 4.0 gauss, 450 LSB/guass +#define LSM303DLHC_MAG_CRB_GAIN_4_7GAUSS \ + 0xA0 // +/- 4.7 gauss, 400 LSB/gauss +#define LSM303DLHC_MAG_CRB_GAIN_5_6GAUSS \ + 0xC0 // +/- 5.6 gauss, 330 LSB/gauss +#define LSM303DLHC_MAG_CRB_GAIN_8_1GAUSS \ + 0xE0 // +/- 8.1 gauss, 230 LSB/gauss +#define LSM303DLHC_MAG_CRB_GAIN_S \ + 5 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_MAG_MR +// register. +// +//***************************************************************************** +#define LSM303DLHC_MAG_MR_MODE_M \ + 0x3 // Mode select bits +#define LSM303DLHC_MAG_MR_MODE_CONTINUOUS \ + 0x00 // Continuous conversion mode +#define LSM303DLHC_MAG_MR_MODE_SINGLE \ + 0x01 // Single conversion mode +#define LSM303DLHC_MAG_MR_MODE_SLEEP \ + 0x02 // Sleep mode +#define LSM303DLHC_MAG_MR_MODE_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_MAG_SR +// register. +// +//***************************************************************************** +#define LSM303DLHC_MAG_SR_LOCK_M \ + 0x02 // Data output register lock; once + // a new set of measurements is + // available, this bit is set when + // the first magnetic field data + // register has been read +#define LSM303DLHC_MAG_SR_LOCK_LOCKED \ + 0x02 +#define LSM303DLHC_MAG_SR_DATA_M \ + 0x01 // Data ready bit; this bit is set + // when a new set of measures are + // available +#define LSM303DLHC_MAG_SR_DATA_READ \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_MAG_IRA +// register. +// +//***************************************************************************** +#define LSM303DLHC_MAG_IRA_CONSTANT_M \ + 0xFF +#define LSM303DLHC_MAG_IRA_CONSTANT_VAL \ + 0x48 +#define LSM303DLHC_MAG_IRA_CONSTANT_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_MAG_IRB +// register. +// +//***************************************************************************** +#define LSM303DLHC_MAG_IRB_CONSTANT_M \ + 0xFF +#define LSM303DLHC_MAG_IRB_CONSTANT_VAL \ + 0x34 +#define LSM303DLHC_MAG_IRB_CONSTANT_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_MAG_IRC +// register. +// +//***************************************************************************** +#define LSM303DLHC_MAG_IRC_CONSTANT_M \ + 0xFF +#define LSM303DLHC_MAG_IRC_CONSTANT_VAL \ + 0x33 +#define LSM303DLHC_MAG_IRC_CONSTANT_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_CTRL1 +// register. +// +//***************************************************************************** +#define LSM303DLHC_CTRL1_ODR_M 0xF0 // data rate selection +#define LSM303DLHC_CTRL1_ODR_PD 0x00 // Power-down mode +#define LSM303DLHC_CTRL1_ODR_1HZ \ + 0x10 +#define LSM303DLHC_CTRL1_ODR_10HZ \ + 0x20 +#define LSM303DLHC_CTRL1_ODR_25HZ \ + 0x30 +#define LSM303DLHC_CTRL1_ODR_50HZ \ + 0x40 +#define LSM303DLHC_CTRL1_ODR_100HZ \ + 0x50 +#define LSM303DLHC_CTRL1_ODR_200HZ \ + 0x60 +#define LSM303DLHC_CTRL1_ODR_400HZ \ + 0x70 +#define LSM303DLHC_CTRL1_ODR_1620HZ \ + 0x80 // Low power mode +#define LSM303DLHC_CTRL1_ODR_5376HZ \ + 0x90 // 1.344KHz normal, 5.376KHz low + // power +#define LSM303DLHC_CTRL1_POWER_M \ + 0x08 // Power control +#define LSM303DLHC_CTRL1_POWER_LOWPOW \ + 0x00 +#define LSM303DLHC_CTRL1_POWER_NORMAL \ + 0x08 +#define LSM303DLHC_CTRL1_AXIS_M 0x7 // Axis power control +#define LSM303DLHC_CTRL1_AXIS_Y_EN \ + 0x01 // Y-axis enable +#define LSM303DLHC_CTRL1_AXIS_X_EN \ + 0x02 // X-axis enable +#define LSM303DLHC_CTRL1_AXIS_Z_EN \ + 0x04 // Z-axis enable +#define LSM303DLHC_CTRL1_ODR_S 4 +#define LSM303DLHC_CTRL1_AXIS_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_CTRL2 +// register. +// +//***************************************************************************** +#define LSM303DLHC_CTRL2_HPMODE_M \ + 0xC0 // high pass filter mode selection +#define LSM303DLHC_CTRL2_HPMODE_NORMAL_RESET \ + 0x00 // normal mode (reset reading + // HP_RESET_FILTER) +#define LSM303DLHC_CTRL2_HPMODE_REFERENCE \ + 0x40 // reference signal for filtering +#define LSM303DLHC_CTRL2_HPMODE_NORMAL \ + 0x80 // Normal mode +#define LSM303DLHC_CTRL2_HPMODE_AUTORESET \ + 0xC0 // autoreset on interrupt event +#define LSM303DLHC_CTRL2_HPCUTOFF_M \ + 0x30 // high pass filter cut-off + // frequency selection +#define LSM303DLHC_CTRL2_FDS_M 0x08 // Filtered data selection +#define LSM303DLHC_CTRL2_FDS_BYPASSED \ + 0x00 +#define LSM303DLHC_CTRL2_FDS_FILTERED \ + 0x08 +#define LSM303DLHC_CTRL2_HPCLICK_M \ + 0x04 // High pass filter enabled for + // CLICK function +#define LSM303DLHC_CTRL2_HPCLICK_BYPASSED \ + 0x00 +#define LSM303DLHC_CTRL2_HPCLICK_FILTERED \ + 0x04 +#define LSM303DLHC_CTRL2_HPIS2_M \ + 0x02 // High pass filter enabled for AOI + // function on Interrupt 2 +#define LSM303DLHC_CTRL2_HPIS2_BYPASSED \ + 0x00 +#define LSM303DLHC_CTRL2_HPIS2_FILTERED \ + 0x02 +#define LSM303DLHC_CTRL2_HPIS1_M \ + 0x01 // High pass filter enabled for AOI + // function on Interrupt 1 +#define LSM303DLHC_CTRL2_HPIS1_BYPASSED \ + 0x00 +#define LSM303DLHC_CTRL2_HPIS1_FILTERED \ + 0x01 +#define LSM303DLHC_CTRL2_HPMODE_S \ + 6 +#define LSM303DLHC_CTRL2_HPCUTOFF_S \ + 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_CTRL3 +// register. +// +//***************************************************************************** +#define LSM303DLHC_CTRL3_I1CLICK_M \ + 0x80 // CLICK on INT1 +#define LSM303DLHC_CTRL3_I1CLICK_DIS \ + 0x00 +#define LSM303DLHC_CTRL3_I1CLICK_EN \ + 0x80 +#define LSM303DLHC_CTRL3_I1AOI1_M \ + 0x40 // AOI1 on INT1 +#define LSM303DLHC_CTRL3_I1AOI1_DIS \ + 0x00 +#define LSM303DLHC_CTRL3_I1AOI1_EN \ + 0x40 +#define LSM303DLHC_CTRL3_I1AOI2_M \ + 0x20 // AOI2 on INT1 +#define LSM303DLHC_CTRL3_I1AOI2_DIS \ + 0x00 +#define LSM303DLHC_CTRL3_I1AOI2_EN \ + 0x20 +#define LSM303DLHC_CTRL3_I1DRDY1_M \ + 0x10 // DRDY1 on INT1 +#define LSM303DLHC_CTRL3_I1DRDY1_DIS \ + 0x00 +#define LSM303DLHC_CTRL3_I1DRDY1_EN \ + 0x10 +#define LSM303DLHC_CTRL3_I1DRDY2_M \ + 0x08 // DRDY2 on INT1 +#define LSM303DLHC_CTRL3_I1DRDY2_DIS \ + 0x00 +#define LSM303DLHC_CTRL3_I1DRDY2_EN \ + 0x08 +#define LSM303DLHC_CTRL3_I1WTM_M \ + 0x04 // FIFO watermark interrupt on INT1 +#define LSM303DLHC_CTRL3_I1WTM_DIS \ + 0x00 +#define LSM303DLHC_CTRL3_I1WTM_EN \ + 0x04 +#define LSM303DLHC_CTRL3_I1OVERRUN_M \ + 0x02 // FIFO overrun interrupt on INT1 +#define LSM303DLHC_CTRL3_I1OVERRUN_DIS \ + 0x00 +#define LSM303DLHC_CTRL3_I1OVERRUN_EN \ + 0x02 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_CTRL4 +// register. +// +//***************************************************************************** +#define LSM303DLHC_CTRL4_BDU_M 0x80 // Block data update; default + // value: 0 (0: continuous update; + // 1: output registers not updated + // until MSB and LSB reading) +#define LSM303DLHC_CTRL4_BDU_CONTINUOUS \ + 0x00 +#define LSM303DLHC_CTRL4_BDU_MSBLSB \ + 0x80 +#define LSM303DLHC_CTRL4_ENDIAN_M \ + 0x40 // Endian selection +#define LSM303DLHC_CTRL4_ENDIAN_LITTLE \ + 0x00 +#define LSM303DLHC_CTRL4_ENDIAN_BIG \ + 0x40 +#define LSM303DLHC_CTRL4_FS_M 0x30 // full scale selection; default + // value: 0 +#define LSM303DLHC_CTRL4_FS_2G 0x00 +#define LSM303DLHC_CTRL4_FS_4G 0x10 +#define LSM303DLHC_CTRL4_FS_8G 0x20 +#define LSM303DLHC_CTRL4_FS_16G 0x30 +#define LSM303DLHC_CTRL4_RESOLUTION_M \ + 0x08 // resolution output mode +#define LSM303DLHC_CTRL4_RESOLUTION_LOW \ + 0x00 +#define LSM303DLHC_CTRL4_RESOLUTION_HIGH \ + 0x08 +#define LSM303DLHC_CTRL4_SIM_M 0x01 // SPI Serial Interface Mode + // selection (default = 0, 4-wire) +#define LSM303DLHC_CTRL4_SIM_4WIRE \ + 0x00 +#define LSM303DLHC_CTRL4_SIM_3WIRE \ + 0x01 +#define LSM303DLHC_CTRL4_FS_S 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_CTRL5 +// register. +// +//***************************************************************************** +#define LSM303DLHC_CTRL5_REBOOTCTL_M \ + 0x80 // Reboot memory conent +#define LSM303DLHC_CTRL5_REBOOTCTL_NORMAL \ + 0x00 +#define LSM303DLHC_CTRL5_REBOOTCTL_REBOOT \ + 0x80 +#define LSM303DLHC_CTRL5_FIFO_M 0x40 // FIFO enable +#define LSM303DLHC_CTRL5_FIFO_DIS \ + 0x00 +#define LSM303DLHC_CTRL5_FIFO_EN \ + 0x40 +#define LSM303DLHC_CTRL5_LIR_INT1_M \ + 0x08 // Latch interrupt on INT1 +#define LSM303DLHC_CTRL5_LIR_INT1_DIS \ + 0x00 +#define LSM303DLHC_CTRL5_LIR_INT1_EN \ + 0x08 +#define LSM303DLHC_CTRL5_D4D_INT1_M \ + 0x04 // 4D Int enable on INT1 +#define LSM303DLHC_CTRL5_D4D_INT1_DIS \ + 0x00 +#define LSM303DLHC_CTRL5_D4D_INT1_EN \ + 0x04 +#define LSM303DLHC_CTRL5_LIR_INT2_M \ + 0x02 // Latch interrupt request on + // INT2_SRC register, with INT2_SRC + // register cleared by reading + // INT2_SRC itself; default value: + // 0 +#define LSM303DLHC_CTRL5_LIR_INT2_DIS \ + 0x00 +#define LSM303DLHC_CTRL5_LIR_INT2_EN \ + 0x02 +#define LSM303DLHC_CTRL5_D4D_INT2_M \ + 0x01 // 4D enable: 4D detection is + // enabled on INT2 when 6D bit on + // INT2_CFG is set to 1 +#define LSM303DLHC_CTRL5_D4D_INT2_DIS \ + 0x00 +#define LSM303DLHC_CTRL5_D4D_INT2_EN \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_CTRL6 +// register. +// +//***************************************************************************** +#define LSM303DLHC_CTRL6_I2_CLICK_M \ + 0x80 // CLICK interrupt on PAD2 +#define LSM303DLHC_CTRL6_I2_CLICK_DIS \ + 0x00 +#define LSM303DLHC_CTRL6_I2_CLICK_EN \ + 0x80 +#define LSM303DLHC_CTRL6_I2_INT1_M \ + 0x40 // Interrupt 1 on PAD2; default + // value 0 +#define LSM303DLHC_CTRL6_I2_INT1_DIS \ + 0x00 +#define LSM303DLHC_CTRL6_I2_INT1_EN \ + 0x40 +#define LSM303DLHC_CTRL6_I2_INT2_M \ + 0x20 // Interrupt 2 on PAD2; default + // value 0 +#define LSM303DLHC_CTRL6_I2_INT2_DIS \ + 0x00 +#define LSM303DLHC_CTRL6_I2_INT2_EN \ + 0x20 +#define LSM303DLHC_CTRL6_BOOT_I2_M \ + 0x10 // Reboot memory content on PAD2; + // default value: 0 +#define LSM303DLHC_CTRL6_BOOT_I2_DIS \ + 0x00 +#define LSM303DLHC_CTRL6_BOOT_I2_EN \ + 0x10 +#define LSM303DLHC_CTRL6_P2_ACT_M \ + 0x08 // active function status on PAD2 +#define LSM303DLHC_CTRL6_P2_ACT_DIS \ + 0x00 +#define LSM303DLHC_CTRL6_P2_ACT_EN \ + 0x08 +#define LSM303DLHC_CTRL6_H_LACTIVE_M \ + 0x02 // interrupt active configuration + // on INT; default value 0 (0: + // high; 1:low) +#define LSM303DLHC_CTRL6_H_LACTIVE_HI \ + 0x00 +#define LSM303DLHC_CTRL6_H_LACTIVE_LOW \ + 0x02 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_STATUS +// register. +// +//***************************************************************************** +#define LSM303DLHC_STATUS_OR_M 0xF0 // Axis data overrun +#define LSM303DLHC_STATUS_OR_X 0x10 // X-axis data overrun +#define LSM303DLHC_STATUS_OR_Y 0x20 // Y-axis data overrun +#define LSM303DLHC_STATUS_OR_Z 0x40 // Z-axis data overrun +#define LSM303DLHC_STATUS_OR_ZYX \ + 0x80 // X, Y, and X data overrun +#define LSM303DLHC_STATUS_DA_M 0xF // Axis data available +#define LSM303DLHC_STATUS_DA_X 0x01 // X-axis data available +#define LSM303DLHC_STATUS_DA_Y 0x02 // Y-axis data available +#define LSM303DLHC_STATUS_DA_Z 0x04 // Z-axis data available +#define LSM303DLHC_STATUS_DA_ZYX \ + 0x08 // X, Y, and X data available +#define LSM303DLHC_STATUS_OR_S 4 +#define LSM303DLHC_STATUS_DA_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_FIFO_CTRL +// register. +// +//***************************************************************************** +#define LSM303DLHC_FIFO_CTRL_TR_M \ + 0x20 +#define LSM303DLHC_FIFO_CTRL_TR_INT1 \ + 0x00 +#define LSM303DLHC_FIFO_CTRL_TR_INT2 \ + 0x20 +#define LSM303DLHC_FIFO_CTRL_THRESH_M \ + 0x1F // FIFO Threshold setting +#define LSM303DLHC_FIFO_CTRL_MODE_M \ + 0xC // FIFO mode setting +#define LSM303DLHC_FIFO_CTRL_MODE_BYPASS \ + 0x00 // Bypass mode +#define LSM303DLHC_FIFO_CTRL_MODE_FIFO \ + 0x40 // FIFO mode +#define LSM303DLHC_FIFO_CTRL_MODE_STREAM \ + 0x80 // Stream mode +#define LSM303DLHC_FIFO_CTRL_MODE_TRIGGER \ + 0xC0 // Trigger mode +#define LSM303DLHC_FIFO_CTRL_MODE_S \ + 6 +#define LSM303DLHC_FIFO_CTRL_THRESH_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_FIFO_SRC +// register. +// +//***************************************************************************** +#define LSM303DLHC_FIFO_SRC_FTH_M \ + 0x80 // FIFO threshold is greater than + // or equal to level or less than + // level +#define LSM303DLHC_FIFO_SRC_FTH_LT \ + 0x00 +#define LSM303DLHC_FIFO_SRC_FTH_GEQ \ + 0x80 +#define LSM303DLHC_FIFO_SRC_OVRN_M \ + 0x40 // overrun status bit +#define LSM303DLHC_FIFO_SRC_OVRN_FILLED \ + 0x40 +#define LSM303DLHC_FIFO_SRC_EMPTY_M \ + 0x20 // FIFO empty +#define LSM303DLHC_FIFO_SRC_EMPTY_EMPTY \ + 0x20 +#define LSM303DLHC_FIFO_SRC_STORE_SAMPLES_M \ + 0x1F // FIFO stored data level of the + // unread samples +#define LSM303DLHC_FIFO_SRC_STORE_SAMPLES_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_INT1_CFG_A +// register. +// +//***************************************************************************** +#define LSM303DLHC_INT1_CFG_A_ANDOR_M \ + 0x80 // AND/OR combination of Interrupt + // events; default value: 0 +#define LSM303DLHC_INT1_CFG_A_ANDOR_OR \ + 0x00 +#define LSM303DLHC_INT1_CFG_A_ANDOR_AND \ + 0x80 +#define LSM303DLHC_INT1_CFG_A_6D_M \ + 0x40 // 6-direction function enabled +#define LSM303DLHC_INT1_CFG_A_6D_EN \ + 0x40 +#define LSM303DLHC_INT1_CFG_A_ZHI_M \ + 0x20 // enable interrupt generation on + // Z-hi event +#define LSM303DLHC_INT1_CFG_A_ZHI_EN \ + 0x20 +#define LSM303DLHC_INT1_CFG_A_ZUPE_M \ + 0x20 // enable interrupt generation on + // Z-hi event +#define LSM303DLHC_INT1_CFG_A_ZUPE_EN \ + 0x20 +#define LSM303DLHC_INT1_CFG_A_ZDOWNE_M \ + 0x10 // enable interrupt generation on + // Z-low event +#define LSM303DLHC_INT1_CFG_A_ZDOWNE_EN \ + 0x10 +#define LSM303DLHC_INT1_CFG_A_ZLI_M \ + 0x10 // enable interrupt generation on + // Z-low event +#define LSM303DLHC_INT1_CFG_A_ZLI_EN \ + 0x10 +#define LSM303DLHC_INT1_CFG_A_YUPE_M \ + 0x08 // enable interrupt generation on + // Y-hi event +#define LSM303DLHC_INT1_CFG_A_YUPE_EN \ + 0x08 +#define LSM303DLHC_INT1_CFG_A_YHI_M \ + 0x08 // enable interrupt generation on + // Y-hi event +#define LSM303DLHC_INT1_CFG_A_YHI_EN \ + 0x08 +#define LSM303DLHC_INT1_CFG_A_YDOWNE_M \ + 0x04 // enable interrupt generation on + // Y-low event +#define LSM303DLHC_INT1_CFG_A_YDOWNE_EN \ + 0x04 +#define LSM303DLHC_INT1_CFG_A_YLI_M \ + 0x04 // enable interrupt generation on + // Y-low event +#define LSM303DLHC_INT1_CFG_A_YLI_EN \ + 0x04 +#define LSM303DLHC_INT1_CFG_A_XHI_M \ + 0x02 // enable interrupt generation on + // X-hi event +#define LSM303DLHC_INT1_CFG_A_XHI_EN \ + 0x02 +#define LSM303DLHC_INT1_CFG_A_XUPE_M \ + 0x02 // enable interrupt generation on + // X-hi event +#define LSM303DLHC_INT1_CFG_A_XUPE_EN \ + 0x02 +#define LSM303DLHC_INT1_CFG_A_XLI_M \ + 0x01 // enable interrupt generation on + // X-low event +#define LSM303DLHC_INT1_CFG_A_XLI_EN \ + 0x01 +#define LSM303DLHC_INT1_CFG_A_XDOWNE_M \ + 0x01 // enable interrupt generation on + // X-low event +#define LSM303DLHC_INT1_CFG_A_XDOWNE_EN \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_INT1_SRC_A +// register. +// +//***************************************************************************** +#define LSM303DLHC_INT1_SRC_A_ZERO_M \ + 0x80 // This bit must be zero +#define LSM303DLHC_INT1_SRC_A_ZERO_ZERO \ + 0x00 +#define LSM303DLHC_INT1_SRC_A_ZERO_INVALID \ + 0x80 +#define LSM303DLHC_INT1_SRC_A_IA_M \ + 0x40 // interrupt active +#define LSM303DLHC_INT1_SRC_A_IA_ACTIVE \ + 0x40 +#define LSM303DLHC_INT1_SRC_A_ZH_M \ + 0x20 // Z-Hi event occurred +#define LSM303DLHC_INT1_SRC_A_ZH_OCCURRED \ + 0x20 +#define LSM303DLHC_INT1_SRC_A_ZL_M \ + 0x10 // Z-Low event occurred +#define LSM303DLHC_INT1_SRC_A_ZL_OCCURRED \ + 0x10 +#define LSM303DLHC_INT1_SRC_A_YH_M \ + 0x08 // Y-Hi event occurred +#define LSM303DLHC_INT1_SRC_A_YH_OCCURRED \ + 0x08 +#define LSM303DLHC_INT1_SRC_A_YL_M \ + 0x04 // Y-Low event occurred +#define LSM303DLHC_INT1_SRC_A_YL_OCCURRED \ + 0x04 +#define LSM303DLHC_INT1_SRC_A_XH_M \ + 0x02 // X-Hi event occurred +#define LSM303DLHC_INT1_SRC_A_XH_OCCURRED \ + 0x02 +#define LSM303DLHC_INT1_SRC_A_XL_M \ + 0x01 // X-Low event occurred +#define LSM303DLHC_INT1_SRC_A_XL_OCCURRED \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// LSM303DLHC_O_MAG_TEMP_OUT_MSB register. +// +//***************************************************************************** +#define LSM303DLHC_MAG_TEMP_OUT_MSB_MSB_M \ + 0xFF +#define LSM303DLHC_MAG_TEMP_OUT_MSB_MSB_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// LSM303DLHC_O_MAG_TEMP_OUT_LSB register. +// +//***************************************************************************** +#define LSM303DLHC_MAG_TEMP_OUT_LSB_LSB_M \ + 0xF0 +#define LSM303DLHC_MAG_TEMP_OUT_LSB_LSB_S \ + 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_INT1_THS_A +// register. +// +//***************************************************************************** +#define LSM303DLHC_INT1_THS_A_ZERO_M \ + 0x80 // This bit must be zero +#define LSM303DLHC_INT1_THS_A_ZERO_ZERO \ + 0x00 +#define LSM303DLHC_INT1_THS_A_ZERO_INVALID \ + 0x80 +#define LSM303DLHC_INT1_THS_A_THS_M \ + 0x7F // THS[6-0] Interrupt threshold, + // default 0x0 +#define LSM303DLHC_INT1_THS_A_THS_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// LSM303DLHC_O_INT1_DURATION_A register. +// +//***************************************************************************** +#define LSM303DLHC_INT1_DURATION_A_ZERO_M \ + 0x80 // This bit must be zero +#define LSM303DLHC_INT1_DURATION_A_ZERO_ZERO \ + 0x00 +#define LSM303DLHC_INT1_DURATION_A_ZERO_INVALID \ + 0x80 +#define LSM303DLHC_INT1_DURATION_A_DURATION_M \ + 0x7F // Duration D[6-0] +#define LSM303DLHC_INT1_DURATION_A_DURATION_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_INT2_CFG_A +// register. +// +//***************************************************************************** +#define LSM303DLHC_INT2_CFG_A_ANDOR_M \ + 0x80 // AND/OR combination of Interrupt + // events; default value: 0 +#define LSM303DLHC_INT2_CFG_A_ANDOR_OR \ + 0x00 +#define LSM303DLHC_INT2_CFG_A_ANDOR_AND \ + 0x80 +#define LSM303DLHC_INT2_CFG_A_6D_M \ + 0x40 // 6-direction function enabled +#define LSM303DLHC_INT2_CFG_A_6D_EN \ + 0x40 +#define LSM303DLHC_INT2_CFG_A_ZHI_M \ + 0x20 // enable interrupt generation on + // Z-hi event +#define LSM303DLHC_INT2_CFG_A_ZHI_EN \ + 0x20 +#define LSM303DLHC_INT2_CFG_A_ZLI_M \ + 0x10 // enable interrupt generation on + // Z-low event +#define LSM303DLHC_INT2_CFG_A_ZLI_EN \ + 0x10 +#define LSM303DLHC_INT2_CFG_A_YHI_M \ + 0x08 // enable interrupt generation on + // Y-hi event +#define LSM303DLHC_INT2_CFG_A_YHI_EN \ + 0x08 +#define LSM303DLHC_INT2_CFG_A_YLI_M \ + 0x04 // enable interrupt generation on + // Y-low event +#define LSM303DLHC_INT2_CFG_A_YLI_EN \ + 0x04 +#define LSM303DLHC_INT2_CFG_A_XHI_M \ + 0x02 // enable interrupt generation on + // X-hi event +#define LSM303DLHC_INT2_CFG_A_XHI_EN \ + 0x02 +#define LSM303DLHC_INT2_CFG_A_XLI_M \ + 0x01 // enable interrupt generation on + // X-low event +#define LSM303DLHC_INT2_CFG_A_XLI_EN \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_INT2_SRC_A +// register. +// +//***************************************************************************** +#define LSM303DLHC_INT2_SRC_A_ZERO_M \ + 0x80 // This bit must be zero +#define LSM303DLHC_INT2_SRC_A_ZERO_ZERO \ + 0x00 +#define LSM303DLHC_INT2_SRC_A_ZERO_INVALID \ + 0x80 +#define LSM303DLHC_INT2_SRC_A_IA_M \ + 0x40 // interrupt active +#define LSM303DLHC_INT2_SRC_A_IA_ACTIVE \ + 0x40 +#define LSM303DLHC_INT2_SRC_A_ZH_M \ + 0x20 // Z-Hi event occurred +#define LSM303DLHC_INT2_SRC_A_ZH_OCCURRED \ + 0x20 +#define LSM303DLHC_INT2_SRC_A_ZL_M \ + 0x10 // Z-Low event occurred +#define LSM303DLHC_INT2_SRC_A_ZL_OCCURRED \ + 0x10 +#define LSM303DLHC_INT2_SRC_A_YH_M \ + 0x08 // Y-Hi event occurred +#define LSM303DLHC_INT2_SRC_A_YH_OCCURRED \ + 0x08 +#define LSM303DLHC_INT2_SRC_A_YL_M \ + 0x04 // Y-Low event occurred +#define LSM303DLHC_INT2_SRC_A_YL_OCCURRED \ + 0x04 +#define LSM303DLHC_INT2_SRC_A_XH_M \ + 0x02 // X-Hi event occurred +#define LSM303DLHC_INT2_SRC_A_XH_OCCURRED \ + 0x02 +#define LSM303DLHC_INT2_SRC_A_XL_M \ + 0x01 // X-Low event occurred +#define LSM303DLHC_INT2_SRC_A_XL_OCCURRED \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_INT2_THS_A +// register. +// +//***************************************************************************** +#define LSM303DLHC_INT2_THS_A_ZERO_M \ + 0x80 // This bit must be zero +#define LSM303DLHC_INT2_THS_A_ZERO_ZERO \ + 0x00 +#define LSM303DLHC_INT2_THS_A_ZERO_INVALID \ + 0x80 +#define LSM303DLHC_INT2_THS_A_THS_M \ + 0x7F // THS[6-0] Interrupt threshold, + // default 0x0 +#define LSM303DLHC_INT2_THS_A_THS_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// LSM303DLHC_O_INT2_DURATION_A register. +// +//***************************************************************************** +#define LSM303DLHC_INT2_DURATION_A_ZERO_M \ + 0x80 // This bit must be zero +#define LSM303DLHC_INT2_DURATION_A_ZERO_ZERO \ + 0x00 +#define LSM303DLHC_INT2_DURATION_A_ZERO_INVALID \ + 0x80 +#define LSM303DLHC_INT2_DURATION_A_DURATION_M \ + 0x7F // Duration D[6-0] +#define LSM303DLHC_INT2_DURATION_A_DURATION_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_CLICK_CFG_A +// register. +// +//***************************************************************************** +#define LSM303DLHC_CLICK_CFG_A_ZD_M \ + 0x20 // Enable interrupt double click on + // Z axis +#define LSM303DLHC_CLICK_CFG_A_ZD_DIS \ + 0x00 +#define LSM303DLHC_CLICK_CFG_A_ZD_EN \ + 0x20 +#define LSM303DLHC_CLICK_CFG_A_ZS_M \ + 0x10 // Enable interrupt single click on + // Z axis +#define LSM303DLHC_CLICK_CFG_A_ZS_DIS \ + 0x00 +#define LSM303DLHC_CLICK_CFG_A_ZS_EN \ + 0x10 +#define LSM303DLHC_CLICK_CFG_A_YD_M \ + 0x08 // Enable interrupt double click on + // Y axis +#define LSM303DLHC_CLICK_CFG_A_YD_DIS \ + 0x00 +#define LSM303DLHC_CLICK_CFG_A_YD_EN \ + 0x08 +#define LSM303DLHC_CLICK_CFG_A_YS_M \ + 0x04 // Enable interrupt single click on + // Y axis +#define LSM303DLHC_CLICK_CFG_A_YS_DIS \ + 0x00 +#define LSM303DLHC_CLICK_CFG_A_YS_EN \ + 0x04 +#define LSM303DLHC_CLICK_CFG_A_XD_M \ + 0x02 // Enable interrupt double click on + // X axis +#define LSM303DLHC_CLICK_CFG_A_XD_DIS \ + 0x00 +#define LSM303DLHC_CLICK_CFG_A_XD_EN \ + 0x02 +#define LSM303DLHC_CLICK_CFG_A_XS_M \ + 0x01 // Enable interrupt single click on + // X axis +#define LSM303DLHC_CLICK_CFG_A_XS_DIS \ + 0x00 +#define LSM303DLHC_CLICK_CFG_A_XS_EN \ + 0x01 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_CLICK_SRC_A +// register. +// +//***************************************************************************** +#define LSM303DLHC_CLICK_SRC_A_IA_M \ + 0x40 // Interrupt pending +#define LSM303DLHC_CLICK_SRC_A_IA_NONE \ + 0x00 +#define LSM303DLHC_CLICK_SRC_A_IA_PENDING \ + 0x40 +#define LSM303DLHC_CLICK_SRC_A_DCLICK_M \ + 0x20 // double click-click enable +#define LSM303DLHC_CLICK_SRC_A_DCLICK_DIS \ + 0x00 +#define LSM303DLHC_CLICK_SRC_A_DCLICK_EN \ + 0x20 +#define LSM303DLHC_CLICK_SRC_A_SCLICK_M \ + 0x10 // single click-click enable +#define LSM303DLHC_CLICK_SRC_A_SCLICK_DIS \ + 0x00 +#define LSM303DLHC_CLICK_SRC_A_SCLICK_EN \ + 0x10 +#define LSM303DLHC_CLICK_SRC_A_SIGN_M \ + 0x08 // click-click sign +#define LSM303DLHC_CLICK_SRC_A_SIGN_POSITIVE \ + 0x00 +#define LSM303DLHC_CLICK_SRC_A_SIGN_NEGATIVE \ + 0x08 +#define LSM303DLHC_CLICK_SRC_A_AXIS_M \ + 0x7 // Axis click detection +#define LSM303DLHC_CLICK_SRC_A_AXIS_X \ + 0x01 // X click-click detection +#define LSM303DLHC_CLICK_SRC_A_AXIS_Y \ + 0x02 // Y click-click detection +#define LSM303DLHC_CLICK_SRC_A_AXIS_Z \ + 0x04 // Z click-click detection +#define LSM303DLHC_CLICK_SRC_A_AXIS_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the LSM303DLHC_O_CLICK_THS_A +// register. +// +//***************************************************************************** +#define LSM303DLHC_CLICK_THS_A_THS_M \ + 0x7F // Threshold; 1 LSB = full-scale / + // 128; THS6 through THS0 define + // the threshold which is used by + // the system to start the click + // detection procedure; the + // threshold value is expressed + // over 7 bits as an unsigned + // number +#define LSM303DLHC_CLICK_THS_A_THS_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// LSM303DLHC_O_TIME_LIMIT_A register. +// +//***************************************************************************** +#define LSM303DLHC_TIME_LIMIT_A_TLI_M \ + 0x7F // Time Limit; 1 LSB = 1/ODR; TLI7 + // through TLI0 define the maximum + // time interval that can elapse + // between the start of the click + // detection procedure (the + // acceleration on the selected + // channel exceeds the programmed + // threshold) and when the + // acceleration goes back below the + // threshold +#define LSM303DLHC_TIME_LIMIT_A_TLI_S \ + 0 + +#endif // __SENSORLIB_HW_LSM303DLHC_H__ diff --git a/sensorlib/hw_mpu6050.h b/sensorlib/hw_mpu6050.h new file mode 100644 index 0000000..32706e9 --- /dev/null +++ b/sensorlib/hw_mpu6050.h @@ -0,0 +1,1313 @@ +//***************************************************************************** +// +// hw_mpu6050.h - Macros used when accessing the Invensense MPU6050 +// accelerometer/gyroscope/magnetometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_MPU6050_H__ +#define __SENSORLIB_HW_MPU6050_H__ + +//***************************************************************************** +// +// The following are defines for the MPU6050 register addresses. +// +//***************************************************************************** +#define MPU6050_O_SELF_TEST_X 0x0D // Self test X register +#define MPU6050_O_SELF_TEST_Y 0x0E // Self test Y register +#define MPU6050_O_SELF_TEST_Z 0x0F // Self test Z register +#define MPU6050_O_SELF_TEST_A 0x10 // Self test A register +#define MPU6050_O_SMPLRT_DIV 0x19 // Sample rate divider register +#define MPU6050_O_CONFIG 0x1A // Configuration register +#define MPU6050_O_GYRO_CONFIG 0x1B // Gyro configuration register +#define MPU6050_O_ACCEL_CONFIG 0x1C // Accelerometer configuration + // register +#define MPU6050_O_MOT_THR 0x1F // Motion detection threshold + // register +#define MPU6050_O_FIFO_EN 0x23 // FIFO enable register +#define MPU6050_O_I2C_MST_CTRL 0x24 // I2C master control register +#define MPU6050_O_I2C_SLV0_ADDR 0x25 // I2C slave 0 address register +#define MPU6050_O_I2C_SLV0_REG 0x26 // I2C slave 0 register number + // register +#define MPU6050_O_I2C_SLV0_CTRL 0x27 // I2C slave 0 control register +#define MPU6050_O_I2C_SLV1_ADDR 0x28 // I2C slave 1 address register +#define MPU6050_O_I2C_SLV1_REG 0x29 // I2C slave 1 register number + // register +#define MPU6050_O_I2C_SLV1_CTRL 0x2A // I2C slave 1 control register +#define MPU6050_O_I2C_SLV2_ADDR 0x2B // I2C slave 2 address register +#define MPU6050_O_I2C_SLV2_REG 0x2C // I2C slave 2 register number + // register +#define MPU6050_O_I2C_SLV2_CTRL 0x2D // I2C slave 2 control register +#define MPU6050_O_I2C_SLV3_ADDR 0x2E // I2C slave 3 address register +#define MPU6050_O_I2C_SLV3_REG 0x2F // I2C slave 3 register number + // register +#define MPU6050_O_I2C_SLV3_CTRL 0x30 // I2C slave 3 control register +#define MPU6050_O_I2C_SLV4_ADDR 0x31 // I2C slave 4 address register +#define MPU6050_O_I2C_SLV4_REG 0x32 // I2C slave 4 register number + // register +#define MPU6050_O_I2C_SLV4_DO 0x33 // I2C slave 4 output data register +#define MPU6050_O_I2C_SLV4_CTRL 0x34 // I2C slave 4 control register +#define MPU6050_O_I2C_SLV4_DI 0x35 // I2C slave 4 input data register +#define MPU6050_O_I2C_MST_STATUS \ + 0x36 // I2C master status register +#define MPU6050_O_INT_PIN_CFG 0x37 // INT pin configuration register +#define MPU6050_O_INT_ENABLE 0x38 // Interrupt enable register +#define MPU6050_O_INT_STATUS 0x3A // Interrupt status register +#define MPU6050_O_ACCEL_XOUT_H 0x3B // X-axis acceleration data MSB + // register +#define MPU6050_O_ACCEL_XOUT_L 0x3C // X-axis acceleration data LSB + // register +#define MPU6050_O_ACCEL_YOUT_H 0x3D // Y-axis acceleration data MSB + // register +#define MPU6050_O_ACCEL_YOUT_L 0x3E // Y-axis accelearation data LSB + // register +#define MPU6050_O_ACCEL_ZOUT_H 0x3F // Z-axis acceleration data MSB + // register +#define MPU6050_O_ACCEL_ZOUT_L 0x40 // Z-axis acceleration data LSB + // register +#define MPU6050_O_TEMP_OUT_H 0x41 // Temperature data MSB register +#define MPU6050_O_TEMP_OUT_L 0x42 // Temperature data LSB register +#define MPU6050_O_GYRO_XOUT_H 0x43 // X-axis gyro data MSB register +#define MPU6050_O_GYRO_XOUT_L 0x44 // X-axis gyro data LSB register +#define MPU6050_O_GYRO_YOUT_H 0x45 // Y-axis gyro data MSB register +#define MPU6050_O_GYRO_YOUT_L 0x46 // Y-axis gyro data LSB register +#define MPU6050_O_GYRO_ZOUT_H 0x47 // Z-axis gyro data MSB register +#define MPU6050_O_GYRO_ZOUT_L 0x48 // Z-axis gyro data LSB register +#define MPU6050_O_EXT_SENS_DATA_00 \ + 0x49 // External sensor data 0 register +#define MPU6050_O_EXT_SENS_DATA_01 \ + 0x4A // External sensor data 1 register +#define MPU6050_O_EXT_SENS_DATA_02 \ + 0x4B // External sensor data 2 register +#define MPU6050_O_EXT_SENS_DATA_03 \ + 0x4C // External sensor data 3 register +#define MPU6050_O_EXT_SENS_DATA_04 \ + 0x4D // External sensor data 4 register +#define MPU6050_O_EXT_SENS_DATA_05 \ + 0x4E // External sensor data 5 register +#define MPU6050_O_EXT_SENS_DATA_06 \ + 0x4F // External sensor data 6 register +#define MPU6050_O_EXT_SENS_DATA_07 \ + 0x50 // External sensor data 7 register +#define MPU6050_O_EXT_SENS_DATA_08 \ + 0x51 // External sensor data 8 register +#define MPU6050_O_EXT_SENS_DATA_09 \ + 0x52 // External sensor data 9 register +#define MPU6050_O_EXT_SENS_DATA_10 \ + 0x53 // External sensor data 10 register +#define MPU6050_O_EXT_SENS_DATA_11 \ + 0x54 // External sensor data 11 register +#define MPU6050_O_EXT_SENS_DATA_12 \ + 0x55 // External sensor data 12 register +#define MPU6050_O_EXT_SENS_DATA_13 \ + 0x56 // External sensor data 13 register +#define MPU6050_O_EXT_SENS_DATA_14 \ + 0x57 // External sensor data 14 register +#define MPU6050_O_EXT_SENS_DATA_15 \ + 0x58 // External sensor data 15 register +#define MPU6050_O_EXT_SENS_DATA_16 \ + 0x59 // External sensor data 16 register +#define MPU6050_O_EXT_SENS_DATA_17 \ + 0x5A // External sensor data 17 register +#define MPU6050_O_EXT_SENS_DATA_18 \ + 0x5B // External sensor data 18 register +#define MPU6050_O_EXT_SENS_DATA_19 \ + 0x5C // External sensor data 19 register +#define MPU6050_O_EXT_SENS_DATA_20 \ + 0x5D // External sensor data 20 register +#define MPU6050_O_EXT_SENS_DATA_21 \ + 0x5E // External sensor data 21 register +#define MPU6050_O_EXT_SENS_DATA_22 \ + 0x5F // External sensor data 22 register +#define MPU6050_O_EXT_SENS_DATA_23 \ + 0x60 // External sensor data 23 register +#define MPU6050_O_I2C_SLV0_DO 0x63 // I2C slave 0 output data register +#define MPU6050_O_I2C_SLV1_DO 0x64 // I2C slave 1 output data register +#define MPU6050_O_I2C_SLV2_DO 0x65 // I2C slave 2 output data register +#define MPU6050_O_I2C_SLV3_DO 0x66 // I2C slave 3 output data register +#define MPU6050_O_I2C_MST_DELAY_CTRL \ + 0x67 // I2C master delay control + // register +#define MPU6050_O_SIGNAL_PATH_RESET \ + 0x68 // Signal path reset register +#define MPU6050_O_MOT_DETECT_CTRL \ + 0x69 // Motion detection control + // register +#define MPU6050_O_USER_CTRL 0x6A // User control register +#define MPU6050_O_PWR_MGMT_1 0x6B // Power management 1 register +#define MPU6050_O_PWR_MGMT_2 0x6C // Power management 2 register +#define MPU6050_O_FIFO_COUNTH 0x72 // FIFO count MSB register +#define MPU6050_O_FIFO_COUNTL 0x73 // FIFO count LSB register +#define MPU6050_O_FIFO_R_W 0x74 // FIFO read write register +#define MPU6050_O_WHO_AM_I 0x75 // Who am I register + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_SELF_TEST_X +// register. +// +//***************************************************************************** +#define MPU6050_SELF_TEST_X_XA_TEST_M \ + 0xE0 // Accelerometer XA_TEST[4:2] +#define MPU6050_SELF_TEST_X_XG_TEST_M \ + 0x1F // Gyro XG_TEST[4:0] +#define MPU6050_SELF_TEST_X_XA_TEST_S \ + 5 +#define MPU6050_SELF_TEST_X_XG_TEST_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_SELF_TEST_Y +// register. +// +//***************************************************************************** +#define MPU6050_SELF_TEST_Y_YA_TEST_M \ + 0xE0 // Accelerometer YA_TEST[4:2] +#define MPU6050_SELF_TEST_Y_YG_TEST_M \ + 0x1F // Gyro YG_TEST[4:0] +#define MPU6050_SELF_TEST_Y_YA_TEST_S \ + 5 +#define MPU6050_SELF_TEST_Y_YG_TEST_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_SELF_TEST_Z +// register. +// +//***************************************************************************** +#define MPU6050_SELF_TEST_Z_ZA_TEST_M \ + 0xE0 // Accelerometer ZA_TEST[4:2] +#define MPU6050_SELF_TEST_Z_ZG_TEST_M \ + 0x1F // Gyro ZG_TEST[4:0] +#define MPU6050_SELF_TEST_Z_ZA_TEST_S \ + 5 +#define MPU6050_SELF_TEST_Z_ZG_TEST_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_SELF_TEST_A +// register. +// +//***************************************************************************** +#define MPU6050_SELF_TEST_A_XA_TEST_M \ + 0x30 // Accelerometer XA_TEST[1:0] +#define MPU6050_SELF_TEST_A_YA_TEST_M \ + 0x0C // Accelerometer YA_TEST[1:0] +#define MPU6050_SELF_TEST_A_ZA_TEST_M \ + 0x03 // Accelerometer ZA_TEST[1:0] +#define MPU6050_SELF_TEST_A_XA_TEST_S \ + 4 +#define MPU6050_SELF_TEST_A_YA_TEST_S \ + 2 +#define MPU6050_SELF_TEST_A_ZA_TEST_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_SMPLRT_DIV +// register. +// +//***************************************************************************** +#define MPU6050_SMPLRT_DIV_M 0xFF // Gyro output rate divider +#define MPU6050_SMPLRT_DIV_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_CONFIG +// register. +// +//***************************************************************************** +#define MPU6050_CONFIG_EXT_SYNC_SET_M \ + 0x38 // FSYNC pin sample location +#define MPU6050_CONFIG_EXT_SYNC_SET_DIS \ + 0x00 // FSYNC input disabled +#define MPU6050_CONFIG_EXT_SYNC_SET_TEMP_OUT_L \ + 0x08 // FSYNC on TEMP_OUT_L[0] +#define MPU6050_CONFIG_EXT_SYNC_SET_GYRO_XOUT_L \ + 0x10 // FSYNC on GYRO_XOUT_L[0] +#define MPU6050_CONFIG_EXT_SYNC_SET_GYRO_YOUT_L \ + 0x18 // FSYNC on GYRO_YOUT_L[0] +#define MPU6050_CONFIG_EXT_SYNC_SET_GYRO_ZOUT_L \ + 0x20 // FSYNC on GYRO_ZOUT_L[0] +#define MPU6050_CONFIG_EXT_SYNC_SET_ACCEL_XOUT_L \ + 0x28 // FSYNC on ACCEL_XOUT_L[0] +#define MPU6050_CONFIG_EXT_SYNC_SET_ACCEL_YOUT_L \ + 0x30 // FSYNC on ACCEL_YOUT_L[0] +#define MPU6050_CONFIG_EXT_SYNC_SET_ACCEL_ZOUT_L \ + 0x38 // FSYNC on ACCEL_ZOUT_L[0] +#define MPU6050_CONFIG_DLPF_CFG_M \ + 0x07 // Digital low-pass filter + // configuration +#define MPU6050_CONFIG_DLPF_CFG_260_256 \ + 0x00 // 260 Hz accelerometer bandwidth, + // 256 Hz gyro bandwidth +#define MPU6050_CONFIG_DLPF_CFG_184_188 \ + 0x01 // 184 Hz accelerometer bandwidth, + // 188 Hz gyro bandwidth +#define MPU6050_CONFIG_DLPF_CFG_94_98 \ + 0x02 // 94 Hz accelerometer bandwidth, + // 98 Hz gyro bandwidth +#define MPU6050_CONFIG_DLPF_CFG_44_42 \ + 0x03 // 44 Hz accelerometer bandwidth, + // 42 Hz gyro bandwidth +#define MPU6050_CONFIG_DLPF_CFG_21_20 \ + 0x04 // 21 Hz accelerometer bandwidth, + // 20 Hz gyro bandwidth +#define MPU6050_CONFIG_DLPF_CFG_10_10 \ + 0x05 // 10 Hz accelerometer bandwidth, + // 10 Hz gyro bandwidth +#define MPU6050_CONFIG_DLPF_CFG_5_5 \ + 0x06 // 5 Hz accelerometer bandwidth, 5 + // Hz gyro bandwidth +#define MPU6050_CONFIG_EXT_SYNC_SET_S \ + 3 +#define MPU6050_CONFIG_DLPF_CFG_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_GYRO_CONFIG +// register. +// +//***************************************************************************** +#define MPU6050_GYRO_CONFIG_FS_SEL_M \ + 0x18 // Gyro full-scale range +#define MPU6050_GYRO_CONFIG_FS_SEL_250 \ + 0x00 // Gyro full-scale range +/- 250 + // degrees/sec +#define MPU6050_GYRO_CONFIG_FS_SEL_500 \ + 0x08 // Gyro full-scale range +/- 500 + // degrees/sec +#define MPU6050_GYRO_CONFIG_FS_SEL_1000 \ + 0x10 // Gyro full-scale range +/- 1000 + // degrees/sec +#define MPU6050_GYRO_CONFIG_FS_SEL_2000 \ + 0x18 // Gyro full-scale range +/- 2000 + // degrees/sec +#define MPU6050_GYRO_CONFIG_FS_SEL_S \ + 3 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_ACCEL_CONFIG +// register. +// +//***************************************************************************** +#define MPU6050_ACCEL_CONFIG_XA_ST \ + 0x80 // X-axis accelerometer self-test + // enable +#define MPU6050_ACCEL_CONFIG_YA_ST \ + 0x40 // Y-axis accelerometer self-test + // enable +#define MPU6050_ACCEL_CONFIG_ZA_ST \ + 0x20 // Z-axis accelerometer self-test + // enable +#define MPU6050_ACCEL_CONFIG_AFS_SEL_M \ + 0x18 // Accelerometer full-scale range +#define MPU6050_ACCEL_CONFIG_AFS_SEL_2G \ + 0x00 // Accelerometer full-scale range 2 + // g +#define MPU6050_ACCEL_CONFIG_AFS_SEL_4G \ + 0x08 // Accelerometer full-scale range 4 + // g +#define MPU6050_ACCEL_CONFIG_AFS_SEL_8G \ + 0x10 // Accelerometer full-scale range 8 + // g +#define MPU6050_ACCEL_CONFIG_AFS_SEL_16G \ + 0x18 // Accelerometer full-scale range + // 16 g +#define MPU6050_ACCEL_CONFIG_AFS_SEL_S \ + 3 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_MOT_THR +// register. +// +//***************************************************************************** +#define MPU6050_MOT_THR_M 0xFF // Motion detection threshold value +#define MPU6050_MOT_THR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_FIFO_EN +// register. +// +//***************************************************************************** +#define MPU6050_FIFO_EN_TEMP 0x80 // Temperature sensor FIFO enable +#define MPU6050_FIFO_EN_XG 0x40 // X-axis gyro FIFO enable +#define MPU6050_FIFO_EN_YG 0x20 // Y-axis gyro FIFO enable +#define MPU6050_FIFO_EN_ZG 0x10 // Z-axis gyro FIFO enable +#define MPU6050_FIFO_EN_ACCEL 0x08 // Accelerometer FIFO enable +#define MPU6050_FIFO_EN_SLV2 0x04 // Slave 2 FIFO enable +#define MPU6050_FIFO_EN_SLV1 0x02 // Slave 1 FIFO enable +#define MPU6050_FIFO_EN_SLV0 0x01 // Slave 0 FIFO enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_MST_CTRL +// register. +// +//***************************************************************************** +#define MPU6050_I2C_MST_CTRL_MULT_MST_EN \ + 0x80 // Multi-master enable +#define MPU6050_I2C_MST_CTRL_WAIT_FOR_ES \ + 0x40 // Wait for external sensor data +#define MPU6050_I2C_MST_CTRL_SLV3_FIFO_EN \ + 0x20 // Slave 3 FIFO enable +#define MPU6050_I2C_MST_CTRL_I2C_MST_P_NSR \ + 0x10 // No repeated start conditions +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_M \ + 0x0F // I2C master clock speed +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_348 \ + 0x00 // 348 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_333 \ + 0x01 // 333 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_320 \ + 0x02 // 320 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_308 \ + 0x03 // 308 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_296 \ + 0x04 // 296 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_286 \ + 0x05 // 286 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_276 \ + 0x06 // 276 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_267 \ + 0x07 // 267 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_258 \ + 0x08 // 258 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_500 \ + 0x09 // 500 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_471 \ + 0x0A // 471 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_444 \ + 0x0B // 444 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_421 \ + 0x0C // 421 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_400 \ + 0x0D // 400 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_381 \ + 0x0E // 381 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_364 \ + 0x0F // 364 kHz I2C master clock +#define MPU6050_I2C_MST_CTRL_I2C_MST_CLK_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV0_ADDR +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV0_ADDR_RW \ + 0x80 // Read/not write +#define MPU6050_I2C_SLV0_ADDR_M 0x7F // Slave address +#define MPU6050_I2C_SLV0_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV0_REG +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV0_REG_M 0xFF // Slave register number +#define MPU6050_I2C_SLV0_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV0_CTRL +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV0_CTRL_EN \ + 0x80 // Enable slave +#define MPU6050_I2C_SLV0_CTRL_BYTE_SW \ + 0x40 // Byte-swap word pairs +#define MPU6050_I2C_SLV0_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU6050_I2C_SLV0_CTRL_GRP \ + 0x10 // Word pair grouping +#define MPU6050_I2C_SLV0_CTRL_LEN_M \ + 0x0F // Number of bytes to transfer +#define MPU6050_I2C_SLV0_CTRL_LEN_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV1_ADDR +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV1_ADDR_RW \ + 0x80 // Read/not write +#define MPU6050_I2C_SLV1_ADDR_M 0x7F // Slave address +#define MPU6050_I2C_SLV1_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV1_REG +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV1_REG_M 0xFF // Slave register number +#define MPU6050_I2C_SLV1_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV1_CTRL +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV1_CTRL_EN \ + 0x80 // Enable slave +#define MPU6050_I2C_SLV1_CTRL_BYTE_SW \ + 0x40 // Byte-swap word pairs +#define MPU6050_I2C_SLV1_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU6050_I2C_SLV1_CTRL_GRP \ + 0x10 // Word pair grouping +#define MPU6050_I2C_SLV1_CTRL_LEN_M \ + 0x0F // Number of bytes to transfer +#define MPU6050_I2C_SLV1_CTRL_LEN_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV2_ADDR +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV2_ADDR_RW \ + 0x80 // Read/not write +#define MPU6050_I2C_SLV2_ADDR_M 0x7F // Slave address +#define MPU6050_I2C_SLV2_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV2_REG +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV2_REG_M 0xFF // Slave register number +#define MPU6050_I2C_SLV2_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV2_CTRL +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV2_CTRL_EN \ + 0x80 // Enable slave +#define MPU6050_I2C_SLV2_CTRL_BYTE_SW \ + 0x40 // Byte-swap word pairs +#define MPU6050_I2C_SLV2_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU6050_I2C_SLV2_CTRL_GRP \ + 0x10 // Word pair grouping +#define MPU6050_I2C_SLV2_CTRL_LEN_M \ + 0x0F // Number of bytes to transfer +#define MPU6050_I2C_SLV2_CTRL_LEN_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV3_ADDR +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV3_ADDR_RW \ + 0x80 // Read/not write +#define MPU6050_I2C_SLV3_ADDR_M 0x7F // Slave address +#define MPU6050_I2C_SLV3_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV3_REG +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV3_REG_M 0xFF // Slave register number +#define MPU6050_I2C_SLV3_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV3_CTRL +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV3_CTRL_EN \ + 0x80 // Enable slave +#define MPU6050_I2C_SLV3_CTRL_BYTE_SW \ + 0x40 // Byte-swap word pairs +#define MPU6050_I2C_SLV3_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU6050_I2C_SLV3_CTRL_GRP \ + 0x10 // Word pair grouping +#define MPU6050_I2C_SLV3_CTRL_LEN_M \ + 0x0F // Number of bytes to transfer +#define MPU6050_I2C_SLV3_CTRL_LEN_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV4_ADDR +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV4_ADDR_RW \ + 0x80 // Read/not write +#define MPU6050_I2C_SLV4_ADDR_M 0x7F // Slave address +#define MPU6050_I2C_SLV4_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV4_REG +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV4_REG_M 0xFF // Slave register number +#define MPU6050_I2C_SLV4_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV4_CTRL +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV4_CTRL_EN \ + 0x80 // Enable slave +#define MPU6050_I2C_SLV4_CTRL_INT_EN \ + 0x40 // Interrupt enable +#define MPU6050_I2C_SLV4_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU6050_I2C_SLV4_CTRL_I2C_MST_DLY_M \ + 0x1F // Slave access delay +#define MPU6050_I2C_SLV4_CTRL_I2C_MST_DLY_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV4_DI +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV4_DI_M 0xFF // Input data +#define MPU6050_I2C_SLV4_DI_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_MST_STATUS +// register. +// +//***************************************************************************** +#define MPU6050_I2C_MST_STATUS_PASS_THROUGH \ + 0x80 // Pass through FSYNC interrupt + // status +#define MPU6050_I2C_MST_STATUS_I2C_SLV4_DONE \ + 0x40 // I2C slave 4 completion status +#define MPU6050_I2C_MST_STATUS_I2C_LOST_ARB \ + 0x20 // I2C arbitration lost status +#define MPU6050_I2C_MST_STATUS_I2C_SLV4_NACK \ + 0x10 // I2C slave 4 NACK status +#define MPU6050_I2C_MST_STATUS_I2C_SLV3_NACK \ + 0x08 // I2C slave 3 NACK status +#define MPU6050_I2C_MST_STATUS_I2C_SLV2_NACK \ + 0x04 // I2C slave 2 NACK status +#define MPU6050_I2C_MST_STATUS_I2C_SLV1_NACK \ + 0x02 // I2C slave 1 NACK status +#define MPU6050_I2C_MST_STATUS_I2C_SLV0_NACK \ + 0x01 // I2C slave 0 NACK status + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_INT_PIN_CFG +// register. +// +//***************************************************************************** +#define MPU6050_INT_PIN_CFG_INT_LEVEL \ + 0x80 // INT pin active low +#define MPU6050_INT_PIN_CFG_INT_OPEN \ + 0x40 // INT pin open-drain +#define MPU6050_INT_PIN_CFG_LATCH_INT_EN \ + 0x20 // Latch INT pin output +#define MPU6050_INT_PIN_CFG_INT_RD_CLEAR \ + 0x10 // Interrupt clear on any read +#define MPU6050_INT_PIN_CFG_FSYNC_INT_LEVEL \ + 0x08 // FSYNC pin active low +#define MPU6050_INT_PIN_CFG_FSYNC_INT_EN \ + 0x04 // FSYNC pin interrupt enable +#define MPU6050_INT_PIN_CFG_I2C_BYPASS_EN \ + 0x02 // I2C bypass enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_INT_ENABLE +// register. +// +//***************************************************************************** +#define MPU6050_INT_ENABLE_MOT_EN \ + 0x40 // Motion detection interrupt + // enable +#define MPU6050_INT_ENABLE_FIFO_OFLOW_EN \ + 0x10 // FIFO overflow interrupt enable +#define MPU6050_INT_ENABLE_I2C_MST_INT_EN \ + 0x08 // I2C master interrupt enable +#define MPU6050_INT_ENABLE_DATA_RDY_EN \ + 0x01 // Data ready interrupt enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_INT_STATUS +// register. +// +//***************************************************************************** +#define MPU6050_INT_STATUS_MOT_INT \ + 0x40 // Motion detection interrupt + // status +#define MPU6050_INT_STATUS_FIFO_OFLOW_INT \ + 0x10 // FIFO overflow interrupt status +#define MPU6050_INT_STATUS_I2C_MST_INT \ + 0x08 // I2C master interrupt status +#define MPU6050_INT_STATUS_DATA_RDY_INT \ + 0x01 // Data ready interrupt status + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_ACCEL_XOUT_H +// register. +// +//***************************************************************************** +#define MPU6050_ACCEL_XOUT_H_M 0xFF // Bits [15:8] of X-axis + // acceleration data +#define MPU6050_ACCEL_XOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_ACCEL_XOUT_L +// register. +// +//***************************************************************************** +#define MPU6050_ACCEL_XOUT_L_M 0xFF // Bits [7:0] of X-axis + // acceleration data +#define MPU6050_ACCEL_XOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_ACCEL_YOUT_H +// register. +// +//***************************************************************************** +#define MPU6050_ACCEL_YOUT_H_M 0xFF // Bits [15:8] of Y-axis + // acceleration data +#define MPU6050_ACCEL_YOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_ACCEL_YOUT_L +// register. +// +//***************************************************************************** +#define MPU6050_ACCEL_YOUT_L_M 0xFF // Bits [7:0] of Y-axis + // acceleration data +#define MPU6050_ACCEL_YOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_ACCEL_ZOUT_H +// register. +// +//***************************************************************************** +#define MPU6050_ACCEL_ZOUT_H_M 0xFF // Bits [15:8] of Z-axis + // acceleration data +#define MPU6050_ACCEL_ZOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_ACCEL_ZOUT_L +// register. +// +//***************************************************************************** +#define MPU6050_ACCEL_ZOUT_L_M 0xFF // Bits [7:0] of Z-axis + // acceleration data +#define MPU6050_ACCEL_ZOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_TEMP_OUT_H +// register. +// +//***************************************************************************** +#define MPU6050_ACCEL_TEMP_OUT_H_M \ + 0xFF // Bits [15:8] of temperature data +#define MPU6050_ACCEL_TEMP_OUT_H_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_TEMP_OUT_L +// register. +// +//***************************************************************************** +#define MPU6050_ACCEL_TEMP_OUT_L_M \ + 0xFF // Bits [7:0] of temperature data +#define MPU6050_ACCEL_TEMP_OUT_L_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_GYRO_XOUT_H +// register. +// +//***************************************************************************** +#define MPU6050_GYRO_XOUT_H_M 0xFF // Bits [15:8] of X-axis gyro data +#define MPU6050_GYRO_XOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_GYRO_XOUT_L +// register. +// +//***************************************************************************** +#define MPU6050_GYRO_XOUT_L_M 0xFF // Bits [7:0] of X-axis gyro data +#define MPU6050_GYRO_XOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_GYRO_YOUT_H +// register. +// +//***************************************************************************** +#define MPU6050_GYRO_YOUT_H_M 0xFF // Bits [15:8] of Y-axis gyro data +#define MPU6050_GYRO_YOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_GYRO_YOUT_L +// register. +// +//***************************************************************************** +#define MPU6050_GYRO_YOUT_L_M 0xFF // Bits [7:0] of Y-axis gyro data +#define MPU6050_GYRO_YOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_GYRO_ZOUT_H +// register. +// +//***************************************************************************** +#define MPU6050_GYRO_ZOUT_H_M 0xFF // Bits [15:8] of Z-axis gyro data +#define MPU6050_GYRO_ZOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_GYRO_ZOUT_L +// register. +// +//***************************************************************************** +#define MPU6050_GYRO_ZOUT_L_M 0xFF // Bits [7:0] of Z-axis gyro data +#define MPU6050_GYRO_ZOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_00 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_00_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_00_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_01 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_01_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_01_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_02 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_02_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_02_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_03 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_03_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_03_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_04 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_04_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_04_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_05 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_05_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_05_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_06 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_06_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_06_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_07 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_07_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_07_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_08 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_08_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_08_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_09 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_09_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_09_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_10 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_10_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_10_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_11 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_11_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_11_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_12 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_12_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_12_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_13 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_13_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_13_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_14 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_14_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_14_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_15 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_15_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_15_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_16 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_16_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_16_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_17 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_17_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_17_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_18 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_18_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_18_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_19 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_19_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_19_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_20 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_20_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_20_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_21 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_21_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_21_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_22 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_22_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_22_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_EXT_SENS_DATA_23 register. +// +//***************************************************************************** +#define MPU6050_EXT_SENS_DATA_23_M \ + 0xFF // External sensor data +#define MPU6050_EXT_SENS_DATA_23_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV0_DO +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV0_DO_M 0xFF // Output data +#define MPU6050_I2C_SLV0_DO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV1_DO +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV1_DO_M 0xFF // Output data +#define MPU6050_I2C_SLV1_DO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV2_DO +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV2_DO_M 0xFF // Output data +#define MPU6050_I2C_SLV2_DO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_I2C_SLV3_DO +// register. +// +//***************************************************************************** +#define MPU6050_I2C_SLV3_DO_M 0xFF // Output data +#define MPU6050_I2C_SLV3_DO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_I2C_MST_DELAY_CTRL register. +// +//***************************************************************************** +#define MPU6050_I2C_MST_DELAY_CTRL_DELAY_ES_SHADOW \ + 0x80 // Delay external sensor data +#define MPU6050_I2C_MST_DELAY_CTRL_I2C_SLV4_DLY_EN \ + 0x10 // I2C slave 4 delay enable +#define MPU6050_I2C_MST_DELAY_CTRL_I2C_SLV3_DLY_EN \ + 0x08 // I2C slave 3 delay enable +#define MPU6050_I2C_MST_DELAY_CTRL_I2C_SLV2_DLY_EN \ + 0x04 // I2C slave 2 delay enable +#define MPU6050_I2C_MST_DELAY_CTRL_I2C_SLV1_DLY_EN \ + 0x02 // I2C slave 1 delay enable +#define MPU6050_I2C_MST_DELAY_CTRL_I2C_SLV0_DLY_EN \ + 0x01 // I2C slave 0 delay enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_SIGNAL_PATH_RESET register. +// +//***************************************************************************** +#define MPU6050_SIGNAL_PATH_RESET_GYRO \ + 0x04 // Reset gyro +#define MPU6050_SIGNAL_PATH_RESET_ACCEL \ + 0x02 // Reset accelerometer +#define MPU6050_SIGNAL_PATH_RESET_TEMP \ + 0x01 // Reset temperature sensor + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU6050_O_MOT_DETECT_CTRL register. +// +//***************************************************************************** +#define MPU6050_MOT_DETECT_CTRL_ACCEL_ON_DELAY_M \ + 0x30 // Accelerometer wake-up delay +#define MPU6050_MOT_DETECT_CTRL_ACCEL_ON_DELAY_4MS \ + 0x00 // Delay 4 ms +#define MPU6050_MOT_DETECT_CTRL_ACCEL_ON_DELAY_5MS \ + 0x10 // Delay 5 ms +#define MPU6050_MOT_DETECT_CTRL_ACCEL_ON_DELAY_6MS \ + 0x20 // Delay 6 ms +#define MPU6050_MOT_DETECT_CTRL_ACCEL_ON_DELAY_7MS \ + 0x30 // Delay 7 ms +#define MPU6050_MOT_DETECT_CTRL_ACCEL_ON_DELAY_S \ + 4 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_USER_CTRL +// register. +// +//***************************************************************************** +#define MPU6050_USER_CTRL_FIFO_EN \ + 0x40 // FIFO enable +#define MPU6050_USER_CTRL_I2C_MST_EN \ + 0x20 // I2C master mode enable +#define MPU6050_USER_CTRL_I2C_IF_DIS \ + 0x10 // Write as zero +#define MPU6050_USER_CTRL_FIFO_RESET \ + 0x04 // Reset FIFO buffer +#define MPU6050_USER_CTRL_I2C_MST_RESET \ + 0x02 // Reset I2C master +#define MPU6050_USER_CTRL_SIG_COND_RESET \ + 0x01 // Reset all sensors + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_PWR_MGMT_1 +// register. +// +//***************************************************************************** +#define MPU6050_PWR_MGMT_1_DEVICE_RESET \ + 0x80 // Device reset +#define MPU6050_PWR_MGMT_1_SLEEP \ + 0x40 // Enter sleep mode +#define MPU6050_PWR_MGMT_1_CYCLE \ + 0x20 // Enable automatic sleep +#define MPU6050_PWR_MGMT_1_TEMP_DIS \ + 0x08 // Disable temperature sensor +#define MPU6050_PWR_MGMT_1_CLKSEL_M \ + 0x07 // Clock source select +#define MPU6050_PWR_MGMT_1_CLKSEL_INT \ + 0x00 // Internal 8 MHz oscillator +#define MPU6050_PWR_MGMT_1_CLKSEL_XG \ + 0x01 // PLL with X-axis gyro reference +#define MPU6050_PWR_MGMT_1_CLKSEL_YG \ + 0x02 // PLL with Y-axis gyro reference +#define MPU6050_PWR_MGMT_1_CLKSEL_ZG \ + 0x03 // PLL with Z-axis gyro reference +#define MPU6050_PWR_MGMT_1_CLKSEL_EXT32K \ + 0x04 // PLL with external 32.768 kHz + // reference +#define MPU6050_PWR_MGMT_1_CLKSEL_EXT19M \ + 0x05 // PLL with external 19.2 MHz + // reference +#define MPU6050_PWR_MGMT_1_CLKSEL_STOP \ + 0x07 // Clock disable +#define MPU6050_PWR_MGMT_1_CLKSEL_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_PWR_MGMT_2 +// register. +// +//***************************************************************************** +#define MPU6050_PWR_MGMT_2_LP_WAKE_CTRL_M \ + 0xC0 // Wake-up frequency +#define MPU6050_PWR_MGMT_2_LP_WAKE_CTRL_1_25 \ + 0x00 // Wake-up at 1.25 Hz +#define MPU6050_PWR_MGMT_2_LP_WAKE_CTRL_5 \ + 0x40 // Wake-up at 5 Hz +#define MPU6050_PWR_MGMT_2_LP_WAKE_CTRL_20 \ + 0x80 // Wake-up at 20 Hz +#define MPU6050_PWR_MGMT_2_LP_WAKE_CTRL_40 \ + 0xC0 // Wake-up at 40 Hz +#define MPU6050_PWR_MGMT_2_STBY_XA \ + 0x20 // Put X-axis accelerometer into + // standby mode +#define MPU6050_PWR_MGMT_2_STBY_YA \ + 0x10 // Put Y-axis accelerometer into + // standby mode +#define MPU6050_PWR_MGMT_2_STBY_ZA \ + 0x08 // Put Z-axis accelerometer into + // standby mode +#define MPU6050_PWR_MGMT_2_STBY_XG \ + 0x04 // Put X-axis gyro into standby + // mode +#define MPU6050_PWR_MGMT_2_STBY_YG \ + 0x02 // Put Y-axis gyro into standby + // mode +#define MPU6050_PWR_MGMT_2_STBY_ZG \ + 0x01 // Put Z-axis gyro into standby + // mode +#define MPU6050_PWR_MGMT_2_LP_WAKE_CTRL_S \ + 6 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_FIFO_COUNTH +// register. +// +//***************************************************************************** +#define MPU6050_FIFO_COUNTH_M 0xFF // FIFO count [15:8] +#define MPU6050_FIFO_COUNTH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_FIFO_COUNTL +// register. +// +//***************************************************************************** +#define MPU6050_FIFO_COUNTL_M 0xFF // FIFO count [7:0] +#define MPU6050_FIFO_COUNTL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_FIFO_R_W +// register. +// +//***************************************************************************** +#define MPU6050_FIFO_R_W_M 0xFF // FIFO data +#define MPU6050_FIFO_R_W_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU6050_O_WHO_AM_I +// register. +// +//***************************************************************************** +#define MPU6050_WHO_AM_I_M 0x7E // I2C address +#define MPU6050_WHO_AM_I_MPU6050 \ + 0x68 // MPU6050 +#define MPU6050_WHO_AM_I_S 1 + +#endif // __SENSORLIB_HW_MPU6050_H__ diff --git a/sensorlib/hw_mpu9150.h b/sensorlib/hw_mpu9150.h new file mode 100644 index 0000000..b586d1b --- /dev/null +++ b/sensorlib/hw_mpu9150.h @@ -0,0 +1,1454 @@ +//***************************************************************************** +// +// hw_mpu9150.h - Macros used when accessing the Invensense MPU9150 +// accelerometer/gyroscope/magnetometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_MPU9150_H__ +#define __SENSORLIB_HW_MPU9150_H__ + +//***************************************************************************** +// +// The following are defines for the MPU9150 register addresses. +// +//***************************************************************************** +#define MPU9150_O_SELF_TEST_X 0x0D // Self test X register +#define MPU9150_O_SELF_TEST_Y 0x0E // Self test Y register +#define MPU9150_O_SELF_TEST_Z 0x0F // Self test Z register +#define MPU9150_O_SELF_TEST_A 0x10 // Self test A register +#define MPU9150_O_SMPLRT_DIV 0x19 // Sample rate divider register +#define MPU9150_O_CONFIG 0x1A // Configuration register +#define MPU9150_O_GYRO_CONFIG 0x1B // Gyro configuration register +#define MPU9150_O_ACCEL_CONFIG 0x1C // Accelerometer configuration + // register +#define MPU9150_O_FF_THR 0x1D // Free-fall threshold register +#define MPU9150_O_FF_DUR 0x1E // Free-fall duration register +#define MPU9150_O_MOT_THR 0x1F // Motion detection threshold + // register +#define MPU9150_O_MOT_DUR 0x20 // Motion detection duration + // register +#define MPU9150_O_ZRMOT_THR 0x21 // Zero motion detection threshold + // register +#define MPU9150_O_ZRMOT_DUR 0x22 // Zero motion detection duration + // register +#define MPU9150_O_FIFO_EN 0x23 // FIFO enable register +#define MPU9150_O_I2C_MST_CTRL 0x24 // I2C master control register +#define MPU9150_O_I2C_SLV0_ADDR 0x25 // I2C slave 0 address register +#define MPU9150_O_I2C_SLV0_REG 0x26 // I2C slave 0 register number + // register +#define MPU9150_O_I2C_SLV0_CTRL 0x27 // I2C slave 0 control register +#define MPU9150_O_I2C_SLV1_ADDR 0x28 // I2C slave 1 address register +#define MPU9150_O_I2C_SLV1_REG 0x29 // I2C slave 1 register number + // register +#define MPU9150_O_I2C_SLV1_CTRL 0x2A // I2C slave 1 control register +#define MPU9150_O_I2C_SLV2_ADDR 0x2B // I2C slave 2 address register +#define MPU9150_O_I2C_SLV2_REG 0x2C // I2C slave 2 register number + // register +#define MPU9150_O_I2C_SLV2_CTRL 0x2D // I2C slave 2 control register +#define MPU9150_O_I2C_SLV3_ADDR 0x2E // I2C slave 3 address register +#define MPU9150_O_I2C_SLV3_REG 0x2F // I2C slave 3 register number + // register +#define MPU9150_O_I2C_SLV3_CTRL 0x30 // I2C slave 3 control register +#define MPU9150_O_I2C_SLV4_ADDR 0x31 // I2C slave 4 address register +#define MPU9150_O_I2C_SLV4_REG 0x32 // I2C slave 4 register number + // register +#define MPU9150_O_I2C_SLV4_DO 0x33 // I2C slave 4 output data register +#define MPU9150_O_I2C_SLV4_CTRL 0x34 // I2C slave 4 control register +#define MPU9150_O_I2C_SLV4_DI 0x35 // I2C slave 4 input data register +#define MPU9150_O_I2C_MST_STATUS \ + 0x36 // I2C master status register +#define MPU9150_O_INT_PIN_CFG 0x37 // INT pin configuration register +#define MPU9150_O_INT_ENABLE 0x38 // Interrupt enable register +#define MPU9150_O_INT_STATUS 0x3A // Interrupt status register +#define MPU9150_O_ACCEL_XOUT_H 0x3B // X-axis acceleration data MSB + // register +#define MPU9150_O_ACCEL_XOUT_L 0x3C // X-axis acceleration data LSB + // register +#define MPU9150_O_ACCEL_YOUT_H 0x3D // Y-axis acceleration data MSB + // register +#define MPU9150_O_ACCEL_YOUT_L 0x3E // Y-axis accelearation data LSB + // register +#define MPU9150_O_ACCEL_ZOUT_H 0x3F // Z-axis acceleration data MSB + // register +#define MPU9150_O_ACCEL_ZOUT_L 0x40 // Z-axis acceleration data LSB + // register +#define MPU9150_O_TEMP_OUT_H 0x41 // Temperature data MSB register +#define MPU9150_O_TEMP_OUT_L 0x42 // Temperature data LSB register +#define MPU9150_O_GYRO_XOUT_H 0x43 // X-axis gyro data MSB register +#define MPU9150_O_GYRO_XOUT_L 0x44 // X-axis gyro data LSB register +#define MPU9150_O_GYRO_YOUT_H 0x45 // Y-axis gyro data MSB register +#define MPU9150_O_GYRO_YOUT_L 0x46 // Y-axis gyro data LSB register +#define MPU9150_O_GYRO_ZOUT_H 0x47 // Z-axis gyro data MSB register +#define MPU9150_O_GYRO_ZOUT_L 0x48 // Z-axis gyro data LSB register +#define MPU9150_O_EXT_SENS_DATA_00 \ + 0x49 // External sensor data 0 register +#define MPU9150_O_EXT_SENS_DATA_01 \ + 0x4A // External sensor data 1 register +#define MPU9150_O_EXT_SENS_DATA_02 \ + 0x4B // External sensor data 2 register +#define MPU9150_O_EXT_SENS_DATA_03 \ + 0x4C // External sensor data 3 register +#define MPU9150_O_EXT_SENS_DATA_04 \ + 0x4D // External sensor data 4 register +#define MPU9150_O_EXT_SENS_DATA_05 \ + 0x4E // External sensor data 5 register +#define MPU9150_O_EXT_SENS_DATA_06 \ + 0x4F // External sensor data 6 register +#define MPU9150_O_EXT_SENS_DATA_07 \ + 0x50 // External sensor data 7 register +#define MPU9150_O_EXT_SENS_DATA_08 \ + 0x51 // External sensor data 8 register +#define MPU9150_O_EXT_SENS_DATA_09 \ + 0x52 // External sensor data 9 register +#define MPU9150_O_EXT_SENS_DATA_10 \ + 0x53 // External sensor data 10 register +#define MPU9150_O_EXT_SENS_DATA_11 \ + 0x54 // External sensor data 11 register +#define MPU9150_O_EXT_SENS_DATA_12 \ + 0x55 // External sensor data 12 register +#define MPU9150_O_EXT_SENS_DATA_13 \ + 0x56 // External sensor data 13 register +#define MPU9150_O_EXT_SENS_DATA_14 \ + 0x57 // External sensor data 14 register +#define MPU9150_O_EXT_SENS_DATA_15 \ + 0x58 // External sensor data 15 register +#define MPU9150_O_EXT_SENS_DATA_16 \ + 0x59 // External sensor data 16 register +#define MPU9150_O_EXT_SENS_DATA_17 \ + 0x5A // External sensor data 17 register +#define MPU9150_O_EXT_SENS_DATA_18 \ + 0x5B // External sensor data 18 register +#define MPU9150_O_EXT_SENS_DATA_19 \ + 0x5C // External sensor data 19 register +#define MPU9150_O_EXT_SENS_DATA_20 \ + 0x5D // External sensor data 20 register +#define MPU9150_O_EXT_SENS_DATA_21 \ + 0x5E // External sensor data 21 register +#define MPU9150_O_EXT_SENS_DATA_22 \ + 0x5F // External sensor data 22 register +#define MPU9150_O_EXT_SENS_DATA_23 \ + 0x60 // External sensor data 23 register +#define MPU9150_O_MOT_DETECT_STATUS \ + 0x61 // Motion detection status register +#define MPU9150_O_I2C_SLV0_DO 0x63 // I2C slave 0 output data register +#define MPU9150_O_I2C_SLV1_DO 0x64 // I2C slave 1 output data register +#define MPU9150_O_I2C_SLV2_DO 0x65 // I2C slave 2 output data register +#define MPU9150_O_I2C_SLV3_DO 0x66 // I2C slave 3 output data register +#define MPU9150_O_I2C_MST_DELAY_CTRL \ + 0x67 // I2C master delay control + // register +#define MPU9150_O_SIGNAL_PATH_RESET \ + 0x68 // Signal path reset register +#define MPU9150_O_MOT_DETECT_CTRL \ + 0x69 // Motion detection control + // register +#define MPU9150_O_USER_CTRL 0x6A // User control register +#define MPU9150_O_PWR_MGMT_1 0x6B // Power management 1 register +#define MPU9150_O_PWR_MGMT_2 0x6C // Power management 2 register +#define MPU9150_O_FIFO_COUNTH 0x72 // FIFO count MSB register +#define MPU9150_O_FIFO_COUNTL 0x73 // FIFO count LSB register +#define MPU9150_O_FIFO_R_W 0x74 // FIFO read write register +#define MPU9150_O_WHO_AM_I 0x75 // Who am I register + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_SELF_TEST_X +// register. +// +//***************************************************************************** +#define MPU9150_SELF_TEST_X_XA_TEST_M \ + 0xE0 // Accelerometer XA_TEST[4:2] +#define MPU9150_SELF_TEST_X_XG_TEST_M \ + 0x1F // Gyro XG_TEST[4:0] +#define MPU9150_SELF_TEST_X_XA_TEST_S \ + 5 +#define MPU9150_SELF_TEST_X_XG_TEST_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_SELF_TEST_Y +// register. +// +//***************************************************************************** +#define MPU9150_SELF_TEST_Y_YA_TEST_M \ + 0xE0 // Accelerometer YA_TEST[4:2] +#define MPU9150_SELF_TEST_Y_YG_TEST_M \ + 0x1F // Gyro YG_TEST[4:0] +#define MPU9150_SELF_TEST_Y_YA_TEST_S \ + 5 +#define MPU9150_SELF_TEST_Y_YG_TEST_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_SELF_TEST_Z +// register. +// +//***************************************************************************** +#define MPU9150_SELF_TEST_Z_ZA_TEST_M \ + 0xE0 // Accelerometer ZA_TEST[4:2] +#define MPU9150_SELF_TEST_Z_ZG_TEST_M \ + 0x1F // Gyro ZG_TEST[4:0] +#define MPU9150_SELF_TEST_Z_ZA_TEST_S \ + 5 +#define MPU9150_SELF_TEST_Z_ZG_TEST_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_SELF_TEST_A +// register. +// +//***************************************************************************** +#define MPU9150_SELF_TEST_A_XA_TEST_M \ + 0x30 // Accelerometer XA_TEST[1:0] +#define MPU9150_SELF_TEST_A_YA_TEST_M \ + 0x0C // Accelerometer YA_TEST[1:0] +#define MPU9150_SELF_TEST_A_ZA_TEST_M \ + 0x03 // Accelerometer ZA_TEST[1:0] +#define MPU9150_SELF_TEST_A_XA_TEST_S \ + 4 +#define MPU9150_SELF_TEST_A_YA_TEST_S \ + 2 +#define MPU9150_SELF_TEST_A_ZA_TEST_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_SMPLRT_DIV +// register. +// +//***************************************************************************** +#define MPU9150_SMPLRT_DIV_M 0xFF // Gyro output rate divider +#define MPU9150_SMPLRT_DIV_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_CONFIG +// register. +// +//***************************************************************************** +#define MPU9150_CONFIG_EXT_SYNC_SET_M \ + 0x38 // FSYNC pin sample location +#define MPU9150_CONFIG_EXT_SYNC_SET_DIS \ + 0x00 // FSYNC input disabled +#define MPU9150_CONFIG_EXT_SYNC_SET_TEMP_OUT_L \ + 0x08 // FSYNC on TEMP_OUT_L[0] +#define MPU9150_CONFIG_EXT_SYNC_SET_GYRO_XOUT_L \ + 0x10 // FSYNC on GYRO_XOUT_L[0] +#define MPU9150_CONFIG_EXT_SYNC_SET_GYRO_YOUT_L \ + 0x18 // FSYNC on GYRO_YOUT_L[0] +#define MPU9150_CONFIG_EXT_SYNC_SET_GYRO_ZOUT_L \ + 0x20 // FSYNC on GYRO_ZOUT_L[0] +#define MPU9150_CONFIG_EXT_SYNC_SET_ACCEL_XOUT_L \ + 0x28 // FSYNC on ACCEL_XOUT_L[0] +#define MPU9150_CONFIG_EXT_SYNC_SET_ACCEL_YOUT_L \ + 0x30 // FSYNC on ACCEL_YOUT_L[0] +#define MPU9150_CONFIG_EXT_SYNC_SET_ACCEL_ZOUT_L \ + 0x38 // FSYNC on ACCEL_ZOUT_L[0] +#define MPU9150_CONFIG_DLPF_CFG_M \ + 0x07 // Digital low-pass filter + // configuration +#define MPU9150_CONFIG_DLPF_CFG_260_256 \ + 0x00 // 260 Hz accelerometer bandwidth, + // 256 Hz gyro bandwidth +#define MPU9150_CONFIG_DLPF_CFG_184_188 \ + 0x01 // 184 Hz accelerometer bandwidth, + // 188 Hz gyro bandwidth +#define MPU9150_CONFIG_DLPF_CFG_94_98 \ + 0x02 // 94 Hz accelerometer bandwidth, + // 98 Hz gyro bandwidth +#define MPU9150_CONFIG_DLPF_CFG_44_42 \ + 0x03 // 44 Hz accelerometer bandwidth, + // 42 Hz gyro bandwidth +#define MPU9150_CONFIG_DLPF_CFG_21_20 \ + 0x04 // 21 Hz accelerometer bandwidth, + // 20 Hz gyro bandwidth +#define MPU9150_CONFIG_DLPF_CFG_10_10 \ + 0x05 // 10 Hz accelerometer bandwidth, + // 10 Hz gyro bandwidth +#define MPU9150_CONFIG_DLPF_CFG_5_5 \ + 0x06 // 5 Hz accelerometer bandwidth, 5 + // Hz gyro bandwidth +#define MPU9150_CONFIG_EXT_SYNC_SET_S \ + 3 +#define MPU9150_CONFIG_DLPF_CFG_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_GYRO_CONFIG +// register. +// +//***************************************************************************** +#define MPU9150_GYRO_CONFIG_XG_ST \ + 0x80 // X-axis gyro self-test enable +#define MPU9150_GYRO_CONFIG_YG_ST \ + 0x40 // Y-axis gyro self-test enable +#define MPU9150_GYRO_CONFIG_ZG_ST \ + 0x20 // Z-axis gyro self-test enable +#define MPU9150_GYRO_CONFIG_FS_SEL_M \ + 0x18 // Gyro full-scale range +#define MPU9150_GYRO_CONFIG_FS_SEL_250 \ + 0x00 // Gyro full-scale range +/- 250 + // degrees/sec +#define MPU9150_GYRO_CONFIG_FS_SEL_500 \ + 0x08 // Gyro full-scale range +/- 500 + // degrees/sec +#define MPU9150_GYRO_CONFIG_FS_SEL_1000 \ + 0x10 // Gyro full-scale range +/- 1000 + // degrees/sec +#define MPU9150_GYRO_CONFIG_FS_SEL_2000 \ + 0x18 // Gyro full-scale range +/- 2000 + // degrees/sec +#define MPU9150_GYRO_CONFIG_FS_SEL_S \ + 3 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_ACCEL_CONFIG +// register. +// +//***************************************************************************** +#define MPU9150_ACCEL_CONFIG_XA_ST \ + 0x80 // X-axis accelerometer self-test + // enable +#define MPU9150_ACCEL_CONFIG_YA_ST \ + 0x40 // Y-axis accelerometer self-test + // enable +#define MPU9150_ACCEL_CONFIG_ZA_ST \ + 0x20 // Z-axis accelerometer self-test + // enable +#define MPU9150_ACCEL_CONFIG_AFS_SEL_M \ + 0x18 // Accelerometer full-scale range +#define MPU9150_ACCEL_CONFIG_AFS_SEL_2G \ + 0x00 // Accelerometer full-scale range 2 + // g +#define MPU9150_ACCEL_CONFIG_AFS_SEL_4G \ + 0x08 // Accelerometer full-scale range 4 + // g +#define MPU9150_ACCEL_CONFIG_AFS_SEL_8G \ + 0x10 // Accelerometer full-scale range 8 + // g +#define MPU9150_ACCEL_CONFIG_AFS_SEL_16G \ + 0x18 // Accelerometer full-scale range + // 16 g +#define MPU9150_ACCEL_CONFIG_ACCEL_HPF_M \ + 0x07 // High-pass filter setting +#define MPU9150_ACCEL_CONFIG_ACCEL_HPF_RESET \ + 0x00 // High-pass filter reset +#define MPU9150_ACCEL_CONFIG_ACCEL_HPF_5HZ \ + 0x01 // High-pass filter at 5 Hz +#define MPU9150_ACCEL_CONFIG_ACCEL_HPF_2_5HZ \ + 0x02 // High-pass filter at 2.5 Hz +#define MPU9150_ACCEL_CONFIG_ACCEL_HPF_1_25HZ \ + 0x03 // High-pass filter at 1.25 Hz +#define MPU9150_ACCEL_CONFIG_ACCEL_HPF_0_63HZ \ + 0x04 // High-pass filter at 0.63 Hz +#define MPU9150_ACCEL_CONFIG_ACCEL_HPF_HOLD \ + 0x07 // High-pass filter hold +#define MPU9150_ACCEL_CONFIG_AFS_SEL_S \ + 3 +#define MPU9150_ACCEL_CONFIG_ACCEL_HPF_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_FF_THR +// register. +// +//***************************************************************************** +#define MPU9150_FF_THR_M 0xFF // Free-fall threshold value +#define MPU9150_FF_THR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_FF_DUR +// register. +// +//***************************************************************************** +#define MPU9150_FF_DUR_M 0xFF // Free-fall duration value +#define MPU9150_FF_DUR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_MOT_THR +// register. +// +//***************************************************************************** +#define MPU9150_MOT_THR_M 0xFF // Motion detection threshold value +#define MPU9150_MOT_THR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_MOT_DUR +// register. +// +//***************************************************************************** +#define MPU9150_MOT_DUR_M 0xFF // Motion detection duration value +#define MPU9150_MOT_DUR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_ZRMOT_THR +// register. +// +//***************************************************************************** +#define MPU9150_ZRMOT_THR_M 0xFF // Zero motion detection threshold + // value +#define MPU9150_ZRMOT_THR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_ZRMOT_DUR +// register. +// +//***************************************************************************** +#define MPU9150_ZRMOT_DUR_M 0xFF // Zero motion detection duration + // value +#define MPU9150_ZRMOT_DUR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_FIFO_EN +// register. +// +//***************************************************************************** +#define MPU9150_FIFO_EN_TEMP 0x80 // Temperature sensor FIFO enable +#define MPU9150_FIFO_EN_XG 0x40 // X-axis gyro FIFO enable +#define MPU9150_FIFO_EN_YG 0x20 // Y-axis gyro FIFO enable +#define MPU9150_FIFO_EN_ZG 0x10 // Z-axis gyro FIFO enable +#define MPU9150_FIFO_EN_ACCEL 0x08 // Accelerometer FIFO enable +#define MPU9150_FIFO_EN_SLV2 0x04 // Slave 2 FIFO enable +#define MPU9150_FIFO_EN_SLV1 0x02 // Slave 1 FIFO enable +#define MPU9150_FIFO_EN_SLV0 0x01 // Slave 0 FIFO enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_MST_CTRL +// register. +// +//***************************************************************************** +#define MPU9150_I2C_MST_CTRL_MULT_MST_EN \ + 0x80 // Multi-master enable +#define MPU9150_I2C_MST_CTRL_WAIT_FOR_ES \ + 0x40 // Wait for external sensor data +#define MPU9150_I2C_MST_CTRL_SLV3_FIFO_EN \ + 0x20 // Slave 3 FIFO enable +#define MPU9150_I2C_MST_CTRL_I2C_MST_P_NSR \ + 0x10 // No repeated start conditions +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_M \ + 0x0F // I2C master clock speed +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_348 \ + 0x00 // 348 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_333 \ + 0x01 // 333 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_320 \ + 0x02 // 320 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_308 \ + 0x03 // 308 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_296 \ + 0x04 // 296 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_286 \ + 0x05 // 286 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_276 \ + 0x06 // 276 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_267 \ + 0x07 // 267 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_258 \ + 0x08 // 258 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_500 \ + 0x09 // 500 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_471 \ + 0x0A // 471 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_444 \ + 0x0B // 444 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_421 \ + 0x0C // 421 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_400 \ + 0x0D // 400 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_381 \ + 0x0E // 381 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_364 \ + 0x0F // 364 kHz I2C master clock +#define MPU9150_I2C_MST_CTRL_I2C_MST_CLK_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV0_ADDR +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV0_ADDR_RW \ + 0x80 // Read/not write +#define MPU9150_I2C_SLV0_ADDR_M 0x7F // Slave address +#define MPU9150_I2C_SLV0_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV0_REG +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV0_REG_M 0xFF // Slave register number +#define MPU9150_I2C_SLV0_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV0_CTRL +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV0_CTRL_EN \ + 0x80 // Enable slave +#define MPU9150_I2C_SLV0_CTRL_BYTE_SW \ + 0x40 // Byte-swap word pairs +#define MPU9150_I2C_SLV0_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU9150_I2C_SLV0_CTRL_GRP \ + 0x10 // Word pair grouping +#define MPU9150_I2C_SLV0_CTRL_LEN_M \ + 0x0F // Number of bytes to transfer +#define MPU9150_I2C_SLV0_CTRL_LEN_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV1_ADDR +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV1_ADDR_RW \ + 0x80 // Read/not write +#define MPU9150_I2C_SLV1_ADDR_M 0x7F // Slave address +#define MPU9150_I2C_SLV1_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV1_REG +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV1_REG_M 0xFF // Slave register number +#define MPU9150_I2C_SLV1_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV1_CTRL +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV1_CTRL_EN \ + 0x80 // Enable slave +#define MPU9150_I2C_SLV1_CTRL_BYTE_SW \ + 0x40 // Byte-swap word pairs +#define MPU9150_I2C_SLV1_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU9150_I2C_SLV1_CTRL_GRP \ + 0x10 // Word pair grouping +#define MPU9150_I2C_SLV1_CTRL_LEN_M \ + 0x0F // Number of bytes to transfer +#define MPU9150_I2C_SLV1_CTRL_LEN_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV2_ADDR +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV2_ADDR_RW \ + 0x80 // Read/not write +#define MPU9150_I2C_SLV2_ADDR_M 0x7F // Slave address +#define MPU9150_I2C_SLV2_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV2_REG +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV2_REG_M 0xFF // Slave register number +#define MPU9150_I2C_SLV2_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV2_CTRL +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV2_CTRL_EN \ + 0x80 // Enable slave +#define MPU9150_I2C_SLV2_CTRL_BYTE_SW \ + 0x40 // Byte-swap word pairs +#define MPU9150_I2C_SLV2_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU9150_I2C_SLV2_CTRL_GRP \ + 0x10 // Word pair grouping +#define MPU9150_I2C_SLV2_CTRL_LEN_M \ + 0x0F // Number of bytes to transfer +#define MPU9150_I2C_SLV2_CTRL_LEN_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV3_ADDR +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV3_ADDR_RW \ + 0x80 // Read/not write +#define MPU9150_I2C_SLV3_ADDR_M 0x7F // Slave address +#define MPU9150_I2C_SLV3_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV3_REG +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV3_REG_M 0xFF // Slave register number +#define MPU9150_I2C_SLV3_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV3_CTRL +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV3_CTRL_EN \ + 0x80 // Enable slave +#define MPU9150_I2C_SLV3_CTRL_BYTE_SW \ + 0x40 // Byte-swap word pairs +#define MPU9150_I2C_SLV3_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU9150_I2C_SLV3_CTRL_GRP \ + 0x10 // Word pair grouping +#define MPU9150_I2C_SLV3_CTRL_LEN_M \ + 0x0F // Number of bytes to transfer +#define MPU9150_I2C_SLV3_CTRL_LEN_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV4_ADDR +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV4_ADDR_RW \ + 0x80 // Read/not write +#define MPU9150_I2C_SLV4_ADDR_M 0x7F // Slave address +#define MPU9150_I2C_SLV4_ADDR_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV4_REG +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV4_REG_M 0xFF // Slave register number +#define MPU9150_I2C_SLV4_REG_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV4_CTRL +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV4_CTRL_EN \ + 0x80 // Enable slave +#define MPU9150_I2C_SLV4_CTRL_INT_EN \ + 0x40 // Interrupt enable +#define MPU9150_I2C_SLV4_CTRL_REG_DIS \ + 0x20 // Disable register number transfer +#define MPU9150_I2C_SLV4_CTRL_I2C_MST_DLY_M \ + 0x1F // Slave access delay +#define MPU9150_I2C_SLV4_CTRL_I2C_MST_DLY_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV4_DI +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV4_DI_M 0xFF // Input data +#define MPU9150_I2C_SLV4_DI_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_MST_STATUS +// register. +// +//***************************************************************************** +#define MPU9150_I2C_MST_STATUS_PASS_THROUGH \ + 0x80 // Pass through FSYNC interrupt + // status +#define MPU9150_I2C_MST_STATUS_I2C_SLV4_DONE \ + 0x40 // I2C slave 4 completion status +#define MPU9150_I2C_MST_STATUS_I2C_LOST_ARB \ + 0x20 // I2C arbitration lost status +#define MPU9150_I2C_MST_STATUS_I2C_SLV4_NACK \ + 0x10 // I2C slave 4 NACK status +#define MPU9150_I2C_MST_STATUS_I2C_SLV3_NACK \ + 0x08 // I2C slave 3 NACK status +#define MPU9150_I2C_MST_STATUS_I2C_SLV2_NACK \ + 0x04 // I2C slave 2 NACK status +#define MPU9150_I2C_MST_STATUS_I2C_SLV1_NACK \ + 0x02 // I2C slave 1 NACK status +#define MPU9150_I2C_MST_STATUS_I2C_SLV0_NACK \ + 0x01 // I2C slave 0 NACK status + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_INT_PIN_CFG +// register. +// +//***************************************************************************** +#define MPU9150_INT_PIN_CFG_INT_LEVEL \ + 0x80 // INT pin active low +#define MPU9150_INT_PIN_CFG_INT_OPEN \ + 0x40 // INT pin open-drain +#define MPU9150_INT_PIN_CFG_LATCH_INT_EN \ + 0x20 // Latch INT pin output +#define MPU9150_INT_PIN_CFG_INT_RD_CLEAR \ + 0x10 // Interrupt clear on any read +#define MPU9150_INT_PIN_CFG_FSYNC_INT_LEVEL \ + 0x08 // FSYNC pin active low +#define MPU9150_INT_PIN_CFG_FSYNC_INT_EN \ + 0x04 // FSYNC pin interrupt enable +#define MPU9150_INT_PIN_CFG_I2C_BYPASS_EN \ + 0x02 // I2C bypass enable +#define MPU9150_INT_PIN_CFG_CLKOUT_EN \ + 0x01 // CLKOUT enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_INT_ENABLE +// register. +// +//***************************************************************************** +#define MPU9150_INT_ENABLE_FF_EN \ + 0x80 // Free-fall interrupt enable +#define MPU9150_INT_ENABLE_MOT_EN \ + 0x40 // Motion detection interrupt + // enable +#define MPU9150_INT_ENABLE_ZMOT_EN \ + 0x20 // Zero motion interrupt enable +#define MPU9150_INT_ENABLE_FIFO_OFLOW_EN \ + 0x10 // FIFO overflow interrupt enable +#define MPU9150_INT_ENABLE_I2C_MST_INT_EN \ + 0x08 // I2C master interrupt enable +#define MPU9150_INT_ENABLE_DATA_RDY_EN \ + 0x01 // Data ready interrupt enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_INT_STATUS +// register. +// +//***************************************************************************** +#define MPU9150_INT_STATUS_FF_INT \ + 0x80 // Free-fall interrupt status +#define MPU9150_INT_STATUS_MOT_INT \ + 0x40 // Motion detection interrupt + // status +#define MPU9150_INT_STATUS_ZMOT_INT \ + 0x20 // Zero motion interrupt status +#define MPU9150_INT_STATUS_FIFO_OFLOW_INT \ + 0x10 // FIFO overflow interrupt status +#define MPU9150_INT_STATUS_I2C_MST_INT \ + 0x08 // I2C master interrupt status +#define MPU9150_INT_STATUS_DATA_RDY_INT \ + 0x01 // Data ready interrupt status + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_ACCEL_XOUT_H +// register. +// +//***************************************************************************** +#define MPU9150_ACCEL_XOUT_H_M 0xFF // Bits [15:8] of X-axis + // acceleration data +#define MPU9150_ACCEL_XOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_ACCEL_XOUT_L +// register. +// +//***************************************************************************** +#define MPU9150_ACCEL_XOUT_L_M 0xFF // Bits [7:0] of X-axis + // acceleration data +#define MPU9150_ACCEL_XOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_ACCEL_YOUT_H +// register. +// +//***************************************************************************** +#define MPU9150_ACCEL_YOUT_H_M 0xFF // Bits [15:8] of Y-axis + // acceleration data +#define MPU9150_ACCEL_YOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_ACCEL_YOUT_L +// register. +// +//***************************************************************************** +#define MPU9150_ACCEL_YOUT_L_M 0xFF // Bits [7:0] of Y-axis + // acceleration data +#define MPU9150_ACCEL_YOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_ACCEL_ZOUT_H +// register. +// +//***************************************************************************** +#define MPU9150_ACCEL_ZOUT_H_M 0xFF // Bits [15:8] of Z-axis + // acceleration data +#define MPU9150_ACCEL_ZOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_ACCEL_ZOUT_L +// register. +// +//***************************************************************************** +#define MPU9150_ACCEL_ZOUT_L_M 0xFF // Bits [7:0] of Z-axis + // acceleration data +#define MPU9150_ACCEL_ZOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_TEMP_OUT_H +// register. +// +//***************************************************************************** +#define MPU9150_ACCEL_TEMP_OUT_H_M \ + 0xFF // Bits [15:8] of temperature data +#define MPU9150_ACCEL_TEMP_OUT_H_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_TEMP_OUT_L +// register. +// +//***************************************************************************** +#define MPU9150_ACCEL_TEMP_OUT_L_M \ + 0xFF // Bits [7:0] of temperature data +#define MPU9150_ACCEL_TEMP_OUT_L_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_GYRO_XOUT_H +// register. +// +//***************************************************************************** +#define MPU9150_GYRO_XOUT_H_M 0xFF // Bits [15:8] of X-axis gyro data +#define MPU9150_GYRO_XOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_GYRO_XOUT_L +// register. +// +//***************************************************************************** +#define MPU9150_GYRO_XOUT_L_M 0xFF // Bits [7:0] of X-axis gyro data +#define MPU9150_GYRO_XOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_GYRO_YOUT_H +// register. +// +//***************************************************************************** +#define MPU9150_GYRO_YOUT_H_M 0xFF // Bits [15:8] of Y-axis gyro data +#define MPU9150_GYRO_YOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_GYRO_YOUT_L +// register. +// +//***************************************************************************** +#define MPU9150_GYRO_YOUT_L_M 0xFF // Bits [7:0] of Y-axis gyro data +#define MPU9150_GYRO_YOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_GYRO_ZOUT_H +// register. +// +//***************************************************************************** +#define MPU9150_GYRO_ZOUT_H_M 0xFF // Bits [15:8] of Z-axis gyro data +#define MPU9150_GYRO_ZOUT_H_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_GYRO_ZOUT_L +// register. +// +//***************************************************************************** +#define MPU9150_GYRO_ZOUT_L_M 0xFF // Bits [7:0] of Z-axis gyro data +#define MPU9150_GYRO_ZOUT_L_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_00 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_00_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_00_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_01 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_01_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_01_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_02 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_02_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_02_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_03 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_03_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_03_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_04 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_04_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_04_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_05 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_05_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_05_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_06 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_06_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_06_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_07 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_07_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_07_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_08 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_08_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_08_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_09 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_09_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_09_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_10 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_10_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_10_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_11 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_11_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_11_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_12 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_12_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_12_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_13 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_13_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_13_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_14 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_14_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_14_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_15 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_15_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_15_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_16 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_16_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_16_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_17 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_17_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_17_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_18 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_18_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_18_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_19 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_19_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_19_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_20 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_20_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_20_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_21 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_21_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_21_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_22 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_22_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_22_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_EXT_SENS_DATA_23 register. +// +//***************************************************************************** +#define MPU9150_EXT_SENS_DATA_23_M \ + 0xFF // External sensor data +#define MPU9150_EXT_SENS_DATA_23_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_MOT_DETECT_STATUS register. +// +//***************************************************************************** +#define MPU9150_MOT_DETECT_STATUS_MOT_XNEG \ + 0x80 // Negative X-axis motion detect + // status +#define MPU9150_MOT_DETECT_STATUS_MOT_XPOS \ + 0x40 // Positive X-axis motion detect + // status +#define MPU9150_MOT_DETECT_STATUS_MOT_YNEG \ + 0x20 // Negative Y-axis motion detect + // status +#define MPU9150_MOT_DETECT_STATUS_MOT_YPOS \ + 0x10 // Positive Y-axis motion detect + // status +#define MPU9150_MOT_DETECT_STATUS_MOT_ZNEG \ + 0x08 // Negative Z-axis motion detect + // status +#define MPU9150_MOT_DETECT_STATUS_MOT_ZPOS \ + 0x04 // Positive Z-axis motion detect + // status +#define MPU9150_MOT_DETECT_STATUS_MOT_ZRMOT \ + 0x01 // Zero motion detect status + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV0_DO +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV0_DO_M 0xFF // Output data +#define MPU9150_I2C_SLV0_DO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV1_DO +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV1_DO_M 0xFF // Output data +#define MPU9150_I2C_SLV1_DO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV2_DO +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV2_DO_M 0xFF // Output data +#define MPU9150_I2C_SLV2_DO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_I2C_SLV3_DO +// register. +// +//***************************************************************************** +#define MPU9150_I2C_SLV3_DO_M 0xFF // Output data +#define MPU9150_I2C_SLV3_DO_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_I2C_MST_DELAY_CTRL register. +// +//***************************************************************************** +#define MPU9150_I2C_MST_DELAY_CTRL_DELAY_ES_SHADOW \ + 0x80 // Delay external sensor data +#define MPU9150_I2C_MST_DELAY_CTRL_I2C_SLV4_DLY_EN \ + 0x10 // I2C slave 4 delay enable +#define MPU9150_I2C_MST_DELAY_CTRL_I2C_SLV3_DLY_EN \ + 0x08 // I2C slave 3 delay enable +#define MPU9150_I2C_MST_DELAY_CTRL_I2C_SLV2_DLY_EN \ + 0x04 // I2C slave 2 delay enable +#define MPU9150_I2C_MST_DELAY_CTRL_I2C_SLV1_DLY_EN \ + 0x02 // I2C slave 1 delay enable +#define MPU9150_I2C_MST_DELAY_CTRL_I2C_SLV0_DLY_EN \ + 0x01 // I2C slave 0 delay enable + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_SIGNAL_PATH_RESET register. +// +//***************************************************************************** +#define MPU9150_SIGNAL_PATH_RESET_GYRO \ + 0x04 // Reset gyro +#define MPU9150_SIGNAL_PATH_RESET_ACCEL \ + 0x02 // Reset accelerometer +#define MPU9150_SIGNAL_PATH_RESET_TEMP \ + 0x01 // Reset temperature sensor + +//***************************************************************************** +// +// The following are defines for the bit fields in the +// MPU9150_O_MOT_DETECT_CTRL register. +// +//***************************************************************************** +#define MPU9150_MOT_DETECT_CTRL_ACCEL_ON_DELAY_M \ + 0x30 // Accelerometer wake-up delay +#define MPU9150_MOT_DETECT_CTRL_ACCEL_ON_DELAY_4MS \ + 0x00 // Delay 4 ms +#define MPU9150_MOT_DETECT_CTRL_ACCEL_ON_DELAY_5MS \ + 0x10 // Delay 5 ms +#define MPU9150_MOT_DETECT_CTRL_ACCEL_ON_DELAY_6MS \ + 0x20 // Delay 6 ms +#define MPU9150_MOT_DETECT_CTRL_ACCEL_ON_DELAY_7MS \ + 0x30 // Delay 7 ms +#define MPU9150_MOT_DETECT_CTRL_FF_COUNT_M \ + 0x0C // Free-fall counter decrement rate +#define MPU9150_MOT_DETECT_CTRL_FF_COUNT_RESET \ + 0x00 // Reset counter +#define MPU9150_MOT_DETECT_CTRL_FF_COUNT_1 \ + 0x04 // Decrement by 1 +#define MPU9150_MOT_DETECT_CTRL_FF_COUNT_2 \ + 0x08 // Decrement by 2 +#define MPU9150_MOT_DETECT_CTRL_FF_COUNT_4 \ + 0x0C // Decrement by 4 +#define MPU9150_MOT_DETECT_CTRL_MOT_COUNT_M \ + 0x03 // Motion detect counter decrement + // rate +#define MPU9150_MOT_DETECT_CTRL_MOT_COUNT_RESET \ + 0x00 // Reset counter +#define MPU9150_MOT_DETECT_CTRL_MOT_COUNT_1 \ + 0x04 // Decrement by 1 +#define MPU9150_MOT_DETECT_CTRL_MOT_COUNT_2 \ + 0x08 // Decrement by 2 +#define MPU9150_MOT_DETECT_CTRL_MOT_COUNT_4 \ + 0x0C // Decrement by 4 +#define MPU9150_MOT_DETECT_CTRL_ACCEL_ON_DELAY_S \ + 4 +#define MPU9150_MOT_DETECT_CTRL_FF_COUNT_S \ + 2 +#define MPU9150_MOT_DETECT_CTRL_MOT_COUNT_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_USER_CTRL +// register. +// +//***************************************************************************** +#define MPU9150_USER_CTRL_FIFO_EN \ + 0x40 // FIFO enable +#define MPU9150_USER_CTRL_I2C_MST_EN \ + 0x20 // I2C master mode enable +#define MPU9150_USER_CTRL_I2C_IF_DIS \ + 0x10 // Write as zero +#define MPU9150_USER_CTRL_FIFO_RESET \ + 0x04 // Reset FIFO buffer +#define MPU9150_USER_CTRL_I2C_MST_RESET \ + 0x02 // Reset I2C master +#define MPU9150_USER_CTRL_SIG_COND_RESET \ + 0x01 // Reset all sensors + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_PWR_MGMT_1 +// register. +// +//***************************************************************************** +#define MPU9150_PWR_MGMT_1_DEVICE_RESET \ + 0x80 // Device reset +#define MPU9150_PWR_MGMT_1_SLEEP \ + 0x40 // Enter sleep mode +#define MPU9150_PWR_MGMT_1_CYCLE \ + 0x20 // Enable automatic sleep +#define MPU9150_PWR_MGMT_1_TEMP_DIS \ + 0x08 // Disable temperature sensor +#define MPU9150_PWR_MGMT_1_CLKSEL_M \ + 0x07 // Clock source select +#define MPU9150_PWR_MGMT_1_CLKSEL_INT \ + 0x00 // Internal 8 MHz oscillator +#define MPU9150_PWR_MGMT_1_CLKSEL_XG \ + 0x01 // PLL with X-axis gyro reference +#define MPU9150_PWR_MGMT_1_CLKSEL_YG \ + 0x02 // PLL with Y-axis gyro reference +#define MPU9150_PWR_MGMT_1_CLKSEL_ZG \ + 0x03 // PLL with Z-axis gyro reference +#define MPU9150_PWR_MGMT_1_CLKSEL_EXT32K \ + 0x04 // PLL with external 32.768 kHz + // reference +#define MPU9150_PWR_MGMT_1_CLKSEL_EXT19M \ + 0x05 // PLL with external 19.2 MHz + // reference +#define MPU9150_PWR_MGMT_1_CLKSEL_STOP \ + 0x07 // Clock disable +#define MPU9150_PWR_MGMT_1_CLKSEL_S \ + 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_PWR_MGMT_2 +// register. +// +//***************************************************************************** +#define MPU9150_PWR_MGMT_2_LP_WAKE_CTRL_M \ + 0xC0 // Wake-up frequency +#define MPU9150_PWR_MGMT_2_LP_WAKE_CTRL_1_25 \ + 0x00 // Wake-up at 1.25 Hz +#define MPU9150_PWR_MGMT_2_LP_WAKE_CTRL_5 \ + 0x40 // Wake-up at 5 Hz +#define MPU9150_PWR_MGMT_2_LP_WAKE_CTRL_20 \ + 0x80 // Wake-up at 20 Hz +#define MPU9150_PWR_MGMT_2_LP_WAKE_CTRL_40 \ + 0xC0 // Wake-up at 40 Hz +#define MPU9150_PWR_MGMT_2_STBY_XA \ + 0x20 // Put X-axis accelerometer into + // standby mode +#define MPU9150_PWR_MGMT_2_STBY_YA \ + 0x10 // Put Y-axis accelerometer into + // standby mode +#define MPU9150_PWR_MGMT_2_STBY_ZA \ + 0x08 // Put Z-axis accelerometer into + // standby mode +#define MPU9150_PWR_MGMT_2_STBY_XG \ + 0x04 // Put X-axis gyro into standby + // mode +#define MPU9150_PWR_MGMT_2_STBY_YG \ + 0x02 // Put Y-axis gyro into standby + // mode +#define MPU9150_PWR_MGMT_2_STBY_ZG \ + 0x01 // Put Z-axis gyro into standby + // mode +#define MPU9150_PWR_MGMT_2_LP_WAKE_CTRL_S \ + 6 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_FIFO_COUNTH +// register. +// +//***************************************************************************** +#define MPU9150_FIFO_COUNTH_M 0x07 // FIFO count [10:8] +#define MPU9150_FIFO_COUNTH_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_FIFO_COUNTL +// register. +// +//***************************************************************************** +#define MPU9150_FIFO_COUNTL_M 0xFF // FIFO count [7:0] +#define MPU9150_FIFO_COUNTL_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_FIFO_R_W +// register. +// +//***************************************************************************** +#define MPU9150_FIFO_R_W_M 0xFF // FIFO data +#define MPU9150_FIFO_R_W_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the MPU9150_O_WHO_AM_I +// register. +// +//***************************************************************************** +#define MPU9150_WHO_AM_I_M 0x7E // I2C address +#define MPU9150_WHO_AM_I_MPU9150 \ + 0x68 // MPU9150 +#define MPU9150_WHO_AM_I_S 1 + +#endif // __SENSORLIB_HW_MPU9150_H__ diff --git a/sensorlib/hw_sht21.h b/sensorlib/hw_sht21.h new file mode 100644 index 0000000..550af75 --- /dev/null +++ b/sensorlib/hw_sht21.h @@ -0,0 +1,76 @@ +//***************************************************************************** +// +// hw_sht21.h - Macros used for accessing the Intersil SHT21 humidity sensor +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_SHT21_H__ +#define __SENSORLIB_HW_SHT21_H__ + +//***************************************************************************** +// +// The following are defines for the SHT21 Commands and Registers +// +//***************************************************************************** +#define SHT21_CMD_MEAS_T_HOLD 0xE3 // Measure temperatue with I2C bus + // hold +#define SHT21_CMD_MEAS_RH_HOLD 0xE5 // Measure humidity with I2C bus + // hold +#define SHT21_CMD_WRITE_CONFIG 0xE6 // Write the user config register +#define SHT21_CMD_READ_CONFIG 0xE7 // Read the user config register +#define SHT21_CMD_MEAS_T 0xF3 // Measure temperature polled +#define SHT21_CMD_MEAS_RH 0xF5 // Measure humidity polled +#define SHT21_CMD_SOFT_RESET 0xFE // Perform a device reset + +//***************************************************************************** +// +// The following are defines for the bit fields in the SHT21_CONFIG register. +// +//***************************************************************************** +#define SHT21_CONFIG_RES_M 0x81 // Resolution config for + // temperature and humidity +#define SHT21_CONFIG_RES_12 0x00 // RH 12 bit, T 14 bit +#define SHT21_CONFIG_RES_8 0x01 // RH 8 bit, T 12 bit +#define SHT21_CONFIG_RES_10 0x80 // RH 10 bit, T 13 bit +#define SHT21_CONFIG_RES_11 0x81 // RH 11 bit, T 11 bit +#define SHT21_CONFIG_BATT_M 0x40 // Battery status indicator +#define SHT21_CONFIG_BATT_GOOD 0x00 +#define SHT21_CONFIG_BATT_LOW 0x40 +#define SHT21_CONFIG_HEATER_M 0x04 // On chip heater for test and + // diagnostics +#define SHT21_CONFIG_HEATER_DISABLE \ + 0x00 // Heater off +#define SHT21_CONFIG_HEATER_ENABLE \ + 0x04 // Heater on +#define SHT21_CONFIG_OTP_RELOAD_M \ + 0x02 // OTP reload control; soft reset + // is preferred +#define SHT21_CONFIG_OTP_RELOAD_ENABLE \ + 0x00 // OTP reload enabled +#define SHT21_CONFIG_OTP_RELOAD_DISABLE \ + 0x02 // OTP reload disabled +#define SHT21_CONFIG_BATT_S 6 +#define SHT21_CONFIG_HEATER_S 2 +#define SHT21_CONFIG_OTP_RELOAD_S \ + 1 +#define SHT21_CONFIG_RES_S 0 + +#endif // __SENSORLIB_HW_SHT21_H__ diff --git a/sensorlib/hw_tmp006.h b/sensorlib/hw_tmp006.h new file mode 100644 index 0000000..6c350c8 --- /dev/null +++ b/sensorlib/hw_tmp006.h @@ -0,0 +1,75 @@ +//***************************************************************************** +// +// hw_tmp006.h - Macros used when accessing the Texas Instruments TMP006 +// Infrared Temperature Sensor +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_TMP006_H__ +#define __SENSORLIB_HW_TMP006_H__ + +//***************************************************************************** +// +// The following are defines for the TMP006 Register Addresses +// +//***************************************************************************** +#define TMP006_O_VOBJECT 0x00 // Raw object voltage measurement +#define TMP006_O_TAMBIENT 0x01 // Die temperature of the TMP006 +#define TMP006_O_CONFIG 0x02 // TMP006 Configuration +#define TMP006_O_MFG_ID 0xFE // TMP006 Manufacture + // Identification +#define TMP006_O_DEV_ID 0xFF // TMP006 Device Identification + +//***************************************************************************** +// +// The following are defines for the bit fields in the TMP006_O_CONFIG +// register. +// +//***************************************************************************** +#define TMP006_CONFIG_RESET_M 0x8000 // TMP006 device reset +#define TMP006_CONFIG_RESET_ASSERT \ + 0x8000 // Reset TMP006; self clearing +#define TMP006_CONFIG_MODE_M 0x7000 // Operation mode +#define TMP006_CONFIG_MODE_PD 0x0000 // Power down +#define TMP006_CONFIG_MODE_CONT 0x7000 // Continuous sampling +#define TMP006_CONFIG_CR_M 0x0E00 // Conversion rate setting +#define TMP006_CONFIG_CR_4 0x0000 // 4Hz conversion rate +#define TMP006_CONFIG_CR_2 0x0200 // 2Hz conversion rate +#define TMP006_CONFIG_CR_1 0x0400 // 1Hz conversion rate +#define TMP006_CONFIG_CR_0_5 0x0600 // 0.5Hz conversion rate +#define TMP006_CONFIG_CR_0_25 0x0800 // 0.25Hz conversion rate +#define TMP006_CONFIG_EN_DRDY_PIN_M \ + 0x0100 // Enable the DRDY output pin +#define TMP006_CONFIG_DIS_DRDY_PIN \ + 0x0000 // DRDY pin disabled +#define TMP006_CONFIG_EN_DRDY_PIN \ + 0x0100 // DRDY pin enabled +#define TMP006_CONFIG_DRDY_M 0x0080 // Data ready flag +#define TMP006_CONFIG_IN_PROG 0x0000 // Conversion in progress +#define TMP006_CONFIG_DRDY 0x0080 // Conversion complete +#define TMP006_CONFIG_RESET_S 15 +#define TMP006_CONFIG_MODE_S 12 +#define TMP006_CONFIG_CR_S 9 +#define TMP006_CONFIG_EN_DRDY_PIN_S \ + 8 +#define TMP006_CONFIG_DRDY_S 7 + +#endif // __SENSORLIB_HW_TMP006_H__ diff --git a/sensorlib/hw_tmp100.h b/sensorlib/hw_tmp100.h new file mode 100644 index 0000000..5debb87 --- /dev/null +++ b/sensorlib/hw_tmp100.h @@ -0,0 +1,93 @@ +//***************************************************************************** +// +// hw_tmp100.h - Macros used when accessing the Texas Instruments TMP100 +// Temperature Sensor +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_HW_TMP100_H__ +#define __SENSORLIB_HW_TMP100_H__ + +//***************************************************************************** +// +// The following are defines for the TMP100 Register Addresses +// +//***************************************************************************** +#define TMP100_O_TEMP 0x00 // Temperature register +#define TMP100_O_CONFIG 0x01 // Configuration register +#define TMP100_O_TEMP_LOW 0x02 // Temperature low register +#define TMP100_O_TEMP_HIGH 0x03 // Temperature high register + +//***************************************************************************** +// +// The following are defines for the bit fields in the TMP100_O_TEMP register. +// +//***************************************************************************** +#define TMP100_TEMP_M 0xFFFF // Temperature data +#define TMP100_TEMP_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the TMP100_O_CONFIG +// register. +// +//***************************************************************************** +#define TMP100_CONFIG_OS_ALERT 0x80 // Starts a one-shot when written, + // reports alert when read +#define TMP100_CONFIG_RES_M 0x60 // Converter resolution +#define TMP100_CONFIG_RES_9BIT 0x00 // 9-bit resolution (40 ms + // conversion time) +#define TMP100_CONFIG_RES_10BIT 0x20 // 10-bit resolution (80 ms + // conversion time) +#define TMP100_CONFIG_RES_11BIT 0x40 // 11-bit resolution (160 ms + // conversion time) +#define TMP100_CONFIG_RES_12BIT 0x60 // 12-bit resolution (320 ms + // conversion time) +#define TMP100_CONFIG_CR_M 0x18 // Consecutive fault configuration +#define TMP100_CONFIG_FAULT_1 0x00 // 1 consecutive fault +#define TMP100_CONFIG_FAULT_2 0x08 // 2 consecutive faults +#define TMP100_CONFIG_FAULT_4 0x10 // 4 consecutive faults +#define TMP100_CONFIG_FAULT_6 0x18 // 6 consecutive faults +#define TMP100_CONFIG_POL 0x04 // Alert pin polarity +#define TMP100_CONFIG_TM 0x02 // Thermostat mode +#define TMP100_CONFIG_SD 0x01 // Shutdown mode +#define TMP100_CONFIG_RES_S 5 +#define TMP100_CONFIG_FAULT_S 3 + +//***************************************************************************** +// +// The following are defines for the bit fields in the TMP100_O_TEMP_LOW +// register. +// +//***************************************************************************** +#define TMP100_TEMP_LOW_M 0xFFFF // Temperature low data +#define TMP100_TEMP_LOW_S 0 + +//***************************************************************************** +// +// The following are defines for the bit fields in the TMP100_O_TEMP_HIGH +// register. +// +//***************************************************************************** +#define TMP100_TEMP_HIGH_M 0xFFFF // Temperature high data +#define TMP100_TEMP_HIGH_S 0 + +#endif // __SENSORLIB_HW_TMP100_H__ diff --git a/sensorlib/i2cm_drv.c b/sensorlib/i2cm_drv.c new file mode 100644 index 0000000..1e603e2 --- /dev/null +++ b/sensorlib/i2cm_drv.c @@ -0,0 +1,2256 @@ +//***************************************************************************** +// +// i2cm_drv.c - Interrupt-driven I2C master driver. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_i2c.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/i2c.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "sensorlib/i2cm_drv.h" + +//***************************************************************************** +// +//! \addtogroup i2cm_drv_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states in the interrupt handler state machine. +// +//***************************************************************************** +#define STATE_IDLE 0 +#define STATE_WRITE_NEXT 1 +#define STATE_WRITE_FINAL 2 +#define STATE_WRITE_PAUSE 3 +#define STATE_READ_ONE 4 +#define STATE_READ_FIRST 5 +#define STATE_READ_NEXT 6 +#define STATE_READ_FINAL 7 +#define STATE_READ_PAUSE 8 +#define STATE_READ_WAIT 9 +#define STATE_CALLBACK 10 + +//***************************************************************************** +// +// The states in the I2C read-modify-write state machine. +// +//***************************************************************************** +#define I2CM_RMW_STATE_IDLE 0 +#define I2CM_RMW_STATE_READ 1 +#define I2CM_RMW_STATE_WRITE 2 + +//***************************************************************************** +// +//! Writes data to an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8Data is a pointer to the data buffer to be written. +//! \param ui16Count is the number of bytes to be written. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C write to the queue of commands to be sent. If +//! successful, the I2C write is then performed in the background using the +//! interrupt handler. When the write is complete, the callback function, if +//! provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of the data buffer contains the I2C address of the device to +//! access, and the remaining \e ui16Count bytes contain the data to be written +//! to the device. The \e ui16Count parameter can be zero if there are no +//! bytes to be written. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMWrite(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +//! Reads data from an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8WriteData is a pointer to the data buffer to be written. +//! \param ui16WriteCount is the number of bytes to be written. +//! \param pui8ReadData is a pointer to the buffer to be filled with the read +//! data. +//! \param ui16ReadCount is the number of bytes to be read. +//! \param pfnCallback is the function to be called when the transfer has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C read to the queue of commands to be sent. If +//! successful, the I2C read is then performed in the background using the +//! interrupt handler. When the read is complete, the callback function, if +//! provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of \e pui8WriteData contains the I2C address of the device +//! to access, the next \e ui16WriteCount bytes contains the data to be written +//! to the device. The data read from the device is written into the first +//! \e ui16ReadCount bytes of \e pui8ReadData. The \e ui16WriteCount or +//! \e ui16ReadCount parameters can be zero if there are no bytes to be read or +//! written. The write bytes are sent to the device first, and then the read +//! bytes are read from the device afterward. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMRead(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8WriteData, + uint_fast16_t ui16WriteCount, + uint8_t *pui8ReadData, + uint_fast16_t ui16ReadCount, + tSensorCallback pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +//! Writes data in batches to an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8Data is a pointer to the data buffer to be written. +//! \param ui16Count is the number of bytes to be written. +//! \param ui16BatchSize is the number of bytes in each write batch. +//! \param pfnCallback is the function to be called when the transfer has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C write to the queue of commands to be sent. If +//! successful, the I2C write is then performed in the background using the +//! interrupt handler. When the write is complete, the callback function, if +//! provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of the data buffer contains the I2C address of the device to +//! access, and the remaining \e ui16Count bytes contain the data to be written +//! to the device. The \e ui16Count parameter can be zero if there are no +//! bytes to be written. +//! +//! The data is written in batches of \e ui16WriteBatchSize. The callback +//! function is called after each batch is written, and I2CMTransferResume() +//! must be called when the next batch should be written. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMWriteBatched(tI2CMInstance *psInst, + uint_fast8_t ui8Addr, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + uint_fast16_t ui16BatchSize, + tSensorCallback pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +//! Reads data in batches from an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8WriteData is a pointer to the data buffer to be written. +//! \param ui16WriteCount is the number of bytes to be written. +//! \param ui16WriteBatchSize is the number of bytes in each write batch. +//! \param pui8ReadData is a pointer to the buffer to be filled with the read +//! data. +//! \param ui16ReadCount is the number of bytes to be read. +//! \param ui16ReadBatchSize is the number of bytes in each read batch. +//! \param pfnCallback is the function to be called when the transfer has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C read to the queue of commands to be sent. If +//! successful, the I2C read is then performed in the background using the +//! interrupt handler. When the read is complete, the callback function, if +//! provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of \e pui8WriteData contains the I2C address of the device +//! to access, the next \e ui16WriteCount bytes contains the data to be written +//! to the device. The data read from the device is written into the first +//! \e ui16ReadCount bytes of \e pui8ReadData. The \e ui16WriteCount or +//! \e ui16ReadCount parameters can be zero if there are no bytes to be read or +//! written. The write bytes are sent to the device first, and then the read +//! bytes are read from the device afterward. +//! +//! The data is written in batches of \e ui16WriteBatchSize. The callback +//! function is called after each batch is written, and I2CMTransferResume() +//! must be called when the next batch should be written. +//! +//! The data is read in batches of \e ui16ReadBatchSize. The callback function +//! is called after each batch is read, and I2CMTransferResume() must be called +//! when the next batch should be read. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMReadBatched(tI2CMInstance *psInst, + uint_fast8_t ui8Addr, + const uint8_t *pui8WriteData, + uint_fast16_t ui16WriteCount, + uint_fast16_t ui16WriteBatchSize, + uint8_t *pui8ReadData, + uint_fast16_t ui16ReadCount, + uint_fast16_t ui16ReadBatchSize, + tSensorCallback pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +//! Performs a read-modify-write of 16 bits of big-endian data in an I2C +//! device. +//! +//! \param psInst is a pointer to the read-modify-write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param ui16Mask is the mask indicating the register bits that should be +//! maintained. +//! \param ui16Value is the value indicating the new value for the register +//! bits that are not maintained. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read-modify-write transaction of 16 bits of +//! big-endian data in an I2C device. The modify portion of the operation is +//! performed by AND-ing the register value with \e ui16Mask and then OR-ing +//! the result with \e ui16Value. When the read-modify-write is complete, the +//! callback function, if provided, is called in the context of the I2C master +//! interrupt handler. +//! +//! If the mask (in \e ui16Mask) is zero, then none of the bits in the current +//! register value are maintained. In this case, the read portion of the +//! read-modify-write is bypassed, and the new register value (in \e ui16Value) +//! is directly written to the I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMReadModifyWrite16BE(tI2CMReadModifyWrite16 *psInst, + tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, + uint_fast8_t ui8Reg, + uint_fast16_t ui16Mask, + uint_fast16_t ui16Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +// This function handles the idle state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateIdle(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Do nothing if there is not another transfer in the queue. + // + if(psInst->ui8ReadPtr == psInst->ui8WritePtr) + { + return; + } + + // + // See if there is any data to be written. + // + if(pCommand->ui16WriteCount != 0) + { + // + // Set the slave address and indicate a write. + // + MAP_I2CMasterSlaveAddrSet(psInst->ui32Base, pCommand->ui8Addr, false); + + // + // Place the first data byte to be written in the data register. + // + MAP_I2CMasterDataPut(psInst->ui32Base, pCommand->pui8WriteData[0]); + + // + // See if there is just a single byte to be written and no bytes to be + // read. + // + if((pCommand->ui16WriteCount == 1) && (pCommand->ui16ReadCount == 0)) + { + // + // Perform a single byte send. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_SINGLE_SEND); + + // + // The next state is the callback state. + // + psInst->ui8State = STATE_CALLBACK; + } + + // + // Otherwise, see if there is just a single byte to be written and at + // least one byte to be read. + // + else if(pCommand->ui16WriteCount == 1) + { + // + // Perform a single send, writing the first byte as the only byte. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the next state of the interrupt state machine based on the + // number of bytes to read. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 1) ? + STATE_READ_ONE : STATE_READ_FIRST); + } + + // + // Otherwise, there is more than one byte to be written. + // + else + { + // + // Start the burst cycle, writing the first byte. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the index to indicate that the first byte has been + // transmitted. + // + psInst->ui16Index = 1; + + // + // Set the next state of the interrupt state machine based on the + // number of bytes to write. + // + psInst->ui8State = ((pCommand->ui16WriteCount != 2) ? + STATE_WRITE_NEXT : STATE_WRITE_FINAL); + } + } + else + { + // + // Set the slave address and indicate a read. + // + MAP_I2CMasterSlaveAddrSet(psInst->ui32Base, pCommand->ui8Addr, true); + + // + // Set the index to indicate that the first byte is being read. + // + psInst->ui16Index = 0; + + // + // See if there is just a single byte to be read. + // + if(pCommand->ui16ReadCount == 1) + { + // + // Perform a single byte read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_SINGLE_RECEIVE); + + // + // The next state is the wait for final read state. + // + psInst->ui8State = STATE_READ_WAIT; + } + else + { + // + // Start the burst receive. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_START); + + // + // Set the next state appropriately. If the read count is two, the + // next state must finish the transaction. If it is greater than + // two, the burst read must be continued. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 2) ? + STATE_READ_FINAL : STATE_READ_NEXT); + } + } +} + +//***************************************************************************** +// +// This function handles the write next state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateWriteNext(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // See if the write batch has been sent. + // + if(psInst->ui16Index == pCommand->ui16WriteBatchSize) + { + // + // Move to the write pause state. + // + psInst->ui8State = STATE_WRITE_PAUSE; + + // + // Call the callback function. + // + if(pCommand->pfnCallback) + { + pCommand->pfnCallback(pCommand->pvCallbackData, + I2CM_STATUS_BATCH_DONE); + } + } + else + { + // + // Write the next byte to the data register. + // + MAP_I2CMasterDataPut(psInst->ui32Base, + pCommand->pui8WriteData[psInst->ui16Index]); + psInst->ui16Index++; + + // + // Continue the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // If there is one byte left, set the next state to the final write + // state. + // + if((pCommand->ui16WriteCount - psInst->ui16Index) == 1) + { + psInst->ui8State = STATE_WRITE_FINAL; + } + } +} + +//***************************************************************************** +// +// This function handles the write final state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateWriteFinal(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // See if the write batch has been sent. + // + if(psInst->ui16Index == pCommand->ui16WriteBatchSize) + { + // + // Move to the write pause state. + // + psInst->ui8State = STATE_WRITE_PAUSE; + + // + // Call the callback function. + // + if(pCommand->pfnCallback) + { + pCommand->pfnCallback(pCommand->pvCallbackData, + I2CM_STATUS_BATCH_DONE); + } + } + else + { + // + // Write the final byte to the data register. + // + MAP_I2CMasterDataPut(psInst->ui32Base, + pCommand->pui8WriteData[psInst->ui16Index]); + + // + // See if there is data to be read after this byte is written. + // + if(pCommand->ui16ReadCount == 0) + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_FINISH); + + // + // The next state is the callback state. + // + psInst->ui8State = STATE_CALLBACK; + } + else + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // Set the next state of the interrupt state machine based on the + // number of bytes to read. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 1) ? + STATE_READ_ONE : STATE_READ_FIRST); + } + } +} + +//***************************************************************************** +// +// This function handles the write pause state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateWritePause(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Decrement the write count by the batch size. + // + pCommand->ui16WriteCount -= pCommand->ui16WriteBatchSize; + + // + // Write the next byte to the data register. + // + MAP_I2CMasterDataPut(psInst->ui32Base, pCommand->pui8WriteData[0]); + + // + // Set the index to indicate that the first byte has been transmitted. + // + psInst->ui16Index = 1; + + // + // See if there is more than one byte left to be written. + // + if((pCommand->ui16WriteCount - psInst->ui16Index) == 0) + { + // + // See if there is data to be read after this byte is written. + // + if(pCommand->ui16ReadCount == 0) + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_FINISH); + + // + // The next state is the callback state. + // + psInst->ui8State = STATE_CALLBACK; + } + else + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // Set the next state of the interrupt state machine based on the + // number of bytes to read. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 1) ? + STATE_READ_ONE : STATE_READ_FIRST); + } + } + else + { + // + // Continue the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // The next state is the write next state. + // + if((pCommand->ui16WriteCount - psInst->ui16Index) == 1) + { + psInst->ui8State = STATE_WRITE_FINAL; + } + else + { + psInst->ui8State = STATE_WRITE_NEXT; + } + } +} + +//***************************************************************************** +// +// This function handles the read one state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadOne(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Put the I2C master into receive mode. + // + MAP_I2CMasterSlaveAddrSet(psInst->ui32Base, pCommand->ui8Addr, true); + + // + // Perform a single byte read. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_SINGLE_RECEIVE); + + // + // Set the index to indicate that the first byte is being read. + // + psInst->ui16Index = 0; + + // + // The next state is the wait for final read state. + // + psInst->ui8State = STATE_READ_WAIT; +} + +//***************************************************************************** +// +// This function handles the read first state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadFirst(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Put the I2C master into receive mode. + // + MAP_I2CMasterSlaveAddrSet(psInst->ui32Base, pCommand->ui8Addr, true); + + // + // Start the burst receive. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_BURST_RECEIVE_START); + + // + // Set the index to indicate that the first byte is being read. + // + psInst->ui16Index = 0; + + // + // Set the next state appropriately. If the count is greater than two it + // is the middle of the burst read. If exactly two, the next state must + // finish the transaction. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 2) ? + STATE_READ_FINAL : STATE_READ_NEXT); +} + +//***************************************************************************** +// +// This function handles the read next state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadNext(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Read the received character. + // + pCommand->pui8ReadData[psInst->ui16Index] = + MAP_I2CMasterDataGet(psInst->ui32Base); + psInst->ui16Index++; + + // + // See if the read batch has been filled. + // + if(psInst->ui16Index == pCommand->ui16ReadBatchSize) + { + // + // Move to the read pause state. + // + psInst->ui8State = STATE_READ_PAUSE; + + // + // Call the callback function. + // + if(pCommand->pfnCallback) + { + pCommand->pfnCallback(pCommand->pvCallbackData, + I2CM_STATUS_BATCH_READY); + } + } + else + { + // + // Continue the burst read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_CONT); + + // + // If there are two characters left to be read, make the next state be + // the end of burst read state. + // + if((pCommand->ui16ReadCount - psInst->ui16Index) == 2) + { + psInst->ui8State = STATE_READ_FINAL; + } + } +} + +//***************************************************************************** +// +// This function handles the read final state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadFinal(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Read the received character. + // + pCommand->pui8ReadData[psInst->ui16Index] = + MAP_I2CMasterDataGet(psInst->ui32Base); + psInst->ui16Index++; + + // + // See if the read batch has been filled. + // + if(psInst->ui16Index == pCommand->ui16ReadBatchSize) + { + // + // Move to the read pause state. + // + psInst->ui8State = STATE_READ_PAUSE; + + // + // Call the callback function. + // + if(pCommand->pfnCallback) + { + pCommand->pfnCallback(pCommand->pvCallbackData, + I2CM_STATUS_BATCH_READY); + } + } + else + { + // + // Finish the burst read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // The next state is the wait for final read state. + // + psInst->ui8State = STATE_READ_WAIT; + } +} + +//***************************************************************************** +// +// This function handles the read pause state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadPause(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Decrement the read count by the batch size. + // + pCommand->ui16ReadCount -= pCommand->ui16ReadBatchSize; + + // + // Reset the read index. + // + psInst->ui16Index = 0; + + // + // See if there is more than one byte left to be read. + // + if((pCommand->ui16ReadCount - psInst->ui16Index) == 1) + { + // + // Finish the burst read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // The next state is the wait for final read state. + // + psInst->ui8State = STATE_READ_WAIT; + } + else + { + // + // Continue the burst read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_CONT); + + // + // Determine the next state based on the number of bytes left to read. + // + if((pCommand->ui16ReadCount - psInst->ui16Index) == 2) + { + psInst->ui8State = STATE_READ_FINAL; + } + else + { + psInst->ui8State = STATE_READ_NEXT; + } + } +} + +//***************************************************************************** +// +// This function handles the read wait state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadWait(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Read the received character. + // + pCommand->pui8ReadData[psInst->ui16Index] = + MAP_I2CMasterDataGet(psInst->ui32Base); + + // + // The state machine is now in the callback state. + // + psInst->ui8State = STATE_CALLBACK; +} + +//***************************************************************************** +// +// This function handles the callback state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateCallback(tI2CMInstance *psInst, tI2CMCommand *pCommand, + uint32_t ui32Status) +{ + tSensorCallback *pfnCallback; + void *pvCallbackData; + + // + // Save the callback information. + // + pfnCallback = pCommand->pfnCallback; + pvCallbackData = pCommand->pvCallbackData; + + // + // This command has been completed, so increment the read pointer. + // + psInst->ui8ReadPtr++; + if(psInst->ui8ReadPtr == NUM_I2CM_COMMANDS) + { + psInst->ui8ReadPtr = 0; + } + + // + // If there is a callback function then call it now. + // + if(pfnCallback) + { + // + // Convert the status from the I2C driver into the I2C master + // driver status. + // + if((ui32Status & (I2C_MCS_ARBLST | I2C_MCS_ERROR)) == 0) + { + ui32Status = I2CM_STATUS_SUCCESS; + } + else if(ui32Status & I2C_MCS_ARBLST) + { + ui32Status = I2CM_STATUS_ARB_LOST; + } + else if(ui32Status & I2C_MCS_ADRACK) + { + ui32Status = I2CM_STATUS_ADDR_NACK; + } + else if(ui32Status & I2C_MCS_DATACK) + { + ui32Status = I2CM_STATUS_DATA_NACK; + } + else + { + ui32Status = I2CM_STATUS_ERROR; + } + + // + // Call the callback function. + // + pfnCallback(pvCallbackData, ui32Status); + } + + // + // The state machine is now idle. + // + psInst->ui8State = STATE_IDLE; +} + +//***************************************************************************** +// +//! Handles I2C master interrupts. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! +//! This function performs the processing required in response to an I2C +//! interrupt. The application-supplied interrupt handler should call this +//! function with the correct instance data in response to the I2C interrupt. +//! +//! \return None. +// +//***************************************************************************** +void +I2CMIntHandler(tI2CMInstance *psInst) +{ + tI2CMCommand *pCommand; + uint32_t ui32Status; + + // + // Clear the I2C interrupt. + // + MAP_I2CMasterIntClear(psInst->ui32Base); + ui32Status = HWREG(psInst->ui32Base + I2C_O_MCS); + + // + // Get a pointer to the current command. + // + pCommand = &(psInst->pCommands[psInst->ui8ReadPtr]); + + // + // See if an error occurred during the last transaction. + // + if((ui32Status & (I2C_MCS_ERROR | I2C_MCS_ARBLST)) && + (psInst->ui8State != STATE_IDLE)) + { + // + // An error occurred, so halt the I2C transaction. The error stop + // command for send and receive is identical, so it does not matter + // which one is used here. Only issue the stop if the bus is busy. + // + if(ui32Status & I2C_MCS_BUSBSY) + { + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_ERROR_STOP); + } + + // + // Move to the callback state. + // + psInst->ui8State = STATE_CALLBACK; + } + + // + // Loop forever. Most states will return when they have completed their + // action. However, a few states require multi-state processing, so those + // states will break and this loop repeated. + // + while(1) + { + // + // Determine what to do based on the current state. + // + switch(psInst->ui8State) + { + // + // The idle state. + // + case STATE_IDLE: + { + // + // Handle the idle state. + // + I2CMStateIdle(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the middle of a burst write. + // + case STATE_WRITE_NEXT: + { + // + // Handle the write next state. + // + I2CMStateWriteNext(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the final write of a burst sequence. + // + case STATE_WRITE_FINAL: + { + // + // Handle the write final state. + // + I2CMStateWriteFinal(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for a paused write. + // + case STATE_WRITE_PAUSE: + { + // + // Handle the write pause state. + // + I2CMStateWritePause(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for a single byte read. + // + case STATE_READ_ONE: + { + // + // Handle the read one state. + // + I2CMStateReadOne(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the start of a burst read. + // + case STATE_READ_FIRST: + { + // + // Handle the read first state. + // + I2CMStateReadFirst(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the middle of a burst read. + // + case STATE_READ_NEXT: + { + // + // Handle the read next state. + // + I2CMStateReadNext(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the end of a burst read. + // + case STATE_READ_FINAL: + { + // + // Handle the read final state. + // + I2CMStateReadFinal(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for a paused read. + // + case STATE_READ_PAUSE: + { + // + // Handle the read pause state. + // + I2CMStateReadPause(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // This state is for the final read of a single or burst read. + // + case STATE_READ_WAIT: + { + // + // Handle the read wait state. + // + I2CMStateReadWait(psInst, pCommand); + + // + // This state is done and the next state needs to be handled + // immediately. + // + break; + } + + // + // This state is for providing the transaction complete callback. + // + case STATE_CALLBACK: + { + // + // Handle the callback state. + // + I2CMStateCallback(psInst, pCommand, ui32Status); + + // + // If an error occurred, this state is done. The completion of + // the error handling stop condition above, if issued, will + // cause the next state to be processed. + // + if((ui32Status & (I2C_MCS_ERROR | I2C_MCS_ARBLST)) && + (ui32Status & I2C_MCS_BUSBSY)) + { + return; + } + + // + // Update the pointer to the current command. + // + pCommand = &(psInst->pCommands[psInst->ui8ReadPtr]); + + // + // This state is done and the next state needs to be handled + // immediately. + // + break; + } + } + } +} + +//***************************************************************************** +// +//! Initializes the I2C master driver. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui32Base is the base address of the I2C module. +//! \param ui8Int is the interrupt number for the I2C module. +//! \param ui8TxDMA is the uDMA channel number used for transmitting data to +//! the I2C module. +//! \param ui8RxDMA is the uDMA channel number used for receiving data from +//! the I2C module. +//! \param ui32Clock is the clock frequency of the input clock to the I2C +//! module. +//! +//! This function prepares both the I2C master module and driver for operation, +//! and must be the first I2C master driver function called for each I2C master +//! instance. It is assumed that the application has enabled the I2C module, +//! configured the I2C pins, and provided an I2C interrupt handler that calls +//! I2CMIntHandler(). +//! +//! The uDMA module cannot be used at present to transmit/receive data, so the +//! \e ui8TxDMA and \e ui8RxDMA parameters are unused. They are reserved for +//! future use and should be set to 0xff in order to ensure future +//! compatibility. +//! +//! \return None. +// +//***************************************************************************** +void +I2CMInit(tI2CMInstance *psInst, uint32_t ui32Base, uint_fast8_t ui8Int, + uint_fast8_t ui8TxDMA, uint_fast8_t ui8RxDMA, uint32_t ui32Clock) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT((ui32Base == I2C0_BASE) || (ui32Base == I2C1_BASE) || + (ui32Base == I2C2_BASE) || (ui32Base == I2C3_BASE) || + (ui32Base == I2C4_BASE) || (ui32Base == I2C5_BASE) || + (ui32Base == I2C6_BASE) || (ui32Base == I2C7_BASE) || + (ui32Base == I2C8_BASE) || (ui32Base == I2C9_BASE)); + ASSERT(ui8Int); + ASSERT(ui32Clock); + + // + // Initialize the state structure. + // + psInst->ui32Base = ui32Base; + psInst->ui8Int = ui8Int; + psInst->ui8TxDMA = ui8TxDMA; + psInst->ui8RxDMA = ui8RxDMA; + psInst->ui8State = STATE_IDLE; + psInst->ui8ReadPtr = 0; + psInst->ui8WritePtr = 0; + + // + // Initialize the I2C master module. + // + MAP_I2CMasterInitExpClk(ui32Base, ui32Clock, true); + + // + // Enable the I2C interrupt. + // + MAP_IntEnable(ui8Int); + MAP_I2CMasterIntEnableEx(ui32Base, I2C_MASTER_INT_DATA); +} + +//***************************************************************************** +// +//! Sends a command to an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8WriteData is a pointer to the data buffer to be written. +//! \param ui16WriteCount is the number of bytes to be written. +//! \param ui16WriteBatchSize is the number of bytes in each write batch. +//! \param pui8ReadData is a pointer to the buffer to be filled with the read +//! data. +//! \param ui16ReadCount is the number of bytes to be read. +//! \param ui16ReadBatchSize is the number of bytes to be read in each batch. +//! \param pfnCallback is the function to be called when the transfer has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C command to the queue of commands to be sent. If +//! successful, the I2C command is then transferred in the background using the +//! interrupt handler. When the transfer is complete, the callback function, +//! if provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of \e pui8WriteData contains the I2C address of the device +//! to access, the next \e ui16WriteCount bytes contains the data to be written +//! to the device. The data read from the device is written into the first +//! \e ui16ReadCount bytes of \e pui8ReadData. The \e ui16WriteCount or +//! \e ui16ReadCount parameters can be zero if there are no bytes to be read or +//! written. The write bytes are sent to the device first, and then the read +//! bytes are read from the device afterward. +//! +//! If \e ui16WriteBatchSize is less than \e ui16WriteCount, the write portion +//! of the transfer is broken up into as many \e ui16WriteBatchSize batches as +//! required to write \e ui16WriteCount bytes. After each batch, the callback +//! function is called with an \b I2CM_STATUS_BATCH_DONE status, and the +//! transfer is paused (with the I2C bus held). The transfer is resumed when +//! I2CMTransferResume() is called. This procedure can be used to perform very +//! large writes without requiring all the data be available at once, at the +//! expense of tying up the I2C bus for the extended duration of the transfer. +//! +//! If \e ui16ReadBatchSize is less than \e ui16ReadCount, the read portion of +//! the transfer is broken up into as many \e ui16ReadBatchSize batches as +//! required to read \e ui16ReadCount bytes. After each batch, the callback +//! function is called with an \b I2CM_STATUS_BATCH_READY status, and the +//! transfer is paused (with the I2C bus held). The transfer is resumed when +//! I2CMTransferResume() is called. This procedure can be used to perform very +//! large reads without requiring a large SRAM buffer, at the expense of tying +//! up the I2C bus for the extended duration of the transfer. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMCommand(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8WriteData, uint_fast16_t ui16WriteCount, + uint_fast16_t ui16WriteBatchSize, uint8_t *pui8ReadData, + uint_fast16_t ui16ReadCount, uint_fast16_t ui16ReadBatchSize, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + uint_fast8_t ui8Next, ui8Enabled; + tI2CMCommand *pCommand; + + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(pui8WriteData || !ui16WriteCount); + ASSERT(!ui16WriteCount || (ui16WriteBatchSize > 0)); + ASSERT(pui8ReadData || !ui16ReadCount); + ASSERT(!ui16ReadCount || (ui16ReadBatchSize > 0)); + + // + // Disable the I2C interrupt. + // + if(MAP_IntIsEnabled(psInst->ui8Int)) + { + ui8Enabled = 1; + MAP_IntDisable(psInst->ui8Int); + } + else + { + ui8Enabled = 0; + } + + // + // Compute the new value of the write pointer (after this command is added + // to the queue). + // + ui8Next = psInst->ui8WritePtr + 1; + if(ui8Next == NUM_I2CM_COMMANDS) + { + ui8Next = 0; + } + + // + // Return a failure if the command queue is full. + // + if(psInst->ui8ReadPtr == ui8Next) + { + if(ui8Enabled) + { + MAP_IntEnable(psInst->ui8Int); + } + return(0); + } + + // + // Get a pointer to the command structure. + // + pCommand = &(psInst->pCommands[psInst->ui8WritePtr]); + + // + // Fill in the command structure with the details of this command. + // + pCommand->ui8Addr = ui8Addr; + pCommand->pui8WriteData = pui8WriteData; + pCommand->ui16WriteCount = ui16WriteCount; + pCommand->ui16WriteBatchSize = ui16WriteBatchSize; + pCommand->pui8ReadData = pui8ReadData; + pCommand->ui16ReadCount = ui16ReadCount; + pCommand->ui16ReadBatchSize = ui16ReadBatchSize; + pCommand->pfnCallback = pfnCallback; + pCommand->pvCallbackData = pvCallbackData; + + // + // Update the write pointer. + // + psInst->ui8WritePtr = ui8Next; + + // + // See if the state machine is idle. + // + if(psInst->ui8State == STATE_IDLE) + { + // + // Generate a fake I2C interrupt, which will commence the I2C transfer. + // + IntTrigger(psInst->ui8Int); + } + + // + // Re-enable the I2C master interrupt. + // + if(ui8Enabled) + { + MAP_IntEnable(psInst->ui8Int); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Resumes an I2C transfer. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param pui8Data is a pointer to the buffer to be used for the next batch of +//! data. +//! +//! This function resumes an I2C transfer that has been paused via the use of +//! the write or read batch size capability. +//! +//! \return Returns 1 if the transfer was resumed and 0 if there was not a +//! paused transfer to resume. +// +//***************************************************************************** +uint_fast8_t +I2CMTransferResume(tI2CMInstance *psInst, uint8_t *pui8Data) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(pui8Data); + + // + // Return an error if there is not a paused transfer. + // + if((psInst->ui8State != STATE_WRITE_PAUSE) && + (psInst->ui8State != STATE_READ_PAUSE)) + { + return(0); + } + + // + // Save the pointer for the next buffer. + // + if(psInst->ui8State == STATE_WRITE_PAUSE) + { + psInst->pCommands[psInst->ui8ReadPtr].pui8WriteData = pui8Data; + } + else + { + psInst->pCommands[psInst->ui8ReadPtr].pui8ReadData = pui8Data; + } + + // + // Trigger the I2C interrupt, resuming the transfer. + // + IntTrigger(psInst->ui8Int); + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of of a +// read-modify-write operation of 8 bits of data have completed. +// +//***************************************************************************** +static void +I2CMReadModifyWrite8Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMReadModifyWrite8 *psInst; + + // + // Convert the instance data into a pointer to a tI2CMReadModifyWrite8 + // structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result ina callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = I2CM_RMW_STATE_IDLE; + } + + // + // Determine the current state of the I2C master read-modify-write state + // machine. + // + switch(psInst->ui8State) + { + // + // The read portion of the read-modify-write has completed. + // + case I2CM_RMW_STATE_READ: + { + // + // Modify the register data that was just read. + // + psInst->pui8Buffer[1] = ((psInst->pui8Buffer[1] & + psInst->ui8Mask) | psInst->ui8Value); + + // + // Write the data back to the device. + // + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Buffer, + 2, I2CMReadModifyWrite8Callback, psInst); + + // + // Move to the wait for write state. + // + psInst->ui8State = I2CM_RMW_STATE_WRITE; + + // + // Done. + // + break; + } + + // + // The write portion of the read-modify-write has completed. + // + case I2CM_RMW_STATE_WRITE: + { + // + // Move to the idle state. + // + psInst->ui8State = I2CM_RMW_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == I2CM_RMW_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a read-modify-write of 8 bits of data in an I2C device. +//! +//! \param psInst is a pointer to the read-modify-write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param ui8Mask is the mask indicating the register bits that should be +//! maintained. +//! \param ui8Value is the value indicating the new value for the register bits +//! that are not maintained. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read-modify-write transaction of 8 bits of data +//! in an I2C device. The modify portion of the operation is performed by +//! AND-ing the register value with \e ui8Mask and then OR-ing the result with +//! \e ui8Value. When the read-modify-write is complete, the callback +//! function, if provided, is called in the context of the I2C master interrupt +//! handler. +//! +//! If the mask (in \e ui8Mask) is zero, then none of the bits in the current +//! register value are maintained. In this case, the read portion of the +//! read-modify-write is bypassed, and the new register value (in \e ui8Value) +//! is directly written to the I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMReadModifyWrite8(tI2CMReadModifyWrite8 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the read-modify-write structure with the details of this + // request. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8Addr; + psInst->ui8Mask = ui8Mask; + psInst->ui8Value = ui8Value; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Construct the I2C command to access the requested register. + // + psInst->pui8Buffer[0] = ui8Reg; + + // + // See if this is a write or a read-modify-write. + // + if(ui8Mask == 0) + { + // + // Set the state to waiting for the write portion of the + // read-modify-write. + // + psInst->ui8State = I2CM_RMW_STATE_WRITE; + + // + // Set the new register value in the command buffer. + // + psInst->pui8Buffer[1] = ui8Value; + + // + // Add the write command to the I2C master queue. + // + if(I2CMWrite(psI2CInst, ui8Addr, psInst->pui8Buffer, 2, + I2CMReadModifyWrite8Callback, psInst) == 0) + { + return(0); + } + } + else + { + // + // Set the state to waiting for the read portion of the + // read-modify-write. + // + psInst->ui8State = I2CM_RMW_STATE_READ; + + // + // Add the read command to the I2C master queue. + // + if(I2CMRead(psI2CInst, ui8Addr, psInst->pui8Buffer, 1, + psInst->pui8Buffer + 1, 1, I2CMReadModifyWrite8Callback, + psInst) == 0) + { + return(0); + } + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of a +// read-modify-write operation of 16 bits of little-endian data have completed. +// +//***************************************************************************** +static void +I2CMReadModifyWrite16LECallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMReadModifyWrite16 *psInst; + uint16_t ui16Value; + + // + // Convert the instance data into a pointer to a tI2CMReadModifyWrite16 + // structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result ina callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = I2CM_RMW_STATE_IDLE; + } + + // + // Determine the current state of the I2C master read-modify-write state + // machine. + // + switch(psInst->ui8State) + { + // + // The read portion of the read-modify-write has completed. + // + case I2CM_RMW_STATE_READ: + { + // + // Modify the register data that was just read. + // + ui16Value = (psInst->pui8Buffer[2] << 8) | psInst->pui8Buffer[1]; + ui16Value = (ui16Value & psInst->ui16Mask) | psInst->ui16Value; + psInst->pui8Buffer[1] = ui16Value & 0xff; + psInst->pui8Buffer[2] = (ui16Value >> 8) & 0xff; + + // + // Write the data back to the device. + // + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Buffer, + 3, I2CMReadModifyWrite16LECallback, psInst); + + // + // Move to the wait for write state. + // + psInst->ui8State = I2CM_RMW_STATE_WRITE; + + // + // Done. + // + break; + } + + // + // The write portion of the read-modify-write has completed. + // + case I2CM_RMW_STATE_WRITE: + { + // + // Move to the idle state. + // + psInst->ui8State = I2CM_RMW_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == I2CM_RMW_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a read-modify-write of 16 bits of little-endian data in an I2C +//! device. +//! +//! \param psInst is a pointer to the read-modify-write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param ui16Mask is the mask indicating the register bits that should be +//! maintained. +//! \param ui16Value is the value indicating the new value for the register +//! bits that are not maintained. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read-modify-write transaction of 16 bits of +//! little-endian data in an I2C device. The modify portion of the operation +//! is performed by AND-ing the register value with \e ui16Mask and then OR-ing +//! the result with \e ui16Value. When the read-modify-write is complete, the +//! callback function, if provided, is called in the context of the I2C master +//! interrupt handler. +//! +//! If the mask (in \e ui16Mask) is zero, then none of the bits in the current +//! register value are maintained. In this case, the read portion of the +//! read-modify-write is bypassed, and the new register value (in \e ui16Value) +//! is directly written to the I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMReadModifyWrite16LE(tI2CMReadModifyWrite16 *psInst, + tI2CMInstance *psI2CInst, uint_fast8_t ui8Addr, + uint_fast8_t ui8Reg, uint_fast16_t ui16Mask, + uint_fast16_t ui16Value, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the read-modify-write structure with the details of this + // request. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8Addr; + psInst->ui16Mask = ui16Mask; + psInst->ui16Value = ui16Value; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Construct the I2C command to access the requested register. + // + psInst->pui8Buffer[0] = ui8Reg; + + // + // See if this is a write or a read-modify-write. + // + if(ui16Mask == 0) + { + // + // Set the state to waiting for the write portion of the + // read-modify-write. + // + psInst->ui8State = I2CM_RMW_STATE_WRITE; + + // + // Set the new register value in the command buffer. + // + psInst->pui8Buffer[1] = ui16Value & 0xff; + psInst->pui8Buffer[2] = (ui16Value >> 8) & 0xff; + + // + // Add the write command to the I2C master queue. + // + if(I2CMWrite(psI2CInst, ui8Addr, psInst->pui8Buffer, 3, + I2CMReadModifyWrite16LECallback, psInst) == 0) + { + return(0); + } + } + else + { + // + // Set the state to waiting for the read portion of the + // read-modify-write. + // + psInst->ui8State = I2CM_RMW_STATE_READ; + + // + // Add the read command to the I2C master queue. + // + if(I2CMRead(psI2CInst, ui8Addr, psInst->pui8Buffer, 1, + psInst->pui8Buffer + 1, 2, I2CMReadModifyWrite16LECallback, + psInst) == 0) + { + return(0); + } + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of a +// write operation of 8-bit data have completed. +// +//***************************************************************************** +static void +I2CMWrite8Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMWrite8 *psInst; + + // + // Convert the instance data into a pointer to a tI2CMWrite8 structure. + // + psInst = pvCallbackData; + + // + // See if the current batch is done and more data is needed. + // + if(ui8Status == I2CM_STATUS_BATCH_DONE) + { + // + // Place the next two bytes into the write buffer. + // + psInst->pui8Buffer[0] = psInst->pui8Data[0]; + if(psInst->ui16Count > 1) + { + psInst->pui8Buffer[1] = psInst->pui8Data[1]; + } + + // + // Advance past the next two bytes of the input buffer. + // + psInst->pui8Data += 2; + psInst->ui16Count -= 2; + + // + // Resume the batched write. + // + I2CMTransferResume(psInst->psI2CInst, psInst->pui8Buffer); + } + + // + // The transfer has completed, or an error has occurred. In both cases, + // see if there is a callback function. + // + else if(psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a write of 8-bit data to an I2C device. +//! +//! \param psInst is a pointer to the 8-bit write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param pui8Data is a pointer to the register data to be written. +//! \param ui16Count is the number of register values to be written. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a write transaction of 8-bit data to an I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMWrite8(tI2CMWrite8 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8Addr, + uint_fast8_t ui8Reg, const uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the write structure with the details of this request. + // + psInst->psI2CInst = psI2CInst; + psInst->pui8Data = pui8Data + 1; + psInst->ui16Count = ui16Count - 1; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Initiate the I2C write to this device. + // + psInst->pui8Buffer[0] = ui8Reg; + psInst->pui8Buffer[1] = pui8Data[0]; + if(I2CMWriteBatched(psI2CInst, ui8Addr, psInst->pui8Buffer, ui16Count + 1, + 2, I2CMWrite8Callback, psInst) == 0) + { + // + // The I2C write failed, so return a failure. + // + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of a read +// operation of 16-bit big-endian data have completed. +// +//***************************************************************************** +static void +I2CMRead16BECallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMRead16BE *psInst; + uint8_t ui8Temp; + + // + // Convert the instance data into a pointer to a tI2CMRead16BE structure. + // + psInst = pvCallbackData; + + // + // See if the transaction completed successfully. + // + if(ui8Status == I2CM_STATUS_SUCCESS) + { + // + // Loop through the 16-bit values read from the I2C device. + // + while(psInst->ui16Count--) + { + // + // Byte swap this value. + // + ui8Temp = psInst->pui8Data[0]; + psInst->pui8Data[0] = psInst->pui8Data[1]; + psInst->pui8Data[1] = ui8Temp; + + // + // Skip to the next value. + // + psInst->pui8Data += 2; + } + } + + // + // See if there is a callback function. + // + if(psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a read of 16-bit big-endian data from an I2C device. +//! +//! \param psInst is a pointer to the 16-bit big-endian read instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param pui16Data is a pointer to the buffer to be filled with the register +//! data. +//! \param ui16Count is the number of 16-bit register values to be read. +//! \param pfnCallback is the function to be called when the read has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read transaction of 16-bit big-endian data from +//! an I2C device. The data is provided by the device in big-endian format and +//! is byte-swapped as it is read from the I2C device, returning the data in +//! little-endian format. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMRead16BE(tI2CMRead16BE *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, uint_fast8_t ui8Reg, + uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the read structure with the details of this request. + // + psInst->psI2CInst = psI2CInst; + psInst->pui8Data = (uint8_t *)pui16Data; + psInst->ui16Count = ui16Count; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Initiate the I2C write to this device. + // + psInst->pui8Data[0] = ui8Reg; + if(I2CMRead(psI2CInst, ui8Addr, psInst->pui8Data, 1, psInst->pui8Data, + ui16Count * 2, I2CMRead16BECallback, psInst) == 0) + { + // + // The I2C write failed, so return a failure. + // + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of a +// write operation of 16-bit big-endian data have completed. +// +//***************************************************************************** +static void +I2CMWrite16BECallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMWrite16BE *psInst; + + // + // Convert the instance data into a pointer to a tI2CMWrite16BE structure. + // + psInst = pvCallbackData; + + // + // See if the current batch is done and more data is needed. + // + if(ui8Status == I2CM_STATUS_BATCH_DONE) + { + // + // Place the next two bytes into the write buffer. + // + psInst->pui8Buffer[0] = psInst->pui8Data[0]; + if(psInst->ui16Count > 1) + { + psInst->pui8Buffer[1] = psInst->pui8Data[3]; + } + + // + // Advance past the next two bytes of the input buffer. + // + psInst->pui8Data += 2; + psInst->ui16Count--; + + // + // Resume the batched write. + // + I2CMTransferResume(psInst->psI2CInst, psInst->pui8Buffer); + } + + // + // The transfer has completed, or an error has occurred. In both cases, + // see if there is a callback function. + // + else if(psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a write of 16-bit big-endian data to an I2C device. +//! +//! \param psInst is a pointer to the 16-bit big-endian write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param pui16Data is a pointer to the register data to be written. +//! \param ui16Count is the number of 16-bit register values to be written. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a write transaction of 16-bit big-endian data to an +//! I2C device. The data in the buffer is provided in little-endian format and +//! is byte-swapped as it is being written to the I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMWrite16BE(tI2CMWrite16BE *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, uint_fast8_t ui8Reg, + const uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the write structure with the details of this request. + // + psInst->psI2CInst = psI2CInst; + psInst->pui8Data = (const uint8_t *)pui16Data; + psInst->ui16Count = ui16Count; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Initiate the I2C write to this device. + // + psInst->pui8Buffer[0] = ui8Reg; + psInst->pui8Buffer[1] = psInst->pui8Data[1]; + if(I2CMWriteBatched(psI2CInst, ui8Addr, psInst->pui8Buffer, + (ui16Count * 2) + 1, 2, I2CMWrite16BECallback, + psInst) == 0) + { + // + // The I2C write failed, so return a failure. + // + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/i2cm_drv.h b/sensorlib/i2cm_drv.h new file mode 100644 index 0000000..397c04d --- /dev/null +++ b/sensorlib/i2cm_drv.h @@ -0,0 +1,544 @@ +//***************************************************************************** +// +// i2cm_drv.h - Prototypes for the interrupt-driven I2C master driver. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_I2CM_DRV_H__ +#define __SENSORLIB_I2CM_DRV_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// A prototype for the callback function used by the I2C master driver. +// +//***************************************************************************** +typedef void (tSensorCallback)(void *pvData, uint_fast8_t ui8Status); + +//***************************************************************************** +// +// The possible status values that can be returned by the I2C command callback. +// +//***************************************************************************** +#define I2CM_STATUS_SUCCESS 0 +#define I2CM_STATUS_ADDR_NACK 1 +#define I2CM_STATUS_DATA_NACK 2 +#define I2CM_STATUS_ARB_LOST 3 +#define I2CM_STATUS_ERROR 4 +#define I2CM_STATUS_BATCH_DONE 5 +#define I2CM_STATUS_BATCH_READY 6 + +//***************************************************************************** +// +// The maximum number of outstanding commands for each I2C master instance. +// +//***************************************************************************** +#define NUM_I2CM_COMMANDS 10 + +//***************************************************************************** +// +// The structure that defines an I2C master command. +// +//***************************************************************************** +typedef struct +{ + // + // The I2C address of the device being accessed. + // + uint8_t ui8Addr; + + // + // The data buffer containing the data to be written. + // + const uint8_t *pui8WriteData; + + // + // The total number of bytes to be written by the command. + // + uint16_t ui16WriteCount; + + // + // The number of bytes to be written in each batch. + // + uint16_t ui16WriteBatchSize; + + // + // The data buffer to store data that has been read. + // + uint8_t *pui8ReadData; + + // + // The total number of bytes to be read by the command. + // + uint16_t ui16ReadCount; + + // + // The number of bytes to be read in each chuck. + // + uint16_t ui16ReadBatchSize; + + // + // The function that is called when this command has been transferred. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; +} +tI2CMCommand; + +//***************************************************************************** +// +// The structure that contains the state of an I2C master instance. +// +//***************************************************************************** +typedef struct +{ + // + // The base address of the I2C module. + // + uint32_t ui32Base; + + // + // The interrupt number associated with the I2C module. + // + uint8_t ui8Int; + + // + // The uDMA channel used to write data to the I2C module. + // + uint8_t ui8TxDMA; + + // + // The uDMA channel used to read data from the I2C module. + // + uint8_t ui8RxDMA; + + // + // The current state of the I2C master driver. + // + uint8_t ui8State; + + // + // The offset of the next command to be read. The buffer is empty when + // this value is equal to the write pointer. + // + uint8_t ui8ReadPtr; + + // + // The offset of the next space in the buffer to write a command. The + // buffer is full if this value is one less than the read pointer. + // + uint8_t ui8WritePtr; + + // + // The index into the data buffer of the next byte to be transferred. + // + uint16_t ui16Index; + + // + // An array of commands queued up to be sent via the I2C module. + // + tI2CMCommand pCommands[NUM_I2CM_COMMANDS]; +} +tI2CMInstance; + +//***************************************************************************** +// +// The structure that contains the state of an I2C read-modify-write request of +// 8 bits of data. +// +//***************************************************************************** +typedef struct +{ + // + // A pointer to the I2C master interface instance used for the + // read-modify-write request. + // + tI2CMInstance *psI2CInst; + + // + // The buffer used for the I2C transfers. + // + uint8_t pui8Buffer[4]; + + // + // The current state of the I2C read-modify-write state machine. + // + uint8_t ui8State; + + // + // The I2C address of the device being accessed. + // + uint8_t ui8Addr; + + // + // The value to AND with the I2C register data. + // + uint8_t ui8Mask; + + // + // The value to OR with the I2C register data. + // + uint8_t ui8Value; + + // + // The function that is called when the read-modify-write has been + // completed. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; +} +tI2CMReadModifyWrite8; + +//***************************************************************************** +// +// The structure that contains the state of an I2C read-modify-write request of +// 16 bits of data. +// +//***************************************************************************** +typedef struct +{ + // + // A pointer to the I2C master interface instance used for the + // read-modify-write request. + // + tI2CMInstance *psI2CInst; + + // + // The buffer used for the I2C transfers. + // + uint8_t pui8Buffer[4]; + + // + // The current state of the I2C read-modify-write state machine. + // + uint8_t ui8State; + + // + // The I2C address of the device being accessed. + // + uint8_t ui8Addr; + + // + // The value to AND with the I2C register data. + // + uint16_t ui16Mask; + + // + // The value to OR with the I2C register data. + // + uint16_t ui16Value; + + // + // The function that is called when the read-modify-write has been + // completed. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; +} +tI2CMReadModifyWrite16; + +//***************************************************************************** +// +// The structure that contains the state of an I2C write request of 8-bit data. +// +//***************************************************************************** +typedef struct +{ + // + // A pointer to the I2C master interface instance used for the write + // request. + // + tI2CMInstance *psI2CInst; + + // + // The buffer used for the I2C transfers. + // + uint8_t pui8Buffer[2]; + + // + // The number of values to write to the I2C device. + // + uint16_t ui16Count; + + // + // A pointer to the buffer containing the data to write to the I2C device. + // + const uint8_t *pui8Data; + + // + // The function that is called when the write has been completed. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; +} +tI2CMWrite8; + +//***************************************************************************** +// +// The structure that contains the state of an I2C read request of 16-bit data +// from a big-endian device. +// +//***************************************************************************** +typedef struct +{ + // + // A pointer to the I2C master interface instance used for the read + // request. + // + tI2CMInstance *psI2CInst; + + // + // A pointer to the buffer containing the data read from the I2C device. + // + uint8_t *pui8Data; + + // + // The number of 16-bit values to read from the I2C device. + // + uint16_t ui16Count; + + // + // The function that is called when the read has been completed. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; +} +tI2CMRead16BE; + +//***************************************************************************** +// +// The structure that contains the state of an I2C write request of 16-bit data +// to a big-endian device. +// +//***************************************************************************** +typedef struct +{ + // + // A pointer to the I2C master interface instance used for the write + // request. + // + tI2CMInstance *psI2CInst; + + // + // The buffer used for the I2C transfers. + // + uint8_t pui8Buffer[2]; + + // + // The number of 16-bit values to write to the I2C device. + // + uint16_t ui16Count; + + // + // A pointer to the buffer containing the data to write to the I2C device. + // + const uint8_t *pui8Data; + + // + // The function that is called when the write has been completed. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; +} +tI2CMWrite16BE; + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern void I2CMIntHandler(tI2CMInstance *psInst); +extern void I2CMInit(tI2CMInstance *psInst, uint32_t ui32Base, + uint_fast8_t ui8Int, uint_fast8_t ui8TxDMA, + uint_fast8_t ui8RxDMA, uint32_t ui32Clock); +extern uint_fast8_t I2CMCommand(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8WriteData, + uint_fast16_t ui16WriteCount, + uint_fast16_t ui16WriteBatchSize, + uint8_t *pui8ReadData, + uint_fast16_t ui16ReadCount, + uint_fast16_t ui16ReadBatchSize, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t I2CMTransferResume(tI2CMInstance *psInst, + uint8_t *pui8Data); +extern uint_fast8_t I2CMReadModifyWrite8(tI2CMReadModifyWrite8 *psInst, + tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, + uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t I2CMReadModifyWrite16LE(tI2CMReadModifyWrite16 *psInst, + tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, + uint_fast8_t ui8Reg, + uint_fast16_t ui16Mask, + uint_fast16_t ui16Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t I2CMWrite8(tI2CMWrite8 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, uint_fast8_t ui8Reg, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t I2CMRead16BE(tI2CMRead16BE *psInst, + tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, uint_fast8_t ui8Reg, + uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t I2CMWrite16BE(tI2CMWrite16BE *psInst, + tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, uint_fast8_t ui8Reg, + const uint16_t *pui16Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +// A convenience wrapper around I2CMCommand to perform a write. +// +//***************************************************************************** +inline uint_fast8_t +I2CMWrite(tI2CMInstance *psInst, uint_fast8_t ui8Addr, const uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + return(I2CMCommand(psInst, ui8Addr, pui8Data, ui16Count, ui16Count, 0, 0, + 0, pfnCallback, pvCallbackData)); +} + +//***************************************************************************** +// +// A convenience wrapper around I2CMCommand to perform a read. +// +//***************************************************************************** +inline uint_fast8_t +I2CMRead(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8WriteData, uint_fast16_t ui16WriteCount, + uint8_t *pui8ReadData, uint_fast16_t ui16ReadCount, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + return(I2CMCommand(psInst, ui8Addr, pui8WriteData, ui16WriteCount, + ui16WriteCount, pui8ReadData, ui16ReadCount, + ui16ReadCount, pfnCallback, pvCallbackData)); +} + +//***************************************************************************** +// +// A convenience wrapper around I2CMCommand to perform a batched write. +// +//***************************************************************************** +inline uint_fast8_t +I2CMWriteBatched(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8Data, uint_fast16_t ui16Count, + uint_fast16_t ui16BatchSize, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + return(I2CMCommand(psInst, ui8Addr, pui8Data, ui16Count, ui16BatchSize, 0, + 0, 0, pfnCallback, pvCallbackData)); +} + +//***************************************************************************** +// +// A convenience wrapper around I2CMCommand to perform a batched read. +// +//***************************************************************************** +inline uint_fast8_t +I2CMReadBatched(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8WriteData, uint_fast16_t ui16WriteCount, + uint_fast16_t ui16WriteBatchSize, uint8_t *pui8ReadData, + uint_fast16_t ui16ReadCount, uint_fast16_t ui16ReadBatchSize, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + return(I2CMCommand(psInst, ui8Addr, pui8WriteData, ui16WriteCount, + ui16WriteBatchSize, pui8ReadData, ui16ReadCount, + ui16ReadBatchSize, pfnCallback, pvCallbackData)); +} + +//***************************************************************************** +// +// A 16-bit big-endian read-modify-write in terms of the little-endian version. +// +//***************************************************************************** +inline uint_fast8_t +I2CMReadModifyWrite16BE(tI2CMReadModifyWrite16 *psInst, + tI2CMInstance *psI2CInst, uint_fast8_t ui8Addr, + uint_fast8_t ui8Reg, uint_fast16_t ui16Mask, + uint_fast16_t ui16Value, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + return(I2CMReadModifyWrite16LE(psInst, psI2CInst, ui8Addr, ui8Reg, + (((ui16Mask & 0xff00) >> 8) | + ((ui16Mask & 0x00ff) << 8)), + (((ui16Value & 0xff00) >> 8) | + ((ui16Value & 0x00ff) << 8)), + pfnCallback, pvCallbackData)); +} + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_I2CM_DRV_H__ diff --git a/sensorlib/isl29023.c b/sensorlib/isl29023.c new file mode 100644 index 0000000..313ced9 --- /dev/null +++ b/sensorlib/isl29023.c @@ -0,0 +1,668 @@ +//***************************************************************************** +// +// isl29023.c - Driver for the ISL29023 Light Sensor +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "sensorlib/hw_isl29023.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/isl29023.h" + +//***************************************************************************** +// +//! \addtogroup isl29023_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Range setting to floating point range value lookup table +// +//***************************************************************************** +const float g_fRangeLookup[4] = +{ + 1000.0, + 4000.0, + 16000.0, + 64000.0 +}; + +//***************************************************************************** +// +// Resolution setting to floating point resolution value lookup table +// +//***************************************************************************** +const float g_fResolutionLookup[4] = +{ + 65536.0, + 4096.0, + 256.0, + 16.0 +}; + +//***************************************************************************** +// +// Beta value lookup based on datasheet typical values for DATA_IR1, DATA_IR2, +// DATA_IR3, DATA_IR4. These should be reasonable for 16 bit conversions. +// However, Beta changes with resolution and background IR conditions. +// +//***************************************************************************** +const float g_fBetaLookup[4] = +{ + 95.238, + 23.810, + 5.952, + 1.486 +}; + +//***************************************************************************** +// +// The states of the ISL29023 state machine. +// +//***************************************************************************** +#define ISL29023_STATE_IDLE 0 +#define ISL29023_STATE_INIT 1 +#define ISL29023_STATE_READ 2 +#define ISL29023_STATE_WRITE 3 +#define ISL29023_STATE_RMW 4 +#define ISL29023_STATE_READ_DATA 5 + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// ISL29023 have completed. +// +//***************************************************************************** +static void +ISL29023Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tISL29023 *psInst; + uint8_t *pui8Data; + + // + // Convert the instance data into a pointer to a tBMA180 structure. + // + psInst = (tISL29023 *)pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = ISL29023_STATE_IDLE; + } + + // + // Determine the current state of the ISL29023 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case ISL29023_STATE_INIT: + case ISL29023_STATE_READ: + case ISL29023_STATE_READ_DATA: + default: + { + // + // A register operation is complete. Return to IDLE state + // the value read from the register is in the pui8Data buffer and + // can be accessed directly by the calling application after + // receiving the callback. + // + psInst->ui8State = ISL29023_STATE_IDLE; + break; + } + + // + // A write to the ISL29023 control and config registers is complete. + // + case ISL29023_STATE_WRITE: + { + // + // Set the range and resolution to the new values. If the register + // was not modified, the values will be the same so this has no + // effect. + // + psInst->ui8Range = psInst->ui8NewRange; + psInst->ui8Resolution = psInst->ui8NewResolution; + + // + // The state machine is now idle. + // + psInst->ui8State = ISL29023_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A read modify write operation has just completed. + // + case ISL29023_STATE_RMW: + { + // + // Check if the register that was written contained the range or + // resolution data that we need to track. + // + pui8Data = psInst->uCommand.sReadModifyWriteState.pui8Buffer; + if((pui8Data[0] == ISL29023_O_CMD_II) && + (ui8Status == I2CM_STATUS_SUCCESS)) + { + // + // Store the latest range and resolution settings + // + psInst->ui8Range = ((pui8Data[1] & ISL29023_CMD_II_RANGE_M) >> + ISL29023_CMD_II_RANGE_S); + psInst->ui8Resolution = ((pui8Data[1] & + ISL29023_CMD_II_ADC_RES_M) >> + ISL29023_CMD_II_ADC_RES_S); + } + + // + // The state machine is now idle. + // + psInst->ui8State = ISL29023_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == ISL29023_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the ISL29023 driver. +//! +//! \param psInst is a pointer to the ISL29023 instance data. +//! \param psI2CInst is a pointer to the I2C driver instance data. +//! \param ui8I2CAddr is the I2C address of the ISL29023 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the ISL29023 driver, preparing it for operation. +//! This function also asserts a reset signal to the ISL29023 to clear any +//! previous configuration data. +//! +//! \return Returns 1 if the ISL29023 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +ISL29023Init(tISL29023 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Initialize the ISL29023 instance structure + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = ISL29023_STATE_INIT; + psInst->ui8Range = ISL29023_CMD_II_RANGE_1K >> ISL29023_CMD_II_RANGE_S; + psInst->ui8NewRange = ISL29023_CMD_II_RANGE_1K >> ISL29023_CMD_II_RANGE_S; + psInst->ui8Resolution = (ISL29023_CMD_II_ADC_RES_16 >> + ISL29023_CMD_II_ADC_RES_S); + psInst->ui8NewResolution = (ISL29023_CMD_II_ADC_RES_16 >> + ISL29023_CMD_II_ADC_RES_S); + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Put the device into power down mode. + // + psInst->pui8Data[0] = ISL29023_O_CMD_I; + psInst->pui8Data[1] = ISL29023_CMD_I_OP_MODE_POWER_DOWN; + if(I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, 2, + ISL29023Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = ISL29023_STATE_IDLE; + return(0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from ISL29023 registers. +//! +//! \param psInst is a pointer to the ISL29023 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the ISL29023. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +ISL29023Read(tISL29023 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the ISL29023 driver is not idle (in other words, + // there is already an outstanding request to the ISL29023). + // + if(psInst->ui8State != ISL29023_STATE_IDLE) + { + return(0); + } + // + // Store the Callback information + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // set ISL29023 state + // + psInst->ui8State = ISL29023_STATE_READ; + + // + // Load the command buffer with the appropriate register information + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + + // + // Start the I2CM read and return indication. + // + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + ISL29023Callback, psInst) == 0) + { + psInst->ui8State = ISL29023_STATE_IDLE; + return(0); + } + + return(1); +} + +//***************************************************************************** +// +//! Write register data to the ISL29023. +//! +//! \param psInst is a pointer to the ISL29023 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the ISL29023. The first byte of the \e pui8Data buffer contains the value +//! to be written into the \e ui8Reg register, the second value contains the +//! data to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +ISL29023Write(tISL29023 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the ISL29023 driver is not idle (in other words, + // there is already an outstanding request to the ISL29023). + // + if(psInst->ui8State != ISL29023_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if the CMD_II register is being written. + // + if((ui8Reg <= ISL29023_O_CMD_II) && + ((ui8Reg + ui16Count) > ISL29023_O_CMD_II)) + { + // + // Extract the range and resolution from the CMD_II register value. + // + psInst->ui8NewRange = ((pui8Data[ui8Reg - ISL29023_O_CMD_II] & + ISL29023_CMD_II_RANGE_M) >> + ISL29023_CMD_II_RANGE_S); + psInst->ui8NewResolution = ((pui8Data[ui8Reg - ISL29023_O_CMD_II] & + ISL29023_CMD_II_ADC_RES_M) >> + ISL29023_CMD_II_ADC_RES_S); + } + + // + // Move state machine to the write state + // + psInst->ui8State = ISL29023_STATE_WRITE; + + // + // Add the i2c address and the register offset to the data buffer. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, + ISL29023Callback, psInst) == 0) + { + // + // I2C write failed, move to idle state and return the failure. + // + psInst->ui8State = ISL29023_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of an ISL29023 register. +//! +//! \param psInst is a pointer to the ISL29023 instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the ISL29023 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! ISL29023. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +ISL29023ReadModifyWrite(tISL29023 *psInst, uint_fast8_t ui8Reg, + uint8_t ui8Mask, uint8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the ISL29023 driver is not in the idle state. + // + if(psInst->ui8State != ISL29023_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // set ISL29023 state + // + psInst->ui8State = ISL29023_STATE_RMW; + + // + // Submit the read-modify-write request to the ISL29023. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, ISL29023Callback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = ISL29023_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the light data from the ISL29023. +//! +//! \param psInst is a pointer to the ISL29023 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the ISL29023 data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - ISL29023DataLightVisibleGetRaw() +//! - ISL29023DataLightVisibleGetFloat() +//! - ISL29023DataLightIRGetRaw() +//! - ISL29023DataLightIRGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +ISL29023DataRead(tISL29023 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the ISL29023 driver is not idle (in other words, + // there is already an outstanding request to the ISL29023). + // + if(psInst->ui8State != ISL29023_STATE_IDLE) + { + return(0); + } + + // + // Store the Callback information + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // set ISL29023 state + // + psInst->ui8State = ISL29023_STATE_READ_DATA; + + psInst->uCommand.pui8Buffer[0] = ISL29023_O_DATA_OUT_LSB; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 2, + ISL29023Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = ISL29023_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the ISL29023 instance data. +//! \param pui16Visible is a pointer to the value into which the raw light data +//! is stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +ISL29023DataLightVisibleGetRaw(tISL29023 *psInst, uint16_t *pui16Visible) +{ + // + // Return the raw light value. + // + *pui16Visible = (psInst->pui8Data[1] << 8) | psInst->pui8Data[0]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the ISL29023 instance data. +//! \param pfVisibleLight is a pointer to the value into which the light data +//! is stored as floating point lux. +//! +//! This function returns the light data from the most recent data read, +//! converted into lux. +//! +//! \return None. +// +//***************************************************************************** +void +ISL29023DataLightVisibleGetFloat(tISL29023 *psInst, float *pfVisibleLight) +{ + uint16_t ui16Light; + float fRange, fResolution; + + // + // Get the raw light data from the instance structure + // + ISL29023DataLightVisibleGetRaw(psInst, &ui16Light); + + // + // Get the floating point values for range and resolution from the lookup. + // + fRange = g_fRangeLookup[psInst->ui8Range]; + fResolution = g_fResolutionLookup[psInst->ui8Resolution]; + + // + // Calculate light reading in lux. + // + *pfVisibleLight = ((float)ui16Light) * (fRange / fResolution); +} + +//***************************************************************************** +// +//! Gets the raw measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the ISL29023 instance data. +//! \param pui16IR is a pointer to the value into which the raw IR data is +//! stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +ISL29023DataLightIRGetRaw(tISL29023 *psInst, uint16_t *pui16IR) +{ + // + // Return the raw light value. + // + *pui16IR = (psInst->pui8Data[1] << 8) | psInst->pui8Data[0]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the ISL29023 instance data. +//! \param pfIR is a pointer to the value into which the IR data is stored as +//! floating point lux. +//! +//! This function returns the IR data from the most recent data read, converted +//! into lux. +//! +//! \return None. +// +//***************************************************************************** +void +ISL29023DataLightIRGetFloat(tISL29023 *psInst, float *pfIR) +{ + uint16_t i16IR; + + ISL29023DataLightIRGetRaw(psInst, &i16IR); + + *pfIR = ((float) i16IR) / g_fBetaLookup[psInst->ui8Range]; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/isl29023.h b/sensorlib/isl29023.h new file mode 100644 index 0000000..184c702 --- /dev/null +++ b/sensorlib/isl29023.h @@ -0,0 +1,166 @@ +//***************************************************************************** +// +// isl29023.h - Prototypes for the ISL29023 light sensor driver. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_ISL29023_H__ +#define __SENSORLIB_ISL29023_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the ISL29023 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the ISL29023. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the ISL29023. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the ISL29023. + // + uint8_t ui8State; + + // + // The data buffer used for sending/receiving data to/from the ISL29023. + // + uint8_t pui8Data[4]; + + // + // Instance copy of the range setting. Used in GetFloat functions + // + uint8_t ui8Range; + + // + // The new range, which is used when a register write succeeds. + // + uint8_t ui8NewRange; + + // + // Instance copy of the resolution setting. Used in GetFloat function. + // + uint8_t ui8Resolution; + + // + // The new resolution, which is used when a register write succeeds. + // + uint8_t ui8NewResolution; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[3]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tISL29023; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t ISL29023Init(tISL29023 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t ISL29023Read(tISL29023 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t ISL29023Write(tISL29023 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t ISL29023ReadModifyWrite(tISL29023 *psInst, + uint_fast8_t ui8Reg, + uint8_t ui8Mask, uint8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t ISL29023DataRead(tISL29023 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void ISL29023DataLightVisibleGetRaw(tISL29023 *psInst, + uint16_t *pui16Visible); +extern void ISL29023DataLightVisibleGetFloat(tISL29023 *psInst, + float *pfVisible); +extern void ISL29023DataLightIRGetRaw(tISL29023 *psInst, uint16_t *pui16IR); +extern void ISL29023DataLightIRGetFloat(tISL29023 *psInst, float *pfIR); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_ISL29023_H__ + diff --git a/sensorlib/kxti9.c b/sensorlib/kxti9.c new file mode 100644 index 0000000..38de17b --- /dev/null +++ b/sensorlib/kxti9.c @@ -0,0 +1,777 @@ +//***************************************************************************** +// +// kxti9.c - Driver for the KXTI9 accelerometer. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_kxti9.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/kxti9.h" + +//***************************************************************************** +// +//! \addtogroup kxti9_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the KXTI9 state machine. +// +//***************************************************************************** +#define KXTI9_STATE_IDLE 0 // State machine is idle +#define KXTI9_STATE_INIT_RES 1 // Waiting for intialization +#define KXTI9_STATE_INIT_WAIT 2 // Waiting for reset to complete +#define KXTI9_STATE_LAST 3 // Last state of init +#define KXTI9_STATE_READ 4 // Waiting for read +#define KXTI9_STATE_WRITE 5 // Waiting for write +#define KXTI9_STATE_RMW 6 // Waiting for read-modify-write + +//***************************************************************************** +// +// The factors used to convert the 8-bit acceleration readings from the KXTI9 +// into floating point values in m/s^2. +// +//***************************************************************************** +static const float g_fAccelFactors8[] = +{ + (2.0 * 9.81) / 128.0, + (4.0 * 9.81) / 128.0, + (8.0 * 9.81) / 128.0 +}; + +//***************************************************************************** +// +// The factors used to convert the 12-bit acceleration readings from the KXTI9 +// into floating point values in m/s^2. +// +//***************************************************************************** +static const float g_fAccelFactors12[] = +{ + (2.0 * 9.81) / 2048.0, + (4.0 * 9.81) / 2048.0, + (8.0 * 9.81) / 2048.0 +}; + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the KXTI9 +// have completed. +// +//***************************************************************************** +static void +KXTI9Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tKXTI9 *psInst; + + // + // Convert the instance data into a pointer to a tKXTI9 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if((ui8Status != I2CM_STATUS_SUCCESS) && + (psInst->ui8State != KXTI9_STATE_INIT_WAIT)) + { + psInst->ui8State = KXTI9_STATE_IDLE; + } + + // + // Determine the current state of the KXTI9 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case KXTI9_STATE_LAST: + case KXTI9_STATE_READ: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = KXTI9_STATE_IDLE; + + // + // Done. + // + break; + } + + case KXTI9_STATE_INIT_RES: + { + // + // Try to read back to determine if reset is done. We expect to see + // a NAK. + // + psInst->uCommand.pui8Buffer[0] = KXTI9_O_CTRL3; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 1, + KXTI9Callback, psInst); + + psInst->ui8State = KXTI9_STATE_INIT_WAIT; + break; + } + + case KXTI9_STATE_INIT_WAIT: + { + // + // Check to see if there was finally an ACK. + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + // + // Read again. + // + psInst->uCommand.pui8Buffer[0] = KXTI9_O_CTRL3; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 1, + KXTI9Callback, psInst); + } + else + { + // + // Check the read data to make sure it jibes. + // + if(psInst->pui8Data[0] == 0x4d) + { + // + // Device is out of reset, enable the device. + // + psInst->uCommand.pui8Buffer[0] = KXTI9_O_CTRL1; + psInst->uCommand.pui8Buffer[1] = KXTI9_CTRL1_PC1; + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, KXTI9Callback, + psInst); + } + else + { + ui8Status = I2CM_STATUS_ERROR; + } + + // + // This is the last init write. + // + psInst->ui8State = KXTI9_STATE_LAST; + } + break; + } + + case KXTI9_STATE_WRITE: + { + // + // Set the accelerometer range and resolution to the new value. + // If the register was not modified, the values will be the same so + // this has no effect. + // + psInst->ui8Resolution = psInst->ui8NewResolution; + psInst->ui8Range = psInst->ui8NewRange; + + // + // The state machine is now idle. + // + psInst->ui8State = KXTI9_STATE_IDLE; + break; + } + + case KXTI9_STATE_RMW: + { + // + // See if the CTRL3 register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + KXTI9_O_CTRL3) + { + // + // See if a soft reset has been issued. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + KXTI9_CTRL3_SRST) + { + // + // Default range setting is +/- 2 g + // + psInst->ui8Range = 0; + psInst->ui8NewRange = 0; + + // + // Default resolution is 8-bit. + // + psInst->ui8Resolution = 0; + psInst->ui8NewResolution = 0; + } + } + + // + // See if the CTRL1 register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + KXTI9_O_CTRL1) + { + // + // Extract the range and resolution from the register value. + // + psInst->ui8Range = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + KXTI9_CTRL1_GSEL_M) >> KXTI9_CTRL1_GSEL_S); + psInst->ui8Resolution = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + KXTI9_CTRL1_RES) >> 6); + } + + // + // The state machine is now idle. + // + psInst->ui8State = KXTI9_STATE_IDLE; + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == KXTI9_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + //s + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the KXTI9 driver. +//! +//! \param psInst is a pointer to the KXTI9 instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the KXTI9 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the KXTI9 driver, preparing it for operation. +//! +//! \return Returns 1 if the KXTI9 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +KXTI9Init(tKXTI9 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Initialize the KXTI9 instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = KXTI9_STATE_INIT_RES; + psInst->ui8Resolution = 0; + psInst->ui8NewResolution = 0; + psInst->ui8Range = KXTI9_CTRL1_GSEL_2G >> KXTI9_CTRL1_GSEL_S; + psInst->ui8NewRange = KXTI9_CTRL1_GSEL_2G >> KXTI9_CTRL1_GSEL_S; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Write the EE_W bit of CTRL_REG0 (allowing the configuration registers to + // be modified). + // + psInst->pui8Data[0] = KXTI9_O_CTRL3; + psInst->pui8Data[1] = KXTI9_CTRL3_SRST; + if(I2CMWrite(psInst->psI2CInst, ui8I2CAddr, psInst->pui8Data, 2, + KXTI9Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = KXTI9_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from KXTI9 registers. +//! +//! \param psInst is a pointer to the KXTI9 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the KXTI9. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +KXTI9Read(tKXTI9 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the KXTI9 driver is not idle (in other words, there + // is already an outstanding request to the KXTI9). + // + if(psInst->ui8State != KXTI9_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = KXTI9_STATE_READ; + + // + // Read the requested registers from the KXTI9. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + KXTI9Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = KXTI9_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to KXTI9 registers. +//! +//! \param psInst is a pointer to the KXTI9 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the KXTI9. The first byte of the \e pui8Data buffer contains the value to +//! be written into the \e ui8Reg register, the second value contains the data +//! to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +KXTI9Write(tKXTI9 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the KXTI9 driver is not idle (in other words, there + // is already an outstanding request to the KXTI9). + // + if(psInst->ui8State != KXTI9_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + psInst->ui8NewRange = psInst->ui8Range; + psInst->ui8NewResolution = psInst->ui8Resolution; + + // + // See if the CTRL3 register is being written. + // + if((ui8Reg <= KXTI9_O_CTRL3) && + ((ui8Reg + ui16Count) > KXTI9_O_CTRL3)) + { + // + // See if a soft reset is being requested. + // + if(pui8Data[ui8Reg - KXTI9_O_CTRL3] & KXTI9_CTRL3_SRST) + { + // + // Default range setting is +/- 2 g. + // + psInst->ui8NewRange = 0; + + // + // Default resolution is 8-bit. + // + psInst->ui8NewResolution = 0; + } + } + + // + // See if the CTRL1 register is being written. + // + if((ui8Reg <= KXTI9_O_CTRL1) && + ((ui8Reg + ui16Count) > KXTI9_O_CTRL1)) + { + // + // Extract the range and resolution the register value. + // + psInst->ui8NewRange = + ((pui8Data[ui8Reg - KXTI9_O_CTRL1] & KXTI9_CTRL1_GSEL_M) + >> KXTI9_CTRL1_GSEL_S); + psInst->ui8NewResolution = + ((pui8Data[ui8Reg - KXTI9_O_CTRL1] & KXTI9_CTRL1_RES) >> 6); + } + + // + // Save the details of this write. + // + psInst->uCommand.sWriteState.pui8Data = pui8Data; + psInst->uCommand.sWriteState.ui16Count = ui16Count; + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = KXTI9_STATE_WRITE; + + // + // Write the requested registers to the KXTI9. + // + pui8Data[0] = ui8Reg; + if(I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, pui8Data, ui16Count + 1, + KXTI9Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = KXTI9_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a KXTI9 register. +//! +//! \param psInst is a pointer to the KXTI9 instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the KXTI9 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! KXTI9. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +KXTI9ReadModifyWrite(tKXTI9 *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the KXTI9 driver is not idle (in other words, there + // is already an outstanding request to the KXTI9). + // + if(psInst->ui8State != KXTI9_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = KXTI9_STATE_RMW; + + // + // Submit the read-modify-write request to the KXTI9. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, KXTI9Callback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = KXTI9_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the acceleration and temperature data from the KXTI9. +//! +//! \param psInst is a pointer to the KXTI9 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the KXTI9 data registers. When the read +//! has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - KXTI9DataAccelGetRaw() +//! - KXTI9DataAccelGetFloat() +//! - KXTI9DataTemperatureGetRaw() +//! - KXTI9DataTemperatureGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +KXTI9DataRead(tKXTI9 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the KXTI9 driver is not idle (in other words, there + // is already an outstanding request to the KXTI9). + // + if(psInst->ui8State != KXTI9_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = KXTI9_STATE_READ; + + // + // Read the data registers from the KXTI9. + // + psInst->pui8Data[0] = KXTI9_O_XOUT_L; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, 1, + psInst->pui8Data, 6, KXTI9Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = KXTI9_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw acceleration data from the most recent data read. +//! +//! \param psInst is a pointer to the KXTI9 instance data. +//! \param pui16AccelX is a pointer to the value into which the raw X-axis +//! acceleration data is stored. +//! \param pui16AccelY is a pointer to the value into which the raw Y-axis +//! acceleration data is stored. +//! \param pui16AccelZ is a pointer to the value into which the raw Z-axis +//! acceleration data is stored. +//! +//! This function returns the raw acceleration data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +KXTI9DataAccelGetRaw(tKXTI9 *psInst, uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, uint_fast16_t *pui16AccelZ) +{ + // + // Return the raw acceleration values. + // + if(pui16AccelX) + { + *pui16AccelX = ((psInst->pui8Data[1] << 4) | + (psInst->pui8Data[0] >> 4)); + } + if(pui16AccelY) + { + *pui16AccelY = ((psInst->pui8Data[3] << 4) | + (psInst->pui8Data[2] >> 4)); + } + if(pui16AccelZ) + { + *pui16AccelZ = ((psInst->pui8Data[5] << 4) | + (psInst->pui8Data[4] >> 4)); + } +} + +//***************************************************************************** +// +//! Gets the acceleration data from the most recent data read. +//! +//! \param psInst is a pointer to the KXTI9 instance data. +//! \param pfAccelX is a pointer to the value into which the X-axis +//! acceleration data is stored. +//! \param pfAccelY is a pointer to the value into which the Y-axis +//! acceleration data is stored. +//! \param pfAccelZ is a pointer to the value into which the Z-axis +//! acceleration data is stored. +//! +//! This function returns the acceleration data from the most recent data read, +//! converted into g. If any of the output data pointers are \b NULL, the +//! corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +KXTI9DataAccelGetFloat(tKXTI9 *psInst, float *pfAccelX, float *pfAccelY, + float *pfAccelZ) +{ + float fFactor; + int16_t iX, iY, iZ; + + // + // Get the acceleration conversion factor for the current range. + // + fFactor = (psInst->ui8Resolution == 0) ? g_fAccelFactors8[psInst->ui8Range] : + g_fAccelFactors12[psInst->ui8Range]; + + if(psInst->ui8Resolution) + { + // + // Get conversion data and store in temporary variables. + // + iX = (int16_t)((psInst->pui8Data[1] << 4) | (psInst->pui8Data[0] >> 4)); + iY = (int16_t)((psInst->pui8Data[3] << 4) | (psInst->pui8Data[2] >> 4)); + iZ = (int16_t)((psInst->pui8Data[5] << 4) | (psInst->pui8Data[4] >> 4)); + + // + // Sign extend 12-bit data. + // + iX |= (iX & 0x800) ? 0xf000 : 0; + iY |= (iY & 0x800) ? 0xf000 : 0; + iZ |= (iZ & 0x800) ? 0xf000 : 0; + } + else + { + // + // Chop off the lower 4 bits of the data. 8-bit mode only returns 8 + // valid bits, but can have garbage in the lower 4. + // + iX = (int16_t)(psInst->pui8Data[1]); + iY = (int16_t)(psInst->pui8Data[3]); + iZ = (int16_t)(psInst->pui8Data[5]); + + // + // Sign extend 8-bit data. + // + iX |= (iX & 0x80) ? 0xff00 : 0; + iY |= (iY & 0x80) ? 0xff00 : 0; + iZ |= (iZ & 0x80) ? 0xff00 : 0; + } + + // + // Convert the acceleration values into floating-point g values. + // + if(pfAccelX) + { + *pfAccelX = (float)(iX) * fFactor; + } + if(pfAccelY) + { + *pfAccelY = (float)(iY) * fFactor; + } + if(pfAccelZ) + { + *pfAccelZ = (float)(iZ) * fFactor; + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/kxti9.h b/sensorlib/kxti9.h new file mode 100644 index 0000000..1c8e7fc --- /dev/null +++ b/sensorlib/kxti9.h @@ -0,0 +1,170 @@ +//***************************************************************************** +// +// KXTI9.h - Prototypes for the KXTI9 accelerometer driver. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_KXTI9_H__ +#define __SENSORLIB_KXTI9_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the KXTI9 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the KXTI9. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the KXTI9. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the KXTI9. + // + uint8_t ui8State; + + // + // The ADC resolution of the KXTI9. + // + uint8_t ui8Resolution; + + uint8_t ui8NewResolution; + + // + // The current operating range (g force) of the KXTI9. + // + uint8_t ui8Range; + + uint8_t ui8NewRange; + + // + // The data buffer used for sending/receiving data to/from the KXTI9. + // + uint8_t pui8Data[7]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The write state used to write register values. + // + struct + { + // + // The buffer that is being written to the KXTI9. + // + uint8_t *pui8Data; + + // + // The number of bytes being written to the KXTI9. + // + uint16_t ui16Count; + } + sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tKXTI9; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t KXTI9Init(tKXTI9 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t KXTI9Read(tKXTI9 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t KXTI9Write(tKXTI9 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t KXTI9ReadModifyWrite(tKXTI9 *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t KXTI9DataRead(tKXTI9 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void KXTI9DataAccelGetRaw(tKXTI9 *psInst, uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, + uint_fast16_t *pui16AccelZ); +extern void KXTI9DataAccelGetFloat(tKXTI9 *psInst, float *pfAccelX, + float *pfAccelY, float *pfAccelZ); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_KXTI9_H__ diff --git a/sensorlib/l3gd20h.c b/sensorlib/l3gd20h.c new file mode 100644 index 0000000..6694660 --- /dev/null +++ b/sensorlib/l3gd20h.c @@ -0,0 +1,722 @@ +//***************************************************************************** +// +// l3gd20h.c - Driver for the ST L3GD20H gyroscope. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_l3gd20h.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/l3gd20h.h" + +//***************************************************************************** +// +//! \addtogroup l3gd20h_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the L3GD20H state machine. +// +//***************************************************************************** +#define L3GD20H_STATE_IDLE 0 // State machine is idle +#define L3GD20H_STATE_INIT_RES 1 // Waiting for initialization +#define L3GD20H_STATE_INIT_WAIT 2 // Waiting for reset to complete +#define L3GD20H_STATE_READ 3 // Waiting for read +#define L3GD20H_STATE_WRITE 4 // Waiting for write +#define L3GD20H_STATE_RMW 5 // Waiting for read-modify-write + +//***************************************************************************** +// +// The factors used to convert the gyroscope readings from the L3GD20H into +// floating point values in radians per second. +// +// Per the data-sheet, the sensitivity is 8.75, 17.50, and 70.00 mdps/digit for +// for 245, 500, and 2000 DPS scales respectively. +// +// mdeg 1 deg PI rad +// 8.75 ---- * ---- * --- = 1.5271630955e-4f rad/sec per digit +// sec 1000 mdeg 180 deg +// +// Values are obtained by taking the degree per second conversion factors +// from the data sheet and then converting to radians per sec (1 degree = +// 0.0174532925 radians). +// +//***************************************************************************** +static const float g_pfL3GD20HGyroFactors[] = +{ + 1.5271631e-5f, // Range = +/- 245 dps + 3.0543262e-4f, // Range = +/- 500 dps + 1.2217305e-3f, // Range = +/- 2000 dps + 1.2217305e-3f // Range = +/- 2000 dps +}; + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// L3GD20H have completed. +// +//***************************************************************************** +static void +L3GD20HCallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tL3GD20H *psInst; + + // + // Convert the instance data into a pointer to a tL3GD20H structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = L3GD20H_STATE_IDLE; + } + + // + // Determine the current state of the L3GD20H state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case L3GD20H_STATE_READ: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = L3GD20H_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // L3GD20H Device reset was issued + // + case L3GD20H_STATE_INIT_RES: + { + // + // Issue a read of the status register to confirm reset is done. + // + psInst->uCommand.pui8Buffer[0] = L3GD20H_O_LOW_ODR; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 1, + L3GD20HCallback, psInst); + + psInst->ui8State = L3GD20H_STATE_INIT_WAIT; + + // + // Done. + // + break; + } + + // + // Status register was read, check if reset is done before proceeding. + // + case L3GD20H_STATE_INIT_WAIT: + { + // + // Check the value read back from status to determine if device + // is still in reset or if it is ready. + // + if(psInst->pui8Data[0] & L3GD20H_LOW_ODR_SWRESET_M) + { + // + // Device still in reset so begin polling this register. + // + psInst->uCommand.pui8Buffer[0] = L3GD20H_O_LOW_ODR; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 1, + L3GD20HCallback, psInst); + } + else + { + // + // Device is out of reset, move to the idle state. + // + psInst->ui8State = L3GD20H_STATE_IDLE; + } + + // + // Done. + // + break; + } + + // + // A write just completed + // + case L3GD20H_STATE_WRITE: + { + // + // Set the gyroscope ranges to the new values. If the register was + // not modified, the values will be the same so this has no effect. + // + psInst->ui8GyroFsSel = psInst->ui8NewGyroFsSel; + + // + // The state machine is now idle. + // + psInst->ui8State = L3GD20H_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A read-modify-write just completed + // + case L3GD20H_STATE_RMW: + { + // + // See if the PWR_MGMT_1 register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + L3GD20H_O_LOW_ODR) + { + // + // See if a soft reset has been issued. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + L3GD20H_LOW_ODR_SWRESET_M) + { + // + // Default range setting is +/- 245 degrees/s + // + psInst->ui8GyroFsSel = 0; + psInst->ui8NewGyroFsSel = 0; + } + } + + // + // See if the GYRO_CONFIG register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + L3GD20H_O_CTRL4) + { + // + // Extract the FS_SEL from the GYRO_CONFIG register value. + // + psInst->ui8GyroFsSel = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + L3GD20H_CTRL4_FS_M) >> + L3GD20H_CTRL4_FS_S); + } + + // + // The state machine is now idle. + // + psInst->ui8State = L3GD20H_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == L3GD20H_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the L3GD20H driver. +//! +//! \param psInst is a pointer to the L3GD20H instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the L3GD20H device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the L3GD20H driver, preparing it for operation. +//! +//! \return Returns 1 if the L3GD20H driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +L3GD20HInit(tL3GD20H *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Initialize the L3GD20H instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Default range setting is +/- 245 degrees/s + // + psInst->ui8GyroFsSel = + (L3GD20H_CTRL4_FS_245DPS & L3GD20H_CTRL4_FS_M) >> L3GD20H_CTRL4_FS_S; + psInst->ui8NewGyroFsSel = + (L3GD20H_CTRL4_FS_245DPS & L3GD20H_CTRL4_FS_M) >> L3GD20H_CTRL4_FS_S; + + // + // Set the state to show we are initiating a reset. + // + psInst->ui8State = L3GD20H_STATE_INIT_RES; + + // + // Load the buffer with command to perform device reset + // + psInst->pui8Data[0] = L3GD20H_O_LOW_ODR; + psInst->pui8Data[1] = L3GD20H_LOW_ODR_SWRESET_RESET; + if(I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, L3GD20HCallback, psInst) == 0) + { + psInst->ui8State = L3GD20H_STATE_IDLE; + return(0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from L3GD20H registers. +//! +//! \param psInst is a pointer to the L3GD20H instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the L3GD20H. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +L3GD20HRead(tL3GD20H *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the L3GD20H driver is not idle (in other words, + // there is already an outstanding request to the L3GD20H). + // + if(psInst->ui8State != L3GD20H_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = L3GD20H_STATE_READ; + + // + // Read the requested registers from the L3GD20H. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + L3GD20HCallback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = L3GD20H_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to L3GD20H registers. +//! +//! \param psInst is a pointer to the L3GD20H instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the L3GD20H. The first byte of the \e pui8Data buffer contains the value +//! to be written into the \e ui8Reg register, the second value contains the +//! data to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +L3GD20HWrite(tL3GD20H *psInst, uint_fast8_t ui8Reg, const uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the L3GD20H driver is not idle (in other words, + // there is already an outstanding request to the L3GD20H). + // + if(psInst->ui8State != L3GD20H_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if the PWR_MGMT_1 register is being written. + // + if((ui8Reg <= L3GD20H_O_LOW_ODR) && + ((ui8Reg + ui16Count) > L3GD20H_O_LOW_ODR)) + { + // + // See if a soft reset is being requested. + // + if(pui8Data[ui8Reg - L3GD20H_O_LOW_ODR] & L3GD20H_LOW_ODR_SWRESET_M) + { + // + // Default range setting is +/- 245 degrees/s. + // + psInst->ui8NewGyroFsSel = 0; + } + } + + // + // See if the GYRO_CONFIG register is being written. + // + if((ui8Reg <= L3GD20H_O_CTRL4) && + ((ui8Reg + ui16Count) > L3GD20H_O_CTRL4)) + { + // + // Extract the FS_SEL from the GYRO_CONFIG register value. + // + psInst->ui8NewGyroFsSel = ((pui8Data[ui8Reg - L3GD20H_O_CTRL4] & + L3GD20H_CTRL4_FS_M) >> + L3GD20H_CTRL4_FS_S); + } + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = L3GD20H_STATE_WRITE; + + // + // Write the requested registers to the L3GD20H. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, + L3GD20HCallback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = L3GD20H_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a L3GD20H register. +//! +//! \param psInst is a pointer to the L3GD20H instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the L3GD20H via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! L3GD20H. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +L3GD20HReadModifyWrite(tL3GD20H *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the L3GD20H driver is not idle (in other words, + // there is already an outstanding request to the L3GD20H). + // + if(psInst->ui8State != L3GD20H_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = L3GD20H_STATE_RMW; + + // + // Submit the read-modify-write request to the L3GD20H. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, L3GD20HCallback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = L3GD20H_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the gyroscope data from the L3GD20H. +//! +//! \param psInst is a pointer to the L3GD20H instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the L3GD20H data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - L3GD20HDataGyroGetRaw() +//! - L3GD20HDataGyroGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +L3GD20HDataRead(tL3GD20H *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the L3GD20H driver is not idle (in other words, + // there is already an outstanding request to the L3GD20H). + // + if(psInst->ui8State != L3GD20H_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = L3GD20H_STATE_READ; + + // + // Read the data registers from the L3GD20H. + // + psInst->pui8Data[0] = L3GD20H_O_STATUS | 0x80; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, 1, + psInst->pui8Data, 7, L3GD20HCallback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = L3GD20H_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw gyroscope data from the most recent data read. +//! +//! \param psInst is a pointer to the L3GD20H instance data. +//! \param pui16GyroX is a pointer to the value into which the raw X-axis +//! gyroscope data is stored. +//! \param pui16GyroY is a pointer to the value into which the raw Y-axis +//! gyroscope data is stored. +//! \param pui16GyroZ is a pointer to the value into which the raw Z-axis +//! gyroscope data is stored. +//! +//! This function returns the raw gyroscope data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +L3GD20HDataGyroGetRaw(tL3GD20H *psInst, uint_fast16_t *pui16GyroX, + uint_fast16_t *pui16GyroY, uint_fast16_t *pui16GyroZ) +{ + // + // Return the raw gyroscope values. + // + if(pui16GyroX) + { + *pui16GyroX = (psInst->pui8Data[2] << 8) | psInst->pui8Data[1]; + } + if(pui16GyroY) + { + *pui16GyroY = (psInst->pui8Data[4] << 8) | psInst->pui8Data[3]; + } + if(pui16GyroZ) + { + *pui16GyroZ = (psInst->pui8Data[6] << 8) | psInst->pui8Data[5]; + } +} + +//***************************************************************************** +// +//! Gets the gyroscope data from the most recent data read. +//! +//! \param psInst is a pointer to the L3GD20H instance data. +//! \param pfGyroX is a pointer to the value into which the X-axis gyroscope +//! data is stored. +//! \param pfGyroY is a pointer to the value into which the Y-axis gyroscope +//! data is stored. +//! \param pfGyroZ is a pointer to the value into which the Z-axis gyroscope +//! data is stored. +//! +//! This function returns the gyroscope data from the most recent data read, +//! converted into radians per second. If any of the output data pointers are +//! \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +L3GD20HDataGyroGetFloat(tL3GD20H *psInst, float *pfGyroX, float *pfGyroY, + float *pfGyroZ) +{ + float fFactor; + + // + // Get the conversion factor for the current data format. + // + fFactor = g_pfL3GD20HGyroFactors[psInst->ui8GyroFsSel]; + + // + // Convert the gyroscope values into rad/sec. + // + if(pfGyroX) + { + *pfGyroX = ((float)(int16_t)((psInst->pui8Data[2] << 8) | + psInst->pui8Data[1]) * fFactor); + } + if(pfGyroY) + { + *pfGyroY = ((float)(int16_t)((psInst->pui8Data[4] << 8) | + psInst->pui8Data[3]) * fFactor); + } + if(pfGyroZ) + { + *pfGyroZ = ((float)(int16_t)((psInst->pui8Data[6] << 8) | + psInst->pui8Data[5]) * fFactor); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/l3gd20h.h b/sensorlib/l3gd20h.h new file mode 100644 index 0000000..c56bf36 --- /dev/null +++ b/sensorlib/l3gd20h.h @@ -0,0 +1,158 @@ +//***************************************************************************** +// +// l3gd20h.c - Driver for the ST L3GD20H gyroscope. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_L3GD20H_H__ +#define __SENSORLIB_L3GD20H_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the L3GD20H driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the L3GD20H. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the L3GD20H. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the L3GD20H. + // + uint8_t ui8State; + + // + // The current gyroscope fs_sel setting. + // + uint8_t ui8GyroFsSel; + + // + // The new gyroscope fs_sel setting, which is used when a register write + // succeeds. + // + uint8_t ui8NewGyroFsSel; + + // + // The data buffer used for sending/receiving data to/from the L3GD20H. + // We need 7 bytes (1 status + 3axis * 2bytes per axis) + // + uint8_t pui8Data[8]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The callback data provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tL3GD20H; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t L3GD20HInit(tL3GD20H *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t L3GD20HRead(tL3GD20H *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t L3GD20HWrite(tL3GD20H *psInst, uint_fast8_t ui8Reg, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t L3GD20HReadModifyWrite(tL3GD20H *psInst, + uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t L3GD20HDataRead(tL3GD20H *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void L3GD20HDataGyroGetRaw(tL3GD20H *psInst, uint_fast16_t *pui16GyroX, + uint_fast16_t *pui16GyroY, + uint_fast16_t *pui16GyroZ); +extern void L3GD20HDataGyroGetFloat(tL3GD20H *psInst, float *pfGyroX, + float *pfGyroY, float *pfGyroZ); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_L3GD20H_H__ diff --git a/sensorlib/lsm303d.c b/sensorlib/lsm303d.c new file mode 100644 index 0000000..e21a5b8 --- /dev/null +++ b/sensorlib/lsm303d.c @@ -0,0 +1,835 @@ +//***************************************************************************** +// +// lsm303d.c - Driver for the ST LSM303D accelerometer/magnetometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_lsm303d.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/lsm303d.h" + +//***************************************************************************** +// +//! \addtogroup lsm303dlhc_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the LSM303D state machine. +// +//***************************************************************************** +#define LSM303D_STATE_IDLE 0 // State machine is idle +#define LSM303D_STATE_INIT 1 // Waiting for init +#define LSM303D_STATE_READ_MAG \ + 2 // Waiting for mag read +#define LSM303D_STATE_READ_ACCEL \ + 3 // Waiting for accel read +#define LSM303D_STATE_WRITE 4 // Waiting for write +#define LSM303D_STATE_RMW 5 // Waiting for read-modify-write + +//***************************************************************************** +// +// The factors used to convert the acceleration readings from the LSM303D +// into floating point values in meters per second squared. +// +// Values are obtained by taking the g conversion factors from the data sheet +// and multiplying by 9.81 (1 g = 9.81 m/s^2). +// +//***************************************************************************** +static const float g_pfLSM303DAccelFactors[] = +{ + 0.00059875, // Range = +/- 2 g (16384 lsb/g) + 0.00119751, // Range = +/- 4 g (8192 lsb/g) + 0.00239502, // Range = +/- 8 g (4096 lsb/g) + 0.00479004 // Range = +/- 16 g (2048 lsb/g) +}; +static const float g_pfLSM303DMagFactors[] = +{ + 8.0e-6f, // Range = +/- 2 (0.080 mgauss/lsb) + 1.6e-5f, // Range = +/- 4 (0.160 mgauss/lsb) + 3.2e-5f, // Range = +/- 8 (0.320 mgauss/lsb) + 4.79e-5f // Range = +/- 12 (0.479 mgauss/lsb) +}; +// +// Uninitialized values will default to zero which is what we want. 0x80 is +// ORed into the register address so the writes auto-increment +// +static const uint8_t g_pui8ZeroInit[] = +{ + 0x80 | LSM303D_O_MAG_INT_CTRL, + 0xE8, // MAG_INT_CTRL + 0x0, // int_src (RO) + 0x0, // THS_LSB + 0x0, // THS_MSB + 0x0, // OFFSET_X_LSB + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, // REF_X + 0x0, + 0x0, + 0x0, // CTRL0 + 0x7, + 0x0, + 0x0, + 0x0, + 0x18, // CTRL5 + 0x20, + 0x1, + 0x0, // status (RO) + 0x0, // out_x_lsb (RO) + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, // FIFO_CTRL + 0x0, // fifo_src (RO) + 0x0, // IG_CFG1 + 0x0, // ig_src1 (RO) + 0x0, + 0x0, + 0x0, + 0x0, // ig_src2 (RO) + 0x0, + 0x0, + 0x0, + 0x0, // clk_src (RO) + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0 +}; + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// LSM303D have completed. +// +//***************************************************************************** +static void +LSM303DCallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tLSM303D *psInst; + + // + // Convert the instance data into a pointer to a tLSM303D structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = LSM303D_STATE_IDLE; + } + + // + // Determine the current state of the LSM303D state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = LSM303D_STATE_IDLE; + + // + // Done. + // + break; + } + + case LSM303D_STATE_READ_MAG: + { + // + // Move the state machine to the wait for accel data read state. + // + psInst->ui8State = LSM303D_STATE_READ_ACCEL; + + // + // Read the accel data registers from the LSM303D. + // + psInst->pui8DataAccel[0] = LSM303D_O_STATUS | 0x80; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8DataAccel, + 1, psInst->pui8DataAccel, 7, LSM303DCallback, psInst); + + // + // Done. + // + break; + } + case LSM303D_STATE_INIT: + { + psInst->ui8State = LSM303D_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A write just completed + // + case LSM303D_STATE_WRITE: + { + // + // Set the accelerometer ranges to the new values. If the register + // was not modified, the values will be the same so this has no + // effect. + // + psInst->ui8AccelFSSel = psInst->ui8NewAccelFSSel; + psInst->ui8MagFSSel = psInst->ui8NewMagFSSel; + + // + // The state machine is now idle. + // + psInst->ui8State = LSM303D_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A read-modify-write just completed + // + case LSM303D_STATE_RMW: + { + // + // See if the accel scale register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + LSM303D_O_CTRL2) + { + // + // Extract the FS_SEL from the ACCEL_CONFIG register value. + // + psInst->ui8AccelFSSel = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + LSM303D_CTRL2_AFS_M) >> LSM303D_CTRL2_AFS_S); + } + + // + // See if the mag scale register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + LSM303D_O_CTRL6) + { + // + // Extract the FS_SEL from the mag scale register value. + // + psInst->ui8MagFSSel = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + LSM303D_CTRL6_MFS_M) >> LSM303D_CTRL6_MFS_S); + + } + + // + // The state machine is now idle. + // + psInst->ui8State = LSM303D_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == LSM303D_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the LSM303D driver. +//! +//! \param psInst is a pointer to the LSM303D instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the LSM303D device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the LSM303D driver, preparing it for +//! operation. +//! +//! \return Returns 1 if the LSM303D driver was successfully initialized and +//! 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +LSM303DInit(tLSM303D *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Initialize the LSM303D instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Default range setting is +/- 2 g + // + psInst->ui8AccelFSSel = (LSM303D_CTRL2_AFS_2G >> LSM303D_CTRL2_AFS_S); + psInst->ui8NewAccelFSSel = (LSM303D_CTRL2_AFS_2G >> LSM303D_CTRL2_AFS_S); + psInst->ui8MagFSSel = (LSM303D_CTRL6_MFS_2G >> LSM303D_CTRL6_MFS_S); + psInst->ui8NewMagFSSel = (LSM303D_CTRL6_MFS_2G >> LSM303D_CTRL6_MFS_S); + + // + // There is no soft reset on the LSM303. Force registers back to their + // spec'ed POR defaults. + // + psInst->ui8State = LSM303D_STATE_INIT; + if(I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, g_pui8ZeroInit, + sizeof(g_pui8ZeroInit), LSM303DCallback, (void *)psInst) == 0) + { + psInst->ui8State = LSM303D_STATE_IDLE; + return(0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from LSM303D registers. +//! +//! \param psInst is a pointer to the LSM303D instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the LSM303D. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +LSM303DRead(tLSM303D *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the LSM303D driver is not idle (in other words, + // there is already an outstanding request to the LSM303D). + // + if(psInst->ui8State != LSM303D_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = LSM303D_STATE_READ_MAG; + + // + // Read the requested registers from the LSM303D. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + LSM303DCallback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = LSM303D_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to LSM303D registers. +//! +//! \param psInst is a pointer to the LSM303D instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the LSM303D. The first byte of the \e pui8Data buffer contains the +//! value to be written into the \e ui8Reg register, the second value contains +//! the data to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +LSM303DWrite(tLSM303D *psInst, uint_fast8_t ui8Reg, + const uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the LSM303D driver is not idle (in other words, + // there is already an outstanding request to the LSM303D). + // + if(psInst->ui8State != LSM303D_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if the accel full scale select register is being written. + // + if((ui8Reg <= LSM303D_O_CTRL2) && + ((ui8Reg + ui16Count) > LSM303D_O_CTRL2)) + { + // + // Extract the AFS_SEL from the ACCEL_CONFIG register value. + // + psInst->ui8NewAccelFSSel = + ((pui8Data[ui8Reg - LSM303D_O_CTRL2] & + LSM303D_CTRL2_AFS_M) >> LSM303D_CTRL2_AFS_S); + } + + // + // See if the mag full scale select register is being written. + // + if((ui8Reg <= LSM303D_O_CTRL6) && + ((ui8Reg + ui16Count) > LSM303D_O_CTRL6)) + { + // + // Extract the AFS_SEL from the ACCEL_CONFIG register value. + // + psInst->ui8NewMagFSSel = + ((pui8Data[ui8Reg - LSM303D_O_CTRL6] & + LSM303D_CTRL6_MFS_M) >> LSM303D_CTRL6_MFS_S); + } + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = LSM303D_STATE_WRITE; + + // + // Write the requested registers to the LSM303D. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, + LSM303DCallback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = LSM303D_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a LSM303D register. +//! +//! \param psInst is a pointer to the LSM303D instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the LSM303D via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! LSM303D. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +LSM303DReadModifyWrite(tLSM303D *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the LSM303D driver is not idle (in other words, + // there is already an outstanding request to the LSM303D). + // + if(psInst->ui8State != LSM303D_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = LSM303D_STATE_RMW; + + // + // Submit the read-modify-write request to the LSM303D. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, LSM303DCallback, + psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = LSM303D_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the accelerometer data from the LSM303D. +//! +//! \param psInst is a pointer to the LSM303D instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the LSM303D data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - LSM303DDataAccelGetRaw() +//! - LSM303DDataAccelGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +LSM303DDataRead(tLSM303D *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the LSM303D driver is not idle (in other words, + // there is already an outstanding request to the LSM303D). + // + if(psInst->ui8State != LSM303D_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for mag data read state. + // + psInst->ui8State = LSM303D_STATE_READ_MAG; + + // + // Read the data registers from the LSM303D. + // + psInst->pui8DataMag[0] = LSM303D_O_MAG_STATUS | 0x80; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8DataMag, 1, + psInst->pui8DataMag, 7, LSM303DCallback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = LSM303D_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw accelerometer data from the most recent data read. +//! +//! \param psInst is a pointer to the LSM303D instance data. +//! \param pui16AccelX is a pointer to the value into which the raw X-axis +//! accelerometer data is stored. +//! \param pui16AccelY is a pointer to the value into which the raw Y-axis +//! accelerometer data is stored. +//! \param pui16AccelZ is a pointer to the value into which the raw Z-axis +//! accelerometer data is stored. +//! +//! This function returns the raw accelerometer data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +LSM303DDataAccelGetRaw(tLSM303D *psInst, + uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, + uint_fast16_t *pui16AccelZ) +{ + // + // Return the raw accelerometer values. + // + if(pui16AccelX) + { + *pui16AccelX = (psInst->pui8DataAccel[2] << 8) | psInst->pui8DataAccel[1]; + } + if(pui16AccelY) + { + *pui16AccelY = (psInst->pui8DataAccel[4] << 8) | psInst->pui8DataAccel[3]; + } + if(pui16AccelZ) + { + *pui16AccelZ = (psInst->pui8DataAccel[6] << 8) | psInst->pui8DataAccel[5]; + } +} + +//***************************************************************************** +// +//! Gets the raw accelerometer data from the most recent data read. +//! +//! \param psInst is a pointer to the LSM303D instance data. +//! \param pui16AccelX is a pointer to the value into which the raw X-axis +//! accelerometer data is stored. +//! \param pui16AccelY is a pointer to the value into which the raw Y-axis +//! accelerometer data is stored. +//! \param pui16AccelZ is a pointer to the value into which the raw Z-axis +//! accelerometer data is stored. +//! +//! This function returns the raw accelerometer data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +LSM303DDataMagnetoGetRaw(tLSM303D *psInst, + uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, + uint_fast16_t *pui16AccelZ) +{ + // + // Return the raw accelerometer values. + // + if(pui16AccelX) + { + *pui16AccelX = (psInst->pui8DataMag[2] << 8) | psInst->pui8DataMag[1]; + } + if(pui16AccelY) + { + *pui16AccelY = (psInst->pui8DataMag[4] << 8) | psInst->pui8DataMag[3]; + } + if(pui16AccelZ) + { + *pui16AccelZ = (psInst->pui8DataMag[6] << 8) | psInst->pui8DataMag[5]; + } +} + +//***************************************************************************** +// +//! Gets the accelerometer data from the most recent data read. +//! +//! \param psInst is a pointer to the LSM303D instance data. +//! \param pfAccelX is a pointer to the value into which the X-axis +//! accelerometer data is stored. +//! \param pfAccelY is a pointer to the value into which the Y-axis +//! accelerometer data is stored. +//! \param pfAccelZ is a pointer to the value into which the Z-axis +//! accelerometer data is stored. +//! +//! This function returns the accelerometer data from the most recent data +//! read, converted into meters per second squared (m/s^2). If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +LSM303DDataAccelGetFloat(tLSM303D *psInst, float *pfAccelX, + float *pfAccelY, float *pfAccelZ) +{ + float fFactor; + + // + // Get the acceleration conversion factor for the current data format. + // + fFactor = g_pfLSM303DAccelFactors[psInst->ui8AccelFSSel]; + + // + // Convert the Accelerometer values into floating-point gravity values. + // + if(pfAccelX) + { + *pfAccelX = (float)(((int16_t)((psInst->pui8DataAccel[2] << 8) | + psInst->pui8DataAccel[1])) * fFactor); + } + if(pfAccelY) + { + *pfAccelY = (float)(((int16_t)((psInst->pui8DataAccel[4] << 8) | + psInst->pui8DataAccel[3])) * fFactor); + } + if(pfAccelZ) + { + *pfAccelZ = (float)(((int16_t)((psInst->pui8DataAccel[6] << 8) | + psInst->pui8DataAccel[5])) * fFactor); + } +} + +//***************************************************************************** +// +//! Gets the magnetometer data from the most recent data read. +//! +//! \param psInst is a pointer to the LSM303D instance data. +//! \param pfMagX is a pointer to the value into which the X-axis +//! accelerometer data is stored. +//! \param pfMagY is a pointer to the value into which the Y-axis +//! accelerometer data is stored. +//! \param pfMagZ is a pointer to the value into which the Z-axis +//! accelerometer data is stored. +//! +//! This function returns the magnetometer data from the most recent data +//! read, converted into tesla. If any of the output data pointers are +//! \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +LSM303DDataMagnetoGetFloat(tLSM303D *psInst, float *pfMagX, + float *pfMagY, float *pfMagZ) +{ + float fFactor; + + // + // Get the magnetometer conversion factor for the current data format. + // + fFactor = g_pfLSM303DMagFactors[psInst->ui8MagFSSel]; + + // + // Convert the Accelerometer values into floating-point gravity values. + // + if(pfMagX) + { + *pfMagX = (float)(((int16_t)((psInst->pui8DataMag[2] << 8) | + psInst->pui8DataMag[1])) * fFactor); + } + if(pfMagY) + { + *pfMagY = (float)(((int16_t)((psInst->pui8DataMag[4] << 8) | + psInst->pui8DataMag[3])) * fFactor); + } + if(pfMagZ) + { + *pfMagZ = (float)(((int16_t)((psInst->pui8DataMag[6] << 8) | + psInst->pui8DataMag[5])) * fFactor); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/lsm303d.h b/sensorlib/lsm303d.h new file mode 100644 index 0000000..a236e0f --- /dev/null +++ b/sensorlib/lsm303d.h @@ -0,0 +1,186 @@ +//***************************************************************************** +// +// lsm303d.h - Driver for the ST LSM303D accelerometer/magnetometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_LSM303D_H__ +#define __SENSORLIB_LSM303D_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the LSM303DLHC driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the LSM303DLHC. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the LSM303DLHC. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the LSM303DLHC. + // + uint8_t ui8State; + + // + // The current accelerometer afs_sel setting. + // + uint8_t ui8AccelFSSel; + + // + // The new accelerometer afs_sel setting, which is used when a register + // write succeeds. + // + uint8_t ui8NewAccelFSSel; + + // + // The current accelerometer afs_sel setting. + // + uint8_t ui8MagFSSel; + + // + // The new accelerometer afs_sel setting, which is used when a register + // write succeeds. + // + uint8_t ui8NewMagFSSel; + + // + // The data buffers used for sending/receiving data to/from the LSM303DLHC. + // + uint8_t pui8DataMag[8]; + + // + // The data buffers used for sending/receiving data to/from the LSM303DLHC. + // + uint8_t pui8DataAccel[8]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The callback data provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tLSM303D; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t LSM303DInit(tLSM303D *psInst, + tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DRead(tLSM303D *psInst, + uint_fast8_t ui8Reg, + uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DWrite(tLSM303D *psInst, + uint_fast8_t ui8Reg, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DReadModifyWrite(tLSM303D *psInst, + uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallbak, + void *pvCallbackData); +extern uint_fast8_t LSM303DDataRead(tLSM303D *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void LSM303DDataAccelGetRaw(tLSM303D *psInst, + uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, + uint_fast16_t *pui16AccelZ); +extern void LSM303DDataAccelGetFloat(tLSM303D *psInst, + float *pfAccelX, float *pfAccelY, + float *pfAccelZ); +extern void LSM303DDataMagnetoGetRaw(tLSM303D *psInst, + uint_fast16_t *pui16MagX, + uint_fast16_t *pui16MagY, + uint_fast16_t *pui16MagZ); +extern void LSM303DDataMagnetoGetFloat(tLSM303D *psInst, + float *pfMagX, float *pfMagY, + float *pfMagZ); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_LSM303D_H__ diff --git a/sensorlib/lsm303dlhc_accel.c b/sensorlib/lsm303dlhc_accel.c new file mode 100644 index 0000000..ad46b37 --- /dev/null +++ b/sensorlib/lsm303dlhc_accel.c @@ -0,0 +1,662 @@ +//***************************************************************************** +// +// lsm303dlhc.c - Driver for the ST LSM303DLHC accelerometer +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_lsm303dlhc.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/lsm303dlhc_accel.h" + +//***************************************************************************** +// +//! \addtogroup lsm303dlhc_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the LSM303DLHC state machine. +// +//***************************************************************************** +#define LSM303DLHC_STATE_IDLE 0 // State machine is idle +#define LSM303DLHC_STATE_INIT 1 // Waiting for init +#define LSM303DLHC_STATE_READ 2 // Waiting for read +#define LSM303DLHC_STATE_WRITE 3 // Waiting for write +#define LSM303DLHC_STATE_RMW 4 // Waiting for read-modify-write + +//***************************************************************************** +// +// The factors used to convert the acceleration readings from the LSM303DLHC +// into floating point values in meters per second squared. +// +// Values are obtained by taking the g conversion factors from the data sheet +// and multiplying by 9.81 (1 g = 9.81 m/s^2). +// +//***************************************************************************** +static const float g_pfLSM303DLHCAccelFactors[] = +{ + 0.00059875, // Range = +/- 2 g (16384 lsb/g) + 0.00119751, // Range = +/- 4 g (8192 lsb/g) + 0.00239502, // Range = +/- 8 g (4096 lsb/g) + 0.00479004 // Range = +/- 16 g (2048 lsb/g) +}; + +// +// Uninitialized values will default to zero which is what we want. 0x80 is +// ORed into the register address so the writes auto-increment +// +static const uint8_t g_pui8ZeroCtrl1[8] = +{ + 0x80 | LSM303DLHC_O_CTRL1 +}; +static const uint8_t g_pui8ZeroFifoCtl[14] = +{ + 0x80 | LSM303DLHC_O_FIFO_CTRL +}; + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// LSM303DLHC have completed. +// +//***************************************************************************** +static void +LSM303DLHCCallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tLSM303DLHCAccel *psInst; + + // + // Convert the instance data into a pointer to a tLSM303DLHC structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = LSM303DLHC_STATE_IDLE; + } + + // + // Determine the current state of the LSM303DLHC state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case LSM303DLHC_STATE_READ: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + + // + // Done. + // + break; + } + + case LSM303DLHC_STATE_INIT: + { + psInst->ui8State = LSM303DLHC_STATE_IDLE; + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, g_pui8ZeroFifoCtl, + 14, LSM303DLHCCallback, pvCallbackData); + + // + // Done. + // + break; + } + + // + // A write just completed + // + case LSM303DLHC_STATE_WRITE: + { + // + // Set the accelerometer ranges to the new values. If the register + // was not modified, the values will be the same so this has no + // effect. + // + psInst->ui8AccelAfsSel = psInst->ui8NewAccelAfsSel; + + // + // The state machine is now idle. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A read-modify-write just completed + // + case LSM303DLHC_STATE_RMW: + { + // + // See if the ACCEL_CONFIG register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + LSM303DLHC_O_CTRL4) + { + // + // Extract the FS_SEL from the ACCEL_CONFIG register value. + // + psInst->ui8AccelAfsSel = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + LSM303DLHC_CTRL4_FS_M) >> LSM303DLHC_CTRL4_FS_S); + } + + // + // The state machine is now idle. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == LSM303DLHC_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the LSM303DLHC driver. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the LSM303DLHC device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the LSM303DLHC driver, preparing it for +//! operation. +//! +//! \return Returns 1 if the LSM303DLHC driver was successfully initialized and +//! 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCAccelInit(tLSM303DLHCAccel *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Initialize the LSM303DLHC instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Default range setting is +/- 2 g + // + psInst->ui8AccelAfsSel = (LSM303DLHC_CTRL4_FS_2G >> LSM303DLHC_CTRL4_FS_S); + psInst->ui8NewAccelAfsSel = (LSM303DLHC_CTRL4_FS_2G >> LSM303DLHC_CTRL4_FS_S); + + // + // There is no soft reset on the LSM303. Force registers back to their + // spec'ed POR defaults. + // + psInst->ui8State = LSM303DLHC_STATE_INIT; + if(I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, g_pui8ZeroCtrl1, 7, + LSM303DLHCCallback, (void *)psInst) == 0) + { + psInst->ui8State = LSM303DLHC_STATE_IDLE; + return(0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from LSM303DLHC registers. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the LSM303DLHC. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCAccelRead(tLSM303DLHCAccel *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the LSM303DLHC driver is not idle (in other words, + // there is already an outstanding request to the LSM303DLHC). + // + if(psInst->ui8State != LSM303DLHC_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = LSM303DLHC_STATE_READ; + + // + // Read the requested registers from the LSM303DLHC. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + LSM303DLHCCallback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to LSM303DLHC registers. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the LSM303DLHC. The first byte of the \e pui8Data buffer contains the +//! value to be written into the \e ui8Reg register, the second value contains +//! the data to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCAccelWrite(tLSM303DLHCAccel *psInst, uint_fast8_t ui8Reg, + const uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the LSM303DLHC driver is not idle (in other words, + // there is already an outstanding request to the LSM303DLHC). + // + if(psInst->ui8State != LSM303DLHC_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if they're rebooting via CTRL5 + // + if((ui8Reg <= LSM303DLHC_O_CTRL5) && + ((ui8Reg + ui16Count) > LSM303DLHC_O_CTRL5)) + { + // + // See if a soft reset is being requested. + // + if(pui8Data[ui8Reg - LSM303DLHC_O_CTRL5] & LSM303DLHC_CTRL5_REBOOTCTL_M) + { + // + // Default range setting is +/- 2 g. + // + psInst->ui8NewAccelAfsSel = 0; + } + } + + // + // See if the ACCEL_CONFIG register is being written. + // + if((ui8Reg <= LSM303DLHC_O_CTRL4) && + ((ui8Reg + ui16Count) > LSM303DLHC_O_CTRL4)) + { + // + // Extract the AFS_SEL from the ACCEL_CONFIG register value. + // + psInst->ui8NewAccelAfsSel = + ((pui8Data[ui8Reg - LSM303DLHC_O_CTRL4] & + LSM303DLHC_CTRL4_FS_M) >> LSM303DLHC_CTRL4_FS_S); + } + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = LSM303DLHC_STATE_WRITE; + + // + // Write the requested registers to the LSM303DLHC. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, + LSM303DLHCCallback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a LSM303DLHC register. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the LSM303DLHC via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! LSM303DLHC. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCAccelReadModifyWrite(tLSM303DLHCAccel *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the LSM303DLHC driver is not idle (in other words, + // there is already an outstanding request to the LSM303DLHC). + // + if(psInst->ui8State != LSM303DLHC_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = LSM303DLHC_STATE_RMW; + + // + // Submit the read-modify-write request to the LSM303DLHC. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, LSM303DLHCCallback, + psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the accelerometer data from the LSM303DLHC. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the LSM303DLHC data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - LSM303DLHCDataAccelGetRaw() +//! - LSM303DLHCDataAccelGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCAccelDataRead(tLSM303DLHCAccel *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the LSM303DLHC driver is not idle (in other words, + // there is already an outstanding request to the LSM303DLHC). + // + if(psInst->ui8State != LSM303DLHC_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = LSM303DLHC_STATE_READ; + + // + // Read the data registers from the LSM303DLHC. + // + psInst->pui8Data[0] = LSM303DLHC_O_OUT_X_LSB | 0x80; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, 1, + psInst->pui8Data, 6, LSM303DLHCCallback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw accelerometer data from the most recent data read. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param pui16AccelX is a pointer to the value into which the raw X-axis +//! accelerometer data is stored. +//! \param pui16AccelY is a pointer to the value into which the raw Y-axis +//! accelerometer data is stored. +//! \param pui16AccelZ is a pointer to the value into which the raw Z-axis +//! accelerometer data is stored. +//! +//! This function returns the raw accelerometer data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +LSM303DLHCAccelDataAccelGetRaw(tLSM303DLHCAccel *psInst, + uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, + uint_fast16_t *pui16AccelZ) +{ + // + // Return the raw accelerometer values. + // + if(pui16AccelX) + { + *pui16AccelX = (psInst->pui8Data[1] << 8) | psInst->pui8Data[0]; + } + if(pui16AccelY) + { + *pui16AccelY = (psInst->pui8Data[3] << 8) | psInst->pui8Data[2]; + } + if(pui16AccelZ) + { + *pui16AccelZ = (psInst->pui8Data[5] << 8) | psInst->pui8Data[4]; + } +} + +//***************************************************************************** +// +//! Gets the accelerometer data from the most recent data read. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param pfAccelX is a pointer to the value into which the X-axis +//! accelerometer data is stored. +//! \param pfAccelY is a pointer to the value into which the Y-axis +//! accelerometer data is stored. +//! \param pfAccelZ is a pointer to the value into which the Z-axis +//! accelerometer data is stored. +//! +//! This function returns the accelerometer data from the most recent data +//! read, converted into meters per second squared (m/s^2). If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +LSM303DLHCAccelDataAccelGetFloat(tLSM303DLHCAccel *psInst, float *pfAccelX, + float *pfAccelY, float *pfAccelZ) +{ + float fFactor; + + // + // Get the acceleration conversion factor for the current data format. + // + fFactor = g_pfLSM303DLHCAccelFactors[psInst->ui8AccelAfsSel]; + + // + // Convert the Accelerometer values into floating-point gravity values. + // + if(pfAccelX) + { + *pfAccelX = (float)(((int16_t)((psInst->pui8Data[1] << 8) | + psInst->pui8Data[0])) * fFactor); + } + if(pfAccelY) + { + *pfAccelY = (float)(((int16_t)((psInst->pui8Data[3] << 8) | + psInst->pui8Data[2])) * fFactor); + } + if(pfAccelZ) + { + *pfAccelZ = (float)(((int16_t)((psInst->pui8Data[5] << 8) | + psInst->pui8Data[4])) * fFactor); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/lsm303dlhc_accel.h b/sensorlib/lsm303dlhc_accel.h new file mode 100644 index 0000000..00d72f7 --- /dev/null +++ b/sensorlib/lsm303dlhc_accel.h @@ -0,0 +1,163 @@ +//***************************************************************************** +// +// lsm303dlhc.c - Driver for the ST L3GD20H gyrometer +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_LSM303DLHC_ACCEL_H__ +#define __SENSORLIB_LSM303DLHC_ACCEL_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the LSM303DLHC driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the LSM303DLHC. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the LSM303DLHC. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the LSM303DLHC. + // + uint8_t ui8State; + + // + // The current accelerometer afs_sel setting. + // + uint8_t ui8AccelAfsSel; + + // + // The new accelerometer afs_sel setting, which is used when a register + // write succeeds. + // + uint8_t ui8NewAccelAfsSel; + + // + // The data buffers used for sending/receiving data to/from the LSM303DLHC. + // + uint8_t pui8Data[8]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The callback data provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tLSM303DLHCAccel; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t LSM303DLHCAccelInit(tLSM303DLHCAccel *psInst, + tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DLHCAccelRead(tLSM303DLHCAccel *psInst, + uint_fast8_t ui8Reg, + uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DLHCAccelWrite(tLSM303DLHCAccel *psInst, + uint_fast8_t ui8Reg, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DLHCAccelReadModifyWrite(tLSM303DLHCAccel *psInst, + uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallbak, + void *pvCallbackData); +extern uint_fast8_t LSM303DLHCAccelDataRead(tLSM303DLHCAccel *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void LSM303DLHCAccelDataAccelGetRaw(tLSM303DLHCAccel *psInst, + uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, + uint_fast16_t *pui16AccelZ); +extern void LSM303DLHCAccelDataAccelGetFloat(tLSM303DLHCAccel *psInst, + float *pfAccelX, float *pfAccelY, + float *pfAccelZ); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_LSM303DLHC_ACCEL_H__ diff --git a/sensorlib/lsm303dlhc_mag.c b/sensorlib/lsm303dlhc_mag.c new file mode 100644 index 0000000..d8abaa8 --- /dev/null +++ b/sensorlib/lsm303dlhc_mag.c @@ -0,0 +1,615 @@ +//***************************************************************************** +// +// lsm303dlhc.c - Driver for the ST LSM303DLHC magnetometer +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_lsm303dlhc.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/lsm303dlhc_mag.h" + +//***************************************************************************** +// +//! \addtogroup lsm303dlhc_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the LSM303DLHC state machine. +// +//***************************************************************************** +#define LSM303DLHC_STATE_IDLE 0 // State machine is idle +#define LSM303DLHC_STATE_READ 1 // Waiting for read +#define LSM303DLHC_STATE_WRITE 2 // Waiting for write +#define LSM303DLHC_STATE_RMW 3 // Waiting for read-modify-write + +//***************************************************************************** +// +// The factors used to convert the magnetometer readings from the LSM303 into +// floating point values in tesla +// +//***************************************************************************** +static const float g_pfLSM303DLHCMagnetoFactors[] = +{ + 0, + 9.09E-08f, + 1.17E-07f, + 1.49E-07f, + 2.22E-07f, + 2.50E-07f, + 3.03E-07f, + 4.35E-07f, +}; + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// LSM303DLHC have completed. +// +//***************************************************************************** +static void +LSM303DLHCCallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tLSM303DLHCMag *psInst; + + // + // Convert the instance data into a pointer to a tLSM303DLHC structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = LSM303DLHC_STATE_IDLE; + } + + // + // Determine the current state of the LSM303DLHC state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case LSM303DLHC_STATE_READ: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A write just completed + // + case LSM303DLHC_STATE_WRITE: + { + // + // Set the magneto ranges to the new values. If the register was + // not modified, the values will be the same so this has no effect. + // + psInst->ui8MagnetoFsSel = psInst->ui8NewMagnetoFsSel; + + // + // The state machine is now idle. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A read-modify-write just completed + // + case LSM303DLHC_STATE_RMW: + { + // + // See if the MAGNETO_CONFIG register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + LSM303DLHC_O_MAG_CRB) + { + // + // Extract the FS_SEL from the MAGNETO_CONFIG register value. + // + psInst->ui8MagnetoFsSel = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + LSM303DLHC_MAG_CRB_GAIN_M) >> LSM303DLHC_MAG_CRB_GAIN_S); + } + + // + // The state machine is now idle. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == LSM303DLHC_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the LSM303DLHC driver. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the LSM303DLHC device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the LSM303DLHC driver, preparing it for +//! operation. +//! +//! \return Returns 1 if the LSM303DLHC driver was successfully initialized and +//! 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCMagInit(tLSM303DLHCMag *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Initialize the LSM303DLHC instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Default range setting is +/- 1.3 + // TODO: double-check default + // + psInst->ui8MagnetoFsSel = (LSM303DLHC_MAG_CRB_GAIN_1_3GAUSS >> + LSM303DLHC_MAG_CRB_GAIN_S); + psInst->ui8NewMagnetoFsSel = (LSM303DLHC_MAG_CRB_GAIN_1_3GAUSS >> + LSM303DLHC_MAG_CRB_GAIN_S); + psInst->ui8State = LSM303DLHC_STATE_IDLE; + + if(pfnCallback) + { + pfnCallback(pvCallbackData, I2CM_STATUS_SUCCESS); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from LSM303DLHC registers. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the LSM303DLHC. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCMagRead(tLSM303DLHCMag *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the LSM303DLHC driver is not idle (in other words, + // there is already an outstanding request to the LSM303DLHC). + // + if(psInst->ui8State != LSM303DLHC_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = LSM303DLHC_STATE_READ; + + // + // Read the requested registers from the LSM303DLHC. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + LSM303DLHCCallback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to LSM303DLHC registers. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the LSM303DLHC. The first byte of the \e pui8Data buffer contains the +//! value to be written into the \e ui8Reg register, the second value contains +//! the data to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCMagWrite(tLSM303DLHCMag *psInst, uint_fast8_t ui8Reg, + const uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the LSM303DLHC driver is not idle (in other words, + // there is already an outstanding request to the LSM303DLHC). + // + if(psInst->ui8State != LSM303DLHC_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if the MAGNETO_CONFIG register is being written. + // + if((ui8Reg <= LSM303DLHC_O_MAG_CRB) && + ((ui8Reg + ui16Count) > LSM303DLHC_O_MAG_CRB)) + { + // + // Extract the FS_SEL from the MAGNETO_CONFIG register value. + // + psInst->ui8NewMagnetoFsSel = ((pui8Data[ui8Reg - LSM303DLHC_O_MAG_CRB] & + LSM303DLHC_MAG_CRB_GAIN_M) >> + LSM303DLHC_MAG_CRB_GAIN_S); + } + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = LSM303DLHC_STATE_WRITE; + + // + // Write the requested registers to the LSM303DLHC. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, + LSM303DLHCCallback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a LSM303DLHC register. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the LSM303DLHC via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! LSM303DLHC. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCMagReadModifyWrite(tLSM303DLHCMag *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the LSM303DLHC driver is not idle (in other words, + // there is already an outstanding request to the LSM303DLHC). + // + if(psInst->ui8State != LSM303DLHC_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = LSM303DLHC_STATE_RMW; + + // + // Submit the read-modify-write request to the LSM303DLHC. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, LSM303DLHCCallback, + psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the magneto data from the LSM303DLHC. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the LSM303DLHC data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - LSM303DLHCDataMagnetoGetRaw() +//! - LSM303DLHCDataMagnetoGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +LSM303DLHCMagDataRead(tLSM303DLHCMag *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the LSM303DLHC driver is not idle (in other words, + // there is already an outstanding request to the LSM303DLHC). + // + if(psInst->ui8State != LSM303DLHC_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = LSM303DLHC_STATE_READ; + + // + // Read the data registers from the LSM303DLHC. + // + psInst->pui8Data[0] = LSM303DLHC_O_MAG_OUT_X_MSB; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, 1, + psInst->pui8Data, 7, LSM303DLHCCallback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = LSM303DLHC_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw magneto data from the most recent data read. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param pui16MagnetoX is a pointer to the value into which the raw X-axis +//! magnetometer data is stored. +//! \param pui16MagnetoY is a pointer to the value into which the raw Y-axis +//! magnetometer data is stored. +//! \param pui16MagnetoZ is a pointer to the value into which the raw Z-axis +//! magnetometer data is stored. +//! +//! This function returns the raw magnetometer data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +LSM303DLHCMagDataMagnetoGetRaw(tLSM303DLHCMag *psInst, + uint_fast16_t *pui16MagnetoX, + uint_fast16_t *pui16MagnetoY, + uint_fast16_t *pui16MagnetoZ) +{ + // + // Return the raw magnetometer values. + // + if(pui16MagnetoX) + { + *pui16MagnetoX = (psInst->pui8Data[0] << 8) | psInst->pui8Data[1]; + } + if(pui16MagnetoY) + { + *pui16MagnetoY = (psInst->pui8Data[2] << 8) | psInst->pui8Data[3]; + } + if(pui16MagnetoZ) + { + *pui16MagnetoZ = (psInst->pui8Data[4] << 8) | psInst->pui8Data[5]; + } +} + +//***************************************************************************** +// +//! Gets the magnetometer data from the most recent data read. +//! +//! \param psInst is a pointer to the LSM303DLHC instance data. +//! \param pfMagnetoX is a pointer to the value into which the X-axis +//! magnetometer data is stored. +//! \param pfMagnetoY is a pointer to the value into which the Y-axis +//! magnetometer data is stored. +//! \param pfMagnetoZ is a pointer to the value into which the Z-axis +//! magnetometer data is stored. +//! +//! This function returns the magnetometer data from the most recent data read, +//! converted into radians per second. If any of the output data pointers are +//! \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +LSM303DLHCMagDataMagnetoGetFloat(tLSM303DLHCMag *psInst, float *pfMagnetoX, + float *pfMagnetoY, float *pfMagnetoZ) +{ + float fFactor; + + // + // Get the conversion factor for the current data format. + // + fFactor = g_pfLSM303DLHCMagnetoFactors[psInst->ui8MagnetoFsSel]; + + // + // Convert the magnetometer values into rad/sec + // + if(pfMagnetoX) + { + *pfMagnetoX = (float)(((int16_t)((psInst->pui8Data[0] << 8) | + psInst->pui8Data[1])) * fFactor); + } + if(pfMagnetoY) + { + *pfMagnetoY = (float)(((int16_t)((psInst->pui8Data[2] << 8) | + psInst->pui8Data[3])) * fFactor); + } + if(pfMagnetoZ) + { + *pfMagnetoZ = (float)(((int16_t)((psInst->pui8Data[4] << 8) | + psInst->pui8Data[5])) * fFactor); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/lsm303dlhc_mag.h b/sensorlib/lsm303dlhc_mag.h new file mode 100644 index 0000000..a7426c3 --- /dev/null +++ b/sensorlib/lsm303dlhc_mag.h @@ -0,0 +1,164 @@ +//***************************************************************************** +// +// lsm303dlhc.c - Driver for the ST L3GD20H gyrometer +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_LSM303DLHC_MAG_H__ +#define __SENSORLIB_LSM303DLHC_MAG_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the LSM303DLHC driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the LSM303DLHC. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the LSM303DLHC. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the LSM303DLHC. + // + uint8_t ui8State; + + // + // The current gyroscope fs_sel setting. + // + uint8_t ui8MagnetoFsSel; + + // + // The new gyroscope fs_sel setting, which is used when a register write + // succeeds. + // + uint8_t ui8NewMagnetoFsSel; + + // + // The data buffers used for sending/receiving data to/from the LSM303DLHC. + // + uint8_t pui8Data[8]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The callback data provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tLSM303DLHCMag; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t LSM303DLHCMagInit(tLSM303DLHCMag *psInst, + tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DLHCMagRead(tLSM303DLHCMag *psInst, + uint_fast8_t ui8Reg, + uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DLHCMagWrite(tLSM303DLHCMag *psInst, + uint_fast8_t ui8Reg, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DLHCMagReadModifyWrite(tLSM303DLHCMag *psInst, + uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t LSM303DLHCMagDataRead(tLSM303DLHCMag *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void LSM303DLHCMagDataMagnetoGetRaw(tLSM303DLHCMag *psInst, + uint_fast16_t *pui16MagnetoX, + uint_fast16_t *pui16MagnetoY, + uint_fast16_t *pui16MagnetoZ); +extern void LSM303DLHCMagDataMagnetoGetFloat(tLSM303DLHCMag *psInst, + float *pfMagnetoX, + float *pfMagnetoY, + float *pfMagnetoZ); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_LSM303DLHC_MAG_H__ diff --git a/sensorlib/magneto.c b/sensorlib/magneto.c new file mode 100644 index 0000000..b7f7595 --- /dev/null +++ b/sensorlib/magneto.c @@ -0,0 +1,248 @@ +//***************************************************************************** +// +// magneto.c - Functions for manipulating magnetometer readings. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/magneto.h" + +//***************************************************************************** +// +//! \addtogroup magneto_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Initializes the magnetometer hard- and soft-iron compensation state. +//! +//! \param psInst is a pointer to the magnetometer compensation state +//! structure. +//! \param fXOffset is the hard-iron compensation for the X axis. +//! \param fYOffset is the hard-iron compensation for the Y axis. +//! \param fZOffset is the hard-iron compensation for the Z axis. +//! \param fXYAngle is the amount to rotate around the Z axis prior to scaling +//! the Y axis reading, in radians. +//! \param fYRatio is the amount to scale the Y axis reading. +//! \param fXZAngle is the amount to rotate around the Y axis prior to scaling +//! the Z axis reading, in radians. +//! \param fZRatio is the amount to scale the Z axis reading. +//! +//! This function initializes the magnetometer compensation state structure +//! with the values that are used to perform hard- and soft-iron compensation +//! of magnetometer readings. +//! +//! \return None. +// +//***************************************************************************** +void +MagnetoCompensateInit(tMagnetoCompensation *psInst, float fXOffset, + float fYOffset, float fZOffset, float fXYAngle, + float fYRatio, float fXZAngle, float fZRatio) +{ + // + // Save the hard- and soft-iron compensation values. + // + psInst->fXOffset = fXOffset; + psInst->fYOffset = fYOffset; + psInst->fZOffset = fZOffset; + psInst->fXYAngle = fXYAngle; + psInst->fYRatio = fYRatio; + psInst->fXZAngle = fXZAngle; + psInst->fZRatio = fZRatio; +} + +//***************************************************************************** +// +//! Performs hard- and soft-iron compensation on magnetometer readings. +//! +//! \param psInst is a pointer to the magnetometer compensation state +//! structure. +//! \param pfMagnetoX is a pointer to the magnetometer X-axis reading. +//! \param pfMagnetoY is a pointer to the magnetometer Y-axis reading. +//! \param pfMagnetoZ is a pointer to the magnetometer Z-axis reading. +//! +//! This function performs hard- and soft-iron compensation on the given +//! magnetometer reading. Hard-iron distortions cause a fixed offset in the +//! reading, regardless of orientation. Hard-iron compensation is performed by +//! negating this fixed offset. +//! +//! Soft-iron distortion is more complicated, causing an offset that varies as +//! the sensor rotates, which results in the sensor returning an ellipse as it +//! rotates instead of a circle. Performing soft-iron compensation requires +//! rotating the sensor reading such that the major axis of the ellipse is +//! aligned with one of the magnetometer axes, scaling one of the axes, then +//! rotating the scaled sensor reading back. This operation is performed two +//! times; once to scale the Y axis to the same scale as the X axis, and once +//! again to scale the Z axis to the same scale as the X axis. +//! +//! Hard-iron compensation is performed prior to soft-iron compensation. +//! +//! \return None. +// +//***************************************************************************** +void +MagnetoCompensate(tMagnetoCompensation *psInst, float *pfMagnetoX, + float *pfMagnetoY, float *pfMagnetoZ) +{ + float fSin, fCos, fX, fY, fZ, fTemp; + + // + // Get the magnetometer values. + // + fX = *pfMagnetoX; + fY = *pfMagnetoY; + fZ = *pfMagnetoZ; + + // + // Perform hard-iron distortion compensation. + // + fX += psInst->fXOffset; + fY += psInst->fYOffset; + fZ += psInst->fZOffset; + + // + // Perform soft-iron distortion compensation on the X-Y plane. Start by + // computing the sine and cosine of the rotation angle (which will be used + // multiple times below). + // + fSin = sinf(psInst->fXYAngle); + fCos = cosf(psInst->fXYAngle); + + // + // Rotate the magnetometer reading around the Z axis. + // + fTemp = (fCos * fX) - (fSin * fY); + fY = (fCos * fY) + (fSin * fX); + fX = fTemp; + + // + // Scale the Y-axis reading so that it has the same range as the X-axis + // reading. + // + fY *= psInst->fYRatio; + + // + // Rotate the magnetometer reading around the Z axis again, this time in + // the opposite direction. + // + fTemp = (fCos * fX) + (fSin * fY); + fY = (fCos * fY) - (fSin * fX); + fX = fTemp; + + // + // Perform soft-iron distortion compensation on the X-Z plane. Start by + // computing the sine and cosine of the rotation angle (which will be used + // multiple times below). + // + fSin = sinf(psInst->fXZAngle); + fCos = cosf(psInst->fXZAngle); + + // + // Rotate the magnetometer reading around the Y axis. + // + fTemp = (fCos * fZ) - (fSin * fX); + fX = (fCos * fX) + (fSin * fZ); + fZ = fTemp; + + // + // Scale the Z-axis reading so that it has the same range as the X-axis + // reading. + // + fZ *= psInst->fZRatio; + + // + // Rotate the magnetometer reading around the Y axis again, this time in + // the opposite direction. + // + fTemp = (fCos * fZ) + (fSin * fX); + fX = (fCos * fX) - (fSin * fZ); + fZ = fTemp; + + // + // Return the compensated magnetometer values. + // + *pfMagnetoX = fX; + *pfMagnetoY = fY; + *pfMagnetoZ = fZ; +} + +//***************************************************************************** +// +//! Computes the compass heading from magnetometer data and roll/pitch. +//! +//! \param fMagnetoX is the X component of the magnetometer reading. +//! \param fMagnetoY is the Y component of the magnetometer reading. +//! \param fMagnetoZ is the Z component of the magnetometer reading. +//! \param fRoll is the roll angle, in radians. +//! \param fPitch is the pitch angle, in radians. +//! +//! This function computes the compass heading by performing tilt compensation +//! on the magnetometer reading. +//! +//! \return Returns the compass heading, in radians. +// +//***************************************************************************** +float +MagnetoHeadingCompute(float fMagnetoX, float fMagnetoY, float fMagnetoZ, + float fRoll, float fPitch) +{ + float fSinRoll, fCosRoll, fSinPitch, fCosPitch, fX, fY, fHeading; + + // + // Compute the sine and cosine of the roll and pitch angles. + // + fSinRoll = sinf(fRoll); + fCosRoll = cosf(fRoll); + fSinPitch = sinf(fPitch); + fCosPitch = cosf(fPitch); + + // + // Rotate the magnetometer data such that it is level with the ground, + // based on the provided roll and pitch. + // + fX = ((fMagnetoX * fCosPitch) + (fMagnetoY * fSinRoll * fSinPitch) + + (fMagnetoZ * fCosRoll * fSinPitch)); + fY = (fMagnetoY * fCosRoll) - (fMagnetoZ * fSinRoll); + + // + // Compute the compass heading and make it positive. + // + fHeading = atan2f(-fY, fX); + if(fHeading < 0) + { + fHeading += 2 * 3.141592; + } + + // + // Return the computed compass heading. + // + return(fHeading); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/magneto.h b/sensorlib/magneto.h new file mode 100644 index 0000000..811491e --- /dev/null +++ b/sensorlib/magneto.h @@ -0,0 +1,92 @@ +//***************************************************************************** +// +// magneto.h - Prototypes for the functions that manipulate magnetometer +// readings. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_MAGNETO_H__ +#define __SENSORLIB_MAGNETO_H__ + +//***************************************************************************** +// +// The structure that defines the internal state of the magnetometer hard and +// soft iron compensation. +// +//***************************************************************************** +typedef struct +{ + // + // The hard iron induced offset in the X axis of the magnetometer. + // + float fXOffset; + + // + // The hard iron induced offset in the Y axis of the magnetometer. + // + float fYOffset; + + // + // The hard iron induced offset in the Z axis of the magnetometer. + // + float fZOffset; + + // + // The Z axis rotation required to align the major/minor axes of the + // ellipse in the X-Y plane with the X-Y axes, specified in radians. + // + float fXYAngle; + + // + // The amount to scale the Y axis in order to turn the X-Y ellipse into a + // circle. + // + float fYRatio; + + // + // The Y axis rotation required to align the major/minor axes of the + // ellipse in the X-Z plane with X-Z axes, specified in radians. + // + float fXZAngle; + + // + // The amount to scale the Z axis in order to turn the X-Z ellipse into a + // circle. + // + float fZRatio; +} +tMagnetoCompensation; + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern void MagnetoCompensateInit(tMagnetoCompensation *psInst, float fXOffset, + float fYOffset, float fZOffset, + float fXYAngle, float fYRatio, + float fXZAngle, float fZRatio); +extern void MagnetoCompensate(tMagnetoCompensation *psInst, float *pfMagnetoX, + float *pfMagnetoY, float *pfMagnetoZ); +extern float MagnetoHeadingCompute(float fMagnetoX, float fMagnetoY, + float fMagnetoZ, float fRoll, float fPitch); + +#endif // __SENSORLIB_MAGNETO_H__ diff --git a/sensorlib/mpu6050.c b/sensorlib/mpu6050.c new file mode 100644 index 0000000..f095e40 --- /dev/null +++ b/sensorlib/mpu6050.c @@ -0,0 +1,879 @@ +//***************************************************************************** +// +// mpu6050.c - Driver for the MPU6050 accelerometer and gyroscope. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_mpu6050.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/mpu6050.h" + +//***************************************************************************** +// +//! \addtogroup mpu6050_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the MPU6050 state machine. +// +//***************************************************************************** +#define MPU6050_STATE_IDLE 0 // State machine is idle +#define MPU6050_STATE_INIT_RES 1 // Waiting for initialization +#define MPU6050_STATE_INIT_WAIT 2 // Waiting for reset to complete +#define MPU6050_STATE_READ 3 // Waiting for read +#define MPU6050_STATE_WRITE 4 // Waiting for write +#define MPU6050_STATE_RMW 5 // Waiting for read-modify-write + +//***************************************************************************** +// +// The factors used to convert the acceleration readings from the MPU6050 into +// floating point values in meters per second squared. +// +// Values are obtained by taking the g conversion factors from the data sheet +// and multiplying by 9.81 (1 g = 9.81 m/s^2). +// +//***************************************************************************** +static const float g_fMPU6050AccelFactors[] = +{ + 0.00059875, // Range = +/- 2 g (16384 lsb/g) + 0.00119751, // Range = +/- 4 g (8192 lsb/g) + 0.00239502, // Range = +/- 8 g (4096 lsb/g) + 0.00479004 // Range = +/- 16 g (2048 lsb/g) +}; + +//***************************************************************************** +// +// The factors used to convert the acceleration readings from the MPU6050 into +// floating point values in radians per second. +// +// Values are obtained by taking the degree per second conversion factors +// from the data sheet and then converting to radians per sec (1 degree = +// 0.0174532925 radians). +// +//***************************************************************************** +static const float g_fMPU6050GyroFactors[] = +{ + 1.3323124e-4f, // Range = +/- 250 dps (131.0 LSBs/DPS) + 2.6646248e-4f, // Range = +/- 500 dps (65.5 LSBs/DPS) + 5.3211258e-4f, // Range = +/- 1000 dps (32.8 LSBs/DPS) + 0.0010642252f // Range = +/- 2000 dps (16.4 LSBs/DPS) +}; +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// MPU6050 have completed. +// +//***************************************************************************** +static void +MPU6050Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tMPU6050 *psInst; + + // + // Convert the instance data into a pointer to a tMPU6050 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). Except in the case that we are in the reset wait state and the + // error is an address NACK. This error is handled by the reset wait + // state. + // + if((ui8Status != I2CM_STATUS_SUCCESS) && + !((ui8Status == I2CM_STATUS_ADDR_NACK) && + (psInst->ui8State == MPU6050_STATE_INIT_WAIT))) + { + psInst->ui8State = MPU6050_STATE_IDLE; + } + + // + // Determine the current state of the MPU6050 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case MPU6050_STATE_READ: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = MPU6050_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // MPU6050 Device reset was issued + // + case MPU6050_STATE_INIT_RES: + { + // + // Issue a read of the status register to confirm reset is done. + // + psInst->uCommand.pui8Buffer[0] = MPU6050_O_PWR_MGMT_1; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 1, + MPU6050Callback, psInst); + + psInst->ui8State = MPU6050_STATE_INIT_WAIT; + break; + } + + // + // Status register was read, check if reset is done before proceeding. + // + case MPU6050_STATE_INIT_WAIT: + { + // + // Check the value read back from status to determine if device + // is still in reset or if it is ready. Reset state for this + // register is 0x40, which has sleep bit set. Device may also + // respond with an address NACK during very early stages of the its + // internal reset. Keep polling until we verify device is ready. + // + // + if((psInst->pui8Data[0] != MPU6050_PWR_MGMT_1_SLEEP) || + (ui8Status == I2CM_STATUS_ADDR_NACK)) + { + // + // Device still in reset so begin polling this register. + // + psInst->uCommand.pui8Buffer[0] = MPU6050_O_PWR_MGMT_1; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 1, + MPU6050Callback, psInst); + + // + // Intentionally stay in this state to create polling effect. + // + } + else + { + // + // Device is out of reset, move to the idle state. + // + psInst->ui8State = MPU6050_STATE_IDLE; + } + break; + } + + // + // A write just completed + // + case MPU6050_STATE_WRITE: + { + // + // Set the accelerometer and gyroscope ranges to the new values. + // If the register was not modified, the values will be the same so + // this has no effect. + // + psInst->ui8AccelAfsSel = psInst->ui8NewAccelAfsSel; + psInst->ui8GyroFsSel = psInst->ui8NewGyroFsSel; + + // + // The state machine is now idle. + // + psInst->ui8State = MPU6050_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A read-modify-write just completed + // + case MPU6050_STATE_RMW: + { + // + // See if the PWR_MGMT_1 register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + MPU6050_O_PWR_MGMT_1) + { + // + // See if a soft reset has been issued. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + MPU6050_PWR_MGMT_1_DEVICE_RESET) + { + // + // Default range setting is +/- 2 g + // + psInst->ui8AccelAfsSel = 0; + psInst->ui8NewAccelAfsSel = 0; + + // + // Default range setting is +/- 250 degrees/s + // + psInst->ui8GyroFsSel = 0; + psInst->ui8NewGyroFsSel = 0; + } + } + + // + // See if the GYRO_CONFIG register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + MPU6050_O_GYRO_CONFIG) + { + // + // Extract the FS_SEL from the GYRO_CONFIG register value. + // + psInst->ui8GyroFsSel = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + MPU6050_GYRO_CONFIG_FS_SEL_M) >> + MPU6050_GYRO_CONFIG_FS_SEL_S); + } + + // + // See if the ACCEL_CONFIG register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + MPU6050_O_ACCEL_CONFIG) + { + // + // Extract the FS_SEL from the ACCEL_CONFIG register value. + // + psInst->ui8AccelAfsSel = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + MPU6050_ACCEL_CONFIG_AFS_SEL_M) >> + MPU6050_ACCEL_CONFIG_AFS_SEL_S); + } + + // + // The state machine is now idle. + // + psInst->ui8State = MPU6050_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == MPU6050_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the MPU6050 driver. +//! +//! \param psInst is a pointer to the MPU6050 instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the MPU6050 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the MPU6050 driver, preparing it for operation. +//! +//! \return Returns 1 if the MPU6050 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +MPU6050Init(tMPU6050 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Initialize the MPU6050 instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Default range setting is +/- 2 g + // + psInst->ui8AccelAfsSel = (MPU6050_ACCEL_CONFIG_AFS_SEL_2G >> + MPU6050_ACCEL_CONFIG_AFS_SEL_S); + psInst->ui8NewAccelAfsSel = (MPU6050_ACCEL_CONFIG_AFS_SEL_2G >> + MPU6050_ACCEL_CONFIG_AFS_SEL_S); + + // + // Default range setting is +/- 250 degrees/s + // + psInst->ui8GyroFsSel = (MPU6050_GYRO_CONFIG_FS_SEL_250 >> + MPU6050_GYRO_CONFIG_FS_SEL_S); + psInst->ui8NewGyroFsSel = (MPU6050_GYRO_CONFIG_FS_SEL_250 >> + MPU6050_GYRO_CONFIG_FS_SEL_S); + + // + // Set the state to show we are initiating a reset. + // + psInst->ui8State = MPU6050_STATE_INIT_RES; + + // + // Load the buffer with command to perform device reset + // + psInst->uCommand.pui8Buffer[0] = MPU6050_O_PWR_MGMT_1; + psInst->uCommand.pui8Buffer[1] = MPU6050_PWR_MGMT_1_DEVICE_RESET; + if(I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, MPU6050Callback, psInst) == 0) + { + psInst->ui8State = MPU6050_STATE_IDLE; + return(0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from MPU6050 registers. +//! +//! \param psInst is a pointer to the MPU6050 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the MPU6050. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +MPU6050Read(tMPU6050 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the MPU6050 driver is not idle (in other words, + // there is already an outstanding request to the MPU6050). + // + if(psInst->ui8State != MPU6050_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = MPU6050_STATE_READ; + + // + // Read the requested registers from the MPU6050. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + MPU6050Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = MPU6050_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to MPU6050 registers. +//! +//! \param psInst is a pointer to the MPU6050 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the MPU6050. The first byte of the \e pui8Data buffer contains the value +//! to be written into the \e ui8Reg register, the second value contains the +//! data to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +MPU6050Write(tMPU6050 *psInst, uint_fast8_t ui8Reg, const uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the MPU6050 driver is not idle (in other words, + // there is already an outstanding request to the MPU6050). + // + if(psInst->ui8State != MPU6050_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if the PWR_MGMT_1 register is being written. + // + if((ui8Reg <= MPU6050_O_PWR_MGMT_1) && + ((ui8Reg + ui16Count) > MPU6050_O_PWR_MGMT_1)) + { + // + // See if a soft reset is being requested. + // + if(pui8Data[ui8Reg - MPU6050_O_PWR_MGMT_1] & + MPU6050_PWR_MGMT_1_DEVICE_RESET) + { + // + // Default range setting is +/- 2 g. + // + psInst->ui8NewAccelAfsSel = 0; + + // + // Default range setting is +/- 250 degrees/s. + // + psInst->ui8NewGyroFsSel = 0; + } + } + + // + // See if the GYRO_CONFIG register is being written. + // + if((ui8Reg <= MPU6050_O_GYRO_CONFIG) && + ((ui8Reg + ui16Count) > MPU6050_O_GYRO_CONFIG)) + { + // + // Extract the FS_SEL from the GYRO_CONFIG register value. + // + psInst->ui8NewGyroFsSel = ((pui8Data[ui8Reg - MPU6050_O_GYRO_CONFIG] & + MPU6050_GYRO_CONFIG_FS_SEL_M) >> + MPU6050_GYRO_CONFIG_FS_SEL_S); + } + + // + // See if the ACCEL_CONFIG register is being written. + // + if((ui8Reg <= MPU6050_O_ACCEL_CONFIG) && + ((ui8Reg + ui16Count) > MPU6050_O_ACCEL_CONFIG)) + { + // + // Extract the AFS_SEL from the ACCEL_CONFIG register value. + // + psInst->ui8NewAccelAfsSel = + ((pui8Data[ui8Reg - MPU6050_O_ACCEL_CONFIG] & + MPU6050_ACCEL_CONFIG_AFS_SEL_M) >> + MPU6050_ACCEL_CONFIG_AFS_SEL_S); + } + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = MPU6050_STATE_WRITE; + + // + // Write the requested registers to the MPU6050. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, + MPU6050Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = MPU6050_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a MPU6050 register. +//! +//! \param psInst is a pointer to the MPU6050 instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the MPU6050 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! MPU6050. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +MPU6050ReadModifyWrite(tMPU6050 *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the MPU6050 driver is not idle (in other words, + // there is already an outstanding request to the MPU6050). + // + if(psInst->ui8State != MPU6050_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = MPU6050_STATE_RMW; + + // + // Submit the read-modify-write request to the MPU6050. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, MPU6050Callback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = MPU6050_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the accelerometer and gyroscope data from the MPU6050. +//! +//! \param psInst is a pointer to the MPU6050 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the MPU6050 data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - MPU6050DataAccelGetRaw() +//! - MPU6050DataAccelGetFloat() +//! - MPU6050DataGyroGetRaw() +//! - MPU6050DataGyroGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +MPU6050DataRead(tMPU6050 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the MPU6050 driver is not idle (in other words, + // there is already an outstanding request to the MPU6050). + // + if(psInst->ui8State != MPU6050_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = MPU6050_STATE_READ; + + // + // Read the data registers from the MPU6050. + // + psInst->pui8Data[0] = MPU6050_O_ACCEL_XOUT_H; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Data, 1, + psInst->pui8Data, 14, MPU6050Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = MPU6050_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw accelerometer data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU6050 instance data. +//! \param pui16AccelX is a pointer to the value into which the raw X-axis +//! accelerometer data is stored. +//! \param pui16AccelY is a pointer to the value into which the raw Y-axis +//! accelerometer data is stored. +//! \param pui16AccelZ is a pointer to the value into which the raw Z-axis +//! accelerometer data is stored. +//! +//! This function returns the raw accelerometer data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU6050DataAccelGetRaw(tMPU6050 *psInst, uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, uint_fast16_t *pui16AccelZ) +{ + // + // Return the raw accelerometer values. + // + if(pui16AccelX) + { + *pui16AccelX = (psInst->pui8Data[0] << 8) | psInst->pui8Data[1]; + } + if(pui16AccelY) + { + *pui16AccelY = (psInst->pui8Data[2] << 8) | psInst->pui8Data[3]; + } + if(pui16AccelZ) + { + *pui16AccelZ = (psInst->pui8Data[4] << 8) | psInst->pui8Data[5]; + } +} + +//***************************************************************************** +// +//! Gets the accelerometer data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU6050 instance data. +//! \param pfAccelX is a pointer to the value into which the X-axis +//! accelerometer data is stored. +//! \param pfAccelY is a pointer to the value into which the Y-axis +//! accelerometer data is stored. +//! \param pfAccelZ is a pointer to the value into which the Z-axis +//! accelerometer data is stored. +//! +//! This function returns the accelerometer data from the most recent data +//! read, converted into meters per second squared (m/s^2). If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU6050DataAccelGetFloat(tMPU6050 *psInst, float *pfAccelX, float *pfAccelY, + float *pfAccelZ) +{ + float fFactor; + + // + // Get the acceleration conversion factor for the current data format. + // + fFactor = g_fMPU6050AccelFactors[psInst->ui8AccelAfsSel]; + + // + // Convert the Accelerometer values into floating-point gravity values. + // + if(pfAccelX) + { + *pfAccelX = (float)((int16_t)((psInst->pui8Data[0] << 8) | + psInst->pui8Data[1]) * fFactor); + } + if(pfAccelY) + { + *pfAccelY = (float)((int16_t)((psInst->pui8Data[2] << 8) | + psInst->pui8Data[3]) * fFactor); + } + if(pfAccelZ) + { + *pfAccelZ = (float)((int16_t)((psInst->pui8Data[4] << 8) | + psInst->pui8Data[5]) * fFactor); + } +} +//***************************************************************************** +// +//! Gets the raw gyroscope data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU6050 instance data. +//! \param pui16GyroX is a pointer to the value into which the raw X-axis +//! gyroscope data is stored. +//! \param pui16GyroY is a pointer to the value into which the raw Y-axis +//! gyroscope data is stored. +//! \param pui16GyroZ is a pointer to the value into which the raw Z-axis +//! gyroscope data is stored. +//! +//! This function returns the raw gyroscope data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU6050DataGyroGetRaw(tMPU6050 *psInst, uint_fast16_t *pui16GyroX, + uint_fast16_t *pui16GyroY, uint_fast16_t *pui16GyroZ) +{ + // + // Return the raw gyroscope values. + // + if(pui16GyroX) + { + *pui16GyroX = (psInst->pui8Data[8] << 8) | psInst->pui8Data[9]; + } + if(pui16GyroY) + { + *pui16GyroY = (psInst->pui8Data[10] << 8) | psInst->pui8Data[11]; + } + if(pui16GyroZ) + { + *pui16GyroZ = (psInst->pui8Data[12] << 8) | psInst->pui8Data[13]; + } +} + +//***************************************************************************** +// +//! Gets the gyroscope data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU6050 instance data. +//! \param pfGyroX is a pointer to the value into which the X-axis +//! gyroscope data is stored. +//! \param pfGyroY is a pointer to the value into which the Y-axis +//! gyroscope data is stored. +//! \param pfGyroZ is a pointer to the value into which the Z-axis +//! gyroscope data is stored. +//! +//! This function returns the gyroscope data from the most recent data read, +//! converted into radians per second. If any of the output data pointers are +//! \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU6050DataGyroGetFloat(tMPU6050 *psInst, float *pfGyroX, float *pfGyroY, + float *pfGyroZ) +{ + float fFactor; + + // + // Get the conversion factor for the current data format. + // + fFactor = g_fMPU6050GyroFactors[psInst->ui8GyroFsSel]; + + // + // Convert the gyroscope values into rad/sec + // + if(pfGyroX) + { + *pfGyroX = ((float)(int16_t)((psInst->pui8Data[8] << 8) | + psInst->pui8Data[9]) * fFactor); + } + if(pfGyroY) + { + *pfGyroY = ((float)(int16_t)((psInst->pui8Data[10] << 8) | + psInst->pui8Data[11]) * fFactor); + } + if(pfGyroZ) + { + *pfGyroZ = ((float)(int16_t)((psInst->pui8Data[12] << 8) | + psInst->pui8Data[13]) * fFactor); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/mpu6050.h b/sensorlib/mpu6050.h new file mode 100644 index 0000000..ec11ab8 --- /dev/null +++ b/sensorlib/mpu6050.h @@ -0,0 +1,174 @@ +//***************************************************************************** +// +// mpu6050.h - Prototypes for the MPU6050 accelerometer and gyroscope driver. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_MPU6050_H__ +#define __SENSORLIB_MPU6050_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the MPU6050 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the MPU6050. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the MPU6050. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the MPU6050. + // + uint8_t ui8State; + + // + // The current accelerometer afs_sel setting. + // + uint8_t ui8AccelAfsSel; + + // + // The new accelerometer afs_sel setting, which is used when a register + // write succeeds. + // + uint8_t ui8NewAccelAfsSel; + + // + // The current gyroscope fs_sel setting. + // + uint8_t ui8GyroFsSel; + + // + // The new gyroscope fs_sel setting, which is used when a register write + // succeeds. + // + uint8_t ui8NewGyroFsSel; + + // + // The data buffer used for sending/receiving data to/from the MPU6050. + // + uint8_t pui8Data[16]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The callback data provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tMPU6050; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t MPU6050Init(tMPU6050 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t MPU6050Read(tMPU6050 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t MPU6050Write(tMPU6050 *psInst, uint_fast8_t ui8Reg, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t MPU6050ReadModifyWrite(tMPU6050 *psInst, + uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t MPU6050DataRead(tMPU6050 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void MPU6050DataAccelGetRaw(tMPU6050 *psInst, + uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, + uint_fast16_t *pui16AccelZ); +extern void MPU6050DataAccelGetFloat(tMPU6050 *psInst, float *pfAccelX, + float *pfAccelY, float *pfAccelZ); +extern void MPU6050DataGyroGetRaw(tMPU6050 *psInst, uint_fast16_t *pui16GyroX, + uint_fast16_t *pui16GyroY, + uint_fast16_t *pui16GyroZ); +extern void MPU6050DataGyroGetFloat(tMPU6050 *psInst, float *pfGyroX, + float *pfGyroY, float *pfGyroZ); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_MPU6050_H__ diff --git a/sensorlib/mpu9150.c b/sensorlib/mpu9150.c new file mode 100644 index 0000000..a1515e2 --- /dev/null +++ b/sensorlib/mpu9150.c @@ -0,0 +1,1180 @@ +//***************************************************************************** +// +// mpu9150.c - Driver for the MPU9150 accelerometer, gyroscope, and +// magnetometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_ak8975.h" +#include "sensorlib/hw_mpu9150.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/ak8975.h" +#include "sensorlib/mpu9150.h" + +//***************************************************************************** +// +//! \addtogroup mpu9150_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the MPU9150 state machine. +// +//***************************************************************************** +#define MPU9150_STATE_IDLE 0 // State machine is idle +#define MPU9150_STATE_LAST 1 // Last step in a sequence +#define MPU9150_STATE_READ 2 // Waiting for read +#define MPU9150_STATE_WRITE 3 // Waiting for write +#define MPU9150_STATE_RMW 4 // Waiting for read modify write +#define MPU9150_STATE_INIT_RESET \ + 5 // reset request issued. +#define MPU9150_STATE_INIT_RESET_WAIT \ + 6 // polling wait for reset complete +#define MPU9150_STATE_INIT_PWR_MGMT \ + 7 // wake up the device. +#define MPU9150_STATE_INIT_USER_CTRL \ + 8 // init user control +#define MPU9150_STATE_INIT_SAMPLE_RATE_CFG \ + 9 // init the sensors and filters +#define MPU9150_STATE_INIT_I2C_SLAVE_DLY \ + 10 // set the ak8975 polling delay +#define MPU9150_STATE_INIT_I2C_SLAVE_0 \ + 11 // config ak8975 automatic read +#define MPU9150_STATE_RD_DATA 12 // Waiting for data read + +//***************************************************************************** +// +// The factors used to convert the acceleration readings from the MPU9150 into +// floating point values in meters per second squared. +// +// Values are obtained by taking the g conversion factors from the data sheet +// and multiplying by 9.81 (1 g = 9.81 m/s^2). +// +//***************************************************************************** +static const float g_fMPU9150AccelFactors[] = +{ + 0.0005985482, // Range = +/- 2 g (16384 lsb/g) + 0.0011970964, // Range = +/- 4 g (8192 lsb/g) + 0.0023941928, // Range = +/- 8 g (4096 lsb/g) + 0.0047883855 // Range = +/- 16 g (2048 lsb/g) +}; + +//***************************************************************************** +// +// The factors used to convert the acceleration readings from the MPU9150 into +// floating point values in radians per second. +// +// Values are obtained by taking the degree per second conversion factors +// from the data sheet and then converting to radians per sec (1 degree = +// 0.0174532925 radians). +// +//***************************************************************************** +static const float g_fMPU9150GyroFactors[] = +{ + 1.3323124e-4, // Range = +/- 250 dps (131.0) + 2.6646248e-4, // Range = +/- 500 dps (65.5) + 5.3211258e-4, // Range = +/- 1000 dps (32.8) + 0.0010642252 // Range = +/- 2000 dps (16.4) +}; + +//***************************************************************************** +// +// Converting sensor data to tesla (0.3 uT per LSB) +// +//***************************************************************************** +#define CONVERT_TO_TESLA 0.0000003 + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the +// MPU9150 have completed. +// +//***************************************************************************** +static void +MPU9150Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tMPU9150 *psInst; + + // + // Convert the instance data into a pointer to a tMPU9150 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). Except in the case that we are in the reset wait state and the + // error is an address NACK. This error is handled by the reset wait + // state. + // + if((ui8Status != I2CM_STATUS_SUCCESS) && + !((ui8Status == I2CM_STATUS_ADDR_NACK) && + (psInst->ui8State == MPU9150_STATE_INIT_RESET_WAIT))) + { + psInst->ui8State = MPU9150_STATE_IDLE; + } + + // + // Determine the current state of the MPU9150 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case MPU9150_STATE_READ: + case MPU9150_STATE_LAST: + case MPU9150_STATE_RD_DATA: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = MPU9150_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // MPU9150 Device reset was issued + // + case MPU9150_STATE_INIT_RESET: + { + // + // Issue a read of the status register to confirm reset is done. + // + psInst->uCommand.pui8Buffer[0] = MPU9150_O_PWR_MGMT_1; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 1, + MPU9150Callback, psInst); + + psInst->ui8State = MPU9150_STATE_INIT_RESET_WAIT; + break; + } + + // + // Status register was read, check if reset is done before proceeding. + // + case MPU9150_STATE_INIT_RESET_WAIT: + { + // + // Check the value read back from status to determine if device + // is still in reset or if it is ready. Reset state for this + // register is 0x40, which has sleep bit set. Device may also + // respond with an address NACK during very early stages of the + // its internal reset. Keep polling until we verify device is + // ready. + // + if((psInst->pui8Data[0] != MPU9150_PWR_MGMT_1_SLEEP) || + (ui8Status == I2CM_STATUS_ADDR_NACK)) + { + // + // Device still in reset so begin polling this register. + // + psInst->uCommand.pui8Buffer[0] = MPU9150_O_PWR_MGMT_1; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 1, + MPU9150Callback, psInst); + + // + // Intentionally stay in this state to create polling effect. + // + } + else + { + // + // Device is out of reset, bring it out of sleep mode. + // + psInst->uCommand.pui8Buffer[0] = MPU9150_O_PWR_MGMT_1; + psInst->uCommand.pui8Buffer[1] = MPU9150_PWR_MGMT_1_CLKSEL_XG; + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, MPU9150Callback, + psInst); + + // + // Update state to show we are modifing user control and + // power management 1 regs. + // + psInst->ui8State = MPU9150_STATE_INIT_PWR_MGMT; + } + break; + } + + // + // Reset complete now take device out of sleep mode. + // + case MPU9150_STATE_INIT_PWR_MGMT: + { + psInst->uCommand.pui8Buffer[0] = MPU9150_O_USER_CTRL; + psInst->uCommand.pui8Buffer[1] = MPU9150_USER_CTRL_I2C_MST_EN; + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, MPU9150Callback, + psInst); + + // + // Update state to show we are modifing user control and + // power management 1 regs. + // + psInst->ui8State = MPU9150_STATE_INIT_USER_CTRL; + + break; + } + + // + // Change to power mode complete, device is ready for configuration. + // + case MPU9150_STATE_INIT_USER_CTRL: + { + // + // Load index 0 with the sample rate register number. + // + psInst->uCommand.pui8Buffer[0] = MPU9150_O_SMPLRT_DIV; + + // + // Set sample rate to 50 hertz. 1000 hz / (1 + 19) + // + psInst->uCommand.pui8Buffer[1] = 19; + + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, MPU9150Callback, psInst); + + // + // update state to show are in process of configuring sensors. + // + psInst->ui8State = MPU9150_STATE_INIT_SAMPLE_RATE_CFG; + break; + } + + // + // Sensor configuration is complete. + // + case MPU9150_STATE_INIT_SAMPLE_RATE_CFG: + { + // + // Write the I2C Master delay control so we only sample the AK + // every 5th time that we sample accel/gyro. Delay Count itself + // handled in next state. + // + psInst->uCommand.pui8Buffer[0] = MPU9150_O_I2C_MST_DELAY_CTRL; + psInst->uCommand.pui8Buffer[1] = + (MPU9150_I2C_MST_DELAY_CTRL_I2C_SLV0_DLY_EN | + MPU9150_I2C_MST_DELAY_CTRL_I2C_SLV4_DLY_EN); + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, MPU9150Callback, psInst); + + // + // Update state to show we are configuring i2c slave delay between + // slave events. Slave 0 and Slave 4 transaction only occur every + // 5th sample cycle. + // + psInst->ui8State = MPU9150_STATE_INIT_I2C_SLAVE_DLY; + break; + } + + // + // Master slave delay configuration complete. + // + case MPU9150_STATE_INIT_I2C_SLAVE_DLY: + { + // + // Write the configuration for I2C master control clock 400khz + // and wait for external sensor before asserting data ready + // + psInst->uCommand.pui8Buffer[0] = MPU9150_O_I2C_MST_CTRL; + psInst->uCommand.pui8Buffer[1] = + (MPU9150_I2C_MST_CTRL_I2C_MST_CLK_400 | + MPU9150_I2C_MST_CTRL_WAIT_FOR_ES); + + // + // Configure I2C Slave 0 for read of AK8975 (I2C Address 0x0C) + // Start at AK8975 register status 1 + // Read 8 bytes and enable this slave transaction + // + psInst->uCommand.pui8Buffer[2] = MPU9150_I2C_SLV0_ADDR_RW | 0x0C; + psInst->uCommand.pui8Buffer[3] = AK8975_O_ST1; + psInst->uCommand.pui8Buffer[4] = MPU9150_I2C_SLV0_CTRL_EN | 0x08; + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 5, MPU9150Callback, psInst); + + // + // Update state. Now in process of configuring slave 0. + // + psInst->ui8State = MPU9150_STATE_INIT_I2C_SLAVE_0; + break; + } + + // + // I2C slave 0 init complete. + // + case MPU9150_STATE_INIT_I2C_SLAVE_0: + { + // + // Write the configuration for I2C Slave 4 transaction to AK8975 + // 0x0c is the AK8975 address on i2c bus. + // we want to write the control register with the value for a + // starting a single measurement. + // + psInst->uCommand.pui8Buffer[0] = MPU9150_O_I2C_SLV4_ADDR; + psInst->uCommand.pui8Buffer[1] = 0x0C; + psInst->uCommand.pui8Buffer[2] = AK8975_O_CNTL; + psInst->uCommand.pui8Buffer[3] = AK8975_CNTL_MODE_SINGLE; + + // + // Enable the SLV4 transaction and set the master delay to + // 0x04 + 1. This means the slave transactions with delay enabled + // will run every fifth accel/gyro sample. + // + psInst->uCommand.pui8Buffer[4] = MPU9150_I2C_SLV4_CTRL_EN | 0x04; + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 5, MPU9150Callback, psInst); + + // + // Update state. Now in the final init state. + // + psInst->ui8State = MPU9150_STATE_LAST; + break; + } + + // + // A write just completed + // + case MPU9150_STATE_WRITE: + { + // + // Set the accelerometer and gyroscope ranges to the new values. + // If the register was not modified, the values will be the same so + // this has no effect. + // + psInst->ui8AccelAfsSel = psInst->ui8NewAccelAfsSel; + psInst->ui8GyroFsSel = psInst->ui8NewGyroFsSel; + + // + // The state machine is now idle. + // + psInst->ui8State = MPU9150_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // A read-modify-write just completed + // + case MPU9150_STATE_RMW: + { + // + // See if the PWR_MGMT_1 register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + MPU9150_O_PWR_MGMT_1) + { + // + // See if a soft reset has been issued. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + MPU9150_PWR_MGMT_1_DEVICE_RESET) + { + // + // Default range setting is +/- 2 g + // + psInst->ui8AccelAfsSel = 0; + psInst->ui8NewAccelAfsSel = 0; + + // + // Default range setting is +/- 250 degrees/s + // + psInst->ui8GyroFsSel = 0; + psInst->ui8NewGyroFsSel = 0; + } + } + + // + // See if the GYRO_CONFIG register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + MPU9150_O_GYRO_CONFIG) + { + // + // Extract the FS_SEL from the GYRO_CONFIG register value. + // + psInst->ui8GyroFsSel = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + MPU9150_GYRO_CONFIG_FS_SEL_M) >> + MPU9150_GYRO_CONFIG_FS_SEL_S); + } + + // + // See if the ACCEL_CONFIG register was just modified. + // + if(psInst->uCommand.sReadModifyWriteState.pui8Buffer[0] == + MPU9150_O_ACCEL_CONFIG) + { + // + // Extract the FS_SEL from the ACCEL_CONFIG register value. + // + psInst->ui8AccelAfsSel = + ((psInst->uCommand.sReadModifyWriteState.pui8Buffer[1] & + MPU9150_ACCEL_CONFIG_AFS_SEL_M) >> + MPU9150_ACCEL_CONFIG_AFS_SEL_S); + } + + // + // The state machine is now idle. + // + psInst->ui8State = MPU9150_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == MPU9150_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the MPU9150 driver. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param psI2CInst is a pointer to the I2C master driver instance data. +//! \param ui8I2CAddr is the I2C address of the MPU9150 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the MPU9150 driver, preparing it for operation. +//! +//! \return Returns 1 if the MPU9150 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +MPU9150Init(tMPU9150 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Initialize the MPU9150 instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Default range setting is +/- 2 g + // + psInst->ui8AccelAfsSel = (MPU9150_ACCEL_CONFIG_AFS_SEL_2G >> + MPU9150_ACCEL_CONFIG_AFS_SEL_S); + psInst->ui8NewAccelAfsSel = (MPU9150_ACCEL_CONFIG_AFS_SEL_2G >> + MPU9150_ACCEL_CONFIG_AFS_SEL_S); + + // + // Default range setting is +/- 250 degrees/s + // + psInst->ui8GyroFsSel = (MPU9150_GYRO_CONFIG_FS_SEL_250 >> + MPU9150_GYRO_CONFIG_FS_SEL_S); + psInst->ui8NewGyroFsSel = (MPU9150_GYRO_CONFIG_FS_SEL_250 >> + MPU9150_GYRO_CONFIG_FS_SEL_S); + + // + // Set the state to show we are initiating a reset. + // + psInst->ui8State = MPU9150_STATE_INIT_RESET; + + // + // Load the buffer with command to perform device reset + // + psInst->uCommand.pui8Buffer[0] = MPU9150_O_PWR_MGMT_1; + psInst->uCommand.pui8Buffer[1] = MPU9150_PWR_MGMT_1_DEVICE_RESET; + if(I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, MPU9150Callback, psInst) == 0) + { + psInst->ui8State = MPU9150_STATE_IDLE; + return(0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Returns the pointer to the tAK8975 object +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! +//! The MPU9150 contains in internal AK8975 magnetometer. To access data from +//! that sensor, application should use this function to get a pointer to the +//! tAK8975 object, and then use the AK8975 APIs. +//! +//! \return Returns the pointer to the tAK8975 object +// +//***************************************************************************** +tAK8975 * +MPU9150MagnetoInstGet(tMPU9150 *psInst) +{ + return(&(psInst->sAK8975Inst)); +} + +//***************************************************************************** +// +//! Reads data from MPU9150 registers. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count is the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the MPU9150. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +MPU9150Read(tMPU9150 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the MPU9150 driver is not idle (in other words, + // there is already an outstanding request to the MPU9150). + // + if(psInst->ui8State != MPU9150_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = MPU9150_STATE_READ; + + // + // Read the requested registers from the MPU9150. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + MPU9150Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = MPU9150_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to MPU9150 registers. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui8Data is a pointer to the data to write. +//! \param ui16Count is the number of data bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the MPU9150. The first byte of the \e pui8Data buffer contains the value +//! to be written into the \e ui8Reg register, the second value contains the +//! data to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +MPU9150Write(tMPU9150 *psInst, uint_fast8_t ui8Reg, const uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the MPU9150 driver is not idle (in other words, + // there is already an outstanding request to the MPU9150). + // + if(psInst->ui8State != MPU9150_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // See if the PWR_MGMT_1 register is being written. + // + if((ui8Reg <= MPU9150_O_PWR_MGMT_1) && + ((ui8Reg + ui16Count) > MPU9150_O_PWR_MGMT_1)) + { + // + // See if a soft reset is being requested. + // + if(pui8Data[ui8Reg - MPU9150_O_PWR_MGMT_1] & + MPU9150_PWR_MGMT_1_DEVICE_RESET) + { + // + // Default range setting is +/- 2 g. + // + psInst->ui8NewAccelAfsSel = 0; + + // + // Default range setting is +/- 250 degrees/s. + // + psInst->ui8NewGyroFsSel = 0; + } + } + + // + // See if the GYRO_CONFIG register is being written. + // + if((ui8Reg <= MPU9150_O_GYRO_CONFIG) && + ((ui8Reg + ui16Count) > MPU9150_O_GYRO_CONFIG)) + { + // + // Extract the FS_SEL from the GYRO_CONFIG register value. + // + psInst->ui8NewGyroFsSel = ((pui8Data[ui8Reg - MPU9150_O_GYRO_CONFIG] & + MPU9150_GYRO_CONFIG_FS_SEL_M) >> + MPU9150_GYRO_CONFIG_FS_SEL_S); + } + + // + // See if the ACCEL_CONFIG register is being written. + // + if((ui8Reg <= MPU9150_O_ACCEL_CONFIG) && + ((ui8Reg + ui16Count) > MPU9150_O_ACCEL_CONFIG)) + { + // + // Extract the AFS_SEL from the ACCEL_CONFIG register value. + // + psInst->ui8NewAccelAfsSel = + ((pui8Data[ui8Reg - MPU9150_O_ACCEL_CONFIG] & + MPU9150_ACCEL_CONFIG_AFS_SEL_M) >> + MPU9150_ACCEL_CONFIG_AFS_SEL_S); + } + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = MPU9150_STATE_WRITE; + + // + // Write the requested registers to the MPU9150. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, + MPU9150Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = MPU9150_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a MPU9150 register. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the MPU9150 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the +//! MPU9150. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +MPU9150ReadModifyWrite(tMPU9150 *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the MPU9150 driver is not idle (in other words, + // there is already an outstanding request to the MPU9150). + // + if(psInst->ui8State != MPU9150_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = MPU9150_STATE_RMW; + + // + // Submit the read-modify-write request to the MPU9150. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, MPU9150Callback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = MPU9150_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the accelerometer and gyroscope data from the MPU9150 and the +//! magnetometer data from the on-chip aK8975. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the MPU9150 data registers. When the +//! read has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - MPU9150DataAccelGetRaw() +//! - MPU9150DataAccelGetFloat() +//! - MPU9150DataGyroGetRaw() +//! - MPU9150DataGyroGetFloat() +//! - MPU9150DataMagnetoGetRaw() +//! - MPU9150DataMagnetoGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +MPU9150DataRead(tMPU9150 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the MPU9150 driver is not idle (in other words, + // there is already an outstanding request to the MPU9150). + // + if(psInst->ui8State != MPU9150_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = MPU9150_STATE_RD_DATA; + + // + // Read the data registers from the MPU9150. + // + // (ACCEL_XOUT_H(0x3B) -> GYRO_ZOUT_L(0x48) = 14 bytes + // Grab Ext Sens Data as well for another 8 bytes. ST1 + Mag Data + ST2 + // + psInst->uCommand.pui8Buffer[0] = MPU9150_O_ACCEL_XOUT_H; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 22, + MPU9150Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = MPU9150_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw accelerometer data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param pui16AccelX is a pointer to the value into which the raw X-axis +//! accelerometer data is stored. +//! \param pui16AccelY is a pointer to the value into which the raw Y-axis +//! accelerometer data is stored. +//! \param pui16AccelZ is a pointer to the value into which the raw Z-axis +//! accelerometer data is stored. +//! +//! This function returns the raw accelerometer data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU9150DataAccelGetRaw(tMPU9150 *psInst, uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, uint_fast16_t *pui16AccelZ) +{ + // + // Return the raw accelerometer values. + // + if(pui16AccelX) + { + *pui16AccelX = (psInst->pui8Data[0] << 8) | psInst->pui8Data[1]; + } + if(pui16AccelY) + { + *pui16AccelY = (psInst->pui8Data[2] << 8) | psInst->pui8Data[3]; + } + if(pui16AccelZ) + { + *pui16AccelZ = (psInst->pui8Data[4] << 8) | psInst->pui8Data[5]; + } +} + +//***************************************************************************** +// +//! Gets the accelerometer data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param pfAccelX is a pointer to the value into which the X-axis +//! accelerometer data is stored. +//! \param pfAccelY is a pointer to the value into which the Y-axis +//! accelerometer data is stored. +//! \param pfAccelZ is a pointer to the value into which the Z-axis +//! accelerometer data is stored. +//! +//! This function returns the accelerometer data from the most recent data +//! read, converted into meters per second squared (m/s^2). If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU9150DataAccelGetFloat(tMPU9150 *psInst, float *pfAccelX, float *pfAccelY, + float *pfAccelZ) +{ + float fFactor; + + // + // Get the acceleration conversion factor for the current data format. + // + fFactor = g_fMPU9150AccelFactors[psInst->ui8AccelAfsSel]; + + // + // Convert the accelerometer values into m/sec^2 + // + if(pfAccelX) + { + *pfAccelX = ((float)(int16_t)((psInst->pui8Data[0] << 8) | + psInst->pui8Data[1]) * fFactor); + } + if(pfAccelY) + { + *pfAccelY = ((float)(int16_t)((psInst->pui8Data[2] << 8) | + psInst->pui8Data[3]) * fFactor); + } + if(pfAccelZ) + { + *pfAccelZ = ((float)(int16_t)((psInst->pui8Data[4] << 8) | + psInst->pui8Data[5]) * fFactor); + } +} + +//***************************************************************************** +// +//! Gets the raw gyroscope data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param pui16GyroX is a pointer to the value into which the raw X-axis +//! gyroscope data is stored. +//! \param pui16GyroY is a pointer to the value into which the raw Y-axis +//! gyroscope data is stored. +//! \param pui16GyroZ is a pointer to the value into which the raw Z-axis +//! gyroscope data is stored. +//! +//! This function returns the raw gyroscope data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU9150DataGyroGetRaw(tMPU9150 *psInst, uint_fast16_t *pui16GyroX, + uint_fast16_t *pui16GyroY, uint_fast16_t *pui16GyroZ) +{ + // + // Return the raw gyroscope values. + // + if(pui16GyroX) + { + *pui16GyroX = (psInst->pui8Data[8] << 8) | psInst->pui8Data[9]; + } + if(pui16GyroY) + { + *pui16GyroY = (psInst->pui8Data[10] << 8) | psInst->pui8Data[11]; + } + if(pui16GyroZ) + { + *pui16GyroZ = (psInst->pui8Data[12] << 8) | psInst->pui8Data[13]; + } +} + +//***************************************************************************** +// +//! Gets the gyroscope data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param pfGyroX is a pointer to the value into which the X-axis +//! gyroscope data is stored. +//! \param pfGyroY is a pointer to the value into which the Y-axis +//! gyroscope data is stored. +//! \param pfGyroZ is a pointer to the value into which the Z-axis +//! gyroscope data is stored. +//! +//! This function returns the gyroscope data from the most recent data read, +//! converted into radians per second. If any of the output data pointers are +//! \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU9150DataGyroGetFloat(tMPU9150 *psInst, float *pfGyroX, float *pfGyroY, + float *pfGyroZ) +{ + float fFactor; + int16_t i16Temp; + + // + // Get the gyroscope conversion factor for the current data format. + // + fFactor = g_fMPU9150GyroFactors[psInst->ui8GyroFsSel]; + + // + // Convert the gyroscope values into rad/sec + // + if(pfGyroX) + { + i16Temp = (int16_t)psInst->pui8Data[8]; + i16Temp <<= 8; + i16Temp += psInst->pui8Data[9]; + *pfGyroX = (float)i16Temp; + *pfGyroX *= fFactor; + } + if(pfGyroY) + { + i16Temp = (int16_t)psInst->pui8Data[10]; + i16Temp <<= 8; + i16Temp += psInst->pui8Data[11]; + *pfGyroY = (float)i16Temp; + *pfGyroY *= fFactor; + } + if(pfGyroZ) + { + i16Temp = (int16_t)psInst->pui8Data[12]; + i16Temp <<= 8; + i16Temp += psInst->pui8Data[13]; + *pfGyroZ = (float)i16Temp; + *pfGyroZ *= fFactor; + } +} + +//***************************************************************************** +// +//! Gets the raw magnetometer data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param pui16MagnetoX is a pointer to the value into which the raw X-axis +//! magnetometer data is stored. +//! \param pui16MagnetoY is a pointer to the value into which the raw Y-axis +//! magnetometer data is stored. +//! \param pui16MagnetoZ is a pointer to the value into which the raw Z-axis +//! magnetometer data is stored. +//! +//! This function returns the raw magnetometer data from the most recent data +//! read. The data is not manipulated in any way by the driver. If any of the +//! output data pointers are \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU9150DataMagnetoGetRaw(tMPU9150 *psInst, uint_fast16_t *pui16MagnetoX, + uint_fast16_t *pui16MagnetoY, + uint_fast16_t *pui16MagnetoZ) +{ + uint8_t *pui8ExtSensData; + + pui8ExtSensData = &(psInst->pui8Data[14]); + + // + // Return the raw magnetometer values. + // + if(pui16MagnetoX) + { + *pui16MagnetoX = (pui8ExtSensData[2] << 8) | pui8ExtSensData[1]; + } + if(pui16MagnetoY) + { + *pui16MagnetoY = (pui8ExtSensData[4] << 8) | pui8ExtSensData[3]; + } + if(pui16MagnetoZ) + { + *pui16MagnetoZ = (pui8ExtSensData[6] << 8) | pui8ExtSensData[5]; + } +} + +//***************************************************************************** +// +//! Gets the magnetometer data from the most recent data read. +//! +//! \param psInst is a pointer to the MPU9150 instance data. +//! \param pfMagnetoX is a pointer to the value into which the X-axis +//! magnetometer data is stored. +//! \param pfMagnetoY is a pointer to the value into which the Y-axis +//! magnetometer data is stored. +//! \param pfMagnetoZ is a pointer to the value into which the Z-axis +//! magnetometer data is stored. +//! +//! This function returns the magnetometer data from the most recent data read, +//! converted into tesla. If any of the output data pointers are +//! \b NULL, the corresponding data is not provided. +//! +//! \return None. +// +//***************************************************************************** +void +MPU9150DataMagnetoGetFloat(tMPU9150 *psInst, float *pfMagnetoX, + float *pfMagnetoY, float *pfMagnetoZ) +{ + int16_t *pi16Data; + + pi16Data = (int16_t *)(psInst->pui8Data + 15); + + // + // Convert the magnetometer values into floating-point tesla values. + // + if(pfMagnetoX) + { + *pfMagnetoX = (float)pi16Data[0]; + *pfMagnetoX *= CONVERT_TO_TESLA; + } + if(pfMagnetoY) + { + *pfMagnetoY = (float)pi16Data[1]; + *pfMagnetoY *= CONVERT_TO_TESLA; + } + if(pfMagnetoZ) + { + *pfMagnetoZ = (float)pi16Data[2]; + *pfMagnetoZ *= CONVERT_TO_TESLA; + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/mpu9150.h b/sensorlib/mpu9150.h new file mode 100644 index 0000000..459d6ca --- /dev/null +++ b/sensorlib/mpu9150.h @@ -0,0 +1,187 @@ +//***************************************************************************** +// +// mpu9150.h - Prototypes for the MPU9150 accelerometer, gyroscope, and +// magnetometer driver. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_MPU9150_H__ +#define __SENSORLIB_MPU9150_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the MPU9150 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the MPU9150. + // + tI2CMInstance *psI2CInst; + + // + // The AK8975 inst that used to access the on-chip AK8975 magnetometer + // + tAK8975 sAK8975Inst; + + // + // The I2C address of the MPU9150. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the MPU9150. + // + uint8_t ui8State; + + // + // The current accelerometer afs_sel setting + // + uint8_t ui8AccelAfsSel; + + // + // The new accelerometer afs_sel setting, which is used when a register + // write succeeds. + // + uint8_t ui8NewAccelAfsSel; + + // + // The current gyroscope fs_sel setting + // + uint8_t ui8GyroFsSel; + + // + // The new gyroscope fs_sel setting, which is used when a register write + // succeeds. + // + uint8_t ui8NewGyroFsSel; + + // + // The data buffer used for sending/receiving data to/from the MPU9150. + // + uint8_t pui8Data[24]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The callback data provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[6]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tMPU9150; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t MPU9150Init(tMPU9150 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern tAK8975 *MPU9150InstAK8975Get(tMPU9150 *psInst); +extern uint_fast8_t MPU9150Read(tMPU9150 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t MPU9150Write(tMPU9150 *psInst, uint_fast8_t ui8Reg, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t MPU9150ReadModifyWrite(tMPU9150 *psInst, + uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t MPU9150DataRead(tMPU9150 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void MPU9150DataAccelGetRaw(tMPU9150 *psInst, + uint_fast16_t *pui16AccelX, + uint_fast16_t *pui16AccelY, + uint_fast16_t *pui16AccelZ); +extern void MPU9150DataAccelGetFloat(tMPU9150 *psInst, float *pfAccelX, + float *pfAccelY, float *pfAccelZ); +extern void MPU9150DataGyroGetRaw(tMPU9150 *psInst, uint_fast16_t *pui16GyroX, + uint_fast16_t *pui16GyroY, + uint_fast16_t *pui16GyroZ); +extern void MPU9150DataGyroGetFloat(tMPU9150 *psInst, float *pfGyroX, + float *pfGyroY, float *pfGyroZ); +extern void MPU9150DataMagnetoGetRaw(tMPU9150 *psInst, + uint_fast16_t *pui16MagnetoX, + uint_fast16_t *pui16MagnetoY, + uint_fast16_t *pui16MagnetoZ); +extern void MPU9150DataMagnetoGetFloat(tMPU9150 *psInst, float *pfMagnetoX, + float *pfMagnetoY, float *pfMagnetoZ); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_MPU9150_H__ diff --git a/sensorlib/quaternion.c b/sensorlib/quaternion.c new file mode 100644 index 0000000..78296aa --- /dev/null +++ b/sensorlib/quaternion.c @@ -0,0 +1,286 @@ +//***************************************************************************** +// +// quaternion.c - Functions for performing quaternion operations. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/quaternion.h" + +//***************************************************************************** +// +//! \addtogroup quaternion_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// If M_PI has not been defined by the system headers, define it here. +// +//***************************************************************************** +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +//***************************************************************************** +// +//! Computes a quaternion from a set of eueler angles specified in degrees +//! +//! \param pfQOut is the inverted quaternion in W,X,Y,Z form +//! \param fRollDeg is roll in degrees +//! \param fPitchDeg is pitch in degrees +//! \param fYawDeg is yaw in degrees +//! +//! This function computes a quaternion from a set of euler angles specified +//! in degrees +//! +//! \return Returns a quaternion representing the provided eulers +// +//***************************************************************************** +void +QuaternionFromEuler(float pfQOut[4], float fRollDeg, float fPitchDeg, + float fYawDeg) +{ + float fRoll, fPitch, fYaw; + float fCOSY, fCOSP, fCOSR; + float fSINY, fSINP, fSINR; + + // + // Convert roll, pitch, and yaw from degrees into radians + // + fRoll = fRollDeg * M_PI / 180.0; + fPitch = fPitchDeg * M_PI / 180.0; + fYaw = fYawDeg * M_PI / 180.0; + + // + // Pre-calculate the cosine of (yaw, pitch, roll divided by 2) + // + fCOSY = cosf(fYaw / 2.0); + fCOSP = cosf(fPitch / 2.0); + fCOSR = cosf(fRoll / 2.0); + + // + // Pre-calculate the sine of (yaw, pitch, roll divided by 2) + // + fSINY = sinf(fYaw / 2.0); + fSINP = sinf(fPitch / 2.0); + fSINR = sinf(fRoll / 2.0); + + // + // The W component + // + pfQOut[Q_W] = fCOSY * fCOSP * fCOSR - fSINY * fSINP * fSINR; + + // + // The X component + // + pfQOut[Q_X] = fSINY * fSINP * fCOSR + fCOSY * fCOSP * fSINR; + + // + // The Y component + // + pfQOut[Q_Y] = fCOSY * fSINP * fCOSR - fSINY * fCOSP * fSINR; + + // + // The Z component + // + pfQOut[Q_Z] = fSINY * fCOSP * fCOSR + fCOSY * fSINP * fSINR; +} + +//***************************************************************************** +// +//! Computes the magnitude of a quaternion. +//! +//! \param pfQIn is the source quaternion in W,X,Y,Z form +//! +//! This function computes the magnitude of a quaternion by summing the square +//! of each of the quatnerion components. +//! +//! \return Returns the scalar magnitude of the quaternion +// +//***************************************************************************** +float +QuaternionMagnitude(float pfQIn[4]) +{ + float fSumSq; + + // + // Calculate the magnitude of the quaternion by finding the sum of the + // squares of each component. + // + fSumSq = ((pfQIn[Q_W] * pfQIn[Q_W]) + (pfQIn[Q_X] * pfQIn[Q_X]) + + (pfQIn[Q_Y] * pfQIn[Q_Y]) + (pfQIn[Q_Z] * pfQIn[Q_Z])); + + return(fSumSq); +} + +//***************************************************************************** +// +//! Computes the inverse of a quaternion. +//! +//! \param pfQOut is the inverted quaternion in W,X,Y,Z form +//! \param pfQIn is the source quaternion in W,X,Y,Z form +//! +//! This function computes the inverse of a quaternion. The inverse of a +//! quaternion produces a rotation opposite to the source quaternion. This +//! can be achieved by simply changing the signs of the imaginary components +//! of a quaternion when the quatnerion is a unit quaternion. +//! +//! \return Returns the inverse of a quaternion. +// +//***************************************************************************** +void +QuaternionInverse(float pfQOut[4], float pfQIn[4]) +{ + float fMag; + + // + // Find magnitude of the quaternion. This will be used to normalize the + // source quaternion if it's not already. If it is a unit quaternion then + // the magnitude should be nearly equal to 1.0 and dividing by the + // magnitude has no mathemtical effect. + // + fMag = QuaternionMagnitude(pfQIn); + + // + // Normalize the W component + // + pfQOut[Q_W] = pfQIn[Q_W] / fMag; + + // + // Invert and normalize the X component + // + pfQOut[Q_X] = -pfQIn[Q_X] / fMag; + + // + // Invert and normalize the Y component + // + pfQOut[Q_Y] = -pfQIn[Q_Y] / fMag; + + // + // Invert and normalize the Z component + // + pfQOut[Q_Z] = -pfQIn[Q_Z] / fMag; +} + +//***************************************************************************** +// +//! Computes the product of two quaternions. +//! +//! \param pfQOut is the product of In1 X In2 +//! \param pfQIn1 is the source quaternion in W,X,Y,Z form +//! \param pfQIn2 is the source quaternion in W,X,Y,Z form +//! +//! This function computes the cross product of two quaternions. +//! +//! \return Returns the cross product of the two quaternions. +// +//***************************************************************************** +void +QuaternionMult(float pfQOut[4], float pfQIn1[4], float pfQIn2[4]) +{ + // + // Let Q1 and Q2 be two quaternions with components w,x,y,z + // Let Qp be the cross product Q1 x Q2. The components of Qp can be + // calculated as follows: + // + // Qp.w = (Q1w Q2w) - (Q1x Q2x) - (Q1y Q2y) - (Q1z Q2z) + // Qp.x = (Q1w Q2x) + (Q1x Q2w) - (Q1z Q2y) + (Q1y Q2z) + // Qp.y = (Q1y Q2w) + (Q1z Q2x) + (Q1w Q2y) - (Q1x Q2z) + // Qp.z = (Q1z Q2w) - (Q1y Q2x) + (Q1x Q2y) + (Q1w Q2z) + // + + // + // Calculate the W term + // + pfQOut[Q_W] = ((pfQIn2[Q_W] * pfQIn1[Q_W]) - (pfQIn2[Q_X] * pfQIn1[Q_X]) - + (pfQIn2[Q_Y] * pfQIn1[Q_Y]) - (pfQIn2[Q_Z] * pfQIn1[Q_Z])); + + // + // Calculate the X term + // + pfQOut[Q_X]= ((pfQIn2[Q_X] * pfQIn1[Q_W]) + (pfQIn2[Q_W] * pfQIn1[Q_X]) - + (pfQIn2[Q_Y] * pfQIn1[Q_Z]) + (pfQIn2[Q_Z] * pfQIn1[Q_Y])); + + // + // Calculate the Y term + // + pfQOut[Q_Y]= ((pfQIn2[Q_W] * pfQIn1[Q_Y]) + (pfQIn2[Q_X] * pfQIn1[Q_Z]) - + (pfQIn2[Q_Y] * pfQIn1[Q_W]) - (pfQIn2[Q_Z] * pfQIn1[Q_X])); + + // + // Calculate the Z term + // + pfQOut[Q_Z] = ((pfQIn2[Q_W] * pfQIn1[Q_Z]) - (pfQIn2[Q_X] * pfQIn1[Q_Y]) - + (pfQIn2[Q_Y] * pfQIn1[Q_X]) + (pfQIn2[Q_Z] * pfQIn1[Q_W])); +} + +//***************************************************************************** +// +//! Computes the angle between two quaternions +//! +//! \param pfQIn1 is a source quaternion in W,X,Y,Z form +//! \param pfQIn2 is a source quaternion in W,X,Y,Z form +//! +//! This function computes the angle between two quaternions. +//! +//! \return Returns the angle, in radians, between the two quaternions. +// +//***************************************************************************** +float +QuaternionAngle(float pfQIn1[4], float pfQIn2[4]) +{ + float pfQInv[4]; + float pfQProd[4]; + + // + // Let Q1 and Q2 be two quaternions having components w,x,y,z. The angle + // between the orientations represented by Q1 and Q2 can be calculated + // with: + // + // angle = arccos( (Q2 * Q1').w ) * 2.0; + // + // where Q1' is the inverse of Q1 + // + + // + // Calculate the inverse of Q1 + // + QuaternionInverse(pfQInv, pfQIn1); + + // + // Find the product of Q2 x Q1` + // + QuaternionMult(pfQProd, pfQIn2, pfQInv); + + // + // calculate the arccos of the w component of the previous product. + // + return(acosf(pfQProd[Q_W]) * 2.0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/quaternion.h b/sensorlib/quaternion.h new file mode 100644 index 0000000..79f9270 --- /dev/null +++ b/sensorlib/quaternion.h @@ -0,0 +1,70 @@ +//***************************************************************************** +// +// quaternion.h - Prototypes for the quaternion functions. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_QUATERNION_H__ +#define __SENSORLIB_QUATERNION_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The index of the components in a quaternion vector. +// +//***************************************************************************** +#define Q_W 0 +#define Q_X 1 +#define Q_Y 2 +#define Q_Z 3 + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern void QuaternionFromEuler(float pfQOut[4], float fRollDeg, + float fPitchDeg, float fYawDeg); +extern float QuaternionMagnitude(float pfQIn[4]); +extern void QuaternionInverse(float pfQOut[4], float pfQIn[4]); +extern void QuaternionMult(float pfQOut[4], float pfQIn1[4], float pfQIn2[4]); +extern float QuaternionAngle(float pfQIn1[4], float pfQIn2[4]); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_QUATERNION_H__ diff --git a/sensorlib/readme.txt b/sensorlib/readme.txt new file mode 100644 index 0000000..209ec0c --- /dev/null +++ b/sensorlib/readme.txt @@ -0,0 +1,21 @@ +This project will build the Texas Instruments Sensor Library. + +------------------------------------------------------------------------------- + +Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +Software License Agreement + +Texas Instruments (TI) is supplying this software for use solely and +exclusively on TI's microcontroller products. The software is owned by +TI and/or its suppliers, and is protected under applicable copyright +laws. You may not combine this software with "viral" open-source +software in order to form a larger program. + +THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, FOR ANY REASON WHATSOEVER. + +This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. diff --git a/sensorlib/rvmdk/sensorlib.lib b/sensorlib/rvmdk/sensorlib.lib new file mode 100644 index 0000000..582e01b Binary files /dev/null and b/sensorlib/rvmdk/sensorlib.lib differ diff --git a/sensorlib/sensorlib.ewp b/sensorlib/sensorlib.ewp new file mode 100644 index 0000000..9504f35 --- /dev/null +++ b/sensorlib/sensorlib.ewp @@ -0,0 +1,833 @@ + + + + 1 + + Debug + + ARM + + 1 + + General + 3 + + 14 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 19 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 7 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 5 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + Source + + $PROJ_DIR$\ak8963.c + + + $PROJ_DIR$\ak8975.c + + + $PROJ_DIR$\bmp180.c + + + $PROJ_DIR$\bq27510g3.c + + + $PROJ_DIR$\cm3218.c + + + $PROJ_DIR$\comp_dcm.c + + + $PROJ_DIR$\i2cm_drv.c + + + $PROJ_DIR$\isl29023.c + + + $PROJ_DIR$\kxti9.c + + + $PROJ_DIR$\l3gd20h.c + + + $PROJ_DIR$\lsm303d.c + + + $PROJ_DIR$\lsm303dlhc_accel.c + + + $PROJ_DIR$\lsm303dlhc_mag.c + + + $PROJ_DIR$\magneto.c + + + $PROJ_DIR$\mpu6050.c + + + $PROJ_DIR$\mpu9150.c + + + $PROJ_DIR$\quaternion.c + + + $PROJ_DIR$\sht21.c + + + $PROJ_DIR$\tmp006.c + + + $PROJ_DIR$\tmp100.c + + + $PROJ_DIR$\vector.c + + + diff --git a/sensorlib/sensorlib.uvopt b/sensorlib/sensorlib.uvopt new file mode 100644 index 0000000..2d5838b --- /dev/null +++ b/sensorlib/sensorlib.uvopt @@ -0,0 +1,524 @@ + + + + 1.0 + +
### uVision Project, (C) Keil Software
+ + + *.c + *.s*; *.src; *.a* + *.obj + *.lib + *.txt; *.h; *.inc + *.plm + *.cpp + + + + 0 + 0 + + + + sensorlib + 0x4 + ARM-ADS + + 8000000 + + 1 + 1 + 1 + 0 + + + 1 + 65535 + 0 + 0 + 0 + + + 79 + 66 + 8 + .\rvmdk\ + + + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 0 + 0 + 0 + 0 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + + + 1 + 0 + 1 + + 255 + + + 0 + Data Sheet + DATASHTS\Luminary\TM4C1230C3PM.PDF + + + + SARMCM3.DLL + -MPU + DCM.DLL + -pCM4 + SARMCM3.DLL + -MPU + TCM.DLL + -pCM4 + + + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 1 + 0 + 1 + 0 + 0 + 3 + + + + + + + + + + + BIN\lmidk-agdi.dll + + + + 0 + lmidk-agdi + -O4622 -S3 -FO29 + + + + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + + + Source + 1 + 0 + 0 + + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\ak8963.c + ak8963.c + + + 1 + 2 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\ak8975.c + ak8975.c + + + 1 + 3 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\bmp180.c + bmp180.c + + + 1 + 4 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\bq27510g3.c + bq27510g3.c + + + 1 + 5 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\cm3218.c + cm3218.c + + + 1 + 6 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\comp_dcm.c + comp_dcm.c + + + 1 + 7 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\i2cm_drv.c + i2cm_drv.c + + + 1 + 8 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\isl29023.c + isl29023.c + + + 1 + 9 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\kxti9.c + kxti9.c + + + 1 + 10 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\l3gd20h.c + l3gd20h.c + + + 1 + 11 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\lsm303d.c + lsm303d.c + + + 1 + 12 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\lsm303dlhc_accel.c + lsm303dlhc_accel.c + + + 1 + 13 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\lsm303dlhc_mag.c + lsm303dlhc_mag.c + + + 1 + 14 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\magneto.c + magneto.c + + + 1 + 15 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\mpu6050.c + mpu6050.c + + + 1 + 16 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\mpu9150.c + mpu9150.c + + + 1 + 17 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\quaternion.c + quaternion.c + + + 1 + 18 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\sht21.c + sht21.c + + + 1 + 19 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\tmp006.c + tmp006.c + + + 1 + 20 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\tmp100.c + tmp100.c + + + 1 + 21 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + .\vector.c + vector.c + + + + + Documentation + 1 + 0 + 0 + + 2 + 22 + 5 + 0 + 1 + 0 + 0 + 1 + 1 + 0 + .\readme.txt + readme.txt + + 44 + 0 + 1 + + -1 + -1 + + + -1 + -1 + + + 0 + 0 + 729 + 300 + + + + + + + 1 + 0 + + 100 + 0 + + + .\readme.txt + 0 + 1 + 1 + + + + + +
diff --git a/sensorlib/sensorlib.uvproj b/sensorlib/sensorlib.uvproj new file mode 100644 index 0000000..7d88e4c --- /dev/null +++ b/sensorlib/sensorlib.uvproj @@ -0,0 +1,510 @@ + + + + 1.1 + +
### uVision Project, (C) Keil Software
+ + + + sensorlib + 0x4 + ARM-ADS + + + TM4C1230C3PM + Texas Instruments + IRAM(0x20000000-0x20002FFF) IROM(0-0x7FFF) CLOCK(8000000) CPUTYPE("Cortex-M4") FPU2 + + "STARTUP\Luminary\Startup.s" ("Luminary Startup Code") + UL2CM3(-O207 -S0 -C0 -FO7 -FD20000000 -FC800 -FN1 -FF0LM4F_32 -FS00 -FL08000) + 5919 + LM4Fxxxx.H + + + + + + + + + + SFD\Luminary\TM4C1230C3PM.SFR + 0 + + + + Luminary\ + Luminary\ + + 0 + 0 + 0 + 0 + 1 + + .\rvmdk\ + sensorlib + 0 + 1 + 0 + 1 + 1 + .\rvmdk\ + 1 + 0 + 0 + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + 0 + + + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + + + SARMCM3.DLL + -MPU + DCM.DLL + -pCM4 + SARMCM3.DLL + -MPU + TCM.DLL + -pCM4 + + + + 1 + 0 + 0 + 0 + 16 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + + + 0 + 1 + 0 + 1 + 1 + 1 + 0 + 1 + + 0 + 3 + + + + + + + + + + + + + + BIN\lmidk-agdi.dll + + + + + 1 + 0 + 0 + 0 + 1 + 4097 + + BIN\lmidk-agdi.dll + + + + + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + "Cortex-M4" + + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 2 + 0 + 0 + 8 + 1 + 0 + 0 + 3 + 3 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x3000 + + + 1 + 0x0 + 0x8000 + + + 0 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x8000 + + + 1 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x3000 + + + 0 + 0x0 + 0x0 + + + + + + 0 + 3 + 1 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 2 + 0 + + --c99 + rvmdk + + ..; + + + + 1 + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + 0 + 1 + 0 + 0x00000000 + 0x20000000 + + + + + + + + + + + + Source + + + ak8963.c + 1 + .\ak8963.c + + + ak8975.c + 1 + .\ak8975.c + + + bmp180.c + 1 + .\bmp180.c + + + bq27510g3.c + 1 + .\bq27510g3.c + + + cm3218.c + 1 + .\cm3218.c + + + comp_dcm.c + 1 + .\comp_dcm.c + + + i2cm_drv.c + 1 + .\i2cm_drv.c + + + isl29023.c + 1 + .\isl29023.c + + + kxti9.c + 1 + .\kxti9.c + + + l3gd20h.c + 1 + .\l3gd20h.c + + + lsm303d.c + 1 + .\lsm303d.c + + + lsm303dlhc_accel.c + 1 + .\lsm303dlhc_accel.c + + + lsm303dlhc_mag.c + 1 + .\lsm303dlhc_mag.c + + + magneto.c + 1 + .\magneto.c + + + mpu6050.c + 1 + .\mpu6050.c + + + mpu9150.c + 1 + .\mpu9150.c + + + quaternion.c + 1 + .\quaternion.c + + + sht21.c + 1 + .\sht21.c + + + tmp006.c + 1 + .\tmp006.c + + + tmp100.c + 1 + .\tmp100.c + + + vector.c + 1 + .\vector.c + + + + + Documentation + + + readme.txt + 5 + .\readme.txt + + + + + + + +
diff --git a/sensorlib/sht21.c b/sensorlib/sht21.c new file mode 100644 index 0000000..0698593 --- /dev/null +++ b/sensorlib/sht21.c @@ -0,0 +1,564 @@ +//***************************************************************************** +// +// sht21.c - Driver for the SHT21 accelerometer. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include "sensorlib/hw_sht21.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/sht21.h" + +//***************************************************************************** +// +//! \addtogroup sht21_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the SHT21 state machine. +// +//***************************************************************************** +#define SHT21_STATE_IDLE 0 // State machine is idle +#define SHT21_STATE_INIT 1 // Waiting for initialization +#define SHT21_STATE_READ 2 // Waiting for register read +#define SHT21_STATE_WRITE 3 // Waiting for register write +#define SHT21_STATE_RMW 4 +#define SHT21_STATE_READ_DATA 5 // Waiting for temperature or + // humidity data + +//***************************************************************************** +// +// The callback function that is called when I2C transactions to/from the +// SHT21 have completed. +// +//***************************************************************************** +static void +SHT21Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tSHT21 *psInst; + + // + // Convert the instance data into a pointer to a tSHT221 structure. + // + psInst = (tSHT21 *)pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = SHT21_STATE_IDLE; + } + + // + // Determine the current state of the SHT21 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case SHT21_STATE_INIT: + case SHT21_STATE_READ: + case SHT21_STATE_WRITE: + case SHT21_STATE_READ_DATA: + case SHT21_STATE_RMW: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = SHT21_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == SHT21_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the SHT21 driver. +//! +//! \param psInst is a pointer to the SHT21 instance data. +//! \param psI2CInst is a pointer to the I2C driver instance data. +//! \param ui8I2CAddr is the I2C address of the SHT21 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the SHT21 driver, preparing it for operation, and +//! initiates a reset of the SHT21 device, clearing any previous configuration +//! data. +//! +//! \return Returns 1 if the SHT21 driver was successfully initialized and 0 if +//! it was not. +// +//***************************************************************************** +uint_fast8_t +SHT21Init(tSHT21 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Initialize the SHT21 instance structure. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = SHT21_STATE_INIT; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Perform a soft reset of the SHT21. + // + psInst->pui8Data[0] = SHT21_CMD_SOFT_RESET; + if(I2CMWrite(psInst->psI2CInst, ui8I2CAddr, psInst->pui8Data, 1, + SHT21Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = SHT21_STATE_IDLE; + return(0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from SHT21 registers. +//! +//! \param psInst is a pointer to the SHT21 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui8Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count the number of data bytes to read. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the SHT21. +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +SHT21Read(tSHT21 *psInst, uint_fast8_t ui8Reg, uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the SHT21 driver is not idle (in other words, there + // is already an outstanding request to the SHT21). + // + if(psInst->ui8State != SHT21_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = SHT21_STATE_READ; + + // + // Read the requested registers from the SHT21. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, pui8Data, ui16Count, + SHT21Callback, (void *)psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = SHT21_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to SHT21 registers. +//! +//! \param psInst is a pointer to the SHT21 instance data. +//! \param ui8Reg is the register offset to be written. +//! \param pui8Data is the data buffer bytes to write. +//! \param ui16Count is the number of bytes to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the SHT21. The first byte of the \e pui8Data buffer contains the value to +//! be written into the \e ui8Reg register, the second value contains the data +//! to be written into the next register, and so on. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +SHT21Write(tSHT21 *psInst, uint_fast8_t ui8Reg, const uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the SHT21 driver is not idle (in other words, there + // is already an outstanding request to the SHT21). + // + if(psInst->ui8State != SHT21_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = SHT21_STATE_WRITE; + + // + // Write the requested registers to the SHT21. + // + if(I2CMWrite8(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui8Data, ui16Count, SHT21Callback, + psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = SHT21_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a SHT21 register. +//! +//! \param psInst is a pointer to the SHT21 instance data. +//! \param ui8Reg is the register to modify. +//! \param ui8Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui8Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the SHT21 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui8Mask, ORed with \e ui8Value, and then written back to the SHT21. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +SHT21ReadModifyWrite(tSHT21 *psInst, uint_fast8_t ui8Reg, uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the SHT21 driver is not idle (in other words, there + // is already an outstanding request to the SHT21). + // + if(psInst->ui8State != SHT21_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = SHT21_STATE_RMW; + + // + // Submit the read-modify-write request to the TMP006. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui8Mask, ui8Value, SHT21Callback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = SHT21_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the temperature and humidity data from the SHT21. +//! +//! \param psInst is a pointer to the SHT21 instance data +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the SHT21 data registers. The user must +//! first initiate a measurement by using the SHT21Write() function configured +//! to write the command for a humidity or temperature measurement. In the +//! case of a measurement with I2C bus hold, this function is not needed. When +//! the read has completed (as indicated by callback function), the new +//! readings can be obtained via: +//! +//! - SHT21DataTemperatureGetRaw() +//! - SHT21DataTemperatureGetFloat() +//! - SHT21DataHumidityGetRaw() +//! - SHT21DataHumidityGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +SHT21DataRead(tSHT21 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the SHT21 driver is not idle (in other words, there + // is already an outstanding request to the SHT21). + // + if(psInst->ui8State != SHT21_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = SHT21_STATE_READ_DATA; + + // + // Read the data registers from the SHT21. + // + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, 0, 0, psInst->pui8Data, 2, + SHT21Callback, psInst) == 0) + { + psInst->ui8State = SHT21_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Returns the raw temperature measurement as received from the SHT21. +//! +//! \param psInst is a pointer to the SHT21 instance data. +//! \param pui16Temperature is a pointer to the value into which the raw +//! temperature data is stored. +//! +//! This function returns the raw temperature data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +SHT21DataTemperatureGetRaw(tSHT21 *psInst, uint16_t *pui16Temperature) +{ + // + // Return the raw temperature value. + // + *pui16Temperature = ((((uint16_t)psInst->pui8Data[0]) << 8) | + (uint16_t)psInst->pui8Data[1]); +} + +//***************************************************************************** +// +//! Returns the most recent temperature measurement in floating point degrees +//! Celsius. +//! +//! \param psInst is a pointer to the SHT21 instance data. +//! \param pfTemperature is a pointer to the value into which the temperature +//! data is stored. +//! +//! This function converts the raw temperature measurement data into floating +//! point degrees Celsius and returns the result. See the SHT21 datasheet +//! section 6.2 for more information about the conversion formula used. +//! +//! \return None. +// +//***************************************************************************** +void +SHT21DataTemperatureGetFloat(tSHT21 *psInst, float *pfTemperature) +{ + uint16_t ui16TemperatureRaw; + + // + // Get the raw temperature into a floating point variable + // + SHT21DataTemperatureGetRaw(psInst, &ui16TemperatureRaw); + *pfTemperature = (float)(ui16TemperatureRaw & 0xFFFC); + + // + // Equation from SHT21 datasheet for raw to Celsius conversion. + // + *pfTemperature = -46.85 + 175.72 * (*pfTemperature / 65536.0); +} + +//***************************************************************************** +// +//! Returns the raw humidity measurement from the SHT21. +//! +//! \param psInst is a pointer to the SHT21 instance data. +//! \param pui16Humidity is a pointer to the value into which the raw humidity +//! data is stored. +//! +//! This function returns the raw humidity data from the most recent data read. +//! The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +SHT21DataHumidityGetRaw(tSHT21 *psInst, uint16_t *pui16Humidity) +{ + // + // Return the raw humidity value. + // + *pui16Humidity = ((((uint16_t)psInst->pui8Data[0]) << 8) | + (uint16_t)psInst->pui8Data[1]); +} + +//***************************************************************************** +// +//! Returns the relative humidity measurement as a floating point percentage. +//! +//! \param psInst pointer to the SHT21 instance data. +//! \param pfHumidity is a pointer to the value into which the humidity data +//! is stored. +//! +//! This function converts the raw humidity measurement to +//! floating-point-percentage relative humidity over water. For more +//! information on the conversion algorithm see the SHT21 datasheet section +//! 6.1. +//! +//! \return None. +// +//***************************************************************************** +void +SHT21DataHumidityGetFloat(tSHT21 *psInst, float *pfHumidity) +{ + uint16_t ui16HumidityRaw; + + // + // Convert the raw measure to float for later math. + // + SHT21DataHumidityGetRaw(psInst, &ui16HumidityRaw); + *pfHumidity = (float)(ui16HumidityRaw & 0xFFFC); + + // + // Convert from raw measurement to percent relative humidity over water + // per the datasheet formula. + // + *pfHumidity = -6.0 + 125.0 * (*pfHumidity / 65536.0); + + // + // Convert to a number from 0 to 1.0 instead of 0 to 100%. + // + *pfHumidity /= 100.0; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/sht21.h b/sensorlib/sht21.h new file mode 100644 index 0000000..7956b0e --- /dev/null +++ b/sensorlib/sht21.h @@ -0,0 +1,156 @@ +//***************************************************************************** +// +// sht21.h - Prototypes for the SHT21 accelerometer driver. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_SHT21_H__ +#define __SENSORLIB_SHT21_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the SHT21 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the SHT21. + // + tI2CMInstance *psI2CInst; + + // + // the I2C address of the SHT21. + // + uint8_t ui8Addr; + + // + // the state of the state machine used while accessing the + // SHT21. + // + uint8_t ui8State; + + // + // the data buffer used for sending/receiving data to/from the + // SHT21. + // + uint8_t pui8Data[5]; + + // + // the 16 bit raw temperature reading + // + uint16_t ui16Temperature; + + // + // the 16 bit raw humidity reading + // + uint16_t ui16Humidity; + + // + // the function that is called when the current request has + // completed processing. + // + tSensorCallback *pfnCallback; + + // + // the callback data provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The write state used to write register values. + // + tI2CMWrite8 sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState; + } + uCommand; +} +tSHT21; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t SHT21Init(tSHT21 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t SHT21Read(tSHT21 *psInst, uint_fast8_t ui8Reg, + uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t SHT21Write(tSHT21 *psInst, uint_fast8_t ui8Reg, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t SHT21ReadModifyWrite(tSHT21 *psInst, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, + uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t SHT21DataRead(tSHT21 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void SHT21DataTemperatureGetRaw(tSHT21 *psInst, + uint16_t *pui16Temperature); +extern void SHT21DataTemperatureGetFloat(tSHT21 *psInst, float *pfTemperature); +extern void SHT21DataHumidityGetRaw(tSHT21 *psInst, uint16_t *pui16Humidity); +extern void SHT21DataHumidityGetFloat(tSHT21 *psInst, float *pfHumidity); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_SHT21_H__ diff --git a/sensorlib/tmp006.c b/sensorlib/tmp006.c new file mode 100644 index 0000000..e29900a --- /dev/null +++ b/sensorlib/tmp006.c @@ -0,0 +1,606 @@ +//***************************************************************************** +// +// tmp006.c - Driver for the TI TMP006 Temperature Sensor +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "sensorlib/hw_tmp006.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/tmp006.h" + +//***************************************************************************** +// +//! \addtogroup tmp006_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the TMP006 state machine. +// +//***************************************************************************** +#define TMP006_STATE_IDLE 0 +#define TMP006_STATE_INIT 1 +#define TMP006_STATE_READ 2 +#define TMP006_STATE_WRITE 3 +#define TMP006_STATE_RMW 4 +#define TMP006_STATE_READ_AMB 5 +#define TMP006_STATE_READ_OBJ 6 + +//***************************************************************************** +// +// The constants used to calculate object temperature. +// +//***************************************************************************** +#define T_REF 298.15 +#define A1 1.75e-03 +#define A2 -1.678e-05 +#define B0 -2.94e-05 +#define B1 -5.70e-07 +#define B2 4.63e-09 +#define C2 13.4 + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the TMP006 +// have completed. +// +//***************************************************************************** +static void +TMP006Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tTMP006 *psInst; + + // + // Convert the instance data into a pointer to a tTMP006 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = TMP006_STATE_IDLE; + } + + // + // Determine the current state of the TMP006 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case TMP006_STATE_INIT: + case TMP006_STATE_READ: + case TMP006_STATE_WRITE: + case TMP006_STATE_RMW: + case TMP006_STATE_READ_OBJ: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = TMP006_STATE_IDLE; + + // + // Done. + // + break; + } + + // + // The ambient temperature was just read. + // + case TMP006_STATE_READ_AMB: + { + // + // Move to the read object temperature state. + // + psInst->ui8State = TMP006_STATE_READ_OBJ; + + // + // Start a read of the object temperature now that this read is + // complete. + // + psInst->uCommand.pui8Buffer[0] = TMP006_O_VOBJECT; + I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data + 2, 2, + TMP006Callback, psInst); + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == TMP006_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the TMP006 driver. +//! +//! \param psInst is a pointer to the TMP006 instance data. +//! \param psI2CInst is a pointer to the I2C driver instance data. +//! \param ui8I2CAddr is the I2C address of the TMP006 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the TMP006 driver, preparing it for operation, +//! and initiates a reset of the TMP006 device, clearing any previous +//! configuration data. +//! +//! \return Returns 1 if the TMP006 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +TMP006Init(tTMP006 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Initialize the TMP006 instance structure + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = TMP006_STATE_INIT; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Set the calibration factor to a reasonable estimate, applications + // should perform a calibration in their environment and directly overwrite + // this value after calling TMP006Init with the system specific value. + // + psInst->fCalibrationFactor = 6.40e-14; + + // + // Load the data buffer to write the reset sequence + // + psInst->pui8Data[0] = TMP006_O_CONFIG; + psInst->pui8Data[1] = (uint8_t)(TMP006_CONFIG_RESET_ASSERT >> 8); + psInst->pui8Data[2] = (uint8_t)(TMP006_CONFIG_RESET_ASSERT & 0x00FF); + + // + // Write the reset bit and issue a callback when finished. + // + if(I2CMWrite(psInst->psI2CInst, ui8I2CAddr, psInst->pui8Data, 3, + TMP006Callback, psInst) == 0) + { + // + // I2CMWrite failed so reset TMP006 state and return zero to indicate + // failure. + // + psInst->ui8State = TMP006_STATE_IDLE; + return(0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from TMP006 registers. +//! +//! \param psInst is a pointer to the TMP006 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui16Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count the number of register values to read. +//! \param pfnCallback is the function to be called when data read is complete +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the TMP006. +//! +//! \note The TMP006 does not auto-increment the register pointer, so reads of +//! more than one value returns garbage for the subsequent values. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +TMP006Read(tTMP006 *psInst, uint_fast8_t ui8Reg, uint16_t *pui16Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the TMP006 driver is not idle (in other words, there + // is already an outstanding request to the TMP006). + // + if(psInst->ui8State != TMP006_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = TMP006_STATE_READ; + + // + // Read the requested registers from the TMP006. + // + if(I2CMRead16BE(&(psInst->uCommand.sReadState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui16Data, ui16Count, + TMP006Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = TMP006_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to TMP006 registers. +//! +//! \param psInst is a pointer to the TMP006 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui16Data is a pointer to the 16-bit register data to write. +//! \param ui16Count is the number of 16-bit registers to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the TMP006. The first value in the \e pui16Data buffer contains the data +//! to be written into the \e ui8Reg register, the second value contains the +//! data to be written into the next register, and so on. +//! +//! \note The TMP006 does not auto-increment the register pointer, so writes of +//! more than one register are rejected by the TMP006. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +TMP006Write(tTMP006 *psInst, uint_fast8_t ui8Reg, const uint16_t *pui16Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the TMP006 driver is not idle (in other words, there + // is already an outstanding request to the TMP006). + // + if(psInst->ui8State != TMP006_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = TMP006_STATE_WRITE; + + // + // Write the requested registers to the TMP006. + // + if(I2CMWrite16BE(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui16Data, ui16Count, + TMP006Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = TMP006_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a TMP006 register. +//! +//! \param psInst is a pointer to the TMP006 instance data. +//! \param ui8Reg is the register offset to read modify and write +//! \param ui16Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui16Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the TMP006 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui16Mask, ORed with \e ui16Value, and then written back to the +//! TMP006. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +TMP006ReadModifyWrite(tTMP006 *psInst, uint_fast8_t ui8Reg, + uint_fast16_t ui16Mask, uint_fast16_t ui16Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the TMP006 driver is not idle (in other words, there + // is already an outstanding request to the TMP006). + // + if(psInst->ui8State != TMP006_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = TMP006_STATE_RMW; + + // + // Submit the read-modify-write request to the TMP006. + // + if(I2CMReadModifyWrite16BE(&(psInst->uCommand.sReadModifyWriteState), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui16Mask, ui16Value, TMP006Callback, + psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = TMP006_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the temperature data from the TMP006. +//! +//! \param psInst is a pointer to the TMP006 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the TMP006 data registers. When the read +//! has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - TMP006DataTemperatureGetRaw() +//! - TMP006DataTemperatureGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +TMP006DataRead(tTMP006 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the TMP006 driver is not idle (in other words, there + // is already an outstanding request to the TMP006). + // + if(psInst->ui8State != TMP006_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for ambient data read state. + // + psInst->ui8State = TMP006_STATE_READ_AMB; + + // + // Read the ambient temperature data from the TMP006. + // + psInst->uCommand.pui8Buffer[0] = TMP006_O_TAMBIENT; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 2, + TMP006Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = TMP006_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the TMP006 instance data. +//! \param pi16Ambient is a pointer to the value into which the raw ambient +//! temperature data is stored. +//! \param pi16Object is a pointer to the value into which the raw object +//! temperature data is stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +TMP006DataTemperatureGetRaw(tTMP006 *psInst, int16_t *pi16Ambient, + int16_t *pi16Object) +{ + // + // Return the raw temperature value. + // + *pi16Ambient = ((int16_t)psInst->pui8Data[0] << 8) | psInst->pui8Data[1]; + *pi16Object = ((int16_t)psInst->pui8Data[2] << 8) | psInst->pui8Data[3]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the TMP006 instance data. +//! \param pfAmbient is a pointer to the value into which the ambient +//! temperature data is stored as floating point degrees Celsius. +//! \param pfObject is a pointer to the value into which the object temperature +//! data is stored as floating point degrees Celsius. +//! +//! This function returns the temperature data from the most recent data read, +//! converted into Celsius. +//! +//! \return None. +// +//***************************************************************************** +void +TMP006DataTemperatureGetFloat(tTMP006 *psInst, float *pfAmbient, + float *pfObject) +{ + float fTdie2, fS, fVo, fVx, fObj; + int16_t i16Ambient; + int16_t i16Object; + + // + // Get the raw readings. + // + TMP006DataTemperatureGetRaw(psInst, &i16Ambient, &i16Object); + + // + // The bottom two bits are not temperature data, so discard them but keep + // the sign information. + // + *pfAmbient = (float)(i16Ambient / 4); + + // + // Divide by 32 to get unit scaling correct. + // + *pfAmbient = *pfAmbient / 32.0; + + // + // fTdie2 is measured ambient temperature in degrees Kelvin. + // + fTdie2 = *pfAmbient + T_REF; + + // + // S is the sensitivity. + // + fS = psInst->fCalibrationFactor * (1.0f + (A1 * (*pfAmbient)) + + (A2 * ((*pfAmbient) * (*pfAmbient)))); + + // + // Vos is the offset voltage. + // + fVo = B0 + (B1 * (*pfAmbient)) + (B2 * ((*pfAmbient) * (*pfAmbient))); + + // + // Vx is the difference between raw object voltage and Vos + // 156.25e-9 is nanovolts per least significant bit from the voltage + // register. + // + fVx = (((float) i16Object) * 156.25e-9) - fVo; + + // + // fObj is the feedback coefficient. + // + fObj = fVx + C2 * (fVx * fVx); + + // + // Finally calculate the object temperature. + // + *pfObject = (sqrtf(sqrtf((fTdie2 * fTdie2 * fTdie2 * fTdie2) + + (fObj / fS))) - T_REF); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/tmp006.h b/sensorlib/tmp006.h new file mode 100644 index 0000000..e22e410 --- /dev/null +++ b/sensorlib/tmp006.h @@ -0,0 +1,157 @@ +//***************************************************************************** +// +// tmp006.h - Prototypes for the Texas Instruments TMP006 temperature sensor +// driver. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_TMP006_H__ +#define __SENSORLIB_TMP006_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the TMP006 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the TMP006. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the TMP006. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the TMP006. + // + uint8_t ui8State; + + // + // The data buffer used for sending/receiving data to/from the TMP006. + // + uint8_t pui8Data[4]; + + // + // Calibration factor. Left to application to implement calibration + // See term S0 in http://www.ti.com/lit/ug/sbou107/sbou107.pdf + // + float fCalibrationFactor; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The read state used to read register values. + // + tI2CMRead16BE sReadState; + + // + // The write state used to write register values. + // + tI2CMWrite16BE sWriteState; + + // + // The read-modify-write state used to modify register values. + // + tI2CMReadModifyWrite16 sReadModifyWriteState; + } + uCommand; +} +tTMP006; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t TMP006Init(tTMP006 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t TMP006Read(tTMP006 *psInst, uint_fast8_t ui8Reg, + uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t TMP006Write(tTMP006 *psInst, uint_fast8_t ui8Reg, + const uint16_t *pui16Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t TMP006ReadModifyWrite(tTMP006 *psInst, uint_fast8_t ui8Reg, + uint_fast16_t ui16Mask, + uint_fast16_t ui16Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t TMP006DataRead(tTMP006 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void TMP006DataTemperatureGetRaw(tTMP006 *psInst, int16_t *pui16Ambient, + int16_t *pui16Object); +extern void TMP006DataTemperatureGetFloat(tTMP006 *psInst, float *pfAmbient, + float *pfObject); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_TMP006_H__ + diff --git a/sensorlib/tmp100.c b/sensorlib/tmp100.c new file mode 100644 index 0000000..d4cc3a8 --- /dev/null +++ b/sensorlib/tmp100.c @@ -0,0 +1,582 @@ +//***************************************************************************** +// +// tmp100.c - Driver for the TI TMP100 Temperature Sensor +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "sensorlib/hw_tmp100.h" +#include "sensorlib/i2cm_drv.h" +#include "sensorlib/tmp100.h" + +//***************************************************************************** +// +//! \addtogroup tmp100_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states of the TMP100 state machine. +// +//***************************************************************************** +#define TMP100_STATE_IDLE 0 +#define TMP100_STATE_INIT 1 +#define TMP100_STATE_READ 2 +#define TMP100_STATE_WRITE 3 +#define TMP100_STATE_RMW 4 + +//***************************************************************************** +// +// The callback function that is called when I2C transations to/from the TMP100 +// have completed. +// +//***************************************************************************** +static void +TMP100Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tTMP100 *psInst; + + // + // Convert the instance data into a pointer to a tTMP100 structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result in a callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = TMP100_STATE_IDLE; + } + + // + // Determine the current state of the TMP100 state machine. + // + switch(psInst->ui8State) + { + // + // All states that trivially transition to IDLE, and all unknown + // states. + // + case TMP100_STATE_INIT: + case TMP100_STATE_READ: + case TMP100_STATE_WRITE: + case TMP100_STATE_RMW: + default: + { + // + // The state machine is now idle. + // + psInst->ui8State = TMP100_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == TMP100_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Initializes the TMP100 driver. +//! +//! \param psInst is a pointer to the TMP100 instance data. +//! \param psI2CInst is a pointer to the I2C driver instance data. +//! \param ui8I2CAddr is the I2C address of the TMP100 device. +//! \param pfnCallback is the function to be called when the initialization has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initializes the TMP100 driver, preparing it for operation, +//! and initiates a reset of the TMP100 device, clearing any previous +//! configuration data. +//! +//! \return Returns 1 if the TMP100 driver was successfully initialized and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +TMP100Init(tTMP100 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Initialize the TMP100 instance structure + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8I2CAddr; + psInst->ui8State = TMP100_STATE_INIT; + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Write the configuration register to its default value. + // + psInst->pui8Data[0] = TMP100_O_CONFIG; + psInst->pui8Data[1] = 0x00; + + // + // Write the reset bit and issue a callback when finished. + // + if(I2CMWrite(psInst->psI2CInst, ui8I2CAddr, psInst->pui8Data, 2, + TMP100Callback, psInst) == 0) + { + // + // I2CMWrite failed so reset TMP100 state and return zero to indicate + // failure. + // + psInst->ui8State = TMP100_STATE_IDLE; + return(0); + } + + // + // Success + // + return(1); +} + +//***************************************************************************** +// +//! Reads data from TMP100 registers. +//! +//! \param psInst is a pointer to the TMP100 instance data. +//! \param ui8Reg is the first register to read. +//! \param pui16Data is a pointer to the location to store the data that is +//! read. +//! \param ui16Count the number of register values to read. +//! \param pfnCallback is the function to be called when data read is complete +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function reads a sequence of data values from consecutive registers in +//! the TMP100. +//! +//! \note The TMP100 does not auto-increment the register pointer, so reads of +//! more than one value returns garbage for the subsequent values. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +TMP100Read(tTMP100 *psInst, uint_fast8_t ui8Reg, uint16_t *pui16Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the TMP100 driver is not idle (in other words, there + // is already an outstanding request to the TMP100). + // + if(psInst->ui8State != TMP100_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read state. + // + psInst->ui8State = TMP100_STATE_READ; + + // + // Read the requested registers from the TMP100. + // + if(ui8Reg == TMP100_O_CONFIG) + { + // + // The configuration register is only one byte, so only a single byte + // read is necessary and no endian swapping is required. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, (uint8_t *)pui16Data, 1, + TMP100Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = TMP100_STATE_IDLE; + return(0); + } + } + else + { + // + // This is one of the temperature registers, which are 16-bit + // big-endian registers. + // + if(I2CMRead16BE(&(psInst->uCommand.sReadState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui16Data, ui16Count, + TMP100Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = TMP100_STATE_IDLE; + return(0); + } + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Writes data to TMP100 registers. +//! +//! \param psInst is a pointer to the TMP100 instance data. +//! \param ui8Reg is the first register to write. +//! \param pui16Data is a pointer to the 16-bit register data to write. +//! \param ui16Count is the number of 16-bit registers to write. +//! \param pfnCallback is the function to be called when the data has been +//! written (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function writes a sequence of data values to consecutive registers in +//! the TMP100. The first value in the \e pui16Data buffer contains the data +//! to be written into the \e ui8Reg register, the second value contains the +//! data to be written into the next register, and so on. +//! +//! \note The TMP100 does not auto-increment the register pointer, so writes of +//! more than one register are rejected by the TMP100. +//! +//! \return Returns 1 if the write was successfully started and 0 if it was +//! not. +// +//***************************************************************************** +uint_fast8_t +TMP100Write(tTMP100 *psInst, uint_fast8_t ui8Reg, const uint16_t *pui16Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the TMP100 driver is not idle (in other words, there + // is already an outstanding request to the TMP100). + // + if(psInst->ui8State != TMP100_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for write state. + // + psInst->ui8State = TMP100_STATE_WRITE; + + // + // Write the requested registers to the TMP100. + // + if(ui8Reg == TMP100_O_CONFIG) + { + // + // The configuration register is only one byte, so only a single byte + // write is necessary and no endian swapping is required. + // + psInst->uCommand.pui8Buffer[0] = ui8Reg; + psInst->uCommand.pui8Buffer[1] = *pui16Data & 0xff; + if(I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 2, TMP100Callback, + psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = TMP100_STATE_IDLE; + return(0); + } + } + else + { + // + // This is one of the temperature registers, which are 16-bit + // big-endian registers. + // + if(I2CMWrite16BE(&(psInst->uCommand.sWriteState), psInst->psI2CInst, + psInst->ui8Addr, ui8Reg, pui16Data, ui16Count, + TMP100Callback, psInst) == 0) + { + // + // The I2C write failed, so move to the idle state and return a + // failure. + // + psInst->ui8State = TMP100_STATE_IDLE; + return(0); + } + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Performs a read-modify-write of a TMP100 register. +//! +//! \param psInst is a pointer to the TMP100 instance data. +//! \param ui8Reg is the register offset to read modify and write +//! \param ui16Mask is the bit mask that is ANDed with the current register +//! value. +//! \param ui16Value is the bit mask that is ORed with the result of the AND +//! operation. +//! \param pfnCallback is the function to be called when the data has been +//! changed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function changes the value of a register in the TMP100 via a +//! read-modify-write operation, allowing one of the fields to be changed +//! without disturbing the other fields. The \e ui8Reg register is read, ANDed +//! with \e ui16Mask, ORed with \e ui16Value, and then written back to the +//! TMP100. +//! +//! \return Returns 1 if the read-modify-write was successfully started and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +TMP100ReadModifyWrite(tTMP100 *psInst, uint_fast8_t ui8Reg, + uint_fast16_t ui16Mask, uint_fast16_t ui16Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Return a failure if the TMP100 driver is not idle (in other words, there + // is already an outstanding request to the TMP100). + // + if(psInst->ui8State != TMP100_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for read-modify-write state. + // + psInst->ui8State = TMP100_STATE_RMW; + + // + // Submit the read-modify-write request to the TMP100. + // + if(ui8Reg == TMP100_O_CONFIG) + { + // + // The configuration register is only one byte, so only a single byte + // read-modify-write is necessary and no endian swapping is required. + // + if(I2CMReadModifyWrite8(&(psInst->uCommand.sReadModifyWriteState8), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui16Mask & 0xff, ui16Value & 0xff, + TMP100Callback, psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = TMP100_STATE_IDLE; + return(0); + } + } + else + { + // + // This is one of the temperature registers, which are 16-bit + // big-endian registers. + // + if(I2CMReadModifyWrite16BE(&(psInst->uCommand.sReadModifyWriteState16), + psInst->psI2CInst, psInst->ui8Addr, ui8Reg, + ui16Mask, ui16Value, TMP100Callback, + psInst) == 0) + { + // + // The I2C read-modify-write failed, so move to the idle state and + // return a failure. + // + psInst->ui8State = TMP100_STATE_IDLE; + return(0); + } + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Reads the temperature data from the TMP100. +//! +//! \param psInst is a pointer to the TMP100 instance data. +//! \param pfnCallback is the function to be called when the data has been read +//! (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read of the TMP100 data registers. When the read +//! has completed (as indicated by calling the callback function), the new +//! readings can be obtained via: +//! +//! - TMP100DataTemperatureGetRaw() +//! - TMP100DataTemperatureGetFloat() +//! +//! \return Returns 1 if the read was successfully started and 0 if it was not. +// +//***************************************************************************** +uint_fast8_t +TMP100DataRead(tTMP100 *psInst, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Return a failure if the TMP100 driver is not idle (in other words, there + // is already an outstanding request to the TMP100). + // + if(psInst->ui8State != TMP100_STATE_IDLE) + { + return(0); + } + + // + // Save the callback information. + // + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Move the state machine to the wait for data read state. + // + psInst->ui8State = TMP100_STATE_READ; + + // + // Read the temperature data from the TMP100. + // + psInst->uCommand.pui8Buffer[0] = TMP100_O_TEMP; + if(I2CMRead(psInst->psI2CInst, psInst->ui8Addr, + psInst->uCommand.pui8Buffer, 1, psInst->pui8Data, 2, + TMP100Callback, psInst) == 0) + { + // + // The I2C read failed, so move to the idle state and return a failure. + // + psInst->ui8State = TMP100_STATE_IDLE; + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the raw measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the TMP100 instance data. +//! \param pi16Temperature is a pointer to the value into which the raw +//! temperature data is stored. +//! +//! This function returns the raw measurement data from the most recent data +//! read. The data is not manipulated in any way by the driver. +//! +//! \return None. +// +//***************************************************************************** +void +TMP100DataTemperatureGetRaw(tTMP100 *psInst, int16_t *pi16Temperature) +{ + // + // Return the raw temperature value. + // + *pi16Temperature = + ((int16_t)psInst->pui8Data[0] << 8) | psInst->pui8Data[1]; +} + +//***************************************************************************** +// +//! Gets the measurement data from the most recent data read. +//! +//! \param psInst is a pointer to the TMP100 instance data. +//! \param pfTemperature is a pointer to the value into which the temperature +//! data is stored as floating point degrees Celsius. +//! +//! This function returns the temperature data from the most recent data read, +//! converted into Celsius. +//! +//! \return None. +// +//***************************************************************************** +void +TMP100DataTemperatureGetFloat(tTMP100 *psInst, float *pfTemperature) +{ + // + // Convert the temperature reading into Celcius. + // + *pfTemperature = ((float)(((int16_t)psInst->pui8Data[0] << 8) | + psInst->pui8Data[1]) / 256.0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/tmp100.h b/sensorlib/tmp100.h new file mode 100644 index 0000000..bbb923c --- /dev/null +++ b/sensorlib/tmp100.h @@ -0,0 +1,156 @@ +//***************************************************************************** +// +// tmp100.h - Prototypes for the Texas Instruments TMP100 temperature sensor +// driver. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_TMP100_H__ +#define __SENSORLIB_TMP100_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The structure that defines the internal state of the TMP100 driver. +// +//***************************************************************************** +typedef struct +{ + // + // The pointer to the I2C master interface instance used to communicate + // with the TMP100. + // + tI2CMInstance *psI2CInst; + + // + // The I2C address of the TMP100. + // + uint8_t ui8Addr; + + // + // The state of the state machine used while accessing the TMP100. + // + uint8_t ui8State; + + // + // The data buffer used for sending/receiving data to/from the TMP100. + // + uint8_t pui8Data[4]; + + // + // The function that is called when the current request has completed + // processing. + // + tSensorCallback *pfnCallback; + + // + // The pointer provided to the callback function. + // + void *pvCallbackData; + + // + // A union of structures that are used for read, write and + // read-modify-write operations. Since only one operation can be active at + // a time, it is safe to re-use the memory in this manner. + // + union + { + // + // A buffer used to store the write portion of a register read. + // + uint8_t pui8Buffer[2]; + + // + // The read state used to read register values. + // + tI2CMRead16BE sReadState; + + // + // The write state used to write register values. + // + tI2CMWrite16BE sWriteState; + + // + // The read-modify-write state used to modify 8-bit register values. + // + tI2CMReadModifyWrite8 sReadModifyWriteState8; + + // + // The read-modify-write state used to modify 16-bit register values. + // + tI2CMReadModifyWrite16 sReadModifyWriteState16; + } + uCommand; +} +tTMP100; + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern uint_fast8_t TMP100Init(tTMP100 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8I2CAddr, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t TMP100Read(tTMP100 *psInst, uint_fast8_t ui8Reg, + uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t TMP100Write(tTMP100 *psInst, uint_fast8_t ui8Reg, + const uint16_t *pui16Data, + uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t TMP100ReadModifyWrite(tTMP100 *psInst, uint_fast8_t ui8Reg, + uint_fast16_t ui16Mask, + uint_fast16_t ui16Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern uint_fast8_t TMP100DataRead(tTMP100 *psInst, + tSensorCallback *pfnCallback, + void *pvCallbackData); +extern void TMP100DataTemperatureGetRaw(tTMP100 *psInst, + int16_t *pui16Temperature); +extern void TMP100DataTemperatureGetFloat(tTMP100 *psInst, + float *pfTemperature); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_TMP100_H__ + diff --git a/sensorlib/vector.c b/sensorlib/vector.c new file mode 100644 index 0000000..208b671 --- /dev/null +++ b/sensorlib/vector.c @@ -0,0 +1,139 @@ +//***************************************************************************** +// +// vector.c - Functions for performing vector operations. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include "sensorlib/vector.h" + +//***************************************************************************** +// +//! \addtogroup vector_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Computes the dot product of two vectors. +//! +//! \param pfVectorIn1 is the first vector. +//! \param pfVectorIn2 is the second vector. +//! +//! This function computes the dot product of two 3-dimensional vector. +//! +//! \return Returns the dot product of the two vectors. +// +//***************************************************************************** +float +VectorDotProduct(float pfVectorIn1[3], float pfVectorIn2[3]) +{ + // + // Compute and return the vector dot product. + // + return((pfVectorIn1[0] * pfVectorIn2[0]) + + (pfVectorIn1[1] * pfVectorIn2[1]) + + (pfVectorIn1[2] * pfVectorIn2[2])); +} + +//***************************************************************************** +// +//! Computes the cross product of two vectors. +//! +//! \param pfVectorOut is the output vector. +//! \param pfVectorIn1 is the first vector. +//! \param pfVectorIn2 is the second vector. +//! +//! This function computes the cross product of two 3-dimensional vectors. +//! +//! \return None. +// +//***************************************************************************** +void +VectorCrossProduct(float pfVectorOut[3], float pfVectorIn1[3], + float pfVectorIn2[3]) +{ + // + // Compute the cross product of the input vectors. + // + pfVectorOut[0] = ((pfVectorIn1[1] * pfVectorIn2[2]) - + (pfVectorIn1[2] * pfVectorIn2[1])); + pfVectorOut[1] = ((pfVectorIn1[2] * pfVectorIn2[0]) - + (pfVectorIn1[0] * pfVectorIn2[2])); + pfVectorOut[2] = ((pfVectorIn1[0] * pfVectorIn2[1]) - + (pfVectorIn1[1] * pfVectorIn2[0])); +} + +//***************************************************************************** +// +//! Scales a vector. +//! +//! \param pfVectorOut is the output vector. +//! \param pfVectorIn is the input vector. +//! \param fScale is the scale factor. +//! +//! This function scales a 3-dimensional vector by multiplying each of its +//! components by the scale factor. +//! +//! \return None. +// +//***************************************************************************** +void +VectorScale(float pfVectorOut[3], float pfVectorIn[3], float fScale) +{ + // + // Scale each component of the vector by the scale factor. + // + pfVectorOut[0] = pfVectorIn[0] * fScale; + pfVectorOut[1] = pfVectorIn[1] * fScale; + pfVectorOut[2] = pfVectorIn[2] * fScale; +} + +//***************************************************************************** +// +//! Adds two vectors. +//! +//! \param pfVectorOut is the output vector. +//! \param pfVectorIn1 is the first vector. +//! \param pfVectorIn2 is the second vector. +//! +//! This function adds two 3-dimensional vectors. +//! +//! \return None. +// +//***************************************************************************** +void +VectorAdd(float pfVectorOut[3], float pfVectorIn1[3], float pfVectorIn2[3]) +{ + // + // Add the components of the two vectors. + // + pfVectorOut[0] = pfVectorIn1[0] + pfVectorIn2[0]; + pfVectorOut[1] = pfVectorIn1[1] + pfVectorIn2[1]; + pfVectorOut[2] = pfVectorIn1[2] + pfVectorIn2[2]; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/sensorlib/vector.h b/sensorlib/vector.h new file mode 100644 index 0000000..d18b1e2 --- /dev/null +++ b/sensorlib/vector.h @@ -0,0 +1,61 @@ +//***************************************************************************** +// +// vector.h - Prototypes for the vector functions. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#ifndef __SENSORLIB_VECTOR_H__ +#define __SENSORLIB_VECTOR_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern float VectorDotProduct(float pfVectorIn1[3], float pfVectorIn2[3]); +extern void VectorCrossProduct(float pfVectorOut[3], float pfVectorIn1[3], + float pfVectorIn2[3]); +extern void VectorScale(float pfVectorOut[3], float pfVectorIn[3], + float fScale); +extern void VectorAdd(float pfVectorOut[3], float pfVectorIn1[3], + float pfVectorIn2[3]); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SENSORLIB_VECTOR_H__ -- cgit v1.3.1