前言
在上一篇中,我簡單介紹了 SPI 的用法,而除了 SPI 外還有另一種非常常見的通訊協定——I²C(以下稱 I2C)。
I2C 和 SPI 一樣是主從式架構,I2C 的主要特色就是無論有多少 Slave device 都只需要兩條線就可以完成通訊。
在這一篇文章中,我不會詳細介紹 I2C 本身,但建議還是要對它有基本的瞭解比較好,在此推薦「I2C bus 簡介 (Inter-Integrated Circuit Bus) @ 傑克! 真是太神奇了!」及「【Day21】I2C的介紹 - iT 邦幫忙」這兩篇文章。
24C256 是一個擁有 I2C 介面的 EEPROM,這次將示範如何使用 STM32 來透過 I2C 對其進行資料的讀寫,且可以用 USART 進行操作。
正文
首先一樣以 Nucleo-F446RE 做示範。
首先建立一個 PIO 的專案,選擇 Framework 為「libopencm3」,並在 src/
資料夾中新增並開啓 main.c
與 main.h
。
完整程式
分段說明
Include
除了基本的 rcc.h
和 gpio.h
及這次的 i2c.h
外,因為我要使用 USART 和中斷功能,所以還會需要 usart.h
與 nvic.h
。
設定 I2C
首先一樣先設定好 I2C 要使用的 SCL 與 SDA 接腳,將其設為 Open-Drain 的 AF 功能。
再來要設定 I2C 本身。不同於 SPI 規定比較寬鬆(或說自由),I2C 本身的通訊規範基本上都定義好了,所以我們需要調整(或說可以調整)的設定就很少。這裡我們只需要設定要使用的 I2C 速度即可。
24C256 支援的 I2C 速度模式有:
- Standard mode: 100 kbps
- Fast mode: 400 kbps
- Fast mode Plus: 1Mbps
這裡我選擇使用「Fast mode」。以 i2c_set_speed()
函式進行設定,此函式的第二個引數 i2c_speed_fm_400k
就代表要使用「Fast mode」,而第三個引數要給的是 I2C 的時脈,對於 F446RE 或大多數的 STM32,這個速度等同 APB1。
USART ISQ
這是 USART 的 ISQ。
我自己定義了一個簡單的 USART 指令格式:<RW> <Address_1> <Address_2> <Data>
若要在 24C256 的 0x0102
位置寫入資料 0xAB
,就是用 USART 傳送:0x00 0x01 0x02 0xAB
,完成後會收到一個 0xF0
作為 ACK 確認。同理,要在 0x0FCD
寫入 0x40
拿就是要傳送 0x00 0x0F 0xCD 0x40
。
要讀取 0x0102
位置的資料的話,那就是用 USART 傳送:0x01 0x01 0x02
,然後 STM32 就會回傳該位置的資料。
24C256 的定址範圍為 0x0000
~ 0x7FFF
共 32768 個位置,每個位置皆為一個 Byte。
當 USART 接收到一筆資料時,會先判斷這是要進行寫(0x00
)還是讀(0x01
)。然後再使用 I2C 傳送資料。
i2c_transfer7()
用來進行 I2C 的傳輸,讀和寫都靠它。其參數意義依序為:
- 使用的 I2C。這裡是用
I2C1
。
- 要溝通的 Slave device I2C 7-bit 位置。24C256 的預設位置為
0x50
。
- 傳送資料陣列,即要傳送的位元組陣列。
- 傳送資料長度,要傳送幾個 Byte。填
0
代表不進行傳送。
- 接收資料陣列,接收到的資料會存進來。
- 接收資料長度,要接收幾個 Byte。填
0
代表不進行接收。
24C256 基本的讀寫操作也是很簡單。要寫的話就是依序傳送「位置-高
、位置-低
、資料
」這 3 個位元組即可。要讀的話就是依序傳送「位置-高
、位置-低
」這 2 個位元組,然後就可以讀取 該位置的資料位元組。
因此寫入的程式為:
而讀取的程式為:
多環境程式(F446RE + F103RB)
由於 STM32F1 的部分函式不同,所以 F103RB 沒辦法直接使用上面的 F446RE 的程式。
由於本例的差異比較大,為了不佔版面這裡就不列出的,完整的程式請看 GitHub repo。
特別要主要的是,F103RB 要使用 PB8 和 PB9 作為 I2C 的 SCL 及 SDA 腳時,要啓用「Remap」。詳細請參考 DS5319 的 Table 5。
成果
我首先將 0xAB
寫入 0x0000
(00 00 00 AB
),再寫入 0x39
到 0x0001
(00 00 01 39
)。
然後讀取 0x0000
(01 00 00
)得到回傳的 0xAB
,再讀取 0x0001
(01 00 01
)得到 0x39
。
最後再次寫入 0xCD
到 0x0000
(00 00 00 CD
),再讀取它(01 00 00
)得到 0xCD
。
小結
這次介紹了 I2C 的程式寫法。SPI 與 I2C 是各種電路模組或 IC 會使用的通訊協定,只要會使用 SPI 與 I2C,那基本上常見的模組都可以使用了,因此 I2C 是一個很重要的功能,還好 STM32 本身的硬體及 LibOpenCM3 都把那些複雜的設定做好了,因此要使用 I2C 相當容易。
參考資料
本文的程式也有放在 GitHub 上。
本文同步發表於 iT 邦幫忙-2022 iThome 鐵人賽。
留言可能不會立即顯示。若過了幾天仍未出現,請 Email 聯繫:)