大家在使用MCU IIC通信時(shí),若碰到設(shè)備復(fù)位或者總線干擾等情況,可能會(huì)導(dǎo)致IIC總線卡死,表現(xiàn)上總線上SDA或者SCL其中一根線為低電平,IIC總線一直處于busy狀態(tài)。此時(shí)若代碼上一直等待總線空閑,則可能導(dǎo)致軟件死機(jī),為解決該問題,本視頻提供了軟件配置釋放IIC總線的方法。
首先為大家介紹發(fā)生IIC總線卡死的兩種情況:(1)主機(jī)在發(fā)送 START 信號(hào)后, 控制 SCL 產(chǎn)生 8 個(gè)時(shí)鐘脈沖,然后拉低 SCL 信號(hào)為低電平,在這個(gè)時(shí)候,從設(shè)備輸出應(yīng)答信號(hào),將 SDA 信號(hào)拉為低電平。如果這個(gè)時(shí)候主機(jī)異常復(fù)位, SCL 就會(huì)被釋放為高電平。此時(shí),如果從機(jī)沒有復(fù)位,就會(huì)繼續(xù) I2C 的應(yīng)答,將 SDA一直拉為低電平,直到 SCL 變?yōu)榈碗娖?,才?huì)結(jié)束應(yīng)答信號(hào)。 而由于 I2C 主機(jī)復(fù)位后檢測總線的狀態(tài),如果 SDA 信號(hào)為低電平,則 I2C 總線被占用,會(huì)一直等待 SCL 和 SDA信號(hào)變?yōu)楦唠娖剑虼?,?I2C 主機(jī)等待從機(jī)釋放 SDA 信號(hào)時(shí), I2C 從機(jī)又在等待主機(jī)將 SCL 信號(hào)拉低以釋放應(yīng)答信號(hào),兩者相互等待, I2C 總線進(jìn)入死鎖狀態(tài) ;(2)主機(jī)在發(fā)送 START 信號(hào)后, 控制 SCL 產(chǎn)生 8 個(gè)時(shí)鐘脈沖,然后拉低 SCL 信號(hào)為低電平,在這個(gè)時(shí)候,從設(shè)備輸出應(yīng)答信號(hào),將 SDA 信號(hào)拉為低電平。如果這個(gè)時(shí)候主機(jī)異常復(fù)位, SCL 就會(huì)被釋放為高電平。此時(shí),如果從機(jī)沒有復(fù)位,就會(huì)繼續(xù) I2C 的應(yīng)答,將 SDA一直拉為低電平,直到 SCL 變?yōu)榈碗娖?,才?huì)結(jié)束應(yīng)答信號(hào)。 而由于 I2C 主機(jī)復(fù)位后檢測總線的狀態(tài),如果 SDA 信號(hào)為低電平,則 I2C 總線被占用,會(huì)一直等待 SCL 和 SDA信號(hào)變?yōu)楦唠娖?。因此,?I2C 主機(jī)等待從機(jī)釋放 SDA 信號(hào)時(shí), I2C 從機(jī)又在等待主機(jī)將 SCL 信號(hào)拉低以釋放應(yīng)答信號(hào),兩者相互等待, I2C 總線進(jìn)入死鎖狀態(tài) 。
以下為兩種復(fù)位IIC總線卡死的軟件方法,大家可以嘗試使用:
(1)將SDA和SCL配置為推挽輸出,強(qiáng)制輸出stop信號(hào)
在 I2C 主機(jī)復(fù)位后,主機(jī)檢測 I2C 總線一直為 BUSY 狀態(tài),且超過設(shè)定的時(shí)間,則總線被鎖死。可通過將 I2C 的 SCL 和 SDA 引腳初始化成普通 GPIO 功能,配置成推挽輸出。 先拉高SCL 信號(hào),在拉高 SDA 信號(hào),模擬產(chǎn)生一個(gè) STOP 信號(hào),然后再配置為 I2C 的引腳復(fù)用功能。配置代碼如下所示。
/*! \brief reset i2c bus \param[in] none \param[out] none \retval none */ void i2c_bus_reset() { GPIO_BC(GPIOB) |= GPIO_PIN_6 | GPIO_PIN_7; gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6|GPIO_PIN_7); __nop(); __nop(); __nop(); __nop(); __nop(); GPIO_BOP(GPIOB) |= GPIO_PIN_6; __nop(); __nop(); __nop(); __nop(); __nop(); GPIO_BOP(GPIOB) |= GPIO_PIN_7; gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7); } /*! \brief check the I2C is or not busy \param[in] none \param[out] none \retval none */ void check_bus_status(void) { while(i2c_flag_get(I2C0,I2C_FLAG_I2CBSY)) { if(--time_out == 0){ i2c_bus_reset(); } } }
(2)將SCL配置為推挽輸出,強(qiáng)制輸出9個(gè)clk
在 I2C 主機(jī)中增加 I2C 總線恢復(fù)程序。每次 I2C 主設(shè)備復(fù)位后,如果檢測到 SDA 數(shù)據(jù)線被拉低,則控制 I2C 中的 SCL 時(shí)鐘線產(chǎn)生 9 個(gè)時(shí)鐘脈沖(針對(duì) 8 位數(shù)據(jù)的情況),這樣 I2C 從設(shè)備就可以完成被掛起的操作,從死鎖狀態(tài)中恢復(fù)過來。
I2C 主機(jī)通過將 SCL 引腳初始化為普通 GPIO 功能,配置成推挽輸出。保證連續(xù)發(fā)送 9 個(gè)時(shí)鐘脈沖,為保證后續(xù) I2C 正常通信,先將 I2C 模塊復(fù)位,再置位,最后再配置為 I2C 的引腳復(fù)用功能。配置代碼如下所示。
/*! \brief reset i2c bus \param[in] none \param[out] none \retval none */ void i2c_bus_reset() { uint8_t I = 0; gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6); /* SCL output clock signal */ for(I = 0; I < 10; i++){ gpio_bit_reset(GPIOB, GPIO_PIN_6); delay_1us(2); gpio_bit_set(GPIOB, GPIO_PIN_6); delay_1us(2); } /* reset I2C */ i2c_software_reset_config(I2C0, I2C_SRESET_RESET); i2c_software_reset_config(I2C0, I2C_SRESET_SET); gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7); } /*! \brief check the I2C is or not busy \param[in] none \param[out] none \retval none */ void check_bus_status(void) { while(i2c_flag_get(I2C0,I2C_FLAG_I2CBSY)) { if(--time_out == 0){ i2c_bus_reset(); } } }
如有其他問題或建議,歡迎評(píng)論區(qū)討論。
-
單片機(jī)
+關(guān)注
關(guān)注
6037文章
44558瀏覽量
635213 -
mcu
+關(guān)注
關(guān)注
146文章
17148瀏覽量
351182 -
脈沖
+關(guān)注
關(guān)注
20文章
889瀏覽量
95639 -
IIC
+關(guān)注
關(guān)注
11文章
300瀏覽量
38333
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論