完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
1 中断的基本概念
中断装置和中断处理中断处理程序统称为中断系统。中断(Interrupt)是计算机的一个重要概念,现代计算机普遍采用中断技术。 当计算机执行正常程序时,系统中会出现某些急需处理的异常情况和特殊请求,此时 CPU 会暂时中止现行程序,转去对随机发生的更为紧迫的事件进行处理,处理完毕后,CPU 自动返回原来的程序继续执行,此过程就叫做中断。实现中断功能的硬件和软件统称中断系统。一个完整的中断处理过程包括中断请求、中断响应、中断处理和中断返回。
我们从一个生活中的例子引入。你正在家中看书,突然电话铃响了,你放下书本,去接电话,和来电话的人交谈,然后放下电话,回来继续看你的书。这就是生活中的“中断”的现象,就是正常的工作过程被外部的事件打断了。 常见的中断有外部中断和时钟中断:
如果没有中断的话,arduino 是一直运行 loop 内的代码,一遍一遍重复运行。当有中断产生时候,单片机会停止 loop 的代码,开始运行中断服务函数的代码,运行一遍中断服务函数后,继续回到 loop 内接着刚才运行的代码运行。2 外部中断 外部中断是由外部设备发起请求的中断。要想使用外部中断,就需了解中断引脚的位置,根据外部设备选择中断模式,以及编写一个中断被触发后需执行的中断函数。 2.1 中断引脚与中断编号 在不同型号的 Arduino 控制器上,中断引脚的位置也不相同,只有中断信号发生在带有外部中断功能的引脚上,Arduino才能捕获到该中断信号并做出响应,下表列举了 Arduino 常见型号控制器的中断引脚所对应的外部中断编号。 [tr]Arduino 型号int0 int1 int2 int3 int4 int5 [/tr]
2.2 中断模式 为了设置中断模式,还需要了解设备触发外部中断的输入信号类型。中断模式也就是中断触发的方式,在大多数 Arduino 上支持下表中的四种中断触发方式。 [tr]模式名称说明[/tr]
2.3 中断函数 除了设置中断模式外,还需要编写一个响应中断的处理程序——中断函数,当中断被触发后,便可以让Arduino运行该中断函数。中断函数就是当中断被触发后要去执行的函数,该函数不能带有任何参数,且返回类型为空,如: void counter() { count++; } 当中断被触发后,Arduino 便会执行该函数中的语句。 这些准备工作完成后,还需要在 setup() 中使用 attachInterrupt() 函数对中断引脚进行初始化配置,以开启 Arduino 的外部中断功能,其用法如下: (1)attachInterrupt( interrupt,function,mode) 功能:对中断引脚进行初始化配置。 参数: interrupt,中断编号,注意,这里的中断编号并不是引脚编号。 function,中断函数名,当中断被触发后即会运行此函数名称所代表的中断函数。 mode,中断模式。 attachInterrupt(0,counter,RISING); 如果使用的是 Arduino UNO 或者 MEGA 控制器,则该语句即会开启2号引脚(中断编号0)上的外部中断功能,并指定上升沿时触发该中断。当2号引脚上的电平由低变高后,该中断会被触发,而 Arduino 即会运行 counter()函数中的语句。 如果不需要使用外部中断了,则可以使用中断分离函数 detachInterrupt() 来关闭中断功能。 在使用 attachInterrupt 函数时要注意以下几点:
功能:禁用外部中断。 参数: interrupt,需要禁用的中断编号。 (3)interrupts()和nolnterrupts() interrupts 和 noInterrupts 函数在 Arduino 中负责打开和关闭总中断,函数无返回值,无参数,可以在文件 wiring.h 中查看函数原型,如下: #define interrupts() sei() #define noInterrupts() cli() 3 定时器中断 3.1 定时器的作用 定时器对于单片机来说就类似我们现实生活中的时钟,记录很多和时间相关的事件。在我们平时经常使用的 delay() millis() ,micros() ,delayMicroseconds() ,PWM 波生成的 analogWrite() 和 tone() 函数都是通过定时器实现的,不过这些都被 Arduino 的封装库隐藏起来了,为了让使用者更快更便捷地开发项目。 我们平常使用的 Arduino 单片机为 UNO,NANO和MEGA 2560。UNO 和 NANO 都使用的是 ATmega328 芯片,这款芯片有3个定时器,Timer0,Timer1,Timer2,其中Timer0和Timer2都是8位寄存器(256),Timer1是16位寄存器(65536),意味着更高的分辨率。mege2560 使用的是 ATmege2560 芯片,这款芯片有 6 个定时器,在328 的基础上,增加了 Timer3,Timer4,Timer5。这三个定时器都是16位的寄存器。 [tr]Arduino 型号 参数Timer0 Timer1 Timer2 Timer3 Timer4 Timer5 [/tr]
3.2 定时器的基本概念 1、寄存器 寄存器列表如下,x代表0,1,2,3,4,5这6种定时器。 [tr]寄存器作用[/tr]
2、预分频系数与比较匹配器 Arduino UNO 时钟以16MHz运行。计数器的一个刻度值表示 1 / 16,000,000秒(~63ns),跑完1s需要计数值16,000,000。 (1)Timer0 和 Timer2 是8位定时器,可以存储最大计数器值255。 (2)Timer1 是一个16位定时器,可以存储最大计数器值65535。 一旦计数器达到其最大值,它将回到零(这称为溢出)。因此,需要对时钟频率进行分频处理,即预分频器。通过预分频器控制定时计数器的增量速度。预分频器与定时器的计数速度如下: 定时器速度(HZ) = Arduino UNO时钟速度(16MHz) / 预分频器系数因此,1预分频器将以16MHz递增计数器,8预分频器将在2MHz递增,64预分频器= 250kHz,依此类推。 定时器 Timer0 的预分频系数配置如表: 现在您可以用以下公式计算中断频率: 中断频率(Hz)=(Arduino时钟速度16MHz)/(预分频器*(比较匹配寄存器+ 1))重新排列上面的等式,给出你想要的中断频率,你可以求解比较匹配寄存器值: 比较匹配寄存器= [16,000,000Hz /(预分频器*所需的中断频率)] - 1当你使用定时器0和2时,这个数字必须小于256,对于timer1小于65536。 所以如果你想每秒一次中断(频率为1Hz): 比较匹配寄存器= [16,000,000 /(预分频器 * 1)] -1预分频器为1024,你得到: 比较匹配寄存器= [16,000,000 /(1024 * 1)] -1 = 15,624因为256 <15,624 <65,536,你必须使用timer1来实现这个中断。 3、定时器模式 定时器可以配置为不同的模式。 (1)PWM模式。 纸浆宽度调制模式。 OCxy输出用于生成PWM信号 (2)CTC模式。 比较匹配时清除计时器。 当定时器计数器到达比较匹配寄存器时,定时器将被清除。 定时器 Timer0 的模式选择配置如表: 3.3 定时器的配置 int toggle0,toggle1,toggle2; void setup(){ cli();关闭全局中断 //设置定时器0为10kHz(100us) TCCR0A = 0;//将整个TCCR0A寄存器设置为0 TCCR0B = 0;//将整个TCCR0B寄存器设置为0 TCNT0 = 0;//将计数器值初始化为0 //设置计数器为10kHZ,即100us OCR0A = 24;//比较匹配寄存器= [16,000,000Hz /(预分频器*所需中断频率)] - 1 //比较匹配寄存器=24,中断间隔=100us即中断频率10khz TCCR0A |= (1 << WGM01);//打开CTC模式 TCCR0B |= (1 << CS01) | (1 << CS00); //设置CS01位为1,CS00位为1(64倍预分频) TIMSK0 |= (1 << OCIE0A);//启用定时器比较中断 //设置定时器1为1kHz TCCR1A = 0;//将整个TCCR1A寄存器设置为0 TCCR1B = 0;//将整个TCCR1B寄存器设置为0 TCNT1 = 0;//将计数器值初始化为0 //设置计数器为1kHZ,即1ms OCR1A = 1999;// = (16*10^6)/(1000*8) - 1 (must be <65536) TCCR1B |= (1 << WGM12);//打开CTC模式 TCCR1B |= (1 << CS11);//设置CS11位为1(8倍预分频) TIMSK1 |= (1 << OCIE1A); //设置定时器2为8kHz TCCR2A = 0;// set entire TCCR2A register to 0 TCCR2B = 0;// same for TCCR2B TCNT2 = 0;//initialize counter value to 0 // set compare match register for 8khz increments OCR2A = 249;// = (16*10^6) / (8000*8) - 1 (must be <256) // turn on CTC mode TCCR2A |= (1 << WGM21);//打开CTC模式 // Set CS21 bit for 8 prescaler TCCR2B |= (1 << CS21); // enable timer compare interrupt TIMSK2 |= (1 << OCIE2A); sei();//打开全局中断 } //中断0服务函数 ISR(TIMER0_COMPA_vect){// timer0中断2Hz切换引脚13(LED) //产生频率为10kHz / 2 = 5kHz的脉冲波 if(toggle0){ digitalWrite(8,HIGH); toggle0 = 0; } else{ digitalWrite(8,LOW); toggle0 = 1; } } ISR(TIMER1_COMPA_vect){// timer1中断2Hz切换引脚13(LED) //产生频率为2Hz / 2 = 1Hz的脉冲波 if(toggle1>=500) digitalWrite(13,HIGH); if(toggle1<=500) digitalWrite(13,LOW); toggle1 += 1; if(toggle1 >= 1000) toggle1 = 0; } ISR(TIMER2_COMPA_vect){// timer2中断8kHz切换引脚9 //产生频率为8kHz / 2 = 4kHz的脉冲波 if(toggle2){ digitalWrite(9,HIGH); toggle2 = 0; } else{ digitalWrite(9,LOW); toggle2 = 1; } } //loop function void loop(){ } 3.4 定时器中断的使用 使用定时器中断前,必须先安装对应的 Timer 库,然后导入到 Arduino 的库文件里,并在程序中引用头文件 Timer.h 。 实例: // 秒切换一次引脚13的电平 // 包含定时器库的头文件 #include // 中断服务程序 void flash() { static boolean output = HIGH; digitalWrite(13, output); output = !output; } void setup() { pinMode(13, OUTPUT); FlexiTimer2::set(500, flash); // 中断设置函数,每 500ms 进入一次中断 FlexiTimer2::start(); // 开始计时 } void loop() { } 其他 Timer 的使用基本一样,只要找到合适的库函数就能够使用,注意使用的定时器不要和你已使用的封装函数冲突,比如对于 UNO 来说,你在使用 Servo.h 的时候,就不能再使用 timer1 了,此时IDE会给你编译报错。 |
|||||||
|
|||||||
只有小组成员才能发言,加入小组>>
3309 浏览 9 评论
2988 浏览 16 评论
3490 浏览 1 评论
9050 浏览 16 评论
4085 浏览 18 评论
1171浏览 3评论
602浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
594浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2332浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1894浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-20 16:11 , Processed in 1.135585 second(s), Total 50, Slave 41 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号