RabbitECUTeensyMCUXpresso/source/Peripherals/PERADC.c

408 lines
13 KiB
C

/******************************************************************************/
/* Copyright (c) 2016 MD Automotive Controls. Original Work. */
/* License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher */
/******************************************************************************/
/* CONTEXT:KERNEL */
/* PACKAGE TITLE: XXX */
/* DESCRIPTION: XXX */
/* FILE NAME: XXX.c */
/* REVISION HISTORY: 19-08-2016 | 1.0 | Initial revision */
/* */
/******************************************************************************/
#include <string.h>
#include <stddef.h>
#include "CPUAbstract.h"
#include "IOAPI.h"
#include "ADCAPI.h"
#include "types.h"
#include "ADCHA.h"
#include "PERADC.h"
#include "client.h"
#include "CQUEUE.h"
#include "CRC16.h"
#include "declarations.h"
#include "FEE.h"
#include "IRQ.h"
#include "MSG.h"
#include "regset.h"
#include "SIM.h"
#include "sys.h"
ADCHA_tstADCConversion ADC_astADCConversions[ADCHA_enQueueCount][ADC_nConversionsMax];
CQUEUE_tstQueue ADC_astConversionQueue[ADCHA_enQueueCount];
bool ADC_boCyclicQueuePending;
ADC_tstADCResult ADC_astResult[ADCHA_enQueueCount][ADC_nConversionsMax];
MSG_tstMBX* ADC_apstMBX[ADCHA_enQueueCount][ADC_nConversionsMax];
sint32 ADC_i32CyclicQueueIDX;
/* Array to determine which queue triggered the last module conversion. Allows
same AD channel to appear in different queues! */
ADCHA_tenQueue ADC_astADCActiveQueue[ADCHA_enADCModuleCount];
static bool ADC_boInitiateConversion(ADCHA_tstADCConversion*, ADCHA_tenQueue, uint32);
#ifdef ADC_CALIBRATE
static void ADC_vCalibrate(tstADCModule*, uint32, uint32);
#endif //ADC_CALIBRATE
#ifdef BUILD_MK60
static void ADC_vInitInterrupts(IRQn_Type);
#endif //BUILD_MK60
static void ADC_vRunConversionQueues(void);
void ADC_vStart(uint32* const pu32Stat)
{
uint32 u32QueueIDX;
uint32 u32QueueElementIDX;
for (u32QueueIDX = 0; u32QueueIDX < ADCHA_enQueueCount; u32QueueIDX++)
{
for (u32QueueElementIDX = 0; u32QueueElementIDX < ADC_nConversionsMax; u32QueueElementIDX++)
{
/* NULL the mailbox pointer array */
ADC_apstMBX[u32QueueIDX][u32QueueElementIDX] = NULL;
}
}
for (u32QueueIDX = 0; u32QueueIDX < 0xff; u32QueueIDX++)
{
;/* Waste some time *///matthew review
}
ADCHA_vStart(pu32Stat);
CQUEUE_xInit(ADC_astConversionQueue, ADC_nConversionsMax, ADC_astADCConversions);
CQUEUE_xInit(ADC_astConversionQueue + 1, ADC_nConversionsMax, (uint32)ADC_astADCConversions + sizeof(ADCHA_tstADCConversion) * ADC_nConversionsMax * 1);
CQUEUE_xInit(ADC_astConversionQueue + 2, ADC_nConversionsMax, (uint32)ADC_astADCConversions + sizeof(ADCHA_tstADCConversion) * ADC_nConversionsMax * 2);
CQUEUE_xInit(ADC_astConversionQueue + 3, ADC_nConversionsMax, (uint32)ADC_astADCConversions + sizeof(ADCHA_tstADCConversion) * ADC_nConversionsMax * 3);
CQUEUE_xInit(ADC_astConversionQueue + 4, ADC_nConversionsMax, (uint32)ADC_astADCConversions + sizeof(ADCHA_tstADCConversion) * ADC_nConversionsMax * 4);
ADC_i32CyclicQueueIDX = -1;
ADC_boCyclicQueuePending = FALSE;
OS_xModuleStartOK(*pu32Stat);
}
void ADC_vRun(uint32* const pu32Stat)
{
uint32 u32CyclicQueueIDX;
static uint32 u32ADCQueueStuckCount;
ADCHA_tenQueue enQueue;
if ((ADC_astConversionQueue[ADCHA_enQueueCyclic].u32Tail > ADC_astConversionQueue[ADCHA_enQueueCyclic].u32Head) && (-1 == ADC_i32CyclicQueueIDX))
{
for (u32CyclicQueueIDX = ADC_astConversionQueue[ADCHA_enQueueCyclic].u32Head;
u32CyclicQueueIDX <= ADC_astConversionQueue[ADCHA_enQueueCyclic].u32Tail;
u32CyclicQueueIDX++)
{
if (0 < ADC_astADCConversions[ADCHA_enQueueCyclic][u32CyclicQueueIDX].u32ControlCount)
{
ADC_astADCConversions[ADCHA_enQueueCyclic][u32CyclicQueueIDX].u32ControlCount -= 1;
}
}
/* EC - don't want AD conversion complete interrupting here */
CPU_xEnterCritical();
for (u32CyclicQueueIDX = ADC_astConversionQueue[0].u32Head;
u32CyclicQueueIDX <= ADC_astConversionQueue[0].u32Tail;
u32CyclicQueueIDX++)
{
if (0 == ADC_astADCConversions[ADCHA_enQueueCyclic][u32CyclicQueueIDX].u32ControlCount)
{
ADC_i32CyclicQueueIDX = u32CyclicQueueIDX;
//ADC_boCyclicQueuePending = ADC_boInitiateConversion(&ADC_astADCConversions[ADCHA_enQueueCyclic][ADC_astConversionQueue[ADCHA_enQueueCyclic].u32Head], ADCHA_enQueueCyclic, ADC_astConversionQueue[0].u32Tail);
break;
}
}
CPU_xExitCritical();
}
/* Check if the ADC queues are stuck */
for (enQueue = ADCHA_enQueueCyclic; enQueue < ADCHA_enQueueCount; enQueue++)
{
if (ADC_astConversionQueue[enQueue].u32Head != ADC_astConversionQueue[enQueue].u32Tail)
{
u32ADCQueueStuckCount++;
}
}
if (5 < u32ADCQueueStuckCount)
{
ADCHA_vReset(ADCHA_enADC0);
u32ADCQueueStuckCount = 0;
}
}
bool ADC_boBackupCalibrations(void)
{
return ADCHA_boBackupCalibrations();
}
SYSAPI_tenSVCResult ADC_vInitADCResource(IOAPI_tenEHIOResource enIOResource, IOAPI_tenEHIOType enEHIOType, ADCAPI_tstADCCB* pstADCCB)
{
uint32 u32QueueIDX;
SYSAPI_tenSVCResult enResult = SYSAPI_enOK;
ADCHA_tstADCConversion* pstADCConversion;
u32QueueIDX = (ADCAPI_enTrigger1 > pstADCCB->enTrigger) ? 0 : pstADCCB->enTrigger - ADCAPI_en1Hz;
if (!CQUEUE_xIsFull(ADC_astConversionQueue + u32QueueIDX))
{
CPU_xEnterCritical();
pstADCConversion = &ADC_astADCConversions[u32QueueIDX][0] +
ADC_astConversionQueue[u32QueueIDX].u32Tail;
ADCHA_vInitConversion(enIOResource, pstADCConversion, pstADCCB, u32QueueIDX);
switch (pstADCCB->enTrigger)
{
case ADCAPI_en1000Hz: pstADCConversion->u32ResetCount = 1; break;
case ADCAPI_en500Hz: pstADCConversion->u32ResetCount = 2; break;
case ADCAPI_en250Hz: pstADCConversion->u32ResetCount = 4; break;
case ADCAPI_en125Hz: pstADCConversion->u32ResetCount = 8; break;
case ADCAPI_en64Hz: pstADCConversion->u32ResetCount = 31; break;
case ADCAPI_en32Hz: pstADCConversion->u32ResetCount = 32; break;
case ADCAPI_en16Hz: pstADCConversion->u32ResetCount = 63; break;
case ADCAPI_en8Hz: pstADCConversion->u32ResetCount = 125; break;
case ADCAPI_en4Hz: pstADCConversion->u32ResetCount = 250; break;
case ADCAPI_en2Hz: pstADCConversion->u32ResetCount = 500; break;
case ADCAPI_en1Hz: pstADCConversion->u32ResetCount = 1000; break;
default: pstADCConversion->u32ResetCount = 1000; break;
}
CQUEUE_xAddItem(ADC_astConversionQueue + u32QueueIDX);
/* Remove to leave it in the queue but exhaust the queue */
CQUEUE_xRemoveItem(ADC_astConversionQueue + u32QueueIDX);
if (IOAPI_enADD == enEHIOType)
{
ADCHA_vInitADCResourcePGA(pstADCConversion);
}
ADCHA_vInitChannel(enIOResource);
CPU_xExitCritical();
}
else
{
enResult = SYSAPI_enQueueFull;
}
return enResult;
}
void ADC_vTerminate(uint32* const u32Stat)
{
}
void ADC_vInterruptHandler(IOAPI_tenEHIOResource enEHIOResource, void* pvData)
{
uint32 enADCModule;
uint32 u32ADCChannel;
MSG_tstMBX* pstMBX;
uint32 u32QueueIDX;
ADCHA_tenQueue enQueue;
CLIENT_tenErr enErr;
tpfClientCB pfClientCB;
uint32 u32ADCResult;
tstADCModule* pstADC;
uint32 u32ADCResultAvailableCount;
uint32 u32DequeueIDX;
enADCModule = enEHIOResource - EH_VIO_ADC0;
/* Which queue initiated this conversion? */
u32QueueIDX = (ADCHA_enQueueCyclic == ADC_astADCActiveQueue[enADCModule]) ?
(uint32)ADC_i32CyclicQueueIDX :
ADC_astConversionQueue[ADC_astADCActiveQueue[enADCModule]].u32Head;
pstADC = ADCHA_pstGetADCModule(enADCModule);
u32ADCResultAvailableCount = ADCHA_u32GetAvailableResultCount(pstADC);
u32ADCResultAvailableCount = MIN(u32ADCResultAvailableCount, ADC_astConversionQueue[ADC_astADCActiveQueue[enADCModule]].u32Tail);
for (u32DequeueIDX = u32QueueIDX; u32DequeueIDX < u32QueueIDX + u32ADCResultAvailableCount; u32DequeueIDX++)
{
enQueue = ADC_astADCActiveQueue[enADCModule];
u32ADCChannel = ADC_astADCConversions[enQueue][u32DequeueIDX].stADCChannel.u32ADChannel;
enEHIOResource = ADCHA_enGetResourceAndResult(enADCModule, pstADC, u32ADCChannel, &u32ADCResult);
/* Populate the result array - the reference of the element will
be posted to the client MBX* queue for callback to user */
ADC_astResult[enQueue][u32DequeueIDX].u32Result = u32ADCResult;
ADC_astResult[enQueue][u32DequeueIDX].enEHIOResource = enEHIOResource;
pstMBX = ADC_apstMBX[enQueue][u32DequeueIDX];
MSG_vMBXPost(pstMBX, (void*)&ADC_astResult[enQueue][u32DequeueIDX]);
pfClientCB = (tpfClientCB)ADC_astADCConversions[enQueue][u32DequeueIDX].pfResultCB;
if ((NULL != pfClientCB) && (MSG_boMBXValid(pstMBX)))
{
enErr = CLIENT_enEnqueueCB(pstMBX, pfClientCB);
}
if (ADCHA_enQueueCyclic == enQueue)
{
while (-1 != ADC_i32CyclicQueueIDX)
{
ADC_i32CyclicQueueIDX = ((ADC_astConversionQueue[ADCHA_enQueueCyclic].u32Tail - 1) > ADC_i32CyclicQueueIDX) ? ADC_i32CyclicQueueIDX + 1 : -1;
if (-1 != ADC_i32CyclicQueueIDX)
{
if (0 == ADC_astADCConversions[ADCHA_enQueueCyclic][ADC_i32CyclicQueueIDX].u32ControlCount)
{
break;
}
}
}
}
else
{
CQUEUE_xRemoveItem(ADC_astConversionQueue + enQueue);
if (ADC_astConversionQueue[enQueue].u32Head == ADC_astConversionQueue[enQueue].u32Tail)
{
/* Release the ADC module now the conversion queue has been processed */
ADCHA_vClearModuleBusy(enADCModule);
}
}
}
ADC_vRunConversionQueues();
/* TODO suppress warning */
(void)enErr;
}
bool ADC_vTriggerQueue(ADCAPI_tenTrigger enTrigger)
{
ADCHA_tenQueue enQueue;
bool boResult = FALSE;
if ((ADCAPI_enTrigger1 <= enTrigger) && (ADCAPI_enTriggerCount > enTrigger))
{
enQueue = (ADCHA_tenQueue)(enTrigger - ADCAPI_enTrigger1 + 1);
if (false == ADCHA_boGetModuleBusy(ADCHA_enADC0))
{
ADC_astConversionQueue[enQueue].u32Head = 0;
ADC_vRunConversionQueues();
boResult = TRUE;
}
}
return boResult;
}
static bool ADC_boInitiateConversion(ADCHA_tstADCConversion* pstADCConversion, ADCHA_tenQueue enQueue, uint32 u32QueueTail)
{
bool boADCInitPending = TRUE;
uint32 u32QueueIDX;
MSG_tenMBXErr enMBXErr = MSG_enOK;
uint32 u32ConversionIDX;
u32QueueIDX = (ADCHA_enQueueCyclic == enQueue) ? ADC_i32CyclicQueueIDX :
ADC_astConversionQueue[enQueue].u32Head;
for (u32ConversionIDX = u32QueueIDX; u32ConversionIDX < u32QueueTail; u32ConversionIDX++)
{
/* Check if a mailbox is allocated for this conversion */
if (NULL == ADC_apstMBX[enQueue][u32ConversionIDX])
{
enMBXErr = MSG_enMBXNew((MSG_tstMBX**)&ADC_apstMBX[enQueue][u32ConversionIDX],
MSG_enADCResult);
}
}
if (MSG_enOK == enMBXErr)
{
/* Remember which queue initiated this conversion */
ADC_astADCActiveQueue[pstADCConversion->stADCChannel.enADCModule] = enQueue;
boADCInitPending = ADCHA_boInitiateConversion(pstADCConversion, enQueue, u32QueueTail, FALSE);
pstADCConversion->u32ControlCount = pstADCConversion->u32ResetCount;
}
return boADCInitPending;
}
#ifdef ADC_CALIBRATE
static void ADC_vCalibrate(tstADCModule* pstADC, uint32 u32ADCIDX, uint32 u32CalFlag)
{
ADCHA_vCalibrate(pstADC, u32ADCIDX, u32CalFlag);
}
#endif //ADC_CALIBRATE
static void ADC_vRunConversionQueues(void)
{
ADCHA_tenQueue enQueue;
bool boModuleBusy[ADCHA_enADCModuleCount];
ADCHA_tenADCModule enADCModule;
bool boConversionPending;
ADCHA_tstADCConversion* pstConversion;
uint32 u32QueueIDX;
for(enADCModule = ADCHA_enADC0; enADCModule < ADCHA_enADCModuleCount; enADCModule++)
{
boModuleBusy[enADCModule] = ADCHA_boGetModuleBusy(enADCModule);
}
for (enQueue = ADCHA_enQueueCyclic; enQueue < ADCHA_enQueueCount; enQueue++)
{
switch (enQueue)
{
case ADCHA_enQueueCyclic:
{
if (-1 < ADC_i32CyclicQueueIDX)
{
pstConversion = &ADC_astADCConversions[enQueue][ADC_i32CyclicQueueIDX];
enADCModule = pstConversion->stADCChannel.enADCModule;
if (FALSE == boModuleBusy[enADCModule])
{
boConversionPending = ADC_boInitiateConversion(pstConversion, enQueue, ADC_astConversionQueue[enQueue].u32Tail);
if (!boConversionPending)
{
boModuleBusy[enADCModule] = TRUE;
}
}
}
break;
}
case ADCHA_enQueueTriggered1:
case ADCHA_enQueueTriggered2:
case ADCHA_enQueueTriggered3:
case ADCHA_enQueueTriggered4:
{
if (false == CQUEUE_xIsEmpty((CQUEUE_tstQueue*)&ADC_astConversionQueue + enQueue))
{
u32QueueIDX = ADC_astConversionQueue[enQueue].u32Head;
pstConversion = &ADC_astADCConversions[enQueue][u32QueueIDX];
enADCModule = pstConversion->stADCChannel.enADCModule;
if (FALSE == boModuleBusy[enADCModule])
{
boConversionPending = ADC_boInitiateConversion(pstConversion, enQueue, ADC_astConversionQueue[enQueue].u32Tail);
if (!boConversionPending)
{
boModuleBusy[enADCModule] = TRUE;
}
}
}
break;
}
default:
{
break;
}
}
}
}