
1、对发布者和订阅者进行抽象// 1. 定义事件 ID你可以用枚举来管理typedefuint32_tEventID;// 2. 定义回调函数类型订阅者需要实现这个函数// 参数: 事件ID, 数据指针, 数据长度typedefvoid(*EventCallback)(EventID event,void*data,uint32_tlen);// 配置参数#defineMAX_EVENTS32// 最大支持的事件种类#defineMAX_SUBSCRIBERS8// 每个事件最大支持的订阅者数量// 内部数据结构typedefstruct{EventCallback callbacks[MAX_SUBSCRIBERS];uint8_tcount;}EventNode;staticEventNode event_table[MAX_EVENTS];staticbool is_initializedfalse;// 初始化voidpubsub_init(void){memset(event_table,0,sizeof(event_table));is_initializedtrue;}// 订阅boolpubsub_subscribe(EventID event,EventCallback cb){if(!is_initialized||eventMAX_EVENTS||cbNULL)returnfalse;EventNode*nodeevent_table[event];// 检查是否已经订阅过for(uint8_ti0;inode-count;i){if(node-callbacks[i]cb)returntrue;}// 添加新订阅if(node-countMAX_SUBSCRIBERS){node-callbacks[node-count]cb;returntrue;}returnfalse;// 订阅者满了}// 取消订阅boolpubsub_unsubscribe(EventID event,EventCallback cb){if(!is_initialized||eventMAX_EVENTS||cbNULL)returnfalse;EventNode*nodeevent_table[event];for(uint8_ti0;inode-count;i){if(node-callbacks[i]cb){// 用最后一个元素覆盖当前元素然后减少计数node-callbacks[i]node-callbacks[--node-count];returntrue;}}returnfalse;}// 发布voidpubsub_publish(EventID event,void*data,uint32_tlen){if(!is_initialized||eventMAX_EVENTS)return;EventNode*nodeevent_table[event];// 遍历并执行所有回调for(uint8_ti0;inode-count;i){if(node-callbacks[i]!NULL){node-callbacks[i](event,data,len);}}}// 定义事件枚举enum{EVT_SENSOR_DATA0,EVT_BUTTON_PRESSED,EVT_MAX};// --- 订阅者 A打印传感器数据 ---voidsensor_logger(EventID event,void*data,uint32_tlen){int*val(int*)data;printf([Logger] 收到传感器数据: %d\n,*val);}// --- 订阅者 B数据超限报警 ---voidthreshold_checker(EventID event,void*data,uint32_tlen){int*val(int*)data;if(*val100){printf([Checker] 警告数据超限: %d\n,*val);}}// --- 发布者模拟硬件读取 ---voidhardware_task(){intsensor_value120;// 假设读到了 120printf([Publisher] 硬件读取完毕发布事件...\n);// 发布者只管发不管谁在听pubsub_publish(EVT_SENSOR_DATA,sensor_value,sizeof(sensor_value));}intmain(){pubsub_init();// 订阅者注册自己的处理函数pubsub_subscribe(EVT_SENSOR_DATA,sensor_logger);pubsub_subscribe(EVT_SENSOR_DATA,threshold_checker);// 触发一次发布hardware_task();// 取消订阅pubsub_unsubscribe(EVT_SENSOR_DATA,sensor_logger);printf(\n--- 取消 Logger 后再次发布 ---\n);hardware_task();return0;}从结构流程上看发布者——》给出清单event——》并发表event对应的动作如gpio反转。订阅者———》获取到event——》注册对应事件的回调处理如发布者发布了gpio反转的这个事件那对应订阅者的回调就会出现gpio反转这个动作如对这个动作进行传输显示到屏幕上。2、具体如何使用这里给出实例串口1作为交互和输出信息。串口5gpiog7fdcan1time3这些作为event触发者给发布者提供事件订阅者则根据订阅的内容进行输出当前订阅后对应什么事件。这里展示部分主要代码typedefenum{EVT_NONE0,EVT_TIMER_TICK,EVT_UART5_RX,EVT_FDCAN_RX,EVT_FDCAN_TX_DONE,EVT_GPIO7_TOGGLE,EVT_MAX}EventType;// 订阅者回调函数类型每个订阅者收到事件时执行此函数。// evt事件类型data附加参数如消息指针、状态指针等。typedefvoid(*SubscriberCallback)(EventType evt,void*data);// 每个订阅者节点都是一个链表节点事件类型 回调函数 下一个节点。typedefstructSubscriberNode{EventType evt;SubscriberCallback callback;structSubscriberNode*next;}SubscriberNode;// 事件队列中断函数将事件写入队列主循环统一消费。// 这样可以避免在中断里做复杂逻辑保证中断服务时间短。typedefstruct{EventType evt;void*data;}EventMsg;staticEventMsg s_eventQueue[EVENT_QUEUE_SIZE];staticvolatileuint16_ts_head0;staticvolatileuint16_ts_tail0;// 初始化 // 作用清空订阅关系、事件队列和节点池确保系统从干净状态启动。voidPubSub_Init(void){for(inti0;iEVT_MAX;i){s_subscribers[i]NULL;}memset(s_nodeAllocFlag,0,sizeof(s_nodeAllocFlag));s_head0;s_tail0;}// 订阅/取消订阅 // 作用让某个模块关注某类事件。// 例如系统状态模块订阅 EVT_TIMER_TICK、EVT_UART5_RX 等。boolPubSub_Subscribe(EventType evt,SubscriberCallback cb){if(evtEVT_NONE||evtEVT_MAX||cbNULL)returnfalse;SubscriberNode*nodes_subscribers[evt];while(node){if(node-callbackcb)returnfalse;nodenode-next;}SubscriberNode*newNodeNodeAlloc();if(!newNode)returnfalse;newNode-evtevt;newNode-callbackcb;newNode-nexts_subscribers[evt];s_subscribers[evt]newNode;returntrue;}boolPubSub_Unsubscribe(EventType evt,SubscriberCallback cb){if(evtEVT_NONE||evtEVT_MAX||cbNULL)returnfalse;SubscriberNode**pps_subscribers[evt];while(*pp){if((*pp)-callbackcb){SubscriberNode*toDelete*pp;*pp(*pp)-next;NodeFree(toDelete);returntrue;}pp(*pp)-next;}returnfalse;}// 作用真正把事件送给所有订阅该事件的回调函数。staticvoidDispatch(EventType evt,void*data){if(evtEVT_NONE||evtEVT_MAX)return;printf([PUBSUB] dispatch evt%d to subscribers\r\n,(int)evt);SubscriberNode*nodes_subscribers[evt];while(node){if(node-callback){printf([PUBSUB] callback invoked for evt%d\r\n,(int)evt);node-callback(evt,data);}nodenode-next;}}// 作用中断函数调用直接把事件压入队列不在中断中做耗时工作。voidPubSub_PublishFromISR(EventType evt,void*data){uint16_tnext(s_head1U)%EVENT_QUEUE_SIZE;if(next!s_tail){s_eventQueue[s_head].evtevt;s_eventQueue[s_head].datadata;s_headnext;printf([PUBSUB] published evt%d to queue\r\n,(int)evt);}else{printf([PUBSUB] queue full, evt%d dropped\r\n,(int)evt);}}// 作用普通线程/函数发布事件先关中断再入队保证线程安全。voidPubSub_Publish(EventType evt,void*data){printf([PUBSUB] publish request evt%d\r\n,(int)evt);__disable_irq();PubSub_PublishFromISR(evt,data);__enable_irq();}// 作用主循环中消费事件依次取出队列并分发给订阅者。voidPubSub_ProcessQueue(void){__disable_irq();while(s_head!s_tail){EventMsg msgs_eventQueue[s_tail];s_tail(s_tail1U)%EVENT_QUEUE_SIZE;__enable_irq();Dispatch(msg.evt,msg.data);__disable_irq();}__enable_irq();}// // 2. 业务订阅者系统状态处理模块// 作用作为“唯一的业务处理者”统一处理定时器、串口、FDCAN 和 GPIO 事件。// 这样主循环和硬件回调不直接写具体业务代码而是做事件分发。此处就是//对应订阅者的处理。// staticvoidSystemStatusConsumer(EventType evt,void*data){switch(evt){caseEVT_TIMER_TICK:// printf([SYS] Timer tick - heartbeat\r\n);HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_7);//随定时器的周期闪烁。但需要将打印信息注释break;caseEVT_UART5_RX:printf([SYS] UART5 receive event\r\n);break;caseEVT_FDCAN_RX:printf([SYS] FDCAN receive event\r\n);break;caseEVT_FDCAN_TX_DONE:printf([SYS] FDCAN tx complete\r\n);break;caseEVT_GPIO7_TOGGLE:HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_7);break;default:break;}}// 作用注册所有真存在的事件订阅者。voidSystemSubscribers_Init(void){PubSub_Subscribe(EVT_TIMER_TICK,SystemStatusConsumer);PubSub_Subscribe(EVT_UART5_RX,SystemStatusConsumer);PubSub_Subscribe(EVT_FDCAN_RX,SystemStatusConsumer);PubSub_Subscribe(EVT_FDCAN_TX_DONE,SystemStatusConsumer);PubSub_Subscribe(EVT_GPIO7_TOGGLE,SystemStatusConsumer);}// // 3. GPIOG7 输出控制// 作用GPIOG7 是当前板子上真实存在的输出引脚// 用来做状态指示或心跳输出配合 TIM 定时器周期翻转。// voidGpio7Publisher_Init(void){HAL_GPIO_WritePin(GPIOG,GPIO_PIN_7,GPIO_PIN_RESET);}voidGpio7Publisher_Toggle(void){PubSub_Publish(EVT_GPIO7_TOGGLE,NULL);}main(){// 1) 初始化事件总线清空订阅关系和事件队列准备接收后续硬件事件PubSub_Init();// 2) 初始化真实存在的功能模块Gpio7Publisher_Init();// 3) 注册事件订阅关系哪些模块关心哪些事件SystemSubscribers_Init();// 4) 触发测试事件验证事件总线工作正常Gpio7Publisher_Toggle();while(1){PubSub_ProcessQueue();}}源码https://gitee.com/c_y_l_N_H/tmp_pit/tree/master对应输出结果