Jump to content

    

LPC2368, ADC и ....

Добрый день!

 

FreeRTOS завел, даже думал написать краткий мануал, а потом понял, что мануал давно написан и называется FreeRTOS Porting Guide,

zltigo хоть и суров, но прав на 100% ;)

 

Теперь взялся за ADC.

Последовательность такая: прочитал User Manual, посмотрел исходники для gcc, посмотрел библиотеку NXP (LPC CDL), подключил ее.

Причина использования библиотеки - любовь к функциям типа PINSEL_Set, оставшаяся от писания под SAM7S.

 

Итого получилось так:

void xSetupADC(void)
{
// Select pin that used as ADC function

    PINSEL_Set (PORT_0, 23, FUNC_1); 
    PINSEL_Set (PORT_0, 24, FUNC_1);
    PINSEL_Set (PORT_0, 25, FUNC_1);
    PINSEL_Set (PORT_0, 26, FUNC_1);
    PINSEL_Set (PORT_1, 30, FUNC_3); 
    PINSEL_Set (PORT_1, 31, FUNC_3);  

PINSEL_SetResistorMode(PORT_0,23,PINMODE_TRISTATE);
PINSEL_SetResistorMode(PORT_0,24,PINMODE_TRISTATE);
PINSEL_SetResistorMode(PORT_0,25,PINMODE_TRISTATE);
PINSEL_SetResistorMode(PORT_0,26,PINMODE_TRISTATE);
PINSEL_SetResistorMode(PORT_1,30,PINMODE_TRISTATE);
PINSEL_SetResistorMode(PORT_1,31,PINMODE_TRISTATE);

/* Init ADC device */
if ((adcdev = ADC_Init()) == 0)
     {
         while(1);
     }
    
        // Install ADC interrupt handler
    if ((VIC_InstallIRQ (ADC0_INT, (void *) ADC_ISR, 0x0F)) == _ERROR)
     {
         while(1);
     }

    // Set call back function for ADC
    //ADC_SetCBS (adcdev, (void *)usr_adc_cbs);
    
    // Configure ADC with 1MHz of freq, 10-bit of resolution
    ADC_Config (adcdev, 500000, 10);
    
    // Enable all channels
    ADC_EnableCH (adcdev, 0);
    ADC_EnableCH (adcdev, 1);
    ADC_EnableCH (adcdev, 2);
    ADC_EnableCH (adcdev, 3);
    ADC_EnableCH (adcdev, 4);
    ADC_EnableCH (adcdev, 5);
    
    // Enable interrupt for ADC0 channel 5
    ADC_EnableInt (adcdev, 5);
        //ADC_EnableGlobalInt(adcdev);
    
    // Enable ADC interrupt source
    VIC_EnableInt (ADC0_INT);

        // Start ADC conversion
    ADC_Start (adcdev, ADC_START_CONTINUOUS, 0);
}

 

ADC_Config пришлось подправить, ибо частоту я выставляю не через библиотечные функции:

S32 ADC_Config (S32 ADCdev, U32 freq, U32 resolution)
{
    ADC_CFG_T *pADCdev = (ADC_CFG_T *) ADCdev;
    U32 tmp;
    
    if (pADCdev->init == FALSE)
    {
        return _ERROR;
    }
    
    if ((resolution > 10) || (resolution < 3))
    {
        return _ERROR;
    }
    
    if (freq > 4500000)
    {
        return _ERROR;
    }
    
    tmp = SCB_GetPCLK (PCLK_ADC);
    
    //tmp = (tmp / freq) - 1;
        tmp = (30000000 / freq) -1;
    pADCdev->regptr->CR &= ~(ADC_CR_CLKDIV(0xFF));
    pADCdev->regptr->CR |= ADC_CR_CLKDIV(tmp);
    
    pADCdev->regptr->CR &= ~(ADC_CR_BITS(3));
    pADCdev->regptr->CR |= ADC_CR_BITS(resolution);
    
    return _NO_ERROR;
}

 

Обработчик прерывания:

__irq __arm void ADC_ISR (void)
{
        //FIXME For debug 
    U32 regval;//= adccfg.regptr->STAT;
        //FIXME For debug
        volatile U32 ulStatus = AD0STAT;
    volatile U32 dummy;
    U8 i;
    
        regval = ulStatus;
    if (regval & 0x0000FF00)    /* check OVERRUN error first */
    {
        regval = ADC_STAT_CH_OVERRUN_FLAG(regval);
               //regval = AD0STAT;

                // BUGBUG There are 6 channels, not 8        
        for (i = 0; i < 6; i++)
        {
            if (regval & 0x01)
            {
                /* if overrun, just read ADDR to clear */
                dummy = adccfg.regptr->DR[i];
            }
            regval = regval >> 1;
        }
    }
    // Check DONE bit
    else if (regval & ADC_STAT_INT_FLAG)
    {
        regval = ADC_STAT_CH_DONE_FLAG(regval);
         
                // BUGBUG There are 6 channels, not 8
        for (i = 0; i < 6; i++)
        {
            if (regval & 0x01)
            {
                /* if overrun, just read ADDR to clear */
                adccfg.ADCBuff[i] = adccfg.regptr->DR[i];
            }
            regval = regval >> 1;
        }
    }
    
    // call callback function if it is already installed
    if (adccfg.cbs != NULL)
    {
        adccfg.cbs();
    }
    
if ((ulStatus & 0x3f) != 0x3f)
  ulStatus=0;

// Acknowledge interrupt
VIC_Ack();
}

 

Чего хотел добиться:

1. Чтобы работали все 6 каналов.

2. Чтобы работало в Free run в Burst-режиме

3. Чтобы возникало одно прерывание при отработке всех 6 каналов.

4. Прерывание поставлено на 5 канал, потому что он должен отработаться последним:

The first conversion after the start corresponds to the least-significant 1 in the SEL field, then

higher numbered 1 bits (pins) if applicable.

 

В общем, ставлю я два брейка - один на VIC_Ack(), другой - на ulStatus=0, запускаю.

 

Дальше что-то, что я не понимаю.

Если стоит два брейка, то остановка происходит только на VIC_Ack(), и статусный регистр содержит 0x00013F3F, что более или менее соответствует ожиданиям. Не соответствует Overrun, происхождение которого я не понимаю.

 

Но стоит снять брейк с VIC_Ack(), начинает срабатывать брейк на ulStatus=0, при этом ulStatus == 0x00013C3C, т.е. каналы 0 и 1 не отработали.

 

Частоту АЦМ пробовал менять, история повторяется от 4.5МГц до 30кГц.

 

Вопрос традиционный: что я делаю не так?

Заранее спасибо!

Share this post


Link to post
Share on other sites

Ну раз никто не знает, то сам расскажу ;)

 

Во-первых, на моей отладочной плате (SK-MLPC2368) не был заведен Vref, и это раз.

Во-вторых, я передумал вообще смотреть на статус каналов, и теперь в обработчике делаю так:

 U32 regval;//= adccfg.regptr->STAT;
volatile U32 ulStatus = AD0STAT;
volatile U32 dummy;
U8 i;
    
regval = ulStatus;
for(i=0;i<6;i++)
  {
   /* Read ADC value */
   dummy=adccfg.regptr->DR[i];
   /* Cut to 10 bits */
   dummy = ( dummy >>6 ) & 0x3ff;
   /* Add data to accumulator */
   ulADC_RawBuf[i] += dummy;
  }
/* Oversampling counter */
ulCnt++;
if (ulCnt == 128)
  {
   /* Copy data to global buffer */
   for(i=0;i<6;i++)
    {
     /* Copy data to global buffer and divide it by 2 */
     *(ptrADC_Buf+i) = (ulADC_RawBuf[i]>>1);
     /* Reset raw buffer */
     ulADC_RawBuf[i]=0;
    }
   /* Reset oversampling counter */
   ulCnt=0;
  }

Все вполне кошерно за исключением того, что 1023*64 никак не равно 65535, но в моем применении это - всё равно.

Share this post


Link to post
Share on other sites
Причина использования библиотеки - любовь к функциям типа PINSEL_Set, оставшаяся от писания под SAM7S.

Даже помыслить не мог, что для этого требуется какая-то "библиотека" :(

#define PX( x, y )    ( ((unsigned long)((y)&0x3))<<(((x)&0xF)<<1) )

#define PINSEL( x, y, z )     (x) = ( ( (x)&( ~PX( (y), 0x3 ) ) )|PX( (y), (z) ) )

Share this post


Link to post
Share on other sites

В библиотэке оно выглядит так:

void PINSEL_Set ( U32 portnum, U32 pinnum, U32 funcnum)
{
    U32 pinnum_t = pinnum;
    U32 pinselreg_idx = 2 * portnum;
    
    if (pinnum_t >= 16)
    {
        pinnum_t -= 16;
        pinselreg_idx++;
    }
    
    PINSEL->PINSELn[pinselreg_idx] &= ~((U32)(3 << (pinnum_t * 2)));
    PINSEL->PINSELn[pinselreg_idx] |= (U32)(funcnum << (pinnum_t * 2));
}

 

Я занялся чисткой кода, библиотека - библиотекой, но мне оно на дефайнах нравится много больше, поэтому для освоенной части как раз такие дефайны и пишу.

Share this post


Link to post
Share on other sites
В библиотэке оно выглядит так:

Не удивили :( обычный мрачный бред в стиле "я программировать не умею", только почему-то распространяемая публично :(, причем, если я Вас правильно понял, и, что что убивает наповал :( :( - ПРОИЗВОДИТЕЛЕМ!

Share this post


Link to post
Share on other sites
причем, если я Вас правильно понял, и, что что убивает наповал :( :( - ПРОИЗВОДИТЕЛЕМ!

Да, это вполне себе Филипсовская библиотека.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this