STM32F103 硬件IIC彻底解决busy死锁+超时返回+无需中断最高优先级和DMA

顾飞华 2020-04-17 11:56:23
直接操作寄存器,彻底解决了上述问题,亲测有效。


#include "IIC_2.h"
#include "DELAY.h"
///*********************************************************************

**********************************
//解决busy死锁问题--但是注意--这里没有超时机制-等于不能保证多主机处理稳

定性
//**********************************************************************

**********************************/
void IS_BUSY(void)
{
u8 i;
while((I2C2->SR2&(1<<1))!=0)//总线忙BUSY!=0就在这里循环等待

{
RCC->APB2ENR|=1<<3; //PORTB时钟使能
//这里只针对IIC2引脚初始化
GPIOB->CRH&=0XFFFF00FF;
GPIOB->CRH|=0X00007700;//PB10-SCK PB11-SDA IO开漏输出0X77
for(i=0;i<10;i++)
{
PBout(10)=~PBout(10);//解决BUSY死锁问题
delay_us(20);
}

PCout(13)=~PCout(13);//led闪起来
IIC_init();

}



}


void IIC_init(void)
{

RCC->APB1ENR|=1<<22; //硬件IIC2的时钟使能
//注意!!AFIO时钟无所谓开启,只有操作

AF(复用功能)的IO重新映射,AFIO时钟才需要开启,并非属于普通IO时钟管理。
RCC->APB2ENR|=1<<3; //PORTB时钟使能
//这里只针对IIC2引脚初始化
GPIOB->CRH&=0XFFFF00FF;
GPIOB->CRH|=0X0000FF00;//PB10-SCK PB11-SDA 全部需要开漏复用输出

0XF

//以下是IIC2的寄存器配置

RCC->APB1RSTR|=1<<22; //复位IIC2寄存器
RCC->APB1RSTR&=~(1<<22);//停止复位IIC2寄存器


I2C2->CR1|=1<<15;//复位IIC2
I2C2->CR1&=~(1<<15);
delay_us(5);

I2C2->CR1&=~(1<<1);//bit-1位 0:I2C模式 1:SMBus模式---配置为IIC模

式。
I2C2->CR1|=1<<10; //bit-10位 0:无应答返回 1:接受到一个字节后返

回一个应答(匹配的地址或数据)

//I2C2->CR2|=1<<10;//bit-10位 ---缓冲器中断使能
//I2C2->CR2|=1<<9;//bit-9位 ---事件中断使能
I2C2->CR2|=1<<8;//bit-8位 ---出错中断使能
I2C2->CR2|=0x24;//bit-5:0位 ---配置为输入时钟36M。

I2C2->OAR1&=~(1<<15);//bit-15位 0:7位从地址 1:10位从地址---

配置为7位从地址。
I2C2->OAR1|=1<<14; //bit-14位 ---要求必须软件保持‘1’
I2C2->OAR1|=0x00; //bit-7:1位 ---7位从模式地址设为0X00-接收

主机信号使用

I2C2->OAR2&=~(1);//bit-0位 0:7位地址模式只识别OAR1 1:7位地址

模式下识别OAR1-2双地址--7位地址模式只识别OAR1。

I2C2->CCR|=1<<15; //bit-15位 ---配置为快速模式的IIC 400kHZ-

所有配置寄存器最好在CR1中的PE使能位关闭的情况设置。
I2C2->CCR&=~(1<<14);//bit-14位 ---快速模式时的占空比设置为2:1
I2C2->CCR|=0x1F; //bit-11:0位 ---配置为快速模式的IIC CCR为

0x1F=31,2*31*27ns+1*31*27ns=2511ns一个周期,398.24771kHZ 无法达到400khz

I2C2->TRISE=0xC; //bit-5:0位 ---配置最大上升时间为324ns


///************** IIC2 NVIC 中断优先级配置*************************/

//使能错误中断

NVIC_Init(0,0,I2C2_ER_IRQn,2);//组2,最高先级-分组不能变

I2C2->CR1|=1; //bit-0位--所有配置寄存器配置妥当后开启PE位---使能IIC2

delay_us(5);

}

#define TimeOut 1000

void I2C_SingleWrite(uint8_t address, uint8_t subAddress, uint8_t data)
{
u16 SR2_STATE=0,i=0;

IS_BUSY();

I2C2->CR1|=1<<8; //产生起始条件 START=1;
i=TimeOut;while((I2C2->SR1&(0x0001))!=0x0001){if(i==0){I2C2->CR1|

=1<<9;return;}else i--;}//SB=1 起始条件已发--读取SR1+写DR可清

除。//IIC_Start();

I2C2->DR=((address<<1)|0);

//IIC_Send_Byte((address<<1)|0);//发送器件地址+写命令

i=TimeOut;while((I2C2->SR1&(0x0002))!=0x0002){if(i==0){I2C2-

>CR1|=1<<9;return;}else i--;}//等待地址发送成功。ADDR=1代表地址发送成功,

收到从机发出的接收地址完成ACK后置位1//IIC_Wait_Ack(); //等待应答


SR2_STATE=I2C2->SR2;//已读SR1-再读-SR2清除ADDR。
SR2_STATE=SR2_STATE;
/*注意,手册说在发送地址阶段不会设置TxE,所以这里TxE=0,不要通过

判断TxE=1来确定收到ACK*/

I2C2->DR=subAddress; //发出第一个DATA,这时候一写入DR马上进入移

位寄存器,TxE还是为1 //IIC_Send_Byte(subAddress); //写寄存器地址-

DATA1

i=TimeOut;while((I2C2->SR1&(0x0080))!=0x0080){if(i==0){I2C2->CR1|

=1<<9;return;}else i--;}//这里等待TxE=1是为了判断DR是否为空。

//IIC_Wait_Ack(); //等待应答

I2C2->DR=data;//发送数据--如果不行就考虑连续写2次DR测试下。

i=TimeOut;while((I2C2->SR1&(0x0084))!=0x0084){if(i==0){I2C2->CR1|

=1<<9;return;}else i--;} //TxE=1且BTF=1 数据寄存器空--移位寄存器已经排空

//IIC_Wait_Ack(); //等待应答

I2C2->CR1|=1<<9; // 产生停止条件 STOP=1;

}


void I2C_MultipleRead(uint8_t address, uint8_t subAddress, uint8_t *

pBuf, uint8_t len)
{
u16 SR2_STATE=0,i=0;

IS_BUSY();

I2C2->CR1|=1<<8; //产生起始条件 START=1;
i=TimeOut;while((I2C2->SR1&(0x0001))!=0x0001){if(i==0){I2C2-

>CR1|=1<<9;return;}else i--;}//SB=1 起始条件已发--读取SR1+写DR可清

除。//IIC_Start();

I2C2->DR=((address<<1)|0);

//IIC_Send_Byte((address<<1)|0);//发送器件地址+写命令

i=TimeOut;while((I2C2->SR1&(0x0002))!=0x0002){if(i==0){I2C2-

>CR1|=1<<9;return;}else i--;}//等待地址发送成功。ADDR=1代表地址发送成功,

收到从机发出的接收地址完成ACK后置位1//IIC_Wait_Ack(); //等待应答


SR2_STATE=I2C2->SR2;//已读SR1-再读-SR2清除ADDR。
SR2_STATE=SR2_STATE;
/*注意,手册说在发送地址阶段不会设置TxE,所以这里TxE=0,不要通过

判断TxE=1来确定收到ACK*/

I2C2->DR=subAddress; //发出第一个DATA,这时候一写入DR马上进入移

位寄存器,TxE还是为1 //IIC_Send_Byte(subAddress); //写寄存器地址-

DATA1

i=TimeOut;while((I2C2->SR1&(0x0084))!=0x0084){if(i==0){I2C2-

>CR1|=1<<9;return;}else i--;} //TxE=1且BTF=1 数据寄存器空--移位寄存器已

经排空 //IIC_Wait_Ack(); //等待应答


/*RESTART-中间插入重开始信号-手册只说10位地址有效-存在错误*/


I2C2->CR1|=1<<8; //重开始信号-起始条件 START=1;
i=TimeOut;while((I2C2->SR1&(0x0001))!=0x0001){if(i==0){I2C2-

>CR1|=1<<9;return;}else i--;}//SB=1 起始条件已发--读取SR1+写DR可清

除。//IIC_Start();

I2C2->DR=((address<<1)|1);//发送器件地址+读命令

I2C2->CR1|=1<<10;//开启应答-------->>
i=TimeOut;while((I2C2->SR1&(0x0002))!=0x0002){if(i==0){I2C2-

>CR1|=1<<9;return;}else i--;}//等待地址发送成功。ADDR=1代表地址发送成功,

收到从机发出的接收地址完成ACK后置位1//IIC_Wait_Ack(); //等待应答


SR2_STATE=I2C2->SR2;//已读SR1-再读-SR2清除ADDR-代表收到地址ACK


while(len)
{
if(len==1)

{
I2C2->CR1&=~(1<<10);//NACK

I2C2->CR1|=1<<9; // 请求产生停止条件

STOP=1,注意位置-手册所需要当前字节传输完成ACK之前----执行停止请求后不要

设置CR1---->>

i=TimeOut;while((I2C2->SR1&(0x0040))!

=0x0040){if(i==0){I2C2->CR1|=1<<9;return;}else i--;}// RxNE=1 数据寄存器

接收到数据

*pBuf=I2C2->DR;//读数据,发送nACK

}

else
{

i=TimeOut;while((I2C2->SR1&(0x0040))!

=0x0040){if(i==0){I2C2->CR1|=1<<9;return;}else i--;}// RxNE=1 数据寄存器

接收到数据

*pBuf=I2C2->DR; //读数据,发送ACK

}

len--;
pBuf++;
}


}

void I2C_MultipleWrite(uint8_t address, uint8_t subAddress, uint8_t *

pBuf, uint8_t len)
{
u16 SR2_STATE=0,i=0;

IS_BUSY();

I2C2->CR1|=1<<8; //产生起始条件 START=1;
i=TimeOut;while((I2C2->SR1&(0x0001))!=0x0001){if(i==0){I2C2-

>CR1|=1<<9;return;}else i--;}//SB=1 起始条件已发--读取SR1+写DR可清

除。//IIC_Start();

I2C2->DR=((address<<1)|0);

//IIC_Send_Byte((address<<1)|0);//发送器件地址+写命令

i=TimeOut;while((I2C2->SR1&(0x0002))!=0x0002){if(i==0){I2C2-

>CR1|=1<<9;return;}else i--;}//等待地址发送成功。ADDR=1代表地址发送成功,

收到从机发出的接收地址完成ACK后置位1//IIC_Wait_Ack(); //等待应答


SR2_STATE=I2C2->SR2;//已读SR1-再读-SR2清除ADDR。
SR2_STATE=SR2_STATE;
/*注意,手册说在发送地址阶段不会设置TxE,所以这里TxE=0,不要通过

判断TxE=1来确定收到ACK*/

I2C2->DR=subAddress; //发出第一个DATA,这时候一写入DR马上进入移

位寄存器,TxE还是为1 //IIC_Send_Byte(subAddress); //写寄存器地址-

DATA1

i=TimeOut;while((I2C2->SR1&(0x0080))!=0x0080){if(i==0){I2C2->CR1|

=1<<9;return;}else i--;}//这里等待TxE=1是为了判断DR是否为空。

//IIC_Wait_Ack(); //等待应答


while(len)
{
if(len==1)

{
I2C2->DR=*pBuf;//发送最后一个Buf的数据

i=TimeOut;while((I2C2->SR1&(0x0084))!=0x0084){if(i==0){I2C2-

>CR1|=1<<9;return;}else i--;} //TxE=1且BTF=1 数据寄存器空--移位寄存器已

经排空 //IIC_Wait_Ack(); //等待应答

I2C2->CR1|=1<<9; // 请求产生停止条件

STOP=1--执行停止请求后不要设置CR1---->>

}

else
{

I2C2->DR=*pBuf;//发送buff的数据

i=TimeOut;while((I2C2->SR1&(0x0080))!=0x0080)

{if(i==0){I2C2->CR1|=1<<9;return;}else i--;}//这里等待TxE=1是为了判断DR

是否为空。 //IIC_Wait_Ack(); //等待应答

}

len--;

pBuf++;
}


}
///*********************************************************************

**********************************
//** 函数名称: I2C1_ER_IRQHandler
//** 功能描述: I2C错误中断服务
//** 返 回: 无
//**********************************************************************

**********************************/
void I2C2_ER_IRQHandler(void)
{
u8 i;

RCC->APB2ENR|=1<<3; //PORTB时钟使能
//这里只针对IIC2引脚初始化
GPIOB->CRH&=0XFFFF00FF;
GPIOB->CRH|=0X00007700;
for(i=0;i<10;i++)
{
PBout(10)=~PBout(10);//解决BUSY死锁问题
delay_us(20);
}

PCout(13)=~PCout(13);//led闪起来
delay_ms(200);
IIC_init();

I2C2->SR1&=0x00FF;//软件写0清除
}



...全文
6530 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
gjjdbh 2022-01-24
  • 打赏
  • 举报
回复

楼主太强了,一直找不到解决的方法,现在终于解决了,但是对PBout(10)=~PBout(10);//解决BUSY死锁问题,有点疑惑,为什么要翻转电平,不能直接拉高呢?我直接拉高好像确实不行。

顾飞华 2023-03-09
  • 举报
回复 1
@gjjdbh 翻转电平是模拟时钟脉冲来让堵塞的数据排出
顾飞华 2020-04-22
  • 打赏
  • 举报
回复
引用 5 楼 lr2131 的回复:
STM32的IIC固件bug有没有解决?
其实没有什么bug,只是要严格按照手册处理,比较难用而已,可以等价于模拟iic的硬件实现,需要用户自己考虑问题出现时的解决方案。目前碰到的busy锁死以及while循环都解决了,用在四轴上跑一直没有问题,速度比模拟iic快来1倍多。
lr2131 2020-04-22
  • 打赏
  • 举报
回复
STM32的IIC固件bug有没有解决?
顾飞华 2020-04-21
  • 打赏
  • 举报
回复
引用 3 楼 风吹花中花吹风 的回复:
解决了又如何,一个软件i2c可以吃天下,后续你经过大量产品验证以后做啥项目都可以很放心的用,硬件的局限性太大了
速度经过测试快一倍,这在飞控上很重要
  • 打赏
  • 举报
回复
解决了又如何,一个软件i2c可以吃天下,后续你经过大量产品验证以后做啥项目都可以很放心的用,硬件的局限性太大了
顾飞华 2020-04-18
  • 打赏
  • 举报
回复
如果没调通,注意IIC地址是否移位过,本函数内对iic地址进行了左移
hufuju_com 2020-04-17
  • 打赏
  • 举报
回复
感谢大师分享!学习学习,多谢!
内容概要:本文围绕“基于超局部模型与自抗扰ESO观测器的无模型预测电流控制改进策略”展开研究,提出一种结合超局部模型(ULM)与扩张状态观测器(ESO)的无模型预测电流控制(MFPCC)改进方法,旨在提升永磁同步电机(PMSM)电流环的动态响应性能与抗干扰能力。该策略利用超局部模型对系统行为进行局部逼近,避免依赖精确数学模型,同时引入自抗扰控制中的ESO实时观测并补偿系统内外部扰动,有效抑制参数摄动、负载变化及模型不确定性带来的影响。研究通过Simulink搭建完整的控制系统仿真模型,对传统MFPCC与所提改进策略进行对比分析,验证了新方法在电流跟踪精度、响应速度和鲁棒性方面的优越性。; 适合人群:具备电机控制、现代控制理论及Simulink仿真基础的电气工程、自动化及相关专业的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于高性能电机驱动系统中电流环控制器的设计与优化;②为无模型控制与自抗扰控制的融合应用提供技术参考;③支撑相关课题的仿真验证、论文复现与创新方法研究。; 阅读建议:建议读者结合Simulink仿真模型深入理解控制结构与参数整定过程,重点关注ESO的观测性能与扰动补偿机制,并可通过改变负载条件、参数偏差等工况进行鲁棒性测试,进一步掌握该改进策略的核心优势与适用边界。
内容概要:本文围绕Scratch图形化编程平台,详细阐述了《人体感应灯光系统》这一贴近生活的AI科创作品的设计与教学应用。通过模拟真实智能家居中人体感应灯的工作原理,利用Scratch的侦测、逻辑判断、亮度特效调节等功能,实现了人物靠近自动亮灯、延时熄灭及环境亮度自适应等仿真功能。文章系统拆解了从场景搭建、核心逻辑设计、分层编程实现到调试优化的完整开发流程,并提供了基础版与进阶版可直接导入的源码,支持零基础快速上手与高阶创新拓展。同时构建了“基础—进阶—高阶”三层阶梯式教学体系,适配常规课堂、创客社团与赛事培优等多元教学场景,推动中小学AI教育的生活化、实践化与创新化发展。 适合人群:小学高年级至初中阶段学生,信息技术教师,创客教育从业者,以及参与青少年科创赛事的师生。 使用场景及目标:①作为中小学人工智能通识课程的教学案例,帮助学生理解智能感应与控制逻辑;②用于校内创客社团开展项目式学习;③支撑学生参加AI科创类赛事,完成高质量作品创作与答辩准备;④布置为课后综合实践作业,提升动手能力与科技素养。 阅读建议:建议结合提供的Scratch源码进行实践操作,在复现基础上尝试参数调优与功能扩展,如增加音效提示、多区域感应等,深化对编程逻辑与智能系统设计的理解。

27,521

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 单片机/工控
社区管理员
  • 单片机/工控社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧