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的中断模式的应用