视频摘自
https://www.rt-thread.org/page/video.html
https://www.bilibili.com/video/av79513262?p=1
第二讲 MDK裸机系统动态内存配置和使用
board.c -> rt_system_heap_init((void)*HEAP_BEGIN,(void*)HEAP_END)
动态内存空间 64K RAM ->动态内存空间
Image$$RW_IRAM1$$ZI$$limit
内存起始地址
Map文件 : RW段+ZI段 RW+ZI
p=(char*)rt_malloc(10);
rt_free(p)
动态内存管理实例:分配动态内存空间
dynmem_sample 1<<i
p = rt_malloc(10);
rt = memset(p,0,10);
内存复位:清零操作
内存泄露:Memory Leak
rt_malloc 和 rt_free缓解到系统中去,rt_malloc和rt_free配合使用。
void *rt_realloc(void *rmem,rt_size_t newsize); //数据保持不变
rt_calloc(rt_size_t count ,rt_size_t size); //count 块 count*size 空间
第三讲:线程的创建
RT-Thread线程三部曲:(1)线程代码(入口函数)(2)线程控制块(3)线程堆栈
线程代码(1)无线循环(2)顺序执行结构
线程控制块:数据结构 优先级 链表结构
创建线程必须有线程控制块:
创建线程:
rt_err_t rt_thread_init(struct rt_thread *thread
const char *name,
void (*entry)(void *parameter), //函数指针
void *parameter, //传入相关参数
void *stack_start, //栈起始地址
rt_uint32_t stack_size,//大小
rt_uint8_t priority,//优先级
rt_uint32_t tick)
实例化如下:
rt_thread_init(&thread2,
"thread2",
thread2_entry,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack),
THREAD_PRIORITY-1,
THREAD_TIMER_SLICE
);
动态线程
rt_thread_t rt_thread_create(const char *name, //线程名称
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size;
rt_uint8_t priority,
rt_uint32_t tick)
启动线程 rt_thread_startup()
tid1 = rt_thread_create("thread1",thread1_entry,RT_NULL,SIZE,PRIORITY,SLICE);
rt_thread_create不成功->0
thread2 1-9
静态需要栈和大小
静态线程VS动态线程
先定义 运行效率
线程状态切换
系统心跳时钟:Systick来完成的
RT_TICK_PER_SECOND SysTick_Handler.
drv_gpio.c 栈空间分配技巧。
第五讲 线程的时间片轮询调度
线程时间片轮询调度,优先级,时间片
STM32默认设置最大支持32个优先级,0为最高优先级。用户可以通过rt_config.h中的RT_THREAD_PRIORITY_MAX 线程时间片。
时间优先级抢占调度,时间片轮询调度 timeslice_sample.c
第六讲 空闲线程及两个常用的钩子函数
系统线程
空闲线程->永不循环->永不挂起
用户线程
Kernel->idle.c 空闲线程入口代码
空闲线程钩子函数
设置钩子函数
rt_err_t rt_thread_idle_sethook(void(*hook)(void));
删除钩子函数
rt_err_t rt_thread_idle_delhook(void(*hook)(void))
(1)设置空闲线程钩子(可以设置4个)
rt_thread_idle_sethook(idle_hook);
临界区保护 使用注意
rt_thread_delay()
rt_sem_take()
空闲线程可以设置多个函数,钩子函数指针,多个空间线程。
(2)系统调度函数(只能设置一个)
rt_schedule_sethook(void(*hook)(struct rt_thread *form,struct rt_thread*to))
设置调度器钩子函数 rt_scheduler_sethook(hook_of_scheduler);
from->name,to->name;
static void hook_of_scheduler(struct rt_thread*from,struct rt_thread*to)
第七讲:临界区保护
共享资源 变量 缓冲区
访问(操作)临界资源的代码称为临界区,我们每次只允许一个线程进入临界。
临界资源(Crttical Section)
关系系统调度保护临界区(1)禁止调度(2)关闭中断
互斥性保护临界区,信号量、互斥量
1.禁止调度 把调度器锁住
调度器上锁:
rt_enter_critical()
调度器解锁:
rt_exit_critical();
2.关闭中断
关闭中断
level = rt_hw_interrupt_disable();
/使能中断/
rt_hw_interrupt_enable(level);
第八讲:信号量的使用
线程和ISR
线程间通信(Internal Process Communication IPC)
IPC机制 信号量 互斥量 事件 邮箱 消息队列
需求:(1)同步(2)互斥(3)数据交互
信号量控制块
struct rt_semaphore
{
struct_ipc_object parent;
rt_uint16_t value;
}
定义静态信号量:struct rt_semaphore static_sem
定义动态信号量:rt_sem_t,dynamic_sem
信号量控制块 信号量
信号量API
rt_sem_t_sem函数 rt_err_t信号量为零不可用
释放信号量,信号量示例
动态信号量 RT_IPC_FLAG_FIFO
创建两个线程,信号量创建,线程中,中断中调用尝试take
第九讲 生产者 消费者问题
信号量用于解决生产者、消费者问题模型
示例代码:信号量 product&cosumer
1.初始化三个信号量 &sem_lock(互斥关系)二值信号量 sem_empty sem_full
2.创建生产者线程 互斥 同步
生产者互斥操作、缓冲区,流程:互斥
(1)获取一个空位empty
(2)上锁lock
(3)修改
(4)释放 release(&sem_lock)
(5)发布一个满位(sem_full)
创建生产者和消费者线程 同步中包含一个互斥
消费者线程(1)获取一个满位(2)上锁、操作、解锁(3)释放一个空位
第十讲-互斥量的使用
互斥量 互斥访问的IPC对象,二值信号量
互斥量LOCKER和UNLOCKER
互斥量控制块(数据结构)
struct rt_mutex
{
struct rt_ipc_object parent;
rt_uint16_t value;
rt_uint8_t original_priority;
rt_uint8_t hold;
struct rt_thread *owner;
}
定义静态互斥量
struct_mutex static_mutex
定义动态变量
rt_mutex dynamic_mutex
互斥量的操作
初始化与脱离
创建与删除
init(rt_mutex mutex,const char *name,rt_uint8_t flag)
创建与删除
共享资源 线程1 线程2
rt_mutex_take(dynamic_mutex,RT_WATITING_FOREVER)
{
number1++;
rt_thread_mdelay(10);
number2++;
rt_mutex_release(dynamic_mutex);
}
1.信号量强调运行步骤,互斥量强调 许可和权限 信号量存在优先级反转的问题。
线程的优先级翻转
RT-THREAD 互斥量优先级继承算法
继承优先级继承,释放共享优先级
一个互斥量,三个线程 创建互斥锁
mutex = rt_mutex_create("mutex",RT_IPC_FLAG_FIFIO);
优先级翻转现象提醒:共享资源进行互斥代码应该尽可能短,低优先级快执行,释放共享资源。
P12 事件集的使用
事件集(1)特定事件唤醒事件(2)任意单个事件唤醒事件(3)多个事件同时发生才能唤醒事件
信号量:一对一线程同步 、
一对多,多对多,多对一 事件集合
逻辑或:独立型同步:线程与任何事件之一发生同步,只要有一个条件发生,即满足条件
事件集合控制块
struct rt_event
{
struct rt_ipc_object parent;
rt uint32_t set;
}
typedef struct rt_event *rt_event_t;
定义静态事件集合:
struct rt_event static_evt
定义动态事件集合:
rt_event_t dynamic_evt
初始化与脱离
rt_event_init(rt_event_t event,const char*name,rt_uint8_t flag)
事件集合使用示例
初始化事件对象
result = rt_event_init(&event,="event",RT_IPC_FLAG_FIFO)
创建两个线程(1)发送事件(2)接收事件
rt_event_recv(&event,
EVENT_FLAG3|EVENT_FLAG5
RT_EVENT_OR|RT_EVENT_FLAG_CLEAR
RT_WAITING_FOREVER
&e);
挂起,在接收事件,延时及挂起,最后这个流程比较烂。