|
|
dereksoftstuff
Expert Boarder |
|
2008/11/10 06:19 |
|
HowTo : Cyclic Buffers + multi-sensor example
A library for generic Cyclic buffers. Can be used for implementing low level FIFO buffers (BYTE) or any user data type buffers (BUFFER_ITEM). Buffer is allocated dynamically on creation, and remains at this fixed size for its duration of use.
Built upon :-
malloc
locks
If using LOCKS, can have multiple simultaneous writers to one buffer, and one reader. This is demonstrated in the example for a multi-sensor data logger.
cyclicBuffer.h & cyclicBuffer.c
| Code: |
#ifndef CYCLIC_BUFFER_H_
#define CYCLIC_BUFFER_H_
#include <systemDefs.h>
#include <locks.h>
#ifdef __cplusplus
extern "C"
{
#endif
// ----------------------------------------------------------------------------------
// generic cyclic buffer
// can be used with ANY User data type
// each data item is the same size - allocate on create
// data is allocated dynamically
//
// Default : One writer and one reader allowed simultaneously (e.g function & ISR) - separate read/
write ptrs
// Multiple writers allowed depending on locks used, still with one reader
// There are 2 sets of functions - based on BUFFER_ITEM and BYTE types - choose only one, don't mix
// BUFFER_ITEM can be any type
// - bufferWriteItemWithLock() is for use with multiple writers (ISRs) e.g queues
// BYTE is for low level use with FIFOs ...
//
// ----------------------------------------------------------------------------------
typedef void *BUFFER_ID;
typedef void *BUFFER_ITEM; // user data type (your stuff)
typedef struct bufferSizeType {
UINT bufferSize; // length of buffer e.g. 100 32bit integers = (100, 4)
UINT itemByteSize; // unit size
};
// ----------------------------------------------------------------------------------
#define BUFFER_ID_ERROR 0
#if defined(USE_LOCKS)
// can specify the interrupt locking strategy(if any) via params - for multiple writers
BUFFER_ID createBufferWithNewLock(struct bufferSizeType sizeInfo, struct lockType lockInfo);
// if already have a lock to use
BUFFER_ID createBufferWithLock(struct bufferSizeType sizeInfo, LOCK_ID lockId);
#else
BUFFER_ID createBuffer(struct bufferSizeType sizeInfo);
#endif // USE_LOCKS
BUFFER_ID deleteBuffer(BUFFER_ID bufferId); // ensure bufferId reset by return
boolean cleanBuffer(BUFFER_ID bufferId); // reset to empty
boolean isBufferEmpty(BUFFER_ID bufferId);
boolean isBufferFull(BUFFER_ID bufferId);
// ----------------------------------------------------------------------------------
// ITEM only
// ----------------------------------------------------------------------------------
UINT itemsInBuffer(BUFFER_ID bufferId);
UINT freeItemsInBuffer(BUFFER_ID bufferId);
void bufferWriteItem(BUFFER_ID bufferId, BUFFER_ITEM item);
#if defined(USE_LOCKS)
boolean bufferWriteItemWithLock(BUFFER_ID bufferId, BUFFER_ITEM item);
#endif // USE_LOCKS
void bufferReadItem(BUFFER_ID bufferId, BUFFER_ITEM item);
// ----------------------------------------------------------------------------------
// BYTE only
// ----------------------------------------------------------------------------------
UINT bytesInBuffer(BUFFER_ID bufferId);
UINT freeBytesInBuffer(BUFFER_ID bufferId);
BYTE bufferReadByte(BUFFER_ID bufferId);
void bufferWriteByte(BUFFER_ID bufferId, BYTE itemByte);
// for peeking and direct manipulation of bytes - use with care
void startBufferPeek(BUFFER_ID bufferId);
BYTE bufferPeekByte(BUFFER_ID bufferId);
void stopBufferPeek(BUFFER_ID bufferId);
#ifdef __cplusplus
}
#endif
#endif // CYCLIC_BUFFER_H_
|
| Code: |
#include <cyclicBuffer.h>
#include <systemRAM.h>
#include <locks.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
#define MIN_BUFFER_SIZE 2
#define MIN_BUFFER_ITEM_SIZE 1 // bytes
typedef BYTE *BUFFER_UNIT;
typedef struct bufferInfoType {
UINT itemSize;
UINT bufferSize;
UINT totalSize;
BUFFER_UNIT readPtr;
BUFFER_UNIT writePtr;
BUFFER_UNIT bufferStart;
BUFFER_UNIT bufferEnd; // start byte of last item
BUFFER_UNIT bufferByteEnd; // last byte
BUFFER_UNIT peekStart;
BUFFER_UNIT peekPtr;
#if defined(USE_LOCKS)
boolean reuseLock;
LOCK_ID lockId;
#endif // USE_LOCKS
};
// *************************************************************
// locals
// *************************************************************
BUFFER_UNIT cyclicBufferIncrement(struct bufferInfoType *bufferInfo, BUFFER_UNIT ptr) {
if (ptr == bufferInfo->bufferEnd) {
ptr = bufferInfo->bufferStart; // wrap
} else {
ptr += bufferInfo->itemSize;
}
return ptr;
}
BUFFER_UNIT cyclicBufferDecrement(struct bufferInfoType *bufferInfo, BUFFER_UNIT ptr) {
if (ptr == bufferInfo->bufferStart) {
ptr = bufferInfo->bufferEnd; // wrap
} else {
ptr -= bufferInfo->itemSize;
}
return ptr;
}
void resetBufferPtrs(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
bufferInfo->readPtr = bufferInfo->bufferStart;
bufferInfo->writePtr = bufferInfo->bufferStart;
bufferInfo->peekStart = bufferInfo->bufferStart;
bufferInfo->peekPtr = bufferInfo->bufferStart;
}
// *************************************************************
// implementations
// *************************************************************
#if defined(USE_LOCKS)
BUFFER_ID createBufferWithReuseLock(struct bufferSizeType sizeInfo, LOCK_ID lockId, boolean reuseLock) {
#else
BUFFER_ID createBuffer(struct bufferSizeType sizeInfo) {
#endif // USE_LOCKS
#if defined(USE_LOCKS)
lockAll();
#endif // USE_LOCKS
BUFFER_ID bufferId;
if ((sizeInfo.bufferSize >= MIN_BUFFER_SIZE) &&
(sizeInfo.itemByteSize >= MIN_BUFFER_ITEM_SIZE)) {
bufferId = malloc(sizeof(struct bufferInfoType));
if (bufferId != BUFFER_ID_ERROR) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
UINT totalSize = (sizeInfo.bufferSize + 1) * sizeInfo.itemByteSize; // extra 1 for spare slot (Full check)
bufferInfo->bufferStart = (BYTE*)malloc(totalSize);
if (bufferInfo->bufferStart != BUFFER_ID_ERROR) {
bufferInfo->totalSize = totalSize;
bufferInfo->bufferEnd = bufferInfo->bufferStart + bufferInfo->totalSize - sizeInfo.itemByteSize;
bufferInfo->bufferByteEnd = bufferInfo->bufferStart + bufferInfo->totalSize - 1;
bufferInfo->bufferSize = sizeInfo.bufferSize; // extra slot - empty/full checks
bufferInfo->itemSize = sizeInfo.itemByteSize;
#if defined(USE_LOCKS)
bufferInfo->lockId = lockId;
bufferInfo->reuseLock = reuseLock;
#endif // USE_LOCKS
resetBufferPtrs(bufferId);
} else {
bufferId = freeMemory(bufferId);
}
}
} else {
bufferId = BUFFER_ID_ERROR;
}
#if defined(USE_LOCKS)
//tidy
if ((bufferId == BUFFER_ID_ERROR) && !reuseLock) {
lockId = deleteLock(lockId);
}
#endif // USE_LOCKS
#if defined(USE_LOCKS)
unLockAll();
#endif // USE_LOCKS
return bufferId;
}
#if defined(USE_LOCKS)
BUFFER_ID createBufferWithLock(struct bufferSizeType sizeInfo, LOCK_ID lockId) {
return createBufferWithReuseLock(sizeInfo, lockId, TRUE);
}
BUFFER_ID createBufferWithNewLock(struct bufferSizeType sizeInfo, struct lockType lockInfo) {
LOCK_ID lockId = getLockId(lockInfo);
if (lockId == LOCK_ID_FAILED) {
return BUFFER_ID_ERROR;
}
return createBufferWithReuseLock(sizeInfo, lockId, FALSE);
}
#endif // USE_LOCKS
BUFFER_ID deleteBuffer(BUFFER_ID bufferId) {
if (validHeapAddress(bufferId)) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
#if defined(USE_LOCKS)
lockAll();
#endif // USE_LOCKS
#if defined(USE_LOCKS)
if (!bufferInfo->reuseLock) {
bufferInfo->lockId = deleteLock(bufferInfo->lockId);
}
#endif // USE_LOCKS
bufferInfo->bufferStart = (BYTE*)freeMemory(bufferInfo->bufferStart);
bufferId = freeMemory(bufferId);
#if defined(USE_LOCKS)
unLockAll();
#endif // USE_LOCKS
}
return bufferId;
}
boolean cleanBuffer(BUFFER_ID bufferId) {
if (validHeapAddress(bufferId)) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
#if defined(USE_LOCKS)
lock(bufferInfo->lockId);
#endif // USE_LOCKS
resetBufferPtrs(bufferId);
#if defined(USE_LOCKS)
unLock(bufferInfo->lockId);
#endif // USE_LOCKS
return TRUE;
}
return FALSE;
}
// ----------------------------------------------------------------------------
boolean isBufferEmpty(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
return (bufferInfo->writePtr == bufferInfo->readPtr) ? TRUE : FALSE;
}
boolean isBufferFull(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
return (bytesInBuffer(bufferId) == (bufferInfo->bufferSize * bufferInfo->itemSize)) ? TRUE : FALSE;
}
// ----------------------------------------------------------------------------
UINT itemsInBuffer(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
return bytesInBuffer(bufferId) / bufferInfo->itemSize;
}
UINT freeItemsInBuffer(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
return bufferInfo->bufferSize - itemsInBuffer(bufferId);
}
// use freeItemsInBuffer before calling - to get max number of times to call this
void bufferWriteItem(BUFFER_ID bufferId, BUFFER_ITEM item) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
memcpy(bufferInfo->writePtr, item, bufferInfo->itemSize);
bufferInfo->writePtr = cyclicBufferIncrement(bufferInfo, bufferInfo->writePtr);
}
#if defined(USE_LOCKS)
boolean bufferWriteItemWithLock(BUFFER_ID bufferId, BUFFER_ITEM item) {
boolean successStatus = FALSE;
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
lock(bufferInfo->lockId);
if (!isBufferFull(bufferId)) {
bufferWriteItem(bufferId, item);
successStatus = TRUE;
}
unLock(bufferInfo->lockId);
return successStatus;
}
#endif // USE_LOCKS
// use itemsInBuffer before calling - to get max number of times to call this
void bufferReadItem(BUFFER_ID bufferId, BUFFER_ITEM item) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
memcpy(item, bufferInfo->readPtr, bufferInfo->itemSize);
bufferInfo->readPtr = cyclicBufferIncrement(bufferInfo, bufferInfo->readPtr);
}
// --------------------------------------------------------------------------
// byte access modules for low-level interfacing
// --------------------------------------------------------------------------
UINT bytesInBuffer(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
if (bufferInfo->writePtr >= bufferInfo->readPtr) {
return (bufferInfo->writePtr - bufferInfo->readPtr);
} else {
// wrap
return bufferInfo->totalSize - (bufferInfo->readPtr - bufferInfo->writePtr);
}
}
UINT freeBytesInBuffer(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
return (bufferInfo->bufferSize * bufferInfo->itemSize) - bytesInBuffer(bufferId);
}
// use freeBytesInBuffer before calling - to get max number of times to call this
void bufferWriteByte(BUFFER_ID bufferId, BYTE itemByte) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
*bufferInfo->writePtr = itemByte;
if (bufferInfo->writePtr == bufferInfo->bufferByteEnd) {
bufferInfo->writePtr = bufferInfo->bufferStart; // wrap
} else {
bufferInfo->writePtr++;
}
}
// use bytesInBuffer before calling - to get max number of times to call this
BYTE bufferReadByte(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
BYTE itemByte = *bufferInfo->readPtr;
if (bufferInfo->readPtr == bufferInfo->bufferByteEnd) {
bufferInfo->readPtr = bufferInfo->bufferStart; // wrap
} else {
bufferInfo->readPtr++;
}
return itemByte;
}
// The peek functions are for error detection of your data on the read side of the buffer
// Only use if you need to repeat process the data.
// use bytesInBuffer before calling the peek functions to get the
// number of bytes in the buffer - this will determine how many times to call bufferPeekByte.
// The peek functions allow access to the read side of buffer, without moving
// the read ptr forwards - this allows multiple reads of the same part of the buffer
// for error detection
// order of use :-
// 1) startBufferPeek
// 2) bufferPeekByte - multiple calls
// 3) stopBufferPeek
// Note that 1) and 2) can be repeated also, before calling 3)
// Calling 3) stopBufferPeek will realign the buffer read ptr - so should be used with CARE
void startBufferPeek(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
bufferInfo->peekStart = bufferInfo->readPtr;
bufferInfo->peekPtr = bufferInfo->peekStart;
}
BYTE bufferPeekByte(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
BYTE itemByte = *bufferInfo->peekPtr;
if (bufferInfo->peekPtr == bufferInfo->bufferByteEnd) {
bufferInfo->peekPtr = bufferInfo->bufferStart; // wrap
} else {
bufferInfo->peekPtr++;
}
return itemByte;
}
// use with CARE - aligning the readPtr
void stopBufferPeek(BUFFER_ID bufferId) {
struct bufferInfoType *bufferInfo = (struct bufferInfoType*)bufferId;
bufferInfo->readPtr = bufferInfo->peekPtr;
}
#ifdef __cplusplus
}
#endif
|
login or register to reply
|
|
|
dereksoftstuff
Expert Boarder |
|
2008/11/10 06:29 |
|
Re:HowTo : Cyclic Buffers + multi-sensor example
Example - multi-sensor data logger
Also uses :-
timers
| Code: |
#ifndef EXAMPLE_CYCLIC_BUFFER_H_
#define EXAMPLE_CYCLIC_BUFFER_H_
#include <systemDefs.h>
#ifdef __cplusplus
extern "C"
{
#endif
boolean exampleCyclicBuffer(void);
#ifdef __cplusplus
}
#endif
#endif // EXAMPLE_CYCLIC_BUFFER_H_
|
| Code: |
#include <systemDefs.h>
#include <timerUtils.h>
#include <cyclicBuffer.h>
#include <exampleCyclicBuffer.h>
#include <display.h>
#include <hw_ints.h>
#include <interrupt.h>
#include <sysctl.h>
#include <lmi_timer.h> // maybe called timer.h
#include <stdlib.h> // rand
#ifdef __cplusplus
extern "C"
{
#endif
// Interrupt frequency - simulate new sensor reading available
#define SENSOR_SIMULATOR_usTIME_PERIOD 100
#define SENSOR_BUFFER_SIZE 100 // num items buffer can hold
#define SENSOR_BUFFER_UNIT_SIZE sizeof(UINT) // each item byte size
static BUFFER_ID sensorBuffer;
typedef struct sensorInfoType {
UINT value;
UINT numReadings;
UINT numMissedReadings;
};
static struct sensorInfoType sensor1;
static struct sensorInfoType sensor2;
#if defined(USE_LOCKS)
#if defined(USE_LOCK_GROUP)
static BYTE interruptIds[3] = { INT_TIMER2A, INT_TIMER3A, NULL };
#endif // USE_LOCK_GROUP
#endif // USE_LOCKS
// -------------------------------------------------------------------------
void Timer2IntHandler(void) {
// Clear the timer interrupt.
TimerIntClear(TIMER2_BASE, TIMER_TIMA_TIMEOUT);
sensor1.value = rand() % 100000; // simulate sensor readings
#if defined(USE_LOCKS)
//------------------------------------------
// test multiwriters
//------------------------------------------
if (bufferWriteItemWithLock(sensorBuffer, &sensor1.value)) {
sensor1.numReadings++;
} else {
sensor1.numMissedReadings++;
}
#else
// single writer
if (!isBufferFull(sensorBuffer)) {
bufferWriteItem(sensorBuffer, &sensor1.value);
sensor1.numReadings++;
} else {
sensor1.numMissedReadings++;
}
#endif // USE_LOCKS
}
void Timer3IntHandler(void) {
// Clear the timer interrupt.
TimerIntClear(TIMER3_BASE, TIMER_TIMA_TIMEOUT);
#if defined(USE_LOCKS)
//------------------------------------------
// test multiwriters
//------------------------------------------
sensor2.value = rand() % 100; // simulate sensor readings
if (bufferWriteItemWithLock(sensorBuffer, &sensor2.value)) {
sensor2.numReadings++;
} else {
sensor2.numMissedReadings++;
}
#endif // USE_LOCKS
}
// -------------------------------------------------------------------------
boolean initSensorSimulator(UINT bufferSize, UINT usTimePeriod) {
// ------------------------------------------------------
// writer 1
// ------------------------------------------------------
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
// Configure 32-bit periodic timer
TimerConfigure(TIMER2_BASE, TIMER_CFG_32_BIT_PER);
TimerLoadSet(TIMER2_BASE, TIMER_A, usTimePeriod * NUM_CLOCKS_IN_MICROSEC);
TimerIntEnable(TIMER2_BASE, TIMER_TIMA_TIMEOUT);
TimerEnable(TIMER2_BASE, TIMER_A);
// ------------------------------------------------------
// writer 2
// ------------------------------------------------------
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER3);
// Configure 32-bit periodic timer
TimerConfigure(TIMER3_BASE, TIMER_CFG_32_BIT_PER);
TimerLoadSet(TIMER3_BASE, TIMER_A, ((usTimePeriod * 2) - 1) * NUM_CLOCKS_IN_MICROSEC);
TimerIntEnable(TIMER3_BASE, TIMER_TIMA_TIMEOUT);
TimerEnable(TIMER3_BASE, TIMER_A);
// ------------------------------------------------------
struct bufferSizeType sizeInfo;
sizeInfo.bufferSize = bufferSize;
sizeInfo.itemByteSize = SENSOR_BUFFER_UNIT_SIZE;
#if defined(USE_LOCKS)
struct lockType lockInfo;
// *************************** try each of these **********************************
// lockInfo.lockClass = NO_LOCKS;
lockInfo.lockClass = LOCKS;
// *********************************************************************************
#if defined(USE_LOCK_ALL)
// 2A higher priority than 3A
IntPrioritySet(INT_TIMER2A, 0x80);
IntPrioritySet(INT_TIMER3A, 0xC0);
#elif defined(USE_LOCK_GROUP)
lockInfo.interruptIds = interruptIds;
// 2A higher priority than 3A
IntPrioritySet(INT_TIMER2A, 0x80);
IntPrioritySet(INT_TIMER3A, 0xC0);
#elif defined(USE_LOCK_PRIORITY)
setInterruptPriorityLevel(INT_TIMER2A, 4);
setInterruptPriorityLevel(INT_TIMER3A, 4);
lockInfo.priorityMask = 4; // for thread
#endif // LOCK TYPE
sensorBuffer = createBufferWithNewLock(sizeInfo, lockInfo);
#else
sensorBuffer = createBuffer(sizeInfo);
#endif // USE_LOCKS
if (sensorBuffer == BUFFER_ID_ERROR) {
return FALSE; // error
}
IntEnable(INT_TIMER2A); // go
IntEnable(INT_TIMER3A); // go
// seed random sensor input simulator
srand(TimerValueGet(TIMER0_BASE, TIMER_A));
return TRUE;
}
// -------------------------------------------------------------------------
boolean exampleCyclicBuffer(void) {
UINT numItems = 0;
UINT sensorValue;
UINT i;
UINT numSensorReadingsReceived = 0;
if (!initSensorSimulator(SENSOR_BUFFER_SIZE, SENSOR_SIMULATOR_usTIME_PERIOD)) {
return FALSE;
}
while (TRUE) {
numItems = itemsInBuffer(sensorBuffer);
numSensorReadingsReceived += numItems;
for (i = 0; i < numItems; i++) {
bufferReadItem(sensorBuffer, &sensorValue);
// use sensorValue ...
} // loop
// test results - prints (OLED) take a long time - disable interrupts while printing
#if defined(USE_LOCKS)
#if defined(USE_LOCK_ALL)
lockAll();
#elif defined(USE_LOCK_GROUP)
lockGroup(interruptIds);
#elif defined(USE_LOCK_PRIORITY)
lockPriority(4);
#endif // LOCK TYPE
// sensor results
// ------------------
UINT line = 0;
printValue(numItems, 80, line);
// this value should be zero - or close to zero allowing for buffered data
// if this value starts growing then writers are interfering with each other - ERROR
(NO_LOCKS)
// using LOCKS will cure this error
printValue((sensor1.numReadings + sensor2.numReadings) - numSensorReadingsReceived, 0, line);
line += 10;
printValue(sensor1.numReadings, 0, line);
printValue(sensor1.numMissedReadings, 60, line);
line += 10;
&n
|
| |