You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					309 lines
				
				12 KiB
			
		
		
			
		
	
	
					309 lines
				
				12 KiB
			|   
											8 years ago
										 | /*
 | ||
|  |              LUFA Library | ||
|  |      Copyright (C) Dean Camera, 2017. | ||
|  | 
 | ||
|  |   dean [at] fourwalledcubicle [dot] com | ||
|  |            www.lufa-lib.org | ||
|  | */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |   Copyright 2017  Dean Camera (dean [at] fourwalledcubicle [dot] com) | ||
|  | 
 | ||
|  |   Permission to use, copy, modify, distribute, and sell this | ||
|  |   software and its documentation for any purpose is hereby granted | ||
|  |   without fee, provided that the above copyright notice appear in | ||
|  |   all copies and that both that the copyright notice and this | ||
|  |   permission notice and warranty disclaimer appear in supporting | ||
|  |   documentation, and that the name of the author not be used in | ||
|  |   advertising or publicity pertaining to distribution of the | ||
|  |   software without specific, written prior permission. | ||
|  | 
 | ||
|  |   The author disclaims all warranties with regard to this | ||
|  |   software, including all implied warranties of merchantability | ||
|  |   and fitness.  In no event shall the author be liable for any | ||
|  |   special, indirect or consequential damages or any damages | ||
|  |   whatsoever resulting from loss of use, data or profits, whether | ||
|  |   in an action of contract, negligence or other tortious action, | ||
|  |   arising out of or in connection with the use or performance of | ||
|  |   this software. | ||
|  | */ | ||
|  | 
 | ||
|  | /** \file
 | ||
|  |  *  \brief Lightweight ring (circular) buffer, for fast insertion/deletion of bytes. | ||
|  |  * | ||
|  |  *  Lightweight ring buffer, for fast insertion/deletion. Multiple buffers can be created of | ||
|  |  *  different sizes to suit different needs. | ||
|  |  * | ||
|  |  *  Note that for each buffer, insertion and removal operations may occur at the same time (via | ||
|  |  *  a multi-threaded ISR based system) however the same kind of operation (two or more insertions | ||
|  |  *  or deletions) must not overlap. If there is possibility of two or more of the same kind of | ||
|  |  *  operating occurring at the same point in time, atomic (mutex) locking should be used. | ||
|  |  */ | ||
|  | 
 | ||
|  | /** \ingroup Group_MiscDrivers
 | ||
|  |  *  \defgroup Group_RingBuff Generic Byte Ring Buffer - LUFA/Drivers/Misc/RingBuffer.h | ||
|  |  *  \brief Lightweight ring buffer, for fast insertion/deletion of bytes. | ||
|  |  * | ||
|  |  *  \section Sec_RingBuff_Dependencies Module Source Dependencies | ||
|  |  *  The following files must be built with any user project that uses this module: | ||
|  |  *    - None | ||
|  |  * | ||
|  |  *  \section Sec_RingBuff_ModDescription Module Description | ||
|  |  *  Lightweight ring buffer, for fast insertion/deletion. Multiple buffers can be created of | ||
|  |  *  different sizes to suit different needs. | ||
|  |  * | ||
|  |  *  Note that for each buffer, insertion and removal operations may occur at the same time (via | ||
|  |  *  a multi-threaded ISR based system) however the same kind of operation (two or more insertions | ||
|  |  *  or deletions) must not overlap. If there is possibility of two or more of the same kind of | ||
|  |  *  operating occurring at the same point in time, atomic (mutex) locking should be used. | ||
|  |  * | ||
|  |  *  \section Sec_RingBuff_ExampleUsage Example Usage | ||
|  |  *  The following snippet is an example of how this module may be used within a typical | ||
|  |  *  application. | ||
|  |  * | ||
|  |  *  \code | ||
|  |  *      // Create the buffer structure and its underlying storage array
 | ||
|  |  *      RingBuffer_t Buffer; | ||
|  |  *      uint8_t      BufferData[128]; | ||
|  |  * | ||
|  |  *      // Initialize the buffer with the created storage array
 | ||
|  |  *      RingBuffer_InitBuffer(&Buffer, BufferData, sizeof(BufferData)); | ||
|  |  * | ||
|  |  *      // Insert some data into the buffer
 | ||
|  |  *      RingBuffer_Insert(&Buffer, 'H'); | ||
|  |  *      RingBuffer_Insert(&Buffer, 'E'); | ||
|  |  *      RingBuffer_Insert(&Buffer, 'L'); | ||
|  |  *      RingBuffer_Insert(&Buffer, 'L'); | ||
|  |  *      RingBuffer_Insert(&Buffer, 'O'); | ||
|  |  * | ||
|  |  *      // Cache the number of stored bytes in the buffer
 | ||
|  |  *      uint16_t BufferCount = RingBuffer_GetCount(&Buffer); | ||
|  |  * | ||
|  |  *      // Printer stored data length
 | ||
|  |  *      printf("Buffer Length: %d, Buffer Data: \r\n", BufferCount); | ||
|  |  * | ||
|  |  *      // Print contents of the buffer one character at a time
 | ||
|  |  *      while (BufferCount--) | ||
|  |  *        putc(RingBuffer_Remove(&Buffer)); | ||
|  |  *  \endcode | ||
|  |  * | ||
|  |  *  @{ | ||
|  |  */ | ||
|  | 
 | ||
|  | #ifndef __RING_BUFFER_H__
 | ||
|  | #define __RING_BUFFER_H__
 | ||
|  | 
 | ||
|  | 	/* Includes: */ | ||
|  | 		#include "../../Common/Common.h"
 | ||
|  | 
 | ||
|  | 	/* Enable C linkage for C++ Compilers: */ | ||
|  | 		#if defined(__cplusplus)
 | ||
|  | 			extern "C" { | ||
|  | 		#endif
 | ||
|  | 
 | ||
|  | 	/* Type Defines: */ | ||
|  | 		/** \brief Ring Buffer Management Structure.
 | ||
|  | 		 * | ||
|  | 		 *  Type define for a new ring buffer object. Buffers should be initialized via a call to | ||
|  | 		 *  \ref RingBuffer_InitBuffer() before use. | ||
|  | 		 */ | ||
|  | 		typedef struct | ||
|  | 		{ | ||
|  | 			uint8_t* In; /**< Current storage location in the circular buffer. */ | ||
|  | 			uint8_t* Out; /**< Current retrieval location in the circular buffer. */ | ||
|  | 			uint8_t* Start; /**< Pointer to the start of the buffer's underlying storage array. */ | ||
|  | 			uint8_t* End; /**< Pointer to the end of the buffer's underlying storage array. */ | ||
|  | 			uint16_t Size; /**< Size of the buffer's underlying storage array. */ | ||
|  | 			uint16_t Count; /**< Number of bytes currently stored in the buffer. */ | ||
|  | 		} RingBuffer_t; | ||
|  | 
 | ||
|  | 	/* Inline Functions: */ | ||
|  | 		/** Initializes a ring buffer ready for use. Buffers must be initialized via this function
 | ||
|  | 		 *  before any operations are called upon them. Already initialized buffers may be reset | ||
|  | 		 *  by re-initializing them using this function. | ||
|  | 		 * | ||
|  | 		 *  \param[out] Buffer   Pointer to a ring buffer structure to initialize. | ||
|  | 		 *  \param[out] DataPtr  Pointer to a global array that will hold the data stored into the ring buffer. | ||
|  | 		 *  \param[out] Size     Maximum number of bytes that can be stored in the underlying data array. | ||
|  | 		 */ | ||
|  | 		static inline void RingBuffer_InitBuffer(RingBuffer_t* Buffer, | ||
|  | 		                                         uint8_t* const DataPtr, | ||
|  | 		                                         const uint16_t Size) ATTR_NON_NULL_PTR_ARG(1) ATTR_NON_NULL_PTR_ARG(2); | ||
|  | 		static inline void RingBuffer_InitBuffer(RingBuffer_t* Buffer, | ||
|  | 		                                         uint8_t* const DataPtr, | ||
|  | 		                                         const uint16_t Size) | ||
|  | 		{ | ||
|  | 			GCC_FORCE_POINTER_ACCESS(Buffer); | ||
|  | 
 | ||
|  | 			uint_reg_t CurrentGlobalInt = GetGlobalInterruptMask(); | ||
|  | 			GlobalInterruptDisable(); | ||
|  | 
 | ||
|  | 			Buffer->In     = DataPtr; | ||
|  | 			Buffer->Out    = DataPtr; | ||
|  | 			Buffer->Start  = &DataPtr[0]; | ||
|  | 			Buffer->End    = &DataPtr[Size]; | ||
|  | 			Buffer->Size   = Size; | ||
|  | 			Buffer->Count  = 0; | ||
|  | 
 | ||
|  | 			SetGlobalInterruptMask(CurrentGlobalInt); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/** Retrieves the current number of bytes stored in a particular buffer. This value is computed
 | ||
|  | 		 *  by entering an atomic lock on the buffer, so that the buffer cannot be modified while the | ||
|  | 		 *  computation takes place. This value should be cached when reading out the contents of the buffer, | ||
|  | 		 *  so that as small a time as possible is spent in an atomic lock. | ||
|  | 		 * | ||
|  | 		 *  \note The value returned by this function is guaranteed to only be the minimum number of bytes | ||
|  | 		 *        stored in the given buffer; this value may change as other threads write new data, thus | ||
|  | 		 *        the returned number should be used only to determine how many successive reads may safely | ||
|  | 		 *        be performed on the buffer. | ||
|  | 		 * | ||
|  | 		 *  \param[in] Buffer  Pointer to a ring buffer structure whose count is to be computed. | ||
|  | 		 * | ||
|  | 		 *  \return Number of bytes currently stored in the buffer. | ||
|  | 		 */ | ||
|  | 		static inline uint16_t RingBuffer_GetCount(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1); | ||
|  | 		static inline uint16_t RingBuffer_GetCount(RingBuffer_t* const Buffer) | ||
|  | 		{ | ||
|  | 			uint16_t Count; | ||
|  | 
 | ||
|  | 			uint_reg_t CurrentGlobalInt = GetGlobalInterruptMask(); | ||
|  | 			GlobalInterruptDisable(); | ||
|  | 
 | ||
|  | 			Count = Buffer->Count; | ||
|  | 
 | ||
|  | 			SetGlobalInterruptMask(CurrentGlobalInt); | ||
|  | 			return Count; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/** Retrieves the free space in a particular buffer. This value is computed by entering an atomic lock
 | ||
|  | 		 *  on the buffer, so that the buffer cannot be modified while the computation takes place. | ||
|  | 		 * | ||
|  | 		 *  \note The value returned by this function is guaranteed to only be the maximum number of bytes | ||
|  | 		 *        free in the given buffer; this value may change as other threads write new data, thus | ||
|  | 		 *        the returned number should be used only to determine how many successive writes may safely | ||
|  | 		 *        be performed on the buffer when there is a single writer thread. | ||
|  | 		 * | ||
|  | 		 *  \param[in] Buffer  Pointer to a ring buffer structure whose free count is to be computed. | ||
|  | 		 * | ||
|  | 		 *  \return Number of free bytes in the buffer. | ||
|  | 		 */ | ||
|  | 		static inline uint16_t RingBuffer_GetFreeCount(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1); | ||
|  | 		static inline uint16_t RingBuffer_GetFreeCount(RingBuffer_t* const Buffer) | ||
|  | 		{ | ||
|  | 			return (Buffer->Size - RingBuffer_GetCount(Buffer)); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/** Atomically determines if the specified ring buffer contains any data. This should
 | ||
|  | 		 *  be tested before removing data from the buffer, to ensure that the buffer does not | ||
|  | 		 *  underflow. | ||
|  | 		 * | ||
|  | 		 *  If the data is to be removed in a loop, store the total number of bytes stored in the | ||
|  | 		 *  buffer (via a call to the \ref RingBuffer_GetCount() function) in a temporary variable | ||
|  | 		 *  to reduce the time spent in atomicity locks. | ||
|  | 		 * | ||
|  | 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to insert into. | ||
|  | 		 * | ||
|  | 		 *  \return Boolean \c true if the buffer contains no free space, \c false otherwise. | ||
|  | 		 */ | ||
|  | 		static inline bool RingBuffer_IsEmpty(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1); | ||
|  | 		static inline bool RingBuffer_IsEmpty(RingBuffer_t* const Buffer) | ||
|  | 		{ | ||
|  | 			return (RingBuffer_GetCount(Buffer) == 0); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/** Atomically determines if the specified ring buffer contains any free space. This should
 | ||
|  | 		 *  be tested before storing data to the buffer, to ensure that no data is lost due to a | ||
|  | 		 *  buffer overrun. | ||
|  | 		 * | ||
|  | 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to insert into. | ||
|  | 		 * | ||
|  | 		 *  \return Boolean \c true if the buffer contains no free space, \c false otherwise. | ||
|  | 		 */ | ||
|  | 		static inline bool RingBuffer_IsFull(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1); | ||
|  | 		static inline bool RingBuffer_IsFull(RingBuffer_t* const Buffer) | ||
|  | 		{ | ||
|  | 			return (RingBuffer_GetCount(Buffer) == Buffer->Size); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/** Inserts an element into the ring buffer.
 | ||
|  | 		 * | ||
|  | 		 *  \warning Only one execution thread (main program thread or an ISR) may insert into a single buffer | ||
|  | 		 *           otherwise data corruption may occur. Insertion and removal may occur from different execution | ||
|  | 		 *           threads. | ||
|  | 		 * | ||
|  | 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to insert into. | ||
|  | 		 *  \param[in]     Data    Data element to insert into the buffer. | ||
|  | 		 */ | ||
|  | 		static inline void RingBuffer_Insert(RingBuffer_t* Buffer, | ||
|  | 		                                     const uint8_t Data) ATTR_NON_NULL_PTR_ARG(1); | ||
|  | 		static inline void RingBuffer_Insert(RingBuffer_t* Buffer, | ||
|  | 		                                     const uint8_t Data) | ||
|  | 		{ | ||
|  | 			GCC_FORCE_POINTER_ACCESS(Buffer); | ||
|  | 
 | ||
|  | 			*Buffer->In = Data; | ||
|  | 
 | ||
|  | 			if (++Buffer->In == Buffer->End) | ||
|  | 			  Buffer->In = Buffer->Start; | ||
|  | 
 | ||
|  | 			uint_reg_t CurrentGlobalInt = GetGlobalInterruptMask(); | ||
|  | 			GlobalInterruptDisable(); | ||
|  | 
 | ||
|  | 			Buffer->Count++; | ||
|  | 
 | ||
|  | 			SetGlobalInterruptMask(CurrentGlobalInt); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/** Removes an element from the ring buffer.
 | ||
|  | 		 * | ||
|  | 		 *  \warning Only one execution thread (main program thread or an ISR) may remove from a single buffer | ||
|  | 		 *           otherwise data corruption may occur. Insertion and removal may occur from different execution | ||
|  | 		 *           threads. | ||
|  | 		 * | ||
|  | 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to retrieve from. | ||
|  | 		 * | ||
|  | 		 *  \return Next data element stored in the buffer. | ||
|  | 		 */ | ||
|  | 		static inline uint8_t RingBuffer_Remove(RingBuffer_t* Buffer) ATTR_NON_NULL_PTR_ARG(1); | ||
|  | 		static inline uint8_t RingBuffer_Remove(RingBuffer_t* Buffer) | ||
|  | 		{ | ||
|  | 			GCC_FORCE_POINTER_ACCESS(Buffer); | ||
|  | 
 | ||
|  | 			uint8_t Data = *Buffer->Out; | ||
|  | 
 | ||
|  | 			if (++Buffer->Out == Buffer->End) | ||
|  | 			  Buffer->Out = Buffer->Start; | ||
|  | 
 | ||
|  | 			uint_reg_t CurrentGlobalInt = GetGlobalInterruptMask(); | ||
|  | 			GlobalInterruptDisable(); | ||
|  | 
 | ||
|  | 			Buffer->Count--; | ||
|  | 
 | ||
|  | 			SetGlobalInterruptMask(CurrentGlobalInt); | ||
|  | 
 | ||
|  | 			return Data; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/** Returns the next element stored in the ring buffer, without removing it.
 | ||
|  | 		 * | ||
|  | 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to retrieve from. | ||
|  | 		 * | ||
|  | 		 *  \return Next data element stored in the buffer. | ||
|  | 		 */ | ||
|  | 		static inline uint8_t RingBuffer_Peek(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1); | ||
|  | 		static inline uint8_t RingBuffer_Peek(RingBuffer_t* const Buffer) | ||
|  | 		{ | ||
|  | 			return *Buffer->Out; | ||
|  | 		} | ||
|  | 
 | ||
|  | 	/* Disable C linkage for C++ Compilers: */ | ||
|  | 		#if defined(__cplusplus)
 | ||
|  | 			} | ||
|  | 		#endif
 | ||
|  | 
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | /** @} */ | ||
|  | 
 |