江阴网站优化公司/广州百度网站排名优化
1、嵌入式软件与设计模式
思从深而行从简
软件开发,难的不是编写软件,而是编写功能正常的软件。软件工程化才能保证软件质量和项目进度,而设计模式使代码开发真正工程化,设计模式是软件工程的基石。
所谓设计模式就是对常见问题的通解,合理地运用设计模式可以很好地解决很多问题,每种模式针对一个通用问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。真正的高手能云淡风轻地用最简单的方法解决最复杂的问题,这也是高级程序员与新手的本质区别之一。
一般常见的是四人帮模式即GOF的23种设计模式,是偏向于可复用的面向对象的软件,并不能很完美的契合嵌入式软件,因为嵌入式C语言是结构化的语言,与硬件关联。虽然也可强制封装结构体实现类似效果(复杂的嵌入式应用软件也可使用,但对于通用PC的高级语言存在差距)。
基于嵌入式系统的工作流,选择合适的设计模式或代码框架,将复杂软件解耦或者分层,提高代码复用度和可扩展性具有一定意义,当然,代价是对资源和实时性的损耗。
针对嵌入式系统软件,提供四大类设计模式。
嵌入式系统软件设计模式 :
1、硬件访问类
2、并发同步类
3、状态与工作流类
4、安全性与可靠性类
设计模式是在已具备一定开发基础的前提下,对软件架构的优化,因此部分章节需要熟悉数字电路、RTOS实时操作系统等,才能更好的理解。更多理论信息可关注微信公众号【嵌入式系统】的其他文章。
2、硬件访问类设计模式
2.1 硬件访问的概念
嵌入式系统最明显的特性是可直接访问硬件,硬件操作通常会包括初始化、配置、控制等步骤,嵌入式软件管理硬件,给硬件提供命令或数据,或者从其获取信息,这块即是常说的硬件驱动代码。这类软件设计主要是考虑硬件器件的更换与兼容,对业务层的封装和隔离。
2.2 硬件代理模式
硬件代理模式(Hardware Proxy Pattem)使用结构体封装所有硬件设备访问,无论其硬件接口是怎样的,代理为客户提供与硬件形态无关的接口。
如果应用层直接访问硬件设备,硬件的变化所导致的问题会加剧;一个细节的改变,应用层均需要重新调整,通过提供介于应用层和硬件之间的代理,就极大地限制了硬件改变的影响,从而减少这样的修改。类似常说的代理律师,就是即使不懂法律,但可以请律师,由代理律师去活动。
硬件代理接口可以形如 init、open、close、read、write、control,其内部实现无需开放。函数名称或者结构体内函数指针名只是参考,也可以自定义诸如config等,这里只是说明,对不同的硬件设备统一访问接口,封闭细节。
但是也有一定缺点,硬件细节已经在硬件代理内部完全封装,对运行实时性有不良的影响。其实,所有的设计模式都是利于软件维护,而不利于实时性。代理模式只是简单封装接口,不能实现线程安全,除非在封装时额外增加加临界区或者队列保护。
例如开启加速度传感器,最初需求只有一颗如BMA425,其开启接口为 gsensor_bma425_open;但后期硬件替换为SCA720,如果是直接操作则需重写驱动,将原开启接口全部替换为gsensor_sc7a20_open。这种方式固然效率高,但也导致代码维护困难,从驱动层到业务层都需要替换接口。而且不能实现软件自动识别其型号,自动匹配驱动,对项目维护也是较大工作量。
如果改为硬件代理,则可以先封装函数指针结构体,伪代码形如
typedef struct
{*init;*open;*close;
}gsensor_ops_t;gsensor_ops_t bma425;
gsensor_ops_t sc7a20;
gsensor_ops_t* p_gsensor_ops;
所有业务层访问p_gsensor_ops,至于其究竟是哪一颗传感器则无需关注,扩展或者更换硬件无需修改业务层。最佳的方案是驱动层可以根据硬件固有差异,如芯片ID或者I2C从机地址,识别出具体硬件型号,自动将p_gsensor_ops指向具体的型号的驱动接口,对软件版本和生产维护更加友好,不管硬件如何变化,软件一版即可。
2.3 硬件适配器模式
硬件适配器模式 (Hardware Adapter Patterm) 在两个接口之间进行转换,使已经存在硬件接口能适应新的期望。
适配器模式的最直观应用是手机充电器,市电220V的交流电,但手机只支持5V的直流电,要确保手机正常充电,就是在充电链路中增加适配器,将220v的交流转换为5v的直流。
一般来说具有同一个功能的硬件器件,其接口往往相似,比如前面提到的加速度传感器,不管是哪个厂商,都是提供I2C或者SPI接口。硬件适配器模式则是在业务层和硬件层之间加入相互转换,创建适配器提供客户期望的接口,而不是重写硬件设备的接口,最少化返工代码。
在软件开发中经常有同个物理量,不同硬件的表示数值不同,前面提到的加速度传感器,因为量程和精度的差异,加速度大小1g的表示值,不同的传感器xyz三轴数值不同,有的是512表示1g,有的是256表示1g;这样对业务层的逻辑算法就难以统一标准。所以可以在业务算法和硬件驱动之间,增加适配转换,统一1g的表示值,这样才能保证硬件的变化不影响算法。
再比如简单的例子,将第三方库移植到不同的平台,因为参数等信息不完全相同,原来是传入1-100表示百分比,但新接口是以0.01~1.00表示,则需要在调用接口前转换两者关系。所以,硬件适配器模式一般用在硬件器件更换,或者软件跨平台移植,它并没具体的接口套路,是因地制宜,按接口形式转换。
2.4 中介者模式
中介者模式(Mediator Pattern)是用来降低多个元素之间的通信复杂性,提供一个中介类,协调处理不同类之间的通信,各子类之间不直接通信,松耦合,使代码易于维护。
比如而二手房交易,有10个买家与10个卖家,如果都直接去沟通对接,每人需要和10人对接,而且信息没经过过滤,沟通效率低;如果有个中介,所有人都只与中介对接,中介再按买家和卖家意愿,转达有效意见,买家与卖家无需直接交流,就能促成满意的交易。每添加新元素就需更新中介者,最终可能导致中介者越发难以维护;如果中介者出现问题,则中介功能包括相关元素程序崩溃,这和房产交易中介卷钱跑路一样。
软件中定义两种角色分别为合作者和中介者,合作者 (Collaborator) 指所有可能被中介者调用的具体对象,中介者 (Mediaior) 协调多个具体合作者。中介者需要明确每个合作者交互的消息,从哪来,到哪去。当感兴趣的事件发生时,合作者可以给中介者发送消息,中介者提供协调逻辑,或者与消息关联的合作者通信。
中介者与每个具体合作者一般通过多个指针连接,如果具体的合作者的接口一致,指针数组是最好的。两种角色的结构体定义伪代码形如下,针对的场景是根据汽车引擎点火acc状态,加速度传感器gsensor监测的震动信息,GPS卫星定位获取运行速度,三组数据组合判断当前车辆是处于什么状态。
//中介者管理3个关联合作者
typedef struct mediator_t
{colleague_t *acc;colleague_t *gsensor;colleague_t *gps;mediator_relay relay;
}mediator_t;//每个合作者提供2个接口,向中介者发消息,和接收处理中介者的消息
typedef struct colleague_t
{mediator_t *m_mediator;colleague_send send;colleague_receive receive;
}colleague_t;
合作者发送消息时不明确是谁执行,只管发送;中介者需要识别类型,按既定规则转发给对应的合作者执行。这也是中介者模式的缺点,中介者代码庞大,随着合作者数量的增加会变得复杂难以维护。完整的演示代码如下。
//微信公众号:嵌入式系统
#include <stdio.h>typedef enum
{EVENT_1,EVENT_2,EVENT_3,EVENT_MAX
}event_t;//模拟测试消息struct mediator_t;
typedef int (*mediator_relay)(event_t id,void *data,int len);struct colleague_t;
typedef int (*colleague_send)(event_t id,void *data,int len);
typedef int (*colleague_receive)(event_t id,void *data,int len);typedef struct mediator_t
{colleague_t *acc;colleague_t *gsensor;colleague_t *gps;mediator_relay relay;
}mediator_t;typedef struct colleague_t
{mediator_t *m_mediator;colleague_send send;colleague_receive receive;
}colleague_t;/*******************************************************/
//合作者接口
static colleague_t colleague_acc={0};
static colleague_t colleague_gsensor={0};
static colleague_t colleague_gps={0};
static int colleague_acc_send(event_t id,void *data,int len)
{colleague_t *handle=&colleague_acc;handle->m_mediator->relay(id,data,len);
}static int colleague_acc_receive(event_t id,void *data,int len)
{printf("ACC recv id=%d,%s\r\n",id,data);
}static int colleague_gsensor_send(event_t id,void *data,int len)
{colleague_t *handle=&colleague_gsensor;handle->m_mediator->relay(id,data,len);
}static int colleague_gsensor_receive(event_t id,void *data,int len)
{printf("gSensor recv id=%d,%s\r\n",id,data);
}static int colleague_gps_send(event_t id,void *data,int len)
{colleague_t *handle=&colleague_gps;handle->m_mediator->relay(id,data,len);
}static int colleague_gps_receive(event_t id,void *data,int len)
{printf("GPS recv id=%d,%s\r\n",id,data);
}/*******************************************************/
//中介者接口
static mediator_t mediator_manager={0};//中介者协调全局,将对应的事件转发给有需要的合作者,范例只是说明用法,随意定义的关系
//这个函数中介者模式维护的重点,也是它的缺点
static int mediator_msg_relay(event_t id,void *data,int len)
{mediator_t *handle=&mediator_manager;switch(id){case EVENT_1:handle->gsensor->receive(id,data,len);break;case EVENT_2:handle->gps->receive(id,data,len);break;case EVENT_3:handle->acc->receive(id,data,len);break;default:break;}
}/*******************************************************/
//测试接口
//如果觉得这样有一定耦合度,可以由中介者提供注册API给合作者调用,传入自身地址给中介者
static void init_member(void)
{colleague_acc.m_mediator=&mediator_manager;colleague_acc.send=colleague_acc_send;colleague_acc.receive=colleague_acc_receive;colleague_gsensor.m_mediator=&mediator_manager;colleague_gsensor.send=colleague_gsensor_send;colleague_gsensor.receive=colleague_gsensor_receive;colleague_gps.m_mediator=&mediator_manager;colleague_gps.send=colleague_gps_send;colleague_gps.receive=colleague_gps_receive;mediator_manager.acc=&colleague_acc;mediator_manager.gsensor=&colleague_gsensor;mediator_manager.gps=&colleague_gps;mediator_manager.relay=mediator_msg_relay;
}//微信公众号:嵌入式系统
int main(void)
{printf("embedded-system\r\n");init_member();colleague_acc.send(EVENT_1,(void*)"from acc",0);colleague_gsensor.send(EVENT_2,(void*)"from gsensor",0);colleague_gps.send(EVENT_3,(void*)"from gps",0);return 0;
}
看懂范例才能更好的理解中介者模式的价值。
//微信公众号:嵌入式系统
//运行结果:
embedded-system
gSensor recv id=0,from acc
GPS recv id=1,from gsensor
ACC recv id=2,from gps
三个关联合作者互相交互,只处理与自己关联的事件,对外或者app有问题就找中介,不与具体的合作者通信。
2.5 观察者模式
观察者模式提供一种方法来使对象“监听”其他对象,而不需要修改任何数据服务器。在嵌入式领域,适合传感器采样或者某些周期更新的数据,转发给关注它的元素,比较类似发布--订阅模式。
好比在红绿灯路口,交通信号灯由绿变红,整条车道的车都会收到该信息并进行制动处理。信号灯本身不关注外界,只是按自己的节奏控制灯的变化;而众多车主观察到红灯,都进行停车动作。
一个目标对象的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。在嵌入式软件的实现上,观察者模式通过在数据生产服务器添加订阅和取消订阅,服务器端不需要任何客户的先验信息。数据服务器按一定的更新策略,通知对其感兴趣的客户。
软件上,通知列表最简单的方式是定义一个足够大的数组来包含所有潜在的客户,在有很多客户的高度动态的系统中这会浪费内存,另一种方案是用链表构建一个系统。数据产生服务提供众多函数指针,有需求的客户提供自身句柄,一般是回调函数指针;当响应数据变化按既定策略执行回调,实现数据源的变化信息广播到所有观察者的效果。
例如设备支持GPS卫星定位,在驱动上报GPS信息的接口,底层提供一个函数指针数组,上层用自身的回调函数填充数组。底层获取到GPS信息后,查询数组,若回调函数非空则执行。每个回调函数由各模块分别实现,代码整洁,耦合性低。
//微信公众号:嵌入式系统
#include <string.h>
#include <stdio.h>#define PAL_GNSS_SUBSCRIPTIONS_MAX 5typedef unsigned char uint8_t;
typedef void (*gnss_info_callback)(void* data);typedef struct
{gnss_info_callback m_cb;
} pal_gnss_subscription_info;//订阅池
static pal_gnss_subscription_info g_gnss_subscription_pool[PAL_GNSS_SUBSCRIPTIONS_MAX] = {0};//订阅
uint8_t pal_gnss_subscribe(gnss_info_callback callback)
{uint8_t i;uint8_t ret=0;if(callback != NULL){for(i = 0; i < PAL_GNSS_SUBSCRIPTIONS_MAX; i++){if(g_gnss_subscription_pool[i].m_cb == NULL){//RTOS注意竞争g_gnss_subscription_pool[i].m_cb = callback;ret = 1;break;}}}else{ret = 0;}return ret;
}//取消订阅
void pal_gnss_unsubscribe(gnss_info_callback callback)
{uint8_t i;for(i = 0; i < PAL_GNSS_SUBSCRIPTIONS_MAX; i++){if(g_gnss_subscription_pool[i].m_cb == callback){//RTOS注意竞争g_gnss_subscription_pool[i].m_cb = NULL;break;}}
}//广播给观察者,执行回调
void pal_gnss_info_update(void)
{uint8_t i;uint8_t data=1;//testfor(i = 0; i < PAL_GNSS_SUBSCRIPTIONS_MAX; i++){if(g_gnss_subscription_pool[i].m_cb != NULL){//RTOS中使用消息队列更好,这里只是演示效果g_gnss_subscription_pool[i].m_cb((void*)&data);}}
}int main(void)
{printf("embedded-system\r\n");return 0;
}
观察者模式,订阅-发布机制尤其传感器采集数据,分发给不同模块,各模块收到广播后按自身需求处理数据的场景。
2.6 消抖过滤模式
这个简单的模式用于消除来自于金属表面间歇性连接引起的多个假事件。
按钮、拨动开关和机电式继电器等机械式输入设备,它们都有一个共同的问题,即接触金属产生连接,金属变形或“弹性”在开关转换时产生间歇连接。从而导致控制系统中有多个电子信号。消抖过滤模式通过在首次检测到异常信号后,等待一段时间将多个信号减少到一个信号。简单且常见的场景就是按键消抖,这也是嵌入式入门的基础。
嵌入式系统软件检测到首次跳变事件,设置延迟定时器 〈如果需要关闭设备中断) ,随后检查设备状态。一定时间后(去抖动时间),如果状态不同,则事件一定是真实的,则发送相应的信息给应用层。因为按键防抖属于嵌入式软件入门基础,这里不做详细描述,重点关注定时器,可以采用CPU延时等待,也可以采用硬件定时实现,后者更合理。
关于按键检测,底层区分按键码和按键事件类型(短按、长按、连续按),可以参考 《按键检测》,结合观察者模式,使用二维数据管理按键回调函数,可以实现按键检测驱动与业务的隔离。
2.7 中断模式
物理世界从根本上来说是并发与异步的,事情该发生时它就会发生,如果嵌入式系统不加以注意,这些事件可能丢失。为了及时监测感兴趣的事件,硬件中断模式,即中断中断服务程序是最有效的方法。
中断模式是一种构造系统的方式,用于对传入事件作出适当的反应。在嵌入式系统中,事件分为不同等级的紧急度,即使在系统非常繁忙地处理其它事件时也必须处理。在本章其他内容中讨论的轮询模式中,在系统方便的时候查询感兴趣的事件;虽然这是有好处的,不中断当前正在执行的过程,但它的缺点是高紧急度和高频率的事件可能得不到及时处理。中断模式通过立即停止当前的过程,处理传入事件来解决这个问题,并且随后返回原来的流程。
通常情况下,当中断服务程序ISR执行时,关闭中断,这意味着中断服务程序必须快速执行以确保不会丢失其他中断。因为中断服务程序必须很短,当它们调用其他的系统服务时必须非常小心。如果ISR 处理占用太长时间,在共享资源上出现竞争条件或发生死锁,问题很难跟踪。
中断模式是嵌入式软件特有的,硬件中断的特点是响应及时,处理要简短,尤其是在RTOS中。
2.8 轮询模式
另一种从硬件获取传感器数据或信号的常用模式是定期检查,称为轮询过程。当数据或信号不是非常紧急到不能等待到下一个轮询时段来收取,或当数据或信号可用时,硬件没有能力生成中断(或缺乏中断检测口),这时轮询非常有用。
轮询分定期或者不定期进行,定期轮询使用定时器按固定间隔查询,不定期即机会轮询是当系统方便的时候才轮询,没有固定间隔。
定期轮询主要用于周期性的变化,或者变化很缓慢的状态,按合适的间隔定时查询设备状态,如果数据或信号轮询时间加上反应处理时间,比数据更新间隔长,那就必须引起注意,否则数据将会丢失。因为定期轮流,其本身检查绑定一个定时器中断,因此定期轮询模式其实是中断模式的特殊情况。
不定期轮询是当系统方便的时候才轮询,如在主系统功能或在重复执行的周期点之间,在低端单片机上比较常见,对其他系统从事的活动的及时性影响也小。非定期的模式如果时间非常短,也可以嵌套在其它驱动中,诸如死循环循环等待某个状态,比如查询UART发送完成,IIC的ACK响应,但是这种循环体一定要注意,必须留有一定会退出循环的条件。
2.9 小结
硬件代理模式关注指定硬件细节的封装,解决硬件元件更新迭代和多个同类器件的兼容。
而硬件适配器模式为适应不同但是相似的硬件,也解决跨平台移植。
硬件器件组合工作,或者软件需求的复杂交互则适合中介者模式。
观察者模式为硬件数据支持动态添加和删除客户,适合多个模块共享传感器数据的场景。
消抖过滤模式、中断模式、轮询模式用于解决与硬件交互的低层次问题。
软件模式的选择,与硬件框架、资源和软件需求、应用场景相关,合适的才是最好的。
3、并发同步类设计模式
3.1 并发和RTOS概念
基于RTOS的软件,宏观上多个任务并行,实际是多任务的分时调度,对应着硬件资源可能就是前任务还未完成访问,后任务要抢占使用,这切换过程中就存在竞争。若没有RTOS相关基础,可以先参考基于《RTOS的软件开发理论》 和 《FreeRTOS及其应用,万字长文,基础入门》 ,否则本章信息可能无法理解。当任务调度启动后,所有的任务独立运行,如何设计避免一个资源被多个任务抢占使用,按串行访问共享资源?
3.2 临界区模式
临界区模式是与任务协调相关的最简单粗暴的模式。禁止任务在区域内转换,通过禁用任务转换甚至禁止中断来处理竞争关系,保证当前任务不间断的执行,直到完成相关操作退出临界区。
临界区模式结构简单,受保护的元素是资源而不是任务,在临界区开始之前禁止任务切换,并在服务结束后重新可用。RTOS提供临界区进出时的任务切换使能处理,或者直接在硬件级别配置中断等方式开关中断处理。
临界区模式关闭任务调度或者中断响应,实际会影响其他任务的时序。因此要求临界区持续时间很短,一般是在同一个任务内使用互斥锁或者临界区接口,快速完成相应操作,否则可能导致系统异常。例如有2个任务模块共享读写某一块内存数据,或者操作某个寄存器,就比较适合临界区。
3.3 守卫调用模式
守卫调用模式,通过提供的锁定机制串行访问,以阻止锁定后其他线程的调用服务,简单描述就是A任务占用某个资源后,将其锁定;优先级高于A的B任务想要使用它,得先咨询能否使用,如果资源处于锁定状态则延时等待(相应的任务阻塞,即使优先级更高),等到前一个任务使用结束,解锁释放资源,B任务才能执行。
这里面存在一定问题,如果还有任务C,其优先级介于A和B之间,表面上C会先执行,导致优先级低的C竟然比任务B先执行,即优先级反转,实际不会这样。
一般RTOS信号量支持优先级继承,即任务B使用某个信号量等待A任务时,临时会将A任务的优先级提高,和B相等,实际执行顺序是B-A-C。
通过信号量的获取和释放,来独占的访问某个硬件或者软件资源,其对时间没有太严格要求,这种在业务层开发更常见。一般在两个任务或者任务与中断间,进行锁定,确保共享资源按顺序使用,也可用于同步交互。
两个任务同步处理的场景,AT指令的发送任务和接收解析任务适合信号量,发送任务必须等前一AT回复,发出AT后阻塞等待信号量;接收解析任务确认接收完成,释放信号量;这时发送任务才能退出阻塞,发送下一个AT。
3.3 队列模式
队列模式是任务间异步通信最常见的实现,在非耦合的任务间及时通信,通过队列先进先出的数据结构,发送者将消息存入队列,接收者从队列中取出消息。它也提供一种简单方法串行访问共享资源,将访问消息排队,并且在稍后的时间中处理,这就避免了共享资源常见的相互排斥的问题。
消息队列使用异步通信,并且不会遇到互斥问题,这是因为没有引用共享资源。消息队列以单一的形式避免并发系统中通过传递引用共享信息产生的资源损坏的问题。在传值共享中,制作一个信息的副本,并且发送给接收线程进行处理。接收线程完全拥有收到的数据,并且因此能够自由修改,而不需要考虑由于多个写者,或者在一个写者和多个读者中共享它们造成信息损坏。其缺点之一是发送者传递消息后不能立即处理,需要进程等待,直到接收者任务运行,并且能够处理正等待的消息。
队列模式通过对数据和命令排队串行访问数据,允许接收者每次处理一个。由于它是异步完成,所以消息发送和处理之间的时间是非耦合的。这可能不满足系统的性能需求。守卫调用模式也串行访问,但是同步执行,以便数据和命令传输发生在时间上更加接近。但关于信号量的释放使用出现问题,会导致较严重的错误。
队列的最简单的实现策略是消息元素数组。这具有简单性的优点,但是缺乏链表的灵活性队列是很容易实现的,但是有很多可能的变体。有时一些消息比其他的更加紧急或者重要,并且应该在等待的低优先级消息之前处理。扩展添加多个缓冲区 (每个优先级一个) 实现优先级修改,或者基于消息优先级 ,但是这样会很麻烦。
一般还是固定长度的数组队列,但是可以向队列头或者尾插入新数据,这种满足绝大部分需求,这种情况下需要注意的是接收处理要及时,避免队列溢出。
例如只有1个UART,通过开关切换分时复用接2个外设,就比较适合队列模式,读写请求缓存到队列,按序取出执行,避免出现收发数据不完整的问题。
3.4 汇合模式
任务同步发生在简单的函数调用、共享单一资源或者传递数据,可用队列模式或守卫调用模式,但如果同步需要的条件更加复杂,涉及多个任务间的同步,汇合模式更适合解决任务间复杂形式同步的问题。
在这个模式中,两个或更多的任务通过操作类似全局变量的位,按变量相应位的状态执行不同动作。在RTOS内核中,如freeRTOS,这个全局变量叫事件组,其读写操作也有相应的API。
例如3个子任务各自监测不同外设,主任务收到3个子任务反馈的外设连接正常的事件,主任务才在UI界面提示所有外设连接正常。
3.5 小结
并发才是嵌入式软件开发的常态,事情并行发生必须预防竞争与冲突,这也是实时操作系统 (RTOS) 的基本要求。
临界区模式、守卫调用模式和队列模式解决在多任务环境下串行访问资源的问题。临界区模式在资源访问期间关闭任务转换,因此防止可能的资源数据损坏,但是阻塞了更高优先级任务,使它们永远不能访问资源。
守卫调用模式通过信号量完成相同的资源保护目标,该模式可能能够导致优先级倒置,所以该模式需要使用支持优先级继承的方式。
队列模式串行访问资源,通过将请求放在队列中,并且按照先进先出 (FIFO) 的顺序从队列取出,队列模式概念上非常简单,但是导致资源的请求响应延迟,影响时效性。
看起来这些模式很高级,其实主流的实时操作系统,其内核都支持这些模式相关的接口。内核开发的大佬们,早就洞悉了并发的风险与模式,使用内核的互斥锁、信号量、队列和事件组,可以很容易的实现这些模式。
如果基于裸机开发,主程序和中断程序也近似存在共享冲突,可以自定义全局变量、循环数组实现守卫调用模式或队列模式;其他模式,裸机基本上也用不上。
PS
全文四类设计模式篇幅过长,拆分发布,其他模式稍后。文中提到的RTOS开发相关可阅读其它文章,并发同步类相关的理论比较重要。
相关文章:

嵌入式软件设计方式与方法
1、嵌入式软件与设计模式 思从深而行从简 软件开发,难的不是编写软件,而是编写功能正常的软件。软件工程化才能保证软件质量和项目进度,而设计模式使代码开发真正工程化,设计模式是软件工程的基石。 所谓设计模式就是对常见问题的…...

ELAdmin 前端启动
开发工具 官方指导的是使用WebStorm,但是本人后端开发一枚,最终还是继续使用了 idea,主打一个能用就行。 idea正式版激活方式: 访问这个查找可用链接:https://3.jetbra.in/进入任意一个能用的里面,顶部提…...

完全让ChatGPT写一个风格迁移的例子,不改动任何代码
⭐️ 前言 小编让ChatGPT写一个风格迁移的例子,注意注意,代码无任何改动,直接运行,输出结果。 额。。。。这不是风格转换后的结果图。 ⭐️ 风格迁移基本原理 风格迁移是一种计算机视觉领域的图像处理技术,它的目标…...

查看jar包编译的jdk版本
解压jar包 jar xf xxx.jar 查看对象 javap -v Myclassname javap -v KafkaProducer.class |grep version -C 3 J2SE 8.0 52(0x33 hex) J2SE 7.0 51(0x32 hex) J2SE 6.0 50 (0x32 hex) J2SE 5.0 49 (0x31 hex) JDK 1.4 48 (0x30 hex) JDK 1.3 47 (0x2F hex) JDK 1.2 46 …...

未来之梦:畅想人工智能操控手机的辉煌时代
引言: 在当今数字化快速发展的时代,人工智能技术正日益深入我们的生活。其中,手机作为人们日常生活不可或缺的一部分,其未来将如何受到人工智能技术的影响,引发了广泛的关注和研究。本文将深入探讨人工智能操控手机的…...

产品经理--分享在项目中产品与研发之间会遇到的问题 在面试这一岗位时,面试官常问的问题之一,且分享两大原则来回答面试官这一问题
目录 一.STAR原则 1.1 简介 1.2 如何使用 1.3 举例说明 二.PDCA原则 2.1 简介 2.2 如何使用 2.3 运用场景 2.4 举例说明 三.产品与研发的沟通痛点 3.1 沟通痛点的原因 3.2 分享案例 前言 本篇会详细阐明作为一个产品经理会在项目遇到的问题,如:产…...

node环境打包js,webpack和rollup两个打包工具打包,能支持vue
引言 项目中经常用到共用的js,这里就需要用到共用js打包,这篇文章讲解两种打包方式,webpack打包和rollup打包两种方式 1、webpack打包js 1.1 在根目录创建 webpack.config.js,配置如下 const path require(path); module.expo…...

图数据库 之 Neo4j - 图数据库基础(2)
图数据库是一种专门用于存储、管理和查询图数据的数据库。与传统的关系型数据库不同,图数据库以图的形式存储数据,其中节点表示实体,边表示实体之间的关系。这种图数据模型非常适合表示复杂的关系和连接。 图数据库的定义和特点 图数据库是一…...

20240202在Ubuntu20.04.6下配置环境变量之后让nvcc --version显示正常
20240202在Ubuntu20.04.6下配置环境变量之后让nvcc --version显示正常 2024/2/2 20:19 在Ubuntu20.04.6下编译whiper.cpp的显卡模式的时候,报告nvcc异常了! 百度:nvcc -v nvidia-cuda-toolkit rootrootrootroot-X99-Turbo:~/whisper.cpp$ WH…...

数字孪生网络攻防模拟与城市安全演练
在数字化浪潮的推动下,网络攻防模拟和城市安全演练成为维护社会稳定的不可或缺的环节。基于数字孪生技术我们能够在虚拟环境中进行高度真实的网络攻防模拟,为安全专业人员提供实战经验,从而提升应对网络威胁的能力。同时,在城市安…...

LeetCode、62.不同路径的数目(一)【简单,动态规划或递归】
文章目录 前言LeetCode、62.不同路径的数目(一)【简单,动态规划或递归】题目描述与分类思路思路1:动态规划思路2:递归实现简洁写法补充:2024.1.30 资料获取 前言 博主介绍:✌目前全网粉丝2W,csdn博客专家、…...

re:从0开始的CSS学习之路 4. 长度单位
1. 长度单位 像素px:一个像素就是屏幕中一个不可分割的点。我们应用的屏幕实际上是由一个个的像素点构成的。 不同显示器的像素点大小也不同,在屏幕尺寸相同的情况下,像素越小,显示效果越清晰。 大部分浏览器默认字体大小是16px …...

golang开源定时任务调度框架
golang开源定时任务调度框架 Go语言中有很多开源的定时任务调度框架,以下几个是比较流行常用的: golang开源定时任务框架介绍 cron 一个基于Cron表达式的定时任务库,可以精确到秒级。它提供了简单易用的API来定义和管理定时任务ÿ…...

GridModel事件集合——yonBIP低代码
我们接着看表格相关的事件,用友的文档打不开,真的是天大的404,客观请看这个开发文档网址,找不到了,你说holy 不咯?http://tinper.org/mdf/(如果有哪位小伙伴知道这个地址是不是迁移了的话&#…...

苹果macbook电脑删除数据恢复该怎么做?Mac电脑误删文件的恢复方法
苹果电脑删除数据恢复该怎么做?Mac电脑误删文件的恢复方法 如何在Mac上恢复误删除的文件?在日常使用Mac电脑时,无论是工作还是娱乐,我们都会创建和处理大量的文件。然而,有时候可能会不小心删除一些重要的文件&#x…...

2024年R2移动式压力容器充装证模拟考试题库及R2移动式压力容器充装理论考试试题
题库来源:安全生产模拟考试一点通公众号小程序 2024年R2移动式压力容器充装证模拟考试题库及R2移动式压力容器充装理论考试试题是由安全生产模拟考试一点通提供,R2移动式压力容器充装证模拟考试题库是根据R2移动式压力容器充装最新版教材,R2…...

云开发超多功能工具箱组合微信小程序源码/附带流量主
这是一款云开发超多功能工具箱组合微信小程序源码附带流量主功能,小程序内包含了40余个功能,堪称全能工具箱了,大致功能如下: 证件照制作 | 垃圾分类查询 | 个性签名制作 二维码生成丨文字九宫格 | 手持弹幕丨照片压缩 | 照片编…...

挑战杯 python+深度学习+opencv实现植物识别算法系统
0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于深度学习的植物识别算法研究与实现 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:4分工作量:4分创新点:4分 🧿 更多…...

pytest的常用插件和Allure测试报告
pytest常用插件 pytest-html插件 安装: pip install pytest-html -U 用途: 生成html的测试报告 用法: 在.ini配置文件里面添加 addopts --htmlreport.html --self-contained-html 效果: 执行结果中存在html测试报告路…...

神经网络的权重是什么?
请参考这个视频https://www.bilibili.com/video/BV18P4y1j7uH/?spm_id_from333.788&vd_source1a3cc412e515de9bdf104d2101ecc26a左边是拟合的函数,右边是均方和误差,也就是把左边的拟合函数隐射到了右边,右边是真实值与预测值之间的均方…...

C语言代码 在屏幕上输出9*9乘法口诀表
在屏幕上输出9*9乘法口诀表。 代码示例: #include <stdio.h>int main() {int i 0;for (i 1; i < 9; i)//打印所有行的循环{int j 0;for (j 1; j < i; j)//打印每一行中所有列的循环{printf("%d*%d%-2d ", i, j, i * j);//%-2d的意思是两…...

11.0 Zookeeper watcher 事件机制原理剖析
zookeeper 的 watcher 机制,可以分为四个过程: 客户端注册 watcher。服务端处理 watcher。服务端触发 watcher 事件。客户端回调 watcher。 其中客户端注册 watcher 有三种方式,调用客户端 API 可以分别通过 getData、exists、getChildren …...

HGAME 2024 WEEK 1 :web ezHTTP
题目: 看到这个就知道是文件头伪造 第一想法就是Referer伪造 所以伪造 Referer: vidar.club 然后构造伪造的Referer 然后提示通过那些东西访问页面,User-Agent: 是构造你浏览器访问信息的,所以复制右边那一串替代就好了 然后要求我们从本地…...

Linux【docker 设置阿里源】
文章目录 一、查看本地docker的镜像配置二、配置阿里镜像三、检查配置 一、查看本地docker的镜像配置 docker info一般没有配置过是不会出现Registry字段的 二、配置阿里镜像 直接执行下面代码即可,安装1.10.0以上版本的Docker客户端都会有/etc/docker 1.建立配置…...

app逆向-frida-rpc详解
Frida-RPC是Frida工具的一个组件,用于在应用程序和Frida脚本之间进行远程过程调用(RPC)。远程过程调用是一种允许应用程序的不同部分或不同的应用程序之间进行通信的方法。在Frida中,RPC通过JavaScript脚本和应用程序之间建立通信…...

计算机网络(第六版)复习提纲27
7 TCP流量控制 A 利用滑动窗口实现流量控制 所谓流量控制,就是让发送方发送速率不要太快,让接收方来得及接收 1 利用窗口进行流量控制 2 持续计时器和零窗口探测报文(仅携带一字节的数据) B TCP的传输效率(TCP报文段的…...

解析与模拟常用字符串函数strcpy,strcat,strcmp,strstr(一)
今天也是去学习了一波字符串函数,想着也为了加深记忆,所以写一下这篇博客。既帮助了我也帮助了想学习字符串函数的各位。下面就开始今天的字符串函数的学习吧。 目录 strcpy与strncpy strcat与strncat strcmpy strstr strcpy与strncpy 在 C 语言中&…...

node.js后端+小程序前端+mongoDB(增删改查)
前言 今天我对比了以下node.js的express与python的fastAPI,我决定我还是出一期关于node.jsmangoDB小程序的小案例吧。 不是python的fastAPI不好用,因为fastAPI是python较新的技术,我不敢果断发出教学文章(这件事情还是留着给pyt…...

thinkphp数据批量提交(群发消息)
<form id="edit-form" class="form-horizontal" role="form" data-toggle<...

大华 DSS 数字监控系统 attachment_getAttList.action SQL 注入漏洞复现
0x01 产品简介 大华 DSS 数字监控系统是大华开发的一款安防视频监控系统,拥有实时监视、云台操作、录像回放、报警处理、设备管理等功能。 0x02 漏洞概述 大华 DSS存在SQL注入漏洞,攻击者 /portal/attachment_getAttList.action 路由发送特殊构造的数据包,利用报错注入获…...