PIC 16F628A clears its registers? - pic

System is basic but I have terrible problem and I can not solve it pls help me. When my system works PIC keep running but clear the registers 4-5 times in a day.
How system should work:
-I have a PIC, pneumatic cylinder and 3 sensor(works with 24V DC).
-Main sensor take the signal from another system.
-When a signal came from main sensor, if the cyclinder is backward, cylinder should go to forward until forward sensor see it and if the cylinder is forward, cyclinder should come to backward until backward sensor see it.
Program:
#include <16F628A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or
B5(PIC18) used for I/O
#use delay(crystal=4000000)
#use fast_io(a)
#use fast_io(b)
#define goForward PIN_A0
#define comeBackward PIN_A1
#define main_sensor PIN_B0
#define positionSensorForward PIN_B5
#define positionSensorBackward PIN_B4
int1 pistonPositionedForward=0, pistonPositionedBackward=1;
int1 positionForwardReg=0, positionBackwardReg=0;
int1 pistonForwarding=0, pistonBackwarding=0;
#priority rb,ext
#int_RB NOCLEAR
void B_change()
{
positionForwardReg=input(positionSensorForward);
positionBackwardReg=input(positionSensorBackward);
if(positionForwardReg&&pistonForwarding) //if forwarding and forward sensor see
{
disable_interrupts(INT_RB);
output_low(goForward);
pistonPositionedForward=1;
pistonPositionedBackward=0;
write_eeprom(0,1);
write_eeprom(1,0);
pistonForwarding=0;
pistonBackwarding=0;
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
}
else if(positionBackwardReg&&pistonBackwarding) //if backwarding and backward sensor see
{
disable_interrupts(INT_RB);
output_low(comeBackward);
pistonPositionedForward=0;
pistonPositionedBackward=1;
write_eeprom(0,0);
write_eeprom(1,1);
pistonForwarding=0;
pistonBackwarding=0;
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
}
clear_interrupt(int_rb);
}
#int_ext NOCLEAR
void ext_interrupt()
{
disable_interrupts(INT_EXT);
positionForwardReg=input(positionSensorForward);
positionBackwardReg=input(positionSensorBackward);
if(positionForwardReg^positionBackwardReg) //if one of position sensor is see then position according to sensor, else position according to memory
{
pistonPositionedForward=positionForwardReg;
pistonPositionedBackward=positionBackwardReg;
}
if(pistonPositionedForward)
{
pistonBackwarding=1;
pistonForwarding=0;
output_high(comeBackward);
clear_interrupt(int_rb);
enable_interrupts(INT_RB);
}
else if(pistonPositionedBackward)
{
pistonForwarding=1;
pistonBackwarding=0;
output_high(goForward);
clear_interrupt(int_rb);
enable_interrupts(INT_RB);
}
clear_interrupt(int_ext);
}
void main()
{
//to remember last position after power off
pistonPositionedForward=read_eeprom(0);
pistonPositionedBackward==read_eeprom(1);
set_tris_a(0x00);
set_tris_b(0xFF);
output_a(0x00);
delay_ms(1000);
ext_int_edge(L_TO_H);
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
while(TRUE)
{
}
}
And my circuit:
CIRCUIT
*j2,j3 connected selonoid valve
*J4,J5,J6 connected 3 sensors 1. pin +24VDC,2. pin GND, 3.pin sensor data
***B1 and B2 connections changed. Now B1 connected to B5,B2 connected to B4
And These are I tried:
-I have 3 PIC all of them do same thing
-I changed 24V power supply
-I cancelled 7805 and 7812 and I connected seperate 5V power supply istead of 7805.
I am debugging via LEDs. Sometimes system stop running just waiting at one of positions. Take main sensor signal but doesnot anything, And pistonPositionedForward and pistonPositionedBackward register values are 0. I cant find problem how can it clear these registers?

You have unconnected pins on RB that are configured as inputs, with no internal pull ups set. Electrical noise may well trigger unwanted interrupts on PORTB, that has been known to happen.
The use of interrupts is making the overall logic a bit hard to follow for such a simple device. Have you tried rewriting the program NOT using interrupts (except maybe for EXT)? It should not take long and I think it may greatly improve the reliability - and maintainability, without impacting performance of the physical system.
I suggest you first configure the unused PORTA and PORTB pins as outputs, and see if the problem goes away. If that fails, a rewrite not using interrupts should take no more than an hour. This would probably make sense since that is probably way shorter than the time you have already spent chasing the issue.
Reading the description, I came up with this solution.
#include <16F628A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B5(PIC18) used for I/O
#use delay(crystal=4000000)
#use fast_io(a)
#use fast_io(b)
#define FWD_MOVE PIN_A0
#define BACK_MOVE PIN_A1
#define PORTA_RESET (0x00) // outputs=LO, unused pins as outputs
#define PORTA_TRISTATE (0x00)
#define EXT_SENSOR PIN_B0
#define FWD_REST PIN_B5
#define BACK_REST PIN_B4
#define PORTB_RESET (0xCE) // can't use pull ups because of HW logic...
#define PORTB_TRISTATE (0x31)
#define EEPROM_STATUS_ADDR (0x0000)
#define EEPROM_STATUS_FWD (0x01)
#define EEPROM_STATUS_BACK (0x00)
int1 extLast;
int1 extCur;
void main()
{
// setup
output_a(PORTA_RESET):
output_b(PORTB_RESET):
// setting to last known state...
// safety check.
output_low(FWD_MOVE);
output_low(BACK_MOVE);
// This will activate the outputs to make sure we have good
// positioning.
switch(eeprom_read(EEPROM_STATUS_ADDR))
{
default: // EEPROM error... I'll let you decide what to do here.
// either move forward or back.
// this implementation goes back by default.
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_BACK);
disable_interrupts(GLOBAL);
// falling through...
case EEPROM_STATUS_BACK:
output_high(BACK_MOVE);
break;
case EEPROM_STATUS_FWD:
output_high(FWD_MOVE);
break;
}
// activate outputs... watch your fingers!
set_tris_a(PORTA_TRISTATE);
set_tris_b(PORTB_TRISTATE);
extLast = input(EXT_SENSOR);
for (;;)
{
// read external sensor, take action.
extCur = input(EXT_SENSOR);
if (extCur && !extlast)
{
// safety check.
output_low(FWD_MOVE);
output_low(BACK_MOVE);
// control logic
switch(eeprom_read(EEPROM_STATUS_ADDR))
{
default: // should never happen.
// falling through...
case EEPROM_STATUS_BACK:
output_high(FWD_MOVE);
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_FWD);
disable_interrupts(GLOBAL);
break;
case EEPROM_STATUS_FWD:
output_high(BACK_MOVE);
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_BACK);
disable_interrupts(GLOBAL);
break;
}
}
extLast = extCur;
// mechanical interface:
// read the limit guards and stop movement when done.
if (input(FWD_REST))
{
output_low(FWD_MOVE);
}
if (input(BACK_REST))
{
output_low(BACK_MOVE);
}
}
}
Of course, I could not check the above code on-site :).
After reviewing the schematics, I must also advise to add 1N4001 diodes in parallel of the 2 output MOSFETS to give them better protection against reverse voltage spikes. MOSFET built-in diodes are not very sturdy. 1N4148 or 1N914 would work there as well.
The 16F628 has very limited stack space. It looks like you are experiencing stack overflow during the call to write_eeprom. Calling write_eeprom from an interrupt may not be such a good idea after all.
There was a bug in older CCS compilers, related to the use of write_eeprom. It seems write_eeprom is enabling interrupts during the call. I've added calls to disable interrupts after the writes. I don't know if that bug was fixed, since I never use CCS.
[EDIT] after checking your HW. I realized you cannot use internal pull-ups because the HW logic is positive-going. The pull-ups in the PIC are meant to work with NPN transistors in the open collector configuration (emitter to ground). I changed the code accordingly.
The way you write to EEPROM is not good. eeprom writes take time, and the second write is usually taken care of in the eeprom interrupt. The CCS bug that enables the global interrupt and unmask the EEIE in eeprom_write does not help. Unhandled interrupts do generate a reset.

Related

STM32MP1 linux IRQs & EXTI controller config in DTS file

I'm running a buildroot linux environment on a STM32MP157 dev board. I have a button with an internal pullup on pin B12. I want to fire an interrupt once the line goes low. On other linux boards like the RPi, I've been able to call gpio_to_irq(<gpio#>) and get the IRQ for that pin. Done, simple. However, on this board, there are only 16 external interrupts connected to the EXTI peripheral; they are configurable in a sense that any port may be connected to the EXTI, but the pin numbers cannot overlap. For example GPIO A12 and B12 may NOT be connected to the EXTI at the same time. I have ensured that no other devices are using and GPIO port pin 12.
I have edited my DTS file to reflect that I want my GPIO B12 connected to the EXTI controller. But so far I have had no luck in making that happen. Here is the documentation for the interrupts provided by ST. If someone can explain how to fix the device tree such that I can request the B12 interrupt from my driver I would really appreciate it.
Here's my DTS file:
/dts-v1/;
#include "stm32mp157.dtsi"
#include "stm32mp15xa.dtsi"
#include "stm32mp15-pinctrl.dtsi"
#include "stm32mp15xxac-pinctrl.dtsi"
#include "stm32mp15xx-dkx.dtsi"
/ {
model = "STMicroelectronics STM32MP157A-DK1 Discovery Board";
compatible = "st,stm32mp157a-dk1", "st,stm32mp157";
chosen {
stdout-path = "serial0:115200n8";
};
button {
compatible = "test,button";
input-gpios = <&gpiob 12 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; //Works with pull-up once the driver is loaded.
interrupts-extended = <&gpiob 12 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "qwerty";
status = "okay";
};
led {
extern-led {
compatible = "test,led";
gpios = <&gpiob 10 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "cpu";
};
};
};
I have tried the following:
interrupts-extended = <&exti 28 IRQ_TYPE_EDGE_FALLING>; (This SOC only has 16 pins per GPIO bank, so B12 is global GPIO 28)
interrupts-extended = <&gpiob 12 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpiob>;
interrupts = <12
IRQ_TYPE_EDGE_FALLING>;
Lastly, my stretch goal is to be able to request the IRQ by name, from the interrupt-name property in the device tree. Something like request_irq("qwerty"). Is that possible?
EDIT: I have temporarily connected my pushbutton to GPIO A12, and it successfully fires the interrupt, confirming that the EXTI #12 interrupt is connected to GPIO bank A. How can I go about changing this from within the device tree? Thank you in advance.
Okay I have solved this. Apparently iterating through your GPIO pins with the gpio*_to_irq() functions was the problem. When the function was called, the kernel would immediately configure the EXTI interface for that pin. I thought it was defaulting to Port A, but that was actually caused by iterating through all the GPIO pins looking for the interrupt number starting at GPIO 0, aka Port A Pin 0. So by only calling the gpio_to_irq or gpiod_to_irq function for the pins you need, the kernel will properly configure the EXTI interface for the requested pins.

ESP32 Deep Sleep Wakeup Though Touchpad

I'm trying to write a program that puts my ESP32 into a deep sleep state, and uses touch input to wake it up. I'm able to put it into the deep sleep state, but as soon as it enters, it wakes up and never calls into the callback function.
Reading the raw data from the touch pad, it idles around 25k, and touch inputs from my hand give it a value of around 180k. The 100k value in the code snippet below is the threshold to where I'm comfortable to determine that a touch has been detected.
I'd like to point out that this is different from ext0 and ext1 wake ups.
static void touchsensor_interrupt_cb(void *arg)
{
... // code here turns on an LED and prints to serial
}
void setup(){
...
touch_pad_init();
touch_pad_config(TOUCH_PAD_NUM2);
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM2, 100000);
touch_pad_isr_register(touchsensor_interrupt_cb, NULL, TOUCH_PAD_INTR_MASK_ACTIVE);
touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_ACTIVE);
touch_pad_sleep_channel_enable(TOUCH_PAD_NUM2, true);
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
touch_pad_fsm_start();
esp_sleep_enable_touchpad_wakeup();
Serial.println("entering deep sleep");
esp_deep_sleep_start();
}
I've triple-checked that my circuit is correct. Running on an ESP32S3 Dev Kit v1.0. If there's a better place to post this please let me know.
The issue ended up being this line:
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM2, 100000);
The threshold here was relative to the raw sensor data. Instead, it should be relative to raw_data - benchmark, where (I'm assuming) the benchmark is computed from what it considers the sensor's baseline value. You can get your benchmark using touch_pad_sleep_channel_read_benchmark.
Printing out the expression above on raw, unfiltered data gives values like:
01:25:10.619 -> 3
01:25:10.696 -> -4
01:25:10.774 -> 3
01:25:10.851 -> -10
01:25:10.930 -> -4
01:25:11.006 -> 20
01:25:11.100 -> -2
01:25:11.177 -> 2
01:25:11.255 -> 1
01:25:11.333 -> 0
Where those values oscillate around 0 (the noise in my touch sensor). I changed the line above it to:
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM2, benchmark * threshold);
Where threshold is 0.2, meaning I'll consider a "touch" when the ESP32 reads sensor values of 120% of the benchmark value. I hope this helps someone.

Device Tree GPIO Reset

I'm working on a board based on the iMX6 and am trying to configure a number of GPIOs that are being used as chip enable and reset lines. Based on the research I've done, the way to handle this is via the gpio-reset driver in the device tree. Following the documentation I've come up with the below code which compiles but I'm not sure how to then control these reset lines from user space.
The first device tree driver I used was the gpio-leds which created an leds folder in sys/class with nodes to control the LED. However I don't see anything similar for reset. So I have 2 questions:
1) Is GPIO-RESET the correct binding to use for controlling reset lines, enable lines, etc.
2) Is there documentation on how to handle this and other bindings from user space, similar to how I'm controlling the GPIO-LED?
Kernel: Linux buildroot 4.1.15
/dts-v1/;
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/omap.h>
#include "imx6ul.dtsi"
/ {
model = "Freescale i.MX6 UltraLite 14x14 EVK Board";
compatible = "fsl,imx6ul-14x14-evk", "fsl,imx6ul";
memory {
reg = <0x80000000 0x20000000>;
};
/* Reset Line Configuration */
gpio_resets {
compatible = "linux,gpio-reset";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpioreset>;
gnss {
gpios = <&gpio1 4 0>;
asserted-state = <0>;
duration-ms = <100>;
auto;
};
};
...
};
&iomuxc {
pinctrl-names = "default";
imx6ul-evk {
pinctrl_gpioreset:
gpiorstgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B0 /* GNSS RESET_N */
>;
};
...
};
I'm not exactly sure about the gpio-reset sysfs interface as I couldn't find any information in bindings documentation, but for the normal gpio interface you need to export the gpio before it will show up in /sys/class/gpio/gpio*. Basically you just need to write the number of the gpio you wish to use to the export file underneath /sys/class/gpio. Here is an example of someone doing that. If you're just toggling the gpio on/off that interface should be enough.
Is GPIO-RESET the correct binding to use for controlling reset lines, enable lines, etc.
I have been looking for such a driver too.
I can see that there was a proposal for exactly this:
https://lwn.net/Articles/585145/
but I cannot find it in my kernel version (tracking the 5.4.y releases).
Only in some stale imx6 kernel: https://github.com/samnazarko/linux-imx6/blob/master/Documentation/devicetree/bindings/reset/gpio-reset.txt
So I will either
create a small driver to support the "delayed" function based on the above proposal (time of asserting the reset at boot).
use gpio-led with a default-state. Maybe using the "one-shot" trigger, to provide a single-write API to my apps. (Write once to the sysfs shot file results in a single toggle of the pin for a configurable time.)
Totally handle it from the userspace via libgpio or sysfs. (Maybe combined with gpio-led, to have at least a clearly defined state of the line during boot.)

SysTick->LOAD vs SysTick->CALIB

I am currently porting my DCF77 library (you may find the source code at GitHub) from Arduino (AVR based) to Arduino Due (ARM Cortex M3).
The library requires precise 1ms timing. An obvious candidate is the use of the systicks. Conveneniently the Arduino Due is already setup for systicks with 1 kHz.
However my (AVR) DCF77 library is capable to tune the timing once it locks to DCF77. This is done by manipulating the timer reload values like so
void isr_handler() {
cumulated_phase_deviation += adjust_pp16m;
// 1 / 250 / 64000 = 1 / 16 000 000
if (cumulated_phase_deviation >= 64000) {
cumulated_phase_deviation -= 64000;
// cumulated drift exceeds 1 timer step (4 microseconds)
// drop one timer step to realign
OCR2A = 248;
} else if (cumulated_phase_deviation <= -64000) {
// cumulated drift exceeds 1 timer step (4 microseconds)
// insert one timer step to realign
cumulated_phase_deviation += 64000;
OCR2A = 250;
} else {
// 249 + 1 == 250 == 250 000 / 1000 = (16 000 000 / 64) / 1000
OCR2A = 249;
}
DCF77_Clock_Controller::process_1_kHz_tick_data(the_input_provider());
}
I want to port this to the ARM processor. In the ARM information center I found the following documentation.
Configuring SysTick
...
To configure the SysTick you need to load the SysTick Reload Value
register with the interval required between SysTick events. The timer
interrupt or COUNTFLAG bit (in the SysTick Control and Status
register) is activated on the transition from 1 to 0, therefore it
activates every n+1 clock ticks. If a period of 100 is required 99
should be written to the SysTick Reload Value register. The SysTick
Reload Value register supports values between 1 and 0x00FFFFFF.
If you want to use the SysTick to generate an event at a timed
interval, for example 1ms, you can use the SysTick Calibration Value
Register to scale your value for the Reload register. The SysTick
Calibration Value Register is a read-only register that contains the
number of pulses for a period of 10ms, in the TENMS field (bits 0 to
23). This register also has a SKEW bit (30) that is used to indicate
that the calibration for 10ms in the TENMS section is not exactly 10ms
due to small variations in clock frequency. Bit 31 is used to indicate
if the reference clock is provided.
...
Unfortunately I did not find anything on how SysTick->LOAD and SysTick->CALIB are connected. That is: if I want to throttle or accelerate systicks, do I need to manipulate the LOAD or the CALIB value? And which values do I need to put into these registers?
Searching the internet did not bring up any better hints. Maybe I am searching at the wrong places.
Is there anywhere a more detailed reference for these questions? Or maybe even some good examples?
Comparing the AtMega328 datasheet with the Cortex-M3 TRM, the standout point is that the timers work opposite ways round: on the AVR, you're loading a value into OCR2A and waiting for the timer in TCNT2 to count up to it, whereas on the M3 you load the delay value into SYST_RVR, then the system will count down from this value to 0 in SYST_CVR.
The big difference for calibration is going to be because the comparison value is fixed at 0 and you can only adjust the reload value, you might have more latency compared to adjusting the comparison value directly (assuming the counter reload happens at the same time the interrupt is generated).
The read-only value in SYST_CALIB (if indeed it even exists, being implementation-defined and optional), is merely for relating SYSTICK ticks to actual wallclock time - when first initialising the timer, you need to know the tick frequency in order to pick an appropriate reload value for your desired period, so having a register field that says "this many reference clock ticks happen in 10ms (possibly)" offers some possibility of calculating that at runtime in a portable fashion, rather than having to hard-code a value that might need changing for different devices.
In this case, however, not only does having an even-more-accurate external clock to synchronise against makes this less important, but crucially, the firmware has already configured the timer for you. Thus you can assume that whatever value is in SYST_RVR represents close-enough-to-1KHz, and work from there - in fact to simply fine-tune the 1KHz period you don't even need to know what the actual value is, just do SysTick->LOAD++ or SysTick->LOAD-- if the error gets too big in either direction.
Delving a bit deeper, the SAM3X datasheet shows that for the particular M3 implementation in that SoC, SYSTICK has a 10.5 MHz reference clock, therefore the SYST_CALIB register should give a value of 105000 ticks for 10ms. Except it doesn't, because apparently Atmel thought it would be really clever to make the unambiguously-named TENMS field give the tick count for 1ms, 10500, instead. Wonderful.
Just for the reason that others do not have to dig around like had to do - here is what I found out in addition.
In arduino-1.5.8/hardware/arduino/sam/system/CMSIS/CMSIS/Include/core_cm*.h there is code to manipulate SysTick. In particular in core_cm3.h there is a function
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
Then in arduino-1.5.8/hardware/arduino/sam/variants/arduino_due_x/variant.cpp in function init there is
// Set Systick to 1ms interval, common to all SAM3 variants
if (SysTick_Config(SystemCoreClock / 1000))
{
// Capture error
while (true);
}
Since SystemCoreClock evaluates to 84000000 it follows that this compiles like SysTick_Config(84000). I verified against a DCF77 module that SysTick_Config(84001) will slow down SysTicks while SysTick_Config(83999) will speed it up.

Why do some analog pins on my PIC32 report zero when disconnected and others report non-zero?

I am using a PIC32MX534F064L (datasheet), and trying to read several of its analog pins (marked AN0 to AN15).
With none of those pins connected to anything, I expect to read a value of zero. Instead on AN0 through AN5 I read values between 650 and 900. Only from the rest (AN6 through AN15) I get a value of zero.
When each of the pins is connected to a source, they report correctly. Each of the pins, AN0 through AN15 will report 0 for 0.0V, and 1023 for 3.3V.
I've tried sampling the values in pairs, and each separately. Whether sampled together or apart, AN0 will report non-zero values (usually around 700-800), and AN13 will report 0.
My first thought was that I somehow failed to properly set up the ADC. Here's my code:
#include <stdio.h>
#include <plib.h>
unsigned int an0;
unsigned int offset;
char buffer[100];
int main(void)
{
SYSTEMConfigPerformance(72000000L);
CloseADC10();
#define ADC_CONFIG1 ADC_MODULE_ON | ADC_FORMAT_INTG | \
ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON
#define ADC_CONFIG2 ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | \
ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_2 | \
ADC_ALT_BUF_ON | ADC_ALT_INPUT_ON
#define ADC_CONFIG3 ADC_CONV_CLK_INTERNAL_RC | ADC_SAMPLE_TIME_15
#define ADC_CONFIGSCAN SKIP_SCAN_ALL
#define ADC_CONFIGPORT ENABLE_AN0_ANA
SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN0 );
OpenADC10( ADC_CONFIG1, ADC_CONFIG2, ADC_CONFIG3, \
ADC_CONFIGPORT, ADC_CONFIGSCAN );
EnableADC10();
while ( ! mAD1GetIntFlag() ) { }
while (1)
{
offset = 8 * ((~ReadActiveBufferADC10() & 0x01));
an0 = ReadADC10(offset);
sprintf(buffer, "AN0 = %u", an0);
}
return 0;
}
Looking in the PIC's datasheet, I noticed two things:
The pins AN0 to AN5, the ones mis-reporting non-zero values, are also CNx pins. These pins are "Change Notification" pins, that are meant to raise an interrupt when the value on the pins changes.
There is a "weak pull-up" that can be enabled on all CNx pins.
So I tried disabling the "weak pull-up" by using this line:
mCNClose();
Which disables all the CNx pins and their pull-ups. Sadly, this did not help. And when I checked the value of the CN-pull-up-register (CNPUE
What else can I try? Am I doing something wrong in my code?
Well, your expectation is wrong!
The minimum input resistance for source should be only few kilo ohms check datasheet.
If ADC pins is floating (not connected) the unpredicted value of internal parasitic current will cause that measuring value will be bigger than 0. Remember the ADC sample capacitor has only few pF capacity so floating pins can oscillate in wide voltage range also from external EM (electromagnetic) influences.
So, connect at least 1M resistors to pull down voltage on ADC pin, the resistance of pull down resistor is depended of ADC sample time. If ADC sample time is short than decrease the pull down value of resistor.
EDIT:
Check datasheet page 214 parameter AD17: Recommended Impedance of Analog Voltage Source is 5 KOhms. And AD15 say that that max. Leakage Current on ADC input pins can be +/-0.61 uA.
It's probably just noise, since the inputs are high impedance when nothing is connected. Try grounding the inputs (connect to 0V) as an experiment - the values should then be close to 0. If you need the inputs to be zero when nothing is connected then connect a pull-down resistor to each input (between input and 0V) to lower the impedance - a value of 10k ohms should do it.
Do not leave pins unconnected! The unconnected pin is essentially an antenna which could pick up voltages outside of the Vss and Vdd range. Section 2.10 of the datasheet says to not leave any pins unconnected (or if you do, configure them as outputs and drive them low.)
If you want to test your A2D, you can configure the pin as a digital output (the analog setting only overrides the digital input) and then drive it high and low to test.

Resources