实现简单的多任务轮询(C语言)

实现简单的多任务轮询(C语言)

前言

好久没有做MCU的项目了,最近因为工作需要接手一个STM32的项目,因为项目要求比较简单,也就没有用到操作系统,而是用了简单的状态机+任务轮询的方式。闲暇之余写下这篇简短的博客,记录一下自己的所知所想,也希望对那些刚进入MCU的新手们,能有些许的帮助。

总体思路

利用状态机的方式,在一个循环中不停的去判断每一个任务的执行标识,当判断标识为真时,则执行响应的任务,任务执行结束后及时的清除任务标识。

实现过程

先定义一个任务的结构体,结构体中包含任务的执行函数和是否需要执行的标识。

typedef struct __TASK{
	int (*func)(void *);		//任务执行的函数,执行时会传入下面的参数
	void *arg;						//任务执行时带入的参数
	int flag;
} task_t;

在定义一个任务组用于管理这些需要被轮询的任务:

typedef struct __TASK_GRP{
	task_t *task_list;			//需要执行任务集合
	int list_size;				//list的大小
}taskGrp_t;

下面定义两个宏用于定义和初始化一个任务列表和任务组。注意下面的方法在有些编译器中不支持,目前我知道的就是在keil下的C89就不支持这种方式。

#define TASK_LIST_INIT(name,size) 	task_t name[size]={0};
#define TASK_GPR_INIT(name,list,size) \
struct __TASK_GRP name={ \
	.task_list = list, \
	.list_size = size, \
};	

有些人看到这个肯定有点懵!其实上面的方式就是利用在定义变量时对变量进行初始化,然后再用宏把这个过程包装起来就行,在使用的时候只需要调用宏并传入响应的参数就行。例如:TASK_GPR_INIT(task_group,task_list,TASK_SIZE);
等价于
struct __TASK_GRP task_group={
.task_list = task_list,
.list_size = TASK_SIZE,
};
然后定义几个函数用于对任务组和任务进行操作:

初始化一个任务组,目的和上面提高的两个初始化相同。两种方式调用哪个都可以。

void task_grop_init(taskGrp_t *grp,task_t *list,int size)
{
	
	grp->task_list = list;
	grp->list_size = size;
	memset(list,0,sizeof(task_t)*size);
}

将要执行的操作函数添加到任务组中相应的任务中。flag标识用于是否默认激活任务。

void task_add_to_list(taskGrp_t *grp,int index,int (*func)(void *),void *arg,int flag)
{
	grp->task_list[index].func = func;
	grp->task_list[index].arg = arg;
	grp->task_list[index].flag = flag;
}

激活索引为index的任务,让其能参与轮询。

void activate_task(taskGrp_t *grp,int index)
{
	grp->task_list[index].flag = 1;
}

开始任务轮询操作,里面是一个大循环,会一直查找任务状态并执行相应任务。当判断到任务的返回值为1时则认为任务一次执行完成,将关闭任务,只有等待下一次重新激活后,才重新执行相应任务。

void start_polling(struct __TASK_GRP *grp)
{
	int i=0;
	if(grp->task_list != NULL){
		while(1){
			for(i=0;i<grp->list_size;i++){
				if((grp->task_list[i].flag)&&(grp->task_list[i].func != NULL)){
					grp->task_list[i].flag = grp->task_list[i].func(grp->task_list[i].arg) ? 0 : 1;
				}else{
					grp->task_list[i].flag = 0;
				}			
			}
		}
	}
}

调用示例

enum{
	TASK_1,
	TASK_2,
	TASK_3,
	TASK_SIZE
}TASK_INDEX;

TASK_LIST_INIT(task_list,TASK_SIZE);
TASK_GPR_INIT(task_group,task_list,TASK_SIZE);

int task1_func(void *arg)
{
	int retval=0;
	static int step=0;
	switch(step){
	case 0:
		printf("%s step %d\n",__func__,step);
		step=1;
		break;
	case 1:
		printf("%s step %d\n",__func__,step);
		step=2;
		break;
	case 2:
		printf("%s step %d\n",__func__,step);
		step=0;
		retval=1;
		break;
	}
	return retval;
}

int task2_func(void *arg)
{
	int retval=0;
	static int step=0;
	switch(step){
	case 0:
		printf("%s step %d\n",__func__,step);
		step=1;
		break;
	case 1:
		printf("%s step %d\n",__func__,step);
		step=2;
		break;
	case 2:
		printf("%s step %d\n",__func__,step);
		step=0;
		retval=1;
		break;
	}
	return retval;
}
int task3_func(void *arg)
{
	int retval=0;
	static int step=0;
	switch(step){
	case 0:
		printf("%s step %d\n",__func__,step);
		step=1;
		break;
	case 1:
		printf("%s step %d\n",__func__,step);
		step=2;
		break;
	case 2:
		printf("%s step %d\n",__func__,step);
		step=0;
		retval=1;
		break;
	}
	return retval;
}

int main(void)
{
	task_add_to_list(&task_group,TASK_1,task1_func,NULL,1);
	task_add_to_list(&task_group,TASK_2,task2_func,NULL,1);
	task_add_to_list(&task_group,TASK_3,task3_func,NULL,1);
	start_polling(&task_group);
}

完整代码

task.h

#ifndef __TASK_H__
#define __TASK_H__

#define TASK_LIST_INIT(name,size) 	task_t name[size]={0};
#define TASK_GPR_INIT(name,list,size) \
struct __TASK_GRP name={ \
	.task_list = list, \
	.list_size = size, \
};	




typedef struct __TASK{
	int (*func)(void *);
	void *arg;						//任务执行时带入的参数
	int flag;
} task_t;


typedef struct __TASK_GRP{
	task_t *task_list;			//需要执行任务表
	int list_size;				//list的大小
}taskGrp_t;


extern void task_grop_init(taskGrp_t *grp,task_t *list,int size);
extern void task_add_to_list(taskGrp_t *grp,int index,int (*func)(void *),void *arg,int flag);
extern void activate_task(taskGrp_t *grp,int index);
extern void start_polling(struct __TASK_GRP *grp);
#endif /* __TASK_H__ */

task.c

#include <string.h>
#include "task.h"

void task_grop_init(taskGrp_t *grp,task_t *list,int size)
{
	
	grp->task_list = list;
	grp->list_size = size;
	memset(list,0,sizeof(task_t)*size);
}

void task_add_to_list(taskGrp_t *grp,int index,int (*func)(void *),void *arg,int flag)
{
	grp->task_list[index].func = func;
	grp->task_list[index].arg = arg;
	grp->task_list[index].flag = flag;
}

void activate_task(taskGrp_t *grp,int index)
{
	grp->task_list[index].flag = 1;
}

void start_polling(struct __TASK_GRP *grp)
{
	int i=0;
	if(grp->task_list != NULL){
		while(1){
			for(i=0;i<grp->list_size;i++){
				if((grp->task_list[i].flag)&&(grp->task_list[i].func != NULL)){
					grp->task_list[i].flag = grp->task_list[i].func(grp->task_list[i].arg) ? 0 : 1;
				}else{
					grp->task_list[i].flag = 0;
				}			
			}
		}
	}
}

main.c

#include <stdio.h>
#include "task.h"

enum{
	TASK_1,
	TASK_2,
	TASK_3,
	TASK_SIZE
}TASK_INDEX;

TASK_LIST_INIT(task_list,TASK_SIZE);
TASK_GPR_INIT(task_group,task_list,TASK_SIZE);

int task1_func(void *arg)
{
	int retval=0;
	static int step=0;
	switch(step){
	case 0:
		printf("%s step %d\n",__func__,step);
		step=1;
		break;
	case 1:
		printf("%s step %d\n",__func__,step);
		step=2;
		break;
	case 2:
		printf("%s step %d\n",__func__,step);
		step=0;
		retval=1;
		break;
	}
	return retval;
}

int task2_func(void *arg)
{
	int retval=0;
	static int step=0;
	switch(step){
	case 0:
		printf("%s step %d\n",__func__,step);
		step=1;
		break;
	case 1:
		printf("%s step %d\n",__func__,step);
		step=2;
		break;
	case 2:
		printf("%s step %d\n",__func__,step);
		step=0;
		retval=1;
		break;
	}
	return retval;
}
int task3_func(void *arg)
{
	int retval=0;
	static int step=0;
	switch(step){
	case 0:
		printf("%s step %d\n",__func__,step);
		step=1;
		break;
	case 1:
		printf("%s step %d\n",__func__,step);
		step=2;
		break;
	case 2:
		printf("%s step %d\n",__func__,step);
		step=0;
		retval=1;
		break;
	}
	return retval;
}

int main(void)
{
	task_add_to_list(&task_group,TASK_1,task1_func,NULL,1);
	task_add_to_list(&task_group,TASK_2,task2_func,NULL,1);
	task_add_to_list(&task_group,TASK_3,task3_func,NULL,1);
	start_polling(&task_group);
}
  • 9
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 以下是一个简单C语言代码示例,演示如何在主从设备之间使用RS485通信进行轮询。 主设备代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <termios.h> int main(int argc, char *argv[]) { int fd; struct termios options; // 打开串口设备 fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY); if (fd == -1) { printf("无法打开串口设备\n"); exit(1); } // 配置串口 tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; tcsetattr(fd, TCSANOW, &options); // 发送轮询命令给从设备 char poll_cmd[] = {0x01, 0x02, 0x03, 0x04}; write(fd, poll_cmd, sizeof(poll_cmd)); // 接收从设备的响应 char buffer[255]; int n = read(fd, buffer, sizeof(buffer)); if (n > 0) { printf("收到从设备响应: %s\n", buffer); } close(fd); return 0; } ``` 从设备代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <termios.h> int main(int argc, char *argv[]) { int fd; struct termios options; // 打开串口设备 fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY); if (fd == -1) { printf("无法打开串口设备\n"); exit(1); } // 配置串口 tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; tcsetattr(fd, TCSANOW, &options); // 等待收到轮询命令 char buffer[255]; int n = read(fd, buffer, sizeof(buffer)); if (n > 0) { printf("收到轮询命令\n"); // 处理轮询命令并回复响应 char response[] = "OK"; write(fd, response, sizeof(response)); } close(fd); return 0; } ``` 需要注意的是,在使用RS485通信时,需要根据实际硬件设置串口的参数,如波特率、数据位、停止位等。上述代码中的串口设备路径为`/dev/tty ### 回答2: RS485通讯是一种常用的工业通信协议,采用差分传输方式,可以实现远距离、高速、抗干扰的通信。主从轮询是RS485通讯中的一种通信方式,主设备通过发送查询命令,从设备接收并响应。 以下是一个基于C语言简单示例代码: 主设备代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> int main() { int fd; struct termios newtio; // 打开串口设备 fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY); if (fd < 0) { perror("Open serial port error!"); return -1; } // 配置串口通信参数 tcgetattr(fd, &newtio); newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~CSIZE; newtio.c_cflag |= CS8; newtio.c_cflag &= ~CRTSCTS; newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); newtio.c_oflag &= ~OPOST; newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 1; tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &newtio); // 向从设备发送查询命令 unsigned char command[5] = {0x01, 0x03, 0x00, 0x00, 0x00}; write(fd, command, sizeof(command)); // 读取从设备响应的数据 unsigned char response[256]; int len = read(fd, response, sizeof(response)); if (len > 0) { for (int i = 0; i < len; i++) { printf("%02X ", response[i]); } printf("\n"); } // 关闭串口设备 close(fd); return 0; } ``` 从设备代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> int main() { int fd; struct termios newtio; // 打开串口设备 fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY); if (fd < 0) { perror("Open serial port error!"); return -1; } // 配置串口通信参数 tcgetattr(fd, &newtio); newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~CSIZE; newtio.c_cflag |= CS8; newtio.c_cflag &= ~CRTSCTS; newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); newtio.c_oflag &= ~OPOST; newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 1; tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &newtio); // 接收主设备发送的查询命令 unsigned char command[5]; int len = read(fd, command, sizeof(command)); if (len > 0) { // 生成从设备的响应数据 unsigned char response[8] = {0x01, 0x03, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04}; write(fd, response, sizeof(response)); } // 关闭串口设备 close(fd); return 0; } ``` 以上代码是一个简单的RS485通讯示例,主设备发送一个查询命令,从设备接收后返回固定的响应数据。这只是基础代码,实际应用中可能需要根据具体通信协议和设备规范进行更详细的开发和配置。 ### 回答3: RS485通信是一种多点通信的串行通信方式,通常用于远距离数据传输。在RS485通信中,常常使用主从轮询的方式进行数据通信。 C语言代码中,首先需要调用RS485通信相关的库函数,并设置通信参数以及打开串口。然后,通过获取从机地址、设定数据发送的寄存器地址以及传输长度等信息,进行数据的发送和接收。 例如,在主机端的C语言代码中,可以使用以下代码实现RS485通信主从轮询: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> int main(int argc, char* argv[]){ int fd; char *dev_path = "/dev/ttyUSB0"; // 串口设备路径 char tx_buf[10] = {0x01, 0x03, 0x00, 0x01, 0x00, 0x02, 0xC4, 0x0B}; // 发送数据缓冲区 char rx_buf[10] = {0}; // 接收数据缓冲区 // 打开串口 fd = open(dev_path, O_RDWR|O_NOCTTY|O_NONBLOCK); if(fd == -1){ printf("无法打开串口设备!\n"); return -1; } // 设置串口参数 struct termios opt; tcgetattr(fd, &opt); cfsetispeed(&opt, B9600); // 设置波特率为9600 cfsetospeed(&opt, B9600); tcsetattr(fd, TCSANOW, &opt); // 主从轮询 for(int slave_addr = 1; slave_addr <= 5; slave_addr++){ // 设置从机地址 tx_buf[0] = slave_addr; // 发送数据 write(fd, tx_buf, sizeof(tx_buf)); // 接收数据 read(fd, rx_buf, sizeof(rx_buf)); // 处理接收到的数据 for(int i = 0; i < sizeof(rx_buf); i++){ printf("接收到的数据:%02X\n", rx_buf[i]); } // 清空接收缓冲区 memset(rx_buf, 0, sizeof(rx_buf)); // 等待一段时间 sleep(1); } close(fd); // 关闭串口 return 0; } 这段代码实现了通过RS485通信进行主从轮询,并打印接收到的数据。在代码中需要注意的是,串口设备路径、波特率、从机地址等需要根据实际情况进行设置。另外,需要注意错误处理以及数据格式的转换等问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飘忽不定的bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值