直接操作暫存器,徹底解決了上述問題,親測有效。
#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清除
}
uj5u.com熱心網友回復:
感謝大師分享!學習學習,多謝!
uj5u.com熱心網友回復:
如果沒調通,注意IIC地址是否移位過,本函式內對iic地址進行了左移uj5u.com熱心網友回復:
解決了又如何,一個軟體i2c可以吃天下,后續你經過大量產品驗證以后做啥專案都可以很放心的用,硬體的局限性太大了uj5u.com熱心網友回復:
速度經過測驗快一倍,這在飛控上很重要uj5u.com熱心網友回復:
STM32的IIC韌體bug有沒有解決?uj5u.com熱心網友回復:
其實沒有什么bug,只是要嚴格按照手冊處理,比較難用而已,可以等價于模擬iic的硬體實作,需要用戶自己考慮問題出現時的解決方案。目前碰到的busy鎖死以及while回圈都解決了,用在四軸上跑一直沒有問題,速度比模擬iic快來1倍多。轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/39122.html
標籤:單片機/工控
下一篇:單片機匯編語言
