|
NUCLEO-L476RG到手一段時間了,今天來測試一下硬件I2C的操作,聽說很多BUG,試一試看如何。
準備用I2C來訪問AT24C02 EEPROM,將數據寫入到EEPROM中,然后再讀取出來,驗證讀寫操作的正確性。
硬件平臺如下
NUCLEO-L476RG
前段時間FSL的KL02Z套裝,上面的MINIDOCK擴展板上恰好有AT24C02,所以借過來用一下,FSL不要生氣啊
標準的ARDUINO接口,放到哪都行。合體照。
硬件連接及原理圖
MINI DOCI上的AT24C02連接
ARDUINO接口:SCL及SDA
NUCLEO-L476RG上對應的接口
寫程序之前,要了解硬件的基本特性,首先是STM32L476RG上I2C接口支持的特性;其次是AT24C02的特性。
STM32L476RG的I2C功能框圖如下
值得一提的是,L4的I2C有自己獨立的內核時鐘,可以在SYSCLK、PCLK及HSI中任選一種做為I2C的內核時鐘。另外L4的I2C支持三種不同的通信速率:100KHZ, 400KHZ,1MHZ。由于AT24C02只支持最高400KHZ,所以這里就選擇400KHZ做為內核時鐘。
創建I2C測試工程,建立過程略。幾點要注意的地方。
首先,本例程中使用的是I2C1,對應的SCL及SDA引腳為PB8及PB9,時鐘配置后SYSCLK為80MHZ。
I2C硬件配置參數如圖
速度有三種可選:標準,快速,快速加。另外針對不同的特定應用,可以選擇設置上升和下降沿的時間,以納秒為單位,范圍為0-120納秒之間。注意這個TIMING參數,是灰色的,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)
- {
- }
復制代碼
根據STD庫中用戶的反饋,I2C出錯的原因主要由幾方面造成,一是ST設計的I2C接口,通信過程中一些狀態變量及標志位的設置非常嚴格,如果程序邏輯考慮不周全,會導致I2C停止工作,需要軟復位后才能繼續;另一方面,I2C接口在處理單個,雙字節及多字節傳輸過程中要分開考慮,進一步將問題復雜化了;還有就是結合具體的I2C設備,不同的時序要求,進一步復雜化了通信過程。
這些問題在HAL庫中都得到了很好的解決,為此,HAL庫中設計了幾種不同類型的API函數供調用。一類是通用的接收和發送函數,這類API函數將I2C接口視為類似于“流對象”,通信時從流對象中接收或發送數據;另一類則是類似于EEPROM之類的特殊存儲對象,從這些存儲對象中接收或發送數據時,都需要提供一個地址,以便準確定位。使用EEPROM來實驗時,我們使用的是第二類API函數。兩個主要的API函數原型及參數如下。
- /**
- * @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)
復制代碼
關于這兩個函數的更詳細的調用方法及說明,請參考API文檔。
注意在具體操作EEPROM的時候,還要注意,EEPROM分字節讀寫及頁面讀寫兩種類型,對HAL API的調用還要進行適當的修改。
由于ST HAL API做了大量的后臺工作,上面的代碼看起來過于簡單了。但實際上API函數在后臺做了大量的工作,這不正是我們期待的么!
最后來看看運行結果。
UART打印的寫入及讀取取結果
邏輯分析儀LA
I2C通信全程,LA分析結果
開始通信,順序依次是開始信號,地址,EEPROM寫入地址,第一個數據字節
字符0x0D, 0x0A, 結束信號。
接收開始,順序依次是開始信號,地址,EEPROM讀取地址,第一個數據字節
讀取結束
附工程
eeprom.zip
(3.17 MB, 下載次數: 28)
2016-6-17 15:26 上傳
點擊文件名下載附件
|
|