From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- utils/cmdline.c | 193 ++ utils/cmdline.h | 137 ++ utils/cpu_usage.c | 206 +++ utils/cpu_usage.h | 57 + utils/flash_pb.c | 493 +++++ utils/flash_pb.h | 58 + utils/fswrapper.c | 860 +++++++++ utils/fswrapper.h | 142 ++ utils/isqrt.c | 118 ++ utils/isqrt.h | 55 + utils/locator.c | 342 ++++ utils/locator.h | 61 + utils/lwiplib.c | 1399 +++++++++++++++ utils/lwiplib.h | 121 ++ utils/ptpdlib.c | 56 + utils/ptpdlib.h | 55 + utils/random.c | 169 ++ utils/random.h | 56 + utils/ringbuf.c | 712 ++++++++ utils/ringbuf.h | 105 ++ utils/scheduler.c | 310 ++++ utils/scheduler.h | 140 ++ utils/sine.c | 126 ++ utils/sine.h | 85 + utils/smbus.c | 5173 +++++++++++++++++++++++++++++++++++++++++++++++++++++ utils/smbus.h | 463 +++++ utils/softi2c.c | 1321 ++++++++++++++ utils/softi2c.h | 195 ++ utils/softssi.c | 1297 ++++++++++++++ utils/softssi.h | 280 +++ utils/softuart.c | 2591 +++++++++++++++++++++++++++ utils/softuart.h | 375 ++++ utils/speexlib.c | 377 ++++ utils/speexlib.h | 63 + utils/spi_flash.c | 2484 +++++++++++++++++++++++++ utils/spi_flash.h | 166 ++ utils/swupdate.c | 355 ++++ utils/swupdate.h | 66 + utils/tftp.c | 710 ++++++++ utils/tftp.h | 215 +++ utils/uartstdio.c | 1720 ++++++++++++++++++ utils/uartstdio.h | 86 + utils/ustdlib.c | 1826 +++++++++++++++++++ utils/ustdlib.h | 82 + utils/wavfile.c | 291 +++ utils/wavfile.h | 97 + 46 files changed, 26289 insertions(+) create mode 100644 utils/cmdline.c create mode 100644 utils/cmdline.h create mode 100644 utils/cpu_usage.c create mode 100644 utils/cpu_usage.h create mode 100644 utils/flash_pb.c create mode 100644 utils/flash_pb.h create mode 100644 utils/fswrapper.c create mode 100644 utils/fswrapper.h create mode 100644 utils/isqrt.c create mode 100644 utils/isqrt.h create mode 100644 utils/locator.c create mode 100644 utils/locator.h create mode 100644 utils/lwiplib.c create mode 100644 utils/lwiplib.h create mode 100644 utils/ptpdlib.c create mode 100644 utils/ptpdlib.h create mode 100644 utils/random.c create mode 100644 utils/random.h create mode 100644 utils/ringbuf.c create mode 100644 utils/ringbuf.h create mode 100644 utils/scheduler.c create mode 100644 utils/scheduler.h create mode 100644 utils/sine.c create mode 100644 utils/sine.h create mode 100644 utils/smbus.c create mode 100644 utils/smbus.h create mode 100644 utils/softi2c.c create mode 100644 utils/softi2c.h create mode 100644 utils/softssi.c create mode 100644 utils/softssi.h create mode 100644 utils/softuart.c create mode 100644 utils/softuart.h create mode 100644 utils/speexlib.c create mode 100644 utils/speexlib.h create mode 100644 utils/spi_flash.c create mode 100644 utils/spi_flash.h create mode 100644 utils/swupdate.c create mode 100644 utils/swupdate.h create mode 100644 utils/tftp.c create mode 100644 utils/tftp.h create mode 100644 utils/uartstdio.c create mode 100644 utils/uartstdio.h create mode 100644 utils/ustdlib.c create mode 100644 utils/ustdlib.h create mode 100644 utils/wavfile.c create mode 100644 utils/wavfile.h (limited to 'utils') diff --git a/utils/cmdline.c b/utils/cmdline.c new file mode 100644 index 0000000..c9f5971 --- /dev/null +++ b/utils/cmdline.c @@ -0,0 +1,193 @@ +//***************************************************************************** +// +// cmdline.c - Functions to help with processing command lines. +// +// 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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup cmdline_api +//! @{ +// +//***************************************************************************** + +#include +#include +#include +#include "utils/cmdline.h" + +//***************************************************************************** +// +// Defines the maximum number of arguments that can be parsed. +// +//***************************************************************************** +#ifndef CMDLINE_MAX_ARGS +#define CMDLINE_MAX_ARGS 8 +#endif + +//***************************************************************************** +// +// An array to hold the pointers to the command line arguments. +// +//***************************************************************************** +static char *g_ppcArgv[CMDLINE_MAX_ARGS + 1]; + +//***************************************************************************** +// +//! Process a command line string into arguments and execute the command. +//! +//! \param pcCmdLine points to a string that contains a command line that was +//! obtained by an application by some means. +//! +//! This function will take the supplied command line string and break it up +//! into individual arguments. The first argument is treated as a command and +//! is searched for in the command table. If the command is found, then the +//! command function is called and all of the command line arguments are passed +//! in the normal argc, argv form. +//! +//! The command table is contained in an array named g_psCmdTable +//! containing tCmdLineEntry structures which must be provided by the +//! application. The array must be terminated with an entry whose \b pcCmd +//! field contains a NULL pointer. +//! +//! \return Returns \b CMDLINE_BAD_CMD if the command is not found, +//! \b CMDLINE_TOO_MANY_ARGS if there are more arguments than can be parsed. +//! Otherwise it returns the code that was returned by the command function. +// +//***************************************************************************** +int +CmdLineProcess(char *pcCmdLine) +{ + char *pcChar; + uint_fast8_t ui8Argc; + bool bFindArg = true; + tCmdLineEntry *psCmdEntry; + + // + // Initialize the argument counter, and point to the beginning of the + // command line string. + // + ui8Argc = 0; + pcChar = pcCmdLine; + + // + // Advance through the command line until a zero character is found. + // + while(*pcChar) + { + // + // If there is a space, then replace it with a zero, and set the flag + // to search for the next argument. + // + if(*pcChar == ' ') + { + *pcChar = 0; + bFindArg = true; + } + + // + // Otherwise it is not a space, so it must be a character that is part + // of an argument. + // + else + { + // + // If bFindArg is set, then that means we are looking for the start + // of the next argument. + // + if(bFindArg) + { + // + // As long as the maximum number of arguments has not been + // reached, then save the pointer to the start of this new arg + // in the argv array, and increment the count of args, argc. + // + if(ui8Argc < CMDLINE_MAX_ARGS) + { + g_ppcArgv[ui8Argc] = pcChar; + ui8Argc++; + bFindArg = false; + } + + // + // The maximum number of arguments has been reached so return + // the error. + // + else + { + return(CMDLINE_TOO_MANY_ARGS); + } + } + } + + // + // Advance to the next character in the command line. + // + pcChar++; + } + + // + // If one or more arguments was found, then process the command. + // + if(ui8Argc) + { + // + // Start at the beginning of the command table, to look for a matching + // command. + // + psCmdEntry = &g_psCmdTable[0]; + + // + // Search through the command table until a null command string is + // found, which marks the end of the table. + // + while(psCmdEntry->pcCmd) + { + // + // If this command entry command string matches argv[0], then call + // the function for this command, passing the command line + // arguments. + // + if(!strcmp(g_ppcArgv[0], psCmdEntry->pcCmd)) + { + return(psCmdEntry->pfnCmd(ui8Argc, g_ppcArgv)); + } + + // + // Not found, so advance to the next entry. + // + psCmdEntry++; + } + } + + // + // Fall through to here means that no matching command was found, so return + // an error. + // + return(CMDLINE_BAD_CMD); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/cmdline.h b/utils/cmdline.h new file mode 100644 index 0000000..42ab9ea --- /dev/null +++ b/utils/cmdline.h @@ -0,0 +1,137 @@ +//***************************************************************************** +// +// cmdline.h - Prototypes for command line processing functions. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __CMDLINE_H__ +#define __CMDLINE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup cmdline_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Defines the value that is returned if the command is not found. +// +//***************************************************************************** +#define CMDLINE_BAD_CMD (-1) + +//***************************************************************************** +// +//! Defines the value that is returned if there are too many arguments. +// +//***************************************************************************** +#define CMDLINE_TOO_MANY_ARGS (-2) + +//***************************************************************************** +// +//! Defines the value that is returned if there are too few arguments. +// +//***************************************************************************** +#define CMDLINE_TOO_FEW_ARGS (-3) + +//***************************************************************************** +// +//! Defines the value that is returned if an argument is invalid. +// +//***************************************************************************** +#define CMDLINE_INVALID_ARG (-4) + +//***************************************************************************** +// +// Command line function callback type. +// +//***************************************************************************** +typedef int (*pfnCmdLine)(int argc, char *argv[]); + +//***************************************************************************** +// +//! Structure for an entry in the command list table. +// +//***************************************************************************** +typedef struct +{ + // + //! A pointer to a string containing the name of the command. + // + const char *pcCmd; + + // + //! A function pointer to the implementation of the command. + // + pfnCmdLine pfnCmd; + + // + //! A pointer to a string of brief help text for the command. + // + const char *pcHelp; +} +tCmdLineEntry; + +//***************************************************************************** +// +//! This is the command table that must be provided by the application. The +//! last element of the array must be a structure whose pcCmd field contains +//! a NULL pointer. +// +//***************************************************************************** +extern tCmdLineEntry g_psCmdTable[]; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern int CmdLineProcess(char *pcCmdLine); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __CMDLINE_H__ diff --git a/utils/cpu_usage.c b/utils/cpu_usage.c new file mode 100644 index 0000000..1adf020 --- /dev/null +++ b/utils/cpu_usage.c @@ -0,0 +1,206 @@ +//***************************************************************************** +// +// cpu_usage.c - Routines to determine the CPU utilization. +// +// 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 Utility Library. +// +//***************************************************************************** +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/timer.h" +#include "utils/cpu_usage.h" + +//***************************************************************************** +// +//! \addtogroup cpu_usage_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The peripheral identifier for the timer modules that could be used for +// tracking CPU utilization. +// +//***************************************************************************** +static uint32_t g_pui32CPUUsageTimerPeriph[6] = +{ + SYSCTL_PERIPH_TIMER0, SYSCTL_PERIPH_TIMER1, SYSCTL_PERIPH_TIMER2, + SYSCTL_PERIPH_TIMER3, SYSCTL_PERIPH_TIMER4, SYSCTL_PERIPH_TIMER5 +}; + +//***************************************************************************** +// +// The base address of the timer modules that could be used for tracking CPU +// utilization. +// +//***************************************************************************** +static uint32_t g_pui32CPUUsageTimerBase[6] = +{ + TIMER0_BASE, TIMER1_BASE, TIMER2_BASE, TIMER3_BASE, TIMER4_BASE, + TIMER5_BASE +}; + +//***************************************************************************** +// +// The index of the timer module that will be used for tracking CPU +// utilization. +// +//***************************************************************************** +static uint32_t g_ui32CPUUsageTimer; + +//***************************************************************************** +// +// The number of processor clock ticks per timing period. +// +//***************************************************************************** +static uint32_t g_ui32CPUUsageTicks; + +//***************************************************************************** +// +// The value of timer two on the previous timing period. This is used to +// determine the number of clock ticks counted by the timer during the timing +// period. +// +//***************************************************************************** +static uint32_t g_ui32CPUUsagePrevious; + +//***************************************************************************** +// +//! Updates the CPU usage for the new timing period. +//! +//! This function, when called at the end of a timing period, will update the +//! CPU usage. +//! +//! \return Returns the CPU usage percentage as a 16.16 fixed-point value. +// +//***************************************************************************** +uint32_t +CPUUsageTick(void) +{ + uint32_t ui32Value, ui32Usage; + + // + // Get the current value of the timer. + // + ui32Value = + MAP_TimerValueGet(g_pui32CPUUsageTimerBase[g_ui32CPUUsageTimer], + TIMER_A); + + // + // Based on the number of clock ticks accumulated by the timer during the + // previous timing period, compute the CPU usage as a 16.16 fixed-point + // value. + // + ui32Usage = ((((g_ui32CPUUsagePrevious - ui32Value) * 6400) / + g_ui32CPUUsageTicks) * 1024); + + // + // Save the previous value of the timer. + // + g_ui32CPUUsagePrevious = ui32Value; + + // + // Return the new CPU usage value. + // + return(ui32Usage); +} + +//***************************************************************************** +// +//! Initializes the CPU usage measurement module. +//! +//! \param ui32ClockRate is the rate of the clock supplied to the timer module. +//! \param ui32Rate is the number of times per second that CPUUsageTick() is +//! called. +//! \param ui32Timer is the index of the timer module to use. +//! +//! This function prepares the CPU usage measurement module for measuring the +//! CPU usage of the application. +//! +//! \return None. +// +//***************************************************************************** +void +CPUUsageInit(uint32_t ui32ClockRate, uint32_t ui32Rate, uint32_t ui32Timer) +{ + // + // Check the arguments. + // + ASSERT(ui32ClockRate > ui32Rate); + ASSERT(ui32Timer < 6); + + // + // Save the timer index. + // + g_ui32CPUUsageTimer = ui32Timer; + + // + // Determine the number of system clocks per measurement period. + // + g_ui32CPUUsageTicks = ui32ClockRate / ui32Rate; + + // + // Set the previous value of the timer to the initial timer value. + // + g_ui32CPUUsagePrevious = 0xffffffff; + + // + // Enable peripheral clock gating. + // + MAP_SysCtlPeripheralClockGating(true); + + // + // Enable the third timer while the processor is in run mode, but disable + // it in sleep mode. It will therefore count system clocks when the + // processor is running but not when it is sleeping. + // + MAP_SysCtlPeripheralEnable(g_pui32CPUUsageTimerPeriph[ui32Timer]); + MAP_SysCtlPeripheralSleepDisable(g_pui32CPUUsageTimerPeriph[ui32Timer]); + + // + // Configure the third timer for 32-bit periodic operation. + // + MAP_TimerConfigure(g_pui32CPUUsageTimerBase[ui32Timer], + TIMER_CFG_PERIODIC); + + // + // Set the load value for the third timer to the maximum value. + // + MAP_TimerLoadSet(g_pui32CPUUsageTimerBase[ui32Timer], TIMER_A, 0xffffffff); + + // + // Enable the third timer. It will now count the number of system clocks + // during which the processor is executing code. + // + MAP_TimerEnable(g_pui32CPUUsageTimerBase[ui32Timer], TIMER_A); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/cpu_usage.h b/utils/cpu_usage.h new file mode 100644 index 0000000..181ea2c --- /dev/null +++ b/utils/cpu_usage.h @@ -0,0 +1,57 @@ +//***************************************************************************** +// +// cpu_usage.h - Prototypes for the CPU utilization routines. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __CPU_USAGE_H__ +#define __CPU_USAGE_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 for the CPU utilization routines. +// +//***************************************************************************** +extern uint32_t CPUUsageTick(void); +extern void CPUUsageInit(uint32_t ui32ClockRate, uint32_t ui32Rate, + uint32_t ui32Timer); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __CPU_USAGE_H__ diff --git a/utils/flash_pb.c b/utils/flash_pb.c new file mode 100644 index 0000000..ac85830 --- /dev/null +++ b/utils/flash_pb.c @@ -0,0 +1,493 @@ +//***************************************************************************** +// +// flash_pb.c - Flash parameter block 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_flash.h" +#include "inc/hw_types.h" +#include "inc/hw_sysctl.h" +#include "driverlib/debug.h" +#include "driverlib/flash.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "utils/flash_pb.h" + +//***************************************************************************** +// +//! \addtogroup flash_pb_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The address of the beginning of the flash used for storing parameter blocks; +// this must be the start of an erase block in the flash. +// +//***************************************************************************** +static uint8_t *g_pui8FlashPBStart; + +//***************************************************************************** +// +// The address of the end of the flash used for storing parameter blocks; this +// must be the start of an erase block in the flash, or the first location +// after the end of the flash array if the last erase block is used for storing +// parameters. +// +//***************************************************************************** +static uint8_t *g_pui8FlashPBEnd; + +//***************************************************************************** +// +// The size of the parameter block when stored in flash; this must be a power +// of two less than or equal to the flash erase sector size such that a single +// flash sector contains an integral number of parameter blocks. +// +//***************************************************************************** +static uint32_t g_ui32FlashPBSize; + +//***************************************************************************** +// +// The address of the most recent parameter block in flash. +// +//***************************************************************************** +static uint8_t *g_pui8FlashPBCurrent; + +//***************************************************************************** +// +// The erase sector size of the current flash. +// +//***************************************************************************** +#define FLASH_SECTOR_SIZE MAP_SysCtlFlashSectorSizeGet() + +//***************************************************************************** +// +//! Determines if the parameter block at the given address is valid. +//! +//! \param pui8Offset is the address of the parameter block to check. +//! +//! This function will compute the checksum of a parameter block in flash to +//! determine if it is valid. +//! +//! \return Returns one if the parameter block is valid and zero if it is not. +// +//***************************************************************************** +static uint32_t +FlashPBIsValid(uint8_t *pui8Offset) +{ + uint32_t ui32Idx, ui32Sum; + + // + // Check the arguments. + // + ASSERT(pui8Offset != (void *)0); + + // + // Loop through the bytes in the block, computing the checksum. + // + for(ui32Idx = 0, ui32Sum = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + ui32Sum += pui8Offset[ui32Idx]; + } + + // + // The checksum should be zero, so return a failure if it is not. + // + if((ui32Sum & 255) != 0) + { + return(0); + } + + // + // If the sum is equal to the size * 255, then the block is all ones and + // should not be considered valid. + // + if((g_ui32FlashPBSize * 255) == ui32Sum) + { + return(0); + } + + // + // This is a valid parameter block. + // + return(1); +} + +//***************************************************************************** +// +//! Gets the address of the most recent parameter block. +//! +//! This function returns the address of the most recent parameter block that +//! is stored in flash. +//! +//! \return Returns the address of the most recent parameter block, or NULL if +//! there are no valid parameter blocks in flash. +// +//***************************************************************************** +uint8_t * +FlashPBGet(void) +{ + // + // See if there is a valid parameter block. + // + if(g_pui8FlashPBCurrent) + { + // + // Return the address of the most recent parameter block. + // + return(g_pui8FlashPBCurrent); + } + + // + // There are no valid parameter blocks in flash, so return NULL. + // + return(0); +} + +//***************************************************************************** +// +//! Writes a new parameter block to flash. +//! +//! \param pui8Buffer is the address of the parameter block to be written to +//! flash. +//! +//! This function will write a parameter block to flash. Saving the new +//! parameter blocks involves three steps: +//! +//! - Setting the sequence number such that it is one greater than the sequence +//! number of the latest parameter block in flash. +//! - Computing the checksum of the parameter block. +//! - Writing the parameter block into the storage immediately following the +//! latest parameter block in flash; if that storage is at the start of an +//! erase block, that block is erased first. +//! +//! By this process, there is always a valid parameter block in flash. If +//! power is lost while writing a new parameter block, the checksum will not +//! match and the partially written parameter block will be ignored. This is +//! what makes this fault-tolerant. +//! +//! Another benefit of this scheme is that it provides wear leveling on the +//! flash. Since multiple parameter blocks fit into each erase block of flash, +//! and multiple erase blocks are used for parameter block storage, it takes +//! quite a few parameter block saves before flash is re-written. +//! +//! \return None. +// +//***************************************************************************** +void +FlashPBSave(uint8_t *pui8Buffer) +{ + uint8_t *pui8New; + uint32_t ui32Idx, ui32Sum; + + // + // Check the arguments. + // + ASSERT(pui8Buffer != (void *)0); + + // + // See if there is a valid parameter block in flash. + // + if(g_pui8FlashPBCurrent) + { + // + // Set the sequence number to one greater than the most recent + // parameter block. + // + pui8Buffer[0] = g_pui8FlashPBCurrent[0] + 1; + + // + // Try to write the new parameter block immediately after the most + // recent parameter block. + // + pui8New = g_pui8FlashPBCurrent + g_ui32FlashPBSize; + if(pui8New == g_pui8FlashPBEnd) + { + pui8New = g_pui8FlashPBStart; + } + } + else + { + // + // There is not a valid parameter block in flash, so set the sequence + // number of this parameter block to zero. + // + pui8Buffer[0] = 0; + + // + // Try to write the new parameter block at the beginning of the flash + // space for parameter blocks. + // + pui8New = g_pui8FlashPBStart; + } + + // + // Compute the checksum of the parameter block to be written. + // + for(ui32Idx = 0, ui32Sum = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + ui32Sum -= pui8Buffer[ui32Idx]; + } + + // + // Store the checksum into the parameter block. + // + pui8Buffer[1] += ui32Sum; + + // + // Look for a location to store this parameter block. This infinite loop + // will be explicitly broken out of when a valid location is found. + // + while(1) + { + // + // See if this location is at the start of an erase block. + // + if(((uint32_t)pui8New & (FLASH_SECTOR_SIZE - 1)) == 0) + { + // + // Erase this block of the flash. This does not assume that the + // erase succeeded in case this block of the flash has become bad + // through too much use. Given the extremely low frequency that + // the parameter blocks are written, this will likely never fail. + // But, that assumption is not made in order to be safe. + // + MAP_FlashErase((uint32_t)pui8New); + } + + // + // Loop through this portion of flash to see if is all ones (in other + // words, it is an erased portion of flash). + // + for(ui32Idx = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + if(pui8New[ui32Idx] != 0xff) + { + break; + } + } + + // + // If all bytes in this portion of flash are ones, then break out of + // the loop since this is a good location for storing the parameter + // block. + // + if(ui32Idx == g_ui32FlashPBSize) + { + break; + } + + // + // Increment to the next parameter block location. + // + pui8New += g_ui32FlashPBSize; + if(pui8New == g_pui8FlashPBEnd) + { + pui8New = g_pui8FlashPBStart; + } + + // + // If every possible location has been checked and none are valid, then + // it will not be possible to write this parameter block. Simply + // return without writing it. + // + if((g_pui8FlashPBCurrent && (pui8New == g_pui8FlashPBCurrent)) || + (!g_pui8FlashPBCurrent && (pui8New == g_pui8FlashPBStart))) + { + return; + } + } + + // + // Write this parameter block to flash. + // + MAP_FlashProgram((uint32_t *)pui8Buffer, (uint32_t)pui8New, + g_ui32FlashPBSize); + + // + // Compare the parameter block data to the data that should now be in + // flash. Return if any of the data does not compare, leaving the previous + // parameter block in flash as the most recent (since the current parameter + // block failed to properly program). + // + for(ui32Idx = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + if(pui8New[ui32Idx] != pui8Buffer[ui32Idx]) + { + return; + } + } + + // + // The new parameter block becomes the most recent parameter block. + // + g_pui8FlashPBCurrent = pui8New; +} + +//***************************************************************************** +// +//! Initializes the flash parameter block. +//! +//! \param ui32Start is the address of the flash memory to be used for storing +//! flash parameter blocks; this must be the start of an erase block in the +//! flash. +//! \param ui32End is the address of the end of flash memory to be used for +//! storing flash parameter blocks; this must be the start of an erase block in +//! the flash (the first block that is NOT part of the flash memory to be +//! used), or the address of the first word after the flash array if the last +//! block of flash is to be used. +//! \param ui32Size is the size of the parameter block when stored in flash; +//! this must be a power of two less than or equal to the flash erase block +//! size (typically 1024). +//! +//! This function initializes a fault-tolerant, persistent storage mechanism +//! for a parameter block for an application. The last several erase blocks +//! of flash (as specified by \e ui32Start and \e ui32End are used for the +//! storage; more than one erase block is required in order to be +//! fault-tolerant. +//! +//! A parameter block is an array of bytes that contain the persistent +//! parameters for the application. The only special requirement for the +//! parameter block is that the first byte is a sequence number (explained +//! in FlashPBSave()) and the second byte is a checksum used to validate the +//! correctness of the data (the checksum byte is the byte such that the sum of +//! all bytes in the parameter block is zero). +//! +//! The portion of flash for parameter block storage is split into N +//! equal-sized regions, where each region is the size of a parameter block +//! (\e ui32Size). Each region is scanned to find the most recent valid +//! parameter block. The region that has a valid checksum and has the highest +//! sequence number (with special consideration given to wrapping back to zero) +//! is considered to be the current parameter block. +//! +//! In order to make this efficient and effective, three conditions must be +//! met. The first is \e ui32Start and \e ui32End must be specified such that +//! at least two erase blocks of flash are dedicated to parameter block +//! storage. If not, fault tolerance can not be guaranteed since an erase of a +//! single block will leave a window where there are no valid parameter blocks +//! in flash. The second condition is that the size (\e ui32Size) of the +//! parameter block must be an integral divisor of the size of an erase block +//! of flash. If not, a parameter block will end up spanning between two erase +//! blocks of flash, making it more difficult to manage. The final condition +//! is that the size of the flash dedicated to parameter blocks (\e ui32End - +//! \e ui32Start) divided by the parameter block size (\e ui32Size) must be +//! less than or equal to 128. If not, it will not be possible in all cases to +//! determine which parameter block is the most recent (specifically when +//! dealing with the sequence number wrapping back to zero). +//! +//! When the microcontroller is initially programmed, the flash blocks used for +//! parameter block storage are left in an erased state. +//! +//! This function must be called before any other flash parameter block +//! functions are called. +//! +//! \return None. +// +//***************************************************************************** +void +FlashPBInit(uint32_t ui32Start, uint32_t ui32End, uint32_t ui32Size) +{ + uint8_t *pui8Offset, *pui8Current; + uint8_t ui8One, ui8Two; + + // + // Check the arguments. + // + ASSERT((ui32Start % FLASH_SECTOR_SIZE) == 0); + ASSERT((ui32End % FLASH_SECTOR_SIZE) == 0); + ASSERT((FLASH_SECTOR_SIZE % ui32Size) == 0); + + // + // Save the characteristics of the flash memory to be used for storing + // parameter blocks. + // + g_pui8FlashPBStart = (uint8_t *)ui32Start; + g_pui8FlashPBEnd = (uint8_t *)ui32End; + g_ui32FlashPBSize = ui32Size; + + // + // Loop through the portion of flash memory used for storing parameter + // blocks. + // + for(pui8Offset = g_pui8FlashPBStart, pui8Current = 0; + pui8Offset < g_pui8FlashPBEnd; pui8Offset += g_ui32FlashPBSize) + { + // + // See if this is a valid parameter block (in other words, the checksum + // is correct). + // + if(FlashPBIsValid(pui8Offset)) + { + // + // See if a valid parameter block has been previously found. + // + if(pui8Current != 0) + { + // + // Get the sequence numbers for the current and new parameter + // blocks. + // + ui8One = pui8Current[0]; + ui8Two = pui8Offset[0]; + + // + // See if the sequence number for the new parameter block is + // greater than the current block. The comparison isn't + // straightforward since the one byte sequence number will wrap + // after 256 parameter blocks. + // + if(((ui8One > ui8Two) && ((ui8One - ui8Two) < 128)) || + ((ui8Two > ui8One) && ((ui8Two - ui8One) > 128))) + { + // + // The new parameter block is older than the current + // parameter block, so skip the new parameter block and + // keep searching. + // + continue; + } + } + + // + // The new parameter block is more recent than the current one, so + // make it the new current parameter block. + // + pui8Current = pui8Offset; + } + } + + // + // Save the address of the most recent parameter block found. If no valid + // parameter blocks were found, this will be a NULL pointer. + // + g_pui8FlashPBCurrent = pui8Current; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/flash_pb.h b/utils/flash_pb.h new file mode 100644 index 0000000..f68329d --- /dev/null +++ b/utils/flash_pb.h @@ -0,0 +1,58 @@ +//***************************************************************************** +// +// flash_pb.h - Prototypes for the flash parameter block 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 Utility Library. +// +//***************************************************************************** + +#ifndef __FLASH_PB_H__ +#define __FLASH_PB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Prototype for the flash parameter block functions. +// +//***************************************************************************** +extern uint8_t *FlashPBGet(void); +extern void FlashPBSave(uint8_t *pui8Buffer); +extern void FlashPBInit(uint32_t ui32Start, uint32_t ui32End, + uint32_t ui32Size); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __FLASH_PB_H__ diff --git a/utils/fswrapper.c b/utils/fswrapper.c new file mode 100644 index 0000000..74e80af --- /dev/null +++ b/utils/fswrapper.c @@ -0,0 +1,860 @@ +//***************************************************************************** +// +// fswrapper.c - File System Processing for lwIP Web Server Apps. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "httpserver_raw/fs.h" +#include "httpserver_raw/fsdata.h" +#include "fatfs/src/ff.h" +#include "fatfs/src/diskio.h" +#include "utils/fswrapper.h" +#include "utils/lwiplib.h" +#include "utils/ustdlib.h" + +//***************************************************************************** +// +//! \addtogroup fswrapper_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Static file system images for use with this module may be created using +// the makefsfile.exe utility. Both position-independent (built using the -b +// command line option to makefsfile) and position dependent file system images +// may be used. +// +//***************************************************************************** +typedef struct +{ + // + // The index of the file system containing this file. + // + uint32_t ui32MountIndex; + + // + // The FatFs file structure allocated if the target file is in the FAT + // file system. + // + FIL *psFATFile; +} +fs_wrapper_data; + +//***************************************************************************** +// +// A marker used to indicate that a passed filename cannot be mapped to any of +// the configured mount points. +// +//***************************************************************************** +#define BAD_MOUNT_INDEX 0xFFFFFFFF + +//***************************************************************************** +// +// This macro is used to extract pointers from the file descriptors. We +// support files systems linked into the image as well as external, position +// independent file system images and this macro allows us to use the same code +// to extract pointers from file descriptors in each case. +// +//***************************************************************************** +#define FS_POINTER(ptTree, ptValue, bPosInd) \ + ((char *)((bPosInd) ? ((int8_t *)(ptTree) + (uint32_t)(ptValue)) : \ + (int8_t *)(ptValue))) + +//***************************************************************************** +// +// The pointer to the mount point table and the number of entries in the +// table. +// +//***************************************************************************** +static fs_mount_data *g_psMountPoints = NULL; +static uint32_t g_ui32NumMountPoints = 0; +static uint32_t g_ui32DefaultMountIndex = BAD_MOUNT_INDEX; +static bool g_bFatFsEnabled = false; + +//***************************************************************************** +// +// Given a filename, this function determine which of the configured mount +// points it resides under. It returns the index of the mount point in the +// g_psMountPoints array and also a pointer to the first character of the +// filename with the mount point name (directory) stripped from it. +// +//***************************************************************************** +static uint32_t +fs_find_mount_index(const char *pcName, char **ppcFSFilename) +{ + uint32_t ui32Loop; + int iLenDirName; + int iLenMountName; + char *pcSlash; + + // + // First extract the top level directory name which we need to match + // with the mount point name. For this to exist, the pcName string + // must start with a '/' character and must contain at least one more + // '/'. + // + if(pcName[0] == '/') + { + // + // The string starts with a '/'. Does it contain a second one? + // + pcSlash = strchr(pcName + 1, '/'); + + // + // Did we find another forward slash character? + // + if(pcSlash) + { + // + // Yes - the mount point name is between the start of the + // string and the slash we just found. How long is this string? + // + iLenDirName = (int)(pcSlash - (pcName + 1)); + } + else + { + // + // The mount point name is the whole string. + // + iLenDirName = ustrlen(pcName + 1); + pcSlash = (char *)pcName + 1 + iLenDirName; + } + + // + // Now figure out which, if any, of the mount points this matches. + // + for(ui32Loop = 0; ui32Loop < g_ui32NumMountPoints; ui32Loop++) + { + // + // Skip the default mount point if found. + // + if(!g_psMountPoints[ui32Loop].pcNamePrefix) + { + continue; + } + + // + // How long is the name of this mount point? + // + iLenMountName = ustrlen(g_psMountPoints[ui32Loop].pcNamePrefix); + + // + // Does the mount point name match the directory name extracted + // from the passed pcName? + // + if(iLenMountName == iLenDirName) + { + // + // The lengths match but are the strings the same? + // + if(!ustrncmp(g_psMountPoints[ui32Loop].pcNamePrefix, + pcName + 1, iLenDirName)) + { + // + // Yes - we have a match. Set the stripped filename to + // the second '/' and return the mount point index. + // + *ppcFSFilename = pcSlash; + return(ui32Loop); + } + } + } + } + + // + // If we drop out of the loop, we didn't find a specific mount point for + // this file so just return the filename passed and the default mount + // point. + // + *ppcFSFilename = (char *)pcName; + + return(g_ui32DefaultMountIndex); +} + +//***************************************************************************** +// +//! Initializes the file system wrapper. +//! +//! \param psMountPoints points to an array of fs_mount_data structures. Each +//! element in the array maps a top level directory name to a particular +//! file system image or to the FAT file system and a logical drive number. +//! \param ui32NumMountPoints provides the number of populated elements in the +//! \e psMountPoints array. +//! +//! This function should be called to initialize the file system wrapper and +//! provide it with the information required to access the files in multiple +//! file system images via a single filename space. +//! +//! Each entry in \e psMountPoints describes a top level directory in the +//! unified namespace and indicates to fswrapper where the files for that +//! directory can be found. Each entry can describe either a file system +//! image in system memory or a logical disk handled via the FatFs file system +//! driver. +//! +//! For example, consider the following 3 entry mount point table: +//! +//! \verbatim +//! { +//! { "internal", &g_pui8FSImage, 0, NULL, NULL }, +//! { "sdcard", NULL, 0, SDCardEnable, SDCardDisable }, +//! { NULL, &g_pui8FSDefault, 0, NULL, NULL} +//! } +//! \endverbatim +//! +//! Requests to open file ``/internal/index.html'' will be handled by +//! attempting to open ``/index.html'' in the internal file system pointed to +//! by \e g_pui8FSImage. Similarly, opening ``/sdcard/images/logo.gif'' will +//! result in a call to the FAT f_open function requesting +//! ``0:/images/logo.gif''. If a request to open ``index.htm'' is received, +//! this is handled by attempting to open ``index.htm'' in the default internal +//! file system image, \e g_pui8FSDefault. +//! +//! \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +bool +fs_init(fs_mount_data *psMountPoints, uint32_t ui32NumMountPoints) +{ + uint32_t ui32Loop; + + // + // Check for non-zero parameters in debug builds. + // + ASSERT(psMountPoints); + ASSERT(ui32NumMountPoints); + + // + // Remember the mount point information we have been given. + // + if(psMountPoints && ui32NumMountPoints) + { + // + // Remember the information passed. + // + g_psMountPoints = psMountPoints; + g_ui32NumMountPoints = ui32NumMountPoints; + + // + // Check to determine if any of the mount points refer to FAT file + // system drivers. We also hijack this loop to determine what the + // default mount point (if any) is. + // + g_bFatFsEnabled = false; + for(ui32Loop = 0; ui32Loop < g_ui32NumMountPoints; ui32Loop++) + { + // + // If the pui8FSImage field of a mount point structure is NULL, + // this implies that we are using the FAT file system for that + // node. + // + if(!g_psMountPoints[ui32Loop].pui8FSImage) + { + g_bFatFsEnabled = true; + } + + // + // Does this entry describe the default mount point? + // + if(g_psMountPoints[ui32Loop].pcNamePrefix == NULL) + { + g_ui32DefaultMountIndex = ui32Loop; + } + } + + return(true); + } + else + { + // + // Return an error due to being passed a bad parameter. + // + return(false); + } +} + +//***************************************************************************** +// +//! Provides a periodic tick for the file system. +//! +//! \param ui32TickMS is the number of milliseconds which have elapsed since +//! the last time this function was called. +//! +//! Applications making use of the file system wrapper with underlying FatFs +//! drives must call this function at least once every 10 milliseconds to +//! provide a time reference for use by the file system. It is typically +//! called in the context of the application's SysTick interrupt handler or +//! from the handler of some other timer interrupt. +//! +//! If only binary file system images are in use, this function need not be +//! called. +//! +//! \return None +// +// +//***************************************************************************** +void +fs_tick(uint32_t ui32TickMS) +{ + static uint32_t ui32TickCounter = 0; + + // + // Check if the file system has been enabled yet. + // + if(!g_bFatFsEnabled) + { + return; + } + + // + // Increment the tick counter. + // + ui32TickCounter += ui32TickMS; + + // + // Check to see if the FAT FS tick needs to run. + // + if(ui32TickCounter >= 10) + { + ui32TickCounter = 0; + disk_timerproc(); + } +} + +//***************************************************************************** +// +//! Opens a file. +//! +//! \param pcName points to a NULL terminated string containing the path and +//! file name to open. +//! +//! This function opens a file and returns a handle allowing it to be read. +//! +//! \return Returns a valid file handle on success or NULL on failure. +// +//***************************************************************************** +struct fs_file * +fs_open(const char *pcName) +{ + const struct fsdata_file *psTree; + const struct fsdata_file *psEnd = NULL; + struct fs_file *psFile = NULL; + fs_wrapper_data *psWrapper; + FRESULT fresult = FR_OK; + bool bPosInd = false; + char *pcFSFilename; + char *pcFilename; + uint32_t ui32Length; + + // + // Allocate memory for the file system structure. + // + psFile = mem_malloc(sizeof(struct fs_file)); + if(NULL == psFile) + { + return(NULL); + } + + // + // Allocate memory for our internal control structure. + // + psFile->pextension = mem_malloc(sizeof(fs_wrapper_data)); + psWrapper = (fs_wrapper_data *)psFile->pextension; + + if(NULL == psWrapper) + { + return(NULL); + } + + // + // Find which mount point we need to use to satisfy this file open request. + // + psWrapper->ui32MountIndex = fs_find_mount_index(pcName, &pcFSFilename); + if(psWrapper->ui32MountIndex == BAD_MOUNT_INDEX) + { + // + // We can't map the mount index so return an error. + // + mem_free(psWrapper); + mem_free(psFile); + return(NULL); + } + + // + // Enable access to the physical medium if we have been provided with + // a callback for this. + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pfnEnable) + { + g_psMountPoints[psWrapper->ui32MountIndex]. + pfnEnable(psWrapper->ui32MountIndex); + } + + // + // Are we opening a file on an internal file system image? + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pui8FSImage) + { + // + // Initialize the file system tree pointer to the root of the linked + // list for this mount point's file system image. + // + psTree = ((const struct fsdata_file *) + g_psMountPoints[psWrapper->ui32MountIndex].pui8FSImage); + + // + // Which type of file system are we dealing with? + // + if(psTree->next == FILE_SYSTEM_MARKER) + { + // + // If we found the marker, this is a position independent file + // system image. Remember this and fix up the pointer to the + // first descriptor by skipping over the 4 byte marker and the + // 4 byte image size entry. We also keep track of where the file + // system image ends since this allows us to do a bit more error + // checking later. + // + bPosInd = true; + ui32Length = *(uint32_t *)((uint8_t *)psTree + 4); + psTree = (struct fsdata_file *)((int8_t *)psTree + 8); + psEnd = (struct fsdata_file *)((int8_t *)psTree + ui32Length); + } + + // + // Begin processing the linked list, looking for the requested file + // name. + // + while(NULL != psTree) + { + // + // Compare the requested file "name" to the file name in the + // current node. + // + if(ustrncmp(pcFSFilename, + FS_POINTER(psTree, psTree->name, bPosInd), + psTree->len) == 0) + { + // + // Fill in the data pointer and length values from the + // linked list node. + // + psFile->data = FS_POINTER(psTree, psTree->data, bPosInd); + psFile->len = psTree->len; + + // + // For now, we setup the read index to the end of the file, + // indicating that all data has been read. This indicates that + // all the data is currently available in a contiguous block + // of memory (which is always the case with an internal file + // system image). + // + psFile->index = psTree->len; + + // + // We are not using a FAT file system file and don't need to + // remap the filename so set these pointers to NULL. + // + psWrapper->psFATFile = NULL; + + // + // Exit the loop and return the file system pointer. + // + break; + } + + // + // If we get here, we did not find the file at this node of the + // linked list. Get the next element in the list. We can't just + // assign psTree from psTree->next since this will give us the + // wrong pointer for a position independent image (where the values + // in the structure are offsets from the start of the file + // descriptor, not absolute pointers) but we do know that a 0 in + // the "next" field does indicate that this is the last file so we + // can use that info to force the loop to exit at the end. + // + if(psTree->next == 0) + { + psTree = NULL; + } + else + { + psTree = (struct fsdata_file *)FS_POINTER(psTree, psTree->next, + bPosInd); + + // + // If this is a position independent file system image, we can + // also check that the new node is within the image. If it + // isn't, the image is corrupted to stop the search. + // + if(bPosInd && (psTree >= psEnd)) + { + psTree = NULL; + } + } + } + + // + // If we didn't find the file, ptTee will be NULL. Make sure we + // return a NULL pointer if this happens. + // + if(NULL == psTree) + { + mem_free(psFile->pextension); + mem_free(psFile); + psFile = NULL; + } + } + else + { + // + // This file is on the FAT file system. + // + + // + // Allocate memory for the Fat File system handle. + // + psWrapper->psFATFile = mem_malloc(sizeof(FIL)); + if(NULL == psWrapper->psFATFile) + { + mem_free(psFile->pextension); + mem_free(psFile); + psFile = NULL; + } + else + { + // + // Reformat the filename to start with the FAT logical drive + // number. + // + ui32Length = ustrlen(pcFSFilename) + 16; + pcFilename = mem_malloc(ui32Length); + if(!pcFilename) + { + // + // Can't allocate temporary storage for the reformatted + // filename! + // + mem_free(psWrapper->psFATFile); + mem_free(psFile->pextension); + mem_free(psFile); + psFile = NULL; + } + else + { + usnprintf(pcFilename, ui32Length, "%d:%s", + g_psMountPoints[psWrapper->ui32MountIndex]. + ui32DriveNum, pcFSFilename); + // + // Attempt to open the file on the Fat File System. + // + fresult = f_open(psWrapper->psFATFile, pcFilename, FA_READ); + + // + // Free the filename storage + // + mem_free(pcFilename); + + // + // Did we open the file correctly? + // + if(FR_OK == fresult) + { + // + // Yes - fill in the file structure to indicate that a + // FAT file is in use. + // + psFile->data = NULL; + psFile->len = 0; + psFile->index = 0; + } + else + { + // + // If we get here, we failed to find the file on the FAT + // file system so free up the FAT handle/object. + // + mem_free(psWrapper->psFATFile); + mem_free(psWrapper); + mem_free(psFile); + psFile = NULL; + } + } + } + } + + // + // Disable access to the physical medium if we have been provided with + // a callback for this. + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pfnDisable) + { + g_psMountPoints[psWrapper->ui32MountIndex]. + pfnDisable(psWrapper->ui32MountIndex); + } + + return(psFile); +} + +//***************************************************************************** +// +//! Closes a file. +//! +//! \param phFile is the handle of the file that is to be closed. This will +//! have been returned by an earlier call to fs_open(). +//! +//! This function closes the file identified by \e phFile and frees all +//! resources associated with the file handle. +//! +//! \return None. +// +//***************************************************************************** +void +fs_close(struct fs_file *phFile) +{ + fs_wrapper_data *psWrapper; + + psWrapper = (fs_wrapper_data *)phFile->pextension; + + // + // If a Fat file was opened, free its object. + // + if(psWrapper->psFATFile) + { + // + // Close the file. + // + f_close(psWrapper->psFATFile); + + // + // Free the file object. + // + mem_free(psWrapper->psFATFile); + } + + // + // Free our file wrapper control structure. + // + mem_free(phFile->pextension); + + // + // Free the main file system object. + // + mem_free(phFile); +} + +//***************************************************************************** +// +//! Reads data from an open file. +//! +//! \param phFile is the handle of the file which is to be read. This will +//! have been returned by a previous call to fs_open(). +//! \param pcBuffer points to the first byte of the buffer into which the +//! data read from the file will be copied. This buffer must be large enough +//! to hold \e iCount bytes. +//! \param iCount is the maximum number of bytes of data that are to be read +//! from the file. +//! +//! This function reads the next block of data from the given file into a +//! buffer and returns the number of bytes read or -1 if the end of the file +//! has been reached. +//! +//! \return Returns the number of bytes read from the file or -1 if the end of +//! the file has been reached and no more data is available. +// +//***************************************************************************** +int +fs_read(struct fs_file *phFile, char *pcBuffer, int iCount) +{ + int iAvailable, iRetcode; + fs_wrapper_data *psWrapper; + + psWrapper = (fs_wrapper_data *)phFile->pextension; + + // + // Call the application's enable function for this physical medium (if + // an enable function has been provided). + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pfnEnable) + { + g_psMountPoints[psWrapper->ui32MountIndex]. + pfnEnable(psWrapper->ui32MountIndex); + } + + // + // Check to see if a Fat File was opened and process it. + // + if(psWrapper->psFATFile) + { + uint32_t ui32BytesRead; + FRESULT fresult; + + // + // Read the data. + // + fresult = f_read(psWrapper->psFATFile, pcBuffer, iCount, + (UINT*)&ui32BytesRead); + if((fresult != FR_OK) || (ui32BytesRead == 0)) + { + iRetcode = -1; + } + else + { + iRetcode = (int)ui32BytesRead; + } + } + else + { + // + // We are reading a file from a file system image. Check to see if + // more data is available. + // + if(phFile->len == phFile->index) + { + // + // There is no remaining data. Return a -1 for EOF indication. + // + return(-1); + } + + // + // Determine how much data we can copy. The minimum of the 'iCount' + // parameter or the available data in the file system buffer. + // + iAvailable = phFile->len - phFile->index; + if(iAvailable > iCount) + { + iAvailable = iCount; + } + + // + // Copy the data. + // + memcpy(pcBuffer, phFile->data + phFile->index, iAvailable); + phFile->index += iAvailable; + + // + // Return the count of data that we copied. + // + iRetcode = iAvailable; + } + + // + // Call the application's disable function now that we have finished + // accessing the file. + // + if(g_psMountPoints[psWrapper->ui32MountIndex].pfnDisable) + { + g_psMountPoints[psWrapper->ui32MountIndex]. + pfnDisable(psWrapper->ui32MountIndex); + } + + // + // Return the number of bytes read. + // + return(iRetcode); +} + +//***************************************************************************** +// +//! Maps a path string containing mount point names to a path suitable for +//! use in calls to the FatFs APIs. +//! +//! \param pcPath points to a string containing a path in the namespace +//! defined by the mount information passed to fs_init(). +//! \param pcMapped points to a buffer into which the mapped path string will +//! be written. +//! \param iLen is the size, in bytes, of the buffer pointed to by pcMapped. +//! +//! This function may be used by applications which want to make use of FatFs +//! functions which are not directly mapped by the fswrapper layer. A path +//! in the namespace defined by the mount points passed to function fs_init() +//! is translated to an equivalent path in the FatFs namespace and this may +//! then be used in a direct call to functions such as f_opendir() or +//! f_getfree(). +//! +//! \return Returns \b true on success or \b false if fs_init() has not +//! been called, if the path provided maps to an internal file system image +//! rather than a FatFs logical drive or if the buffer pointed to by +//! \e pcMapped is too small to fit the output string. +// +//***************************************************************************** +bool +fs_map_path(const char *pcPath, char *pcMapped, int iLen) +{ + char *pcFSFilename; + uint32_t ui32MountIndex; + int iCount; + + // + // If no mount points have been defined, return an error. + // + if(!g_psMountPoints) + { + return(false); + } + + // + // Find which mount point we need to use to satisfy this file open request. + // + ui32MountIndex = fs_find_mount_index(pcPath, &pcFSFilename); + + // + // If we got a bad mount index or the index returned represents a mount + // point that is not in the FAT file system, return an error. + // + if((ui32MountIndex == BAD_MOUNT_INDEX) || + (g_psMountPoints[ui32MountIndex].pui8FSImage)) + { + // + // We can't map the mount index so return an error. + // + return(false); + } + + // + // Now we can generate the FatFs namespace path string. + // + iCount = usnprintf(pcMapped, iLen, "%d:%s", + g_psMountPoints[ui32MountIndex].ui32DriveNum, + pcFSFilename); + + // + // Tell the user how we got on. The count returned by usnprintf is the + // number of characters that should have been written, excluding the + // terminating NULL so we use this to check for overflow of the output + // buffer. + // + return((iLen >= (iCount + 1)) ? true : false); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/fswrapper.h b/utils/fswrapper.h new file mode 100644 index 0000000..889cdd4 --- /dev/null +++ b/utils/fswrapper.h @@ -0,0 +1,142 @@ +//***************************************************************************** +// +// fswrapper.h - Public type definitons and function prototypes for the simple +// file system wrapper module. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __FSWRAPPER_H__ +#define __FSWRAPPER_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup fswrapper_api +//! @{ +// +//***************************************************************************** + +typedef struct +{ + // + //! This string provides a pseudo-directory name that will be used to + //! identify this mount point in future calls to fs_open. If this string + //! is NULL, this indicates that this is the default file system which will + //! be used when any simple filename, not including a leading directory + //! name is passed or when the leading directory name is not found in the + //! list of mount points passed to fs_init. + // + const char *pcNamePrefix; + + // + //! A pointer to the start of the file system image that is to be used to + //! satisfy requests for files whose name begins "/name" where + //! "name" is the string provided in the pcNamePrefix field. This pointer + //! may point to either a position-dependent or position-independent file + //! system image generated by the makefsfile executable or makefsdata + //! Perl script. If NULL, it is assumed that the FAT file system is to be + //! used and that the drive number provided in ui32DriveNum should be + //! substituted for "name" in the supplied filename before attempting to + //! open the FAT file. + // + uint8_t *pui8FSImage; + + // + //! If this mount point describes a logical drive in the FAT file system, + //! this field indicates the drive number that is to be accessed. This + //! number will be substituted for the string provided in the pcNamePrefix + //! field in the filename passed to fs_open before that filename is passed + //! down to the FAT file system. For example, if pcNamePrefix is "sdcard" + //! and ui32DriveNum is 0, a call to fs_open passing + //! "/sdcard/images/logo.gif" will be passed to the FATfs f_open call as + //! "/0/images/logo.gif". This field is ignored if pui8FSImage is not NULL + //! (indicating that this mount point refers to a file system image rather + //! than the FAT file system). + //! + uint32_t ui32DriveNum; + + // + //! This function pointer is called whenever a file is to be opened, read + //! or (for read/write file systems) written on this file system. If any + //! special setup is required to allow access to the physical medium (for + //! example, setting the SSI mode or clock frequency), the application may + //! use this callback to perform that initialization. If this field is + //! NULL, no callback will be made. + // + void (*pfnEnable)(uint32_t ui32FSIndex); + + // + //! This callback is made after access to the physical medium has been + //! completed. An application may assume that no further access to the + //! medium will be made until a call to pfnEnable is made. If this field + //! is NULL, no callback will be made. + // + void (*pfnDisable)(uint32_t ui32FSIndex); +} +fs_mount_data; + +//***************************************************************************** +// +// This marker, "FIMG", is placed at the beginning of a position-independent +// file system image to differentiate it from a position-dependent image. +// +//***************************************************************************** +#define FILE_SYSTEM_MARKER ((const struct fsdata_file *)0x474D4946) + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Public function prototypes +// +//***************************************************************************** +extern bool fs_init(fs_mount_data *psMountPoints, uint32_t ui32NumMountPoints); +extern void fs_tick(uint32_t ui32TickMS); +extern struct fs_file *fs_open(const char *name); +extern void fs_close(struct fs_file *file); +extern int fs_read(struct fs_file *file, char *buffer, int count); +extern bool fs_map_path(const char *pcPath, char *pcMapped, int iLen); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __FSWRAPPER_H__ diff --git a/utils/isqrt.c b/utils/isqrt.c new file mode 100644 index 0000000..aa908f6 --- /dev/null +++ b/utils/isqrt.c @@ -0,0 +1,118 @@ +//***************************************************************************** +// +// isqrt.c - Integer square root. +// +// Copyright (c) 2005-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 Utility Library. +// +//***************************************************************************** + +#include +#include "utils/isqrt.h" + +//***************************************************************************** +// +//! \addtogroup isqrt_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Compute the integer square root of an integer. +//! +//! \param ui32Value is the value whose square root is desired. +//! +//! This function will compute the integer square root of the given input +//! value. Since the value returned is also an integer, it is actually better +//! defined as the largest integer whose square is less than or equal to the +//! input value. +//! +//! \return Returns the square root of the input value. +// +//***************************************************************************** +uint32_t +isqrt(uint32_t ui32Value) +{ + uint32_t ui32Rem, ui32Root, ui32Idx; + + // + // Initialize the remainder and root to zero. + // + ui32Rem = 0; + ui32Root = 0; + + // + // Loop over the sixteen bits in the root. + // + for(ui32Idx = 0; ui32Idx < 16; ui32Idx++) + { + // + // Shift the root up by a bit to make room for the new bit that is + // about to be computed. + // + ui32Root <<= 1; + + // + // Get two more bits from the input into the remainder. + // + ui32Rem = ((ui32Rem << 2) + (ui32Value >> 30)); + ui32Value <<= 2; + + // + // Make the test root be 2n + 1. + // + ui32Root++; + + // + // See if the root is greater than the remainder. + // + if(ui32Root <= ui32Rem) + { + // + // Subtract the test root from the remainder. + // + ui32Rem -= ui32Root; + + // + // Increment the root, setting the second LSB. + // + ui32Root++; + } + else + { + // + // The root is greater than the remainder, so the new bit of the + // root is actually zero. + // + ui32Root--; + } + } + + // + // Return the computed root. + // + return(ui32Root >> 1); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/isqrt.h b/utils/isqrt.h new file mode 100644 index 0000000..2d0de39 --- /dev/null +++ b/utils/isqrt.h @@ -0,0 +1,55 @@ +//***************************************************************************** +// +// isqrt.h - Prototype for the integer square root function. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __ISQRT_H__ +#define __ISQRT_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 prototype for the integer square root function. +// +//***************************************************************************** +extern uint32_t isqrt(uint32_t ui32Value); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/utils/locator.c b/utils/locator.c new file mode 100644 index 0000000..fb240b2 --- /dev/null +++ b/utils/locator.c @@ -0,0 +1,342 @@ +//***************************************************************************** +// +// locator.c - A device locator server using UDP in lwIP. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include "utils/locator.h" +#include "utils/lwiplib.h" + +//***************************************************************************** +// +//! \addtogroup locator_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These defines are used to describe the device locator protocol. +// +//***************************************************************************** +#define TAG_CMD 0xff +#define TAG_STATUS 0xfe +#define CMD_DISCOVER_TARGET 0x02 + +//***************************************************************************** +// +// An array that contains the device locator response data. The format of the +// data is as follows: +// +// Byte Description +// -------- ------------------------ +// 0 TAG_STATUS +// 1 packet length +// 2 CMD_DISCOVER_TARGET +// 3 board type +// 4 board ID +// 5..8 client IP address +// 9..14 MAC address +// 15..18 firmware version +// 19..82 application title +// 83 checksum +// +//***************************************************************************** +static uint8_t g_pui8LocatorData[84]; + +//***************************************************************************** +// +// This function is called by the lwIP TCP/IP stack when it receives a UDP +// packet from the discovery port. It produces the response packet, which is +// sent back to the querying client. +// +//***************************************************************************** +static void +LocatorReceive(void *arg, struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + uint8_t *pui8Data; + uint32_t ui32Idx; + + // + // Validate the contents of the datagram. + // + pui8Data = p->payload; + if((p->len != 4) || (pui8Data[0] != TAG_CMD) || (pui8Data[1] != 4) || + (pui8Data[2] != CMD_DISCOVER_TARGET) || + (pui8Data[3] != ((0 - TAG_CMD - 4 - CMD_DISCOVER_TARGET) & 0xff))) + { + pbuf_free(p); + return; + } + + // + // The incoming pbuf is no longer needed, so free it. + // + pbuf_free(p); + + // + // Allocate a new pbuf for sending the response. + // + p = pbuf_alloc(PBUF_TRANSPORT, sizeof(g_pui8LocatorData), PBUF_RAM); + if(p == NULL) + { + return; + } + + // + // Calculate and fill in the checksum on the response packet. + // + for(ui32Idx = 0, g_pui8LocatorData[sizeof(g_pui8LocatorData) - 1] = 0; + ui32Idx < (sizeof(g_pui8LocatorData) - 1); ui32Idx++) + { + g_pui8LocatorData[sizeof(g_pui8LocatorData) - 1] -= + g_pui8LocatorData[ui32Idx]; + } + + // + // Copy the response packet data into the pbuf. + // + pui8Data = p->payload; + for(ui32Idx = 0; ui32Idx < sizeof(g_pui8LocatorData); ui32Idx++) + { + pui8Data[ui32Idx] = g_pui8LocatorData[ui32Idx]; + } + + // + // Send the response. + // + udp_sendto(pcb, p, addr, port); + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +//! Initializes the locator service. +//! +//! This function prepares the locator service to handle device discovery +//! requests. A UDP server is created and the locator response data is +//! initialized to all empty. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorInit(void) +{ + uint32_t ui32Idx; + void *pcb; + + // + // Clear out the response data. + // + for(ui32Idx = 0; ui32Idx < 84; ui32Idx++) + { + g_pui8LocatorData[ui32Idx] = 0; + } + + // + // Fill in the header for the response data. + // + g_pui8LocatorData[0] = TAG_STATUS; + g_pui8LocatorData[1] = sizeof(g_pui8LocatorData); + g_pui8LocatorData[2] = CMD_DISCOVER_TARGET; + + // + // Fill in the MAC address for the response data. + // + g_pui8LocatorData[9] = 0; + g_pui8LocatorData[10] = 0; + g_pui8LocatorData[11] = 0; + g_pui8LocatorData[12] = 0; + g_pui8LocatorData[13] = 0; + g_pui8LocatorData[14] = 0; + + // + // Create a new UDP port for listening to device locator requests. + // + pcb = udp_new(); + udp_recv(pcb, LocatorReceive, NULL); + udp_bind(pcb, IP_ADDR_ANY, 23); +} + +//***************************************************************************** +// +//! Sets the board type in the locator response packet. +//! +//! \param ui32Type is the type of the board. +//! +//! This function sets the board type field in the locator response packet. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorBoardTypeSet(uint32_t ui32Type) +{ + // + // Save the board type in the response data. + // + g_pui8LocatorData[3] = ui32Type & 0xff; +} + +//***************************************************************************** +// +//! Sets the board ID in the locator response packet. +//! +//! \param ui32ID is the ID of the board. +//! +//! This function sets the board ID field in the locator response packet. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorBoardIDSet(uint32_t ui32ID) +{ + // + // Save the board ID in the response data. + // + g_pui8LocatorData[4] = ui32ID & 0xff; +} + +//***************************************************************************** +// +//! Sets the client IP address in the locator response packet. +//! +//! \param ui32IP is the IP address of the currently connected client. +//! +//! This function sets the IP address of the currently connected client in the +//! locator response packet. The IP should be set to 0.0.0.0 if there is no +//! client connected. It should never be set for devices that do not have a +//! strict one-to-one mapping of client to server (for example, a web server). +//! +//! \return None. +// +//***************************************************************************** +void +LocatorClientIPSet(uint32_t ui32IP) +{ + // + // Save the client IP address in the response data. + // + g_pui8LocatorData[5] = ui32IP & 0xff; + g_pui8LocatorData[6] = (ui32IP >> 8) & 0xff; + g_pui8LocatorData[7] = (ui32IP >> 16) & 0xff; + g_pui8LocatorData[8] = (ui32IP >> 24) & 0xff; +} + +//***************************************************************************** +// +//! Sets the MAC address in the locator response packet. +//! +//! \param pui8MACArray is the MAC address of the network interface. +//! +//! This function sets the MAC address of the network interface in the locator +//! response packet. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorMACAddrSet(uint8_t *pui8MACArray) +{ + // + // Save the MAC address. + // + g_pui8LocatorData[9] = pui8MACArray[0]; + g_pui8LocatorData[10] = pui8MACArray[1]; + g_pui8LocatorData[11] = pui8MACArray[2]; + g_pui8LocatorData[12] = pui8MACArray[3]; + g_pui8LocatorData[13] = pui8MACArray[4]; + g_pui8LocatorData[14] = pui8MACArray[5]; +} + +//***************************************************************************** +// +//! Sets the firmware version in the locator response packet. +//! +//! \param ui32Version is the version number of the device firmware. +//! +//! This function sets the version number of the device firmware in the locator +//! response packet. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorVersionSet(uint32_t ui32Version) +{ + // + // Save the firmware version number in the response data. + // + g_pui8LocatorData[15] = ui32Version & 0xff; + g_pui8LocatorData[16] = (ui32Version >> 8) & 0xff; + g_pui8LocatorData[17] = (ui32Version >> 16) & 0xff; + g_pui8LocatorData[18] = (ui32Version >> 24) & 0xff; +} + +//***************************************************************************** +// +//! Sets the application title in the locator response packet. +//! +//! \param pcAppTitle is a pointer to the application title string. +//! +//! This function sets the application title in the locator response packet. +//! The string is truncated at 64 characters if it is longer (without a +//! terminating 0), and is zero-filled to 64 characters if it is shorter. +//! +//! \return None. +// +//***************************************************************************** +void +LocatorAppTitleSet(const char *pcAppTitle) +{ + uint32_t ui32Count; + + // + // Copy the application title string into the response data. + // + for(ui32Count = 0; (ui32Count < 64) && *pcAppTitle; ui32Count++) + { + g_pui8LocatorData[ui32Count + 19] = *pcAppTitle++; + } + + // + // Zero-fill the remainder of the space in the response data (if any). + // + for(; ui32Count < 64; ui32Count++) + { + g_pui8LocatorData[ui32Count + 19] = 0; + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/locator.h b/utils/locator.h new file mode 100644 index 0000000..0ef7979 --- /dev/null +++ b/utils/locator.h @@ -0,0 +1,61 @@ +//***************************************************************************** +// +// locator.h - Prototypes for the device locator server. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __LOCATOR_H__ +#define __LOCATOR_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Function prototypes. +// +//***************************************************************************** +extern void LocatorInit(void); +extern void LocatorBoardTypeSet(uint32_t ui32Type); +extern void LocatorBoardIDSet(uint32_t ui32ID); +extern void LocatorClientIPSet(uint32_t ui32IP); +extern void LocatorMACAddrSet(uint8_t *pui8MACArray); +extern void LocatorVersionSet(uint32_t ui32Version); +extern void LocatorAppTitleSet(const char *pcAppTitle); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __LOCATOR_H__ diff --git a/utils/lwiplib.c b/utils/lwiplib.c new file mode 100644 index 0000000..89539bd --- /dev/null +++ b/utils/lwiplib.c @@ -0,0 +1,1399 @@ +//***************************************************************************** +// +// lwiplib.c - lwIP TCP/IP Library Abstraction Layer. +// +// 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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +// Ensure that the lwIP compile time options are included first. +// +//***************************************************************************** +#include +#include +#include "utils/lwiplib.h" + +//***************************************************************************** +// +// Ensure that ICMP checksum offloading is enabled; otherwise the TM4C129 +// driver will not operate correctly. +// +//***************************************************************************** +#ifndef LWIP_OFFLOAD_ICMP_CHKSUM +#define LWIP_OFFLOAD_ICMP_CHKSUM 1 +#endif + +//***************************************************************************** +// +// Include lwIP high-level API code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/api/api_lib.c" +#include "third_party/lwip-1.4.1/src/api/api_msg.c" +#include "third_party/lwip-1.4.1/src/api/err.c" +#include "third_party/lwip-1.4.1/src/api/netbuf.c" +#include "third_party/lwip-1.4.1/src/api/netdb.c" +#include "third_party/lwip-1.4.1/src/api/netifapi.c" +#include "third_party/lwip-1.4.1/src/api/sockets.c" +#include "third_party/lwip-1.4.1/src/api/tcpip.c" + +//***************************************************************************** +// +// Include the core lwIP TCP/IP stack code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/core/def.c" +#include "third_party/lwip-1.4.1/src/core/dhcp.c" +#include "third_party/lwip-1.4.1/src/core/dns.c" +#include "third_party/lwip-1.4.1/src/core/init.c" +#include "third_party/lwip-1.4.1/src/core/mem.c" +#include "third_party/lwip-1.4.1/src/core/memp.c" +#include "third_party/lwip-1.4.1/src/core/netif.c" +#include "third_party/lwip-1.4.1/src/core/pbuf.c" +#include "third_party/lwip-1.4.1/src/core/raw.c" +#include "third_party/lwip-1.4.1/src/core/stats.c" +#include "third_party/lwip-1.4.1/src/core/sys.c" +#include "third_party/lwip-1.4.1/src/core/tcp.c" +#include "third_party/lwip-1.4.1/src/core/tcp_in.c" +#include "third_party/lwip-1.4.1/src/core/tcp_out.c" +#include "third_party/lwip-1.4.1/src/core/timers.c" +#include "third_party/lwip-1.4.1/src/core/udp.c" + +//***************************************************************************** +// +// Include the IPV4 code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/core/ipv4/autoip.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/icmp.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/igmp.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/inet.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/inet_chksum.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/ip.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/ip_addr.c" +#include "third_party/lwip-1.4.1/src/core/ipv4/ip_frag.c" + +//***************************************************************************** +// +// Include the IPV6 code. +// Note: Code is experimental and not ready for use. +// References are included for completeness. +// +//***************************************************************************** +#if 0 +#include "third_party/lwip-1.4.1/src/core/ipv6/icmp6.c" +#include "third_party/lwip-1.4.1/src/core/ipv6/inet6.c" +#include "third_party/lwip-1.4.1/src/core/ipv6/ip6.c" +#include "third_party/lwip-1.4.1/src/core/ipv6/ip6_addr.c" +#endif + +//***************************************************************************** +// +// Include the lwIP SNMP code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/core/snmp/asn1_dec.c" +#include "third_party/lwip-1.4.1/src/core/snmp/asn1_enc.c" +#include "third_party/lwip-1.4.1/src/core/snmp/mib2.c" +#include "third_party/lwip-1.4.1/src/core/snmp/mib_structs.c" +#include "third_party/lwip-1.4.1/src/core/snmp/msg_in.c" +#include "third_party/lwip-1.4.1/src/core/snmp/msg_out.c" + +//***************************************************************************** +// +// Include the network interface code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/netif/etharp.c" + +//***************************************************************************** +// +// Include the network interface PPP code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/src/netif/ppp/auth.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/chap.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/chpms.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/fsm.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/ipcp.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/lcp.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/magic.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/md5.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/pap.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/ppp.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/ppp_oe.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/randm.c" +#include "third_party/lwip-1.4.1/src/netif/ppp/vj.c" + +//***************************************************************************** +// +// Include Tiva-specific lwIP interface/porting layer code. +// +//***************************************************************************** +#include "third_party/lwip-1.4.1/ports/tiva-tm4c129/perf.c" +#include "third_party/lwip-1.4.1/ports/tiva-tm4c129/sys_arch.c" +#include "third_party/lwip-1.4.1/ports/tiva-tm4c129/netif/tiva-tm4c129.c" + +//***************************************************************************** +// +//! \addtogroup lwiplib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The lwIP Library abstration layer provides for a host callback function to +// be called periodically in the lwIP context. This is the timer interval, in +// ms, for this periodic callback. If the timer interval is defined to 0 (the +// default value), then no periodic host callback is performed. +// +//***************************************************************************** +#ifndef HOST_TMR_INTERVAL +#define HOST_TMR_INTERVAL 0 +#else +extern void lwIPHostTimerHandler(void); +#endif + +//***************************************************************************** +// +// The link detect polling interval. +// +//***************************************************************************** +#define LINK_TMR_INTERVAL 10 + +//***************************************************************************** +// +// Set the PHY configuration to the default (internal) option if necessary. +// +//***************************************************************************** +#ifndef EMAC_PHY_CONFIG +#define EMAC_PHY_CONFIG (EMAC_PHY_TYPE_INTERNAL | \ + EMAC_PHY_INT_MDIX_EN | \ + EMAC_PHY_AN_100B_T_FULL_DUPLEX) +#endif + +//***************************************************************************** +// +// Driverlib headers needed for this library module. +// +//***************************************************************************** +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_nvic.h" +#include "driverlib/debug.h" +#include "driverlib/emac.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#if !NO_SYS +#if RTOS_FREERTOS +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" +#endif +#if ((RTOS_FREERTOS) < 1) + #error No RTOS is defined. Please define an RTOS. +#endif +#if ((RTOS_FREERTOS) > 1) + #error More than one RTOS defined. Please define only one RTOS at a time. +#endif +#endif + +//***************************************************************************** +// +// The lwIP network interface structure for the Tiva Ethernet MAC. +// +//***************************************************************************** +static struct netif g_sNetIF; + +//***************************************************************************** +// +// The application's interrupt handler for hardware timer events from the MAC. +// +//***************************************************************************** +tHardwareTimerHandler g_pfnTimerHandler; + +//***************************************************************************** +// +// The local time for the lwIP Library Abstraction layer, used to support the +// Host and lwIP periodic callback functions. +// +//***************************************************************************** +#if NO_SYS +uint32_t g_ui32LocalTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the TCP timer was last serviced. +// +//***************************************************************************** +#if NO_SYS +static uint32_t g_ui32TCPTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the HOST timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && HOST_TMR_INTERVAL +static uint32_t g_ui32HostTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the ARP timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_ARP +static uint32_t g_ui32ARPTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the AutoIP timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_AUTOIP +static uint32_t g_ui32AutoIPTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the DHCP Coarse timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_DHCP +static uint32_t g_ui32DHCPCoarseTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the DHCP Fine timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_DHCP +static uint32_t g_ui32DHCPFineTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the IP Reassembly timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && IP_REASSEMBLY +static uint32_t g_ui32IPReassemblyTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the IGMP timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_IGMP +static uint32_t g_ui32IGMPTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the DNS timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && LWIP_DNS +static uint32_t g_ui32DNSTimer = 0; +#endif + +//***************************************************************************** +// +// The local time when the link detect timer was last serviced. +// +//***************************************************************************** +#if NO_SYS && (LWIP_AUTOIP || LWIP_DHCP) +static uint32_t g_ui32LinkTimer = 0; +#endif + +//***************************************************************************** +// +// The default IP address acquisition mode. +// +//***************************************************************************** +static uint32_t g_ui32IPMode = IPADDR_USE_STATIC; + +//***************************************************************************** +// +// The most recently detected link state. +// +//***************************************************************************** +#if LWIP_AUTOIP || LWIP_DHCP +static bool g_bLinkActive = false; +#endif + +//***************************************************************************** +// +// The IP address to be used. This is used during the initialization of the +// stack and when the interface configuration is changed. +// +//***************************************************************************** +static uint32_t g_ui32IPAddr; + +//***************************************************************************** +// +// The netmask to be used. This is used during the initialization of the stack +// and when the interface configuration is changed. +// +//***************************************************************************** +static uint32_t g_ui32NetMask; + +//***************************************************************************** +// +// The gateway address to be used. This is used during the initialization of +// the stack and when the interface configuration is changed. +// +//***************************************************************************** +static uint32_t g_ui32GWAddr; + +//***************************************************************************** +// +// The stack size for the interrupt task. +// +//***************************************************************************** +#if !NO_SYS +#define STACKSIZE_LWIPINTTASK 128 +#endif + +//***************************************************************************** +// +// The handle for the "queue" (semaphore) used to signal the interrupt task +// from the interrupt handler. +// +//***************************************************************************** +#if !NO_SYS +static xQueueHandle g_pInterrupt; +#endif + +//***************************************************************************** +// +// This task handles reading packets from the Ethernet controller and supplying +// them to the TCP/IP thread. +// +//***************************************************************************** +#if !NO_SYS +static void +lwIPInterruptTask(void *pvArg) +{ + // + // Loop forever. + // + while(1) + { + // + // Wait until the semaphore has been signaled. + // + while(xQueueReceive(g_pInterrupt, &pvArg, portMAX_DELAY) != pdPASS) + { + } + + // + // Processes any packets waiting to be sent or received. + // + tivaif_interrupt(&g_sNetIF, (uint32_t)pvArg); + + // + // Re-enable the Ethernet interrupts. + // + MAP_EMACIntEnable(EMAC0_BASE, (EMAC_INT_RECEIVE | EMAC_INT_TRANSMIT | + EMAC_INT_TX_STOPPED | + EMAC_INT_RX_NO_BUFFER | + EMAC_INT_RX_STOPPED | EMAC_INT_PHY)); + } +} +#endif + +//***************************************************************************** +// +// This function performs a periodic check of the link status and responds +// appropriately if it has changed. +// +//***************************************************************************** +#if LWIP_AUTOIP || LWIP_DHCP +static void +lwIPLinkDetect(void) +{ + bool bHaveLink; + struct ip_addr ip_addr; + struct ip_addr net_mask; + struct ip_addr gw_addr; + + // + // See if there is an active link. + // + bHaveLink = MAP_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) & EPHY_BMSR_LINKSTAT; + + // + // Return without doing anything else if the link state hasn't changed. + // + if(bHaveLink == g_bLinkActive) + { + return; + } + + // + // Save the new link state. + // + g_bLinkActive = bHaveLink; + + // + // Clear any address information from the network interface. + // + ip_addr.addr = 0; + net_mask.addr = 0; + gw_addr.addr = 0; + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + + // + // See if there is a link now. + // + if(bHaveLink) + { + // + // Start DHCP, if enabled. + // +#if LWIP_DHCP + if(g_ui32IPMode == IPADDR_USE_DHCP) + { + dhcp_start(&g_sNetIF); + } +#endif + + // + // Start AutoIP, if enabled and DHCP is not. + // +#if LWIP_AUTOIP + if(g_ui32IPMode == IPADDR_USE_AUTOIP) + { + autoip_start(&g_sNetIF); + } +#endif + } + else + { + // + // Stop DHCP, if enabled. + // +#if LWIP_DHCP + if(g_ui32IPMode == IPADDR_USE_DHCP) + { + dhcp_stop(&g_sNetIF); + } +#endif + + // + // Stop AutoIP, if enabled and DHCP is not. + // +#if LWIP_AUTOIP + if(g_ui32IPMode == IPADDR_USE_AUTOIP) + { + autoip_stop(&g_sNetIF); + } +#endif + } +} +#endif + +//***************************************************************************** +// +// This function services all of the lwIP periodic timers, including TCP and +// Host timers. This should be called from the lwIP context, which may be +// the Ethernet interrupt (in the case of a non-RTOS system) or the lwIP +// thread, in the event that an RTOS is used. +// +//***************************************************************************** +#if NO_SYS +static void +lwIPServiceTimers(void) +{ + // + // Service the host timer. + // +#if HOST_TMR_INTERVAL + if((g_ui32LocalTimer - g_ui32HostTimer) >= HOST_TMR_INTERVAL) + { + g_ui32HostTimer = g_ui32LocalTimer; + lwIPHostTimerHandler(); + } +#endif + + // + // Service the ARP timer. + // +#if LWIP_ARP + if((g_ui32LocalTimer - g_ui32ARPTimer) >= ARP_TMR_INTERVAL) + { + g_ui32ARPTimer = g_ui32LocalTimer; + etharp_tmr(); + } +#endif + + // + // Service the TCP timer. + // +#if LWIP_TCP + if((g_ui32LocalTimer - g_ui32TCPTimer) >= TCP_TMR_INTERVAL) + { + g_ui32TCPTimer = g_ui32LocalTimer; + tcp_tmr(); + } +#endif + + // + // Service the AutoIP timer. + // +#if LWIP_AUTOIP + if((g_ui32LocalTimer - g_ui32AutoIPTimer) >= AUTOIP_TMR_INTERVAL) + { + g_ui32AutoIPTimer = g_ui32LocalTimer; + autoip_tmr(); + } +#endif + + // + // Service the DCHP Coarse Timer. + // +#if LWIP_DHCP + if((g_ui32LocalTimer - g_ui32DHCPCoarseTimer) >= DHCP_COARSE_TIMER_MSECS) + { + g_ui32DHCPCoarseTimer = g_ui32LocalTimer; + dhcp_coarse_tmr(); + } +#endif + + // + // Service the DCHP Fine Timer. + // +#if LWIP_DHCP + if((g_ui32LocalTimer - g_ui32DHCPFineTimer) >= DHCP_FINE_TIMER_MSECS) + { + g_ui32DHCPFineTimer = g_ui32LocalTimer; + dhcp_fine_tmr(); + } +#endif + + // + // Service the IP Reassembly Timer + // +#if IP_REASSEMBLY + if((g_ui32LocalTimer - g_ui32IPReassemblyTimer) >= IP_TMR_INTERVAL) + { + g_ui32IPReassemblyTimer = g_ui32LocalTimer; + ip_reass_tmr(); + } +#endif + + // + // Service the IGMP Timer + // +#if LWIP_IGMP + if((g_ui32LocalTimer - g_ui32IGMPTimer) >= IGMP_TMR_INTERVAL) + { + g_ui32IGMPTimer = g_ui32LocalTimer; + igmp_tmr(); + } +#endif + + // + // Service the DNS Timer + // +#if LWIP_DNS + if((g_ui32LocalTimer - g_ui32DNSTimer) >= DNS_TMR_INTERVAL) + { + g_ui32DNSTimer = g_ui32LocalTimer; + dns_tmr(); + } +#endif + + // + // Service the link timer. + // +#if LWIP_AUTOIP || LWIP_DHCP + if((g_ui32LocalTimer - g_ui32LinkTimer) >= LINK_TMR_INTERVAL) + { + g_ui32LinkTimer = g_ui32LocalTimer; + lwIPLinkDetect(); + } +#endif +} +#endif + +//***************************************************************************** +// +// Handles the timeout for the host callback function timer when using a RTOS. +// +//***************************************************************************** +#if !NO_SYS && HOST_TMR_INTERVAL +static void +lwIPPrivateHostTimer(void *pvArg) +{ + // + // Call the application-supplied host timer callback function. + // + lwIPHostTimerHandler(); + + // + // Re-schedule the host timer callback function timeout. + // + sys_timeout(HOST_TMR_INTERVAL, lwIPPrivateHostTimer, NULL); +} +#endif + +//***************************************************************************** +// +// Handles the timeout for the link detect timer when using a RTOS. +// +//***************************************************************************** +#if !NO_SYS && (LWIP_AUTOIP || LWIP_DHCP) +static void +lwIPPrivateLinkTimer(void *pvArg) +{ + // + // Perform the link detection. + // + lwIPLinkDetect(); + + // + // Re-schedule the link detect timer timeout. + // + sys_timeout(LINK_TMR_INTERVAL, lwIPPrivateLinkTimer, NULL); +} +#endif + +//***************************************************************************** +// +// Completes the initialization of lwIP. This is directly called when not +// using a RTOS and provided as a callback to the TCP/IP thread when using a +// RTOS. +// +//***************************************************************************** +static void +lwIPPrivateInit(void *pvArg) +{ + struct ip_addr ip_addr; + struct ip_addr net_mask; + struct ip_addr gw_addr; + + // + // If not using a RTOS, initialize the lwIP stack. + // +#if NO_SYS + lwip_init(); +#endif + + // + // If using a RTOS, create a queue (to be used as a semaphore) to signal + // the Ethernet interrupt task from the Ethernet interrupt handler. + // +#if !NO_SYS +#if RTOS_FREERTOS + g_pInterrupt = xQueueCreate(1, sizeof(void *)); +#endif +#endif + + // + // If using a RTOS, create the Ethernet interrupt task. + // +#if !NO_SYS +#if RTOS_FREERTOS + xTaskCreate(lwIPInterruptTask, (signed portCHAR *)"eth_int", + STACKSIZE_LWIPINTTASK, 0, tskIDLE_PRIORITY + 1, + 0); +#endif +#endif + + // + // Setup the network address values. + // + if(g_ui32IPMode == IPADDR_USE_STATIC) + { + ip_addr.addr = htonl(g_ui32IPAddr); + net_mask.addr = htonl(g_ui32NetMask); + gw_addr.addr = htonl(g_ui32GWAddr); + } + else + { + ip_addr.addr = 0; + net_mask.addr = 0; + gw_addr.addr = 0; + } + + // + // Create, configure and add the Ethernet controller interface with + // default settings. ip_input should be used to send packets directly to + // the stack when not using a RTOS and tcpip_input should be used to send + // packets to the TCP/IP thread's queue when using a RTOS. + // +#if NO_SYS + netif_add(&g_sNetIF, &ip_addr, &net_mask, &gw_addr, NULL, tivaif_init, + ip_input); +#else + netif_add(&g_sNetIF, &ip_addr, &net_mask, &gw_addr, NULL, tivaif_init, + tcpip_input); +#endif + netif_set_default(&g_sNetIF); + + // + // Bring the interface up. + // + netif_set_up(&g_sNetIF); + + // + // Setup a timeout for the host timer callback function if using a RTOS. + // +#if !NO_SYS && HOST_TMR_INTERVAL + sys_timeout(HOST_TMR_INTERVAL, lwIPPrivateHostTimer, NULL); +#endif + + // + // Setup a timeout for the link detect callback function if using a RTOS. + // +#if !NO_SYS && (LWIP_AUTOIP || LWIP_DHCP) + sys_timeout(LINK_TMR_INTERVAL, lwIPPrivateLinkTimer, NULL); +#endif +} + +//***************************************************************************** +// +//! Initializes the lwIP TCP/IP stack. +//! +//! \param ui32SysClkHz is the current system clock rate in Hz. +//! \param pui8MAC is a pointer to a six byte array containing the MAC +//! address to be used for the interface. +//! \param ui32IPAddr is the IP address to be used (static). +//! \param ui32NetMask is the network mask to be used (static). +//! \param ui32GWAddr is the Gateway address to be used (static). +//! \param ui32IPMode is the IP Address Mode. \b IPADDR_USE_STATIC will force +//! static IP addressing to be used, \b IPADDR_USE_DHCP will force DHCP with +//! fallback to Link Local (Auto IP), while \b IPADDR_USE_AUTOIP will force +//! Link Local only. +//! +//! This function performs initialization of the lwIP TCP/IP stack for the +//! Ethernet MAC, including DHCP and/or AutoIP, as configured. +//! +//! \return None. +// +//***************************************************************************** +void +lwIPInit(uint32_t ui32SysClkHz, const uint8_t *pui8MAC, uint32_t ui32IPAddr, + uint32_t ui32NetMask, uint32_t ui32GWAddr, uint32_t ui32IPMode) +{ + // + // Check the parameters. + // +#if LWIP_DHCP && LWIP_AUTOIP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_DHCP) || + (ui32IPMode == IPADDR_USE_AUTOIP)); +#elif LWIP_DHCP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_DHCP)); +#elif LWIP_AUTOIP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_AUTOIP)); +#else + ASSERT(ui32IPMode == IPADDR_USE_STATIC); +#endif + + // + // Enable the ethernet peripheral. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_EMAC0); + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_EMAC0); + + // + // Enable the internal PHY if it's present and we're being + // asked to use it. + // + if((EMAC_PHY_CONFIG & EMAC_PHY_TYPE_MASK) == EMAC_PHY_TYPE_INTERNAL) + { + // + // We've been asked to configure for use with the internal + // PHY. Is it present? + // + if(MAP_SysCtlPeripheralPresent(SYSCTL_PERIPH_EPHY0)) + { + // + // Yes - enable and reset it. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_EPHY0); + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0); + } + else + { + // + // Internal PHY is not present on this part so hang here. + // + while(1) + { + } + } + } + + // + // Wait for the MAC to come out of reset. + // + while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_EMAC0)) + { + } + + // + // Configure for use with whichever PHY the user requires. + // + MAP_EMACPHYConfigSet(EMAC0_BASE, EMAC_PHY_CONFIG); + + // + // Initialize the MAC and set the DMA mode. + // + MAP_EMACInit(EMAC0_BASE, ui32SysClkHz, + EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, + 4, 4, 0); + + // + // Set MAC configuration options. + // + MAP_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); + + // + // Program the hardware with its MAC address (for filtering). + // + MAP_EMACAddrSet(EMAC0_BASE, 0, (uint8_t *)pui8MAC); + + // + // Save the network configuration for later use by the private + // initialization. + // + g_ui32IPMode = ui32IPMode; + g_ui32IPAddr = ui32IPAddr; + g_ui32NetMask = ui32NetMask; + g_ui32GWAddr = ui32GWAddr; + + // + // Initialize lwIP. The remainder of initialization is done immediately if + // not using a RTOS and it is deferred to the TCP/IP thread's context if + // using a RTOS. + // +#if NO_SYS + lwIPPrivateInit(0); +#else + tcpip_init(lwIPPrivateInit, 0); +#endif +} + +//***************************************************************************** +// +//! Registers an interrupt callback function to handle the IEEE-1588 timer. +//! +//! \param pfnTimerFunc points to a function which is called whenever the +//! Ethernet MAC reports an interrupt relating to the IEEE-1588 hardware timer. +//! +//! This function allows an application to register a handler for all +//! interrupts generated by the IEEE-1588 hardware timer in the Ethernet MAC. +//! To allow minimal latency timer handling, the callback function provided +//! will be called in interrupt context, regardless of whether or not lwIP is +//! configured to operate with an RTOS. In an RTOS environment, the callback +//! function is responsible for ensuring that all processing it performs is +//! compatible with the low level interrupt context it is called in. +//! +//! The callback function takes two parameters. The first is the base address +//! of the MAC reporting the timer interrupt and the second is the timer +//! interrupt status as reported by EMACTimestampIntStatus(). Note that +//! EMACTimestampIntStatus() causes the timer interrupt sources to be cleared +//! so the application should not call EMACTimestampIntStatus() within the +//! handler. +//! +//! \return None. +// +//***************************************************************************** +void +lwIPTimerCallbackRegister(tHardwareTimerHandler pfnTimerFunc) +{ + // + // Remember the callback function address passed. + // + g_pfnTimerHandler = pfnTimerFunc; +} + +//***************************************************************************** +// +//! Handles periodic timer events for the lwIP TCP/IP stack. +//! +//! \param ui32TimeMS is the incremental time for this periodic interrupt. +//! +//! This function will update the local timer by the value in \e ui32TimeMS. +//! If the system is configured for use without an RTOS, an Ethernet interrupt +//! will be triggered to allow the lwIP periodic timers to be serviced in the +//! Ethernet interrupt. +//! +//! \return None. +// +//***************************************************************************** +#if NO_SYS +void +lwIPTimer(uint32_t ui32TimeMS) +{ + // + // Increment the lwIP Ethernet timer. + // + g_ui32LocalTimer += ui32TimeMS; + + // + // Generate an Ethernet interrupt. This will perform the actual work + // of checking the lwIP timers and taking the appropriate actions. This is + // needed since lwIP is not re-entrant, and this allows all lwIP calls to + // be placed inside the Ethernet interrupt handler ensuring that all calls + // into lwIP are coming from the same context, preventing any reentrancy + // issues. Putting all the lwIP calls in the Ethernet interrupt handler + // avoids the use of mutexes to avoid re-entering lwIP. + // + HWREG(NVIC_SW_TRIG) |= INT_EMAC0 - 16; +} +#endif + +//***************************************************************************** +// +//! Handles Ethernet interrupts for the lwIP TCP/IP stack. +//! +//! This function handles Ethernet interrupts for the lwIP TCP/IP stack. At +//! the lowest level, all receive packets are placed into a packet queue for +//! processing at a higher level. Also, the transmit packet queue is checked +//! and packets are drained and transmitted through the Ethernet MAC as needed. +//! If the system is configured without an RTOS, additional processing is +//! performed at the interrupt level. The packet queues are processed by the +//! lwIP TCP/IP code, and lwIP periodic timers are serviced (as needed). +//! +//! \return None. +// +//***************************************************************************** +void +lwIPEthernetIntHandler(void) +{ + uint32_t ui32Status; + uint32_t ui32TimerStatus; +#if !NO_SYS + portBASE_TYPE xWake; +#endif + + // + // Read and Clear the interrupt. + // + ui32Status = MAP_EMACIntStatus(EMAC0_BASE, true); + + // + // If the interrupt really came from the Ethernet and not our + // timer, clear it. + // + if(ui32Status) + { + MAP_EMACIntClear(EMAC0_BASE, ui32Status); + } + + // + // Check to see whether a hardware timer interrupt has been reported. + // + if(ui32Status & EMAC_INT_TIMESTAMP) + { + // + // Yes - read and clear the timestamp interrupt status. + // + ui32TimerStatus = EMACTimestampIntStatus(EMAC0_BASE); + + // + // If a timer interrupt handler has been registered, call it. + // + if(g_pfnTimerHandler) + { + g_pfnTimerHandler(EMAC0_BASE, ui32TimerStatus); + } + } + + // + // The handling of the interrupt is different based on the use of a RTOS. + // +#if NO_SYS + // + // No RTOS is being used. If a transmit/receive interrupt was active, + // run the low-level interrupt handler. + // + if(ui32Status) + { + tivaif_interrupt(&g_sNetIF, ui32Status); + } + + // + // Service the lwIP timers. + // + lwIPServiceTimers(); +#else + // + // A RTOS is being used. Signal the Ethernet interrupt task. + // + xQueueSendFromISR(g_pInterrupt, (void *)&ui32Status, &xWake); + + // + // Disable the Ethernet interrupts. Since the interrupts have not been + // handled, they are not asserted. Once they are handled by the Ethernet + // interrupt task, it will re-enable the interrupts. + // + MAP_EMACIntDisable(EMAC0_BASE, (EMAC_INT_RECEIVE | EMAC_INT_TRANSMIT | + EMAC_INT_TX_STOPPED | + EMAC_INT_RX_NO_BUFFER | + EMAC_INT_RX_STOPPED | EMAC_INT_PHY)); + + // + // Potentially task switch as a result of the above queue write. + // +#if RTOS_FREERTOS + if(xWake == pdTRUE) + { + portYIELD_FROM_ISR(true); + } +#endif +#endif +} + +//***************************************************************************** +// +//! Returns the IP address for this interface. +//! +//! This function will read and return the currently assigned IP address for +//! the Stellaris Ethernet interface. +//! +//! \return Returns the assigned IP address for this interface. +// +//***************************************************************************** +uint32_t +lwIPLocalIPAddrGet(void) +{ +#if LWIP_AUTOIP || LWIP_DHCP + if(g_bLinkActive) + { + return((uint32_t)g_sNetIF.ip_addr.addr); + } + else + { + return(0xffffffff); + } +#else + return((uint32_t)g_sNetIF.ip_addr.addr); +#endif +} + +//***************************************************************************** +// +//! Returns the network mask for this interface. +//! +//! This function will read and return the currently assigned network mask for +//! the Stellaris Ethernet interface. +//! +//! \return the assigned network mask for this interface. +// +//***************************************************************************** +uint32_t +lwIPLocalNetMaskGet(void) +{ + return((uint32_t)g_sNetIF.netmask.addr); +} + +//***************************************************************************** +// +//! Returns the gateway address for this interface. +//! +//! This function will read and return the currently assigned gateway address +//! for the Stellaris Ethernet interface. +//! +//! \return the assigned gateway address for this interface. +// +//***************************************************************************** +uint32_t +lwIPLocalGWAddrGet(void) +{ + return((uint32_t)g_sNetIF.gw.addr); +} + +//***************************************************************************** +// +//! Returns the local MAC/HW address for this interface. +//! +//! \param pui8MAC is a pointer to an array of bytes used to store the MAC +//! address. +//! +//! This function will read the currently assigned MAC address into the array +//! passed in \e pui8MAC. +//! +//! \return None. +// +//***************************************************************************** +void +lwIPLocalMACGet(uint8_t *pui8MAC) +{ + MAP_EMACAddrGet(EMAC0_BASE, 0, pui8MAC); +} + +//***************************************************************************** +// +// Completes the network configuration change. This is directly called when +// not using a RTOS and provided as a callback to the TCP/IP thread when using +// a RTOS. +// +//***************************************************************************** +static void +lwIPPrivateNetworkConfigChange(void *pvArg) +{ + uint32_t ui32IPMode; + struct ip_addr ip_addr; + struct ip_addr net_mask; + struct ip_addr gw_addr; + + // + // Get the new address mode. + // + ui32IPMode = (uint32_t)pvArg; + + // + // Setup the network address values. + // + if(ui32IPMode == IPADDR_USE_STATIC) + { + ip_addr.addr = htonl(g_ui32IPAddr); + net_mask.addr = htonl(g_ui32NetMask); + gw_addr.addr = htonl(g_ui32GWAddr); + } +#if LWIP_DHCP || LWIP_AUTOIP + else + { + ip_addr.addr = 0; + net_mask.addr = 0; + gw_addr.addr = 0; + } +#endif + + // + // Switch on the current IP Address Aquisition mode. + // + switch(g_ui32IPMode) + { + // + // Static IP + // + case IPADDR_USE_STATIC: + { + // + // Set the new address parameters. This will change the address + // configuration in lwIP, and if necessary, will reset any links + // that are active. This is valid for all three modes. + // + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + + // + // If we are going to DHCP mode, then start the DHCP server now. + // +#if LWIP_DHCP + if((ui32IPMode == IPADDR_USE_DHCP) && g_bLinkActive) + { + dhcp_start(&g_sNetIF); + } +#endif + + // + // If we are going to AutoIP mode, then start the AutoIP process + // now. + // +#if LWIP_AUTOIP + if((ui32IPMode == IPADDR_USE_AUTOIP) && g_bLinkActive) + { + autoip_start(&g_sNetIF); + } +#endif + + // + // And we're done. + // + break; + } + + // + // DHCP (with AutoIP fallback). + // +#if LWIP_DHCP + case IPADDR_USE_DHCP: + { + // + // If we are going to static IP addressing, then disable DHCP and + // force the new static IP address. + // + if(ui32IPMode == IPADDR_USE_STATIC) + { + dhcp_stop(&g_sNetIF); + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + } + + // + // If we are going to AUTO IP addressing, then disable DHCP, set + // the default addresses, and start AutoIP. + // +#if LWIP_AUTOIP + else if(ui32IPMode == IPADDR_USE_AUTOIP) + { + dhcp_stop(&g_sNetIF); + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + if(g_bLinkActive) + { + autoip_start(&g_sNetIF); + } + } +#endif + break; + } +#endif + + // + // AUTOIP + // +#if LWIP_AUTOIP + case IPADDR_USE_AUTOIP: + { + // + // If we are going to static IP addressing, then disable AutoIP and + // force the new static IP address. + // + if(ui32IPMode == IPADDR_USE_STATIC) + { + autoip_stop(&g_sNetIF); + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + } + + // + // If we are going to DHCP addressing, then disable AutoIP, set the + // default addresses, and start dhcp. + // +#if LWIP_DHCP + else if(ui32IPMode == IPADDR_USE_DHCP) + { + autoip_stop(&g_sNetIF); + netif_set_addr(&g_sNetIF, &ip_addr, &net_mask, &gw_addr); + if(g_bLinkActive) + { + dhcp_start(&g_sNetIF); + } + } +#endif + break; + } +#endif + } + + // + // Bring the interface up. + // + netif_set_up(&g_sNetIF); + + // + // Save the new mode. + // + g_ui32IPMode = ui32IPMode; +} + +//***************************************************************************** +// +//! Change the configuration of the lwIP network interface. +//! +//! \param ui32IPAddr is the new IP address to be used (static). +//! \param ui32NetMask is the new network mask to be used (static). +//! \param ui32GWAddr is the new Gateway address to be used (static). +//! \param ui32IPMode is the IP Address Mode. \b IPADDR_USE_STATIC 0 will +//! force static IP addressing to be used, \b IPADDR_USE_DHCP will force DHCP +//! with fallback to Link Local (Auto IP), while \b IPADDR_USE_AUTOIP will +//! force Link Local only. +//! +//! This function will evaluate the new configuration data. If necessary, the +//! interface will be brought down, reconfigured, and then brought back up +//! with the new configuration. +//! +//! \return None. +// +//***************************************************************************** +void +lwIPNetworkConfigChange(uint32_t ui32IPAddr, uint32_t ui32NetMask, + uint32_t ui32GWAddr, uint32_t ui32IPMode) +{ + // + // Check the parameters. + // +#if LWIP_DHCP && LWIP_AUTOIP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_DHCP) || + (ui32IPMode == IPADDR_USE_AUTOIP)); +#elif LWIP_DHCP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_DHCP)); +#elif LWIP_AUTOIP + ASSERT((ui32IPMode == IPADDR_USE_STATIC) || + (ui32IPMode == IPADDR_USE_AUTOIP)); +#else + ASSERT(ui32IPMode == IPADDR_USE_STATIC); +#endif + + // + // Save the network configuration for later use by the private network + // configuration change. + // + g_ui32IPAddr = ui32IPAddr; + g_ui32NetMask = ui32NetMask; + g_ui32GWAddr = ui32GWAddr; + + // + // Complete the network configuration change. The remainder is done + // immediately if not using a RTOS and it is deferred to the TCP/IP + // thread's context if using a RTOS. + // +#if NO_SYS + lwIPPrivateNetworkConfigChange((void *)ui32IPMode); +#else + tcpip_callback(lwIPPrivateNetworkConfigChange, (void *)ui32IPMode); +#endif +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/lwiplib.h b/utils/lwiplib.h new file mode 100644 index 0000000..3a3f622 --- /dev/null +++ b/utils/lwiplib.h @@ -0,0 +1,121 @@ +//***************************************************************************** +// +// lwiplib.h - Prototypes for the lwIP library wrapper API. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __LWIPLIB_H__ +#define __LWIPLIB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// lwIP Options +// +//***************************************************************************** +#include "lwip/opt.h" + +//***************************************************************************** +// +// Ensure that AUTOIP COOP option is configured correctly. +// +//***************************************************************************** +#undef LWIP_DHCP_AUTOIP_COOP +#define LWIP_DHCP_AUTOIP_COOP ((LWIP_DHCP) && (LWIP_AUTOIP)) + +//***************************************************************************** +// +// lwIP API Header Files +// +//***************************************************************************** +#include +#include "lwip/api.h" +#include "lwip/netifapi.h" +#include "lwip/tcp.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" +#include "lwip/sockets.h" +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/tcp_impl.h" +#include "lwip/timers.h" + +//***************************************************************************** +// +// IP Address Acquisition Modes +// +//***************************************************************************** +#define IPADDR_USE_STATIC 0 +#define IPADDR_USE_DHCP 1 +#define IPADDR_USE_AUTOIP 2 + +//***************************************************************************** +// +// Hardware timer interrupt callback function type (available only when running +// on TM4C parts). This function is called in interrupt context whenever the +// Ethernet MAC reports an interrupt from the IEEE-1588 timestamping +// timer. The first parameter is the base address of the MAC and the second +// is the interrupt status as reported via EthMACTimestampIntStatus. +// +//***************************************************************************** +typedef void (* tHardwareTimerHandler)(uint32_t ui32Base, + uint32_t ui32IntStatus); + +//***************************************************************************** +// +// lwIP Abstraction Layer API +// +//***************************************************************************** +extern void lwIPInit(uint32_t ui32SysClkHz, const uint8_t *pui8Mac, + uint32_t ui32IPAddr, uint32_t ui32NetMask, + uint32_t ui32GWAddr, uint32_t ui32IPMode); +extern void lwIPTimerCallbackRegister(tHardwareTimerHandler pfnTimerFunc); +extern void lwIPTimer(uint32_t ui32TimeMS); +extern void lwIPEthernetIntHandler(void); +extern uint32_t lwIPLocalIPAddrGet(void); +extern uint32_t lwIPLocalNetMaskGet(void); +extern uint32_t lwIPLocalGWAddrGet(void); +extern void lwIPLocalMACGet(uint8_t *pui8Mac); +extern void lwIPNetworkConfigChange(uint32_t ui32IPAddr, uint32_t ui32NetMask, + uint32_t ui32GWAddr, uint32_t ui32IPMode); +extern uint32_t lwIPAcceptUDPPort(uint16_t ui16Port); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __LWIPLIB_H__ diff --git a/utils/ptpdlib.c b/utils/ptpdlib.c new file mode 100644 index 0000000..ec340d2 --- /dev/null +++ b/utils/ptpdlib.c @@ -0,0 +1,56 @@ +//***************************************************************************** +// +// ptpdlib.c - ptpd Library Abstraction Layer. +// +// 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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +// Include the necessary system header files. +// +//***************************************************************************** +#include + +//***************************************************************************** +// +// Include the library source code header files next. +// +//***************************************************************************** +#include "utils/ptpdlib.h" + +//***************************************************************************** +// +// Include ptpd library code. +// +//***************************************************************************** +#include "ptpd-1.1.0/src/arith.c" +#include "ptpd-1.1.0/src/bmc.c" +#include "ptpd-1.1.0/src/protocol.c" + +//***************************************************************************** +// +// Include ptpd porting layer code. +// +//***************************************************************************** +#include "ptpd-1.1.0/src/dep-tiva/ptpd_timer.c" +#include "ptpd-1.1.0/src/dep-tiva/ptpd_servo.c" +#include "ptpd-1.1.0/src/dep-tiva/ptpd_msg.c" +#include "ptpd-1.1.0/src/dep-tiva/ptpd_net.c" diff --git a/utils/ptpdlib.h b/utils/ptpdlib.h new file mode 100644 index 0000000..5692877 --- /dev/null +++ b/utils/ptpdlib.h @@ -0,0 +1,55 @@ +//***************************************************************************** +// +// ptpdlib.h - Prototypes for the ptpd library wrapper API. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __PTPDLIB_H__ +#define __PTPDLIB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// ptpd API Header Files +// +//***************************************************************************** +#include "ptpd-1.1.0/src/ptpd.h" + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __PTPDLIB_H__ diff --git a/utils/random.c b/utils/random.c new file mode 100644 index 0000000..0588c44 --- /dev/null +++ b/utils/random.c @@ -0,0 +1,169 @@ +//***************************************************************************** +// +// random.c - Random number generator utilizing MD4 hash function of +// environmental noise captured as the seed and a linear congruence +// generator for the random numbers. +// +// Copyright (c) 2011-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 Utility Library. +// +//***************************************************************************** + +#include +#include "ustdlib.h" +#include "random.h" + +//***************************************************************************** +// +//! \addtogroup random_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The pool of entropy that has been collected. +// +//***************************************************************************** +static uint32_t g_pui32RandomEntropy[16]; + +//***************************************************************************** +// +// The index of the next byte to be added to the entropy pool. +// +//***************************************************************************** +static uint32_t g_ui32RandomIndex = 0; + +//***************************************************************************** +// +//! Add entropy to the pool. +//! +//! \param ui32Entropy is an 8-bit value that is added to the entropy pool +//! +//! This function allows the user application code to add entropy (random data) +//! to the pool at any time. +//! +//! \return None +// +//***************************************************************************** +void +RandomAddEntropy(uint32_t ui32Entropy) +{ + // + // Add this byte to the entropy pool. + // + ((uint8_t *)g_pui32RandomEntropy)[g_ui32RandomIndex] = ui32Entropy & 0xff; + + // + // Increment to the next byte of the entropy pool. + // + g_ui32RandomIndex = (g_ui32RandomIndex + 1) & 63; +} + +//***************************************************************************** +// +//! Set the random number generator seed. +//! +//! Seed the random number generator by running a MD4 hash on the entropy pool. +//! Note that the entropy pool may change from beneath us, but for the purposes +//! of generating random numbers that is not a concern. Also, the MD4 hash was +//! broken long ago, but since it is being used to generate random numbers +//! instead of providing security this is not a concern. +//! +//! \return New seed value. +// +//***************************************************************************** +uint32_t +RandomSeed(void) +{ + uint32_t ui32A, ui32B, ui32C, ui32D, ui32Temp, ui32Idx; + + // + // Initialize the digest. + // + ui32A = 0x67452301; + ui32B = 0xefcdab89; + ui32C = 0x98badcfe; + ui32D = 0x10325476; + + // + // Perform the first round of operations. + // +#define F(a, b, c, d, k, s) \ + { \ + ui32Temp = a + (d ^ (b & (c ^ d))) + g_pui32RandomEntropy[k]; \ + a = (ui32Temp << s) | (ui32Temp >> (32 - s)); \ + } + for(ui32Idx = 0; ui32Idx < 16; ui32Idx += 4) + { + F(ui32A, ui32B, ui32C, ui32D, ui32Idx + 0, 3); + F(ui32D, ui32A, ui32B, ui32C, ui32Idx + 1, 7); + F(ui32C, ui32D, ui32A, ui32B, ui32Idx + 2, 11); + F(ui32B, ui32C, ui32D, ui32A, ui32Idx + 3, 19); + } + + // + // Perform the second round of operations. + // +#define G(a, b, c, d, k, s) \ + { \ + ui32Temp = (a + ((b & c) | (b & d) | (c & d)) + \ + g_pui32RandomEntropy[k] + 0x5a827999); \ + a = (ui32Temp << s) | (ui32Temp >> (32 - s)); \ + } + for(ui32Idx = 0; ui32Idx < 4; ui32Idx++) + { + G(ui32A, ui32B, ui32C, ui32D, ui32Idx + 0, 3); + G(ui32D, ui32A, ui32B, ui32C, ui32Idx + 4, 5); + G(ui32C, ui32D, ui32A, ui32B, ui32Idx + 8, 9); + G(ui32B, ui32C, ui32D, ui32A, ui32Idx + 12, 13); + } + + // + // Perform the third round of operations. + // +#define H(a, b, c, d, k, s) \ + { \ + ui32Temp = a + (b ^ c ^ d) + g_pui32RandomEntropy[k] + 0x6ed9eba1; \ + a = (ui32Temp << s) | (ui32Temp >> (32 - s)); \ + } + for(ui32Idx = 0; ui32Idx < 4; ui32Idx += 2) + { + H(ui32A, ui32B, ui32C, ui32D, ui32Idx + 0, 3); + H(ui32D, ui32A, ui32B, ui32C, ui32Idx + 8, 9); + H(ui32C, ui32D, ui32A, ui32B, ui32Idx + 4, 11); + H(ui32B, ui32C, ui32D, ui32A, ui32Idx + 12, 15); + + if(ui32Idx == 2) + { + ui32Idx -= 3; + } + } + + // + // Use the first word of the resulting digest as the random number seed. + // + return(ui32A + 0x67452301); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/random.h b/utils/random.h new file mode 100644 index 0000000..a2c719e --- /dev/null +++ b/utils/random.h @@ -0,0 +1,56 @@ +//***************************************************************************** +// +// random.h - Header for random number generation functions. +// +// Copyright (c) 2011-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 Utility Library. +// +//***************************************************************************** + +#ifndef __RANDOM_H__ +#define __RANDOM_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 for the random number generator functions. +// +//***************************************************************************** +extern void RandomAddEntropy(uint32_t ui32Entropy); +extern uint32_t RandomSeed(void); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __RANDOM_H__ diff --git a/utils/ringbuf.c b/utils/ringbuf.c new file mode 100644 index 0000000..f489239 --- /dev/null +++ b/utils/ringbuf.c @@ -0,0 +1,712 @@ +//***************************************************************************** +// +// ringbuf.c - Ring buffer management utilities. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "utils/ringbuf.h" + +//***************************************************************************** +// +//! \addtogroup ringbuf_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Define NULL, if not already defined. +// +//***************************************************************************** +#ifndef NULL +#define NULL ((void *)0) +#endif + +//***************************************************************************** +// +// Change the value of a variable atomically. +// +// \param pui32Val points to the index whose value is to be modified. +// \param ui32Delta is the number of bytes to increment the index by. +// \param ui32Size is the size of the buffer the index refers to. +// +// This function is used to increment a read or write buffer index that may be +// written in various different contexts. It ensures that the +// read/modify/write sequence is not interrupted and, hence, guards against +// corruption of the variable. The new value is adjusted for buffer wrap. +// +// \return None. +// +//***************************************************************************** +static void +UpdateIndexAtomic(volatile uint32_t *pui32Val, uint32_t ui32Delta, + uint32_t ui32Size) +{ + bool bIntsOff; + + // + // Turn interrupts off temporarily. + // + bIntsOff = IntMasterDisable(); + + // + // Update the variable value. + // + *pui32Val += ui32Delta; + + // + // Correct for wrap. We use a loop here since we don't want to use a + // modulus operation with interrupts off but we don't want to fail in + // case ui32Delta is greater than ui32Size (which is extremely unlikely + // but...) + // + while(*pui32Val >= ui32Size) + { + *pui32Val -= ui32Size; + } + + // + // Restore the interrupt state + // + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is full or not. +//! +//! \param psRingBuf is the ring buffer object to empty. +//! +//! This function is used to determine whether or not a given ring buffer is +//! full. The structure is specifically to ensure that we do not see +//! warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is full or \b false otherwise. +// +//***************************************************************************** +bool +RingBufFull(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the full status of the buffer. + // + return((((ui32Write + 1) % psRingBuf->ui32Size) == ui32Read) ? true : + false); +} + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is empty or not. +//! +//! \param psRingBuf is the ring buffer object to empty. +//! +//! This function is used to determine whether or not a given ring buffer is +//! empty. The structure is specifically to ensure that we do not see +//! warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is empty or \b false otherwise. +// +//***************************************************************************** +bool +RingBufEmpty(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the empty status of the buffer. + // + return((ui32Write == ui32Read) ? true : false); +} + +//***************************************************************************** +// +//! Empties the ring buffer. +//! +//! \param psRingBuf is the ring buffer object to empty. +//! +//! Discards all data from the ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufFlush(tRingBufObject *psRingBuf) +{ + bool bIntsOff; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Set the Read/Write pointers to be the same. Do this with interrupts + // disabled to prevent the possibility of corruption of the read index. + // + bIntsOff = IntMasterDisable(); + psRingBuf->ui32ReadIndex = psRingBuf->ui32WriteIndex; + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Returns number of bytes stored in ring buffer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the number of bytes stored in the ring buffer. +//! +//! \return Returns the number of bytes stored in the ring buffer. +// +//***************************************************************************** +uint32_t +RingBufUsed(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the number of bytes contained in the ring buffer. + // + return((ui32Write >= ui32Read) ? (ui32Write - ui32Read) : + (psRingBuf->ui32Size - (ui32Read - ui32Write))); +} + +//***************************************************************************** +// +//! Returns number of bytes available in a ring buffer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the number of bytes available in the ring buffer. +//! +//! \return Returns the number of bytes available in the ring buffer. +// +//***************************************************************************** +uint32_t +RingBufFree(tRingBufObject *psRingBuf) +{ + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return((psRingBuf->ui32Size - 1) - RingBufUsed(psRingBuf)); +} + +//***************************************************************************** +// +//! Returns number of contiguous bytes of data stored in ring buffer ahead of +//! the current read pointer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the number of contiguous bytes of data available in +//! the ring buffer ahead of the current read pointer. This represents the +//! largest block of data which does not straddle the buffer wrap. +//! +//! \return Returns the number of contiguous bytes available. +// +//***************************************************************************** +uint32_t +RingBufContigUsed(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the number of contiguous bytes available. + // + return((ui32Write >= ui32Read) ? (ui32Write - ui32Read) : + (psRingBuf->ui32Size - ui32Read)); +} + +//***************************************************************************** +// +//! Returns number of contiguous free bytes available in a ring buffer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the number of contiguous free bytes ahead of the +//! current write pointer in the ring buffer. +//! +//! \return Returns the number of contiguous bytes available in the ring +//! buffer. +// +//***************************************************************************** +uint32_t +RingBufContigFree(tRingBufObject *psRingBuf) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ui32Write = psRingBuf->ui32WriteIndex; + ui32Read = psRingBuf->ui32ReadIndex; + + // + // Return the number of contiguous bytes available. + // + if(ui32Read > ui32Write) + { + // + // The read pointer is above the write pointer so the amount of free + // space is the difference between the two indices minus 1 to account + // for the buffer full condition (write index one behind read index). + // + return((ui32Read - ui32Write) - 1); + } + else + { + // + // If the write pointer is above the read pointer, the amount of free + // space is the size of the buffer minus the write index. We need to + // add a special-case adjustment if the read index is 0 since we need + // to leave 1 byte empty to ensure we can tell the difference between + // the buffer being full and empty. + // + return(psRingBuf->ui32Size - ui32Write - ((ui32Read == 0) ? 1 : 0)); + } +} + +//***************************************************************************** +// +//! Return size in bytes of a ring buffer. +//! +//! \param psRingBuf is the ring buffer object to check. +//! +//! This function returns the size of the ring buffer. +//! +//! \return Returns the size in bytes of the ring buffer. +// +//***************************************************************************** +uint32_t +RingBufSize(tRingBufObject *psRingBuf) +{ + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return(psRingBuf->ui32Size); +} + +//***************************************************************************** +// +//! Reads a single byte of data from a ring buffer. +//! +//! \param psRingBuf points to the ring buffer to be written to. +//! +//! This function reads a single byte of data from a ring buffer. +//! +//! \return The byte read from the ring buffer. +// +//***************************************************************************** +uint8_t +RingBufReadOne(tRingBufObject *psRingBuf) +{ + uint8_t ui8Temp; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(RingBufUsed(psRingBuf) != 0); + + // + // Write the data byte. + // + ui8Temp = psRingBuf->pui8Buf[psRingBuf->ui32ReadIndex]; + + // + // Increment the read index. + // + UpdateIndexAtomic(&psRingBuf->ui32ReadIndex, 1, psRingBuf->ui32Size); + + // + // Return the character read. + // + return(ui8Temp); +} + +//***************************************************************************** +// +//! Reads data from a ring buffer. +//! +//! \param psRingBuf points to the ring buffer to be read from. +//! \param pui8Data points to where the data should be stored. +//! \param ui32Length is the number of bytes to be read. +//! +//! This function reads a sequence of bytes from a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufRead(tRingBufObject *psRingBuf, uint8_t *pui8Data, uint32_t ui32Length) +{ + uint32_t ui32Temp; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + ASSERT(pui8Data != NULL); + ASSERT(ui32Length != 0); + + // + // Verify that data is available in the buffer. + // + ASSERT(ui32Length <= RingBufUsed(psRingBuf)); + + // + // Read the data from the ring buffer. + // + for(ui32Temp = 0; ui32Temp < ui32Length; ui32Temp++) + { + pui8Data[ui32Temp] = RingBufReadOne(psRingBuf); + } +} + +//***************************************************************************** +// +//! Remove bytes from the ring buffer by advancing the read index. +//! +//! \param psRingBuf points to the ring buffer from which bytes are to be +//! removed. +//! \param ui32NumBytes is the number of bytes to be removed from the buffer. +//! +//! This function advances the ring buffer read index by a given number of +//! bytes, removing that number of bytes of data from the buffer. If +//! \e ui32NumBytes is larger than the number of bytes currently in the buffer, +//! the buffer is emptied. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufAdvanceRead(tRingBufObject *psRingBuf, uint32_t ui32NumBytes) +{ + uint32_t ui32Count; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Make sure that we are not being asked to remove more data than is + // there to be removed. + // + ui32Count = RingBufUsed(psRingBuf); + ui32Count = (ui32Count < ui32NumBytes) ? ui32Count : ui32NumBytes; + + // + // Advance the buffer read index by the required number of bytes. + // + UpdateIndexAtomic(&psRingBuf->ui32ReadIndex, ui32Count, + psRingBuf->ui32Size); +} + +//***************************************************************************** +// +//! Add bytes to the ring buffer by advancing the write index. +//! +//! \param psRingBuf points to the ring buffer to which bytes have been added. +//! \param ui32NumBytes is the number of bytes added to the buffer. +//! +//! This function should be used by clients who wish to add data to the buffer +//! directly rather than via calls to RingBufWrite() or RingBufWriteOne(). It +//! advances the write index by a given number of bytes. If the +//! \e ui32NumBytes parameter is larger than the amount of free space in the +//! buffer, the read pointer will be advanced to cater for the addition. Note +//! that this will result in some of the oldest data in the buffer being +//! discarded. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufAdvanceWrite(tRingBufObject *psRingBuf, + uint32_t ui32NumBytes) +{ + uint32_t ui32Count; + bool bIntsOff; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Make sure we were not asked to add a silly number of bytes. + // + ASSERT(ui32NumBytes <= psRingBuf->ui32Size); + + // + // Determine how much free space we currently think the buffer has. + // + ui32Count = RingBufFree(psRingBuf); + + // + // Advance the buffer write index by the required number of bytes and + // check that we have not run past the read index. Note that we must do + // this within a critical section (interrupts disabled) to prevent + // race conditions that could corrupt one or other of the indices. + // + bIntsOff = IntMasterDisable(); + + // + // Update the write pointer. + // + psRingBuf->ui32WriteIndex += ui32NumBytes; + + // + // Check and correct for wrap. + // + if(psRingBuf->ui32WriteIndex >= psRingBuf->ui32Size) + { + psRingBuf->ui32WriteIndex -= psRingBuf->ui32Size; + } + + // + // Did the client add more bytes than the buffer had free space for? + // + if(ui32Count < ui32NumBytes) + { + // + // Yes - we need to advance the read pointer to ahead of the write + // pointer to discard some of the oldest data. + // + psRingBuf->ui32ReadIndex = psRingBuf->ui32WriteIndex + 1; + + // + // Correct for buffer wrap if necessary. + // + if(psRingBuf->ui32ReadIndex >= psRingBuf->ui32Size) + { + psRingBuf->ui32ReadIndex -= psRingBuf->ui32Size; + } + } + + // + // Restore interrupts if we turned them off earlier. + // + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Writes a single byte of data to a ring buffer. +//! +//! \param psRingBuf points to the ring buffer to be written to. +//! \param ui8Data is the byte to be written. +//! +//! This function writes a single byte of data into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufWriteOne(tRingBufObject *psRingBuf, uint8_t ui8Data) +{ + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(RingBufFree(psRingBuf) != 0); + + // + // Write the data byte. + // + psRingBuf->pui8Buf[psRingBuf->ui32WriteIndex] = ui8Data; + + // + // Increment the write index. + // + UpdateIndexAtomic(&psRingBuf->ui32WriteIndex, 1, psRingBuf->ui32Size); +} + +//***************************************************************************** +// +//! Writes data to a ring buffer. +//! +//! \param psRingBuf points to the ring buffer to be written to. +//! \param pui8Data points to the data to be written. +//! \param ui32Length is the number of bytes to be written. +//! +//! This function write a sequence of bytes into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufWrite(tRingBufObject *psRingBuf, uint8_t *pui8Data, + uint32_t ui32Length) +{ + uint32_t ui32Temp; + + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + ASSERT(pui8Data != NULL); + ASSERT(ui32Length != 0); + + // + // Verify that space is available in the buffer. + // + ASSERT(ui32Length <= RingBufFree(psRingBuf)); + + // + // Write the data into the ring buffer. + // + for(ui32Temp = 0; ui32Temp < ui32Length; ui32Temp++) + { + RingBufWriteOne(psRingBuf, pui8Data[ui32Temp]); + } +} + +//***************************************************************************** +// +//! Initialize a ring buffer object. +//! +//! \param psRingBuf points to the ring buffer to be initialized. +//! \param pui8Buf points to the data buffer to be used for the ring buffer. +//! \param ui32Size is the size of the buffer in bytes. +//! +//! This function initializes a ring buffer object, preparing it to store data. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufInit(tRingBufObject *psRingBuf, uint8_t *pui8Buf, + uint32_t ui32Size) +{ + // + // Check the arguments. + // + ASSERT(psRingBuf != NULL); + ASSERT(pui8Buf != NULL); + ASSERT(ui32Size != 0); + + // + // Initialize the ring buffer object. + // + psRingBuf->ui32Size = ui32Size; + psRingBuf->pui8Buf = pui8Buf; + psRingBuf->ui32WriteIndex = psRingBuf->ui32ReadIndex = 0; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/ringbuf.h b/utils/ringbuf.h new file mode 100644 index 0000000..9b1ff56 --- /dev/null +++ b/utils/ringbuf.h @@ -0,0 +1,105 @@ +//***************************************************************************** +// +// ringbuf.h - Defines and Macros for the ring buffer utilities. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __RINGBUF_H__ +#define __RINGBUF_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 used for encapsulating all the items associated with a +// ring buffer. +// +//***************************************************************************** +typedef struct +{ + // + // The ring buffer size. + // + uint32_t ui32Size; + + // + // The ring buffer write index. + // + volatile uint32_t ui32WriteIndex; + + // + // The ring buffer read index. + // + volatile uint32_t ui32ReadIndex; + + // + // The ring buffer. + // + uint8_t *pui8Buf; + +} +tRingBufObject; + +//***************************************************************************** +// +// API Function prototypes +// +//***************************************************************************** +extern bool RingBufFull(tRingBufObject *psRingBuf); +extern bool RingBufEmpty(tRingBufObject *psRingBuf); +extern void RingBufFlush(tRingBufObject *psRingBuf); +extern uint32_t RingBufUsed(tRingBufObject *psRingBuf); +extern uint32_t RingBufFree(tRingBufObject *psRingBuf); +extern uint32_t RingBufContigUsed(tRingBufObject *psRingBuf); +extern uint32_t RingBufContigFree(tRingBufObject *psRingBuf); +extern uint32_t RingBufSize(tRingBufObject *psRingBuf); +extern uint8_t RingBufReadOne(tRingBufObject *psRingBuf); +extern void RingBufRead(tRingBufObject *psRingBuf, uint8_t *pui8Data, + uint32_t ui32Length); +extern void RingBufWriteOne(tRingBufObject *psRingBuf, uint8_t ui8Data); +extern void RingBufWrite(tRingBufObject *psRingBuf, uint8_t *pui8Data, + uint32_t ui32Length); +extern void RingBufAdvanceWrite(tRingBufObject *psRingBuf, + uint32_t ui32NumBytes); +extern void RingBufAdvanceRead(tRingBufObject *psRingBuf, + uint32_t ui32NumBytes); +extern void RingBufInit(tRingBufObject *psRingBuf, uint8_t *pui8Buf, + uint32_t ui32Size); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __RINGBUF_H__ diff --git a/utils/scheduler.c b/utils/scheduler.c new file mode 100644 index 0000000..bb66c4e --- /dev/null +++ b/utils/scheduler.c @@ -0,0 +1,310 @@ +//***************************************************************************** +// +// scheduler.c - A simple task scheduler +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "inc/hw_ints.h" +#include "driverlib/systick.h" +#include "driverlib/sysctl.h" +#include "driverlib/interrupt.h" +#include "driverlib/debug.h" +#include "utils/scheduler.h" + +//***************************************************************************** +// +//! \addtogroup scheduler_api +//! @{ +// +//***************************************************************************** + +static volatile uint32_t g_ui32SchedulerTickCount; + +//***************************************************************************** +// +//! Handles the SysTick interrupt on behalf of the scheduler module. +//! +//! Applications using the scheduler module must ensure that this function is +//! hooked to the SysTick interrupt vector. +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerSysTickIntHandler(void) +{ + g_ui32SchedulerTickCount++; +} + +//***************************************************************************** +// +//! Initializes the task scheduler. +//! +//! \param ui32TicksPerSecond sets the basic frequency of the SysTick interrupt +//! used by the scheduler to determine when to run the various task functions. +//! +//! This function must be called during application startup to configure the +//! SysTick timer. This is used by the scheduler module to determine when each +//! of the functions provided in the g_psSchedulerTable array is called. +//! +//! The caller is responsible for ensuring that SchedulerSysTickIntHandler() +//! has previously been installed in the SYSTICK vector in the vector table +//! and must also ensure that interrupts are enabled at the CPU level. +//! +//! Note that this call does not start the scheduler calling the configured +//! functions. All function calls are made in the context of later calls to +//! SchedulerRun(). This call merely configures the SysTick interrupt that is +//! used by the scheduler to determine what the current system time is. +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerInit(uint32_t ui32TicksPerSecond) +{ + ASSERT(ui32TicksPerSecond); + + // + // Configure SysTick for a periodic interrupt. + // + SysTickPeriodSet(SysCtlClockGet() / ui32TicksPerSecond); + SysTickEnable(); + SysTickIntEnable(); +} + +//***************************************************************************** +// +//! Instructs the scheduler to update its task table and make calls to +//! functions needing called. +//! +//! This function must be called periodically by the client to allow the +//! scheduler to make calls to any configured task functions if it is their +//! time to be called. The call must be made at least as frequently as the +//! most frequent task configured in the g_psSchedulerTable array. +//! +//! Although the scheduler makes use of the SysTick interrupt, all calls to +//! functions configured in \e g_psSchedulerTable are made in the context of +//! SchedulerRun(). +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerRun(void) +{ + uint32_t ui32Loop; + tSchedulerTask *pi16Task; + + // + // Loop through each task in the task table. + // + for(ui32Loop = 0; ui32Loop < g_ui32SchedulerNumTasks; ui32Loop++) + { + // + // Get a pointer to the task information. + // + pi16Task = &g_psSchedulerTable[ui32Loop]; + + // + // Is this task active and, if so, is it time to call it's function? + // + if(pi16Task->bActive && + (SchedulerElapsedTicksGet(pi16Task->ui32LastCall) >= + pi16Task->ui32FrequencyTicks)) + { + // + // Remember the timestamp at which we make the function call. + // + pi16Task->ui32LastCall = g_ui32SchedulerTickCount; + + // + // Call the task function, passing the provided parameter. + // + pi16Task->pfnFunction(pi16Task->pvParam); + } + } +} + +//***************************************************************************** +// +//! Enables a task and allows the scheduler to call it periodically. +//! +//! \param ui32Index is the index of the task which is to be enabled in the +//! global \e g_psSchedulerTable array. +//! \param bRunNow is \b true if the task is to be run on the next call to +//! SchedulerRun() or \b false if one whole period is to elapse before the task +//! is run. +//! +//! This function marks one of the configured tasks as enabled and causes +//! SchedulerRun() to call that task periodically. The caller may choose to +//! have the enabled task run for the first time on the next call to +//! SchedulerRun() or to wait one full task period before making the first +//! call. +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerTaskEnable(uint32_t ui32Index, bool bRunNow) +{ + // + // Is the task index passed valid? + // + if(ui32Index < g_ui32SchedulerNumTasks) + { + // + // Yes - mark the task as active. + // + g_psSchedulerTable[ui32Index].bActive = true; + + // + // Set the last call time to ensure that the function is called either + // next time the scheduler is run or after the desired number of ticks + // depending upon the value of the bRunNow parameter. + // + if(bRunNow) + { + // + // Cause the task to run on the next call to SchedulerRun(). + // + g_psSchedulerTable[ui32Index].ui32LastCall = + (g_ui32SchedulerTickCount - + g_psSchedulerTable[ui32Index].ui32FrequencyTicks); + } + else + { + // + // Cause the task to run after one full time period. + // + g_psSchedulerTable[ui32Index].ui32LastCall = + g_ui32SchedulerTickCount; + } + } +} + +//***************************************************************************** +// +//! Disables a task and prevents the scheduler from calling it. +//! +//! \param ui32Index is the index of the task which is to be disabled in the +//! global \e g_psSchedulerTable array. +//! +//! This function marks one of the configured tasks as inactive and prevents +//! SchedulerRun() from calling it. The task may be reenabled by calling +//! SchedulerTaskEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SchedulerTaskDisable(uint32_t ui32Index) +{ + // + // Is the task index passed valid? + // + if(ui32Index < g_ui32SchedulerNumTasks) + { + // + // Yes - mark the task as inactive. + // + g_psSchedulerTable[ui32Index].bActive = false; + } +} + +//***************************************************************************** +// +//! Returns the current system time in ticks since power on. +//! +//! This function may be called by a client to retrieve the current system +//! time. The value returned is a count of ticks elapsed since the system +//! last booted. +//! +//! \return Tick count since last boot. +// +//***************************************************************************** +uint32_t +SchedulerTickCountGet(void) +{ + return(g_ui32SchedulerTickCount); +} + +//***************************************************************************** +// +//! Returns the number of ticks elapsed since the provided tick count. +//! +//! \param ui32TickCount is the tick count from which to determine the elapsed +//! time. +//! +//! This function may be called by a client to determine how much time has +//! passed since a particular tick count provided in the \e ui32TickCount +//! parameter. This function takes into account wrapping of the global tick +//! counter and assumes that the provided tick count always represents a time +//! in the past. The returned value will, of course, be wrong if the tick +//! counter has wrapped more than once since the passed \e ui32TickCount. As a +//! result, please do not use this function if you are dealing with timeouts +//! of 497 days or longer (assuming you use a 10mS tick period). +//! +//! \return The number of ticks elapsed since the provided tick count. +// +//***************************************************************************** +uint32_t +SchedulerElapsedTicksGet(uint32_t ui32TickCount) +{ + // + // Determine the calculation based upon whether the global tick count has + // wrapped since the passed ui32TickCount. + // + return(SchedulerElapsedTicksCalc(ui32TickCount, g_ui32SchedulerTickCount)); +} + +//***************************************************************************** +// +//! Returns the number of ticks elapsed between two times. +//! +//! \param ui32TickStart is the system tick count for the start of the period. +//! \param ui32TickEnd is the system tick count for the end of the period. +//! +//! This function may be called by a client to determine the number of ticks +//! which have elapsed between provided starting and ending tick counts. The +//! function takes into account wrapping cases where the end tick count is +//! lower than the starting count assuming that the ending tick count always +//! represents a later time than the starting count. +//! +//! \return The number of ticks elapsed between the provided start and end +//! counts. +// +//***************************************************************************** +uint32_t +SchedulerElapsedTicksCalc(uint32_t ui32TickStart, uint32_t ui32TickEnd) +{ + return((ui32TickEnd > ui32TickStart) ? (ui32TickEnd - ui32TickStart) : + ((0xFFFFFFFF - ui32TickStart) + ui32TickEnd + 1)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/scheduler.h b/utils/scheduler.h new file mode 100644 index 0000000..79ae1e2 --- /dev/null +++ b/utils/scheduler.h @@ -0,0 +1,140 @@ +//***************************************************************************** +// +// scheduler.h - Public header for the simple timed function scheduler module. +// +// 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 Utility Library. +// +//***************************************************************************** +#ifndef __SCHEDULER_H__ +#define __SCHEDULER_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup scheduler_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototype of a function that the scheduler can call periodically. +// +//***************************************************************************** +typedef void (*tSchedulerFunction)(void *pvParam); + +//***************************************************************************** +// +//! The structure defining a function which the scheduler will call +//! periodically. +// +//***************************************************************************** +typedef struct +{ + // + //! A pointer to the function which is to be called periodically by the + //! scheduler. + // + void (*pfnFunction)(void *); + + // + //! The parameter which is to be passed to this function when it is called. + // + void *pvParam; + + // + //! The frequency the function is to be called expressed in terms of system + //! ticks. If this value is 0, the function will be called on every call + //! to SchedulerRun. + // + uint32_t ui32FrequencyTicks; + + // + //! Tick count when this function was last called. This field is updated + //! by the scheduler. + // + uint32_t ui32LastCall; + + // + //! A flag indicating whether or not this task is active. If true, the + //! function will be called periodically. If false, the function is + //! disabled and will not be called. + // + bool bActive; +} +tSchedulerTask; + +//***************************************************************************** +// +//! This global table must be populated by the client and contains information +//! on each function that the scheduler is to call. +// +//***************************************************************************** +extern tSchedulerTask g_psSchedulerTable[]; + +//***************************************************************************** +// +//! This global variable must be exported by the client. It must contain the +//! number of entries in the g_psSchedulerTable array. +// +//***************************************************************************** +extern uint32_t g_ui32SchedulerNumTasks; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Public function prototypes +// +//***************************************************************************** +extern void SchedulerSysTickIntHandler(void); +extern void SchedulerInit(uint32_t ui32TicksPerSecond); +extern void SchedulerRun(void); +extern void SchedulerTaskEnable(uint32_t ui32Index, bool bRunNow); +extern void SchedulerTaskDisable(uint32_t ui32Index); +extern uint32_t SchedulerTickCountGet(void); +extern uint32_t SchedulerElapsedTicksGet(uint32_t ui32TickCount); +extern uint32_t SchedulerElapsedTicksCalc(uint32_t ui32TickStart, + uint32_t ui32TickEnd); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __ SCHEDULER_H_ diff --git a/utils/sine.c b/utils/sine.c new file mode 100644 index 0000000..52cd222 --- /dev/null +++ b/utils/sine.c @@ -0,0 +1,126 @@ +//***************************************************************************** +// +// sine.c - Fixed point sine trigonometric function. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include "utils/sine.h" + +//***************************************************************************** +// +//! \addtogroup sine_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// A table of the value of the sine function for the first ninety degrees with +// 129 entries (that is, [0] = 0 degrees, [128] = 90 degrees). Each entry is +// in 0.16 fixed point notation. +// +//***************************************************************************** +static const uint16_t g_pui16FixedSineTable[] = +{ + 0x0000, 0x0324, 0x0648, 0x096C, 0x0C8F, 0x0FB2, 0x12D5, 0x15F6, 0x1917, + 0x1C37, 0x1F56, 0x2273, 0x2590, 0x28AA, 0x2BC4, 0x2EDB, 0x31F1, 0x3505, + 0x3817, 0x3B26, 0x3E33, 0x413E, 0x4447, 0x474D, 0x4A50, 0x4D50, 0x504D, + 0x5347, 0x563E, 0x5931, 0x5C22, 0x5F0E, 0x61F7, 0x64DC, 0x67BD, 0x6A9B, + 0x6D74, 0x7049, 0x7319, 0x75E5, 0x78AD, 0x7B70, 0x7E2E, 0x80E7, 0x839C, + 0x864B, 0x88F5, 0x8B9A, 0x8E39, 0x90D3, 0x9368, 0x95F6, 0x987F, 0x9B02, + 0x9D7F, 0x9FF6, 0xA267, 0xA4D2, 0xA736, 0xA994, 0xABEB, 0xAE3B, 0xB085, + 0xB2C8, 0xB504, 0xB73A, 0xB968, 0xBB8F, 0xBDAE, 0xBFC7, 0xC1D8, 0xC3E2, + 0xC5E4, 0xC7DE, 0xC9D1, 0xCBBB, 0xCD9F, 0xCF7A, 0xD14D, 0xD318, 0xD4DB, + 0xD695, 0xD848, 0xD9F2, 0xDB94, 0xDD2D, 0xDEBE, 0xE046, 0xE1C5, 0xE33C, + 0xE4AA, 0xE60F, 0xE76B, 0xE8BF, 0xEA09, 0xEB4B, 0xEC83, 0xEDB2, 0xEED8, + 0xEFF5, 0xF109, 0xF213, 0xF314, 0xF40B, 0xF4FA, 0xF5DE, 0xF6BA, 0xF78B, + 0xF853, 0xF912, 0xF9C7, 0xFA73, 0xFB14, 0xFBAC, 0xFC3B, 0xFCBF, 0xFD3A, + 0xFDAB, 0xFE13, 0xFE70, 0xFEC4, 0xFF0E, 0xFF4E, 0xFF84, 0xFFB1, 0xFFD3, + 0xFFEC, 0xFFFB, 0xFFFF +}; + +//***************************************************************************** +// +//! Computes an approximation of the sine of the input angle. +//! +//! \param ui32Angle is an angle expressed as a 0.32 fixed-point value that is +//! the percentage of the way around a circle. +//! +//! This function computes the sine for the given input angle. The angle is +//! specified in 0.32 fixed point format, and is therefore always between 0 and +//! 360 degrees, inclusive of 0 and exclusive of 360. +//! +//! \return Returns the sine of the angle, in 16.16 fixed point format. +// +//***************************************************************************** +int32_t +sine(uint32_t ui32Angle) +{ + uint32_t ui32Idx; + + // + // Add 0.5 to the angle. Since only the upper 9 bits are used to compute + // the sine value, adding one to the tenth bit is 0.5 from the point of + // view of the sine table. + // + ui32Angle += 0x00400000; + + // + // Get the index into the sine table from bits 30:23. + // + ui32Idx = (ui32Angle >> 23) & 255; + + // + // If bit 30 is set, the angle is between 90 and 180 or 270 and 360. In + // these cases, the sine value is decreasing from one instead of increasing + // from zero. The indexing into the table needs to be reversed. + // + if(ui32Angle & 0x40000000) + { + ui32Idx = 256 - ui32Idx; + } + + // + // Get the value of the sine. + // + ui32Idx = g_pui16FixedSineTable[ui32Idx]; + + // + // If bit 31 is set, the angle is between 180 and 360. In this case, the + // sine value is negative; otherwise it is positive. + // + if(ui32Angle & 0x80000000) + { + return(0 - ui32Idx); + } + else + { + return(ui32Idx); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/sine.h b/utils/sine.h new file mode 100644 index 0000000..c3f8c51 --- /dev/null +++ b/utils/sine.h @@ -0,0 +1,85 @@ +//***************************************************************************** +// +// sine.h - Prototypes for the fixed point sine trigonometric function. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __SINE_H__ +#define __SINE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup sine_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Computes an approximation of the cosine of the input angle. +//! +//! \param ui32Angle is an angle expressed as a 0.32 fixed-point value that is +//! the percentage of the way around a circle. +//! +//! This function computes the cosine for the given input angle. The angle is +//! specified in 0.32 fixed point format, and is therefore always between 0 and +//! 360 degrees, inclusive of 0 and exclusive of 360. +//! +//! \return Returns the cosine of the angle, in 16.16 fixed point format. +// +//***************************************************************************** +#define cosine(ui32Angle) sine((ui32Angle) + 0x40000000) + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototype for the fixed point sine function. +// +//***************************************************************************** +extern int32_t sine(uint32_t ui32Angle); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SINE_H__ diff --git a/utils/smbus.c b/utils/smbus.c new file mode 100644 index 0000000..8ed9c3a --- /dev/null +++ b/utils/smbus.c @@ -0,0 +1,5173 @@ +//***************************************************************************** +// +// smbus.c - SMBus protocol layer API. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_i2c.h" +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/i2c.h" +#include "driverlib/sw_crc.h" +#include "driverlib/sysctl.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/udma.h" +#include "utils/smbus.h" + +//***************************************************************************** +// +//! \addtogroup smbus_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states for the master and slave interrupt handler state machines. +// +//***************************************************************************** +#define SMBUS_STATE_IDLE 0 +#define SMBUS_STATE_SLAVE_POST_COMMAND 1 +#define SMBUS_STATE_WRITE_BLOCK_SIZE 2 +#define SMBUS_STATE_WRITE_NEXT 3 +#define SMBUS_STATE_WRITE_FINAL 4 +#define SMBUS_STATE_WRITE_DONE 5 +#define SMBUS_STATE_READ_ONE 6 +#define SMBUS_STATE_READ_FIRST 7 +#define SMBUS_STATE_READ_BLOCK_SIZE 8 +#define SMBUS_STATE_READ_NEXT 9 +#define SMBUS_STATE_READ_FINAL 10 +#define SMBUS_STATE_READ_WAIT 11 +#define SMBUS_STATE_READ_PEC 12 +#define SMBUS_STATE_READ_DONE 13 +#define SMBUS_STATE_READ_ERROR_STOP 14 + +//***************************************************************************** +// +// Status flags for various instance-specific tasks. +// +//***************************************************************************** +#define FLAG_PEC 0 +#define FLAG_PROCESS_CALL 1 +#define FLAG_BLOCK_TRANSFER 2 +#define FLAG_TRANSFER_IN_PROGRESS 3 +#define FLAG_RAW_I2C 4 +#define FLAG_ADDRESS_RESOLVED 5 +#define FLAG_ADDRESS_VALID 6 +#define FLAG_ARP 7 +//***************************************************************************** +// +//! Enables Packet Error Checking (PEC). +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function enables the transmission and checking of a PEC byte in SMBus +//! transactions. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusPECEnable(tSMBus *psSMBus) +{ + // + // Set the PEC flag in the configuration structure. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 1; +} + +//***************************************************************************** +// +//! Disables Packet Error Checking (PEC). +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function disables the transmission and checking of a PEC byte in SMBus +//! transactions. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusPECDisable(tSMBus *psSMBus) +{ + // + // Clear the PEC flag in the configuration structure. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; +} + +//***************************************************************************** +// +//! Sets the ARP flag in the configuration structure. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sets the Address Resolution Protocol (ARP) flag in the +//! configuration structure. This flag can be used to track the state of a +//! device during the ARP process. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusARPEnable(tSMBus *psSMBus) +{ + // + // Set the ARP flag in the configuration structure. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_ARP) = 1; +} + +//***************************************************************************** +// +//! Clears the ARP flag in the configuration structure. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function clears the Address Resolution Protocol (ARP) flag in the +//! configuration structure. This flag can be used to track the state of a +//! device during the ARP process. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusARPDisable(tSMBus *psSMBus) +{ + // + // Clear the ARP flag in the configuration structure. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_ARP) = 0; +} + +//***************************************************************************** +// +//! Returns the number of bytes in the receive buffer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function returns the number of bytes in the active receive buffer. +//! It can be used to determine how many bytes have been received in the slave +//! receive or master block read configurations. +//! +//! \return Number of bytes in the buffer. +// +//***************************************************************************** +uint8_t +SMBusRxPacketSizeGet(tSMBus *psSMBus) +{ + // + // Return the number of bytes received. + // + return(psSMBus->ui8RxIndex); +} + +//***************************************************************************** +// +//! Returns the state of an SMBus transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function returns the status of an SMBus transaction. It can be used +//! to determine whether a transfer is ongoing or complete. +//! +//! \return Returns \b SMBUS_TRANSFER_IN_PROGRESS if transfer is ongoing, or +//! \b SMBUS_TRANSFER_COMPLETE if transfer has completed. +// +//***************************************************************************** +tSMBusStatus +SMBusStatusGet(tSMBus *psSMBus) +{ + // + // Check to see if there is an ongoing transfer. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS)) + { + // + // If the flag is set, return in progress status. + // + return(SMBUS_TRANSFER_IN_PROGRESS); + } + + // + // If the transfer complete flag is cleared, transfer is done. + // + else + { + // + // If the flag isn't set, return complete status. + // + return(SMBUS_TRANSFER_COMPLETE); + } +} + +//***************************************************************************** +// +//! Encodes a UDID structure and address into SMBus-transferable byte order. +//! +//! \param pUDID specifies the structure to encode. +//! \param ui8Address specifies the address to send with the UDID (byte 17). +//! \param pui8Data specifies the location of the destination data buffer. +//! +//! This function takes a tSMBusUDID structure and re-orders the bytes so that +//! it can be transferred on the bus. The destination data buffer must contain +//! at least 17 bytes. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusARPUDIDPacketEncode(tSMBusUDID *pUDID, uint8_t ui8Address, + uint8_t *pui8Data) +{ + // + // Place data from the UDID structure and address into the data buffer + // using the correct MSB->LSB + address order. + // + pui8Data[0] = pUDID->ui8DeviceCapabilities; + pui8Data[1] = pUDID->ui8Version; + pui8Data[2] = (uint8_t)((pUDID->ui16VendorID & 0xff00) >> 8); + pui8Data[3] = (uint8_t)(pUDID->ui16VendorID & 0x00ff); + pui8Data[4] = (uint8_t)((pUDID->ui16DeviceID & 0xff00) >> 8); + pui8Data[5] = (uint8_t)(pUDID->ui16DeviceID & 0x00ff); + pui8Data[6] = (uint8_t)((pUDID->ui16Interface & 0xff00) >> 8); + pui8Data[7] = (uint8_t)(pUDID->ui16Interface & 0x00ff); + pui8Data[8] = (uint8_t)((pUDID->ui16SubSystemVendorID & 0xff00) >> 8); + pui8Data[9] = (uint8_t)(pUDID->ui16SubSystemVendorID & 0x00ff); + pui8Data[10] = (uint8_t)((pUDID->ui16SubSystemDeviceID & 0xff00) >> 8); + pui8Data[11] = (uint8_t)(pUDID->ui16SubSystemDeviceID & 0x00ff); + pui8Data[12] = (uint8_t)((pUDID->ui32VendorSpecificID & 0xff000000) >> + 24); + pui8Data[13] = (uint8_t)((pUDID->ui32VendorSpecificID & 0x00ff0000) >> + 16); + pui8Data[14] = (uint8_t)((pUDID->ui32VendorSpecificID & 0x0000ff00) >> + 8); + pui8Data[15] = (uint8_t)(pUDID->ui32VendorSpecificID & 0x000000ff); + pui8Data[16] = ui8Address; +} + +//***************************************************************************** +// +//! Decodes an SMBus packet into a UDID structure and address. +//! +//! \param pUDID specifies the structure that is updated with new data. +//! \param pui8Address specifies the location of the variable that holds the +//! the address sent with the UDID (byte 17). +//! \param pui8Data specifies the location of the source data. +//! +//! This function takes a data buffer and decodes it into a tSMBusUDID +//! structure and an address variable. It is assumed that there are 17 bytes +//! in the data buffer. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusARPUDIDPacketDecode(tSMBusUDID *pUDID, uint8_t *pui8Address, + uint8_t *pui8Data) +{ + // + // Populate the UDID structure with data from the input data buffer. + // + pUDID->ui8DeviceCapabilities = pui8Data[0]; + pUDID->ui8Version = pui8Data[1]; + pUDID->ui16VendorID = (uint16_t)((pui8Data[2] << 8) | pui8Data[3]); + pUDID->ui16DeviceID = (uint16_t)((pui8Data[4] << 8) | pui8Data[5]); + pUDID->ui16Interface = (uint16_t)((pui8Data[6] << 8) | pui8Data[7]); + pUDID->ui16SubSystemVendorID = (uint16_t)((pui8Data[8] << 8) | + pui8Data[9]); + pUDID->ui16SubSystemDeviceID = (uint16_t)((pui8Data[10] << 8) | + pui8Data[11]); + pUDID->ui32VendorSpecificID = (uint32_t)((pui8Data[12] << 24) | + (pui8Data[13] << 16) | + (pui8Data[14] << 8) | + pui8Data[15]); + + // + // Populate the address. + // + *pui8Address = pui8Data[16]; +} + +//***************************************************************************** +// +//! Initiates a master Quick Command transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param bData is the value of the single data bit sent to the slave. +//! +//! Quick Command is an SMBus protocol that sends a single data bit using the +//! I2C R/S bit. This function issues a single I2C transfer with the slave +//! address and data bit. +//! +//! This protocol does not support PEC. The PEC flag is explicitly cleared +//! within this function, so if PEC is enabled prior to calling it, it must +//! be re-enabled afterwards. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterQuickCommand(tSMBus *psSMBus, uint8_t ui8TargetAddress, + bool bData) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8TxSize = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // This protocol does NOT support PEC, so the flag must be cleared. If + // PEC is needed again after this transaction, it should be explicitly + // enabled again. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Initialize the buffer index to 0 and the interrupt state machine to + // the appropriate state so that there is a known starting point + // for each transaction. + // + psSMBus->ui8TxIndex = 0; + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, bData); + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, I2C_MASTER_CMD_QUICK_COMMAND); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Host Notify transfer to the SMBus Host. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8OwnSlaveAddress specifies the peripheral's own slave address. +//! \param pui8Data is a pointer to the two byte data payload. +//! +//! The Host Notify protocol is used by SMBus slaves to alert the bus Host +//! about an event. Most slave devices that operate in this environment only +//! become a bus master when this packet type is used. Host Notify always +//! sends two data bytes to the host along with the peripheral's own slave +//! address so that the Host knows which peripheral requested the Host's +//! attention. +//! +//! This protocol does not support PEC. The PEC flag is explicitly cleared +//! within this function, so if PEC is enabled prior to calling it, it must +//! be re-enabled afterwards. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterHostNotify(tSMBus *psSMBus, uint8_t ui8OwnSlaveAddress, + uint8_t *pui8Data) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = SMBUS_ADR_HOST; + psSMBus->pui8TxBuffer = pui8Data; + psSMBus->ui8TxSize = 2; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // This protocol does NOT support PEC, so the flag must be cleared. If + // PEC is needed again after this transaction, it should be explicitly + // enabled again. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Initialize the buffer index to 0 and the interrupt state machine to + // the appropriate state so that there is a known starting point + // for each transaction. + // + psSMBus->ui8TxIndex = 0; + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, ui8OwnSlaveAddress); + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Send Byte transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Data is the data byte to send to the slave. +//! +//! The Send Byte protocol is a basic SMBus protocol that sends a single data +//! byte to the slave. Unlike most of the other SMBus protocols, Send Byte +//! does not send a ``command'' byte before the data payload and is intended +//! for basic communication. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterByteSend(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Data) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Data; + psSMBus->pui8TxBuffer = &ui8Data; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the data byte on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, ui8Data); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the data to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->pui8TxBuffer[0], + 1); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + } + else + { + // + // Update the state machine. Since it's the only byte being sent, + // the state machine's next state is idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, I2C_MASTER_CMD_SINGLE_SEND); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Receive Byte transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8Data is a pointer to the location to store the received data +//! byte. +//! +//! The Receive Byte protocol is a basic SMBus protocol that receives a single +//! data byte from the slave. Unlike most of the other SMBus protocols, +//! Receive Byte does not send a ``command'' byte before the data payload and +//! is intended for basic communication. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterByteReceive(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8Data) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->pui8RxBuffer = pui8Data; + psSMBus->ui8RxSize = 1; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, true); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // set the R/S bit to '1' for the CRC calculation. + // + ui8TempData = ((psSMBus->ui8TargetSlaveAddress << 1) & 0xfe) | 1; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the read operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_START); + } + else + { + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the read operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_SINGLE_RECEIVE); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Write Byte or Write Word transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data payload. +//! \param pui8Data is a pointer to the transmit data buffer. +//! \param ui8Size is the number of bytes to send to the slave. +//! +//! This function supports both the Write Byte and Write Word protocols. The +//! amount of data to send is user defined, but limited to 1 or 2 bytes. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, +//! \b SMBUS_DATA_SIZE_ERROR if ui8Size is greater than 2, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterByteWordWrite(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8Data, + uint8_t ui8Size) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // If more than 2 bytes are requested, indicate error. + // + if(ui8Size > 2) + { + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8TxBuffer = pui8Data; + psSMBus->ui8TxSize = ui8Size; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Initialize the buffer index to 0 and the interrupt state machine to + // the appropriate state so that there is a known starting point + // for each transaction. + // + psSMBus->ui8TxIndex = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Add the data array to the calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + psSMBus->pui8TxBuffer, + psSMBus->ui8TxSize); + + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + } + else + { + // + // If only one byte to send, move to the final state. + // + if(ui8Size == 1) + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + else + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + } + } + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Read Byte or Read Word transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8Data is a pointer to the receive data buffer. +//! \param ui8Size is the number of bytes to receive from the slave. +//! +//! This function supports both the Read Byte and Read Word protocols. The +//! amount of data to receive is user defined, but limited to 1 or 2 bytes. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, +//! \b SMBUS_DATA_SIZE_ERROR if ui8Size is greater than 2, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterByteWordRead(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8Data, + uint8_t ui8Size) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // If more than 2 bytes are requested, indicate error. + // + if(ui8Size > 2) + { + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8RxBuffer = pui8Data; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = ui8Size; + psSMBus->ui8CalculatedCRC = 0; + + // + // Clear the block transfer, process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // set the R/S bit to '1' for the CRC calculation. + // + ui8TempData = psSMBus->ui8TargetSlaveAddress << 1; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + } + else + { + // + // Update the state machine. + // + if(psSMBus->ui8RxSize == 2) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + } + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_ONE; + } + } + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Block Write transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8Data is a pointer to the transmit data buffer. +//! \param ui8Size is the number of bytes to send to the slave. +//! +//! This function supports the Block Write protocol. The amount of data sent +//! to the slave is user defined, but limited to 32 bytes per the SMBus spec. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, +//! \b SMBUS_DATA_SIZE_ERROR if ui8Size is greater than 32, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterBlockWrite(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8Data, + uint8_t ui8Size) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // If more than 32 bytes are requested, indicate error. + // + if(ui8Size > 32) + { + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8TxBuffer = pui8Data; + psSMBus->ui8TxSize = ui8Size; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Set the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1; + + // + // Clear the process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Initialize the buffer index to 0 and the interrupt state machine to + // the appropriate state so that there is a known starting point + // for each transaction. + // + psSMBus->ui8TxIndex = 0; + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Add the size to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8TxSize, 1); + + // + // Add the data array to the calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + psSMBus->pui8TxBuffer, + psSMBus->ui8TxSize); + } + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Write the first byte of the data. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_BLOCK_SIZE; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Block Read transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8Data is a pointer to the receive data buffer. +//! +//! This function supports the Block Read protocol. The amount of data read +//! is defined by the slave device, but should never exceed 32 bytes per the +//! SMBus spec. The receive size is the first data byte returned by the slave, +//! so this function assumes a size of 3 until the actual number is sent by +//! the slave. In the application interrupt handler, SMBusRxPacketSizeGet() +//! can be used to obtain the amount of data sent by the slave. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterBlockRead(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8Data) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8RxBuffer = pui8Data; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8CalculatedCRC = 0; + + // + // Set the block transfer flag.. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1; + + // + // Clear the process call and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Initially set the RX size to 3 to make the state machine work. + // The slave will respond with the actual size of the transfer in the + // first byte and that data will replace this initial value. + // + psSMBus->ui8RxSize = 3; + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // set the R/S bit to '1' for the CRC calculation. + // + ui8TempData = psSMBus->ui8TargetSlaveAddress << 1; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + } + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Process Call transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8TxData is a pointer to the transmit data buffer. +//! \param pui8RxData is a pointer to the receive data buffer. +//! +//! This function supports the Process Call protocol. The amount of data sent +//! to and received from the slave is fixed to 2 bytes per direction (2 sent, +//! 2 received). +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterProcessCall(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8TxData, + uint8_t *pui8RxData) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8TxBuffer = pui8TxData; + psSMBus->pui8RxBuffer = pui8RxData; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8TxSize = 2; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = 2; + psSMBus->ui8CalculatedCRC = 0; + + // + // Set the process call flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1; + + // + // Clear the block transfer and raw I2C flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Add the data array to the calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + psSMBus->pui8TxBuffer, + psSMBus->ui8TxSize); + } + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a master Block Process Call transfer to an SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param ui8Command is the command byte sent before the data is requested. +//! \param pui8TxData is a pointer to the transmit data buffer. +//! \param ui8TxSize is the number of bytes to send to the slave. +//! \param pui8RxData is a pointer to the receive data buffer. +//! +//! This function supports the Block Write/Block Read Process Call protocol. +//! The amount of data sent to the slave is user defined but limited to 32 data +//! bytes. The amount of data read is defined by the slave device, but should +//! never exceed 32 bytes per the SMBus spec. The receive size is the first +//! data byte returned by the slave, so the actual size is populated in +//! SMBusMasterISRProcess(). In the application interrupt handler, +//! SMBusRxPacketSizeGet() can be used to obtain the amount of data sent by +//! the slave. +//! +//! This protocol supports the optional PEC byte for error checking. To use +//! PEC, SMBusPECEnable() must be called before this function. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, +//! \b SMBUS_DATA_SIZE_ERROR if ui8TxSize is greater than 32, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterBlockProcessCall(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t ui8Command, uint8_t *pui8TxData, + uint8_t ui8TxSize, uint8_t *pui8RxData) +{ + uint8_t ui8TempData; + + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // If more than 32 bytes are requested, indicate error. + // + if(ui8TxSize > 32) + { + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->ui8CurrentCommand = ui8Command; + psSMBus->pui8TxBuffer = pui8TxData; + psSMBus->pui8RxBuffer = pui8RxData; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8TxSize = ui8TxSize; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = 3; + psSMBus->ui8CalculatedCRC = 0; + + // + // Set the process call and block transfer flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1; + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1; + + // + // Clear the raw I2C flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; + + // + // Calculate the CRC for PEC (if used). + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Place the target slave address into a temporary data variable and + // make sure the R/S bit is set to '0' for the CRC calculation. + // + ui8TempData = (psSMBus->ui8TargetSlaveAddress << 1) & 0xfe; + + // + // Start off by calculating the CRC of the target slave address with + // an initial value of 0. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(0, &ui8TempData, 1); + + // + // Add the command to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, + 1); + + // + // Add the size to the running CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8TxSize, 1); + + // + // Add the data array to the calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + psSMBus->pui8TxBuffer, + psSMBus->ui8TxSize); + } + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the SMBus command code on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8CurrentCommand); + + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_BLOCK_SIZE; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a ``raw'' I2C write transfer to a slave device. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8Data is a pointer to the transmit data buffer. +//! \param ui8Size is the number of bytes to send to the slave. +//! +//! This function sends a user-defined number of bytes to an I2C slave without +//! using an SMBus protocol. The data size is only limited to the size of the +//! ui8Size variable, which is an unsigned character (8 bits, value of 255). +//! +//! Because this function uses ``raw'' I2C, PEC is not supported. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterI2CWrite(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8Data, uint8_t ui8Size) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->pui8TxBuffer = pui8Data; + psSMBus->ui8TxSize = ui8Size; + psSMBus->ui8TxIndex = 1; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + + // + // PEC is not supported by raw I2C transfers, so force it to be disabled. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Clear the block transfer and process call flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + + // + // Set the raw I2C flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Put the first byte on the bus. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->pui8TxBuffer[0]); + + // + // Choose what to do based on the transmit size. + // + if(ui8Size == 1) + { + // + // Update the state machine. Since it's the only byte being sent, + // the state machine's next state is idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, I2C_MASTER_CMD_SINGLE_SEND); + } + else if(ui8Size == 2) + { + // + // If there are only 2 bytes to send just jump to the final write + // state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + } + else + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a ``raw'' I2C read transfer to a slave device. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8Data is a pointer to the receive data buffer. +//! \param ui8Size is the number of bytes to send to the slave. +//! +//! This function receives a user-defined number of bytes from an I2C slave +//! without using an SMBus protocol. The data size is only limited to the size +//! of the ui8Size variable, which is an unsigned character (8 bits, value of +//! 255). +//! +//! Because this function uses ``raw'' I2C, PEC is not supported. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterI2CRead(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8Data, uint8_t ui8Size) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->pui8RxBuffer = pui8Data; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = ui8Size; + + // + // PEC is not supported by raw I2C transfers, so force it to be disabled. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Clear the block transfer and process call flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + + // + // Set the raw I2C flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, true); + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Choose what to do based on the receive size. + // + if(ui8Size == 1) + { + // + // Update the state machine. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + } + else if(ui8Size == 2) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + else + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + } + + if(ui8Size == 1) + { + // + // Start the single receive. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_SINGLE_RECEIVE); + } + else + { + // + // Start the burst receive. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_START); + } + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Initiates a ``raw'' I2C write-read transfer to a slave device. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8TxData is a pointer to the transmit data buffer. +//! \param ui8TxSize is the number of bytes to send to the slave. +//! \param pui8RxData is a pointer to the receive data buffer. +//! \param ui8RxSize is the number of bytes to receive from the slave. +//! +//! This function initiates a write-read transfer to an I2C slave without using +//! an SMBus protocol. The user-defined number of bytes is written to the +//! slave first, followed by the reception of the user-defined number of bytes. +//! The transmit and receive data sizes are only limited to the size of the +//! ui8TxSize and ui8RxSize variables, which are unsigned characters (8 bits, +//! value of 255). +//! +//! Because this function uses ``raw'' I2C, PEC is not supported. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterI2CWriteRead(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8TxData, uint8_t ui8TxSize, + uint8_t *pui8RxData, uint8_t ui8RxSize) +{ + // + // Make sure that the peripheral is not currently active. + // + if(MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_PERIPHERAL_BUSY); + } + + // + // Update the configuration structure with the data for this transfer. + // + psSMBus->ui8TargetSlaveAddress = ui8TargetAddress; + psSMBus->pui8TxBuffer = pui8TxData; + psSMBus->pui8RxBuffer = pui8RxData; + psSMBus->ui8TxIndex = 1; + psSMBus->ui8TxSize = ui8TxSize; + psSMBus->ui8RxIndex = 0; + psSMBus->ui8RxSize = ui8RxSize; + + // + // PEC is not supported by raw I2C transfers, so force it to be disabled. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC) = 0; + + // + // Set the process call flag. Even though this is technically not an SMBus + // process call, this flag is used in the interrupt state machine for + // the bus turn around. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1; + + // + // Clear the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + + // + // Set the raw I2C flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1; + + // + // Set the slave address and R/S bit. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, false); + + // + // Write the first byte of the data. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->pui8TxBuffer[0]); + + // + // Choose what to do based on the transmit size. + // + if(ui8TxSize == 1) + { + // + // Move to the read first state for the turn around. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + } + else if(ui8TxSize == 2) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + else + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + } + + // + // Make sure that the bus is idle. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + return(SMBUS_BUS_BUSY); + } + + // + // Initiate the write operation. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Return to the caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! \internal +//! Sends a ``general'' Get UDID packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the receive data buffer. +//! +//! This function sends a ``general'' Get UDID packet, used during Address +//! Resolution Protocol (ARP). Since SMBus requires that data bytes be +//! transmitted in a certain order, the raw data in the pui8Data needs to be +//! treated as such. To put the data in a known order, use +//! SMBusARPUDIDPacketDecode(). +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPGetUDIDGen(tSMBus *psSMBus, uint8_t *pui8Data) +{ + // + // Use the block read protocol to receive the UDID. + // + return(SMBusMasterBlockRead(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + SMBUS_CMD_ARP_GET_UDID, pui8Data)); +} + +//***************************************************************************** +// +//! \internal +//! Sends a ``directed'' Get UDID packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8TargetAddress specifies the slave address of the target device. +//! \param pui8Data is a pointer to the receive data buffer. +//! +//! This function sends a ``directed'' Get UDID packet, used during Address +//! Resolution Protocol (ARP). A directed packet differs from a general packet +//! in that it targets a specific slave device. Since SMBus requires that data +//! bytes be transmitted in a certain order, the raw data in the pui8Data needs +//! to be treated as such. To put the data in a known order, use +//! SMBusARPUDIDPacketDecode(). +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPGetUDIDDir(tSMBus *psSMBus, uint8_t ui8TargetAddress, + uint8_t *pui8Data) +{ + // + // Use the block read protocol to receive the UDID. + // + return(SMBusMasterBlockRead(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + (ui8TargetAddress << 1 | 1), pui8Data)); +} + +//***************************************************************************** +// +//! \internal +//! Sends a ``general'' Reset Device packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sends a ``general'' Reset Device packet, used during Address +//! Resolution Protocol (ARP). This packet is used by an ARP Master to force +//! all non-PSA (Persistent Slave Address), ARP-capable devices to return to +//! their initial state. This packet also tells the devices to clear their +//! Address Resolved (AR) and Address Valid (AV) flags. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPResetDeviceGen(tSMBus *psSMBus) +{ + // + // Use the Send Byte protocol to send the packet. + // + return(SMBusMasterByteSend(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + SMBUS_CMD_ARP_RESET_DEVICE)); +} + +//***************************************************************************** +// +//! \internal +//! Sends a ``directed'' Reset Device packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sends a ``directed'' Reset Device packet, used during Address +//! Resolution Protocol (ARP). This packet is used by an ARP Master to force +//! a specific non-PSA (Persistent Slave Address), ARP-capable device to return +//! to its initial state. This packet also tells the device to clear its +//! Address Resolved (AR) and Address Valid (AV) flags. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPResetDeviceDir(tSMBus *psSMBus, uint8_t ui8TargetAddress) +{ + // + // Use the Send Byte protocol to send the packet. + // + return(SMBusMasterByteSend(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + (ui8TargetAddress << 1))); +} + +//***************************************************************************** +// +//! Sends an ARP Assign Address packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the transmit data buffer. This buffer +//! should be correctly formatted using SMBusARPUDIDPacketEncode() and +//! should contain the UDID data and the address for the slave. +//! +//! This function sends an Assign Address packet, used during Address +//! Resolution Protocol (ARP). Because SMBus requires data bytes be sent out +//! MSB first, the UDID and target address should be formatted correctly by the +//! application or using SMBusARPUDIDPacketEncode() and placed into a data +//! buffer pointed to by pui8Data. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPAssignAddress(tSMBus *psSMBus, uint8_t *pui8Data) +{ + // + // Use the Block Write protocol to send the packet. + // + return(SMBusMasterBlockWrite(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + SMBUS_CMD_ARP_ASSIGN_ADDRESS, pui8Data, 17)); +} + +//***************************************************************************** +// +//! Sends a Notify ARP Master packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the transmit data buffer. The data payload +//! should be 0x0000 for this packet. +//! +//! This function sends a Notify ARP Master packet, used during Address +//! Resolution Protocol (ARP). This packet is used by a slave to indicate +//! to the ARP Master that it needs attention. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPNotifyMaster(tSMBus *psSMBus, uint8_t *pui8Data) +{ + // + // Use the Host Notify protocol to send the packet. + // + return(SMBusMasterHostNotify(psSMBus, (SMBUS_ADR_DEFAULT_DEVICE << 1), + pui8Data)); +} + +//***************************************************************************** +// +//! Sends a Prepare to ARP packet. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sends a Prepare to ARP packet, used during Address Resolution +//! Protocol (ARP). This packet is used by an ARP Master to alert devices on +//! the bus that ARP is about to begin. All ARP-capable devices must +//! acknowledge all bytes in this packet and clear their Address Resolved (AR) +//! flag. +//! +//! \return Returns \b SMBUS_PERIPHERAL_BUSY if the I2C peripheral is currently +//! active, \b SMBUS_BUS_BUSY if the bus is already in use, or \b SMBUS_OK if +//! the transfer has successfully been initiated. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterARPPrepareToARP(tSMBus *psSMBus) +{ + // + // Use the Send Byte protocol to send the packet. + // + return(SMBusMasterByteSend(psSMBus, SMBUS_ADR_DEFAULT_DEVICE, + SMBUS_CMD_PREPARE_TO_ARP)); +} + +//***************************************************************************** +// +//! Master ISR processing function for the SMBus application. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function must be called in the application interrupt service routine +//! (ISR) to process SMBus master interrupts. +//! +//! \return Returns \b SMBUS_TIMEOUT if a bus timeout is detected, +//! \b SMBUS_ARB_LOST if I2C bus arbitration lost is detected, +//! \b SMBUS_ADDR_ACK_ERROR if the address phase of a transfer results in a +//! NACK, \b SMBUS_DATA_ACK_ERROR if the data phase of a transfer results in a +//! NACK, \b SMBUS_DATA_SIZE_ERROR if a receive buffer overrun is detected or +//! if a transmit operation tries to write more data than is allowed, +//! \b SMBUS_MASTER_ERROR if an unknown error occurs, \b SMBUS_PEC_ERROR if the +//! received PEC byte does not match the locally calculated value, or +//! \b SMBUS_OK if processing finished successfully. +// +//***************************************************************************** +tSMBusStatus +SMBusMasterIntProcess(tSMBus *psSMBus) +{ + uint32_t ui32IntStatus; + uint32_t ui32ErrorStatus; + uint8_t ui8TempData; + + // + // Determine which interrupt made us get here. + // + ui32IntStatus = MAP_I2CMasterIntStatusEx(psSMBus->ui32I2CBase, true); + + // + // Check for the timeout interrupt. Since the peripheral will + // automatically issue a stop, just clear the interrupt and return. + // + if(ui32IntStatus & I2C_MASTER_INT_TIMEOUT) + { + // + // Clear all pending interrupts and wait for the bus to become + // free so we can issue a STOP. + // + MAP_I2CMasterIntClearEx(psSMBus->ui32I2CBase, I2C_MASTER_INT_TIMEOUT | + I2C_MASTER_INT_DATA); + + // + // Clear the transfer in progress flag. New transactions will + // be aborted until the bus is free. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return to caller. + // + return(SMBUS_TIMEOUT); + } + else + { + // + // Clear the data interrupt. + // + MAP_I2CMasterIntClearEx(psSMBus->ui32I2CBase, I2C_MASTER_INT_DATA); + } + + // + // Read the master interrupt status bits. + // + ui32ErrorStatus = HWREG(psSMBus->ui32I2CBase + I2C_O_MCS); + + // + // Check for arbitration lost. + // + if(ui32ErrorStatus & I2C_MCS_ARBLST) + { + // + // Put the state machine back in the idle state. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return to caller. + // + return(SMBUS_ARB_LOST); + } + + // + // Check for an error. + // + if(ui32ErrorStatus & I2C_MCS_ERROR) + { + // + // Put the state machine back in the idle state. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Check to see if the bus is free. There are two interrupts when a + // NACK happens, and the bus should only be free during the second + // interrupt. During the first interrupt (when the bus is busy), + // generate the necessary STOP condition. + // + if(MAP_I2CMasterBusBusy(psSMBus->ui32I2CBase)) + { + // + // Issue a STOP. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_ERROR_STOP); + } + else + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + } + + // + // Check for ACK errors. + // + if(ui32ErrorStatus & I2C_MCS_ADRACK) + { + // + // Return to caller. + // + return(SMBUS_ADDR_ACK_ERROR); + } + else if(ui32ErrorStatus & I2C_MCS_DATACK) + { + // + // Return to caller. + // + return(SMBUS_DATA_ACK_ERROR); + } + else + { + // + // Return to caller. Should never get here. + // + return(SMBUS_MASTER_ERROR); + } + } + + // + // If no error conditions, determine what to do based on the state. + // + switch(psSMBus->ui8MasterState) + { + // + // The idle state. This state should only be reached after the last + // byte of a master transmit. + // + case SMBUS_STATE_IDLE: + { + // + // If the peripheral is not busy clear the transfer in progress + // flag. This means that the peripheral has given up the bus, + // most likely due to the end of a transmit operation. + // + if(!MAP_I2CMasterBusy(psSMBus->ui32I2CBase)) + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + } + + // + // This state is done. + // + break; + } + + // + // When using a block write, the transfer size must be sent before the + // data payload. + // + case SMBUS_STATE_WRITE_BLOCK_SIZE: + { + // + // Write the block write size to the data register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, psSMBus->ui8TxSize); + + // + // Continue the burst write. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // The next data byte is from the data payload. + // + if((psSMBus->ui8TxSize == 1) && + !(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC))) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + else + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_NEXT; + } + + // + // This state is done. + // + break; + } + // + // The state for the middle of a burst write. + // + case SMBUS_STATE_WRITE_NEXT: + { + // + // Write the next byte to the data register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, + psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++]); + + // + // Continue the burst write. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // Determine the next state based on the values of the PEC and + // process call flags. + // + + // + // If PEC is active and process call is not active. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // If a process call, there is no PEC byte on the transmit. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL)) + { + // + // Check to see if the TX index is equal to size minus 1. + // + if(psSMBus->ui8TxIndex == (psSMBus->ui8TxSize - 1)) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + } + else + { + // + // If the TX index is the same as the size, we're done. + // + if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + } + } + + // + // If PEC is not used, regardless of whether this is a process + // call. + // + else + { + // + // Check to see if the TX index is equal to the size minus 1. + // + if(psSMBus->ui8TxIndex == (psSMBus->ui8TxSize - 1)) + { + psSMBus->ui8MasterState = SMBUS_STATE_WRITE_FINAL; + } + } + + // + // This state is done. + // + break; + } + + // + // The state for the final write of a burst sequence. + // + case SMBUS_STATE_WRITE_FINAL: + { + // + // Determine what data to write to the data register based + // on the values of the PEC and process call flags. + // + // + // If PEC is active, write the PEC byte to the data register. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // If a process call is active, send data, not CRC. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL)) + { + // + // Write the final byte from TX buffer to the data + // register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, + psSMBus->pui8TxBuffer[psSMBus-> + ui8TxIndex++]); + } + else + { + // + // Write the calculated CRC (PEC) byte to the data + // register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, + psSMBus->ui8CalculatedCRC); + } + } + else + { + // + // Write the final byte from TX buffer to the data register. + // + MAP_I2CMasterDataPut(psSMBus->ui32I2CBase, + psSMBus->pui8TxBuffer[psSMBus-> + ui8TxIndex++]); + } + + // + // If a process call is active, send out the repeated start to + // begin the RX portion. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL)) + { + // + // Move to the read first "turnaround" state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_FIRST; + + // + // Continue the burst write. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_CONT); + } + else + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_FINISH); + + // + // Since we end the transaction after the last byte is sent, + // the next state is idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + } + + // + // This state is done. + // + break; + } + + // + // The state for a single byte read. + // + case SMBUS_STATE_READ_ONE: + { + // + // Put the I2C master into receive mode. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, true); + + // + // Perform a single byte read. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_SINGLE_RECEIVE); + + // + // The next state is the wait for final read state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + + // + // This state is done. + // + break; + } + + // + // The state for the start of a burst read. + // + case SMBUS_STATE_READ_FIRST: + { + // + // Put the I2C master into receive mode. + // + MAP_I2CMasterSlaveAddrSet(psSMBus->ui32I2CBase, + psSMBus->ui8TargetSlaveAddress, true); + + // + // Handle the case where PEC is used. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the target address and R/S bit to the running CRC + // calculation. + // + ui8TempData = + ((psSMBus->ui8TargetSlaveAddress << 1) & 0xfe) | 1; + + // + // Update the calculated CRC value in the configuration + // structure. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, &ui8TempData, 1); + + // + // Set the next state in the state machine. + // + if(psSMBus->ui8RxSize > 1) + { + // + // If this is a block transfer, the next state is to read + // back the number of bytes that the slave will be sending. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_BLOCK_SIZE; + } + + // + // For every other case... + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + } + } + + // + // If 1 byte remains, move to the final read state. + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + } + else + { + // + // Set the next state in the state machine. + // + if(psSMBus->ui8RxSize > 2) + { + // + // If this is a block transfer, the next state is to read + // back the number of bytes that the slave will be sending. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_BLOCK_SIZE; + } + + // + // For every other case... + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + } + } + + // + // If 2 bytes remain, move to the final read state. + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + } + + // + // Start the burst receive. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_START); + + // + // This state is done. + // + break; + } + + // + // The state for the size of a block read. + // + case SMBUS_STATE_READ_BLOCK_SIZE: + { + // + // Update the RX size with the data byte. + // + psSMBus->ui8RxSize = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // If more than 32 bytes are going to be sent, error. + // + if((psSMBus->ui8RxSize > 32) || (psSMBus->ui8RxSize == 0)) + { + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_ERROR_STOP; + + // + // If too many or too few bytes, error. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_SINGLE_RECEIVE); + + // + // Break from this case. + // + break; + } + + // + // If PEC is enabled, add the size byte to the calculation and + // add one to the size variable to account for the extra PEC byte. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Calculate the new CRC and update configuration structure. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8RxSize, 1); + } + + // + // Update the state machine. + // + switch(psSMBus->ui8RxSize) + { + // + // 1 byte remaining. + // + case 1: + { + // + // If only one byte remains and PEC, go to the second + // to last byte state. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + + // + // If only one byte remains and no PEC, end the burst + // transfer. + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + } + + // + // This switch is done. + // + break; + } + + // + // 2 bytes remaining. + // + case 2: + { + // + // If two bytes and PEC remain, move to read next + // state. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + } + + // + // If two bytes remain, move to the final read state. + // + else + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + + // + // This switch is done. + // + break; + } + + // + // For every other situation (in other words, remaining bytes + // is greater than 2). + // + default: + { + // + // If more than 2 bytes to read, move to the next byte + // state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_NEXT; + + // + // This switch is done. + // + break; + } + } + + // + // Determine how to step the I2C state machine. + // + if((psSMBus->ui8RxSize == 1) && + !HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // If exactly 1 byte remains, read the byte and send a STOP. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_SEND_FINISH); + } + else + { + // + // Otherwise, continue the burst read. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_CONT); + } + + // + // This state is done. + // + break; + } + + // + // The state for the middle of a burst read. + // + case SMBUS_STATE_READ_NEXT: + { + // + // Check for a buffer overrun. + // + if(psSMBus->ui8RxIndex >= psSMBus->ui8RxSize) + { + // + // Dummy read of data register. + // + ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // If too many or too few bytes, error. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_ERROR_STOP; + + // + // Break from this case. + // + break; + } + + // + // Read the received character. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex] = + MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // Continue the burst read. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_CONT); + + // + // If PEC is enabled, add the received byte to the calculation. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Calculate the new CRC and update configuration structure. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex], + 1); + + // + // Increment the receive buffer index. + // + psSMBus->ui8RxIndex++; + + // + // If there is 1 byte remaining, make next state be the + // end of burst read state. + // + if((psSMBus->ui8RxSize - psSMBus->ui8RxIndex) == 1) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + } + else + { + // + // Increment the receive buffer index. + // + psSMBus->ui8RxIndex++; + + // + // If there are two bytes remaining, make next state be the + // end of burst read state. + // + if((psSMBus->ui8RxSize - psSMBus->ui8RxIndex) == 2) + { + psSMBus->ui8MasterState = SMBUS_STATE_READ_FINAL; + } + } + + // + // This state is done. + // + break; + } + + // + // The state for the end of a burst read. + // + case SMBUS_STATE_READ_FINAL: + { + // + // Check for a buffer overrun. + // + if(psSMBus->ui8RxIndex >= psSMBus->ui8RxSize) + { + // + // Dummy read of data register. + // + ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // If too many or too few bytes, error. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // Set the next state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_ERROR_STOP; + + // + // Break from this case. + // + break; + } + + // + // Read the received character. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex] = + MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // The next state is the wait for final read state. + // + psSMBus->ui8MasterState = SMBUS_STATE_READ_WAIT; + + // + // Finish the burst read. + // + MAP_I2CMasterControl(psSMBus->ui32I2CBase, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // If PEC is enabled, add the received byte to the calculation. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Calculate the new CRC and update configuration structure. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex], + 1); + } + + // + // Increment the receive buffer index. + // + psSMBus->ui8RxIndex++; + + // + // This state is done. + // + break; + } + + // + // This state is for the final read of a single or burst read. + // + case SMBUS_STATE_READ_WAIT: + { + // + // Read the received byte. + // + ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // If PEC is enabled, check the value that just came in to see + // if it matches. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Check for a buffer overrun. + // + if(psSMBus->ui8RxIndex > psSMBus->ui8RxSize) + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, + FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return the error condition. + // + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Store the received CRC byte. + // + psSMBus->ui8ReceivedCRC = ui8TempData; + + // + // If the CRC doesn't match, send a NACK and indicate the + // failure to the application. + // + if(psSMBus->ui8ReceivedCRC != psSMBus->ui8CalculatedCRC) + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, + FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return the error condition. + // + return(SMBUS_PEC_ERROR); + } + } + else + { + // + // Check for a buffer overrun. + // + if(psSMBus->ui8RxIndex >= psSMBus->ui8RxSize) + { + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, + FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return the error condition. + // + return(SMBUS_DATA_SIZE_ERROR); + } + + // + // Read the received byte. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex] = ui8TempData; + + // + // Increment the receive buffer index. + // + psSMBus->ui8RxIndex++; + } + + // + // The state machine is now idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // This state is done. + // + break; + } + + // + // This state is for a transaction that needed to end due to a + // size error. + // + case SMBUS_STATE_READ_ERROR_STOP: + { + // + // Dummy read the received byte. + // + ui8TempData = MAP_I2CMasterDataGet(psSMBus->ui32I2CBase); + + // + // The state machine is now idle. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + + // + // Clear the transfer in progress flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Return the error condition. + // + return(SMBUS_DATA_SIZE_ERROR); + } + } + + // + // Return to caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Enables the appropriate master interrupts for stack processing. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function enables the I2C interrupts used by the SMBus master. Both +//! the peripheral-level and NVIC-level interrupts are enabled. +//! SMBusMasterInit() must be called before this function because this function +//! relies on the I2C base address being defined. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusMasterIntEnable(tSMBus *psSMBus) +{ + // + // Enable the master interrupts. + // + MAP_I2CMasterIntEnableEx(psSMBus->ui32I2CBase, I2C_MASTER_INT_DATA | + I2C_MASTER_INT_TIMEOUT); + + // + // Enable the interrupt in the NVIC. + // + switch(psSMBus->ui32I2CBase) + { + case I2C0_BASE: + { + MAP_IntEnable(INT_I2C0); + break; + } + + case I2C1_BASE: + { + MAP_IntEnable(INT_I2C1); + break; + } + + case I2C2_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C2_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C2_TM4C129); + } + break; + } + + case I2C3_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C3_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C3_TM4C129); + } + break; + } + + case I2C4_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C4_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C4_TM4C129); + } + break; + } + + case I2C5_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C5_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C5_TM4C129); + } + break; + } + + case I2C6_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C6_TM4C129); + } + break; + } + + case I2C7_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C7_TM4C129); + } + break; + } + + case I2C8_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C8_TM4C129); + } + break; + } + + case I2C9_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C9_TM4C129); + } + break; + } + } +} + +//***************************************************************************** +// +//! Initializes an I2C master peripheral for SMBus functionality. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui32I2CBase specifies the base address of the I2C master peripheral. +//! \param ui32SMBusClock specifies the system clock speed of the MCU. +//! +//! This function initializes an I2C peripheral for SMBus master use. The +//! instance-specific configuration structure is initialized to a set of known +//! values and the I2C peripheral is configured for 100kHz use, which is +//! required by the SMBus specification. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusMasterInit(tSMBus *psSMBus, uint32_t ui32I2CBase, + uint32_t ui32SMBusClock) +{ + // + // Initialize the configuration structure. + // + psSMBus->pUDID = 0; + psSMBus->ui32I2CBase = ui32I2CBase; + psSMBus->ui16Flags = 0; + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + psSMBus->ui8OwnSlaveAddress = 0; + psSMBus->ui8TargetSlaveAddress = 0; + psSMBus->ui8CurrentCommand = 0; + psSMBus->ui8CalculatedCRC = 0; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + + // + // Enable and initialize the I2C master module Using the system clock. + // The I2C transfer rate will always be 100kHz since fast mode is not + // supported by SMBus. + // + MAP_I2CMasterInitExpClk(psSMBus->ui32I2CBase, ui32SMBusClock, false); + + // + // Configure bus timeout to 25ms. 12-bit value for 25ms is 0x9C4 (2500 + // clocks), so round upper 8 bits to 0x9C. Each clock is 10us since + // 100kHz I2C is required for SMBus. + // + MAP_I2CMasterTimeoutSet(psSMBus->ui32I2CBase, 0x9C); +} + +//***************************************************************************** +// +//! Slave ISR processing function for the SMBus application. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function must be called in the application interrupt service routine +//! (ISR) to process SMBus slave interrupts. +//! +//! If manual acknowledge is enabled using SMBusSlaveManualACKEnable(), this +//! function processes the data byte, but does not send the ACK/NACK value. In +//! this case, the user application is responsible for sending the acknowledge +//! bit based on the return code of this function. +//! +//! When receiving a Quick Command from the master, the slave has some set-up +//! requirements. When the master sends the R/S (data) bit as '0', nothing +//! additional needs to be done in the slave and SMBusSlaveIntProcess() returns +//! \b SMBUS_SLAVE_QCMD_0. However, when the master sends the R/S (data) bit +//! as '1', the slave must write the data register with data containing a '1' +//! in bit 7. This means that when receiving a Quick Command, the slave must +//! set up the TX buffer to either have 1 data byte with bit 7 set to '1' or +//! set up the TX buffer to be zero length. In the case where 1 data byte is +//! put in the TX buffer, SMBusSlaveIntProcess() returns \b SMBUS_OK the first +//! time its called and \b SMBUS_SLAVE_QCMD_0 the second. In the case where +//! the TX buffer has no data, SMBusSlaveIntProcess() will return +//! \b SMBUS_SLAVE_ERROR the first time its called, and \b SMBUS_SLAVE_QCMD_1 +//! the second time. +//! +//! \return Returns \b SMBUS_SLAVE_FIRST_BYTE if the first byte (typically the +//! SMBus command) has been received; \b SMBUS_SLAVE_NOT_READY if the slave's +//! transmit buffer is not yet initialized when the master requests data from +//! the slave; \b SMBUS_DATA_SIZE_ERROR if during a master block write, the +//! size sent by the master is greater than the amount of available space in +//! the receive buffer; \b SMBUS_SLAVE_ERROR if a buffer overrun is detected +//! during a slave receive operation or if data is sent and was not expected; +//! \b SMBUS_SLAVE_QCMD_0 if a Quick Command was received with data '0'; +//! \b SMBUS_SLAVE_QCMD_1 if a Quick Command was received with data '1'; +//! \b SMBUS_TRANSFER_COMPLETE if a STOP is detected on the bus, marking the +//! end of a transfer; \b SMBUS_PEC_ERROR if the received PEC byte does not +//! match the locally calculated value; or \b SMBUS_OK if processing finished +//! successfully. +// +//***************************************************************************** +tSMBusStatus +SMBusSlaveIntProcess(tSMBus *psSMBus) +{ + uint32_t ui32InterruptStatus; + uint32_t ui32SlaveStatus = 0; + uint8_t ui8CRCTemp; + uint8_t ui8DataTemp; + + // + // Determine which interrupt was asserted. + // + ui32InterruptStatus = I2CSlaveIntStatusEx(psSMBus->ui32I2CBase, true); + + // + // Check the status register. + // + ui32SlaveStatus = I2CSlaveStatus(psSMBus->ui32I2CBase); + + // + // Check for the START interrupt. + // + if(ui32InterruptStatus & I2C_SLAVE_INT_START) + { + // + // Clear the interrupt. + // + I2CSlaveIntClearEx(psSMBus->ui32I2CBase, I2C_SLAVE_INT_START); + + + // + // This interrupt is not supported outside of using the FIFO. + // + return(SMBUS_OK); + } + + // + // Check for the STOP interrupt. + // + if(ui32InterruptStatus & I2C_SLAVE_INT_STOP) + { + // + // Make sure the transfer in progress flag is cleared. In the case + // of Quick Command, it should never be set, so this is safe. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Clear the interrupt. + // + I2CSlaveIntClearEx(psSMBus->ui32I2CBase, I2C_SLAVE_INT_STOP); + + // + // Check to see if a Quick Command was sent. + // + if(ui32SlaveStatus & 0x10) + { + // + // Make sure the TX/RX index is 0. If not, we should not be here. + // Other data should not have been sent or received during a Quick + // Command. + // + if((psSMBus->ui8RxIndex != 0) || (psSMBus->ui8TxIndex != 0)) + { + // + // Return an error. + // + return(SMBUS_SLAVE_ERROR); + } + + // + // Tell caller a Quick Command has occurred and the data value. + // + if(ui32SlaveStatus & 0x20) + { + return(SMBUS_SLAVE_QCMD_1); + } + else + { + return(SMBUS_SLAVE_QCMD_0); + } + } + + // + // Move to the idle state. + // + psSMBus->ui8SlaveState = SMBUS_STATE_IDLE; + + // + // Return end of transfer. + // + return(SMBUS_TRANSFER_COMPLETE); + } + + // + // Check for the DATA interrupt. + // + if(ui32InterruptStatus & I2C_SLAVE_INT_DATA) + { + // + // Clear the I2C interrupt. + // + I2CSlaveIntClearEx(psSMBus->ui32I2CBase, I2C_SLAVE_INT_DATA); + + // + // Make sure that at least one of the relevant status bits is set. + // + if(!(ui32SlaveStatus & 0x07)) + { + // + // No status bits were set - this is bad. Should never get here. + // + return(SMBUS_SLAVE_ERROR); + } + + // + // Every time this interrupt occurs, a transfer is in progress. Make + // sure the flag is set appropriately. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 1; + + // + // Handle the request type. + // + switch((ui32SlaveStatus & 0x07)) + { + // + // The first byte after the slave's own address has been received. + // This is almost always the command byte in SMBus. The only + // exception is when the Send Byte protocol is used by the master. + // + case I2C_SLAVE_ACT_RREQ_FBR: + { + // + // Check which slave address was called out. Set the active + // address to the matched address. + // + if(I2CSlaveStatus(psSMBus->ui32I2CBase) & I2C_SCSR_OAR2SEL) + { + psSMBus->ui8OwnSlaveAddress = + HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR2) & 0x7f; + } + else + { + psSMBus->ui8OwnSlaveAddress = + HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR); + } + + // + // If raw I2C, data goes into buffer. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C)) + { + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex++] = + I2CSlaveDataGet(psSMBus->ui32I2CBase); + } + + // + // Read the first byte into the ui8CurrentCommand member. + // + else + { + psSMBus->ui8CurrentCommand = + I2CSlaveDataGet(psSMBus->ui32I2CBase); + } + + // + // If PEC is enabled, add the address to the CRC calculation. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the address to the CRC calculation. In this case + // R/S will always be 0. Also, this is the start of the + // CRC calculation, so the initial value is 0. + // + ui8CRCTemp = psSMBus->ui8OwnSlaveAddress << 1; + + // + // Calculate new CRC. + // + psSMBus->ui8CalculatedCRC = Crc8CCITT(0, &ui8CRCTemp, 1); + + // + // Add the data byte (ui8CurrentCommand) to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &psSMBus->ui8CurrentCommand, 1); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_SLAVE_POST_COMMAND; + + // + // Actions for this case are complete. + // + return(SMBUS_SLAVE_FIRST_BYTE); + } + + // + // A data byte other than the first data byte has been received. + // + case I2C_SLAVE_ACT_RREQ: + { + // + // Determine what to do based on the current state. + // + switch(psSMBus->ui8SlaveState) + { + // + // Receive first post-command byte. + // + case SMBUS_STATE_SLAVE_POST_COMMAND: + { + // + // Read the data into the a temporary variable. + // + ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase); + + // + // Check if this is a block transfer. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + // + // Make sure there is enough space in the buffer. + // If not, NACK. If there is, overwrite the + // current size with the size sent by the master. + // + if(ui8DataTemp > psSMBus->ui8RxSize) + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE; + + // + // Indicate a size error. + // + return(SMBUS_DATA_SIZE_ERROR); + } + else + { + // + // Update the size. + // + psSMBus->ui8RxSize = ui8DataTemp; + + // + // Check to see if PEC is enabled. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_NEXT; + } + + // + // This state is done. + // + break; + } + + // + // If there is no data to receive and no PEC, nothing + // to do. Software should never get here. + // + if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize) + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE; + + // + // Report an error. + // + return(SMBUS_SLAVE_ERROR); + } + else + { + // + // Put the data in the buffer. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex++] = + ui8DataTemp; + + // + // If this is the last data byte. + // + if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize) + { + // + // Check for PEC usage. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_PEC; + } + else + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_DONE; + } + } + + // + // All other cases. + // + else + { + // + // Check for PEC usage. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_NEXT; + } + } + + // + // Actions for this case are complete. + // + break; + } + + // + // Read the next byte into the buffer. + // + case SMBUS_STATE_READ_NEXT: + { + // + // Read the data into the a temporary variable. + // + ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase); + + // + // If there is no data to receive and no PEC, nothing + // to do. Software should never get here. + // + if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize) + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE; + + // + // Report an error. + // + return(SMBUS_SLAVE_ERROR); + } + else + { + // + // Put the data in the buffer. + // + psSMBus->pui8RxBuffer[psSMBus->ui8RxIndex++] = + ui8DataTemp; + + // + // If this is the last data byte. + // + if(psSMBus->ui8RxIndex == psSMBus->ui8RxSize) + { + // + // Check for PEC usage. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Update the state machine. + // + if(HWREGBITB(&psSMBus->ui16Flags, + FLAG_PROCESS_CALL)) + { + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_DONE; + } + else + { + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_PEC; + } + } + else + { + // + // Update the state machine. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_READ_DONE; + } + } + + // + // All other cases. + // + else + { + // + // Check for PEC usage. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the size byte to the CRC + // calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_NEXT; + } + } + + break; + } + + // + // Read the PEC byte and compare it. + // + case SMBUS_STATE_READ_PEC: + { + // + // Read the data into the a temporary variable. + // + ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase); + + // + // Compare PEC. + // + if(psSMBus->ui8CalculatedCRC != ui8DataTemp) + { + // + // Indicate PEC error. + // + return(SMBUS_PEC_ERROR); + } + + // + // Update the state machine. + // + psSMBus->ui8SlaveState = SMBUS_STATE_READ_DONE; + + break; + } + + // + // No more data to receive. If we get here, read data + // into a dummy variable and NACK. + // + case SMBUS_STATE_READ_DONE: + { + // + // Read the data into the a temporary variable. + // + ui8DataTemp = I2CSlaveDataGet(psSMBus->ui32I2CBase); + + // + // Report an error. + // + return(SMBUS_SLAVE_ERROR); + } + } + + // + // Actions for this case are complete. + // + break; + } + + // + // The master has requested that the slave transmit data back to + // master. + // + case I2C_SLAVE_ACT_TREQ: + { + // + // Initialize temporary variable that stores transmit byte to + // 0xff. If data is not set by another condition, the 0xff + // carries through. This happens if ui8TxIndex is equal to or + // greater than ui8TxSize. + // + ui8DataTemp = 0xff; + + // + // Determine what to do based on the current state. + // + switch(psSMBus->ui8SlaveState) + { + // + // The state machine is currently idle, or if the last + // state was SMBUS_STATE_SLAVE_POST_COMMAND or + // SMBUS_READ_DONE, this is the first byte transmitted. In + // the case of slave post command, this means that the + // command was received followed by a repeated start (with + // R/S = 1). In the case of read next, this means that a + // raw I2C master transmit changed direction with a + // repeated start and is now a master receive. In the case + // of read done, this means that a previous master transmit + // was finished (non-command followed by a repeated start). + // + case SMBUS_STATE_IDLE: + case SMBUS_STATE_SLAVE_POST_COMMAND: + case SMBUS_STATE_READ_NEXT: + case SMBUS_STATE_READ_DONE: + { + // + // Check which slave address was called out. Set the + // active address to the matched address. + // + if(I2CSlaveStatus(psSMBus->ui32I2CBase) & + I2C_SCSR_OAR2SEL) + { + psSMBus->ui8OwnSlaveAddress = + (HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR2) & + 0x7f); + } + else + { + psSMBus->ui8OwnSlaveAddress = + HWREG(psSMBus->ui32I2CBase + I2C_O_SOAR); + } + + // + // Check to see if the TX buffer is populated. If not, + // return not ready without writing to the data + // register. + // + if(psSMBus->ui8TxSize == 0) + { + return(SMBUS_SLAVE_NOT_READY); + } + + // + // Is this a block transfer? + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + // + // The first byte to send is the size. + // + ui8DataTemp = psSMBus->ui8TxSize; + } + else + { + // + // Is there data to send? + // + if(psSMBus->ui8TxIndex < psSMBus->ui8TxSize) + { + // + // Set the transmit data to the next item in + // the buffer. + // + ui8DataTemp = + psSMBus->pui8TxBuffer[psSMBus-> + ui8TxIndex++]; + } + else + { + // + // Send 0xff per spec. + // + ui8DataTemp = 0xff; + } + } + + // + // Check to see if PEC is required. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Start calculating the CRC with the address. + // + ui8CRCTemp = + (psSMBus->ui8OwnSlaveAddress << 1) | 1; + + // + // Add the address and R/S bit to the CRC. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8CRCTemp, 1); + + // + // Add the data byte to the CRC calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Move to the next state. + // + if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize) + { + // + // Final byte is the CRC byte. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_FINAL; + } + else + { + // + // All other cases, move to the next byte + // state. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_NEXT; + } + } + else + { + // + // Move to the next state. + // + switch(psSMBus->ui8TxSize - psSMBus->ui8TxIndex) + { + // + // If all of the data has been sent, move to + // the done state. + // + case 0: + { + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_DONE; + + break; + } + + // + // If 1 left, move to the final byte state. + // + case 1: + { + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_FINAL; + + break; + } + + // + // All other cases, move to the next byte + // state. + // + default: + { + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_NEXT; + + break; + } + } + } + + // + // Send the data. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp); + + // + // This state is done. + // + break; + } + + // + // The first byte has already been sent, handle the rest. + // + case SMBUS_STATE_WRITE_NEXT: + { + // + // Set the transmit data to the next item in the + // buffer. + // + ui8DataTemp = + psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++]; + + // + // Check to see if PEC is required. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Add the byte to the CRC calculation. + // + psSMBus->ui8CalculatedCRC = + MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Check if it's time to move to the next state. + // + if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize) + { + // + // Final byte is the CRC byte. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_FINAL; + } + } + else + { + // + // Move to the next state. + // + if((psSMBus->ui8TxSize - psSMBus->ui8TxIndex) == 1) + { + // + // If only 1 byte remains, move to the final + // state. + // + psSMBus->ui8SlaveState = + SMBUS_STATE_WRITE_FINAL; + } + } + + // + // Send the data. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp); + + // + // This state is done. + // + break; + } + + // + // Write the final byte, whether PEC or data. + // + case SMBUS_STATE_WRITE_FINAL: + { + // + // Check to see if PEC is required. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Send the CRC byte. + // + ui8DataTemp = psSMBus->ui8CalculatedCRC; + } + else + { + // + // Send the last data byte. + // + ui8DataTemp = + psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++]; + } + + // + // Send the data. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp); + + // + // Move to the write done state. + // + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_DONE; + + // + // This state is done. + // + break; + } + + // + // All data has been sent, send 0xff. + // + case SMBUS_STATE_WRITE_DONE: + { + // + // Send 0xff because there is no more data to send. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, 0xff); + + // + // This state is done. + // + break; + } + } + + // + // Actions for this case are complete. + // + break; + } + } + + // + // Return OK status. + // + return(SMBUS_OK); + } + + // + // Return OK. Should never get here. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Sends data outside of the interrupt processing function. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function sends data outside the interrupt processing function, and +//! should only be used when SMBusSlaveIntProcess() returns +//! \b SMBUS_SLAVE_NOT_READY. At this point, the application should set up the +//! transfer and call this function (it assumes that the transmit buffer has +//! already been populated when called). When called, this function updates +//! the slave state machine as if SMBusSlaveIntProcess() were called. +//! +//! \return Returns \b SMBUS_SLAVE_NOT_READY if the slave's transmit buffer is +//! not yet initialized (ui8TxSize is 0), or \b SMBUS_OK if processing finished +//! successfully. +// +//***************************************************************************** +tSMBusStatus +SMBusSlaveDataSend(tSMBus *psSMBus) +{ + uint8_t ui8CRCTemp; + uint8_t ui8DataTemp; + + // + // Check to see if the TX buffer is populated. If not, + // return not ready without writing to the data register. + // + if(psSMBus->ui8TxSize == 0) + { + return(SMBUS_SLAVE_NOT_READY); + } + + // + // Is this a block transfer? + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER)) + { + // + // The first byte to send is the size. + // + ui8DataTemp = psSMBus->ui8TxSize; + } + else + { + // + // Is there data to send? + // + if(psSMBus->ui8TxIndex < psSMBus->ui8TxSize) + { + // + // Set the transmit data to the next item in + // the buffer. + // + ui8DataTemp = psSMBus->pui8TxBuffer[psSMBus->ui8TxIndex++]; + } + else + { + // + // Send 0xff per spec. Should not get here. + // + ui8DataTemp = 0xff; + } + } + + // + // Check to see if PEC is required. + // + if(HWREGBITB(&psSMBus->ui16Flags, FLAG_PEC)) + { + // + // Start calculating the CRC with the address. + // + ui8CRCTemp = (psSMBus->ui8OwnSlaveAddress << 1) | 1; + + // + // Add the address and R/S bit to the CRC. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8CRCTemp, 1); + + // + // Add the data byte to the CRC calculation. + // + psSMBus->ui8CalculatedCRC = MAP_Crc8CCITT(psSMBus->ui8CalculatedCRC, + &ui8DataTemp, 1); + + // + // Move to the next state. + // + if(psSMBus->ui8TxIndex == psSMBus->ui8TxSize) + { + // + // Final byte is the CRC byte. + // + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_FINAL; + } + else + { + // + // All other cases, move to the next byte state. + // + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_NEXT; + } + } + else + { + // + // Move to the next state. + // + switch(psSMBus->ui8TxSize - psSMBus->ui8TxIndex) + { + // + // If all of the data has been sent, move to the + // done state. + // + case 0: + { + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_DONE; + + break; + } + + // + // If 1 left, move to the final byte state. + // + case 1: + { + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_FINAL; + + break; + } + + // + // All other cases, move to the next byte state. + // + default: + { + psSMBus->ui8SlaveState = SMBUS_STATE_WRITE_NEXT; + + break; + } + } + } + + // + // Send the data. + // + I2CSlaveDataPut(psSMBus->ui32I2CBase, ui8DataTemp); + + // + // Return to caller. + // + return(SMBUS_OK); +} + +//***************************************************************************** +// +//! Set the address and size of the slave transmit buffer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the transmit data buffer. +//! \param ui8Size is the number of bytes in the buffer. +//! +//! This function sets the address and size of the slave transmit buffer. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveTxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data, + uint8_t ui8Size) +{ + // + // Set the trasmit buffer. + // + psSMBus->pui8TxBuffer = pui8Data; + + // + // Set the size. + // + psSMBus->ui8TxSize = ui8Size; +} + +//***************************************************************************** +// +//! Set the address and size of the slave receive buffer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pui8Data is a pointer to the receive data buffer. +//! \param ui8Size is the number of bytes in the buffer. +//! +//! This function sets the address and size of the slave receive buffer. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveRxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data, + uint8_t ui8Size) +{ + // + // Set the receive buffer. + // + psSMBus->pui8RxBuffer = pui8Data; + + // + // Set the size. + // + psSMBus->ui8RxSize = ui8Size; +} + +//***************************************************************************** +// +//! Get the current command byte. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Returns the current value of the ui8CurrentCommand variable in the SMBus +//! configuration structure. This can be used to help the user application +//! set up the SMBus slave transmit and receive buffers. +//! +//! \return None. +// +//***************************************************************************** +uint8_t +SMBusSlaveCommandGet(tSMBus *psSMBus) +{ + // + // Return the current command. + // + return(psSMBus->ui8CurrentCommand); +} + +//***************************************************************************** +// +//! Sets the process call flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Sets the process call flag in the configuration structure so that the SMBus +//! slave can respond correctly to a Process Call request. This flag must be +//! set prior to the data portion of the packet. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveProcessCallEnable(tSMBus *psSMBus) +{ + // + // Set the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 1; +} + +//***************************************************************************** +// +//! Clears the process call flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Clears the process call flag in the configuration structure. The user +//! application can either call this function to clear the flag, or use +//! SMBusSlaveTransferInit() to clear out all transfer-specific flags. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveProcessCallDisable(tSMBus *psSMBus) +{ + // + // Clear the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; +} + +//***************************************************************************** +// +//! Sets the block transfer flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Sets the block transfer flag in the configuration structure so that the +//! SMBus slave can respond correctly to a Block Write or Block Read request. +//! This flag must be set prior to the data portion of the packet. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveBlockTransferEnable(tSMBus *psSMBus) +{ + // + // Set the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 1; +} + +//***************************************************************************** +// +//! Clears the block transfer flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Clears the block transfer flag in the configuration structure. The user +//! application can either call this function to clear the flag, or use +//! SMBusSlaveTransferInit() to clear out all transfer-specific flags. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveBlockTransferDisable(tSMBus *psSMBus) +{ + // + // Clear the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; +} + +//***************************************************************************** +// +//! Sets the ``raw'' I2C flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Sets the raw I2C flag in the configuration structure so that the +//! SMBus slave can respond correctly to raw I2C (non-SMBus protocol) requests. +//! This flag must be set prior to the transfer, and is a global setting. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveI2CEnable(tSMBus *psSMBus) +{ + // + // Set the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 1; +} + +//***************************************************************************** +// +//! Clears the ``raw'' I2C flag for an SMBus slave transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Clears the raw I2C flag in the configuration structure. This flag is a +//! global setting similar to the PEC flag and cannot be cleared using +//! SMBusSlaveTransferInit(). +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveI2CDisable(tSMBus *psSMBus) +{ + // + // Clear the block transfer flag. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_RAW_I2C) = 0; +} + +//***************************************************************************** +// +//! Sets the value of the AR (Address Resolved) flag. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param bValue is the value to set the flag. +//! +//! This function allows the application to set the value of the AR flag. All +//! SMBus slaves must support the AR and AV flags. On POR, the AR flag is +//! cleared. It is also cleared when a slave receives the ARP Reset Device +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveARPFlagARSet(tSMBus *psSMBus, bool bValue) +{ + // + // Set the block address resolved flag to the desired value. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_RESOLVED) = bValue; +} + +//***************************************************************************** +// +//! Returns the current value of the AR (Address Resolved) flag. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This returns the value of the AR (Address Resolved) flag. +//! +//! \return Returns \b true if set, \b false if cleared. +// +//***************************************************************************** +bool +SMBusSlaveARPFlagARGet(tSMBus *psSMBus) +{ + // + // Get the value of the block address resolved flag. + // + return(HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_RESOLVED)); +} + +//***************************************************************************** +// +//! Sets the value of the AV (Address Valid) flag. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param bValue is the value to set the flag. +//! +//! This function allows the application to set the value of the AV flag. All +//! SMBus slaves must support the AR and AV flags. On POR, the AV flag is +//! cleared. It is also cleared when a slave receives the ARP Reset Device +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveARPFlagAVSet(tSMBus *psSMBus, bool bValue) +{ + // + // Set the block address valid flag to the desired value. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_VALID) = bValue; +} + +//***************************************************************************** +// +//! Returns the current value of the AV (Address Valid) flag. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This returns the value of the AV (Address Valid) flag. +//! +//! \return Returns \b true if set, or \b false if cleared. +// +//***************************************************************************** +bool +SMBusSlaveARPFlagAVGet(tSMBus *psSMBus) +{ + // + // Get the value of the block address valid flag. + // + return(HWREGBITB(&psSMBus->ui16Flags, FLAG_ADDRESS_VALID)); +} + +//***************************************************************************** +// +//! Sets up the SMBus slave for a new transfer. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function is used to re-initialize the configuration structure for a +//! new transfer. Once a transfer is complete and the data has been processed, +//! unused flags, states, the data buffers and buffer indexes should be reset +//! to a known state before a new transfer. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveTransferInit(tSMBus *psSMBus) +{ + // + // Clear the block transfer, process call and transfer in progress flags. + // + HWREGBITB(&psSMBus->ui16Flags, FLAG_BLOCK_TRANSFER) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_PROCESS_CALL) = 0; + HWREGBITB(&psSMBus->ui16Flags, FLAG_TRANSFER_IN_PROGRESS) = 0; + + // + // Set the configuration structure to a known, zeroed state. + // + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + psSMBus->ui8SlaveState = SMBUS_STATE_IDLE; + psSMBus->ui8CurrentCommand = 0; + psSMBus->ui8CalculatedCRC = 0; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; +} + +//***************************************************************************** +// +//! Sets the value of the ACK bit when using manual acknowledgement. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param bACK specifies whether to ACK (\b true) or NACK (\b false). +//! +//! This function sets the value of the ACK bit. In order for the ACK bit to +//! take effect, manual acknowledgement must be enabled on the slave using +//! SMBusSlaveManualACKEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveACKSend(tSMBus *psSMBus, bool bACK) +{ + // + // Send ACK or NACK based on the value of bACK. + // + if(bACK) + { + I2CSlaveACKValueSet(psSMBus->ui32I2CBase, true); + } + else + { + I2CSlaveACKValueSet(psSMBus->ui32I2CBase, false); + } +} + +//***************************************************************************** +// +//! Enables manual acknowledgement for the SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function enables manual acknowledge capability in the slave. If the +//! application requires that the slave NACK on a bad command or a bad PEC +//! calculation, manual acknowledgement allows this to happen. +//! +//! In the case of responding to a bad command with a NACK, the application +//! should use SMBusSlaveACKSend() to ACK/NACK the command. The slave ISR +//! should check for the SMBUS_SLAVE_FIRST_BYTE return code from +//! SMBusSlaveISRProcess() and ACK/NACK accordingly. All other cases should be +//! handled in the application based on the return code of +//! SMBusSlaveISRProcess(). +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveManualACKEnable(tSMBus *psSMBus) +{ + // + // Enable manual acknowledge. + // + I2CSlaveACKOverride(psSMBus->ui32I2CBase, true); +} + +//***************************************************************************** +// +//! Disables manual acknowledgement for the SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function disables manual acknowledge capability in the slave. When +//! manual acknowledgement is disabled, the slave automatically ACKs every +//! byte sent by the master. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveManualACKDisable(tSMBus *psSMBus) +{ + // + // Disable manual acknowledge. + // + I2CSlaveACKOverride(psSMBus->ui32I2CBase, false); +} + +//***************************************************************************** +// +//! Returns the manual acknowledgement status of the SMBus slave. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function returns the state of the I2C ACKOEN bit in the I2CSACKCTL +//! register. This feature is disabled out of reset and must be enabled +//! using SMBusSlaveManualACKEnable(). +//! +//! \return Returns \b true if manual acknowledge is enabled, or \b false if +//! manual acknowledge is disabled. +// +//***************************************************************************** +bool +SMBusSlaveManualACKStatusGet(tSMBus *psSMBus) +{ + // + // Return the value of the bit. + // + return(HWREG(psSMBus->ui32I2CBase + I2C_O_SACKCTL) & 0x1); +} + +//***************************************************************************** +// +//! Determine whether primary or secondary slave address has been requested by +//! the master. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! Tells the caller whether the I2C slave address requested by the master or +//! SMBus Host is the primary or secondary I2C slave address of the peripheral. +//! The primary is defined as the address programmed into I2CSOAR, and the +//! secondary as the address programmed into I2CSOAR2. +//! +//! \return Returns \b SMBUS_SLAVE_ADDR_PRIMARY if the primary address is +//! called out or \b SMBUS_SLAVE_ADDR_SECONDARY if the secondary address is +//! called out. +// +//***************************************************************************** +tSMBusStatus +SMBusSlaveIntAddressGet(tSMBus *psSMBus) +{ + // + // Determine whether the primary or secondary address was called out. + // + if(I2CSlaveStatus(psSMBus->ui32I2CBase) & I2C_SCSR_OAR2SEL) + { + return(SMBUS_SLAVE_ADDR_SECONDARY); + } + else + { + return(SMBUS_SLAVE_ADDR_PRIMARY); + } +} + +//***************************************************************************** +// +//! Enables the appropriate slave interrupts for stack processing. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! +//! This function enables the I2C interrupts used by the SMBus slave. Both +//! the peripheral-level and NVIC-level interrupts are enabled. +//! SMBusSlaveInit() must be called before this function because this function +//! relies on the I2C base address being defined. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveIntEnable(tSMBus *psSMBus) +{ + // + // Enable the slave interrupts. + // + I2CSlaveIntEnableEx(psSMBus->ui32I2CBase, + I2C_SLAVE_INT_DATA | I2C_SLAVE_INT_STOP); + + // + // Enable the interrupt in the NVIC. + // + switch(psSMBus->ui32I2CBase) + { + case I2C0_BASE: + { + MAP_IntEnable(INT_I2C0); + break; + } + + case I2C1_BASE: + { + MAP_IntEnable(INT_I2C1); + break; + } + + case I2C2_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C2_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C2_TM4C129); + } + break; + } + + case I2C3_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C3_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C3_TM4C129); + } + break; + } + + case I2C4_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C4_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C4_TM4C129); + } + break; + } + + case I2C5_BASE: + { + if(CLASS_IS_TM4C123) + { + MAP_IntEnable(INT_I2C5_TM4C123); + } + else if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C5_TM4C129); + } + break; + } + + case I2C6_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C6_TM4C129); + } + break; + } + + case I2C7_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C7_TM4C129); + } + break; + } + + case I2C8_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C8_TM4C129); + } + break; + } + + case I2C9_BASE: + { + if(CLASS_IS_TM4C129) + { + MAP_IntEnable(INT_I2C9_TM4C129); + } + break; + } + } +} + +//***************************************************************************** +// +//! Sets the slave address for an SMBus slave peripheral. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui8AddressNum specifies which address (primary or secondary) +//! \param ui8SlaveAddress is the address of the slave. +//! +//! This function sets the slave address. Both the primary and secondary +//! addresses can be set using this function. To set the primary address +//! (stored in I2CSOAR), ui8AddressNum should be '0'. To set the secondary +//! address (stored in I2CSOAR2), ui8AddressNum should be '1'. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveAddressSet(tSMBus *psSMBus, uint8_t ui8AddressNum, + uint8_t ui8SlaveAddress) +{ + // + // Write the slave address. + // + I2CSlaveAddressSet(psSMBus->ui32I2CBase, ui8AddressNum, ui8SlaveAddress); +} + +//***************************************************************************** +// +//! Sets a slave's UDID structure. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param pUDID is a pointer to the UDID configuration for the slave. This +//! is only needed if the slave is on a bus that uses ARP. +//! +//! This function sets the UDID for a slave instance. +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveUDIDSet(tSMBus *psSMBus, tSMBusUDID *pUDID) +{ + psSMBus->pUDID = pUDID; +} + +//***************************************************************************** +// +//! Initializes an I2C slave peripheral for SMBus functionality. +//! +//! \param psSMBus specifies the SMBus configuration structure. +//! \param ui32I2CBase specifies the base address of the I2C slave peripheral. +//! +//! This function initializes an I2C peripheral for SMBus slave use. The +//! instance-specific configuration structure is initialized to a set of known +//! values and the I2C peripheral is configured based on the input arguments. +//! +//! The default configuration of the SMBus slave uses automatic +//! acknowledgement. If manual acknowledgement is required, call +//! SMBusSlaveManualACKEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SMBusSlaveInit(tSMBus *psSMBus, uint32_t ui32I2CBase) +{ + // + // Initialize the configuration structure. + // + psSMBus->pUDID = 0; + psSMBus->ui32I2CBase = ui32I2CBase; + psSMBus->ui16Flags = 0; + psSMBus->ui8MasterState = SMBUS_STATE_IDLE; + psSMBus->ui8SlaveState = SMBUS_STATE_IDLE; + psSMBus->ui8OwnSlaveAddress = 0; + psSMBus->ui8TargetSlaveAddress = 0; + psSMBus->ui8CurrentCommand = 0; + psSMBus->ui8CalculatedCRC = 0; + psSMBus->ui8TxSize = 0; + psSMBus->ui8TxIndex = 0; + psSMBus->ui8RxSize = 0; + psSMBus->ui8RxIndex = 0; + + // + // Enable the I2C slave module. The slave is always enabled because the + // SMBus spec requires that all devices respond whne their slave address + // is put on the bus. + // + I2CSlaveEnable(psSMBus->ui32I2CBase); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/smbus.h b/utils/smbus.h new file mode 100644 index 0000000..c483025 --- /dev/null +++ b/utils/smbus.h @@ -0,0 +1,463 @@ +//***************************************************************************** +// +// smbus.h - Prototypes for the SMBus 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 Utility Library. +// +//***************************************************************************** + +#ifndef __SMBUS_H__ +#define __SMBUS_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup smbus_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure holds the SMBus Unique Device ID (UDID). For detailed +//! information, please refer to the SMBus Specification. +// +//***************************************************************************** +typedef struct +{ + // + //! Device capabilities field. This 8-bit field reports generic SMBus + //! capabilities such as address type for ARP. + // + uint8_t ui8DeviceCapabilities; + + // + //! Version Revision field. This 8-bit field reports UDID revision + //! information as well as some vendor-specific things such as silicon + //! revision. + // + uint8_t ui8Version; + + // + //! Vendor ID. This 16-bit field contains the manufacturer's ID as + //! assigned by the SBS Implementers' Forum of the PCI SIG. + // + uint16_t ui16VendorID; + + // + //! Device ID. This 16-bit field contains the device ID assigned by the + //! device manufacturer. + // + uint16_t ui16DeviceID; + + // + //! Interface. This 16-bit field identifies the protocol layer interfaces + //! supported over the SMBus connection. + // + uint16_t ui16Interface; + + // + //! Subsystem Vendor ID. This 16-bit field holds additional information + //! that may be derived from the vendor ID or other information. + // + uint16_t ui16SubSystemVendorID; + + // + //! Subsystem Device ID. This 16-bit field holds additional information + //! that may be derived from the device ID or other information. + // + uint16_t ui16SubSystemDeviceID; + + // + //! Vendor-specific ID. This 32-bit field contains a unique number that + //! can be assigned per device by the manufacturer. + // + uint32_t ui32VendorSpecificID; +} +tSMBusUDID; + +//***************************************************************************** +// +//! This structure contains the state of a single instance of an SMBus module. +//! Master and slave instances require unique configuration structures. +// +//***************************************************************************** +typedef struct +{ + // + //! The SMBus Unique Device ID (UDID) for this SMBus instance. If + //! operating as a host, master-only, or on a bus that does not use Address + //! Resolution Protocol (ARP), this is not required. This member can be + //! set via a direct structure access or using the SMBusSlaveInit + //! function. For detailed information about the UDID, refer to the SMBus + //! spec. + // + tSMBusUDID *pUDID; + + // + //! The base address of the I2C master peripheral. This member can be set + //! via a direct structure access or using the SMBusMasterInit or + //! SMBusSlaveInit functions. + // + uint32_t ui32I2CBase; + + // + //! The address of the data buffer used for transmit operations. For + //! master operations, this member is set by the SMBusMasterxxxx functions + //! that pass a buffer pointer (for example, SMBusMasterBlockWrite). For + //! slave operations, this member can be set via direct structure access or + //! using the SMBusSlaveTxBufferSet function. + // + uint8_t *pui8TxBuffer; + + // + //! The address of the data buffer used for receive operations. For master + //! operations, this member is set by the SMBusMasterxxxx functions that + //! pass a buffer pointer (for example, SMBusMasterBlockRead). For slave + //! operations, this member can be set via direct structure access or using + //! the SMBusSlaveRxBufferSet function. + // + uint8_t *pui8RxBuffer; + + // + //! The amount of data to transmit from pui8TxBuffer. For master + //! operations this member is set by the SMBusMasterxxxx functions either + //! via an input argument (example SMBusMasterByteWordWrite) or explicitly + //! (example SMBusMasterSendByte). In master mode, this member should not + //! be accessed or modified by the application. For slave operations, this + //! member can be set via direct structure access of using the + //! SMBusSlaveTxBufferSet function. + // + uint8_t ui8TxSize; + + // + //! The current index in the transmit buffer. This member should not be + //! accessed or modified by the application. + // + uint8_t ui8TxIndex; + + // + //! The amount of data to receive into pui8RxBuffer. For master + //! operations, this member is set by the SMBusMasterxxxx functions either + //! via an input argument (example SMBusMasterByteWordRead), explicitly + //! (example SMBusMasterReceiveByte), or by the slave (example + //! SMBusMasterBlockRead). In master mode, this member should not be + //! accessed or modified by the application. For slave operations, this + //! member can be set via direct structure access of using the + //! SMBusSlaveRxBufferSet function. + // + uint8_t ui8RxSize; + + // + //! The current index in the receive buffer. This member should not be + //! accessed or modified by the application. + // + uint8_t ui8RxIndex; + + // + //! The active slave address of the I2C peripheral on the device. + //! When using dual address in slave mode, the active address is store + //! here. In master mode, this member is not used. This member is updated + //! as requests come in from the master. + // + uint8_t ui8OwnSlaveAddress; + + // + //! The address of the targeted slave device. In master mode, this member + //! is set by the ui8TargetSlaveAddress argument in the SMBusMasterxxxx + //! transfer functions. In slave mode, it is not used. This member should + //! not be modified by the application. + // + uint8_t ui8TargetSlaveAddress; + + // + //! The last used command. In master mode, this member is set by the + //! ui8Command argument in the SMBusMasterxxxx transfer functions. In + //! slave mode, the first received byte will always be considered the + //! command. This member should not be modified by the application. + // + uint8_t ui8CurrentCommand; + + // + //! The running CRC calculation used for transfers that require Packet + //! Error Checking (PEC). This member is updated by the SMBus software and + //! should not be modified by the application. + // + uint8_t ui8CalculatedCRC; + + // + //! The received CRC calculation used for transfers that require Packet + //! Error Checking (PEC). This member is updated by the SMBus software and + //! should not be modified by the application. + // + uint8_t ui8ReceivedCRC; + + // + //! The current state of the SMBusMasterISRProcess state machine. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8MasterState; + + // + //! The current state of the SMBusSlaveISRProcess state machine. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8SlaveState; + + // + //! Flags used for various items in the SMBus state machines for different + //! transaction types and status. + //! + //! FLAG_PEC can be modified via the SMBusPECEnable or SMBusPECDisable + //! functions or via direct structure access. + //! + //! FLAG_BLOCK_TRANSFER can be set via the SMBusSlaveBlockTransferEnable + //! function and is cleared automatically by the SMBusSlaveTransferInit + //! function or manually using the SMBusSlaveBlockTransferDisable function. + //! + //! FLAG_RAW_I2C can be modified via the SMBusSlaveI2CEnable or + //! SMBusSlaveI2CDisable functions or via direct structure access. + //! + //! FLAG_TRANSFER_IN_PROGRESS should not be modified by the application, + //! but can be read via the SMBusStatusGet function. + //! + //! FLAG_PROCESS_CALL can be set via the SMBusSlaveProcessCallEnable + //! function and is cleared automatically by the SMBusSlaveTransferInit + //! function or manually using the SMBusSlaveProcessCallDisable function. + //! + //! FLAG_ADDRESS_RESOLVED is only used by an SMBus Slave that supports ARP. + //! This flag can be modified via the SMBusSlaveARPFlagARSet function and + //! read via SMBusSlaveARPFlagARGet. It can also be modified by direct + //! structure access. + //! + //! FLAG_ADDRESS_VALID is only used by an SMBus Slave that supports ARP. + //! This flag can be modified via the SMBusSlaveARPFlagAVSet function and + //! read via SMBusSlaveARPFlagAVGet. It can also be modified by direct + //! structure access. + //! + //! FLAG_ARP is used to indicate that ARP is currently active. This flag + //! is not used by the SMBus stack and can (optionally) be used by the + //! application to keep track of the ARP session. + // + uint16_t ui16Flags; +} +tSMBus; + +//***************************************************************************** +// +// ! Return codes. +// +//***************************************************************************** +typedef enum +{ + SMBUS_OK = 0, // General "OK" return code + SMBUS_TIMEOUT, // Master detected bus timeout from slave + SMBUS_PERIPHERAL_BUSY, // The I2C peripheral is currently in use + SMBUS_BUS_BUSY, // The I2C bus is currently in use + SMBUS_ARB_LOST, // Bus arbitration was lost (master mode) + SMBUS_ADDR_ACK_ERROR, // In master mode, the address was NAK'd + SMBUS_DATA_ACK_ERROR, // Data transfer was NAK'd by receiver + SMBUS_PEC_ERROR, // PEC mismatch occurred + SMBUS_DATA_SIZE_ERROR, // Data size error has occurred + SMBUS_MASTER_ERROR, // Error occurred in the master ISR + SMBUS_SLAVE_ERROR, // Error occurred in the slave ISR + SMBUS_SLAVE_QCMD_0, // Slave transaction is Quick Command with + // data value 0. + SMBUS_SLAVE_QCMD_1, // Slave transaction is Quick Command with + // data value 1. + SMBUS_SLAVE_FIRST_BYTE, // The first byte has been received + SMBUS_SLAVE_ADDR_PRIMARY, // Primary address was detected + SMBUS_SLAVE_ADDR_SECONDARY, // Secondary address was detected + SMBUS_TRANSFER_IN_PROGRESS, // A transfer is currently in progress + SMBUS_TRANSFER_COMPLETE, // The last active transfer is complete + SMBUS_SLAVE_NOT_READY, // A slave transmit has been requested, but is + // not ready (TX buffer not set). + SMBUS_FIFO_ERROR, // A master receive operation did not receive + // enough data from the slave. +} +tSMBusStatus; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// ARP Commands +// +//***************************************************************************** +#define SMBUS_CMD_PREPARE_TO_ARP 0x01 +#define SMBUS_CMD_ARP_RESET_DEVICE 0x02 +#define SMBUS_CMD_ARP_GET_UDID 0x03 +#define SMBUS_CMD_ARP_ASSIGN_ADDRESS 0x04 + +//***************************************************************************** +// +// Fixed addresses defined by the SMBus specification. +// +//***************************************************************************** +#define SMBUS_ADR_HOST 0x08 +#define SMBUS_ADR_SMART_BATTERY_CHARGER 0x09 +#define SMBUS_ADR_SMART_BATTERY_SELECTOR 0x0A +#define SMBUS_ADR_SMART_BATTERY 0x0B +#define SMBUS_ADR_DEFAULT_DEVICE 0x61 + +//***************************************************************************** +// +// API Function prototypes +// +//***************************************************************************** +extern void SMBusPECEnable(tSMBus *psSMBus); +extern void SMBusPECDisable(tSMBus *psSMBus); +extern void SMBusARPEnable(tSMBus *psSMBus); +extern void SMBusARPDisable(tSMBus *psSMBus); +extern tSMBusStatus SMBusStatusGet(tSMBus *psSMBus); +extern void SMBusARPUDIDPacketEncode(tSMBusUDID *pUDID, + uint8_t ui8Address, + uint8_t *pui8Data); +extern void SMBusARPUDIDPacketDecode(tSMBusUDID *pUDID, + uint8_t *pui8Address, + uint8_t *pui8Data); +extern uint8_t SMBusRxPacketSizeGet(tSMBus *psSMBus); +extern void SMBusUDIDDataGet(tSMBus *psSMBus, tSMBusUDID *pUDID); +extern tSMBusStatus SMBusMasterQuickCommand(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + bool bData); +extern tSMBusStatus SMBusMasterByteSend(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Data); +extern tSMBusStatus SMBusMasterByteReceive(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterByteWordWrite(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterBlockWrite(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterByteWordRead(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterBlockRead(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterProcessCall(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8TxData, + uint8_t *pui8RxData); +extern tSMBusStatus SMBusMasterBlockProcessCall(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t ui8Command, + uint8_t *pui8TxData, + uint8_t ui8TxSize, + uint8_t *pui8RxData); +extern tSMBusStatus SMBusMasterHostNotify(tSMBus *psSMBus, + uint8_t ui8OwnSlaveAddress, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterI2CWrite(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterI2CRead(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8Data, + uint8_t ui8Size); +extern tSMBusStatus SMBusMasterI2CWriteRead(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8TxData, + uint8_t ui8TxSize, + uint8_t *pui8RxData, + uint8_t ui8RxSize); +extern tSMBusStatus SMBusMasterARPGetUDIDGen(tSMBus *psSMBus, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterARPGetUDIDDir(tSMBus *psSMBus, + uint8_t ui8TargetAddress, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterARPResetDeviceGen(tSMBus *psSMBus); +extern tSMBusStatus SMBusMasterARPResetDeviceDir(tSMBus *psSMBus, + uint8_t ui8TargetAddress); +extern tSMBusStatus SMBusMasterARPAssignAddress(tSMBus *psSMBus, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterARPNotifyMaster(tSMBus *psSMBus, + uint8_t *pui8Data); +extern tSMBusStatus SMBusMasterARPPrepareToARP(tSMBus *psSMBus); +extern tSMBusStatus SMBusMasterIntProcess(tSMBus *psSMBus); +extern void SMBusMasterIntEnable(tSMBus *psSMBus); +extern void SMBusMasterInit(tSMBus *psSMBus, uint32_t ui32I2CBase, + uint32_t ui32SMBusClock); +extern void SMBusSlaveTxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data, + uint8_t ui8Size); +extern void SMBusSlaveRxBufferSet(tSMBus *psSMBus, uint8_t *pui8Data, + uint8_t ui8Size); +extern uint8_t SMBusSlaveCommandGet(tSMBus *psSMBus); +extern void SMBusSlaveProcessCallEnable(tSMBus *psSMBus); +extern void SMBusSlaveProcessCallDisable(tSMBus *psSMBus); +extern void SMBusSlaveBlockTransferEnable(tSMBus *psSMBus); +extern void SMBusSlaveBlockTransferDisable(tSMBus *psSMBus); +extern void SMBusSlaveI2CEnable(tSMBus *psSMBus); +extern void SMBusSlaveI2CDisable(tSMBus *psSMBus); +extern void SMBusSlaveARPFlagARSet(tSMBus *psSMBus, bool bValue); +extern bool SMBusSlaveARPFlagARGet(tSMBus *psSMBus); +extern void SMBusSlaveARPFlagAVSet(tSMBus *psSMBus, bool bValue); +extern bool SMBusSlaveARPFlagAVGet(tSMBus *psSMBus); +extern void SMBusSlaveTransferInit(tSMBus *psSMBus); +extern tSMBusStatus SMBusSlaveIntProcess(tSMBus *psSMBus); +extern tSMBusStatus SMBusSlaveDataSend(tSMBus *psSMBus); +extern void SMBusSlaveACKSend(tSMBus *psSMBus, bool bACK); +extern void SMBusSlaveManualACKEnable(tSMBus *psSMBus); +extern void SMBusSlaveManualACKDisable(tSMBus *psSMBus); +extern bool SMBusSlaveManualACKStatusGet(tSMBus *psSMBus); +extern tSMBusStatus SMBusSlaveIntAddressGet(tSMBus *psSMBus); +extern void SMBusSlaveIntEnable(tSMBus *psSMBus); +extern void SMBusSlaveUDIDSet(tSMBus *psSMBus, tSMBusUDID *pUDID); +extern void SMBusSlaveAddressSet(tSMBus *psSMBus, uint8_t ui8AddressNum, + uint8_t ui8SlaveAddress); +extern void SMBusSlaveInit(tSMBus *psSMBus, uint32_t ui32I2CBase); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SMBUS_H__ diff --git a/utils/softi2c.c b/utils/softi2c.c new file mode 100644 index 0000000..ab3e5a6 --- /dev/null +++ b/utils/softi2c.c @@ -0,0 +1,1321 @@ +//***************************************************************************** +// +// softi2c.c - Driver for the SoftI2C. +// +// 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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softi2c_api +//! @{ +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/gpio.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "utils/softi2c.h" + +//***************************************************************************** +// +// The states in the SoftI2C state machine. The code depends upon the fact +// that the value of STATE_X1 is exactly one greater than STATE_X0 (for any +// value of X and for any following digit)...however there is no dependence on +// the values of STATE_Xn and STATE_Yn. +// +//***************************************************************************** +#define SOFTI2C_STATE_IDLE 0 +#define SOFTI2C_STATE_START0 1 +#define SOFTI2C_STATE_START1 2 +#define SOFTI2C_STATE_START2 3 +#define SOFTI2C_STATE_START3 4 +#define SOFTI2C_STATE_START4 5 +#define SOFTI2C_STATE_START5 6 +#define SOFTI2C_STATE_START6 7 +#define SOFTI2C_STATE_START7 8 +#define SOFTI2C_STATE_ADDR0 9 +#define SOFTI2C_STATE_ADDR1 10 +#define SOFTI2C_STATE_ADDR2 11 +#define SOFTI2C_STATE_ADDR3 12 +#define SOFTI2C_STATE_SEND0 13 +#define SOFTI2C_STATE_SEND1 14 +#define SOFTI2C_STATE_SEND2 15 +#define SOFTI2C_STATE_SEND3 16 +#define SOFTI2C_STATE_RECV0 17 +#define SOFTI2C_STATE_RECV1 18 +#define SOFTI2C_STATE_RECV2 19 +#define SOFTI2C_STATE_RECV3 20 +#define SOFTI2C_STATE_STOP0 21 +#define SOFTI2C_STATE_STOP1 22 +#define SOFTI2C_STATE_STOP2 23 +#define SOFTI2C_STATE_STOP3 24 +#define SOFTI2C_STATE_STOP4 25 + +//***************************************************************************** +// +// The flags in the SoftI2C ui8Flags structure member. The first four flags, +// RUN, START, STOP, and ACK, must match with the definitions of the +// SOFTI2C_CMD_* commands in softi2c.h. +// +//***************************************************************************** +#define SOFTI2C_FLAG_RUN 0 +#define SOFTI2C_FLAG_START 1 +#define SOFTI2C_FLAG_STOP 2 +#define SOFTI2C_FLAG_ACK 3 +#define SOFTI2C_FLAG_ADDR_ACK 5 +#define SOFTI2C_FLAG_DATA_ACK 6 +#define SOFTI2C_FLAG_RECEIVE 7 + +//***************************************************************************** +// +//! Performs the periodic update of the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function performs the periodic, time-based updates to the SoftI2C +//! module. The transmission and reception of data over the SoftI2C link is +//! performed by the state machine in this function. +//! +//! This function must be called at four times the desired SoftI2C clock rate. +//! For example, to run the SoftI2C clock at 10 KHz, this function must be +//! called at a 40 KHz rate. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CTimerTick(tSoftI2C *psI2C) +{ + // + // Determine the current state of the state machine. + // + switch(psI2C->ui8State) + { + // + // The state machine is idle. + // + case SOFTI2C_STATE_IDLE: + { + // + // See if the START flag is set, indicating that a start condition + // should be generated. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_START) == 1) + { + // + // Based on the current state of the SCL and SDA pins, pick the + // appropriate place within the state machine to begin the + // start/repeated-start signalling. + // + if(HWREG(psI2C->ui32SCLGPIO) != 0) + { + psI2C->ui8State = SOFTI2C_STATE_START4; + } + else if(HWREG(psI2C->ui32SDAGPIO) == 0) + { + psI2C->ui8State = SOFTI2C_STATE_START0; + } + else + { + psI2C->ui8State = SOFTI2C_STATE_START2; + } + } + + // + // Otherwise, see if the RUN flag is set, indicating that a data + // byte should be transferred. + // + else if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RUN) == 1) + { + // + // Start the transfer from the first bit. + // + psI2C->ui8CurrentBit = 0; + + // + // See if a byte should be sent or received. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RECEIVE) == 0) + { + // + // A byte should be sent. + // + psI2C->ui8State = SOFTI2C_STATE_SEND0; + } + else + { + // + // A byte should be received. Clear out the receive data + // buffer in preparation for receiving the new byte. + // + psI2C->ui8Data = 0; + psI2C->ui8State = SOFTI2C_STATE_RECV0; + } + } + + // + // Otherwise, see if the STOP flag is set, indicating that a stop + // condition should be generated. + // + else if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Generate a stop condition. + // + psI2C->ui8State = SOFTI2C_STATE_STOP0; + } + + // + // See if the SoftI2C state machine has left the idle state. + // + if(psI2C->ui8State != SOFTI2C_STATE_IDLE) + { + // + // The address and data ACK error flags should be cleared; they + // will be set if appropriate while the current command is + // being executed. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_ADDR_ACK) = 0; + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_DATA_ACK) = 0; + } + + // + // This state has been handled. + // + break; + } + + // + // The beginning of the start condition sequence when SDA and SCL are + // low. SDA must be driven high prior to driving SCL high so that a + // repeated-start is generated, instead of a stop then start. + // + case SOFTI2C_STATE_START0: + { + // + // Set SDA high. + // + HWREG(psI2C->ui32SDAGPIO) = 255; + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_START1; + + // + // This state has been handled. + // + break; + } + + // + // Each of these states exists only to provide some timing delay in + // order to conform with the signalling requirements of I2C. This + // depends upon STATE_Xn and STATE_X(n+1) being consecutively numbered. + // + case SOFTI2C_STATE_START1: + case SOFTI2C_STATE_START3: + case SOFTI2C_STATE_START5: + case SOFTI2C_STATE_STOP1: + case SOFTI2C_STATE_STOP3: + { + // + // Advance to the next state. + // + psI2C->ui8State++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, SCL must be driven high. This depends upon + // STATE_Xn and STATE_X(n+1) being consecutively numbered. + // + case SOFTI2C_STATE_START2: + case SOFTI2C_STATE_ADDR1: + case SOFTI2C_STATE_SEND1: + case SOFTI2C_STATE_RECV1: + case SOFTI2C_STATE_STOP2: + { + // + // Set SCL high. + // + HWREG(psI2C->ui32SCLGPIO) = 255; + + // + // Advance to the next state. + // + psI2C->ui8State++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, SDA must be driven low. This depends upon + // STATE_Xn and STATE_X(n+1) being consecutively numbered. + // + case SOFTI2C_STATE_START4: + case SOFTI2C_STATE_STOP0: + { + // + // Set SDA low. + // + HWREG(psI2C->ui32SDAGPIO) = 0; + + // + // Advance to the next state. + // + psI2C->ui8State++; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. + // + case SOFTI2C_STATE_START6: + { + // + // Set SCL low. + // + HWREG(psI2C->ui32SCLGPIO) = 0; + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_START7; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the start condition has been generated. + // + case SOFTI2C_STATE_START7: + { + // + // Start with the first bit of the address. + // + psI2C->ui8CurrentBit = 0; + + // + // Advance to the address output state. + // + psI2C->ui8State = SOFTI2C_STATE_ADDR0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the next bit of the slave address must be sent. + // + case SOFTI2C_STATE_ADDR0: + { + // + // See if this is one of the first seven bits of the address phase. + // + if(psI2C->ui8CurrentBit < 7) + { + // + // Write the next bit of the slave address to SDA. + // + HWREG(psI2C->ui32SDAGPIO) = + ((psI2C->ui8SlaveAddr & + (1 << (6 - psI2C->ui8CurrentBit))) ? 255 : 0); + } + + // + // Otherwise, see if this is the eight bit of the address phase + // (which is the read/not write bit). + // + else if(psI2C->ui8CurrentBit == 7) + { + // + // Write the read/not write bit to SDA. + // + HWREG(psI2C->ui32SDAGPIO) = + (HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RECEIVE) ? + 255 : 0); + } + + // + // Otherwise, this is the ninth bit of the address phase (in other + // words, the ACK bit). + // + else + { + // + // Change the SDA GPIO into an input so that the ACK or NAK + // provided by the slave can be read. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_ADDR1; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, wait until SCL has gone high (it has been + // released by the SoftI2C master, but may be held low by the slave). + // This depends upon STATE_Xn and STATE_X(n+1) being consecutively + // numbered. + // + case SOFTI2C_STATE_ADDR2: + case SOFTI2C_STATE_SEND2: + case SOFTI2C_STATE_RECV2: + { + // + // See if SCL has gone high. + // + if(HWREG(psI2C->ui32SCLGPIO) != 0) + { + // + // Advance to the next state now that SCL has gone high. + // + psI2C->ui8State++; + } + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. If on the ninth bit of the + // address transfer, the ACK/NAK status is read from the slave. + // + case SOFTI2C_STATE_ADDR3: + { + // + // See if this is the ninth bit of the address phase (in other + // words, the ACK bit). + // + if(psI2C->ui8CurrentBit == 8) + { + // + // See if the SDA line is high. + // + if(HWREG(psI2C->ui32SDAGPIO) != 0) + { + // + // Since the SDA line is high, the address byte has not + // been ACKed by any slave. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_ADDR_ACK) = 1; + } + + // + // Change the SDA GPIO back into an output. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // The start phase (start or repeated-start, plus the address + // byte) have completed, so clear the START flag. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_START) = 0; + + // + // See if the RUN flag is set, indicating that a data byte + // should be transferred as well. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RUN) == 1) + { + // + // Reset the current bit to zero for the start of the data + // phase. + // + psI2C->ui8CurrentBit = 0; + + // + // See if the data byte is being sent or received. + // + if(HWREGBITB(&(psI2C->ui8Flags), + SOFTI2C_FLAG_RECEIVE) == 0) + { + // + // The data byte is being sent, so advance to the data + // send state. + // + psI2C->ui8State = SOFTI2C_STATE_SEND0; + } + else + { + // + // The data byte is being received, so clear the data + // buffer and advance to the data receive state. + // + psI2C->ui8Data = 0; + psI2C->ui8State = SOFTI2C_STATE_RECV0; + } + } + + // + // Otherwise, see if the STOP flag is set, indicating that a + // stop condition should be generated. + // + else if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + psI2C->ui8State = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + psI2C->ui8IntStatus = 1; + + // + // Advance to the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the address should be transferred. + // + else + { + // + // Increment the bit count. + // + psI2C->ui8CurrentBit++; + + // + // Advance to the address tranfer state. + // + psI2C->ui8State = SOFTI2C_STATE_ADDR0; + } + + // + // Set SCL low. + // + HWREG(psI2C->ui32SCLGPIO) = 0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the next bit of the data byte must be sent. + // + case SOFTI2C_STATE_SEND0: + { + // + // See if this is one of the first eight bits of the data phase. + // + if(psI2C->ui8CurrentBit < 8) + { + // + // Write the next bit of the data byte to SDA. + // + HWREG(psI2C->ui32SDAGPIO) = + ((psI2C->ui8Data & + (1 << (7 - psI2C->ui8CurrentBit))) ? 255 : 0); + } + + // + // Otherwise, this is the ninth bit of the data phase (in other + // words, the ACK bit). + // + else + { + // + // Change the SDA GPIO into an input so that the ACK or NAK + // provided by the slave can be read. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_SEND1; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. If on the ninth bit of the + // data transfer, the ACK/NAK status is read from the slave. + // + case SOFTI2C_STATE_SEND3: + { + // + // See if this is the ninth bit of the data phase (in other words, + // the ACK bit). + // + if(psI2C->ui8CurrentBit == 8) + { + // + // See if the SDA line is high. + // + if(HWREG(psI2C->ui32SDAGPIO) != 0) + { + // + // Since the SDA line is high, the data byte has not been + // ACKed by the slave. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_DATA_ACK) = 1; + } + + // + // Change the SDA GPIO back into an output. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // The data phase has completed, so clear the RUN flag. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RUN) = 0; + + // + // See if the STOP flag is set, indicating that a stop + // condition should be generated. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + psI2C->ui8State = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + psI2C->ui8IntStatus = 1; + + // + // Advance to the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the data should be transferred. + // + else + { + // + // Increment the bit count. + // + psI2C->ui8CurrentBit++; + + // + // Advance to the data transmit state. + // + psI2C->ui8State = SOFTI2C_STATE_SEND0; + } + + // + // Set SCL low. + // + HWREG(psI2C->ui32SCLGPIO) = 0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the next bit of the data byte must be received. + // + case SOFTI2C_STATE_RECV0: + { + // + // See if this is the first bit of the data phase. + // + if(psI2C->ui8CurrentBit == 0) + { + // + // Change the SDA GPIO into an input so that the data provided + // by the slave can be read. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Otherwise, see if this is the ninth bit of the data phase (in + // other words, the ACK bit). + // + else if(psI2C->ui8CurrentBit == 8) + { + // + // Change the SDA GPIO into an output so that the ACK bit can + // be driven to the slave. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // See if this byte should be ACKed or NAKed. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_ACK) == 1) + { + // + // Drive SDA low to ACK the data byte. + // + HWREG(psI2C->ui32SDAGPIO) = 0; + } + else + { + // + // Allow SDA to get pulled high to NAK the data byte. + // + HWREG(psI2C->ui32SDAGPIO) = 255; + } + } + + // + // Advance to the next state. + // + psI2C->ui8State = SOFTI2C_STATE_RECV1; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. For the first eight bits of + // the data transfer, the data bits are read from the slave. + // + case SOFTI2C_STATE_RECV3: + { + // + // See if this is the ninth bit of the data phase (in other words, + // the ACK bit). + // + if(psI2C->ui8CurrentBit == 8) + { + // + // The data phase has completed, so clear the RUN flag. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RUN) = 0; + + // + // See if the STOP flag is set, indicating that a stop + // condition should be generated. + // + if(HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + psI2C->ui8State = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + psI2C->ui8IntStatus = 1; + + // + // Advance to the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the data should be transferred. + // + else + { + // + // Read the next bit of data from the SDA line. + // + psI2C->ui8Data |= (HWREG(psI2C->ui32SDAGPIO) ? + (1 << (7 - psI2C->ui8CurrentBit)) : 0); + + // + // Increment the bit count. + // + psI2C->ui8CurrentBit++; + + // + // Advance to the data receive state. + // + psI2C->ui8State = SOFTI2C_STATE_RECV0; + } + + // + // Set SCL low. + // + HWREG(psI2C->ui32SCLGPIO) = 0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SDA must be driven high to create the stop condition. + // + case SOFTI2C_STATE_STOP4: + { + // + // Set SDA high to create the stop condition. + // + HWREG(psI2C->ui32SDAGPIO) = 255; + + // + // The stop condition has been generated, so clear the STOP flag. + // + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_STOP) = 0; + + // + // Since the requested operations have completed, set the SoftI2C + // ``interrupt''. + // + psI2C->ui8IntStatus = 1; + + // + // Advance to the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; + + // + // This state has been handled. + // + break; + } + } + + // + // Call the "interrupt" callback while there are enabled "interrupts" + // asserted. By calling in a loop until the "interrupts" are no longer + // asserted, this mimics the behavior of a real hardware implementation of + // the I2C peripheral. + // + while(((psI2C->ui8IntStatus & psI2C->ui8IntMask) != 0) && + (psI2C->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + psI2C->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Initializes the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function initializes operation of the SoftI2C module. After +//! successful initialization of the SoftI2C module, the software I2C bus is in +//! the idle state. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CInit(tSoftI2C *psI2C) +{ + // + // Configure the SCL pin. + // + MAP_GPIODirModeSet(psI2C->ui32SCLGPIO & 0xfffff000, + (psI2C->ui32SCLGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + MAP_GPIOPadConfigSet(psI2C->ui32SCLGPIO & 0xfffff000, + (psI2C->ui32SCLGPIO & 0x00000fff) >> 2, + GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_OD); + + // + // Set the SCL pin high. + // + HWREG(psI2C->ui32SCLGPIO) = 255; + + // + // Configure the SDA pin. + // + MAP_GPIODirModeSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + MAP_GPIOPadConfigSet(psI2C->ui32SDAGPIO & 0xfffff000, + (psI2C->ui32SDAGPIO & 0x00000fff) >> 2, + GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_OD); + + // + // Set the SDA pin high. + // + HWREG(psI2C->ui32SDAGPIO) = 255; + + // + // The ``interrupt'' is not asserted at the start. + // + psI2C->ui8IntStatus = 0; + + // + // There are no flags at the start. + // + psI2C->ui8Flags = 0; + + // + // Start the SoftI2C state machine in the idle state. + // + psI2C->ui8State = SOFTI2C_STATE_IDLE; +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param pfnCallback is a pointer to the callback function. +//! +//! This function sets the address of the callback function that is called when +//! there is an ``interrupt'' produced by the SoftI2C module. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CCallbackSet(tSoftI2C *psI2C, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + psI2C->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftI2C SCL signal. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftI2C SCL signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CSCLGPIOSet(tSoftI2C *psI2C, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the SCL signal. + // + psI2C->ui32SCLGPIO = ui32Base + (ui8Pin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftI2C SDA signal. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftI2C SDA signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CSDAGPIOSet(tSoftI2C *psI2C, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the SDA signal. + // + psI2C->ui32SDAGPIO = ui32Base + (ui8Pin << 2); +} + +//***************************************************************************** +// +//! Enables the SoftI2C ``interrupt''. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! Enables the SoftI2C ``interrupt'' source. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntEnable(tSoftI2C *psI2C) +{ + // + // Enable the master interrupt. + // + psI2C->ui8IntMask = 1; +} + +//***************************************************************************** +// +//! Disables the SoftI2C ``interrupt''. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! Disables the SoftI2C ``interrupt'' source. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntDisable(tSoftI2C *psI2C) +{ + // + // Disable the master interrupt. + // + psI2C->ui8IntMask = 0; +} + +//***************************************************************************** +// +//! Gets the current SoftI2C ``interrupt'' status. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param bMasked is \b false if the raw ``interrupt'' status is requested and +//! \b true if the masked ``interrupt'' status is requested. +//! +//! This returns the ``interrupt'' status for the SoftI2C module. Either the +//! raw ``interrupt'' status or the status of ``interrupts'' that are allowed +//! to reflect to the processor can be returned. +//! +//! \return The current interrupt status, returned as \b true if active +//! or \b false if not active. +// +//***************************************************************************** +bool +SoftI2CIntStatus(tSoftI2C *psI2C, bool bMasked) +{ + // + // Return either the interrupt status or the raw interrupt status as + // requested. + // + if(bMasked) + { + return((psI2C->ui8IntStatus & psI2C->ui8IntMask) ? true : false); + } + else + { + return(psI2C->ui8IntStatus ? true : false); + } +} + +//***************************************************************************** +// +//! Clears the SoftI2C ``interrupt''. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! The SoftI2C ``interrupt'' source is cleared, so that it no longer asserts. +//! This function must be called in the ``interrupt'' handler to keep it from +//! being called again immediately on exit. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntClear(tSoftI2C *psI2C) +{ + // + // Clear the SoftI2C interrupt source. + // + psI2C->ui8IntStatus = 0; +} + +//***************************************************************************** +// +//! Sets the address that the SoftI2C module places on the bus. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui8SlaveAddr 7-bit slave address +//! \param bReceive flag indicating the type of communication with the slave. +//! +//! This function sets the address that the SoftI2C module places on the bus +//! when initiating a transaction. When the \e bReceive parameter is set to +//! \b true, the address indicates that the SoftI2C moudle is initiating a read +//! from the slave; otherwise the address indicates that the SoftI2C module is +//! initiating a write to the slave. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CSlaveAddrSet(tSoftI2C *psI2C, uint8_t ui8SlaveAddr, + bool bReceive) +{ + // + // Check the arguments. + // + ASSERT(!(ui8SlaveAddr & 0x80)); + + // + // Set the address of the slave with which the master will communicate. + // + psI2C->ui8SlaveAddr = ui8SlaveAddr; + + // + // Set a flag to indicate if this is a transmit or receive. + // + if(bReceive) + { + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RECEIVE) = 1; + } + else + { + HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_RECEIVE) = 0; + } +} + +//***************************************************************************** +// +//! Indicates whether or not the SoftI2C module is busy. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function returns an indication of whether or not the SoftI2C module is +//! busy transmitting or receiving data. +//! +//! \return Returns \b true if the SoftI2C module is busy; otherwise, returns +//! \b false. +// +//***************************************************************************** +bool +SoftI2CBusy(tSoftI2C *psI2C) +{ + // + // Return the busy status. + // + if(psI2C->ui8State != SOFTI2C_STATE_IDLE) + { + return(true); + } + else + { + return(false); + } +} + +//***************************************************************************** +// +//! Controls the state of the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui32Cmd command to be issued to the SoftI2C module. +//! +//! This function is used to control the state of the SoftI2C module send and +//! receive operations. The \e ui8Cmd parameter can be one of the following +//! values: +//! +//! - \b SOFTI2C_CMD_SINGLE_SEND +//! - \b SOFTI2C_CMD_SINGLE_RECEIVE +//! - \b SOFTI2C_CMD_BURST_SEND_START +//! - \b SOFTI2C_CMD_BURST_SEND_CONT +//! - \b SOFTI2C_CMD_BURST_SEND_FINISH +//! - \b SOFTI2C_CMD_BURST_SEND_ERROR_STOP +//! - \b SOFTI2C_CMD_BURST_RECEIVE_START +//! - \b SOFTI2C_CMD_BURST_RECEIVE_CONT +//! - \b SOFTI2C_CMD_BURST_RECEIVE_FINISH +//! - \b SOFTI2C_CMD_BURST_RECEIVE_ERROR_STOP +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CControl(tSoftI2C *psI2C, uint32_t ui32Cmd) +{ + // + // Check the arguments. + // + ASSERT((ui32Cmd == SOFTI2C_CMD_SINGLE_SEND) || + (ui32Cmd == SOFTI2C_CMD_SINGLE_RECEIVE) || + (ui32Cmd == SOFTI2C_CMD_BURST_SEND_START) || + (ui32Cmd == SOFTI2C_CMD_BURST_SEND_CONT) || + (ui32Cmd == SOFTI2C_CMD_BURST_SEND_FINISH) || + (ui32Cmd == SOFTI2C_CMD_BURST_SEND_ERROR_STOP) || + (ui32Cmd == SOFTI2C_CMD_BURST_RECEIVE_START) || + (ui32Cmd == SOFTI2C_CMD_BURST_RECEIVE_CONT) || + (ui32Cmd == SOFTI2C_CMD_BURST_RECEIVE_FINISH) || + (ui32Cmd == SOFTI2C_CMD_BURST_RECEIVE_ERROR_STOP)); + + // + // Send the command. + // + psI2C->ui8Flags = (psI2C->ui8Flags & 0xf0) | ui32Cmd; +} + +//***************************************************************************** +// +//! Gets the error status of the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function is used to obtain the error status of the SoftI2C module send +//! and receive operations. +//! +//! \return Returns the error status, as one of \b SOFTI2C_ERR_NONE, +//! \b SOFTI2C_ERR_ADDR_ACK, or \b SOFTI2C_ERR_DATA_ACK. +// +//***************************************************************************** +uint32_t +SoftI2CErr(tSoftI2C *psI2C) +{ + // + // If the SoftI2C is busy, there is no error to report. + // + if(psI2C->ui8State != SOFTI2C_STATE_IDLE) + { + return(SOFTI2C_ERR_NONE); + } + + // + // Return any errors that may have occurred. + // + return((HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_ADDR_ACK) ? + SOFTI2C_ERR_ADDR_ACK : 0) | + (HWREGBITB(&(psI2C->ui8Flags), SOFTI2C_FLAG_DATA_ACK) ? + SOFTI2C_ERR_DATA_ACK : 0)); +} + +//***************************************************************************** +// +//! Transmits a byte from the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! \param ui8Data data to be transmitted from the SoftI2C module. +//! +//! This function places the supplied data into SoftI2C module in preparation +//! for being transmitted via an appropriate call to SoftI2CControl(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CDataPut(tSoftI2C *psI2C, uint8_t ui8Data) +{ + // + // Write the byte. + // + psI2C->ui8Data = ui8Data; +} + +//***************************************************************************** +// +//! Receives a byte that has been sent to the SoftI2C module. +//! +//! \param psI2C specifies the SoftI2C data structure. +//! +//! This function reads a byte of data from the SoftI2C module that was +//! received as a result of an appropriate call to SoftI2CControl(). +//! +//! \return Returns the byte received by the SoftI2C module, cast as an +//! uint32_t. +// +//***************************************************************************** +uint32_t +SoftI2CDataGet(tSoftI2C *psI2C) +{ + // + // Read a byte. + // + return(psI2C->ui8Data); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/softi2c.h b/utils/softi2c.h new file mode 100644 index 0000000..0ac876a --- /dev/null +++ b/utils/softi2c.h @@ -0,0 +1,195 @@ +//***************************************************************************** +// +// softi2c.h - Defines and macros for the SoftI2C. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __SOFTI2C_H__ +#define __SOFTI2C_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup softi2c_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure contains the state of a single instance of a SoftI2C module. +// +//***************************************************************************** +typedef struct +{ + // + //! The address of the callback function that is called to simulate the + //! interrupts that would be produced by a hardware I2C implementation. + //! This address can be set via a direct structure access or using the + //! SoftI2CCallbackSet function. + // + void (*pfnIntCallback)(void); + + // + //! The address of the GPIO pin to be used for the SCL signal. This member + //! can be set via a direct structure access or using the SoftI2CSCLGPIOSet + //! function. + // + uint32_t ui32SCLGPIO; + + // + //! The address of the GPIO pin to be used for the SDA signal. This member + //! can be set via a direct structure access or using the SoftI2CSDAGPIOSet + //! function. + /// + uint32_t ui32SDAGPIO; + + // + //! The flags that control the operation of the SoftI2C module. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8Flags; + + // + //! The slave address that is currently being accessed. This member should + //! not be accessed or modified by the application. + // + uint8_t ui8SlaveAddr; + + // + //! The data that is currently being transmitted or received. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8Data; + + // + //! The current state of the SoftI2C state machine. This member should not + //! be accessed or modified by the application. + // + uint8_t ui8State; + + // + //! The number of bits that have been transmitted and received in the + //! current frame. This member should not be accessed or modified by the + //! application. + // + uint8_t ui8CurrentBit; + + // + //! The set of virtual interrupts that should be sent to the callback + //! function. This member should not be accessed or modified by the + //! application. + // + uint8_t ui8IntMask; + + // + //! The set of virtual interrupts that are currently asserted. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8IntStatus; +} +tSoftI2C; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// SoftI2C commands. +// +//***************************************************************************** +#define SOFTI2C_CMD_SINGLE_SEND 0x00000007 +#define SOFTI2C_CMD_SINGLE_RECEIVE \ + 0x00000007 +#define SOFTI2C_CMD_BURST_SEND_START \ + 0x00000003 +#define SOFTI2C_CMD_BURST_SEND_CONT \ + 0x00000001 +#define SOFTI2C_CMD_BURST_SEND_FINISH \ + 0x00000005 +#define SOFTI2C_CMD_BURST_SEND_ERROR_STOP \ + 0x00000004 +#define SOFTI2C_CMD_BURST_RECEIVE_START \ + 0x0000000b +#define SOFTI2C_CMD_BURST_RECEIVE_CONT \ + 0x00000009 +#define SOFTI2C_CMD_BURST_RECEIVE_FINISH \ + 0x00000005 +#define SOFTI2C_CMD_BURST_RECEIVE_ERROR_STOP \ + 0x00000004 + +//***************************************************************************** +// +// SoftI2C error status. +// +//***************************************************************************** +#define SOFTI2C_ERR_NONE 0x00000000 +#define SOFTI2C_ERR_ADDR_ACK 0x00000004 +#define SOFTI2C_ERR_DATA_ACK 0x00000008 + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern bool SoftI2CBusy(tSoftI2C *psI2C); +extern void SoftI2CCallbackSet(tSoftI2C *psI2C, void (*pfnCallback)(void)); +extern void SoftI2CControl(tSoftI2C *psI2C, uint32_t ui32Cmd); +extern uint32_t SoftI2CDataGet(tSoftI2C *psI2C); +extern void SoftI2CDataPut(tSoftI2C *psI2C, uint8_t ui8Data); +extern uint32_t SoftI2CErr(tSoftI2C *psI2C); +extern void SoftI2CInit(tSoftI2C *psI2C); +extern void SoftI2CIntClear(tSoftI2C *psI2C); +extern void SoftI2CIntDisable(tSoftI2C *psI2C); +extern void SoftI2CIntEnable(tSoftI2C *psI2C); +extern bool SoftI2CIntStatus(tSoftI2C *psI2C, bool bMasked); +extern void SoftI2CSCLGPIOSet(tSoftI2C *psI2C, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftI2CSDAGPIOSet(tSoftI2C *psI2C, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftI2CSlaveAddrSet(tSoftI2C *psI2C, uint8_t ui8SlaveAddr, + bool bReceive); +extern void SoftI2CTimerTick(tSoftI2C *psI2C); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SOFTI2C_H__ diff --git a/utils/softssi.c b/utils/softssi.c new file mode 100644 index 0000000..d2a155c --- /dev/null +++ b/utils/softssi.c @@ -0,0 +1,1297 @@ +//***************************************************************************** +// +// softssi.c - Driver for the SoftSSI. +// +// 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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softssi_api +//! @{ +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "utils/softssi.h" + +//***************************************************************************** +// +// The states in the SoftSSI state machine. +// +//***************************************************************************** +#define SOFTSSI_STATE_IDLE 0 +#define SOFTSSI_STATE_START 1 +#define SOFTSSI_STATE_IN 2 +#define SOFTSSI_STATE_OUT 3 +#define SOFTSSI_STATE_STOP1 4 +#define SOFTSSI_STATE_STOP2 5 + +//***************************************************************************** +// +// The flags in the SoftSSI ui8Flags structure member. +// +//***************************************************************************** +#define SOFTSSI_FLAG_ENABLE 0x80 +#define SOFTSSI_FLAG_SPH 0x02 +#define SOFTSSI_FLAG_SPO 0x01 + +//***************************************************************************** +// +//! Sets the configuration of a SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui8Protocol specifes the data transfer protocol. +//! \param ui8Bits specifies the number of bits transferred per frame. +//! +//! This function configures the data format of a SoftSSI module. The +//! \e ui8Protocol parameter can be one of the following values: +//! \b SOFTSSI_FRF_MOTO_MODE_0, \b SOFTSSI_FRF_MOTO_MODE_1, +//! \b SOFTSSI_FRF_MOTO_MODE_2, or \b SOFTSSI_FRF_MOTO_MODE_3. These frame +//! formats imply the following polarity and phase configurations: +//! +//!
+//! Polarity Phase         Mode
+//!   0       0   SOFTSSI_FRF_MOTO_MODE_0
+//!   0       1   SOFTSSI_FRF_MOTO_MODE_1
+//!   1       0   SOFTSSI_FRF_MOTO_MODE_2
+//!   1       1   SOFTSSI_FRF_MOTO_MODE_3
+//! 
+//! +//! The \e ui8Bits parameter defines the width of the data transfers, and can +//! be a value between 4 and 16, inclusive. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIConfigSet(tSoftSSI *psSSI, uint8_t ui8Protocol, + uint8_t ui8Bits) +{ + // + // See if a GPIO pin has been set for Fss. + // + if(psSSI->ui32FssGPIO != 0) + { + // + // Configure the Fss pin. + // + MAP_GPIOPinTypeGPIOOutput(psSSI->ui32FssGPIO & 0xfffff000, + (psSSI->ui32FssGPIO & 0x00000fff) >> 2); + + // + // Set the Fss pin high. + // + HWREG(psSSI->ui32FssGPIO) = 255; + } + + // + // Configure the Clk pin. + // + MAP_GPIOPinTypeGPIOOutput(psSSI->ui32ClkGPIO & 0xfffff000, + (psSSI->ui32ClkGPIO & 0x00000fff) >> 2); + + // + // Set the Clk pin high or low based on the configured clock polarity. + // + if((ui8Protocol & SOFTSSI_FLAG_SPO) == 0) + { + HWREG(psSSI->ui32ClkGPIO) = 0; + } + else + { + HWREG(psSSI->ui32ClkGPIO) = 255; + } + + // + // Configure the Tx pin and set it low. + // + MAP_GPIOPinTypeGPIOOutput(psSSI->ui32TxGPIO & 0xfffff000, + (psSSI->ui32TxGPIO & 0x00000fff) >> 2); + HWREG(psSSI->ui32TxGPIO) = 0; + + // + // See if a GPIO pin has been set for Rx. + // + if(psSSI->ui32RxGPIO != 0) + { + // + // Configure the Rx pin. + // + MAP_GPIOPinTypeGPIOInput(psSSI->ui32RxGPIO & 0xfffff000, + (psSSI->ui32RxGPIO & 0x00000fff) >> 2); + } + + // + // Make sure that the transmit and receive FIFOs are empty. + // + psSSI->ui16TxBufferRead = 0; + psSSI->ui16TxBufferWrite = 0; + psSSI->ui16RxBufferRead = 0; + psSSI->ui16RxBufferWrite = 0; + + // + // Save the frame protocol. + // + psSSI->ui8Flags = ui8Protocol; + + // + // Save the number of data bits. + // + psSSI->ui8Bits = ui8Bits; + + // + // Since the FIFOs are empty, the transmit FIFO "interrupt" is asserted. + // + psSSI->ui8IntStatus = SOFTSSI_TXFF; + + // + // Reset the idle counter. + // + psSSI->ui8IdleCount = 0; + + // + // Disable the SoftSSI module. + // + psSSI->ui8Flags &= ~(SOFTSSI_FLAG_ENABLE); + + // + // Start the SoftSSI state machine in the idle state. + // + psSSI->ui8State = SOFTSSI_STATE_IDLE; +} + +//***************************************************************************** +// +//! Handles the assertion/deassertion of the transmit FIFO ``interrupt''. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function is used to determine when to assert or deassert the transmit +//! FIFO ``interrupt''. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftSSITxInt(tSoftSSI *psSSI) +{ + uint16_t ui16Temp; + + // + // Determine the number of words left in the transmit FIFO. + // + if(psSSI->ui16TxBufferRead > psSSI->ui16TxBufferWrite) + { + ui16Temp = (psSSI->ui16TxBufferLen + psSSI->ui16TxBufferWrite - + psSSI->ui16TxBufferRead); + } + else + { + ui16Temp = psSSI->ui16TxBufferWrite - psSSI->ui16TxBufferRead; + } + + // + // If the transmit FIFO is now half full or less, generate a transmit FIFO + // "interrupt". Otherwise, clear the transmit FIFO "interrupt". + // + if(ui16Temp <= (psSSI->ui16TxBufferLen / 2)) + { + psSSI->ui8IntStatus |= SOFTSSI_TXFF; + } + else + { + psSSI->ui8IntStatus &= ~(SOFTSSI_TXFF); + } +} + +//***************************************************************************** +// +//! Handles the assertion/deassertion of the receive FIFO ``interrupt''. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function is used to determine when to assert or deassert the receive +//! FIFO ``interrupt''. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftSSIRxInt(tSoftSSI *psSSI) +{ + uint16_t ui16Temp; + + // + // Determine the number of words in the receive FIFO. + // + if(psSSI->ui16RxBufferRead > psSSI->ui16RxBufferWrite) + { + ui16Temp = (psSSI->ui16RxBufferLen + psSSI->ui16RxBufferWrite - + psSSI->ui16RxBufferRead); + } + else + { + ui16Temp = psSSI->ui16RxBufferWrite - psSSI->ui16RxBufferRead; + } + + // + // If the receive FIFO is now half full or more, generate a receive FIFO + // "interrupt". Otherwise, clear the receive FIFO "interrupt". + // + if(ui16Temp >= (psSSI->ui16RxBufferLen / 2)) + { + psSSI->ui8IntStatus |= SOFTSSI_RXFF; + } + else + { + psSSI->ui8IntStatus &= ~(SOFTSSI_RXFF); + } +} + +//***************************************************************************** +// +//! Performs the periodic update of the SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function performs the periodic, time-based updates to the SoftSSI +//! module. The transmission and reception of data over the SoftSSI link is +//! performed by the state machine in this function. +//! +//! This function must be called at twice the desired SoftSSI clock rate. For +//! example, to run the SoftSSI clock at 10 KHz, this function must be called +//! at a 20 KHz rate. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSITimerTick(tSoftSSI *psSSI) +{ + uint16_t ui16Temp; + + // + // Determine the current state of the state machine. + // + switch(psSSI->ui8State) + { + // + // The state machine is idle. + // + case SOFTSSI_STATE_IDLE: + { + // + // See if the SoftSSI module is enabled and there is data in the + // transmit FIFO. + // + if(((psSSI->ui8Flags & SOFTSSI_FLAG_ENABLE) != 0) && + (psSSI->ui16TxBufferRead != psSSI->ui16TxBufferWrite)) + { + // + // Assert the Fss signal if it is configured. + // + if(psSSI->ui32FssGPIO != 0) + { + HWREG(psSSI->ui32FssGPIO) = 0; + } + + // + // Move to the start state. + // + psSSI->ui8State = SOFTSSI_STATE_START; + } + + // + // Otherwise, see if there is data in the receive FIFO. + // + else if((psSSI->ui16RxBufferRead != psSSI->ui16RxBufferWrite) && + (psSSI->ui8IdleCount != 64)) + { + // + // Increment the idle counter. + // + psSSI->ui8IdleCount++; + + // + // See if the idle counter has become large enough to trigger + // a timeout "interrupt". + // + if(psSSI->ui8IdleCount == 64) + { + // + // Trigger the receive timeout "interrupt". + // + psSSI->ui8IntStatus |= SOFTSSI_RXTO; + } + } + + // + // This state has been handled. + // + break; + } + + // + // The start machine is in the transfer start state. + // + case SOFTSSI_STATE_START: + { + // + // Get the next word to transfer from the transmit FIFO. + // + psSSI->ui16TxData = + (psSSI->pui16TxBuffer[psSSI->ui16TxBufferRead] << + (16 - psSSI->ui8Bits)); + + // + // Initialize the receive buffer to zero. + // + psSSI->ui16RxData = 0; + + // + // Initialize the count of bits tranferred. + // + psSSI->ui8CurrentBit = 0; + + // + // Write the first bit of the transmit word to the Tx pin. + // + HWREG(psSSI->ui32TxGPIO) = + (psSSI->ui16TxData & 0x8000) ? 255 : 0; + + // + // Shift to the next bit of the transmit word. + // + psSSI->ui16TxData <<= 1; + + // + // If in SPI mode 1 or 3, then the Clk signal needs to be toggled. + // + if((psSSI->ui8Flags & SOFTSSI_FLAG_SPH) != 0) + { + HWREG(psSSI->ui32ClkGPIO) ^= 255; + } + + // + // Move to the data input state. + // + psSSI->ui8State = SOFTSSI_STATE_IN; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the data input state. + // + case SOFTSSI_STATE_IN: + { + // + // Read the next bit from the Rx signal if it is configured. + // + if(psSSI->ui32RxGPIO != 0) + { + psSSI->ui16RxData = ((psSSI->ui16RxData << 1) | + (HWREG(psSSI->ui32RxGPIO) ? 1 : 0)); + } + + // + // Toggle the Clk signal. + // + HWREG(psSSI->ui32ClkGPIO) ^= 255; + + // + // Increment the number of bits transferred. + // + psSSI->ui8CurrentBit++; + + // + // See if the entire word has been transferred. + // + if(psSSI->ui8CurrentBit != psSSI->ui8Bits) + { + // + // There are more bits to transfer, so move to the data output + // state. + // + psSSI->ui8State = SOFTSSI_STATE_OUT; + } + else + { + // + // Increment the transmit read pointer, removing the word that + // was just transferred from the transmit FIFO. + // + psSSI->ui16TxBufferRead++; + if(psSSI->ui16TxBufferRead == psSSI->ui16TxBufferLen) + { + psSSI->ui16TxBufferRead = 0; + } + + // + // See if a transmit FIFO "interrupt" needs to be asserted. + // + SoftSSITxInt(psSSI); + + // + // Determine the new value for the receive FIFO write pointer. + // + ui16Temp = psSSI->ui16RxBufferWrite + 1; + if(ui16Temp >= psSSI->ui16RxBufferLen) + { + ui16Temp = 0; + } + + // + // See if there is space in the receive FIFO for the word that + // was just received. + // + if(ui16Temp == psSSI->ui16RxBufferRead) + { + // + // The receive FIFO is full, so generate a receive FIFO + // overrun "interrupt". + // + psSSI->ui8IntStatus |= SOFTSSI_RXOR; + } + else + { + // + // Store the new word into the receive FIFO. + // + psSSI->pui16RxBuffer[psSSI->ui16RxBufferWrite] = + psSSI->ui16RxData; + + // + // Save the new receive FIFO write pointer. + // + psSSI->ui16RxBufferWrite = ui16Temp; + + // + // See if a receive FIFO "interrupt" needs to be asserted. + // + SoftSSIRxInt(psSSI); + } + + // + // See if the next word should be transmitted immediately. + // This will occur when there is data in the transmit FIFO, the + // SoftSSI module is enabled, and the SoftSSI module is in SPI + // mode 1 or 3. + // + if(((psSSI->ui8Flags & SOFTSSI_FLAG_ENABLE) != 0) && + ((psSSI->ui8Flags & SOFTSSI_FLAG_SPH) != 0) && + (psSSI->ui16TxBufferRead != psSSI->ui16TxBufferWrite)) + { + // + // Get the next word to transfer from the transmit FIFO. + // + psSSI->ui16TxData = + (psSSI->pui16TxBuffer[psSSI->ui16TxBufferRead] << + (16 - psSSI->ui8Bits)); + + // + // Initialize the receive buffer to zero. + // + psSSI->ui16RxData = 0; + + // + // Initialize the count of bits tranferred. + // + psSSI->ui8CurrentBit = 0; + + // + // Move to the data output state. + // + psSSI->ui8State = SOFTSSI_STATE_OUT; + } + else + { + // + // The next word should not be transmitted immediately, so + // move to the first step of the stop state. + // + psSSI->ui8State = SOFTSSI_STATE_STOP1; + } + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the data output state. + // + case SOFTSSI_STATE_OUT: + { + // + // Write the next bit of the transmit word to the Tx pin. + // + HWREG(psSSI->ui32TxGPIO) = (psSSI->ui16TxData & 0x8000) ? 255 : 0; + + // + // Toggle the Clk signal. + // + HWREG(psSSI->ui32ClkGPIO) ^= 255; + + // + // Shift to the next bit of the transmit word. + // + psSSI->ui16TxData <<= 1; + + // + // Move to the data input state. + // + psSSI->ui8State = SOFTSSI_STATE_IN; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the first step of the stop state. + // + case SOFTSSI_STATE_STOP1: + { + // + // Set the Tx pin low. + // + HWREG(psSSI->ui32TxGPIO) = 0; + + // + // If in SPI mode 1 or 3, then the Clk signal needs to be toggled. + // + if((psSSI->ui8Flags & SOFTSSI_FLAG_SPH) == 0) + { + HWREG(psSSI->ui32ClkGPIO) ^= 255; + } + + // + // Move to the second step of the stop state. + // + psSSI->ui8State = SOFTSSI_STATE_STOP2; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the second step of the stop state. + // + case SOFTSSI_STATE_STOP2: + { + // + // Deassert the Fss signal if it is configured. + // + if(psSSI->ui32FssGPIO != 0) + { + HWREG(psSSI->ui32FssGPIO) = 255; + } + + // + // Move to the idle state. + // + psSSI->ui8State = SOFTSSI_STATE_IDLE; + + // + // Reset the idle counter. + // + psSSI->ui8IdleCount = 0; + + // + // See if the end of transfer "interrupt" should be generated. + // + if(psSSI->ui16TxBufferRead == psSSI->ui16TxBufferWrite) + { + psSSI->ui8IntStatus |= SOFTSSI_TXEOT; + } + + // + // This state has been handled. + // + break; + } + } + + // + // Call the "interrupt" callback while there are enabled "interrupts" + // asserted. By calling in a loop until the "interrupts" are no longer + // asserted, this mimics the behavior of a real hardware implementation of + // the SSI peripheral. + // + while(((psSSI->ui8IntStatus & psSSI->ui8IntMask) != 0) && + (psSSI->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + psSSI->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Enables the SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function enables operation of the SoftSSI module. The SoftSSI module +//! must be configured before it is enabled. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIEnable(tSoftSSI *psSSI) +{ + // + // Enable the SoftSSI module. + // + psSSI->ui8Flags |= SOFTSSI_FLAG_ENABLE; +} + +//***************************************************************************** +// +//! Disables the SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function disables operation of the SoftSSI module. If a data transfer +//! is in progress, it is finished before the module is fully disabled. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIDisable(tSoftSSI *psSSI) +{ + // + // Disable the SoftSSI module. + // + psSSI->ui8Flags &= ~(SOFTSSI_FLAG_ENABLE); +} + +//***************************************************************************** +// +//! Enables individual SoftSSI ``interrupt'' sources. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32IntFlags is a bit mask of the ``interrupt'' sources to be +//! enabled. +//! +//! Enables the indicated SoftSSI ``interrupt'' sources. Only the sources that +//! are enabled can be reflected to the callback function; disabled sources do +//! not result in a callback. The \e ui32IntFlags parameter can be any of the +//! \b SOFTSSI_TXEOT, \b SOFTSSI_TXFF, \b SOFTSSI_RXFF, \b SOFTSSI_RXTO, or +//! \b SOFTSSI_RXOR values. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIIntEnable(tSoftSSI *psSSI, uint32_t ui32IntFlags) +{ + // + // Enable the specified "interrupts". + // + psSSI->ui8IntMask |= ui32IntFlags; +} + +//***************************************************************************** +// +//! Disables individual SoftSSI ``interrupt'' sources. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32IntFlags is a bit mask of the ``interrupt'' sources to be +//! disabled. +//! +//! Disables the indicated SoftSSI ``interrupt'' sources. The \e ui32IntFlags +//! parameter can be any of the \b SOFTSSI_TXEOT, \b SOFTSSI_TXFF, +//! \b SOFTSSI_RXFF, \b SOFTSSI_RXTO, or \b SOFTSSI_RXOR values. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIIntDisable(tSoftSSI *psSSI, uint32_t ui32IntFlags) +{ + // + // Disable the specified "interrupts". + // + psSSI->ui8IntMask &= ~(ui32IntFlags); +} + +//***************************************************************************** +// +//! Gets the current ``interrupt'' status. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param bMasked is \b false if the raw ``interrupt'' status is required or +//! \b true if the masked ``interrupt'' status is required. +//! +//! This function returns the ``interrupt'' status for the SoftSSI module. +//! Either the raw ``interrupt'' status or the status of ``interrupts'' that +//! are allowed to reflect to the callback can be returned. +//! +//! \return The current ``interrupt'' status, enumerated as a bit field of +//! \b SOFTSSI_TXEOT, \b SOFTSSI_TXFF, \b SOFTSSI_RXFF, \b SOFTSSI_RXTO, and +//! \b SOFTSSI_RXOR. +// +//***************************************************************************** +uint32_t +SoftSSIIntStatus(tSoftSSI *psSSI, bool bMasked) +{ + // + // Return either the "interrupt" status or the raw "interrupt" status as + // requested. + // + if(bMasked) + { + return(psSSI->ui8IntStatus & psSSI->ui8IntMask); + } + else + { + return(psSSI->ui8IntStatus); + } +} + +//***************************************************************************** +// +//! Clears SoftSSI ``interrupt'' sources. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32IntFlags is a bit mask of the ``interrupt'' sources to be +//! cleared. +//! +//! The specified SoftSSI ``interrupt'' sources are cleared so that they no +//! longer assert. This function must be called in the ``interrupt'' handler +//! to keep the ``interrupt'' from being recognized again immediately upon +//! exit. The \e ui32IntFlags parameter is the logical OR of any of the +//! \b SOFTSSI_TXEOT, \b SOFTSSI_RXTO, and \b SOFTSSI_RXOR values. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIIntClear(tSoftSSI *psSSI, uint32_t ui32IntFlags) +{ + // + // Clear the requested "interrupt" sources. + // + psSSI->ui8IntStatus &= ~(ui32IntFlags) | SOFTSSI_TXFF | SOFTSSI_RXFF; +} + +//***************************************************************************** +// +//! Determines if there is any data in the receive FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function determines if there is any data available to be read from the +//! receive FIFO. +//! +//! \return Returns \b true if there is data in the receive FIFO or \b false +//! if there is no data in the receive FIFO. +// +//***************************************************************************** +bool +SoftSSIDataAvail(tSoftSSI *psSSI) +{ + // + // Return the availability of data. + // + return((psSSI->ui16RxBufferRead == psSSI->ui16RxBufferWrite) ? false : + true); +} + +//***************************************************************************** +// +//! Determines if there is any space in the transmit FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! This function determines if there is space available in the transmit FIFO. +//! +//! \return Returns \b true if there is space available in the transmit FIFO or +//! \b false if there is no space available in the transmit FIFO. +// +//***************************************************************************** +bool +SoftSSISpaceAvail(tSoftSSI *psSSI) +{ + uint16_t ui16Temp; + + // + // Determine the values of the write pointer once incremented. + // + ui16Temp = psSSI->ui16TxBufferWrite + 1; + if(ui16Temp == psSSI->ui16TxBufferLen) + { + ui16Temp = 0; + } + + // + // Return the availability of space. + // + return((psSSI->ui16TxBufferRead == ui16Temp) ? false : true); +} + +//***************************************************************************** +// +//! Puts a data element into the SoftSSI transmit FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Data is the data to be transmitted over the SoftSSI interface. +//! +//! This function places the supplied data into the transmit FIFO of the +//! specified SoftSSI module. +//! +//! \note The upper 32 - N bits of the \e ui32Data are discarded, where N is +//! the data width as configured by SoftSSIConfigSet(). For example, if the +//! interface is configured for 8-bit data width, the upper 24 bits of +//! \e ui32Data are discarded. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIDataPut(tSoftSSI *psSSI, uint32_t ui32Data) +{ + uint16_t ui16Temp; + + // + // Wait until there is space. + // + ui16Temp = psSSI->ui16TxBufferWrite + 1; + if(ui16Temp == psSSI->ui16TxBufferLen) + { + ui16Temp = 0; + } + while(ui16Temp == *(volatile uint16_t *)(&(psSSI->ui16TxBufferRead))) + { + } + + // + // Write the data to the SoftSSI. + // + psSSI->pui16TxBuffer[psSSI->ui16TxBufferWrite] = ui32Data; + psSSI->ui16TxBufferWrite = ui16Temp; + + // + // See if a transmit FIFO "interrupt" needs to be cleared. + // + SoftSSITxInt(psSSI); +} + +//***************************************************************************** +// +//! Puts a data element into the SoftSSI transmit FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Data is the data to be transmitted over the SoftSSI interface. +//! +//! This function places the supplied data into the transmit FIFO of the +//! specified SoftSSI module. If there is no space in the FIFO, then this +//! function returns a zero. +//! +//! \note The upper 32 - N bits of the \e ui32Data are discarded, where N is +//! the data width as configured by SoftSSIConfigSet(). For example, if the +//! interface is configured for 8-bit data width, the upper 24 bits of +//! \e ui32Data are discarded. +//! +//! \return Returns the number of elements written to the SSI transmit FIFO. +// +//***************************************************************************** +int32_t +SoftSSIDataPutNonBlocking(tSoftSSI *psSSI, uint32_t ui32Data) +{ + uint16_t ui16Temp; + + // + // Determine the values of the write pointer once incremented. + // + ui16Temp = psSSI->ui16TxBufferWrite + 1; + if(ui16Temp == psSSI->ui16TxBufferLen) + { + ui16Temp = 0; + } + + // + // Check for space to write. + // + if(ui16Temp != psSSI->ui16TxBufferRead) + { + psSSI->pui16TxBuffer[psSSI->ui16TxBufferWrite] = ui32Data; + psSSI->ui16TxBufferWrite = ui16Temp; + SoftSSITxInt(psSSI); + return(1); + } + else + { + return(0); + } +} + +//***************************************************************************** +// +//! Gets a data element from the SoftSSI receive FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pui32Data is a pointer to a storage location for data that was +//! received over the SoftSSI interface. +//! +//! This function gets received data from the receive FIFO of the specified +//! SoftSSI module and places that data into the location specified by the +//! \e pui32Data parameter. +//! +//! \note Only the lower N bits of the value written to \e pui32Data contain +//! valid data, where N is the data width as configured by SoftSSIConfigSet(). +//! For example, if the interface is configured for 8-bit data width, only the +//! lower 8 bits of the value written to \e pui32Data contain valid data. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIDataGet(tSoftSSI *psSSI, uint32_t *pui32Data) +{ + // + // Wait until there is data to be read. + // + while(psSSI->ui16RxBufferRead == + *(volatile uint16_t *)(&(psSSI->ui16RxBufferWrite))) + { + } + + // + // Read data from SoftSSI. + // + *pui32Data = psSSI->pui16RxBuffer[psSSI->ui16RxBufferRead]; + psSSI->ui16RxBufferRead++; + if(psSSI->ui16RxBufferRead == psSSI->ui16RxBufferLen) + { + psSSI->ui16RxBufferRead = 0; + } + + // + // See if a receive FIFO "interrupt" needs to be cleared. + // + SoftSSIRxInt(psSSI); +} + +//***************************************************************************** +// +//! Gets a data element from the SoftSSI receive FIFO. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pui32Data is a pointer to a storage location for data that was +//! received over the SoftSSI interface. +//! +//! This function gets received data from the receive FIFO of the specified +//! SoftSSI module and places that data into the location specified by the +//! \e ui32Data parameter. If there is no data in the FIFO, then this function +//! returns a zero. +//! +//! \note Only the lower N bits of the value written to \e pui32Data contain +//! valid data, where N is the data width as configured by SoftSSIConfigSet(). +//! For example, if the interface is configured for 8-bit data width, only the +//! lower 8 bits of the value written to \e pui32Data contain valid data. +//! +//! \return Returns the number of elements read from the SoftSSI receive FIFO. +// +//***************************************************************************** +int32_t +SoftSSIDataGetNonBlocking(tSoftSSI *psSSI, uint32_t *pui32Data) +{ + // + // Check for data to read. + // + if(psSSI->ui16RxBufferRead != psSSI->ui16RxBufferWrite) + { + *pui32Data = psSSI->pui16RxBuffer[psSSI->ui16RxBufferRead]; + psSSI->ui16RxBufferRead++; + if(psSSI->ui16RxBufferRead == psSSI->ui16RxBufferLen) + { + psSSI->ui16RxBufferRead = 0; + } + SoftSSIRxInt(psSSI); + return(1); + } + else + { + return(0); + } +} + +//***************************************************************************** +// +//! Determines whether the SoftSSI transmitter is busy or not. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! +//! Allows the caller to determine whether all transmitted bytes have cleared +//! the transmitter. If \b false is returned, then the transmit FIFO is empty +//! and all bits of the last transmitted word have left the shift register. +//! +//! \return Returns \b true if the SoftSSI is transmitting or \b false if all +//! transmissions are complete. +// +//***************************************************************************** +bool +SoftSSIBusy(tSoftSSI *psSSI) +{ + // + // Determine if the SSI is busy. + // + return(((psSSI->ui8State == SOFTSSI_STATE_IDLE) && + (((psSSI->ui8Flags & SOFTSSI_FLAG_ENABLE) == 0) || + (psSSI->ui16TxBufferRead == psSSI->ui16TxBufferWrite))) ? false : + true); +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pfnCallback is a pointer to the callback function. +//! +//! This function sets the address of the callback function that is called when +//! there is an ``interrupt'' produced by the SoftSSI module. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSICallbackSet(tSoftSSI *psSSI, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + psSSI->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Fss signal. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftSSI Fss signal. +//! If there is not a GPIO pin allocated for Fss, the SoftSSI module does not +//! assert/deassert the Fss signal, leaving it to the application either to do +//! manually or to not do at all if the slave device has Fss tied to ground. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIFssGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Fss signal. + // + if(ui32Base == 0) + { + psSSI->ui32FssGPIO = 0; + } + else + { + psSSI->ui32FssGPIO = ui32Base + (ui8Pin << 2); + } +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Clk signal. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftSSI Clk signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIClkGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Clk signal. + // + psSSI->ui32ClkGPIO = ui32Base + (ui8Pin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Tx signal. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftSSI Tx signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSITxGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Tx signal. + // + psSSI->ui32TxGPIO = ui32Base + (ui8Pin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Rx signal. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftSSI Rx signal. If +//! there is not a GPIO pin allocated for Rx, the SoftSSI module does not read +//! data from the slave device. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIRxGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Rx signal. + // + if(ui32Base == 0) + { + psSSI->ui32RxGPIO = 0; + } + else + { + psSSI->ui32RxGPIO = ui32Base + (ui8Pin << 2); + } +} + +//***************************************************************************** +// +//! Sets the transmit FIFO buffer for a SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pui16TxBuffer is the address of the transmit FIFO buffer. +//! \param ui16Len is the size, in 16-bit half-words, of the transmit FIFO +//! buffer. +//! +//! This function sets the address and size of the transmit FIFO buffer and +//! also resets the read and write pointers, marking the transmit FIFO as +//! empty. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSITxBufferSet(tSoftSSI *psSSI, uint16_t *pui16TxBuffer, + uint16_t ui16Len) +{ + // + // Save the transmit FIFO buffer address and length. + // + psSSI->pui16TxBuffer = pui16TxBuffer; + psSSI->ui16TxBufferLen = ui16Len; + + // + // Reset the transmit FIFO read and write pointers. + // + psSSI->ui16TxBufferRead = 0; + psSSI->ui16TxBufferWrite = 0; +} + +//***************************************************************************** +// +//! Sets the receive FIFO buffer for a SoftSSI module. +//! +//! \param psSSI specifies the SoftSSI data structure. +//! \param pui16RxBuffer is the address of the receive FIFO buffer. +//! \param ui16Len is the size, in 16-bit half-words, of the receive FIFO +//! buffer. +//! +//! This function sets the address and size of the receive FIFO buffer and also +//! resets the read and write pointers, marking the receive FIFO as empty. +//! When the buffer pointer and length are configured as zero, all data +//! received from the slave device is discarded. This capability is useful +//! when there is no GPIO pin allocated for the Rx signal. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIRxBufferSet(tSoftSSI *psSSI, uint16_t *pui16RxBuffer, + uint16_t ui16Len) +{ + // + // Save the receive FIFO buffer address and length. + // + psSSI->pui16RxBuffer = pui16RxBuffer; + psSSI->ui16RxBufferLen = ui16Len; + + // + // Reset the receive FIFO read and write pointers. + // + psSSI->ui16RxBufferRead = 0; + psSSI->ui16RxBufferWrite = 0; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/softssi.h b/utils/softssi.h new file mode 100644 index 0000000..72ee068 --- /dev/null +++ b/utils/softssi.h @@ -0,0 +1,280 @@ +//***************************************************************************** +// +// softssi.h - Defines and macros for the SoftSSI. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __SOFTSSI_H__ +#define __SOFTSSI_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup softssi_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure contains the state of a single instance of a SoftSSI module. +// +//***************************************************************************** +typedef struct +{ + // + //! The address of the callback function that is called to simulate the + //! interrupts that would be produced by a hardware SSI implementation. + //! This address can be set via a direct structure access or using the + //! SoftSSICallbackSet function. + // + void (*pfnIntCallback)(void); + + // + //! The address of the GPIO pin to be used for the Fss signal. If this + //! member is zero, the Fss signal is not generated. This member can be + //! set via a direct structure access or using the SoftSSIFssGPIOSet + //! function. + /// + uint32_t ui32FssGPIO; + + // + //! The address of the GPIO pin to be used for the Clk signal. This member + //! can be set via a direct structure access or using the SoftSSIClkGPIOSet + //! function. + // + uint32_t ui32ClkGPIO; + + // + //! The address of the GPIO pin to be used for the Tx signal. This member + //! can be set via a direct structure access or using the SoftSSITxGPIOSet + //! function. + // + uint32_t ui32TxGPIO; + + // + //! The address of the GPIO pin to be used for the Rx signal. If this + //! member is zero, the Rx signal is not read. This member can be set via + //! a direct structure access or using the SoftSSIRxGPIOSet function. + // + uint32_t ui32RxGPIO; + + // + //! The address of the data buffer used for the transmit FIFO. This member + //! can be set via a direct structure access or using the + //! SoftSSITxBufferSet function. + // + uint16_t *pui16TxBuffer; + + // + //! The address of the data buffer used for the receive FIFO. This member + //! can be set via a direct structure access or using the + //! SoftSSIRxBufferSet function. + // + uint16_t *pui16RxBuffer; + + // + //! The length of the transmit FIFO. This member can be set via a direct + //! structure access or using the SoftSSITxBufferSet function. + // + uint16_t ui16TxBufferLen; + + // + //! The index into the transmit FIFO of the next word to be transmitted. + //! This member should be initialized to zero, but should not be accessed + //! or modified by the application. + // + uint16_t ui16TxBufferRead; + + // + //! The index into the transmit FIFO of the next location to store data + //! into the FIFO. This member should be initialized to zero, but should + //! not be accessed or modified by the application. + // + uint16_t ui16TxBufferWrite; + + // + //! The length of the receive FIFO. This member can be set via a direct + //! structure access or using the SoftSSIRxBufferSet function. + // + uint16_t ui16RxBufferLen; + + // + //! The index into the receive FIFO of the next word to be read from the + //! FIFO. This member should be initialized to zero, but should not be + //! accessed or modified by the application. + // + uint16_t ui16RxBufferRead; + + // + //! The index into the receive FIFO of the location to store the next word + //! received. This member should be initialized to zero, but should not be + //! accessed or modified by the application. + // + uint16_t ui16RxBufferWrite; + + // + //! The word that is currently being transmitted. This member should not + //! be accessed or modified by the application. + // + uint16_t ui16TxData; + + // + //! The word that is currently being received. This member should not be + //! accessed or modified by the application. + // + uint16_t ui16RxData; + + // + //! The flags that control the operation of the SoftSSI module. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8Flags; + + // + //! The number of data bits in each SoftSSI frame, which also specifies the + //! width of each data item in the transmit and receive FIFOs. This member + //! can be set via a direct structure access or using the SoftSSIConfigSet + //! function. + // + uint8_t ui8Bits; + + // + //! The current state of the SoftSSI state machine. This member should not + //! be accessed or modified by the application. + // + uint8_t ui8State; + + // + //! The number of bits that have been transmitted and received in the + //! current frame. This member should not be accessed or modified by the + //! application. + // + uint8_t ui8CurrentBit; + + // + //! The set of virtual interrupts that should be sent to the callback + //! function. This member should not be accessed or modified by the + //! application. + // + uint8_t ui8IntMask; + + // + //! The set of virtual interrupts that are currently asserted. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8IntStatus; + + // + //! The number of tick counts that the SoftSSI module has been idle with + //! data stored in the receive FIFO, which is used to generate the receive + //! timeout interrupt. This member should not be accessed or modified by + //! the application. + // + uint8_t ui8IdleCount; +} +tSoftSSI; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Values that can be passed to SoftSSIIntEnable, SoftSSIIntDisable, and +// SoftSSIIntClear as the ui32IntFlags parameter, and returned by +// SoftSSIIntStatus. +// +//***************************************************************************** +#define SOFTSSI_TXEOT 0x00000010 // TX end of transmit +#define SOFTSSI_TXFF 0x00000008 // TX FIFO half full or less +#define SOFTSSI_RXFF 0x00000004 // RX FIFO half full or more +#define SOFTSSI_RXTO 0x00000002 // RX timeout +#define SOFTSSI_RXOR 0x00000001 // RX overrun + +//***************************************************************************** +// +// Values that can be passed to SoftSSIConfigSet. +// +//***************************************************************************** +#define SOFTSSI_FRF_MOTO_MODE_0 0x00000000 // Moto fmt, polarity 0, phase 0 +#define SOFTSSI_FRF_MOTO_MODE_1 0x00000002 // Moto fmt, polarity 0, phase 1 +#define SOFTSSI_FRF_MOTO_MODE_2 0x00000001 // Moto fmt, polarity 1, phase 0 +#define SOFTSSI_FRF_MOTO_MODE_3 0x00000003 // Moto fmt, polarity 1, phase 1 + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern bool SoftSSIBusy(tSoftSSI *psSSI); +extern void SoftSSICallbackSet(tSoftSSI *psSSI, void (*pfnCallback)(void)); +extern void SoftSSIClkGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftSSIConfigSet(tSoftSSI *psSSI, uint8_t ui8Protocol, + uint8_t ui8Bits); +extern bool SoftSSIDataAvail(tSoftSSI *psSSI); +extern void SoftSSIDataGet(tSoftSSI *psSSI, uint32_t *pui32Data); +extern int32_t SoftSSIDataGetNonBlocking(tSoftSSI *psSSI, uint32_t *pui32Data); +extern void SoftSSIDataPut(tSoftSSI *psSSI, uint32_t ui32Data); +extern int32_t SoftSSIDataPutNonBlocking(tSoftSSI *psSSI, uint32_t ui32Data); +extern void SoftSSIDisable(tSoftSSI *psSSI); +extern void SoftSSIEnable(tSoftSSI *psSSI); +extern void SoftSSIFssGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftSSIIntClear(tSoftSSI *psSSI, uint32_t ui32IntFlags); +extern void SoftSSIIntDisable(tSoftSSI *psSSI, uint32_t ui32IntFlags); +extern void SoftSSIIntEnable(tSoftSSI *psSSI, uint32_t ui32IntFlags); +extern uint32_t SoftSSIIntStatus(tSoftSSI *psSSI, bool bMasked); +extern void SoftSSIRxBufferSet(tSoftSSI *psSSI, uint16_t *pui16RxBuffer, + uint16_t ui16Len); +extern void SoftSSIRxGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, + uint8_t ui8Pin); +extern bool SoftSSISpaceAvail(tSoftSSI *psSSI); +extern void SoftSSITimerTick(tSoftSSI *psSSI); +extern void SoftSSITxBufferSet(tSoftSSI *psSSI, uint16_t *pui16TxBuffer, + uint16_t ui16Len); +extern void SoftSSITxGPIOSet(tSoftSSI *psSSI, uint32_t ui32Base, + uint8_t ui8Pin); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SOFTSSI_H__ diff --git a/utils/softuart.c b/utils/softuart.c new file mode 100644 index 0000000..d4aa3b3 --- /dev/null +++ b/utils/softuart.c @@ -0,0 +1,2591 @@ +//***************************************************************************** +// +// softuart.c - Driver for the SoftUART. +// +// 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 Utility Library. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softuart_api +//! @{ +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "inc/hw_uart.h" +#include "driverlib/debug.h" +#include "driverlib/gpio.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/uart.h" +#include "utils/softuart.h" + +//***************************************************************************** +// +// The states in the SoftUART transmit state machine. The code depends upon +// the fact that the value of TXSTATE_DATA_n is n + 1, and that TXSTATE_DATA_0 +// is 1. +// +//***************************************************************************** +#define SOFTUART_TXSTATE_IDLE 0 +#define SOFTUART_TXSTATE_DATA_0 1 +#define SOFTUART_TXSTATE_DATA_1 2 +#define SOFTUART_TXSTATE_DATA_2 3 +#define SOFTUART_TXSTATE_DATA_3 4 +#define SOFTUART_TXSTATE_DATA_4 5 +#define SOFTUART_TXSTATE_DATA_5 6 +#define SOFTUART_TXSTATE_DATA_6 7 +#define SOFTUART_TXSTATE_DATA_7 8 +#define SOFTUART_TXSTATE_START 9 +#define SOFTUART_TXSTATE_PARITY 10 +#define SOFTUART_TXSTATE_STOP_0 11 +#define SOFTUART_TXSTATE_STOP_1 12 +#define SOFTUART_TXSTATE_BREAK 13 + +//***************************************************************************** +// +// The states of the SoftUART receive state machine. The code depends upon the +// the fact that the value of RXSTATE_DATA_n is n, and that RXSTATE_DATA_0 is +// 0. +// +//***************************************************************************** +#define SOFTUART_RXSTATE_DATA_0 0 +#define SOFTUART_RXSTATE_DATA_1 1 +#define SOFTUART_RXSTATE_DATA_2 2 +#define SOFTUART_RXSTATE_DATA_3 3 +#define SOFTUART_RXSTATE_DATA_4 4 +#define SOFTUART_RXSTATE_DATA_5 5 +#define SOFTUART_RXSTATE_DATA_6 6 +#define SOFTUART_RXSTATE_DATA_7 7 +#define SOFTUART_RXSTATE_IDLE 8 +#define SOFTUART_RXSTATE_PARITY 9 +#define SOFTUART_RXSTATE_STOP_0 10 +#define SOFTUART_RXSTATE_STOP_1 11 +#define SOFTUART_RXSTATE_BREAK 12 +#define SOFTUART_RXSTATE_DELAY 13 + +//***************************************************************************** +// +// The flags in the SoftUART ui8Flags structure member. +// +//***************************************************************************** +#define SOFTUART_FLAG_ENABLE 0x01 +#define SOFTUART_FLAG_TXBREAK 0x02 + +//***************************************************************************** +// +// The flags in the SoftUART ui8RxFlags structure member. +// +//***************************************************************************** +#define SOFTUART_RXFLAG_OE 0x08 +#define SOFTUART_RXFLAG_BE 0x04 +#define SOFTUART_RXFLAG_PE 0x02 +#define SOFTUART_RXFLAG_FE 0x01 + +//***************************************************************************** +// +// Additional internal configuration stored in the SoftUART ui16Config +// structure member. +// +//***************************************************************************** +#define SOFTUART_CONFIG_BASE_M 0x00ff +#define SOFTUART_CONFIG_EXT_M 0xff00 +#define SOFTUART_CONFIG_TXLVL_M 0x0700 +#define SOFTUART_CONFIG_TXLVL_1 0x0000 +#define SOFTUART_CONFIG_TXLVL_2 0x0100 +#define SOFTUART_CONFIG_TXLVL_4 0x0200 +#define SOFTUART_CONFIG_TXLVL_6 0x0300 +#define SOFTUART_CONFIG_TXLVL_7 0x0400 +#define SOFTUART_CONFIG_RXLVL_M 0x3800 +#define SOFTUART_CONFIG_RXLVL_1 0x0000 +#define SOFTUART_CONFIG_RXLVL_2 0x0800 +#define SOFTUART_CONFIG_RXLVL_4 0x1000 +#define SOFTUART_CONFIG_RXLVL_6 0x1800 +#define SOFTUART_CONFIG_RXLVL_7 0x2000 + +//***************************************************************************** +// +// The odd parity of each possible data byte. The odd parity of N can be found +// by looking at bit N % 32 of word N / 32. +// +//***************************************************************************** +static uint32_t g_pui32ParityOdd[] = +{ + 0x69969669, 0x96696996, 0x96696996, 0x69969669, + 0x96696996, 0x69969669, 0x69969669, 0x96696996 +}; + +//***************************************************************************** +// +//! Initializes the SoftUART module. +//! +//! \param psUART specifies the soft UART data structure. +//! +//! This function initializes the data structure for the SoftUART module, +//! putting it into the default configuration. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTInit(tSoftUART *psUART) +{ + // + // Clear the SoftUART data structure. + // + memset(psUART, 0, sizeof(tSoftUART)); + + // + // Set the default transmit and receive buffer interrupt level. + // + psUART->ui16Config = SOFTUART_CONFIG_TXLVL_4 | SOFTUART_CONFIG_RXLVL_4; +} + +//***************************************************************************** +// +//! Sets the configuration of a SoftUART module. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32Config is the data format for the port (number of data bits, +//! number of stop bits, and parity). +//! +//! This function configures the SoftUART for operation in the specified data +//! format, as specified in the \e ui32Config parameter. +//! +//! The \e ui32Config parameter is the logical OR of three values: the number +//! of data bits, the number of stop bits, and the parity. +//! \b SOFTUART_CONFIG_WLEN_8, \b SOFTUART_CONFIG_WLEN_7, +//! \b SOFTUART_CONFIG_WLEN_6, and \b SOFTUART_CONFIG_WLEN_5 select from eight +//! to five data bits per byte (respectively). \b SOFTUART_CONFIG_STOP_ONE and +//! \b SOFTUART_CONFIG_STOP_TWO select one or two stop bits (respectively). +//! \b SOFTUART_CONFIG_PAR_NONE, \b SOFTUART_CONFIG_PAR_EVEN, +//! \b SOFTUART_CONFIG_PAR_ODD, \b SOFTUART_CONFIG_PAR_ONE, and +//! \b SOFTUART_CONFIG_PAR_ZERO select the parity mode (no parity bit, even +//! parity bit, odd parity bit, parity bit always one, and parity bit always +//! zero, respectively). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTConfigSet(tSoftUART *psUART, uint32_t ui32Config) +{ + // + // See if a GPIO pin has been set for Tx. + // + if(psUART->ui32TxGPIO != 0) + { + // + // Configure the Tx pin. + // + MAP_GPIOPinTypeGPIOOutput(psUART->ui32TxGPIO & 0xfffff000, + (psUART->ui32TxGPIO & 0x00000fff) >> 2); + + // + // Set the Tx pin high. + // + HWREG(psUART->ui32TxGPIO) = 255; + } + + // + // See if a GPIO pin has been set for Rx. + // + if(psUART->ui32RxGPIOPort != 0) + { + // + // Configure the Rx pin. + // + MAP_GPIOPinTypeGPIOInput(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // Set the Rx pin to generate an interrupt on the next falling edge. + // + MAP_GPIOIntTypeSet(psUART->ui32RxGPIOPort, psUART->ui8RxPin, + GPIO_FALLING_EDGE); + + // + // Enable the Rx pin interrupt. + // + GPIOIntClear(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + GPIOIntEnable(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + } + + // + // Make sure that the transmit and receive buffers are empty. + // + psUART->ui16TxBufferRead = 0; + psUART->ui16TxBufferWrite = 0; + psUART->ui16RxBufferRead = 0; + psUART->ui16RxBufferWrite = 0; + + // + // Save the data format. + // + psUART->ui16Config = ((psUART->ui16Config & SOFTUART_CONFIG_EXT_M) | + (ui32Config & SOFTUART_CONFIG_BASE_M)); + + // + // Enable the SoftUART module. + // + psUART->ui8Flags |= SOFTUART_FLAG_ENABLE; + + // + // The next value to be written to the Tx pin is one since the SoftUART is + // idle. + // + psUART->ui8TxNext = 255; + + // + // Start the SoftUART state machines in the idle state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_IDLE; + psUART->ui8RxState = SOFTUART_RXSTATE_IDLE; +} + +//***************************************************************************** +// +//! Performs the periodic update of the SoftUART transmitter. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function performs the periodic, time-based updates to the SoftUART +//! transmitter. The transmission of data from the SoftUART is performed by +//! the state machine in this function. +//! +//! This function must be called at the desired SoftUART baud rate. For +//! example, to run the SoftUART at 115,200 baud, this function must be called +//! at a 115,200 Hz rate. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTTxTimerTick(tSoftUART *psUART) +{ + uint32_t ui32Temp; + + // + // Write the next value to the Tx data line. This value was computed on + // the previous timer tick, which helps to reduce the jitter on the Tx + // edges (which is important since a UART connection does not contain a + // clock signal). + // + HWREG(psUART->ui32TxGPIO) = psUART->ui8TxNext; + + // + // Determine the current state of the state machine. + // + switch(psUART->ui8TxState) + { + // + // The state machine is idle. + // + case SOFTUART_TXSTATE_IDLE: + { + // + // See if the SoftUART module is enabled. + // + if(!(psUART->ui8Flags & SOFTUART_FLAG_ENABLE)) + { + // + // The SoftUART module is not enabled, so do nothing and stay + // in the idle state. + // + break; + } + + // + // See if the break signal should be asserted. + // + else if(psUART->ui8Flags & SOFTUART_FLAG_TXBREAK) + { + // + // The data line should be driven low while in the break state. + // + psUART->ui8TxNext = 0; + + // + // Move to the break state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_BREAK; + } + + // + // Otherwise, see if there is data in the transmit buffer. + // + else if(psUART->ui16TxBufferRead != psUART->ui16TxBufferWrite) + { + // + // The data line should be driven low to indicate a start bit. + // + psUART->ui8TxNext = 0; + + // + // Move to the start bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_START; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the start bit state. + // + case SOFTUART_TXSTATE_START: + { + // + // Get the next byte to be transmitted. + // + psUART->ui8TxData = psUART->pui8TxBuffer[psUART->ui16TxBufferRead]; + + // + // The next value to be written to the data line is the LSB of the + // next data byte. + // + psUART->ui8TxNext = (psUART->ui8TxData & 1) ? 255 : 0; + + // + // Move to the data bit 0 state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_DATA_0; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, a bit of the data byte must be output. + // This depends upon TXSTATE_DATA_n and TXSTATE_DATA_(n+1) being + // consecutively numbered. + // + case SOFTUART_TXSTATE_DATA_0: + case SOFTUART_TXSTATE_DATA_1: + case SOFTUART_TXSTATE_DATA_2: + case SOFTUART_TXSTATE_DATA_3: + { + // + // The next value to be written to the data line is the next bit of + // the data byte. + // + psUART->ui8TxNext = + (psUART->ui8TxData & (1 << psUART->ui8TxState)) ? 255 : 0; + + // + // Advance to the next state. + // + psUART->ui8TxState++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, a bit of the data byte must be output. + // Additionally, based on the configuration of the SoftUART, this bit + // might be the last data bit of the data byte. This depends upon + // TXSTATE_DATA_n and TXSTATE_DATA_(n+1) being consecutively numbered. + // + case SOFTUART_TXSTATE_DATA_4: + case SOFTUART_TXSTATE_DATA_5: + case SOFTUART_TXSTATE_DATA_6: + case SOFTUART_TXSTATE_DATA_7: + { + // + // See if the bit that was just transferred is the last bit of the + // data byte (based on the configuration of the SoftUART). + // + if(((psUART->ui16Config & SOFTUART_CONFIG_WLEN_MASK) >> + SOFTUART_CONFIG_WLEN_S) == + (psUART->ui8TxState - SOFTUART_TXSTATE_DATA_4)) + { + // + // See if parity is enabled. + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) != + SOFTUART_CONFIG_PAR_NONE) + { + // + // See if the parity is set to one. + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ONE) + { + // + // The next value to be written to the data line is + // one. + // + psUART->ui8TxNext = 255; + } + + // + // Otherwise, see if the parity is set to zero. + // + else if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ZERO) + { + // + // The next value to be written to the data line is + // zero. + // + psUART->ui8TxNext = 0; + } + + // + // Otherwise, there is either even or odd parity. + // + else + { + // + // Find the odd parity for the data byte. + // + psUART->ui8TxNext = + ((g_pui32ParityOdd[psUART->ui8TxData >> 5] & + (1 << (psUART->ui8TxData & 31))) ? 255 : 0); + + // + // If the parity is set to even, then invert the + // parity just computed (making it even parity). + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_EVEN) + { + psUART->ui8TxNext ^= 255; + } + } + + // + // Advance to the parity state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_PARITY; + } + + // + // Parity is not enabled. + // + else + { + // + // The next value to write to the data line is the stop + // bit. + // + psUART->ui8TxNext = 255; + + // + // See if there are one or two stop bits. + // + if((psUART->ui16Config & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_0; + } + else + { + // + // Advance to the one stop bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_1; + } + } + } + + // + // Otherwise, there are more data bits to transfer. + // + else + { + // + // The next value to be written to the data line is the next + // bit of the data byte. + // + psUART->ui8TxNext = + (psUART->ui8TxData & (1 << psUART->ui8TxState)) ? 255 : 0; + + // + // Advance to the next state. + // + psUART->ui8TxState++; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the parity bit state. + // + case SOFTUART_TXSTATE_PARITY: + { + // + // The next value to write to the data line is the stop bit. + // + psUART->ui8TxNext = 255; + + // + // See if there are one or two stop bits. + // + if((psUART->ui16Config & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_0; + } + else + { + // + // Advance to the one stop bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_1; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the two stop bits state. + // + case SOFTUART_TXSTATE_STOP_0: + { + // + // Advance to the one stop bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_STOP_1; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the one stop bit state. + // + case SOFTUART_TXSTATE_STOP_1: + { + // + // The data byte has been completely transferred, so advance the + // read pointer. + // + psUART->ui16TxBufferRead++; + if(psUART->ui16TxBufferRead == psUART->ui16TxBufferLen) + { + psUART->ui16TxBufferRead = 0; + } + + // + // Determine the number of characters in the transmit buffer. + // + if(psUART->ui16TxBufferRead > psUART->ui16TxBufferWrite) + { + ui32Temp = (psUART->ui16TxBufferLen - + (psUART->ui16TxBufferRead - + psUART->ui16TxBufferWrite)); + } + else + { + ui32Temp = (psUART->ui16TxBufferWrite - + psUART->ui16TxBufferRead); + } + + // + // If the transmit buffer fullness just crossed the programmed + // level, generate a transmit "interrupt". + // + if(ui32Temp == psUART->ui16TxBufferLevel) + { + psUART->ui16IntStatus |= SOFTUART_INT_TX; + } + + // + // See if the SoftUART module is enabled. + // + if(!(psUART->ui8Flags & SOFTUART_FLAG_ENABLE)) + { + // + // The SoftUART module is not enabled, so do advance to the + // idle state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_IDLE; + } + + // + // See if the break signal should be asserted. + // + else if(psUART->ui8Flags & SOFTUART_FLAG_TXBREAK) + { + // + // The data line should be driven low while in the break state. + // + psUART->ui8TxNext = 0; + + // + // Move to the break state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_BREAK; + } + + // + // Otherwise, see if there is data in the transmit buffer. + // + else if(psUART->ui16TxBufferRead != psUART->ui16TxBufferWrite) + { + // + // The data line should be driven low to indicate a start bit. + // + psUART->ui8TxNext = 0; + + // + // Move to the start bit state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_START; + } + + // + // Otherwise, there is nothing to do. + // + else + { + // + // Assert the end of transmission "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_EOT; + + // + // Advance to the idle state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_IDLE; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the break state. + // + case SOFTUART_TXSTATE_BREAK: + { + // + // See if the break should be deasserted. + // + if(!(psUART->ui8Flags & SOFTUART_FLAG_ENABLE) || + !(psUART->ui8Flags & SOFTUART_FLAG_TXBREAK)) + { + // + // The data line should be driven high to indicate it is idle. + // + psUART->ui8TxNext = 255; + + // + // Advance to the idle state. + // + psUART->ui8TxState = SOFTUART_TXSTATE_IDLE; + } + + // + // This state has been handled. + // + break; + } + } + + // + // Call the "interrupt" callback while there are enabled "interrupts" + // asserted. By calling in a loop until the "interrupts" are no longer + // asserted, this mimics the behavior of a real hardware implementation of + // the UART peripheral. + // + while(((psUART->ui16IntStatus & psUART->ui16IntMask) != 0) && + (psUART->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + psUART->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Handles the assertion of the receive ``interrupt''. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function is used to determine when to assert the receive ``interrupt'' +//! as a result of writing data into the receive buffer (when characters are +//! received from the Rx pin). +//! +//! \return None. +// +//***************************************************************************** +static void +SoftUARTRxWriteInt(tSoftUART *psUART) +{ + uint32_t ui32Temp; + + // + // Determine the number of characters in the receive buffer. + // + if(psUART->ui16RxBufferWrite > psUART->ui16RxBufferRead) + { + ui32Temp = psUART->ui16RxBufferWrite - psUART->ui16RxBufferRead; + } + else + { + ui32Temp = (psUART->ui16RxBufferLen + psUART->ui16RxBufferWrite - + psUART->ui16RxBufferRead); + } + + // + // If the receive buffer fullness just crossed the programmed level, + // generate a receive "interrupt". + // + if(ui32Temp == psUART->ui16RxBufferLevel) + { + psUART->ui16IntStatus |= SOFTUART_INT_RX; + } +} + +//***************************************************************************** +// +//! Performs the periodic update of the SoftUART receiver. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param bEdgeInt should be \b true if this function is being called because +//! of a GPIO edge interrupt and \b false if it is being called because of a +//! timer interrupt. +//! +//! This function performs the periodic, time-based updates to the SoftUART +//! receiver. The reception of data to the SoftUART is performed by the state +//! machine in this function. +//! +//! This function must be called by the GPIO interrupt handler, and then +//! periodically at the desired SoftUART baud rate. For example, to run the +//! SoftUART at 115,200 baud, this function must be called at a 115,200 Hz +//! rate. +//! +//! \return Returns \b SOFTUART_RXTIMER_NOP if the receive timer should +//! continue to operate or \b SOFTUART_RXTIMER_END if it should be stopped. +// +//***************************************************************************** +uint32_t +SoftUARTRxTick(tSoftUART *psUART, bool bEdgeInt) +{ + uint32_t ui32PinState, ui32Temp, ui32Ret; + + // + // Read the current state of the Rx data line. + // + ui32PinState = MAP_GPIOPinRead(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // The default return code inidicates that the receive timer does not need + // to be stopped. + // + ui32Ret = SOFTUART_RXTIMER_NOP; + + // + // See if this is an edge interrupt while delaying for the receive timeout + // interrupt. + // + if(bEdgeInt && (psUART->ui8RxState == SOFTUART_RXSTATE_DELAY)) + { + // + // The receive timeout has been cancelled since the next character has + // started, so go to the idle state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_IDLE; + } + + // + // Determine the current state of the state machine. + // + switch(psUART->ui8RxState) + { + // + // The state machine is idle. + // + case SOFTUART_RXSTATE_IDLE: + { + // + // The falling edge of the start bit was just sampled, so disable + // the GPIO edge interrupt since the remainder of the character + // will be read using a timer tick. + // + GPIOIntClear(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + GPIOIntDisable(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // Clear the receive data buffer. + // + psUART->ui8RxData = 0; + + // + // Clear all reception errors other than overrun (which is cleared + // only when the first character after the overrun is written into + // the receive buffer), and set the break error (which is cleared + // if any non-zero bits are read during this character). + // + psUART->ui8RxFlags = ((psUART->ui8RxFlags & SOFTUART_RXFLAG_OE) | + SOFTUART_RXFLAG_BE); + + // + // Advance to the first data bit state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_DATA_0; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, a bit of the data byte is read. This + // depends upon RXSTATE_DATA_n and RXSTATE_DATA_(n+1) being + // consecutively numbered. + // + case SOFTUART_RXSTATE_DATA_0: + case SOFTUART_RXSTATE_DATA_1: + case SOFTUART_RXSTATE_DATA_2: + case SOFTUART_RXSTATE_DATA_3: + { + // + // See if the Rx pin is high. + // + if(ui32PinState != 0) + { + // + // Set this bit of the received character. + // + psUART->ui8RxData |= 1 << psUART->ui8RxState; + + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // Advance to the next state. + // + psUART->ui8RxState++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, a bit of the data byte is read. + // Additionally, based on the configuration of the SoftUART, this bit + // might be the last bit of the data byte. This depends upon + // RXSTATE_DATA_n and RXSTATE_DATA_(n+1) being consecutively numbered. + // + case SOFTUART_RXSTATE_DATA_4: + case SOFTUART_RXSTATE_DATA_5: + case SOFTUART_RXSTATE_DATA_6: + case SOFTUART_RXSTATE_DATA_7: + { + // + // See if the Rx pin is high. + // + if(ui32PinState != 0) + { + // + // Set this bit of the received character. + // + psUART->ui8RxData |= 1 << psUART->ui8RxState; + + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // See if the bit that was just transferred is the last bit of the + // data byte (based on the configuration of the SoftUART). + // + if(((psUART->ui16Config & SOFTUART_CONFIG_WLEN_MASK) >> + SOFTUART_CONFIG_WLEN_S) == + (psUART->ui8RxState - SOFTUART_RXSTATE_DATA_4)) + { + // + // See if parity is enabled. + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) != + SOFTUART_CONFIG_PAR_NONE) + { + // + // Advance to the parity state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_PARITY; + } + + // + // Otherwise, see if there are one or two stop bits. + // + else if((psUART->ui16Config & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_0; + } + + // + // Otherwise, advance to the one stop bit state. + // + else + { + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_1; + } + } + + // + // Otherwise, there are more bits to receive. + // + else + { + // + // Advance to the next state. + // + psUART->ui8RxState++; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the parity bit state. + // + case SOFTUART_RXSTATE_PARITY: + { + // + // See if the parity is set to one. + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ONE) + { + // + // Set the expected parity to one. + // + ui32Temp = psUART->ui8RxPin; + } + + // + // Otherwise, see if the parity is set to zero. + // + else if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ZERO) + { + // + // Set the expected parity to zero. + // + ui32Temp = 0; + } + + // + // Otherwise, there is either even or odd parity. + // + else + { + // + // Find the odd parity for the data byte. + // + ui32Temp = ((g_pui32ParityOdd[psUART->ui8RxData >> 5] & + (1 << (psUART->ui8RxData & 31))) ? + psUART->ui8RxPin : 0); + + // + // If the parity is set to even, then invert the parity just + // computed (making it even parity). + // + if((psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_EVEN) + { + ui32Temp ^= psUART->ui8RxPin; + } + } + + // + // See if the pin state matches the expected parity. + // + if(ui32PinState != ui32Temp) + { + // + // The parity does not match, so set the parity error flag. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_PE; + } + + // + // See if the Rx pin is high. + // + if(ui32PinState != 0) + { + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // See if there are one or two stop bits. + // + if((psUART->ui16Config & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_0; + } + else + { + // + // Advance to the one stop bit state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_1; + } + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the two stop bits state. + // + case SOFTUART_RXSTATE_STOP_0: + { + // + // See if the Rx pin is low. + // + if(ui32PinState == 0) + { + // + // Since the Rx pin is low, there is a framing error. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_FE; + } + else + { + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // Advance to the one stop bit state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_STOP_1; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the one stop bit state. + // + case SOFTUART_RXSTATE_STOP_1: + { + // + // See if the Rx pin is low. + // + if(ui32PinState == 0) + { + // + // Since the Rx pin is low, there is a framing error. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_FE; + } + else + { + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // See if the break error is still asserted (meaning that every bit + // received was zero). + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_BE) + { + // + // Since every bit was zero, advance to the break state. + // + psUART->ui8RxState = SOFTUART_RXSTATE_BREAK; + + // + // This state has been handled. + // + break; + } + + // + // Compute the value of the write pointer advanced by one. + // + ui32Temp = psUART->ui16RxBufferWrite + 1; + if(ui32Temp == psUART->ui16RxBufferLen) + { + ui32Temp = 0; + } + + // + // See if there is space in the receive buffer. + // + if(ui32Temp == psUART->ui16RxBufferRead) + { + // + // Set the overrun error flag. This will remain set until a + // new character can be placed into the receive buffer, which + // will then be given this status. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_OE; + + // + // Set the receive overrun "interrupt" and status if it is not + // already set. + // + if(!(psUART->ui8RxStatus & SOFTUART_RXERROR_OVERRUN)) + { + psUART->ui8RxStatus |= SOFTUART_RXERROR_OVERRUN; + psUART->ui16IntStatus |= SOFTUART_INT_OE; + } + } + + // + // Otherwise, there is space in the receive buffer. + // + else + { + // + // Write this data byte, along with the receive flags, into the + // receive buffer. + // + psUART->pui16RxBuffer[psUART->ui16RxBufferWrite] = + psUART->ui8RxData | (psUART->ui8RxFlags << 8); + + // + // Advance the write pointer. + // + psUART->ui16RxBufferWrite = ui32Temp; + + // + // Clear the receive flags, most importantly the overrun flag + // since it was just written into the receive buffer. + // + psUART->ui8RxFlags = 0; + + // + // Assert the receive "interrupt" if appropriate. + // + SoftUARTRxWriteInt(psUART); + } + + // + // See if this character had a parity error. + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_PE) + { + // + // Assert the parity error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_PE; + } + + // + // See if this character had a framing error. + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_FE) + { + // + // Assert the framing error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_FE; + } + + // + // Enable the falling edge interrupt on the Rx pin so that the next + // start bit can be detected. + // + GPIOIntClear(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + GPIOIntEnable(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // Advance to the receive timeout delay state. + // + psUART->ui8RxData = 0; + psUART->ui8RxState = SOFTUART_RXSTATE_DELAY; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the break state. + // + case SOFTUART_RXSTATE_BREAK: + { + // + // See if the Rx pin is high. + // + if(ui32PinState != 0) + { + // + // Clear the break error since a non-zero bit was received. + // + psUART->ui8RxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // Compute the value of the write pointer advanced by one. + // + ui32Temp = psUART->ui16RxBufferWrite + 1; + if(ui32Temp == psUART->ui16RxBufferLen) + { + ui32Temp = 0; + } + + // + // See if there is space in the receive buffer. + // + if(ui32Temp == psUART->ui16RxBufferRead) + { + // + // Set the overrun error flag. This will remain set until a + // new character can be placed into the receive buffer, which + // will then be given this status. + // + psUART->ui8RxFlags |= SOFTUART_RXFLAG_OE; + + // + // Set the receive overrun "interrupt" and status if it is not + // already set. + // + if(!(psUART->ui8RxStatus & SOFTUART_RXERROR_OVERRUN)) + { + psUART->ui8RxStatus |= SOFTUART_RXERROR_OVERRUN; + psUART->ui16IntStatus |= SOFTUART_INT_OE; + } + } + + // + // Otherwise, there is space in the receive buffer. + // + else + { + // + // Write this data byte, along with the receive flags, into the + // receive buffer. + // + psUART->pui16RxBuffer[psUART->ui16RxBufferWrite] = + psUART->ui8RxData | (psUART->ui8RxFlags << 8); + + // + // Advance the write pointer. + // + psUART->ui16RxBufferWrite = ui32Temp; + + // + // Clear the receive flags, most importantly the overrun flag + // since it was just written into the receive buffer. + // + psUART->ui8RxFlags = 0; + + // + // Assert the receive "interrupt" if appropriate. + // + SoftUARTRxWriteInt(psUART); + } + + // + // See if this was a break error. + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_BE) + { + // + // Assert the break error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_BE; + } + + // + // See if this character had a parity error. + // + if(psUART->ui8RxFlags & SOFTUART_RXFLAG_PE) + { + // + // Assert the parity error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_PE; + } + + // + // Assert the framing error "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_FE; + + // + // Enable the falling edge interrupt on the Rx pin so that the next + // start bit can be detected. + // + GPIOIntClear(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + GPIOIntEnable(psUART->ui32RxGPIOPort, psUART->ui8RxPin); + + // + // Advance to the receive timeout delay state. + // + psUART->ui8RxData = 0; + psUART->ui8RxState = SOFTUART_RXSTATE_DELAY; + + // + // This state has been handled. + // + break; + } + + // + // The state machine is in the receive timeout delay state. + // + case SOFTUART_RXSTATE_DELAY: + { + // + // See if the receive timeout has expired. + // + if(psUART->ui8RxData++ == 32) + { + // + // Assert the receive timeout "interrupt". + // + psUART->ui16IntStatus |= SOFTUART_INT_RT; + + // + // Tell the caller that the receive timer can be disabled. + // + ui32Ret = SOFTUART_RXTIMER_END; + } + + // + // This state has been handled. + // + break; + } + } + + // + // Call the "interrupt" callback while there are enabled "interrupts" + // asserted. By calling in a loop until the "interrupts" are no longer + // asserted, this mimics the behavior of a real hardware implementation of + // the UART peripheral. + // + while(((psUART->ui16IntStatus & psUART->ui16IntMask) != 0) && + (psUART->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + psUART->pfnIntCallback(); + } + + // + // Return to the caller. + // + return(ui32Ret); +} + +//***************************************************************************** +// +//! Sets the type of parity. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32Parity specifies the type of parity to use. +//! +//! Sets the type of parity to use for transmitting and expect when receiving. +//! The \e ui32Parity parameter must be one of \b SOFTUART_CONFIG_PAR_NONE, +//! \b SOFTUART_CONFIG_PAR_EVEN, \b SOFTUART_CONFIG_PAR_ODD, +//! \b SOFTUART_CONFIG_PAR_ONE, or \b SOFTUART_CONFIG_PAR_ZERO. The last two +//! allow direct control of the parity bit; it is always either one or zero +//! based on the mode. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTParityModeSet(tSoftUART *psUART, uint32_t ui32Parity) +{ + // + // Check the arguments. + // + ASSERT((ui32Parity == SOFTUART_CONFIG_PAR_NONE) || + (ui32Parity == SOFTUART_CONFIG_PAR_EVEN) || + (ui32Parity == SOFTUART_CONFIG_PAR_ODD) || + (ui32Parity == SOFTUART_CONFIG_PAR_ONE) || + (ui32Parity == SOFTUART_CONFIG_PAR_ZERO)); + + // + // Set the parity mode. + // + psUART->ui16Config = + (psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK) | ui32Parity; +} + +//***************************************************************************** +// +//! Gets the type of parity currently being used. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function gets the type of parity used for transmitting data and +//! expected when receiving data. +//! +//! \return Returns the current parity settings, specified as one of +//! \b SOFTUART_CONFIG_PAR_NONE, \b SOFTUART_CONFIG_PAR_EVEN, +//! \b SOFTUART_CONFIG_PAR_ODD, \b SOFTUART_CONFIG_PAR_ONE, or +//! \b SOFTUART_CONFIG_PAR_ZERO. +// +//***************************************************************************** +uint32_t +SoftUARTParityModeGet(tSoftUART *psUART) +{ + // + // Return the current parity setting. + // + return(psUART->ui16Config & SOFTUART_CONFIG_PAR_MASK); +} + +//***************************************************************************** +// +//! Sets the transmit ``interrupt'' buffer level. +//! +//! \param psUART specifies the soft UART data structure. +//! +//! This function computes the transmit buffer level at which the transmit +//! ``interrupt'' is generated. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftUARTTxLevelSet(tSoftUART *psUART) +{ + // + // Determine the transmit buffer "interrupt" fullness setting. + // + switch(psUART->ui16Config & SOFTUART_CONFIG_TXLVL_M) + { + // + // The transmit "interrupt" should be generated when the buffer is 1/8 + // full. + // + case SOFTUART_CONFIG_TXLVL_1: + { + // + // Set the transmit buffer level to 1/8 of the buffer length. + // + psUART->ui16TxBufferLevel = psUART->ui16TxBufferLen / 8; + + // + // This setting has been handled. + // + break; + } + + // + // The transmit "interrupt" should be generated when the buffer is 1/4 + // (2/8) full. + // + case SOFTUART_CONFIG_TXLVL_2: + { + // + // Set the transmit buffer level to 1/4 of the buffer length. + // + psUART->ui16TxBufferLevel = psUART->ui16TxBufferLen / 4; + + // + // This setting has been handled. + // + break; + } + + // + // The transmit "interrupt" should be generated when the buffer is 1/2 + // (4/8) full. + // + case SOFTUART_CONFIG_TXLVL_4: + { + // + // Set the transmit buffer level to 1/2 of the buffer length. + // + psUART->ui16TxBufferLevel = psUART->ui16TxBufferLen / 2; + + // + // This setting has been handled. + // + break; + } + + // + // The transmit "interrupt" should be generated when the buffer is 3/4 + // (6/8) full. + // + case SOFTUART_CONFIG_TXLVL_6: + { + // + // Set the transmit buffer level to 3/4 of the buffer length. + // + psUART->ui16TxBufferLevel = (psUART->ui16TxBufferLen * 3) / 4; + + // + // This setting has been handled. + // + break; + } + + // + // The transmit "interrupt" should be generated when the buffer is 7/8 + // full. + // + case SOFTUART_CONFIG_TXLVL_7: + { + // + // Set the transmit buffer level to 7/8 of the buffer length. + // + psUART->ui16TxBufferLevel = (psUART->ui16TxBufferLen * 7) / 8; + + // + // This setting has been handled. + // + break; + } + } +} + +//***************************************************************************** +// +//! Sets the receive ``interrupt'' buffer level. +//! +//! \param psUART specifies the soft UART data structure. +//! +//! This function computes the receive buffer level at which the receive +//! ``interrupt'' is generated. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftUARTRxLevelSet(tSoftUART *psUART) +{ + // + // Determine the receive buffer "interrupt" fullness setting. + // + switch(psUART->ui16Config & SOFTUART_CONFIG_RXLVL_M) + { + // + // The receive "interrupt" should be generated when the buffer is 1/8 + // full. + // + case SOFTUART_CONFIG_RXLVL_1: + { + // + // Set the receive buffer level to 1/8 of the buffer length. + // + psUART->ui16RxBufferLevel = psUART->ui16RxBufferLen / 8; + + // + // This setting has been handled. + // + break; + } + + // + // The receive "interrupt" should be generated when the buffer is 1/4 + // (2/8) full. + // + case SOFTUART_CONFIG_RXLVL_2: + { + // + // Set the receive buffer level to 1/4 of the buffer length. + // + psUART->ui16RxBufferLevel = psUART->ui16RxBufferLen / 4; + + // + // This setting has been handled. + // + break; + } + + // + // The receive "interrupt" should be generated when the buffer is 1/2 + // (4/8) full. + // + case SOFTUART_CONFIG_RXLVL_4: + { + // + // Set the receive buffer level to 1/2 of the buffer length. + // + psUART->ui16RxBufferLevel = psUART->ui16RxBufferLen / 2; + + // + // This setting has been handled. + // + break; + } + + // + // The receive "interrupt" should be generated when the buffer is 3/4 + // (6/8) full. + // + case SOFTUART_CONFIG_RXLVL_6: + { + // + // Set the receive buffer level to 3/4 of the buffer length. + // + psUART->ui16RxBufferLevel = (psUART->ui16RxBufferLen * 3) / 4; + + // + // This setting has been handled. + // + break; + } + + // + // The receive "interrupt" should be generated when the buffer is 7/8 + // full. + // + case SOFTUART_CONFIG_RXLVL_7: + { + // + // Set the receive buffer level to 7/8 of the buffer length. + // + psUART->ui16RxBufferLevel = (psUART->ui16RxBufferLen * 7) / 8; + + // + // This setting has been handled. + // + break; + } + } +} + +//***************************************************************************** +// +//! Sets the buffer level at which ``interrupts'' are generated. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32TxLevel is the transmit buffer ``interrupt'' level, specified as +//! one of \b UART_FIFO_TX1_8, \b UART_FIFO_TX2_8, \b UART_FIFO_TX4_8, +//! \b UART_FIFO_TX6_8, or \b UART_FIFO_TX7_8. +//! \param ui32RxLevel is the receive buffer ``interrupt'' level, specified as +//! one of \b UART_FIFO_RX1_8, \b UART_FIFO_RX2_8, \b UART_FIFO_RX4_8, +//! \b UART_FIFO_RX6_8, or \b UART_FIFO_RX7_8. +//! +//! This function sets the buffer level at which transmit and receive +//! ``interrupts'' are generated. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTFIFOLevelSet(tSoftUART *psUART, uint32_t ui32TxLevel, + uint32_t ui32RxLevel) +{ + // + // Check the arguments. + // + ASSERT((ui32TxLevel == SOFTUART_FIFO_TX1_8) || + (ui32TxLevel == SOFTUART_FIFO_TX2_8) || + (ui32TxLevel == SOFTUART_FIFO_TX4_8) || + (ui32TxLevel == SOFTUART_FIFO_TX6_8) || + (ui32TxLevel == SOFTUART_FIFO_TX7_8)); + ASSERT((ui32RxLevel == SOFTUART_FIFO_RX1_8) || + (ui32RxLevel == SOFTUART_FIFO_RX2_8) || + (ui32RxLevel == SOFTUART_FIFO_RX4_8) || + (ui32RxLevel == SOFTUART_FIFO_RX6_8) || + (ui32RxLevel == SOFTUART_FIFO_RX7_8)); + + // + // Save the buffer "interrupt" levels. + // + psUART->ui16Config = ((psUART->ui16Config & SOFTUART_CONFIG_BASE_M) | + ((ui32TxLevel | ui32RxLevel) << 8)); + + // + // Compute the new buffer "interrupt" levels. + // + SoftUARTTxLevelSet(psUART); + SoftUARTRxLevelSet(psUART); +} + +//***************************************************************************** +// +//! Gets the buffer level at which ``interrupts'' are generated. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pui32TxLevel is a pointer to storage for the transmit buffer level, +//! returned as one of \b UART_FIFO_TX1_8, \b UART_FIFO_TX2_8, +//! \b UART_FIFO_TX4_8, \b UART_FIFO_TX6_8, or \b UART_FIFO_TX7_8. +//! \param pui32RxLevel is a pointer to storage for the receive buffer level, +//! returned as one of \b UART_FIFO_RX1_8, \b UART_FIFO_RX2_8, +//! \b UART_FIFO_RX4_8, \b UART_FIFO_RX6_8, or \b UART_FIFO_RX7_8. +//! +//! This function gets the buffer level at which transmit and receive +//! ``interrupts'' are generated. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTFIFOLevelGet(tSoftUART *psUART, uint32_t *pui32TxLevel, + uint32_t *pui32RxLevel) +{ + // + // Extract the transmit and receive buffer levels. + // + *pui32TxLevel = (psUART->ui16Config & SOFTUART_CONFIG_TXLVL_M) >> 8; + *pui32RxLevel = (psUART->ui16Config & SOFTUART_CONFIG_RXLVL_M) >> 8; +} + +//***************************************************************************** +// +//! Gets the current configuration of a UART. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pui32Config is a pointer to storage for the data format. +//! +//! Returns the data format of the SoftUART. The data format returned in +//! \e pui32Config is enumerated the same as the \e ui32Config parameter of +//! SoftUARTConfigSet(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTConfigGet(tSoftUART *psUART, uint32_t *pui32Config) +{ + // + // Get the data format. + // + *pui32Config = psUART->ui16Config & SOFTUART_CONFIG_BASE_M; +} + +//***************************************************************************** +// +//! Enables the SoftUART. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function enables the SoftUART, allowing data to be transmitted and +//! received. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTEnable(tSoftUART *psUART) +{ + // + // Enable the SoftUART. + // + psUART->ui8Flags |= SOFTUART_FLAG_ENABLE; +} + +//***************************************************************************** +// +//! Disables the SoftUART. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function disables the SoftUART after waiting for it to become idle. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTDisable(tSoftUART *psUART) +{ + // + // Wait for end of TX. + // + while(SoftUARTBusy(psUART)) + { + } + + // + // Disable the SoftUART. + // + psUART->ui8Flags &= ~(SOFTUART_FLAG_ENABLE); +} + +//***************************************************************************** +// +//! Determines if there are any characters in the receive buffer. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function returns a flag indicating whether or not there is data +//! available in the receive buffer. +//! +//! \return Returns \b true if there is data in the receive buffer or \b false +//! if there is no data in the receive buffer. +// +//***************************************************************************** +bool +SoftUARTCharsAvail(tSoftUART *psUART) +{ + // + // Return the availability of characters. + // + return((psUART->ui16RxBufferRead == psUART->ui16RxBufferWrite) ? false : + true); +} + +//***************************************************************************** +// +//! Determines if there is any space in the transmit buffer. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function returns a flag indicating whether or not there is space +//! available in the transmit buffer. +//! +//! \return Returns \b true if there is space available in the transmit buffer +//! or \b false if there is no space available in the transmit buffer. +// +//***************************************************************************** +bool +SoftUARTSpaceAvail(tSoftUART *psUART) +{ + uint16_t ui16Temp; + + // + // Determine the values of the write pointer once incremented. + // + ui16Temp = psUART->ui16TxBufferWrite + 1; + if(ui16Temp == psUART->ui16TxBufferLen) + { + ui16Temp = 0; + } + + // + // Return the availability of space. + // + return((psUART->ui16TxBufferRead == ui16Temp) ? false : true); +} + +//***************************************************************************** +// +//! Handles the deassertion of the receive ``interrupts''. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function is used to determine when to deassert the receive +//! ``interrupt'' as a result of reading data from the receive buffer. +//! +//! \return None. +// +//***************************************************************************** +static void +SoftUARTRxReadInt(tSoftUART *psUART) +{ + uint32_t ui32Temp; + + // + // Determine the number of characters in the receive buffer. + // + if(psUART->ui16RxBufferWrite > psUART->ui16RxBufferRead) + { + ui32Temp = psUART->ui16RxBufferWrite - psUART->ui16RxBufferRead; + } + else + { + ui32Temp = (psUART->ui16RxBufferLen + psUART->ui16RxBufferWrite - + psUART->ui16RxBufferRead); + } + + // + // See if the number of characters in the receive buffer have dropped below + // the receive trigger level. + // + if(ui32Temp < psUART->ui16RxBufferLevel) + { + // + // Deassert the receive "interrupt". + // + psUART->ui16IntStatus &= ~(SOFTUART_INT_RX); + } + + // + // See if the receive buffer is now empty. + // + if(ui32Temp == 0) + { + // + // Deassert the receive timeout "interrupt". + // + psUART->ui16IntStatus &= ~(SOFTUART_INT_RT); + } +} + +//***************************************************************************** +// +//! Receives a character from the specified port. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! Gets a character from the receive buffer for the specified port. +//! +//! \return Returns the character read from the specified port, cast as a +//! \e int32_t. A \b -1 is returned if there are no characters present in the +//! receive buffer. The SoftUARTCharsAvail() function should be called before +//! attempting to call this function. +// +//***************************************************************************** +int32_t +SoftUARTCharGetNonBlocking(tSoftUART *psUART) +{ + int32_t i32Temp; + + // + // See if there are any characters in the receive buffer. + // + if(psUART->ui16RxBufferRead != psUART->ui16RxBufferWrite) + { + // + // Read the next character. + // + i32Temp = psUART->pui16RxBuffer[psUART->ui16RxBufferRead]; + psUART->ui16RxBufferRead++; + if(psUART->ui16RxBufferRead == psUART->ui16RxBufferLen) + { + psUART->ui16RxBufferRead = 0; + } + + // + // Deassert the receive "interrupt(s)" if appropriate. + // + SoftUARTRxReadInt(psUART); + + // + // Set the receive status to match this character. + // + psUART->ui8RxStatus = + ((psUART->ui8RxStatus & SOFTUART_RXERROR_OVERRUN) | + ((i32Temp >> 8) & ~(SOFTUART_RXERROR_OVERRUN))); + + // + // Return this character. + // + return(i32Temp); + } + else + { + // + // There are no characters, so return a failure. + // + return(-1); + } +} + +//***************************************************************************** +// +//! Waits for a character from the specified port. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! Gets a character from the receive buffer for the specified port. If there +//! are no characters available, this function waits until a character is +//! received before returning. +//! +//! \return Returns the character read from the specified port, cast as a +//! \e int32_t. +// +//***************************************************************************** +int32_t +SoftUARTCharGet(tSoftUART *psUART) +{ + int32_t i32Temp; + + // + // Wait until a int8_t is available. + // + while(psUART->ui16RxBufferRead == + *(volatile uint16_t *)(&(psUART->ui16RxBufferWrite))) + { + } + + // + // Read the next character. + // + i32Temp = psUART->pui16RxBuffer[psUART->ui16RxBufferRead]; + psUART->ui16RxBufferRead++; + if(psUART->ui16RxBufferRead == psUART->ui16RxBufferLen) + { + psUART->ui16RxBufferRead = 0; + } + + // + // Deassert the receive "interrupt(s)" if appropriate. + // + SoftUARTRxReadInt(psUART); + + // + // Set the receive status to match this character. + // + psUART->ui8RxStatus = ((psUART->ui8RxStatus & SOFTUART_RXERROR_OVERRUN) | + ((i32Temp >> 8) & ~(SOFTUART_RXERROR_OVERRUN))); + + // + // Return this character. + // + return(i32Temp); +} + +//***************************************************************************** +// +//! Sends a character to the specified port. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui8Data is the character to be transmitted. +//! +//! Writes the character \e ui8Data to the transmit buffer for the specified +//! port. This function does not block, so if there is no space available, +//! then a \b false is returned, and the application must retry the function +//! later. +//! +//! \return Returns \b true if the character was successfully placed in the +//! transmit buffer or \b false if there was no space available in the +//! transmit buffer. +// +//***************************************************************************** +bool +SoftUARTCharPutNonBlocking(tSoftUART *psUART, uint8_t ui8Data) +{ + uint16_t ui16Temp; + + // + // Determine the values of the write pointer once incremented. + // + ui16Temp = psUART->ui16TxBufferWrite + 1; + if(ui16Temp == psUART->ui16TxBufferLen) + { + ui16Temp = 0; + } + + // + // See if there is space in the transmit buffer. + // + if(ui16Temp != psUART->ui16TxBufferRead) + { + // + // Write this character to the transmit buffer. + // + psUART->pui8TxBuffer[psUART->ui16TxBufferWrite] = ui8Data; + psUART->ui16TxBufferWrite = ui16Temp; + + // + // Success. + // + return(true); + } + else + { + // + // There is no space in the transmit buffer, so return a failure. + // + return(false); + } +} + +//***************************************************************************** +// +//! Waits to send a character from the specified port. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui8Data is the character to be transmitted. +//! +//! Sends the character \e ui8Data to the transmit buffer for the specified +//! port. If there is no space available in the transmit buffer, this function +//! waits until there is space available before returning. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTCharPut(tSoftUART *psUART, uint8_t ui8Data) +{ + uint16_t ui16Temp; + + // + // Wait until space is available. + // + ui16Temp = psUART->ui16TxBufferWrite + 1; + if(ui16Temp == psUART->ui16TxBufferLen) + { + ui16Temp = 0; + } + while(ui16Temp == *(volatile uint16_t *)(&(psUART->ui16TxBufferRead))) + { + } + + // + // Send the int8_t. + // + psUART->pui8TxBuffer[psUART->ui16TxBufferWrite] = ui8Data; + psUART->ui16TxBufferWrite = ui16Temp; +} + +//***************************************************************************** +// +//! Causes a BREAK to be sent. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param bBreakState controls the output level. +//! +//! Calling this function with \e bBreakState set to \b true asserts a break +//! condition on the SoftUART. Calling this function with \e bBreakState set +//! to \b false removes the break condition. For proper transmission of a +//! break command, the break must be asserted for at least two complete frames. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTBreakCtl(tSoftUART *psUART, bool bBreakState) +{ + // + // Set the break condition as requested. + // + if(bBreakState) + { + psUART->ui8Flags |= SOFTUART_FLAG_TXBREAK; + } + else + { + psUART->ui8Flags &= ~(SOFTUART_FLAG_TXBREAK); + } +} + +//***************************************************************************** +// +//! Determines whether the UART transmitter is busy or not. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! Allows the caller to determine whether all transmitted bytes have cleared +//! the transmitter hardware. If \b false is returned, the transmit buffer is +//! empty and all bits of the last transmitted character, including all stop +//! bits, have left the hardware shift register. +//! +//! \return Returns \b true if the UART is transmitting or \b false if all +//! transmissions are complete. +// +//***************************************************************************** +bool +SoftUARTBusy(tSoftUART *psUART) +{ + // + // Determine if the UART is busy. + // + return(((psUART->ui8TxState == SOFTUART_TXSTATE_IDLE) && + (((psUART->ui8Flags & SOFTUART_FLAG_ENABLE) == 0) || + (psUART->ui16TxBufferRead == psUART->ui16TxBufferWrite))) ? + false : true); +} + +//***************************************************************************** +// +//! Enables individual SoftUART ``interrupt'' sources. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32IntFlags is the bit mask of the ``interrupt'' sources to be +//! enabled. +//! +//! Enables the indicated SoftUART ``interrupt'' sources. Only the sources +//! that are enabled can be reflected to the SoftUART callback. +//! +//! The \e ui32IntFlags parameter is the logical OR of any of the following: +//! +//! - \b SOFTUART_INT_OE - Overrun Error ``interrupt'' +//! - \b SOFTUART_INT_BE - Break Error ``interrupt'' +//! - \b SOFTUART_INT_PE - Parity Error ``interrupt'' +//! - \b SOFTUART_INT_FE - Framing Error ``interrupt'' +//! - \b SOFTUART_INT_RT - Receive Timeout ``interrupt'' +//! - \b SOFTUART_INT_TX - Transmit ``interrupt'' +//! - \b SOFTUART_INT_RX - Receive ``interrupt'' +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTIntEnable(tSoftUART *psUART, uint32_t ui32IntFlags) +{ + // + // Enable the specified interrupts. + // + psUART->ui16IntMask |= ui32IntFlags; +} + +//***************************************************************************** +// +//! Disables individual SoftUART ``interrupt'' sources. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32IntFlags is the bit mask of the ``interrupt'' sources to be +//! disabled. +//! +//! Disables the indicated SoftUART ``interrupt'' sources. Only the sources +//! that are enabled can be reflected to the SoftUART callback. +//! +//! The \e ui32IntFlags parameter has the same definition as the +//! \e ui32IntFlags parameter to SoftUARTIntEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTIntDisable(tSoftUART *psUART, uint32_t ui32IntFlags) +{ + // + // Disable the specified interrupts. + // + psUART->ui16IntMask &= ~(ui32IntFlags); +} + +//***************************************************************************** +// +//! Gets the current SoftUART ``interrupt'' status. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param bMasked is \b false if the raw ``interrupt'' status is required and +//! \b true if the masked ``interrupt'' status is required. +//! +//! This returns the ``interrupt'' status for the SoftUART. Either the raw +//! ``interrupt'' status or the status of ``interrupts'' that are allowed to +//! reflect to the SoftUART callback can be returned. +//! +//! \return Returns the current ``interrupt'' status, enumerated as a bit field +//! of values described in SoftUARTIntEnable(). +// +//***************************************************************************** +uint32_t +SoftUARTIntStatus(tSoftUART *psUART, bool bMasked) +{ + // + // Return either the interrupt status or the raw interrupt status as + // requested. + // + if(bMasked) + { + return(psUART->ui16IntStatus & psUART->ui16IntMask); + } + else + { + return(psUART->ui16IntStatus); + } +} + +//***************************************************************************** +// +//! Clears SoftUART ``interrupt'' sources. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32IntFlags is a bit mask of the ``interrupt'' sources to be +//! cleared. +//! +//! The specified SoftUART ``interrupt'' sources are cleared, so that they no +//! longer assert. This function must be called in the callback function to +//! keep the ``interrupt'' from being recognized again immediately upon exit. +//! +//! The \e ui32IntFlags parameter has the same definition as the +//! \e ui32IntFlags parameter to SoftUARTIntEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTIntClear(tSoftUART *psUART, uint32_t ui32IntFlags) +{ + // + // Clear the requested interrupt sources. + // + psUART->ui16IntStatus &= ~(ui32IntFlags); +} + +//***************************************************************************** +// +//! Gets current receiver errors. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function returns the current state of each of the 4 receiver error +//! sources. The returned errors are equivalent to the four error bits +//! returned via the previous call to SoftUARTCharGet() or +//! SoftUARTCharGetNonBlocking() with the exception that the overrun error is +//! set immediately when the overrun occurs rather than when a character is +//! next read. +//! +//! \return Returns a logical OR combination of the receiver error flags, +//! \b SOFTUART_RXERROR_FRAMING, \b SOFTUART_RXERROR_PARITY, +//! \b SOFTUART_RXERROR_BREAK and \b SOFTUART_RXERROR_OVERRUN. +// +//***************************************************************************** +uint32_t +SoftUARTRxErrorGet(tSoftUART *psUART) +{ + // + // Return the current value of the receive status. + // + return(psUART->ui8RxStatus); +} + +//***************************************************************************** +// +//! Clears all reported receiver errors. +//! +//! \param psUART specifies the SoftUART data structure. +//! +//! This function is used to clear all receiver error conditions reported via +//! SoftUARTRxErrorGet(). If using the overrun, framing error, parity error or +//! break interrupts, this function must be called after clearing the interrupt +//! to ensure that later errors of the same type trigger another interrupt. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTRxErrorClear(tSoftUART *psUART) +{ + // + // Clear any receive error status. + // + psUART->ui8RxStatus = 0; +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftUART module. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pfnCallback is a pointer to the callback function. +//! +//! This function sets the address of the callback function that is called when +//! there is an ``interrupt'' produced by the SoftUART module. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTCallbackSet(tSoftUART *psUART, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + psUART->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftUART Tx signal. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used when the SoftUART must assert +//! the Tx signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTTxGPIOSet(tSoftUART *psUART, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Tx signal. + // + if(ui32Base == 0) + { + psUART->ui32TxGPIO = 0; + } + else + { + psUART->ui32TxGPIO = ui32Base + (ui8Pin << 2); + } +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftUART Rx signal. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param ui32Base is the base address of the GPIO module. +//! \param ui8Pin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used when the SoftUART must sample +//! the Rx signal. If there is not a GPIO pin allocated for Rx, the SoftUART +//! module will not read data from the slave device. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTRxGPIOSet(tSoftUART *psUART, uint32_t ui32Base, uint8_t ui8Pin) +{ + // + // Save the base address and pin for the Rx signal. + // + if(ui32Base == 0) + { + psUART->ui32RxGPIOPort = 0; + psUART->ui8RxPin = 0; + } + else + { + psUART->ui32RxGPIOPort = ui32Base; + psUART->ui8RxPin = ui8Pin; + } +} + +//***************************************************************************** +// +//! Sets the transmit buffer for a SoftUART module. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pui8TxBuffer is the address of the transmit buffer. +//! \param ui16Len is the size, in 8-bit bytes, of the transmit buffer. +//! +//! This function sets the address and size of the transmit buffer. It also +//! resets the read and write pointers, marking the transmit buffer as empty. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTTxBufferSet(tSoftUART *psUART, uint8_t *pui8TxBuffer, uint16_t ui16Len) +{ + // + // Save the transmit buffer address and length. + // + psUART->pui8TxBuffer = pui8TxBuffer; + psUART->ui16TxBufferLen = ui16Len; + + // + // Reset the transmit buffer read and write pointers. + // + psUART->ui16TxBufferRead = 0; + psUART->ui16TxBufferWrite = 0; + + // + // Compute the new buffer "interrupt" level. + // + SoftUARTTxLevelSet(psUART); +} + +//***************************************************************************** +// +//! Sets the receive buffer for a SoftUART module. +//! +//! \param psUART specifies the SoftUART data structure. +//! \param pui16RxBuffer is the address of the receive buffer. +//! \param ui16Len is the size, in 16-bit half-words, of the receive buffer. +//! +//! This function sets the address and size of the receive buffer. It also +//! resets the read and write pointers, marking the receive buffer as empty. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTRxBufferSet(tSoftUART *psUART, uint16_t *pui16RxBuffer, + uint16_t ui16Len) +{ + // + // Save the receive buffer address and length. + // + psUART->pui16RxBuffer = pui16RxBuffer; + psUART->ui16RxBufferLen = ui16Len; + + // + // Reset the receive read and write pointers. + // + psUART->ui16RxBufferRead = 0; + psUART->ui16RxBufferWrite = 0; + + // + // Compute the new buffer "interrupt" level. + // + SoftUARTRxLevelSet(psUART); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/softuart.h b/utils/softuart.h new file mode 100644 index 0000000..d9184d7 --- /dev/null +++ b/utils/softuart.h @@ -0,0 +1,375 @@ +//***************************************************************************** +// +// softuart.h - Defines and macros for the SoftUART. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __SOFTUART_H__ +#define __SOFTUART_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup softuart_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure contains the state of a single instance of a SoftUART +//! module. +// +//***************************************************************************** +typedef struct +{ + // + //! The address of the callback function that is called to simulate the + //! interrupts that would be produced by a hardware UART implementation. + //! This address can be set via a direct structure access or using the + //! SoftUARTCallbackSet function. + // + void (*pfnIntCallback)(void); + + // + //! The address of the GPIO pin to be used for the Tx signal. This member + //! can be set via a direct structure access or using the SoftUARTTxGPIOSet + //! function. + // + uint32_t ui32TxGPIO; + + // + //! The address of the GPIO port to be used for the Rx signal. This member + //! can be set via a direct structure access or using the SoftUARTRxGPIOSet + //! function. + // + uint32_t ui32RxGPIOPort; + + // + //! The address of the data buffer used for the transmit buffer. This + //! member can be set via a direct structure access or using the + //! SoftUARTTxBufferSet function. + // + uint8_t *pui8TxBuffer; + + // + //! The address of the data buffer used for the receive buffer. This + //! member can be set via a direct structure access or using the + //! SoftUARTRxBufferSet function. + // + uint16_t *pui16RxBuffer; + + // + //! The length of the transmit buffer. This member can be set via a direct + //! structure access or using the SoftUARTTxBufferSet function. + // + uint16_t ui16TxBufferLen; + + // + //! The index into the transmit buffer of the next character to be + //! transmitted. This member should not be accessed or modified by the + //! application. + // + uint16_t ui16TxBufferRead; + + // + //! The index into the transmit buffer of the next location to store a + //! character into the buffer. This member should not be accessed or + //! modified by the application. + // + uint16_t ui16TxBufferWrite; + + // + //! The transmit buffer level at which the transmit interrupt is asserted. + //! This member should not be accessed or modified by the application. + // + uint16_t ui16TxBufferLevel; + + // + //! The length of the receive buffer. This member can be set via a direct + //! structure access or using the SoftUARTRxBufferSet function. + // + uint16_t ui16RxBufferLen; + + // + //! The index into the receive buffer of the next character to be read from + //! the buffer. This member should not be accessed or modified by the + //! application. + // + uint16_t ui16RxBufferRead; + + // + //! The index into the receive buffer of the lcoation to store the next + //! character received. This member should not be accessed or modified by + //! the application. + // + uint16_t ui16RxBufferWrite; + + // + //! The receive buffer level at which the receive interrupt is asserted. + //! This member should not be accessed or modified by the application. + // + uint16_t ui16RxBufferLevel; + + // + //! The set of virtual interrupts that are currently asserted. This member + //! should not be accessed or modified by the application. + // + uint16_t ui16IntStatus; + + // + //! The set of virtual interrupts that should be sent to the callback + //! function. This member should not be accessed or modified by the + //! application. + // + uint16_t ui16IntMask; + + // + //! The configuration of the SoftUART module. This member can be set via + //! the SoftUARTConfigSet and SoftUARTFIFOLevelSet functions. + // + uint16_t ui16Config; + + // + //! The flags that control the operation of the SoftUART module. This + //! member should not be be accessed or modified by the application. + // + uint8_t ui8Flags; + + // + //! The current state of the SoftUART transmit state machine. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8TxState; + + // + //! The value that is written to the Tx pin at the start of the next + //! transmit timer tick. This member should not be accessed or modified + //! by the application. + // + uint8_t ui8TxNext; + + // + //! The character that is currently be sent via the Tx pin. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8TxData; + + // + //! The GPIO pin to be used for the Rx signal. This member can be set via + //! a direct structure access or using the SoftUARTRxGPIOSet function. + // + uint8_t ui8RxPin; + + // + //! The current state of the SoftUART receive state machine. This member + //! should not be accessed or modified by the application. + // + uint8_t ui8RxState; + + // + //! The character that is currently being received via the Rx pin. This + //! member should not be accessed or modified by the application. + // + uint8_t ui8RxData; + + // + //! The flags that indicate any errors that have occurred during the + //! reception of the current character via the Rx pin. This member should + //! not be accessed or modified by the application. + // + uint8_t ui8RxFlags; + + // + //! The receive error status. This member should only be accessed via the + //! SoftUARTRxErrorGet and SoftURATRxErrorClear functions. + // + uint8_t ui8RxStatus; +} +tSoftUART; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Values that can be passed to SoftUARTIntEnable(), SoftUARTIntDisable(), and +// SoftUARTIntClear() as the ui32IntFlags parameter, and returned from +// SoftUARTIntStatus(). +// +//***************************************************************************** +#define SOFTUART_INT_EOT 0x800 // End of transmission interrupt +#define SOFTUART_INT_OE 0x400 // Overrun error interrupt +#define SOFTUART_INT_BE 0x200 // Break error interrupt +#define SOFTUART_INT_PE 0x100 // Parity error interrupt +#define SOFTUART_INT_FE 0x080 // Framing error interrupt +#define SOFTUART_INT_RT 0x040 // Receive timeout interrupt +#define SOFTUART_INT_TX 0x020 // Transmit interrupt +#define SOFTUART_INT_RX 0x010 // Receive interrupt + +//***************************************************************************** +// +// Values that can be passed to SoftUARTConfigSet() as the ui32Config parameter +// and returned by SoftUARTConfigGet() in the pui32Config parameter. +// Additionally, the UART_CONFIG_PAR_* subset can be passed to +// SoftUARTParityModeSet() as the ui32Parity parameter, and are returned by +// SoftUARTParityModeGet(). +// +//***************************************************************************** +#define SOFTUART_CONFIG_WLEN_MASK \ + 0x00000060 // Mask for extracting word length +#define SOFTUART_CONFIG_WLEN_8 0x00000060 // 8 bit data +#define SOFTUART_CONFIG_WLEN_7 0x00000040 // 7 bit data +#define SOFTUART_CONFIG_WLEN_6 0x00000020 // 6 bit data +#define SOFTUART_CONFIG_WLEN_5 0x00000000 // 5 bit data +#define SOFTUART_CONFIG_STOP_MASK \ + 0x00000008 // Mask for extracting stop bits +#define SOFTUART_CONFIG_STOP_ONE \ + 0x00000000 // One stop bit +#define SOFTUART_CONFIG_STOP_TWO \ + 0x00000008 // Two stop bits +#define SOFTUART_CONFIG_PAR_MASK \ + 0x00000086 // Mask for extracting parity +#define SOFTUART_CONFIG_PAR_NONE \ + 0x00000000 // No parity +#define SOFTUART_CONFIG_PAR_EVEN \ + 0x00000006 // Even parity +#define SOFTUART_CONFIG_PAR_ODD 0x00000002 // Odd parity +#define SOFTUART_CONFIG_PAR_ONE 0x00000082 // Parity bit is one +#define SOFTUART_CONFIG_PAR_ZERO \ + 0x00000086 // Parity bit is zero +#define SOFTUART_CONFIG_WLEN_S 5 + +//***************************************************************************** +// +// Values that can be passed to SoftUARTFIFOLevelSet() as the ui32TxLevel +// parameter and returned by SoftUARTFIFOLevelGet() in the pui32TxLevel. +// +//***************************************************************************** +#define SOFTUART_FIFO_TX1_8 0x00000000 // Transmit interrupt at 1/8 Full +#define SOFTUART_FIFO_TX2_8 0x00000001 // Transmit interrupt at 1/4 Full +#define SOFTUART_FIFO_TX4_8 0x00000002 // Transmit interrupt at 1/2 Full +#define SOFTUART_FIFO_TX6_8 0x00000003 // Transmit interrupt at 3/4 Full +#define SOFTUART_FIFO_TX7_8 0x00000004 // Transmit interrupt at 7/8 Full + +//***************************************************************************** +// +// Values that can be passed to SoftUARTFIFOLevelSet() as the ui32RxLevel +// parameter and returned by SoftUARTFIFOLevelGet() in the pui32RxLevel. +// +//***************************************************************************** +#define SOFTUART_FIFO_RX1_8 0x00000000 // Receive interrupt at 1/8 Full +#define SOFTUART_FIFO_RX2_8 0x00000008 // Receive interrupt at 1/4 Full +#define SOFTUART_FIFO_RX4_8 0x00000010 // Receive interrupt at 1/2 Full +#define SOFTUART_FIFO_RX6_8 0x00000018 // Receive interrupt at 3/4 Full +#define SOFTUART_FIFO_RX7_8 0x00000020 // Receive interrupt at 7/8 Full + +//***************************************************************************** +// +// Values returned from SoftUARTRxErrorGet(). +// +//***************************************************************************** +#define SOFTUART_RXERROR_OVERRUN \ + 0x00000008 // An overrun error occurred +#define SOFTUART_RXERROR_BREAK 0x00000004 // A break was received +#define SOFTUART_RXERROR_PARITY 0x00000002 // A parity error occurred +#define SOFTUART_RXERROR_FRAMING \ + 0x00000001 // A framing error occurred + +//***************************************************************************** +// +// Values returned from SoftUARTRxTick(). +// +//***************************************************************************** +#define SOFTUART_RXTIMER_NOP 0 // The timer should continue to run +#define SOFTUART_RXTIMER_END 1 // The timer should be stopped + +//***************************************************************************** +// +// API Function prototypes +// +//***************************************************************************** +extern void SoftUARTInit(tSoftUART *psUART); +extern void SoftUARTParityModeSet(tSoftUART *psUART, uint32_t ui32Parity); +extern uint32_t SoftUARTParityModeGet(tSoftUART *psUART); +extern void SoftUARTFIFOLevelSet(tSoftUART *psUART, uint32_t ui32TxLevel, + uint32_t ui32RxLevel); +extern void SoftUARTFIFOLevelGet(tSoftUART *psUART, uint32_t *pui32TxLevel, + uint32_t *pui32RxLevel); +extern void SoftUARTConfigSet(tSoftUART *psUART, uint32_t ui32Config); +extern void SoftUARTConfigGet(tSoftUART *psUART, uint32_t *pui32Config); +extern void SoftUARTEnable(tSoftUART *psUART); +extern void SoftUARTDisable(tSoftUART *psUART); +extern void SoftUARTFIFOEnable(tSoftUART *psUART); +extern void SoftUARTFIFODisable(tSoftUART *psUART); +extern bool SoftUARTCharsAvail(tSoftUART *psUART); +extern bool SoftUARTSpaceAvail(tSoftUART *psUART); +extern int32_t SoftUARTCharGetNonBlocking(tSoftUART *psUART); +extern int32_t SoftUARTCharGet(tSoftUART *psUART); +extern bool SoftUARTCharPutNonBlocking(tSoftUART *psUART, + uint8_t ui8Data); +extern void SoftUARTCharPut(tSoftUART *psUART, uint8_t ui8Data); +extern void SoftUARTBreakCtl(tSoftUART *psUART, bool bBreakState); +extern bool SoftUARTBusy(tSoftUART *psUART); +extern void SoftUARTIntEnable(tSoftUART *psUART, uint32_t ui32IntFlags); +extern void SoftUARTIntDisable(tSoftUART *psUART, uint32_t ui32IntFlags); +extern uint32_t SoftUARTIntStatus(tSoftUART *psUART, bool bMasked); +extern void SoftUARTIntClear(tSoftUART *psUART, uint32_t ui32IntFlags); +extern uint32_t SoftUARTRxErrorGet(tSoftUART *psUART); +extern void SoftUARTRxErrorClear(tSoftUART *psUART); +extern uint32_t SoftUARTRxTick(tSoftUART *psUART, bool bEdgeInt); +extern void SoftUARTTxIntModeSet(tSoftUART *psUART, uint32_t ui32Mode); +extern uint32_t SoftUARTTxIntModeGet(tSoftUART *psUART); +extern void SoftUARTTxTimerTick(tSoftUART *psUART); +extern void SoftUARTCallbackSet(tSoftUART *psUART, void (*pfnCallback)(void)); +extern void SoftUARTTxGPIOSet(tSoftUART *psUART, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftUARTRxGPIOSet(tSoftUART *psUART, uint32_t ui32Base, + uint8_t ui8Pin); +extern void SoftUARTTxBufferSet(tSoftUART *psUART, uint8_t *pui8TxBuffer, + uint16_t ui16Len); +extern void SoftUARTRxBufferSet(tSoftUART *psUART, uint16_t *pui16RxBuffer, + uint16_t ui16Len); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SOFTUART_H__ diff --git a/utils/speexlib.c b/utils/speexlib.c new file mode 100644 index 0000000..3a58ebc --- /dev/null +++ b/utils/speexlib.c @@ -0,0 +1,377 @@ +//***************************************************************************** +// +// speexlib.c - interface to the speex coder/encoder library. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/sysctl.h" +#include "third_party/speex-1.2rc1/include/speex/speex.h" +#include "third_party/speex-1.2rc1/include/speex/speex_header.h" +#include "utils/speexlib.h" +#include "driverlib/debug.h" + +//***************************************************************************** +// +// The private structure that is used by the speex encoder or decoder for +// holding all state information for an encoder or decoder. +// +//***************************************************************************** +typedef struct +{ + // + // Holds the state of the decoder. + // + void *pvState; + + // + // Holds bits so they can be read and written to by the Speex routines + // + SpeexBits sBits; + + // + // Holds the header information for the current file. + // + SpeexHeader sHeader; + + // + // The current Segment table for the stream. + // + uint8_t pui8SegTable[256]; + + // + // The size of the current Segment table. + // + uint8_t ui8SegTableSize; + + // + // The current active page in a segment. + // + uint8_t ui8PageCurrent; + + // + // Current state flags. + // + uint32_t ui32Flags; +} +tSpeexInstance; + +//***************************************************************************** +// +// The decoder and encoder instance data. +// +//***************************************************************************** +tSpeexInstance g_sSpeexDecoder, g_sSpeexEncoder; + +//***************************************************************************** +// +//! Initialize the decoder's state to prepare for decoding new frames. +//! +//! This function will initializes the decoder so that it is prepared to start +//! receiving frames to decode. +//! +//! \return This function returns 0. +// +//***************************************************************************** +int32_t +SpeexDecodeInit(void) +{ + int iTemp; + + // + // Clear out the flags for this instance. + // + g_sSpeexDecoder.ui32Flags = 0; + + // + // Create a new decoder state in narrow band mode. + // + g_sSpeexDecoder.pvState = speex_decoder_init(&speex_nb_mode); + + // + // Disable enhanced decoding to reduce processing requirements. + // + iTemp = 0; + speex_decoder_ctl(g_sSpeexDecoder.pvState, SPEEX_SET_ENH, &iTemp); + + // + // Initialization of the structure that holds the bits. + // + speex_bits_init(&g_sSpeexDecoder.sBits); + + return(0); +} + +//***************************************************************************** +// +//! This function returns the current frame size from the decoder. +//! +//! This function queries the decoder for the current decode frame size in byte +//! and returns it to the caller. +//! +//! \return The current decoder frame size. +// +//***************************************************************************** +int32_t +SpeexDecodeFrameSizeGet(void) +{ + int iFrameSize; + + // + // Return 0 if the wrong request is made. + // + iFrameSize = 0; + + // + // Query the decoder for the current frame size. + // + speex_decoder_ctl(g_sSpeexDecoder.pvState, SPEEX_GET_FRAME_SIZE, + &iFrameSize); + + return(iFrameSize); +} + +//***************************************************************************** +// +//! This function decodes a single frame of Speex encoded audio. +//! +//! \param pui8InBuffer is the buffer that contains the Speex encoded audio. +//! \param ui32InSize is the number of valid bytes in the \e pui8InBuffer +//! buffer. +//! \param pui8OutBuffer is a pointer to the buffer to store decoded audio. +//! \param ui32OutSize is the size of the buffer pointed to by the +//! \e pui8OutBuffer pointer. +//! +//! This function will take a buffer of Speex encoded audio and decode it into +//! raw PCM audio. The \e pui16InBuffer parameter should contain a single +//! frame encoded Speex audio. The \e pui8OutBuffer will contain the decoded +//! audio after returning from this function. +//! +//! \return This function returns the number of decoded bytes in the +//! \e pui8OutBuffer buffer. +// +//***************************************************************************** +int32_t +SpeexDecode(uint8_t *pui8InBuffer, uint32_t ui32InSize, uint8_t *pui8OutBuffer, + uint32_t ui32OutSize) +{ + int32_t i32Bytes; + + // + // Read in the bit stream to the Speex library. + // + speex_bits_read_from(&g_sSpeexDecoder.sBits, (char *)pui8InBuffer, + ui32InSize); + + // + // Decode one frame of data. + // + i32Bytes = speex_decode_int(g_sSpeexDecoder.pvState, + &g_sSpeexDecoder.sBits, + (int16_t *)pui8OutBuffer); + + return(i32Bytes); +} + +//***************************************************************************** +// +//! This function sets the current quality setting for the Speex encoder. +//! +//! \param iQuality is the new Quality setting to use for the Speex encoder. +//! +//! This function will use the \e iQuality setting as the new quality setting +//! for the Speex encoder. +//! +//! \return This function returns 0. +// +//***************************************************************************** +int32_t +SpeexEncodeQualitySet(int iQuality) +{ + // + // Set the current encoder quality setting. + // + speex_encoder_ctl(g_sSpeexEncoder.pvState, SPEEX_SET_QUALITY, &iQuality); + + return(0); +} + +//***************************************************************************** +// +//! This function returns the current frame size from the encoder. +//! +//! This function queries the encoder for the current encode frame size in byte +//! and returns it to the caller. +//! +//! \return The current encoder frame size. +// +//***************************************************************************** +int32_t +SpeexEncodeFrameSizeGet(void) +{ + int iFrameSize; + + // + // Return 0 if the wrong request is made. + // + iFrameSize = 0; + + // + // Query the encoder for the current frame size. + // + speex_encoder_ctl(g_sSpeexEncoder.pvState, SPEEX_GET_FRAME_SIZE, + &iFrameSize); + + return(iFrameSize); +} + +//***************************************************************************** +// +//! Initialize the encoder's state to prepare for encoding new frames. +//! +//! \param iSampleRate is the sample rate of the incoming audio. +//! \param iComplexity is the complexity setting for the encoder. +//! \param iQuality is the quality setting for the encoder. +//! +//! This function will initializes the encoder by setting the sample rate, +//! complexity and quality settings. The \e iComplexity and \e iQuality +//! settings are explained further in the Speex documentation. +//! +//! \return This function returns 0. +// +//***************************************************************************** +int32_t +SpeexEncodeInit(int iSampleRate, int iComplexity, int iQuality) +{ + const SpeexMode *psMode; + + // + // Clear out the flags for this instance. + // + g_sSpeexEncoder.ui32Flags = 0; + + // + // Read out the current encoder mode. + // + psMode = speex_lib_get_mode(SPEEX_MODEID_NB); + + // + // Create a new decoder state in narrow band mode. + // + g_sSpeexEncoder.pvState = speex_encoder_init(psMode); + + // + // Initialize the bit stream. + // + speex_bits_init(&g_sSpeexEncoder.sBits); + + // + // Set the quality. + // + SpeexEncodeQualitySet(iQuality); + + // + // Set the complexity and sample rate for the encoder. + // + speex_encoder_ctl(g_sSpeexEncoder.pvState, SPEEX_SET_COMPLEXITY, + &iComplexity); + speex_encoder_ctl(g_sSpeexEncoder.pvState, SPEEX_SET_SAMPLING_RATE, + &iSampleRate); + + return(0); +} + +//***************************************************************************** +// +//! Encode a single frame of speex encoded audio. +//! +//! \param pui16InBuffer is the buffer that contains the raw PCM audio. +//! \param ui32InSize is the number of valid bytes in the \e pui16InBuffer +//! buffer. +//! \param pui8OutBuffer is a pointer to the buffer to store the encoded audio. +//! \param ui32OutSize is the size of the buffer pointed to by the +//! \e pui8OutBuffer pointer. +//! +//! This function will take a buffer of PCM audio and encode it into a frame +//! of speex compressed audio. The \e pui16InBuffer parameter should contain +//! a single frame of PCM audio. The \e pui8OutBuffer will contain the encoded +//! audio after returning from this function. +//! +//! \return This function returns the number of encoded bytes in the +//! \e pui8OutBuffer parameter. +// +//***************************************************************************** +int32_t +SpeexEncode(int16_t *pui16InBuffer, uint32_t ui32InSize, + uint8_t *pui8OutBuffer, uint32_t ui32OutSize) +{ + int32_t i32Bytes; + + // + // Reset the bit stream before encoding a new frame. + // + speex_bits_reset(&g_sSpeexEncoder.sBits); + + // + // Encode a single frame. + // + speex_encode_int(g_sSpeexEncoder.pvState, pui16InBuffer, + &g_sSpeexEncoder.sBits); + + // + // Read the PCM data from the encoded bit stream. + // + i32Bytes = speex_bits_write(&g_sSpeexEncoder.sBits, (char *)pui8OutBuffer, + ui32OutSize); + + // + // Return the number of bytes in the PCM data. + // + return(i32Bytes); +} + +//***************************************************************************** +// +// This is called by speex in the event of a fatal error. +// +//***************************************************************************** +void +_speex_fatal(const int8_t *str, const int8_t *file, int line) +{ + ASSERT(0); + while(1) + { + } +} + +//***************************************************************************** +// +// Speex wrapper for putc so that it does not use any file writing library +// functions. The speex library uses some file access for debug printing, this +// will disable this feature. +// +//***************************************************************************** +void +_speex_putc(int ch, void *file) +{ +} diff --git a/utils/speexlib.h b/utils/speexlib.h new file mode 100644 index 0000000..5b3f712 --- /dev/null +++ b/utils/speexlib.h @@ -0,0 +1,63 @@ +//***************************************************************************** +// +// speexlib.h - interface to the speex coder/encoder library. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __SPEEXLIB_H__ +#define __SPEEXLIB_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 int32_t SpeexEncodeInit(int iSampleRate, int iComplexity, int iQuality); +extern int32_t SpeexEncode(int16_t *pui16InBuffer, uint32_t ui32InSize, + uint8_t *pui8OutBuffer, uint32_t ui32OutSize); +extern int32_t SpeexEncodeQualitySet(int iQuality); +extern int32_t SpeexEncodeFrameSizeGet(void); +extern int32_t SpeexDecodeFrameSizeGet(void); +extern int32_t SpeexDecodeInit(void); +extern int32_t SpeexDecode(uint8_t *pui8InBuffer, uint32_t ui32InSize, + uint8_t *pui8OutBuffer, uint32_t ui32OutSize); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SPEEXLIB_H__ diff --git a/utils/spi_flash.c b/utils/spi_flash.c new file mode 100644 index 0000000..6e5b24a --- /dev/null +++ b/utils/spi_flash.c @@ -0,0 +1,2484 @@ +//***************************************************************************** +// +// spi_flash.c - Driver for a SPI flash that supports the "Intel" SPI flash +// command set, capable of utilizing Bi-SPI and Quad-SPI. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_ssi.h" +#include "inc/hw_types.h" +#include "inc/hw_udma.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/ssi.h" +#include "driverlib/udma.h" +#include "utils/spi_flash.h" + +//***************************************************************************** +// +//! \addtogroup spi_flash_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The commands that can be sent to the SPI flash. This is the "generic" +// command set that is supported by a wide number of SPI flashes. +// +//***************************************************************************** +#define CMD_WRSR 0x01 // Write status register +#define CMD_PP 0x02 // Page program +#define CMD_READ 0x03 // Read data +#define CMD_WRDI 0x04 // Disable writes +#define CMD_RDSR 0x05 // Read status register +#define CMD_WREN 0x06 // Enable writes +#define CMD_FREAD 0x0b // Fast read data +#define CMD_SE 0x20 // Sector erase (4K) +#define CMD_DREAD 0x3b // 1 in 2 out read data +#define CMD_BE32 0x52 // Block erase (32K) +#define CMD_QREAD 0x6b // 1 in 4 out read data +#define CMD_RDID 0x9f // Read JEDEC ID +#define CMD_CE 0xc7 // Chip erase +#define CMD_BE64 0xd8 // Block erase (64K) + +//***************************************************************************** +// +// The states for the SPI flash interrupt handler state machine. +// +//***************************************************************************** +#define STATE_IDLE 0 +#define STATE_CMD 1 +#define STATE_ADDR1 2 +#define STATE_ADDR2 3 +#define STATE_ADDR3 4 +#define STATE_READ_DUMMY 5 +#define STATE_READ_DATA_SETUP 6 +#define STATE_READ_DATA 7 +#define STATE_READ_DATA_DMA 8 +#define STATE_READ_DATA_END 9 +#define STATE_WRITE_DATA_SETUP 10 +#define STATE_WRITE_DATA 11 +#define STATE_WRITE_DATA_DMA 12 +#define STATE_WRITE_DATA_END 13 + +//***************************************************************************** +// +//! Handles SSI module interrupts for the SPI flash driver. +//! +//! \param pState is a pointer to the SPI flash driver instance data. +//! +//! This function handles SSI module interrupts that are generated as a result +//! of SPI flash driver operations. This must be called by the application in +//! response to the SSI module interrupt when using the SPIFlashxxxNonBlocking +//! APIs. +//! +//! \return Returns \b SPI_FLASH_IDLE if there is no transfer in progress, +//! \b SPI_FLASH_WORKING is the requested transfer is still in progress, or +//! \b SPI_FLASH_DONE if the requested transfer has completed. +// +//***************************************************************************** +uint32_t +SPIFlashIntHandler(tSPIFlashState *pState) +{ + uint32_t ui32Data, ui32Count; + + // + // Set the write count to four. This is the maximum number of bytes that + // will be written into the SSI transmit FIFO in the interrupt handler. + // Writing more might be possible but makes the latency of handling future + // SSI interrupt critical to preventing receive FIFO overruns. + // + ui32Count = 4; + + // + // Get the set of asserted and unmasked SSI module interrupts. Only some + // of these are directly handled; the others are implicitly handled via the + // operation of the state machine. + // + ui32Data = HWREG(pState->ui32Base + SSI_O_MIS); + + // + // See if the uDMA transmit complete interrupt has asserted. + // + if(ui32Data & SSI_MIS_DMATXMIS) + { + // + // Determine the size of the uDMA transfer based on the number of bytes + // left to write. + // + if(pState->ui32WriteCount > 1024) + { + // + // There are more than 1024 bytes left to transfer, so the uDMA + // transfer that just completed was for a full 1024 bytes. + // + pState->ui32WriteCount -= 1024; + + // + // If a page program is being performed, then the data buffer + // pointer needs to be incremented as well. + // + if(pState->ui16Cmd == CMD_PP) + { + // + // Increment the data buffer pointer. + // + pState->pui8Buffer += 1024; + + // + // See if there is more than one byte left to transfer. + // + if(pState->ui32WriteCount > 1) + { + // + // Configure the uDMA to transmit the next portion of the + // data buffer. + // + uDMAChannelTransferSet(pState->ui32TxChannel, + UDMA_MODE_BASIC, + pState->pui8Buffer, + (void *)(pState->ui32Base + + SSI_O_DR), + (pState->ui32WriteCount > 1024) ? + 1024 : pState->ui32WriteCount - 1); + + // + // Enable the uDMA transmit channel. + // + uDMAChannelEnable(pState->ui32TxChannel); + } + } + } + else + { + // + // There are 1024 or less bytes left to transfer, so the uDMA + // transfer that just copmleted was for one less than the remaining + // transfer count. If a page program is being performed, then the + // data buffer pointer needs to be incremented. + // + if(pState->ui16Cmd == CMD_PP) + { + pState->pui8Buffer += (pState->ui32WriteCount - 1); + } + + // + // Set the remaining transfer count to 1. The final byte will be + // transferred with PIO since the end of frame flag needs to be set + // first. + // + pState->ui32WriteCount = 1; + } + + // + // Clear the uDMA transmit complete interrupt. + // + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC; + } + + // + // See if the uDMA receive complete interrupt has asserted. + // + if(ui32Data & SSI_MIS_DMARXMIS) + { + // + // Determine the size of the uDMA transfer based on the number of bytes + // left to read. + // + if(pState->ui32ReadCount >= 1024) + { + // + // There are 1024 or more bytes left to transfer, so the uDMA + // transfer that just completed was for a full 1024 bytes. + // + pState->ui32ReadCount -= 1024; + if(pState->ui32WriteCount != 0) + { + pState->ui32WriteCount -= 1024; + } + + // + // The data buffer pointer needs to be incremented as well. + // + pState->pui8Buffer += 1024; + + // + // See if there is additional data to transfer. + // + if(pState->ui32ReadCount != 0) + { + // + // Configure the transmit uDMA if there is more than one byte + // left to write. + // + if(pState->ui32WriteCount > 1) + { + // + // Configure the uDMA to transmit the next portion of the + // data buffer. + // + uDMAChannelTransferSet(pState->ui32TxChannel, + UDMA_MODE_BASIC, pState->pui8Buffer, + (void *)(pState->ui32Base + + SSI_O_DR), + (pState->ui32WriteCount > 1024) ? + 1024 : pState->ui32WriteCount - 1); + + // + // Enable the uDMA transmit channel. + // + uDMAChannelEnable(pState->ui32TxChannel); + } + + // + // Configure the uDMA to receive the next portion of the data + // buffer. + // + uDMAChannelTransferSet(pState->ui32RxChannel, UDMA_MODE_BASIC, + (void *)(pState->ui32Base + SSI_O_DR), + pState->pui8Buffer, + (pState->ui32ReadCount >= 1024) ? + 1024 : pState->ui32ReadCount); + + // + // Enable the uDMA receive channel. + // + uDMAChannelEnable(pState->ui32RxChannel); + + // + // If this is the final receive uDMA buffer and there is a + // transmit uDMA buffer associated, enable the DMA transmit + // interrupt. + // + if((pState->ui32ReadCount <= 1024) && + (pState->ui32WriteCount > 1)) + { + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC; + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM; + } + } + } + else + { + // + // There are less than 1024 bytes left to transfer, so the uDMA + // transfer that copmleted was for the remaining transfer count. + // + pState->ui32ReadCount = 0; + } + + // + // Clear the uDMA receive complete interrupt. + // + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMARXIC; + } + + // + // Drain the receive FIFO is not using uDMA. + // + if(!pState->bUseDMA) + { + // + // Loop while there is more data in the receive FIFO and more data to + // be read. + // + while((pState->ui32ReadCount != 0) && + (MAP_SSIDataGetNonBlocking(pState->ui32Base, &ui32Data) != 0)) + { + // + // Save this byte into the data buffer. + // + *(pState->pui8Buffer)++ = ui32Data & 0xff; + + // + // Decrement the read count. + // + pState->ui32ReadCount--; + } + } + + // + // The SPI flash state machine. Loop forever; the state machine will + // explicitly return to the caller when there is no further work that can + // be done without stalling. + // + while(1) + { + // + // Determine the current state. + // + switch(pState->ui16State) + { + // + // The state machine is idle. + // + case STATE_IDLE: + { + // + // Return indicating that the state machine is idle. This + // should never happen since no further interrupts should occur + // once the transfer has completed and the state machine goes + // into the idle state. + // + return(SPI_FLASH_IDLE); + } + + // + // The state machine is in the command state. + // + case STATE_CMD: + { + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(pState->ui32Base, SSI_ADV_MODE_WRITE); + + // + // Attempt to write the command byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + pState->ui16Cmd) == 0) + { + // + // The command byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The command byte has been written, so move to the first + // address byte state. + // + pState->ui16State = STATE_ADDR1; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the first address byte state. + // + case STATE_ADDR1: + { + // + // Attempt to write the first address byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + (pState->ui32Addr >> 16) & + 0xff) == 0) + { + // + // The first address byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The first address byte has been written, so move to the + // second address byte state. + // + pState->ui16State = STATE_ADDR2; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the second address byte state. + // + case STATE_ADDR2: + { + // + // Attempt to write the second address byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + (pState->ui32Addr >> 8) & 0xff) == + 0) + { + // + // The second address byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The second address byte has been written, so move to the + // third address byte state. + // + pState->ui16State = STATE_ADDR3; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the third address byte state. + // + case STATE_ADDR3: + { + // + // Attempt to write the third address byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + pState->ui32Addr & 0xff) == 0) + { + // + // The third address byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The third address byte has been written, so determine + // the next state based on the command byte. + // + if(pState->ui16Cmd == CMD_PP) + { + // + // A page program is being performed, so move to the + // write data setup state. + // + pState->ui16State = STATE_WRITE_DATA_SETUP; + } + else if(pState->ui16Cmd == CMD_READ) + { + // + // A read is being performed, so move to the read data + // setup state. + // + pState->ui16State = STATE_READ_DATA_SETUP; + } + else + { + // + // The other forms of read (fast read, dual read, and + // quad read) all require a dummy byte. Move to the + // dummy byte state. + // + pState->ui16State = STATE_READ_DUMMY; + } + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the dummy byte state. + // + case STATE_READ_DUMMY: + { + // + // Attempt to write the dummy byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, 0) == 0) + { + // + // THe dummy byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + else + { + // + // The dummy byte has been written, so move to the read + // data setup state. + // + pState->ui16State = STATE_READ_DATA_SETUP; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the read data setup state. + // + case STATE_READ_DATA_SETUP: + { + // + // Set the SSI module into the appropriate mode based on the + // command byte. + // + if(pState->ui16Cmd == CMD_DREAD) + { + // + // Bi-SPI read mode is used for the dual read command. + // + MAP_SSIAdvModeSet(pState->ui32Base, SSI_ADV_MODE_BI_READ); + } + else if(pState->ui16Cmd == CMD_QREAD) + { + // + // Quad-SPI read mode is used for the quad read command. + // + MAP_SSIAdvModeSet(pState->ui32Base, + SSI_ADV_MODE_QUAD_READ); + } + else + { + // + // Advanced read/write mode is used for the read and fast + // read commands. + // + MAP_SSIAdvModeSet(pState->ui32Base, + SSI_ADV_MODE_READ_WRITE); + } + + // + // See if a single byte is being transferred. + // + if(pState->ui32ReadCount == 1) + { + // + // Disable the use of uDMA. + // + pState->bUseDMA = false; + + // + // Move to the read data end state to transfer the single + // byte. This uses PIO even if uDMA has been requested. + // + pState->ui16State = STATE_READ_DATA_END; + } + + // + // See if uDMA has been requested for this transfer. + // + else if(!pState->bUseDMA || (pState->ui32ReadCount < 4)) + { + // + // Disable the use of uDMA. + // + pState->bUseDMA = false; + + // + // Move to the read data state. + // + pState->ui16State = STATE_READ_DATA; + } + + // + // This transfer should use uDMA. + // + else + { + // + // If the transfer is larger than 1024 bytes, enable the + // uDMA receive complete interrupt which will be used to + // move to the next block of the transfer. Otherwise, + // enable the uDMA transmit complete interrupt which will + // be used to complete the transaction. + // + if(pState->ui32ReadCount > 1024) + { + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMARXIM; + } + else + { + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM; + } + + // + // Disable the uDMA channels. + // + HWREG(UDMA_ENACLR) = ((1 << pState->ui32TxChannel) | + (1 << pState->ui32RxChannel)); + + // + // Configure the attributes for the transmit uDMA channel. + // + HWREG(UDMA_USEBURSTSET) = ((1 << pState->ui32TxChannel) | + (1 << pState->ui32RxChannel)); + HWREG(UDMA_ALTCLR) = ((1 << pState->ui32TxChannel) | + (1 << pState->ui32RxChannel)); + HWREG(UDMA_PRIOCLR) = 1 << pState->ui32TxChannel; + HWREG(UDMA_PRIOSET) = 1 << pState->ui32RxChannel; + HWREG(UDMA_REQMASKCLR) = ((1 << pState->ui32TxChannel) | + (1 << pState->ui32RxChannel)); + + // + // Configure the control parameters of the uDMA channels. + // + uDMAChannelControlSet(pState->ui32TxChannel, + UDMA_SRC_INC_NONE | + UDMA_DST_INC_NONE | + UDMA_SIZE_8 | UDMA_ARB_2); + uDMAChannelControlSet(pState->ui32RxChannel, + UDMA_SRC_INC_NONE | + UDMA_DST_INC_8 | + UDMA_SIZE_8 | UDMA_ARB_4); + + // + // Configure the uDMA receive channel to transfer the first + // portion of the data buffer. + // + uDMAChannelTransferSet(pState->ui32RxChannel, + UDMA_MODE_BASIC, + (void *)(pState->ui32Base + + SSI_O_DR), + pState->pui8Buffer, + (pState->ui32ReadCount >= 1024) ? + 1024 : pState->ui32ReadCount); + + // + // Enable the uDMA receive channel. + // + uDMAChannelEnable(pState->ui32RxChannel); + + // + // Configure the uDMA channel to transfer the dummy bytes + // for the first portion of the data buffer. The last + // dummy byte will not be included since it must be treated + // special. + // + uDMAChannelTransferSet(pState->ui32TxChannel, + UDMA_MODE_BASIC, + pState->pui8Buffer, + (void *)(pState->ui32Base + + SSI_O_DR), + (pState->ui32WriteCount > 1024) ? + 1024 : pState->ui32WriteCount - 1); + + // + // Enable the uDMA transmit channel. + // + uDMAChannelEnable(pState->ui32TxChannel); + + // + // Clear any previously pending uDMA completion interrupt. + // + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMARXIC; + + // + // Enable uDMA transmit and receive in the SSI module. + // + MAP_SSIDMAEnable(pState->ui32Base, + SSI_DMA_TX | SSI_DMA_RX); + + // + // Move to the uDMA data read state. + // + pState->ui16State = STATE_READ_DATA_DMA; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the read data state. + // + case STATE_READ_DATA: + { + // + // Loop while there is more than one byte left to write. + // + while(pState->ui32WriteCount != 1) + { + // + // Dummy bytes are written into the FIFO in order to + // trigger the read operation. Attempt to write another + // dummy byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, 0) == 0) + { + // + // The dummy byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + + // + // Decrement the count of dummy bytes to write. + // + pState->ui32WriteCount--; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Move to the read data end state. + // + pState->ui16State = STATE_READ_DATA_END; + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the uDMA read data state. + // + case STATE_READ_DATA_DMA: + { + // + // See if the write count is greater than one. + // + if(pState->ui32WriteCount > 1) + { + // + // Return indicating that the transfer is still in + // progress. + // + return(SPI_FLASH_WORKING); + } + + // + // Disable uDMA transmit in the SSI module. + // + MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_TX); + + // + // Enable the uDMA receive done and FIFO transmit interrupt. + // + HWREG(pState->ui32Base + SSI_O_IM) = + SSI_IM_DMARXIM | SSI_IM_TXIM; + + // + // Move to the read data end state. + // + pState->ui16State = STATE_READ_DATA_END; + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the data read end state. + // + case STATE_READ_DATA_END: + { + // + // See if the final dummy byte still needs to be written. + // + if(pState->ui32WriteCount != 0) + { + // + // Attempt to write the final dummy byte into the FIFO and + // mark it as the end of the frame. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIAdvDataPutFrameEndNonBlocking(pState->ui32Base, + 0) == 0) + { + // + // The dummy byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + + // + // The write portion of the transfer has completed. + // + pState->ui32WriteCount = 0; + + // + // Disable the transmit interrupt now that the write + // write portion of the transfer has completed. + // + HWREG(pState->ui32Base + SSI_O_IM) &= ~(SSI_IM_TXIM); + } + + // + // Return indicating that the transfer is still in progress if + // there are still data bytes to be read. + // + if(pState->ui32ReadCount != 0) + { + return(SPI_FLASH_WORKING); + } + + // + // Disable uDMA receive in the SSI module. + // + MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_RX); + + // + // The transfer is complete, so disable all interrupts. + // + HWREG(pState->ui32Base + SSI_O_IM) = 0; + + // + // Move to the idle state. + // + pState->ui16State = STATE_IDLE; + + // + // Return indicating that the transfer has completed. + // + return(SPI_FLASH_DONE); + } + + // + // The state machine is in the write data setup state. + // + case STATE_WRITE_DATA_SETUP: + { + // + // See if a single data byte is being transferred. + // + if(pState->ui32WriteCount == 1) + { + // + // Disable the use of uDMA. + // + pState->bUseDMA = false; + + // + // Move to the write data end state to transfer the single + // byte. This uses PIO even if uDMA has been requested. + // + pState->ui16State = STATE_WRITE_DATA_END; + } + + // + // See if uDMA has been requested for this transfer. + // + else if(!pState->bUseDMA || (pState->ui32WriteCount < 4)) + { + // + // Disable the use of uDMA. + // + pState->bUseDMA = false; + + // + // uDMA is not being used, so move to the write data state. + // + pState->ui16State = STATE_WRITE_DATA; + } + + // + // This transfer should use uDMA. + // + else + { + // + // Enable the uDMA transmit complete interrupt. + // + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM; + + // + // Disable the transmit uDMA channel. + // + HWREG(UDMA_ENACLR) = 1 << pState->ui32TxChannel; + + // + // Configure the attributes for the transmit uDMA channel. + // + HWREG(UDMA_USEBURSTSET) = 1 << pState->ui32TxChannel; + HWREG(UDMA_ALTCLR) = 1 << pState->ui32TxChannel; + HWREG(UDMA_PRIOCLR) = 1 << pState->ui32TxChannel; + HWREG(UDMA_REQMASKCLR) = 1 << pState->ui32TxChannel; + + // + // Configure the control parameters of the uDMA channel. + // + uDMAChannelControlSet(pState->ui32TxChannel, + UDMA_SRC_INC_8 | + UDMA_DST_INC_NONE | + UDMA_SIZE_8 | UDMA_ARB_4); + + // + // Configure the uDMA channel to transfer the next portion + // of the data buffer. The last byte in the buffer will + // not be included since it must be treated special. + // + uDMAChannelTransferSet(pState->ui32TxChannel, + UDMA_MODE_BASIC, + pState->pui8Buffer, + (void *)(pState->ui32Base + + SSI_O_DR), + (pState->ui32WriteCount > 1024) ? + 1024 : pState->ui32WriteCount - 1); + + // + // Enable the uDMA transmit channel. + // + uDMAChannelEnable(pState->ui32TxChannel); + HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC; + + // + // Enable uDMA in the SSI module. + // + MAP_SSIDMAEnable(pState->ui32Base, SSI_DMA_TX); + + // + // Move to the uDMA data write state. + // + pState->ui16State = STATE_WRITE_DATA_DMA; + } + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the write data state. + // + case STATE_WRITE_DATA: + { + // + // Loop while there is more than one byte left to write. + // + while(pState->ui32WriteCount != 1) + { + // + // Attempt to write the next data byte into the FIFO. + // + if(ui32Count == 0) + { + return(SPI_FLASH_WORKING); + } + if(MAP_SSIDataPutNonBlocking(pState->ui32Base, + *(pState->pui8Buffer)) == 0) + { + // + // The next data byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + + // + // Increment the buffer pointer and decrement the byte + // count. + // + pState->pui8Buffer++; + pState->ui32WriteCount--; + + // + // Decrement the count of bytes that have been written. + // + ui32Count--; + } + + // + // Move to the write data end state. + // + pState->ui16State = STATE_WRITE_DATA_END; + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the uDMA write data state. + // + case STATE_WRITE_DATA_DMA: + { + // + // See if the write count is greater than one. + // + if(pState->ui32WriteCount > 1) + { + // + // Return indicating that the transfer is still in + // progress. + // + return(SPI_FLASH_WORKING); + } + + // + // Disable uDMA in the SSI module. + // + MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_TX); + + // + // Disable the uDMA transmit complete interrupt and enable the + // FIFO interrupt. + // + HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_TXIM; + + // + // Move to the write data end state. + // + pState->ui16State = STATE_WRITE_DATA_END; + + // + // Done with this state. + // + break; + } + + // + // The state machine is in the write data end state. + // + case STATE_WRITE_DATA_END: + { + // + // Attempt to write the final data byte into the FIFO. + // + if(MAP_SSIAdvDataPutFrameEndNonBlocking(pState->ui32Base, + *(pState->pui8Buffer)) == + 0) + { + // + // The final data byte could not be written, so return + // indicating that the transfer is still in progress. + // + return(SPI_FLASH_WORKING); + } + + // + // The transfer is complete, so disable all interrupts. + // + HWREG(pState->ui32Base + SSI_O_IM) = 0; + + // + // Move to the idle state. + // + pState->ui16State = STATE_IDLE; + + // + // Return indicating that the transfer has completed. + // + return(SPI_FLASH_DONE); + } + } + } +} + +//***************************************************************************** +// +//! Initializes the SPI flash driver. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Clock is the rate of the clock supplied to the SSI module. +//! \param ui32BitRate is the SPI clock rate. +//! +//! This function configures the SSI module for use by the SPI flash driver. +//! The SSI module will be placed into the correct mode of operation to allow +//! communication with the SPI flash. This function must be called prior to +//! calling the remaining SPI flash driver APIs. It can be called at a later +//! point to reconfigure the SSI module, such as to increase the SPI clock rate +//! once it has been determined that it is safe to use a higher speed clock. +//! +//! It is the responsibility of the caller to enable the SSI module and +//! configure the pins that it will utilize. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashInit(uint32_t ui32Base, uint32_t ui32Clock, uint32_t ui32BitRate) +{ + // + // Configure the SPI module. + // + MAP_SSIConfigSetExpClk(ui32Base, ui32Clock, SSI_FRF_MOTO_MODE_0, + SSI_MODE_MASTER, ui32BitRate, 8); + + // + // Enable the advanced mode of operation, defaulting to read/write mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // Enable the frame hold feature. + // + MAP_SSIAdvFrameHoldEnable(ui32Base); + + // + // Enable the SPI module. + // + MAP_SSIEnable(ui32Base); +} + +//***************************************************************************** +// +//! Writes the SPI flash status register. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui8Status is the value to write to the status register. +//! +//! This function writes the SPI flash status register. This uses the 0x01 SPI +//! flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashWriteStatus(uint32_t ui32Base, uint8_t ui8Status) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the write status register command. + // + MAP_SSIDataPut(ui32Base, CMD_WRSR); + + // + // Send the new status register value, marking this byte as the end of the + // frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, ui8Status); +} + +//***************************************************************************** +// +//! Programs the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to be programmed. +//! \param pui8Data is a pointer to the data to be programmed. +//! \param ui32Count is the number of bytes to be programmed. +//! +//! This function programs data into the SPI flash, using PIO mode. This +//! function will not return until the entire program command has been written +//! into the SSI transmit FIFO. This uses the 0x02 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashPageProgram(uint32_t ui32Base, uint32_t ui32Addr, + const uint8_t *pui8Data, uint32_t ui32Count) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the page program command. + // + MAP_SSIDataPut(ui32Base, CMD_PP); + + // + // Send the address of the first byte to program. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Loop while there is more than one data byte left to be sent. + // + while(ui32Count-- != 1) + { + // + // Send the next data byte. + // + MAP_SSIDataPut(ui32Base, *pui8Data++); + } + + // + // Send the last data byte, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, *pui8Data); +} + +//***************************************************************************** +// +//! Programs the SPI flash in the background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to be programmed. +//! \param pui8Data is a pointer to the data to be programmed. +//! \param ui32Count is the number of bytes to be programmed. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! +//! This function programs data into the SPI flash, using either interrupts or +//! uDMA to transfer the data. This function will return immediately and send +//! the data in the background. In order for this to complete successfully, +//! several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the programming operation is indicated when +//! SPIFlashIntHandler() returns \b SPI_FLASH_DONE. +//! +//! Like SPIFlashPageProgram(), this uses the 0x02 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashPageProgramNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, const uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel) +{ + // + // Save the parameters of this program operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_PP; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = (uint8_t *)pui8Data; + pState->ui32ReadCount = 0; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + + // + // Enable the SSI transmit interrupt. This will start the transfer. If + // uDMA is being used, the uDMA-related interrupt will be enabled at the + // appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! +//! This function reads data from the SPI flash, using PIO mode. This function +//! will not return until the read has completed. This uses the 0x03 SPI flash +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the read command. + // + MAP_SSIDataPut(ui32Base, CMD_READ); + + // + // Send the address of the first byte to read. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Set the SSI module into read/write mode. In this mode, dummy writes are + // required in order to make the transfer occur; the SPI flash will ignore + // the data. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // See if there is a single byte to be read. + // + if(ui32Count == 1) + { + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + } + else + { + // + // Perform a dummy write to prime the loop. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Loop while there is more than one byte left to be read. + // + while(--ui32Count != 1) + { + // + // Perform a dummy write to keep the transmit FIFO from going + // empty. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into + // the data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Perform the final dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Read the final data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash in the background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! \param ui32RxChannel is the uDMA channel to be used for reading from the +//! SSI module. +//! +//! This function reads data from the SPI flash, using either interrupts or +//! uDMA to transfer the data. This function will return immediately and read +//! the data in the background. In order for this to complete successfully, +//! several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the read operation is indicated when SPIFlashIntHandler() +//! returns \b SPI_FLASH_DONE. +//! +//! Like SPIFlashRead(), this uses the 0x03 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, uint32_t ui32RxChannel) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Save the parameters of this read operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_READ; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = pui8Data; + pState->ui32ReadCount = ui32Count; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + pState->ui32RxChannel = ui32RxChannel & 0x1f; + + // + // Enable the SSI transmit and receive interrupts. This will start the + // transfer. If uDMA is being used, the uDMA-related interrupts will be + // enabled at the appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM; +} + +//***************************************************************************** +// +//! Disables SPI flash write operations. +//! +//! \param ui32Base is the SSI module base address. +//! +//! This function sets the SPI flash to disallow program and erase operations. +//! This uses the 0x04 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashWriteDisable(uint32_t ui32Base) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the write disable command, marking this byte as the end of the + // frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_WRDI); +} + +//***************************************************************************** +// +//! Reads the SPI flash status register. +//! +//! \param ui32Base is the SSI module base address. +//! +//! This function reads the SPI flash status register. This uses the 0x05 SPI +//! flash command. +//! +//! \return Returns the value of the SPI flash status register. +// +//***************************************************************************** +uint8_t +SPIFlashReadStatus(uint32_t ui32Base) +{ + uint32_t ui32Data; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Data) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the write status register command. + // + MAP_SSIDataPut(ui32Base, CMD_RDSR); + + // + // Set the SSI module into read/write mode. In this mode, dummy writes are + // required in order to make the transfer occur; the SPI flash will ignore + // the data. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the value of the status register. + // + MAP_SSIDataGet(ui32Base, &ui32Data); + + // + // Return the status register value. + // + return(ui32Data & 0xff); +} + +//***************************************************************************** +// +//! Enables SPI flash write operations. +//! +//! \param ui32Base is the SSI module base address. +//! +//! This function sets the SPI flash to allow program and erase operations. +//! This must be done prior to each SPI flash program or erase operation; the +//! SPI flash will automatically disable program and erase operations once a +//! program or erase operation has completed. This uses the 0x06 SPI flash +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashWriteEnable(uint32_t ui32Base) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the write enable command, marking this byte as the end of the + // frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_WREN); +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using the fast read command. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! +//! This function reads data from the SPI flash with the fast read command, +//! using PIO mode. The fast read command allows the SPI flash to be read at +//! a higher SPI clock rate because of the addition of a dummy cycle during the +//! command setup. This function will not return until the read has completed. +//! This uses the 0x0b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashFastRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the fast read command. + // + MAP_SSIDataPut(ui32Base, CMD_FREAD); + + // + // Send the address of the first byte to read. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Send a dummy byte. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Set the SSI module into read/write mode. In this mode, dummy writes are + // required in order to make the transfer occur; the SPI flash will ignore + // the data. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // See if there is a single byte to be read. + // + if(ui32Count == 1) + { + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + } + else + { + // + // Perform a dummy write to prime the loop. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Loop while there is more than one byte left to be read. + // + while(--ui32Count != 1) + { + // + // Perform a dummy write to keep the transmit FIFO from going + // empty. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into + // the data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Perform the final dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Read the final data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using the fast read command in the +//! background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! \param ui32RxChannel is the uDMA channel to be used for reading from the +//! SSI module. +//! +//! This function reads data from the SPI flash with the fast read command, +//! using either interrupts or uDMA to transfer the data. The fast read +//! command allows the SPI flash to be read at a higher SPI clock rate because +//! of the addition of a dummy cycle during the command setup. This function +//! will return immediately and read the data in the background. In order for +//! this to complete successfully, several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the read operation is indicated when SPIFlashIntHandler() +//! returns \b SPI_FLASH_DONE. +//! +//! Like SPIFlashFastRead(), this uses the 0x0b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashFastReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, uint32_t ui32RxChannel) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Save the parameters of this read operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_FREAD; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = pui8Data; + pState->ui32ReadCount = ui32Count; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + pState->ui32RxChannel = ui32RxChannel & 0x1f; + + // + // Enable the SSI transmit and receive interrupts. This will start the + // transfer. If uDMA is being used, the uDMA-related interrupts will be + // enabled at the appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM; +} + +//***************************************************************************** +// +//! Erases a 4 KB sector of the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to erase. +//! +//! This function erases a sector of the SPI flash. Each sector is 4 KB with a +//! 4 KB alignment; the SPI flash will ignore the lower ten bits of the address +//! provided. The sector erase command is issued by this function; +//! SPIFlashReadStatus() must be used to query the SPI flash to determine when +//! the sector erase operation has completed. This uses the 0x20 SPI flash +//! command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashSectorErase(uint32_t ui32Base, uint32_t ui32Addr) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the sector erase command. + // + MAP_SSIDataPut(ui32Base, CMD_SE); + + // + // Send the address of the sector to be erased, marking the last byte of + // the address as the end of the frame. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff); +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using Bi-SPI. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! +//! This function reads data from the SPI flash with Bi-SPI, using PIO mode. +//! This function will not return until the read has completed. This uses the +//! 0x3b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashDualRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the dual read command. + // + MAP_SSIDataPut(ui32Base, CMD_DREAD); + + // + // Send the address of the first byte to read. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Send a dummy byte. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Set the SSI module into Bi-SPI read mode. In this mode, dummy writes + // are required in order to make the transfer occur; the SSI module will + // ignore the data (the SPI flash will never see the dummy data since + // Bi-SPI read mode is a uni-directional input mode). + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_BI_READ); + + // + // See if there is a single byte to be read. + // + if(ui32Count == 1) + { + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + } + else + { + // + // Perform a dummy write to prime the loop. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Loop while there is more than one byte left to be read. + // + while(--ui32Count != 1) + { + // + // Perform a dummy write to keep the transmit FIFO from going + // empty. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into + // the data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Perform the final dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Read the final data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using Bi-SPI in the background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! \param ui32RxChannel is the uDMA channel to be used for reading from the +//! SSI module. +//! +//! This function reads data from the SPI flash with Bi-SPI, using either +//! interrupts or uDMA to transfer the data. This function will return +//! immediately and read the data in the background. In order for this to +//! complete successfully, several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the read operation is indicated when SPIFlashIntHandler() +//! returns \b SPI_FLASH_DONE. +//! +//! Like SPIFLashDualRead(), this uses the 0x3b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashDualReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, uint32_t ui32RxChannel) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Save the parameters of this read operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_DREAD; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = pui8Data; + pState->ui32ReadCount = ui32Count; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + pState->ui32RxChannel = ui32RxChannel & 0x1f; + + // + // Enable the SSI transmit and receive interrupts. This will start the + // transfer. If uDMA is being used, the uDMA-related interrupts will be + // enabled at the appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM; +} + +//***************************************************************************** +// +//! Erases a 32 KB block of the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to erase. +//! +//! This function erases a 32 KB block of the SPI flash. Each 32 KB block has +//! a 32 KB alignment; the SPI flash will ignore the lower 15 bits of the +//! address provided. The 32 KB block erase command is issued by this +//! function; SPIFlashReadStatus() must be used to query the SPI flash to +//! determine when the 32 KB block erase operation has completed. This uses +//! the 0x52 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashBlockErase32(uint32_t ui32Base, uint32_t ui32Addr) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the 32 KB block erase command command. + // + MAP_SSIDataPut(ui32Base, CMD_BE32); + + // + // Send the address of the 32 KB block to be erased, marking the last byte + // of the address as the end of the frame. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff); +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using Quad-SPI. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! +//! This function reads data from the SPI flash with Quad-SPI, using PIO mode. +//! This function will not return until the read has completed. This uses the +//! 0x6b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashQuadRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the quad read command. + // + MAP_SSIDataPut(ui32Base, CMD_QREAD); + + // + // Send the address of the first byte to read. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); + + // + // Send a dummy byte. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Set the SSI module into Quad-SPI read mode. In this mode, dummy writes + // are required in order to make the transfer occur; the SSI module will + // ignore the data (the SPI flash will never see the dummy data since + // Quad-SPI read mode is a uni-directional input mode). + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_QUAD_READ); + + // + // See if there is a single byte to be read. + // + if(ui32Count == 1) + { + // + // Perform a single dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + } + else + { + // + // Perform a dummy write to prime the loop. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Loop while there is more than one byte left to be read. + // + while(--ui32Count != 1) + { + // + // Perform a dummy write to keep the transmit FIFO from going + // empty. + // + MAP_SSIDataPut(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into + // the data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Perform the final dummy write, marking it as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the next data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; + } + + // + // Read the final data byte from the receive FIFO and place it into the + // data buffer. + // + MAP_SSIDataGet(ui32Base, &ui32Addr); + *pui8Data++ = ui32Addr & 0xff; +} + +//***************************************************************************** +// +//! Reads data from the SPI flash using Quad-SPI in the background. +//! +//! \param pState is a pointer to the SPI flash state structure. +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to read. +//! \param pui8Data is a pointer to the data buffer to into which to read the +//! data. +//! \param ui32Count is the number of bytes to read. +//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise. +//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI +//! module. +//! \param ui32RxChannel is the uDMA channel to be used for reading from the +//! SSI module. +//! +//! This function reads data from the SPI flash with Quad-SPI, using either +//! interrupts or uDMA to transfer the data. This function will return +//! immediately and read the data in the background. In order for this to +//! complete successfully, several conditions must be satisfied: +//! +//! - Prior to calling this function: +//! - The SSI module must be enabled in SysCtl. +//! - The SSI pins must be configured for use by the SSI module. +//! - The SSI module interrupt must be enabled in NVIC. +//! - The uDMA module must be enabled in SysCtl and the control table set (if +//! using uDMA). +//! - The uDMA channels must be assigned to the SSI module. +//! +//! - After calling this function: +//! - The interrupt handler for the SSI module must call +//! SPIFlashIntHandler(), passing the same pState structure pointer that +//! was supplied to this function. +//! - No other SPI flash operation can be called until this operation has +//! completed. +//! +//! Completion of the read operation is indicated when SPIFlashIntHandler() +//! returns \b SPI_FLASH_DONE. +//! +//! Like SPIFlashQuadRead(), this uses the 0x6b SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashQuadReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, uint32_t ui32RxChannel) +{ + uint32_t ui32Trash; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) + { + } + + // + // Save the parameters of this read operation to the state structure. + // + pState->ui32Base = ui32Base; + pState->ui16Cmd = CMD_QREAD; + pState->ui16State = STATE_CMD; + pState->ui32Addr = ui32Addr; + pState->pui8Buffer = pui8Data; + pState->ui32ReadCount = ui32Count; + pState->ui32WriteCount = ui32Count; + pState->bUseDMA = bUseDMA; + pState->ui32TxChannel = ui32TxChannel & 0x1f; + pState->ui32RxChannel = ui32RxChannel & 0x1f; + + // + // Enable the SSI transmit and receive interrupts. This will start the + // transfer. If uDMA is being used, the uDMA-related interrupts will be + // enabled at the appropriate time by the interrupt handler. + // + HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC; + HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM; +} + +//***************************************************************************** +// +//! Reads the manufacturer and device IDs from the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param pui8ManufacturerID is a pointer to the location into which to store +//! the manufacturer ID. +//! \param pui16DeviceID is a pointer to the location into which to store the +//! device ID. +//! +//! This function reads the manufacturer and device IDs from the SPI flash. +//! These values can be used to identify the SPI flash that is attached, as +//! well as determining if a SPI flash is attached (if the \b SSIRx pin is +//! pulled up or down, either using the pad's weak pull up/down or using an +//! external resistor, which will cause the returned IDs to be either all zeros +//! or all ones if the SPI flash is not attached). This uses the 0x9f SPI +//! flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashReadID(uint32_t ui32Base, uint8_t *pui8ManufacturerID, + uint16_t *pui16DeviceID) +{ + uint32_t ui32Data1, ui32Data2; + + // + // Drain any residual data from the receive FIFO. + // + while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Data1) != 0) + { + } + + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the read ID command. + // + MAP_SSIDataPut(ui32Base, CMD_RDID); + + // + // Set the SSI module into read/write mode. In this mode, dummy writes are + // required in order to make the transfer occur; the SPI flash will ignore + // the data. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); + + // + // Send three dummy bytes, marking the last as the end of the frame. + // + MAP_SSIDataPut(ui32Base, 0); + MAP_SSIDataPut(ui32Base, 0); + MAP_SSIAdvDataPutFrameEnd(ui32Base, 0); + + // + // Read the first returned data byte, which contains the manufacturer ID. + // + MAP_SSIDataGet(ui32Base, &ui32Data1); + *pui8ManufacturerID = ui32Data1 & 0xff; + + // + // Read the remaining two data bytes, which contain the device ID. + // + MAP_SSIDataGet(ui32Base, &ui32Data1); + MAP_SSIDataGet(ui32Base, &ui32Data2); + *pui16DeviceID = ((ui32Data1 & 0xff) << 8) | (ui32Data2 & 0xff); +} + +//***************************************************************************** +// +//! Erases the entire SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! +//! This command erase the entire SPI flash. The chip erase command is issued +//! by this function; SPIFlashReadStatus() must be used to query the SPI flash +//! to determine when the chip erase operation has completed. This uses the +//! 0xc7 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashChipErase(uint32_t ui32Base) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the chip erase command, marking this byte as the end of the frame. + // + MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_CE); +} + +//***************************************************************************** +// +//! Erases a 64 KB block of the SPI flash. +//! +//! \param ui32Base is the SSI module base address. +//! \param ui32Addr is the SPI flash address to erase. +//! +//! This function erases a 64 KB block of the SPI flash. Each 64 KB block has +//! a 64 KB alignment; the SPI flash will ignore the lower 16 bits of the +//! address provided. The 64 KB block erase command is issued by this +//! function; SPIFlashReadStatus() must be used to query the SPI flash to +//! determine when the 64 KB block erase operation has completed. This uses +//! the 0xd8 SPI flash command. +//! +//! \return None. +// +//***************************************************************************** +void +SPIFlashBlockErase64(uint32_t ui32Base, uint32_t ui32Addr) +{ + // + // Set the SSI module into write-only mode. + // + MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); + + // + // Send the 64 KB block erase command command. + // + MAP_SSIDataPut(ui32Base, CMD_BE64); + + // + // Send the address of the 64 KB block to be erased, marking the last byte + // of the address as the end of the frame. + // + MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); + MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); + MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/spi_flash.h b/utils/spi_flash.h new file mode 100644 index 0000000..59311fb --- /dev/null +++ b/utils/spi_flash.h @@ -0,0 +1,166 @@ +//***************************************************************************** +// +// spi_flash.h - Prototypes for the SPI flash 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 Utility Library. +// +//***************************************************************************** + +#ifndef __SPI_FLASH_H__ +#define __SPI_FLASH_H__ + +//***************************************************************************** +// +//! \addtogroup spi_flash_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The state structure used when performing non-blocking SPI flash operations. +// +//***************************************************************************** +typedef struct +{ + // + //! The base address of the SSI module that is being used. + // + uint32_t ui32Base; + + // + //! The command that is being send to the SPI flash. + // + uint16_t ui16Cmd; + + // + //! The current state of the SPI flash state machine. + // + uint16_t ui16State; + + // + //! The SPI flash address associated with the command. + // + uint32_t ui32Addr; + + // + //! A pointer to the data buffer that is being read or written. + // + uint8_t *pui8Buffer; + + // + //! The count of bytes left to be read. + // + uint32_t ui32ReadCount; + + // + //! The count of bytes left to be written. + // + uint32_t ui32WriteCount; + + // + //! A flag that is true if uDMA used be used for the transfer. + // + bool bUseDMA; + + // + //! The uDMA channel to use for transmitting when using uDMA for the + //! transfer. + // + uint32_t ui32TxChannel; + + // + //! The uDMA channel to use for receiving when using uDMA for the transfer. + // + uint32_t ui32RxChannel; +} +tSPIFlashState; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// The possible return values from the SPI flash interrupt handler. +// +//***************************************************************************** +#define SPI_FLASH_IDLE 0 +#define SPI_FLASH_WORKING 1 +#define SPI_FLASH_DONE 3 + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern uint32_t SPIFlashIntHandler(tSPIFlashState *pState); +extern void SPIFlashInit(uint32_t ui32Base, uint32_t ui32Clock, + uint32_t ui32BitRate); +extern void SPIFlashWriteStatus(uint32_t ui32Base, uint8_t ui8Status); +extern void SPIFlashPageProgram(uint32_t ui32Base, uint32_t ui32Addr, + const uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashPageProgramNonBlocking(tSPIFlashState *pState, + uint32_t ui32Base, + uint32_t ui32Addr, + const uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel); +extern void SPIFlashRead(uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base, + uint32_t ui32Addr, uint8_t *pui8Data, + uint32_t ui32Count, bool bUseDMA, + uint32_t ui32TxChannel, + uint32_t ui32RxChannel); +extern void SPIFlashWriteDisable(uint32_t ui32Base); +extern uint8_t SPIFlashReadStatus(uint32_t ui32Base); +extern void SPIFlashWriteEnable(uint32_t ui32Base); +extern void SPIFlashFastRead(uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashFastReadNonBlocking(tSPIFlashState *pState, + uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count, + bool bUseDMA, uint32_t ui32TxChannel, + uint32_t ui32RxChannel); +extern void SPIFlashSectorErase(uint32_t ui32Base, uint32_t ui32Addr); +extern void SPIFlashDualRead(uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashDualReadNonBlocking(tSPIFlashState *pState, + uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count, + bool bUseDMA, uint32_t ui32TxChannel, + uint32_t ui32RxChannel); +extern void SPIFlashBlockErase32(uint32_t ui32Base, uint32_t ui32Addr); +extern void SPIFlashQuadRead(uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count); +extern void SPIFlashQuadReadNonBlocking(tSPIFlashState *pState, + uint32_t ui32Base, uint32_t ui32Addr, + uint8_t *pui8Data, uint32_t ui32Count, + bool bUseDMA, uint32_t ui32TxChannel, + uint32_t ui32RxChannel); +extern void SPIFlashReadID(uint32_t ui32Base, uint8_t *pui8ManufacturerID, + uint16_t *pui16DeviceID); +extern void SPIFlashChipErase(uint32_t ui32Base); +extern void SPIFlashBlockErase64(uint32_t ui32Base, uint32_t ui32Addr); + +#endif // __SPI_FLASH_H__ diff --git a/utils/swupdate.c b/utils/swupdate.c new file mode 100644 index 0000000..edd523b --- /dev/null +++ b/utils/swupdate.c @@ -0,0 +1,355 @@ +//***************************************************************************** +// +// swupdate.c - A module wrapping the Ethernet bootloader software update +// functionality. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_nvic.h" +#include "inc/hw_sysctl.h" +#include "inc/hw_types.h" +#include "driverlib/flash.h" +#include "driverlib/rom.h" +#include "driverlib/sysctl.h" +#include "driverlib/systick.h" +#include "utils/lwiplib.h" +#include "utils/swupdate.h" + +//***************************************************************************** +// +//! \addtogroup swupdate_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The UDP port used to send the remote firmware update request signal. This +// is the well-known port associated with "discard" function and is also used +// by some Wake-On-LAN implementations. +// +//***************************************************************************** +#define MPACKET_PORT 9 + +//***************************************************************************** +// +// The length of the various parts of the remote firmware update request magic +// packet and its total length. This contains a 6 byte header followed by 4 +// copies of the target MAC address. +// +//***************************************************************************** +#define MPACKET_HEADER_LEN 6 +#define MPACKET_MAC_REP 4 +#define MPACKET_MAC_LEN 6 +#define MPACKET_LEN (MPACKET_HEADER_LEN + \ + (MPACKET_MAC_REP * MPACKET_MAC_LEN)) + +//***************************************************************************** +// +// The marker byte used at the start of the magic packet. This is repeated +// MPACKET_HEADER_LEN times. +// +//***************************************************************************** +#define MPACKET_MARKER 0xAA + +//***************************************************************************** +// +// The callback function which is used to determine whether or not the +// application wants to allow a remotely-requested firmware update. +// +//***************************************************************************** +tSoftwareUpdateRequested g_pfnUpdateCallback = NULL; + +//***************************************************************************** +// +// A pointer to the remote firmware update signal PCB data structure. +// +//***************************************************************************** +static struct udp_pcb *g_psMagicPacketPCB = NULL; + +//***************************************************************************** +// +// The MAC address for this board. +// +//***************************************************************************** +static uint8_t g_pui8MACAddr[6]; + +//***************************************************************************** +// +// Receives a UDP port 9 packet from lwIP. +// +// \param arg is not used in this implementation. +// \param pcb is the pointer to the UDB control structure. +// \param p is the pointer to the PBUF structure containing the packet data. +// \param addr is the source (remote) IP address for this packet. +// \param port is the source (remote) port for this packet. +// +// This function is called when the lwIP TCP/IP stack has an incoming +// UDP packet to be processed on the remote firmware update signal port. +// +// \return None. +// +//***************************************************************************** +static void +SoftwareUpdateUDPReceive(void *arg, struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + int8_t *pi8Data = p->payload; + uint32_t ui32Loop, ui32MACLoop; + + // + // Check that the packet length is what we expect. If not, ignore the + // packet. + // + if(p->len == MPACKET_LEN) + { + // + // The length matches so now look for the 6 byte header + // + for(ui32Loop = 0; ui32Loop < MPACKET_HEADER_LEN; ui32Loop++) + { + // + // Does this header byte match the expected marker? + // + if((*pi8Data & 0x000000FF)!= MPACKET_MARKER) + { + // + // No - free the buffer and return - this is not a packet + // we are interested in. + // + pbuf_free(p); + return; + } + else + { + // + // Byte matched so move on to the next one. + // + pi8Data++; + } + } + } + else + { + // + // No - free the buffer and return - this is not a packet + // we are interested in. + // + pbuf_free(p); + return; + } + + // + // If we get here, the packet length and header markers indicate + // that this is a remote firmware update request. Now check that it + // is for us and that it contains the required number of copies of + // the MAC address. + // + + // + // Loop through each of the expected MAC address copies. + // + for(ui32Loop = 0; ui32Loop < MPACKET_MAC_REP; ui32Loop++) + { + // + // Loop through each byte of the MAC address in this + // copy. + // + for(ui32MACLoop = 0; ui32MACLoop < MPACKET_MAC_LEN; ui32MACLoop++) + { + // + // Does the payload MAC address byte match what we expect? + // + if((*pi8Data & 0x000000FF) != g_pui8MACAddr[ui32MACLoop]) + { + // + // No match - free the packet and return. + // + pbuf_free(p); + return; + } + else + { + // + // Byte matched so move on to the next one. + // + pi8Data++; + } + } + } + + // + // Free the pbuf since we are finished with it now. + // + pbuf_free(p); + + // + // If we get this far, we've received a valid remote firmare update + // request targetted at this board. Signal this to the application + // if we have a valid callback pointer. + // + if(g_pfnUpdateCallback) + { + g_pfnUpdateCallback(); + } +} + +//***************************************************************************** +// +//! Initializes the remote Ethernet software update notification feature. +//! +//! \param pfnCallback is a pointer to a function which will be called whenever +//! a remote firmware update request is received. If the application wishes +//! to allow the update to go ahead, it must call SoftwareUpdateBegin() from +//! non-interrupt context after the callback is received. Note that the +//! callback will most likely be made in interrupt context so it is not safe +//! to call SoftwareUpdateBegin() from within the callback itself. +//! +//! This function may be used on Ethernet-enabled parts to support +//! remotely-signaled firmware updates over Ethernet. The LM Flash Programmer +//! (LMFlash.exe) application sends a magic packet to UDP port 9 whenever the +//! user requests an Ethernet-based firmware update. This packet consists of +//! 6 bytes of 0xAA followed by the target MAC address repeated 4 times. +//! This function starts listening on UDP port 9 and, if a magic packet +//! matching the MAC address of this board is received, makes a call to the +//! provided callback function to indicate that an update has been requested. +//! +//! The callback function provided here will typically be called in the context +//! of the lwIP Ethernet interrupt handler. It is not safe to call +//! SoftwareUpdateBegin() in this context so the application should use the +//! callback to signal code running in a non-interrupt context to perform the +//! update if it is to be allowed. +//! +//! UDP port 9 is chosen for this function since this is the well-known port +//! associated with ``discard'' operation. In other words, any other system +//! receiving the magic packet will simply ignore it. The actual magic packet +//! used is modeled on Wake-On-LAN which uses a similar structure (6 bytes of +//! 0xFF followed by 16 repetitions of the target MAC address). Some +//! Wake-On-LAN implementations also use UDP port 9 for their signaling. +//! +//! \note Applications using this function must initialize the lwIP stack prior +//! to making this call and must ensure that the lwIPTimer() function is called +//! periodically. lwIP UDP must be enabled in lwipopts.h to ensure that the +//! magic packets can be received. +//! +//! \return None. +// +//***************************************************************************** +void +SoftwareUpdateInit(tSoftwareUpdateRequested pfnCallback) +{ + uint32_t ui32User0, ui32User1; + + // + // Remember the callback function pointer we have been given. + // + g_pfnUpdateCallback = pfnCallback; + + // + // Get the MAC address from the user registers in NV ram. + // + FlashUserGet(&ui32User0, &ui32User1); + + // + // Convert the 24/24 split MAC address from NV ram into a MAC address + // array. + // + g_pui8MACAddr[0] = ui32User0 & 0xff; + g_pui8MACAddr[1] = (ui32User0 >> 8) & 0xff; + g_pui8MACAddr[2] = (ui32User0 >> 16) & 0xff; + g_pui8MACAddr[3] = ui32User1 & 0xff; + g_pui8MACAddr[4] = (ui32User1 >> 8) & 0xff; + g_pui8MACAddr[5] = (ui32User1 >> 16) & 0xff; + + // + // Set up a UDP PCB to allow us to receive the magic packets sent from + // LMFlash. These may be sent to port 9 from any port on the source + // machine so we do not call udp_connect here (since this causes lwIP to + // filter any packet that did not originate from port 9 too). + // + g_psMagicPacketPCB = udp_new(); + udp_recv(g_psMagicPacketPCB, SoftwareUpdateUDPReceive, NULL); + udp_bind(g_psMagicPacketPCB, IP_ADDR_ANY, MPACKET_PORT); +} + +//***************************************************************************** +// +//! Passes control to the bootloader and initiates a remote software update +//! over Ethernet. +//! +//! This function passes control to the bootloader and initiates an update of +//! the main application firmware image via BOOTP across Ethernet. This +//! function may only be used on parts supporting Ethernet and in cases where +//! the Ethernet boot loader is in use alongside the main application image. +//! It must not be called in interrupt context. +//! +//! Applications wishing to make use of this function must be built to +//! operate with the bootloader. If this function is called on a system +//! which does not include the bootloader, the results are unpredictable. +//! +//! \note It is not safe to call this function from within the callback +//! provided on the initial call to SoftwareUpdateInit(). The application +//! must use the callback to signal a pending update (assuming the update is to +//! be permitted) to some other code running in a non-interrupt context. +//! +//! \return Never returns. +// +//***************************************************************************** +void +SoftwareUpdateBegin(uint32_t ui32SysClock) +{ + // + // Disable all processor interrupts. Instead of disabling them + // one at a time (and possibly missing an interrupt if new sources + // are added), a direct write to NVIC is done to disable all + // peripheral interrupts. + // + HWREG(NVIC_DIS0) = 0xffffffff; + HWREG(NVIC_DIS1) = 0xffffffff; + HWREG(NVIC_DIS2) = 0xffffffff; + HWREG(NVIC_DIS3) = 0xffffffff; + HWREG(NVIC_DIS4) = 0xffffffff; + + // + // Also disable the SysTick interrupt. + // + SysTickIntDisable(); + SysTickDisable(); + + // + // Return control to the boot loader. This is a call to the SVC + // handler in the flashed-based boot loader, or to the ROM if configured. + // +#if ((defined ROM_UpdateEthernet) && !(defined USE_FLASH_BOOT_LOADER)) + ROM_UpdateEMAC(ui32SysClock); +#else + (*((void (*)(void))(*(uint32_t *)0x2c)))(); +#endif +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/swupdate.h b/utils/swupdate.h new file mode 100644 index 0000000..1cd44db --- /dev/null +++ b/utils/swupdate.h @@ -0,0 +1,66 @@ +//***************************************************************************** +// +// swupdate.h - Prototypes for the bootloader software update module. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __SWUPDATE_H__ +#define __SWUPDATE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// This function pointer represents the callback made to the application in +// cases where a remote host requests a software update be performed. The +// application should use this to trigger a call to SoftwareUpdateBegin from +// a non-interrupt context. +// +//***************************************************************************** +typedef void (*tSoftwareUpdateRequested)(void); + +//***************************************************************************** +// +// Public function prototypes. +// +//***************************************************************************** +extern void SoftwareUpdateInit(tSoftwareUpdateRequested pfnCallback); +extern void SoftwareUpdateBegin(uint32_t ui32SysClock); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SWUPDATE_H__ diff --git a/utils/tftp.c b/utils/tftp.c new file mode 100644 index 0000000..674c307 --- /dev/null +++ b/utils/tftp.c @@ -0,0 +1,710 @@ +//***************************************************************************** +// +// tftp.c - A very simple lwIP TFTP server. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "utils/uartstdio.h" +#include "utils/lwiplib.h" +#include "utils/ustdlib.h" + +//***************************************************************************** +// +//! \addtogroup tftp_api +//! @{ +// +//***************************************************************************** +#include "utils/tftp.h" + +//***************************************************************************** +// +// 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 port for the TFTP server. +// +//***************************************************************************** +#define TFTP_PORT 69 + +//***************************************************************************** +// +// Application connection notification callback. +// +//***************************************************************************** +static tTFTPRequest g_pfnRequest; + +//***************************************************************************** +// +// Close the TFTP connection and free associated resources. +// +//***************************************************************************** +static void +TFTPClose(tTFTPConnection *psTFTP) +{ + // + // Tell the application we are closing the connection. + // + if(psTFTP->pfnClose) + { + psTFTP->pfnClose(psTFTP); + } + + // + // Close the underlying UDP connection. + // + udp_remove(psTFTP->psPCB); + + // + // Free the instance data structure. + // + mem_free(psTFTP); +} + +//***************************************************************************** +// +// Sends a TFTP error packet. +// +//***************************************************************************** +static void +TFTPErrorSend(tTFTPConnection *psTFTP, tTFTPError eError) +{ + uint32_t ui32Length; + uint8_t *pui8Data; + struct pbuf *p; + + // + // How big is this packet going to be? + // + ui32Length = 5 + strlen(psTFTP->pcErrorString); + + // + // Allocate a pbuf for this data packet. + // + p = pbuf_alloc(PBUF_TRANSPORT, ui32Length, PBUF_RAM); + if(!p) + { + return; + } + + // + // Get a pointer to the data packet. + // + pui8Data = (uint8_t *)p->payload; + + // + // Fill in the packet. + // + pui8Data[0] = (TFTP_ERROR >> 8) & 0xff; + pui8Data[1] = TFTP_ERROR & 0xff; + pui8Data[2] = ((uint32_t)eError >> 8) & 0xff; + pui8Data[3] = (uint32_t)eError & 0xff; + memcpy(&pui8Data[4], psTFTP->pcErrorString, ui32Length - 5); + + // + // Send the data packet. + // + udp_send(psTFTP->psPCB, p); + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Sends a TFTP data packet. +// +//***************************************************************************** +static void +TFTPDataSend(tTFTPConnection *psTFTP) +{ + uint32_t ui32Length; + uint8_t *pui8Data; + tTFTPError eError; + struct pbuf *p; + + // + // Determine the number of bytes to place into this packet. + // + if(psTFTP->ui32DataRemaining < (psTFTP->ui32BlockNum * TFTP_BLOCK_SIZE)) + { + ui32Length = psTFTP->ui32DataRemaining & (TFTP_BLOCK_SIZE - 1); + } + else + { + ui32Length = TFTP_BLOCK_SIZE; + } + + // + // Allocate a pbuf for this data packet. + // + p = pbuf_alloc(PBUF_TRANSPORT, ui32Length + 4, PBUF_RAM); + if(!p) + { + return; + } + + // + // Get a pointer to the data packet. + // + pui8Data = (uint8_t *)p->payload; + + // + // Fill in the packet header. + // + pui8Data[0] = (TFTP_DATA >> 8) & 0xff; + pui8Data[1] = TFTP_DATA & 0xff; + pui8Data[2] = (psTFTP->ui32BlockNum >> 8) & 0xff; + pui8Data[3] = psTFTP->ui32BlockNum & 0xff; + + // + // Ask the application to provide the data we need. + // + psTFTP->pui8Data = pui8Data + 4; + psTFTP->ui32DataLength = ui32Length; + eError = psTFTP->pfnGetData(psTFTP); + + // + // Send the data packet or, if an error was reported, send an error. + // + if(eError == TFTP_OK) + { + udp_send(psTFTP->psPCB, p); + } + else + { + TFTPErrorSend(psTFTP, eError); + TFTPClose(psTFTP); + } + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Send an ACK packet back to the TFTP client. +// +//***************************************************************************** +static void +TFTPDataAck(tTFTPConnection *psTFTP) +{ + uint8_t *pui8Data; + struct pbuf *p; + + // + // Allocate a pbuf for this data packet. + // + p = pbuf_alloc(PBUF_TRANSPORT, 4, PBUF_RAM); + if(!p) + { + return; + } + + // + // Get a pointer to the data packet. + // + pui8Data = (uint8_t *)p->payload; + + // + // Fill in the packet header. + // + pui8Data[0] = (TFTP_ACK >> 8) & 0xff; + pui8Data[1] = TFTP_ACK & 0xff; + pui8Data[2] = (psTFTP->ui32BlockNum >> 8) & 0xff; + pui8Data[3] = psTFTP->ui32BlockNum & 0xff; + + // + // Send the data packet. + // + udp_send(psTFTP->psPCB, p); + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Handles datagrams received from the TFTP data connection. +// +//***************************************************************************** +static void +TFTPDataRecv(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + uint8_t *pui8Data; + uint32_t ui32Block; + struct pbuf *pBuf; + tTFTPConnection *psTFTP; + tTFTPError eRetcode; + + // + // Initialize our return code. + // + eRetcode = TFTP_ERR_NOT_DEFINED; + + // + // Get a pointer to the connection instance data. + // + psTFTP = (tTFTPConnection *)arg; + + // + // Get a pointer to the TFTP packet. + // + pui8Data = (uint8_t *)(p->payload); + + // + // If this is an ACK packet, send back the next block to satisfy an + // ongoing GET (read) request. + // + if((pui8Data[0] == ((TFTP_ACK >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_ACK & 0xff))) + { + // + // Extract the block number from the acknowledge. + // + ui32Block = (pui8Data[2] << 8) + pui8Data[3]; + + // + // DEBUG ONLY! + // + UARTprintf("ACK %d\n", ui32Block); + + // + // See if there is more data to be sent. Note that we need the "<=" + // here to ensure that we send back a zero length packet in the case + // that the file is a multiple of 512 bytes (in other words, the last + // packet of valid data was a full packet). + // + if((ui32Block * TFTP_BLOCK_SIZE) <= psTFTP->ui32DataRemaining) + { + // + // Send the next block of the file. + // + psTFTP->ui32BlockNum = ui32Block + 1; + TFTPDataSend(psTFTP); + } + else + { + // + // The transfer is complete, so close the data connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + else + { + // + // If this is a DATA packet, get the payload and write it to the + // appropriate location in the serial flash. + // + if((pui8Data[0] == ((TFTP_DATA >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_DATA & 0xff))) + { + // + // This is a data packet. Extract the block number from the packet + // and set the offset within the block (stored in + // ui32DataRemaining) to zero. + // + psTFTP->ui32BlockNum = (pui8Data[2] << 8) + pui8Data[3]; + psTFTP->ui32DataRemaining = 0; + psTFTP->ui32DataLength = p->len - 4; + + // + // Pass the data back to the application for handling. Remember + // that the data may be stored across several pbufs in the chain. + // We can't assume it is in a contiguous block. + // + psTFTP->pui8Data = pui8Data + 4; + pBuf = p; + + // + // Keep writing until we run out of data. + // + while(pBuf) + { + // + // Pass this block to the application. + // + eRetcode = psTFTP->pfnPutData(psTFTP); + + // + // Was the data written successfully? + // + if(eRetcode != TFTP_OK) + { + // + // No - drop out. + // + break; + } + + // + // Update the offset so that it is correct for the next pbuf + // in the chain. + // + psTFTP->ui32DataRemaining += psTFTP->ui32DataLength; + + // + // Move to the next pbuf in the chain + // + pBuf = pBuf->next; + if(pBuf) + { + psTFTP->pui8Data = pBuf->payload; + psTFTP->ui32DataLength = pBuf->len; + } + } + + // + // If we get here and there was an error reported, pass the error + // back to the TFTP client. + // + if(psTFTP && (eRetcode != TFTP_OK)) + { + // + // Send the error code to the client. + // + TFTPErrorSend(psTFTP, eRetcode); + + // + // Close the connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + else + { + // + // Acknowledge this block. + // + TFTPDataAck(psTFTP); + + // + // Is the transfer finished? + // + if(p->tot_len < (TFTP_BLOCK_SIZE + 4)) + { + // + // We got a short packet so the transfer is complete. + // Close the connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + } + else + { + // + // Is the client reporting an error? + // + if((pui8Data[0] == ((TFTP_ERROR >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_ERROR & 0xff))) + { + // + // Yes - we got an error so close the connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + } + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Parses the request string to determine the transfer mode, netascii, octet or +// mail, for this request. +// +//***************************************************************************** +static tTFTPMode +TFTPModeGet(uint8_t *pui8Request, uint32_t ui32Len) +{ + uint32_t ui32Loop, ui32Max; + + // + // Look for the first zero after the start of the filename string (skipping + // the first two bytes of the request packet). + // + for(ui32Loop = 2; ui32Loop < ui32Len; ui32Loop++) + { + if(pui8Request[ui32Loop] == (uint8_t)0) + { + break; + } + } + + // + // Skip past the zero. + // + ui32Loop++; + + // + // Did we run off the end of the string? + // + if(ui32Loop >= ui32Len) + { + // + // Yes - this appears to be an invalid request. + // + return(TFTP_MODE_INVALID); + } + + // + // How much data do we have left to look for the mode string? + // + ui32Max = ui32Len - ui32Loop; + + // + // Now determine which of the modes this request asks for. Is it ASCII? + // + if(!ustrncasecmp("netascii", (char *)&pui8Request[ui32Loop], ui32Max)) + { + // + // This is an ASCII file transfer. + // + return(TFTP_MODE_NETASCII); + } + + // + // Binary transfer? + // + if(!ustrncasecmp("octet", (char *)&pui8Request[ui32Loop], ui32Max)) + { + // + // This is a binary file transfer. + // + return(TFTP_MODE_OCTET); + } + + // + // All other strings are invalid or obsolete ("mail" for example). + // + return(TFTP_MODE_INVALID); +} + +//***************************************************************************** +// +// Handles datagrams received on the TFTP server port. +// +//***************************************************************************** +static void +TFTPRecv(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, + u16_t port) +{ + uint8_t *pui8Data; + bool bGetRequest; + tTFTPMode eMode; + tTFTPError eRetcode; + tTFTPConnection *psTFTP; + + // + // Get a pointer to the TFTP packet. + // + pui8Data = (uint8_t *)(p->payload); + + // + // Is this a read (GET) request? + // + if((pui8Data[0] == ((TFTP_RRQ >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_RRQ & 0xff))) + { + // + // Yes - remember that this is a GET request. + // + bGetRequest = true; + } + + // + // Is this a write (PUT) request? + // + else if((pui8Data[0] == ((TFTP_WRQ >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_WRQ & 0xff))) + { + // + // Yes - remember that this is a PUT request. + // + bGetRequest = false; + } + else + { + // + // The request is neither GET nor PUT so just ignore it. + // + pbuf_free(p); + return; + } + + // + // What is the mode for this request? + // + eMode = TFTPModeGet(pui8Data, p->len); + + // + // Was the transfer mode valid? + // + if(eMode != TFTP_MODE_INVALID) + { + // + // The transfer mode is valid so allocate a new connection instance + // and pass this to the client to have it tell us how to proceed. + // + psTFTP = (tTFTPConnection *)mem_malloc(sizeof(tTFTPConnection)); + + // + // If we can't allocate the connection instance, all we can do is + // ignore the datagram. + // + if(!psTFTP) + { + pbuf_free(p); + return; + } + + // + // Clear out the structure and initialize a few fields. + // + memset(psTFTP, 0, sizeof(tTFTPConnection)); + psTFTP->pcErrorString = "Unknown error"; + + // + // Yes - create the new UDP connection and set things up to + // handle this request. + // + psTFTP->psPCB = udp_new(); + udp_recv(psTFTP->psPCB, TFTPDataRecv, psTFTP); + udp_connect(psTFTP->psPCB, addr, port); + + // + // Ask the application if it wants to proceed with this request. + // + eRetcode = g_pfnRequest(psTFTP, bGetRequest, (int8_t *)(pui8Data + 2), + eMode); + + // + // Does it want to go on? + // + if(eRetcode == TFTP_OK) + { + // + // Yes - what kind of request is this? + // + if(bGetRequest) + { + // + // For a GET request, we send back the first block of data. + // + psTFTP->ui32BlockNum = 1; + TFTPDataSend(psTFTP); + } + else + { + // + // For a PUT request, we acknowledge the transfer which tells + // the TFTP client that it can start sending us data. + // + psTFTP->ui32BlockNum = 0; + TFTPDataAck(psTFTP); + } + } + else + { + // + // The application indicated that there was an error. Send the + // error report and close the connection. + // + TFTPErrorSend(psTFTP, eRetcode); + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +//! Initializes the TFTP server module. +//! +//! \param pfnRequest - A pointer to the function which the server will call +//! whenever a new incoming TFTP request is received. This function must +//! determine whether the request can be handled and return a value telling the +//! server whether to continue processing the request or ignore it. +//! +//! This function initializes the lwIP TFTP server and starts listening for +//! incoming requests from clients. It must be called after the network stack +//! is initialized using a call to lwIPInit(). +//! +//! \return None. +// +//***************************************************************************** +void +TFTPInit(tTFTPRequest pfnRequest) +{ + void *pcb; + + // + // Remember the application's notification callback. + // + g_pfnRequest = pfnRequest; + + // + // Start listening for incoming TFTP requests. + // + pcb = udp_new(); + udp_recv(pcb, TFTPRecv, NULL); + udp_bind(pcb, IP_ADDR_ANY, TFTP_PORT); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/tftp.h b/utils/tftp.h new file mode 100644 index 0000000..b02b8ea --- /dev/null +++ b/utils/tftp.h @@ -0,0 +1,215 @@ +//***************************************************************************** +// +// tftp.h - Public function prototypes and globals related to the lwIP TFTP +// server. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __TFTP_H__ +#define __TFTP_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup tftp_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! TFTP error codes. Note that this enum is mapped so that all positive +//! values match the TFTP protocol-defined error codes. +// +//***************************************************************************** +typedef enum +{ + TFTP_OK = -1, + TFTP_ERR_NOT_DEFINED = 0, + TFTP_FILE_NOT_FOUND = 1, + TFTP_ACCESS_VIOLATION = 2, + TFTP_DISK_FULL = 3, + TFTP_ILLEGAL_OP = 4, + TFTP_UNKNOWN_TID = 5, + TFTP_FILE_EXISTS = 6, + TFTP_NO_SUCH_USER = 7 +} +tTFTPError; + +//***************************************************************************** +// +//! TFTP file transfer modes. This enum contains members defining ASCII +//! text transfer mode (TFTP_MODE_NETASCII), binary transfer mode +//! (TFTP_MODE_OCTET) and a marker for an invalid mode (TFTP_MODE_INVALID). +// +//***************************************************************************** +typedef enum +{ + TFTP_MODE_NETASCII, + TFTP_MODE_OCTET, + TFTP_MODE_INVALID +} +tTFTPMode; + +//***************************************************************************** +// +//! Data transfer under TFTP is performed using fixed-size blocks. This label +//! defines the size of a block of TFTP data. +// +//***************************************************************************** +#define TFTP_BLOCK_SIZE 512 + +//***************************************************************************** +// +// Callback function prototypes passed to TFTPInit. These functions receive +// notification of incoming GET and PUT requests, allowing the client to decide +// whether to accept the request or not. +// +//***************************************************************************** +struct _tTFTPConnection; + +typedef tTFTPError (*tTFTPRequest)(struct _tTFTPConnection *psTFTP, bool bGet, + int8_t *pui8FileName, tTFTPMode eMode); +typedef tTFTPError (*tTFTPTransfer)(struct _tTFTPConnection *psTFTP); +typedef void (*tTFTPClose)(struct _tTFTPConnection *psTFTP); + +//***************************************************************************** +// +//! The TFTP connection control structure. This is passed to a client on all +//! callbacks relating to a given TFTP connection. Depending upon the +//! callback, the client may need to fill in values to various fields or use +//! field values to determine where to transfer data from or to. +// +//***************************************************************************** +typedef struct _tTFTPConnection +{ + // + //! Pointer to the start of the buffer into which GET data should be copied + //! or from which PUT data should be read. + // + uint8_t *pui8Data; + + // + //! The length of the data requested in response to a single pfnGetData + //! callback or the size of the received data for a pfnPutData callback. + // + uint32_t ui32DataLength; + + // + //! Count of remaining bytes to send during a GET request or the byte + //! offset within a block during a PUT request. The application must set + //! this field to the size of the requested file during the tTFTPRequest + // callback if a GET request is to be accepted. + // + uint32_t ui32DataRemaining; + + // + //! Application function which is called whenever more data is required to + //! satisfy a GET request. The function must copy ui32DataLength bytes + //! into the buffer pointed to by pui8Data. + // + tTFTPTransfer pfnGetData; + + // + //! Application function which is called whenever a packet of file data is + //! received during a PUT request. The function must save the data to the + //! target file using ui32BlockNum and ui32DataRemaining to indicate the + //! position of the data in the file, and return an appropriate error code. + //! Note that several calls to this function may be made for a given + //! received TFTP block since the underlying networking stack may have + //! split the TFTP packet between several packets and a callback is made + //! for each of these. This avoids the need for a 512 byte buffer. The + //! ui32DataRemaining is used in these cases to indicate the offset of the + //! data within the current block. + // + tTFTPTransfer pfnPutData; + + // + //! Application function which is called when the TFTP connection is to + //! be closed. The function should tidy up and free any resources + //! associated with the connection prior to returning. + // + tTFTPClose pfnClose; + + // + //! This field may be used by the client to store an application-specific + //! pointer that will be accessible on all callbacks from the TFTP module + //! relating to this connection. + // + uint8_t *pui8User; + + // + //! Pointer to an error string which the client must fill in if reporting + //! an error. This string will be sent to the TFTP client in any case + //! where pfnPutData or pfnGetData return a value other than TFTP_OK. + // + char *pcErrorString; + + // + //! A pointer to the underlying UDP connection. Applications must not + //! modify this field. + // + struct udp_pcb *psPCB; + + // + //! The current block number for an ongoing TFTP transfer. Applications + //! may read this value to determine which data to return on a pfnGetData + //! callback or where to write incoming data on a pfnPutData callback but + //! must not modify it. + // + uint32_t ui32BlockNum; +} +tTFTPConnection; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Public function prototypes. +// +//***************************************************************************** +extern void TFTPInit(tTFTPRequest pfnRequest); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __TFTP_H__ diff --git a/utils/uartstdio.c b/utils/uartstdio.c new file mode 100644 index 0000000..8ec2eaa --- /dev/null +++ b/utils/uartstdio.c @@ -0,0 +1,1720 @@ +//***************************************************************************** +// +// uartstdio.c - Utility driver to provide simple UART console functions. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_uart.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/uart.h" +#include "utils/uartstdio.h" + +//***************************************************************************** +// +//! \addtogroup uartstdio_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// If buffered mode is defined, set aside RX and TX buffers and read/write +// pointers to control them. +// +//***************************************************************************** +#ifdef UART_BUFFERED + +//***************************************************************************** +// +// This global controls whether or not we are echoing characters back to the +// transmitter. By default, echo is enabled but if using this module as a +// convenient method of implementing a buffered serial interface over which +// you will be running an application protocol, you are likely to want to +// disable echo by calling UARTEchoSet(false). +// +//***************************************************************************** +static bool g_bDisableEcho; + +//***************************************************************************** +// +// Output ring buffer. Buffer is full if g_ui32UARTTxReadIndex is one ahead of +// g_ui32UARTTxWriteIndex. Buffer is empty if the two indices are the same. +// +//***************************************************************************** +static unsigned char g_pcUARTTxBuffer[UART_TX_BUFFER_SIZE]; +static volatile uint32_t g_ui32UARTTxWriteIndex = 0; +static volatile uint32_t g_ui32UARTTxReadIndex = 0; + +//***************************************************************************** +// +// Input ring buffer. Buffer is full if g_ui32UARTTxReadIndex is one ahead of +// g_ui32UARTTxWriteIndex. Buffer is empty if the two indices are the same. +// +//***************************************************************************** +static unsigned char g_pcUARTRxBuffer[UART_RX_BUFFER_SIZE]; +static volatile uint32_t g_ui32UARTRxWriteIndex = 0; +static volatile uint32_t g_ui32UARTRxReadIndex = 0; + +//***************************************************************************** +// +// Macros to determine number of free and used bytes in the transmit buffer. +// +//***************************************************************************** +#define TX_BUFFER_USED (GetBufferCount(&g_ui32UARTTxReadIndex, \ + &g_ui32UARTTxWriteIndex, \ + UART_TX_BUFFER_SIZE)) +#define TX_BUFFER_FREE (UART_TX_BUFFER_SIZE - TX_BUFFER_USED) +#define TX_BUFFER_EMPTY (IsBufferEmpty(&g_ui32UARTTxReadIndex, \ + &g_ui32UARTTxWriteIndex)) +#define TX_BUFFER_FULL (IsBufferFull(&g_ui32UARTTxReadIndex, \ + &g_ui32UARTTxWriteIndex, \ + UART_TX_BUFFER_SIZE)) +#define ADVANCE_TX_BUFFER_INDEX(Index) \ + (Index) = ((Index) + 1) % UART_TX_BUFFER_SIZE + +//***************************************************************************** +// +// Macros to determine number of free and used bytes in the receive buffer. +// +//***************************************************************************** +#define RX_BUFFER_USED (GetBufferCount(&g_ui32UARTRxReadIndex, \ + &g_ui32UARTRxWriteIndex, \ + UART_RX_BUFFER_SIZE)) +#define RX_BUFFER_FREE (UART_RX_BUFFER_SIZE - RX_BUFFER_USED) +#define RX_BUFFER_EMPTY (IsBufferEmpty(&g_ui32UARTRxReadIndex, \ + &g_ui32UARTRxWriteIndex)) +#define RX_BUFFER_FULL (IsBufferFull(&g_ui32UARTRxReadIndex, \ + &g_ui32UARTRxWriteIndex, \ + UART_RX_BUFFER_SIZE)) +#define ADVANCE_RX_BUFFER_INDEX(Index) \ + (Index) = ((Index) + 1) % UART_RX_BUFFER_SIZE +#endif + +//***************************************************************************** +// +// The base address of the chosen UART. +// +//***************************************************************************** +static uint32_t g_ui32Base = 0; + +//***************************************************************************** +// +// A mapping from an integer between 0 and 15 to its ASCII character +// equivalent. +// +//***************************************************************************** +static const char * const g_pcHex = "0123456789abcdef"; + +//***************************************************************************** +// +// The list of possible base addresses for the console UART. +// +//***************************************************************************** +static const uint32_t g_ui32UARTBase[3] = +{ + UART0_BASE, UART1_BASE, UART2_BASE +}; + +#ifdef UART_BUFFERED +//***************************************************************************** +// +// The list of possible interrupts for the console UART. +// +//***************************************************************************** +static const uint32_t g_ui32UARTInt[3] = +{ + INT_UART0, INT_UART1, INT_UART2 +}; + +//***************************************************************************** +// +// The port number in use. +// +//***************************************************************************** +static uint32_t g_ui32PortNum; +#endif + +//***************************************************************************** +// +// The list of UART peripherals. +// +//***************************************************************************** +static const uint32_t g_ui32UARTPeriph[3] = +{ + SYSCTL_PERIPH_UART0, SYSCTL_PERIPH_UART1, SYSCTL_PERIPH_UART2 +}; + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is full or not. +//! +//! \param pui32Read points to the read index for the buffer. +//! \param pui32Write points to the write index for the buffer. +//! \param ui32Size is the size of the buffer in bytes. +//! +//! This function is used to determine whether or not a given ring buffer is +//! full. The structure of the code is specifically to ensure that we do not +//! see warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is full or \b false otherwise. +// +//***************************************************************************** +#ifdef UART_BUFFERED +static bool +IsBufferFull(volatile uint32_t *pui32Read, + volatile uint32_t *pui32Write, uint32_t ui32Size) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + ui32Write = *pui32Write; + ui32Read = *pui32Read; + + return((((ui32Write + 1) % ui32Size) == ui32Read) ? true : false); +} +#endif + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is empty or not. +//! +//! \param pui32Read points to the read index for the buffer. +//! \param pui32Write points to the write index for the buffer. +//! +//! This function is used to determine whether or not a given ring buffer is +//! empty. The structure of the code is specifically to ensure that we do not +//! see warnings from the compiler related to the order of volatile accesses +//! being undefined. +//! +//! \return Returns \b true if the buffer is empty or \b false otherwise. +// +//***************************************************************************** +#ifdef UART_BUFFERED +static bool +IsBufferEmpty(volatile uint32_t *pui32Read, + volatile uint32_t *pui32Write) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + ui32Write = *pui32Write; + ui32Read = *pui32Read; + + return((ui32Write == ui32Read) ? true : false); +} +#endif + +//***************************************************************************** +// +//! Determines the number of bytes of data contained in a ring buffer. +//! +//! \param pui32Read points to the read index for the buffer. +//! \param pui32Write points to the write index for the buffer. +//! \param ui32Size is the size of the buffer in bytes. +//! +//! This function is used to determine how many bytes of data a given ring +//! buffer currently contains. The structure of the code is specifically to +//! ensure that we do not see warnings from the compiler related to the order +//! of volatile accesses being undefined. +//! +//! \return Returns the number of bytes of data currently in the buffer. +// +//***************************************************************************** +#ifdef UART_BUFFERED +static uint32_t +GetBufferCount(volatile uint32_t *pui32Read, + volatile uint32_t *pui32Write, uint32_t ui32Size) +{ + uint32_t ui32Write; + uint32_t ui32Read; + + ui32Write = *pui32Write; + ui32Read = *pui32Read; + + return((ui32Write >= ui32Read) ? (ui32Write - ui32Read) : + (ui32Size - (ui32Read - ui32Write))); +} +#endif + +//***************************************************************************** +// +// Take as many bytes from the transmit buffer as we have space for and move +// them into the UART transmit FIFO. +// +//***************************************************************************** +#ifdef UART_BUFFERED +static void +UARTPrimeTransmit(uint32_t ui32Base) +{ + // + // Do we have any data to transmit? + // + if(!TX_BUFFER_EMPTY) + { + // + // Disable the UART interrupt. If we don't do this there is a race + // condition which can cause the read index to be corrupted. + // + MAP_IntDisable(g_ui32UARTInt[g_ui32PortNum]); + + // + // Yes - take some characters out of the transmit buffer and feed + // them to the UART transmit FIFO. + // + while(MAP_UARTSpaceAvail(ui32Base) && !TX_BUFFER_EMPTY) + { + MAP_UARTCharPutNonBlocking(ui32Base, + g_pcUARTTxBuffer[g_ui32UARTTxReadIndex]); + ADVANCE_TX_BUFFER_INDEX(g_ui32UARTTxReadIndex); + } + + // + // Reenable the UART interrupt. + // + MAP_IntEnable(g_ui32UARTInt[g_ui32PortNum]); + } +} +#endif + +//***************************************************************************** +// +//! Configures the UART console. +//! +//! \param ui32PortNum is the number of UART port to use for the serial console +//! (0-2) +//! \param ui32Baud is the bit rate that the UART is to be configured to use. +//! \param ui32SrcClock is the frequency of the source clock for the UART +//! module. +//! +//! This function will configure the specified serial port to be used as a +//! serial console. The serial parameters are set to the baud rate +//! specified by the \e ui32Baud parameter and use 8 bit, no parity, and 1 stop +//! bit. +//! +//! This function must be called prior to using any of the other UART console +//! functions: UARTprintf() or UARTgets(). This function assumes that the +//! caller has previously configured the relevant UART pins for operation as a +//! UART rather than as GPIOs. +//! +//! \return None. +// +//***************************************************************************** +void +UARTStdioConfig(uint32_t ui32PortNum, uint32_t ui32Baud, uint32_t ui32SrcClock) +{ + // + // Check the arguments. + // + ASSERT((ui32PortNum == 0) || (ui32PortNum == 1) || + (ui32PortNum == 2)); + +#ifdef UART_BUFFERED + // + // In buffered mode, we only allow a single instance to be opened. + // + ASSERT(g_ui32Base == 0); +#endif + + // + // Check to make sure the UART peripheral is present. + // + if(!MAP_SysCtlPeripheralPresent(g_ui32UARTPeriph[ui32PortNum])) + { + return; + } + + // + // Select the base address of the UART. + // + g_ui32Base = g_ui32UARTBase[ui32PortNum]; + + // + // Enable the UART peripheral for use. + // + MAP_SysCtlPeripheralEnable(g_ui32UARTPeriph[ui32PortNum]); + + // + // Configure the UART for 115200, n, 8, 1 + // + MAP_UARTConfigSetExpClk(g_ui32Base, ui32SrcClock, ui32Baud, + (UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE | + UART_CONFIG_WLEN_8)); + +#ifdef UART_BUFFERED + // + // Set the UART to interrupt whenever the TX FIFO is almost empty or + // when any character is received. + // + MAP_UARTFIFOLevelSet(g_ui32Base, UART_FIFO_TX1_8, UART_FIFO_RX1_8); + + // + // Flush both the buffers. + // + UARTFlushRx(); + UARTFlushTx(true); + + // + // Remember which interrupt we are dealing with. + // + g_ui32PortNum = ui32PortNum; + + // + // We are configured for buffered output so enable the master interrupt + // for this UART and the receive interrupts. We don't actually enable the + // transmit interrupt in the UART itself until some data has been placed + // in the transmit buffer. + // + MAP_UARTIntDisable(g_ui32Base, 0xFFFFFFFF); + MAP_UARTIntEnable(g_ui32Base, UART_INT_RX | UART_INT_RT); + MAP_IntEnable(g_ui32UARTInt[ui32PortNum]); +#endif + + // + // Enable the UART operation. + // + MAP_UARTEnable(g_ui32Base); +} + +//***************************************************************************** +// +//! Writes a string of characters to the UART output. +//! +//! \param pcBuf points to a buffer containing the string to transmit. +//! \param ui32Len is the length of the string to transmit. +//! +//! This function will transmit the string to the UART output. The number of +//! characters transmitted is determined by the \e ui32Len parameter. This +//! function does no interpretation or translation of any characters. Since +//! the output is sent to a UART, any LF (/n) characters encountered will be +//! replaced with a CRLF pair. +//! +//! Besides using the \e ui32Len parameter to stop transmitting the string, if +//! a null character (0) is encountered, then no more characters will be +//! transmitted and the function will return. +//! +//! In non-buffered mode, this function is blocking and will not return until +//! all the characters have been written to the output FIFO. In buffered mode, +//! the characters are written to the UART transmit buffer and the call returns +//! immediately. If insufficient space remains in the transmit buffer, +//! additional characters are discarded. +//! +//! \return Returns the count of characters written. +// +//***************************************************************************** +int +UARTwrite(const char *pcBuf, uint32_t ui32Len) +{ +#ifdef UART_BUFFERED + unsigned int uIdx; + + // + // Check for valid arguments. + // + ASSERT(pcBuf != 0); + ASSERT(g_ui32Base != 0); + + // + // Send the characters + // + for(uIdx = 0; uIdx < ui32Len; uIdx++) + { + // + // If the character to the UART is \n, then add a \r before it so that + // \n is translated to \n\r in the output. + // + if(pcBuf[uIdx] == '\n') + { + if(!TX_BUFFER_FULL) + { + g_pcUARTTxBuffer[g_ui32UARTTxWriteIndex] = '\r'; + ADVANCE_TX_BUFFER_INDEX(g_ui32UARTTxWriteIndex); + } + else + { + // + // Buffer is full - discard remaining characters and return. + // + break; + } + } + + // + // Send the character to the UART output. + // + if(!TX_BUFFER_FULL) + { + g_pcUARTTxBuffer[g_ui32UARTTxWriteIndex] = pcBuf[uIdx]; + ADVANCE_TX_BUFFER_INDEX(g_ui32UARTTxWriteIndex); + } + else + { + // + // Buffer is full - discard remaining characters and return. + // + break; + } + } + + // + // If we have anything in the buffer, make sure that the UART is set + // up to transmit it. + // + if(!TX_BUFFER_EMPTY) + { + UARTPrimeTransmit(g_ui32Base); + MAP_UARTIntEnable(g_ui32Base, UART_INT_TX); + } + + // + // Return the number of characters written. + // + return(uIdx); +#else + unsigned int uIdx; + + // + // Check for valid UART base address, and valid arguments. + // + ASSERT(g_ui32Base != 0); + ASSERT(pcBuf != 0); + + // + // Send the characters + // + for(uIdx = 0; uIdx < ui32Len; uIdx++) + { + // + // If the character to the UART is \n, then add a \r before it so that + // \n is translated to \n\r in the output. + // + if(pcBuf[uIdx] == '\n') + { + MAP_UARTCharPut(g_ui32Base, '\r'); + } + + // + // Send the character to the UART output. + // + MAP_UARTCharPut(g_ui32Base, pcBuf[uIdx]); + } + + // + // Return the number of characters written. + // + return(uIdx); +#endif +} + +//***************************************************************************** +// +//! A simple UART based get string function, with some line processing. +//! +//! \param pcBuf points to a buffer for the incoming string from the UART. +//! \param ui32Len is the length of the buffer for storage of the string, +//! including the trailing 0. +//! +//! This function will receive a string from the UART input and store the +//! characters in the buffer pointed to by \e pcBuf. The characters will +//! continue to be stored until a termination character is received. The +//! termination characters are CR, LF, or ESC. A CRLF pair is treated as a +//! single termination character. The termination characters are not stored in +//! the string. The string will be terminated with a 0 and the function will +//! return. +//! +//! In both buffered and unbuffered modes, this function will block until +//! a termination character is received. If non-blocking operation is required +//! in buffered mode, a call to UARTPeek() may be made to determine whether +//! a termination character already exists in the receive buffer prior to +//! calling UARTgets(). +//! +//! Since the string will be null terminated, the user must ensure that the +//! buffer is sized to allow for the additional null character. +//! +//! \return Returns the count of characters that were stored, not including +//! the trailing 0. +// +//***************************************************************************** +int +UARTgets(char *pcBuf, uint32_t ui32Len) +{ +#ifdef UART_BUFFERED + uint32_t ui32Count = 0; + int8_t cChar; + + // + // Check the arguments. + // + ASSERT(pcBuf != 0); + ASSERT(ui32Len != 0); + ASSERT(g_ui32Base != 0); + + // + // Adjust the length back by 1 to leave space for the trailing + // null terminator. + // + ui32Len--; + + // + // Process characters until a newline is received. + // + while(1) + { + // + // Read the next character from the receive buffer. + // + if(!RX_BUFFER_EMPTY) + { + cChar = g_pcUARTRxBuffer[g_ui32UARTRxReadIndex]; + ADVANCE_RX_BUFFER_INDEX(g_ui32UARTRxReadIndex); + + // + // See if a newline or escape character was received. + // + if((cChar == '\r') || (cChar == '\n') || (cChar == 0x1b)) + { + // + // Stop processing the input and end the line. + // + break; + } + + // + // Process the received character as long as we are not at the end + // of the buffer. If the end of the buffer has been reached then + // all additional characters are ignored until a newline is + // received. + // + if(ui32Count < ui32Len) + { + // + // Store the character in the caller supplied buffer. + // + pcBuf[ui32Count] = cChar; + + // + // Increment the count of characters received. + // + ui32Count++; + } + } + } + + // + // Add a null termination to the string. + // + pcBuf[ui32Count] = 0; + + // + // Return the count of int8_ts in the buffer, not counting the trailing 0. + // + return(ui32Count); +#else + uint32_t ui32Count = 0; + int8_t cChar; + static int8_t bLastWasCR = 0; + + // + // Check the arguments. + // + ASSERT(pcBuf != 0); + ASSERT(ui32Len != 0); + ASSERT(g_ui32Base != 0); + + // + // Adjust the length back by 1 to leave space for the trailing + // null terminator. + // + ui32Len--; + + // + // Process characters until a newline is received. + // + while(1) + { + // + // Read the next character from the console. + // + cChar = MAP_UARTCharGet(g_ui32Base); + + // + // See if the backspace key was pressed. + // + if(cChar == '\b') + { + // + // If there are any characters already in the buffer, then delete + // the last. + // + if(ui32Count) + { + // + // Rub out the previous character. + // + UARTwrite("\b \b", 3); + + // + // Decrement the number of characters in the buffer. + // + ui32Count--; + } + + // + // Skip ahead to read the next character. + // + continue; + } + + // + // If this character is LF and last was CR, then just gobble up the + // character because the EOL processing was taken care of with the CR. + // + if((cChar == '\n') && bLastWasCR) + { + bLastWasCR = 0; + continue; + } + + // + // See if a newline or escape character was received. + // + if((cChar == '\r') || (cChar == '\n') || (cChar == 0x1b)) + { + // + // If the character is a CR, then it may be followed by a LF which + // should be paired with the CR. So remember that a CR was + // received. + // + if(cChar == '\r') + { + bLastWasCR = 1; + } + + // + // Stop processing the input and end the line. + // + break; + } + + // + // Process the received character as long as we are not at the end of + // the buffer. If the end of the buffer has been reached then all + // additional characters are ignored until a newline is received. + // + if(ui32Count < ui32Len) + { + // + // Store the character in the caller supplied buffer. + // + pcBuf[ui32Count] = cChar; + + // + // Increment the count of characters received. + // + ui32Count++; + + // + // Reflect the character back to the user. + // + MAP_UARTCharPut(g_ui32Base, cChar); + } + } + + // + // Add a null termination to the string. + // + pcBuf[ui32Count] = 0; + + // + // Send a CRLF pair to the terminal to end the line. + // + UARTwrite("\r\n", 2); + + // + // Return the count of int8_ts in the buffer, not counting the trailing 0. + // + return(ui32Count); +#endif +} + +//***************************************************************************** +// +//! Read a single character from the UART, blocking if necessary. +//! +//! This function will receive a single character from the UART and store it at +//! the supplied address. +//! +//! In both buffered and unbuffered modes, this function will block until a +//! character is received. If non-blocking operation is required in buffered +//! mode, a call to UARTRxAvail() may be made to determine whether any +//! characters are currently available for reading. +//! +//! \return Returns the character read. +// +//***************************************************************************** +unsigned char +UARTgetc(void) +{ +#ifdef UART_BUFFERED + unsigned char cChar; + + // + // Wait for a character to be received. + // + while(RX_BUFFER_EMPTY) + { + // + // Block waiting for a character to be received (if the buffer is + // currently empty). + // + } + + // + // Read a character from the buffer. + // + cChar = g_pcUARTRxBuffer[g_ui32UARTRxReadIndex]; + ADVANCE_RX_BUFFER_INDEX(g_ui32UARTRxReadIndex); + + // + // Return the character to the caller. + // + return(cChar); +#else + // + // Block until a character is received by the UART then return it to + // the caller. + // + return(MAP_UARTCharGet(g_ui32Base)); +#endif +} + +//***************************************************************************** +// +//! A simple UART based vprintf function supporting \%c, \%d, \%p, \%s, \%u, +//! \%x, and \%X. +//! +//! \param pcString is the format string. +//! \param vaArgP is a variable argument list pointer whose content will depend +//! upon the format string passed in \e pcString. +//! +//! This function is very similar to the C library vprintf() function. +//! All of its output will be sent to the UART. Only the following formatting +//! characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%s, \%d, \%i, \%u, \%p, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeroes instead of spaces. +//! +//! The type of the arguments in the variable arguments list must match the +//! requirements of the format string. For example, if an integer was passed +//! where a string was expected, an error of some kind will most likely occur. +//! +//! \return None. +// +//***************************************************************************** +void +UARTvprintf(const char *pcString, va_list vaArgP) +{ + uint32_t ui32Idx, ui32Value, ui32Pos, ui32Count, ui32Base, ui32Neg; + char *pcStr, pcBuf[16], cFill; + + // + // Check the arguments. + // + ASSERT(pcString != 0); + + // + // Loop while there are more characters in the string. + // + while(*pcString) + { + // + // Find the first non-% character, or the end of the string. + // + for(ui32Idx = 0; + (pcString[ui32Idx] != '%') && (pcString[ui32Idx] != '\0'); + ui32Idx++) + { + } + + // + // Write this portion of the string. + // + UARTwrite(pcString, ui32Idx); + + // + // Skip the portion of the string that was written. + // + pcString += ui32Idx; + + // + // See if the next character is a %. + // + if(*pcString == '%') + { + // + // Skip the %. + // + pcString++; + + // + // Set the digit count to zero, and the fill character to space + // (in other words, to the defaults). + // + ui32Count = 0; + cFill = ' '; + + // + // It may be necessary to get back here to process more characters. + // Goto's aren't pretty, but effective. I feel extremely dirty for + // using not one but two of the beasts. + // +again: + + // + // Determine how to handle the next character. + // + switch(*pcString++) + { + // + // Handle the digit characters. + // + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + // + // If this is a zero, and it is the first digit, then the + // fill character is a zero instead of a space. + // + if((pcString[-1] == '0') && (ui32Count == 0)) + { + cFill = '0'; + } + + // + // Update the digit count. + // + ui32Count *= 10; + ui32Count += pcString[-1] - '0'; + + // + // Get the next character. + // + goto again; + } + + // + // Handle the %c command. + // + case 'c': + { + // + // Get the value from the varargs. + // + ui32Value = va_arg(vaArgP, uint32_t); + + // + // Print out the character. + // + UARTwrite((char *)&ui32Value, 1); + + // + // This command has been handled. + // + break; + } + + // + // Handle the %d and %i commands. + // + case 'd': + case 'i': + { + // + // Get the value from the varargs. + // + ui32Value = va_arg(vaArgP, uint32_t); + + // + // Reset the buffer position. + // + ui32Pos = 0; + + // + // If the value is negative, make it positive and indicate + // that a minus sign is needed. + // + if((int32_t)ui32Value < 0) + { + // + // Make the value positive. + // + ui32Value = -(int32_t)ui32Value; + + // + // Indicate that the value is negative. + // + ui32Neg = 1; + } + else + { + // + // Indicate that the value is positive so that a minus + // sign isn't inserted. + // + ui32Neg = 0; + } + + // + // Set the base to 10. + // + ui32Base = 10; + + // + // Convert the value to ASCII. + // + goto convert; + } + + // + // Handle the %s command. + // + case 's': + { + // + // Get the string pointer from the varargs. + // + pcStr = va_arg(vaArgP, char *); + + // + // Determine the length of the string. + // + for(ui32Idx = 0; pcStr[ui32Idx] != '\0'; ui32Idx++) + { + } + + // + // Write the string. + // + UARTwrite(pcStr, ui32Idx); + + // + // Write any required padding spaces + // + if(ui32Count > ui32Idx) + { + ui32Count -= ui32Idx; + while(ui32Count--) + { + UARTwrite(" ", 1); + } + } + + // + // This command has been handled. + // + break; + } + + // + // Handle the %u command. + // + case 'u': + { + // + // Get the value from the varargs. + // + ui32Value = va_arg(vaArgP, uint32_t); + + // + // Reset the buffer position. + // + ui32Pos = 0; + + // + // Set the base to 10. + // + ui32Base = 10; + + // + // Indicate that the value is positive so that a minus sign + // isn't inserted. + // + ui32Neg = 0; + + // + // Convert the value to ASCII. + // + goto convert; + } + + // + // Handle the %x and %X commands. Note that they are treated + // identically; in other words, %X will use lower case letters + // for a-f instead of the upper case letters it should use. We + // also alias %p to %x. + // + case 'x': + case 'X': + case 'p': + { + // + // Get the value from the varargs. + // + ui32Value = va_arg(vaArgP, uint32_t); + + // + // Reset the buffer position. + // + ui32Pos = 0; + + // + // Set the base to 16. + // + ui32Base = 16; + + // + // Indicate that the value is positive so that a minus sign + // isn't inserted. + // + ui32Neg = 0; + + // + // Determine the number of digits in the string version of + // the value. + // +convert: + for(ui32Idx = 1; + (((ui32Idx * ui32Base) <= ui32Value) && + (((ui32Idx * ui32Base) / ui32Base) == ui32Idx)); + ui32Idx *= ui32Base, ui32Count--) + { + } + + // + // If the value is negative, reduce the count of padding + // characters needed. + // + if(ui32Neg) + { + ui32Count--; + } + + // + // If the value is negative and the value is padded with + // zeros, then place the minus sign before the padding. + // + if(ui32Neg && (cFill == '0')) + { + // + // Place the minus sign in the output buffer. + // + pcBuf[ui32Pos++] = '-'; + + // + // The minus sign has been placed, so turn off the + // negative flag. + // + ui32Neg = 0; + } + + // + // Provide additional padding at the beginning of the + // string conversion if needed. + // + if((ui32Count > 1) && (ui32Count < 16)) + { + for(ui32Count--; ui32Count; ui32Count--) + { + pcBuf[ui32Pos++] = cFill; + } + } + + // + // If the value is negative, then place the minus sign + // before the number. + // + if(ui32Neg) + { + // + // Place the minus sign in the output buffer. + // + pcBuf[ui32Pos++] = '-'; + } + + // + // Convert the value into a string. + // + for(; ui32Idx; ui32Idx /= ui32Base) + { + pcBuf[ui32Pos++] = + g_pcHex[(ui32Value / ui32Idx) % ui32Base]; + } + + // + // Write the string. + // + UARTwrite(pcBuf, ui32Pos); + + // + // This command has been handled. + // + break; + } + + // + // Handle the %% command. + // + case '%': + { + // + // Simply write a single %. + // + UARTwrite(pcString - 1, 1); + + // + // This command has been handled. + // + break; + } + + // + // Handle all other commands. + // + default: + { + // + // Indicate an error. + // + UARTwrite("ERROR", 5); + + // + // This command has been handled. + // + break; + } + } + } + } +} + +//***************************************************************************** +// +//! A simple UART based printf function supporting \%c, \%d, \%p, \%s, \%u, +//! \%x, and \%X. +//! +//! \param pcString is the format string. +//! \param ... are the optional arguments, which depend on the contents of the +//! format string. +//! +//! This function is very similar to the C library fprintf() function. +//! All of its output will be sent to the UART. Only the following formatting +//! characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%s, \%d, \%i, \%u, \%p, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeroes instead of spaces. +//! +//! The type of the arguments after \e pcString must match the requirements of +//! the format string. For example, if an integer was passed where a string +//! was expected, an error of some kind will most likely occur. +//! +//! \return None. +// +//***************************************************************************** +void +UARTprintf(const char *pcString, ...) +{ + va_list vaArgP; + + // + // Start the varargs processing. + // + va_start(vaArgP, pcString); + + UARTvprintf(pcString, vaArgP); + + // + // We're finished with the varargs now. + // + va_end(vaArgP); +} + +//***************************************************************************** +// +//! Returns the number of bytes available in the receive buffer. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to determine the number +//! of bytes of data currently available in the receive buffer. +//! +//! \return Returns the number of available bytes. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +int +UARTRxBytesAvail(void) +{ + return(RX_BUFFER_USED); +} +#endif + +#if defined(UART_BUFFERED) || defined(DOXYGEN) +//***************************************************************************** +// +//! Returns the number of bytes free in the transmit buffer. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to determine the amount +//! of space currently available in the transmit buffer. +//! +//! \return Returns the number of free bytes. +// +//***************************************************************************** +int +UARTTxBytesFree(void) +{ + return(TX_BUFFER_FREE); +} +#endif + +//***************************************************************************** +// +//! Looks ahead in the receive buffer for a particular character. +//! +//! \param ucChar is the character that is to be searched for. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to look ahead in the +//! receive buffer for a particular character and report its position if found. +//! It is typically used to determine whether a complete line of user input is +//! available, in which case ucChar should be set to CR ('\\r') which is used +//! as the line end marker in the receive buffer. +//! +//! \return Returns -1 to indicate that the requested character does not exist +//! in the receive buffer. Returns a non-negative number if the character was +//! found in which case the value represents the position of the first instance +//! of \e ucChar relative to the receive buffer read pointer. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +int +UARTPeek(unsigned char ucChar) +{ + int iCount; + int iAvail; + uint32_t ui32ReadIndex; + + // + // How many characters are there in the receive buffer? + // + iAvail = (int)RX_BUFFER_USED; + ui32ReadIndex = g_ui32UARTRxReadIndex; + + // + // Check all the unread characters looking for the one passed. + // + for(iCount = 0; iCount < iAvail; iCount++) + { + if(g_pcUARTRxBuffer[ui32ReadIndex] == ucChar) + { + // + // We found it so return the index + // + return(iCount); + } + else + { + // + // This one didn't match so move on to the next character. + // + ADVANCE_RX_BUFFER_INDEX(ui32ReadIndex); + } + } + + // + // If we drop out of the loop, we didn't find the character in the receive + // buffer. + // + return(-1); +} +#endif + +//***************************************************************************** +// +//! Flushes the receive buffer. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to discard any data +//! received from the UART but not yet read using UARTgets(). +//! +//! \return None. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +void +UARTFlushRx(void) +{ + uint32_t ui32Int; + + // + // Temporarily turn off interrupts. + // + ui32Int = MAP_IntMasterDisable(); + + // + // Flush the receive buffer. + // + g_ui32UARTRxReadIndex = 0; + g_ui32UARTRxWriteIndex = 0; + + // + // If interrupts were enabled when we turned them off, turn them + // back on again. + // + if(!ui32Int) + { + MAP_IntMasterEnable(); + } +} +#endif + +//***************************************************************************** +// +//! Flushes the transmit buffer. +//! +//! \param bDiscard indicates whether any remaining data in the buffer should +//! be discarded (\b true) or transmitted (\b false). +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to flush the transmit +//! buffer, either discarding or transmitting any data received via calls to +//! UARTprintf() that is waiting to be transmitted. On return, the transmit +//! buffer will be empty. +//! +//! \return None. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +void +UARTFlushTx(bool bDiscard) +{ + uint32_t ui32Int; + + // + // Should the remaining data be discarded or transmitted? + // + if(bDiscard) + { + // + // The remaining data should be discarded, so temporarily turn off + // interrupts. + // + ui32Int = MAP_IntMasterDisable(); + + // + // Flush the transmit buffer. + // + g_ui32UARTTxReadIndex = 0; + g_ui32UARTTxWriteIndex = 0; + + // + // If interrupts were enabled when we turned them off, turn them + // back on again. + // + if(!ui32Int) + { + MAP_IntMasterEnable(); + } + } + else + { + // + // Wait for all remaining data to be transmitted before returning. + // + while(!TX_BUFFER_EMPTY) + { + } + } +} +#endif + +//***************************************************************************** +// +//! Enables or disables echoing of received characters to the transmitter. +//! +//! \param bEnable must be set to \b true to enable echo or \b false to +//! disable it. +//! +//! This function, available only when the module is built to operate in +//! buffered mode using \b UART_BUFFERED, may be used to control whether or not +//! received characters are automatically echoed back to the transmitter. By +//! default, echo is enabled and this is typically the desired behavior if +//! the module is being used to support a serial command line. In applications +//! where this module is being used to provide a convenient, buffered serial +//! interface over which application-specific binary protocols are being run, +//! however, echo may be undesirable and this function can be used to disable +//! it. +//! +//! \return None. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +void +UARTEchoSet(bool bEnable) +{ + g_bDisableEcho = !bEnable; +} +#endif + +//***************************************************************************** +// +//! Handles UART interrupts. +//! +//! This function handles interrupts from the UART. It will copy data from the +//! transmit buffer to the UART transmit FIFO if space is available, and it +//! will copy data from the UART receive FIFO to the receive buffer if data is +//! available. +//! +//! \return None. +// +//***************************************************************************** +#if defined(UART_BUFFERED) || defined(DOXYGEN) +void +UARTStdioIntHandler(void) +{ + uint32_t ui32Ints; + int8_t cChar; + int32_t i32Char; + static bool bLastWasCR = false; + + // + // Get and clear the current interrupt source(s) + // + ui32Ints = MAP_UARTIntStatus(g_ui32Base, true); + MAP_UARTIntClear(g_ui32Base, ui32Ints); + + // + // Are we being interrupted because the TX FIFO has space available? + // + if(ui32Ints & UART_INT_TX) + { + // + // Move as many bytes as we can into the transmit FIFO. + // + UARTPrimeTransmit(g_ui32Base); + + // + // If the output buffer is empty, turn off the transmit interrupt. + // + if(TX_BUFFER_EMPTY) + { + MAP_UARTIntDisable(g_ui32Base, UART_INT_TX); + } + } + + // + // Are we being interrupted due to a received character? + // + if(ui32Ints & (UART_INT_RX | UART_INT_RT)) + { + // + // Get all the available characters from the UART. + // + while(MAP_UARTCharsAvail(g_ui32Base)) + { + // + // Read a character + // + i32Char = MAP_UARTCharGetNonBlocking(g_ui32Base); + cChar = (unsigned char)(i32Char & 0xFF); + + // + // If echo is disabled, we skip the various text filtering + // operations that would typically be required when supporting a + // command line. + // + if(!g_bDisableEcho) + { + // + // Handle backspace by erasing the last character in the + // buffer. + // + if(cChar == '\b') + { + // + // If there are any characters already in the buffer, then + // delete the last. + // + if(!RX_BUFFER_EMPTY) + { + // + // Rub out the previous character on the users + // terminal. + // + UARTwrite("\b \b", 3); + + // + // Decrement the number of characters in the buffer. + // + if(g_ui32UARTRxWriteIndex == 0) + { + g_ui32UARTRxWriteIndex = UART_RX_BUFFER_SIZE - 1; + } + else + { + g_ui32UARTRxWriteIndex--; + } + } + + // + // Skip ahead to read the next character. + // + continue; + } + + // + // If this character is LF and last was CR, then just gobble up + // the character since we already echoed the previous CR and we + // don't want to store 2 characters in the buffer if we don't + // need to. + // + if((cChar == '\n') && bLastWasCR) + { + bLastWasCR = false; + continue; + } + + // + // See if a newline or escape character was received. + // + if((cChar == '\r') || (cChar == '\n') || (cChar == 0x1b)) + { + // + // If the character is a CR, then it may be followed by an + // LF which should be paired with the CR. So remember that + // a CR was received. + // + if(cChar == '\r') + { + bLastWasCR = 1; + } + + // + // Regardless of the line termination character received, + // put a CR in the receive buffer as a marker telling + // UARTgets() where the line ends. We also send an + // additional LF to ensure that the local terminal echo + // receives both CR and LF. + // + cChar = '\r'; + UARTwrite("\n", 1); + } + } + + // + // If there is space in the receive buffer, put the character + // there, otherwise throw it away. + // + if(!RX_BUFFER_FULL) + { + // + // Store the new character in the receive buffer + // + g_pcUARTRxBuffer[g_ui32UARTRxWriteIndex] = + (unsigned char)(i32Char & 0xFF); + ADVANCE_RX_BUFFER_INDEX(g_ui32UARTRxWriteIndex); + + // + // If echo is enabled, write the character to the transmit + // buffer so that the user gets some immediate feedback. + // + if(!g_bDisableEcho) + { + UARTwrite((const char *)&cChar, 1); + } + } + } + + // + // If we wrote anything to the transmit buffer, make sure it actually + // gets transmitted. + // + UARTPrimeTransmit(g_ui32Base); + MAP_UARTIntEnable(g_ui32Base, UART_INT_TX); + } +} +#endif + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/uartstdio.h b/utils/uartstdio.h new file mode 100644 index 0000000..fbadabc --- /dev/null +++ b/utils/uartstdio.h @@ -0,0 +1,86 @@ +//***************************************************************************** +// +// uartstdio.h - Prototypes for the UART console functions. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __UARTSTDIO_H__ +#define __UARTSTDIO_H__ + +#include + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// If built for buffered operation, the following labels define the sizes of +// the transmit and receive buffers respectively. +// +//***************************************************************************** +#ifdef UART_BUFFERED +#ifndef UART_RX_BUFFER_SIZE +#define UART_RX_BUFFER_SIZE 128 +#endif +#ifndef UART_TX_BUFFER_SIZE +#define UART_TX_BUFFER_SIZE 1024 +#endif +#endif + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern void UARTStdioConfig(uint32_t ui32Port, uint32_t ui32Baud, + uint32_t ui32SrcClock); +extern int UARTgets(char *pcBuf, uint32_t ui32Len); +extern unsigned char UARTgetc(void); +extern void UARTprintf(const char *pcString, ...); +extern void UARTvprintf(const char *pcString, va_list vaArgP); +extern int UARTwrite(const char *pcBuf, uint32_t ui32Len); +#ifdef UART_BUFFERED +extern int UARTPeek(unsigned char ucChar); +extern void UARTFlushTx(bool bDiscard); +extern void UARTFlushRx(void); +extern int UARTRxBytesAvail(void); +extern int UARTTxBytesFree(void); +extern void UARTEchoSet(bool bEnable); +#endif + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __UARTSTDIO_H__ diff --git a/utils/ustdlib.c b/utils/ustdlib.c new file mode 100644 index 0000000..0fa6f05 --- /dev/null +++ b/utils/ustdlib.c @@ -0,0 +1,1826 @@ +//***************************************************************************** +// +// ustdlib.c - Simple standard library functions. +// +// 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 Utility Library. +// +//***************************************************************************** + +#include +#include "driverlib/debug.h" +#include "utils/ustdlib.h" + +//***************************************************************************** +// +//! \addtogroup ustdlib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// A mapping from an integer between 0 and 15 to its ASCII character +// equivalent. +// +//***************************************************************************** +static const char * const g_pcHex = "0123456789abcdef"; + +//***************************************************************************** +// +//! Copies a certain number of characters from one string to another. +//! +//! \param s1 is a pointer to the destination buffer into which characters +//! are to be copied. +//! \param s2 is a pointer to the string from which characters are to be +//! copied. +//! \param n is the number of characters to copy to the destination buffer. +//! +//! This function copies at most \e n characters from the string pointed to +//! by \e s2 into the buffer pointed to by \e s1. If the end of \e s2 is found +//! before \e n characters have been copied, remaining characters in \e s1 +//! will be padded with zeroes until \e n characters have been written. Note +//! that the destination string will only be NULL terminated if the number of +//! characters to be copied is greater than the length of \e s2. +//! +//! \return Returns \e s1. +// +//***************************************************************************** +char * +ustrncpy(char * restrict s1, const char * restrict s2, size_t n) +{ + size_t count; + + // + // Check the arguments. + // + ASSERT(s1); + ASSERT(s2); + + // + // Start at the beginning of the source string. + // + count = 0; + + // + // Copy the source string until we run out of source characters or + // destination space. + // + while(n && s2[count]) + { + s1[count] = s2[count]; + count++; + n--; + } + + // + // Pad the destination if we are not yet done. + // + while(n) + { + s1[count++] = (char)0; + n--; + } + + // + // Pass the destination pointer back to the caller. + // + return(s1); +} + +//***************************************************************************** +// +//! A simple vsnprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and +//! \%X. +//! +//! \param s points to the buffer where the converted string is stored. +//! \param n is the size of the buffer. +//! \param format is the format string. +//! \param arg is the list of optional arguments, which depend on the +//! contents of the format string. +//! +//! This function is very similar to the C library vsnprintf() +//! function. Only the following formatting characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%d, \%i, \%p, \%s, \%u, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeroes instead of spaces. +//! +//! The type of the arguments after \e format must match the requirements of +//! the format string. For example, if an integer was passed where a string +//! was expected, an error of some kind will most likely occur. +//! +//! The \e n parameter limits the number of characters that will be +//! stored in the buffer pointed to by \e s to prevent the possibility of +//! a buffer overflow. The buffer size should be large enough to hold the +//! expected converted output string, including the null termination character. +//! +//! The function will return the number of characters that would be converted +//! as if there were no limit on the buffer size. Therefore it is possible for +//! the function to return a count that is greater than the specified buffer +//! size. If this happens, it means that the output was truncated. +//! +//! \return Returns the number of characters that were to be stored, not +//! including the NULL termination character, regardless of space in the +//! buffer. +// +//***************************************************************************** +int +uvsnprintf(char * restrict s, size_t n, const char * restrict format, + va_list arg) +{ + unsigned long ulIdx, ulValue, ulCount, ulBase, ulNeg; + char *pcStr, cFill; + int iConvertCount = 0; + + // + // Check the arguments. + // + ASSERT(s); + ASSERT(n); + ASSERT(format); + + // + // Adjust buffer size limit to allow one space for null termination. + // + if(n) + { + n--; + } + + // + // Initialize the count of characters converted. + // + iConvertCount = 0; + + // + // Loop while there are more characters in the format string. + // + while(*format) + { + // + // Find the first non-% character, or the end of the string. + // + for(ulIdx = 0; (format[ulIdx] != '%') && (format[ulIdx] != '\0'); + ulIdx++) + { + } + + // + // Write this portion of the string to the output buffer. If there are + // more characters to write than there is space in the buffer, then + // only write as much as will fit in the buffer. + // + if(ulIdx > n) + { + ustrncpy(s, format, n); + s += n; + n = 0; + } + else + { + ustrncpy(s, format, ulIdx); + s += ulIdx; + n -= ulIdx; + } + + // + // Update the conversion count. This will be the number of characters + // that should have been written, even if there was not room in the + // buffer. + // + iConvertCount += ulIdx; + + // + // Skip the portion of the format string that was written. + // + format += ulIdx; + + // + // See if the next character is a %. + // + if(*format == '%') + { + // + // Skip the %. + // + format++; + + // + // Set the digit count to zero, and the fill character to space + // (that is, to the defaults). + // + ulCount = 0; + cFill = ' '; + + // + // It may be necessary to get back here to process more characters. + // Goto's aren't pretty, but effective. I feel extremely dirty for + // using not one but two of the beasts. + // +again: + + // + // Determine how to handle the next character. + // + switch(*format++) + { + // + // Handle the digit characters. + // + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + // + // If this is a zero, and it is the first digit, then the + // fill character is a zero instead of a space. + // + if((format[-1] == '0') && (ulCount == 0)) + { + cFill = '0'; + } + + // + // Update the digit count. + // + ulCount *= 10; + ulCount += format[-1] - '0'; + + // + // Get the next character. + // + goto again; + } + + // + // Handle the %c command. + // + case 'c': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(arg, unsigned long); + + // + // Copy the character to the output buffer, if there is + // room. Update the buffer size remaining. + // + if(n != 0) + { + *s++ = (char)ulValue; + n--; + } + + // + // Update the conversion count. + // + iConvertCount++; + + // + // This command has been handled. + // + break; + } + + // + // Handle the %d and %i commands. + // + case 'd': + case 'i': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(arg, unsigned long); + + // + // If the value is negative, make it positive and indicate + // that a minus sign is needed. + // + if((long)ulValue < 0) + { + // + // Make the value positive. + // + ulValue = -(long)ulValue; + + // + // Indicate that the value is negative. + // + ulNeg = 1; + } + else + { + // + // Indicate that the value is positive so that a + // negative sign isn't inserted. + // + ulNeg = 0; + } + + // + // Set the base to 10. + // + ulBase = 10; + + // + // Convert the value to ASCII. + // + goto convert; + } + + // + // Handle the %s command. + // + case 's': + { + // + // Get the string pointer from the varargs. + // + pcStr = va_arg(arg, char *); + + // + // Determine the length of the string. + // + for(ulIdx = 0; pcStr[ulIdx] != '\0'; ulIdx++) + { + } + + // + // Update the convert count to include any padding that + // should be necessary (regardless of whether we have space + // to write it or not). + // + if(ulCount > ulIdx) + { + iConvertCount += (ulCount - ulIdx); + } + + // + // Copy the string to the output buffer. Only copy as much + // as will fit in the buffer. Update the output buffer + // pointer and the space remaining. + // + if(ulIdx > n) + { + ustrncpy(s, pcStr, n); + s += n; + n = 0; + } + else + { + ustrncpy(s, pcStr, ulIdx); + s += ulIdx; + n -= ulIdx; + + // + // Write any required padding spaces assuming there is + // still space in the buffer. + // + if(ulCount > ulIdx) + { + ulCount -= ulIdx; + if(ulCount > n) + { + ulCount = n; + } + n = -ulCount; + + while(ulCount--) + { + *s++ = ' '; + } + } + } + + // + // Update the conversion count. This will be the number of + // characters that should have been written, even if there + // was not room in the buffer. + // + iConvertCount += ulIdx; + + // + // This command has been handled. + // + break; + } + + // + // Handle the %u command. + // + case 'u': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(arg, unsigned long); + + // + // Set the base to 10. + // + ulBase = 10; + + // + // Indicate that the value is positive so that a minus sign + // isn't inserted. + // + ulNeg = 0; + + // + // Convert the value to ASCII. + // + goto convert; + } + + // + // Handle the %x and %X commands. Note that they are treated + // identically; that is, %X will use lower case letters for a-f + // instead of the upper case letters is should use. We also + // alias %p to %x. + // + case 'x': + case 'X': + case 'p': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(arg, unsigned long); + + // + // Set the base to 16. + // + ulBase = 16; + + // + // Indicate that the value is positive so that a minus sign + // isn't inserted. + // + ulNeg = 0; + + // + // Determine the number of digits in the string version of + // the value. + // +convert: + for(ulIdx = 1; + (((ulIdx * ulBase) <= ulValue) && + (((ulIdx * ulBase) / ulBase) == ulIdx)); + ulIdx *= ulBase, ulCount--) + { + } + + // + // If the value is negative, reduce the count of padding + // characters needed. + // + if(ulNeg) + { + ulCount--; + } + + // + // If the value is negative and the value is padded with + // zeros, then place the minus sign before the padding. + // + if(ulNeg && (n != 0) && (cFill == '0')) + { + // + // Place the minus sign in the output buffer. + // + *s++ = '-'; + n--; + + // + // Update the conversion count. + // + iConvertCount++; + + // + // The minus sign has been placed, so turn off the + // negative flag. + // + ulNeg = 0; + } + + // + // See if there are more characters in the specified field + // width than there are in the conversion of this value. + // + if((ulCount > 1) && (ulCount < 65536)) + { + // + // Loop through the required padding characters. + // + for(ulCount--; ulCount; ulCount--) + { + // + // Copy the character to the output buffer if there + // is room. + // + if(n != 0) + { + *s++ = cFill; + n--; + } + + // + // Update the conversion count. + // + iConvertCount++; + } + } + + // + // If the value is negative, then place the minus sign + // before the number. + // + if(ulNeg && (n != 0)) + { + // + // Place the minus sign in the output buffer. + // + *s++ = '-'; + n--; + + // + // Update the conversion count. + // + iConvertCount++; + } + + // + // Convert the value into a string. + // + for(; ulIdx; ulIdx /= ulBase) + { + // + // Copy the character to the output buffer if there is + // room. + // + if(n != 0) + { + *s++ = g_pcHex[(ulValue / ulIdx) % ulBase]; + n--; + } + + // + // Update the conversion count. + // + iConvertCount++; + } + + // + // This command has been handled. + // + break; + } + + // + // Handle the %% command. + // + case '%': + { + // + // Simply write a single %. + // + if(n != 0) + { + *s++ = format[-1]; + n--; + } + + // + // Update the conversion count. + // + iConvertCount++; + + // + // This command has been handled. + // + break; + } + + // + // Handle all other commands. + // + default: + { + // + // Indicate an error. + // + if(n >= 5) + { + ustrncpy(s, "ERROR", 5); + s += 5; + n -= 5; + } + else + { + ustrncpy(s, "ERROR", n); + s += n; + n = 0; + } + + // + // Update the conversion count. + // + iConvertCount += 5; + + // + // This command has been handled. + // + break; + } + } + } + } + + // + // Null terminate the string in the buffer. + // + *s = 0; + + // + // Return the number of characters in the full converted string. + // + return(iConvertCount); +} + +//***************************************************************************** +// +//! A simple sprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and \%X. +//! +//! \param s is the buffer where the converted string is stored. +//! \param format is the format string. +//! \param ... are the optional arguments, which depend on the contents of the +//! format string. +//! +//! This function is very similar to the C library sprintf() function. +//! Only the following formatting characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%d, \%i, \%p, \%s, \%u, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeros instead of spaces. +//! +//! The type of the arguments after \e format must match the requirements of +//! the format string. For example, if an integer was passed where a string +//! was expected, an error of some kind will most likely occur. +//! +//! The caller must ensure that the buffer \e s is large enough to hold the +//! entire converted string, including the null termination character. +//! +//! \return Returns the count of characters that were written to the output +//! buffer, not including the NULL termination character. +// +//***************************************************************************** +int +usprintf(char * restrict s, const char *format, ...) +{ + va_list arg; + int ret; + + // + // Start the varargs processing. + // + va_start(arg, format); + + // + // Call vsnprintf to perform the conversion. Use a large number for the + // buffer size. + // + ret = uvsnprintf(s, 0xffff, format, arg); + + // + // End the varargs processing. + // + va_end(arg); + + // + // Return the conversion count. + // + return(ret); +} + +//***************************************************************************** +// +//! A simple snprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and +//! \%X. +//! +//! \param s is the buffer where the converted string is stored. +//! \param n is the size of the buffer. +//! \param format is the format string. +//! \param ... are the optional arguments, which depend on the contents of the +//! format string. +//! +//! This function is very similar to the C library sprintf() function. +//! Only the following formatting characters are supported: +//! +//! - \%c to print a character +//! - \%d or \%i to print a decimal value +//! - \%s to print a string +//! - \%u to print an unsigned decimal value +//! - \%x to print a hexadecimal value using lower case letters +//! - \%X to print a hexadecimal value using lower case letters (not upper case +//! letters as would typically be used) +//! - \%p to print a pointer as a hexadecimal value +//! - \%\% to print out a \% character +//! +//! For \%d, \%i, \%p, \%s, \%u, \%x, and \%X, an optional number may reside +//! between the \% and the format character, which specifies the minimum number +//! of characters to use for that value; if preceded by a 0 then the extra +//! characters will be filled with zeros instead of spaces. For example, +//! ``\%8d'' will use eight characters to print the decimal value with spaces +//! added to reach eight; ``\%08d'' will use eight characters as well but will +//! add zeros instead of spaces. +//! +//! The type of the arguments after \e format must match the requirements of +//! the format string. For example, if an integer was passed where a string +//! was expected, an error of some kind will most likely occur. +//! +//! The function will copy at most \e n - 1 characters into the buffer +//! \e s. One space is reserved in the buffer for the null termination +//! character. +//! +//! The function will return the number of characters that would be converted +//! as if there were no limit on the buffer size. Therefore it is possible for +//! the function to return a count that is greater than the specified buffer +//! size. If this happens, it means that the output was truncated. +//! +//! \return Returns the number of characters that were to be stored, not +//! including the NULL termination character, regardless of space in the +//! buffer. +// +//***************************************************************************** +int +usnprintf(char * restrict s, size_t n, const char * restrict format, ...) +{ + va_list arg; + int ret; + + // + // Start the varargs processing. + // + va_start(arg, format); + + // + // Call vsnprintf to perform the conversion. + // + ret = uvsnprintf(s, n, format, arg); + + // + // End the varargs processing. + // + va_end(arg); + + // + // Return the conversion count. + // + return(ret); +} + +//***************************************************************************** +// +// This array contains the number of days in a year at the beginning of each +// month of the year, in a non-leap year. +// +//***************************************************************************** +static const time_t g_psDaysToMonth[12] = +{ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +//***************************************************************************** +// +//! Converts from seconds to calendar date and time. +//! +//! \param timer is the number of seconds. +//! \param tm is a pointer to the time structure that is filled in with the +//! broken down date and time. +//! +//! This function converts a number of seconds since midnight GMT on January 1, +//! 1970 (traditional Unix epoch) into the equivalent month, day, year, hours, +//! minutes, and seconds representation. +//! +//! \return None. +// +//***************************************************************************** +void +ulocaltime(time_t timer, struct tm *tm) +{ + time_t temp, months; + + // + // Extract the number of seconds, converting time to the number of minutes. + // + temp = timer / 60; + tm->tm_sec = timer - (temp * 60); + timer = temp; + + // + // Extract the number of minutes, converting time to the number of hours. + // + temp = timer / 60; + tm->tm_min = timer - (temp * 60); + timer = temp; + + // + // Extract the number of hours, converting time to the number of days. + // + temp = timer / 24; + tm->tm_hour = timer - (temp * 24); + timer = temp; + + // + // Compute the day of the week. + // + tm->tm_wday = (timer + 4) % 7; + + // + // Compute the number of leap years that have occurred since 1968, the + // first leap year before 1970. For the beginning of a leap year, cut the + // month loop below at March so that the leap day is classified as February + // 29 followed by March 1, instead of March 1 followed by another March 1. + // + timer += 366 + 365; + temp = timer / ((4 * 365) + 1); + if((timer - (temp * ((4 * 365) + 1))) > (31 + 28)) + { + temp++; + months = 12; + } + else + { + months = 2; + } + + // + // Extract the year. + // + tm->tm_year = ((timer - temp) / 365) + 68; + timer -= ((tm->tm_year - 68) * 365) + temp; + + // + // Extract the month. + // + for(temp = 0; temp < months; temp++) + { + if(g_psDaysToMonth[temp] > timer) + { + break; + } + } + tm->tm_mon = temp - 1; + + // + // Extract the day of the month. + // + tm->tm_mday = timer - g_psDaysToMonth[temp - 1] + 1; +} + +//***************************************************************************** +// +//! Compares two time structures and determines if one is greater than, +//! less than, or equal to the other. +//! +//! \param t1 is the first time structure to compare. +//! \param t2 is the second time structure to compare. +//! +//! This function compares two time structures and returns a signed number +//! to indicate the result of the comparison. If the time represented by +//! \e t1 is greater than the time represented by \e t2 then a positive +//! number is returned. Likewise if \e t1 is less than \e t2 then a +//! negative number is returned. If the two times are equal then the function +//! returns 0. +//! +//! \return Returns 0 if the two times are equal, +1 if \e t1 is greater +//! than \e t2, and -1 if \e t1 is less than \e t2. +// +//***************************************************************************** +static int +ucmptime(struct tm *t1, struct tm *t2) +{ + // + // Compare each field in descending signficance to determine if + // greater than, less than, or equal. + // + if(t1->tm_year > t2->tm_year) + { + return(1); + } + else if(t1->tm_year < t2->tm_year) + { + return(-1); + } + else if(t1->tm_mon > t2->tm_mon) + { + return(1); + } + else if(t1->tm_mon < t2->tm_mon) + { + return(-1); + } + else if(t1->tm_mday > t2->tm_mday) + { + return(1); + } + else if(t1->tm_mday < t2->tm_mday) + { + return(-1); + } + else if(t1->tm_hour > t2->tm_hour) + { + return(1); + } + else if(t1->tm_hour < t2->tm_hour) + { + return(-1); + } + else if(t1->tm_min > t2->tm_min) + { + return(1); + } + else if(t1->tm_min < t2->tm_min) + { + return(-1); + } + else if(t1->tm_sec > t2->tm_sec) + { + return(1); + } + else if(t1->tm_sec < t2->tm_sec) + { + return(-1); + } + else + { + // + // Reaching this branch of the conditional means that all of the + // fields are equal, and thus the two times are equal. + // + return(0); + } +} + +//***************************************************************************** +// +//! Converts calendar date and time to seconds. +//! +//! \param timeptr is a pointer to the time structure that is filled in with +//! the broken down date and time. +//! +//! This function converts the date and time represented by the \e timeptr +//! structure pointer to the number of seconds since midnight GMT on January 1, +//! 1970 (traditional Unix epoch). +//! +//! \return Returns the calendar time and date as seconds. If the conversion +//! was not possible then the function returns (uint32_t)(-1). +// +//***************************************************************************** +time_t +umktime(struct tm *timeptr) +{ + struct tm sTimeGuess; + unsigned long ulTimeGuess = 0x80000000; + unsigned long ulAdjust = 0x40000000; + int iSign; + + // + // Seed the binary search with the first guess. + // + ulocaltime(ulTimeGuess, &sTimeGuess); + iSign = ucmptime(timeptr, &sTimeGuess); + + // + // While the time is not yet found, execute a binary search. + // + while(iSign && ulAdjust) + { + // + // Adjust the time guess up or down depending on the result of the + // last compare. + // + ulTimeGuess = ((iSign > 0) ? (ulTimeGuess + ulAdjust) : + (ulTimeGuess - ulAdjust)); + ulAdjust /= 2; + + // + // Compare the new time guess against the time pointed at by the + // function parameters. + // + ulocaltime(ulTimeGuess, &sTimeGuess); + iSign = ucmptime(timeptr, &sTimeGuess); + } + + // + // If the above loop was exited with iSign == 0, that means that the + // time in seconds was found, so return that value to the caller. + // + if(iSign == 0) + { + return(ulTimeGuess); + } + + // + // Otherwise the time could not be converted so return an error. + // + else + { + return((unsigned long)-1); + } +} + +//***************************************************************************** +// +//! Converts a string into its numeric equivalent. +//! +//! \param nptr is a pointer to the string containing the integer. +//! \param endptr is a pointer that will be set to the first character past +//! the integer in the string. +//! \param base is the radix to use for the conversion; can be zero to +//! auto-select the radix or between 2 and 16 to explicitly specify the radix. +//! +//! This function is very similar to the C library strtoul() function. +//! It scans a string for the first token (that is, non-white space) and +//! converts the value at that location in the string into an integer value. +//! +//! \return Returns the result of the conversion. +// +//***************************************************************************** +unsigned long +ustrtoul(const char * restrict nptr, const char ** restrict endptr, int base) +{ + unsigned long ulRet, ulDigit, ulNeg, ulValid; + const char *pcPtr; + + // + // Check the arguments. + // + ASSERT(nptr); + ASSERT((base == 0) || ((base > 1) && (base <= 16))); + + // + // Initially, the result is zero. + // + ulRet = 0; + ulNeg = 0; + ulValid = 0; + + // + // Skip past any leading white space. + // + pcPtr = nptr; + while((*pcPtr == ' ') || (*pcPtr == '\t')) + { + pcPtr++; + } + + // + // Take a leading + or - from the value. + // + if(*pcPtr == '-') + { + ulNeg = 1; + pcPtr++; + } + else if(*pcPtr == '+') + { + pcPtr++; + } + + // + // See if the radix was not specified, or is 16, and the value starts with + // "0x" or "0X" (to indicate a hex value). + // + if(((base == 0) || (base == 16)) && (*pcPtr == '0') && + ((pcPtr[1] == 'x') || (pcPtr[1] == 'X'))) + { + // + // Skip the leading "0x". + // + pcPtr += 2; + + // + // Set the radix to 16. + // + base = 16; + } + + // + // See if the radix was not specified. + // + if(base == 0) + { + // + // See if the value starts with "0". + // + if(*pcPtr == '0') + { + // + // Values that start with "0" are assumed to be radix 8. + // + base = 8; + } + else + { + // + // Otherwise, the values are assumed to be radix 10. + // + base = 10; + } + } + + // + // Loop while there are more valid digits to consume. + // + while(1) + { + // + // See if this character is a number. + // + if((*pcPtr >= '0') && (*pcPtr <= '9')) + { + // + // Convert the character to its integer equivalent. + // + ulDigit = *pcPtr++ - '0'; + } + + // + // Otherwise, see if this character is an upper case letter. + // + else if((*pcPtr >= 'A') && (*pcPtr <= 'Z')) + { + // + // Convert the character to its integer equivalent. + // + ulDigit = *pcPtr++ - 'A' + 10; + } + + // + // Otherwise, see if this character is a lower case letter. + // + else if((*pcPtr >= 'a') && (*pcPtr <= 'z')) + { + // + // Convert the character to its integer equivalent. + // + ulDigit = *pcPtr++ - 'a' + 10; + } + + // + // Otherwise, this is not a valid character. + // + else + { + // + // Stop converting this value. + // + break; + } + + // + // See if this digit is valid for the chosen radix. + // + if(ulDigit >= base) + { + // + // Since this was not a valid digit, move the pointer back to the + // character that therefore should not have been consumed. + // + pcPtr--; + + // + // Stop converting this value. + // + break; + } + + // + // Add this digit to the converted value. + // + ulRet *= base; + ulRet += ulDigit; + + // + // Since a digit has been added, this is now a valid result. + // + ulValid = 1; + } + + // + // Set the return string pointer to the first character not consumed. + // + if(endptr) + { + *endptr = ulValid ? pcPtr : nptr; + } + + // + // Return the converted value. + // + return(ulNeg ? (0 - ulRet) : ulRet); +} + +//***************************************************************************** +// +// An array of the value of ten raised to the power-of-two exponents. This is +// used for converting the decimal exponent into the floating-point value of +// 10^exp. +// +//***************************************************************************** +static const float g_pfExponents[] = +{ + 1.0e+01, + 1.0e+02, + 1.0e+04, + 1.0e+08, + 1.0e+16, + 1.0e+32, +}; + +//***************************************************************************** +// +//! Converts a string into its floating-point equivalent. +//! +//! \param nptr is a pointer to the string containing the floating-point +//! value. +//! \param endptr is a pointer that will be set to the first character past +//! the floating-point value in the string. +//! +//! This function is very similar to the C library strtof() function. +//! It scans a string for the first token (that is, non-white space) and +//! converts the value at that location in the string into a floating-point +//! value. +//! +//! \return Returns the result of the conversion. +// +//***************************************************************************** +float +ustrtof(const char *nptr, const char **endptr) +{ + unsigned long ulNeg, ulExp, ulExpNeg, ulValid, ulIdx; + float fRet, fDigit, fExp; + const char *pcPtr; + + // + // Check the arguments. + // + ASSERT(nptr); + + // + // Initially, the result is zero. + // + fRet = 0; + ulNeg = 0; + ulValid = 0; + + // + // Skip past any leading white space. + // + pcPtr = nptr; + while((*pcPtr == ' ') || (*pcPtr == '\t')) + { + pcPtr++; + } + + // + // Take a leading + or - from the value. + // + if(*pcPtr == '-') + { + ulNeg = 1; + pcPtr++; + } + else if(*pcPtr == '+') + { + pcPtr++; + } + + // + // Loop while there are valid digits to consume. + // + while((*pcPtr >= '0') && (*pcPtr <= '9')) + { + // + // Add this digit to the converted value. + // + fRet *= 10; + fRet += *pcPtr++ - '0'; + + // + // Since a digit has been added, this is now a valid result. + // + ulValid = 1; + } + + // + // See if the next character is a period and the character after that is a + // digit, indicating the start of the fractional portion of the value. + // + if((*pcPtr == '.') && (pcPtr[1] >= '0') && (pcPtr[1] <= '9')) + { + // + // Skip the period. + // + pcPtr++; + + // + // Loop while there are valid fractional digits to consume. + // + fDigit = 0.1; + while((*pcPtr >= '0') && (*pcPtr <= '9')) + { + // + // Add this digit to the converted value. + // + fRet += (*pcPtr++ - '0') * fDigit; + fDigit /= (float)10.0; + + // + // Since a digit has been added, this is now a valid result. + // + ulValid = 1; + } + } + + // + // See if the next character is an "e" and a valid number has been + // converted, indicating the start of the exponent. + // + if(((pcPtr[0] == 'e') || (pcPtr[0] == 'E')) && (ulValid == 1) && + (((pcPtr[1] >= '0') && (pcPtr[1] <= '9')) || + (((pcPtr[1] == '+') || (pcPtr[1] == '-')) && + (pcPtr[2] >= '0') && (pcPtr[2] <= '9')))) + { + // + // Skip the "e". + // + pcPtr++; + + // + // Take a leading + or - from the exponenet. + // + ulExpNeg = 0; + if(*pcPtr == '-') + { + ulExpNeg = 1; + pcPtr++; + } + else if(*pcPtr == '+') + { + pcPtr++; + } + + // + // Loop while there are valid digits in the exponent. + // + ulExp = 0; + while((*pcPtr >= '0') && (*pcPtr <= '9')) + { + // + // Add this digit to the converted value. + // + ulExp *= 10; + ulExp += *pcPtr++ - '0'; + } + + // + // Raise ten to the power of the exponent. Do this via binary + // decomposition; for each binary bit set in the exponent, multiply the + // floating-point representation by ten raised to that binary value + // (extracted from the table above). + // + fExp = 1; + for(ulIdx = 0; ulIdx < 7; ulIdx++) + { + if(ulExp & (1 << ulIdx)) + { + fExp *= g_pfExponents[ulIdx]; + } + } + + // + // If the exponent is negative, then the exponent needs to be inverted. + // + if(ulExpNeg == 1) + { + fExp = 1 / fExp; + } + + // + // Multiply the result by the computed exponent value. + // + fRet *= fExp; + } + + // + // Set the return string pointer to the first character not consumed. + // + if(endptr) + { + *endptr = ulValid ? pcPtr : nptr; + } + + // + // Return the converted value. + // + return(ulNeg ? (0 - fRet) : fRet); +} + +//***************************************************************************** +// +//! Returns the length of a null-terminated string. +//! +//! \param s is a pointer to the string whose length is to be found. +//! +//! This function is very similar to the C library strlen() function. +//! It determines the length of the null-terminated string passed and returns +//! this to the caller. +//! +//! This implementation assumes that single byte character strings are passed +//! and will return incorrect values if passed some UTF-8 strings. +//! +//! \return Returns the length of the string pointed to by \e s. +// +//***************************************************************************** +size_t +ustrlen(const char *s) +{ + size_t len; + + // + // Check the arguments. + // + ASSERT(s); + + // + // Initialize the length. + // + len = 0; + + // + // Step throug the string looking for a zero character (marking its end). + // + while(s[len]) + { + // + // Zero not found so move on to the next character. + // + len++; + } + + return(len); +} + +//***************************************************************************** +// +//! Finds a substring within a string. +//! +//! \param s1 is a pointer to the string that will be searched. +//! \param s2 is a pointer to the substring that is to be found within +//! \e s1. +//! +//! This function is very similar to the C library strstr() function. +//! It scans a string for the first instance of a given substring and returns +//! a pointer to that substring. If the substring cannot be found, a NULL +//! pointer is returned. +//! +//! \return Returns a pointer to the first occurrence of \e s2 within +//! \e s1 or NULL if no match is found. +// +//***************************************************************************** +char * +ustrstr(const char *s1, const char *s2) +{ + size_t n; + + // + // Get the length of the string to be found. + // + n = ustrlen(s2); + + // + // Loop while we have not reached the end of the string. + // + while(*s1) + { + // + // Check to see if the substring appears at this position. + // + if(ustrncmp(s2, s1, n) == 0) + { + // + // It does so return the pointer. + // + return((char *)s1); + } + + // + // Move to the next position in the string being searched. + // + s1++; + } + + // + // We reached the end of the string without finding the substring so + // return NULL. + // + return((char *)0); +} + +//***************************************************************************** +// +//! Compares two strings without regard to case. +//! +//! \param s1 points to the first string to be compared. +//! \param s2 points to the second string to be compared. +//! \param n is the maximum number of characters to compare. +//! +//! This function is very similar to the C library strncasecmp() +//! function. It compares at most \e n characters of two strings without +//! regard to case. The comparison ends if a terminating NULL character is +//! found in either string before \e n characters are compared. In this case, +//! the shorter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less +//! than \e s2 and 1 if \e s1 is greater than \e s2. +// +//***************************************************************************** +int +ustrncasecmp(const char *s1, const char *s2, size_t n) +{ + char c1, c2; + + // + // Loop while there are more characters to compare. + // + while(n) + { + // + // If we reached a NULL in both strings, they must be equal so + // we end the comparison and return 0 + // + if(!*s1 && !*s2) + { + return(0); + } + + // + // Lower case the characters at the current position before we compare. + // + c1 = (((*s1 >= 'A') && (*s1 <= 'Z')) ? (*s1 + ('a' - 'A')) : *s1); + c2 = (((*s2 >= 'A') && (*s2 <= 'Z')) ? (*s2 + ('a' - 'A')) : *s2); + + // + // Compare the two characters and, if different, return the relevant + // return code. + // + if(c2 < c1) + { + return(1); + } + if(c1 < c2) + { + return(-1); + } + + // + // Move on to the next character. + // + s1++; + s2++; + n--; + } + + // + // If we fall out, the strings must be equal for at least the first n + // characters so return 0 to indicate this. + // + return(0); +} + +//***************************************************************************** +// +//! Compares two strings without regard to case. +//! +//! \param s1 points to the first string to be compared. +//! \param s2 points to the second string to be compared. +//! +//! This function is very similar to the C library strcasecmp() +//! function. It compares two strings without regard to case. The comparison +//! ends if a terminating NULL character is found in either string. In this +//! case, the int16_ter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less +//! than \e s2 and 1 if \e s1 is greater than \e s2. +// +//***************************************************************************** +int +ustrcasecmp(const char *s1, const char *s2) +{ + // + // Just let ustrncasecmp() handle this. + // + return(ustrncasecmp(s1, s2, (size_t)-1)); +} + +//***************************************************************************** +// +//! Compares two strings. +//! +//! \param s1 points to the first string to be compared. +//! \param s2 points to the second string to be compared. +//! \param n is the maximum number of characters to compare. +//! +//! This function is very similar to the C library strncmp() function. +//! It compares at most \e n characters of two strings taking case into +//! account. The comparison ends if a terminating NULL character is found in +//! either string before \e n characters are compared. In this case, the +//! int16_ter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less +//! than \e s2 and 1 if \e s1 is greater than \e s2. +// +//***************************************************************************** +int +ustrncmp(const char *s1, const char *s2, size_t n) +{ + // + // Loop while there are more characters. + // + while(n) + { + // + // If we reached a NULL in both strings, they must be equal so we end + // the comparison and return 0 + // + if(!*s1 && !*s2) + { + return(0); + } + + // + // Compare the two characters and, if different, return the relevant + // return code. + // + if(*s2 < *s1) + { + return(1); + } + if(*s1 < *s2) + { + return(-1); + } + + // + // Move on to the next character. + // + s1++; + s2++; + n--; + } + + // + // If we fall out, the strings must be equal for at least the first n + // characters so return 0 to indicate this. + // + return(0); +} + +//***************************************************************************** +// +//! Compares two strings. +//! +//! \param s1 points to the first string to be compared. +//! \param s2 points to the second string to be compared. +//! +//! This function is very similar to the C library strcmp() +//! function. It compares two strings, taking case into account. The +//! comparison ends if a terminating NULL character is found in either string. +//! In this case, the int16_ter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e s1 is less +//! than \e s2 and 1 if \e s1 is greater than \e s2. +// +//***************************************************************************** +int +ustrcmp(const char *s1, const char *s2) +{ + // + // Pass this on to ustrncmp. + // + return(ustrncmp(s1, s2, (size_t)-1)); +} + +//***************************************************************************** +// +// Random Number Generator Seed Value +// +//***************************************************************************** +static unsigned int g_iRandomSeed = 1; + +//***************************************************************************** +// +//! Set the random number generator seed. +//! +//! \param seed is the new seed value to use for the random number +//! generator. +//! +//! This function is very similar to the C library srand() function. +//! It will set the seed value used in the urand() function. +//! +//! \return None +// +//***************************************************************************** +void +usrand(unsigned int seed) +{ + g_iRandomSeed = seed; +} + +//***************************************************************************** +// +//! Generate a new (pseudo) random number +//! +//! This function is very similar to the C library rand() function. +//! It will generate a pseudo-random number sequence based on the seed value. +//! +//! \return A pseudo-random number will be returned. +// +//***************************************************************************** +int +urand(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_iRandomSeed = (g_iRandomSeed * 1664525) + 1013904223; + + // + // Return the new random number. + // + return((int)g_iRandomSeed); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/ustdlib.h b/utils/ustdlib.h new file mode 100644 index 0000000..b9227f9 --- /dev/null +++ b/utils/ustdlib.h @@ -0,0 +1,82 @@ +//***************************************************************************** +// +// ustdlib.h - Prototypes for simple standard library functions. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef __USTDLIB_H__ +#define __USTDLIB_H__ + +//***************************************************************************** +// +// Include the standard C headers upon which these replacements are based. +// +//***************************************************************************** +#include +#include + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern void ulocaltime(time_t timer, struct tm *tm); +extern time_t umktime(struct tm *timeptr); +extern int urand(void); +extern int usnprintf(char * restrict s, size_t n, const char * restrict format, + ...); +extern int usprintf(char * restrict s, const char * restrict format, ...); +extern void usrand(unsigned int seed); +extern int ustrcasecmp(const char *s1, const char *s2); +extern int ustrcmp(const char *s1, const char *s2); +extern size_t ustrlen(const char *s); +extern int ustrncasecmp(const char *s1, const char *s2, size_t n); +extern int ustrncmp(const char *s1, const char *s2, size_t n); +extern char *ustrncpy(char * restrict s1, const char * restrict s2, size_t n); +extern char *ustrstr(const char *s1, const char *s2); +extern float ustrtof(const char * restrict nptr, + const char ** restrict endptr); +extern unsigned long int ustrtoul(const char * restrict nptr, + const char ** restrict endptr, int base); +extern int uvsnprintf(char * restrict s, size_t n, + const char * restrict format, va_list arg); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USTDLIB_H__ diff --git a/utils/wavfile.c b/utils/wavfile.c new file mode 100644 index 0000000..b2ce150 --- /dev/null +++ b/utils/wavfile.c @@ -0,0 +1,291 @@ +//****************************************************************************** +// +// wavfile.c - This file supports reading audio data from a .wav file and +// reading the file format. +// +// 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 Utility Library. +// +//****************************************************************************** + +#include +#include "inc/hw_types.h" +#include "third_party/fatfs/src/ff.h" +#include "third_party/fatfs/src/diskio.h" +#include "wavfile.h" + +//****************************************************************************** +// +// The flag values for the ui32Flags member of the tWavFile structure. +// +//****************************************************************************** +#define WAV_FLAG_FILEOPEN 0x00000001 + +//****************************************************************************** +// +// Basic wav file RIFF header information used to open and read a wav file. +// +//****************************************************************************** +#define RIFF_CHUNK_ID_RIFF 0x46464952 +#define RIFF_CHUNK_ID_FMT 0x20746d66 +#define RIFF_CHUNK_ID_DATA 0x61746164 +#define RIFF_TAG_WAVE 0x45564157 +#define RIFF_FORMAT_UNKNOWN 0x0000 +#define RIFF_FORMAT_PCM 0x0001 +#define RIFF_FORMAT_MSADPCM 0x0002 +#define RIFF_FORMAT_IMAADPCM 0x0011 + +//****************************************************************************** +// +// This function returns the format of a wav file that has been opened with +// the WavOpen() function. +// +// \param psWavData is the structure that was passed to the WavOpen() function. +// \param psWavHeader is the structure to fill with the format of the wav file. +// +// This function is used to get the audio format of a file that was opened +// with the WavOpen() function. The \e psWavData parameter should be the +// same structure that was passed to the WavOpen() function. The +// \e psWavHeader function will be filled with the format of the open file if +// the \e psWavData is a valid open file. If this function is called with +// an invalid \e psWavData then the results will be undetermined. +// +// \return None. +// +//****************************************************************************** +void +WavGetFormat(tWavFile *psWavData, tWavHeader *psWavHeader) +{ + // + // Only return data if the file is open. + // + psWavHeader->ui32DataSize = psWavData->sWavHeader.ui32DataSize; + psWavHeader->ui16NumChannels = psWavData->sWavHeader.ui16NumChannels; + psWavHeader->ui32SampleRate = psWavData->sWavHeader.ui32SampleRate; + psWavHeader->ui32AvgByteRate = psWavData->sWavHeader.ui32AvgByteRate; + psWavHeader->ui16BitsPerSample = psWavData->sWavHeader.ui16BitsPerSample; +} + +//****************************************************************************** +// +// This function is called to open and determine if a file is a valid .wav +// file. +// +// \param pcFileName is the null terminated string for the file to open. +// \param psWavData is the structure used to hold the file state information. +// +// This function is used to open a file and determine if it is a valid .wav +// file. The \e pcFileName will be opened and read to look for a valid .wav +// file header and prepared for calling the WavRead() or WavGetFormat() +// functions. When an application is done with the .wav file it should call +// the WavClose() function to free up the file. The function will return +// zero if the function successfully opened a .wav file and a non-zero value +// indicates that the file was a valid .wav file or the file could not be +// opened. +// +// \return A value of zero indicates that the file was successfully opened and +// any other value indicates that the file was not opened. +// +//****************************************************************************** +int +WavOpen(const char *pcFileName, tWavFile *psWavData) +{ + unsigned char pucBuffer[16]; + uint32_t *pui32Buffer; + uint16_t *pui16Buffer; + uint32_t ui32ChunkSize; + uint32_t ui32Count; + + // + // Create some local pointers using in parsing values. + // + pui32Buffer = (uint32_t *)pucBuffer; + pui16Buffer = (uint16_t *)pucBuffer; + + // + // Open the file as read only. + // + if(f_open(&psWavData->i16File, pcFileName, FA_READ) != FR_OK) + { + return(-1); + } + + // + // File is open. + // + psWavData->ui32Flags = WAV_FLAG_FILEOPEN; + + // + // Read the first 12 bytes. + // + if(f_read(&psWavData->i16File, pucBuffer, 12, (UINT *)&ui32Count) != FR_OK) + { + return(-1); + } + + // + // Look for RIFF tag. + // + if((pui32Buffer[0] != RIFF_CHUNK_ID_RIFF) || + (pui32Buffer[2] != RIFF_TAG_WAVE)) + { + return(-1); + } + + // + // Read the next chunk header. + // + if(f_read(&psWavData->i16File, pucBuffer, 8, (UINT *)&ui32Count) != FR_OK) + { + return(-1); + } + + // + // Now look for the RIFF ID format tag. + // + if(pui32Buffer[0] != RIFF_CHUNK_ID_FMT) + { + return(-1); + } + + // + // Read the format chunk size and insure that it is 16. + // + ui32ChunkSize = pui32Buffer[1]; + + if(ui32ChunkSize > 16) + { + return(-1); + } + + // + // Read the next chunk header. + // + if(f_read(&psWavData->i16File, pucBuffer, ui32ChunkSize, + (UINT *)&ui32Count) != FR_OK) + { + return(-1); + } + + // + // Save the audio format data so that it can be returned later if + // requested. + // + psWavData->sWavHeader.ui16Format = pui16Buffer[0]; + psWavData->sWavHeader.ui16NumChannels = pui16Buffer[1]; + psWavData->sWavHeader.ui32SampleRate = pui32Buffer[1]; + psWavData->sWavHeader.ui32AvgByteRate = pui32Buffer[2]; + psWavData->sWavHeader.ui16BitsPerSample = pui16Buffer[7]; + + // + // Only mono and stereo supported. + // + if(psWavData->sWavHeader.ui16NumChannels > 2) + { + return(-1); + } + + // + // Read the next chunk header. + // + if(f_read(&psWavData->i16File, pucBuffer, 8, (UINT *)&ui32Count) != FR_OK) + { + return(-1); + } + + // + // Now make sure that the file has a data chunk. + // + if(pui32Buffer[0] != RIFF_CHUNK_ID_DATA) + { + return(-1); + } + + // + // Save the size of the data. + // + psWavData->sWavHeader.ui32DataSize = pui32Buffer[1]; + + return(0); +} + +//****************************************************************************** +// +// This is used to close a .wav file that was opened with WavOpen(). +// +// \param psWavData is the file structure that was passed into the WavOpen() +// function. +// +// This function should be called when a function has completed using a .wav +// file that was opened with the WavOpen() function. This will free up any +// file system data that is held while the file is open. +// +// \return None. +// +//****************************************************************************** +void +WavClose(tWavFile *psWavData) +{ + if(psWavData->ui32Flags & WAV_FLAG_FILEOPEN) + { + // + // Close out the file. + // + f_close(&psWavData->i16File); + + // + // Mark file as no longer open. + // + psWavData->ui32Flags &= ~WAV_FLAG_FILEOPEN; + } +} + +//****************************************************************************** +// +// This function is used to read audio data from a file that was opened with +// the WavOpen() function. +// +// \param psWavData is the file structure that was passed into the WavOpen() +// function. +// \param pucBuffer is the buffer to read data into. +// \param ui32Size is the amount of data to read in bytes. +// +// +// This function handles reading data from a .wav file that was opened with +// the WavOpen() function. The function will return the actual number of +// of bytes read from the file. +// +// \return This function returns the number of bytes read from the file. +// +//****************************************************************************** +uint16_t +WavRead(tWavFile *psWavData, unsigned char *pucBuffer, uint32_t ui32Size) +{ + uint32_t ui32Count; + + // + // Read in another buffer from the file. + // + if(f_read(&psWavData->i16File, pucBuffer, ui32Size, + (UINT *)&ui32Count) != FR_OK) + { + return(0); + } + + return(ui32Count); +} diff --git a/utils/wavfile.h b/utils/wavfile.h new file mode 100644 index 0000000..f044301 --- /dev/null +++ b/utils/wavfile.h @@ -0,0 +1,97 @@ +//***************************************************************************** +// +// wavfile.h - This file supports reading audio data from a .wav file and +// reading the file format. +// +// 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 Utility Library. +// +//***************************************************************************** + +#ifndef WAVEFILE_H_ +#define WAVEFILE_H_ + +//***************************************************************************** +// +// The wav file header information. +// +//***************************************************************************** +typedef struct +{ + // + // Sample rate in bytes per second. + // + uint32_t ui32SampleRate; + + // + // The average byte rate for the wav file. + // + uint32_t ui32AvgByteRate; + + // + // The size of the wav data in the file. + // + uint32_t ui32DataSize; + + // + // The number of bits per sample. + // + uint16_t ui16BitsPerSample; + + // + // The wav file format. + // + uint16_t ui16Format; + + // + // The number of audio channels. + // + uint16_t ui16NumChannels; +} +tWavHeader; + +//***************************************************************************** +// +// The structure used to hold the wav file state. +// +//***************************************************************************** +typedef struct +{ + // + // The wav files header information + // + tWavHeader sWavHeader; + + // + // The file information for the current file. + // + FIL i16File; + + // + // Current state flags, a combination of the WAV_FLAG_* values. + // + uint32_t ui32Flags; +} tWavFile; + +void WavGetFormat(tWavFile *psWavData, tWavHeader *psWaveHeader); +int WavOpen(const char *pcFileName, tWavFile *psWavData); +void WavClose(tWavFile *psWavData); +uint16_t WavRead(tWavFile *psWavData, unsigned char *pucBuffer, + uint32_t ui32Size); + +#endif -- cgit v1.3.1