View previous topic :: View next topic |
Author |
Message |
tripper269
Joined: 06 May 2013 Posts: 33 Location: Toronto
|
dsPIC33 ADCON1 FORM 00 |
Posted: Fri Sep 01, 2017 8:25 am |
|
|
Hi All,
I am using dsPIC33FJ256GP710A with CCS v5.074. I am trying to setup ADC FORM as integer. I looked into the manual and in header file to set FORM as integer but couldn't find it. If anybody knows please let me know.
Code: | void main()
{
unsigned int16 x = 0;
pwm_set_duty_percent(250);
setup_adc_ports(sAN5, VSS_VDD);
setup_adc(ADC_CLOCK_INTERNAL);
set_adc_channel (5);
while(1)
{
x= read_adc();
if(x > 500)
output_high(GREEN_LED);
else
output_low(GREEN_LED);
}
} |
|
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Fri Sep 01, 2017 8:45 am |
|
|
I'm sorry, but you are very unclear about what you mean....
The value from the ADC is always an integer.
How big, is controlled by the device statement (#device ADC=12).
There is nothing in the chip called 'ADC FORM', nor in CCS.
Look at the examples using the ADC.
You mean the data format I suspect....
It always defaults to unsigned, _unless you select a second -ve channel_. |
|
![](templates/subSilver/images/spacer.gif) |
tripper269
Joined: 06 May 2013 Posts: 33 Location: Toronto
|
ADC FORM |
Posted: Fri Sep 01, 2017 8:51 am |
|
|
ADxCON1 bits 9:8 called FORM, which sets the data format into 16 bits signed fractional, fractional, signed integer and integer. Its on dsPIC33FJxxxGPx06A/08A/10A manual page 241. In CCS debug mode, you can see FORM as well. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Fri Sep 01, 2017 9:18 am |
|
|
The description of them though is the format bits.
You can only have a signed output, when you are running the chip with differential inputs:
Code: |
#include "33FJ256GP710a.h"
#device ADC=12
//Then suitable clocks etc.
void main()
{
unsigned int16 x = 0;
setup_adc_ports(sAN5, VSS_VDD);
setup_adc(ADC_CLOCK_INTERNAL | ADC_TAD_MUL_4);
set_adc_channel (5);
while(TRUE)
{
x= read_adc();
if(x > 500)
output_high(PIN_G0);
else
output_low(PIN_G0);
}
}
|
Since you are acquiring 'without pause', you need some acquisition time programmed. If you then look at the listing:
Code: |
0022A: MOV #84E0,W4
0022C: MOV W4,320
|
Which is selecting 12bit, unsigned 1000 0100 in byte 321.
#device ADC=signed
With a second channel selected, sets the chip up to deliver a signed result |
|
![](templates/subSilver/images/spacer.gif) |
tripper269
Joined: 06 May 2013 Posts: 33 Location: Toronto
|
|
Posted: Tue Sep 05, 2017 7:46 am |
|
|
Thanks Ttelmah,
You are right, i need a fixed sampling rate. I am trying to use timer with ADC to get fixed sampling rate. I know how to configure timer interrupt, for example to blink a LED. But how to configure it with ADC, i am still confused. Should I turn on the ADC in timer interrupt or once its configured, it will automatically start sampling at fixed intervals ?? |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Tue Sep 05, 2017 8:03 am |
|
|
You are using the wrong timer....
The ADC on your chip can be programmed to start automatically triggered by a timer. Timer5 or Timer3 for ADC1.
What you do is program the timer, setup the ADC to automatically start when the timer tells it to, and then enable INT_ADC1. When you receive this interrupt, you use read_adc(ADC_READ_ONLY), which just reads the result from the conversion that has already been done (so is very quick), and exit the interrupt. When the timer next triggers the same happens again.
Code: |
#byte ADCON1L=getenv("SFR:ADCON1")
#define TRIGGER_FROM_TIMER5 0x80
#define TRIGGER_FROM_TIMER3 0x40
//setup the ADC for the conversion etc. as required.
//Then TIMER5 for the interval required
//Then:
ADCON1L = (ADCON1L & 0x1F) | TRIGGER_FROM_TIMER5;
//Will set the ADC up to automatically start when Timer5 resets
|
Alternatively setup timer3, and select this as the trigger source. |
|
![](templates/subSilver/images/spacer.gif) |
tripper269
Joined: 06 May 2013 Posts: 33 Location: Toronto
|
|
Posted: Tue Sep 05, 2017 3:06 pm |
|
|
I think its #word ADCON1L=getenv("SFR:ADCON1"), not #byte. Also it changes the ADCON1 to 10000010000000. We can do the work around, but what is the use of CCS then. In header file, there are only limited options. Is there any way to configure ADC with timer using CCS inbuilt functions? |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Wed Sep 06, 2017 12:45 am |
|
|
No. You can use #byte.
I only want to update the low byte of the word. Using #byte forces this to happen. If you use #word, you have to expand the mask to cover the high 8bits or it changes things you don't want...
Synchronous sampling, is more commonly done using DMA. CCS have options to set this up, but it is a more complex way to do the simple operation you describe.
They will add the define for you, if you ask CCS, but since it only involves a couple of lines as I show, it is as easy to do it yourself.
It has to be AD1CON1, but works with this change as posted. I was copying from a chip that only has a single ADC. |
|
![](templates/subSilver/images/spacer.gif) |
tripper269
Joined: 06 May 2013 Posts: 33 Location: Toronto
|
|
Posted: Wed Sep 06, 2017 9:33 am |
|
|
Thanks Ttelmah,
You are right. It's setting up all the registers correctly. Below is my code, and there is still something i am doing wrong. Your comments will be much appreciated.
Code: |
#include <33FJ256GP710A.h>
#device ADC=12
#fuses HS,WDT
#use delay(clock = 8000000)
#byte ADCON1L=getenv("SFR:AD1CON1")
#define TRIGGER_FROM_TIMER5 0x80
#define TRIGGER_FROM_TIMER3 0x40
#define GREEN_LED PIN_A0
unsigned int16 x = 0;
#int_adc1
void adc1_isr()
{
x = read_adc(ADC_READ_ONLY);
}
void main()
{
setup_timer2(TMR_INTERNAL | TMR_DIV_BY_256, 1562);
setup_compare(1, COMPARE_PWM | COMPARE_TIMER2);
setup_adc_ports(sAN5, VSS_VDD);
setup_adc(ADC_CLOCK_DIV_8);
ADCON1L = (ADCON1L & 0x1F) | TRIGGER_FROM_TIMER5;
set_adc_channel (5);
setup_timer5(TMR_INTERNAL | TMR_DIV_BY_1,15625);
enable_interrupts(INT_ADC1);
enable_interrupts(INTR_GLOBAL);
set_timer5(0);
while(1)
{
//x= read_adc();
set_pwm_duty(1,x );
if(x > 500)
output_high(GREEN_LED);
else
output_low(GREEN_LED);
}
} |
|
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Thu Sep 07, 2017 1:18 am |
|
|
Try either adding a delay to the loop, or setting a flag in the interrupt routine, and only changing things when this is set. So:
Code: |
unsigned int16 x = 0;
int1 have_reading=FALSE;
#int_adc1
void adc1_isr()
{
x = read_adc(ADC_READ_ONLY);
have_reading=TRUE;
}
//then in the main
while(1)
{
if (have_reading)
{
have_reading=FALSE;
set_pwm_duty(1,x );
if(x > 500)
output_high(GREEN_LED);
else
output_low(GREEN_LED);
}
}
|
Otherwise you are continuously updating the PWM duty with the same value. |
|
![](templates/subSilver/images/spacer.gif) |
|