|
NUCLEO-L476RG到手一段時間了,今天來測試一下硬件I2C的操作,聽說很多BUG,試一試看如何。
準備用I2C來訪問AT24C02 EEPROM,將數(shù)據(jù)寫入到EEPROM中,然后再讀取出來,驗證讀寫操作的正確性。
硬件平臺如下
NUCLEO-L476RG
162307bzoojnw6fflnouob.png (1.05 MB, 下載次數(shù): 108)
下載附件
2016-6-17 16:19 上傳
前段時間FSL的KL02Z套裝,上面的MINIDOCK擴展板上恰好有AT24C02,所以借過來用一下,F(xiàn)SL不要生氣啊
162303id22yx1yxz1d2qyi.png (928.91 KB, 下載次數(shù): 74)
下載附件
2016-6-17 16:19 上傳
標準的ARDUINO接口,放到哪都行。合體照。
162310jkwumomm5oohmoat.png (881.95 KB, 下載次數(shù): 78)
下載附件
2016-6-17 16:19 上傳
硬件連接及原理圖
MINI DOCI上的AT24C02連接
163132q6vdfvdd3shh9srl.png (25.33 KB, 下載次數(shù): 94)
下載附件
2016-6-17 16:19 上傳
ARDUINO接口:SCL及SDA
163133i9ppcq5qqr7gbp9s.png (50.33 KB, 下載次數(shù): 72)
下載附件
2016-6-17 16:19 上傳
NUCLEO-L476RG上對應的接口
163132xxs5hsv4g0uv9vn5.jpg (163.34 KB, 下載次數(shù): 82)
下載附件
2016-6-17 16:19 上傳
寫程序之前,要了解硬件的基本特性,首先是STM32L476RG上I2C接口支持的特性;其次是AT24C02的特性。
STM32L476RG的I2C功能框圖如下
163800kzr7rxy1huwrmxkq.png (95.05 KB, 下載次數(shù): 81)
下載附件
2016-6-17 16:19 上傳
值得一提的是,L4的I2C有自己獨立的內(nèi)核時鐘,可以在SYSCLK、PCLK及HSI中任選一種做為I2C的內(nèi)核時鐘。另外L4的I2C支持三種不同的通信速率:100KHZ, 400KHZ,1MHZ。由于AT24C02只支持最高400KHZ,所以這里就選擇400KHZ做為內(nèi)核時鐘。
創(chuàng)建I2C測試工程,建立過程略。幾點要注意的地方。
首先,本例程中使用的是I2C1,對應的SCL及SDA引腳為PB8及PB9,時鐘配置后SYSCLK為80MHZ。
I2C硬件配置參數(shù)如圖
172553y1ik084akkux0ung.png (38.16 KB, 下載次數(shù): 79)
下載附件
2016-6-17 16:19 上傳
速度有三種可選:標準,快速,快速加。另外針對不同的特定應用,可以選擇設置上升和下降沿的時間,以納秒為單位,范圍為0-120納秒之間。注意這個TIMING參數(shù),是灰色的,CUBEMX自動計算并設置該值,不像以前的F0和F3系列,需要一個專門的工具來設置TIMING的值,夠麻煩的。
下面是使用HAL庫與EEPROM通信的主要代碼。
- /* USER CODE BEGIN PV */
- /* Private variables ---------------------------------------------------------*/
- #define EEPROM_ADDRESS 0xA0
- #define EEPROM_PAGESIZE 0x11
- #define EEPROM_TIMEOUT 1000
- extern I2C_HandleTypeDef hi2c1;
- char msg[] = "This is a test!";
- char buf[40];
- int16_t Remaining_Bytes;
- uint16_t Memory_Address;
- uint16_t len;
- ......
- //write msg to eeprom
- Remaining_Bytes = strlen(msg);
- Memory_Address = 0;
-
- while(Remaining_Bytes > 0)
- {
- if(HAL_I2C_Mem_Write_DMA(&hi2c1, EEPROM_ADDRESS, Memory_Address, I2C_MEMADD_SIZE_8BIT, (uint8_t *)(msg + Memory_Address), EEPROM_PAGESIZE) != HAL_OK)
- {
- Error_Handler();
-
- }
-
- while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
- {
- }
-
- while (HAL_I2C_IsDeviceReady(&hi2c1, EEPROM_ADDRESS, 10, 300) == HAL_TIMEOUT);
-
- while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
- {
- }
-
- Remaining_Bytes -= EEPROM_PAGESIZE;
-
- Memory_Address += EEPROM_PAGESIZE;
- }
-
- //read msg to buff
- if(HAL_I2C_Mem_Read_DMA(&hi2c1 , EEPROM_ADDRESS, 0, I2C_MEMADD_SIZE_8BIT, (uint8_t*)buf, strlen(msg))!= HAL_OK)
- {
- /* Reading process Error */
- Error_Handler();
- }
-
- /* Wait for the end of the transfer */
- while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
- {
- }
復制代碼
根據(jù)STD庫中用戶的反饋,I2C出錯的原因主要由幾方面造成,一是ST設計的I2C接口,通信過程中一些狀態(tài)變量及標志位的設置非常嚴格,如果程序邏輯考慮不周全,會導致I2C停止工作,需要軟復位后才能繼續(xù);另一方面,I2C接口在處理單個,雙字節(jié)及多字節(jié)傳輸過程中要分開考慮,進一步將問題復雜化了;還有就是結(jié)合具體的I2C設備,不同的時序要求,進一步復雜化了通信過程。
這些問題在HAL庫中都得到了很好的解決,為此,HAL庫中設計了幾種不同類型的API函數(shù)供調(diào)用。一類是通用的接收和發(fā)送函數(shù),這類API函數(shù)將I2C接口視為類似于“流對象”,通信時從流對象中接收或發(fā)送數(shù)據(jù);另一類則是類似于EEPROM之類的特殊存儲對象,從這些存儲對象中接收或發(fā)送數(shù)據(jù)時,都需要提供一個地址,以便準確定位。使用EEPROM來實驗時,我們使用的是第二類API函數(shù)。兩個主要的API函數(shù)原型及參數(shù)如下。
- /**
- * @brief Transmit in master mode an amount of data in non-blocking mode with DMA
- * @param hi2c : Pointer to a I2C_HandleTypeDef structure that contains
- * the configuration information for the specified I2C.
- * @param DevAddress: Target device address
- * @param pData: Pointer to data buffer
- * @param Size: Amount of data to be sent
- * @retval HAL status
- */
- HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
復制代碼
- /**
- * @brief Reads an amount of data in non-blocking mode with DMA from a specific memory address.
- * @param hi2c : Pointer to a I2C_HandleTypeDef structure that contains
- * the configuration information for the specified I2C.
- * @param DevAddress: Target device address
- * @param MemAddress: Internal memory address
- * @param MemAddSize: Size of internal memory address
- * @param pData: Pointer to data buffer
- * @param Size: Amount of data to be read
- * @retval HAL status
- */
- HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)
復制代碼
關(guān)于這兩個函數(shù)的更詳細的調(diào)用方法及說明,請參考API文檔。
注意在具體操作EEPROM的時候,還要注意,EEPROM分字節(jié)讀寫及頁面讀寫兩種類型,對HAL API的調(diào)用還要進行適當?shù)男薷摹?br />
由于ST HAL API做了大量的后臺工作,上面的代碼看起來過于簡單了。但實際上API函數(shù)在后臺做了大量的工作,這不正是我們期待的么!
最后來看看運行結(jié)果。
UART打印的寫入及讀取取結(jié)果
200806jezn1nzhe6rr2ydx.png (26.46 KB, 下載次數(shù): 74)
下載附件
2016-6-17 16:19 上傳
邏輯分析儀LA
202321hmzpld5lhln2sm8m.png (381.09 KB, 下載次數(shù): 79)
下載附件
2016-6-17 16:19 上傳
I2C通信全程,LA分析結(jié)果
200832s528zs8ls8qk9wwp.png (16.41 KB, 下載次數(shù): 72)
下載附件
2016-6-17 16:19 上傳
開始通信,順序依次是開始信號,地址,EEPROM寫入地址,第一個數(shù)據(jù)字節(jié)
200848lycrh44rrg7hh47f.png (17.79 KB, 下載次數(shù): 77)
下載附件
2016-6-17 16:19 上傳
字符0x0D, 0x0A, 結(jié)束信號。
200900j41pomr3m8r3io12.png (17.09 KB, 下載次數(shù): 66)
下載附件
2016-6-17 16:19 上傳
接收開始,順序依次是開始信號,地址,EEPROM讀取地址,第一個數(shù)據(jù)字節(jié)
200912ezdn2srjrd2tbah3.png (17.78 KB, 下載次數(shù): 80)
下載附件
2016-6-17 16:19 上傳
讀取結(jié)束
200921bglxydazegs68a8g.png (16.27 KB, 下載次數(shù): 100)
下載附件
2016-6-17 16:19 上傳
0.png (48.39 KB, 下載次數(shù): 78)
下載附件
2016-6-17 15:25 上傳
附工程
eeprom.zip
(3.17 MB, 下載次數(shù): 28)
2016-6-17 15:26 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|
|