当前位置: 首页 > 行情 >

嵌入式软件开发中的注册机制-天天速看

发布时间:2023-06-19 22:16:38 来源:面包芯语

1.问题描述

在使用定时器的过程中最令人苦恼的就是,定义flag,holdtime,每用一次定义就会导致中断函数中标志位满天飞,时间变量在程序中随处可见。在想要移植,又不敢随便删除。程序处于高耦合状态,失去了一个.c 一个 .h的意义。


(资料图片仅供参考)

2.如何解决这种问题

引入注册机制。为了方便说明注册机制,举一个例子:手机在使用相机这个功能时,有一个操作:将拍摄的照片发送出去。以程序来实现这一过程,最容易想到的方法如下:

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

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

这是最容易想到的实现方式,就如上面定时器的实现方式,哪里要用了,再定义一系列变量就是了。回到相机例子,假设某一天一个比微信还火的聊天软件出现了,用户安装了,想要发送图片,这时该怎么做 ?当然,只能在上面相机的发送发送模块中添加else if(。。。。)和它的实现方式了,也就意味着,每更新一个需要使用图片功能的软件,就必须去修改相机模块,是不是觉得和我们的定时器很像?

注册的精髓:解耦各个模块。程序讲究高内聚,低耦合。我目前对这句话的理解是:高内聚:每一个功能模块(c文件,h文件),内部不和其他模块相互调用,比如障碍物函数里面不应该有状态这一个变量存在,更不应该拥有零地标恢复运行这一操作。它只做一件事,处理IO口信息,产生相应的障碍物状态。低耦合:障碍物函数与其他模块的耦合,仅仅为产生的障碍物状态。下面深入探讨注册机制。

何谓注册:我目前这样理解的,相机要发送图片,面临着多种发送方式,每一种发送方式肯定会调用不同的函数。反过来想,就是我有很多的应用,要使用相机这个模块(此处对比定时器)。既然这样,相机模块定义一个注册函数,供其他模块调用,以告诉相机,允许使用对应的发送方式。

#definenum_max20//最大设备数typedefstruct{ u8 num;//当前注册设备数u8list_name[num_max];//用于保存注册设备列表void(*click[num_max])(u8*temp);//存放不同模块(微信qq)的发送函数地址}Equiment;EquimentCOM;/**************************注册函数****************************************/voidPhoto_Register(void(*a)(u8*temp),u8list)//提供给外部的接口{if(COM.num

微信中若要使用,在安装过程中,提示打开相机权限,便是调用上述注册函数。将微信本身自集成的发送函数地址传给相机,相机每次发送只需判断哪些设备注册了,选择对应的方式即可。如此一来,出现再多的新应用要使用相机,只需注册一次即可。相机与微信QQ微博等模块之间完美解耦!类似的,定时器的解耦也能这样处理。

定时器运用注册机制

首先,要想解耦,必须去掉胡乱定义的标志位与时间变量,只允许一个时间变量。因此定义一个32位的时间变量,不要任何条件限制,让他一直自加。

参考arduino 中定时处理的方法:定义一个函数获取当前时间,保存下当前时间,运行一段时间后,再次查询当前时间,两次做差,便得出运行的时间。从以上不难看出,关键点在于:获取当前时间的函数,当前时间的存放,做差后的时间。以下是实现方法:

time.h

#include"stm32f10x.h"#ifndef__TIME_H#define__TIME_H#defineTimerID_max20//最大注册设备数#defineRunOutOf_time(ID,ms)(systime.now-systime.last[ID-1]

time.c

#include"time.h"/*********提供给外部的API*******************/voidTimer_Init(u16CountData,u16FreqData);unsignedcharsystime_get(void);voidRefresh(u8ID);/***********************************************/SYSTIMEsystime=定义SYSTIME类型变量,并初始化函数指针{.get_id=systime_get,.refresh=Refresh,.timer_init=Timer_Init};/****************************************************///函数名:Timer_init//描述:初始化定时器//输入:中断时间相关//输出:null/****************************************************/voidTimer_Init(u16CountData,u16FreqData){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);NVIC_InitTypeDefNVIC_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_TimeBaseInitTypeDefTIM_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/****************************************************/unsignedcharsystime_get(){if(systime.ID

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

/*********提供给外部的API*******************/voidTimer_Init(u16CountData,u16FreqData);unsignedcharsystime_get(void);voidRefresh(u8ID);/***********************************************/

定时器的使用方法

1./*初始化*/2./**********任务1实现运行等闪烁,频率1s**********/voidtask1(){staticu8Task1_ID;if(!Task1_ID)Task1_ID=systime.get_id();if(RunOutOf_time(Task1_ID,1000))RUN_LED()=1;elseif(RunOutOf_time(Task1_ID,2000))RUN_LED()=0;elseif(RunOutOf_time(Task1_ID,3000))RUN_LED()=1;elseif(RunOutOf_time(Task1_ID,4000))RUN_LED()=0;elseif(RunOutOf_time(Task1_ID,5000)RUN_LED()=1;elsesystime.refresh(Task1_ID);}/*******************任务2实现运行等闪烁,频率100ms**********************/voidtask2(){staticu8Task1_ID;if(!Task1_ID)Task1_ID=systime.get_id();if(RunOutOf_time(Task1_ID,100))RUN_LED()=1;elseif(RunOutOf_time(Task1_ID,200))RUN_LED()=0;elseif(RunOutOf_time(Task1_ID,300))RUN_LED()=1;elseif(RunOutOf_time(Task1_ID,400))RUN_LED()=0;elseif(RunOutOf_time(Task1_ID,500))RUN_LED()=1;elsesystime.refresh(Task1_ID);}/***************main函数实现任务1运行10s,任务2运行10s****************/intmain(void){staticu8main_ID;System_Init();while(1){if(!main_ID)main_ID=systime.get_id();if(RunOutOf_time(main_ID,10000))task1();elseif(RunOutOf_time(main_ID,20000))task2();elsesystime.refresh(main_ID);}}

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

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

参考文献 &引用

[1]取经的孙猴儿. (2018, June24). stm32定时器使用注册机制复用. https://blog.csdn.net/qq_36969440/article/details/84455176?spm=1001.2014.3001.5501

Copyright ©  2015-2022 纵横空调网  版权所有  备案号:浙ICP备2022016517号-12   联系邮箱:51 46 76 11 3 @qq.com