Example Of How To Set Up Input Capture via Registers for STM34F4x - I think

In continuation of my previous post about setting up a counter via register access, I’m trying to setup Input Capture to measure a pulse’s period.
Previous Post: [url]https://www.ghielectronics.com/community/forum/topic?id=22822&page=2#msg212726[/url]

/*
I’ve tried to follow the reference guide’s example slightly modified for Channel 2 plus the GPIO alternate function setup for Timer 9 Input Channel 2. I seem to be getting Zero Values for both CCR1 and CCR2 which should show period and duty cycle. This is regardless of prescale value and frequency and duty cycle of the input PWM.
*/

So, I think I’ve got this working…but not as expected…but maybe I’ve misunderstood. Assuming the Timer Clock is at 168Mhz for the Cerb40II and the prescaler set in the code below, CCR1+CCR2 = Total Period. and CCR2/Total Period = Duty Cycle.

I though CCR1 was supposed to be the Period? Anyway, anyone else want to give this a try?

On another note, sometimes when I re-deploy CCR1 and CCR2 are zero and I need to unplug it, and redeploy to get it to work. Maybe a bug in the setup code?

Here’s my basic code:

    public class STMInternalInputCapture
    { 
        /* Example setup
                This mode is a particular case of input capture mode. The procedure is the same except:
        • Two ICx signals are mapped on the same TIx input.
        • These 2 ICx signals are active on edges with opposite polarity.
        • One of the two TIxFP signals is selected as trigger input and the slave mode controller
        is configured in reset mode.
        For example, you can measure the period (in TIMx_CCR1 register) and the duty cycle (in
        TIMx_CCR2 register) of the PWM applied on TI1 using the following procedure (depending
        on CK_INT frequency and prescaler value):
        
         * 1. Select the active input for TIMx_CCR1: write the CC1S bits to ‘01' in the TIMx_CCMR1
        register (TI1 selected).
        
         * 2. Select the active polarity for TI1FP1 (used both for capture in TIMx_CCR1 and counter
        clear): program the CC1P and CC1NP bits to ‘00' (active on rising edge).
        
         * 3. Select the active input for TIMx_CCR2: write the CC2S bits to ‘10' in the TIMx_CCMR1
        register (TI1 selected).
        
         * 4. Select the active polarity for TI1FP2 (used for capture in TIMx_CCR2): program the
        CC2P and CC2NP bits to ‘11' (active on falling edge).
        
         * 5. Select the valid trigger input: write the TS bits to ‘101' in the TIMx_SMCR register
        (TI1FP1 selected).
        
         * 6. Configure the slave mode controller in reset mode: write the SMS bits to ‘100' in the
        TIMx_SMCR register.
        
         * 7. Enable the captures: write the CC1E and CC2E bits to ‘1' in the TIMx_CCER register.
          */

        private Register TIMx_CCR1; //= new Register(); //+0x34 //this is the capture register1
        private Register TIMx_CCR2;// = new Register(); //+0x38 //this is the capture register2

        private Register TIMx_CNT; //register count
        Register TIMx_EGR;


        private const uint TIMx_BaseAddress = 0x40014000; //Timer 9 Base Address
        private const uint TIMx_PSC_register = TIMx_BaseAddress + 0x28; //prescaler


        private const uint GPIOA_BaseAddress = 0x40020000; //sect 8.4 -enable alternate function
        private const uint GPIOA_AFRL_address = GPIOA_BaseAddress + 0x20; //enable the spcific alternate function for the specific pin

        //register to turn timer on an off again
        private const uint RCC_APB2ENR_register = 0x40023800 + 0x44;


        private const uint TIMx_CCMR1_register = TIMx_BaseAddress + 0x18;
        private const uint TIMx_CCER_register = TIMx_BaseAddress + 0x20;
        private const uint TIMx_SMCR_register = TIMx_BaseAddress + 0x08;
        private const uint TIMx_CR1_register = TIMx_BaseAddress + 0;
        private const uint TIMx_CNT_register = TIMx_BaseAddress + 0x24; //the value of the counter

        private const uint RCC_APB2RSTR_register = 0x40023800 + 0x24;

        private const uint FCPU = 84 * 1000000; //the clocks are 84Mhz with 0 prescale

        public STMInternalInputCapture()
        {
        init();
        }
    
        private void init()
        {
            TIMx_CCR1 = new Register(TIMx_BaseAddress+0x34); //+0x34 //this is the capture register1
            TIMx_CCR2 = new Register(TIMx_BaseAddress + 0x38);

            //Register RCC_APB2RSTR = new Register(RCC_APB2RSTR_register);
            //RCC_APB2RSTR.SetBits(1 << 16);

            //setup alternate function for the GPIO
            Register GPIOA_MODER = new Register(GPIOA_BaseAddress);
            GPIOA_MODER.SetBits(1 << 7 | 0 << 6);

            Register GPIOA_AFRL = new Register(GPIOA_AFRL_address);//Alt Func register page 287 in Ref Manual & page 61 in Datasheet
            GPIOA_AFRL.SetBits(0 << 15 | 0 << 14 | 1 << 13 | 1 << 12); //use AF3 "0011" TIM9_CH2

            //this may be redundant
            Register RCC_APB2ENR = new Register(RCC_APB2ENR_register);
            RCC_APB2ENR.SetBits(1 << 16);


            //setup prescaler
            //The counter clock frequency CK_CNT is equal to fCK_PSC / (PSC[15:0] + 1).
            Register TIMx_PSC = new Register(TIMx_PSC_register);
            TIMx_PSC.Value = 0;
            TIMx_PSC.Value = 419;
            
            
            //step1
            //capture/compare mode register 1
            Register TIMx_CCMR1 = new Register(TIMx_CCMR1_register);//0x18 //TI2 CCS1 "10"
            TIMx_CCMR1.ClearBits(1 << 1 | 1 << 0);
            TIMx_CCMR1.SetBits(1 << 1);// | 0 << 0); //CH2 is input or CC1S
            
            //step2
            Register TIMx_CCER = new Register(TIMx_CCER_register); //0x20 ccenable register //step2
            //TIMx_CCER.SetBits(0 << 3 | 0 << 1); //CC1p cc1np 00 //maybe clear bits to make sure?
            TIMx_CCER.ClearBits(1 << 3 | 1 << 1); //00: noninverted/rising edge
            
            //step3 don't need CCR2, but will setup anyway
            TIMx_CCMR1.ClearBits(1 << 9 | 1 << 8);
            TIMx_CCMR1.SetBits(1 << 8); //USE TI2
            
            //step4 not needed, but will do anyway
            TIMx_CCER.ClearBits(1 << 7 | 1 << 5);
            TIMx_CCER.SetBits(1 << 7 | 1 << 5);//both edges?11

            //step5 /6
            Register TIMx_SMCR = new Register(TIMx_SMCR_register);
            TIMx_SMCR.ClearBits(247); //11110111
            //TS
            TIMx_SMCR.SetBits(1 << 6 | 1 << 5 );  //SMS 110 TI2 
            
            //SMS
            TIMx_SMCR.SetBits(1 << 2 );
            
            TIMx_EGR = new Register(TIMx_BaseAddress + 0x14);
                   
            Register TIMx_DIER = new Register(TIMx_BaseAddress + 0x0C);
            
            //Interrupt Fiddle
            //TIMx_DIER.ClearBits(1 << 1);
            //71 = 1000111
            TIMx_DIER.ClearBits(71);
            //TIMx_DIER.SetBits(1 << 6);
            //TIMx_CCER.TIMx_DIER.SetBits(1 << 0 | 1 << 1 | 1 << 2);

            TIMx_EGR.ClearBits(71);
            /*
            TIMx_EGR.SetBits(1 << 6);
            TIMx_EGR.SetBits(1 << 1);
            TIMx_EGR.SetBits(1 << 2);
            TIMx_EGR.SetBits(1 << 0);
            */
            
            
            //step 7
            //enable the CC1E - Enable the counters!
            TIMx_CCER.SetBits(1 << 0);
            TIMx_CCER.SetBits(1 << 4);

            TIMx_CNT = new Register(TIMx_CNT_register);
            //ResetCounter();
        }

        public uint ReadCCR1()
        {
            return TIMx_CCR1.Value;
        }

        public uint ReadCCR2()
        {
            return TIMx_CCR2.Value;
        }
        public uint ReadTimerCount()
        {
            return TIMx_CNT.Value;
        }
        
        /// <summary>
        /// Reset the TIMx_CNT register via the TIMx_EGR UG bit
        /// </summary>
        public void ResetCounter()
        {
            TIMx_EGR.SetBits(1 << 0);
        }
}

public class Program
    {
        public static void Main()
        {

            STMInternalInputCapture sensor = new STMInternalInputCapture();
            
            PWM pulses = new PWM(GHI.Pins.FEZCerb40II.PwmOutput.PA7, 15, .1, false);
            pulses.Start();

            while (true)
            {
                
               
                uint freqTime;
                uint duty;
                uint timer;
                freqTime = sensor.ReadCCR1();
                duty = sensor.ReadCCR2();
                timer = sensor.ReadTimerCount();
                Debug.Print("Ticks: " + freqTime.ToString());
                Debug.Print("Duty Ticks: " + duty.ToString());
                


                Thread.Sleep(1000);


            }


        }

    }
}
1 Like

I’ve made more tweaks a did a lot more trial and error and seem to be getting more consistent results. But, still bizarre.

[ul]CCR1 = 1-DutyCycle
CCR2 = Period[/ul]

I’ve only had to change the init() method (There are probably some redundant and/or superfluous setups, but I’m trying to cover all the bases of the timer setup:

        /// <summary>
       #region Register Addresses
        private Register TIMx_CCR1; //= new Register(); //+0x34 //this is the capture register1
        private Register TIMx_CCR2;// = new Register(); //+0x38 //this is the capture register2

        private Register TIMx_CNT; //register count
        Register TIMx_EGR;


        private const uint TIMx_BaseAddress = 0x40014000; //Timer 9 Base Address
        private const uint TIMx_PSC_register = TIMx_BaseAddress + 0x28; //prescaler


        private const uint GPIOA_BaseAddress = 0x40020000; //sect 8.4 -enable alternate function
        private const uint GPIOA_AFRL_address = GPIOA_BaseAddress + 0x20; //enable the spcific alternate function for the specific pin

        //register to turn timer on an off again
        private const uint RCC_APB2ENR_register = 0x40023800 + 0x44;


        private const uint TIMx_CCMR1_register = TIMx_BaseAddress + 0x18;
        private const uint TIMx_CCER_register = TIMx_BaseAddress + 0x20;
        private const uint TIMx_SMCR_register = TIMx_BaseAddress + 0x08;
        private const uint TIMx_CR1_register = TIMx_BaseAddress + 0;
        private const uint TIMx_CNT_register = TIMx_BaseAddress + 0x24; //the value of the counter

        private const uint RCC_APB2RSTR_register = 0x40023800 + 0x24;
        #endregion

        private const uint FCPU = 168 * 1000000; //the clocks are 168Mhz with 0 prescale
        private const uint PSC = 525; //change this for your fruquency range - to do: change on the fly
        
        //object blah = new object();


        public STMInternalInputCapture()
        {
        init();
        }

        /*public double FrequencyMode()
        {
 
        }
        */
        public double Frequency2()
        {
            double freq;
            double timerFrequency = FCPU / (PSC);
            double period = ReadCCR2();
            if (period == 0)
            {
                freq = 0;
            }
            else
            {
                freq = timerFrequency / period;
            }
            return freq;
        }        



/// Initialize all of the registers for PWM capture
        /// </summary>
        private void init()
        {
            TIMx_CCR1 = new Register(TIMx_BaseAddress+0x34); //+0x34 //this is the capture register1
            TIMx_CCR2 = new Register(TIMx_BaseAddress + 0x38);

            //Register RCC_APB2RSTR = new Register(RCC_APB2RSTR_register);
            //RCC_APB2RSTR.SetBits(1 << 16);

            Register GPIOA_PUPDR = new Register(GPIOA_BaseAddress + 0x0C);
            GPIOA_PUPDR.ClearBits(3 << 6); //11
            GPIOA_PUPDR.SetBits(1 << 7); //10 pull down 


            //setup alternate function for the GPIO
            Register GPIOA_MODER = new Register(GPIOA_BaseAddress);
            GPIOA_MODER.SetBits(1 << 7 | 0 << 6);

            Register GPIOA_AFRL = new Register(GPIOA_AFRL_address);//Alt Func register page 287 in Ref Manual & page 61 in Datasheet
            GPIOA_AFRL.SetBits(0 << 15 | 0 << 14 | 1 << 13 | 1 << 12); //use AF3 "0011" TIM9_CH2

            //this may be redundant
            Register RCC_APB2ENR = new Register(RCC_APB2ENR_register);
            RCC_APB2ENR.SetBits(1 << 16);


            Register TIMx_CR1 = new Register(TIMx_CR1_register);
            //TIMx_CR1.SetBits(1 << 7); //auto reload ?
            TIMx_CR1.Value = 0;

            //setup prescaler
            //The counter clock frequency CK_CNT is equal to fCK_PSC / (PSC[15:0] + 1).
            Register TIMx_PSC = new Register(TIMx_PSC_register);
            TIMx_PSC.Value = 0;
            TIMx_PSC.Value = PSC-1;
            
            
            //step1
            //capture/compare mode register 1
            Register TIMx_CCMR1 = new Register(TIMx_CCMR1_register);//0x18 //TI2 CCS1 "10"
            //TIMx_CCMR1.ClearBits(1 << 1 | 1 << 0);
            TIMx_CCMR1.Value = 0;
            TIMx_CCMR1.SetBits(1 << 1);// | 0 << 0); //CH2 is input or CC1S
            
            //step2
            Register TIMx_CCER = new Register(TIMx_CCER_register); //0x20 ccenable register //step2
            //TIMx_CCER.SetBits(0 << 3 | 0 << 1); //CC1p cc1np 00 //maybe clear bits to make sure?
            TIMx_CCER.Value = 0;
            //TIMx_CCER.ClearBits(1 << 3 | 1 << 1); //00: noninverted/rising edge
            //TIMx_CCER.Value = (0<<3| 

            //step3 don't need CCR2, but will setup anyway
            //TIMx_CCMR1.ClearBits(1 << 9 | 1 << 8);
            TIMx_CCMR1.SetBits(1 << 8); //USE TI2
            
            //step4 not needed, but will do anyway
            //TIMx_CCER.ClearBits(1 << 7 | 1 << 5);
            //@ only falling edge?
            TIMx_CCER.SetBits(0 << 7 | 1 << 5);//both edges?11  01 falling edge

            //step5 /6
            Register TIMx_SMCR = new Register(TIMx_SMCR_register);
            TIMx_SMCR.Value = 0;
            //TS
            TIMx_SMCR.SetBits(1 << 6 | 1 << 5 );  //SMS 110 TI2 
            //SMS
            TIMx_SMCR.SetBits(1 << 2 );//reset on trgi
            //TIMx_SMCR.SetBits(1 << 7);

            TIMx_EGR = new Register(TIMx_BaseAddress + 0x14);
            TIMx_EGR.Value = 0;
            //TIMx_EGR.SetBits(1 << 2 | 1 << 1);


            //Register TIMx_ARR = new Register(TIMx_BaseAddress + 0x2C);
            //TIMx_ARR.Value = 0;

            Register TIMx_DIER = new Register(TIMx_BaseAddress + 0x0C);
            TIMx_DIER.Value = 0; //disable all interrupts

            //TIMx_EGR.ClearBits(71);
            TIMx_EGR.Value = 0; //clear all event generation

            Register TIMx_SR = new Register(TIMx_BaseAddress + 0x10);
            TIMx_SR.Value = 0;

            /*
            TIMx_EGR.SetBits(1 << 6);
            TIMx_EGR.SetBits(1 << 1);
            TIMx_EGR.SetBits(1 << 2);
            TIMx_EGR.SetBits(1 << 0);
            */
            
            
            //step 7
            //enable the CC1E - Enable the capture counters!
            TIMx_CCER.SetBits(1 << 0 | 1 << 4);
            
            //reset the CCRs
            TIMx_CCR1.Value = 0;
            TIMx_CCR2.Value = 0;

            TIMx_CNT = new Register(TIMx_CNT_register);
            ResetCounter();


            // 6 turn on the timer/counter!
            TIMx_CR1.SetBits(1 << 0);


        }

I’m going to be using this code for my project and will keep this updated if I get more information. I’d be happy to explain what’s what, but it doesn’t seem to work as expected, but it [em]does[/em] work. If anyone is willing to try or collaborate, please let me know.

And here’s a little video showing it in action using both PWM Capture and an Upcounter from the same PWM signal:

1 Like