Use of timer and overflow flag (registers clearing themselves!)

Thread Starter

k1mzz

Joined Jul 31, 2016
16
Hi,

I am working Microchip DV164132 evaluation kit (http://www.microchip.com/DevelopmentTools/ProductDetails.aspx?PartNO=dv164132). This is based on the PIC16LF1937 microcontroller.

I am trying to perform a reaction time test - when an LED lights, the timer starts. When the button is pressed, the TMR0 overflow flag is checked; if the timer hasn't overflowed, you have been successful and a series of LEDs should light up indicating this.

My code is as follows (unsure as to how to define language - it's C!):

Code:
#include <xc.h>

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (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 CPD = OFF        // Data Memory Code Protection (Data 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)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#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 LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

//definition of internal oscillator frequency for use with delay function
#define _XTAL_FREQ 500000

//definition of friendly names for pins
#define STATUS1 PORTDbits.RD1   //LED D1
#define STATUS2 PORTEbits.RE2   //LED D2
#define STATUS3 PORTEbits.RE1   //LED D3
#define START   PORTEbits.RE0   //LED D4
#define BUTTON  PORTDbits.RD2   //BUTTON

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

void main(void) {
    //Configure oscillator
    OSCCONbits.SCS1 = 1;
    OSCCONbits.IRCF = 0b0111;
 
    //Configure timer
    OPTION_REGbits.TMR0CS = 0; //Timer mode to instruction clock
    OPTION_REGbits.PSA = 0; //Enable timer prescaler
    OPTION_REGbits.PS = 111; //Timer prescaler to 1:256
 
    //Set direction of RD2 to input
    TRISDbits.TRISD2 = 1;
    //Make RD2 a digital input
    ANSELDbits.ANSD2 = 0;
    //Set direction of RD1 to output
    TRISDbits.TRISD1 = 0;
    //Set direction of PORTE (all output)
    TRISE = 0b00000000;
 
    for(;;)
    {
            //Zero outputs
            STATUS1 = 0;
            STATUS2 = 0;
            STATUS3 = 0;
            START = 0;
 
            //Delay before start
            __delay_ms(2000);
         
            if(BUTTON == 1)     //if button is NOT pressed
            {
                START = 1;      //indicate ready to start
                TMR0 = 0;       //reset timer0 register
                INTCONbits.TMR0IF = 0;  //reset timer0 overflow flag
                while(BUTTON == 1); //wait until  button low (button press)
                if(INTCONbits.TMR0IF == 0)
                {//indicate success if timer didn't overflow (within reaction time)
                    STATUS1 = 1;
                    STATUS2 = 1;
                    STATUS3 = 1;
                }
                START = 0;
            }
            //1sec delay so success can be observed
            __delay_ms(1000);
            //wait for button release
            while(BUTTON == 0);
    }
    return;
}
This works, however, the success output is only observed on one LED (STATUS1 (PORTDbits.PORTD1 / LED D1)). The other two (friendly names STATUS2 and STATUS3 briefly flash - not enough to be observed properly!).

It seems to me that PORTE, bits 1 and 2 are clearing themselves! Not sure why this is happening - I have set the direction of the ports up correctly and am sure the timer routine is configured correctly as PORTD, bit 1 is functioning correctly.

I am able to light all of the LEDs at once so I don't think it's a power supply issue.

Has anyone experienced anything similar? Thanks in advance.
 

Thread Starter

k1mzz

Joined Jul 31, 2016
16
I found a solution! Reading some of the official Microchip forums, a rule of thumb is:

For writing outputs, use LATx; for reading inputs, use PORTx (and if you need to read what you set an output to, use LATx).

Taking this into account, I modified the definitions for START1 -> 3 as follows:

Code:
//definition of friendly names for pins
#define STATUS1 LATDbits.LATD1   //LED D1
#define STATUS2 LATEbits.LATE2   //LED D2
#define STATUS3 LATEbits.LATE1   //LED D3
#define START   LATEbits.LATE0   //LED D4
#define BUTTON  PORTDbits.RD2   //BUTTON
This provided the required output. Hope this helps somebody!
 

AlbertHall

Joined Jun 4, 2014
12,347
I think this is because you have not set ANSELE.
As you have discovered writing to LATE will solve the problem.
As a rule always set the ANSEL registers appropriately for your application, and use LAT registers for setting outputs.
The problem you found is explained in this part of the data sheet:
"The state of the ANSELE bits has no effect on digital output functions. A pin with TRIS clear and ANSEL set will still operate as a digital output, but the Input mode will be analog. This can cause unexpected behavior when executing read-modify-write instructions on the affected port."
When you set one pin of PORTE, the PIC actually reads the port (reads '0' for analog inputs), sets the bit, and writes the result.
 

Thread Starter

k1mzz

Joined Jul 31, 2016
16
Thanks for the explanation @AlbertHall - I have just read this part of the data sheet and experimented with the old code and addition of the following:

Code:
ANSELEbits.ANSELE = 0b0000;
This also provides the required output. Thanks again!
 

dannyf

Joined Sep 13, 2015
2,197
I have just read this part of the data sheet
You should always read the datasheet thoroughly before getting to the coding part. Or it's massively frustrating, :)

In general, it is a good idea to reset the pins to their gpio states. For the PIC, it typically means turning off the analog (adc+comparator) features.

two suggestions to your code:

1) if you apply an offset to the counter (TMR0 in this case), you can vary the time lag your code can detect. For example:

Code:
  TMR0 = -TMR_PR; //overflow after TMR_PR * prescaler cycles
2) the code's logic could improve a little. I would try something like this:

Code:
  reset the timer / flag / status bits;
  if (button is pressed) {
    while (button remains pressed) continue;
    if (timer flag is set) {
      light up the leds;
      delay a short while;
    }
  }
 
Top