RT-Thread线程&设备&通信接口(备忘录)

RT-Thread线程通信接口


线程接口

#define THREAD_PRIORITY         25
#define THREAD_STACK_SIZE       512
#define THREAD_TIMESLICE        5
rt_thread_yield();/*让出CPU资源*/
rt_thread_sleep(ticks);
rt_thread_delay(ticks);
rt_thread_delay(ms);
rt_thread_suspend(xxx_thread);
rt_thread_resume(xxx_thread);
void xxx_thread_entry(void *parameter)
{
    while(1)
    {

    }
}
static int xxx_task_init(void)
{
    rt_err_t ret = RT_EOK;
    /*device初始化*/

    /*ipc初始化*/

    /* 创建 xxx 线程 --- 动态分配*/
    rt_thread_t xxx_thread = rt_thread_create("xxx", 
                                            xxx_thread_entry,
                                            RT_NULL,
                                            THREAD_STACK_SIZE,
                                            THREAD_PRIORITY,
                                            THREAD_TIMESLICE);
    /* 创建成功则启动线程 */
    if (thread != RT_NULL)
    {
        rt_thread_startup(xxx_thread);
    }
    else
    {
        ret = RT_ERROR;
    }

    /*创建线程 --- 静态分配*/
    ALIGN(RT_ALIGN_SIZE)
    static char xxx_thread_stack[THREAD_STACK_SIZE];
    static struct rt_thread xxx_thread;
    rt_thread_init(xxx_thread,
                "xxx",
                xxx_thread_entry,
                RT_NULL,
                &xxx_thread_stack[0],
                THREAD_STACK_SIZE,
                THREAD_PRIORITY - 1, THREAD_TIMESLICE);
    rt_thread_startup(&xxx_thread);
}
INIT_APP_EXPORT(xxx_task_init);
  • 注意:

线程必须加入while(1),不然执行一遍后会调用rt_thread_exit(),进入exit状态,相当于执行了rt_thread_delete/detach().


设备接口(device)

/*device flag*/
#define RT_DEVICE_FLAG_RDONLY           0x001           /**< read only */
#define RT_DEVICE_FLAG_WRONLY           0x002           /**< write only */
#define RT_DEVICE_FLAG_RDWR             0x003           /**< read and write */
#define RT_DEVICE_FLAG_INT_RX           0x100           /**< INT mode on Rx */
#define RT_DEVICE_FLAG_DMA_RX           0x200           /**< DMA mode on Rx */
#define RT_DEVICE_FLAG_INT_TX           0x400           /**< INT mode on Tx */
#define RT_DEVICE_FLAG_DMA_TX           0x800           /**< DMA mode on Tx */
#define RT_DEVICE_FLAG_STREAM           0x040           /**< stream mode */

/*cmd--device config*/
#define RT_DEVICE_CTRL_CONFIG           0x03            /**< configure device */

static rt_err_t xxx_input(rt_device_t dev, rt_size_t size);

struct serial_configure config= {};/*串口的配置结构体*/
char str[XXX_RB_BUFSZ+1];
/****************设备操作****************/
rt_device_t xxx_device = NULL;
xxx_device = rt_device_find("xxx");
rt_device_control(xxx_device,cmd,param)
ret = rt_device_open(xxx_device,flag);
rt_device_set_rx_indicate(xxx_device,xxx_input)

/*tips回调函数包含dev*/
rt_device_write(xxx_device,pos,str,sizeof(str)-1)
length = rt_device_read(xxx_device,pos,str,size)


线程间通信接口

线程间通信

线程间通信的接口都提供了两种初始化方式 —— 动态和静态.
它们的接口有如下关系:
动态的创建和删除:xxx_create()/xxx_delete()
静态的创建和解绑:xxx_init()/xxx_detach()


1.邮箱


typedef struct rt_mailbox* rt_mailbox_t;
#define XXX_MB_BUFSZ    64
#define XXX_STR_BUFSZ   32
/*************** 静态创建 ********************/
/* 邮箱控制块 */
static struct rt_mailbox xxx_mb;
/* 用于放邮件的内存池 */
static char mb_pool[XXX_MB_BUFSZ];

static char mb_str[XXX_STR_BUFSZ] = "I'm a mail!"
static char *str
result = rt_mb_init(&xxx_mb,
                    "xxx_mb",       /* 名称是 mbt */
                    &mb_pool[0],    /* 邮箱用到的内存池是 mb_pool */
                    sizeof(mb_pool) / 4,    /* 邮箱中的邮件数目,因为一封邮件占 4 字节 */
                    RT_IPC_FLAG_FIFO);      /* 采用 FIFO 方式进行线程等待 */

/*************** 动态创建 ********************/
rt_mailbox_t xxx_mb = RT_NULL;
xxx_mb = rt_mb_create("xxx_mb",sizeof(mb_pool) / 4,RT_IPC_FLAG_FIFO)
/* xxx_thread发送 */
rt_mb_send(&mb, (rt_uint32_t)&mb_str)
/* yyy_thread接收 */
rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER)
  • 邮箱用法
邮箱只能传4字节的数据,通常用邮箱传数据结构的地址,比如,数组的地址,结构体的地址等.

2.消息队列

typedef struct rt_messagequeue* rt_mq_t;
#define XXX_MQ_BUFSZ    2048
/*************** 动态创建 ********************/
rt_mq_t xxx_mq = RT_NULL;
xxx_mq = rt_mq_create("xxx_mq",sizeof(msg_pool),RT_IPC_FLAG_FIFO);
/*************** 静态创建 ********************/
/* 消息队列控制块 */
static struct rt_messagequeue xxx_mq;
/* 消息队列中用到的放置消息的内存池 */
static rt_uint8_t msg_pool[XXX_MQ_BUFSZ];
/* 初始化消息队列 */
result = rt_mq_init(&xxx_mq,
                    "xxx_mq",
                    &msg_pool[0],   /* 内存池指向 msg_pool */
                    1,              /* 每个消息的大小是 1 字节 */
                    sizeof(msg_pool),   /* 内存池的大小是 msg_pool 的大小 */
                    RT_IPC_FLAG_PRIO);  /* 如果有多个线程等待,优先级大小的方法分配消息 */
/*xxx_thread中 发送消息到消息队列中 */
result = rt_mq_send(&xxx_mq, &buf, 1);/*发送一个字节....*/
/*yyy_thread */
ret = rt_mq_recv(&xxx_mq, &buf, sizeof(buf), RT_WAITING_FOREVER)
  • 消息队列的用法

1.发送消息(不带有ACK)

struct msg
{
    rt_uint8_t *data_ptr;    /* 数据块首地址 */
    rt_uint32_t data_size;   /* 数据块大小   */
};
void send_op(void *data, rt_size_t length)
{
    struct msg msg_ptr;

    msg_ptr.data_ptr = data;    /* 指向相应的数据块地址 */
    msg_ptr.data_size = length; /* 数据块的长度 */

    /* 发送这个消息指针给 mq 消息队列 */
    rt_mq_send(mq, (void*)&msg_ptr, sizeof(struct msg));
}

void message_handler()
{
    struct msg msg_ptr; /* 用于放置消息的局部变量 */

    /* 从消息队列中接收消息到 msg_ptr 中 */
    if (rt_mq_recv(mq, (void*)&msg_ptr, sizeof(struct msg), RT_WAITING_FOREVER) == RT_EOK)
    {
        /* 成功接收到消息,进行相应的数据处理 */
    }
}

2.发送同步消息(带有ACK)

struct msg
{
    /* 消息结构其他成员 */
    struct rt_mailbox ack;
    /* 消息的数据成员 */
    rt_uint8_t *data_ptr;    /* 数据块首地址 */
    rt_uint32_t data_size;   /* 数据块大小   */
};
/* 或者 */
struct msg
{
    /* 消息结构其他成员 */
    struct rt_semaphore ack;
    /* 消息的数据成员 */
    rt_uint8_t *data_ptr;    /* 数据块首地址 */
    rt_uint32_t data_size;   /* 数据块大小   */
};
#define XXX_MSG_BUFSZ   512
struct rt_mailbox xxx_mb;
rt_uint8_t xxx_data_pool[XXX_MSG_BUFSZ]

struct msg = {
        .ack = xxx_mb
        .data = xxx_data_pool;
        .data_size = XXX_MSG_BUFSZ;
        } 

/* xxx_thread发送消息 */
send_op(&msg,sizeof(msg))
/* 使用mailbox作为ack */
ret = rt_mq_recv(xxx_mb,&ack_state, 1, timeout);
/* 使用信号量作为ack */
ret = rt_sem_take(xxx_sem, timeout);/*val--*/
/* 超时 */
if(ret = -RT_ETIMEOUT)
{
    /* 超时处理 */
}
else
{
    /*ack正常处理*/
}

/* yyy_thread接收消息 */
void message_handler()
{
    struct msg msg_ptr; /* 用于放置消息的局部变量 */
    int state = 0;
    /* 从消息队列中接收消息到 msg_ptr 中 */
    if (rt_mq_recv(mq, (void*)&msg_ptr, sizeof(struct msg), RT_WAITING_FOREVER) == RT_EOK)
    {
        /* 成功接收到消息,进行相应的数据处理 */
        /* 使用msg_ptr->data_ptr & msg_ptr->data_size的数据 */
    }
    rt_mq_send(msg_ptr->ack, state, 1);
    /* 信号量同样 */
    rt_sem_release(msg_ptr->ack);/*val++*/
}

线程间同步接口

1.临界区

rt_enter_critical()
rt_exit_critical()

2.信号量

typedef struct rt_semaphore* rt_sem_t;
/*************** 动态创建 ********************/
rt_err_t result = RT_EOK;
rt_sem_t xxx_sem= RT_NULL;
/* 创建信号量--基于优先级 */
xxx_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO)

/* 在yyy线程(优先级N)释放信号量 */
rt_sem_release(xxx_sem);/* val++ */
/* 在xxx线程(优先级N-1)获取信号量,此时xxx线程与yyy线程同步 */
result = rt_sem_take(xxx_sem, RT_WAITING_FOREVER);/* val-- */
if (result != RT_EOK)
{
    rt_sem_delete(xxx_sem)/* 删除信号 */
}
效果:
/* 在yyy线程(优先级N)释放信号量 */
/* code 1 */
rt_sem_release(xxx_sem);/* val++ */
/* code 2 */
/* 在xxx线程(优先级N-1)获取信号量,此时xxx线程与yyy线程同步 */
result = rt_sem_take(xxx_sem, RT_WAITING_FOREVER);/* val-- */
if (result != RT_EOK)
{
    rt_sem_delete(xxx_sem)/* 删除信号 */
}
/* code 3 */
/***********************************
* 执行顺序:code 1 -> code 3 -> code 2
************************************/
/*************** 静态创建 ********************/
struct rt_semaphore xxx_sem;
rt_sem_init(&xxx_sem, "xxx", 1, RT_IPC_FLAG_PRIO);/* 允许访问一次 */

/* 原子操作 */
rt_sem_take(xxx_sem, RT_WAITING_FOREVER);
/* 资源M */
rt_sem_release(xxx_sem);
/* xxx线程,yyy线程的优先级不同,不能相差>=2,否则会有优先级反转的问题 */
  • 注意:
    信号量的问题:优先级反转

优先级A>B>C,A和C有共享资源(M)时,当C占有资源,则A会被挂起,此时B因为不需要访问共享资源(M),所以抢占了C的执行,从而A的实时性得不到保障.


3.互斥锁

typedef struct rt_mutex* rt_mutex_t;

/*************** 动态创建 ********************/
static rt_mutex_t xxx_mutex = RT_NULL;
/* 基于优先级创建一个mutex */
xxx_mutex = rt_mutex_create("xxx_mutex", RT_IPC_FLAG_PRIO);

/* xxx_thread中,对共享资源M上锁 */
rt_mutex_take(xxx_mutex, RT_WAITING_FOREVER);
/* 资源M */
rt_mutex_release(xxx_mutex);
/* xxx线程,yyy线程优先级不同,可以相差>=2 */
/*************** 静态创建 ********************/
struct rt_mutex xxx_mutex;
rt_mutex_init(xxx_mutex, "xxx_mutex",RT_IPC_FLAG_PRIO)

3.事件集(event set)

typedef struct rt_event* rt_event_t;

/*************** 动态创建 ********************/
static rt_event_t xxx_event = RT_NULL;
/* 基于优先级创建一个mutex */
xxx_event = rt_event_create("xxx_event", RT_IPC_FLAG_PRIO);

/* xxx_thread中 发生事件 EVENT_FLAG1 */
rt_event_send(xxx_event, EVENT_FLAG1);
/* yyy_thread中,接收到EVENT_FLAG1, 会触发该事件往下执行*/
rt_event_recv(xxx_event, EVENT_FLAG1|EVENT_FLAG2|EVENT_FLAG3, RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &e);   /* 事件之间逻辑或 */
rt_kprintf("thread1: OR recv event 0x%x\n", e);    /* 显示当前的事件 */

rt_event_recv(xxx_event, EVENT_FLAG4|EVENT_FLAG5, RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &e);  /* 事件之间逻辑与 */
rt_kprintf("thread1: AND recv event 0x%x\n", e);    /* 显示当前的事件 */

/*************** 静态创建 ********************/
struct rt_event xxx_event;
rt_event_init(&xxx_event, "xxx_event",RT_IPC_FLAG_PRIO);
/* 操作相同 */
  • 事件集的用法

用来同步多个线程.
可以用事件集来触发那些状态机线程
比如按键线程发送EVENT_UP_FLAG
相关的线程 —— LCD线程,电机线程,以及其它执行线程,收到该事件后:
LCD线程:显示值增加
电机线程:电机的PWM增加
其它执行线程:LED关闭,执行器动作,蜂鸣器鸣叫一下等.

常用数据类型

typedef signed long                     rt_base_t;      /**< Nbit CPU related date type */
typedef rt_base_t                       rt_err_t;       /**< Type for error number */
typedef rt_uint32_t                     rt_time_t;      /**< Type for time stamp */
typedef rt_uint32_t                     rt_tick_t;      /**< Type for tick count */
typedef rt_base_t                       rt_flag_t;      /**< Type for flags */
typedef rt_ubase_t                      rt_dev_t;       /**< Type for device */
typedef rt_base_t                       rt_off_t;       /**< Type for offset */

常用返回值

#define RT_EOK                          0               /**< There is no error */
#define RT_ERROR                        1               /**< A generic error happens */
#define RT_ETIMEOUT                     2               /**< Timed out */
#define RT_EFULL                        3               /**< The resource is full */
#define RT_EEMPTY                       4               /**< The resource is empty */
#define RT_ENOMEM                       5               /**< No memory */
#define RT_ENOSYS                       6               /**< No system */
#define RT_EBUSY                        7               /**< Busy */
#define RT_EIO                          8               /**< IO error */
#define RT_EINTR                        9               /**< Interrupted system call */
#define RT_EINVAL                       10              /**< Invalid argument */
#define RT_ETRAP                        11              /**< Trap event */
#define RT_ENOENT                       12              /**< No entry */
#define RT_ENOSPC                       13              /**< No space left */
posted @ 2023-05-02 01:22  当最后一片树叶落下  阅读(42)  评论(0编辑  收藏  举报