500 kbps Sampling ADC on CAN channel

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

500 kbps Sampling ADC on CAN channel

5,344 Views
Andre_b
Contributor II

Hi

I'm a beginner on S32K144, so I apologize in advance if I will say something totally wrong.

I need to sampling CAN High and CAN Low by ADC0 and ADC1, but I have to be sure that my ADCs sample at least 1 bit per sample when the bitrate on my CAN channel is 500 kbps. I have been read how to configure high sample rate on ADC from https://community.nxp.com/t5/S32K/ADC-High-Sampling-Rate-and-Graph/m-p/871689 but with these parameters I can't achieve the target.

I'd like to know if is possible achieve this sample performance on S32K144.

I attached the ADC.c, FlexCAN.c and clocks_and_modes.h used and below the Matlab graph obtained using 500kbps with 110 samples.

 

voltage_can_500kbps.png

I did a test(below) with CAN 100kbps with others ADC.c, FlexCAN.c settings and this is the result (110 sample) what I'd like to reach, one sample per bit.

voltage_can_100kbps.png

Kindly guide me or small example code will be helpful. Many thanks

Andrea

 

0 Kudos
12 Replies

4,670 Views
Andre_b
Contributor II

Hi PetrS

At the end, I implemented the sampling HW with PDB in back-to-back mode but I notice that it would be perfect if I use DMA. I found your example here:

 https://community.nxp.com/t5/S32K-Knowledge-Base/Example-S32K144-PDB-ADC-trigger-DMA-ISR-S32DS/ta-p/...

This example is implemented for 4 channels, but I didn't understand how to use it just for 2 channels. I tried just to reduce the result[4] to result[2] and set ADC with just to channels but, after that, it doesn't work.

Could you explain me how to do it?

I attached your example with some changes. It works really good just for the first channel (3 in my case) but dosen't work for the other one

Thank you for your help

Andrea

 

0 Kudos

4,640 Views
PetrS
NXP TechSupport
NXP TechSupport

Hi,

you should do changes in ADC , PDB and DMA init functions. You did it in ADC and PDB, for DMA you should change below lines to transfer just 2 results.

DMA->TCD[0].SLAST = DMA_TCD_SLAST_SLAST(-8); // Last Source Address Adjustment is -4*2 = 8

DMA->TCD[0].CITER.ELINKNO = DMA_TCD_CITER_ELINKNO_CITER(2) // Current Major Iteration Count is 2

DMA->TCD[0].DLASTSGA = DMA_TCD_DLASTSGA_DLASTSGA(-8); // Destination last address adjustment is -8

DMA->TCD[0].BITER.ELINKNO = DMA_TCD_BITER_ELINKNO_BITER(2) | // Starting major iteration count is 2 

 

BR, Petr

0 Kudos

5,334 Views
PetrS
NXP TechSupport
NXP TechSupport

Hi,

 

a minimum conversion time is around 1us, refer to DS for specification.
To have 1 sample per bit at 500kbps CAN rate you have to sample each 2us, this is possible. Ideally you should sample 2 times faster, but as above it is on ADC limits. 
Also instead of SW start of single ADC conversion you have, it would be better to use HW trigger, eg. using PBD and trigger ADC at given rate according to CAN bit time. Or at least set continuous mode on ADC and set conversion time for 2us. Using DMA to read results will be ideal for higher rates.

You can refer to S32k144_Project_PDB example within S32DS, where ADC is HW triggered by PDB, which is set to continuous mode with SW trigger. You should be able to modify it for conversion rate you need.

 

BR, Petr

0 Kudos

5,116 Views
Andre_b
Contributor II

Hi PetrS

I finally found a way to sample 2 channels (CANL and  CANH) by tuning PDB in back-to-back mode with ADC0 setting on two channels. The result is quite good but I have a doubt: I need to sample at the same time from CANL and CANH but if I use back-to-back mode the sampling is in sequence I suppose. Would it be better using two ADC to guarantee the simultaneity for those two channels?

Thank you for your help

Andrea

0 Kudos

5,113 Views
PetrS
NXP TechSupport
NXP TechSupport

Hi,

yes, using single PDB-ADC triggering scheme you will do sampling in sequence.

Thus 2 ADCs should be used to get parallel conversion

PetrS_0-1613130655073.png

BR, Petr

0 Kudos

5,238 Views
Andre_b
Contributor II

Hi PetrS

I achieved the rate sampling for my purpose but when I try to use printf() for reading my voltage result the code stops working. I need to stop the sampling after 110 samples and do others calculus with voltage results. At the end, I'd like to start again with an other sampling and so on...

Is it possible? Do you know how to do it?

I really need some feedbacks

Thank you for your help

0 Kudos

5,323 Views
Andre_b
Contributor II

Thank you for your reply.

I tried to modify these values in the example that you suggested me for my purpose:

ADC.c:

PCC->PCCn[PCC_ADC0_INDEX] |= PCC_PCCn_PCS(6);

ADC0->CFG2 = ADC_CFG2_SMPLTS(12);

ADC_CFG1_ADIV(4);

PDB.c:

PDB0->MOD = 400;
PDB0->IDLY = 395; 

PDB_SC_PRESCALER(0) 
PDB_SC_MULT(0)
PDB0->CH[0].DLY[0] = 1;

clock_and_modes.c

void SPLL_init_200MHz(void)
{

SCG->SPLLDIV |= SCG_SPLLDIV_SPLLDIV1(2)| 
SCG_SPLLDIV_SPLLDIV2(3); 

SCG->SPLLCFG = SCG_SPLLCFG_MULT(34);
SCG->SPLLCFG = SCG_SPLLCFG_PREDIV(1)
}

In that way I think to have set SPLL 200MHz, divided by 4 for ADC input clock for 50MHz (max ADC speed), and set the PDB parameters for 500kHz.

With leds example, it works but I can't read the voltage data. I need to start saving the voltage values when the first dominant bit is detected. I modified the example code like below:

void ADC0_IRQHandler(void)
{

     while(((ADC0->SC1[0] & ADC_SC1_COCO_MASK)>>ADC_SC1_COCO_SHIFT) == 0){};

     if (ADC0->R[0] < 1638){

     for (int i=0; i<110; i++)
     {
        vectors.Buffer_Low[i] = ADC0->R[0];

        while(((ADC0->SC1[0] & ADC_SC1_COCO_MASK)>>ADC_SC1_COCO_SHIFT) == 0){};
     }

I supposed that the for cycle dosen't work.

Is there that I'm mistaking? 

Thank you for your help.

Tags (1)
0 Kudos

5,235 Views
PetrS
NXP TechSupport
NXP TechSupport

Hi,

 

I think the interrupt code could work. Only you will leave interrupt function after vectors.Buffer_Low is filled. So it could be better to do not interrupt it by higher priority interrupt to do not miss sample eventually get error in PDB. I think DMA implementation to read results will be ideal.
To stop sampling just disable PDB using PDBEN bit, to start it again set PDBEN and do SW trigger.

 

BR, Petr 

0 Kudos

5,229 Views
Andre_b
Contributor II

I didn't implement DMA yet but I'm working on it. I tried to insert  PDB0->SC |= PDB_SC_PDBEN(0); in Interrupt and then I inserted  PDB0->SC |= PDB_SC_PDBEN(1)| PDB_SC_SWTRIG_MASK;; at the end of the for(;;) main cycle. I can stop sampling but it doesn't start again.

Thank you for your help. I really appreciate it.

Andrea

0 Kudos

5,205 Views
PetrS
NXP TechSupport
NXP TechSupport

Hi,

PDB0->SC |= PDB_SC_PDBEN(0) does not clear PDBEN, it should be rather PDB0->SC &= ~PDB_SC_PDBEN_MASK

Also you can try to clear CONT bit instead of disabling PDB and wait for conversion complete. Then set CONT and do SW trig again.

Check if you do not have sequence error generated.

 

BR, Petr 

0 Kudos

5,187 Views
Andre_b
Contributor II

Hi PetrS

I achieved my sampling target with just one channel. I need to sample the CANH channel too. Is it possible to set one PDB for hardware-triggering 2 ADC? 

I set ADC0 an ADC1 with the same parameters and I add in PDB code this lines:

PDB0->CH[0].C1|=1|PDB_C1_TOS_MASK;

PDB0->CH[1].C1|=1|PDB_C1_TOS_MASK;

PDB0->CH[0].DLY[0] = 1;

PDB0->CH[1].DLY[0] = 205;

I tried this but the second ADC doesn't work. I think I couldn't trigger it. Is it strictly necessary using the back-to-back mode to do it? Have I to set an other interrupt?

Refer to the S32k144_Project_PDB, I don't know how to add an other channel for sampling.

I really thank you so much

Andrea

0 Kudos

5,198 Views
Andre_b
Contributor II

The code has stopped while(((ADC0->SC1[0] &           ADC_SC1_COCO_MASK)>>ADC_SC1_COCO_SHIFT) == 0){};
        }. It works until first set of data. It seems like ADC0->SC1[0] register doesn't give a complete conversion after the first.

void ADC0_IRQHandler(void)
{

   if(ADC0->SC1[0] & ADC_SC1_COCO_MASK)
   {

      if (ADC0->R[0] < 1638 && ADC0->R[0]>0){

         for (i=0; i<120; i++)
        {
          vectors.Buffer_Low[i] = ADC0->R[0];

        while(((ADC0->SC1[0] &           ADC_SC1_COCO_MASK)>>ADC_SC1_COCO_SHIFT) == 0){};
        }

     PDB0->SC &= ~PDB_SC_CONT_MASK;
     recived = true;

   }
}

int main(void)
{

  for(;;)
  {

   if(recived){
                   recived = false;

                   for (k = 0; k<120; k++)
                   {
                   vectors.Buffer_Low_result[k]=(5000*vectors.Buffer_Low[k]/4095);
                   printf(" %lu \n", vectors.Buffer_Low[k]);
                   }

         PDB0->SC |= PDB_SC_CONT_MASK|
         PDB_SC_SWTRIG_MASK;
         }

  }

}

Maybe it is necessary to clean ADC registers but I can't figure out which ones. 

Really thank you for your help.

Andrea

0 Kudos