关于中断(Interrupt)的详细介绍以及IIC、软串口、PinChangeInt库


https://www.arduino.cn/thread-13205-1-1.html

参考文档

1. 关于中断处理的一些常见问题 . . .

1.1 常常看到有人问到:

  1. 我在中断的子程序内加进IIC通信后就进不了中断了…求指点。
  2. 我在中断程序内加入 Lcd_IIC 的程序后就死机…求指点。

其实, 不论是 IIC/TWI, 或是 SPI, 以及硬串口、软串口甚至 Serial.print 都是要靠中断来帮忙处理, 如果你把中断禁止了, 那 IIC/TWI, SPI 都无法动作了 !

啥?
你说你没有禁止中断?
Arduino 一旦进入中断程序 就会自动禁止中断, 因此, 在中断程序内(包括它的子程序内)原则上无法做 IIC 与 SPI 以及软硬串口的通信!
为何说原则上呢?
因为你还是可以在中断程序内把中断打开, 只要这样:
sei( ); //打开中断
cli(); //关闭中断
可是, 那是否有其他问题就要看看你的中断处理到底做何事情, 以及中断来的时间是否太短 ?
就是,如果两次中断的时间间隔足够长,只要来得及处理或是中断重进入(reentrant)不会有问题, 那就可以把中断打开 !

1.2 关于中断的重进入(reentrant), 请参考维基百科:

http://en.wikipedia.org/wiki/Reentrancy_(computing)
当然, 如果你测试结果没问题那就放心的打开中断 !

1.3 关于中断的概念可以看看奈何大神写的这篇有趣文章:

http://www.arduino.cn/thread-2421-1-1.html

该篇主要是介绍Arduino外部中断INT0, INT1的使用, 也就是外部 0 号和 1 号中断(pin2, pin 3)的介绍, 使用 attachInterrupt(INT_number, function, mode);
也可以参考:
http://arduino.cc/en/Reference/attachInterrupt

1.4 如果你是要使用内部定时器(timer0, timer1, timer2)定时中断, 请看我写的这些贴文:

  1. 使用 MsTimer2 库定时做很多事(教程):
    http://www.arduino.cn/thread-12435-1-1.html

  2. 使用TimerOne库(Timer1)定时做多件事(教程):
    http://www.arduino.cn/thread-12441-1-4.html

  3. 自己控制 timer1 定时器定时做多件事(教程):
    http://www.arduino.cn/thread-12445-1-1.html

  4. 自己控制 timer2 定时器定时做多件事(教程)":
    http://www.arduino.cn/thread-12448-1-1.html

  5. 补充设定 timer1 定时器和 timer2 定时器定时做多件事(教程)
    http://www.arduino.cn/thread-12452-1-2.html

    不论是 SPI, IIC 与软串口都是大量使用中断处理(Interrupt), 在中断处理程序内工作没处理完之前是在禁止其它中断的状态, 如果中断处理程序做太多事, 本来就会影响其他中断的进行! !

IIC 使用 ISR(TWI_vect) 中断处理, 软串口SoftwareSerial 使用 ISR(PCINT0_vect) 或类似的(PCINT1/2)中断, 虽然 ISR(PCINT0_vect) 的优先权高于 ISR(TWI_vect), 但一旦进入 ISR(TWI_vect) 内由于中断请求被禁止, 此时即使软串口所用的 ISR(PCINT0_vect) 中断来到, 一样无法处理, 于是导致软串口的通信失常或数据遗失 !

1.5 Arduino的 CPU 之25种中断之优先级

关于Arduino的 CPU 之25种中断之优先级可看:
http://gammon.com.au/interrupts
就是说, 除了 Reset 之外, 还有 25种中断可以使用:
(依照优先级排列, 所以外部 pin 2 的 INT0 是最优先的! , 其次是 pin 3 的 INT1 中断)

优先级名称功能
1.Reset
2.External Interrupt Request 0 (pin D2)(INT0_vect)
3.External Interrupt Request 1 (pin D3)(INT1_vect)
4.Pin Change Interrupt Request 0 (pins D8 to D13)(PCINT0_vect)
5.Pin Change Interrupt Request 1 (pins A0 to A5)(PCINT1_vect)
6.Pin Change Interrupt Request 2 (pins D0 to D7)(PCINT2_vect)
7.Watchdog Time-out Interrupt(WDT_vect)
8.Timer/Counter2 Compare Match A(TIMER2_COMPA_vect)
9.Timer/Counter2 Compare Match B(TIMER2_COMPB_vect)
10.Timer/Counter2 Overflow(TIMER2_OVF_vect)
11.Timer/Counter1 Capture Event(TIMER1_CAPT_vect)
12.Timer/Counter1 Compare Match A(TIMER1_COMPA_vect)
13.Timer/Counter1 Compare Match B(TIMER1_COMPB_vect)
14.Timer/Counter1 Overflow(TIMER1_OVF_vect)
15.Timer/Counter0 Compare Match A(TIMER0_COMPA_vect)
16.Timer/Counter0 Compare Match B(TIMER0_COMPB_vect)
17.Timer/Counter0 Overflow(TIMER0_OVF_vect)
18.SPI Serial Transfer Complete(SPI_STC_vect)
19.USART Rx Complete(USART_RX_vect)
20.USART, Data Register Empty(USART_UDRE_vect)
21.USART, Tx Complete(USART_TX_vect)
22.ADC Conversion Complete(ADC_vect)
23.EEPROM Ready(EE_READY_vect)
24.Analog Comparator(ANALOG_COMP_vect)
25.2-wire Serial Interface (I2C)(TWI_vect)
26.Store Program Memory Ready(SPM_READY_vect)

2. 关于IIC与软串口等的源代码可看:

https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/libraries
( 软串口是在 SoftwareSerial.cpp; IIC 要看 Wire.cpp 和其 utility 目录内的 twi.c )

接下来,
我们来讨论可以用在全部 pin 的 Pin Change Interrupt 针脚状态改变中断 !
http://playground.arduino.cc/Main/PinChangeInterrupt
这个在官网已经有大神帮忙写了Library库可用:
http://playground.arduino.cc/Main/PinChangeInt

3. Pin Change Interrupt

请注意, 如果你使用了软串口(SoftwareSerial), 就是说你用了一下:
#include <SoftwareSerial.h>
那接着我们要讲的 PCI (Pin Change Interrupt) 就不能用了, 因为软串口的库已经写个该三个中断处理 ISR( ) 如下:

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
  SoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect)
{
  SoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect)
{
  SoftwareSerial::handle_interrupt();
}
#endif

这可以在你 Arduino IDE 目录下的以下档案找到:
libraries\SoftwareSerial\SoftwareSerial.cpp

在Arduino UNO 以及大部分的板子有 Digital pin 0 到 pin 13, 以及 analog pin A0 到 A5 (又称 pin 14 到 pin 19);
这 20支 pin 分为三组, 对应到 ISR(PCINT2_vect), ISR(PCINT0_vect), 以及 ISR(PCINT1_vect) 这三个 ISR( ) 中断程序:
ISR (PCINT2_vect) 处理 Pin D0 to D7
ISR (PCINT0_vect) 处理 Pin D8 to D13
ISR (PCINT1_vect) 处理 Pin A0 to A5

3.1 Pin Change Interrupts 范例:


ISR (PCINT0_vect)
{
// handle pin change interrupt for pin D8 to D13 here
} // end of PCINT0_vect
 
ISR (PCINT1_vect)
{
// handle pin change interrupt for pin A0 to A5 here
} // end of PCINT1_vect
 
ISR (PCINT2_vect)
{
// handle pin change interrupt for pin D0 to D7 here
} // end of PCINT2_vect
 
void setup ()
{ 
// pin change interrupt (example for pin D9)
PCMSK0 |= bit (PCINT1); // want pin 9
PCIFR |= bit (PCIF0); // clear any outstanding interrupts
PCICR |= bit (PCIE0); // enable pin change interrupts for D8 to D13
}

Table of pins -> pin change names / masks

pinpin change names / masks
D0PCINT16 (PCMSK2 / PCIF2 / PCIE2)
D1PCINT17 (PCMSK2 / PCIF2 / PCIE2)
D2PCINT18 (PCMSK2 / PCIF2 / PCIE2)
D3PCINT19 (PCMSK2 / PCIF2 / PCIE2)
D4PCINT20 (PCMSK2 / PCIF2 / PCIE2)
D5PCINT21 (PCMSK2 / PCIF2 / PCIE2)
D6PCINT22 (PCMSK2 / PCIF2 / PCIE2)
D7PCINT23 (PCMSK2 / PCIF2 / PCIE2)
D8PCINT0 (PCMSK0 / PCIF0 / PCIE0)
D9PCINT1 (PCMSK0 / PCIF0 / PCIE0) <===== PCINT1 代表 pin D9
D10PCINT2 (PCMSK0 / PCIF0 / PCIE0)
D11PCINT3 (PCMSK0 / PCIF0 / PCIE0)
D12PCINT4 (PCMSK0 / PCIF0 / PCIE0)
D13PCINT5 (PCMSK0 / PCIF0 / PCIE0)
A0PCINT8 (PCMSK1 / PCIF1 / PCIE1)
A1PCINT9 (PCMSK1 / PCIF1 / PCIE1)
A2PCINT10 (PCMSK1 / PCIF1 / PCIE1)
A3PCINT11 (PCMSK1 / PCIF1 / PCIE1)
A4PCINT12 (PCMSK1 / PCIF1 / PCIE1)
A5PCINT13 (PCMSK1 / PCIF1 / PCIE1)

Reference: http://gammon.com.au/interrupts

3.2 中断测试

接下来我们来测试两个使用中断处理的按钮 ! (两个按钮按下与放开都会产生中断),但也可以不必使用真的按钮, 拿一条杜邦线或是一条电导线即可测试了 , (例如把没用的网络线剥开里面有八条电导线可用)
第一个按钮接 pin2 以便使用外部中断 INT0, 因为我们设定 pinMode(2, INPUT_PULLUP); 所以按钮另一端接 GND 即可 !
另一个按钮接 pin 8, (也是用 PULLUP, 且代码已经写成可把 pinPrint 可改为 9, 或 10 都可),

3.2.1代码如下:
// 按钮接 pin 2
// 另一个测试按钮接  pin 8 (pinPrint),  或改 9,  或 10 都可,  pinPrint:
int pinPrint = 8;  // interrupt to print value of cnt; 8/9/10 OK
const byte led = 13;
const byte button = 2;  // pin2 是 INT0 外部中断
volatile int cnt = 0;  // 纪录 button  interrupt 次数
volatile int doPrint = 0;
// Interrupt Service Routine (ISR)
void btnChange ( ) {  // for INT0 to attach
  ++cnt;
  if (digitalRead (button) == HIGH) digitalWrite (led,  HIGH);
  else digitalWrite (led,  LOW);
}  // end of btnChange

void setup ()
{
  pinMode(led,  OUTPUT); 
  digitalWrite(13,  0);
  Serial.begin(9600);
  pinMode(button,  INPUT_PULLUP); //internal pull-up resistor
  setup555( pinPrint );  // set interrupt for pin 8
  attachInterrupt (0,  btnChange,  CHANGE);  //  INT0 == pin 2
  Serial.print("Started... cnt ="); 
  Serial.println(cnt);
}  // end of setup
void loop ()
{
  // loop doing nothing 
  if(doPrint) {
    doPrint = 0;
    Serial.println(String("Interrupt cnt=") +cnt+",  time=" + millis( ) );
  }// if(
  // .. .. ..
} // loop(
void setup555(int pin ) {
  pinMode(pin,  INPUT_PULLUP);  // 启动内部上拉电阻
  cli( );
  switch(pin) {
    case 8: PCMSK0 |= bit (PCINT0);  break; // the pin 8
    case 9: PCMSK0 |= bit (PCINT1);  break; // the pin 9
    case 10: PCMSK0 |= bit (PCINT2);  break; // the pin 10
  }
  PCIFR  |= bit (PCIF0);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE0);   // enable pin change interrupts for D8 to D13
  sei( );
}
void haha( ) {
  digitalWrite(led,  0 );
  doPrint = 1;   // 要求 loop ( ) 内要做一次 print
}
ISR (PCINT0_vect) {
  // one of pins D8 to D13 has changed
  haha( );
}
3.2.2 如何测试呢?

(1)把串口监视器 Serial Monitor 开启
(2)拿一根杜邦线或任意电导线, 一端接 GND,
(a)另一端轻轻触一下 pin 8 (由 pinPrint 决定, 目前写成可以 8, 或 9, 或 10, 改 pinPrint 即可 ! )
(b)改轻轻触一下 pin 2 (或按下这按钮)
你会发现按下这按钮或插入 pin 2 时 Led 13 熄灭, 拉出时或放掉按钮时 Led 13 灯亮 !
©再改回轻轻触一下 pin 8
注意串口监视器输出的答案!
发现了没, 只插入一下 pin 2 又拔掉,
结果 cnt 的值就跳好多, 理论上应该是多 2 (按下放掉各加 1),
但是实际却好像发生了很多次甚至一二十次中断 ! 这就是所谓抖动(Bouncing)的问题 !
还有, 插一下 pin 8 却印好几次, 这也是抖动的问题 !
(3)重复刚刚 (2)全部步骤, 注意 LED 13 的亮灭, 以及串口监视器输出的值 !

再来把以上范例改为方便针对监看 pin 2, 3, 4, 5, 6, 7 这六支 pin
你只要对照面范例与对照表就会改为可监看 pin A0 到 A5 了 !
以下我故意把 pinPrint 设 2, 看看会怎样 ? !
注意, pin 2 同时也是 INT0 的外部中断,
就是说以下程序码中 pinPrint 与 button 都是 2, 这样用接 GND 线轻触一下 pin 2,
势必两种中断都会产生, 那先做哪个呢?
其实这之前就说过了 , 在 25 种中断之中, INT0 的优先权是最高的 !


// 按钮接 pin 2
// 另一个测试按钮接  pin 7 (pinPrint),  或 6, 5, 4, 3, 2 都可,  pinPrint:
int pinPrint = 2;  // 可故意改为 2 测试看看
const byte led = 13;
const byte button = 2;  // pin2 是 INT0 外部中断
volatile int cnt = 0;  // 纪录 button  interrupt 次数
volatile int cnt88 = 0; // by haha
volatile int doPrint = 0;
// Interrupt Service Routine (ISR)
void btnChange ( ) {  // for INT0 to attach
  ++cnt;
  //if (digitalRead (button) == HIGH) 
  digitalWrite (led,  HIGH);
  //else digitalWrite (led,  LOW);
}  // end of btnChange

void setup ()
{
  pinMode(led,  OUTPUT); digitalWrite(13,  0);
  Serial.begin(9600);
  pinMode(button,  INPUT_PULLUP); //internal pull-up resistor
  setup555( pinPrint );  // set interrupt for pin 8
  attachInterrupt (0,  btnChange,  FALLING);  //  INT0 == pin 2
  Serial.print("Started... cnt ="); Serial.println(cnt);
}  // end of setup
void loop ()
{
  // loop doing nothing 
  if(doPrint) {
    doPrint = 0;
    Serial.println(String("Interrupt cnt=") +cnt+
    ",  cnt88=" + cnt88 +
    ",  time=" + millis( ) );
  }// if(
  // .. .. ..
} // loop(
void setup555(int pin ) {
  pinMode(pin,  INPUT_PULLUP);  // 启动内部上拉电阻
  cli( );
  switch(pin) {
    case 2: PCMSK2 |= bit (PCINT18);  break; // the pin 2
    case 3: PCMSK2 |= bit (PCINT19);  break; // the pin 3
    case 4: PCMSK2 |= bit (PCINT20);  break; // the pin 4
    case 5: PCMSK2 |= bit (PCINT21);  break; // the pin 5
    case 6: PCMSK2 |= bit (PCINT22);  break; // the pin 6
    case 7: PCMSK2 |= bit (PCINT23);  break; // the pin 7
  }
  PCIFR  |= bit (PCIF2);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE2);   // enable pin change interrupts for D8 to D13
  sei( );
}
void haha( ) {
  cnt88++;
  digitalWrite(led,  0 );
  doPrint = 1;   // 要求 loop ( ) 内要做一次 print
}
ISR (PCINT2_vect) {
  // one of pins D8 to D13 has changed
  haha( );
 }

以上这范例的测试结果也夹档在后面 !
你可以看出 INT0 的中断优先处理, 否则在 loop( )内第一次印出的 cnt 就应该是 0 才对 !
http://www.arduino.cn/forum.php? mod=attachment&aid=MTA0NDh8ZDRlZDg0NTF8MTQyNzQ4MTY3OHwzMDYwMXwxMzIwNQ%3D%3D%C2%AChumb=yes

请注意, 目前为止我们还没用到国外某大神写的 Pin Change Interrupt 的库喔 !

4. 关于 P-C-I 库 : Pin Change Interrupt Library for the Arduino

4.1 安装PinChangeInt库

要使用 Pin Change Interrupt 库, 必须先下载该 P-C-I 库来安装:
抓回 .zip 档案之后, 从你的 Arduino IDE
Sketch > Import Library… > Add Library…
然后选到该 .zip 档案, 把 PCI 库加进去你的 Arduino IDE.
(不必手动解压缩再把PinChangeInt库目录复制到 libraris 目录, 当然你要那么做也可以! )
该 P-C-I 库可到这抓:
https://github.com/GreyGnome/PinChangeInt

或直接点以下连结: (PinChangeInt-master.zip)
https://github.com/GreyGnome/PinChangeInt/archive/master.zip
这个库从以前 Version 1.0 的 4.5KB,
到现在 2.40版已经有 49KB.

如果你只是要在Arduino UNO 上用, 其实抓P-C-I 库的 1.0版的就可以了 😃
不过要注意 1.0版和 1.72版只可用旧的写法:
PCattachInterrupt(pin, yourISRFunction, FALLING); // FALLING, RISING, CHANGE
PCdetachInterrupt(pin);

(在这篇最后夹档 1.0 版, 1.72版, 以及 2.402 版本)

其实, 这个 P-C-I 库所用的方法就是类似上面我写的范例,
只是P-C-I库已经考虑所有的 pin, 但上述我写的范例只有考虑 pin 8, 9, 10 这三支脚位,
且 P-C-I 库设计成仿照外部中断INT0, INT1的使用方法,
可以用类似写法指定监看某 pin 的变化产生中断, 例如:
attachPinChangeInterrupt(pin, ggyy, FALLING);
表示只要 pin 由高电平往低电平下降(FALLING), 就产生中断跳入函数 ggyy( ) 内 !
再次提醒, 关于外部中断INT0, INT1的使用, 以及中断的概念,
还是请看看奈何大神写的这篇有趣文章:
http://www.arduino.cn/thread-2421-1-1.html

接着我们来看看使用该 P-C-I 库的简单范例,
这是我把范例拿来稍微改过,
测试 pin 8 产生中断的次数, 你可以找一个按钮连接 pin 8 (另一端接 GND),
或是拿一条杜邦线或任意电导线, 一端接 GND, 另一端轻触 pin 8
记得要把串口监视器 Serial Monitor 打开观看!


#include <inChangeInt.h>
//  以下用 pin 8,  你可以改为其他 pin
#define testPIN 8
 
volatile unsigned int interruptCount=0; // 最大到 65535,  接着会归零从头算起
 
// 尽量不要在中断处理程序内用 Serial.print()  
// 中断程序不要做太多事!  这里我们只是把中断次数 + 1
void ggyy() {   // 中断处理程序
  interruptCount++;
}
 
// Attach the interrupt in setup()
void setup() {
  pinMode(testPIN,  INPUT_PULLUP);  // 启动内部上拉电阻 the pullup resistor.
  attachPinChangeInterrupt(testPIN,  ggyy,  FALLING);
  Serial.begin(9600);
  Serial.println("start ------");
}
 
void loop() {
  delay(3388);    // Every  3.388 second, 
  Serial.print("Total: ");
  Serial.print(interruptCount,  DEC);   // print the interrupt count.
  Serial.println(" interrupt times so far.");
}

看到了, 很简单吧 ?
就是先写好中断处理函数例如 ggyy( );
然后用类似使用外部中断 INT0 和 INT1 的方法:
attachPinChangeInterrupt(testPIN, ggyy, FALLING);
表示监督 testPIN 的状态,
当 testPIN 由高电平往低电平下降(FALLING)时产生中断, 跳入函数 ggyy( );
你可以把上述例子复制并改变 testPIN 的值为其它 pin 多多测试看看 !

如果你是使用 1.0版本或 1.72版本,
则改用:
PCattachInterrupt(testPIN, ggyy, FALLING);

如何停止某 pin 的中断处理 ?
很简单, 与使用外部中断 INT0, INT1 类似:
detachPinChangeInterrupt(pin);
这样就可以了 !

如果你是使用 1.0版本或 1.72版本,
对某 pin 启动中断用:
PCattachInterrupt(pin, yourFunction, mode);
停止中断用:
PCdetachInterrupt(pin);

更复杂的用法可自己看抓回 P-C-I 库内的范例 {:soso_e100:}

参考:
http://playground.arduino.cc/Main/PinChangeInt
http://playground.arduino.cc/Main/PinChangeIntExample
http://playground.arduino.cc/Code/ReadReceiver

故意测 pin2 的 pin change interrupt 与 INT0 (也是 pin2)
已经更新加入使用 P-C-I Library 的简单范例,

关于Chris J. Kiick和Michael Schwager写的 P-C-I Library:
https://github.com/GreyGnome/PinChangeInt
Alse see:
http://playground.arduino.cc/Main/PinChangeInt
http://playground.arduino.cc/Main/PinChangeIntExample
http://playground.arduino.cc/Code/ReadReceiver

已经顺便把 ver. 1.0, ver 1.72, ver 2.402 夹档案在原贴文内.

  • 6
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
stm8si2c程序,调试通过 INTERRUPT_HANDLER(I2C_IRQHandler, 19) { /* In order to detect unexpected events during development, it is recommended to set a breakpoint on the following instruction. */ struct SCB_T *system=&system_process; unsigned char i,i2c_sr1,i2c_sr2,i2c_event,i2c__status=0,temp,sr1_analysis_int_resource[8],sr2_analysis_int_resource[8]; char i2c_interrupt_type=0,p;//在一次I2中断中,I2C中断中的中断标志位个数; disableInterrupts();//关总中断 i2c_sr1=I2C->SR1; p=I2C->SR3; i2c_sr2=I2C->SR2; //temp=I2C->SR3; //analysis interrupt resource in i2c->sr1 register sr1_analysis_int_resource[0]=i2c_sr1&I2C_SR1_SB; sr1_analysis_int_resource[1]=i2c_sr1&I2C_SR1_ADDR; sr1_analysis_int_resource[2]=i2c_sr1&I2C_SR1_BTF; sr1_analysis_int_resource[3]=i2c_sr1&I2C_SR1_ADD10; sr1_analysis_int_resource[4]=i2c_sr1&I2C_SR1_STOPF; // sr1_i2c__state[5]=i2c_state&((u8)I2C_SR1_BIT6); sr1_analysis_int_resource[6]=i2c_sr1&I2C_SR1_RXNE; sr1_analysis_int_resource[7]=i2c_sr1&I2C_SR1_TXE; //analysis interrupt resource in i2c->sr2 register sr2_analysis_int_resource[0]=i2c_sr2&I2C_SR2_BERR; sr2_analysis_int_resource[1]=i2c_sr2&I2C_SR2_ARLO; sr2_analysis_int_resource[2]=i2c_sr2&I2C_SR2_AF; sr2_analysis_int_resource[3]=i2c_sr2&I2C_SR2_OVR; sr2_analysis_int_resource[5]=i2c_sr2&I2C_SR2_WUFH; if(sr1_analysis_int_resource[0]==I2C_SR1_SB) {i2c__status=0x01;i2c_interrupt_type++;} if(sr1_analysis_int_resource[1]==I2C_SR1_ADDR) {i2c__status=0x02;i2c_interrupt_type++;} if(sr1_analysis_int_resource[2]==I2C_SR1_BTF) {i2c__status=0x03;i2c_interrupt_type++;} if(sr1_analysis_int_resource[3]==I2C_SR1_ADD10) {i2c__status=0x04;i2c_interrupt_type++;} if(sr1_analysis_int_resource[4]==I2C_SR1_STOPF) {i2c__status=0x05;i2c_interrupt_type++;} if(sr1_analysis_int_resource[6]==I2C_SR1_RXNE) {i2c__status=0x06;i2c_interrupt_type++;} if(sr1_analysis_int_resource[7]==I2C_SR1_TXE) {i2c__status=0x07;i2c_interrupt_type++;} if(sr2_analysis_int_resource[0]==I2C_SR2_BERR) {i2c__status=0x08;i2c_interrupt_type++;} if(sr2_analysis_int_resource[1]==I2C_SR2_ARLO) {i2c__status=0x09;i2c_interrupt_type++;} if(sr2_analysis_int_resource[2]==I2C_SR2_AF) {i2c__status=0x0a;i2c_interrupt_type++;} if(sr2_analysis_int_resource[3]==I2C_SR2_OVR) {i2c__status=0x0b;i2c_interrupt_type++;} if(sr2_analysis_int_resource[5]==I2C_SR2_WUFH) {i2c__status=0x0c;i2c_interrupt_type++;} if(i2c_interrupt_type>=2) /*there are more than one interrupt resource in the time*/ { if(i2c_interrupt_type==2) { if((sr1_analysis_int_resource[1]==I2C_SR1_ADDR)&&(sr1_analysis_int_resource[7]==I2C_SR1_TXE)) { I2C->DR=system->i2c.send_frame.data[system->i2c.send_frame.proc]; system->i2c.send_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x62; } else if((sr1_analysis_int_resource[7]==I2C_SR1_TXE)&&(sr1_analysis_int_resource[2]==I2C_SR1_BTF)) { system->i2c.send_frame.terminate=0; //set I2C transfer terminate bit; system->i2c.send_frame.mod=0; system->i2c.send_frame.write=0; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x64; } else if((sr1_analysis_int_resource[7]==I2C_SR1_TXE)&&(sr2_analysis_int_resource[2]==I2C_SR2_AF)) { I2C->CR2|=I2C_CR2_STOP; I2C->SR2&=(~I2C_SR2_AF);//clear AF bit; system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x64; } else { system->i2c.error=1; I2C_ITConfig(I2C_IT_EVT, DISABLE); system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x37; } } else { system->i2c.error=1; I2C_ITConfig(I2C_IT_EVT, DISABLE); system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x37; } } else { switch(i2c__status) { case I2C_SR1_SB_proc: //如果是发送模式 if(system->i2c.send_frame.mod==1)//说明本次中断之前是从模式,说明这是在从模式下发的起始位; { //EV5 p=I2C->SR1; I2C->DR=system->i2c.send_frame.add__L; //自动清除I2C_SR1_SB标志 system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x38; } else { if(system->i2c.rev_frame.mod==1) //说明本次中断之间是主模式,这次发的是重复起始位; { //EV6如果是接收模式 p=I2C->SR1; I2C->DR=system->i2c.rev_frame.add__L;//自动清除I2C_SR1_SB标志; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x51; } else { system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x36; } } break; case I2C_SR1_ADDR_proc: p=I2C->SR1; temp=I2C->SR3;//件读取SR1寄存器后,对SR3寄存器的读操作将清除该位 temp&=(u8)I2C_SR3_TRA; I2C->CR2|=(u8)I2C_CR2_ACK;// 使能应答位 I2C->CR2&=(u8)(~I2C_CR2_POS);//设置接受到当字节应答 //如果是发送模式 if(system->i2c.send_frame.mod==1) { if(temp==(u8)I2C_SR3_TRA) {; } else { system->i2c.error=1; } } else { if(system->i2c.rev_frame.mod==1) { if(temp==0)//machine at a master-receive mod { system->i2c.rev_frame.proc=0; } else { system->i2c.error=1; } } else { system->i2c.error=1; } } system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x52; break; case I2C_SR1_RXNE_proc: if(system->i2c.rev_frame.proci2c.rev_frame.num-3)) { system->i2c.rev_frame.data[system->i2c.rev_frame.proc]=I2C->DR; system->i2c.rev_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x57; } else if(system->i2c.rev_frame.proc==(u8)(system->i2c.rev_frame.num-2)) { system->i2c.rev_frame.data[system->i2c.rev_frame.proc]=I2C->DR; system->i2c.rev_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x58; I2C->CR2&=(u8)(~I2C_CR2_ACK);//不返回应答 I2C->CR2|=I2C_CR2_STOP; //发停止位结束这次数据接收; } else if(system->i2c.rev_frame.proc>=(u8)(system->i2c.rev_frame.num-1)) { system->i2c.rev_frame.data[system->i2c.rev_frame.proc]=I2C->DR; system->i2c.rev_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x59; system->i2c.rev_frame.terminate=0; //set I2C transfer terminate bit; system->i2c.rev_frame.mod=0; system->i2c.rev_frame.read=0; } else { system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0xfb; } break; case I2C_SR1_TXE_proc: if(system->i2c.send_frame.proci2c.send_frame.num-1)) { I2C->DR=system->i2c.send_frame.data[system->i2c.send_frame.proc]; system->i2c.send_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x61; } else if(system->i2c.send_frame.proc=(u8)(system->i2c.send_frame.num)) { I2C->CR2|=I2C_CR2_STOP; // 发停止位结束 这次数据接收; I2C_ITConfig(I2C_IT_BUF, DISABLE); system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x60; } else { system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0xfc; } break; case I2C_SR2_AF_proc: I2C->CR2|=I2C_CR2_STOP; I2C->SR2&=(~I2C_SR2_AF);//clear AF bit; system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x63; break; default: system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0xfd; break; } } system->i2c.int_debug_count++; enableInterrupts();//开总中断 }
当然可以!以下是您所要求的博客: 51单片机是一种非常常见的嵌入式系统芯片,它内置了多个定时器,这些定时器可以在特定的时间间隔内产生中断信号,从而实现对系统的时间管理和控制。本文将介绍51单片机中定时器中断的相关知识。 1. 定时器中断的概念 定时器中断是指当定时器计数器达到某个预设值时,会产生一个中断请求,使CPU停止当前正在执行的任务,转而去执行与定时器中断相关的中断服务程序。通过这种方式,可以在系统中实现各种定时、计数等功能,从而提高系统的实时性和可靠性。 2. 51单片机中定时器的种类 51单片机内置了4个定时器,分别为:定时器0、定时器1、定时器2和定时器3。其中,定时器0和定时器1为16位定时器,定时器2和定时器3为8位定时器。 3. 51单片机中定时器中断的实现 在51单片机中,实现定时器中断的方法非常简单。以下是一个基本的实现过程: 1)设置计数器初值:首先需要设置定时器的初始计数值,可以通过对相应的寄存器进行赋值来实现。 2)设置中断触发条件:根据需要,可以设置定时器的触发条件,例如定时器计数器达到预设值、定时器溢出等。 3)使能中断:为了使定时器中断有效,需要对相应的中断寄存器进行设置,使其能够响应定时器中断请求。 4)编写中断服务程序:当定时器中断请求被响应时,CPU会跳转到相应的中断服务程序中执行相应的操作。因此,在使用定时器中断时,需要编写相应的中断服务程序。 4. 代码示例 以下是一个使用定时器0实现1秒中断的代码示例: ```c #include <reg52.h> //头文件 unsigned int count; //定义计数器 void timer0_isr() interrupt 1 //定时器0中断服务程序 { TH0 = (65536-50000)/256; //重新设置计数器初值 TL0 = (65536-50000)%256; count++; //计数器加1 } void main() { TMOD = 0x01; //设置定时器0为模式1 TH0 = (65536-50000)/256; //设置计数器初值 TL0 = (65536-50000)%256; ET0 = 1; //使能定时器0中断 EA = 1; //使能总中断 TR0 = 1; //启动定时器0 while(1) { if(count >= 2) //等待2秒钟 { count = 0; //计数器清零 //执行相应的操作 } } } ``` 以上代码中,通过设置计数器初值、中断触发条件和中断服务程序等步骤,实现了1秒钟中断一次的功能。在主函数中,通过对计数器的判断,可以实现在1秒钟内执行相应的操作。 5. 总结 定时器中断是嵌入式系统中非常常见的功能,其可以实现对系统时间的管理和控制,从而提高系统的实时性和可靠性。在51单片机中,通过对定时器的设置和编写相应的中断服务程序,可以非常方便地实现定时器中断的功能。希望本文对大家有所帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值