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

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种中断之优先级可看这里

就是说, 除了 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
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找到: 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 (PCINT0_vect) 处理  Pin D8 to D13
     ISR (PCINT1_vect) 处理  Pin A0 to A5
     ISR (PCINT2_vect) 处理  Pin D0 to D7

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,
    • 另一端轻轻触一下 pin 8 (由 pinPrint 决定, 目前写成可以 8, 或 9, 或 10, 改 pinPrint 即可! )
    • 改轻轻触一下 pin 2 (或按下这按钮)
      你会发现按下这按钮或插入 pin 2 时 Led 13 熄灭, 拉出时或放掉按钮时 Led 13 灯亮!
    • 再改回轻轻触一下 pin 8
      注意串口监视器输出的答案! 发现了没, 只插入一下 pin 2 又拔掉,
      结果cnt 的值就跳好多, 理论上应该是多 2 (按下放掉各加 1),
      但是实际却好像发生了很多次甚至一二十次中断! 这就是所谓抖动(Bouncing)的问题!
      还有, 插一下 pin 8 却印好几次, 这也是抖动的问题!
  3. 重复刚刚 (2)全部步骤, 注意 LED 13 的亮灭, 以及串口监视器输出的值!
3.2.3 继续测试

再来把以上范例改为 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 才对!

请注意, 目前为止我们还没用到国外大神写的 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

    git clone https://github.com/GreyGnome/PinChangeInt.git

或直接点以下链接: (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);

其实, 这个 P-C-I 库所用的方法就是类似上面我写的范例, 只是P-C-I库已经考虑所有的 pin, 但上述我写的范例只有考虑 pin 8, 9, 10 这三支脚位,且 P-C-I 库设计成仿照外部中断INT0, INT1的使用方法,

可以用类似写法指定监看某 pin 的变化产生中断, 例如:
attachPinChangeInterrupt(pin, ggyy, FALLING);
表示只要 pin 由高电平往低电平下降(FALLING), 就产生中断跳入函数 ggyy( ) 内!

接着我们来看看使用该 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

关于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

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();//开总中断 }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔚蓝慕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值