G30 and G80 - Access to Native Timers/Counters?

I couldn’t find the answer on the forums…

Is there access to the native timers on the g30 and G80?.. For example to make a frequency counter?

Not directly but you can use register access.

Hey @ gus

I haven’t done any register access yet…Is there any sample code that shows how to do this?

@ Gismofx - Click Me

1 Like

More details https://www.ghielectronics.com/docs/108/register-access

1 Like

@ Gus That’s a great example!

…I’m having some trouble digging up the exact registers…I’ve been flipping through some datasheets and a the STM32F4 programming manual and haven’t found anything relating to the registers I want to attempt to utilize as a counter. I did a similar exercise with an ATTiny85 and the datasheet was clear about which registers to access to enable the counter. I can’t find that type information about these processors. It seems more generic. That being said, I was able to find what pins map to what timers, but that’s the best I could find.

Also, are there any timers that are off limits? Meaning they would cripple something else on the device when I activate it?

TIMER1 is used by the framework, I believe, but I think it’s the only one. The timers are significantly more complex to enable on the STM32 (and ARM in general) than on the AVRs. Instead of (or in addition to) the STM32 datasheet, there’s a “Reference Manual” that contains more of that kind of information.

If GHI’s implementation is still using the timers in the same way that the original open source port had used them, then TIM5 and TIM12 are used by the framework, chained to create a 48bit timer.

I think I found the “reference manual”…1700+ pages. I’m sure I’ll come back with questions.

@ taylorza - Thank you for the correction. I would imagine it’s still the same, but only GHI knows for sure; though, you could probably check which are enabled via the registers…

I’m testing this with my Cerb40II with the STM32F405xx chip.

I’m referencing these documents:
http://www.st.com/web/en/resource/technical/document/datasheet/DM00037051.pdf < STM32F405xx, STM32F407xx Datasheet.

http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf <-RM0090 Reference Manual

I’m using PA3 and Timer 9. And the following code seems to be working. It would probably work on a G30 and G80 with little or no modifications(only change the base registers?).(need to confirm) Unless anyone else can confirm, I can’t guarantee that setting this up will not affect any other functions. I’m still testing this out.

using System;
using System;
using System.Threading;
using Microsoft.SPOT;

using GHI.Processor;

namespace STM32Counter
{
    /*EXAMPLE FROM STM REF GUIDE
     * For example, to configure the upcounter to count in response to a rising edge on the TI2
input, use the following procedure:

     * 1. Configure channel 2 to detect rising edges on the TI2 input by writing CC2S = ‘01' in
the TIMx_CCMR1 register.

     * 2. Configure the input filter duration by writing the IC2F[3:0] bits in the TIMx_CCMR1
register (if no filter is needed, keep IC2F=0000).

     * 3. Select rising edge polarity by writing CC2P=0 and CC2NP=0 in the TIMx_CCER
register.

     * 4. Configure the timer in external clock mode 1 by writing SMS=111 in the TIMx_SMCR
register.

     * 5. Select TI2 as the trigger input source by writing TS=110 in the TIMx_SMCR register.

     * 6. Enable the counter by writing CEN=1 in the TIMx_CR1 register.

     * Note: The capture prescaler is not used for triggering, so you don't need to configure it.
When a rising edge occurs on TI2, the counter counts once and the TIF flag is set.
The delay between the rising edge on TI2 and the actual clock of the counter is due to the
resynchronization circuit on TI2 input.*/

/*Timer 9 Boundaries
 * 0x4001 4000 - 0x4001 43FF
 */

    public class STM32InternalCounter
    {
        public 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

        //timer specific registers
        private const uint TIMx_BaseAddress = 0x40014000;
        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 TIMx_PSC_register = TIMx_BaseAddress + 0x28; //prescaler

        //register to turn timer on an off again
        private const uint RCC_APB2ENR_register = 0x40023800 + 0x44;
        //timer total reset register? Not used.
        private const uint RCC_APB2RSTR_register = 0x40023800 + 0x24; //reset register for timer 9

        //these registers are re-used
        private Register TIMx_CNT; //counter value
        private Register TIMx_EGR; //Use UG to reset the counter value

        /// <summary>
        /// This is the initializer
        /// </summary>
        public STM32InternalCounter()
        {
            InitCounter();
        }

        /// <summary>
        /// This returns the current count value in the TIMX_CNT Register. Max value is 65,536
        /// </summary>
        public uint Count
        {
            get 
            {
                return TIMx_CNT.Value;
            }
        }

        /// <summary>
        /// Internal Method to setup the registers for counter function from exteral source on TIMx
        /// </summary>
        private void InitCounter()
        {

            //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_CCMR = new Register(TIMx_CCMR1_register);
            //1 & 2
            TIMx_CCMR.SetBits(1 << 8);
            //keep IC2F @ 0000
            
            Register TIMx_CCER = new Register(TIMx_CCER_register);
            TIMx_CCER.SetBits(0 << 5 | 0 << 7);
            

            Register TIMx_SMCR = new Register(TIMx_SMCR_register);
            //set the input to input 2 = 110
            //  4&5
            TIMx_SMCR.SetBits(7 << 0 | 6 << 4);
            //optional way to show exactly what bits are set
            //TIMx_SMCR.SetBits(1 << 0 | 1 << 1 | 1 << 2 | 0 << 4 | 1 << 5 | 1 << 6);
            
            

            Register TIMx_CR1 = new Register(TIMx_CR1_register);
            // 6 turn on the counter!
            TIMx_CR1.SetBits(1 << 0);

            Register TIMx_PSC = new Register(TIMx_PSC_register);
            TIMx_PSC.Value = 0;

            TIMx_CNT = new Register(TIMx_CNT_register);
            TIMx_EGR = new Register(TIMx_BaseAddress + 0x14);
            ResetCounter();
        }

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

and this:

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHI.Pins;

using STM32Counter;

namespace CounterTest
{
    public class Program
    {
        public static void Main()
        {
            STM32InternalCounter myCounter = new STM32InternalCounter();
            
            while (true)
            {
                Debug.Print("Count: " + myCounter.Count.ToString());
                Thread.Sleep(1000);
            }


        }

    }
}

THANK YOU GHI for providing Register Access!!!

Updated above post with working code. You need to setup the Alternate Function for the GPIO pin for this to work correctly. The STM Ref Guide Example leaves this part out.