因为肝了一天,对单片机寄存器有点了解,特地来分享一下。这篇文章主要带大家讲解 51单片机 IE,TCON,TMOD 寄存器 的含义 及 外部中断 和 定时器的使用。
| 中断允许寄存器IE |
| |||||||
| 位序号 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 位符号 | EA | - | - | ES | ET1 | EX1 | ET0 | EX0 |
|
| 总允许位 |
|
| 串行口中断允许位 | 定时器T1溢出中断允许位 | 外部中断1允许位 | 定时器T0溢出中断允许位 | 外部中断 INT0允许位 |
|
| EA=1,CPU开放中断 |
|
| ES=1,允许串行口中断 | ET1=1,允许T1中断 | EX1=1,允许外部中断1中断 | ET0=1,允许T0中断 | EX0=1,允许外部中断0中断 |
EX0:外部中断 INT0 允许位。当 EX0 = 1,允许外部中断INT 0 中断。当 EX0 = 0 ,不允许外部中断 0 中断。EX1 同上。
ET0:定时器T0溢出中断允许位.。当 ET0 = 1, 允许 T0 中断。当 ET0 = 0, 不允许 T0 中断。ET1同上。
ES:串行口中断允许位。当 ES = 1, 允许串口中断。当 ES = 0, 不允许串口中断。
EA:总允许位。当 EA = 1, CPU开开放中断。当 EA = 0, CPU 不开放中断。
然后是
|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
注意:IE0 IE1 TF0 TF1 是标志位,可以理解为由单片机控制,不用我们控制。
IT1: 当 IT1 = 1 时,为跳变沿触发方式,INT1上的电平从高到低的负跳变有效
(可以由按键button触发);
IT1 = 0 时,电平触发方式,INT1上低电平有效。
IT0同上。
注意:IT1 IT0 只是来设置方式。
IE1:
当 IT1 = 1 (方式!!!) 时,若 INT1 为由高电平向低电平的跳变时则将 IE1 置1 (IE1 = 1)
IE1 = 1 时,外部中断1正在向CPU申请中断。CPU响应时,转向中断服务程 序时,该 位由硬件清 0( IE1 = 0)。
当IT1 = 0 (方式!!!)时,若INT1为低电平时,则置1 (IE1 = 1),否则IE1清0 (IE1=0)。
IE0同上。 注意:(可以无需人为控制,由单片机控制)
TR1:
定时器T1的运行控制位。
当 GATE = 0, TR1 =1 时就允许T1开始计数,TR1=0时禁止T1计数。
当 GATE = 1,TR1 = 1 且外部中断1输入位高电平时,才允许T1计数。
TR0同上。
TF1:
T1被允许计数以后,从初值开始加1计数。当最高位产生溢出时由硬件置‘1’(TF1=1),此时向CPU请求中断,一直保持到CPU响应中断时,才由硬件清‘0’ (TF1=0)。
TF0同上。注意:(可以无需人为控制,由单片机控制)
下面以外部中断 0 为例讲一下使用步骤:
讲了那么多,接下来我们开始上代码吧。
#include "reg52.h" //注意:如果是keil5需把头文件换成 #include <REGX52.H> typedef unsigned int u16;typedef unsigned char u8;//定义P1led为P1口 注意:如果是P0口需上拉电阻#define P1led P1//延时函数void Delay(u16 i){while(i--);}//数码管u8 code Leds[] = {0x3F, // 00x06, // 10x5B, // 20x4F, // 30x66, // 40x6D, // 50x7D, // 60x07, // 70x7F, // 80x6F, // 9};//定义,通过for遍历 共阳接法点亮需输出低电平u8 led_status_g[] = { 0xFE, //0 1111 11100xFD, //1 1111 11010xFB, //2 1111 10110xF7, //3 1111 01110xEF, //4 1110 11110xDF, //5 1101 11110xBF, //6 1011 11110x7F //7 0111 1111};void int0Init(){ EA = 1; //总中断打开 EX0 = 1; //外部中断0打开IT0 = 1; //INT0为跳变沿触发方式}void int1Init(){EA = 1;EX1 = 1;IT1 = 1;}void main(){int0Init();int1Init();IP = 0x01; //IP=0x01是外部中断0设置为高优先级中断P2 = 0x3f; //数码管一开始显示0while(1){u16 i;for(i = 0; i < 10; i++) {P1led = led_status_g[i];Delay(50000); //延时}}}//外部中断 0 由 button01 控制 void int0(void) interrupt 0 { u16 i; for(i = 0;i < 10; i++){P2 = Leds[i]; Delay(50000);}P2 = 0x3f; }//外部中断 1 由 button02 控制void int1(void) interrupt 2{int j;for(j = 1; j < 5; j++) {//P1口P1led = 0x00; //此时P1口灯全亮Delay(50000); P1led = 0xFF; //此时P1口灯全暗 Delay(50000);}}
下面是仿真图:

因为设置了 IP = 0x01; 所以 外部中断 0的优先级别高。按下button01,外部中断0开始执行。
最后讲一下
TMOD |
| |||||||
|
| 定时器1 | 定时器0 | ||||||
| 位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 位符号 | GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
|
| 门控制 | C/T=0,定时器模式。C/T=1,计数器模式。 | 用来选择定时计/计数器的工作方式 | 用来选择定时计/计数器的工作方式 | 门控制 | C/T=0,定时器模式。C/T=1,计数器模式。 | 用来选择定时计/计数器的工作方式 | 用来选择定时计/计数器的工作方式 |
注意:对GATE:
当GATE=0, TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。
当GATE=1,TR1=1且外部中断1输入位高电平时,才允许T1计数。
注意:
C/T=1时为计数功能: 加1计数器对来自输入引脚T0(P3.4)和T1(P3.5)的外信号脉冲进行计 数,每来一个脉冲,计数器加1,直到计时器TF1或TF0满溢出
C/T=0时为定时功能: 加1计数器对脉冲f进行计数,每来一个脉冲,计数器加1,
直到计时器TF1或TF0满溢出
| M1 | M0 | TMOD-T0 | TMOD-T1 | 工作模式 | 说明 |
| 0 | 0 | 0x00 | 0x00 | 方式0 | 13位计时计数器 (8192) |
| 0 | 1 | 0x01 | 0x10 | 方式1(常用) | 16位计时计数器 (65536) |
| 1 | 0 | 0x02 | 0x20 | 方式2 | 8位计时计数器,可自动重新载入计数值 (256) |
| 1 | 1 | 0x03 | 0x30 | 方式3 | 仅适用于T0,分成两个8位计数器,T1停止计数 |
注意:
如何定时:
方式1时, 此时定时器的位数为16, (高8位送给TH 1/0,低8位送给TL 1/0 )
注意:
因为完成一条指令需要一个机器周期,一个机器周期有12个振荡周期,振荡周期可以理解为晶振周期,如 12MHz的晶振,振荡周期 = 1 / 12M ,那么
机器周期 = 12 * 1/12 M = 1us 。
计算:
用方式1,定时器为16位,16位最大值为2^16 = 65536。
如果我们要定时1ms = 1000us,12MHz的单片机其机器周期为1us ,
初始值大小x = 65536 - 1000 = 64536 ,
此时把64536 换算成16进制 为fc18, 此时高八位为fc送给TH1=0xfc, 低八位送给TL1=0x18 .

或者:
TH1 = (65536-1000)/256 ; // 1ms 12MHz
TL1 = (65536-1000)%6 ;
(T1被允许计数以后,从初值开始加1计数。 当定时器1计满溢出时,由硬件使TF1置1,此时向CPU请求中断,进入中断服务程序后,由硬件自动清0。 )
当启动T1时,T1从初始值64536开始计数,超过65536则溢出,申请中断。那么该过程就是1ms。
如果需要1s,可以定义u16 n,n++, if (n==1000) n=0;
定时器的使用:
1.工作方式1 定时器T0 / T1 选择控制方式GATE = 0 / 1
定时模式C/T=0 or计数模式 C/T=1. (TMOD=0x01)
2.定时器设初值(设置TH0=0xfc与 T0=0x18)看具体要求
3.开启定时器中断(设置ET0=1或ET1=1)
4.开启总中断(EA=1)
5.定时器/计数器选择T0 / T1?(设置TR1=1 or TR0=1的值)
最后上代码,因为比较多,所以上部分代码。
//定时器0void Timer0Init(void){ //使用方式0 1ms12MHz TMOD |= 0x01; TH0 = (65536-1000)/256; //0xfcTL0 = (65536-1000)%6; //0x18ET0 = 1; //定时器0中断打开EA = 1; //总中断打开TR0 = 1; //定时器0开关打开 }//定时器1void Timer1Init(void) { //使用方式1,16位TMOD |=0x10;定时器TH1 = (65536-50000)/256; //50msTL1 = (65536-50000)%6;ET1 = 1; //定时器1中断打开EA = 1; //总中断打开TR1 = 1; //定时器1打开} //定时器0中断void Timer0(void) interrupt 1{static u16 i;//1ms 12MHzTH0 = (65536-1000)/256; //0xfc TL0 = (65536-1000)%6; //0x18i++;if(i == 1000) //1000ms{num0 ++;i = 0;}}//定时器1中断void Timer1(void) interrupt 3{ static u16 i;TH1 = (65536-50000)/256; //50msTL1 = (65536-50000)%6;i++;if(i == 20) // 1s{num0++;i = 0;}}
最后呢,如果有错误请大家指出来,加以改正。只是为了分享一下理解,同时也借鉴了其他博主的文章,非常感谢。
其他文章借鉴:
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源
嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主