嵌入式设计模式-单一模式

本文介绍了C语言中使用单例模式管理全局结构体变量的方法,以串口操作为例,展示了如何确保程序中只有一个串口类对象,并通过get_uart_handler函数获取这个唯一实例。单例模式能避免资源浪费,提高效率,尤其适用于多线程环境下的外设操作。在实际使用时,需要考虑线程安全,如使用互斥锁保护资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单一模式

单一模式是指在C语言中整个文件只有一个单一的结构体,这个结构体负责所有的结构体变量。
单例模式的特征

  1. 单例类,有且只有一个结构体类对象;
  2. 单例类必有一个函数用来创建自己的唯一对象。
  3. 其他对象都可以访问这个单例类创建的唯一对象。
    先创建一个结构体类型,通过结构体类型定义出来结构体变量,在整个程序中,有且只有一个,其他函数可以通过特定的方法,获得这个结构体变量。
    全局可访问有且只有一个的结构体表

什么情况下使用单例模式

保证程序全局只有一个结构体变量的时候,单例模式很好的解决了同一个结构体类型的变量被反复创建,造成内存浪费,
例如: UART,在多线程下每个外设在使用UART时都会进行初始化,然后造成资源浪费。

如何使用单例模式

创建一个结构体指针变量时,需要先判断这个结构体指针是否已经存在。也就是说,要判断这个结构体指针是否已经获得了内存空间,如果是,则直接返回该内存空间的指针,如果没有则malloc创建,并返回创建后的内存指针。
通常使用一个函数调用malloc来为该结构体指针分配内存(也可以用静态内存的方式进行创建,不用malloc)。
代码示例

  1. 创建一个single_mode.h 头文件,并在这个头文件声明一个串口类操作类,这个包含了串口设备的属性和操作方法。
#ifndef _SINGLE_MODE_H_
#define _SINGLE_MODE_H_
//定义一个串口类,串口类提供了三个串口操作方法
typedef struct uart_handler
{
	/*串口设备属性*/
	unsigned int baudrate;		//波特率
	unsigned char data_bit;		//数据位
	unsigned char stop_bit;		//停止位
	unsigned char parity;		//校验位
	/*串口设备的操作方法*/
	void(*uart_init)(void);									//串口初始化
	int(*uart_read)(char*p_buff_read,int read_len);			//串口读数据
	int(*uart_write)(char *p_buff_write,int write_len);		//串口写数据
}uart_handler_t;
#endif
  1. 然后在single_mode.c文件中,实现了串口的操作方法,主要是串口设备的初始化,串口设备的读操作和写造作。简化函数三个函数是打印功能,可以根据实际进行重写三个函数。
static void uart_init(void)
{
	//通过打印的方式,表示初始话函数调用完成
	printf("uart_init function call success");
}
static int uart_read(char *p_buff_read, int read_len)
{
	printf("uart_read function call success");
	return 1;
}
static int uart_write(char *p_buff_write, int write_len)
{
	printf("uart_write function call success");
	return 2;
}
  1. 单例模式在使用时候,可以通过一个函数来获取这个唯一的实例对象,如果这个对象没有被创建,则进行创建。如果对象已经被创建,则直接返回对象的指针。
//在这个函数里面,获取uart_handler_t的操作句柄,如果没有uart_handler_t构建的实例,则创建一个实例;如果有实例则直接返回指针。
uart_handler_t *get_uart_handler(void)
{
	static uart_handler_t*uart_handler = NULL;	//使用静态方式定一个实例句柄,并赋值为NULL
	if(NULL == uart_handler)		//如果实例句柄为空,也就是首次创建
	{
		uart_handler = (uart_handler_t*)malloc(sizeof(uart_handler_t));//分配内存
		if(NULL != uart_handler)//分配内存成功
		{
			//对函数指针进行赋值
			uart_handler->uart_init = uart_init;
			uart_handler->uart_read = uart_read;
			uart_handler->uart_write = uart_write;
		}
		else	//内存空间分配失败
		{
			printf("uart_handler malloc fail");
		}
	}
	return uart_handler;
}
  1. 在使用的时候,可以直接调用get_uart_handler来获取对象实例,获取后就可以通过这个实例来操作串口设备了,无论线程里面调用多少次,获取到的实例对象都是同一个东西。
int main(void)
{
	char read_buff[256];
	printf("single_mode start");
	/*在不同的线程中,为了避免资源抢夺,这里应该加互斥对资源进行保护,申请获取互斥锁*/
	//多次获取uart_handler对象,无论获取多少次,都不会重复使用*/
	uart_handler_t *uart_handler_1 = get_uart_handler();
	uart_handler_t *uart_handler_2 = get_uart_handler();
	uart_handler_t *uart_handler_3 = get_uart_handler();
	if((uart_handler_1 == uart_handler_2)&&(uart_handler_2 == uart_handler_3))
	{
		print("uart_handler all the same");
	}
	else
	{
		print("uart_handler are different");
	}
	//通过三个对象,分别调用不同的函数
	uart_handler_1->uart_init();
	uart_handler_2->uart_read(read_buff,sizeof(read_buff));
	uart_handler_3->uart_write("hello",sizeof("hello"));
	//释放互斥锁
	return 0} 
  1. 对象实例是在全局唯一,多个线程访问的时候,会存在资源竞争的问题,因此在使用这个对象实例的时候,要使用互斥锁进行线程安全处理。
  2. 确定程序的周期里面,不再使用这个对象实例,可以对其进行销毁操作。
//销毁uart_handler_t构建的实例
void destory_uart_handler(uart_handler_t *uart_handler)
{
	if(NULL != uart_handler)
	{
		free(uart_handler);
		uart_handler = NULL;
	}
}
### 嵌入式 C 语言实现状态机设计模式 #### 定义状态机结构体 为了有效地管理和操作不同状态下程序的行为,可以创建一个包含当前状态、事件以及响应动作的状态机结构体。此结构体用于表示各个可能的状态及其对应的处理逻辑。 ```c typedef struct { unsigned char current_state; // 当前状态编号 unsigned char event_id; // 触发该状态转移的事件ID void (*action)(void); // 执行的动作指针 } StateMachine; ``` #### 初始化状态表 构建一个静态常量数组来存储所有的状态转换关系,其中每一项都描述了一个特定条件下应采取何种行动并转向哪个新状态的信息[^1]。 ```c const static StateMachine state_table[] = { {STATE_INIT, EVENT_KEY_PRESS_0, &HandleKeyPressState}, {STATE_PROCESSING, EVENT_TIMEOUT, &HandleTimeoutState}, ... }; ``` #### 创建辅助函数 编写几个帮助性的子程序以便于更方便地查询和更新状态信息。这些功能包括但不限于查找匹配条目、设置初始条件等。 ```c unsigned char GetNextState(unsigned char currentState, unsigned char eventId); void SetInitialState(void); ``` #### 主循环中的状态切换机制 在主程序里持续监测输入信号的变化情况,并据此调用相应的处理器件以推进流程进展;每次变更完毕之后记得刷新最新的运行状况记录。 ```c int main() { SetInitialState(); while (1) { unsigned char keyEvent = ReadKeyEvent(); // 获取按键事件 if(keyEvent != NO_EVENT){ unsigned char nextState = GetNextState(current_state, keyEvent); if(nextState != INVALID_STATE){ state_table[nextState].action(); // 调用对应状态下的动作函数 current_state = nextState; } } Delay(LOOP_DELAY_MS); // 循环延时防止CPU占用过高 } } ``` 上述代码片段展示了如何利用`switch-case`语句配合预定义好的映射表格来进行有限自动机的设计与编码工作。这种方式不仅提高了源码可读性还简化了后期维护难度[^2]。 对于全局变量的选择上,考虑到单一实例原则能够更好地保障系统内部各部分间的数据同步问题,因此推荐将此类对象声明成具有文件作用域级别的实体[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值