单片机开发常用的软件架构

对于单片机单片机程序来说,大家都不陌生,但是真正使用架构开发,考虑架构的恐怕不多,平时写代码都是想到什么写什么,导致程序代码非常复杂并且不易读懂,下面介绍几种常用的架构开发方案。

一、前后台顺序执行法。

这使初学者常用代码的程序设计框架,不用考虑太多东西,代码简单,或者对系统的整体实时性和并发性要求不高:初始化后通过while(1){}或者for()等循环不断的调用自己编写完成的函数,也基本不考虑每个函数执行所需要的时间,打部分时间在函数中存在毫秒级别延迟。
优点:对初学者来说,这是最容易也是最直观的程序架构,逻辑简单明了,适用于单模块调试或者复杂度较低的软件开发。
缺点:实时性低,由于每个函数或多或少存在毫秒延迟,导致其他函数间隔执行时间不同,虽然可以通过定时器中断方式,但是中断的执行函数必须短。当程序逻辑复杂度提升时,会导致后来维护的人员无法理解,难以分析。

二、时间片轮训法

介于前后台顺序执行和操作系统(RTOS)之间的一种程序设计方案。改设计方案帮助换在学习裸机和操作系统之间的开发者,更好的提高开发效率,主要特点有以下几个:
1:目前的需求设计完全没有必要上操作系统
2:任务的函数无需时刻都执行,存在间隔(比如按键软件防抖,初学者会用延迟去检测按键状态,这部分时间对于CPU来说使比较浪费的,这部分时间完全可以去执行其他任务)
3:对实时性有一定的要求。可以根据任务的重要程度,来规定任务的执行周期。

改设计方案需要使用一个定时器,一般情况下定时1ms就可以了,定时太长影响实时性(频繁的进入中断,效率不高),因此需要考虑每个任务的执行时间。同时要求主函数和任务函数中不能存在毫秒级别的延迟。

下面介绍两种比较常见的任务调度方案

一、使用标志位进行任务切换的设计方案
/*
 * \brief 主函数
 *
 * \param[in] 无
 *
 * \retval  无
 *
 */
int main()
{
 	System_Init();
 	
 	whlie(1)
 	{
 	   if(FLAG0)
      {
        App_Task();
        FLAG0=0;
      }
      if(FLAG1)
      {
        App_Task1();
        FLAG1=0;
      }
      if(FLAG2)
      {
        App_Task2();
        FLAG2=0;
      }
      if(FLAG3)
      {
        App_Task3();
        FLAG3=0;
      }
      if(FLAG4)
      {
        App_Task4();
        FLAG4=0;
      }
 	}

}

/*
 * \brief 定时器中断
 *
 * \param[in] 无
 *
 * \retval  无
 *
 */
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStasus(TIM2,TIM_IT_UPdate) == SET) 
	{
	 timCount++;
	//这里用条件编译,方便代码书写。
	timCount % 1 ==0 ? FLAG0 = 1 : 0;
	timCount % 10 ==0 ? FLAG1 = 1 : 0;
	timCount % 20 ==0 ? FLAG2 = 1 : 0;
	timCount % 100 ==0 ? FLAG3 = 1 : 0;
	timCount % 500 ==0 ? FLAG4 = 1 : 0;
	}
	TIM_ClearITPendingBit(TIM2,TIM_IT_UPdate);
}

如果需要更加精确的检测,放置程序是否跑飞,可以使用看门狗来进行定时喂狗。

二、使用函数指针的设计方案
/*
 * \brief 定义任务相关结构体
 */
typedef struct
{
	uint8_t run;                // 调度标志,1:调度,0:挂起
	uint16_t timCount;          // 时间片计数值
	uint16_t timRload;          // 时间片重载值
	void (*pTaskFuncCb)(void);  // 函数指针变量,用来保存业务功能模块函数地址
} TaskComps_t;
/*
 * \brief 定义任务数量
 */
#define TASK_NUM_MAX   (sizeof(g_taskComps) / sizeof(g_taskComps[0]))

static TaskComps_t g_taskComps[] = 
{
	{0, 1, 1,  AppTask},				
	{0, 10, 10,  AppTask1},				
	{0, 20, 20, AppTask2},			
	{0, 100,100, AppTask3},				
	{0, 500, 500,  AppTask4},					
	/* 添加业务功能模块 */
};
/**
* @brief 任务调度函数
* @param
* @return 
*/
static void TaskHandler(void)
{
	for (uint8_t i = 0; i < TASK_NUM_MAX; i++)
	{
		if (g_taskComps[i].run)                  // 判断时间片标志
		{
			g_taskComps[i].run = 0;              // 标志清零
			g_taskComps[i].pTaskFuncCb();        // 执行调度业务功能模块
		}
	}
}

/**
* @brief 在定时器中断服务函数中被间接调用,设置时间片标记,需要定时器1ms产生1次中断
* @param
* @return 
*/
static void TaskScheduleCb(void)
{
	for (uint8_t i = 0; i < TASK_NUM_MAX; i++)
	{
		if (g_taskComps[i].timCount)
		{
			g_taskComps[i].timCount--;
			if (g_taskComps[i].timCount == 0)
			{
				g_taskComps[i].run = 1;
				g_taskComps[i].timCount = g_taskComps[i].timRload;
			}
		}
	}
}

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStasus(TIM2,TIM_IT_UPdate) == SET) 
	{
		g_pTaskScheduleFunc();
	}
	TIM_ClearITPendingBit(TIM2,TIM_IT_UPdate);
}

static void (*g_pTaskScheduleFunc)(void);  // 函数指针变量,保存任务调度的函数地址

/**
* @brief 注册任务调度回调函数
* @param pFunc, 传入回调函数地址
* @return 
*/
void TaskScheduleCbReg(void (*pFunc)(void))
{
	g_pTaskScheduleFunc = pFunc;
}

int main ()
{
	System_Init();
	
	TaskScheduleCbReg(TaskScheduleCb);
	
	while(1)
	{
		TaskHandler();
	}

}

这里需要注意,在调用的时候需要使用函数指针来实现函数的回调,对函数指针不熟悉的人需要去了解下。

三、操作系统。

嵌入式操作系统,使一种用途广泛的系统软件,对单片机来说,常见的有UCOS、FreeRTOS、RT-Thread、等多种操作系统。操作系统中的时间片轮训法和上面介绍的任务调度有类似的地方,但这类操作系统中往往集成ICP等,复杂功能,初学者接触比较困难。在此不再介绍。

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: MCU(微控制器)的软件架构可以通过以下步骤来设计: 1. 需求分析:在开始设计软件架构之前,首先需要了解系统需要完成的任务和功能。这可以通过收集需求文档、与客户交流以及进行系统分析来实现。 2. 分层设计:将系统功能分解为不同的模块,并将这些模块分配到不同的层次中。通常,MCU软件的设计包括硬件抽象层(HAL)、设备驱动程序、中间件、应用程序等层次。 3. 设计接口:在每个层次中,需要设计接口,以确保模块之间的通信和协作。这些接口应该清晰明了、简单易懂,并能够满足系统要求。 4. 选择编程语言和工具:选择编程语言和工具以编写和调试软件。MCU通常使用C或C++编程语言,可以使用IDE(集成开发环境)进行开发和调试。 5. 编写代码:编写软件代码并进行单元测试以验证功能的正确性。 6. 集成测试:将不同的模块集成到一个完整的系统中,并进行系统测试以验证系统的功能、性能和可靠性。 7. 优化和调试:在集成测试期间,可能需要进行优化和调试,以解决性能问题和系统错误。 最后,需要将软件代码烧录到MCU芯片中,并进行现场测试和验证,以确保软件在MCU上的正确运行。 ### 回答2: 设计 MCU(微控制器单元)的软件架构,可以按照以下几个步骤进行: 1. 确定功能需求:明确 MCU 需要具备的功能和任务。分析需求,确定所需要的资源,包括处理器和存储器等。 2. 划分模块和任务:根据功能需求,将软件划分为若干个模块或任务。每个模块或任务应该具有清晰的功能和职责,能够独立完成一定的功能。 3. 定义接口:确定每个模块或任务之间的接口和通信方式。明确输入输出接口以及数据传输方式,确保各模块之间能够正确地进行交互。 4. 设计主程序:设计主程序控制整个软件流程。主程序应该根据不同任务的优先级来调度各模块的执行顺序,确保任务能够及时且正确地完成。 5. 设计模块:根据每个模块的功能需求,设计相应的模块代码。在设计过程中,应考虑代码的可维护性和可复用性,遵循模块化设计的原则,将代码分解成易于理解和修改的小模块。 6. 编写测试用例:针对每个模块编写相应的测试用例,验证模块的功能是否符合预期。测试用例应该覆盖各种边界情况,确保软件在各种情况下能够正确运行。 7. 调试和优化:在实际使用过程中,对软件进行调试和优化。通过调试工具和技术,解决软件中的bug和性能问题。 8. 文档记录:及时记录软件架构和设计的相关信息,包括接口定义、模块功能说明、代码注释等。这些文档对于后续的维护和升级非常重要。 以上是设计 MCU 软件架构的基本步骤,通过系统化的设计和开发,能够提高软件的可靠性和可维护性,确保 MCU 正确地执行所需功能。 ### 回答3: 设计MCU(单片机)的软件架构需要考虑以下几个方面: 首先,需要明确系统的功能和需求。通过分析单片机的应用场景和期望功能,确定需要哪些模块和功能模块。这些模块可以包括输入输出、通信、控制算法等等。 其次,确定模块之间的依赖关系和通信方式。根据系统需求,确定各个模块之间的数据传输方式,可以使用全局变量、消息队列、事件或者回调函数等方式实现模块间的通信。 然后,进行模块划分和封装。将整个系统按照功能划分为不同的模块,每个模块负责特定的功能。对于每个模块,进行合理的封装,保证模块之间的独立性和可重用性,提高系统的可维护性和可扩展性。 接下来,确定任务调度方式。MCU的资源有限,需要合理分配和利用处理器的时间片和内存资源。可以采用系统时钟中断、优先级调度、时间片轮转等方式进行任务调度,确保不同模块和任务能够按时得到执行。 最后,进行软件测试和优化。通过针对各个模块的单元测试、集成测试和系统测试来验证系统功能的正确性和稳定性。同时,对系统进行优化,提高系统的运行效率和性能。 总结起来,设计MCU的软件架构需要从明确需求、确定通信方式、模块划分和封装、任务调度以及测试和优化等多个方面进行综合考虑,以达到系统功能完备、性能高效的目标。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值