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

顾飞华 顾飞华 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区
直接操作寄存器,彻底解决了上述问题,亲测有效。


#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清除
}



...全文

相关推荐

技术转移机构如何基于数据提升项目撮合精准度?.docx

技术转移机构如何基于数据提升项目撮合精准度?

BIZKEEN

超扭滑模与快速终端滑模的永磁同步电机无感控制:理论、离散化实现与仿真研究(Simulink仿真实现)

内容概要:本文系统研究了超扭滑模控制(STSMC)与快速终端滑模控制(FTSMC)在永磁同步电机(PMSM)无感控制中的应用,重点涵盖其理论基础、离散化实现方法及Simulink仿真验证。文章深入阐述了两种高阶滑模控制策略的设计原理,利用磁链无感观测技术实现转子位置与速度的精确估计,从而省去物理传感器,有效提升了系统的可靠性与鲁棒性。同时,提供了完整的Simulink仿真模型构建方案,包括控制器设计、关键参数整定及动态响应性能分析,充分验证了所提出的方法在零速与低速工况下的卓越控制性能和有效性。; 适合人群:具备电机控制、现代控制理论基础知识及熟练掌握MATLAB/Simulink仿真技能的研究生、科研人员与工程技术人员。; 使用场景及目标:①深入理解高阶滑模控制在电机驱动系统中的设计思想与具体实现;②掌握基于滑模观测器的无感控制核心技术;③开展永磁同步电机高性能控制算法的理论研究、仿真验证与工程化探索。; 阅读建议:建议将文中详尽的理论推导与Simulink仿真模型相结合进行学习,重点关注滑模面的构建、抖振抑制策略的实施以及离散化处理的细节,有条件的研究者可进一步将算法移植到DSP等硬件平台进行实验验证,以深化对理论的理解并检验其实际应用效果。

长安程序猿

多源动态最优潮流的分布鲁棒优化方法(IEEE118节点)(Matlab代码实现)

内容概要:本文系统阐述了基于Matlab的多源动态最优潮流分布鲁棒优化方法,聚焦于IEEE118节点电力系统,旨在应对高比例可再生能源接入背景下源荷双重不确定性所带来的调度挑战。该方法深度融合分布鲁棒优化理论,通过构建能够抵御最坏情况概率分布的决策模型,有效提升了电力系统在复杂不确定性环境下的运行安全性、经济性与可靠性。研究不仅提供了完整的Matlab代码实现,便于结果复现与算法验证,还特别关注了含风电等新能源的现代电网低碳调度问题,为解决实际工程中的优化调度难题提供了强有力的理论支撑和技术工具。; 适合人群:具备电力系统分析基础和Matlab编程能力的研究生、科研人员及从事智能电网优化运行、能源系统规划等相关领域的工程技术人员。; 使用场景及目标:①研究含高比例可再生能源接入的电力系统动态最优潮流问题;②掌握分布鲁棒优化在电力系统经济调度、低碳调度中的建模思想与求解方法;③利用IEEE118标准测试系统进行算法有效性验证与性能对比分析;④基于所提供的Matlab代码进行二次开发,探索新型不确定性优化理论在电力系统中的创新应用。; 阅读建议:建议读者结合电力系统最优潮流、不确定性优化理论(特别是分布鲁棒优化)的背景知识进行学习,重点关注模型的构建逻辑、目标函数与约束条件的设计原理,以及Matlab代码的具体实现细节。在学习过程中,应动手调试代码,尝试修改系统参数(如新能源出力波动范围、负荷水平等),以观察不同场景下优化结果的变化,从而深刻理解该方法的适应性、优势及其潜在的局限性。

天天程序猿

车间调度基于GA、PSO、SA、ACO、TS优化算法的车间调度比较研究(Matlab代码实现)

内容概要:本文系统性地比较了遗传算法(GA)、粒子群优化(PSO)、模拟退火(SA)、蚁群优化(ACO)和禁忌搜索(TS)五种智能优化算法在车间调度问题中的性能表现,并提供了基于Matlab的完整代码实现。研究聚焦于调度优化的核心目标——最小化完工时间(makespan),通过标准测试案例对各算法的收敛速度、求解精度和稳定性进行了深入对比分析,旨在为实际生产调度中算法的选择与改进提供科学依据。文中不仅详述了每种算法的设计思路与关键参数设置,还展示了其实现逻辑,便于读者理解、复现与拓展。; 适合人群:从事智能制造、工业工程、运筹优化等相关领域的研究人员,以及具备Matlab编程基础、希望将智能优化算法应用于实际调度问题的研究生和工程师。; 使用场景及目标:① 掌握主流智能优化算法在解决NP-hard车间调度问题上的具体应用与差异;② 学习如何利用Matlab实现并对比不同优化算法的性能;③ 为解决生产制造、物流配送等领域中的资源调度难题提供算法选型和技术参考。; 阅读建议:建议读者在阅读时重点关注各算法的Matlab实现代码和实验结果对比部分,动手运行代码以直观感受不同算法的寻优过程和效果差异。同时,可结合具体调度约束条件,尝试调整算法参数或融合多种算法思想,以深化对优化机理的理解。

天天程序猿

3D人物伍子胥智能对话及反应动作报告

3D人物伍子胥智能对话及反应动作报告

atye115
评论 8
收藏 已收藏
分享
返回顶部
顾飞华
单片机/工控 27,522 成员 28,796 内容