二级指针操作按键的链表

#include<stdio.h>


//节点的结构体
typedef struct button
{
	  int data;
  	struct button *Next;  
}Button_t;


//头结点 指针
static struct button* Head_Button = NULL;



//打印这个链表   因为头是全局变量  所以无需传参
static void Show_ButtonList(void)
{
	Button_t* p=Head_Button;
	//printf("ADDR::NewNode:%08x-Head:%08x\n",p, &Head_Button);//不一样的!说明p Head_Button的内存地址不同 他们是指向相同而已!head不会变化的
	while(p)
	{
		printf("Node::Add:%08x data:%d Next:%08x--> ",p,p->data,p->Next );
		p=p->Next;
	}
	printf("\n\n" );
}

//查找一个节点  依靠按键类的data判定 如果有 找到返回1  没找到返回0
static int Search_Button(Button_t* btn)
{
  struct button* pass_btn;
  for(pass_btn = Head_Button; pass_btn != NULL; pass_btn = pass_btn->Next)
  {
    if(pass_btn->data == btn->data)
    	return 1;
  }
  return 0;
}

//增加一个节点  成功0 失败1
static int Add_Button(Button_t* btn)
{
  if(Search_Button(btn))//找到
  	return 1;	
  btn->Next = Head_Button;
  Head_Button = btn;
  	return 0;
}

//删除一个节点  成功0 失败1
static int Button_Delete(Button_t *btn)
{
  struct button** curr;
  if(Search_Button(btn)==0)//没找到
  	return 1;	
  for(curr = &Head_Button; *curr;) 
  {
    struct button* entry = *curr;//非常巧妙 二级指针迂回保护了头没有动!
    if (entry == btn) 
    {
      *curr = entry->Next;
    } 
    else
    {
      curr = &entry->Next;
    }
  }
  return 0;
}


Button_t *  Button_Reverse(Button_t *head)
{
  printf("1\n");
  Button_t *  newhead;
  if(head == NULL || head->Next == NULL)
    return head; 
  printf("2\n");
  newhead = Button_Reverse(head->Next);

  printf("new :%d ",newhead->data );
  head->Next->Next = head;
  head->Next=NULL;
   return newhead;
}


int main()
{


	//printf("%s\n","hello" );
	//Show_ButtonList();
	button b1,b2,b3,b4;
	b1.data=1;
	Add_Button(&b1);
	//Show_ButtonList();//1
	b2.data=2;
	Add_Button(&b2);
	//Show_ButtonList();//2-1
  b3.data=3;
 Add_Button(&b3);
 Show_ButtonList();
  //b4.data=4;
  //Add_Button(&b4);
  //Show_ButtonList();


	//Button_Delete(&b3);
	//Show_ButtonList();
	
  Head_Button = Button_Reverse(Head_Button);
  Show_ButtonList();

}

 

<think>嗯,用户问的是如何用函数指针给链表实现STM32裸机多任务调度。这个问题看起来涉及嵌入式系统,特别是STM32在没有RTOS的情况下进行多任务处理。首先,我需要回忆一下裸机多任务调度的常见方法,比如状态机、协作式调度或者时间片轮转。用户提到使用函数指针和链表,应该是指通过链表来管理不同的任务函数,每个任务对应一个函数指针,然后按某种顺序调度执行。 首先,可能需要定义一个结构体来表示任务,包含函数指针、执行间隔、上次执行时间等信息。比如: struct Task { void (*func)(void); uint32_t interval; uint32_t last_run; struct Task *next; }; 这样每个任务节点都有要执行的函数,时间间隔,以及链表指针。然后,维护一个任务链表,通过遍历链表来检查每个任务是否需要执行。 接下来,用户可能需要知道如何初始化链表,添加任务,删除任务等操作。比如,添加任务函数可能会将新任务节点插入链表尾部或头部,取决于调度策略。然后,在主循环中不断检查系统时间,比如通过SysTick定时器来获取当前时间,然后遍历链表,如果当前时间减去任务的上次执行时间超过了设定的间隔,就执行该任务,并更新last_run时间。 另外,需要考虑到裸机环境下如何管理时间。STM32通常有定时器,比如SysTick,可以用来提供时间基准。比如,配置SysTick每1ms中断一次,更新一个全局变量作为系统时钟。这样任务的时间管理可以基于这个全局时间。 然后,任务调度可以是协作式的,即每个任务执行完毕后主动让出CPU,或者基于时间片的抢占式。但裸机下一般没有抢占,所以通常是协作式或时间片轮转。这里用链表的话,可能更倾向于时间片轮转,每个任务按顺序检查是否到执行时间,依次执行。 需要注意的是,函数指针的使用,确保每个任务函数的签名一致,比如void func(void),没有参数和返回值。这样链表中的每个任务节点都能正确指向这些函数。 另外,任务链表操作需要考虑线程安全,但在裸机单线程环境下,可能不需要考虑,因为中断可能会修改链表。如果任务添加或删除是在中断中进行的,可能需要禁用中断来保护链表操作,防止竞争条件。 可能还需要考虑任务的优先级,但简单的链表可能只是按顺序执行,没有优先级。如果需要优先级,可能需要使用优先队列或按优先级排序的链表,但会增加复杂度。 用户可能还关心如何动态添加和删除任务,这时候链表结构就比较灵活,可以动态管理任务节点。比如,在系统运行时,根据条件添加新任务或移除旧任务。 最后,需要给出一个完整的示例代码框架,展示如何定义任务结构,初始化链表,添加任务,主循环调度,以及时间更新的处理。同时,提醒注意事项,比如函数指针的合法性,避免空指针,时间溢出的处理等。 可能还需要解释一下这种方法的优缺点,比如相比RTOS,资源占用少,但缺乏任务优先级、同步机制等高级功能,适合简单的多任务需求。 总结来说,步骤应该是: 1. 定义任务结构体,包含函数指针、时间参数和链表指针。 2. 实现链表操作函数(添加、删除、遍历)。 3. 利用定时器(如SysTick)维护系统时间。 4. 在主循环中遍历链表,执行符合条件的任务。 5. 处理动态任务管理及可能的并发问题。</think>好的,我将以STM32裸机环境为例,分步骤说明如何使用函数指针和链表实现多任务调度: --- ### 一、实现原理 1. **任务抽象**:将每个任务封装为包含函数指针的结构体 2. **链表管理**:通过链表实现任务的动态增删 3. **时间片轮询**:基于SysTick定时器实现时间基准 4. **调度策略**:采用协作式调度(非抢占式) --- ### 二、具体实现步骤 #### 1. 定义任务结构体 ```c typedef struct Task { void (*handler)(void*); // 函数指针 void* arg; // 函数参数 uint32_t interval; // 执行间隔(ms) uint32_t last_run; // 上次执行时间 struct Task* next; // 链表指针 } Task; ``` #### 2. 创建任务链表头 ```c static Task* task_list = NULL; ``` #### 3. 实现SysTick时间基准(1ms中断) ```c volatile uint32_t system_ticks = 0; void SysTick_Handler(void) { system_ticks++; } void systick_init(void) { SysTick_Config(SystemCoreClock / 1000); // 1ms中断 } ``` #### 4. 任务操作函数 ```c // 添加任务到链表 void task_add(void (*handler)(void*), void* arg, uint32_t interval) { Task* new_task = (Task*)malloc(sizeof(Task)); new_task->handler = handler; new_task->arg = arg; new_task->interval = interval; new_task->last_run = system_ticks; new_task->next = task_list; task_list = new_task; } // 从链表删除任务 void task_remove(void (*handler)(void*)) { Task** curr = &task_list; while (*curr) { if ((*curr)->handler == handler) { Task* temp = *curr; *curr = (*curr)->next; free(temp); return; } curr = &(*curr)->next; } } ``` #### 5. 任务调度核心 ```c void task_scheduler(void) { Task* curr = task_list; while (curr) { if ((system_ticks - curr->last_run) >= curr->interval) { curr->handler(curr->arg); // 执行任务 curr->last_run = system_ticks; // 更新时间戳 } curr = curr->next; } } ``` #### 6. 主循环实现 ```c int main(void) { systick_init(); // 示例任务注册 task_add(led_task, (void*)LED1, 500); // LED闪烁 task_add(sensor_read, NULL, 100); // 传感器读取 while (1) { task_scheduler(); __WFI(); // 进入低功耗模式 } } ``` --- ### 三、关键点说明 1. **函数指针规范**: - 所有任务函数必须符合`void func(void*)`格式 - 通过`arg`参数实现任务参数传递 2. **时间管理**: - 使用32位无符号数存储时间戳 - 自动处理时间回绕问题(无符号数减法特性) 3. **内存管理**: - 动态内存分配需谨慎(可替换为静态数组) - 任务删除时必须正确释放内存 4. **调度特性**: - 非抢占式调度 - 任务执行时间必须短于最小间隔 - 最长任务间隔不应超过49.7天(32位计数器限制) --- ### 四、扩展优化建议 1. **优先级支持**: ```c typedef struct Task { // 新增优先级字段 uint8_t priority; } Task; ``` 2. **超时保护机制**: ```c void task_scheduler(void) { Task* curr = task_list; while (curr) { uint32_t start = system_ticks; curr->handler(curr->arg); if ((system_ticks - start) > curr->interval) { // 触发超时处理 } // ... } } ``` 3. **统计功能**: ```c typedef struct Task { // 新增执行统计 uint32_t exec_count; uint32_t max_time; } Task; ``` --- ### 五、优缺点分析 **优势**: - 资源占用小(RAM≈20字节/任务) - 无RTOS学习成本 - 支持动态任务管理 **局限**: - 缺乏抢占式调度 - 无任务间通信机制 - 需自行处理资源竞争 这种实现方式非常适合需要简单任务调度的小型嵌入式系统(如需要同时处理按键扫描、LED指示、传感器采集等任务的场合),但对于复杂系统仍建议使用RTOS。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值