rt-thread-official/bsp/lpc5410x/Libraries/lpc_chip/chip_5410x/hw_adc.c

569 lines
16 KiB
C

/*
* @brief ADC ROM API declarations and functions
*
* @note
* Copyright(C) NXP Semiconductors, 2014
* All rights reserved.
*
* @par
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* LPC products. This software is supplied "AS IS" without any warranties of
* any kind, and NXP Semiconductors and its licensor disclaim any and
* all warranties, express or implied, including all implied warranties of
* merchantability, fitness for a particular purpose and non-infringement of
* intellectual property rights. NXP Semiconductors assumes no responsibility
* or liability for the use of the software, conveys no license or rights under any
* patent, copyright, mask work right, or any other intellectual property rights in
* or to any products. NXP Semiconductors reserves the right to make changes
* in the software without notification. NXP Semiconductors also makes no
* representation or warranty that such application will be suitable for the
* specified use without further testing or modification.
*
* @par
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, under NXP Semiconductors' and its
* licensor's relevant copyrights in the software, without fee, provided that it
* is used in conjunction with NXP Semiconductors microcontrollers. This
* copyright, permission, and disclaimer notice must appear in all copies of
* this code.
*/
#include <string.h>
#include "hw_adc_rom_api.h"
/** @brief ADC ROM Driver Version */
#define ADC_DRIVER_VERSION 0x0100
/* Internal defines */
#define SEQ_A_MASK 0x7C0C5FFF
#define SEQ_B_MASK 0x5C0C5FFF
#define CTRL_MASK 0x00007F00
#define ADC_STATE_INACTIVE 0
#define ADC_STATE_IDLE 1
#define ADC_STATE_ACTIVE 2
/** @brief Channel flags offset */
#define CB_THRES(ch) (1UL << (ch))
#define CB_DATA(ch) (0x10000UL << (ch))
/* ADC Buffer management */
typedef struct {
uint16_t *pCurr; /* ReceiveBuffer Pointer */
size_t szCurr; /* Receive Count */
uint16_t *pNext; /* Ring Buffer Pointer */
size_t szNext; /* Receive Count */
uint32_t idx; /* Current index */
int32_t state; /* State of ADC */
} ADC_BUFFER_T;
/* ADC Driver internal data structure */
typedef struct {
void *pUserData; /* Pointer to user data */
ADC_REGS_T *pREGS; /* Pointer to Registers */
uint32_t valSeq[2]; /* Stored SEQ A/B Values */
ADC_BUFFER_T buffer[2]; /* Buffers to store converted data */
uint32_t flags; /* flags */
uint32_t regThrSel; /* Threshold flags */
uint32_t regInt; /* Interrupt register */
uint32_t flag1; /* Flags Extra */
void(*cbTable[ADC_CBIDX_RESERVED]) (ADC_HANDLE_T, ADC_CBINDEX_T, void *);
} ADC_DRIVER_T;
/* Prototype defines */
static ErrorCode_t ADC_StopConversion(ADC_HANDLE_T hADC, ADC_SEQ_INDEX_T seqIndex);
/* PRIVATE: Invoke a call back function */
static void _ADC_InvokeCallback(ADC_DRIVER_T *pADC, ADC_CBINDEX_T idx, void *param)
{
if (pADC->cbTable[idx]) {
pADC->cbTable[idx]((ADC_HANDLE_T) pADC, idx, param);
}
}
/* PRIVATE: Extract, format data and store into user buffer */
static ErrorCode_t _ADC_GetData(ADC_DRIVER_T *pADC, ADC_SEQ_INDEX_T seqIndex, uint32_t data)
{
uint8_t ch = ADC_DR_CHANNEL(data);
ADC_BUFFER_T *buf = &pADC->buffer[seqIndex];
uint16_t *pDat = &buf->pCurr[buf->idx++];
/* Ignore extra data */
if (!buf->szCurr) {
return LPC_OK;
}
/* If data is not vaild something is wrong! */
if (!(data & ADC_SEQ_GDAT_DATAVALID)) {
return ERR_FAILED;
}
data >>= 4;
if (!(pADC->flag1 & (1UL << ch))) {
data &= 0xFFF;
}
*pDat = data;
/* Invoke the call back for single data read if enabled */
if (pADC->flags & CB_DATA(ch)) {
_ADC_InvokeCallback(pADC, ADC_CHDATA, (void *) ((ch << 16) | data));
}
return LPC_OK;
}
/* PRIVATE: Reads data from the GDAT or DAT register based on mode of operation */
static ErrorCode_t _ADC_ReadData(ADC_DRIVER_T *pADC, ADC_SEQ_INDEX_T seqIndex)
{
ADC_REGS_T *pREGS = pADC->pREGS;
int i;
/* Check if this is End-of-Seq or End-of-SingleConversion */
if (!(pADC->valSeq[seqIndex] & ADC_SEQ_CTRL_MODE_EOS)) {
return _ADC_GetData(pADC, seqIndex, pREGS->SEQ_GDAT[seqIndex]);
}
/* Read channels having conversion data */
for (i = 0; i < sizeof(pREGS->DAT) / sizeof(pREGS->DAT[0]); i++) {
if (pADC->valSeq[seqIndex] & ADC_SEQ_CTRL_CHANSEL(i)) {
if (_ADC_GetData(pADC, seqIndex, pREGS->DAT[i]) != LPC_OK) {
return ERR_FAILED;
}
}
}
return LPC_OK;
}
/* PRIVATE: Overflow handler */
static ErrorCode_t _ADC_Handle_Overflow(ADC_DRIVER_T *pADC, uint32_t flag)
{
_ADC_InvokeCallback(pADC, ADC_OVERFLOW, (void *) ((flag >> 12) & 0x3FFF));
return LPC_OK;
}
/* PRIVATE: ADC Sequence event handler function */
static ErrorCode_t _ADC_Handler_Seq(ADC_DRIVER_T *pADC, ADC_SEQ_INDEX_T seqIndex)
{
ADC_REGS_T *pREGS = pADC->pREGS;
ADC_BUFFER_T *buf = &pADC->buffer[seqIndex];
uint32_t flag = pREGS->FLAGS;
uint32_t tmp;
/* Check if overrun is enabled and got an overrun */
tmp = flag & ADC_FLAGS_SEQN_OVRRUN_MASK(seqIndex);
if (!(flag & ADC_FLAGS_SEQN_INT_MASK(seqIndex))) {
return ERR_ADC_INVALID_SEQUENCE;
}
if (_ADC_ReadData(pADC, seqIndex) != LPC_OK) {
return ERR_FAILED;
}
/* Handle the overflow */
if (tmp) {
_ADC_Handle_Overflow(pADC, flag);
}
/* Clear the interrupt if it is for EOS and not EOC */
if (pADC->valSeq[seqIndex] & ADC_SEQ_CTRL_MODE_EOS) {
pREGS->FLAGS = ADC_FLAGS_SEQN_INT_MASK(seqIndex);
}
/* See if we are done with our buffer */
if (buf->idx >= buf->szCurr) {
_ADC_InvokeCallback(pADC, (ADC_CBINDEX_T) (ADC_BUFFER_DONE + (ADC_CBINDEX_T)seqIndex), buf->pCurr);
if (!buf->pNext || !buf->szNext) {
buf->pCurr = 0;
buf->szCurr = 0;
/* Nothing more to do stop the ADC */
ADC_StopConversion(pADC, seqIndex);
return LPC_OK;
}
buf->pCurr = buf->pNext;
buf->szCurr = buf->szNext;
buf->pNext = 0;
buf->idx = buf->szNext = 0;
}
/* If we are not in burst mode we must trigger next sample */
if (!((pADC->valSeq[seqIndex] >> 12) & 0x1F) && !(pADC->valSeq[seqIndex] & ADC_SEQ_CTRL_BURST)) {
pREGS->SEQ_CTRL[seqIndex] = pADC->valSeq[seqIndex];
}
return LPC_OK;
}
/* PRIVATE: ADC sequence handler polling mode */
static ErrorCode_t _ADC_Handler_SeqPoll(ADC_DRIVER_T *pADC, ADC_SEQ_INDEX_T seqIndex)
{
ADC_REGS_T *pREGS = pADC->pREGS;
ErrorCode_t ret = LPC_OK;
/* Poll as long as the sequence is enabled */
while (pREGS->SEQ_CTRL[seqIndex] & ADC_SEQ_CTRL_SEQ_ENA) {
if (!(pREGS->FLAGS & ADC_FLAGS_SEQN_INT_MASK(seqIndex))) {
continue;
}
ret = _ADC_Handler_Seq(pADC, seqIndex);
if (ret != LPC_OK) {
break;
}
}
return ret;
}
/* PRIVATE: Handler for Overflow event */
static ErrorCode_t _ADC_Handler_Ovr(ADC_DRIVER_T *pADC)
{
uint32_t flags = pADC->pREGS->FLAGS;
/* Invoke Sequence handler to clear-out the data */
if (flags & ADC_FLAGS_SEQA_OVRRUN_MASK) {
return _ADC_Handler_Seq(pADC, ADC_SEQ_A);
}
else if (flags & ADC_FLAGS_SEQB_OVRRUN_MASK) {
return _ADC_Handler_Seq(pADC, ADC_SEQ_B);
}
else {
return ERR_FAILED;
}
}
/* PRIVATE: Threshold event handler */
static ErrorCode_t _ADC_Handler_Thres(ADC_DRIVER_T *pADC)
{
uint32_t flags = pADC->pREGS->FLAGS;
if (!(flags & ADC_FLAGS_THCMP_INT_MASK)) {
return ERR_FAILED;
}
flags &= 0xFFF;
/* Clear out the interrupts */
pADC->pREGS->FLAGS = flags;
_ADC_InvokeCallback(pADC, ADC_THRESHOLD, (void *) flags);
return LPC_OK;
}
/* EXPORTED API: Register a call-back function */
ErrorCode_t ADC_RegisterCallback(ADC_HANDLE_T hADC, ADC_CBINDEX_T idx, void (*cb_func)(ADC_HANDLE_T,
ADC_CBINDEX_T,
void *))
{
if (idx < ADC_CBIDX_RESERVED) {
((ADC_DRIVER_T *) hADC)->cbTable[idx] = cb_func;
}
else {
return ERR_ADC_PARAM;
}
return LPC_OK;
}
/* EXPORTED API: ADC Initialization function */
ADC_HANDLE_T ADC_Init(void *mem, uint32_t base_addr, void *args)
{
ADC_DRIVER_T *pADC;
/* Check if the memory is word aligned */
if ((uint32_t) mem & 0x3) {
return NULL;
}
/* Assign memory provided by application */
pADC = (ADC_DRIVER_T *) mem;
memset(pADC, 0, sizeof(ADC_DRIVER_T));
/* Assign the base address */
pADC->pREGS = (ADC_REGS_T *) base_addr;
pADC->pUserData = args;
/* To be safe stop the ADC in case it is not stopped */
pADC->pREGS->SEQ_CTRL[0] = 0x00;
pADC->pREGS->SEQ_CTRL[1] = 0x00;
return (ADC_HANDLE_T) pADC;
}
/* EXPORTED API: Setup the ADC threshold registers */
void ADC_SetThreshold(ADC_HANDLE_T hADC, uint32_t valThres0, uint32_t valThres1)
{
ADC_REGS_T *pREGS = ((ADC_DRIVER_T *) hADC)->pREGS;
pREGS->THR0_LOW = (valThres0 << ADC_THR_VAL_POS) & ADC_THR_VAL_MASK;
pREGS->THR1_LOW = (valThres1 << ADC_THR_VAL_POS) & ADC_THR_VAL_MASK;
valThres0 >>= 16;
valThres1 >>= 16;
pREGS->THR0_HIGH = (valThres0 << ADC_THR_VAL_POS) & ADC_THR_VAL_MASK;
pREGS->THR1_HIGH = (valThres1 << ADC_THR_VAL_POS) & ADC_THR_VAL_MASK;
}
/* EXPORTED API: Calibrate the ADC */
ErrorCode_t ADC_Calibrate(ADC_HANDLE_T handle, uint32_t sysclk_freq)
{
ADC_REGS_T *pREGS = ((ADC_DRIVER_T *) handle)->pREGS;
volatile uint32_t i;
pREGS->STARTUP = ADC_STARTUP_ENABLE;
for ( i = 0; i < 0x10; i++ ) {}
if ( !(pREGS->STARTUP & ADC_STARTUP_ENABLE) ) {
return ERR_ADC_NO_POWER;
}
/* If not in by-pass mode do the calibration */
if ( (pREGS->CALIBR & ADC_CALREQD) && !(pREGS->CTRL & ADC_CR_BYPASS) ) {
uint32_t ctrl = pREGS->CTRL & (CTRL_MASK | 0xFF);
uint32_t tmp = ctrl;
/* Set ADC to SYNC mode */
tmp &= ~ADC_CR_ASYNC_MODE;
/* To be safe run calibration at 1MHz UM permits upto 30MHz */
if (sysclk_freq > 1000000UL) {
pREGS->CTRL = tmp | (((sysclk_freq / 1000000UL) - 1) & 0xFF);
}
/* Calibration is needed, do it now. */
pREGS->CALIBR = ADC_CALIB;
i = 0xF0000;
while ( (pREGS->CALIBR & ADC_CALIB) && --i ) {}
pREGS->CTRL = ctrl;
return i ? LPC_OK : ERR_TIME_OUT;
}
/* A dummy conversion cycle will be performed. */
pREGS->STARTUP = (pREGS->STARTUP | ADC_STARTUP_INIT) & 0x03;
i = 0x7FFFF;
while ( (pREGS->STARTUP & ADC_STARTUP_INIT) && --i ) {}
return i ? LPC_OK : ERR_TIME_OUT;
}
/* EXPORTED API: Configure the ADC */
ErrorCode_t ADC_Configure(ADC_HANDLE_T hADC, const ADC_CFG_T *pCfg)
{
ADC_DRIVER_T *pADC = hADC;
ADC_REGS_T *pREGS = pADC->pREGS;
pADC->valSeq[ADC_SEQ_A] = ADC_SEQ_CTRL_SEQ_ENA | ADC_SEQ_CTRL_START | (pCfg->flagSeqA & SEQ_A_MASK);
pADC->valSeq[ADC_SEQ_B] = ADC_SEQ_CTRL_SEQ_ENA | ADC_SEQ_CTRL_START | (pCfg->flagSeqB & SEQ_B_MASK);
/* START is not required for BURST or H/W Trigger */
if ((pCfg->flagSeqA & ADC_SEQ_CTRL_TRIGGER_MASK)) {
pADC->valSeq[ADC_SEQ_A] &= ~ADC_SEQ_CTRL_START;
}
/* START is not required for BURST or H/W Trigger */
if ((pCfg->flagSeqB & ADC_SEQ_CTRL_TRIGGER_MASK)) {
pADC->valSeq[ADC_SEQ_B] &= ~ADC_SEQ_CTRL_START;
}
pREGS->CTRL = (pCfg->flagCfg & CTRL_MASK) | (pCfg->clkDiv & ADC_CR_CLKDIV_MASK);
/* Enable/Disable overflow interrupt */
if (pCfg->flagCfg & ENABLE_OVR) {
pADC->regInt |= ADC_INTEN_OVRRUN_ENABLE;
}
else {
pADC->regInt &= ~ADC_INTEN_OVRRUN_ENABLE;
}
return LPC_OK;
}
/* EXPORTED API: Configure channel specific options */
ErrorCode_t ADC_ConfigureChannel(ADC_HANDLE_T hADC, uint32_t chanNum, uint32_t chanOpts)
{
ADC_DRIVER_T *pADC = hADC;
/* Sanity check */
if (chanNum > ADC_MAX_CHANNEL_NUM) {
return ERR_ADC_PARAM;
}
pADC->regInt &= ~(0x18 << (chanNum * 2));
if (chanOpts & ADC_CH_THRES_SEL1) {
pADC->regThrSel |= 1 << chanNum;
}
else {
pADC->regThrSel &= ~(1 << chanNum);
}
if (chanOpts & ADC_CH_THRES_CROSSING) {
pADC->regInt |= 0x10 << (chanNum * 2);
}
else if (chanOpts & ADC_CH_THRES_OUTSIDE) {
pADC->regInt |= 0x08 << (chanNum * 2);
}
if (chanOpts & ADC_CH_DATA_CALLBACK) {
pADC->flags |= CB_DATA(chanNum);
}
else {
pADC->flags &= ~CB_DATA(chanNum);
}
if (chanOpts & ADC_CH_THRES_CALLBACK) {
pADC->flags |= CB_THRES(chanNum);
}
else {
pADC->flags &= ~CB_THRES(chanNum);
}
if (chanOpts & ADC_CH_THRES_DATA) {
pADC->flag1 |= 1 << chanNum;
}
else {
pADC->flag1 &= ~(1 << chanNum);
}
return LPC_OK;
}
/* EXPORTED API: Start analog to digital conversion on selected sequence */
ErrorCode_t ADC_StartConversion(ADC_HANDLE_T hADC, ADC_SEQ_INDEX_T seqIndex, void *buff, size_t bufCount)
{
ADC_DRIVER_T *pADC = hADC;
ADC_BUFFER_T *buf = &pADC->buffer[seqIndex];
/* Sanity check on parameters */
if ((uint32_t) seqIndex > ADC_SEQ_B) {
return ERR_ADC_INVALID_SEQUENCE;
}
/* If an active conversion is going on set the buffer ptr */
if (buf->state == ADC_STATE_ACTIVE) {
if (buf->szNext) {
return ERR_BUSY;
}
buf->pNext = buff;
buf->szNext = bufCount;
return LPC_OK;
}
/* Assign the buffers */
buf->pCurr = buff;
buf->szCurr = bufCount;
buf->idx = 0;
/* Invoke the call back before start */
_ADC_InvokeCallback(pADC, (ADC_CBINDEX_T) (ADC_START_SEQ + (ADC_CBINDEX_T)seqIndex), (void *) &pADC->pREGS);
buf->state = ADC_STATE_ACTIVE;
pADC->pREGS->SEQ_CTRL[seqIndex] = pADC->valSeq[seqIndex] & ~(ADC_SEQ_CTRL_SEQ_ENA | ADC_SEQ_CTRL_START);
pADC->regInt |= (1 << seqIndex);
pADC->pREGS->INTEN = pADC->regInt;
pADC->pREGS->CHAN_THRSEL = pADC->regThrSel;
pADC->pREGS->SEQ_CTRL[seqIndex] = pADC->valSeq[seqIndex];
return LPC_OK;
}
ErrorCode_t ADC_SwTrigger(ADC_HANDLE_T hADC, ADC_SEQ_INDEX_T seqIndex)
{
ADC_DRIVER_T *pADC = hADC;
/* Sanity check on parameters */
if ((uint32_t) seqIndex > ADC_SEQ_B) {
return ERR_ADC_INVALID_SEQUENCE;
}
pADC->pREGS->SEQ_CTRL[seqIndex] = pADC->valSeq[seqIndex] | ADC_SEQ_CTRL_SEQ_ENA | ADC_SEQ_CTRL_START;
return LPC_OK;
}
/* EXPORTED API: Stop conversion on a given sequence */
ErrorCode_t ADC_StopConversion(ADC_HANDLE_T hADC, ADC_SEQ_INDEX_T seqIndex)
{
ADC_DRIVER_T *pADC = hADC;
/* Sanity check on parameters */
if ((uint32_t) seqIndex > ADC_SEQ_B) {
return ERR_ADC_INVALID_SEQUENCE;
}
pADC->regInt &= ~(1 << seqIndex);
pADC->pREGS->INTEN = pADC->regInt; /* Disable interrupts */
pADC->buffer[seqIndex].state = ADC_STATE_IDLE; /* Set state to IDLE */
/* Stop and disable the sequence */
pADC->pREGS->SEQ_CTRL[seqIndex] = pADC->valSeq[seqIndex] &
~(ADC_SEQ_CTRL_SEQ_ENA | ADC_SEQ_CTRL_BURST | ADC_SEQ_CTRL_START);
_ADC_InvokeCallback(hADC, (ADC_CBINDEX_T)(ADC_STOP_SEQ + (ADC_CBINDEX_T)seqIndex), 0);
return LPC_OK;
}
/* EXPORTED API: ADC Event handler */
ErrorCode_t ADC_Handler(ADC_HANDLE_T hADC, ADC_HEVENT_T hEvent)
{
ADC_DRIVER_T *pADC = hADC;
switch (hEvent) {
case ADC_EV_SEQ_A_POLL:
return _ADC_Handler_SeqPoll(pADC, ADC_SEQ_A);
case ADC_EV_SEQ_B_POLL:
return _ADC_Handler_SeqPoll(pADC, ADC_SEQ_B);
case ADC_EV_SEQ_A_INT:
return _ADC_Handler_Seq(pADC, ADC_SEQ_A);
case ADC_EV_SEQ_B_INT:
return _ADC_Handler_Seq(pADC, ADC_SEQ_B);
case ADC_EV_SEQ_A_DMA:
if (!(pADC->valSeq[ADC_SEQ_A] & 0x3F000)) {
ADC_SwTrigger(hADC, ADC_SEQ_A);
}
return LPC_OK;
case ADC_EV_SEQ_B_DMA:
if (!(pADC->valSeq[ADC_SEQ_B] & 0x3F000)) {
ADC_SwTrigger(hADC, ADC_SEQ_B);
}
return LPC_OK;
case ADC_EV_OVR_INT:
return _ADC_Handler_Ovr(pADC);
case ADC_EV_THRES_INT:
return _ADC_Handler_Thres(pADC);
default:
return ERR_ADC_PARAM;
}
}
/* EXPROTED API: Returns memory required for ADC driver */
uint32_t ADC_GetMemSize(void)
{
return sizeof(ADC_DRIVER_T);
}
/* EXPORTED API: Function to Get the firmware Version */
uint32_t ADC_GetDriverVersion(void)
{
return ADC_DRIVER_VERSION;
}
/**
* @brief Table of the addresses of all the 12-Bit ADC functions
* @note This table of function pointers is the API interface.
*/
const ROM_ADC_API_T adcrom_api = {
ADC_GetMemSize,
ADC_Init,
ADC_Configure,
ADC_ConfigureChannel,
ADC_SetThreshold,
ADC_RegisterCallback,
ADC_Calibrate,
ADC_Handler,
ADC_StartConversion,
ADC_StopConversion,
ADC_SwTrigger,
ADC_GetDriverVersion,
};