1.RTOS篇
1.1RT-Thread简介
RT-Thread 是一个集实时操作系统(RTOS)内核、中间件组件和开发者社区于一体的技术平台,由熊谱翔先生带领并集合开源社区力量开发而成,RT-Thread 也是一个组件完整丰富、高度可伸缩、简易开发、超低功耗、高安全性的物联网操作系统。RT-Thread 具备一个 IoT OS 平台所需的所有关键组件,例如GUI、网络协议栈、安全传输、低功耗组件等等。经过11年的累积发展,RT-Thread 已经拥有一个国内最大的嵌入式开源社区,同时被广泛应用于能源、车载、医疗、消费电子等多个行业,累积装机量超过 8亿 台,成为国人自主开发、国内最成熟稳定和装机量最大的开源 RTOS。
1.2时钟管理
1.2.1时钟节拍
任何系统都有一个时钟节拍,负责处理和时间相关的事件,在RT-Thread中时钟节拍可以根据RT_TICK_PER_SECOND宏来定义,下面设定为频率时1000HZ节拍是1ms 和裸机的STM32一样,有一个系统滴答定时器,每1ms触发一次中断 在滴答中断函数里面会有一个全局变量,中断一次就加一
1.3获取系统节拍
我们可以通过下面一个函数获取滴答定时器里面的全局变量。
rt_tick_t rt_tick_get(void)
1.4定时器分类
定时器可以分为硬件定时器和软件定时器
- 硬件定时器:从代码的角度上看,硬件定时器更加精确,可以到达纳妙级别,因为他是通过外部晶振提供给芯片提供时钟。芯片提供一组寄存器,到达设定时钟后产生中断
- 软件定时器:建立在硬件定时器的基础上,可以提供多个定时器
1.5定时器源码分析
- 在RT-Thread启动的时候,会调用rtthread_startup函数,里面初始化了许多事件,如定时器初始化函数rt_system_timer_init();
- 可以看到定时器初始化就是遍历rt_timer_list,对每一个rt_timer_list的内容进行 rt_list_init(rt_timer_list + i)操作
- 那么rt_timer_list是什么呢,其实就是一个队列链表
struct rt_list_node
{
struct rt_list_node *next;
struct rt_list_node *prev;
};
typedef struct rt_list_node rt_list_t;
- rt_list_init里面又执行什么操作呢
t_inline void rt_list_init(rt_list_t *l)
{
l->next = l->prev = l;
}
- rt_system_timer_thread_init软件定时器初始化,其实就是把软件定时器放入rt_list_init中,然后给每一个软件定时器定时器开启线程。
void rt_system_timer_thread_init(void)
{
#ifdef RT_USING_TIMER_SOFT
int i;
for (i = 0;
i < sizeof(rt_soft_timer_list) / sizeof(rt_soft_timer_list[0]);
i++)
{
rt_list_init(rt_soft_timer_list + i);
}
rt_thread_init(&timer_thread,
"timer",
rt_thread_timer_entry,
RT_NULL,
&timer_thread_stack[0],
sizeof(timer_thread_stack),
RT_TIMER_THREAD_PRIO,
10);
rt_thread_startup(&timer_thread);
#endif
}
1.6定时器相关函数
1.61动态创建一个定时器和删除定时器
添加定时器
* #define RT_TIMER_FLAG_PERIODIC 0x2
* #define RT_TIMER_FLAG_HARD_TIMER 0x0
* #define RT_TIMER_FLAG_SOFT_TIMER 0x4
* @return the created timer object
**/
rt_timer_t rt_timer_create(const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
定时器删除
rt_err_t rt_timer_delete(rt_timer_t timer)
1.7初始化和脱离定时器
静态初始化
void rt_timer_init(rt_timer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
定时器脱离
rt_err_t rt_timer_detach(rt_timer_t timer)
1.8启动和停止定时器
启动定时
**
* This function will start the timer
*
* @param timer the timer to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_timer_start(rt_timer_t timer)
停止定时器
rt_err_t rt_timer_stop(rt_timer_t timer)
``
## 定时器控制
当定时器启动后我们想更改定时器的某些状态,可以调用下面的函数
```c
* #define RT_TIMER_CTRL_GET_TIME 0x1
* #define RT_TIMER_CTRL_SET_ONESHOT 0x2
* #define RT_TIMER_CTRL_SET_PERIODIC 0x3
* @return RT_EOK
*/
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
1.9高精度延时
注意:这个函数只支持低于1个OS Tick的延时, 否则SysTick会出现溢出而不能够获得指定的延时时间 /**
- This function will delay for some us.
- @param us the delay time of us
*/ void rt_hw_us_delay(rt_uint32_t us)
1.10实战篇:RTOS定时器代码演示
效果:定时打印结果,当打印十次后调用rt_timer_control(&tm2, RT_TIMER_CTRL_SET_ONESHOT,NULL);变为一次性的定时器。
#include <timer/mytimer.h>
rt_timer_t tm1 ;
struct rt_timer tm2;
int flags = 0;
void tm1_callback(void *parameter)
{
rt_kprintf("tm1_callback running...\n");
}
void tm2_callback(void *parameter)
{
flags++;
if(flags == 10){
rt_timer_control(&tm2, RT_TIMER_CTRL_SET_ONESHOT,NULL);
flags = 0;
}
rt_tick_t timeout = 1000;
rt_timer_control(&tm2, RT_TIMER_CTRL_SET_TIME , (void *)&timeout);
rt_kprintf("[%u]tm2_callback running...\n",rt_tick_get());
}
int main()
{
tm1 = rt_timer_create("tm1_demo",tm1_callback, NULL, 3000, \
RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
if(tm1 == RT_NULL){
LOG_E("rt_timer_create faile...\n");
return -ENOMEM;
}
LOG_D("rt_timer_create successed...\n");
rt_timer_start(tm1);
rt_timer_init(&tm2, "tm2_demo", tm2_callback, NULL, 3000, \
RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
rt_timer_start(&tm2);
return 1;
}
2Linux篇
2.1Linux简介
Linux 内核最初只是由芬兰人林纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的。 Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 UNIX 的多用户、多任务、支持多线程和多 CPU 的操作系统。 Linux 能运行主要的 UNIX 工具软件、应用程序和网络协议。它支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。
2.2Linux定时器机制
Linux分有内核定时器和应用层定时器,在这里我们只讨论应用层的定时器
说起Linux的定时器,又不得不说起信号,应用程序会给系统订一个时间,如果这个时间到达之后,就会发送SIGALRM信号,这个默认的动作是终止调用某个进程
2.3alarm类定时器
这个是最简单的定时器,我们只要使用它的时候调用它就行了,不过他会和sleep函数冲突,因为sleep()函数会中断信号,使得定时器失效,定时之后我们可以去写他的信号函数,在里面执行回调函数,在里面做自己想做的事
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:
在 seconds 秒后,向调用进程发送一个 SIGALRM 信号,SIGALRM 信号的默认动作是终止调用 alarm 函数的进程。
返回值:
若以前没有设置过定时器, 或设置的定时器已超时, 返回 0; 否则返回定时器剩余的秒数, 并重新设定定时器。
2.4进程接收到信号后的处理方式
现在我们知道alarm函数是用来接收SIGALRM信号的,当我们这个信号接收到之后会执行它默认的处理方式,现在我们需要重写它默认的处理方式。程序中可用函数 signal()改变信号的处理方式。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
功能:
注册信号处理函数( 不可用于 SIGKILL、 SIGSTOP 信号), 即确定收到信号后处理函数的入口地址。
参数:
signum: 信号编号
handler 的取值:
忽略该信号: SIG_IGN
执行系统默认动作: SIG_DFL
自定义信号处理函数: 信号处理函数名
返回值:
成功: 返回函数地址, 该地址为此信号上一次注册的信号处理函数的地址。
失败: 返回 SIG_ERR
2.5实战篇1:alarm定时器代码演示
效果:五秒后打印魔动山霸你好呀
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handeler(int signo)
{
int a=31;
printf("%c\n",a);
printf("%d魔动山霸你好呀\n",signo);
}
int main()
{
int serconds = 0;
serconds = alarm(5);
printf("%d\n",serconds);
printf("seconds %d\n",serconds);
signal(SIGALRM,signal_handeler);
while(1);
}
2.6setitimer定时器的设置
函数alarm设置的定时器只能精确到秒,而以下函数理论上可以精确到微妙
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
函数setitimer可以提供三种定时器,它们相互独立,任意一个定时完成都将发送定时信号到进程,并且自动重新计时。参数which确定了定时器的类型,如表10-6所示:表10-6 参数which与定时器类型取值 含义 信号发送 ITIMER_REAL 定时真实时间,与alarm类型相同。 SIGALRM ITIMER_VIRT 定时进程在用户态下的实际执行时间。 SIGVTALRM ITIMER_PROF 定时进程在用户态和核心态下的实际执行时间。 SIGPROF 这三种定时器定时完成时给进程发送的信号各不相同,其中
- ITIMER_REAL类定时器发送SIGALRM信号
- ITIMER_VIRT类定时器发送SIGVTALRM信号
- ITIMER_REAL类定时器发送SIGPROF信号。
函数alarm本质上设置的是低精确、非重载的ITIMER_REAL类定时器,它只能精确到秒,并且每次设置只能产生一次定时。函数setitimer设置的定时器则不同,它们不但可以计时到微妙(理论上),还能自动循环定时。
在一个Unix进程中,不能同时使用alarm和ITIMER_REAL类定时器。结构itimerval描述了定时器的组成: struct itimerval {
struct tim. it_interval;
struct tim. it_value;
}
结构tim.描述了一个精确到微妙的时间:
struct tim. {
long tv_sec;
long tv_usec;
}
2.7实战篇2:setitimer的代码演示
本处设计了一个精确定时器的例子,进程每隔1.5秒数发送定时信号SIGPROF,在接收到信号时将打印定时的次数,用户可以键入CTRL_C或DELETE结束程序
#include <sys/select.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int n = 0;
void timefunc(int sig)
{
fprintf(stderr, "ITIMER_PROF[%d]\n", n++);
}
void main()
{
struct itimerval value;
value.it_value.tv_sec=1;
value.it_value.tv_usec=500000;
value.it_interval.tv_sec=1;
value.it_interval.tv_usec=500000;
signal(SIGALRM, timefunc);
setitimer(ITIMER_REAL, &value, NULL);
while (1);
}
2.8信号的简要说明
信号简要说明:
SIGHUP 终止进程 终端线路挂断
SIGINT 终止进程 中断进程
SIGQUIT 建立CORE文件终止进程,并且生成core文件
SIGILL 建立CORE文件 非法指令
SIGTRAP 建立CORE文件 跟踪自陷
SIGBUS 建立CORE文件 总线错误
SIGSEGV 建立CORE文件 段非法错误
SIGFPE 建立CORE文件 浮点异常
SIGIOT 建立CORE文件 执行I/O自陷
SIGKILL 终止进程 杀死进程
SIGPIPE 终止进程 向一个没有读进程的管道写数据
SIGALARM 终止进程 计时器到时
SIGTERM 终止进程 软件终止信号
SIGSTOP 停止进程 非终端来的停止信号
SIGTSTP 停止进程 终端来的停止信号
SIGCONT 忽略信号 继续执行一个停止的进程
SIGURG 忽略信号 I/O紧急信号
SIGIO 忽略信号 描述符上可以进行I/O
SIGCHLD 忽略信号 当子进程停止或退出时通知父进程
SIGTTOU 停止进程 后台进程写终端
SIGTTIN 停止进程 后台进程读终端
SIGXGPU 终止进程 CPU时限超时
SIGXFSZ 终止进程 文件长度过长
SIGWINCH 忽略信号 窗口大小发生变化
SIGPROF 终止进程 统计分布图用计时器到时
SIGUSR1 终止进程 用户定义信号1
SIGUSR2 终止进程 用户定义信号2
SIGVTALRM 终止进程 虚拟计时器到时
|