PIC12F1571 ADC not working - Page 1
Forum section: electronics / general

author: Lion_Tamer
author_rank: Contributor
timestamp: January 03, 2016, 04:57:58 pm
content: Hi allI am a self taught tinkerer/newbie and am working on a small project that is using a PIC12F1571 (I may switch to a PIC12F1572 if I run out of code space) as the controller in an infra-red key fob transmitter.Due to space constraints I have to keep the physical size of the chip and associated circuitry as small as possible (yet still be able to hand solder the PCB), because of this I am trying to use one ADC pin for all 5 switches, I only ever need to be able to read one switch at a time and so I have set up a simple resister chain - see schematic. When checked with a DMM I can see that the ADC pin is receiving the input that I expect it to but I cannot get it to reliably read the ADC input if at all. (Please note that my test circuit [V1 - built on stripboard] has the switches wired to VDD and R3 to VSS - the configuration has been swapped in the V2 design to allow better low voltage reliability).My programming environment is MPLAB X V3.15 and XC8 V1.35 with a PICKit 3 as the programmer/debuger.Here is my code - I have been over it and the data sheet several times but can't see what is wrong - I am sure that it is probably staring me in the face but need your help to see it.configuration_bits.cCode:if defined(__XC)#include <xc.h> /* XC8 General Include File */#elif defined(HI_TECH_C)#include <htc.h> /* HiTech General Include File */#endif// PIC12F1571 Configuration Bit Settings// 'C' source line config statements#include <xc.h>// #pragma config statements should precede project file includes.// Use project enums instead of #define for ON and OFF.// CONFIG1#pragma config FOSC = INTOSC // (INTOSC oscillator; I/O function on CLKIN pin)#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)#pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)// CONFIG2#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)#pragma config PLLEN = OFF // PLL Enable (4x PLL Disabled)#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)#pragma config LPBOREN = OFF // Low Power Brown-out Reset enable bit (LPBOR is disabled)#pragma config LVP = OFF // Low-Voltage Programming Disable (Low-voltage programming Disabled)system.hCode:#define SYS_FREQ 250000L#define FCY SYS_FREQ/4void ConfigureOscillator(void); /* Handles clock switching/osc initialization */user.hCode:/******************************************************************************//* User Level #define Macros *//******************************************************************************/#define Umpire_Box//#define Medic_Box/* TODO Application specific user parameters used in user.c may go here */#define VisLED_Port LATAbits.LATA2 //Location of the Indicator LED#define VisLED_Tris TRISAbits.TRISA2#define VisLED_Off 0#define VisLED_On 1#define IRLED_Port LATAbits.LATA5 //Location Of the IR LED#define IRLED_Tris TRISAbits.TRISA5#define IRLED_Drive ODCONAbits.ODA5#define IRLED_Off 1#define IRLED_On 0#define PWR_SW_Port LATAbits.LATA1 //Location Of The Power Mosfet#define PWR_SW_Tris TRISAbits.TRISA1#define PWR_SW_Drive ODCONAbits.ODA1#define OP_SW_Tris TRISAbits.TRISA4 //Location of the input switches#define OP_SW_Port ANSELAbits.ANSA4#define OP_SW_CHS 0b00011#define PWR_On 0#define PWR_Off 1#define _XTAL_FREQ 250000 //This is the speed your controller is running atvoid InitApp(void); /* I/O and Peripheral Initialization */void Setup_Switch_Read(void);void Read_Switch_Input(void);void half_second_delay(void);system.cCode:#if defined(__XC)#include <xc.h> /* XC8 General Include File */#elif defined(HI_TECH_C)#include <htc.h> /* HiTech General Include File */#endif#include <stdint.h> /* For uint8_t definition */#include <stdbool.h> /* For true/false definition */#include "system.h"void ConfigureOscillator(void){OSCCONbits.IRCF = 0b0110; //250KHz OscillatorOSCCONbits.SPLLEN = 0; //4X PLL Disabled}main.cCode:#if defined(__XC)#include <xc.h> /* XC8 General Include File */#elif defined(HI_TECH_C)#include <htc.h> /* HiTech General Include File */#endif#include <stdint.h> /* For uint8_t definition */#include <stdbool.h> /* For true/false definition */#include "system.h" /* System funct/params, like osc/peripheral config */#include "user.h" /* User funct/params, such as InitApp */int Switch;void main(void){/* Configure the oscillator for the device */ConfigureOscillator();/* Initialize I/O and Peripherals for application */InitApp();PWR_SW_Port = PWR_On;__delay_ms(1);Setup_Switch_Read();//VisLED_Port = VisLED_On;while(1){Read_Switch_Input();#ifdef Umpire_Box{if (Switch == 1){Set_PWM3_Duty_Cycle(0x2000);}else if (Switch == 2){Set_PWM3_Duty_Cycle(0x4000);}else if (Switch == 3){Set_PWM3_Duty_Cycle(0x6000);}else if (Switch == 4){Set_PWM3_Duty_Cycle(0xC000);}else if (Switch == 5){Set_PWM3_Duty_Cycle(0xF000);}else{Set_PWM3_Duty_Cycle(0x8000);}}#endif#ifdef Medic_Box#endif__delay_ms(1000);// Set_PWM3_Duty_Cycle(PWM3DC + 1000);}// PWR_SW_Port = PWR_Off;}void Read_Switch_Input(void){PIR1bits.ADIF = 0; //Clear ADC Interrupt FlagADCON0bits.GO_nDONE = 1; //Start ADC Conversionwhile(ADCON0bits.GO_nDONE == 1) //Wait for ADC Conversion to be complete{//wait for result}if (ADRESL > 224){Switch = 1;}else if (ADRESL > 173){Switch = 2;}else if (ADRESL > 141){Switch = 3;}else if (ADRESL > 122){Switch = 4;}else if (ADRESL > 96){Switch = 5;}else{Switch = 0;}}user.cCode:#if defined(__XC)#include <xc.h> /* XC8 General Include File */#elif defined(HI_TECH_C)#include <htc.h> /* HiTech General Include File */#endif#include <stdint.h> /* For uint8_t definition */#include <stdbool.h> /* For true/false definition */#include "user.h"void InitApp(void){/* TODO Initialize User Ports/Peripherals/Project here */APFCONbits.P1SEL = 1; //Set PWM1 to RA5CM1CON0bits.C1ON = 0; //disable compareter module.ODCONA = 0x00; //Set Port A pins to drive and sink currentIRLED_Drive = 1; //Set pin as current sink onlyPWR_SW_Drive = 1; //Set pin as current sink onlyPWR_SW_Tris = 0; //Set as OutputVisLED_Tris = 0; //Set as OutputIRLED_Tris = 0; //Set as OutputSetup_PWM3();Set_PWM3_Period(0xFFFF);Set_PWM3_Duty_Cycle(0x8000);Start_PWM3();/* Setup analog functionality and port direction *//* Initialize peripherals *//* Enable interrupts */}void Setup_Switch_Read(void){ADCON0bits.ADON = 1; //Enable ADC ModuleOP_SW_Tris = 1; //Set Input switch port as inputOP_SW_Port = 1; //Set Pin as analog inputADCON0bits.CHS = OP_SW_CHS; //Select Channel for measurement//ADC TAD Clock period should fall between 1.0 and 6.0 uS//Fosc = 250KHz//ADCS 0b000 = Fosc/2 which is a period of 8us <- Too long//Therefore ADCS must be set to FRC = 0b111 or 0b011ADCON1bits.ADCS = 0b111; //Set ADC Conversion clock to FRCADCON1bits.ADPREF = 0b00; //Set ADC voltage reference to VDDADCON1bits.ADFM = 0; //Set ADC result to right justifiedADCON2bits.TRIGSEL = 0x0; //Auto conversion Trigger disabled__delay_ms(5); //Allow the module to settle}Any help that you can give will be much appreciated.Thank youJem

author: Howardlong
author_rank: Super Contributor
timestamp: January 03, 2016, 05:38:14 pm
content: What are the values R3-R7?Have you tried a cap on AN3 to ground, say 1uF?When debugging, are you sure there's nothing leaking into the diodes, or is the header just for programming, not debugging?

author: Lion_Tamer
author_rank: Contributor
timestamp: January 03, 2016, 06:12:14 pm
content: Quote from: Howardlong on January 03, 2016, 05:38:14 pmR3 = 10KR4 - R7 = 3.3KQuote from: Howardlong on January 03, 2016, 05:38:14 pmNo - I will have to give it a go.Quote from: Howardlong on January 03, 2016, 05:38:14 pmThe header is being used for both programming and debugging but I get the same problem even if there is nothing connected to the header.Jem

author: Kalvin
author_rank: Super Contributor
timestamp: January 03, 2016, 06:42:33 pm
content: See the datasheet, page 138 and 139:http://www.microchip.com/stellent/groups/picmicro_sg/documents/devicedoc/en566612.pdfCheck your code how you configure the ADC result format justification and how you read the ADC result (ADRESH vs ADRESL).

author: Lion_Tamer
author_rank: Contributor
timestamp: January 03, 2016, 07:21:20 pm
content: Quote from: Kalvin on January 03, 2016, 06:42:33 pmAssuming that I got the code correct then it should be right justified and I am only reading ADRESL - I do notice though that you have linked to a slightly newer revision data-sheet than the one that I was using and that it now mentions about disabling the weak pull up resisters so I will have to give that a go.Jem

author: Kalvin
author_rank: Super Contributor
timestamp: January 03, 2016, 07:28:12 pm
content: Quote from: Lion_Tamer on January 03, 2016, 07:21:20 pmYou can use the current justification and read the ADRESH instead of ADRESL, you should get correct measurements. If you do change the justification and use the ADRESL, and your signal level is too high, you will get incorrect results again.

author: Lion_Tamer
author_rank: Contributor
timestamp: January 03, 2016, 09:32:59 pm
content: Looks like you may have spotted my error - I had made a not in the comments that it was justified right when in fact it was left justified.I haven't cracked the scope out yet to confirm that the PWM is changing as expected but watching the LED it does look like it, I had gone over it so many times that I just couldn't see it till some one else pointed it out.I have also added the disabling of the internal pull up resistors.It is looking like it may be sorted though I will have to test it a bit more to make sure but that is a job for another evening.Thank youJem

author: Howardlong
author_rank: Super Contributor
timestamp: January 03, 2016, 11:01:22 pm
content: Well caught, Kalvin. When I saw all that code I thought I'd go for the hardware approach first, you clearly have more patience than I do.As a learning experience, it is to always unit test your code first. And then when you have a problem as a system, split it back down to the minimum to reproduce the fault.Even after two decades of PICs, I still unit test each and every part first before putting it together as a system.

author: Kalvin
author_rank: Super Contributor
timestamp: January 04, 2016, 11:04:53 am
content: Quote from: Howardlong on January 03, 2016, 11:01:22 pmI just got lucky. If the code would have been any longer, I wouldn't have bothered to skim it through. As the code was short and simple enough it was easy to read through. Then I thought to take a quick peek into the datasheet and the ADC registers if I could find anything obvious "oops, I missed that one". It was quite easy to spot the problem from there.

author: AndyC_772
author_rank: Super Contributor
timestamp: January 04, 2016, 02:53:17 pm
content: IIRC the header files for some (most? all?) PICs define a register ADRES, which is 16 bits wide, so you don't have to read ADRESL and ADRESH separately. Not sure this is actually documented anywhere.