Good evening,
I'm trying to implement a simple 1 channel ADC reader on a dspic33FJ128MC802, that manually starts sampling data, automatically converts when the sampling is done, and reads and stores data.
This has never been an issue for me, except with this microcontroller, which doesn't seem to have a normal ADC implemented,
I've read through the datasheet section on ADC several times, and I've configured it to my best ability, however the ADC1BUF0 value keeps jumping around inconsistently, between 0 and 4096, when I have a Lab power supply connected directly to the input pins of the ADC.
What I see is the ADC1BUF0 value seems to roughly correspond to the input voltage (0-3.3V), when I pause the debugger, it gives a couple (2-4) readings that are within range +-100 (out of 4096 is not bad).
Then if I continue to run and pause, with the voltage kept the same, the values stored in the buffer suddenly begin to jump +- 500, sometimes even showing 4095 (all 1's) and 0.
Then when I change the lab PSU to a different voltage, it seems to repeat the process of showing me a few correct values, then start to jump around again.
So essentially, it will show me a correct value about 1/2 of the times I pause the debugger.
I don't know what is causing this, I know that I need to run the debugger after changing voltage so that it can clear out the buffers, but something about this microcontroller seems definitely wrong.
Please let me know what can be done to fix this,
The compiler is XC16, IDE is Mplab 8.92
Thanks,
Below is my configuration:
[code]
void InitADC() {
TRISAbits.TRISA0=1;
AD1CON1bits.FORM = 0; // Data Output Format: integer//Signed Fraction (Q15 format)
AD1CON1bits.SSRC = 7; // Interan Counter (SAMC) ends sampling and starts convertion
AD1CON1bits.ASAM = 0; // ADC Sample Control: Sampling begins immediately after conversion
AD1CON1bits.AD12B = 1; // 12-bit ADC operation
AD1CON1bits.SIMSAM =1; // 10-bit ADC operation
AD1CON2bits.CHPS = 0; // Converts CH0
AD1CON2bits.CSCNA = 0; // Do not scan inputs
AD1CON2bits.VCFG = 0; // Use voltage reference Vss/Vdd
AD1CON2bits.ALTS = 0; // Always use input select for channel A
AD1CON2bits.BUFM = 0; // Always start filling at buffer 0
AD1CON3bits.ADRC = 0; // ADC Clock is derived from Systems Clock
AD1CON3bits.SAMC = 0; // Auto Sample Time = 0*Tad
AD1CON3bits.ADCS = 2; // ADC Conversion Clock Tad=Tcy*(ADCS+1)= (1/40M)*3 = 75ns (13.3Mhz)
// ADC Conversion Time for 10-bit Tc=12*Tab = 900ns (1.1MHz)
AD1CON1bits.ADDMABM = 1; // DMA buffers are built in conversion order mode
AD1CON2bits.SMPI = 0; // SMPI must be 0
AD1CON4bits.DMABL = 0; // Only 1 DMA buffer for each analog input
//AD1CHS0/AD1CHS123: A/D Input Select Register
AD1CHS0bits.CH0SA = 0; // MUXA +ve input selection (AIN0) for CH0
AD1CHS0bits.CH0NA = 0; // MUXA -ve input selection (Vref-) for CH0
AD1CHS123bits.CH123SA = 0; // MUXA +ve input selection (AIN0) for CH1
AD1CHS123bits.CH123NA = 0; // MUXA -ve input selection (Vref-) for CH1
IFS0bits.AD1IF = 0; // Clear the A/D interrupt flag bit
IEC0bits.AD1IE = 0; // Do Not Enable A/D interrupt
AD1CSSL = 1; //Scan from AN0 only
AD1PCFGL = 0b111111110; //Only AN0 in analog input mode
AD1CON1bits.ADON = 1; // Turn on the A/D converter
}
int main() {
ADPCFG = 0xFFFE; //make ADC pins all digital except AN0 (RA0)
while(1)
{
AD1CON1bits.SAMP = 1;
while(!AD1CON1bits.DONE);
myVoltage = ADC1BUF0;
}
return 0;
}
[/code]
Seems like I missed a semicolon after while(!AD1CON1bits.DONE)
Without the semicolon it did not wait for the conversion to complete.
I corrected this in the original post, in case someone wants to use the source in this post
Thank you,
Related
I have a program written in C++ on a Raspberry Pi 3. I mmap /dev/gpiomem to access the GPIO registers directly in user mode. Here is my function to write to output pins:
static uint32_t volatile *gpiopage; // initialized by mmap() of /dev/gpiomem
static uint32_t lastretries = 0;
void PhysLib::writegpio (uint32_t value)
{
uint32_t mask = 0xFFF000; // [11:00] input pins
// [23:12] output pins
// all the pins go through an inverter converting the 3.3V to 5V
gpiopage[GPIO_CLR0] = value;
gpiopage[GPIO_SET0] = ~ value;
// sometimes takes 1 or 2 retries to make sure signal gets out
uint32_t retries = 0;
while (true) {
uint32_t readback = ~ gpiopage[GPIO_LEV0];
uint32_t diff = (readback ^ value) & mask;
if (diff == 0) break;
if (++ retries > 1000) {
fprintf (stderr, "PhysLib::writegpio: wrote %08X mask %08X, readback %08X diff %08X\n",
value, mask, readback, diff);
abort ();
}
}
if (lastretries < retries) {
lastretries = retries;
printf ("PhysLib::writegpio: retries %u\n", retries);
}
}
Apparently there is some internal cache or something delaying the actual updating of the pins. So I'm wondering if there is some magic MRC or MCR or whatever that I can put so I don't have to read the pins to wait for the update to actually occur.
I'm quite sure this is happening because this code is part of a loop:
while (true) {
writegpio (0x800000); // set gpio pin 23
software timing loop for 1uS
writegpio (0); // clear gpio pin 23
software timing loop for 1uS
}
Sometimes Linux timeslices during the software timing loops on me and I get a delay longer than 1uS, which is ok for this project. Before I put in the code that loops until it reads the updated bit, sometimes the voltage on the pin stays high for longer than 1uS and is then low for correspondingly less than 1uS, or vice versa, implying that there is a total of 2uS delay for the two timing loops but the update of the actual pin is being delayed by the hardware. After inserting the corrective code, I always get at least 1uS of high voltage and 1uS of low voltage each time through the loop.
I am using a PIC24 to read data using 3 analog inputs but am only getting 1 to show the right result. I looked everywhere on the internet and am still not able to get the code to work.
I am trying to read 3 analog input signals and am only able to read in AN0.
I am using an accelerometer to obtain the data and show it on the LCD screen for now. I was able to implement 3 different ways to take the data and display it but only an0 works and an1 is not the right value.
void InitADC(int amask) {
AD1PCFG = 0xFFF8; // select AN0, AN1, AN2 as analog inputs
AD1CON1 = 0x00E0; // auto convert # end of sampling, Integer Data out.
// see Text pg. 179 & Sec. 17 on AD1CON1.
//AD1CON2bits.CSCNA = 1;
AD1CON3 = 0x1F01; // Tad = 2xTcy = 125ns. 31*Tad for conversion time.
//AD1CSSL = 0xFFF7; // Scan 3 channels
AD1CON1bits.ADON = 1; // Turn on the ADC
} // InitADC
main() {
InitADC(0xFFF8); // initialize the ADC and analog inputs
char x_string [12];
char y_string [12];
char z_string [12];
//TRISB = 1; // all PORTB pins as outputs
TRISBbits.TRISB0 = 1;
TRISBbits.TRISB1 = 1;
TRISBbits.TRISB2 = 1;
InitPMP(); // Initialize the Parallel Master Port
InitLCD(); // Initialize the LCD
float x_val;
float y_val;
float z_val;
float x_axis, y_axis, z_axis;
while (1) // main loop
{
x_axis= SelectPort(0);
x_val= ((((x_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(x_string, "X: %0.2f ", x_val);
ms_delay(2.5);
y_axis= SelectPort(1);
y_val= ((((y_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(y_string, "Y: %0.2f ", y_val);
ms_delay(2.5);
z_axis= SelectPort(2);
z_val= ((((z_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(z_string, "Z: %0.2f ", z_val);
ms_delay(2.5);
}
Here is the code where the data is read:
int SelectPort(int ch)
{
//int *result;
AD1CON1bits.ADON = 0; // Turn off the ADC to reconfigure
//result = &ADC1BUF0;
switch(ch) // set values based on the channel to use
{
case 0: // select AN0 as analog input
//AD1CHSbits.CH0SA=0;
//result = ADC1BUF0;
AD1PCFG = 0xFFFE;
break;
case 1:
//AD1CHSbits.CH0SA=1;
//result = ADC1BUF1;
AD1PCFG = 0xFFFD; // select AN1 as analog input
break;
case 2:
//AD1CHSbits.CH0SA=2;
AD1PCFG = 0xFFFB; // select AN2 as analog input
break;
// there's only so many options here, so there's not really a default case
}
AD1CON1bits.ADON = 1; // Turn on the ADC
AD1CHS = ch; // 1. select analog input channel
AD1CON1bits.SAMP = 1; // 2. Start sampling.
while (!AD1CON1bits.DONE); //5. wait for conversion to complete
AD1CON1bits.DONE = 0; // 6. clear flag. We are responsible see text.
return ADC1BUF0; // 7. read the conversion results
}
I am new to PIC24 and need help to figure out why I am not able to get multiple ADC channels to read the data.
You should always give some time for the ADC input voltage to settle after changing its input port. Also, make sure the impedance of your inputs signals are less than 10K.
Separating the ADC channel switching and reading its value will help, and give you an opportunity to add a delay.
I could not find AD1PCFG in the PIC24 datasheet. AD1PCFG is a PIC32 register... PIC24 uses ANSx and TRSx to set pins as analog inputs. Analog inputs should be set once, during the boot sequence. Feeding an analog signal to a digital input CMOS pin will result in increased power draw, and can even lead to hardware failure!
// Set the pins as analog inputs once and for all in your setup code.
void InitADC()
{
// select RB0/AN0, RB1/AN1, RB2/AN2 as analog inputs
TRISB |= 0x07;
ANSB |= 0x07;
// ...
}
void ADC_SelectInput(int ch)
{
AD1CHS = ch & 0x0F
}
int ADC_Read()
{
AD1CON1bits.SAMP = 1;
while (!AD1CON1bits.DONE)
;
AD1CON1bits.DONE = 0;
return ADC1BUF0;
}
Your loop then becomes:
// ...
int adc_in;
while (1)
{
ADC_SelectInput(0);
ms_delay(3); // settling delay, you could also do something
// else in the meantime instead of waiting.
// NOTE: the argument to ms_delay() is an integer
// so waiting at least 2.5 ms makes it 3.
// when computing floats, use float constants, not double.
x_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(x_string, "X: %0.2f ", x_val);
ADC_SelectInput(1);
ms_delay(3);
y_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(y_string, "Y: %0.2f ", y_val);
ADC_SelectInput(2);
ms_delay(3);
z_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(z_string, "Z: %0.2f ", z_val);
}
So I'm creating a code so that I can read and store temperature values
for at least 8hours and store it in the Arduino EEPROM. I also want the Built in LED to flash once every second, while the temperature sensor records once every minute. I wrote the following but I'm left with the LED staying on for a whole minute then off for another minute and so on. I want it to keep constantly flashing. I know it's because of my delay(6000) but I don't know how to fix it. Any help is appreciated, thanks!
#include <EEPROM.h> //LibrerÃa para controlar la EEPROM de la Arduino
float tempC; //Initialize variable for storing Temperature
int tempPin = 0; //Conected at A0
int addr = 0; //Cantidad de espacios (bytes) iniciales
int count = 0; //counter needed to stop at a certain point before overflowing the EEPROM memory
int Voltages[501]; // Array with space for 501 data Points.A little over 8 hours. Holding integer values of Voltages.
float Temps[501]; //Holds float values for Temperatures
const int SWITCH = 8; //set switch to port 8
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // blink once per second
void setup(){
Serial.begin(115200);
pinMode(LED_BUILTIN,OUTPUT);
pinMode(SWITCH,INPUT_PULLUP);}
void loop(){
int i;
if (digitalRead(SWITCH)==HIGH & count<=501){
for (int i=0;i<EEPROM.length();i++){
EEPROM.write(i,0);} // Clears the EEPROM so that only new recorded values are shown. In case of terminating before 8h
Serial.println("-------Recording-------");
unsigned long currentMillis = millis(); // current time
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // save the last time the LED blinked
if (ledState == LOW) {// if the LED is off turn it on and vice-versa:
ledState = HIGH;} else {
ledState = LOW;}
digitalWrite(LED_BUILTIN, ledState);}
for (i=0; i<501; i++){
count = count+1;
int v = analogRead(tempPin); // reads voltage
Voltages[i] = v;
Serial.println(i);
Serial.println(Voltages[i]);
delay(60000);
EEPROM.put(addr, Voltages[i]); //stores voltages in EEPROM
addr= addr +2;}}
I think the solution you are looking for involves the use of timer interrupts. That would execute an interrupt service routine (might as well be a blinking led) regardless of what happens in the rest of your loop function. This might give you a better in-sight: Arduino timer interrupts
I can't get my pic24f04kl100 to turn on an LED at all. The below code is as simple as possible and it still doesn't turn on the LED on pin 6.
Code
#include <xc.h>
#define LED LATBbits.LATB4
#define LEDans ANSBbits.ANSB4
#define LEDtris TRISBbits.TRISB4
/* Setting up configuration bits */
_FOSCSEL(FNOSC_FRCPLL & IESO_OFF); // FRC w/PLL and int./ext. switch disabled
_FOSC(POSCMD_XT & FCKSM_CSECMD); // Pri. OSC XT mode and clk. switch on, fail-safe off
_FWDT(FWDTEN_OFF); // Watchdog timer off
void initialise();
void delay(int i);
void main() { // Main program loop
initialise(); // Intialise PIC
while (1) { // Infinite loop
LED = 1; // Set LED high
LED = 0; // Set LED low
}
}
void initialise() { // Configures the PIC
OSCCONbits.NOSC = 0b111; // Fast RC Oscillator with Postscaler and PLL module
delay(100);
CLKDIVbits.RCDIV = 0b000; // Set clock div 1:1
delay(100);
LEDans = 0;
delay(100);
LEDtris = 0; // Make LED an output
delay(100);
LED = 0; // Set LED low
}
void delay(int i) {
while(i--);
}
PICkit 3 Output
*****************************************************
Connecting to MPLAB PICkit 3...
Firmware Suite Version.....01.27.04
Firmware type..............dsPIC33F/24F/24H
Target detected
Device ID Revision = 0
The following memory area(s) will be programmed:
program memory: start address = 0x0, end address = 0x3ff
configuration memory
Programming...
Programming/Verify complete
By default B4 pin is analog. Configure it as digital by clearing the ANSB register, bit4
NOTE: Although clearing the bit DID NOT fix the problem. Moving to another pin (with less features) did. So I (fossum) made the assumption that this was at least on some level the correct answer.
The LED is blinking, but it's blinking very fast, try to put some delay between LED turn on and LED turn off.
Try this:
void main() { // Main program loop
initialise(); // Intialise PIC
while (1) { // Infinite loop
LED = 1; // Set LED high
delay(50000); //wait LED on time
LED = 0; // Set LED low
delay(50000); //wait LED off time
}
}
My code is based on this example and a stop function is already implemented, there are functions to uninitialize and stop the audio unit:
AudioOutputUnitStop(toneUnit);
AudioUnitUninitialize(toneUnit);
AudioComponentInstanceDispose(toneUnit);
toneUnit = nil;
In the example I linked to a pause function is not necessary since there's only one frequency being played so there's no difference between pause and stop. In my implementation however, I'm playing a range of different frequencies and I want to be able to pause playback.
Any ideas how this is done?
fade out (~10ms) the AUs' output, feed the output silence after the fade
remember the position you stopped reading your input signal
reset the AUs before you resume
resume from the position you recorded above (here, a fade in of the input signal to the AUs would be also be good)
You can try writing 0 to the buffer when you playback. It is effectively a pause.
Here's some code that you can use within the playback callback.
NSLog(#"Playing silence!");
for (int i = 0 ; i < ioData->mNumberBuffers; i++){
//get the buffer to be filled
AudioBuffer buffer = ioData->mBuffers[i];
UInt32 *frameBuffer = buffer.mData;
//loop through the buffer and fill the frames
for (int j = 0; j < inNumberFrames; j++){
frameBuffer[j] = 0;
}
}