7.1-I2C的中断

I2C的中断与DMA

回顾

HAL_I2C_MASTER_Transmit(&hi2c1,ADRESS,PDate,Size,Time);


HAL_I2C_MASTER_Receive(&hi2c1,ADRESS,PDate,Size,Time);

通信具体过程如下:
在这里插入图片描述

在I2C的轮询模式中

发送时:CPU将以主机0x70 发送 从机 ACK 回复 主机0xAC发送 ACK 回复 主机 0x33 从机ACK…全部发送且接收完成后,才会空闲下来,去处理其他指令,

接收时,也是类似,整个发送或者接收过程中,一直阻塞执行,占用着CPU资源。完成后才回去处理其他代码。

为了解决上述CPU阻塞情况,I2C也有中断模式与DMA模式

中断模式:

CPU发送一字节的数据0x70后,CPU就去执行其他正常任务,直到此字节数据发送成功后(从机返回ACK信号)产生中断 CPU 再来塞入下一字节的数据。

DMA模式:

整个发送或者接收的数据都交给小助手DMA去搬运,等数据接收或者发送完成过,再有DMA通过中断通知CPU前来处理

两种模式的用法与串口一样

I2C设置中断:

1.在图形界面开启NVIC Settings 中开启中断 ,本次主要用到事件中断向量。

在这里插入图片描述

下面用I2C的中断模式改造AHT20_Read()函数

HAL_I2C_Master_Transmit_IT(&hi2c1, ADDRESS, pData, Size);

参数与前面相同,不需要等待的时间

void AHT20_Read(float *Temperature, float *Humidty){
    uint8_t sendBuffer[3] = {0xAC,0x33,0x00};
    uint8_t readBuffer[6] = {0};

    HAL_I2C_Master_Transmit_IT(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
    HAL_Delay(75);
    HAL_I2C_Master_Receive_IT(&hi2c1, AHT20_ADDRESS , readBuffer, 6 , HAL_MAX_DELAY);
    
    
    if( (readBuffer[0] & 0x80 ) == 0x00) {
    	//计算
    	uint32_t data = 0;
    	data  = ((uint32_t)readBuffer[3]>>4)+((uint32_t)readBuffer[2]  << 4) + ((uint32_t)readBuffer[1]<<12);
    	*Humidty = data * 100.0f / (1<<20);

    	data = (((uint32_t)readBuffer[3]  &  0x0f )<<16 )  +   ((uint32_t)readBuffer[4]  << 8) + ((uint32_t)readBuffer[5]);
    	*Temperature = data *  200.0f/(1<<20) - 50;
	}
}

只是简单的修改并不能达到设想的效果,是因为

中断和DMA模式是非阻塞模式,他们将任务交给外设后就会接着向下执行。并不会等待数据的发送/接收完成,整个通信流程都交给外设与中断进行控制,通信完成后,再通过中断来通知我们。所以后面的对数据的处理前的Receive只是通知外设进行了读取。 但尚未读取完成,readBuffer里的数据都是我们初始化的0,解析出的数据自然就不对了

解决办法:将步骤拆解。将AHT20_Read()函数分解,分解成三个AHT20_Send()、AHT20_Get()、AHT20_Analysis().并在aht20.h中声明。


void AHT20_Send(){
	static uint8_t sendBuffer[3] = {0xAC,0x33,0x00};  //static将变量空间保留,不被回收
	HAL_I2C_Master_Transmit_IT(&hi2c1, AHT20_ADDRESS, sendBuffer, 3);
}


uint8_t readBuffer[6] = {0};


void AHT20_Get(){
	HAL_I2C_Master_Receive_IT(&hi2c1, AHT20_ADDRESS , readBuffer, 6);
}

void AHT20_Analysis(float *Temperature, float *Humidty){

	 if( (readBuffer[0] & 0x80 ) == 0x00) {
	    	//计算
	    	uint32_t data = 0;
	    	data  = ((uint32_t)readBuffer[3]>>4)+((uint32_t)readBuffer[2]  << 4) + ((uint32_t)readBuffer[1]<<12);
	    	*Humidty = data * 100.0f / (1<<20);

	    	data = (((uint32_t)readBuffer[3]  &  0x0f )<<16 )  +   ((uint32_t)readBuffer[4]  << 8) + ((uint32_t)readBuffer[5]);
	    	*Temperature = data *  200.0f/(1<<20) - 50;
		}
}

在main.c中编写状态机

  • 常用的一种编程方式:状态机编程

在这里插入图片描述

AHT20_Init();
  
  float temperature;
  float humidity;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
	  if (aht20State == 0) {
		AHT20_Send();
		aht20State == 1;
	}else if (aht20State == 1) {
		
	}else if (aht20State == 2) {
		HAL_Delay(75);
		AHT20_Get();
		aht20State = 3;
	}else if (aht20State == 3) {
		
	}else if (aht20State == 4) {
		AHT20_Analysis(&temperature, &humidity);
		sprintf(message,"温度:%.1f\t 湿度:%.1f%%\t\r",temperature,humidity);

		HAL_UART_Transmit(&huart2, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);
		HAL_Delay(1000);
		aht20State = 0;
	}
	  

对于①②的代码,需要在stm32f1xx_hal_i2c.c中寻找这两个状态。

在这里插入图片描述

在这里插入图片描述

提示文件较大,点击YES后按下图更改

在这里插入图片描述

其中

__weak void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hi2c);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_I2C_MasterTxCpltCallback could be implemented in the user file
   */
}

__weak void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hi2c);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_I2C_MasterRxCpltCallback could be implemented in the user file
   */
}

MasterTxCpltCallback是I2C数据全部发送完成时的回调函数,MasterRxCpltCallback是I2C数据全部接收完成时的回调函数,

把他们在i2c.c文件中重新实现一下 USER CODE BEGIN0对儿中:

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c){
	  //此函数会在通过Transmit_IT函数发送的命令数据全部送达到从机后,由I2C中断调用
	 
   if (hi2c==&hi2c1) {  //判断是否是i2c1引起的中断
		aht20State = 2;
	}
}

void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c){
	if (hi2c==&hi2c1) {
    aht20State = 4;
	}
}

aht20State要在i2c.c中调用因此在mian.h的USER CODE BEGIN EC中将aht20State设置为 外部函数,可在i2c.c中使用。

extern uint8_t aht20State ;

完整代码

mian.c

----
/* USER CODE BEGIN PV */
uint8_t aht20State = 0;   //0初始状态   1 正在发送测量命令,
						  //2. 测量完成等待75ms后读取AHT20数据  3.读取中
						//4.读取完成,解析并展开数据然后恢复到初始状态
/* USER CODE END PV */
----
/* USER CODE BEGIN 2 */
  AHT20_Init();

  float temperature;
  float humidity;
  char message[50];

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
	  if (aht20State == 0) {
		AHT20_Send();
		aht20State = 1;
	}else if (aht20State == 1) {

	}else if (aht20State == 2) {
		HAL_Delay(75);
		AHT20_Get();
		aht20State = 3;
	}else if (aht20State == 3) {

	}else if (aht20State == 4) {
		AHT20_Analysis(&temperature, &humidity);
		sprintf(message,"温度:%.1f\t 湿度:%.1f%%\t\r",temperature,humidity);

		HAL_UART_Transmit(&huart2, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);
		HAL_Delay(1000);
		aht20State = 0;
	}

    /* USER CODE BEGIN 3 */

mian.h中

/* USER CODE BEGIN EC */
extern uint8_t aht20State ;
/* USER CODE END EC */

aht20.h

#ifndef INC_AHT20_H_
#define INC_AHT20_H_

#include"i2c.h"


void AHT20_Init();
void AHT20_Send();
void AHT20_Get();
void AHT20_Analysis(float *Temperature, float *Humidty);

#endif /* INC_AHT20_H_ */

aht20.c

#include"aht20.h"

#define AHT20_ADDRESS 0x70

void AHT20_Init(){
    uint8_t readBuffer;
	HAL_Delay(40);

	HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS , &readBuffer, 1 , HAL_MAX_DELAY);

	if ((readBuffer  &  0x08) == 0x00) {
		uint8_t sendBuffer[3] = { 0xBE , 0x08 , 0x00 };
		HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
	}

}


void AHT20_Send(){
	static uint8_t sendBuffer[3] = {0xAC,0x33,0x00};  //static将变量空间保留,不被回收
	HAL_I2C_Master_Transmit_IT(&hi2c1, AHT20_ADDRESS, sendBuffer, 3);
}


uint8_t readBuffer[6] = {0};


void AHT20_Get(){
	HAL_I2C_Master_Receive_IT(&hi2c1, AHT20_ADDRESS , readBuffer, 6);
}

void AHT20_Analysis(float *Temperature, float *Humidty){

	 if( (readBuffer[0] & 0x80 ) == 0x00) {
	    	//计算
	    	uint32_t data = 0;
	    	data  = ((uint32_t)readBuffer[3]>>4)+((uint32_t)readBuffer[2]  << 4) + ((uint32_t)readBuffer[1]<<12);
	    	*Humidty = data * 100.0f / (1<<20);

	    	data = (((uint32_t)readBuffer[3]  &  0x0f )<<16 )  +   ((uint32_t)readBuffer[4]  << 8) + ((uint32_t)readBuffer[5]);
	    	*Temperature = data *  200.0f/(1<<20) - 50;
		}
}

i2c.c中

/* USER CODE BEGIN 0 */

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c){
	  //此函数会在通过Transmit_IT函数发送的命令数据全部送达到从机后,由I2C中断调用

   if (hi2c==&hi2c1) {  //判断是否是i2c1引起的中断
		aht20State = 2;
	}
}

void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c){
	if (hi2c==&hi2c1) {
    aht20State = 4;
	}
}
/* USER CODE END 0 */

以上就是i2c的中断模式的应用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值