Principle and implementation of C language registration mechanism

Posted by agonzalez on Sat, 20 Nov 2021 18:42:08 +0100

1, Foreword

To understand the registration mechanism, first we need to have a certain understanding of function pointers and callback functions.

My previous blog has explained in detail: Detailed explanation of C language callback function (the most complete in the whole network)

2, Registration mechanism

1. Problem description

In the process of using the timer, the most distressing thing is to define flag and holdtime. Each definition will lead to the flying of flag bits in the interrupt function, and time variables can be seen everywhere in the program. I want to transplant it, but I dare not delete it casually. The program is in a highly coupled state and loses the meaning of. c and. h.

2. How to solve this problem

Introduce registration mechanism. To illustrate the registration mechanism, take an example: when using the camera function, the mobile phone has an operation: send the taken photos. The easiest way to realize this process by program is as follows:

Add the following code to the sending module of the camera:

if (Select Send)
{
     if(Select wechat to send)
     {
         Get sender;
         Select sender;
     }
     else if(choice qq (send)
     {
         Get sender;
         Select sender;
     }
     else if(Select microblog (send)
     {
         Get sender;
         Select sender;
     }
    .
    .Ten thousand lines are omitted here
    .
}

This is the easiest implementation to think of, just like the implementation of the timer above. Where to use it, just define a series of variables. Back to the camera example, suppose that one day a chat software that is more popular than wechat appears, the user installs it and wants to send pictures. What should I do then? Of course, we can only add else if (...) and its implementation in the sending module of the above camera, which means that every time we update a software that needs to use the picture function, we must modify the camera module. Does it feel very similar to our timer?

The essence of registration: decouple each module. The program pays attention to high cohesion and low coupling. My current understanding of this sentence is: high cohesion: each functional module (c file, h file) does not call other modules internally. For example, there should be no state variable in the obstacle function, let alone the operation of zero landmark recovery. It only does one thing to process the IO port information and generate the corresponding obstacle state. Low coupling: the coupling between obstacle function and other modules is only the generated obstacle state. The following is an in-depth discussion of the registration mechanism.

What is registration: according to my current understanding, when a camera wants to send pictures, it faces a variety of sending methods, and each sending method will certainly call different functions. On the contrary, I have many applications and want to use the camera module (compare the timer here). In this case, the camera module defines a registration function for other modules to call to tell the camera that the corresponding sending method is allowed.

#define num_max 20 / / maximum number of devices
 
typedef struct
{
  u8 num;                                //Current number of registered devices
  u8 list_ name[num _max];                //Used to save the list of registered devices
  void (*click[num _max])(u8 * temp);  //Store the sending function addresses of different modules (wechat qq)
}Equiment;
Equiment  COM;            
 
/**************************Register function****************************************/
//Register different functions in the registration list
void  Photo_Register(void(*a)(u8 * temp),u8 list) //Interface provided to external
{
   if(COM.num < num.max)
  {
      COM.click[COM. num]=a;          //Save function address
      COM.List _name[COM.num]=list;   //Save device name to list
      COM.num++;
  }
  else
  {
     /****An error is reported when the maximum number of devices is exceeded******/
  }
}
 
/*Send function in camera*/
void Click(u8 temp)         // Finally, you can send pictures and call this function
{
   u8 i,NUM;
   for(i=0; i<= COM.num ; i++)
   {
    printf("Print a list showing registered devices)
   } 
   NUM =Get(Selected sending method);
   if(!NUM)
   COM.click[NUM](temp);
}
/*******************The above is implemented in the camera************************************/

If you want to use it in wechat, you will be prompted to open the camera permission during the installation process by calling the above registration function. Transfer the self integrated sending function address of wechat itself to the camera. Each time the camera sends, it only needs to judge which devices are registered and select the corresponding method. In this way, no matter how many new applications appear, you only need to register once to use the camera. Perfect decoupling between camera and wechat QQ microblog and other modules!

Similarly, the decoupling of timer can be handled in this way.

Timer application registration mechanism

First, to decouple, we must remove the randomly defined flag bits and time variables, and only one time variable is allowed. Therefore, define a 32-bit time variable without any restrictions. Let it always add itself.

Refer to the timing processing method in arduino: define a function to obtain the current time, save the current time, query the current time again after running for a period of time, and make difference twice to get the running time. It is not difficult to see from the above that the key points are: the function of obtaining the current time, the storage of the current time, and the time after the difference. The following is the implementation method:

time.h

#include "stm32f10x.h"
#ifndef __TIME_H
#define __TIME_H
 
#define TimerID_max 20 / / maximum number of registered devices
#define RunOutOf_time(ID , ms)   ( systime.no w-systime.last[ID -1]< ms ) 
 
typedef struct
{
     u8 ID;                     //Device ID
     u32 now;                   //current time 
     u32 last[TimerID_max];     //Storage time
 
     void (*timer_init)(u16 countdata,u16 freqData);   //Point to initialization function
     u8 (*get_id)(void);                               //Point to get ID function
     void (*refresh)(u8 ID);                           //Point to update time function
 
}SYSTIME;
extern SYSTIME systime;
#endif

time.c

#include "time.h"
 
/*********API provided to external*******************/
void Timer_Init(u16 CountData,u16 FreqData);
unsigned char systime_get(void);
void Refresh(u8 ID);
/***********************************************/
 SYSTIME systime =        definition SYSTIME Type variable and initialize the function pointer
{
     .get_id=systime_get,
     .refresh=Refresh,
     .timer_init=Timer_Init
};
 
/****************************************************/
//Function name: Timer_init
//Description: initialize timer
//Input: interrupt time related
//Output: null
/****************************************************/
void Timer_Init(u16 CountData,u16 FreqData)
{
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);         
 NVIC_InitTypeDef NVIC_InitStructure;                        
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  
 NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;             
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStructure); 
  
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;             
  
 TIM_DeInit(TIM4);
 TIM_TimeBaseStructure.TIM_Prescaler = FreqData;
 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 
 TIM_ClearFlag(TIM4, TIM_FLAG_Update); 
 TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);    
 TIM_Cmd(TIM4, ENABLE);             
 
}
 
/****************************************************/
//Function name: systime_get
//Description: get the current time and generate a registration
//Input: null
//Output: null
/****************************************************/
unsigned char systime_get()
{
    if(systime.ID<TimerID_max)
    {
        systime.last[systime.ID]=systime.now;
 systime.ID++;
        return systime.ID;
    }
    else 
 return 0;
}
/****************************************************/
//Function name: Refresh
//Description: updates the current time
//Input: obtained ID
//Output: null
/****************************************************/
void Refresh(u8 ID)
{
    systime.last[ID-1]=systime.now;
}
/****************************************************/
//Function name: TIM4_IRQHandler 
//Description: 1ms timer
//Input: null
//Output: null
/****************************************************/
void TIM4_IRQHandler(void) 
{
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
 TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
 systime.now++;          
    }
}

After. c and. h implement the above, there are only three functions facing the outside

/*********API provided to external*******************/
void Timer_Init(u16 CountData,u16 FreqData);    
unsigned char systime_get(void);
void Refresh(u8 ID);
/***********************************************/

Usage of timer

1. /*initialization*/
2.
  /**********Task 1 realizes operation and other flashing, with a frequency of 1s**********/    
void task1()
{
        static u8 Task1_ID;
 if(!Task1_ID)
    Task1_ID=systime.get_id();
        if(RunOutOf_time(Task1_ID,1000))
    RUN_LED()=1; 
 else if(RunOutOf_time(Task1_ID,2000))
    RUN_LED()=0; 
 else if(RunOutOf_time(Task1_ID,3000))
    RUN_LED()=1; 
 else if(RunOutOf_time(Task1_ID,4000))
    RUN_LED()=0; 
 else if(RunOutOf_time(Task1_ID,5000)
            RUN_LED()=1; 
 else
    systime.refresh(Task1_ID);   
}
 
/*******************Task 2 realizes operation and other flashing, with a frequency of 100ms**********************/  
void task2()
{
 static u8 Task1_ID;
 if(!Task1_ID)
   Task1_ID=systime.get_id();
 if(RunOutOf_time(Task1_ID,100))
   RUN_LED()=1; 
 else if(RunOutOf_time(Task1_ID,200))
          RUN_LED()=0; 
 else if(RunOutOf_time(Task1_ID,300))
   RUN_LED()=1; 
 else if(RunOutOf_time(Task1_ID,400))
   RUN_LED()=0; 
        else if(RunOutOf_time(Task1_ID,500))
        RUN_LED()=1; 
 else
   systime.refresh(Task1_ID);
}
 
/***************main The function realizes that task 1 runs for 10s and task 2 runs for 10s****************/
int main(void)
{
        static u8 main_ID;
        System_Init();
        while(1)
        {   
    if(!main_ID)
       main_ID=systime.get_id();
    if(RunOutOf_time(main_ID,10000))
       task1();
    else if(RunOutOf_time(main_ID,20000))
       task2();
    else
             systime.refresh(main_ID); 
        }
}

Above, if any function wants to use the timer, it only needs to set up an ID storage variable as required to store the ID allocated during registration, then it can call the timer, and it can be easily transplanted on any platform. It only needs to modify the hardware initialization.

This program cannot realize task execution at any time. For example, a task needs to be executed once in 100ms, which can only be used for execution within a time period. The reason is that the main loop of the program will take time, resulting in the inability to accurately capture the 100ms time during polling. To achieve this effect, it needs to be improved or completely written in another way. For example, the capture is placed in the interrupt, and the main loop queries the 100ms enable bit.

Topics: C architecture