How to stop/reset timer in a callback?

Hello!

 

I'm writing my own framework were I'm using the timer module.

I also define my own callback function, that intercepts the callback from the timer module.

In my callback function I need to stop or to restart the timer that caused the calback in the first place.

 

How to do it?

 

Both, the stop and reset functions of the timer, requires p_ctrl to do it:

ssp_err_t (* stop)(timer_ctrl_t      * const p_ctrl);

ssp_err_t (* reset)(timer_ctrl_t      * const p_ctrl);

 

However in the callback function of the timer, I only get the p_args, that I can use:

void  (* p_callback)(timer_callback_args_t * p_args);

 

The p_args contains:

typedef struct st_timer_callback_args
{
    /** Placeholder for user data.  Set in timer_api_t::open function in ::timer_cfg_t. */
    void const   * p_context;
    timer_event_t  event;        ///< The event can be used to identify what caused the callback (overflow or error).
} timer_callback_args_t;

The event dosen't help too much.

 

Can I extract the p_ctrl out of the p_context somehow?

 

I will be gratefull for any help.

 

Regards,

Gregor

  • Hi,

    Yes, you get p_ctrl from p_context in the callback.

    void my_call_back(timer_callback_args_t *p_args)
    {
    timer_instance_t * p_my_timer;
    timer_ctrl_t * p_my_timer_ctrl;

    p_my_timer = (timer_instance_t *)p_args->p_context;

    p_my_timer_ctrl = p_my_timer->p_ctrl;
    }

    Regards,

    Ian.
  • In reply to Ian:

    Hello Ian!

     

    Thank you for that. Now it's working.

     

    By the way, what other informations contain the context?

    Can I also use it to extract from it informations about other drivers included into the framwork? Or this works only for the driver, that has triggered the ISR (callback) i.e. in this case only for the timer driver?

     

    What I want to do: I would like to set 2 variables saved in the p_ctrl of another  (in the same framework) used driver.

    So every time the callback from the timer is called, I will like to stop the and reset the timer (now I know how) and then set 2 variables in the p_ctrl of my own driver also included into the framework.

     

    How to do this?

     

    Thank you for your support!

    Regards,

    Gregor

  • In reply to gregoryg:

    Hi,

    The p_context pointer has the following definition.

    void const * p_context;

    So, it can point to any data type.

    It is initialised from the configuration structure passed to the timer open API call. For a HAL only project the structure looks like below (edited):

    static const timer_cfg_t g_timer0_cfg =
    { .mode = TIMER_MODE_PERIODIC,
    .period = 10,
    ...
    .p_callback = my_call_back,
    .p_context = &g_timer0,
    .p_extend = &g_timer0_extend,
    .irq_ipl = (8), };

    /* Instance structure to use this module. */
    const timer_instance_t g_timer0 =
    { .p_ctrl = &g_timer0_ctrl, .p_cfg = &g_timer0_cfg, .p_api = &g_timer_on_gpt };

    So, if you are using the timer below a custom framework module you could make this p_context pointer point to whatever structure etc that you wish. Override the configuration structure element to point to your custom structure.

    Regards,

    Ian.
  • In reply to Ian:

    Hello Ian!

     

    Thank you once again for your reply!

    I think, that I geth the idea of overriding the structure element, but I have no clue where to do it...

     

    Maybe I will try to write it in a different way.

    I have "my_driver" which has it's own "my_driver_cfg_t:

                const my_driver_cfg_t my_driver_name_cfg =
                {
                .channel,
                .rate,
                .addr_mode,
                .p_callback,
                .irq_ipl,
                .p_context,
                .p_extend           
                };

    The "my_driver" instance has 2 variables, that I would like to access from a different parts of the driver code:

    typedef struct st_my_driver_instance_ctrl
    {
        ...
        uint8_t         Current_Timer_Ticks;   
        uint8_t         Overall_Timer_Ticks;  

        ...

    }my_driver_instance_ctrl_t;

     

     

    The same "my_driver" uses a timer module, that the user must add to it (GPT or APT).

    I intercept the callback from the timer module in the initialization function of "my_driver" to point to my own function:

        //Intercept the callback function
        hal_timer_cfg.p_callback = &gpt_callback;

     

    Now, the part you have already helped me (thank you for that). In this gpt_callback function I stop/reset the timer like you mentioned:

       

    static void gpt_callback (timer_callback_args_t * p_args)

    {
        timer_instance_t * p_timer_instance;
        timer_api_t  const * p_timer_api;
        timer_ctrl_t *  p_timer_ctrl;
       
        p_timer_instance = (timer_instance_t *)p_args->p_context;
        p_timer_api = p_timer_instance->p_api;
        p_timer_ctrl = p_timer_instance->p_ctrl;
        

        /** Reset the timer counter value to the initial value */
        p_timer_api->reset(p_timer_ctrl);  


        p_ctrl->Current_Timer_Ticks++;
        p_ctrl->Overall_Timer_Ticks++;

    }

     

    I have marked red, what I really want do do.  Just to increase the value of thouse variables.

    Unfortunately they are saved in the p_ctrl of the "my_driver". p_ctrl it's of type my_driver_instance_ctrl_t

     

    How to get the access to this p_ctrl?

    The same way like for the p_timer_ctrl will not work, since the p_context reffer to the timer only, right?

     

    Regards and many thanks!

    Gregor

  • In reply to gregoryg:

    Hello!

     

    To make it more visible what my problem is here the picture:

     

     

    The callback interception from the timer_driver works now.

    How to get the blue text to work? I need to get somehow the refference to the p_ctrl in the function gpt_callback.

    But the function gpt_callback has no access to the p_ctrl, right?

     

    Thank you for any hint how to solve it!

     

    Ragrads,

    Gregor

  • In reply to gregoryg:

    Hi,

    When you override the p_callback pointer so the the GPT HAL ISR calls the callback in your framework driver could you also override the p_context pointer in the GPT HAL so that it points to p_ctrl of your framework driver?

    This should (I have not tested it) allow you to access the lower level driver (if you have a pointer to the lower level in your p_ctrl structure) so you can stop/reset etc and also access the counter variables in your framework driver p_ctrl.

    The p_context pointer is not used by the driver code and is there so the context can be established in an ISR callback by the user. So, you can change it to whatever you need.

    I hope that makes sense.

    Regards,

    Ian.
  • In reply to Ian:

    Hello Ian!

     

    I think, that I slowly get what you mean. But still the implementation lefts a lot of questions for me:)

     

    My p_ctrl stucture from "my_driver" have something like that:

    typedef struct st_my_driver_instance_ctrl
    {                                  
        uint32_t    open;                                            
        void      * p_reg;                                           

        timer_instance_t const * p_lower_lvl_timer;                
        void         (* p_callback)(my_driver_callback_args_t * p_args); 
        void const    * p_context;                                
       
        uint8_t           Current_Timer_Ticks;   
        uint8_t           Overall_Timer_Ticks;  
        volatile bool    transaction_completed;  
    } my_driver_instance_ctrl_t;

     

    In the initialization function I override the callback from the timer like this:

    my_driver_open  (my_driver_instance_ctrl_t * p_ctrl, my_driver_cfg_t * p_cfg)

    {

    ...

    timer_cfg_t hal_timer_cfg = *(p_cfg->p_lower_lvl_timer->p_cfg);

    hal_timer_cfg.p_callback = &gpt_callback;

    ...

    }

     

    So if I got you right, to override the p_context from the GPT HAL is to write in the initialization function an additional line:

    hal_timer_cfg.p_context = p_ctrl->p_lower_lvl_timer->p_ctrl;

     

    Ok, but how to use it then in the gpt_callback (timer_callback_args_t * p_args)?

    my_driver_instance_ctrl_t * p_ctrl = ???

     

    Thank you once again for any help!

    Regards,

    Gregor

  • In reply to gregoryg:

    Hi,

    In your driver open API if you set the HAL driver p_context pointer to the p_ctrl of your timer driver you should be able to access everything you need in the callback.

    hal_timer_cfg.p_context = p_ctrl;

    As you have a pointer to the low level HAL instance you can get to the start and stop. As the pointer is to the higher level p_ctrl then you can get to the counters.

    Regards,

    Ian.
  • In reply to Ian:

    Hi!

     

    Ok, now I think I got this.

     

    I will write in the open function thouse 2 lines (one to intercept the callback from timer and the second to redirect the p_contex)t:


        hal_timer_cfg.p_callback = &gpt_callback;
        hal_timer_cfg.p_context = p_ctrl;

     

    And then in the gpt_callback(timer_callback_args_t * p_args):

        my_driver_instance_ctrl_t * p_ctrl = (my_driver_instance_ctrl_t *) p_args->p_context;
        
        //Increment the ticks
        p_ctrl->Current_Timer_Ticks++;
        p_ctrl->Overall_Timer_Ticks++;


        timer_api_t  const * p_timer_api = p_ctrl->p_lower_lvl_timer->p_api;
        timer_ctrl_t * p_timer_ctrl = p_ctrl->p_lower_lvl_timer->p_ctrl;
       
        /** Reset the timer counter value to the initial value */
        p_timer_api->reset(p_timer_ctrl);   

     

    What is your opinion?

    The code itself can compile without problems, but I haven't tested it with a real HW yet.

    I will get the chance to test it in ~3 weeks, since I'm still waiting for the PCB.

     

    Regards,

    Gregor

  • In reply to gregoryg:

    Hi,

    I believe this will do what you need.

    Regards,

    Ian.
  • In reply to Ian:

    Hi Ian!

    I finally can test the code with a HW.

    Unfortunateley during the debug the code hangs up at this line during the initialization:

    This is the initialization function for the timer.

    It seems that there is a problem with the initialization of the p_cfg.

     

    My p_cfg contains the low level driver like discussed:

        timer_instance_t const * const p_lower_lvl_timer;             ///< Pointer to the Timer instance

     

    During the compilation I get no errors at all (no filters are enabled).

     

    Do you have any idea what can cause this?

    Regards,

    Gregor

     

  • In reply to gregoryg:

    Hi,

    How does the code hang at this point? It looks like it is just an address assignment so I am not sure why code execution would stop here unless there is some sort of hardware contention.

    Regards,

    Ian.
  • In reply to Ian:

    Hi Ian!

     

    Yes, I don't understand this also. I have checked for the HW with another program that uses I2C and Timer and everything works fine. So I think that the HW should be ok.

    I think the problem could be in the type/availablity of the p_cfg.

     

    Here is my code for the open function, were I first open the I2C and then try to open the Timer (code parts with error handling deleted for better reading):

    ssp_err_t R_SMB_MasterOpen (smb_ctrl_t * const p_api_ctrl, smb_cfg_t  const   * const p_cfg)
    {
        smb_instance_ctrl_t * p_ctrl = (smb_instance_ctrl_t *) p_api_ctrl;
        ssp_err_t err;

        /** Initialization for the IIC */
        ssp_feature_t ssp_feature = {{(ssp_ip_t) 0U}};
        ssp_feature.channel = p_cfg->channel;
        ssp_feature.unit = 0U;
        ssp_feature.id = SSP_IP_IIC;
        
        fmi_feature_info_t info = {0U};
        err = g_fmi_on_fmi.productFeatureGet(&ssp_feature, &info);
        SMB_ERROR_RETURN(SSP_SUCCESS == err, err);

        // Save the type of the IIC hw into the base register
        p_ctrl->p_reg = (R_IIC0_Type *) info.ptr;

       
        /** Lock the I2C HW */
        err = R_BSP_HardwareLock(&ssp_feature);
        SMB_ERROR_RETURN(SSP_SUCCESS == err, err);

        // set valid interrupts with user provided priority
        err = smb_set_valid_interrupts_priority(p_ctrl, p_cfg);

        // Open the hardware in master mode
        err = smb_open_hw_master(p_ctrl, &ssp_feature);
       

        // Open the GPT HW in counter overflow mode
        err = gpt_open_hw(p_ctrl, p_cfg);

        return SSP_SUCCESS;
    }

     

    The timer will be initialized in the gpt_open_hw function. The problematic line is marked red:

    static ssp_err_t  gpt_open_hw (smb_instance_ctrl_t * const p_ctrl, smb_cfg_t const * const p_cfg)
    {    
        ssp_err_t ssp_err = SSP_SUCCESS;
       
        // Local copy to use shorter names of ctrl and api pointers
        timer_api_t const * const p_timer_api = p_ctrl->p_lower_lvl_timer->p_api;
        timer_ctrl_t * const p_timer_ctrl = p_ctrl->p_lower_lvl_timer->p_ctrl;    
        timer_cfg_t hal_timer_cfg = *(p_cfg->p_lower_lvl_timer->p_cfg);
        
        //Reset the callback
        hal_timer_cfg.p_callback = NULL;
        hal_timer_cfg.p_context = NULL;
        
        //Intercept the callback function
        hal_timer_cfg.p_callback = &gpt_callback;
        //Set Timer driver context to the handle for the framework module to use in the ISR (Callback)
        hal_timer_cfg.p_context = p_ctrl;
        
        //Now open the Timer driver
        ssp_err = p_timer_api->open(p_timer_ctrl, &hal_timer_cfg);

        return ssp_err;
    }

     

    Here is also the smb_cfg_t:

    typedef struct st_smb_cfg
    {
        /** Generic configuration */
        uint8_t         channel;                                   
        smb_rate_t      rate;                                      
        uint16_t        master;                                     
        smb_addr_mode_t addr_mode;                                 
        uint8_t         irq_ipl;                                   

        /** Parameters to control software behavior */
        void                     (* p_callback)(smb_callback_args_t * p_args);                    
        uint8_t                 timer_channel;                              
        timer_instance_t const * const p_lower_lvl_timer;                    
        void                     const * p_context;                                 

    } smb_cfg_t;

     

    And the smb_instance_ctrl_t:

    typedef struct st_smb_instance_ctrl
    {
        uint32_t    open;                                           
        void      * p_reg;                                           

        timer_instance_t const * p_lower_lvl_timer;                
        void         (* p_callback)(smb_callback_args_t * p_args);
        
        /* Current transfer information. */
        uint8_t     *   p_buff;               

        uint8_t         Current_Timer_Ticks;    
        uint8_t         Overall_Timer_Ticks;   
       
    } smb_instance_ctrl_t;

     

    Is there any basic error? I'm reading the code over and over... Please let me know if you can see anything wrong.

    Regards,

    Gregor

  • In reply to gregoryg:

    Hi Ian!

     

    Ok, I think I have found the source of my problems. This is the .xml file.

     

    I forgot to add the refference to the low level driver (i.e. timer module) in the p_cfg:

    .p_lower_lvl_timer    = &amp;${"module.driver.r_smb_master.requires.timer::module.driver.timer.name},

     

    Ok, now it's there, but I now get the error during compilation:

    error: 'SYNERGY_NOT_DEFINED' undeclared here (not in a function)
       .p_lower_lvl_timer = &SYNERGY_NOT_DEFINED,

     

    This means also, that in the .c file, the compiler is not able to set the variable for timer.

     

    Do I have to declare it in the .xml file also?

    I thought, that this will happen automatically...

     

    Regards,

    Gregor