IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 【操作系统】30天自制操作系统--(11)定时器1 -> 正文阅读

[嵌入式]【操作系统】30天自制操作系统--(11)定时器1

????????本章介绍定时器中断的使用。

一 定时器中断的使用

? ? ? ? 由【操作系统】30天自制操作系统--(5)分割编译与中断处理中的表格可以看到,对于定时器中断的使用就用到了IRQ0这个中断请求。

中断请求硬件设备
IRQ0系统计时器
IRQ1键盘
IRQ2可设置中断控制卡
IRQ3COM2(串行接口2)
IRQ4COM1(串行接口1)
IRQ5
IRQ6磁盘机
IRQ7并行接口
IRQ8CMOS/时钟
IRQ9
IRQ10
IRQ11
IRQ12PS/2鼠标
IRQ13算术处理器(Arithmetic Processor)
IRQ14Primary(主)IDE控制器
IRQ15Secondary(从)IDE控制器

? ? ? ? 其中断周期的变更步骤如下:

? ? ? ? 这边用的是8254定时器芯片 ,其主频为1.19318MHz(参考8254芯片详解

????????就是说,如果想要IRQ0中断时间为10ms,就需要往寄存器中写值11932(0x2e9c):

#include "bootpack.h"

#define PIT_CTRL    0x0043
#define PIT_CNT0    0x0040

void init_pit(void) {
    io_out8(PIT_CTRL, 0x34);
	io_out8(PIT_CNT0, 0x9c);
	io_out8(PIT_CNT0, 0x2e);
	return;
}

? ? ? ? 在主函数中调用(下面关于中断屏蔽寄存器IMR的使用可以参考【操作系统】30天自制操作系统--(5)分割编译与中断处理):

void HariMain(void)
{
    // ...
    init_pit();
    // ...
    io_out8(PIC0_IMR, 0xf8);  /* PIT和PIC1和键盘设置为许可(11111000) */
    io_out8(PIC1_IMR, 0xef);  /* 鼠标设置为许可(11101111) */
    // ...
}

? ? ? ? 当然,编写中断服务程序---->汇编实现中断服务程序的返回---->将编写好的中断处理程序(包含中断服务程序和汇编处理两部分)注册到IDT中断记录表中?这几个步骤也与鼠标和键盘中断的处理如出一辙,这边不再赘述,只简单将这几处程序贴在一起:

void inthandler20(int *esp) {
    io_out8(PIC0_OCW2, 0x60);  /* 把IRQ-00信号接收完了的信息通知给PIC */
    /* 暂时什么都不做 */
    return;
}
_asm_inthandler20:
		PUSH	ES
		PUSH	DS
		PUSHAD
		MOV		EAX,ESP
		PUSH	EAX
		MOV		AX,SS
		MOV		DS,AX
		MOV		ES,AX
		CALL	_inthandler20
		POP		EAX
		POPAD
		POP		DS
		POP		ES
		IRETD
void init_gdtidt(void)
{
    /* 中略 */

    /* 中断注册表IDT注册 */
    set_gatedesc(idt + 0x20, (int)asm_inthandler20, 2*8, AR_INTGATE32); //IRQ0定时中断
	set_gatedesc(idt + 0x21, (int)asm_inthandler21, 2*8, AR_INTGATE32); //IRQ1键盘中断
	set_gatedesc(idt + 0x27, (int)asm_inthandler27, 2*8, AR_INTGATE32); //IRQ7并行接口
	set_gatedesc(idt + 0x2c, (int)asm_inthandler2c, 2*8, AR_INTGATE32); //IR12鼠标中断
}

? ? ? ? 至此,完成了定时器中断处理程序的编写和注册。接下来就是往服务程序里面填写逻辑了。

二 添加定时器功能(计数器、超时返回、设置多个定时器)

? ? ? ? 这边列出了三种功能的使用。

【1】计数器:很简单,定时器中断服务程序中整个变量自加,完了在窗口中显示出来。

【2】超时返回:

? ? ? ? (1)首先向结构体struct TIMERCTL中添加代码记录超时的信息:

struct TIMERCTL {
	unsigned int count;
	unsigned int timeout;  /* 距离暂停还有多少时间 */
	struct FIFO8 *fifo;    /* 接收到时间后程序发送数据的缓冲区 */
	unsigned char data;
};

? ? ? ? (2)随后在中断服务程序里面实现的暂停功能,在settimer中 ,为了防止IRQ0中断乱入,采取的是禁止中断 + 处理 + 开放 的策略(这一点之前鼠标键盘也是这么处理的)。

struct TIMERCTL timerctl;

void init_pit(void) {
    io_out8(PIT_CTRL, 0x34);
	io_out8(PIT_CNT0, 0x9c);
	io_out8(PIT_CNT0, 0x2e);
    timerctl.count = 0;
    timerctl.timeout = 0;
	return;
}

void inthandler20(int *esp) {
    io_out8(PIC0_OCW2, 0x60);  /* 把IRQ-00信号接收完了的信息通知给PIC */
    timerctl.count++;
    if (timerctl.timeout > 0) {  /* 如果设定了超时 */
        timerctl.timeout--;
        if (timerctl.timeout == 0) {
            fifo8_put(timerctl.fifo, timerctl.data);
        }
    }
    return;
}

void settimmer(unsigned int timeout, struct FIFO8 *fifo, unsigned char data){
    int eflags;
    eflags = io_load_eflags();
    timerctl.timeout = timeout;
    timerctl.fifo = fifo;
    timerctl.data = data;
    io_store_eflags(eflags);
    return;
}

? ? ? ? (3)最后,在主函数中读取定时器缓冲数组中的数据(如果有的话),并在屏幕上返回一个定时器超时的信息“10[sec]”:

void HariMain(void)
{
    //(中略)
    struct FIFO8 timerfifo;
    char s[40], keybuf[32], mousebuf[128], timerbuf[8];
    //(中略)
    fifo8_init(&timerfifo, 8, timerbuf);
    settimer(1000, &timerfifo, 1);
    (中略)
    for (;;) {
        //(中略)
        io_cli();
        if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) + fifo8_status(&timerfifo) == 0) {
      	  io_sti();
        } else {
        	if (fifo8_status(&keyfifo) != 0) {
	        //(中略)
    	    } else if (fifo8_status(&mousefifo) != 0) {
        	//(中略)
	        } else if (fifo8_status(&timerfifo) != 0) {
    		    i = fifo8_get(&timerfifo); /* 首先读入(为了设定起始点) */
		        io_sti();
		        putfonts8_asc(buf_back, binfo-
		        >scrnx, 0, 64, COL8_FFFFFF, "10[sec]");
		        sheet_refresh(sht_back, 0, 64, 56, 80);
	        }
        }
    }
}

【3】设置多个定时器:这边类似于之前【操作系统】30天自制操作系统--(8)内存管理中“内存管理表”的方法,添加一个flags标志位,用来标识定时器的状态(包括未使用为0,已配置为1,运行中为2)

????????(1)首先修改结构体struct TIMERCTL(这边最大可以配置500个定时器):

#define MAX_TIMER		500
struct TIMER {
	unsigned int timeout, flags;
	struct FIFO8 *fifo;
	unsigned char data;
};
struct TIMERCTL {
	unsigned int count;
	struct TIMER timer[MAX_TIMER];
};

? ? ? ? (2)添加一些定时器的配置和释放等功能函数(即对flags标志位进行操作):

#include "bootpack.h"

#define PIT_CTRL    0x0043
#define PIT_CNT0    0x0040

struct TIMERCTL timerctl;

#define TIMER_FLAGS_ALLOC		1	/* 已配置状态 */
#define TIMER_FLAGS_USING		2	/* 定时器运行中 */

void init_pit(void) {
    io_out8(PIT_CTRL, 0x34);
	io_out8(PIT_CNT0, 0x9c);
	io_out8(PIT_CNT0, 0x2e);
    timerctl.count = 0;
    int i;
    for (i = 0; i < MAX_TIMER; i++) {
        timerctl.timer[i].flags = 0;  /* 未使用 */
    }
	return;
}

/**
 * @brief 返回未在使用的 timer
 */
struct TIMER *timer_alloc(void){
    int i;
    for (i = 0; i < MAX_TIMER; i++) {
        if (timerctl.timer[i].flags == 0) {
            timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
            return &timerctl.timer[i];
        }
    }
    return 0;
}


void timer_free(struct TIMER *timer){
    timer->flags = 0;
    return;
}

void timer_init(struct TIMER *timer, struct FIFO8 *fifo, unsigned char data){
    timer->fifo = fifo;
    timer->data = data;
    return;
}

void timer_settime(struct TIMER *timer, unsigned int timeout){
    timer->timeout = timeout;
    timer->flags = TIMER_FLAGS_USING;
    return;
}

void inthandler20(int *esp){
    io_out8(PIC0_OCW2, 0x60);  /* 把IRQ-00信号接收结束的信息通知给PIC */
    timerctl.count++;
    int i;
    for (i = 0; i < MAX_TIMER; i++) {
        if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
			timerctl.timer[i].timeout--;
			if (timerctl.timer[i].timeout == 0) {
				timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
				fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
			}
		}
    }
    return;
}

? ? ? ? (3)在主函数中调用(这边作者配置了三个定时器,一个是10s超时显示字符串;一个是3s超时显示字符串;一个是每隔0.5s通过切换定时器的使用状态来控制屏幕上小矩形光标的亮灭,以实现光标闪烁的效果)。这边主函数的代码比较简单但冗长,就不贴出来了。

三 加快定时器中断处理

? ? ? ? 这边列出了三种方法。

【1】原本是timeout参数自减,根据其是否到0来判断超时,现在修改为count参数直接跟timeout参数相比较,减少一行timeout参数的运算。算是一个小优化吧,具体程序略。

【2】在结构体中新增next参数用来记住下一个时间,先和 timeout 最小的作比较。这样就不需要每次都要比较判断所有的定时器。具体程序略。

【3】参考【操作系统】30天自制操作系统--(9)叠加处理中对于图层的处理,专门开辟一块内存用来存放某种顺序排列好的定时器地址struct TIMER *timer[MAX_TIMER],以减少到达next时刻的处理时间(具体程序略。个人感觉方法三,与增加的代码复杂度相比,带来的性能提升感觉不大):

struct TIMERCTL {
	unsigned int count, next, using;
	struct TIMER *timers[MAX_TIMER];
	struct TIMER timers0[MAX_TIMER];
};

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 18:52:42  更:2022-04-22 18:54:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:24:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码