From 4ba8614c006f9828f0796c140bc3e13c9e67938c Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:06:01 +0200 Subject: Added utils --- utils/cmdline.c | 183 ++++ utils/cmdline.h | 135 +++ utils/cpu_usage.c | 203 +++++ utils/cpu_usage.h | 57 ++ utils/crc.c | 756 ++++++++++++++++ utils/crc.h | 65 ++ utils/flash_pb.c | 486 ++++++++++ utils/flash_pb.h | 58 ++ utils/isqrt.c | 117 +++ utils/isqrt.h | 55 ++ utils/ringbuf.c | 709 +++++++++++++++ utils/ringbuf.h | 105 +++ utils/scheduler.c | 304 +++++++ utils/scheduler.h | 140 +++ utils/sine.c | 125 +++ utils/sine.h | 85 ++ utils/softi2c.c | 1316 +++++++++++++++++++++++++++ utils/softi2c.h | 195 ++++ utils/softssi.c | 1288 ++++++++++++++++++++++++++ utils/softssi.h | 280 ++++++ utils/softuart.c | 2584 +++++++++++++++++++++++++++++++++++++++++++++++++++++ utils/softuart.h | 374 ++++++++ utils/uartstdio.c | 1732 +++++++++++++++++++++++++++++++++++ utils/uartstdio.h | 85 ++ utils/ustdlib.c | 1610 +++++++++++++++++++++++++++++++++ utils/ustdlib.h | 130 +++ utils/utils.sgxx | Bin 0 -> 744 bytes 27 files changed, 13177 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/crc.c create mode 100644 utils/crc.h create mode 100644 utils/flash_pb.c create mode 100644 utils/flash_pb.h create mode 100644 utils/isqrt.c create mode 100644 utils/isqrt.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/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/uartstdio.c create mode 100644 utils/uartstdio.h create mode 100644 utils/ustdlib.c create mode 100644 utils/ustdlib.h create mode 100644 utils/utils.sgxx (limited to 'utils') diff --git a/utils/cmdline.c b/utils/cmdline.c new file mode 100644 index 0000000..67744c2 --- /dev/null +++ b/utils/cmdline.c @@ -0,0 +1,183 @@ +//***************************************************************************** +// +// cmdline.c - Functions to help with processing command lines. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup cmdline_api +//! @{ +// +//***************************************************************************** + +#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 + +//***************************************************************************** +// +//! 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_sCmdTable which +//! must be provided by the application. +//! +//! \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) +{ + static char *argv[CMDLINE_MAX_ARGS + 1]; + char *pcChar; + int argc; + int bFindArg = 1; + tCmdLineEntry *pCmdEntry; + + // + // Initialize the argument counter, and point to the beginning of the + // command line string. + // + argc = 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 = 1; + } + + // + // 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(argc < CMDLINE_MAX_ARGS) + { + argv[argc] = pcChar; + argc++; + bFindArg = 0; + } + + // + // 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(argc) + { + // + // Start at the beginning of the command table, to look for a matching + // command. + // + pCmdEntry = &g_sCmdTable[0]; + + // + // Search through the command table until a null command string is + // found, which marks the end of the table. + // + while(pCmdEntry->pcCmd) + { + // + // If this command entry command string matches argv[0], then call + // the function for this command, passing the command line + // arguments. + // + if(!strcmp(argv[0], pCmdEntry->pcCmd)) + { + return(pCmdEntry->pfnCmd(argc, argv)); + } + + // + // Not found, so advance to the next entry. + // + pCmdEntry++; + } + } + + // + // 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..581601a --- /dev/null +++ b/utils/cmdline.h @@ -0,0 +1,135 @@ +//***************************************************************************** +// +// cmdline.h - Prototypes for command line processing functions. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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. +// +//***************************************************************************** +extern tCmdLineEntry g_sCmdTable[]; + +//***************************************************************************** +// +// 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..160e61c --- /dev/null +++ b/utils/cpu_usage.c @@ -0,0 +1,203 @@ +//***************************************************************************** +// +// cpu_usage.c - Routines to determine the CPU utilization. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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 unsigned long g_pulCPUUsageTimerPeriph[4] = +{ + SYSCTL_PERIPH_TIMER0, SYSCTL_PERIPH_TIMER1, SYSCTL_PERIPH_TIMER2, + SYSCTL_PERIPH_TIMER3 +}; + +//***************************************************************************** +// +// The base address of the timer modules that could be used for tracking CPU +// utilization. +// +//***************************************************************************** +static unsigned long g_pulCPUUsageTimerBase[4] = +{ + TIMER0_BASE, TIMER1_BASE, TIMER2_BASE, TIMER3_BASE +}; + +//***************************************************************************** +// +// The index of the timer module that will be used for tracking CPU +// utilization. +// +//***************************************************************************** +static unsigned long g_ulCPUUsageTimer; + +//***************************************************************************** +// +// The number of processor clock ticks per timing period. +// +//***************************************************************************** +static unsigned long g_ulCPUUsageTicks; + +//***************************************************************************** +// +// 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 unsigned long g_ulCPUUsagePrevious; + +//***************************************************************************** +// +//! 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. +// +//***************************************************************************** +unsigned long +CPUUsageTick(void) +{ + unsigned long ulValue, ulUsage; + + // + // Get the current value of the timer. + // + ulValue = MAP_TimerValueGet(g_pulCPUUsageTimerBase[g_ulCPUUsageTimer], + 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. + // + ulUsage = ((((g_ulCPUUsagePrevious - ulValue) * 6400) / + g_ulCPUUsageTicks) * 1024); + + // + // Save the previous value of the timer. + // + g_ulCPUUsagePrevious = ulValue; + + // + // Return the new CPU usage value. + // + return(ulUsage); +} + +//***************************************************************************** +// +//! Initializes the CPU usage measurement module. +//! +//! \param ulClockRate is the rate of the clock supplied to the timer module. +//! \param ulRate is the number of times per second that CPUUsageTick() is +//! called. +//! \param ulTimer 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(unsigned long ulClockRate, unsigned long ulRate, + unsigned long ulTimer) +{ + // + // Check the arguments. + // + ASSERT(ulClockRate > ulRate); + ASSERT(ulTimer < 4); + + // + // Save the timer index. + // + g_ulCPUUsageTimer = ulTimer; + + // + // Determine the number of system clocks per measurement period. + // + g_ulCPUUsageTicks = ulClockRate / ulRate; + + // + // Set the previous value of the timer to the initial timer value. + // + g_ulCPUUsagePrevious = 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_pulCPUUsageTimerPeriph[ulTimer]); + MAP_SysCtlPeripheralSleepDisable(g_pulCPUUsageTimerPeriph[ulTimer]); + + // + // Configure the third timer for 32-bit periodic operation. + // + MAP_TimerConfigure(g_pulCPUUsageTimerBase[ulTimer], TIMER_CFG_PERIODIC); + + // + // Set the load value for the third timer to the maximum value. + // + MAP_TimerLoadSet(g_pulCPUUsageTimerBase[ulTimer], 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_pulCPUUsageTimerBase[ulTimer], TIMER_A); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/cpu_usage.h b/utils/cpu_usage.h new file mode 100644 index 0000000..55a6eba --- /dev/null +++ b/utils/cpu_usage.h @@ -0,0 +1,57 @@ +//***************************************************************************** +// +// cpu_usage.h - Prototypes for the CPU utilization routines. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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 unsigned long CPUUsageTick(void); +extern void CPUUsageInit(unsigned long ulClockRate, unsigned long ulRate, + unsigned long ulTimer); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __CPU_USAGE_H__ diff --git a/utils/crc.c b/utils/crc.c new file mode 100644 index 0000000..cf60e81 --- /dev/null +++ b/utils/crc.c @@ -0,0 +1,756 @@ +//***************************************************************************** +// +// crc.c - CRC functions. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup crc_api +//! @{ +// +//***************************************************************************** + +#include "crc.h" + +//***************************************************************************** +// +// The CRC table for the polynomial C(x) = x^8 + x^2 + x + 1 (CRC-8-CCITT). +// +//***************************************************************************** +static const unsigned char g_pucCrc8CCITT[256] = +{ + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +//***************************************************************************** +// +// The CRC-16 table for the polynomial C(x) = x^16 + x^15 + x^2 + 1 (standard +// CRC-16, also known as CRC-16-IBM and CRC-16-ANSI). +// +//***************************************************************************** +static const unsigned short g_pusCrc16[256] = +{ + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +//***************************************************************************** +// +// The CRC-32 table for the polynomial C(x) = x^32 + x^26 + x^23 + x^22 + +// x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 (standard +// CRC32 as used in Ethernet, MPEG-2, PNG, etc.). +// +//***************************************************************************** +const unsigned long g_pulCrc32[] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +//***************************************************************************** +// +// This macro executes one iteration of the CRC-8-CCITT. +// +//***************************************************************************** +#define CRC8_ITER(crc, data) g_pucCrc8CCITT[(unsigned char)((crc) ^ (data))] + +//***************************************************************************** +// +// This macro executes one iteration of the CRC-16. +// +//***************************************************************************** +#define CRC16_ITER(crc, data) (((crc) >> 8) ^ \ + g_pusCrc16[(unsigned char)((crc) ^ (data))]) + +//***************************************************************************** +// +// This macro executes one iteration of the CRC-32. +// +//***************************************************************************** +#define CRC32_ITER(crc, data) (((crc) >> 8) ^ \ + g_pulCrc32[(unsigned char)((crc & 0xFF) ^ \ + (data))]) + +//***************************************************************************** +// +//! Calculates the CRC-8-CCITT of an array of bytes. +//! +//! \param ucCrc is the starting CRC-8-CCITT value. +//! \param pucData is a pointer to the data buffer. +//! \param ulCount is the number of bytes in the data buffer. +//! +//! This function is used to calculate the CRC-8-CCITT of the input buffer. +//! The CRC-8-CCITT is computed in a running fashion, meaning that the entire +//! data block that is to have its CRC-8-CCITT computed does not need to be +//! supplied all at once. If the input buffer contains the entire block of +//! data, then \b ucCrc should be set to 0. If, however, the entire block of +//! data is not available, then \b ucCrc should be set to 0 for the first +//! portion of the data, and then the returned value should be passed back in +//! as \b ucCrc for the next portion of the data. +//! +//! For example, to compute the CRC-8-CCITT of a block that has been split into +//! three pieces, use the following: +//! +//! \verbatim +//! ucCrc = Crc8CCITT(0, pucData1, ulLen1); +//! ucCrc = Crc8CCITT(ucCrc, pucData2, ulLen2); +//! ucCrc = Crc8CCITT(ucCrc, pucData3, ulLen3); +//! \endverbatim +//! +//! Computing a CRC-8-CCITT in a running fashion is useful in cases where the +//! data is arriving via a serial link (for example) and is therefore not all +//! available at one time. +//! +//! \return The CRC-8-CCITT of the input data. +// +//***************************************************************************** +unsigned char +Crc8CCITT(unsigned char ucCrc, const unsigned char *pucData, + unsigned long ulCount) +{ + unsigned long ulTemp; + + // + // If the data buffer is not short-aligned, then perform a single step of + // the CRC to make it short-aligned. + // + if((unsigned long)pucData & 1) + { + // + // Perform the CRC on this input byte. + // + ucCrc = CRC8_ITER(ucCrc, *pucData); + + // + // Skip this input byte. + // + pucData++; + ulCount--; + } + + // + // If the data buffer is not word-aligned and there are at least two bytes + // of data left, then perform two steps of the CRC to make it word-aligned. + // + if(((unsigned long)pucData & 2) && (ulCount > 1)) + { + // + // Read the next short. + // + ulTemp = *(unsigned short *)pucData; + + // + // Perform the CRC on these two bytes. + // + ucCrc = CRC8_ITER(ucCrc, ulTemp); + ucCrc = CRC8_ITER(ucCrc, ulTemp >> 8); + + // + // Skip these input bytes. + // + pucData += 2; + ulCount -= 2; + } + + // + // While there is at least a word remaining in the data buffer, perform + // four steps of the CRC to consume a word. + // + while(ulCount > 3) + { + // + // Read the next word. + // + ulTemp = *(unsigned long *)pucData; + + // + // Perform the CRC on these four bytes. + // + ucCrc = CRC8_ITER(ucCrc, ulTemp); + ucCrc = CRC8_ITER(ucCrc, ulTemp >> 8); + ucCrc = CRC8_ITER(ucCrc, ulTemp >> 16); + ucCrc = CRC8_ITER(ucCrc, ulTemp >> 24); + + // + // Skip these input bytes. + // + pucData += 4; + ulCount -= 4; + } + + // + // If there is a short left in the input buffer, then perform two steps of + // the CRC. + // + if(ulCount > 1) + { + // + // Read the short. + // + ulTemp = *(unsigned short *)pucData; + + // + // Perform the CRC on these two bytes. + // + ucCrc = CRC8_ITER(ucCrc, ulTemp); + ucCrc = CRC8_ITER(ucCrc, ulTemp >> 8); + + // + // Skip these input bytes. + // + pucData += 2; + ulCount -= 2; + } + + // + // If there is a final byte remaining in the input buffer, then perform a + // single step of the CRC. + // + if(ulCount != 0) + { + ucCrc = CRC8_ITER(ucCrc, *pucData); + } + + // + // Return the resulting CRC-8-CCITT value. + // + return(ucCrc); +} + +//***************************************************************************** +// +//! Calculates the CRC-16 of an array of bytes. +//! +//! \param usCrc is the starting CRC-16 value. +//! \param pucData is a pointer to the data buffer. +//! \param ulCount is the number of bytes in the data buffer. +//! +//! This function is used to calculate the CRC-16 of the input buffer. The +//! CRC-16 is computed in a running fashion, meaning that the entire data block +//! that is to have its CRC-16 computed does not need to be supplied all at +//! once. If the input buffer contains the entire block of data, then \b usCrc +//! should be set to 0. If, however, the entire block of data is not +//! available, then \b usCrc should be set to 0 for the first portion of the +//! data, and then the returned value should be passed back in as \b usCrc for +//! the next portion of the data. +//! +//! For example, to compute the CRC-16 of a block that has been split into +//! three pieces, use the following: +//! +//! \verbatim +//! usCrc = Crc16(0, pucData1, ulLen1); +//! usCrc = Crc16(usCrc, pucData2, ulLen2); +//! usCrc = Crc16(usCrc, pucData3, ulLen3); +//! \endverbatim +//! +//! Computing a CRC-16 in a running fashion is useful in cases where the data +//! is arriving via a serial link (for example) and is therefore not all +//! available at one time. +//! +//! \return The CRC-16 of the input data. +// +//***************************************************************************** +unsigned short +Crc16(unsigned short usCrc, const unsigned char *pucData, + unsigned long ulCount) +{ + unsigned long ulTemp; + + // + // If the data buffer is not short-aligned, then perform a single step of + // the CRC to make it short-aligned. + // + if((unsigned long)pucData & 1) + { + // + // Perform the CRC on this input byte. + // + usCrc = CRC16_ITER(usCrc, *pucData); + + // + // Skip this input byte. + // + pucData++; + ulCount--; + } + + // + // If the data buffer is not word-aligned and there are at least two bytes + // of data left, then perform two steps of the CRC to make it word-aligned. + // + if(((unsigned long)pucData & 2) && (ulCount > 1)) + { + // + // Read the next short. + // + ulTemp = *(unsigned short *)pucData; + + // + // Perform the CRC on these two bytes. + // + usCrc = CRC16_ITER(usCrc, ulTemp); + usCrc = CRC16_ITER(usCrc, ulTemp >> 8); + + // + // Skip these input bytes. + // + pucData += 2; + ulCount -= 2; + } + + // + // While there is at least a word remaining in the data buffer, perform + // four steps of the CRC to consume a word. + // + while(ulCount > 3) + { + // + // Read the next word. + // + ulTemp = *(unsigned long *)pucData; + + // + // Perform the CRC on these four bytes. + // + usCrc = CRC16_ITER(usCrc, ulTemp); + usCrc = CRC16_ITER(usCrc, ulTemp >> 8); + usCrc = CRC16_ITER(usCrc, ulTemp >> 16); + usCrc = CRC16_ITER(usCrc, ulTemp >> 24); + + // + // Skip these input bytes. + // + pucData += 4; + ulCount -= 4; + } + + // + // If there is a short left in the input buffer, then perform two steps of + // the CRC. + // + if(ulCount > 1) + { + // + // Read the short. + // + ulTemp = *(unsigned short *)pucData; + + // + // Perform the CRC on these two bytes. + // + usCrc = CRC16_ITER(usCrc, ulTemp); + usCrc = CRC16_ITER(usCrc, ulTemp >> 8); + + // + // Skip these input bytes. + // + pucData += 2; + ulCount -= 2; + } + + // + // If there is a final byte remaining in the input buffer, then perform a + // single step of the CRC. + // + if(ulCount != 0) + { + usCrc = CRC16_ITER(usCrc, *pucData); + } + + // + // Return the resulting CRC-16 value. + // + return(usCrc); +} + +//***************************************************************************** +// +//! Calculates the CRC-16 of an array of words. +//! +//! \param ulWordLen is the length of the array in words (the number of bytes +//! divided by 4). +//! \param pulData is a pointer to the data buffer. +//! +//! This function is a wrapper around the running CRC-16 function, providing +//! the CRC-16 for a single block of data. +//! +//! \return The CRC-16 of the input data. +// +//***************************************************************************** +unsigned short +Crc16Array(unsigned long ulWordLen, const unsigned long *pulData) +{ + // + // Calculate and return the CRC-16 of this array of words. + // + return(Crc16(0, (const unsigned char *)pulData, ulWordLen * 4)); +} + +//***************************************************************************** +// +//! Calculates three CRC-16s of an array of words. +//! +//! \param ulWordLen is the length of the array in words (the number of bytes +//! divided by 4). +//! \param pulData is a pointer to the data buffer. +//! \param pusCrc3 is a pointer to an array in which to place the three CRC-16 +//! values. +//! +//! This function is used to calculate three CRC-16s of the input buffer; the +//! first uses every byte from the array, the second uses only the even-index +//! bytes from the array (in other words, bytes 0, 2, 4, etc.), and the third +//! uses only the odd-index bytes from the array (in other words, bytes 1, 3, +//! 5, etc.). +//! +//! \return None +// +//***************************************************************************** +void +Crc16Array3(unsigned long ulWordLen, const unsigned long *pulData, + unsigned short *pusCrc3) +{ + unsigned short usCrc, usCrcOdd, usCrcEven; + unsigned long ulTemp; + + // + // Initialize the CRC values to zero. + // + usCrc = 0; + usCrcOdd = 0; + usCrcEven = 0; + + // + // Loop while there are more words in the data buffer. + // + while(ulWordLen--) + { + // + // Read the next word. + // + ulTemp = *pulData++; + + // + // Perform the first CRC on all four data bytes. + // + usCrc = CRC16_ITER(usCrc, ulTemp); + usCrc = CRC16_ITER(usCrc, ulTemp >> 8); + usCrc = CRC16_ITER(usCrc, ulTemp >> 16); + usCrc = CRC16_ITER(usCrc, ulTemp >> 24); + + // + // Perform the second CRC on only the even-index data bytes. + // + usCrcEven = CRC16_ITER(usCrcEven, ulTemp); + usCrcEven = CRC16_ITER(usCrcEven, ulTemp >> 16); + + // + // Perform the third CRC on only the odd-index data bytes. + // + usCrcOdd = CRC16_ITER(usCrcOdd, ulTemp >> 8); + usCrcOdd = CRC16_ITER(usCrcOdd, ulTemp >> 24); + } + + // + // Return the resulting CRC-16 values. + // + pusCrc3[0] = usCrc; + pusCrc3[1] = usCrcEven; + pusCrc3[2] = usCrcOdd; +} + +//***************************************************************************** +// +//! Calculates the CRC-32 of an array of bytes. +//! +//! \param ulCrc is the starting CRC-32 value. +//! \param pucData is a pointer to the data buffer. +//! \param ulCount is the number of bytes in the data buffer. +//! +//! This function is used to calculate the CRC-32 of the input buffer. The +//! CRC-32 is computed in a running fashion, meaning that the entire data block +//! that is to have its CRC-32 computed does not need to be supplied all at +//! once. If the input buffer contains the entire block of data, then \b ulCrc +//! should be set to 0xFFFFFFFF. If, however, the entire block of data is not +//! available, then \b ulCrc should be set to 0xFFFFFFFF for the first portion +//! of the data, and then the returned value should be passed back in as \b +//! ulCrc for the next portion of the data. Once all data has been passed +//! to the function, the final CRC-32 can be obtained by inverting the last +//! returned value. +//! +//! For example, to compute the CRC-32 of a block that has been split into +//! three pieces, use the following: +//! +//! \verbatim +//! ulCrc = Crc32(0xFFFFFFFF, pucData1, ulLen1); +//! ulCrc = Crc32(ulCrc, pucData2, ulLen2); +//! ulCrc = Crc32(ulCrc, pucData3, ulLen3); +//! ulCrc ^= 0xFFFFFFFF; +//! \endverbatim +//! +//! Computing a CRC-32 in a running fashion is useful in cases where the data +//! is arriving via a serial link (for example) and is therefore not all +//! available at one time. +//! +//! \return The accumulated CRC-32 of the input data. +// +//***************************************************************************** +unsigned long +Crc32(unsigned long ulCrc, const unsigned char *pucData, unsigned long ulCount) +{ + unsigned long ulTemp; + + // + // If the data buffer is not short-aligned, then perform a single step of + // the CRC to make it short-aligned. + // + if((unsigned long)pucData & 1) + { + // + // Perform the CRC on this input byte. + // + ulCrc = CRC32_ITER(ulCrc, *pucData); + + // + // Skip this input byte. + // + pucData++; + ulCount--; + } + + // + // If the data buffer is not word-aligned and there are at least two bytes + // of data left, then perform two steps of the CRC to make it word-aligned. + // + if(((unsigned long)pucData & 2) && (ulCount > 1)) + { + // + // Read the next short. + // + ulTemp = *(unsigned short *)pucData; + + // + // Perform the CRC on these two bytes. + // + ulCrc = CRC32_ITER(ulCrc, ulTemp); + ulCrc = CRC32_ITER(ulCrc, ulTemp >> 8); + + // + // Skip these input bytes. + // + pucData += 2; + ulCount -= 2; + } + + // + // While there is at least a word remaining in the data buffer, perform + // four steps of the CRC to consume a word. + // + while(ulCount > 3) + { + // + // Read the next word. + // + ulTemp = *(unsigned long *)pucData; + + // + // Perform the CRC on these four bytes. + // + ulCrc = CRC32_ITER(ulCrc, ulTemp); + ulCrc = CRC32_ITER(ulCrc, ulTemp >> 8); + ulCrc = CRC32_ITER(ulCrc, ulTemp >> 16); + ulCrc = CRC32_ITER(ulCrc, ulTemp >> 24); + + // + // Skip these input bytes. + // + pucData += 4; + ulCount -= 4; + } + + // + // If there is a short left in the input buffer, then perform two steps of + // the CRC. + // + if(ulCount > 1) + { + // + // Read the short. + // + ulTemp = *(unsigned short *)pucData; + + // + // Perform the CRC on these two bytes. + // + ulCrc = CRC32_ITER(ulCrc, ulTemp); + ulCrc = CRC32_ITER(ulCrc, ulTemp >> 8); + + // + // Skip these input bytes. + // + pucData += 2; + ulCount -= 2; + } + + // + // If there is a final byte remaining in the input buffer, then perform a + // single step of the CRC. + // + if(ulCount != 0) + { + ulCrc = CRC32_ITER(ulCrc, *pucData); + } + + // + // Return the resulting CRC-16 value. + // + return(ulCrc); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/crc.h b/utils/crc.h new file mode 100644 index 0000000..432a558 --- /dev/null +++ b/utils/crc.h @@ -0,0 +1,65 @@ +//***************************************************************************** +// +// crc.h - Prototypes for the CRC functions. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#ifndef __CRC_H__ +#define __CRC_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 functions. +// +//***************************************************************************** +extern unsigned char Crc8CCITT(unsigned char ucCrc, + const unsigned char *pucData, + unsigned long ulCount); +extern unsigned short Crc16(unsigned short usCrc, const unsigned char *pucData, + unsigned long ulCount); +extern unsigned short Crc16Array(unsigned long ulWordLen, + const unsigned long *pulData); +extern void Crc16Array3(unsigned long ulWordLen, const unsigned long *pulData, + unsigned short *pusCrc3); +extern unsigned long Crc32(unsigned long ulCrc, const unsigned char *pucData, + unsigned long ulCount); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __CRC_H__ diff --git a/utils/flash_pb.c b/utils/flash_pb.c new file mode 100644 index 0000000..e057120 --- /dev/null +++ b/utils/flash_pb.c @@ -0,0 +1,486 @@ +//***************************************************************************** +// +// flash_pb.c - Flash parameter block functions. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#include "inc/hw_flash.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/flash.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 unsigned char *g_pucFlashPBStart; + +//***************************************************************************** +// +// 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 unsigned char *g_pucFlashPBEnd; + +//***************************************************************************** +// +// The size of the parameter block when stored in flash; this must be a power +// of two less than or equal to 1024. +// +//***************************************************************************** +static unsigned long g_ulFlashPBSize; + +//***************************************************************************** +// +// The address of the most recent parameter block in flash. +// +//***************************************************************************** +static unsigned char *g_pucFlashPBCurrent; + +//***************************************************************************** +// +//! Determines if the parameter block at the given address is valid. +//! +//! \param pucOffset 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 unsigned long +FlashPBIsValid(unsigned char *pucOffset) +{ + unsigned long ulIdx, ulSum; + + // + // Check the arguments. + // + ASSERT(pucOffset != (void *)0); + + // + // Loop through the bytes in the block, computing the checksum. + // + for(ulIdx = 0, ulSum = 0; ulIdx < g_ulFlashPBSize; ulIdx++) + { + ulSum += pucOffset[ulIdx]; + } + + // + // The checksum should be zero, so return a failure if it is not. + // + if((ulSum & 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_ulFlashPBSize * 255) == ulSum) + { + 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. +// +//***************************************************************************** +unsigned char * +FlashPBGet(void) +{ + // + // See if there is a valid parameter block. + // + if(g_pucFlashPBCurrent) + { + // + // Return the address of the most recent parameter block. + // + return(g_pucFlashPBCurrent); + } + + // + // There are no valid parameter blocks in flash, so return NULL. + // + return(0); +} + +//***************************************************************************** +// +//! Writes a new parameter block to flash. +//! +//! \param pucBuffer 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(unsigned char *pucBuffer) +{ + unsigned char *pucNew; + unsigned long ulIdx, ulSum; + + // + // Check the arguments. + // + ASSERT(pucBuffer != (void *)0); + + // + // See if there is a valid parameter block in flash. + // + if(g_pucFlashPBCurrent) + { + // + // Set the sequence number to one greater than the most recent + // parameter block. + // + pucBuffer[0] = g_pucFlashPBCurrent[0] + 1; + + // + // Try to write the new parameter block immediately after the most + // recent parameter block. + // + pucNew = g_pucFlashPBCurrent + g_ulFlashPBSize; + if(pucNew == g_pucFlashPBEnd) + { + pucNew = g_pucFlashPBStart; + } + } + else + { + // + // There is not a valid parameter block in flash, so set the sequence + // number of this parameter block to zero. + // + pucBuffer[0] = 0; + + // + // Try to write the new parameter block at the beginning of the flash + // space for parameter blocks. + // + pucNew = g_pucFlashPBStart; + } + + // + // Compute the checksum of the parameter block to be written. + // + for(ulIdx = 0, ulSum = 0; ulIdx < g_ulFlashPBSize; ulIdx++) + { + ulSum -= pucBuffer[ulIdx]; + } + + // + // Store the checksum into the parameter block. + // + pucBuffer[1] += ulSum; + + // + // 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(((unsigned long)pucNew & 1023) == 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. + // + FlashErase((unsigned long)pucNew); + } + + // + // Loop through this portion of flash to see if is all ones (i.e. it + // is an erased portion of flash). + // + for(ulIdx = 0; ulIdx < g_ulFlashPBSize; ulIdx++) + { + if(pucNew[ulIdx] != 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(ulIdx == g_ulFlashPBSize) + { + break; + } + + // + // Increment to the next parameter block location. + // + pucNew += g_ulFlashPBSize; + if(pucNew == g_pucFlashPBEnd) + { + pucNew = g_pucFlashPBStart; + } + + // + // 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_pucFlashPBCurrent && (pucNew == g_pucFlashPBCurrent)) || + (!g_pucFlashPBCurrent && (pucNew == g_pucFlashPBStart))) + { + return; + } + } + + // + // Write this parameter block to flash. + // + FlashProgram((unsigned long *)pucBuffer, (unsigned long)pucNew, + g_ulFlashPBSize); + + // + // 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(ulIdx = 0; ulIdx < g_ulFlashPBSize; ulIdx++) + { + if(pucNew[ulIdx] != pucBuffer[ulIdx]) + { + return; + } + } + + // + // The new parameter block becomes the most recent parameter block. + // + g_pucFlashPBCurrent = pucNew; +} + +//***************************************************************************** +// +//! Initializes the flash parameter block. +//! +//! \param ulStart 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 ulEnd 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 ulSize 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 ulStart and \e ulEnd 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 ulSize). 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 ulStart and \e ulEnd 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 ulSize) 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 ulEnd - \e ulStart) +//! divided by the parameter block size (\e ulSize) 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(unsigned long ulStart, unsigned long ulEnd, unsigned long ulSize) +{ + unsigned char *pucOffset, *pucCurrent; + unsigned char ucOne, ucTwo; + + // + // Check the arguments. + // + ASSERT((ulStart % FLASH_ERASE_SIZE) == 0); + ASSERT((ulEnd % FLASH_ERASE_SIZE) == 0); + ASSERT((FLASH_ERASE_SIZE % ulSize) == 0); + + // + // Set the number of clocks per microsecond to enable the flash controller + // to properly program the flash. + // + FlashUsecSet(SysCtlClockGet() / 1000000); + + // + // Save the characteristics of the flash memory to be used for storing + // parameter blocks. + // + g_pucFlashPBStart = (unsigned char *)ulStart; + g_pucFlashPBEnd = (unsigned char *)ulEnd; + g_ulFlashPBSize = ulSize; + + // + // Loop through the portion of flash memory used for storing parameter + // blocks. + // + for(pucOffset = g_pucFlashPBStart, pucCurrent = 0; + pucOffset < g_pucFlashPBEnd; pucOffset += g_ulFlashPBSize) + { + // + // See if this is a valid parameter block (i.e. the checksum is + // correct). + // + if(FlashPBIsValid(pucOffset)) + { + // + // See if a valid parameter block has been previously found. + // + if(pucCurrent != 0) + { + // + // Get the sequence numbers for the current and new parameter + // blocks. + // + ucOne = pucCurrent[0]; + ucTwo = pucOffset[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(((ucOne > ucTwo) && ((ucOne - ucTwo) < 128)) || + ((ucTwo > ucOne) && ((ucTwo - ucOne) > 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. + // + pucCurrent = pucOffset; + } + } + + // + // Save the address of the most recent parameter block found. If no valid + // parameter blocks were found, this will be a NULL pointer. + // + g_pucFlashPBCurrent = pucCurrent; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/flash_pb.h b/utils/flash_pb.h new file mode 100644 index 0000000..8d8afbf --- /dev/null +++ b/utils/flash_pb.h @@ -0,0 +1,58 @@ +//***************************************************************************** +// +// flash_pb.h - Prototypes for the flash parameter block functions. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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 unsigned char *FlashPBGet(void); +extern void FlashPBSave(unsigned char *pucBuffer); +extern void FlashPBInit(unsigned long ulStart, unsigned long ulEnd, + unsigned long ulSize); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __FLASH_PB_H__ diff --git a/utils/isqrt.c b/utils/isqrt.c new file mode 100644 index 0000000..d347e1e --- /dev/null +++ b/utils/isqrt.c @@ -0,0 +1,117 @@ +//***************************************************************************** +// +// isqrt.c - Integer square root. +// +// Copyright (c) 2005-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#include "utils/isqrt.h" + +//***************************************************************************** +// +//! \addtogroup isqrt_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Compute the integer square root of an integer. +//! +//! \param ulValue 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. +// +//***************************************************************************** +unsigned long +isqrt(unsigned long ulValue) +{ + unsigned long ulRem, ulRoot, ulIdx; + + // + // Initialize the remainder and root to zero. + // + ulRem = 0; + ulRoot = 0; + + // + // Loop over the sixteen bits in the root. + // + for(ulIdx = 0; ulIdx < 16; ulIdx++) + { + // + // Shift the root up by a bit to make room for the new bit that is + // about to be computed. + // + ulRoot <<= 1; + + // + // Get two more bits from the input into the remainder. + // + ulRem = ((ulRem << 2) + (ulValue >> 30)); + ulValue <<= 2; + + // + // Make the test root be 2n + 1. + // + ulRoot++; + + // + // See if the root is greater than the remainder. + // + if(ulRoot <= ulRem) + { + // + // Subtract the test root from the remainder. + // + ulRem -= ulRoot; + + // + // Increment the root, setting the second LSB. + // + ulRoot++; + } + else + { + // + // The root is greater than the remainder, so the new bit of the + // root is actually zero. + // + ulRoot--; + } + } + + // + // Return the computed root. + // + return(ulRoot >> 1); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/isqrt.h b/utils/isqrt.h new file mode 100644 index 0000000..f25f42b --- /dev/null +++ b/utils/isqrt.h @@ -0,0 +1,55 @@ +//***************************************************************************** +// +// isqrt.h - Prototype for the integer square root function. +// +// Copyright (c) 2006-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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 unsigned long isqrt(unsigned long ulValue); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/utils/ringbuf.c b/utils/ringbuf.c new file mode 100644 index 0000000..ff49f9e --- /dev/null +++ b/utils/ringbuf.c @@ -0,0 +1,709 @@ +//***************************************************************************** +// +// ringbuf.c - Ring buffer management utilities. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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 pulVal points to the index whose value is to be modified. +// \param ulDelta is the number of bytes to increment the index by. +// \param ulSize 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 unsigned long *pulVal, unsigned long ulDelta, + unsigned long ulSize) +{ + tBoolean bIntsOff; + + // + // Turn interrupts off temporarily. + // + bIntsOff = IntMasterDisable(); + + // + // Update the variable value. + // + *pulVal += ulDelta; + + // + // 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 ulDelta is greater than ulSize (which is extremely unlikely but...) + // + while(*pulVal >= ulSize) + { + *pulVal -= ulSize; + } + + // + // Restore the interrupt state + // + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is full or not. +//! +//! \param ptRingBuf 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. +// +//***************************************************************************** +tBoolean +RingBufFull(tRingBufObject *ptRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptRingBuf->ulWriteIndex; + ulRead = ptRingBuf->ulReadIndex; + + // + // Return the full status of the buffer. + // + return((((ulWrite + 1) % ptRingBuf->ulSize) == ulRead) ? true : false); +} + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is empty or not. +//! +//! \param ptRingBuf 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. +// +//***************************************************************************** +tBoolean +RingBufEmpty(tRingBufObject *ptRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptRingBuf->ulWriteIndex; + ulRead = ptRingBuf->ulReadIndex; + + // + // Return the empty status of the buffer. + // + return((ulWrite == ulRead) ? true : false); +} + +//***************************************************************************** +// +//! Empties the ring buffer. +//! +//! \param ptRingBuf is the ring buffer object to empty. +//! +//! Discards all data from the ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufFlush(tRingBufObject *ptRingBuf) +{ + tBoolean bIntsOff; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != 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(); + ptRingBuf->ulReadIndex = ptRingBuf->ulWriteIndex; + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Returns number of bytes stored in ring buffer. +//! +//! \param ptRingBuf 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. +// +//***************************************************************************** +unsigned long +RingBufUsed(tRingBufObject *ptRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptRingBuf->ulWriteIndex; + ulRead = ptRingBuf->ulReadIndex; + + // + // Return the number of bytes contained in the ring buffer. + // + return((ulWrite >= ulRead) ? (ulWrite - ulRead) : + (ptRingBuf->ulSize - (ulRead - ulWrite))); +} + +//***************************************************************************** +// +//! Returns number of bytes available in a ring buffer. +//! +//! \param ptRingBuf 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. +// +//***************************************************************************** +unsigned long +RingBufFree(tRingBufObject *ptRingBuf) +{ + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return((ptRingBuf->ulSize - 1) - RingBufUsed(ptRingBuf)); +} + +//***************************************************************************** +// +//! Returns number of contiguous bytes of data stored in ring buffer ahead of +//! the current read pointer. +//! +//! \param ptRingBuf 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. +// +//***************************************************************************** +unsigned long +RingBufContigUsed(tRingBufObject *ptRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptRingBuf->ulWriteIndex; + ulRead = ptRingBuf->ulReadIndex; + + // + // Return the number of contiguous bytes available. + // + return((ulWrite >= ulRead) ? (ulWrite - ulRead) : + (ptRingBuf->ulSize - ulRead)); +} + +//***************************************************************************** +// +//! Returns number of contiguous free bytes available in a ring buffer. +//! +//! \param ptRingBuf 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. +// +//***************************************************************************** +unsigned long +RingBufContigFree(tRingBufObject *ptRingBuf) +{ + unsigned long ulWrite; + unsigned long ulRead; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Copy the Read/Write indices for calculation. + // + ulWrite = ptRingBuf->ulWriteIndex; + ulRead = ptRingBuf->ulReadIndex; + + // + // Return the number of contiguous bytes available. + // + if(ulRead > ulWrite) + { + // + // 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((ulRead - ulWrite) - 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(ptRingBuf->ulSize - ulWrite - ((ulRead == 0) ? 1 : 0)); + } +} + +//***************************************************************************** +// +//! Return size in bytes of a ring buffer. +//! +//! \param ptRingBuf 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. +// +//***************************************************************************** +unsigned long +RingBufSize(tRingBufObject *ptRingBuf) +{ + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Return the number of bytes available in the ring buffer. + // + return(ptRingBuf->ulSize); +} + +//***************************************************************************** +// +//! Reads a single byte of data from a ring buffer. +//! +//! \param ptRingBuf 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. +// +//***************************************************************************** +unsigned char +RingBufReadOne(tRingBufObject *ptRingBuf) +{ + unsigned char ucTemp; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(RingBufUsed(ptRingBuf) != 0); + + // + // Write the data byte. + // + ucTemp = ptRingBuf->pucBuf[ptRingBuf->ulReadIndex]; + + // + // Increment the read index. + // + UpdateIndexAtomic(&ptRingBuf->ulReadIndex, 1, ptRingBuf->ulSize); + + // + // Return the character read. + // + return(ucTemp); +} + +//***************************************************************************** +// +//! Reads data from a ring buffer. +//! +//! \param ptRingBuf points to the ring buffer to be read from. +//! \param pucData points to where the data should be stored. +//! \param ulLength is the number of bytes to be read. +//! +//! This function reads a sequence of bytes from a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufRead(tRingBufObject *ptRingBuf, unsigned char *pucData, + unsigned long ulLength) +{ + unsigned long ulTemp; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + ASSERT(pucData != NULL); + ASSERT(ulLength != 0); + + // + // Verify that data is available in the buffer. + // + ASSERT(ulLength <= RingBufUsed(ptRingBuf)); + + // + // Read the data from the ring buffer. + // + for(ulTemp = 0; ulTemp < ulLength; ulTemp++) + { + pucData[ulTemp] = RingBufReadOne(ptRingBuf); + } +} + +//***************************************************************************** +// +//! Remove bytes from the ring buffer by advancing the read index. +//! +//! \param ptRingBuf points to the ring buffer from which bytes are to be +//! removed. +//! \param ulNumBytes 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 +//! ulNumBytes is larger than the number of bytes currently in the buffer, the +//! buffer is emptied. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufAdvanceRead(tRingBufObject *ptRingBuf, + unsigned long ulNumBytes) +{ + unsigned long ulCount; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Make sure that we are not being asked to remove more data than is + // there to be removed. + // + ulCount = RingBufUsed(ptRingBuf); + ulCount = (ulCount < ulNumBytes) ? ulCount : ulNumBytes; + + // + // Advance the buffer read index by the required number of bytes. + // + UpdateIndexAtomic(&ptRingBuf->ulReadIndex, ulCount, + ptRingBuf->ulSize); +} + +//***************************************************************************** +// +//! Add bytes to the ring buffer by advancing the write index. +//! +//! \param ptRingBuf points to the ring buffer to which bytes have been added. +//! \param ulNumBytes 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 ulNumBytes +//! 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 *ptRingBuf, + unsigned long ulNumBytes) +{ + unsigned long ulCount; + tBoolean bIntsOff; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Make sure we were not asked to add a silly number of bytes. + // + ASSERT(ulNumBytes <= ptRingBuf->ulSize); + + // + // Determine how much free space we currently think the buffer has. + // + ulCount = RingBufFree(ptRingBuf); + + // + // 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. + // + ptRingBuf->ulWriteIndex += ulNumBytes; + + // + // Check and correct for wrap. + // + if(ptRingBuf->ulWriteIndex >= ptRingBuf->ulSize) + { + ptRingBuf->ulWriteIndex -= ptRingBuf->ulSize; + } + + // + // Did the client add more bytes than the buffer had free space for? + // + if(ulCount < ulNumBytes) + { + // + // Yes - we need to advance the read pointer to ahead of the write + // pointer to discard some of the oldest data. + // + ptRingBuf->ulReadIndex = ptRingBuf->ulWriteIndex + 1; + + // + // Correct for buffer wrap if necessary. + // + if(ptRingBuf->ulReadIndex >= ptRingBuf->ulSize) + { + ptRingBuf->ulReadIndex -= ptRingBuf->ulSize; + } + } + + // + // Restore interrupts if we turned them off earlier. + // + if(!bIntsOff) + { + IntMasterEnable(); + } +} + +//***************************************************************************** +// +//! Writes a single byte of data to a ring buffer. +//! +//! \param ptRingBuf points to the ring buffer to be written to. +//! \param ucData is the byte to be written. +//! +//! This function writes a single byte of data into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufWriteOne(tRingBufObject *ptRingBuf, unsigned char ucData) +{ + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + + // + // Verify that space is available in the buffer. + // + ASSERT(RingBufFree(ptRingBuf) != 0); + + // + // Write the data byte. + // + ptRingBuf->pucBuf[ptRingBuf->ulWriteIndex] = ucData; + + // + // Increment the write index. + // + UpdateIndexAtomic(&ptRingBuf->ulWriteIndex, 1, ptRingBuf->ulSize); +} + +//***************************************************************************** +// +//! Writes data to a ring buffer. +//! +//! \param ptRingBuf points to the ring buffer to be written to. +//! \param pucData points to the data to be written. +//! \param ulLength is the number of bytes to be written. +//! +//! This function write a sequence of bytes into a ring buffer. +//! +//! \return None. +// +//***************************************************************************** +void +RingBufWrite(tRingBufObject *ptRingBuf, unsigned char *pucData, + unsigned long ulLength) +{ + unsigned long ulTemp; + + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + ASSERT(pucData != NULL); + ASSERT(ulLength != 0); + + // + // Verify that space is available in the buffer. + // + ASSERT(ulLength <= RingBufFree(ptRingBuf)); + + // + // Write the data into the ring buffer. + // + for(ulTemp = 0; ulTemp < ulLength; ulTemp++) + { + RingBufWriteOne(ptRingBuf, pucData[ulTemp]); + } +} + +//***************************************************************************** +// +//! Initialize a ring buffer object. +//! +//! \param ptRingBuf points to the ring buffer to be initialized. +//! \param pucBuf points to the data buffer to be used for the ring buffer. +//! \param ulSize 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 *ptRingBuf, unsigned char *pucBuf, + unsigned long ulSize) +{ + // + // Check the arguments. + // + ASSERT(ptRingBuf != NULL); + ASSERT(pucBuf != NULL); + ASSERT(ulSize != 0); + + // + // Initialize the ring buffer object. + // + ptRingBuf->ulSize = ulSize; + ptRingBuf->pucBuf = pucBuf; + ptRingBuf->ulWriteIndex = ptRingBuf->ulReadIndex = 0; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/ringbuf.h b/utils/ringbuf.h new file mode 100644 index 0000000..7aeab65 --- /dev/null +++ b/utils/ringbuf.h @@ -0,0 +1,105 @@ +//***************************************************************************** +// +// ringbuf.h - Defines and Macros for the ring buffer utilities. +// +// Copyright (c) 2008-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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. + // + unsigned long ulSize; + + // + // The ring buffer write index. + // + volatile unsigned long ulWriteIndex; + + // + // The ring buffer read index. + // + volatile unsigned long ulReadIndex; + + // + // The ring buffer. + // + unsigned char *pucBuf; + +} +tRingBufObject; + +//***************************************************************************** +// +// API Function prototypes +// +//***************************************************************************** +extern tBoolean RingBufFull(tRingBufObject *ptRingBuf); +extern tBoolean RingBufEmpty(tRingBufObject *ptRingBuf); +extern void RingBufFlush(tRingBufObject *ptRingBuf); +extern unsigned long RingBufUsed(tRingBufObject *ptRingBuf); +extern unsigned long RingBufFree(tRingBufObject *ptRingBuf); +extern unsigned long RingBufContigUsed(tRingBufObject *ptRingBuf); +extern unsigned long RingBufContigFree(tRingBufObject *ptRingBuf); +extern unsigned long RingBufSize(tRingBufObject *ptRingBuf); +extern unsigned char RingBufReadOne(tRingBufObject *ptRingBuf); +extern void RingBufRead(tRingBufObject *ptRingBuf, unsigned char *pucData, + unsigned long ulLength); +extern void RingBufWriteOne(tRingBufObject *ptRingBuf, unsigned char ucData); +extern void RingBufWrite(tRingBufObject *ptRingBuf, unsigned char *pucData, + unsigned long ulLength); +extern void RingBufAdvanceWrite(tRingBufObject *ptRingBuf, + unsigned long ulNumBytes); +extern void RingBufAdvanceRead(tRingBufObject *ptRingBuf, + unsigned long ulNumBytes); +extern void RingBufInit(tRingBufObject *ptRingBuf, unsigned char *pucBuf, + unsigned long ulSize); + +//***************************************************************************** +// +// 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..82933e1 --- /dev/null +++ b/utils/scheduler.c @@ -0,0 +1,304 @@ +//**************************************************************************** +// +// scheduler.c - A simple task scheduler +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//**************************************************************************** +#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 unsigned long g_ulSchedulerTickCount; + +//**************************************************************************** +// +//! 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_ulSchedulerTickCount++; +} + +//**************************************************************************** +// +//! Initializes the task scheduler. +//! +//! \param ulTicksPerSecond 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(unsigned long ulTicksPerSecond) +{ + ASSERT(ulTicksPerSecond); + + // + // Configure SysTick for a periodic interrupt. + // + SysTickPeriodSet(SysCtlClockGet() / ulTicksPerSecond); + 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) +{ + unsigned long ulLoop; + tSchedulerTask *psTask; + + // + // Loop through each task in the task table. + // + for(ulLoop = 0; ulLoop < g_ulSchedulerNumTasks; ulLoop++) + { + // + // Get a pointer to the task information. + // + psTask = &g_psSchedulerTable[ulLoop]; + + // + // Is this task active and, if so, is it time to call it's function? + // + if(psTask->bActive && (SchedulerElapsedTicksGet(psTask->ulLastCall) >= + psTask->ulFrequencyTicks)) + { + // + // Remember the timestamp at which we make the function call. + // + psTask->ulLastCall = g_ulSchedulerTickCount; + + // + // Call the task function, passing the provided parameter. + // + psTask->pfnFunction(psTask->pvParam); + } + } +} + +//**************************************************************************** +// +//! Enables a task and allows the scheduler to call it periodically. +//! +//! \param ulIndex 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(unsigned long ulIndex, tBoolean bRunNow) +{ + // + // Is the task index passed valid? + // + if(ulIndex < g_ulSchedulerNumTasks) + { + // + // Yes - mark the task as active. + // + g_psSchedulerTable[ulIndex].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[ulIndex].ulLastCall = (g_ulSchedulerTickCount - + g_psSchedulerTable[ulIndex].ulFrequencyTicks); + } + else + { + // + // Cause the task to run after one full time period. + // + g_psSchedulerTable[ulIndex].ulLastCall = g_ulSchedulerTickCount; + } + } +} + +//**************************************************************************** +// +//! Disables a task and prevents the scheduler from calling it. +//! +//! \param ulIndex 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(unsigned long ulIndex) +{ + // + // Is the task index passed valid? + // + if(ulIndex < g_ulSchedulerNumTasks) + { + // + // Yes - mark the task as inactive. + // + g_psSchedulerTable[ulIndex].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. +// +//**************************************************************************** +unsigned long +SchedulerTickCountGet(void) +{ + return(g_ulSchedulerTickCount); +} + +//**************************************************************************** +// +//! Returns the number of ticks elapsed since the provided tick count. +//! +//! \param ulTickCount 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 ulTickCount +//! 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 ulTickCount. 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. +// +//**************************************************************************** +unsigned long +SchedulerElapsedTicksGet(unsigned long ulTickCount) +{ + // + // Determine the calculation based upon whether the global tick count has + // wrapped since the passed ulTickCount. + // + return(SchedulerElapsedTicksCalc(ulTickCount, g_ulSchedulerTickCount)); +} + +//**************************************************************************** +// +//! Returns the number of ticks elapsed between two times. +//! +//! \param ulTickStart is the system tick count for the start of the period. +//! \param ulTickEnd 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. +// +//**************************************************************************** +unsigned long +SchedulerElapsedTicksCalc(unsigned long ulTickStart, unsigned long ulTickEnd) +{ + return((ulTickEnd > ulTickStart) ? (ulTickEnd - ulTickStart) : + ((0xFFFFFFFF - ulTickStart) + ulTickEnd + 1)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/scheduler.h b/utils/scheduler.h new file mode 100644 index 0000000..23b373c --- /dev/null +++ b/utils/scheduler.h @@ -0,0 +1,140 @@ +//**************************************************************************** +// +// scheduler.h - Public header for the simple timed function scheduler module. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//**************************************************************************** +#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. + // + unsigned long ulFrequencyTicks; + + // + //! Tick count when this function was last called. This field is updated + //! by the scheduler. + // + unsigned long ulLastCall; + + // + //! 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. + // + tBoolean 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 unsigned long g_ulSchedulerNumTasks; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Public function prototypes +// +//***************************************************************************** +extern void SchedulerSysTickIntHandler(void); +extern void SchedulerInit(unsigned long ulTicksPerSecond); +extern void SchedulerRun(void); +extern void SchedulerTaskEnable(unsigned long ulIndex, tBoolean bRunNow); +extern void SchedulerTaskDisable(unsigned long ulIndex); +extern unsigned long SchedulerTickCountGet(void); +extern unsigned long SchedulerElapsedTicksGet(unsigned long ulTickCount); +extern unsigned long SchedulerElapsedTicksCalc(unsigned long ulTickStart, + unsigned long ulTickEnd); + +//***************************************************************************** +// +// 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..7b971ca --- /dev/null +++ b/utils/sine.c @@ -0,0 +1,125 @@ +//***************************************************************************** +// +// sine.c - Fixed point sine trigonometric function. +// +// Copyright (c) 2006-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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 unsigned short g_pusFixedSineTable[] = +{ + 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 ulAngle 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. +// +//***************************************************************************** +long +sine(unsigned long ulAngle) +{ + unsigned long ulIdx; + + // + // 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. + // + ulAngle += 0x00400000; + + // + // Get the index into the sine table from bits 30:23. + // + ulIdx = (ulAngle >> 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(ulAngle & 0x40000000) + { + ulIdx = 256 - ulIdx; + } + + // + // Get the value of the sine. + // + ulIdx = g_pusFixedSineTable[ulIdx]; + + // + // 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(ulAngle & 0x80000000) + { + return(0 - ulIdx); + } + else + { + return(ulIdx); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/sine.h b/utils/sine.h new file mode 100644 index 0000000..9c91255 --- /dev/null +++ b/utils/sine.h @@ -0,0 +1,85 @@ +//***************************************************************************** +// +// sine.h - Prototypes for the fixed point sine trigonometric function. +// +// Copyright (c) 2006-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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 ulAngle 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(ulAngle) sine((ulAngle + 0x40000000)) + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototype for the fixed point sine function. +// +//***************************************************************************** +extern long sine(unsigned long ulAngle); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SINE_H__ diff --git a/utils/softi2c.c b/utils/softi2c.c new file mode 100644 index 0000000..9828534 --- /dev/null +++ b/utils/softi2c.c @@ -0,0 +1,1316 @@ +//***************************************************************************** +// +// softi2c.c - Driver for the SoftI2C. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softi2c_api +//! @{ +// +//***************************************************************************** + +#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 ucFlags 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 pI2C 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 *pI2C) +{ + // + // Determine the current state of the state machine. + // + switch(pI2C->ucState) + { + // + // 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(&(pI2C->ucFlags), 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(pI2C->ulSCLGPIO) != 0) + { + pI2C->ucState = SOFTI2C_STATE_START4; + } + else if(HWREG(pI2C->ulSDAGPIO) == 0) + { + pI2C->ucState = SOFTI2C_STATE_START0; + } + else + { + pI2C->ucState = SOFTI2C_STATE_START2; + } + } + + // + // Otherwise, see if the RUN flag is set, indicating that a data + // byte should be transferred. + // + else if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RUN) == 1) + { + // + // Start the transfer from the first bit. + // + pI2C->ucCurrentBit = 0; + + // + // See if a byte should be sent or received. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RECEIVE) == 0) + { + // + // A byte should be sent. + // + pI2C->ucState = SOFTI2C_STATE_SEND0; + } + else + { + // + // A byte should be received. Clear out the receive data + // buffer in preparation for receiving the new byte. + // + pI2C->ucData = 0; + pI2C->ucState = SOFTI2C_STATE_RECV0; + } + } + + // + // Otherwise, see if the STOP flag is set, indicating that a stop + // condition should be generated. + // + else if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Generate a stop condition. + // + pI2C->ucState = SOFTI2C_STATE_STOP0; + } + + // + // See if the SoftI2C state machine has left the idle state. + // + if(pI2C->ucState != 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(&(pI2C->ucFlags), SOFTI2C_FLAG_ADDR_ACK) = 0; + HWREGBITB(&(pI2C->ucFlags), 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(pI2C->ulSDAGPIO) = 255; + + // + // Advance to the next state. + // + pI2C->ucState = 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. + // + pI2C->ucState++; + + // + // 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(pI2C->ulSCLGPIO) = 255; + + // + // Advance to the next state. + // + pI2C->ucState++; + + // + // 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(pI2C->ulSDAGPIO) = 0; + + // + // Advance to the next state. + // + pI2C->ucState++; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. + // + case SOFTI2C_STATE_START6: + { + // + // Set SCL low. + // + HWREG(pI2C->ulSCLGPIO) = 0; + + // + // Advance to the next state. + // + pI2C->ucState = 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. + // + pI2C->ucCurrentBit = 0; + + // + // Advance to the address output state. + // + pI2C->ucState = 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(pI2C->ucCurrentBit < 7) + { + // + // Write the next bit of the slave address to SDA. + // + HWREG(pI2C->ulSDAGPIO) = ((pI2C->ucSlaveAddr & + (1 << (6 - pI2C->ucCurrentBit))) ? + 255 : 0); + } + + // + // Otherwise, see if this is the eight bit of the address phase + // (which is the read/not write bit). + // + else if(pI2C->ucCurrentBit == 7) + { + // + // Write the read/not write bit to SDA. + // + HWREG(pI2C->ulSDAGPIO) = + (HWREGBITB(&(pI2C->ucFlags), 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(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Advance to the next state. + // + pI2C->ucState = 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(pI2C->ulSCLGPIO) != 0) + { + // + // Advance to the next state now that SCL has gone high. + // + pI2C->ucState++; + } + + // + // 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(pI2C->ucCurrentBit == 8) + { + // + // See if the SDA line is high. + // + if(HWREG(pI2C->ulSDAGPIO) != 0) + { + // + // Since the SDA line is high, the address byte has not + // been ACKed by any slave. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_ADDR_ACK) = 1; + } + + // + // Change the SDA GPIO back into an output. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 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(&(pI2C->ucFlags), SOFTI2C_FLAG_START) = 0; + + // + // See if the RUN flag is set, indicating that a data byte + // should be transferred as well. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RUN) == 1) + { + // + // Reset the current bit to zero for the start of the data + // phase. + // + pI2C->ucCurrentBit = 0; + + // + // See if the data byte is being sent or received. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RECEIVE) == 0) + { + // + // The data byte is being sent, so advance to the data + // send state. + // + pI2C->ucState = SOFTI2C_STATE_SEND0; + } + else + { + // + // The data byte is being received, so clear the data + // buffer and advance to the data receive state. + // + pI2C->ucData = 0; + pI2C->ucState = SOFTI2C_STATE_RECV0; + } + } + + // + // Otherwise, see if the STOP flag is set, indicating that a + // stop condition should be generated. + // + else if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + pI2C->ucState = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + pI2C->ucIntStatus = 1; + + // + // Advance to the idle state. + // + pI2C->ucState = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the address should be transferred. + // + else + { + // + // Increment the bit count. + // + pI2C->ucCurrentBit++; + + // + // Advance to the address tranfer state. + // + pI2C->ucState = SOFTI2C_STATE_ADDR0; + } + + // + // Set SCL low. + // + HWREG(pI2C->ulSCLGPIO) = 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(pI2C->ucCurrentBit < 8) + { + // + // Write the next bit of the data byte to SDA. + // + HWREG(pI2C->ulSDAGPIO) = ((pI2C->ucData & + (1 << (7 - pI2C->ucCurrentBit))) ? + 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(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Advance to the next state. + // + pI2C->ucState = 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(pI2C->ucCurrentBit == 8) + { + // + // See if the SDA line is high. + // + if(HWREG(pI2C->ulSDAGPIO) != 0) + { + // + // Since the SDA line is high, the data byte has not been + // ACKed by the slave. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_DATA_ACK) = 1; + } + + // + // Change the SDA GPIO back into an output. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // The data phase has completed, so clear the RUN flag. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RUN) = 0; + + // + // See if the STOP flag is set, indicating that a stop + // condition should be generated. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + pI2C->ucState = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + pI2C->ucIntStatus = 1; + + // + // Advance to the idle state. + // + pI2C->ucState = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the data should be transferred. + // + else + { + // + // Increment the bit count. + // + pI2C->ucCurrentBit++; + + // + // Advance to the data transmit state. + // + pI2C->ucState = SOFTI2C_STATE_SEND0; + } + + // + // Set SCL low. + // + HWREG(pI2C->ulSCLGPIO) = 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(pI2C->ucCurrentBit == 0) + { + // + // Change the SDA GPIO into an input so that the data provided + // by the slave can be read. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 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(pI2C->ucCurrentBit == 8) + { + // + // Change the SDA GPIO into an output so that the ACK bit can + // be driven to the slave. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // See if this byte should be ACKed or NAKed. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_ACK) == 1) + { + // + // Drive SDA low to ACK the data byte. + // + HWREG(pI2C->ulSDAGPIO) = 0; + } + else + { + // + // Allow SDA to get pulled high to NAK the data byte. + // + HWREG(pI2C->ulSDAGPIO) = 255; + } + } + + // + // Advance to the next state. + // + pI2C->ucState = 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(pI2C->ucCurrentBit == 8) + { + // + // The data phase has completed, so clear the RUN flag. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RUN) = 0; + + // + // See if the STOP flag is set, indicating that a stop + // condition should be generated. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + pI2C->ucState = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + pI2C->ucIntStatus = 1; + + // + // Advance to the idle state. + // + pI2C->ucState = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the data should be transferred. + // + else + { + // + // Read the next bit of data from the SDA line. + // + pI2C->ucData |= (HWREG(pI2C->ulSDAGPIO) ? + (1 << (7 - pI2C->ucCurrentBit)) : 0); + + // + // Increment the bit count. + // + pI2C->ucCurrentBit++; + + // + // Advance to the data receive state. + // + pI2C->ucState = SOFTI2C_STATE_RECV0; + } + + // + // Set SCL low. + // + HWREG(pI2C->ulSCLGPIO) = 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(pI2C->ulSDAGPIO) = 255; + + // + // The stop condition has been generated, so clear the STOP flag. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) = 0; + + // + // Since the requested operations have completed, set the SoftI2C + // ``interrupt''. + // + pI2C->ucIntStatus = 1; + + // + // Advance to the idle state. + // + pI2C->ucState = 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(((pI2C->ucIntStatus & pI2C->ucIntMask) != 0) && + (pI2C->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + pI2C->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Initializes the SoftI2C module. +//! +//! \param pI2C 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 *pI2C) +{ + // + // Configure the SCL pin. + // + MAP_GPIODirModeSet(pI2C->ulSCLGPIO & 0xfffff000, + (pI2C->ulSCLGPIO & 0x00000fff) >> 2, GPIO_DIR_MODE_OUT); + MAP_GPIOPadConfigSet(pI2C->ulSCLGPIO & 0xfffff000, + (pI2C->ulSCLGPIO & 0x00000fff) >> 2, + GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_OD); + + // + // Set the SCL pin high. + // + HWREG(pI2C->ulSCLGPIO) = 255; + + // + // Configure the SDA pin. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, GPIO_DIR_MODE_OUT); + MAP_GPIOPadConfigSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_OD); + + // + // Set the SDA pin high. + // + HWREG(pI2C->ulSDAGPIO) = 255; + + // + // The ``interrupt'' is not asserted at the start. + // + pI2C->ucIntStatus = 0; + + // + // There are no flags at the start. + // + pI2C->ucFlags = 0; + + // + // Start the SoftI2C state machine in the idle state. + // + pI2C->ucState = SOFTI2C_STATE_IDLE; +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftI2C module. +//! +//! \param pI2C 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 *pI2C, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + pI2C->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftI2C SCL signal. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin 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 *pI2C, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the SCL signal. + // + pI2C->ulSCLGPIO = ulBase + (ucPin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftI2C SDA signal. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin 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 *pI2C, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the SDA signal. + // + pI2C->ulSDAGPIO = ulBase + (ucPin << 2); +} + +//***************************************************************************** +// +//! Enables the SoftI2C ``interrupt''. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! Enables the SoftI2C ``interrupt'' source. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntEnable(tSoftI2C *pI2C) +{ + // + // Enable the master interrupt. + // + pI2C->ucIntMask = 1; +} + +//***************************************************************************** +// +//! Disables the SoftI2C ``interrupt''. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! Disables the SoftI2C ``interrupt'' source. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntDisable(tSoftI2C *pI2C) +{ + // + // Disable the master interrupt. + // + pI2C->ucIntMask = 0; +} + +//***************************************************************************** +// +//! Gets the current SoftI2C ``interrupt'' status. +//! +//! \param pI2C 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. +// +//***************************************************************************** +tBoolean +SoftI2CIntStatus(tSoftI2C *pI2C, tBoolean bMasked) +{ + // + // Return either the interrupt status or the raw interrupt status as + // requested. + // + if(bMasked) + { + return((pI2C->ucIntStatus & pI2C->ucIntMask) ? true : false); + } + else + { + return(pI2C->ucIntStatus ? true : false); + } +} + +//***************************************************************************** +// +//! Clears the SoftI2C ``interrupt''. +//! +//! \param pI2C 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 *pI2C) +{ + // + // Clear the SoftI2C interrupt source. + // + pI2C->ucIntStatus = 0; +} + +//***************************************************************************** +// +//! Sets the address that the SoftI2C module places on the bus. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ucSlaveAddr 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 *pI2C, unsigned char ucSlaveAddr, + tBoolean bReceive) +{ + // + // Check the arguments. + // + ASSERT(!(ucSlaveAddr & 0x80)); + + // + // Set the address of the slave with which the master will communicate. + // + pI2C->ucSlaveAddr = ucSlaveAddr; + + // + // Set a flag to indicate if this is a transmit or receive. + // + if(bReceive) + { + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RECEIVE) = 1; + } + else + { + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RECEIVE) = 0; + } +} + +//***************************************************************************** +// +//! Indicates whether or not the SoftI2C module is busy. +//! +//! \param pI2C 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. +// +//***************************************************************************** +tBoolean +SoftI2CBusy(tSoftI2C *pI2C) +{ + // + // Return the busy status. + // + if(pI2C->ucState != SOFTI2C_STATE_IDLE) + { + return(true); + } + else + { + return(false); + } +} + +//***************************************************************************** +// +//! Controls the state of the SoftI2C module. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ulCmd 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 ucCmd 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 *pI2C, unsigned long ulCmd) +{ + // + // Check the arguments. + // + ASSERT((ulCmd == SOFTI2C_CMD_SINGLE_SEND) || + (ulCmd == SOFTI2C_CMD_SINGLE_RECEIVE) || + (ulCmd == SOFTI2C_CMD_BURST_SEND_START) || + (ulCmd == SOFTI2C_CMD_BURST_SEND_CONT) || + (ulCmd == SOFTI2C_CMD_BURST_SEND_FINISH) || + (ulCmd == SOFTI2C_CMD_BURST_SEND_ERROR_STOP) || + (ulCmd == SOFTI2C_CMD_BURST_RECEIVE_START) || + (ulCmd == SOFTI2C_CMD_BURST_RECEIVE_CONT) || + (ulCmd == SOFTI2C_CMD_BURST_RECEIVE_FINISH) || + (ulCmd == SOFTI2C_CMD_BURST_RECEIVE_ERROR_STOP)); + + // + // Send the command. + // + pI2C->ucFlags = (pI2C->ucFlags & 0xf0) | ulCmd; +} + +//***************************************************************************** +// +//! Gets the error status of the SoftI2C module. +//! +//! \param pI2C 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. +// +//***************************************************************************** +unsigned long +SoftI2CErr(tSoftI2C *pI2C) +{ + // + // If the SoftI2C is busy, there is no error to report. + // + if(pI2C->ucState != SOFTI2C_STATE_IDLE) + { + return(SOFTI2C_ERR_NONE); + } + + // + // Return any errors that may have occurred. + // + return((HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_ADDR_ACK) ? + SOFTI2C_ERR_ADDR_ACK : 0) | + (HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_DATA_ACK) ? + SOFTI2C_ERR_DATA_ACK : 0)); +} + +//***************************************************************************** +// +//! Transmits a byte from the SoftI2C module. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ucData 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 *pI2C, unsigned char ucData) +{ + // + // Write the byte. + // + pI2C->ucData = ucData; +} + +//***************************************************************************** +// +//! Receives a byte that has been sent to the SoftI2C module. +//! +//! \param pI2C 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 +//! unsigned long. +// +//***************************************************************************** +unsigned long +SoftI2CDataGet(tSoftI2C *pI2C) +{ + // + // Read a byte. + // + return(pI2C->ucData); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/softi2c.h b/utils/softi2c.h new file mode 100644 index 0000000..6f662bf --- /dev/null +++ b/utils/softi2c.h @@ -0,0 +1,195 @@ +//***************************************************************************** +// +// softi2c.h - Defines and macros for the SoftI2C. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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. + // + unsigned long ulSCLGPIO; + + // + //! 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. + /// + unsigned long ulSDAGPIO; + + // + //! The flags that control the operation of the SoftI2C module. This + //! member should not be accessed or modified by the application. + // + unsigned char ucFlags; + + // + //! The slave address that is currently being accessed. This member should + //! not be accessed or modified by the application. + // + unsigned char ucSlaveAddr; + + // + //! The data that is currently being transmitted or received. This member + //! should not be accessed or modified by the application. + // + unsigned char ucData; + + // + //! The current state of the SoftI2C state machine. This member should not + //! be accessed or modified by the application. + // + unsigned char ucState; + + // + //! 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. + // + unsigned char ucCurrentBit; + + // + //! The set of virtual interrupts that should be sent to the callback + //! function. This member should not be accessed or modified by the + //! application. + // + unsigned char ucIntMask; + + // + //! The set of virtual interrupts that are currently asserted. This member + //! should not be accessed or modified by the application. + // + unsigned char ucIntStatus; +} +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 tBoolean SoftI2CBusy(tSoftI2C *pI2C); +extern void SoftI2CCallbackSet(tSoftI2C *pI2C, void (*pfnCallback)(void)); +extern void SoftI2CControl(tSoftI2C *pI2C, unsigned long ulCmd); +extern unsigned long SoftI2CDataGet(tSoftI2C *pI2C); +extern void SoftI2CDataPut(tSoftI2C *pI2C, unsigned char ucData); +extern unsigned long SoftI2CErr(tSoftI2C *pI2C); +extern void SoftI2CInit(tSoftI2C *pI2C); +extern void SoftI2CIntClear(tSoftI2C *pI2C); +extern void SoftI2CIntDisable(tSoftI2C *pI2C); +extern void SoftI2CIntEnable(tSoftI2C *pI2C); +extern tBoolean SoftI2CIntStatus(tSoftI2C *pI2C, tBoolean bMasked); +extern void SoftI2CSCLGPIOSet(tSoftI2C *pI2C, unsigned long ulBase, + unsigned char ucPin); +extern void SoftI2CSDAGPIOSet(tSoftI2C *pI2C, unsigned long ulBase, + unsigned char ucPin); +extern void SoftI2CSlaveAddrSet(tSoftI2C *pI2C, unsigned char ucSlaveAddr, + tBoolean bReceive); +extern void SoftI2CTimerTick(tSoftI2C *pI2C); + +//***************************************************************************** +// +// 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..e8c5c0b --- /dev/null +++ b/utils/softssi.c @@ -0,0 +1,1288 @@ +//***************************************************************************** +// +// softssi.c - Driver for the SoftSSI. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softssi_api +//! @{ +// +//***************************************************************************** + +#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 ucFlags 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 pSSI specifies the SoftSSI data structure. +//! \param ucProtocol specifes the data transfer protocol. +//! \param ucBits specifies the number of bits transferred per frame. +//! +//! This function configures the data format of a SoftSSI module. The +//! \e ucProtocol 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 ucBits parameter defines the width of the data transfers, and can be +//! a value between 4 and 16, inclusive. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIConfigSet(tSoftSSI *pSSI, unsigned char ucProtocol, + unsigned char ucBits) +{ + // + // See if a GPIO pin has been set for Fss. + // + if(pSSI->ulFssGPIO != 0) + { + // + // Configure the Fss pin. + // + MAP_GPIOPinTypeGPIOOutput(pSSI->ulFssGPIO & 0xfffff000, + (pSSI->ulFssGPIO & 0x00000fff) >> 2); + + // + // Set the Fss pin high. + // + HWREG(pSSI->ulFssGPIO) = 255; + } + + // + // Configure the Clk pin. + // + MAP_GPIOPinTypeGPIOOutput(pSSI->ulClkGPIO & 0xfffff000, + (pSSI->ulClkGPIO & 0x00000fff) >> 2); + + // + // Set the Clk pin high or low based on the configured clock polarity. + // + if((ucProtocol & SOFTSSI_FLAG_SPO) == 0) + { + HWREG(pSSI->ulClkGPIO) = 0; + } + else + { + HWREG(pSSI->ulClkGPIO) = 255; + } + + // + // Configure the Tx pin and set it low. + // + MAP_GPIOPinTypeGPIOOutput(pSSI->ulTxGPIO & 0xfffff000, + (pSSI->ulTxGPIO & 0x00000fff) >> 2); + HWREG(pSSI->ulTxGPIO) = 0; + + // + // See if a GPIO pin has been set for Rx. + // + if(pSSI->ulRxGPIO != 0) + { + // + // Configure the Rx pin. + // + MAP_GPIOPinTypeGPIOInput(pSSI->ulRxGPIO & 0xfffff000, + (pSSI->ulRxGPIO & 0x00000fff) >> 2); + } + + // + // Make sure that the transmit and receive FIFOs are empty. + // + pSSI->usTxBufferRead = 0; + pSSI->usTxBufferWrite = 0; + pSSI->usRxBufferRead = 0; + pSSI->usRxBufferWrite = 0; + + // + // Save the frame protocol. + // + pSSI->ucFlags = ucProtocol; + + // + // Save the number of data bits. + // + pSSI->ucBits = ucBits; + + // + // Since the FIFOs are empty, the transmit FIFO "interrupt" is asserted. + // + pSSI->ucIntStatus = SOFTSSI_TXFF; + + // + // Reset the idle counter. + // + pSSI->ucIdleCount = 0; + + // + // Disable the SoftSSI module. + // + pSSI->ucFlags &= ~(SOFTSSI_FLAG_ENABLE); + + // + // Start the SoftSSI state machine in the idle state. + // + pSSI->ucState = SOFTSSI_STATE_IDLE; +} + +//***************************************************************************** +// +//! Handles the assertion/deassertion of the transmit FIFO ``interrupt''. +//! +//! \param pSSI 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 *pSSI) +{ + unsigned short usTemp; + + // + // Determine the number of words left in the transmit FIFO. + // + if(pSSI->usTxBufferRead > pSSI->usTxBufferWrite) + { + usTemp = (pSSI->usTxBufferLen + pSSI->usTxBufferWrite - + pSSI->usTxBufferRead); + } + else + { + usTemp = pSSI->usTxBufferWrite - pSSI->usTxBufferRead; + } + + // + // If the transmit FIFO is now half full or less, generate a transmit FIFO + // "interrupt". Otherwise, clear the transmit FIFO "interrupt". + // + if(usTemp <= (pSSI->usTxBufferLen / 2)) + { + pSSI->ucIntStatus |= SOFTSSI_TXFF; + } + else + { + pSSI->ucIntStatus &= ~(SOFTSSI_TXFF); + } +} + +//***************************************************************************** +// +//! Handles the assertion/deassertion of the receive FIFO ``interrupt''. +//! +//! \param pSSI 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 *pSSI) +{ + unsigned short usTemp; + + // + // Determine the number of words in the receive FIFO. + // + if(pSSI->usRxBufferRead > pSSI->usRxBufferWrite) + { + usTemp = (pSSI->usRxBufferLen + pSSI->usRxBufferWrite - + pSSI->usRxBufferRead); + } + else + { + usTemp = pSSI->usRxBufferWrite - pSSI->usRxBufferRead; + } + + // + // If the receive FIFO is now half full or more, generate a receive FIFO + // "interrupt". Otherwise, clear the receive FIFO "interrupt". + // + if(usTemp >= (pSSI->usRxBufferLen / 2)) + { + pSSI->ucIntStatus |= SOFTSSI_RXFF; + } + else + { + pSSI->ucIntStatus &= ~(SOFTSSI_RXFF); + } +} + +//***************************************************************************** +// +//! Performs the periodic update of the SoftSSI module. +//! +//! \param pSSI 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 *pSSI) +{ + unsigned short usTemp; + + // + // Determine the current state of the state machine. + // + switch(pSSI->ucState) + { + // + // 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(((pSSI->ucFlags & SOFTSSI_FLAG_ENABLE) != 0) && + (pSSI->usTxBufferRead != pSSI->usTxBufferWrite)) + { + // + // Assert the Fss signal if it is configured. + // + if(pSSI->ulFssGPIO != 0) + { + HWREG(pSSI->ulFssGPIO) = 0; + } + + // + // Move to the start state. + // + pSSI->ucState = SOFTSSI_STATE_START; + } + + // + // Otherwise, see if there is data in the receive FIFO. + // + else if((pSSI->usRxBufferRead != pSSI->usRxBufferWrite) && + (pSSI->ucIdleCount != 64)) + { + // + // Increment the idle counter. + // + pSSI->ucIdleCount++; + + // + // See if the idle counter has become large enough to trigger + // a timeout "interrupt". + // + if(pSSI->ucIdleCount == 64) + { + // + // Trigger the receive timeout "interrupt". + // + pSSI->ucIntStatus |= 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. + // + pSSI->usTxData = (pSSI->pusTxBuffer[pSSI->usTxBufferRead] << + (16 - pSSI->ucBits)); + + // + // Initialize the receive buffer to zero. + // + pSSI->usRxData = 0; + + // + // Initialize the count of bits tranferred. + // + pSSI->ucCurrentBit = 0; + + // + // Write the first bit of the transmit word to the Tx pin. + // + HWREG(pSSI->ulTxGPIO) = + (pSSI->usTxData & 0x8000) ? 255 : 0; + + // + // Shift to the next bit of the transmit word. + // + pSSI->usTxData <<= 1; + + // + // If in SPI mode 1 or 3, then the Clk signal needs to be toggled. + // + if((pSSI->ucFlags & SOFTSSI_FLAG_SPH) != 0) + { + HWREG(pSSI->ulClkGPIO) ^= 255; + } + + // + // Move to the data input state. + // + pSSI->ucState = 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(pSSI->ulRxGPIO != 0) + { + pSSI->usRxData = ((pSSI->usRxData << 1) | + (HWREG(pSSI->ulRxGPIO) ? 1 : 0)); + } + + // + // Toggle the Clk signal. + // + HWREG(pSSI->ulClkGPIO) ^= 255; + + // + // Increment the number of bits transferred. + // + pSSI->ucCurrentBit++; + + // + // See if the entire word has been transferred. + // + if(pSSI->ucCurrentBit != pSSI->ucBits) + { + // + // There are more bits to transfer, so move to the data output + // state. + // + pSSI->ucState = SOFTSSI_STATE_OUT; + } + else + { + // + // Increment the transmit read pointer, removing the word that + // was just transferred from the transmit FIFO. + // + pSSI->usTxBufferRead++; + if(pSSI->usTxBufferRead == pSSI->usTxBufferLen) + { + pSSI->usTxBufferRead = 0; + } + + // + // See if a transmit FIFO "interrupt" needs to be asserted. + // + SoftSSITxInt(pSSI); + + // + // Determine the new value for the receive FIFO write pointer. + // + usTemp = pSSI->usRxBufferWrite + 1; + if(usTemp >= pSSI->usRxBufferLen) + { + usTemp = 0; + } + + // + // See if there is space in the receive FIFO for the word that + // was just received. + // + if(usTemp == pSSI->usRxBufferRead) + { + // + // The receive FIFO is full, so generate a receive FIFO + // overrun "interrupt". + // + pSSI->ucIntStatus |= SOFTSSI_RXOR; + } + else + { + // + // Store the new word into the receive FIFO. + // + pSSI->pusRxBuffer[pSSI->usRxBufferWrite] = pSSI->usRxData; + + // + // Save the new receive FIFO write pointer. + // + pSSI->usRxBufferWrite = usTemp; + + // + // See if a receive FIFO "interrupt" needs to be asserted. + // + SoftSSIRxInt(pSSI); + } + + // + // 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(((pSSI->ucFlags & SOFTSSI_FLAG_ENABLE) != 0) && + ((pSSI->ucFlags & SOFTSSI_FLAG_SPH) != 0) && + (pSSI->usTxBufferRead != pSSI->usTxBufferWrite)) + { + // + // Get the next word to transfer from the transmit FIFO. + // + pSSI->usTxData = + (pSSI->pusTxBuffer[pSSI->usTxBufferRead] << + (16 - pSSI->ucBits)); + + // + // Initialize the receive buffer to zero. + // + pSSI->usRxData = 0; + + // + // Initialize the count of bits tranferred. + // + pSSI->ucCurrentBit = 0; + + // + // Move to the data output state. + // + pSSI->ucState = SOFTSSI_STATE_OUT; + } + else + { + // + // The next word should not be transmitted immediately, so + // move to the first step of the stop state. + // + pSSI->ucState = 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(pSSI->ulTxGPIO) = (pSSI->usTxData & 0x8000) ? 255 : 0; + + // + // Toggle the Clk signal. + // + HWREG(pSSI->ulClkGPIO) ^= 255; + + // + // Shift to the next bit of the transmit word. + // + pSSI->usTxData <<= 1; + + // + // Move to the data input state. + // + pSSI->ucState = 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(pSSI->ulTxGPIO) = 0; + + // + // If in SPI mode 1 or 3, then the Clk signal needs to be toggled. + // + if((pSSI->ucFlags & SOFTSSI_FLAG_SPH) == 0) + { + HWREG(pSSI->ulClkGPIO) ^= 255; + } + + // + // Move to the second step of the stop state. + // + pSSI->ucState = 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(pSSI->ulFssGPIO != 0) + { + HWREG(pSSI->ulFssGPIO) = 255; + } + + // + // Move to the idle state. + // + pSSI->ucState = SOFTSSI_STATE_IDLE; + + // + // Reset the idle counter. + // + pSSI->ucIdleCount = 0; + + // + // See if the end of transfer "interrupt" should be generated. + // + if(pSSI->usTxBufferRead == pSSI->usTxBufferWrite) + { + pSSI->ucIntStatus |= 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(((pSSI->ucIntStatus & pSSI->ucIntMask) != 0) && + (pSSI->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + pSSI->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Enables the SoftSSI module. +//! +//! \param pSSI 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 *pSSI) +{ + // + // Enable the SoftSSI module. + // + pSSI->ucFlags |= SOFTSSI_FLAG_ENABLE; +} + +//***************************************************************************** +// +//! Disables the SoftSSI module. +//! +//! \param pSSI 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 *pSSI) +{ + // + // Disable the SoftSSI module. + // + pSSI->ucFlags &= ~(SOFTSSI_FLAG_ENABLE); +} + +//***************************************************************************** +// +//! Enables individual SoftSSI ``interrupt'' sources. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param ulIntFlags 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 ulIntFlags 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 *pSSI, unsigned long ulIntFlags) +{ + // + // Enable the specified "interrupts". + // + pSSI->ucIntMask |= ulIntFlags; +} + +//***************************************************************************** +// +//! Disables individual SoftSSI ``interrupt'' sources. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param ulIntFlags is a bit mask of the ``interrupt'' sources to be +//! disabled. +//! +//! Disables the indicated SoftSSI ``interrupt'' sources. The \e ulIntFlags +//! 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 *pSSI, unsigned long ulIntFlags) +{ + // + // Disable the specified "interrupts". + // + pSSI->ucIntMask &= ~(ulIntFlags); +} + +//***************************************************************************** +// +//! Gets the current ``interrupt'' status. +//! +//! \param pSSI 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. +// +//***************************************************************************** +unsigned long +SoftSSIIntStatus(tSoftSSI *pSSI, tBoolean bMasked) +{ + // + // Return either the "interrupt" status or the raw "interrupt" status as + // requested. + // + if(bMasked) + { + return(pSSI->ucIntStatus & pSSI->ucIntMask); + } + else + { + return(pSSI->ucIntStatus); + } +} + +//***************************************************************************** +// +//! Clears SoftSSI ``interrupt'' sources. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param ulIntFlags 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 ulIntFlags 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 *pSSI, unsigned long ulIntFlags) +{ + // + // Clear the requested "interrupt" sources. + // + pSSI->ucIntStatus &= ~(ulIntFlags) | SOFTSSI_TXFF | SOFTSSI_RXFF; +} + +//***************************************************************************** +// +//! Determines if there is any data in the receive FIFO. +//! +//! \param pSSI 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. +// +//***************************************************************************** +tBoolean +SoftSSIDataAvail(tSoftSSI *pSSI) +{ + // + // Return the availability of data. + // + return((pSSI->usRxBufferRead == pSSI->usRxBufferWrite) ? false : true); +} + +//***************************************************************************** +// +//! Determines if there is any space in the transmit FIFO. +//! +//! \param pSSI 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. +// +//***************************************************************************** +tBoolean +SoftSSISpaceAvail(tSoftSSI *pSSI) +{ + unsigned short usTemp; + + // + // Determine the values of the write pointer once incremented. + // + usTemp = pSSI->usTxBufferWrite + 1; + if(usTemp == pSSI->usTxBufferLen) + { + usTemp = 0; + } + + // + // Return the availability of space. + // + return((pSSI->usTxBufferRead == usTemp) ? false : true); +} + +//***************************************************************************** +// +//! Puts a data element into the SoftSSI transmit FIFO. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param ulData 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 ulData 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 ulData are discarded. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIDataPut(tSoftSSI *pSSI, unsigned long ulData) +{ + unsigned short usTemp; + + // + // Wait until there is space. + // + usTemp = pSSI->usTxBufferWrite + 1; + if(usTemp == pSSI->usTxBufferLen) + { + usTemp = 0; + } + while(usTemp == *(volatile unsigned short *)(&(pSSI->usTxBufferRead))) + { + } + + // + // Write the data to the SoftSSI. + // + pSSI->pusTxBuffer[pSSI->usTxBufferWrite] = ulData; + pSSI->usTxBufferWrite = usTemp; + + // + // See if a transmit FIFO "interrupt" needs to be cleared. + // + SoftSSITxInt(pSSI); +} + +//***************************************************************************** +// +//! Puts a data element into the SoftSSI transmit FIFO. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param ulData 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 ulData 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 ulData are discarded. +//! +//! \return Returns the number of elements written to the SSI transmit FIFO. +// +//***************************************************************************** +long +SoftSSIDataPutNonBlocking(tSoftSSI *pSSI, unsigned long ulData) +{ + unsigned short usTemp; + + // + // Determine the values of the write pointer once incremented. + // + usTemp = pSSI->usTxBufferWrite + 1; + if(usTemp == pSSI->usTxBufferLen) + { + usTemp = 0; + } + + // + // Check for space to write. + // + if(usTemp != pSSI->usTxBufferRead) + { + pSSI->pusTxBuffer[pSSI->usTxBufferWrite] = ulData; + pSSI->usTxBufferWrite = usTemp; + SoftSSITxInt(pSSI); + return(1); + } + else + { + return(0); + } +} + +//***************************************************************************** +// +//! Gets a data element from the SoftSSI receive FIFO. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param pulData 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 pulData parameter. +//! +//! \note Only the lower N bits of the value written to \e pulData 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 pulData contain valid data. +//! +//! \return None. +// +//***************************************************************************** +void +SoftSSIDataGet(tSoftSSI *pSSI, unsigned long *pulData) +{ + // + // Wait until there is data to be read. + // + while(pSSI->usRxBufferRead == + *(volatile unsigned short *)(&(pSSI->usRxBufferWrite))) + { + } + + // + // Read data from SoftSSI. + // + *pulData = pSSI->pusRxBuffer[pSSI->usRxBufferRead]; + pSSI->usRxBufferRead++; + if(pSSI->usRxBufferRead == pSSI->usRxBufferLen) + { + pSSI->usRxBufferRead = 0; + } + + // + // See if a receive FIFO "interrupt" needs to be cleared. + // + SoftSSIRxInt(pSSI); +} + +//***************************************************************************** +// +//! Gets a data element from the SoftSSI receive FIFO. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param pulData 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 ulData 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 pulData 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 pulData contain valid data. +//! +//! \return Returns the number of elements read from the SoftSSI receive FIFO. +// +//***************************************************************************** +long +SoftSSIDataGetNonBlocking(tSoftSSI *pSSI, unsigned long *pulData) +{ + // + // Check for data to read. + // + if(pSSI->usRxBufferRead != pSSI->usRxBufferWrite) + { + *pulData = pSSI->pusRxBuffer[pSSI->usRxBufferRead]; + pSSI->usRxBufferRead++; + if(pSSI->usRxBufferRead == pSSI->usRxBufferLen) + { + pSSI->usRxBufferRead = 0; + } + SoftSSIRxInt(pSSI); + return(1); + } + else + { + return(0); + } +} + +//***************************************************************************** +// +//! Determines whether the SoftSSI transmitter is busy or not. +//! +//! \param pSSI 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. +// +//***************************************************************************** +tBoolean +SoftSSIBusy(tSoftSSI *pSSI) +{ + // + // Determine if the SSI is busy. + // + return(((pSSI->ucState == SOFTSSI_STATE_IDLE) && + (((pSSI->ucFlags & SOFTSSI_FLAG_ENABLE) == 0) || + (pSSI->usTxBufferRead == pSSI->usTxBufferWrite))) ? false : true); +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftSSI module. +//! +//! \param pSSI 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 *pSSI, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + pSSI->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Fss signal. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin 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 *pSSI, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the Fss signal. + // + if(ulBase == 0) + { + pSSI->ulFssGPIO = 0; + } + else + { + pSSI->ulFssGPIO = ulBase + (ucPin << 2); + } +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Clk signal. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin 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 *pSSI, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the Clk signal. + // + pSSI->ulClkGPIO = ulBase + (ucPin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Tx signal. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin 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 *pSSI, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the Tx signal. + // + pSSI->ulTxGPIO = ulBase + (ucPin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftSSI Rx signal. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin 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 *pSSI, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the Rx signal. + // + if(ulBase == 0) + { + pSSI->ulRxGPIO = 0; + } + else + { + pSSI->ulRxGPIO = ulBase + (ucPin << 2); + } +} + +//***************************************************************************** +// +//! Sets the transmit FIFO buffer for a SoftSSI module. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param pusTxBuffer is the address of the transmit FIFO buffer. +//! \param usLen 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 *pSSI, unsigned short *pusTxBuffer, + unsigned short usLen) +{ + // + // Save the transmit FIFO buffer address and length. + // + pSSI->pusTxBuffer = pusTxBuffer; + pSSI->usTxBufferLen = usLen; + + // + // Reset the transmit FIFO read and write pointers. + // + pSSI->usTxBufferRead = 0; + pSSI->usTxBufferWrite = 0; +} + +//***************************************************************************** +// +//! Sets the receive FIFO buffer for a SoftSSI module. +//! +//! \param pSSI specifies the SoftSSI data structure. +//! \param pusRxBuffer is the address of the receive FIFO buffer. +//! \param usLen 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 *pSSI, unsigned short *pusRxBuffer, + unsigned short usLen) +{ + // + // Save the receive FIFO buffer address and length. + // + pSSI->pusRxBuffer = pusRxBuffer; + pSSI->usRxBufferLen = usLen; + + // + // Reset the receive FIFO read and write pointers. + // + pSSI->usRxBufferRead = 0; + pSSI->usRxBufferWrite = 0; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/softssi.h b/utils/softssi.h new file mode 100644 index 0000000..391e901 --- /dev/null +++ b/utils/softssi.h @@ -0,0 +1,280 @@ +//***************************************************************************** +// +// softssi.h - Defines and macros for the SoftSSI. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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. + /// + unsigned long ulFssGPIO; + + // + //! 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. + // + unsigned long ulClkGPIO; + + // + //! 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. + // + unsigned long ulTxGPIO; + + // + //! 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. + // + unsigned long ulRxGPIO; + + // + //! 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. + // + unsigned short *pusTxBuffer; + + // + //! 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. + // + unsigned short *pusRxBuffer; + + // + //! The length of the transmit FIFO. This member can be set via a direct + //! structure access or using the SoftSSITxBufferSet function. + // + unsigned short usTxBufferLen; + + // + //! 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. + // + unsigned short usTxBufferRead; + + // + //! 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. + // + unsigned short usTxBufferWrite; + + // + //! The length of the receive FIFO. This member can be set via a direct + //! structure access or using the SoftSSIRxBufferSet function. + // + unsigned short usRxBufferLen; + + // + //! 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. + // + unsigned short usRxBufferRead; + + // + //! 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. + // + unsigned short usRxBufferWrite; + + // + //! The word that is currently being transmitted. This member should not + //! be accessed or modified by the application. + // + unsigned short usTxData; + + // + //! The word that is currently being received. This member should not be + //! accessed or modified by the application. + // + unsigned short usRxData; + + // + //! The flags that control the operation of the SoftSSI module. This + //! member should not be accessed or modified by the application. + // + unsigned char ucFlags; + + // + //! 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. + // + unsigned char ucBits; + + // + //! The current state of the SoftSSI state machine. This member should not + //! be accessed or modified by the application. + // + unsigned char ucState; + + // + //! 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. + // + unsigned char ucCurrentBit; + + // + //! The set of virtual interrupts that should be sent to the callback + //! function. This member should not be accessed or modified by the + //! application. + // + unsigned char ucIntMask; + + // + //! The set of virtual interrupts that are currently asserted. This member + //! should not be accessed or modified by the application. + // + unsigned char ucIntStatus; + + // + //! 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. + // + unsigned char ucIdleCount; +} +tSoftSSI; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Values that can be passed to SoftSSIIntEnable, SoftSSIIntDisable, and +// SoftSSIIntClear as the ulIntFlags 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 tBoolean SoftSSIBusy(tSoftSSI *pSSI); +extern void SoftSSICallbackSet(tSoftSSI *pSSI, void (*pfnCallback)(void)); +extern void SoftSSIClkGPIOSet(tSoftSSI *pSSI, unsigned long ulBase, + unsigned char ucPin); +extern void SoftSSIConfigSet(tSoftSSI *pSSI, unsigned char ucProtocol, + unsigned char ucBits); +extern tBoolean SoftSSIDataAvail(tSoftSSI *pSSI); +extern void SoftSSIDataGet(tSoftSSI *pSSI, unsigned long *pulData); +extern long SoftSSIDataGetNonBlocking(tSoftSSI *pSSI, unsigned long *pulData); +extern void SoftSSIDataPut(tSoftSSI *pSSI, unsigned long ulData); +extern long SoftSSIDataPutNonBlocking(tSoftSSI *pSSI, unsigned long ulData); +extern void SoftSSIDisable(tSoftSSI *pSSI); +extern void SoftSSIEnable(tSoftSSI *pSSI); +extern void SoftSSIFssGPIOSet(tSoftSSI *pSSI, unsigned long ulBase, + unsigned char ucPin); +extern void SoftSSIIntClear(tSoftSSI *pSSI, unsigned long ulIntFlags); +extern void SoftSSIIntDisable(tSoftSSI *pSSI, unsigned long ulIntFlags); +extern void SoftSSIIntEnable(tSoftSSI *pSSI, unsigned long ulIntFlags); +extern unsigned long SoftSSIIntStatus(tSoftSSI *pSSI, tBoolean bMasked); +extern void SoftSSIRxBufferSet(tSoftSSI *pSSI, unsigned short *pusRxBuffer, + unsigned short usLen); +extern void SoftSSIRxGPIOSet(tSoftSSI *pSSI, unsigned long ulBase, + unsigned char ucPin); +extern tBoolean SoftSSISpaceAvail(tSoftSSI *pSSI); +extern void SoftSSITimerTick(tSoftSSI *pSSI); +extern void SoftSSITxBufferSet(tSoftSSI *pSSI, unsigned short *pusTxBuffer, + unsigned short usLen); +extern void SoftSSITxGPIOSet(tSoftSSI *pSSI, unsigned long ulBase, + unsigned char ucPin); + +//***************************************************************************** +// +// 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..4126bc7 --- /dev/null +++ b/utils/softuart.c @@ -0,0 +1,2584 @@ +//***************************************************************************** +// +// softuart.c - Driver for the SoftUART. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softuart_api +//! @{ +// +//***************************************************************************** + +#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 ucFlags structure member. +// +//***************************************************************************** +#define SOFTUART_FLAG_ENABLE 0x01 +#define SOFTUART_FLAG_TXBREAK 0x02 + +//***************************************************************************** +// +// The flags in the SoftUART ucRxFlags 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 usConfig 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 unsigned long g_pulParityOdd[] = +{ + 0x69969669, 0x96696996, 0x96696996, 0x69969669, + 0x96696996, 0x69969669, 0x69969669, 0x96696996 +}; + +//***************************************************************************** +// +//! Initializes the SoftUART module. +//! +//! \param pUART 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 *pUART) +{ + // + // Clear the SoftUART data structure. + // + memset(pUART, 0, sizeof(tSoftUART)); + + // + // Set the default transmit and receive buffer interrupt level. + // + pUART->usConfig = SOFTUART_CONFIG_TXLVL_4 | SOFTUART_CONFIG_RXLVL_4; +} + +//***************************************************************************** +// +//! Sets the configuration of a SoftUART module. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param ulConfig 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 ulConfig parameter. +//! +//! The \e ulConfig 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 *pUART, unsigned long ulConfig) +{ + // + // See if a GPIO pin has been set for Tx. + // + if(pUART->ulTxGPIO != 0) + { + // + // Configure the Tx pin. + // + MAP_GPIOPinTypeGPIOOutput(pUART->ulTxGPIO & 0xfffff000, + (pUART->ulTxGPIO & 0x00000fff) >> 2); + + // + // Set the Tx pin high. + // + HWREG(pUART->ulTxGPIO) = 255; + } + + // + // See if a GPIO pin has been set for Rx. + // + if(pUART->ulRxGPIOPort != 0) + { + // + // Configure the Rx pin. + // + MAP_GPIOPinTypeGPIOInput(pUART->ulRxGPIOPort, pUART->ucRxPin); + + // + // Set the Rx pin to generate an interrupt on the next falling edge. + // + MAP_GPIOIntTypeSet(pUART->ulRxGPIOPort, pUART->ucRxPin, + GPIO_FALLING_EDGE); + + // + // Enable the Rx pin interrupt. + // + MAP_GPIOPinIntClear(pUART->ulRxGPIOPort, pUART->ucRxPin); + MAP_GPIOPinIntEnable(pUART->ulRxGPIOPort, pUART->ucRxPin); + } + + // + // Make sure that the transmit and receive buffers are empty. + // + pUART->usTxBufferRead = 0; + pUART->usTxBufferWrite = 0; + pUART->usRxBufferRead = 0; + pUART->usRxBufferWrite = 0; + + // + // Save the data format. + // + pUART->usConfig = ((pUART->usConfig & SOFTUART_CONFIG_EXT_M) | + (ulConfig & SOFTUART_CONFIG_BASE_M)); + + // + // Enable the SoftUART module. + // + pUART->ucFlags |= SOFTUART_FLAG_ENABLE; + + // + // The next value to be written to the Tx pin is one since the SoftUART is + // idle. + // + pUART->ucTxNext = 255; + + // + // Start the SoftUART state machines in the idle state. + // + pUART->ucTxState = SOFTUART_TXSTATE_IDLE; + pUART->ucRxState = SOFTUART_RXSTATE_IDLE; +} + +//***************************************************************************** +// +//! Performs the periodic update of the SoftUART transmitter. +//! +//! \param pUART 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 *pUART) +{ + unsigned long ulTemp; + + // + // 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(pUART->ulTxGPIO) = pUART->ucTxNext; + + // + // Determine the current state of the state machine. + // + switch(pUART->ucTxState) + { + // + // The state machine is idle. + // + case SOFTUART_TXSTATE_IDLE: + { + // + // See if the SoftUART module is enabled. + // + if(!(pUART->ucFlags & 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(pUART->ucFlags & SOFTUART_FLAG_TXBREAK) + { + // + // The data line should be driven low while in the break state. + // + pUART->ucTxNext = 0; + + // + // Move to the break state. + // + pUART->ucTxState = SOFTUART_TXSTATE_BREAK; + } + + // + // Otherwise, see if there is data in the transmit buffer. + // + else if(pUART->usTxBufferRead != pUART->usTxBufferWrite) + { + // + // The data line should be driven low to indicate a start bit. + // + pUART->ucTxNext = 0; + + // + // Move to the start bit state. + // + pUART->ucTxState = 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. + // + pUART->ucTxData = pUART->pucTxBuffer[pUART->usTxBufferRead]; + + // + // The next value to be written to the data line is the LSB of the + // next data byte. + // + pUART->ucTxNext = (pUART->ucTxData & 1) ? 255 : 0; + + // + // Move to the data bit 0 state. + // + pUART->ucTxState = 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. + // + pUART->ucTxNext = + (pUART->ucTxData & (1 << pUART->ucTxState)) ? 255 : 0; + + // + // Advance to the next state. + // + pUART->ucTxState++; + + // + // 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(((pUART->usConfig & SOFTUART_CONFIG_WLEN_MASK) >> + SOFTUART_CONFIG_WLEN_S) == + (pUART->ucTxState - SOFTUART_TXSTATE_DATA_4)) + { + // + // See if parity is enabled. + // + if((pUART->usConfig & SOFTUART_CONFIG_PAR_MASK) != + SOFTUART_CONFIG_PAR_NONE) + { + // + // See if the parity is set to one. + // + if((pUART->usConfig & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ONE) + { + // + // The next value to be written to the data line is + // one. + // + pUART->ucTxNext = 255; + } + + // + // Otherwise, see if the parity is set to zero. + // + else if((pUART->usConfig & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ZERO) + { + // + // The next value to be written to the data line is + // zero. + // + pUART->ucTxNext = 0; + } + + // + // Otherwise, there is either even or odd parity. + // + else + { + // + // Find the odd parity for the data byte. + // + pUART->ucTxNext = + ((g_pulParityOdd[pUART->ucTxData >> 5] & + (1 << (pUART->ucTxData & 31))) ? 255 : 0); + + // + // If the parity is set to even, then invert the + // parity just computed (making it even parity). + // + if((pUART->usConfig & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_EVEN) + { + pUART->ucTxNext ^= 255; + } + } + + // + // Advance to the parity state. + // + pUART->ucTxState = SOFTUART_TXSTATE_PARITY; + } + + // + // Parity is not enabled. + // + else + { + // + // The next value to write to the data line is the stop + // bit. + // + pUART->ucTxNext = 255; + + // + // See if there are one or two stop bits. + // + if((pUART->usConfig & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + pUART->ucTxState = SOFTUART_TXSTATE_STOP_0; + } + else + { + // + // Advance to the one stop bit state. + // + pUART->ucTxState = 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. + // + pUART->ucTxNext = + (pUART->ucTxData & (1 << pUART->ucTxState)) ? 255 : 0; + + // + // Advance to the next state. + // + pUART->ucTxState++; + } + + // + // 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. + // + pUART->ucTxNext = 255; + + // + // See if there are one or two stop bits. + // + if((pUART->usConfig & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + pUART->ucTxState = SOFTUART_TXSTATE_STOP_0; + } + else + { + // + // Advance to the one stop bit state. + // + pUART->ucTxState = 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. + // + pUART->ucTxState = 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. + // + pUART->usTxBufferRead++; + if(pUART->usTxBufferRead == pUART->usTxBufferLen) + { + pUART->usTxBufferRead = 0; + } + + // + // Determine the number of characters in the transmit buffer. + // + if(pUART->usTxBufferRead > pUART->usTxBufferWrite) + { + ulTemp = (pUART->usTxBufferLen - + (pUART->usTxBufferRead - pUART->usTxBufferWrite)); + } + else + { + ulTemp = pUART->usTxBufferWrite - pUART->usTxBufferRead; + } + + // + // If the transmit buffer fullness just crossed the programmed + // level, generate a transmit "interrupt". + // + if(ulTemp == pUART->usTxBufferLevel) + { + pUART->usIntStatus |= SOFTUART_INT_TX; + } + + // + // See if the SoftUART module is enabled. + // + if(!(pUART->ucFlags & SOFTUART_FLAG_ENABLE)) + { + // + // The SoftUART module is not enabled, so do advance to the + // idle state. + // + pUART->ucTxState = SOFTUART_TXSTATE_IDLE; + } + + // + // See if the break signal should be asserted. + // + else if(pUART->ucFlags & SOFTUART_FLAG_TXBREAK) + { + // + // The data line should be driven low while in the break state. + // + pUART->ucTxNext = 0; + + // + // Move to the break state. + // + pUART->ucTxState = SOFTUART_TXSTATE_BREAK; + } + + // + // Otherwise, see if there is data in the transmit buffer. + // + else if(pUART->usTxBufferRead != pUART->usTxBufferWrite) + { + // + // The data line should be driven low to indicate a start bit. + // + pUART->ucTxNext = 0; + + // + // Move to the start bit state. + // + pUART->ucTxState = SOFTUART_TXSTATE_START; + } + + // + // Otherwise, there is nothing to do. + // + else + { + // + // Assert the end of transmission "interrupt". + // + pUART->usIntStatus |= SOFTUART_INT_EOT; + + // + // Advance to the idle state. + // + pUART->ucTxState = 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(!(pUART->ucFlags & SOFTUART_FLAG_ENABLE) || + !(pUART->ucFlags & SOFTUART_FLAG_TXBREAK)) + { + // + // The data line should be driven high to indicate it is idle. + // + pUART->ucTxNext = 255; + + // + // Advance to the idle state. + // + pUART->ucTxState = 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(((pUART->usIntStatus & pUART->usIntMask) != 0) && + (pUART->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + pUART->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Handles the assertion of the receive ``interrupt''. +//! +//! \param pUART 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 *pUART) +{ + unsigned long ulTemp; + + // + // Determine the number of characters in the receive buffer. + // + if(pUART->usRxBufferWrite > pUART->usRxBufferRead) + { + ulTemp = pUART->usRxBufferWrite - pUART->usRxBufferRead; + } + else + { + ulTemp = (pUART->usRxBufferLen + pUART->usRxBufferWrite - + pUART->usRxBufferRead); + } + + // + // If the receive buffer fullness just crossed the programmed level, + // generate a receive "interrupt". + // + if(ulTemp == pUART->usRxBufferLevel) + { + pUART->usIntStatus |= SOFTUART_INT_RX; + } +} + +//***************************************************************************** +// +//! Performs the periodic update of the SoftUART receiver. +//! +//! \param pUART 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. +// +//***************************************************************************** +unsigned long +SoftUARTRxTick(tSoftUART *pUART, tBoolean bEdgeInt) +{ + unsigned long ulPinState, ulTemp, ulRet; + + // + // Read the current state of the Rx data line. + // + ulPinState = MAP_GPIOPinRead(pUART->ulRxGPIOPort, pUART->ucRxPin); + + // + // The default return code inidicates that the receive timer does not need + // to be stopped. + // + ulRet = SOFTUART_RXTIMER_NOP; + + // + // See if this is an edge interrupt while delaying for the receive timeout + // interrupt. + // + if(bEdgeInt && (pUART->ucRxState == SOFTUART_RXSTATE_DELAY)) + { + // + // The receive timeout has been cancelled since the next character has + // started, so go to the idle state. + // + pUART->ucRxState = SOFTUART_RXSTATE_IDLE; + } + + // + // Determine the current state of the state machine. + // + switch(pUART->ucRxState) + { + // + // 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. + // + MAP_GPIOPinIntClear(pUART->ulRxGPIOPort, pUART->ucRxPin); + MAP_GPIOPinIntDisable(pUART->ulRxGPIOPort, pUART->ucRxPin); + + // + // Clear the receive data buffer. + // + pUART->ucRxData = 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). + // + pUART->ucRxFlags = ((pUART->ucRxFlags & SOFTUART_RXFLAG_OE) | + SOFTUART_RXFLAG_BE); + + // + // Advance to the first data bit state. + // + pUART->ucRxState = 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(ulPinState != 0) + { + // + // Set this bit of the received character. + // + pUART->ucRxData |= 1 << pUART->ucRxState; + + // + // Clear the break error since a non-zero bit was received. + // + pUART->ucRxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // Advance to the next state. + // + pUART->ucRxState++; + + // + // 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(ulPinState != 0) + { + // + // Set this bit of the received character. + // + pUART->ucRxData |= 1 << pUART->ucRxState; + + // + // Clear the break error since a non-zero bit was received. + // + pUART->ucRxFlags &= ~(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(((pUART->usConfig & SOFTUART_CONFIG_WLEN_MASK) >> + SOFTUART_CONFIG_WLEN_S) == + (pUART->ucRxState - SOFTUART_RXSTATE_DATA_4)) + { + // + // See if parity is enabled. + // + if((pUART->usConfig & SOFTUART_CONFIG_PAR_MASK) != + SOFTUART_CONFIG_PAR_NONE) + { + // + // Advance to the parity state. + // + pUART->ucRxState = SOFTUART_RXSTATE_PARITY; + } + + // + // Otherwise, see if there are one or two stop bits. + // + else if((pUART->usConfig & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + pUART->ucRxState = SOFTUART_RXSTATE_STOP_0; + } + + // + // Otherwise, advance to the one stop bit state. + // + else + { + pUART->ucRxState = SOFTUART_RXSTATE_STOP_1; + } + } + + // + // Otherwise, there are more bits to receive. + // + else + { + // + // Advance to the next state. + // + pUART->ucRxState++; + } + + // + // 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((pUART->usConfig & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ONE) + { + // + // Set the expected parity to one. + // + ulTemp = pUART->ucRxPin; + } + + // + // Otherwise, see if the parity is set to zero. + // + else if((pUART->usConfig & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_ZERO) + { + // + // Set the expected parity to zero. + // + ulTemp = 0; + } + + // + // Otherwise, there is either even or odd parity. + // + else + { + // + // Find the odd parity for the data byte. + // + ulTemp = ((g_pulParityOdd[pUART->ucRxData >> 5] & + (1 << (pUART->ucRxData & 31))) ? + pUART->ucRxPin : 0); + + // + // If the parity is set to even, then invert the parity just + // computed (making it even parity). + // + if((pUART->usConfig & SOFTUART_CONFIG_PAR_MASK) == + SOFTUART_CONFIG_PAR_EVEN) + { + ulTemp ^= pUART->ucRxPin; + } + } + + // + // See if the pin state matches the expected parity. + // + if(ulPinState != ulTemp) + { + // + // The parity does not match, so set the parity error flag. + // + pUART->ucRxFlags |= SOFTUART_RXFLAG_PE; + } + + // + // See if the Rx pin is high. + // + if(ulPinState != 0) + { + // + // Clear the break error since a non-zero bit was received. + // + pUART->ucRxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // See if there are one or two stop bits. + // + if((pUART->usConfig & SOFTUART_CONFIG_STOP_MASK) == + SOFTUART_CONFIG_STOP_TWO) + { + // + // Advance to the two stop bits state. + // + pUART->ucRxState = SOFTUART_RXSTATE_STOP_0; + } + else + { + // + // Advance to the one stop bit state. + // + pUART->ucRxState = 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(ulPinState == 0) + { + // + // Since the Rx pin is low, there is a framing error. + // + pUART->ucRxFlags |= SOFTUART_RXFLAG_FE; + } + else + { + // + // Clear the break error since a non-zero bit was received. + // + pUART->ucRxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // Advance to the one stop bit state. + // + pUART->ucRxState = 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(ulPinState == 0) + { + // + // Since the Rx pin is low, there is a framing error. + // + pUART->ucRxFlags |= SOFTUART_RXFLAG_FE; + } + else + { + // + // Clear the break error since a non-zero bit was received. + // + pUART->ucRxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // See if the break error is still asserted (meaning that every bit + // received was zero). + // + if(pUART->ucRxFlags & SOFTUART_RXFLAG_BE) + { + // + // Since every bit was zero, advance to the break state. + // + pUART->ucRxState = SOFTUART_RXSTATE_BREAK; + + // + // This state has been handled. + // + break; + } + + // + // Compute the value of the write pointer advanced by one. + // + ulTemp = pUART->usRxBufferWrite + 1; + if(ulTemp == pUART->usRxBufferLen) + { + ulTemp = 0; + } + + // + // See if there is space in the receive buffer. + // + if(ulTemp == pUART->usRxBufferRead) + { + // + // 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. + // + pUART->ucRxFlags |= SOFTUART_RXFLAG_OE; + + // + // Set the receive overrun "interrupt" and status if it is not + // already set. + // + if(!(pUART->ucRxStatus & SOFTUART_RXERROR_OVERRUN)) + { + pUART->ucRxStatus |= SOFTUART_RXERROR_OVERRUN; + pUART->usIntStatus |= 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. + // + pUART->pusRxBuffer[pUART->usRxBufferWrite] = + pUART->ucRxData | (pUART->ucRxFlags << 8); + + // + // Advance the write pointer. + // + pUART->usRxBufferWrite = ulTemp; + + // + // Clear the receive flags, most importantly the overrun flag + // since it was just written into the receive buffer. + // + pUART->ucRxFlags = 0; + + // + // Assert the receive "interrupt" if appropriate. + // + SoftUARTRxWriteInt(pUART); + } + + // + // See if this character had a parity error. + // + if(pUART->ucRxFlags & SOFTUART_RXFLAG_PE) + { + // + // Assert the parity error "interrupt". + // + pUART->usIntStatus |= SOFTUART_INT_PE; + } + + // + // See if this character had a framing error. + // + if(pUART->ucRxFlags & SOFTUART_RXFLAG_FE) + { + // + // Assert the framing error "interrupt". + // + pUART->usIntStatus |= SOFTUART_INT_FE; + } + + // + // Enable the falling edge interrupt on the Rx pin so that the next + // start bit can be detected. + // + MAP_GPIOPinIntClear(pUART->ulRxGPIOPort, pUART->ucRxPin); + MAP_GPIOPinIntEnable(pUART->ulRxGPIOPort, pUART->ucRxPin); + + // + // Advance to the receive timeout delay state. + // + pUART->ucRxData = 0; + pUART->ucRxState = 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(ulPinState != 0) + { + // + // Clear the break error since a non-zero bit was received. + // + pUART->ucRxFlags &= ~(SOFTUART_RXFLAG_BE); + } + + // + // Compute the value of the write pointer advanced by one. + // + ulTemp = pUART->usRxBufferWrite + 1; + if(ulTemp == pUART->usRxBufferLen) + { + ulTemp = 0; + } + + // + // See if there is space in the receive buffer. + // + if(ulTemp == pUART->usRxBufferRead) + { + // + // 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. + // + pUART->ucRxFlags |= SOFTUART_RXFLAG_OE; + + // + // Set the receive overrun "interrupt" and status if it is not + // already set. + // + if(!(pUART->ucRxStatus & SOFTUART_RXERROR_OVERRUN)) + { + pUART->ucRxStatus |= SOFTUART_RXERROR_OVERRUN; + pUART->usIntStatus |= 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. + // + pUART->pusRxBuffer[pUART->usRxBufferWrite] = + pUART->ucRxData | (pUART->ucRxFlags << 8); + + // + // Advance the write pointer. + // + pUART->usRxBufferWrite = ulTemp; + + // + // Clear the receive flags, most importantly the overrun flag + // since it was just written into the receive buffer. + // + pUART->ucRxFlags = 0; + + // + // Assert the receive "interrupt" if appropriate. + // + SoftUARTRxWriteInt(pUART); + } + + // + // See if this was a break error. + // + if(pUART->ucRxFlags & SOFTUART_RXFLAG_BE) + { + // + // Assert the break error "interrupt". + // + pUART->usIntStatus |= SOFTUART_INT_BE; + } + + // + // See if this character had a parity error. + // + if(pUART->ucRxFlags & SOFTUART_RXFLAG_PE) + { + // + // Assert the parity error "interrupt". + // + pUART->usIntStatus |= SOFTUART_INT_PE; + } + + // + // Assert the framing error "interrupt". + // + pUART->usIntStatus |= SOFTUART_INT_FE; + + // + // Enable the falling edge interrupt on the Rx pin so that the next + // start bit can be detected. + // + MAP_GPIOPinIntClear(pUART->ulRxGPIOPort, pUART->ucRxPin); + MAP_GPIOPinIntEnable(pUART->ulRxGPIOPort, pUART->ucRxPin); + + // + // Advance to the receive timeout delay state. + // + pUART->ucRxData = 0; + pUART->ucRxState = 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(pUART->ucRxData++ == 32) + { + // + // Assert the receive timeout "interrupt". + // + pUART->usIntStatus |= SOFTUART_INT_RT; + + // + // Tell the caller that the receive timer can be disabled. + // + ulRet = 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(((pUART->usIntStatus & pUART->usIntMask) != 0) && + (pUART->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + pUART->pfnIntCallback(); + } + + // + // Return to the caller. + // + return(ulRet); +} + +//***************************************************************************** +// +//! Sets the type of parity. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param ulParity specifies the type of parity to use. +//! +//! Sets the type of parity to use for transmitting and expect when receiving. +//! The \e ulParity 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 *pUART, unsigned long ulParity) +{ + // + // Check the arguments. + // + ASSERT((ulParity == SOFTUART_CONFIG_PAR_NONE) || + (ulParity == SOFTUART_CONFIG_PAR_EVEN) || + (ulParity == SOFTUART_CONFIG_PAR_ODD) || + (ulParity == SOFTUART_CONFIG_PAR_ONE) || + (ulParity == SOFTUART_CONFIG_PAR_ZERO)); + + // + // Set the parity mode. + // + pUART->usConfig = (pUART->usConfig & SOFTUART_CONFIG_PAR_MASK) | ulParity; +} + +//***************************************************************************** +// +//! Gets the type of parity currently being used. +//! +//! \param pUART 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. +// +//***************************************************************************** +unsigned long +SoftUARTParityModeGet(tSoftUART *pUART) +{ + // + // Return the current parity setting. + // + return(pUART->usConfig & SOFTUART_CONFIG_PAR_MASK); +} + +//***************************************************************************** +// +//! Sets the transmit ``interrupt'' buffer level. +//! +//! \param pUART 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 *pUART) +{ + // + // Determine the transmit buffer "interrupt" fullness setting. + // + switch(pUART->usConfig & 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. + // + pUART->usTxBufferLevel = pUART->usTxBufferLen / 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. + // + pUART->usTxBufferLevel = pUART->usTxBufferLen / 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. + // + pUART->usTxBufferLevel = pUART->usTxBufferLen / 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. + // + pUART->usTxBufferLevel = (pUART->usTxBufferLen * 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. + // + pUART->usTxBufferLevel = (pUART->usTxBufferLen * 7) / 8; + + // + // This setting has been handled. + // + break; + } + } +} + +//***************************************************************************** +// +//! Sets the receive ``interrupt'' buffer level. +//! +//! \param pUART 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 *pUART) +{ + // + // Determine the receive buffer "interrupt" fullness setting. + // + switch(pUART->usConfig & 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. + // + pUART->usRxBufferLevel = pUART->usRxBufferLen / 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. + // + pUART->usRxBufferLevel = pUART->usRxBufferLen / 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. + // + pUART->usRxBufferLevel = pUART->usRxBufferLen / 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. + // + pUART->usRxBufferLevel = (pUART->usRxBufferLen * 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. + // + pUART->usRxBufferLevel = (pUART->usRxBufferLen * 7) / 8; + + // + // This setting has been handled. + // + break; + } + } +} + +//***************************************************************************** +// +//! Sets the buffer level at which ``interrupts'' are generated. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param ulTxLevel 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 ulRxLevel 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 *pUART, unsigned long ulTxLevel, + unsigned long ulRxLevel) +{ + // + // Check the arguments. + // + ASSERT((ulTxLevel == SOFTUART_FIFO_TX1_8) || + (ulTxLevel == SOFTUART_FIFO_TX2_8) || + (ulTxLevel == SOFTUART_FIFO_TX4_8) || + (ulTxLevel == SOFTUART_FIFO_TX6_8) || + (ulTxLevel == SOFTUART_FIFO_TX7_8)); + ASSERT((ulRxLevel == SOFTUART_FIFO_RX1_8) || + (ulRxLevel == SOFTUART_FIFO_RX2_8) || + (ulRxLevel == SOFTUART_FIFO_RX4_8) || + (ulRxLevel == SOFTUART_FIFO_RX6_8) || + (ulRxLevel == SOFTUART_FIFO_RX7_8)); + + // + // Save the buffer "interrupt" levels. + // + pUART->usConfig = ((pUART->usConfig & SOFTUART_CONFIG_BASE_M) | + ((ulTxLevel | ulRxLevel) << 8)); + + // + // Compute the new buffer "interrupt" levels. + // + SoftUARTTxLevelSet(pUART); + SoftUARTRxLevelSet(pUART); +} + +//***************************************************************************** +// +//! Gets the buffer level at which ``interrupts'' are generated. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param pulTxLevel 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 pulRxLevel 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 *pUART, unsigned long *pulTxLevel, + unsigned long *pulRxLevel) +{ + // + // Extract the transmit and receive buffer levels. + // + *pulTxLevel = (pUART->usConfig & SOFTUART_CONFIG_TXLVL_M) >> 8; + *pulRxLevel = (pUART->usConfig & SOFTUART_CONFIG_RXLVL_M) >> 8; +} + +//***************************************************************************** +// +//! Gets the current configuration of a UART. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param pulConfig is a pointer to storage for the data format. +//! +//! Returns the data format of the SoftUART. The data format returned in +//! \e pulConfig is enumerated the same as the \e ulConfig parameter of +//! SoftUARTConfigSet(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTConfigGet(tSoftUART *pUART, unsigned long *pulConfig) +{ + // + // Get the data format. + // + *pulConfig = pUART->usConfig & SOFTUART_CONFIG_BASE_M; +} + +//***************************************************************************** +// +//! Enables the SoftUART. +//! +//! \param pUART specifies the SoftUART data structure. +//! +//! This function enables the SoftUART, allowing data to be transmitted and +//! received. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTEnable(tSoftUART *pUART) +{ + // + // Enable the SoftUART. + // + pUART->ucFlags |= SOFTUART_FLAG_ENABLE; +} + +//***************************************************************************** +// +//! Disables the SoftUART. +//! +//! \param pUART specifies the SoftUART data structure. +//! +//! This function disables the SoftUART after waiting for it to become idle. +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTDisable(tSoftUART *pUART) +{ + // + // Wait for end of TX. + // + while(SoftUARTBusy(pUART)) + { + } + + // + // Disable the SoftUART. + // + pUART->ucFlags &= ~(SOFTUART_FLAG_ENABLE); +} + +//***************************************************************************** +// +//! Determines if there are any characters in the receive buffer. +//! +//! \param pUART 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. +// +//***************************************************************************** +tBoolean +SoftUARTCharsAvail(tSoftUART *pUART) +{ + // + // Return the availability of characters. + // + return((pUART->usRxBufferRead == pUART->usRxBufferWrite) ? false : true); +} + +//***************************************************************************** +// +//! Determines if there is any space in the transmit buffer. +//! +//! \param pUART 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. +// +//***************************************************************************** +tBoolean +SoftUARTSpaceAvail(tSoftUART *pUART) +{ + unsigned short usTemp; + + // + // Determine the values of the write pointer once incremented. + // + usTemp = pUART->usTxBufferWrite + 1; + if(usTemp == pUART->usTxBufferLen) + { + usTemp = 0; + } + + // + // Return the availability of space. + // + return((pUART->usTxBufferRead == usTemp) ? false : true); +} + +//***************************************************************************** +// +//! Handles the deassertion of the receive ``interrupts''. +//! +//! \param pUART 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 *pUART) +{ + unsigned long ulTemp; + + // + // Determine the number of characters in the receive buffer. + // + if(pUART->usRxBufferWrite > pUART->usRxBufferRead) + { + ulTemp = pUART->usRxBufferWrite - pUART->usRxBufferRead; + } + else + { + ulTemp = (pUART->usRxBufferLen + pUART->usRxBufferWrite - + pUART->usRxBufferRead); + } + + // + // See if the number of characters in the receive buffer have dropped below + // the receive trigger level. + // + if(ulTemp < pUART->usRxBufferLevel) + { + // + // Deassert the receive "interrupt". + // + pUART->usIntStatus &= ~(SOFTUART_INT_RX); + } + + // + // See if the receive buffer is now empty. + // + if(ulTemp == 0) + { + // + // Deassert the receive timeout "interrupt". + // + pUART->usIntStatus &= ~(SOFTUART_INT_RT); + } +} + +//***************************************************************************** +// +//! Receives a character from the specified port. +//! +//! \param pUART 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 long. A \b -1 isreturned if there are no characters present in the +//! receive buffer. The SoftUARTCharsAvail() function should be called before +//! attempting to call this function. +// +//***************************************************************************** +long +SoftUARTCharGetNonBlocking(tSoftUART *pUART) +{ + long lTemp; + + // + // See if there are any characters in the receive buffer. + // + if(pUART->usRxBufferRead != pUART->usRxBufferWrite) + { + // + // Read the next character. + // + lTemp = pUART->pusRxBuffer[pUART->usRxBufferRead]; + pUART->usRxBufferRead++; + if(pUART->usRxBufferRead == pUART->usRxBufferLen) + { + pUART->usRxBufferRead = 0; + } + + // + // Deassert the receive "interrupt(s)" if appropriate. + // + SoftUARTRxReadInt(pUART); + + // + // Set the receive status to match this character. + // + pUART->ucRxStatus = ((pUART->ucRxStatus & SOFTUART_RXERROR_OVERRUN) | + ((lTemp >> 8) & ~(SOFTUART_RXERROR_OVERRUN))); + + // + // Return this character. + // + return(lTemp); + } + else + { + // + // There are no characters, so return a failure. + // + return(-1); + } +} + +//***************************************************************************** +// +//! Waits for a character from the specified port. +//! +//! \param pUART 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 long. +// +//***************************************************************************** +long +SoftUARTCharGet(tSoftUART *pUART) +{ + long lTemp; + + // + // Wait until a char is available. + // + while(pUART->usRxBufferRead == + *(volatile unsigned short *)(&(pUART->usRxBufferWrite))) + { + } + + // + // Read the next character. + // + lTemp = pUART->pusRxBuffer[pUART->usRxBufferRead]; + pUART->usRxBufferRead++; + if(pUART->usRxBufferRead == pUART->usRxBufferLen) + { + pUART->usRxBufferRead = 0; + } + + // + // Deassert the receive "interrupt(s)" if appropriate. + // + SoftUARTRxReadInt(pUART); + + // + // Set the receive status to match this character. + // + pUART->ucRxStatus = ((pUART->ucRxStatus & SOFTUART_RXERROR_OVERRUN) | + ((lTemp >> 8) & ~(SOFTUART_RXERROR_OVERRUN))); + + // + // Return this character. + // + return(lTemp); +} + +//***************************************************************************** +// +//! Sends a character to the specified port. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param ucData is the character to be transmitted. +//! +//! Writes the character \e ucData 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. +// +//***************************************************************************** +tBoolean +SoftUARTCharPutNonBlocking(tSoftUART *pUART, unsigned char ucData) +{ + unsigned short usTemp; + + // + // Determine the values of the write pointer once incremented. + // + usTemp = pUART->usTxBufferWrite + 1; + if(usTemp == pUART->usTxBufferLen) + { + usTemp = 0; + } + + // + // See if there is space in the transmit buffer. + // + if(usTemp != pUART->usTxBufferRead) + { + // + // Write this character to the transmit buffer. + // + pUART->pucTxBuffer[pUART->usTxBufferWrite] = ucData; + pUART->usTxBufferWrite = usTemp; + + // + // 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 pUART specifies the SoftUART data structure. +//! \param ucData is the character to be transmitted. +//! +//! Sends the character \e ucData 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 *pUART, unsigned char ucData) +{ + unsigned short usTemp; + + // + // Wait until space is available. + // + usTemp = pUART->usTxBufferWrite + 1; + if(usTemp == pUART->usTxBufferLen) + { + usTemp = 0; + } + while(usTemp == *(volatile unsigned short *)(&(pUART->usTxBufferRead))) + { + } + + // + // Send the char. + // + pUART->pucTxBuffer[pUART->usTxBufferWrite] = ucData; + pUART->usTxBufferWrite = usTemp; +} + +//***************************************************************************** +// +//! Causes a BREAK to be sent. +//! +//! \param pUART 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 *pUART, tBoolean bBreakState) +{ + // + // Set the break condition as requested. + // + if(bBreakState) + { + pUART->ucFlags |= SOFTUART_FLAG_TXBREAK; + } + else + { + pUART->ucFlags &= ~(SOFTUART_FLAG_TXBREAK); + } +} + +//***************************************************************************** +// +//! Determines whether the UART transmitter is busy or not. +//! +//! \param pUART 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. +// +//***************************************************************************** +tBoolean +SoftUARTBusy(tSoftUART *pUART) +{ + // + // Determine if the UART is busy. + // + return(((pUART->ucTxState == SOFTUART_TXSTATE_IDLE) && + (((pUART->ucFlags & SOFTUART_FLAG_ENABLE) == 0) || + (pUART->usTxBufferRead == pUART->usTxBufferWrite))) ? + false : true); +} + +//***************************************************************************** +// +//! Enables individual SoftUART ``interrupt'' sources. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param ulIntFlags 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 ulIntFlags 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 *pUART, unsigned long ulIntFlags) +{ + // + // Enable the specified interrupts. + // + pUART->usIntMask |= ulIntFlags; +} + +//***************************************************************************** +// +//! Disables individual SoftUART ``interrupt'' sources. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param ulIntFlags 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 ulIntFlags parameter has the same definition as the \e ulIntFlags +//! parameter to SoftUARTIntEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTIntDisable(tSoftUART *pUART, unsigned long ulIntFlags) +{ + // + // Disable the specified interrupts. + // + pUART->usIntMask &= ~(ulIntFlags); +} + +//***************************************************************************** +// +//! Gets the current SoftUART ``interrupt'' status. +//! +//! \param pUART 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(). +// +//***************************************************************************** +unsigned long +SoftUARTIntStatus(tSoftUART *pUART, tBoolean bMasked) +{ + // + // Return either the interrupt status or the raw interrupt status as + // requested. + // + if(bMasked) + { + return(pUART->usIntStatus & pUART->usIntMask); + } + else + { + return(pUART->usIntStatus); + } +} + +//***************************************************************************** +// +//! Clears SoftUART ``interrupt'' sources. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param ulIntFlags 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 ulIntFlags parameter has the same definition as the \e ulIntFlags +//! parameter to SoftUARTIntEnable(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftUARTIntClear(tSoftUART *pUART, unsigned long ulIntFlags) +{ + // + // Clear the requested interrupt sources. + // + pUART->usIntStatus &= ~(ulIntFlags); +} + +//***************************************************************************** +// +//! Gets current receiver errors. +//! +//! \param pUART 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. +// +//***************************************************************************** +unsigned long +SoftUARTRxErrorGet(tSoftUART *pUART) +{ + // + // Return the current value of the receive status. + // + return(pUART->ucRxStatus); +} + +//***************************************************************************** +// +//! Clears all reported receiver errors. +//! +//! \param pUART 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 *pUART) +{ + // + // Clear any receive error status. + // + pUART->ucRxStatus = 0; +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftUART module. +//! +//! \param pUART 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 *pUART, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + pUART->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftUART Tx signal. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin 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 *pUART, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the Tx signal. + // + if(ulBase == 0) + { + pUART->ulTxGPIO = 0; + } + else + { + pUART->ulTxGPIO = ulBase + (ucPin << 2); + } +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftUART Rx signal. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin 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 *pUART, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the Rx signal. + // + if(ulBase == 0) + { + pUART->ulRxGPIOPort = 0; + pUART->ucRxPin = 0; + } + else + { + pUART->ulRxGPIOPort = ulBase; + pUART->ucRxPin = ucPin; + } +} + +//***************************************************************************** +// +//! Sets the transmit buffer for a SoftUART module. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param pucTxBuffer is the address of the transmit buffer. +//! \param usLen 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 *pUART, unsigned char *pucTxBuffer, + unsigned short usLen) +{ + // + // Save the transmit buffer address and length. + // + pUART->pucTxBuffer = pucTxBuffer; + pUART->usTxBufferLen = usLen; + + // + // Reset the transmit buffer read and write pointers. + // + pUART->usTxBufferRead = 0; + pUART->usTxBufferWrite = 0; + + // + // Compute the new buffer "interrupt" level. + // + SoftUARTTxLevelSet(pUART); +} + +//***************************************************************************** +// +//! Sets the receive buffer for a SoftUART module. +//! +//! \param pUART specifies the SoftUART data structure. +//! \param pusRxBuffer is the address of the receive buffer. +//! \param usLen 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 *pUART, unsigned short *pusRxBuffer, + unsigned short usLen) +{ + // + // Save the receive buffer address and length. + // + pUART->pusRxBuffer = pusRxBuffer; + pUART->usRxBufferLen = usLen; + + // + // Reset the receive read and write pointers. + // + pUART->usRxBufferRead = 0; + pUART->usRxBufferWrite = 0; + + // + // Compute the new buffer "interrupt" level. + // + SoftUARTRxLevelSet(pUART); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/softuart.h b/utils/softuart.h new file mode 100644 index 0000000..477ca86 --- /dev/null +++ b/utils/softuart.h @@ -0,0 +1,374 @@ +//***************************************************************************** +// +// softuart.h - Defines and macros for the SoftUART. +// +// Copyright (c) 2010-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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. + // + unsigned long ulTxGPIO; + + // + //! 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. + // + unsigned long ulRxGPIOPort; + + // + //! 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. + // + unsigned char *pucTxBuffer; + + // + //! 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. + // + unsigned short *pusRxBuffer; + + // + //! The length of the transmit buffer. This member can be set via a direct + //! structure access or using the SoftUARTTxBufferSet function. + // + unsigned short usTxBufferLen; + + // + //! The index into the transmit buffer of the next character to be + //! transmitted. This member should not be accessed or modified by the + //! application. + // + unsigned short usTxBufferRead; + + // + //! 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. + // + unsigned short usTxBufferWrite; + + // + //! The transmit buffer level at which the transmit interrupt is asserted. + //! This member should not be accessed or modified by the application. + // + unsigned short usTxBufferLevel; + + // + //! The length of the receive buffer. This member can be set via a direct + //! structure access or using the SoftUARTRxBufferSet function. + // + unsigned short usRxBufferLen; + + // + //! 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. + // + unsigned short usRxBufferRead; + + // + //! 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. + // + unsigned short usRxBufferWrite; + + // + //! The receive buffer level at which the receive interrupt is asserted. + //! This member should not be accessed or modified by the application. + // + unsigned short usRxBufferLevel; + + // + //! The set of virtual interrupts that are currently asserted. This member + //! should not be accessed or modified by the application. + // + unsigned short usIntStatus; + + // + //! The set of virtual interrupts that should be sent to the callback + //! function. This member should not be accessed or modified by the + //! application. + // + unsigned short usIntMask; + + // + //! The configuration of the SoftUART module. This member can be set via + //! the SoftUARTConfigSet and SoftUARTFIFOLevelSet functions. + // + unsigned short usConfig; + + // + //! The flags that control the operation of the SoftUART module. This + //! member should not be be accessed or modified by the application. + // + unsigned char ucFlags; + + // + //! The current state of the SoftUART transmit state machine. This member + //! should not be accessed or modified by the application. + // + unsigned char ucTxState; + + // + //! 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. + // + unsigned char ucTxNext; + + // + //! The character that is currently be sent via the Tx pin. This member + //! should not be accessed or modified by the application. + // + unsigned char ucTxData; + + // + //! 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. + // + unsigned char ucRxPin; + + // + //! The current state of the SoftUART receive state machine. This member + //! should not be accessed or modified by the application. + // + unsigned char ucRxState; + + // + //! The character that is currently being received via the Rx pin. This + //! member should not be accessed or modified by the application. + // + unsigned char ucRxData; + + // + //! 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. + // + unsigned char ucRxFlags; + + // + //! The receive error status. This member should only be accessed via the + //! SoftUARTRxErrorGet and SoftURATRxErrorClear functions. + // + unsigned char ucRxStatus; +} +tSoftUART; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Values that can be passed to SoftUARTIntEnable, SoftUARTIntDisable, and +// SoftUARTIntClear as the ulIntFlags 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 ulConfig parameter and +// returned by SoftUARTConfigGet in the pulConfig parameter. Additionally, the +// UART_CONFIG_PAR_* subset can be passed to SoftUARTParityModeSet as the +// ulParity 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 ulTxLevel parameter +// and returned by SoftUARTFIFOLevelGet in the pulTxLevel. +// +//***************************************************************************** +#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 ulRxLevel parameter +// and returned by SoftUARTFIFOLevelGet in the pulRxLevel. +// +//***************************************************************************** +#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 *pUART); +extern void SoftUARTParityModeSet(tSoftUART *pUART, unsigned long ulParity); +extern unsigned long SoftUARTParityModeGet(tSoftUART *pUART); +extern void SoftUARTFIFOLevelSet(tSoftUART *pUART, unsigned long ulTxLevel, + unsigned long ulRxLevel); +extern void SoftUARTFIFOLevelGet(tSoftUART *pUART, unsigned long *pulTxLevel, + unsigned long *pulRxLevel); +extern void SoftUARTConfigSet(tSoftUART *pUART, unsigned long ulConfig); +extern void SoftUARTConfigGet(tSoftUART *pUART, unsigned long *pulConfig); +extern void SoftUARTEnable(tSoftUART *pUART); +extern void SoftUARTDisable(tSoftUART *pUART); +extern void SoftUARTFIFOEnable(tSoftUART *pUART); +extern void SoftUARTFIFODisable(tSoftUART *pUART); +extern tBoolean SoftUARTCharsAvail(tSoftUART *pUART); +extern tBoolean SoftUARTSpaceAvail(tSoftUART *pUART); +extern long SoftUARTCharGetNonBlocking(tSoftUART *pUART); +extern long SoftUARTCharGet(tSoftUART *pUART); +extern tBoolean SoftUARTCharPutNonBlocking(tSoftUART *pUART, + unsigned char ucData); +extern void SoftUARTCharPut(tSoftUART *pUART, unsigned char ucData); +extern void SoftUARTBreakCtl(tSoftUART *pUART, tBoolean bBreakState); +extern tBoolean SoftUARTBusy(tSoftUART *pUART); +extern void SoftUARTIntEnable(tSoftUART *pUART, unsigned long ulIntFlags); +extern void SoftUARTIntDisable(tSoftUART *pUART, unsigned long ulIntFlags); +extern unsigned long SoftUARTIntStatus(tSoftUART *pUART, tBoolean bMasked); +extern void SoftUARTIntClear(tSoftUART *pUART, unsigned long ulIntFlags); +extern unsigned long SoftUARTRxErrorGet(tSoftUART *pUART); +extern void SoftUARTRxErrorClear(tSoftUART *pUART); +extern unsigned long SoftUARTRxTick(tSoftUART *pUART, tBoolean bEdgeInt); +extern void SoftUARTTxIntModeSet(tSoftUART *pUART, unsigned long ulMode); +extern unsigned long SoftUARTTxIntModeGet(tSoftUART *pUART); +extern void SoftUARTTxTimerTick(tSoftUART *pUART); +extern void SoftUARTCallbackSet(tSoftUART *pUART, void (*pfnCallback)(void)); +extern void SoftUARTTxGPIOSet(tSoftUART *pUART, unsigned long ulBase, + unsigned char ucPin); +extern void SoftUARTRxGPIOSet(tSoftUART *pUART, unsigned long ulBase, + unsigned char ucPin); +extern void SoftUARTTxBufferSet(tSoftUART *pUART, unsigned char *pucTxBuffer, + unsigned short usLen); +extern void SoftUARTRxBufferSet(tSoftUART *pUART, unsigned short *pusRxBuffer, + unsigned short usLen); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __SOFTUART_H__ diff --git a/utils/uartstdio.c b/utils/uartstdio.c new file mode 100644 index 0000000..ff2013f --- /dev/null +++ b/utils/uartstdio.c @@ -0,0 +1,1732 @@ +//***************************************************************************** +// +// uartstdio.c - Utility driver to provide simple UART console functions. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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 tBoolean g_bDisableEcho; + +//***************************************************************************** +// +// Output ring buffer. Buffer is full if g_ulUARTTxReadIndex is one ahead of +// g_ulUARTTxWriteIndex. Buffer is empty if the two indices are the same. +// +//***************************************************************************** +static unsigned char g_pcUARTTxBuffer[UART_TX_BUFFER_SIZE]; +static volatile unsigned long g_ulUARTTxWriteIndex = 0; +static volatile unsigned long g_ulUARTTxReadIndex = 0; + +//***************************************************************************** +// +// Input ring buffer. Buffer is full if g_ulUARTTxReadIndex is one ahead of +// g_ulUARTTxWriteIndex. Buffer is empty if the two indices are the same. +// +//***************************************************************************** +static unsigned char g_pcUARTRxBuffer[UART_RX_BUFFER_SIZE]; +static volatile unsigned long g_ulUARTRxWriteIndex = 0; +static volatile unsigned long g_ulUARTRxReadIndex = 0; + +//***************************************************************************** +// +// Macros to determine number of free and used bytes in the transmit buffer. +// +//***************************************************************************** +#define TX_BUFFER_USED (GetBufferCount(&g_ulUARTTxReadIndex, \ + &g_ulUARTTxWriteIndex, \ + UART_TX_BUFFER_SIZE)) +#define TX_BUFFER_FREE (UART_TX_BUFFER_SIZE - TX_BUFFER_USED) +#define TX_BUFFER_EMPTY (IsBufferEmpty(&g_ulUARTTxReadIndex, \ + &g_ulUARTTxWriteIndex)) +#define TX_BUFFER_FULL (IsBufferFull(&g_ulUARTTxReadIndex, \ + &g_ulUARTTxWriteIndex, \ + 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_ulUARTRxReadIndex, \ + &g_ulUARTRxWriteIndex, \ + UART_RX_BUFFER_SIZE)) +#define RX_BUFFER_FREE (UART_RX_BUFFER_SIZE - RX_BUFFER_USED) +#define RX_BUFFER_EMPTY (IsBufferEmpty(&g_ulUARTRxReadIndex, \ + &g_ulUARTRxWriteIndex)) +#define RX_BUFFER_FULL (IsBufferFull(&g_ulUARTRxReadIndex, \ + &g_ulUARTRxWriteIndex, \ + 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 unsigned long g_ulBase = 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 unsigned long g_ulUARTBase[3] = +{ + UART0_BASE, UART1_BASE, UART2_BASE +}; + +#ifdef UART_BUFFERED +//***************************************************************************** +// +// The list of possible interrupts for the console UART. +// +//***************************************************************************** +static const unsigned long g_ulUARTInt[3] = +{ + INT_UART0, INT_UART1, INT_UART2 +}; + +//***************************************************************************** +// +// The port number in use. +// +//***************************************************************************** +static unsigned long g_ulPortNum; +#endif + +//***************************************************************************** +// +// The list of UART peripherals. +// +//***************************************************************************** +static const unsigned long g_ulUARTPeriph[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 pulRead points to the read index for the buffer. +//! \param pulWrite points to the write index for the buffer. +//! \param ulSize 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 tBoolean +IsBufferFull(volatile unsigned long *pulRead, + volatile unsigned long *pulWrite, unsigned long ulSize) +{ + unsigned long ulWrite; + unsigned long ulRead; + + ulWrite = *pulWrite; + ulRead = *pulRead; + + return((((ulWrite + 1) % ulSize) == ulRead) ? true : false); +} +#endif + +//***************************************************************************** +// +//! Determines whether the ring buffer whose pointers and size are provided +//! is empty or not. +//! +//! \param pulRead points to the read index for the buffer. +//! \param pulWrite 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 tBoolean +IsBufferEmpty(volatile unsigned long *pulRead, + volatile unsigned long *pulWrite) +{ + unsigned long ulWrite; + unsigned long ulRead; + + ulWrite = *pulWrite; + ulRead = *pulRead; + + return((ulWrite == ulRead) ? true : false); +} +#endif + +//***************************************************************************** +// +//! Determines the number of bytes of data contained in a ring buffer. +//! +//! \param pulRead points to the read index for the buffer. +//! \param pulWrite points to the write index for the buffer. +//! \param ulSize 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 unsigned long +GetBufferCount(volatile unsigned long *pulRead, + volatile unsigned long *pulWrite, unsigned long ulSize) +{ + unsigned long ulWrite; + unsigned long ulRead; + + ulWrite = *pulWrite; + ulRead = *pulRead; + + return((ulWrite >= ulRead) ? (ulWrite - ulRead) : + (ulSize - (ulRead - ulWrite))); +} +#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(unsigned long ulBase) +{ + // + // 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_ulUARTInt[g_ulPortNum]); + + // + // Yes - take some characters out of the transmit buffer and feed + // them to the UART transmit FIFO. + // + while(MAP_UARTSpaceAvail(ulBase) && !TX_BUFFER_EMPTY) + { + MAP_UARTCharPutNonBlocking(ulBase, + g_pcUARTTxBuffer[g_ulUARTTxReadIndex]); + ADVANCE_TX_BUFFER_INDEX(g_ulUARTTxReadIndex); + } + + // + // Reenable the UART interrupt. + // + MAP_IntEnable(g_ulUARTInt[g_ulPortNum]); + } +} +#endif + +//***************************************************************************** +// +//! Configures the UART console. +//! +//! \param ulPortNum is the number of UART port to use for the serial console +//! (0-2) +//! \param ulBaud is the bit rate that the UART is to be configured to use. +//! \param ulSrcClock 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 ulBaud 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(unsigned long ulPortNum, unsigned long ulBaud, + unsigned long ulSrcClock) +{ + // + // Check the arguments. + // + ASSERT((ulPortNum == 0) || (ulPortNum == 1) || + (ulPortNum == 2)); + +#ifdef UART_BUFFERED + // + // In buffered mode, we only allow a single instance to be opened. + // + ASSERT(g_ulBase == 0); +#endif + + // + // Check to make sure the UART peripheral is present. + // + if(!MAP_SysCtlPeripheralPresent(g_ulUARTPeriph[ulPortNum])) + { + return; + } + + // + // Select the base address of the UART. + // + g_ulBase = g_ulUARTBase[ulPortNum]; + + // + // Enable the UART peripheral for use. + // + MAP_SysCtlPeripheralEnable(g_ulUARTPeriph[ulPortNum]); + + // + // Configure the UART for 115200, n, 8, 1 + // + MAP_UARTConfigSetExpClk(g_ulBase, ulSrcClock, ulBaud, + (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_ulBase, UART_FIFO_TX1_8, UART_FIFO_RX1_8); + + // + // Flush both the buffers. + // + UARTFlushRx(); + UARTFlushTx(true); + + // + // Remember which interrupt we are dealing with. + // + g_ulPortNum = ulPortNum; + + // + // 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_ulBase, 0xFFFFFFFF); + MAP_UARTIntEnable(g_ulBase, UART_INT_RX | UART_INT_RT); + MAP_IntEnable(g_ulUARTInt[ulPortNum]); +#endif + + // + // Enable the UART operation. + // + MAP_UARTEnable(g_ulBase); +} + +//***************************************************************************** +// +//! Initializes the UART console. +//! +//! \param ulPortNum is the number of UART port to use for the serial console +//! (0-2) +//! +//! This function will initialize the specified serial port to be used as a +//! serial console. The serial parameters will be set to 115200, 8-N-1. +//! An application wishing to use a different baud rate may call +//! UARTStdioInitExpClk() instead of this function. +//! +//! This function or UARTStdioInitExpClk() must be called prior to using any +//! of the other UART console functions: UARTprintf() or UARTgets(). In order +//! for this function to work correctly, SysCtlClockSet() must be called prior +//! to calling this function. +//! +//! It is assumed that the caller has previously configured the relevant UART +//! pins for operation as a UART rather than as GPIOs. +//! +//! \return None. +// +//***************************************************************************** +void +UARTStdioInit(unsigned long ulPortNum) +{ + // + // Pass this call on to the version of the function allowing the baud rate + // to be specified. + // + UARTStdioConfig(ulPortNum, 115200, MAP_SysCtlClockGet()); +} + +//***************************************************************************** +// +//! Initializes the UART console and allows the baud rate to be selected. +//! +//! \param ulPortNum is the number of UART port to use for the serial console +//! (0-2) +//! \param ulBaud is the bit rate that the UART is to be configured to use. +//! +//! This function will initialize the specified serial port to be used as a +//! serial console. The serial parameters will be set to 8-N-1 and the bit +//! rate set according to the value of the \e ulBaud parameter. +//! +//! This function or UARTStdioInit() must be called prior to using any of the +//! other UART console functions: UARTprintf() or UARTgets(). In order for +//! this function to work correctly, SysCtlClockSet() must be called prior to +//! calling this function. An application wishing to use 115,200 baud may call +//! UARTStdioInit() instead of this function but should not call both +//! functions. +//! +//! It is assumed that the caller has previously configured the relevant UART +//! pins for operation as a UART rather than as GPIOs. +//! +//! \return None. +// +//***************************************************************************** +void +UARTStdioInitExpClk(unsigned long ulPortNum, unsigned long ulBaud) +{ + UARTStdioConfig(ulPortNum, ulBaud, MAP_SysCtlClockGet()); +} + +//***************************************************************************** +// +//! Writes a string of characters to the UART output. +//! +//! \param pcBuf points to a buffer containing the string to transmit. +//! \param ulLen 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 ulLen 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 ulLen 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, unsigned long ulLen) +{ +#ifdef UART_BUFFERED + unsigned int uIdx; + + // + // Check for valid arguments. + // + ASSERT(pcBuf != 0); + ASSERT(g_ulBase != 0); + + // + // Send the characters + // + for(uIdx = 0; uIdx < ulLen; 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_ulUARTTxWriteIndex] = '\r'; + ADVANCE_TX_BUFFER_INDEX(g_ulUARTTxWriteIndex); + } + else + { + // + // Buffer is full - discard remaining characters and return. + // + break; + } + } + + // + // Send the character to the UART output. + // + if(!TX_BUFFER_FULL) + { + g_pcUARTTxBuffer[g_ulUARTTxWriteIndex] = pcBuf[uIdx]; + ADVANCE_TX_BUFFER_INDEX(g_ulUARTTxWriteIndex); + } + 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_ulBase); + MAP_UARTIntEnable(g_ulBase, 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_ulBase != 0); + ASSERT(pcBuf != 0); + + // + // Send the characters + // + for(uIdx = 0; uIdx < ulLen; 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_ulBase, '\r'); + } + + // + // Send the character to the UART output. + // + MAP_UARTCharPut(g_ulBase, 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 ulLen 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, unsigned long ulLen) +{ +#ifdef UART_BUFFERED + unsigned long ulCount = 0; + char cChar; + + // + // Check the arguments. + // + ASSERT(pcBuf != 0); + ASSERT(ulLen != 0); + ASSERT(g_ulBase != 0); + + // + // Adjust the length back by 1 to leave space for the trailing + // null terminator. + // + ulLen--; + + // + // 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_ulUARTRxReadIndex]; + ADVANCE_RX_BUFFER_INDEX(g_ulUARTRxReadIndex); + + // + // 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(ulCount < ulLen) + { + // + // Store the character in the caller supplied buffer. + // + pcBuf[ulCount] = cChar; + + // + // Increment the count of characters received. + // + ulCount++; + } + } + } + + // + // Add a null termination to the string. + // + pcBuf[ulCount] = 0; + + // + // Return the count of chars in the buffer, not counting the trailing 0. + // + return(ulCount); +#else + unsigned long ulCount = 0; + char cChar; + static char bLastWasCR = 0; + + // + // Check the arguments. + // + ASSERT(pcBuf != 0); + ASSERT(ulLen != 0); + ASSERT(g_ulBase != 0); + + // + // Adjust the length back by 1 to leave space for the trailing + // null terminator. + // + ulLen--; + + // + // Process characters until a newline is received. + // + while(1) + { + // + // Read the next character from the console. + // + cChar = MAP_UARTCharGet(g_ulBase); + + // + // See if the backspace key was pressed. + // + if(cChar == '\b') + { + // + // If there are any characters already in the buffer, then delete + // the last. + // + if(ulCount) + { + // + // Rub out the previous character. + // + UARTwrite("\b \b", 3); + + // + // Decrement the number of characters in the buffer. + // + ulCount--; + } + + // + // 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(ulCount < ulLen) + { + // + // Store the character in the caller supplied buffer. + // + pcBuf[ulCount] = cChar; + + // + // Increment the count of characters received. + // + ulCount++; + + // + // Reflect the character back to the user. + // + MAP_UARTCharPut(g_ulBase, cChar); + } + } + + // + // Add a null termination to the string. + // + pcBuf[ulCount] = 0; + + // + // Send a CRLF pair to the terminal to end the line. + // + UARTwrite("\r\n", 2); + + // + // Return the count of chars in the buffer, not counting the trailing 0. + // + return(ulCount); +#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_ulUARTRxReadIndex]; + ADVANCE_RX_BUFFER_INDEX(g_ulUARTRxReadIndex); + + // + // 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_ulBase)); +#endif +} + +//***************************************************************************** +// +//! 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, ...) +{ + unsigned long ulIdx, ulValue, ulPos, ulCount, ulBase, ulNeg; + char *pcStr, pcBuf[16], cFill; + va_list vaArgP; + + // + // Check the arguments. + // + ASSERT(pcString != 0); + + // + // Start the varargs processing. + // + va_start(vaArgP, pcString); + + // + // Loop while there are more characters in the string. + // + while(*pcString) + { + // + // Find the first non-% character, or the end of the string. + // + for(ulIdx = 0; (pcString[ulIdx] != '%') && (pcString[ulIdx] != '\0'); + ulIdx++) + { + } + + // + // Write this portion of the string. + // + UARTwrite(pcString, ulIdx); + + // + // Skip the portion of the string that was written. + // + pcString += ulIdx; + + // + // See if the next character is a %. + // + if(*pcString == '%') + { + // + // Skip the %. + // + pcString++; + + // + // Set the digit count to zero, and the fill character to space + // (i.e. 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(*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') && (ulCount == 0)) + { + cFill = '0'; + } + + // + // Update the digit count. + // + ulCount *= 10; + ulCount += pcString[-1] - '0'; + + // + // Get the next character. + // + goto again; + } + + // + // Handle the %c command. + // + case 'c': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(vaArgP, unsigned long); + + // + // Print out the character. + // + UARTwrite((char *)&ulValue, 1); + + // + // 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(vaArgP, unsigned long); + + // + // Reset the buffer position. + // + ulPos = 0; + + // + // 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 minus + // 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(vaArgP, char *); + + // + // Determine the length of the string. + // + for(ulIdx = 0; pcStr[ulIdx] != '\0'; ulIdx++) + { + } + + // + // Write the string. + // + UARTwrite(pcStr, ulIdx); + + // + // Write any required padding spaces + // + if(ulCount > ulIdx) + { + ulCount -= ulIdx; + while(ulCount--) + { + UARTwrite(" ", 1); + } + } + // + // This command has been handled. + // + break; + } + + // + // Handle the %u command. + // + case 'u': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(vaArgP, unsigned long); + + // + // Reset the buffer position. + // + ulPos = 0; + + // + // 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; i.e. %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(vaArgP, unsigned long); + + // + // Reset the buffer position. + // + ulPos = 0; + + // + // 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 && (cFill == '0')) + { + // + // Place the minus sign in the output buffer. + // + pcBuf[ulPos++] = '-'; + + // + // The minus sign has been placed, so turn off the + // negative flag. + // + ulNeg = 0; + } + + // + // Provide additional padding at the beginning of the + // string conversion if needed. + // + if((ulCount > 1) && (ulCount < 16)) + { + for(ulCount--; ulCount; ulCount--) + { + pcBuf[ulPos++] = cFill; + } + } + + // + // If the value is negative, then place the minus sign + // before the number. + // + if(ulNeg) + { + // + // Place the minus sign in the output buffer. + // + pcBuf[ulPos++] = '-'; + } + + // + // Convert the value into a string. + // + for(; ulIdx; ulIdx /= ulBase) + { + pcBuf[ulPos++] = g_pcHex[(ulValue / ulIdx) % ulBase]; + } + + // + // Write the string. + // + UARTwrite(pcBuf, ulPos); + + // + // 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; + } + } + } + } + + // + // End the varargs processing. + // + 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; + unsigned long ulReadIndex; + + // + // How many characters are there in the receive buffer? + // + iAvail = (int)RX_BUFFER_USED; + ulReadIndex = g_ulUARTRxReadIndex; + + // + // Check all the unread characters looking for the one passed. + // + for(iCount = 0; iCount < iAvail; iCount++) + { + if(g_pcUARTRxBuffer[ulReadIndex] == 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(ulReadIndex); + } + } + + // + // 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) +{ + unsigned long ulInt; + + // + // Temporarily turn off interrupts. + // + ulInt = MAP_IntMasterDisable(); + + // + // Flush the receive buffer. + // + g_ulUARTRxReadIndex = 0; + g_ulUARTRxWriteIndex = 0; + + // + // If interrupts were enabled when we turned them off, turn them + // back on again. + // + if(!ulInt) + { + 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(tBoolean bDiscard) +{ + unsigned long ulInt; + + // + // Should the remaining data be discarded or transmitted? + // + if(bDiscard) + { + // + // The remaining data should be discarded, so temporarily turn off + // interrupts. + // + ulInt = MAP_IntMasterDisable(); + + // + // Flush the transmit buffer. + // + g_ulUARTTxReadIndex = 0; + g_ulUARTTxWriteIndex = 0; + + // + // If interrupts were enabled when we turned them off, turn them + // back on again. + // + if(!ulInt) + { + 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(tBoolean 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) +{ + unsigned long ulInts; + char cChar; + long lChar; + static tBoolean bLastWasCR = false; + + // + // Get and clear the current interrupt source(s) + // + ulInts = MAP_UARTIntStatus(g_ulBase, true); + MAP_UARTIntClear(g_ulBase, ulInts); + + // + // Are we being interrupted because the TX FIFO has space available? + // + if(ulInts & UART_INT_TX) + { + // + // Move as many bytes as we can into the transmit FIFO. + // + UARTPrimeTransmit(g_ulBase); + + // + // If the output buffer is empty, turn off the transmit interrupt. + // + if(TX_BUFFER_EMPTY) + { + MAP_UARTIntDisable(g_ulBase, UART_INT_TX); + } + } + + // + // Are we being interrupted due to a received character? + // + if(ulInts & (UART_INT_RX | UART_INT_RT)) + { + // + // Get all the available characters from the UART. + // + while(MAP_UARTCharsAvail(g_ulBase)) + { + // + // Read a character + // + lChar = MAP_UARTCharGetNonBlocking(g_ulBase); + cChar = (unsigned char)(lChar & 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_ulUARTRxWriteIndex == 0) + { + g_ulUARTRxWriteIndex = UART_RX_BUFFER_SIZE - 1; + } + else + { + g_ulUARTRxWriteIndex--; + } + } + + // + // 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_ulUARTRxWriteIndex] = + (unsigned char)(lChar & 0xFF); + ADVANCE_RX_BUFFER_INDEX(g_ulUARTRxWriteIndex); + + // + // If echo is enabled, write the character to the transmit + // buffer so that the user gets some immediate feedback. + // + if(!g_bDisableEcho) + { + UARTwrite(&cChar, 1); + } + } + } + + // + // If we wrote anything to the transmit buffer, make sure it actually + // gets transmitted. + // + UARTPrimeTransmit(g_ulBase); + MAP_UARTIntEnable(g_ulBase, UART_INT_TX); + } +} +#endif + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/uartstdio.h b/utils/uartstdio.h new file mode 100644 index 0000000..d54d573 --- /dev/null +++ b/utils/uartstdio.h @@ -0,0 +1,85 @@ +//***************************************************************************** +// +// uartstdio.h - Prototypes for the UART console functions. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#ifndef __UARTSTDIO_H__ +#define __UARTSTDIO_H__ + +//***************************************************************************** +// +// 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(unsigned long ulPort, unsigned long ulBaud, + unsigned long ulSrcClock); +extern void UARTStdioInit(unsigned long ulPort); +extern void UARTStdioInitExpClk(unsigned long ulPort, unsigned long ulBaud); +extern int UARTgets(char *pcBuf, unsigned long ulLen); +extern unsigned char UARTgetc(void); +extern void UARTprintf(const char *pcString, ...); +extern int UARTwrite(const char *pcBuf, unsigned long ulLen); +#ifdef UART_BUFFERED +extern int UARTPeek(unsigned char ucChar); +extern void UARTFlushTx(tBoolean bDiscard); +extern void UARTFlushRx(void); +extern int UARTRxBytesAvail(void); +extern int UARTTxBytesFree(void); +extern void UARTEchoSet(tBoolean 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..a330a53 --- /dev/null +++ b/utils/ustdlib.c @@ -0,0 +1,1610 @@ +//***************************************************************************** +// +// ustdlib.c - Simple standard library functions. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#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 pcDst is a pointer to the destination buffer into which characters +//! are to be copied. +//! \param pcSrc is a pointer to the string from which characters are to be +//! copied. +//! \param iNum is the number of characters to copy to the destination buffer. +//! +//! This function copies at most \e iNum characters from the string pointed to +//! by \e pcSrc into the buffer pointed to by \e pcDst. If the end of \e +//! pcSrc is found before \e iNum characters have been copied, remaining +//! characters in \e pcDst will be padded with zeroes until \e iNum 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 pcSrc. +//! +//! \return Returns \e pcDst. +// +//***************************************************************************** +char * +ustrncpy (char *pcDst, const char *pcSrc, int iNum) +{ + int iCount; + + ASSERT(pcSrc); + ASSERT(pcDst); + + // + // Start at the beginning of the source string. + // + iCount = 0; + + // + // Copy the source string until we run out of source characters or + // destination space. + // + while(iNum && pcSrc[iCount]) + { + pcDst[iCount] = pcSrc[iCount]; + iCount++; + iNum--; + } + + // + // Pad the destination if we are not yet done. + // + while(iNum) + { + pcDst[iCount++] = (char)0; + iNum--; + } + + // + // Pass the destination pointer back to the caller. + // + return(pcDst); +} + +//***************************************************************************** +// +//! A simple vsnprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and +//! \%X. +//! +//! \param pcBuf points to the buffer where the converted string is stored. +//! \param ulSize is the size of the buffer. +//! \param pcString is the format string. +//! \param vaArgP 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 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. +//! +//! The \e ulSize parameter limits the number of characters that will be stored +//! in the buffer pointed to by \e pcBuf 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 *pcBuf, unsigned long ulSize, const char *pcString, + va_list vaArgP) +{ + unsigned long ulIdx, ulValue, ulCount, ulBase, ulNeg; + char *pcStr, cFill; + int iConvertCount = 0; + + // + // Check the arguments. + // + ASSERT(pcString != 0); + ASSERT(pcBuf != 0); + ASSERT(ulSize != 0); + + // + // Adjust buffer size limit to allow one space for null termination. + // + if(ulSize) + { + ulSize--; + } + + // + // Initialize the count of characters converted. + // + iConvertCount = 0; + + // + // Loop while there are more characters in the format string. + // + while(*pcString) + { + // + // Find the first non-% character, or the end of the string. + // + for(ulIdx = 0; (pcString[ulIdx] != '%') && (pcString[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 > ulSize) + { + ustrncpy(pcBuf, pcString, ulSize); + pcBuf += ulSize; + ulSize = 0; + } + else + { + ustrncpy(pcBuf, pcString, ulIdx); + pcBuf += ulIdx; + ulSize -= 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. + // + pcString += ulIdx; + + // + // See if the next character is a %. + // + if(*pcString == '%') + { + // + // Skip the %. + // + pcString++; + + // + // 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(*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') && (ulCount == 0)) + { + cFill = '0'; + } + + // + // Update the digit count. + // + ulCount *= 10; + ulCount += pcString[-1] - '0'; + + // + // Get the next character. + // + goto again; + } + + // + // Handle the %c command. + // + case 'c': + { + // + // Get the value from the varargs. + // + ulValue = va_arg(vaArgP, unsigned long); + + // + // Copy the character to the output buffer, if there is + // room. Update the buffer size remaining. + // + if(ulSize != 0) + { + *pcBuf++ = (char)ulValue; + ulSize--; + } + + // + // 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(vaArgP, 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(vaArgP, 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 > ulSize) + { + ustrncpy(pcBuf, pcStr, ulSize); + pcBuf += ulSize; + ulSize = 0; + } + else + { + ustrncpy(pcBuf, pcStr, ulIdx); + pcBuf += ulIdx; + ulSize -= ulIdx; + + // + // Write any required padding spaces assuming there is + // still space in the buffer. + // + if(ulCount > ulIdx) + { + ulCount -= ulIdx; + if(ulCount > ulSize) + { + ulCount = ulSize; + } + ulSize =- ulCount; + + while(ulCount--) + { + *pcBuf++ = ' '; + } + } + } + + // + // 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(vaArgP, 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(vaArgP, 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 && (ulSize != 0) && (cFill == '0')) + { + // + // Place the minus sign in the output buffer. + // + *pcBuf++ = '-'; + ulSize--; + + // + // 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(ulSize != 0) + { + *pcBuf++ = cFill; + ulSize--; + } + + // + // Update the conversion count. + // + iConvertCount++; + } + } + + // + // If the value is negative, then place the minus sign + // before the number. + // + if(ulNeg && (ulSize != 0)) + { + // + // Place the minus sign in the output buffer. + // + *pcBuf++ = '-'; + ulSize--; + + // + // 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(ulSize != 0) + { + *pcBuf++ = g_pcHex[(ulValue / ulIdx) % ulBase]; + ulSize--; + } + + // + // Update the conversion count. + // + iConvertCount++; + } + + // + // This command has been handled. + // + break; + } + + // + // Handle the %% command. + // + case '%': + { + // + // Simply write a single %. + // + if(ulSize != 0) + { + *pcBuf++ = pcString[-1]; + ulSize--; + } + + // + // Update the conversion count. + // + iConvertCount++; + + // + // This command has been handled. + // + break; + } + + // + // Handle all other commands. + // + default: + { + // + // Indicate an error. + // + if(ulSize >= 5) + { + ustrncpy(pcBuf, "ERROR", 5); + pcBuf += 5; + ulSize -= 5; + } + else + { + ustrncpy(pcBuf, "ERROR", ulSize); + pcBuf += ulSize; + ulSize = 0; + } + + // + // Update the conversion count. + // + iConvertCount += 5; + + // + // This command has been handled. + // + break; + } + } + } + } + + // + // Null terminate the string in the buffer. + // + *pcBuf = 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 pcBuf is the buffer where the converted string is stored. +//! \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 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 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. +//! +//! The caller must ensure that the buffer \e pcBuf 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 *pcBuf, const char *pcString, ...) +{ + va_list vaArgP; + int iRet; + + // + // Start the varargs processing. + // + va_start(vaArgP, pcString); + + // + // Call vsnprintf to perform the conversion. Use a large number for the + // buffer size. + // + iRet = uvsnprintf(pcBuf, 0xffff, pcString, vaArgP); + + // + // End the varargs processing. + // + va_end(vaArgP); + + // + // Return the conversion count. + // + return(iRet); +} + +//***************************************************************************** +// +//! A simple snprintf function supporting \%c, \%d, \%p, \%s, \%u, \%x, and +//! \%X. +//! +//! \param pcBuf is the buffer where the converted string is stored. +//! \param ulSize is the size of the buffer. +//! \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 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 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. +//! +//! The function will copy at most \e ulSize - 1 characters into the buffer +//! \e pcBuf. 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 *pcBuf, unsigned long ulSize, const char *pcString, ...) +{ + int iRet; + va_list vaArgP; + + // + // Start the varargs processing. + // + va_start(vaArgP, pcString); + + // + // Call vsnprintf to perform the conversion. + // + iRet = uvsnprintf(pcBuf, ulSize, pcString, vaArgP); + + // + // End the varargs processing. + // + va_end(vaArgP); + + // + // Return the conversion count. + // + return(iRet); +} + +//***************************************************************************** +// +// 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 short g_psDaysToMonth[12] = +{ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +//***************************************************************************** +// +//! Converts from seconds to calendar date and time. +//! +//! \param ulTime is the number of seconds. +//! \param psTime 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(unsigned long ulTime, tTime *psTime) +{ + unsigned long ulTemp, ulMonths; + + // + // Extract the number of seconds, converting time to the number of minutes. + // + ulTemp = ulTime / 60; + psTime->ucSec = ulTime - (ulTemp * 60); + ulTime = ulTemp; + + // + // Extract the number of minutes, converting time to the number of hours. + // + ulTemp = ulTime / 60; + psTime->ucMin = ulTime - (ulTemp * 60); + ulTime = ulTemp; + + // + // Extract the number of hours, converting time to the number of days. + // + ulTemp = ulTime / 24; + psTime->ucHour = ulTime - (ulTemp * 24); + ulTime = ulTemp; + + // + // Compute the day of the week. + // + psTime->ucWday = (ulTime + 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. + // + ulTime += 366 + 365; + ulTemp = ulTime / ((4 * 365) + 1); + if((ulTime - (ulTemp * ((4 * 365) + 1))) > (31 + 28)) + { + ulTemp++; + ulMonths = 12; + } + else + { + ulMonths = 2; + } + + // + // Extract the year. + // + psTime->usYear = ((ulTime - ulTemp) / 365) + 1968; + ulTime -= ((psTime->usYear - 1968) * 365) + ulTemp; + + // + // Extract the month. + // + for(ulTemp = 0; ulTemp < ulMonths; ulTemp++) + { + if(g_psDaysToMonth[ulTemp] > ulTime) + { + break; + } + } + psTime->ucMon = ulTemp - 1; + + // + // Extract the day of the month. + // + psTime->ucMday = ulTime - g_psDaysToMonth[ulTemp - 1] + 1; +} + +//***************************************************************************** +// +//! Compares two time structures and determines if one is greater than, +//! less than, or equal to the other. +//! +//! \param pTime1 is the first time structure to compare. +//! \param pTime2 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 pTime1 is greater than the time represented by \e pTime2 then a positive +//! number is returned. Likewise if \e pTime1 is less than \e pTime2 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 pTime1 is greater +//! than \e pTime2, and -1 if \e pTime1 is less than \e pTime2. +// +//***************************************************************************** +static int +ucmptime(tTime *pTime1, tTime *pTime2) +{ + // + // Compare each field in descending signficance to determine if + // greater than, less than, or equal. + // + if(pTime1->usYear > pTime2->usYear) + { + return(1); + } + else if(pTime1->usYear < pTime2->usYear) + { + return(-1); + } + else if(pTime1->ucMon > pTime2->ucMon) + { + return(1); + } + else if(pTime1->ucMon < pTime2->ucMon) + { + return(-1); + } + else if(pTime1->ucMday > pTime2->ucMday) + { + return(1); + } + else if(pTime1->ucMday < pTime2->ucMday) + { + return(-1); + } + else if(pTime1->ucHour > pTime2->ucHour) + { + return(1); + } + else if(pTime1->ucHour < pTime2->ucHour) + { + return(-1); + } + else if(pTime1->ucMin > pTime2->ucMin) + { + return(1); + } + else if(pTime1->ucMin < pTime2->ucMin) + { + return(-1); + } + else if(pTime1->ucSec > pTime2->ucSec) + { + return(1); + } + else if(pTime1->ucSec < pTime2->ucSec) + { + 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 psTime 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 psTime +//! 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 (unsigned long)(-1). +// +//***************************************************************************** +unsigned long +umktime(tTime *psTime) +{ + tTime sTimeGuess; + unsigned long ulTimeGuess = 0x80000000; + unsigned long ulAdjust = 0x40000000; + int iSign; + + // + // Seed the binary search with the first guess. + // + ulocaltime(ulTimeGuess, &sTimeGuess); + iSign = ucmptime(psTime, &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(psTime, &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 pcStr is a pointer to the string containing the integer. +//! \param ppcStrRet is a pointer that will be set to the first character past +//! the integer in the string. +//! \param iBase 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 *pcStr, const char **ppcStrRet, int iBase) +{ + unsigned long ulRet, ulDigit, ulNeg, ulValid; + const char *pcPtr; + + // + // Check the arguments. + // + ASSERT(pcStr); + ASSERT((iBase == 0) || ((iBase > 1) && (iBase <= 16))); + + // + // Initially, the result is zero. + // + ulRet = 0; + ulNeg = 0; + ulValid = 0; + + // + // Skip past any leading white space. + // + pcPtr = pcStr; + 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(((iBase == 0) || (iBase == 16)) && (*pcPtr == '0') && + ((pcPtr[1] == 'x') || (pcPtr[1] == 'X'))) + { + // + // Skip the leading "0x". + // + pcPtr += 2; + + // + // Set the radix to 16. + // + iBase = 16; + } + + // + // See if the radix was not specified. + // + if(iBase == 0) + { + // + // See if the value starts with "0". + // + if(*pcPtr == '0') + { + // + // Values that start with "0" are assumed to be radix 8. + // + iBase = 8; + } + else + { + // + // Otherwise, the values are assumed to be radix 10. + // + iBase = 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 >= iBase) + { + // + // 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 *= iBase; + 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(ppcStrRet) + { + *ppcStrRet = ulValid ? pcPtr : pcStr; + } + + // + // Return the converted value. + // + return(ulNeg ? (0 - ulRet) : ulRet); +} + +//***************************************************************************** +// +//! Retruns the length of a null-terminated string. +//! +//! \param pcStr 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 pcStr. +// +//***************************************************************************** +int +ustrlen(const char * pcStr) +{ + int iLen; + + ASSERT(pcStr); + + // + // Initialize the length. + // + iLen = 0; + + // + // Step throug the string looking for a zero character (marking its end). + // + while(pcStr[iLen]) + { + // + // Zero not found so move on to the next character. + // + iLen++; + } + + return(iLen); +} + +//***************************************************************************** +// +//! Finds a substring within a string. +//! +//! \param pcHaystack is a pointer to the string that will be searched. +//! \param pcNeedle is a pointer to the substring that is to be found within +//! \e pcHaystack. +//! +//! 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 pcNeedle within +//! \e pcHaystack or NULL if no match is found. +// +//***************************************************************************** +char * +ustrstr(const char *pcHaystack, const char *pcNeedle) +{ + unsigned long ulLength; + + // + // Get the length of the string to be found. + // + ulLength = ustrlen(pcNeedle); + + // + // Loop while we have not reached the end of the string. + // + while(*pcHaystack) + { + // + // Check to see if the substring appears at this position. + // + if(ustrncmp(pcNeedle, pcHaystack, ulLength) == 0) + { + // + // It does so return the pointer. + // + return((char *)pcHaystack); + } + + // + // Move to the next position in the string being searched. + // + pcHaystack++; + } + + // + // 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 pcStr1 points to the first string to be compared. +//! \param pcStr2 points to the second string to be compared. +//! \param iCount is the maximum number of characters to compare. +//! +//! This function is very similar to the C library strnicmp() function. +//! It compares at most \e iCount characters of two strings without regard to +//! case. The comparison ends if a terminating NULL character is found in +//! either string before \e iCount 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 pcStr1 is less +//! than \e pcStr2 and 1 if \e pcStr1 is greater than \e pcStr2. +// +//***************************************************************************** +int +ustrnicmp(const char *pcStr1, const char *pcStr2, int iCount) +{ + char cL1, cL2; + + while(iCount) + { + // + // If we reached a NULL in both strings, they must be equal so + // we end the comparison and return 0 + // + if(!*pcStr1 && !*pcStr2) + { + return(0); + } + + // + // Lower case the characters at the current position before we compare. + // + cL1 = (((*pcStr1 >= 'A') && (*pcStr1 <= 'Z')) ? + (*pcStr1 + ('a' - 'A')) : *pcStr1); + cL2 = (((*pcStr2 >= 'A') && (*pcStr2 <= 'Z')) ? + (*pcStr2 + ('a' - 'A')) : *pcStr2); + // + // Compare the two characters and, if different, return the relevant + // return code. + // + if(cL2 < cL1) + { + return(1); + } + if(cL1 < cL2) + { + return(-1); + } + + // + // Move on to the next character. + // + pcStr1++; + pcStr2++; + iCount--; + } + + // + // If we fall out, the strings must be equal for at least the first iCount + // characters so return 0 to indicate this. + // + return(0); +} + +//***************************************************************************** +// +//! Compares two strings without regard to case. +//! +//! \param pcStr1 points to the first string to be compared. +//! \param pcStr2 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 shorter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e pcStr1 is less +//! than \e pcStr2 and 1 if \e pcStr1 is greater than \e pcStr2. +// +//***************************************************************************** +int +ustrcasecmp(const char *pcStr1, const char *pcStr2) +{ + // + // Just let ustrnicmp() handle this. + // + return(ustrnicmp(pcStr1, pcStr2, -1)); +} + +//***************************************************************************** +// +//! Compares two strings. +//! +//! \param pcStr1 points to the first string to be compared. +//! \param pcStr2 points to the second string to be compared. +//! \param iCount is the maximum number of characters to compare. +//! +//! This function is very similar to the C library strncmp() function. +//! It compares at most \e iCount characters of two strings taking case into +//! account. The comparison ends if a terminating NULL character is found in +//! either string before \e iCount 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 pcStr1 is less +//! than \e pcStr2 and 1 if \e pcStr1 is greater than \e pcStr2. +// +//***************************************************************************** +int +ustrncmp(const char *pcStr1, const char *pcStr2, int iCount) +{ + while(iCount) + { + // + // If we reached a NULL in both strings, they must be equal so + // we end the comparison and return 0 + // + if(!*pcStr1 && !*pcStr2) + { + return(0); + } + + // + // Compare the two characters and, if different, return the relevant + // return code. + // + if(*pcStr2 < *pcStr1) + { + return(1); + } + if(*pcStr1 < *pcStr2) + { + return(-1); + } + + // + // Move on to the next character. + // + pcStr1++; + pcStr2++; + iCount--; + } + + // + // If we fall out, the strings must be equal for at least the first iCount + // characters so return 0 to indicate this. + // + return(0); + +} + +//***************************************************************************** +// +//! Compares two strings. +//! +//! \param pcStr1 points to the first string to be compared. +//! \param pcStr2 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 shorter string is deemed the lesser. +//! +//! \return Returns 0 if the two strings are equal, -1 if \e pcStr1 is less +//! than \e pcStr2 and 1 if \e pcStr1 is greater than \e pcStr2. +// +//***************************************************************************** +int +ustrcmp(const char *pcStr1, const char *pcStr2) +{ + // + // Pass this on to ustrncmp. + // + return(ustrncmp(pcStr1, pcStr2, -1)); +} + +//***************************************************************************** +// +// Random Number Generator Seed Value +// +//***************************************************************************** +static unsigned long g_ulRandomSeed = 1; + +//***************************************************************************** +// +//! Set the random number generator seed. +//! +//! \param ulSeed 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 long ulSeed) +{ + g_ulRandomSeed = ulSeed; +} + +//***************************************************************************** +// +//! 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_ulRandomSeed = (g_ulRandomSeed * 1664525) + 1013904223; + + // + // Return the new random number. + // + return((int)g_ulRandomSeed); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/utils/ustdlib.h b/utils/ustdlib.h new file mode 100644 index 0000000..3d359a6 --- /dev/null +++ b/utils/ustdlib.h @@ -0,0 +1,130 @@ +//***************************************************************************** +// +// ustdlib.h - Prototypes for simple standard library functions. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +#ifndef __USTDLIB_H__ +#define __USTDLIB_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 + +//***************************************************************************** +// +//! \addtogroup ustdlib_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! A structure that contains the broken down date and time. +// +//***************************************************************************** +typedef struct +{ + // + //! The number of years since 0 AD. + // + unsigned short usYear; + + // + //! The month, where January is 0 and December is 11. + // + unsigned char ucMon; + + // + //! The day of the month. + // + unsigned char ucMday; + + // + //! The day of the week, where Sunday is 0 and Saturday is 6. + // + unsigned char ucWday; + + // + //! The number of hours. + // + unsigned char ucHour; + + // + //! The number of minutes. + // + unsigned char ucMin; + + // + //! The number of seconds. + // + unsigned char ucSec; +} +tTime; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes for the APIs. +// +//***************************************************************************** +extern int uvsnprintf(char *pcBuf, unsigned long ulSize, const char *pcString, + va_list vaArgP); +extern int usprintf(char *pcBuf, const char *pcString, ...); +extern int usnprintf(char *pcBuf, unsigned long ulSize, const char *pcString, + ...); +extern void ulocaltime(unsigned long ulTime, tTime *psTime); +extern unsigned long umktime(tTime *psTime); +extern int ustrlen (const char *pcStr); +extern char *ustrncpy (char *pcDst, const char *pcSrc, int iNum); +extern unsigned long ustrtoul(const char *pcStr, const char **ppcStrRet, + int iBase); +extern char *ustrstr(const char *pcHaystack, const char *pcNeedle); +extern int ustrnicmp(const char *pcStr1, const char *pcStr2, int iCount); +extern int ustrncmp(const char *pcStr1, const char *pcStr2, int iCount); +extern int ustrcmp(const char *pcStr1, const char *pcStr2); +extern int ustrcasecmp(const char *pcStr1, const char *pcStr2); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USTDLIB_H__ diff --git a/utils/utils.sgxx b/utils/utils.sgxx new file mode 100644 index 0000000..a4f24d2 Binary files /dev/null and b/utils/utils.sgxx differ -- cgit v1.3.1