Simple question regarding concatenating TMR1H and TMR1L

Thread Starter

dannybeckett

Joined Dec 9, 2009
185
Rich (BB code):
#include <stdio.h>
#include <string.h>

main() {
       int TMR1H = 12345678;
       int TMR1L = 12345678;
       char str1[8];
       char str0[8];
       str1[0] = TMR1H;
       str0[0] = TMR1L;
       char str[16];
       strcpy (str, str1);
       strcat (str, str0);
       printf("%s", str);
       getchar();
}
This is my code so far. All I want is to create a variable which consists of TMR1H immediately followed by TMR1L, so I can perform math operations on the time captured from TMR1. Obviously, these two variables contain the first half(L) and second half(H) of a 16 bit binary number. I dont want any arithmetic operations performed on them, simply str = 1234567812345678. Ideally, the output type is an int. At the moment I'm getting some odd characters from the printf function. Im probably doing very silly things here so if anyone can enlighten me I would be very grateful!

Dan
 

Thread Starter

dannybeckett

Joined Dec 9, 2009
185
That looks useful, but I dont seem to have timers.h. Using the hi-tech c compiler. I have time.h, but im guessing thats different...
 

t06afre

Joined May 11, 2009
5,934
You can use a union here I think. But your code is dodgy. Have you read the quickstart.pdf document in the compiler install folder see under the folder docs
 

t06afre

Joined May 11, 2009
5,934
Rich (BB code):
#include <stdio.h>
#include <string.h>
 
main() {
       int TMR1H = 12345678;
       int TMR1L = 12345678;
       char str1[8];
       char str0[8];
       str1[0] = TMR1H;
       str0[0] = TMR1L;
       char str[16];
       strcpy (str, str1);
       strcat (str, str0);
       printf("%s", str);
       getchar();
}
This is my code so far. All I want is to create a variable which consists of TMR1H immediately followed by TMR1L, so I can perform math operations on the time captured from TMR1. Obviously, these two variables contain the first half(L) and second half(H) of a 16 bit binary number. I dont want any arithmetic operations performed on them, simply str = 1234567812345678. Ideally, the output type is an int. At the moment I'm getting some odd characters from the printf function. Im probably doing very silly things here so if anyone can enlighten me I would be very grateful!

Dan
Dan which PIC do you use? This should be defined properly in the header file. As an example from a header file. Note the type definition do not mix int and char
Rich (BB code):
// Register: TMR1L
volatile unsigned char           TMR1L               @ 0x00E;
// bit and bitfield definitions
// Register: TMR1H
volatile unsigned char           TMR1H               @ 0x00F;
// bit and bitfield definitions
// Register: TMR1
volatile unsigned int            TMR1                @ 0x00E;
Edit this is from the 9.81 compiler version of PICC (for 16F series pic) Older versions may not have this in the header file.
 
Last edited:

Thread Starter

dannybeckett

Joined Dec 9, 2009
185
I'm using a PIC16F887 - I have just checked my header file and found this:

// Register: TMR1L
volatile unsigned char TMR1L @ 0x00E;
// bit and bitfield definitions

// Register: TMR1H
volatile unsigned char TMR1H @ 0x00F;
// bit and bitfield definitions

// Register: TMR1
volatile unsigned int TMR1 @ 0x00E;

Is TMR1 the full 16 bit timer contents? That would save a bit of hastle lol....
 

t06afre

Joined May 11, 2009
5,934
Yes that is correct. One more thing in your code you do not include the htc.h file (ref quickstart.pdf). And how about your configuration words setting. Is this done in MPLAB?
 

Thread Starter

dannybeckett

Joined Dec 9, 2009
185
Oh thats a little bit of testing code I wrote in devc++ (very badly), not taken from mplab. My full code is as follows:

Rich (BB code):
// A program to measure the period of a square wave

#include <htc.h>
//#include <stdio.h>

#define _XTAL_FREQ 1000000
__CONFIG (FOSC_INTRC_NOCLKOUT & WDTE_OFF & PWRTE_ON & MCLRE_ON & CP_OFF & CPD_OFF & BOREN_OFF & IESO_OFF & FCMEN_OFF & LVP_OFF & DEBUG_OFF);

void initDigitalOut() {
    PORTA = PORTB = PORTC = PORTD = 0;
    TRISA = TRISB = TRISC = TRISD = 0;
    ANSEL = ANSELH = 0;
}

void initIntOscillator() {
    OSCCONbits.IRFC     = 4;        // Internal oscillator frequency set. 3 bits wide, 100 = 1MHz, 11 = 500KHz
    OSCCONbits.OSTS        = 0;        // Use internal oscillator. 1 = ext osc.
    OSCCONbits.HTS        = 1;        // High frequency stability bit (8 MHz to 125 kHz). What do?
    OSCCONbits.LTS        = 0;        // Low frequency stability bit (31KHz). What do?
    OSCCONbits.SCS        = 1;        // System clock select. 1 = int, 0 = ext.
}

void initCaptureRisingCCP1() {
    CCP1CONbits.CCP1M    = 0b101; 
    /* write to the CCP1CON register, specifically the CCP1M (Enhanced Capture/Compare/PWM mode
     * select bits) selection, and set '0101 = Capture mode, every rising edge'. All other pins
     * in register are not used.
     */
}

void initOptionRegister() {            // This is used to configure Timer0 amongst other things.
    OPTION_REGbits.nRBPU    = 1;     // Disable PORTB pullups.
    OPTION_REGbits.INTEDG    = 1;     // Interrupt edge select. 1 = rising edge on INT pin.
    OPTION_REGbits.T0CS        = 0;     // Timer0 Clock source. 0 = Fosc/4, 1 = T0CKI pin.
    OPTION_REGbits.T0SE        = 0;     // Timer0 source edge select. 0 = Increment on low-to-high transition on T0CKI pin (not arsed).
    OPTION_REGbits.PSA        = 0;     // Prescaler allignment. 1 = WDT, 0 = Timer0.
    OPTION_REGbits.PS        = 7;    // 3 bits wide, set prescaler rate. 111 = 1:256.
}


void initTimer1() {
    T1CONbits.T1GINV    = 0;     // Set T1 Gate active high or active low. 1 = high, 0 = low.
    T1CONbits.TMR1GE    = 0;     // 1 = Timer1 is on if Timer1 gate is not active. 0 = Timer1 is on.
    T1CONbits.T1CKPS    = 3;    // Timer1 clock prescale, 2 bits wide. 00 = /1. 11 = /8
    T1CONbits.T1OSCEN    = 0;     // LP oscillator enable for Timer1. Runs at 32.768KHz.
    T1CONbits.T1SYNC    = 0;     // Sync external clock (dont think this applies but sync JIC). Inverted, 0 = sync.
    T1CONbits.TMR1CS    = 0;     // 1 = External clock from T1CLI pin (RC0). 0 = Internal clock. Not sure what this should be when using LP osc!
    T1CONbits.TMR1ON    = 0;     // 1 Enables timer, 0 stops it.
    TMR1H = TMR1L        = 0;     // Clear whatever is in Timer1. Values are unknown on startup.
}

void initInterruptCCP1() {
    INTCONbits.GIE    = 1;    // Global interrupt enable. Enables all unmasked interrupts, must be set. Clears all interrupts also.
    INTCONbits.PEIE    = 1;    // Peripheral interrupt enable. Enables all unmasked peripheral interrupts. Clears all associated interrupts also.
    TRISC2            = 1;    // Pin CCP1 set to input.
    PIE1bits.CCP1IE    = 1;    // CCP1 interrupt enable bit, RC2.
}


volatile float horizontalPixelDelayMS = 0;    // Volatile keyword prevents any optimization from compiler. Tells it that timeCaptured could be altered outside the scope of this program. Don't think its needed tbh.
interrupt void rpmTrigger() {    
    /* Note the keyword ‘interrupt’.  Hi-Tech C handles the code to save and restore the
     * state of the micro, and the calculating the address to hook into the interrupt.
     */
    horizontalPixelDelayMS = (((CCPR1H & 0xFF) * 0x100) + (CCPR1L & 0xFF))*0.032; // Actually might be able to just refer to CCPR1 insead of doing all that BS.

    TMR1H = 0;
    TMR1L = 0;

    PIR1bits.CCP1IF    = 0;    // Clear CCP1 interrupt request flag.
}

void main() {
    initDigitalOut();
    initIntOscillator();
    initTimer1();
    initCaptureRisingCCP1();
    initInterruptCCP1();
    T1CONbits.TMR1ON    = 1;    // Turn timer on.
    while(1) {}
}

The noob friendly comments are there for my refference.
 

t06afre

Joined May 11, 2009
5,934
Take a look here
Rich (BB code):
]#define _XTAL_FREQ 1000000
__CONFIG (FOSC_INTRC_NOCLKOUT......);
You can have max 8MHz with internal OSC. Se figure FIGURE 4-1 in the data sheet. So the #define _XTAL_FREQ 1000000 must be changed to correct value or the delay functions __DELAY_MS, __DELAY_US will be wrong. But so far you have not used them yet.
 

John P

Joined Oct 14, 2008
2,025
You need to be careful with any kind of math done on timer registers, or even copying them to another variable. If the timer is running when you do this, you can't be sure that you're grabbing the contents at a favorable time, and at least the low-order byte and possibly the high-order byte will change quickly. The foolproof way to do it is stop the timer, do what you have to do and restart the timer.

Or grab the high byte, grab the low byte, grab the high byte again and if it changed since the first time, grab the low byte again. But don't let an interrupt occur anywhere in this process.
 

Thread Starter

dannybeckett

Joined Dec 9, 2009
185
Thanks John, noted. My code has changed quite a lot since this, I have realised that TMR1 addresses the whole 16 bits, and I dont perform any maths operations on this register at all. CCPR1 is a 16 bit capture register that captures the contents of TMR1 whenever an event on RC2 is detected, I only use that number in the maths now. Ill paste the code as it stands once its polished up a little bit. Ill put an explanation of what I am doing with it, along side the post =D
 

Thread Starter

dannybeckett

Joined Dec 9, 2009
185
As promised, if anyone's interested - this is the program I am planning to implement to control the persistance of vision led display:

Rich (BB code):
#include <htc.h>
#include <stdint.h>
#include <delay.c>
#include <math.h>

#define _XTAL_FREQ 1000000 // For __delay_us()
#define PIC_CLK 1000000 // For DelayBigUs()
#define RADIUS_ROTOR_MM 220
#define LED_WIDTH_MM 5
#define LED_DUTY_CYCLE 20

__CONFIG (FOSC_INTRC_NOCLKOUT & WDTE_OFF & PWRTE_ON & MCLRE_ON & CP_OFF & CPD_OFF & BOREN_OFF & IESO_OFF & FCMEN_OFF & LVP_OFF & DEBUG_OFF);


/***********
 * Globals *
 ***********/

uint32_t TMR1_FREQ = 0;
float DELAY_H_PIXELS_US = 0;    
uint16_t NUMBER_H_PIXELS = 0;
float DELAY_H_PIXELS_US_ON = 0;
float DELAY_H_PIXELS_US_OFF = 0;
    
/***************
 * Initiatiors *
 ***************
 * Functions to set up the PIC the way we want it.
 */

void initDigitalOut() {
    PORTA = PORTB = PORTC = PORTD = 0;
    TRISA = TRISB = TRISC = TRISD = 0;
    ANSEL = ANSELH = 0;
}

void initIntOscillator() {
    OSCCONbits.IRFC     = 4;        // Internal oscillator frequency set. 3 bits wide, 100 = 1MHz, 11 = 500KHz
    OSCCONbits.OSTS        = 0;        // Use internal oscillator. 1 = ext osc.
    OSCCONbits.HTS        = 1;        // High frequency stability bit (8 MHz to 125 kHz). What do?
    OSCCONbits.LTS        = 0;        // Low frequency stability bit (31KHz). What do?
    OSCCONbits.SCS        = 1;        // System clock select. 1 = int, 0 = ext.
}    


void initTimer1() {
    T1CONbits.T1GINV    = 0;     // Set T1 Gate active high or active low. 1 = high, 0 = low.
    T1CONbits.TMR1GE    = 0;     // 1 = Timer1 is on if Timer1 gate is not active. 0 = Timer1 is on.
    T1CONbits.T1CKPS    = 3;    // Timer1 clock prescale, 2 bits wide. 00 = /1. 11 = /8
    T1CONbits.T1OSCEN    = 0;     // LP oscillator enable for Timer1. Runs at 32.768KHz.
    T1CONbits.T1SYNC    = 0;     // Sync external clock (dont think this applies but sync JIC). Inverted, 0 = sync.
    T1CONbits.TMR1CS    = 0;     // 1 = External clock from T1CLI pin (RC0). 0 = Internal clock. Not sure what this should be when using LP osc!
    T1CONbits.TMR1ON    = 0;     // 1 Enables timer, 0 stops it.
    TMR1H = TMR1L        = 0;     // Clear whatever is in Timer1. Values are unknown on startup.
    
    /******************************
      * Calculate Timer1 Frequency *
     ******************************
      * Unsigned 32bit integer required to deal with large _XTAL_FREQ number.
     */
     
    uint32_t freqTMR1 = PIC_CLK/4;        
    switch(T1CONbits.T1CKPS) {                                    
        case 0 : break;
        case 1 : freqTMR1 = freqTMR1/2; break;
        case 2 : freqTMR1 = freqTMR1/4; break;
        case 3 : freqTMR1 = freqTMR1/8; break;
    }
    TMR1_FREQ = freqTMR1;
    
    T1CONbits.TMR1ON = 1; // Turn Timer1 On
}

void initCCP1CaptureRising() {
    CCP1CONbits.CCP1M    = 0x05;
    /* write to the CCP1CON register, specifically the CCP1M (Enhanced Capture/Compare/PWM mode
     * select bits) selection, and set '0101 = Capture mode, every rising edge'. All other bits
     * in register are not used.
     */
    CCPR1 = 0;
}

void initCCP1Interrupt() {
    INTCONbits.GIE    = 1;    // Global interrupt enable. Enables all unmasked interrupts, must be set. Clears all interrupts also.
    INTCONbits.PEIE    = 1;    // Peripheral interrupt enable. Enables all unmasked peripheral interrupts. Clears all associated interrupts also.
    TRISC2            = 1;    // Pin CCP1 set to input.
    PIE1bits.CCP1IE    = 1;    // CCP1 interrupt enable bit, RC2.
}

void initDimensions() {
    /**********************************************
     * Calculate the number of H-pixels available *
     **********************************************
     * Circumference/LED width will give us the number of independent
     * horizontal pixel positions we have available to use for given 
     * dimentions. Any fraction this sum produces can be truncated without 
     * causing much of an issue. This gets sent to a global variable.
     */
     
    float numberPixelDivisions = (2*M_PI*RADIUS_ROTOR_MM)/LED_WIDTH_MM;
    NUMBER_H_PIXELS = (uint16_t)numberPixelDivisions;
}

/******************************************************************************************************/

void showMeTheLights() {
    int i = 0;
    int Array1[48]; // 48 is the max size an array can be on the PIC16F887. Use multiple arrays for more characters.

    Array1[0] = 0xFF; // Better way would to set up an array with 0x00, 0x01, 0x02... 0xFF and use the numbers to dictate which element to use.
    Array1[1] = 0x81;
    Array1[2] = 0x81;
    Array1[3] = 0x81;
    Array1[4] = 0x81;
    Array1[5] = 0x82;
    Array1[6] = 0x84;
    Array1[7] = 0x78;
    
    while (i != 8) {
        PORTB = Array1;
        DelayBigUs((uint16_t)DELAY_H_PIXELS_US_ON);
        PORTB = 0;
        DelayBigUs((uint16_t)DELAY_H_PIXELS_US_OFF);
        i++;
    }
    PORTB = 0;
    
}

interrupt void math() {
    
    /**********************************************************
     * Calculate the the time of one revolution (H scan rate) *
     **********************************************************
     * Float wont evaluate a full 32 bit number, have to be careful here when
     * using high clock frequencies. 1/freqTMR1 gives time in seconds it takes 
     * for 1 count of TMR1. Multiplying this by CCPR1 gives time in seconds    
     * it takes for 1 revolution to occur. (float) cast on either CCPR1 or      
     * TMR1_FREQ is absolutely neccessary - if performing maths with integers  
     * even using a float type, the calculation is treated as an integers 
     * only, and truncates the fractional part of the answer (gay).
     */

    float revolutionTime_S = (float)CCPR1/TMR1_FREQ; // Checked TMR1_FREQ = 31250. 
    
    /************************************
     * Calculate horizontal pixel delay *
     ************************************
     * Dividing the time it takes for 1 complete revolution by how many pixels
     * we have available will give us the time it takes for the rotor to move
     * from one pixel potition to the next.
     */

    float horizontalDelay_US = revolutionTime_S/NUMBER_H_PIXELS;
    
    /**********************************************************
     * Convert delay time to microseconds, reset timer & flag *
     **********************************************************
     * Convert the delay time in seconds to microseconds, and send that value
     * to the specified global variable, then reset Timer1 and clear CCP1
     * interrupt flag.
     */
    
    float horizontalDelay_US = horizontalDelay_US*1000000;
    DELAY_H_PIXELS_US = (uint16_t)horizontalDelay_US;
    TMR1 = 0;
    PIR1bits.CCP1IF    = 0;
/*    PORTB = CCPR1;
    PORTD = CCPR1 >> 8;
    DelayBigMs(500);
    PORTB = PORTD = 0;
    PORTB = DELAY_H_PIXELS_US;
    PORTD = DELAY_H_PIXELS_US >> 8;    
*/    
/*    The above code was to check whether numbers were being calculated correctly. They were accurate.
    Need to find good accurate delay function
*/                                    

    /********************************
     * Combine delay and duty cycle *
     ********************************/
     
    DELAY_H_PIXELS_US_ON = (DELAY_H_PIXELS_US/100.0)*LED_DUTY_CYCLE;
    DELAY_H_PIXELS_US_OFF = (DELAY_H_PIXELS_US/100.0)*(100 - LED_DUTY_CYCLE);
    
    showMeTheLights();
}


void main() {
    initDigitalOut();
    initIntOscillator();
    initCCP1CaptureRising();
    initCCP1Interrupt();
    initDimensions();
    initTimer1();
    while(1) {}
}
 
Top