STM32 硬件I2C中断实现

发布者:勾剑寒最新更新时间:2018-11-26 来源: eefocus关键字:STM32  硬件I2C  中断实现 手机看文章 扫描二维码
随时随地手机看文章

目录


1、初始化I2C


2、启动传输


3、发送数据流程


4、接收数据


5、Code


6、注意:


对于有嵌入式开发经历的人来说,I2C是使用场合较多的一种通讯方式,比如MPU6050、AT24C02、LRC9663等等都有I2C通讯接口。我们常用的都是用IO去模拟,然而I2C通讯的速度并不高,一般模拟CLK周期为6us左右,


如果发送一个字节给某个地址 


1个起始位+8(地址)+1应答+寄存1 应答 +数据 8 器地址 8++1应答 +1结束= 29个CLK   


    29个CLK*6us= 174us   


   针对通常用的400kbps的传输速率  29个CLK * 2.5us = 72us 


一般传输过程不允许被中断打断,所以会影响系统实时性。


以下内容是在STM32F411上实现的,和MPU9250已移植验证通过。搞了一天时间,稳定性还没怎么测,欢迎留言评论。


转入重点,I2C硬件中断实现过程如下


1、初始化I2C


初始化好相关GPIO,I2C,RCC,NVIC模块,


2、启动传输


准备好发送的器件地址,寄存器地址,数据


发送起始信号,开启中断,启动发送


I2C_ITConfig(i2cPort, I2C_IT_BUF, DISABLE);


I2C_ITConfig(i2cPort, I2C_IT_EVT, ENABLE);


i2cPort->CR1 = (I2C_CR1_START | I2C_CR1_PE);//


接下来就是中断的事了


3、发送数据流程


起始信号完成中断 I2C_SR1_SB


发送器件地址(不设置应答)


器件地址传输完成中断 I2C_SR1_ADDR


    发送寄存器地址,使能发送完成中断I2C_IT_BUF


字节发送完成中断


    发送数据


    判断有无等待发送的数据,没有则关闭中断,发送停止位,发送结束


如果中途检测到应答失败,会进入异常中断I2C1_ER_IRQHandler


4、接收数据


触发起始信号完成中断 I2C_SR1_SB


发送器件地址(不设置应答)


器件地址传输完成中断 I2C_SR1_ADDR


    发送寄存器地址,使能发送完成中断I2C_IT_BUF


字节发送完成中断


    再次发送起始位


起始信号发送完中断


    发送器件读地址,开启自动应答使能


器件地址传输完成中断


   判断待发送的数据长度,如果为空,关闭应答使能


字节接受完成中断


继续读取数据,判断待读取目标数据长度,


如果只剩下一个字节,取消应答使能


如果为无待接收数据,关闭中断,发送停止位,接受结束。


5、Code

 

typedef enum


{


i2cWrite,


i2cRead


} I2cDirection; 

 

typedef enum


{


I2C_STATUS_FREE = 0,


I2C_STATUS_START,


I2C_STATUS_ADDRESS,


I2C_STATUS_START_R,//读数据时第二次发送起始信号


I2C_STATUS_REDATA,


I2C_STATUS_FINSIH,//操作完成


I2C_STATUS_ERR,//错误

 

}I2C_STATUS;

 

typedef struct _I2cMessage


{


uint32_t         Length;


uint32_t Index;


uint8_t          slaveAddress;


I2cDirection     direction;    


I2C_STATUS        status;         


uint16_t         regAddress;   


uint8_t          *buffer;        


} I2cMessage;

  

static I2cMessage Message;

 

void i2cdrvInitBus()


{


I2C_InitTypeDef  I2C_InitStructure;


NVIC_InitTypeDef NVIC_InitStructure;


GPIO_InitTypeDef GPIO_InitStructure;

  

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);


RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

 

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);


GPIO_StructInit(&GPIO_InitStructure);


GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;


GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;


GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; // SCL


GPIO_Init(GPIOB, &GPIO_InitStructure);


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // SDA


GPIO_Init(GPIOB, &GPIO_InitStructure);

  

GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1);


GPIO_PinAFConfig(GPIOB, GPIO_Pin_9Source, GPIO_AF_I2C1);

 

I2C_DeInit(I2C1);


I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;


I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;


I2C_InitStructure.I2C_OwnAddress1 = 0x30;;


I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;


I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;


I2C_InitStructure.I2C_ClockSpeed = 400000;


I2C_Init(I2C1, &I2C_InitStructure);

 

I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);

 

NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;


NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;


NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;


NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;


NVIC_Init(&NVIC_InitStructure);


NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;


NVIC_Init(&NVIC_InitStructure);

 

GPIO_SetBits(GPIOB, GPIO_Pin_8);


GPIO_SetBits(GPIOB, GPIO_Pin_9);

 

}

 

//读取数据


bool i2cdevRead(uint8_t devAddr, uint8_t ReadAddr, uint8_t DatasLen, uint8_t *DataRead)


{


int waitTime;


Message.status = I2C_STATUS_START;


Message.slaveAddress = devAddr;


Message.Index = 0;

 

Message.buffer = DataRead;

 

Message.Length = DatasLen;


Message.regAddress = ReadAddr;//寄存器地址


Message.direction = i2cRead;

 

I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);


I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);


I2C1->CR1 = (I2C_CR1_START | I2C_CR1_PE);//1 触发起始中断


waitTime = clock();


while (1)//等待接收完成


{


if (Message.status == I2C_STATUS_FINSIH)


{


return true;


}


if (Message.status == I2C_STATUS_ERR)


{


return false;


}


if (clock() - waitTime > 10)


{


I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);


return false;


}


}


}

 

 

//发送一个字节


bool i2cdevWriteByte(uint8_t devAddr, uint8_t WriteAddr, uint8_t DataToWrite)


{


int waitTime;


Message.status = I2C_STATUS_START;


Message.slaveAddress = devAddr;


Message.Index = 0;

 

Message.buffer = &DataToWrite;

 

Message.Length = 1;


Message.regAddress = WriteAddr;//寄存器地址


Message.direction = i2cWrite;

 

I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);


I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);


I2C1->CR1 = (I2C_CR1_START | I2C_CR1_PE);//1 触发了起始中断


waitTime = clock();


while (1)//等待发送完成


{


if (Message.status == I2C_STATUS_FINSIH)


{


return true;


}


if (Message.status == I2C_STATUS_ERR)


{


return false;


}


if (clock() - waitTime > 10)


{


I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);


return false;


}


}


}

 

void  I2C1_ER_IRQHandler(void)


{


if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF))


{


I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);

 

I2C_ClearFlag(I2C1, I2C_FLAG_AF);

 

Message.status = I2C_STATUS_ERR;


}


if (I2C_GetFlagStatus(I2C1, I2C_FLAG_BERR))


{


I2C_ClearFlag(I2C1, I2C_FLAG_BERR);


}


if (I2C_GetFlagStatus(I2C1, I2C_FLAG_OVR))


{


I2C_ClearFlag(I2C1, I2C_FLAG_OVR);


}


if (I2C_GetFlagStatus(I2C1, I2C_FLAG_ARLO))


{


I2C_ClearFlag(I2C1, I2C_FLAG_ARLO);


}


}

  

void I2C1_EV_IRQHandler()


{


uint16_t SR1;


uint16_t SR2;

 

SR1 = I2C1->SR1;

 

// Start bit event


if (SR1 & I2C_SR1_SB)


{


if (Message.status == I2C_STATUS_START)


{


I2C_Send7bitAddress(I2C1, Message.slaveAddress << 1, I2C_Direction_Transmitter);//2


Message.status = I2C_STATUS_ADDRESS;


}


else if (Message.status == I2C_STATUS_START_R)


{


I2C_AcknowledgeConfig(I2C1, ENABLE);//5 转为读数据


I2C_Send7bitAddress(I2C1, Message.slaveAddress << 1, I2C_Direction_Receiver);


Message.status = I2C_STATUS_REDATA;


}


}


else if (SR1 & I2C_SR1_ADDR)//地址传输完成


{


SR2 = I2C1->SR2;

 

if (Message.status == I2C_STATUS_ADDRESS)//发送寄存器地址


{


I2C_SendData(I2C1, Message.regAddress);//4 发送地址


}


if (Message.Length == 1)//只接受一个字节


{


if (Message.status == I2C_STATUS_REDATA)


{


I2C_AcknowledgeConfig(I2C1, DISABLE);


}


}

 

I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);    


}


else if (SR1 & I2C_SR1_BTF)


{


//4 上次传输完成

 

if (SR1&I2C_SR1_TXE)//发送完成


{


if (Message.direction == i2cRead)//读数据


{


if (Message.status == I2C_STATUS_ADDRESS)//寄存器发送完


{


I2C1->CR1 = (I2C_CR1_START | I2C_CR1_PE);//


Message.status = I2C_STATUS_START_R;//启动读数据


}


}


else//写数据


{


I2C_SendData(I2C1, Message.buffer[Message.Index++]);


if (Message.Index == Message.Length)


{


I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);//关闭中断


I2C1->CR1 = (I2C_CR1_STOP | I2C_CR1_PE);//后续测试是否添加


Message.status = I2C_STATUS_FINSIH;


}


}


}


}


if (SR1 & I2C_SR1_RXNE) 


{

 

Message.buffer[Message.Index++] = I2C_ReceiveData(I2C1);

 

if (Message.Index == Message.Length - 1)


{


I2C_AcknowledgeConfig(I2C1, DISABLE);//末尾数据禁止应答


}

 

if (Message.Index == Message.Length)


{


I2C1->CR1 = (I2C_CR1_STOP | I2C_CR1_PE);//后续测试是否添加


I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);//接受结束


Message.status = I2C_STATUS_FINSIH;


}


}


else if (SR1 & I2C_SR1_TXE)


{


}


}


6、注意:


在整个读写操作中,发送结束位时作为数据收发成功信号,但此时停止位还没有开始发送。所以如果连续进行读写操作时请一点间隔时间,适当加点延时 ,延时5us左右即可。防止上一个的结束位和下一个的起始位间隔果断造成数据错误。

关键字:STM32  硬件I2C  中断实现 引用地址:STM32 硬件I2C中断实现

上一篇:STM32硬件IIC与51模拟IIC通信
下一篇:STM32Cube MX 下IIC的配置与使用

推荐阅读最新更新时间:2024-03-16 16:18

STM32开发 -- 开发环境搭建
一、Keil下载安装 首先需要下载安装软件Keil。 参看:Keil uVision4使用总结 参看:keil专栏 需要注意的是,GD32F105为ARM® Cortex™ -M3 32-bit MCU,所以需要下载Keil MDK。其最新版本为Keil MDK-ARM 5.24 uVision5开发工具。 再有解决:轻松解决keil4跟keil5在同一个系统下不能共存的问题 打开注册表: win+R 进入运行,输入 regedit 点开HKEY_CLASSER_ROOT选项,找到UVPROJFILE(KEIL4工程文件类型)和UVPROJXFILE(KEIL5工程文件类型) keil 4 keil 5 二、安
[单片机]
<font color='red'>STM32</font>开发 -- 开发环境搭建
STM32 USB NAND FLASH 模拟U盘
这次是做一个SD卡的USB读卡器的功能,我们就在上次NAND Flash模拟出的U盘的工程上修改了,这样的话只要修改一小部分了。 工程的绝大部分不需要修改,只要将fsmc_nand.c文件移除工程,添加上官方的关于SDIO的SD卡的驱动代码文件stm32_eval_sdio_sd.c,我讨厌出现eval,所以将该文件直接改为sdio_sdcard.c。同时,既然我们使用SDIO来驱动SD卡,所以在外设库文件组里要添加stm32_sdio.c文件,否则会出现很多未定错误。 我们接下去修改的就只有mass_mal.c这个文件了。更NAND Flash工程一样,我们改成简洁点: uint32_t Mass_Memory_Size ;
[单片机]
<font color='red'>STM32</font> USB NAND FLASH 模拟U盘
工作问题笔记-----STM32休眠问题
STM32低功耗分三种: SLEEP: 电压调节器开启,Cortex-M3内核停止运行,外设保持运行态; STOP: 电压调节器可选择性开启,所有外设时钟、PLL、HSI和HSE被关闭,Cortex-M3内核和所有外设停止运行,保留SRAM和寄存器的内容; STANDBY: 待机模式Standby:电压调节器关闭、整个1.8v区域断电。除了备份区域和待机电路的寄存器以外,SRAM和寄存器的内容全部丢失。 在休眠状态下,MCU的代码并不会继续执行,而是进入休眠状态,等待唤醒,唤醒的过程会先执行IRQ中断服务函数,然后再执行WFI后的函数。 省电级别由低到高,standby休眠后会让我们的数据丢失,而SLEE
[单片机]
STM32学习笔记之RCC
时钟 三种不同的时钟源可被用来驱动系统时钟(SYSCLK): ● HSI振荡器时钟 ● HSE振荡器时钟 ● PLL时钟 这些设备有以下2种二级时钟源: ● 40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。RTC用于从停机/待机模式下自动唤醒系统。 ● 32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。 当不被使用时,任一个时钟源都可被独立地启动或关闭,由此优化系统功耗。 1.当HSI被用于作为PLL时钟的输入时,系统时钟能得到的最大频率是64MHz。 2.对于内部和外部时钟源的特性,请参考相应产品数据手册中 电气特性 章节。 用户可通过多个预分频器配置AHB、高
[单片机]
STM32开发笔记85: SX1268驱动程序设计(芯片唤醒)
单片机型号:STM32L053R8T6 本系列开发日志,将详述SX1268驱动程序的整个设计过程,本篇介绍芯片唤醒驱动程序。 一、RxDutyCycle模式 在讲述本篇内容之前,我们先来看一下SX1268的一种模式RxDutyCycle,译为中文为接收占空比模式。其可使用SetRxDutyCycle命令进入RxDutyCycle模式,我们来看一下该命令的详细解释。 该命令具有2个参数,从字面的意思可以看出,1个是指接收周期时间,另1个是指睡眠周期时间。我们可分析出,该命令是在RX模式和SLEEP模式之间自动转换的一种模式,其目的是为了节省芯片功耗。如果在使能外部中断后,则单片机可以进入睡眠态,SX1268大部分时间也是
[单片机]
<font color='red'>STM32</font>开发笔记85: SX1268驱动程序设计(芯片唤醒)
STM32-自学笔记(13.NVIC和外部中断
1.NVIC,嵌套中断向量控制器。(通俗点理解就是,许多中断向量交织在一起,形成一个向量网) 和SysTick定时器一样,NVIC属于ARM Cortex-M3内核的内部设备之一,与基于此内核的控制器并无直接联系,就是说任何一款基于ARM Cortex-M3内核的微控制器都带有NVIC. 作用:用来管理中断嵌套的,主要在于优先级的管理。嵌套是什莫?,先回忆一下中断的几个概念。 中断响应:当某个中断来临,会将相应的中断标志位置位。当CPU查询到这个置位的标志位时,将响应此中断,并执行相应的中断服务函数。 中断优先级:每个中断都具有其优先级,其相互之间的优先关系一般以优先级编号较小者拥有较高优先级。而大家容易忽略的是,优
[单片机]
STM32-自学笔记(13.NVIC和外部<font color='red'>中断</font>)
基于STM32系列单片机的数控正弦波逆变电源设计与实现
逆变电源应用广泛,特别是精密仪器对逆变电源的性能要求更高。高性能逆变电源不仅要求工作稳定、逆变效率高、输出波形特性好、保护功能齐全,还要求逆变电源小型化、智能化、并且具备可扩展性。文中提出一种基于STM32系列单片机STM32F103VE的纯数字式正弦逆变电源,该电源的全部功能由单片机控制实现,具有输出电压、频率稳定,效率高,保护功能齐全的特点。 1 系统设计 系统的整体框架如图1所示。系统采用高频逆变方案,即前级升压加后级逆变的结构,这样可以避免使用笨重的工频变压器,有效的降低了电源的体积、重量及成本,提升电源的效率。电路的工作原理是,12 V的直流输入电压经过滤波后由推挽升压和全桥整流升压到350 V的直流母线电压,再
[单片机]
基于<font color='red'>STM32</font>系列单片机的数控正弦波逆变电源设计与<font color='red'>实现</font>
STM32外部中断及定时器编程示例
/*======================================================================================== *名 称: main.c *功 能: *入口 参数: *说 明: *范 例: *编者 时 间: *========================================================================================*/ #include stm32f10x.h #include 12864.h ErrorStatus HSEStartUp
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved