第十四课(7)定时器中断程序示例

一、引入定时器的概念

在这里插入图片描述

定时器内部控制逻辑:

①每来一个时钟 TCNTn 减1
②当TCNTn == TCMPn,可以产生中断 ,也可以使对应的PWM引脚翻转(比如原来是高电平,现在是低电平)
③TCNTn继续减1
当TCNTn == 0时,可以产生中断,PWM引脚再次翻转
④TCNTn == 0时,可自动加载初值

CNTn 、TCMPn的值来自 TCMPBn TCNTBn

怎么使用定时器?

①设置时钟
②设置初值
③加载初值,启动定时器
④设置为自动加载
⑤中断相关设置

PWM是什么呢?

PWM(Pulse-Width Modulation 脉宽调制)
在这里插入图片描述
T1 T2 可调整就能输出不同的脉宽

用占空比可以改变的方波控制直流电机,就可以改变直流电机的输入平均电压,进而控制电机速度。采占空比可以改变的方波叫 PWM(Pulse-Width Modulation 脉宽调制)。脉宽调制大多用在直流电机调速上。给定的电压高,电机转速就快;给定的电压低,电机转速就慢。

二、编写程序:当计时器到0后,采用中断,然后点灯

打开我们的main函数

int main(void)
{
    led_init();
    interrupt_init();  /* 初始化中断控制器 */
//我们初始化了中断源,同样的,我们初始化timer
    key_eint_init();   /* 初始化按键, 设为中断源 */
//初始化定时器
    timer_init();

新建一个 timer.c,我们肯定需要操作一堆寄存器,添加头文件

#include "s3c2440_soc.h"
void timer_init(void)
{
	设置TIMER0的时钟
	设置TIMER0的初值
	加载初值, 启动timer0
	设置为自动加载并启动(值到0以后会自动加载)
}
设置中断,显然我们需要提供一个中断处理函数void timer_irq(void)在这里面我们需要点灯

1、初始化定时器

(1)设置TIMER0的时钟 / (2)设置TIMER0的初值

P313 PWM电路图:
在这里插入图片描述
P322公式:
Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
{prescaler value} = 0~255
{divider value} = 2, 4, 8, 16
定时器输入时钟频率 = PCLK/(预定标值 +1)/(分频因子)

预定标器存储系统时间频率。分频器接收来自预定标器的时钟信号,并进行分频处理,输出有 5 种模式:1/2,1/4,1/8,1/16 和外部时钟 TCLK。

需要操作的寄存器有:

  • 设置TIMER CONFIGURATION REGISTER0 (TCFG0),确定预定标值
  • 设置TIMER CONFIGURATION REGISTER1 (TCFG1),选择定时器0,确定分频因子
  • 设置TIMER 0 COUNT BUFFER REGISTER 和 COMPARE BUFFER REGISTER (TCNTB0/TCMPB0),确定初值和对比值
(3)加载初值

第一次加载初值需要手工更新
需要操作的寄存器有:
TIMER CONTROL (TCON) REGISTER Register
Timer 0 manual update (note) [1]
NOTE: The bit has to be cleared at next writing.

(4)设置为自动加载并启动(值到0以后会自动加载), 启动timer0

需要操作的寄存器有:
TIMER CONTROL (TCON) REGISTER Register
Timer 0 auto reload on/off [3]
Timer 0 start/stop [0]

(5)完整代码:
void timer_init(void)
{
	/* 设置TIMER0的时钟 */
	/* Timer clk = PCLK / {prescaler value+1} / {divider value} 
	             = 50000000/(99+1)/16
	             = 31250   意思就是从31250减到0,过去1s钟
	 */
	TCFG0 = 99;  /* Prescaler 0 = 99, 用于timer0,1 */
	TCFG1 &= ~0xf;
	TCFG1 |= 3;  /* MUX0 : 1/16 */

	/* 设置TIMER0的初值 */
	TCNTB0 = 15625;  /* 0.5s中断一次 */

	/* 加载初值, 启动timer0 */
	TCON |= (1<<1);   /* Update from TCNTB0 & TCMPB0 */

	/* 设置为自动加载并启动 */
	TCON &= ~(1<<1);
	TCON |= (1<<0) | (1<<3);  /* bit0: start, bit3: auto reload */

	/* 设置中断 */
}

2、定时器中断

设置中断,显然我们需要提供一个中断处理函数void timer_irq(void)
在Timer里没有看到中断相关的控制器,我们需要回到中断章节去看看中断控制器,看看有没有定时器相关的中断
我们没有看到更加细致的Timer0寄存器
在这里插入图片描述
当TCNTn=TCMPn时,他不会产生中断,只有当TCNTn等于0的时候才可以产生中断,我们之前以为这个定时器可以产生两种中断,那么肯定有寄存器中断或者禁止两种寄存器其中之一,那现在只有一种中断的话,就相对简单些
设置中断的话,我们只需要设置中断控制器

设置interrupu.c中断控制器
*初始化中断控制器 void interrupt_init(void)
INTMSK &= ~((1<<0) | (1<<2) | (1<<5));

*把定时器相应的位清零就可以了,哪一位呢?
INTPND的哪一位?
INT_TIMER0第10位即可

interrupt.c

void interrupt_init(void)
{
	INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
	INTMSK &= ~((1<<10));
}

void handle_irq_c(void)
{
	int bit = INTOFFSET;
	
	if(bit == 0 || bit == 2 || bit == 5)
	{	
		key_init_irq(bit);
	}
	if(bit == 10)
	{
		timer_irq();
		
	}

	/* 清中断 */
	SRCPND = (1<<bit);
	INTPND = (1<<bit);
}

timer.c
循环点亮

void timer_irq(void)
{
	static int cnt = 0;
	int tmp;
	cnt++;
	
	tmp = ~cnt;
	tmp &= 7;
	GPFDAT &= ~(7<<4);
	GPFDAT |= (tmp<<4);
}

有个寄存器TCNTO0 可以用来调试,看计时器是否在计时

int main(void)
{
	led_init();
	interrupt_init();  /* 初始化中断控制器 */
	key_eint_init();   /* 初始化按键, 设为中断源 */
	timer_init();
	
	puts("\n\rg_A = ");
	printHex(g_A);
	puts("\n\r");

	while (1)
	{
#if 0		
		puts("\n\rg_Char = ");
		printHex(g_Char);
		puts("\n\r");


		puts("\n\rg_Char3 = ");
		printHex(g_Char3);
		puts("\n\r");
#endif
		putchar(g_Char);
		g_Char++;

		putchar(g_Char3);
		g_Char3++;
		delay(1000000);
		//printHex(TCNTO0);
	}

	
	return 0;
}

三、改进程序

进入main函数中执行 timer_init();
还需要修改interrupt.c
初始化函数void interrupt_init(void)
还需要调用中断处理函数void handle_irq_c(void)
每次添加一个中断我都需要修改handle_irq这个函数,这样太麻烦,我能不能保证这个interrupt文件不变,只需要在timer.c中引用即可,这里我们使用函数指针数组

在interrupt.c里,定义函数指针,以及申请函数指针数组:
数组里存放中断执行函数。
为什么是32呢?因为SRCPND是32位的说明有32个中断源。

typedef void(*irq_func)(int);
irq_func irq_array[32];
void handle_irq_c(void)
{
	int bit = INTOFFSET;
	irq_array[bit](bit);  //调用处理函数
	/* 清中断 */
	SRCPND = (1<<bit);
	INTPND = (1<<bit);
}

void register_irq(int bit, irq_func func)
{
	INTMSK &= ~((1<<bit));
	irq_array[bit] = func;
}

在timer.c里注册中断

void timer_init(void)
{
	/* 设置Timer0时钟 */
	//Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
	//定时器时钟频率 = 50000000ns / (99+1) / 16 = 31250
	TCFG0 = 99;
	TCFG1 |= (3<<0);
	/* 设置Timer0初值 */
	TCNTB0 = 15625;  /* 0.5s中断一次 */
	/* 加载Timer0初值 */
	TCON |= (1<<1);
	/* 设置Timer0自动加载,启动Timer0*/
	TCON &= ~(1<<1);
	TCON |= ((1<<0) | (1<<3));
	
	register_irq(10, timer_irq);
}
/* 初始化按键, 设为中断源 */
void key_eint_init(void)
{
	/* 配置GPIO为中断引脚 */
	GPFCON &= ~((3<<0) | (3<<4));
	GPFCON |= ((2<<0) | (2<<4));   /* S2,S3被配置为中断引脚 */

	GPGCON &= ~((3<<6) | (3<<22));
	GPGCON |= ((2<<6) | (2<<22));   /* S4,S5被配置为中断引脚 */
	

	/* 设置中断触发方式: 双边沿触发 */
	EXTINT0 |= (7<<0) | (7<<8);     /* S2,S3 */
	EXTINT1 |= (7<<12);             /* S4 */
	EXTINT2 |= (7<<12);             /* S5 */

	/* 设置EINTMASK使能eint11,19 */
	EINTMASK &= ~((1<<11) | (1<<19));

	register_irq(0, key_eint_irq);
	register_irq(2, key_eint_irq);
	register_irq(5, key_eint_irq);
}
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《Linux设备驱动程序 第三版》是一本针对Linux设备驱动程序开发的专业书籍。该书详细介绍了Linux下常见的设备驱动程序开发技术,包括字符设备、块设备和网络设备等方面。这本书主要面向Linux系统内核开发领域的工程师、软件开发人员、嵌入式系统开发者和设备驱动程序编写者等,也适合对Linux设备驱动程序开发感兴趣的读者使用。 《Linux设备驱动程序 第三版》共分为14章,涵盖了设备驱动的基本知识、字符设备驱动程序、块设备驱动程序、内存映射、中断、高级字符驱动程序、USB驱动程序、串行通讯、网络接口卡驱动程序、媒体网关控制协议、显示屏驱动程序、电源管理、高级块驱动程序等方面。每一章的内容非常详细,涵盖了理论知识和例子,并且还提供了配套的示例代码,方便读者进行实践操作。 这本书的重点在于教授读者如何使用Linux内核框架编写驱动程序,如何开发Linux设备驱动程序,以及如何与设备进行交互。同时,还详细介绍了内核数据结构和编程规范、编译驱动程序等技术,使读者更加深入了解Linux设备驱动程序的开发过程。 总之,《Linux设备驱动程序 第三版》是一本非常优秀的Linux设备驱动程序开发教程,书中内容涵盖面广,基本知识详细介绍,适合初学者和进阶者使用。读者通过学习本书,可以更好地掌握Linux设备驱动程序的开发技术,提高自身的技术水平。 ### 回答2: 《Linux设备驱动程序》第三版PDF,是一本深入介绍Linux驱动程序开发的经典著作之一。本书主要介绍了Linux设备驱动程序的编写和调试方法,以及Linux内核的一些基本特性。 本书首先介绍了Linux内核框架和设备驱动模型,包括字符设备、块设备和网络接口设备等。接着,本书深入分析了设备驱动程序的结构和实现,详细介绍了内核的I/O系统、中断处理、定时器、内存管理、锁等基本特性,以及与硬件相关的总线、中断控制器、DMA等。此外,本书还介绍了如何使用ioctl和procfs接口,以及注册和使用内核模块等典型应用场景。 总体来说,这本书深入浅出,详细介绍了Linux设备驱动程序的开发与调试方法,为读者提供了全面了解内核和驱动开发的实战指南,是Linux驱动程序开发者必不可少的参考读物。 ### 回答3: 《Linux设备驱动程序 第三版 pdf》是一本介绍Linux设备驱动程序方面的经典教材,其内容包括了Linux设备驱动程序的开发、编写以及调试等相关知识。这本教材适合那些想要理解和开发嵌入式操作系统和设备驱动程序程序员和工程师们。 该教材主要分为三部分,第一部分介绍了驱动程序的基础知识,包括设备文件、设备驱动程序机制、字符设备以及模块加载和卸载等内容;第二部分则介绍了高级设备驱动程序的开发,如块设备、网络设备以及USB设备驱动程序等;最后一部分则介绍了调试和测试驱动程序的方法和工具。 该教材有以下几个亮点:一是该教材的内容严谨,通俗易懂;二是该教材使用的代码清晰简单,易于理解和实践;三是该教材不仅仅局限于硬件设备的驱动,还涉及了Linux内核模块的编写、网络驱动的开发等内容。同时,该教材也提供了大量的实例代码和案例,可以帮助读者更好地理解和掌握Linux设备驱动程序的开发和调试。 总之,《Linux设备驱动程序 第三版 pdf》是一本非常优秀的Linux设备驱动程序教材,不仅适合于嵌入式开发工程师,也适合于Linux内核爱好者学习和参考。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值