嵌入式软件开发中的注册机制

一起学嵌入式 2024-03-26 08:43
扫描关注一起学嵌入式,一起学习,一起成长

问题描述

在使用定时器的过程中最令人苦恼的就是,定义flag,holdtime,每用一次定义就会导致中断函数中标志位满天飞,时间变量在程序中随处可见。

在想要移植,又不敢随便删除。程序处于高耦合状态,失去了一个.c 一个 .h的意义。

如何解决这种问题

引入注册机制。为了方便说明注册机制,举一个例子:手机在使用相机这个功能时,有一个操作:将拍摄的照片发送出去。

以程序来实现这一过程,最容易想到的方法如下:

在相机的发送模块添加以下代码:

if (选择发送)
{
     if(选择微信发送)
     {
         获取发送人;
         选择发送人;
     }
     else if(选择qq发送)
     {
         获取发送人;
         选择发送人;
     }
     else if(选择微博发送)
     {
         获取发送人;
         选择发送人;
     }
    .
    .此处省略一万行
    .
}

这是最容易想到的实现方式,就如上面定时器的实现方式,哪里要用了,再定义一系列变量就是了。

回到相机例子,假设某一天一个比微信还火的聊天软件出现了,用户安装了,想要发送图片,这时该怎么做 ?

当然,只能在上面相机的发送发送模块中添加else if(。。。。)和它的实现方式了,也就意味着,每更新一个需要使用图片功能的软件,就必须去修改相机模块,是不是觉得和我们的定时器很像?

注册的精髓:解耦各个模块。程序讲究高内聚,低耦合。

我目前对这句话的理解是:高内聚:每一个功能模块(c文件,h文件),内部不和其他模块相互调用,比如障碍物函数里面不应该有状态这一个变量存在,更不应该拥有零地标恢复运行这一操作。

它只做一件事,处理IO口信息,产生相应的障碍物状态。低耦合:障碍物函数与其他模块的耦合,仅仅为产生的障碍物状态。下面深入探讨注册机制。

何谓注册:我目前这样理解的,相机要发送图片,面临着多种发送方式,每一种发送方式肯定会调用不同的函数。

反过来想,就是我有很多的应用,要使用相机这个模块(此处对比定时器)。

既然这样,相机模块定义一个注册函数,供其他模块调用,以告诉相机,允许使用对应的发送方式。

#define num_max 20                        //最大设备数
 
typedef struct
{
  u8 num;                                //当前注册设备数
  u8 list_ name[num _max];                //用于保存注册设备列表
  void (*click[num _max])(u8 * temp);  //存放不同模块(微信qq)的发送函数地址
}Equiment;
Equiment  COM;            
 
/**************************注册函数****************************************/
void  Photo_Register ( void(*a)(u8 * temp),u8 list )  //提供给外部的接口
{
   if(COM.num < num.max)
  {
      COM. click[COM. num]=a;          //保存函数地址
      COM. List _name [ COM. num ]=list;   //保存设备名至列表
      COM. num++;
  }
  else
  {
     /****超过最大设备数报错******/
  }
}
 
/*相机中的发送函数*/
void Click(u8 temp)           // 最终实现图片发送调用此函数即可
{
   u8 i,NUM;
   for(i=0; i<= COM.num ; i++)
   {
    printf(“打印列表,显示已经注册的设备”)
   } 
   NUM =Get(选择的发送方式);
   if(!NUM)
   COM.click[NUM](temp);
}
/*******************以上在相机中实现************************************/

微信中若要使用,在安装过程中,提示打开相机权限,便是调用上述注册函数。将微信本身自集成的发送函数地址传给相机,相机每次发送只需判断哪些设备注册了,选择对应的方式即可。

如此一来,出现再多的新应用要使用相机,只需注册一次即可。

相机与微信QQ微博等模块之间完美解耦!类似的,定时器的解耦也能这样处理。

定时器运用注册机制

首先,要想解耦,必须去掉胡乱定义的标志位与时间变量,只允许一个时间变量。

因此定义一个32的时间变量,不要任何条件限制,让他一直自加。

参考arduino 中定时处理的方法:定义一个函数获取当前时间,保存下当前时间,运行一段时间后,再次查询当前时间,两次做差,便得出运行的时间。

从以上不难看出,关键点在于:获取当前时间的函数,当前时间的存放,做差后的时间。

以下是实现方法:

time.h

#include "stm32f10x.h"
#ifndef __TIME_H
#define __TIME_H
 
#define TimerID_max 20          //最大注册设备数
#define RunOutOf_time(ID , ms)   ( systime.no w-systime.last[ID -1]< ms ) 
 
typedef struct
{
     u8 ID;                     //设备ID
     u32 now;                   //当前时间
     u32 last[TimerID_max];     //存放抓取到的时间
 
     void (*timer_init)(u16 countdata,u16 freqData);   //指向初始化函数
     u8 (*get_id)(void);                               //指向获取ID函数
     void (*refresh)(u8 ID);                           //指向更新时间函数
 
}SYSTIME;
extern SYSTIME systime;
#endif

time.c

#include "time.h"
 
/*********提供给外部的API*******************/
void Timer_Init(u16 CountData,u16 FreqData);
unsigned char systime_get(void);
void Refresh(u8 ID);
/***********************************************/
 SYSTIME systime =        定义SYSTIME类型变量,并初始化函数指针
{
     .get_id=systime_get,
     .refresh=Refresh,
     .timer_init=Timer_Init
};
 
/****************************************************/
//函数名:Timer_init
//描述:初始化定时器
//输入:中断时间相关
//输出: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);             
 
}
 
/****************************************************/
//函数名:systime_get
//描述:获取当前时间,并产生一个注册
//输入:null
//输出:null
/****************************************************/
unsigned char systime_get()
{
    if(systime.ID    {
        systime.last[systime.ID]=systime.now;
 systime.ID++;
        return systime.ID;
    }
    else 
 return 0;
}
/****************************************************/
//函数名:Refresh
//描述:更新当前时间
//输入:获取到的ID
//输出:null
/****************************************************/
void Refresh(u8 ID)
{
    systime.last[ID-1]=systime.now;
}
/****************************************************/
//函数名:TIM4_IRQHandler
//描述:1ms定时器
//输入:null
//输出:null
/****************************************************/
void TIM4_IRQHandler(void) 
{
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
 TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
 systime.now++;          
    }
}

在.c与.h实现了以上后,面向外部的只有三个函数

/*********提供给外部的API*******************/
void Timer_Init(u16 CountData,u16 FreqData);    
unsigned char systime_get(void);
void Refresh(u8 ID);
/***********************************************/

定时器的使用方法

1. /*初始化*/
2.
  /**********任务1实现运行等闪烁,频率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);   
}
 
/*******************任务2实现运行等闪烁,频率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函数实现任务1运行10s,任务2运行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); 
        }
}

以上,任何函数想要使用定时器,只需要按要求,设立一个ID存储变量,以存储注册时分配的ID,便可调用定时器,且任何平台上均可方便的移植,只需修改硬件初始化。

此程序无法实现时刻任务执行,例如某任务要100ms执行一次,只能用作时间段内执行。

原因在于程序主循环会耗时,导致轮询时无法精准捕捉到100ms时刻,想要达到此效果,还需改进或者完全换一种思路来写,例如捕捉放到中断中,主循环来查询100ms使能位。

文章来源于网络,版权归原作者所有,如有侵权,请联系删除。



关注【一起学嵌入式】,回复加群进技术交流群。



觉得文章不错,点击“分享”、“”、“在看” 呗!

一起学嵌入式 公众号【一起学嵌入式】,RTOS、Linux编程、C/C++,以及经验分享、行业资讯、物联网等技术知
评论 (0)
  • 你是不是也有在公共场合被偷看手机或笔电的经验呢?科技时代下,不少现代人的各式机密数据都在手机、平板或是笔电等可携式的3C产品上处理,若是经常性地需要在公共场合使用,不管是工作上的机密文件,或是重要的个人信息等,民众都有防窃防盗意识,为了避免他人窥探内容,都会选择使用「防窥保护贴片」,以防止数据外泄。现今市面上「防窥保护贴」、「防窥片」、「屏幕防窥膜」等产品就是这种目的下产物 (以下简称防窥片)!防窥片功能与常见问题解析首先,防窥片最主要的功能就是用来防止他人窥视屏幕上的隐私信息,它是利用百叶窗的
    百佳泰测试实验室 2025-04-30 13:28 617浏览
  • 二位半 5线数码管的驱动方法这个2位半的7段数码管只用5个管脚驱动。如果用常规的7段+共阳/阴则需要用10个管脚。如果把每个段看成独立的灯。5个管脚来点亮,任选其中一个作为COM端时,另外4条线可以单独各控制一个灯。所以实际上最多能驱动5*4 = 20个段。但是这里会有一个小问题。如果想点亮B1,可以让第3条线(P3)置高,P4 置低,其它阳极连P3的灯对应阴极P2 P1都应置高,此时会发现C1也会点亮。实际操作时,可以把COM端线P3设置为PP输出,其它线为OD输出。就可以单独控制了。实际的驱
    southcreek 2025-05-07 15:06 54浏览
  • ‌一、高斯计的正确选择‌1、‌明确测量需求‌‌磁场类型‌:区分直流或交流磁场,选择对应仪器(如交流高斯计需支持交变磁场测量)。‌量程范围‌:根据被测磁场强度选择覆盖范围,例如地球磁场(0.3–0.5 G)或工业磁体(数百至数千高斯)。‌精度与分辨率‌:高精度场景(如科研)需选择误差低于1%的仪器,分辨率需匹配微小磁场变化检测需求。2、‌仪器类型选择‌‌手持式‌:便携性强,适合现场快速检测;‌台式‌:精度更高,适用于实验室或工业环境。‌探头类型‌:‌横向/轴向探头‌:根据磁场方向选择,轴向探头适合
    锦正茂科技 2025-05-06 11:36 290浏览
  • 这款无线入耳式蓝牙耳机是长这个样子的,如下图。侧面特写,如下图。充电接口来个特写,用的是卡座卡在PCB板子上的,上下夹紧PCB的正负极,如下图。撬开耳机喇叭盖子,如下图。精致的喇叭(HY),如下图。喇叭是由电学产生声学的,具体结构如下图。电池包(AFS 451012  21 12),用黄色耐高温胶带进行包裹(安规需求),加强隔离绝缘的,如下图。451012是电池包的型号,聚合物锂电池+3.7V 35mAh,详细如下图。电路板是怎么拿出来的呢,剪断喇叭和电池包的连接线,底部抽出PCB板子
    liweicheng 2025-05-06 22:58 175浏览
  • 随着智能驾驶时代到来,汽车正转变为移动计算平台。车载AI技术对存储器提出新挑战:既要高性能,又需低功耗和车规级可靠性。贞光科技代理的紫光国芯车规级LPDDR4存储器,以其卓越性能成为国产芯片产业链中的关键一环,为智能汽车提供坚实的"记忆力"支持。作为官方授权代理商,贞光科技通过专业技术团队和完善供应链,让这款国产存储器更好地服务国内汽车厂商。本文将探讨车载AI算力需求现状及贞光科技如何通过紫光国芯LPDDR4产品满足市场需求。 车载AI算力需求激增的背景与挑战智能驾驶推动算力需求爆发式
    贞光科技 2025-05-07 16:54 57浏览
  • 某国产固态电解的2次和3次谐波失真相当好,值得一试。(仅供参考)现在国产固态电解的性能跟上来了,值得一试。当然不是随便搞低端的那种。电容器对音质的影响_电子基础-面包板社区  https://mbb.eet-china.com/forum/topic/150182_1_1.html (右键复制链接打开)电容器对音质的影响相当大。电容器在音频系统中的角色不可忽视,它们能够调整系统增益、提供合适的偏置、抑制电源噪声并隔离直流成分。然而,在便携式设备中,由于空间、成本的限
    bruce小肥羊 2025-05-04 18:14 132浏览
  • 多功能电锅长什么样子,主视图如下图所示。侧视图如下图所示。型号JZ-18A,额定功率600W,额定电压220V,产自潮州市潮安区彩塘镇精致电子配件厂,铭牌如下图所示。有两颗螺丝固定底盖,找到合适的工具,拆开底盖如下图所示。可见和大部分市场的加热锅一样的工作原理,手绘原理图,根据原理图进一步理解和分析。F1为保险,250V/10A,185℃,CPGXLD 250V10A TF185℃ RY 是一款温度保险丝,额定电压是250V,额定电流是10A,动作温度是185℃。CPGXLD是温度保险丝电器元件
    liweicheng 2025-05-05 18:36 190浏览
  • 一、gao效冷却与控温机制‌1、‌冷媒流动设计‌采用低压液氮(或液氦)通过毛细管路导入蒸发器,蒸汽喷射至样品腔实现快速冷却,冷却效率高(室温至80K约20分钟,至4.2K约30分钟)。通过控温仪动态调节蒸发器加热功率,结合温度传感器(如PT100铂电阻或Cernox磁场不敏感传感器),实现±0.01K的高精度温度稳定性。2、‌宽温区覆盖与扩展性‌标准温区为80K-325K,通过降压选件可将下限延伸至65K(液氮模式)或4K(液氦模式)。可选配475K高温模块,满足材料在ji端温度下的性能测试需求
    锦正茂科技 2025-04-30 13:08 507浏览
  • 浪潮之上:智能时代的觉醒    近日参加了一场课题的答辩,这是医疗人工智能揭榜挂帅的国家项目的地区考场,参与者众多,围绕着医疗健康的主题,八仙过海各显神通,百花齐放。   中国大地正在发生着激动人心的场景:深圳前海深港人工智能算力中心高速运转的液冷服务器,武汉马路上自动驾驶出租车穿行的智慧道路,机器人参与北京的马拉松竞赛。从中央到地方,人工智能相关政策和消息如雨后春笋般不断出台,数字中国的建设图景正在智能浪潮中徐徐展开,战略布局如同围棋
    广州铁金刚 2025-04-30 15:24 334浏览
  • 5小时自学修好BIOS卡住问题  更换硬盘故障现象:f2、f12均失效,只有ESC和开关机键可用。错误页面:经过AI的故障截图询问,确定是机体内灰尘太多,和硬盘损坏造成,开机卡在BIOS。经过亲手拆螺丝和壳体、排线,跟换了新的2.5寸硬盘,故障排除。理论依据:以下是针对“5小时自学修好BIOS卡住问题+更换硬盘”的综合性解决方案,结合硬件操作和BIOS设置调整,分步骤说明:一、判断BIOS卡住的原因1. 初步排查     拔掉多余硬件:断开所有外接设备(如
    丙丁先生 2025-05-04 09:14 73浏览
  • UNISOC Miracle Gaming奇迹手游引擎亮点:• 高帧稳帧:支持《王者荣耀》等主流手游90帧高画质模式,连续丢帧率最高降低85%;• 丝滑操控:游戏冷启动速度提升50%,《和平精英》开镜开枪操作延迟降低80%;• 极速网络:专属游戏网络引擎,使《王者荣耀》平均延迟降低80%;• 智感语音:与腾讯GVoice联合,弱网环境仍能保持清晰通话;• 超高画质:游戏画质增强、超级HDR画质、游戏超分技术,优化游戏视效。全球手游市场规模日益壮大,游戏玩家对极致体验的追求愈发苛刻。紫光展锐全新U
    紫光展锐 2025-05-07 17:07 57浏览
  • 想不到短短几年时间,华为就从“技术封锁”的持久战中突围,成功将“被卡脖子”困境扭转为科技主权的主动争夺战。众所周知,前几年技术霸权国家突然对华为发难,导致芯片供应链被强行掐断,海外市场阵地接连失守,恶意舆论如汹涌潮水,让其瞬间陷入了前所未有的困境。而最近财报显示,华为已经渡过危险期,甚至开始反击。2024年财报数据显示,华为实现全球销售收入8621亿元人民币,净利润626亿元人民币;经营活动现金流为884.17亿元,同比增长26.7%。对比来看,2024年营收同比增长22.42%,2023年为7
    用户1742991715177 2025-05-02 18:40 174浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦