CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

SPI DI and External Interrupt by RB0
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Aug 12, 2013 4:12 pm     Reply with quote

Quote:

I think with 12MHz as SPI's SCLK is enough to receive the data in the final
board in the case of 8 MHz as ADC's CLK.

Yes, a 12 MHz instruction cycle frequency (PIC running at 48 MHz) is fast
enough to read the 24 bits using software SPI. But you have to be
careful with the read loop, and make sure it's fast enough. I have done
that in the program shown below.

Here is a sample driver for the ADS1251. I haven't completely tested it,
but it appears to work OK. Note that this code is for the ADS1251, which
is a single-channel A/D chip. It doesn't have any channel select pins.
So it could be modified for the ads1253 or ads1254 by adding lines of
code to select the channel, and also add the required delay time after
changing the channel. It could also work for the signal channel ads1252.
However, I have only tested this program with the ads1251.

Inside the unrolled loop, SCLK high time is only 166 ns. SCLK high time
is not specified in the ads1251 data sheet. But I don't think it's a problem
because SCLK is specified to run at 8 MHz max. In that case, the high
time would be only 62.5 ns. So I think we're OK running it at 166 ns.

Note that this driver does not use an interrupt routine. Instead, it polls
the hardware External interrupt flag in a for() loop.
Code:

#include <18F4550.h>
#fuses HSPLL,NOWDT,NOLVP,PLL5,CPUDIV1,NOPBADEN
#use delay(clock=48M)
#use rs232(baud=9600, UART1, ERRORS)

#define ADS1251_DOUT_DRDY_PIN  PIN_B0 
#define ADS1251_SCLK_PIN       PIN_B1

//-------------------------------------------
#define ADS_CLK_FREQ  8000000
#define ADS_SAMPLE_FREQ  (ADS_CLK_FREQ / (6 * 64))  // A/D samples/second

#define ADS_CONVERSION_CYCLE_PERIOD_IN_USEC  ((384 * 1000000) / ADS_CLK_FREQ)

#define ADS_TIMEOUT_IN_USEC  ADS_CONVERSION_CYCLE_PERIOD_IN_USEC

//------------------------------------------------------
// Utility macros

#define BytePtr(var, offset)   (&(char)var + offset)

#ifdef __PCM__
#define interrupt_enabled(x)  !!(*make8(x,1) & make8(x,0))
#endif
#ifdef __PCH__
#define interrupt_enabled(x)  !!(*(make8(x,1) | 0xF00) & make8(x,0))
#endif

#ifdef __PCB__
#error  interrupt_enabled(x) is not supported for the PCB compiler.
#endif
#ifdef __PCD__
#error  interrupt_enabled(x) is not supported for the PCD compiler.
#endif

//---------------------------------------------
// This routine returns TRUE if we successfully read a 24-bit signed
// integer result from the ads1251.  If the ads1251 didn't output a
// \DRDY pulse within the expected time, then it returns FALSE.
// The A/D result is not valid in that case.  If the result is valid,
// it is returned in the reference parameter. See the code in main()
// which shows how to call the function.       

int8 read_ads1251(signed int32 &result)
{
int8 i;
int8 gie_enabled;

// We can't allow random delays to be inserted into this routine
// by any interrupts that are running (in a larger program). 
// If Global interrupts are enabled, then disable them now.
if(interrupt_enabled(GLOBAL))
  {
   disable_interrupts(GLOBAL);
   gie_enabled = TRUE;   // Note that interrupts were enabled
  }
else
  {
   gie_enabled = FALSE;
  }

ext_int_edge(H_TO_L);  // Interrupt on falling edge of \DRDY pulse
clear_interrupt(INT_EXT);

// The duration of the timeout loop is not critical.
// It just needs to be a little longer than the conversion cycle.
// Therefore, we can set it to the length of the conversion cycle
// in usec (by using one call to delay_us(1) per loop), and the
// additional loop overhead loop overhead execution time doesn't matter.

// Wait for a negative edge on the \DRDY pulse.
for(i = 0; i < ADS_TIMEOUT_IN_USEC; i++)   // 8 ins. cycles/loop
   {
    if(interrupt_active(INT_EXT))
       break;
    delay_us(1);
   }

if(i == ADS_TIMEOUT_IN_USEC)
   return(FALSE);

delay_us(2);  // Wait at least 1.5 usec from \DRDY falling edge

// Then read 24 bits of data from the ads1251.
// This partially unrolled loop takes 29.41 usec at 48 MHz.
// This is easily within the DOUT period of 43.5 usec.
for(i = 0; i < 6; i++)
   {   
    output_low(ADS1251_SCLK_PIN);
    shift_left(&result, 3, input(ADS1251_DOUT_DRDY_PIN));
    output_high(ADS1251_SCLK_PIN);

    output_low(ADS1251_SCLK_PIN);
    shift_left(&result, 3, input(ADS1251_DOUT_DRDY_PIN));
    output_high(ADS1251_SCLK_PIN);

    output_low(ADS1251_SCLK_PIN);
    shift_left(&result, 3, input(ADS1251_DOUT_DRDY_PIN));
    output_high(ADS1251_SCLK_PIN);

    output_low(ADS1251_SCLK_PIN);
    shift_left(&result, 3, input(ADS1251_DOUT_DRDY_PIN));
    output_high(ADS1251_SCLK_PIN);
 }

output_low(ADS1251_SCLK_PIN);

// If Global interrupts were enabled, we can re-enable them now.
if(gie_enabled)
  {
   enable_interrupts(GLOBAL);
  }

// Do the sign extension to 32 bits.
if(bit_test(result, 23))
   *BytePtr(result, 3) = 0xFF;
else
   *BytePtr(result, 3) = 0x00;

return(TRUE);
}

//=======================================
void main()
{
signed int32 adc_value;
int8 status;

output_low(ADS1251_SCLK_PIN);  // Initialize SCLK to idle state

delay_ms(100);  // Wait for PIC's PLL oscillator to stabilize

printf("Start\n\r");


while(1)
  {
   status = read_ads1251(adc_value);

   if(status == TRUE)
      printf("%lx \n\r", adc_value);
   else
      printf("Timed out \n\r");

   delay_ms(1000);
  }

}



There is one issue with the DOUT/DRDY interface with these chips.
The MSB of the 24-bit result is seen on the DOUT pin during the DOUT
period, before you start sending SCLK pulses to read the data. This is
stated in the ads125x data sheets. Since the top bit is the sign bit, if the
input voltage is positive, the sign bit will be 0. This means that the \DRDY
pulse could be aliased by the DOUT level. See the diagram below:
Code:

     |  1.5us  |
     |         |
       
      \DRDY                   DOUT
      pulse                  period
______    ______                                       _______
     |____|    |_______________________________________|
               
     ^         ^
     |         |
   \DRDY      DOUT for positive sign bit
   falling    aliases the falling edge of
   edge       the DRDY pulse              Note:  \DRDY pulse is 750 ns

It's possible that the falling edge of the start of DOUT could be
falsely interpreted as the falling edge of \DRDY. This could happen
if the code happens to clear the INT_EXT interrupt flag just after
the \DRDY falling edge. In 1.5 usec, it will see the falling edge
of the beginning of the DOUT period. The code will detect and
interpret the DOUT falling edge as the start of a read cycle.
But it doesn't matter. That's because the code that reads 24 bits
of data during the DOUT period is fast enough to finish reading
well before the end of the DOUT period. If a small part of the
DOUT period passes before we start reading, there is still plenty
of time left to read all the data.

This discussion is based on an 8 MHz CLK signal for the ads1251
and a PIC running at 48 MHz.

Also, the DOUT period is shown as a constant low level in the diagram.
In fact, when you start issuing SCLK pulses to read the data (after
the falling edge of DOUT), the DOUT signal will become 1's and 0's,
depending on the data from the ads1251. Only the first part of DOUT
will be a low level, if the sign bit is positive.
JosedeJesusC



Joined: 29 Mar 2013
Posts: 24

View user's profile Send private message

PostPosted: Fri Sep 13, 2013 9:03 pm     Reply with quote

Hi PCM programmer !!!

I'm sorry by the late Embarassed I haven't seen your reply till now !!!! The microcontroller 18F26J50 arrived from USA !!! this morning since I ordered it 1 month ago because it was in stock.

Looking at the code OMG!!!! you have taught me a different way to handle interrupts, I never thought in use the EXT INT as a flag to beginning the data transfer, but I've got a doubt. What is the function of the utility macros?

and does the CCS Compiler have the same USB support of this microcontroller?
I've seen into the forum some code examples but the user don't be so sure if the USB is HI or BULK transfer.

Thanks so much for your valuable code, the ADS1251 is close in behavior with the ADS1254, the only difference are the SEL-CH bit but easily the code can be adapted.

Best regards !!!
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Sep 13, 2013 9:49 pm     Reply with quote

The BytePtr macro allows you to insert a byte into a 16 or 32 bit variable.
I use it to do sign-extension. This is shown in the code.

The interrupt_enabled() macro allows you to check if global interrupts
have been enabled in some other part of the program. If it's enabled,
then it is temporarily disabled while 24 bits of data are read from the
ads1251. The ads1251 read operation has to be done in a specified short
amount of time. Interrupts can not be allowed during the read operation
because they would delay the completion of read loop. It must finish quickly.

I don't know anything about USB with this PIC.
Ttelmah



Joined: 11 Mar 2010
Posts: 19549

View user's profile Send private message

PostPosted: Sat Sep 14, 2013 2:24 am     Reply with quote

On the USB, depends on the age of your compiler.

At the top of pic18_usb.h, it lists the chip families supported.

The 26J50, was added around the late 4.0xx versions. Wasn't there in the 4.07x area, but was by the 4.09x area.

They are nice chips, with lots of extras, and enough ROM/RAM to do a lot of things. Seem to have fewer 'bugs' than many others.

Best Wishes
JosedeJesusC



Joined: 29 Mar 2013
Posts: 24

View user's profile Send private message

PostPosted: Wed Oct 02, 2013 1:37 pm     Reply with quote

Hi everybody I've got a trouble, the pic18f26j50 doesn't do anything in the part of the for loop (in the code above), when the interrupt_active(EXT_INT), is executed, the program is stagnated, why?, I've tried with the register INTCON, INTCON2 and RCON to config the interrupt by pin B0, in low priority (compatibility mode with 16Fxx familiy) but nothing.

Best regards
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Oct 02, 2013 2:03 pm     Reply with quote

Quote:
I've tried with the register INTCON, INTCON2 and RCON to config the interrupt

Post your code.

What is your compiler version ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19549

View user's profile Send private message

PostPosted: Thu Oct 03, 2013 4:27 am     Reply with quote

I'd suggest, depending on the compiler version, adding setup_adc(NO_ANALOGS); at the start of the code. The 26J50, is one of the chips that with some compiler versions wakes up with the analogs enabled.
The INT_EXT line will never work if this is the case.

Repeat PCM_programmers statements though:

Post your code.

What is your compiler version ?

Best Wishes
JosedeJesusC



Joined: 29 Mar 2013
Posts: 24

View user's profile Send private message

PostPosted: Sun Oct 06, 2013 6:26 pm     Reply with quote

Hi thanks for your reply here is my code.
my compiler version is PCWHD 4.105
Code:

#include<18f26J50.h>
#fuses HSPLL, NOWDT, NOPROTECT, NODEBUG, PLLDIV1, NOCPUDIV, STVREN, NOXINST
#USE delay(clock = 48000000)

#define INTCON 0XFF2
#bit    GIEH = INTCON.7// set up global all high priority interrupts
#bit    GIEL = INTCON.6// set up global all low priority interrupts
#bit    INT0IE = INTCON.4// anable interrupts by RB0
#bit    INT0F  = INTCON.1// Flag if interrupt ocurred
#byte INTCON2 = 0XFF1
#byte INTCON3 = 0XFF0
#define RCON     0xFD0
#BIT    IPEN = RCON.7

#define ADS1251_DOUT_DRDY_PIN  PIN_B0
#define ADS1251_SCLK_PIN       PIN_B1

//----------------------Define ADS1254 COSNTANTS---------------------
#define ADS_CLK_FREQ  320000
#define ADS_SAMPLE_FREQ  (ADS_CLK_FREQ / (6 * 64))  // A/D samples/second

#define ADS_CONVERSION_CYCLE_PERIOD_IN_USEC  ((384 * 100000) / ADS_CLK_FREQ)

#define ADS_TIMEOUT_IN_USEC  ADS_CONVERSION_CYCLE_PERIOD_IN_USEC

//------------------------------------------------------
// Utility macros

#define BytePtr(var, offset)   (&(char)var + offset)

#ifdef __PCM__
#define interrupt_enabled(x)  !!(*make8(x,1) & make8(x,0))
#endif
#ifdef __PCH__
#define interrupt_enabled(x)  !!(*(make8(x,1) | 0xF00) & make8(x,0))
#endif

#ifdef __PCB__
#error  interrupt_enabled(x) is not supported for the PCB compiler.
#endif
#ifdef __PCD__
#error  interrupt_enabled(x) is not supported for the PCD compiler.
#endif

int8 read_ads1251(signed int32 &result)
{
   int i = 0, gie_enabled;
   output_high(PIN_B7);  // IT TURN ON
   if(interrupt_enabled(GLOBAL))// another way it tried if((GEIH == true) || (GEIL == true))
  {
   disable_interrupts(GLOBAL);
   gie_enabled = TRUE;   
  }
else
  {
   gie_enabled = FALSE;
   output_low(PIN_B7);
  }
 
  ext_int_edge(H_TO_L); 
  clear_interrupt(INT_EXT);

  output_high(PIN_B6); // ALL IS WORKING TILL NOW
for(i = 0; i < ADS_TIMEOUT_IN_USEC; i++)   // 8 ins. cycles/loop
   {
     //if(INTOF == TRUE) another way I tried
    if(interrupt_active(INT_EXT))// <--- here the pic is broken in any way
    {
       break;
       INT0F = FALSE;
       output_high(PIN_B5);
    }
    delay_us(1);
   } // end of for Loop
   
   if(i == ADS_TIMEOUT_IN_USEC)return(FALSE);
   
   delay_us(2);
// -------------- forward never is executed------------------------   
   output_high(PIN_B4);
   
   for(i = 0; i < 6; i++)
   {   
    output_low(ADS1251_SCLK_PIN);
    shift_left(&result, 3, input(ADS1251_DOUT_DRDY_PIN));
    output_high(ADS1251_SCLK_PIN);

    output_low(ADS1251_SCLK_PIN);
    shift_left(&result, 3, input(ADS1251_DOUT_DRDY_PIN));
    output_high(ADS1251_SCLK_PIN);

    output_low(ADS1251_SCLK_PIN);
    shift_left(&result, 3, input(ADS1251_DOUT_DRDY_PIN));
    output_high(ADS1251_SCLK_PIN);

    output_low(ADS1251_SCLK_PIN);
    shift_left(&result, 3, input(ADS1251_DOUT_DRDY_PIN));
    output_high(ADS1251_SCLK_PIN);
 }// end of for loop

output_low(ADS1251_SCLK_PIN);

if(gie_enabled)
  {
   enable_interrupts(GLOBAL);
  }
 
if(bit_test(result, 23))
   *BytePtr(result, 3) = 0xFF;
else
   *BytePtr(result, 3) = 0x00;

return(TRUE);
}//end of ADS1254_READ

void main(void)
{
 int8 status;
 signed int32 adc_value;
 
 INTCON2 = 0X00; // making sure rb3 and RBport  priority interrupts are low
 INTCON3 = 0X00; // making sure rb3, rb2 and rb1 interrupts are disabled and its priority are low
 IPEN    = 0X00; // making sure low priority
 
 output_low(PIN_B3);
 output_low(PIN_B2);
  status = read_ads1251(adc_value);
// -------- never are executed-------------------------
   if(status == TRUE)
  {
    output_high(PIN_B3);
    output_low(PIN_B2);
  }
   else
  {
  output_high(PIN_B2);
  output_low(PIN_B3);
  }
   delay_ms(1000);
}

As you can see, to verify where is the problem, I used LED in PIN_B7, PIN_B6, PIN_B5, PIN_B4, PIN_B3 and PIN_B2.
In the part of the for loop where the PIC have to verify if the interruption PIN_B0 flag is ocurred, the program never pass of this loop, because the leds PIN_B5 to PIN_B2 never are turned on.
In the oscilloscope the PIN_B1 it's in LOW, so the ADS1254 is waiting the SCLK signal, only I can see is the falling edge from the ADC.
I'm going to try that, you told me Ttelmah.

Thanks so much for your reply best regards have a good day Smile
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Oct 06, 2013 9:11 pm     Reply with quote

Quote:
#define ADS_CLK_FREQ 320000

Do you really have only a 320 KHz clock frequency for the ads chip ?

Quote:
#define ADS_CONVERSION_CYCLE_PERIOD_IN_USEC ((384 * 100000) / ADS_CLK_FREQ)

Why did you change this to 100000 ? It was 1000000 in my example
program. The purpose of that equation is to calculate the conversion
cycle period in microseconds. By changing the constant to only 100000
you have messed it up. With the correct constant of 1000000, the
result will be 1200 usec timeout period with your 320 KHz clock.
You have caused it to be 120 usec timeout, which is totally wrong.

Quote:
int i = 0, gie_enabled;

Also, for the correct timeout value of 1200, 'i' must be declared as an int16.
JosedeJesusC



Joined: 29 Mar 2013
Posts: 24

View user's profile Send private message

PostPosted: Sun Oct 06, 2013 10:56 pm     Reply with quote

OMG!!!! PCM programmer Embarassed I'm so sorry. I'm wrong.

Integer of 8 bits just can represent till 255 :S.

I can achieve 8 MHz but It's just a trial, It seems the PIC is working now adding that instruction setup_adc(NO_ANALOGS); because it's lighting up the PIN_B4 and its after the loop where was the problem.

I'm going to check this out with the Oscilloscope I hope it's Ok.

Thanks so much for your time PCM Programer and Ttelmah. Best wishes Smile
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group