|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Aug 12, 2013 4:12 pm |
|
|
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
|
|
Posted: Fri Sep 13, 2013 9:03 pm |
|
|
Hi PCM programmer !!!
I'm sorry by the late 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
|
|
Posted: Fri Sep 13, 2013 9:49 pm |
|
|
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
|
|
Posted: Sat Sep 14, 2013 2:24 am |
|
|
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
|
|
Posted: Wed Oct 02, 2013 1:37 pm |
|
|
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
|
|
Posted: Wed Oct 02, 2013 2:03 pm |
|
|
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
|
|
Posted: Thu Oct 03, 2013 4:27 am |
|
|
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
|
|
Posted: Sun Oct 06, 2013 6:26 pm |
|
|
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 |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 06, 2013 9:11 pm |
|
|
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
|
|
Posted: Sun Oct 06, 2013 10:56 pm |
|
|
OMG!!!! PCM programmer 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 |
|
|
|
|
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
|