#更新通知:2023-09-06 STM32L151 固件庫 使用I2C 太難了,又宕機了,建議不要在固件庫版本上嘗試硬件IIC 了,一般人真用不了,直接使用軟件模擬的,或者不要使用固件庫了,用HAL 庫吧,據(jù)說HAL 庫沒這么多問題,不死心的我還是死心了,等有空再研究吧
1. STM32L151C8T6 硬件IIC 控制OLED 屏,OLED 驅(qū)動IC CH1116G, 查閱OLED 數(shù)據(jù)手冊
2. STM32 硬件IIC 初始化,用的標(biāo)準(zhǔn)庫,固件庫
// stm32l151c8t6 as master, oled control ic (CH1116G) as slave, and communicate by master iic2
void STM32L151C8T6_IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // GPIO_OType_OD, GPIO_OType_PP
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_400KHz;
GPIO_Init(GPIOB, &GPIO_InitStruct); // IIC2 SCL - PB10, SDA - PB11
GPIO_ResetBits(GPIOB, GPIO_Pin_11);
delay_xms(20);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_I2C2); // set PB10 as IIC2 SCL
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_I2C2); // set PB11 as IIC2 SDA
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = iic_clockSpeed_400Khz; // must be less than 100 Khz
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStruct.I2C_Mode = I2C_Mode_SMBusHost; // 這里很重要
I2C_InitStruct.I2C_OwnAddress1 = IIC2_NOT_USE_OWN_ADDR; // do not use own address
I2C_Init(I2C2, &I2C_InitStruct);
I2C_Cmd(I2C2, ENABLE);
I2C_AcknowledgeConfig(I2C2, ENABLE);
}
3. GPIO 引腳速率要和 I2C 速率匹配,這很重要
3.1 I2C模式,我這里選的是主機模式,選其它模式就會出問題
4. OLED 硬件I2C 發(fā)送函數(shù)封裝,給OLED發(fā)送的東西分兩種:①發(fā)的是數(shù)據(jù),②發(fā)的是命令
// master (STM32L151C8T6) send cmd instruction to oled screen control ic (CH1116G)
void OLED_SendCmd(uint8_t cmd)
{
WaitFor_IIC_ReadyToWorking();
I2C_GenerateSTART(I2C2, ENABLE); // iic start signal
IIC_SendStartSignal_CheckEvent();
I2C_Send7bitAddress(I2C2, OLED_ADDRESS, I2C_Direction_Transmitter); // send device addr and write bit
I2C_SendDeviceAddrWaitAck();
IIC_SendByteToOLED(iic_transmitType_Cmd);
IIC_Delay(IIC_TIMEOUT_COUNTER);
I2C_SendData(I2C2, cmd);
I2C_SendByteDataWaitAck();
I2C_GenerateSTOP(I2C2, ENABLE);
IIC_Delay(IIC_TIMEOUT_COUNTER);
}
5. IIC 發(fā)送數(shù)據(jù)之前先檢查I2C 是否有空,STM32 為了規(guī)避飛利浦的發(fā)明專利,把硬件I2C 搞的很復(fù)雜,導(dǎo)致固件庫會有卡死的問題,據(jù)說HAL 庫解決了這問題,本人沒用過HAL庫
static void WaitFor_IIC_ReadyToWorking(void)
{
while (I2C2->SR2 & 0x02)
{
INFO_LOG("[WaitFor_IIC_ReadyToWorking] i2c2 is busy\r\n");
}
}
6. 發(fā)送完起始信號后,要檢查起始信號事件, 不要用固件庫檢查事件函數(shù),會卡死的
static void IIC_SendStartSignal_CheckEvent(void)
{
while (!((uint16_t)(I2C2->SR1) & (uint16_t)(0x0001)))
{
printf("[IIC_SendStartSignal_CheckEvent][] I2C_SR1=0x%04x, I2C2_SR2=0x%04x\r\n", I2C2->SR1, I2C2->SR2);
}
while (!((uint16_t)(I2C2->SR2) & (uint16_t)(0x0003)) == 0x0003)
{
printf("[IIC_SendStartSignal_CheckEvent][] I2C_SR1=0x%04x, I2C2_SR2=0x%04x\r\n", I2C2->SR1, I2C2->SR2);
}
}
7. 發(fā)送完設(shè)備地址也要檢查發(fā)送設(shè)備地址這個事件,同樣不能用固件庫函數(shù)
static void I2C_SendDeviceAddrWaitAck(void)
{
while (!((uint16_t)(I2C2->SR1) & (uint16_t)(0x0082)) == 0x0082)
{
}
while (!((uint16_t)(I2C2->SR2) & (uint16_t)(0x0007)) == 0x0007)
{
}
}
8. 發(fā)送命令給OLED 屏,這里要發(fā)兩次,我也暫時沒弄明白為什么,這個地方搞死我了,好慘
static void IIC_SendByteToOLED(uint8_t mode)
{
IIC_Delay(IIC_TIMEOUT_COUNTER);
I2C_SendData(I2C2, mode); // 0x00, high 8-bits, cmd code, iic_transmitType_Cmd
I2C_SendByteDataWaitAck();
IIC_Delay(IIC_TIMEOUT_COUNTER);
I2C_SendData(I2C2, mode); // 0x00, low 8-bits, cmd code
I2C_SendByteDataWaitAck();
}
9. 發(fā)送完命令后要檢查標(biāo)志位, while 里面可以啥都不寫,空轉(zhuǎn),死等,也可以加一個超時退出,但是超時退出只能解決卡死的問題,并不能解決I2C busy 卡死導(dǎo)致發(fā)不出去數(shù)據(jù)的問題,治標(biāo)不治本,這一點被別人說的坑死了,網(wǎng)上有人說超時退出可以解決卡死問題,但僅僅只解決了卡死問題,I2C 還是沒有工作起來,我們的最終目的是讓I2C 工作起來
static void I2C_SendByteDataWaitAck(void)
{
while (!((uint16_t)(I2C2->SR1) & (uint16_t)(0x0080)) == 0x0080)
{
}
while (!((uint16_t)(I2C2->SR2) & (uint16_t)(0x0007)) == 0x0007)
{
}
}
10. OLED 初始化,不同的驅(qū)動IC 會有所區(qū)別,但是可以先用我這個套用一下試試,我是在STM32L151C8T6 芯片上還跑了FreeRTOS 實時操作系統(tǒng)的
void OLED_Init(void)
{
delay_xms(20); // oled startup slowly than stm32l151c8t6
INFO_LOG("[OLED_Init] init start\r\n");
OLED_SendCmd(0xAE); // display off
OLED_SendCmd(0x02); // set colum start address
OLED_SendCmd(0x10); // set colum end address
OLED_SendCmd(0x40); // set start line (first row)
OLED_SendCmd(0xB0); // set page address
OLED_SendCmd(0x81); // set contrast ratio
OLED_SendCmd(0xCF); // 128
OLED_SendCmd(0xA1); // set segment remapping, from right to left
OLED_SendCmd(0xA6); // forward display, normal or reverse
OLED_SendCmd(0xA8); // multiple reuse rate, multiple ratio
OLED_SendCmd(0x3F); // duty = 1 / 64
OLED_SendCmd(0xAD); // set charge pump enable
OLED_SendCmd(0x8B); // enable DC-DC
OLED_SendCmd(0x33); // set VPP = 10V
OLED_SendCmd(0xC8); // set output scan direction, COM[N - 1] to COM[0], COM scan direction
OLED_SendCmd(0xD3); // set display offset
OLED_SendCmd(0x00); // 0x00
OLED_SendCmd(0xD5); // set internal clock frequence, set osc frequency
OLED_SendCmd(0xC0);
OLED_SendCmd(0xD9); // set pre-charge period
OLED_SendCmd(0x1F); // 0x22
OLED_SendCmd(0xDA); // set COM pins, pin layout
OLED_SendCmd(0x12);
OLED_SendCmd(0xDB); // set electrical level, set VCOMH
OLED_SendCmd(0x40);
OLED_SendCmd(0xAF); // enable display, display on
INFO_LOG("[OLED_Init] init complete\r\n");
}
11. OLED 測試函數(shù)封裝,B 站博主keysking
void OLED_Test(void)
{
OLED_SendCmd(0xB0); // page 0
OLED_SendCmd(0x00); // colume 0 low 4-bits
OLED_SendCmd(0x10); // colume 0 high 8-bits
OLED_SendCmd(0x40);
OLED_SendCmd(0xAA);
}
12. 主函數(shù)調(diào)用OLED 初始化函數(shù)和測試函數(shù),先延時一會再初始化,因為STM32 比OLED的控制IC 起來的快很多,CH1116G
int main(void)
{
delay_xms(1000);
OLED_Init();
OLED_Test();
}
13. IIC_Delay 函數(shù)
#define IIC_TIMEOUT_COUNTER 0x1000 // iic transmit timeout
static void IIC_Delay(uint32_t delay_time)
{
uint32_t delayTime;
for (delayTime = 0; delayTime < delay_time; delayTime++)
{
}
}
14. 用邏分儀抓的數(shù)據(jù)看不出來細(xì)節(jié),可以用示波器抓一下波形,看細(xì)節(jié),這個波形都變形了,查看原理圖,上面接了兩個電容,把SCL 和SDA 當(dāng)成高頻信號,給我濾掉了,導(dǎo)致這波形亂七八糟的,直接把原理圖上的兩個對地電容去掉,或者更好其它容量的電容,我這里是直接去掉了
15. 接了兩個對地電容,把正常信號當(dāng)成高頻濾掉了
文章來源:http://www.zghlxwxcb.cn/news/detail-696297.html
16. 正常波形,應(yīng)該是這樣的,方波才對,正弦波是有問題的,這里還有一個小臺階,待處理
文章來源地址http://www.zghlxwxcb.cn/news/detail-696297.html
到了這里,關(guān)于STM32 硬件IIC 控制OLED I2C卡死問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!